Medical Imaging Interaction Toolkit  2018.4.99-3e3f1a6e
Medical Imaging Interaction Toolkit
mitkDisplayInteractor.cpp
Go to the documentation of this file.
1 /*============================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
13 #include "mitkDisplayInteractor.h"
14 
15 #include "mitkBaseRenderer.h"
16 #include "mitkCameraController.h"
18 #include "mitkPropertyList.h"
20 #include <mitkRotationOperation.h>
21 #include <cstring>
22 // level window
23 #include "mitkLevelWindow.h"
25 #include "mitkLine.h"
28 #include "vtkRenderWindowInteractor.h"
29 
30 // Rotation
31 #include "mitkInteractionConst.h"
32 #include "rotate_cursor.xpm"
34 #include <mitkRotationOperation.h>
35 
36 #include "mitkImage.h"
38 #include "mitkPixelTypeMultiplex.h"
39 #include "mitkStatusBar.h"
40 
42 
43 void mitk::DisplayInteractor::Notify(InteractionEvent *interactionEvent, bool isHandled)
44 {
45  // to use the state machine pattern,
46  // the event is passed to the state machine interface to be handled
47  if (!isHandled || m_AlwaysReact)
48  {
49  HandleEvent(interactionEvent, nullptr);
50  }
51 }
52 
54  : m_IndexToSliceModifier(4)
55  , m_AutoRepeat(false)
56  , m_InvertScrollDirection(false)
57  , m_InvertZoomDirection(false)
58  , m_InvertMoveDirection(false)
59  , m_InvertLevelWindowDirection(false)
60  , m_AlwaysReact(false)
61  , m_ZoomFactor(2)
62  , m_LinkPlanes(true)
63 {
64  m_StartCoordinateInMM.Fill(0);
65  m_LastDisplayCoordinate.Fill(0);
66  m_LastCoordinateInMM.Fill(0);
67  m_CurrentDisplayCoordinate.Fill(0);
68 }
69 
71 {
72  // nothing here
73 }
74 
76 {
77  CONNECT_CONDITION("check_position_event", CheckPositionEvent);
78  CONNECT_CONDITION("check_can_rotate", CheckRotationPossible);
79  CONNECT_CONDITION("check_can_swivel", CheckSwivelPossible);
80 
81  CONNECT_FUNCTION("init", Init);
82  CONNECT_FUNCTION("move", Move);
83  CONNECT_FUNCTION("zoom", Zoom);
84  CONNECT_FUNCTION("scroll", Scroll);
85  CONNECT_FUNCTION("ScrollOneDown", ScrollOneDown);
86  CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp);
87  CONNECT_FUNCTION("levelWindow", AdjustLevelWindow);
88  CONNECT_FUNCTION("setCrosshair", SetCrosshair);
89 
90  CONNECT_FUNCTION("updateStatusbar", UpdateStatusbar)
91 
92  CONNECT_FUNCTION("startRotation", StartRotation);
93  CONNECT_FUNCTION("endRotation", EndRotation);
94  CONNECT_FUNCTION("rotate", Rotate);
95 
96  CONNECT_FUNCTION("swivel", Swivel);
97 
98  CONNECT_FUNCTION("IncreaseTimeStep", IncreaseTimeStep);
99  CONNECT_FUNCTION("DecreaseTimeStep", DecreaseTimeStep);
100 }
101 
103 {
104  const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
105  if (positionEvent == nullptr)
106  {
107  return false;
108  }
109 
110  return true;
111 }
112 
114 {
115  // Decide between moving and rotation slices.
116  /*
117  Detailed logic:
118 
119  1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be
120  rotated. Needs not even be counted or checked.
121  2. Inspect every other SliceNavigationController
122  - calculate the line intersection of this SliceNavigationController's plane with our rendering plane
123  - if there is NO interesection, ignore and continue
124  - IF there is an intersection
125  - check the mouse cursor's distance from that line.
126  0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in
127  "locked" mode)
128  1. on first line near the cursor, just remember this intersection line as THE other plane that we want to rotate
129  2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to
130  rotate
131  - if yes, we just push this line to the "other" lines and rotate it along
132  - if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to
133  rotate
134  */
135  const auto *posEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
136  if (posEvent == nullptr)
137  return false;
138 
139  BaseRenderer *clickedRenderer = posEvent->GetSender();
140  const PlaneGeometry *ourViewportGeometry = (clickedRenderer->GetCurrentWorldPlaneGeometry());
141 
142  if (!ourViewportGeometry)
143  return false;
144 
145  Point3D cursorPosition = posEvent->GetPositionInWorld();
146  const auto spacing = ourViewportGeometry->GetSpacing();
147  const PlaneGeometry *geometryToBeRotated = nullptr; // this one is under the mouse cursor
148  const PlaneGeometry *anyOtherGeometry = nullptr; // this is also visible (for calculation of intersection ONLY)
149  Line3D intersectionLineWithGeometryToBeRotated;
150 
151  bool hitMultipleLines(false);
152  m_SNCsToBeRotated.clear();
153 
154  const double threshholdDistancePixels = 12.0;
155 
157 
158  for (auto renWin : renWindows)
159  {
161 
162  // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes.
163  if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D)
164  continue;
165 
166  const PlaneGeometry *otherRenderersRenderPlane = snc->GetCurrentPlaneGeometry();
167  if (otherRenderersRenderPlane == nullptr)
168  continue; // ignore, we don't see a plane
169 
170  // check if there is an intersection
171  Line3D intersectionLine; // between rendered/clicked geometry and the one being analyzed
172  if (!ourViewportGeometry->IntersectionLine(otherRenderersRenderPlane, intersectionLine))
173  {
174  continue; // we ignore this plane, it's parallel to our plane
175  }
176 
177  // check distance from intersection line
178  const double distanceFromIntersectionLine =
179  intersectionLine.Distance(cursorPosition) / spacing[snc->GetDefaultViewDirection()];
180 
181  // far away line, only remember for linked rotation if necessary
182  if (distanceFromIntersectionLine > threshholdDistancePixels)
183  {
184  anyOtherGeometry = otherRenderersRenderPlane; // we just take the last one, so overwrite each iteration (we just
185  // need some crossing point)
186  // TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used
187  if (m_LinkPlanes)
188  {
189  m_SNCsToBeRotated.push_back(snc);
190  }
191  }
192  else // close to cursor
193  {
194  if (geometryToBeRotated == nullptr) // first one close to the cursor
195  {
196  geometryToBeRotated = otherRenderersRenderPlane;
197  intersectionLineWithGeometryToBeRotated = intersectionLine;
198  m_SNCsToBeRotated.push_back(snc);
199  }
200  else
201  {
202  // compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane
203  // together with the primary one
204  // if different, DON'T rotate
205  if (intersectionLine.IsParallel(intersectionLineWithGeometryToBeRotated) &&
206  intersectionLine.Distance(intersectionLineWithGeometryToBeRotated.GetPoint1()) < mitk::eps)
207  {
208  m_SNCsToBeRotated.push_back(snc);
209  }
210  else
211  {
212  hitMultipleLines = true;
213  }
214  }
215  }
216  }
217 
218  bool moveSlices(true);
219 
220  if (geometryToBeRotated && anyOtherGeometry && ourViewportGeometry && !hitMultipleLines)
221  {
222  // assure all three are valid, so calculation of center of rotation can be done
223  moveSlices = false;
224  }
225  // question in state machine is: "rotate?"
226  if (moveSlices) // i.e. NOT rotate
227  {
228  return false;
229  }
230  else
231  { // we DO have enough information for rotation
232  m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project(
233  cursorPosition); // remember where the last cursor position ON THE LINE has been observed
234 
235  if (anyOtherGeometry->IntersectionPoint(
236  intersectionLineWithGeometryToBeRotated,
237  m_CenterOfRotation)) // find center of rotation by intersection with any of the OTHER lines
238  {
239  return true;
240  }
241  else
242  {
243  return false;
244  }
245  }
246  return false;
247 }
248 
250 {
251  const ScalarType ThresholdDistancePixels = 6.0;
252 
253  // Decide between moving and rotation: if we're close to the crossing
254  // point of the planes, moving mode is entered, otherwise
255  // rotation/swivel mode
256  const auto *posEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
257 
258  BaseRenderer *renderer = interactionEvent->GetSender();
259 
260  if (!posEvent || !renderer)
261  return false;
262 
263  const Point3D &cursor = posEvent->GetPositionInWorld();
264 
265  m_SNCsToBeRotated.clear();
266 
267  const PlaneGeometry *clickedGeometry(nullptr);
268  const PlaneGeometry *otherGeometry1(nullptr);
269  const PlaneGeometry *otherGeometry2(nullptr);
270 
272 
273  for (auto renWin : renWindows)
274  {
276 
277  // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes.
278  if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D)
279  continue;
280 
281  // unsigned int slice = (*iter)->GetSlice()->GetPos();
282  // unsigned int time = (*iter)->GetTime()->GetPos();
283 
284  const PlaneGeometry *planeGeometry = snc->GetCurrentPlaneGeometry();
285  if (!planeGeometry)
286  continue;
287 
288  if (snc == renderer->GetSliceNavigationController())
289  {
290  clickedGeometry = planeGeometry;
291  m_SNCsToBeRotated.push_back(snc);
292  }
293  else
294  {
295  if (otherGeometry1 == nullptr)
296  {
297  otherGeometry1 = planeGeometry;
298  }
299  else
300  {
301  otherGeometry2 = planeGeometry;
302  }
303  if (m_LinkPlanes)
304  {
305  // If planes are linked, apply rotation to all planes
306  m_SNCsToBeRotated.push_back(snc);
307  }
308  }
309  }
310 
312  mitk::Point3D point;
313  if ((clickedGeometry != nullptr) && (otherGeometry1 != nullptr) && (otherGeometry2 != nullptr) &&
314  clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point))
315  {
316  m_CenterOfRotation = point;
317  if (m_CenterOfRotation.EuclideanDistanceTo(cursor) < ThresholdDistancePixels)
318  {
319  return false;
320  }
321  else
322  {
323  m_ReferenceCursor = posEvent->GetPointerPositionOnScreen();
324 
325  // Get main axes of rotation plane and store it for rotation step
326  m_RotationPlaneNormal = clickedGeometry->GetNormal();
327 
328  ScalarType xVector[] = {1.0, 0.0, 0.0};
329  ScalarType yVector[] = {0.0, 1.0, 0.0};
330  clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(xVector), m_RotationPlaneXVector);
331  clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(yVector), m_RotationPlaneYVector);
332 
333  m_RotationPlaneNormal.Normalize();
334  m_RotationPlaneXVector.Normalize();
335  m_RotationPlaneYVector.Normalize();
336 
337  m_PreviousRotationAxis.Fill(0.0);
338  m_PreviousRotationAxis[2] = 1.0;
339  m_PreviousRotationAngle = 0.0;
340 
341  return true;
342  }
343  }
344  else
345  {
346  return false;
347  }
348  return false;
349 }
350 
352 {
353  auto *positionEvent = static_cast<InteractionPositionEvent *>(interactionEvent);
354 
355  m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
356  m_CurrentDisplayCoordinate = m_LastDisplayCoordinate;
357  positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate, m_StartCoordinateInMM);
358  m_LastCoordinateInMM = m_StartCoordinateInMM;
359 }
360 
362 {
363  BaseRenderer *sender = interactionEvent->GetSender();
364  auto *positionEvent = static_cast<InteractionPositionEvent *>(interactionEvent);
365 
366  float invertModifier = -1.0;
367  if (m_InvertMoveDirection)
368  {
369  invertModifier = 1.0;
370  }
371  // perform translation
372  Vector2D moveVector = (positionEvent->GetPointerPositionOnScreen() - m_LastDisplayCoordinate) * invertModifier;
373  moveVector *= sender->GetScaleFactorMMPerDisplayUnit();
374 
375  sender->GetCameraController()->MoveBy(moveVector);
377  m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
378 }
379 
381 {
382  auto* positionEvent = static_cast<InteractionPositionEvent*>(interactionEvent);
383  Point3D pos = positionEvent->GetPositionInWorld();
384 
385  const BaseRenderer::Pointer sender = interactionEvent->GetSender();
387  for (auto renWin : renWindows)
388  {
389  if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D && renWin != sender->GetRenderWindow())
390  {
392  }
393  }
394 }
395 
397 {
398  auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController();
399  auto stepper = sliceNaviController->GetTime();
400  stepper->SetAutoRepeat(true);
401  stepper->Next();
402 }
403 
405 {
406  auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController();
407  auto stepper = sliceNaviController->GetTime();
408  stepper->SetAutoRepeat(true);
409  stepper->Previous();
410 }
411 
413 {
414  float factor = 1.0;
415  float distance = 0;
416 
417  if (m_ZoomDirection == "updown")
418  {
419  distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1];
420  }
421  else
422  {
423  distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0];
424  }
425 
426  if (m_InvertZoomDirection)
427  {
428  distance *= -1.0;
429  }
430 
431  // set zooming speed
432  if (distance < 0.0)
433  {
434  factor = 1.0 / m_ZoomFactor;
435  }
436  else if (distance > 0.0)
437  {
438  factor = 1.0 * m_ZoomFactor;
439  }
440 
441  auto* positionEvent = static_cast<InteractionPositionEvent*>(interactionEvent);
442  m_LastDisplayCoordinate = m_CurrentDisplayCoordinate;
443  m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
444 
445  if (factor != 1.0)
446  {
447  const BaseRenderer::Pointer sender = interactionEvent->GetSender();
448  sender->GetCameraController()->Zoom(factor, m_StartCoordinateInMM);
449  RenderingManager::GetInstance()->RequestUpdate(sender->GetRenderWindow());
450  }
451 }
452 
454 {
455  auto* positionEvent = static_cast<InteractionPositionEvent *>(interactionEvent);
456 
457  mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController();
458  if (sliceNaviController)
459  {
460  int delta = 0;
461  // Scrolling direction
462  if (m_ScrollDirection == "updown")
463  {
464  delta = static_cast<int>(m_LastDisplayCoordinate[1] - positionEvent->GetPointerPositionOnScreen()[1]);
465  }
466  else
467  {
468  delta = static_cast<int>(m_LastDisplayCoordinate[0] - positionEvent->GetPointerPositionOnScreen()[0]);
469  }
470 
471  if (m_InvertScrollDirection)
472  {
473  delta *= -1;
474  }
475 
476  // Set how many pixels the mouse has to be moved to scroll one slice
477  // if we moved less than 'm_IndexToSliceModifier' pixels slice ONE slice only
478  if (delta > 0 && delta < m_IndexToSliceModifier)
479  {
480  delta = m_IndexToSliceModifier;
481  }
482  else if (delta < 0 && delta > -m_IndexToSliceModifier)
483  {
484  delta = -m_IndexToSliceModifier;
485  }
486  delta /= m_IndexToSliceModifier;
487 
488  int newPos = sliceNaviController->GetSlice()->GetPos() + delta;
489 
490  // if auto repeat is on, start at first slice if you reach the last slice and vice versa
491  int maxSlices = sliceNaviController->GetSlice()->GetSteps();
492  if (m_AutoRepeat)
493  {
494  while (newPos < 0)
495  {
496  newPos += maxSlices;
497  }
498 
499  while (newPos >= maxSlices)
500  {
501  newPos -= maxSlices;
502  }
503  }
504  else
505  {
506  // if the new slice is below 0 we still show slice 0
507  // due to the stepper using unsigned int we have to do this ourselves
508  if (newPos < 1)
509  {
510  newPos = 0;
511  }
512  }
513 
514  m_LastDisplayCoordinate = m_CurrentDisplayCoordinate;
515  m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
516 
517  // set the new position
518  sliceNaviController->GetSlice()->SetPos(newPos);
519  }
520 }
521 
523 {
524  mitk::SliceNavigationController::Pointer sliceNaviController =
525  interactionEvent->GetSender()->GetSliceNavigationController();
526  if (!sliceNaviController->GetSliceLocked())
527  {
528  mitk::Stepper *stepper = sliceNaviController->GetSlice();
529  if (stepper->GetSteps() <= 1)
530  {
531  stepper = sliceNaviController->GetTime();
532  }
533  stepper->Next();
534  }
535 }
536 
538 {
539  mitk::SliceNavigationController::Pointer sliceNaviController =
540  interactionEvent->GetSender()->GetSliceNavigationController();
541  if (!sliceNaviController->GetSliceLocked())
542  {
543  mitk::Stepper *stepper = sliceNaviController->GetSlice();
544  if (stepper->GetSteps() <= 1)
545  {
546  stepper = sliceNaviController->GetTime();
547  }
548  stepper->Previous();
549  }
550 }
551 
553 {
554  BaseRenderer::Pointer sender = interactionEvent->GetSender();
555  auto *positionEvent = static_cast<InteractionPositionEvent *>(interactionEvent);
556 
557  m_LastDisplayCoordinate = m_CurrentDisplayCoordinate;
558  m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
559  // search for active image
560  mitk::DataStorage::Pointer storage = sender->GetDataStorage();
561  mitk::DataNode::Pointer node = nullptr;
562  mitk::DataStorage::SetOfObjects::ConstPointer allImageNodes = storage->GetSubset(mitk::NodePredicateDataType::New("Image"));
563  for (unsigned int i = 0; i < allImageNodes->size(); ++i)
564  {
565  bool isActiveImage = false;
566  bool propFound = allImageNodes->at(i)->GetBoolProperty("imageForLevelWindow", isActiveImage);
567 
568  if (propFound && isActiveImage)
569  {
570  node = allImageNodes->at(i);
571  continue;
572  }
573  }
574  if (node.IsNull())
575  {
576  node = storage->GetNode(mitk::NodePredicateDataType::New("Image"));
577  }
578  if (node.IsNull())
579  {
580  return;
581  }
582 
584  node->GetLevelWindow(lv);
585  ScalarType level = lv.GetLevel();
586  ScalarType window = lv.GetWindow();
587 
588  int levelIndex = 0;
589  int windowIndex = 1;
590 
591  if (m_LevelDirection != "leftright")
592  {
593  levelIndex = 1;
594  windowIndex = 0;
595  }
596 
597  int directionModifier = 1;
598  if (m_InvertLevelWindowDirection)
599  {
600  directionModifier = -1;
601  }
602 
603  // calculate adjustments from mouse movements
604  level += (m_CurrentDisplayCoordinate[levelIndex] - m_LastDisplayCoordinate[levelIndex]) * static_cast<ScalarType>(2) *
605  directionModifier;
606  window += (m_CurrentDisplayCoordinate[windowIndex] - m_LastDisplayCoordinate[windowIndex]) *
607  static_cast<ScalarType>(2) * directionModifier;
608 
609  lv.SetLevelWindow(level, window);
610  dynamic_cast<mitk::LevelWindowProperty *>(node->GetProperty("levelwindow"))->SetLevelWindow(lv);
611 
613 }
614 
616 {
617  this->SetMouseCursor(rotate_cursor_xpm, 0, 0);
618 }
619 
621 {
622  this->ResetMouseCursor();
623 }
624 
626 {
627  const auto *posEvent = dynamic_cast<const InteractionPositionEvent *>(event);
628  if (posEvent == nullptr)
629  return;
630 
631  Point3D cursor = posEvent->GetPositionInWorld();
632 
633  Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation;
634  Vector3D toCursor = cursor - m_CenterOfRotation;
635 
636  // cross product: | A x B | = |A| * |B| * sin(angle)
637  Vector3D axisOfRotation;
638  vnl_vector_fixed<ScalarType, 3> vnlDirection = vnl_cross_3d(toCursor.GetVnlVector(), toProjected.GetVnlVector());
639  axisOfRotation.SetVnlVector(vnlDirection);
640 
641  // scalar product: A * B = |A| * |B| * cos(angle)
642  // tan = sin / cos
643  ScalarType angle = -atan2((double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected));
644  angle *= 180.0 / vnl_math::pi;
645  m_LastCursorPosition = cursor;
646 
647  // create RotationOperation and apply to all SNCs that should be rotated
648  RotationOperation rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle);
649 
650  // iterate the OTHER slice navigation controllers: these are filled in DoDecideBetweenRotationAndSliceSelection
651  for (auto iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
652  {
653  TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry();
654  if (!timeGeometry)
655  continue;
656 
657  timeGeometry->ExecuteOperation(&rotationOperation);
658 
659  (*iter)->SendCreatedWorldGeometryUpdate();
660  }
661 
663 }
664 
666 {
667  const auto *posEvent = dynamic_cast<const InteractionPositionEvent *>(event);
668 
669  if (!posEvent)
670  return;
671 
672  // Determine relative mouse movement projected onto world space
673  Point2D cursor = posEvent->GetPointerPositionOnScreen();
674  Vector2D relativeCursor = cursor - m_ReferenceCursor;
675  Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1];
676 
677  // Determine rotation axis (perpendicular to rotation plane and cursor
678  // movement)
679  Vector3D rotationAxis = itk::CrossProduct(m_RotationPlaneNormal, relativeCursorAxis);
680 
681  ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0;
682 
683  // Restore the initial plane pose by undoing the previous rotation
684  // operation
685  RotationOperation op(OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle);
686 
687  SNCVector::iterator iter;
688  for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
689  {
690  if (!(*iter)->GetSliceRotationLocked())
691  {
692  TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry();
693  if (!timeGeometry)
694  continue;
695 
696  timeGeometry->ExecuteOperation(&op);
697  (*iter)->SendCreatedWorldGeometryUpdate();
698  }
699  }
700 
701  // Apply new rotation operation to all relevant SNCs
702  RotationOperation op2(OpROTATE, m_CenterOfRotation, rotationAxis, rotationAngle);
703 
704  for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
705  {
706  if (!(*iter)->GetSliceRotationLocked())
707  {
708  // Retrieve the TimeGeometry of this SliceNavigationController
709  TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry();
710  if (!timeGeometry)
711  continue;
712 
713  // Execute the new rotation
714  timeGeometry->ExecuteOperation(&op2);
715 
716  // Notify listeners
717  (*iter)->SendCreatedWorldGeometryUpdate();
718  }
719  }
720 
721  m_PreviousRotationAxis = rotationAxis;
722  m_PreviousRotationAngle = rotationAngle;
723 
725  return;
726 }
727 
729 {
730  const auto* posEvent = dynamic_cast<const InteractionPositionEvent *>(event);
731  if (nullptr == posEvent)
732  {
733  return;
734  }
735 
736  const mitk::BaseRenderer::Pointer baseRenderer = posEvent->GetSender();
738  auto globalCurrentTimePoint = baseRenderer->GetTime();
739  mitk::DataStorage::SetOfObjects::ConstPointer nodes = baseRenderer->GetDataStorage()->GetSubset(isImageData).GetPointer();
740  if (nodes.IsNull())
741  {
742  return;
743  }
744 
745  // posEvent->GetPositionInWorld() would return the world position at the
746  // time of initiating the interaction. However, we need to update the
747  // status bar with the position after changing slice. Therefore, we
748  // translate the same display position with the renderer again to
749  // get the new world position.
750  Point3D worldposition;
751  baseRenderer->DisplayToWorld(posEvent->GetPointerPositionOnScreen(), worldposition);
752 
753  mitk::Image::Pointer image3D;
755  mitk::DataNode::Pointer topSourceNode;
756 
757  int component = 0;
758 
759  node = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, baseRenderer);
760  if (node.IsNull())
761  {
762  return;
763  }
764 
765  bool isBinary(false);
766  node->GetBoolProperty("binary", isBinary);
767  if (isBinary)
768  {
769  mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = baseRenderer->GetDataStorage()->GetSources(node, nullptr, true);
770  if (!sourcenodes->empty())
771  {
772  topSourceNode = mitk::FindTopmostVisibleNode(sourcenodes, worldposition, globalCurrentTimePoint, baseRenderer);
773  }
774  if (topSourceNode.IsNotNull())
775  {
776  image3D = dynamic_cast<mitk::Image *>(topSourceNode->GetData());
777  topSourceNode->GetIntProperty("Image.Displayed Component", component);
778  }
779  else
780  {
781  image3D = dynamic_cast<mitk::Image *>(node->GetData());
782  node->GetIntProperty("Image.Displayed Component", component);
783  }
784  }
785  else
786  {
787  image3D = dynamic_cast<mitk::Image *>(node->GetData());
788  node->GetIntProperty("Image.Displayed Component", component);
789  }
790 
791  // get the position and gray value from the image and build up status bar text
792  auto statusBar = StatusBar::GetInstance();
793  if (image3D.IsNotNull() && statusBar != nullptr)
794  {
795  itk::Index<3> p;
796  image3D->GetGeometry()->WorldToIndex(worldposition, p);
797 
798  auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType();
799  if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA)
800  {
801  std::string pixelValue = "Pixel RGB(A) value: ";
802  pixelValue.append(ConvertCompositePixelValueToString(image3D, p));
803  statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue.c_str());
804  }
805  else if (pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR)
806  {
807  std::string pixelValue = "See ODF Details view. ";
808  statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue.c_str());
809  }
810  else
811  {
812  mitk::ScalarType pixelValue;
814  image3D->GetChannelDescriptor().GetPixelType(),
815  image3D,
816  image3D->GetVolumeData(image3D->GetTimeGeometry()->TimePointToTimeStep(globalCurrentTimePoint)),
817  p,
818  pixelValue,
819  component);
820  statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue);
821  }
822  }
823  else
824  {
825  statusBar->DisplayImageInfoInvalid();
826  }
827 }
828 
830 {
832  // auto repeat
833  std::string strAutoRepeat = "";
834  if (properties->GetStringProperty("autoRepeat", strAutoRepeat))
835  {
836  if (strAutoRepeat == "true")
837  {
838  m_AutoRepeat = true;
839  }
840  else
841  {
842  m_AutoRepeat = false;
843  }
844  }
845  // pixel movement for scrolling one slice
846  std::string strPixelPerSlice = "";
847  if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice))
848  {
849  m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str());
850  }
851  else
852  {
853  m_IndexToSliceModifier = 4;
854  }
855  // scroll direction
856  if (!properties->GetStringProperty("scrollDirection", m_ScrollDirection))
857  {
858  m_ScrollDirection = "updown";
859  }
860 
861  m_InvertScrollDirection = GetBoolProperty(properties, "invertScrollDirection", false);
862 
863  // zoom direction
864  if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection))
865  {
866  m_ZoomDirection = "updown";
867  }
868 
869  m_InvertZoomDirection = GetBoolProperty(properties, "invertZoomDirection", false);
870 
871  m_InvertMoveDirection = GetBoolProperty(properties, "invertMoveDirection", false);
872 
873  if (!properties->GetStringProperty("levelWindowDirection", m_LevelDirection))
874  {
875  m_LevelDirection = "leftright";
876  }
877 
878  m_InvertLevelWindowDirection = GetBoolProperty(properties, "invertLevelWindowDirection", false);
879 
880  // coupled rotation
881  std::string strCoupled = "";
882  if (properties->GetStringProperty("coupled", strCoupled))
883  {
884  if (strCoupled == "true")
885  m_LinkPlanes = true;
886  else
887  m_LinkPlanes = false;
888  }
889 
890  // zoom factor
891  std::string strZoomFactor = "";
892  properties->GetStringProperty("zoomFactor", strZoomFactor);
893  m_ZoomFactor = .05;
894  if (atoi(strZoomFactor.c_str()) > 0)
895  {
896  m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0);
897  }
898  // allwaysReact
899  std::string strAlwaysReact = "";
900  if (properties->GetStringProperty("alwaysReact", strAlwaysReact))
901  {
902  if (strAlwaysReact == "true")
903  {
904  m_AlwaysReact = true;
905  }
906  else
907  {
908  m_AlwaysReact = false;
909  }
910  }
911  else
912  {
913  m_AlwaysReact = false;
914  }
915 }
916 
917 bool mitk::DisplayInteractor::FilterEvents(InteractionEvent *interactionEvent, DataNode * /*dataNode*/)
918 {
919  if (interactionEvent->GetSender() == nullptr)
920  return false;
921  if (interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard3D)
922  return false;
923 
924  return true;
925 }
926 
928  const char *propertyName,
929  bool defaultValue)
930 {
931  std::string valueAsString;
932  if (!propertyList->GetStringProperty(propertyName, valueAsString))
933  {
934  return defaultValue;
935  }
936  else
937  {
938  if (valueAsString == "true")
939  {
940  return true;
941  }
942  else
943  {
944  return false;
945  }
946  }
947 }
mitk::ScalarType FastSinglePixelAccess(mitk::PixelType, mitk::Image::Pointer im, ImageDataItem *item, itk::Index< 3 > idx, mitk::ScalarType &val, int component=0)
virtual bool CheckRotationPossible(const InteractionEvent *interactionEvent)
ScalarType GetLevel() const
method that returns the level value, i.e. the center of the current grey value interval ...
bool IsParallel(const Line< TCoordRep, NPointDimension > &line) const
Test if a lines is parallel to this line.
Definition: mitkLine.h:201
Descibes a line.
Definition: mitkLine.h:28
static char * line
Definition: svm.cpp:2870
Super class for all position events.
void SelectSliceByPoint(const mitk::Point3D &point)
Positions the SNC according to the specified point.
The LevelWindowProperty class Property for the mitk::LevelWindow.
static BaseRenderer * GetInstance(vtkRenderWindow *renWin)
virtual void StartRotation(StateMachineAction *, InteractionEvent *)
Starts crosshair rotation.
virtual void EndRotation(StateMachineAction *, InteractionEvent *)
Ends crosshair rotation.
virtual void Rotate(StateMachineAction *, InteractionEvent *event)
double ScalarType
bool HandleEvent(InteractionEvent *event, DataNode *dataNode)
virtual void Scroll(StateMachineAction *, InteractionEvent *)
Performs scrolling relative to mouse/pointer movement.
Organizes the rendering process.
bool FilterEvents(InteractionEvent *interactionEvent, DataNode *dataNode) override
double GetScaleFactorMMPerDisplayUnit() const
bool IntersectionPoint(const Line3D &line, Point3D &intersectionPoint) const
Calculate intersection point between the plane and a line.
virtual const PlaneGeometry * GetCurrentWorldPlaneGeometry()
Get the current 2D-worldgeometry (m_CurrentWorldPlaneGeometry) used for 2D-rendering.
void Notify(InteractionEvent *interactionEvent, bool isHandled) override
void MoveBy(const Vector2D &moveVectorInMM)
Constants for most interaction classes, due to the generic StateMachines.
std::string MITKCORE_EXPORT ConvertCompositePixelValueToString(Image::Pointer image, itk::Index< 3 > index)
Converts composite pixel values to a displayable string.
const SliceNavigationController * GetTimeNavigationController() const
Controls the selection of the slice the associated BaseRenderer will display.
itk::Point< TCoordRep, NPointDimension > Project(const itk::Point< TCoordRep, NPointDimension > &point) const
Project a point on the line.
Definition: mitkLine.h:152
bool GetBoolProperty(mitk::PropertyList::Pointer propertyList, const char *propertyName, bool defaultValue)
Method to retrieve bool-value for given property from string-property in given propertylist.
virtual void Move(StateMachineAction *, InteractionEvent *)
Performs panning of the data set in the render window.
BaseRenderer * GetSender() const
virtual void ScrollOneUp(StateMachineAction *, InteractionEvent *)
Scrolls one layer down.
The LevelWindow class Class to store level/window values.
virtual void DecreaseTimeStep(StateMachineAction *, InteractionEvent *)
Decreases the time step in 3d+t data.
double Distance(const itk::Point< TCoordRep, NPointDimension > &point) const
Distance of a point from the line.
Definition: mitkLine.h:143
const itk::Point< TCoordRep, NPointDimension > & GetPoint1() const
Get start point of the line.
Definition: mitkLine.h:111
bool IntersectionLine(const PlaneGeometry *plane, Line3D &crossline) const
Calculate the intersecting line of two planes.
void ExecuteOperation(Operation *op) override
Executes the given operation on all time steps.
virtual void Previous()
Vector< ScalarType, 3 > Vector3D
Definition: mitkVector.h:130
static RenderingManager * GetInstance()
virtual void IncreaseTimeStep(StateMachineAction *, InteractionEvent *)
Increases the time step in 3d+t data.
virtual void SetAutoRepeat(bool _arg)
Represents an action, that is executed after a certain event (in statemachine-mechanism) TODO: implem...
virtual CameraController * GetCameraController()
virtual MapperSlotId GetMapperID()
Get the MapperSlotId to use.
Image class for storing images.
Definition: mitkImage.h:72
virtual void Init(StateMachineAction *, InteractionEvent *)
Initializes an interaction, saves the pointers start position for further reference.
const mitk::PlaneGeometry * GetCurrentPlaneGeometry()
Returns the currently selected Plane in the current BaseGeometry (if existent).
void ResetMouseCursor()
Resets the mouse cursor (if modified by the SlicesCoordinator) to its original state.
virtual bool CheckPositionEvent(const InteractionEvent *interactionEvent)
virtual void Zoom(StateMachineAction *, InteractionEvent *)
Performs zooming relative to mouse/pointer movement.
virtual void UpdateStatusbar(StateMachineAction *, InteractionEvent *event)
Updates the Statusbar information with the information about the clicked position.
void SetMouseCursor(const char *xpm[], int hotspotX, int hotspotY)
Sets the specified mouse cursor.
virtual void SetCrosshair(StateMachineAction *, InteractionEvent *)
Sets crosshair at clicked position*.
static StatusBar * GetInstance()
static method to get the GUI dependent StatusBar-instance so the methods DisplayText, etc. can be called No reference counting, cause of decentral static use!
static Pointer New(const char *_arg)
virtual void AdjustLevelWindow(StateMachineAction *, InteractionEvent *)
Adjusts the level windows relative to mouse/pointer movement.
ScalarType GetWindow() const
returns the current window size, i.e the range size of the current grey value interval ...
void RequestUpdate(vtkRenderWindow *renderWindow)
vtkRenderWindow * GetRenderWindow() const
Access the RenderWindow into which this renderer renders.
#define CONNECT_CONDITION(a, f)
Vector3D GetNormal() const
Normal of the plane.
Helper class to step through a list.
Definition: mitkStepper.h:47
const mitk::Vector3D GetSpacing() const
Get the spacing (size of a pixel).
virtual void ScrollOneDown(StateMachineAction *, InteractionEvent *)
Scrolls one layer up.
MITKCORE_EXPORT const ScalarType eps
virtual bool CheckSwivelPossible(const InteractionEvent *interactionEvent)
virtual unsigned int GetSteps() const
virtual SliceNavigationController * GetSliceNavigationController()
PropertyList::Pointer GetAttributes() const
mitk::Stepper * GetTime()
Get the Stepper through the time.
void ConnectActionsAndFunctions() override
#define mitkPixelTypeMultiplex5(function, ptype, param1, param2, param3, param4, param5)
Describes a two-dimensional, rectangular plane.
#define CONNECT_FUNCTION(a, f)
void SetLevelWindow(ScalarType level, ScalarType window, bool expandRangesIfNecessary=true)
MITKCORE_EXPORT DataNode::Pointer FindTopmostVisibleNode(const DataStorage::SetOfObjects::ConstPointer nodes, const Point3D worldPosition, const TimePointType timePoint, const BaseRenderer *baseRender)
returns the topmost visible node of a given list of nodes. The function returns a node that is visibl...
Operation, that holds everything necessary for an rotation operation on mitk::BaseData.
virtual void Next()
const RenderWindowVector & GetAllRegisteredRenderWindows()
void RequestUpdateAll(RequestType type=REQUEST_UPDATE_ALL)
virtual void Swivel(StateMachineAction *, InteractionEvent *event)
Class for nodes of the DataTree.
Definition: mitkDataNode.h:57