Medical Imaging Interaction Toolkit  2018.4.99-bd7b41ba
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  const PlaneGeometry *planeGeometry = snc->GetCurrentPlaneGeometry();
282  if (!planeGeometry)
283  continue;
284 
285  if (snc == renderer->GetSliceNavigationController())
286  {
287  clickedGeometry = planeGeometry;
288  m_SNCsToBeRotated.push_back(snc);
289  }
290  else
291  {
292  if (otherGeometry1 == nullptr)
293  {
294  otherGeometry1 = planeGeometry;
295  }
296  else
297  {
298  otherGeometry2 = planeGeometry;
299  }
300  if (m_LinkPlanes)
301  {
302  // If planes are linked, apply rotation to all planes
303  m_SNCsToBeRotated.push_back(snc);
304  }
305  }
306  }
307 
309  mitk::Point3D point;
310  if ((clickedGeometry != nullptr) && (otherGeometry1 != nullptr) && (otherGeometry2 != nullptr) &&
311  clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point))
312  {
313  m_CenterOfRotation = point;
314  if (m_CenterOfRotation.EuclideanDistanceTo(cursor) < ThresholdDistancePixels)
315  {
316  return false;
317  }
318  else
319  {
320  m_ReferenceCursor = posEvent->GetPointerPositionOnScreen();
321 
322  // Get main axes of rotation plane and store it for rotation step
323  m_RotationPlaneNormal = clickedGeometry->GetNormal();
324 
325  ScalarType xVector[] = {1.0, 0.0, 0.0};
326  ScalarType yVector[] = {0.0, 1.0, 0.0};
327  clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(xVector), m_RotationPlaneXVector);
328  clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(yVector), m_RotationPlaneYVector);
329 
330  m_RotationPlaneNormal.Normalize();
331  m_RotationPlaneXVector.Normalize();
332  m_RotationPlaneYVector.Normalize();
333 
334  m_PreviousRotationAxis.Fill(0.0);
335  m_PreviousRotationAxis[2] = 1.0;
336  m_PreviousRotationAngle = 0.0;
337 
338  return true;
339  }
340  }
341  else
342  {
343  return false;
344  }
345  return false;
346 }
347 
349 {
350  auto *positionEvent = static_cast<InteractionPositionEvent *>(interactionEvent);
351 
352  m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
353  m_CurrentDisplayCoordinate = m_LastDisplayCoordinate;
354  positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate, m_StartCoordinateInMM);
355  m_LastCoordinateInMM = m_StartCoordinateInMM;
356 }
357 
359 {
360  BaseRenderer *sender = interactionEvent->GetSender();
361  auto *positionEvent = static_cast<InteractionPositionEvent *>(interactionEvent);
362 
363  float invertModifier = -1.0;
364  if (m_InvertMoveDirection)
365  {
366  invertModifier = 1.0;
367  }
368  // perform translation
369  Vector2D moveVector = (positionEvent->GetPointerPositionOnScreen() - m_LastDisplayCoordinate) * invertModifier;
370  moveVector *= sender->GetScaleFactorMMPerDisplayUnit();
371 
372  sender->GetCameraController()->MoveBy(moveVector);
374  m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
375 }
376 
378 {
379  auto* positionEvent = static_cast<InteractionPositionEvent*>(interactionEvent);
380  Point3D pos = positionEvent->GetPositionInWorld();
381 
382  const BaseRenderer::Pointer sender = interactionEvent->GetSender();
384  for (auto renWin : renWindows)
385  {
386  if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D && renWin != sender->GetRenderWindow())
387  {
389  }
390  }
391 }
392 
394 {
395  auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController();
396  auto stepper = sliceNaviController->GetTime();
397  stepper->SetAutoRepeat(true);
398  stepper->Next();
399 }
400 
402 {
403  auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController();
404  auto stepper = sliceNaviController->GetTime();
405  stepper->SetAutoRepeat(true);
406  stepper->Previous();
407 }
408 
410 {
411  float factor = 1.0;
412  float distance = 0;
413 
414  if (m_ZoomDirection == "updown")
415  {
416  distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1];
417  }
418  else
419  {
420  distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0];
421  }
422 
423  if (m_InvertZoomDirection)
424  {
425  distance *= -1.0;
426  }
427 
428  // set zooming speed
429  if (distance < 0.0)
430  {
431  factor = 1.0 / m_ZoomFactor;
432  }
433  else if (distance > 0.0)
434  {
435  factor = 1.0 * m_ZoomFactor;
436  }
437 
438  auto* positionEvent = static_cast<InteractionPositionEvent*>(interactionEvent);
439  m_LastDisplayCoordinate = m_CurrentDisplayCoordinate;
440  m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
441 
442  if (factor != 1.0)
443  {
444  const BaseRenderer::Pointer sender = interactionEvent->GetSender();
445  sender->GetCameraController()->Zoom(factor, m_StartCoordinateInMM);
446  RenderingManager::GetInstance()->RequestUpdate(sender->GetRenderWindow());
447  }
448 }
449 
451 {
452  auto* positionEvent = static_cast<InteractionPositionEvent *>(interactionEvent);
453 
454  mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController();
455  if (sliceNaviController)
456  {
457  int delta = 0;
458  // Scrolling direction
459  if (m_ScrollDirection == "updown")
460  {
461  delta = static_cast<int>(m_LastDisplayCoordinate[1] - positionEvent->GetPointerPositionOnScreen()[1]);
462  }
463  else
464  {
465  delta = static_cast<int>(m_LastDisplayCoordinate[0] - positionEvent->GetPointerPositionOnScreen()[0]);
466  }
467 
468  if (m_InvertScrollDirection)
469  {
470  delta *= -1;
471  }
472 
473  // Set how many pixels the mouse has to be moved to scroll one slice
474  // if we moved less than 'm_IndexToSliceModifier' pixels slice ONE slice only
475  if (delta > 0 && delta < m_IndexToSliceModifier)
476  {
477  delta = m_IndexToSliceModifier;
478  }
479  else if (delta < 0 && delta > -m_IndexToSliceModifier)
480  {
481  delta = -m_IndexToSliceModifier;
482  }
483  delta /= m_IndexToSliceModifier;
484 
485  int newPos = sliceNaviController->GetSlice()->GetPos() + delta;
486 
487  // if auto repeat is on, start at first slice if you reach the last slice and vice versa
488  int maxSlices = sliceNaviController->GetSlice()->GetSteps();
489  if (m_AutoRepeat)
490  {
491  while (newPos < 0)
492  {
493  newPos += maxSlices;
494  }
495 
496  while (newPos >= maxSlices)
497  {
498  newPos -= maxSlices;
499  }
500  }
501  else
502  {
503  // if the new slice is below 0 we still show slice 0
504  // due to the stepper using unsigned int we have to do this ourselves
505  if (newPos < 1)
506  {
507  newPos = 0;
508  }
509  }
510 
511  m_LastDisplayCoordinate = m_CurrentDisplayCoordinate;
512  m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
513 
514  // set the new position
515  sliceNaviController->GetSlice()->SetPos(newPos);
516  }
517 }
518 
520 {
521  mitk::SliceNavigationController::Pointer sliceNaviController =
522  interactionEvent->GetSender()->GetSliceNavigationController();
523  if (!sliceNaviController->GetSliceLocked())
524  {
525  mitk::Stepper *stepper = sliceNaviController->GetSlice();
526  if (stepper->GetSteps() <= 1)
527  {
528  stepper = sliceNaviController->GetTime();
529  }
530  stepper->Next();
531  }
532 }
533 
535 {
536  mitk::SliceNavigationController::Pointer sliceNaviController =
537  interactionEvent->GetSender()->GetSliceNavigationController();
538  if (!sliceNaviController->GetSliceLocked())
539  {
540  mitk::Stepper *stepper = sliceNaviController->GetSlice();
541  if (stepper->GetSteps() <= 1)
542  {
543  stepper = sliceNaviController->GetTime();
544  }
545  stepper->Previous();
546  }
547 }
548 
550 {
551  BaseRenderer::Pointer sender = interactionEvent->GetSender();
552  auto *positionEvent = static_cast<InteractionPositionEvent *>(interactionEvent);
553 
554  m_LastDisplayCoordinate = m_CurrentDisplayCoordinate;
555  m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
556  // search for active image
557  mitk::DataStorage::Pointer storage = sender->GetDataStorage();
558  mitk::DataNode::Pointer node = nullptr;
559  mitk::DataStorage::SetOfObjects::ConstPointer allImageNodes = storage->GetSubset(mitk::NodePredicateDataType::New("Image"));
560  for (unsigned int i = 0; i < allImageNodes->size(); ++i)
561  {
562  bool isActiveImage = false;
563  bool propFound = allImageNodes->at(i)->GetBoolProperty("imageForLevelWindow", isActiveImage);
564 
565  if (propFound && isActiveImage)
566  {
567  node = allImageNodes->at(i);
568  continue;
569  }
570  }
571  if (node.IsNull())
572  {
573  node = storage->GetNode(mitk::NodePredicateDataType::New("Image"));
574  }
575  if (node.IsNull())
576  {
577  return;
578  }
579 
581  node->GetLevelWindow(lv);
582  ScalarType level = lv.GetLevel();
583  ScalarType window = lv.GetWindow();
584 
585  int levelIndex = 0;
586  int windowIndex = 1;
587 
588  if (m_LevelDirection != "leftright")
589  {
590  levelIndex = 1;
591  windowIndex = 0;
592  }
593 
594  int directionModifier = 1;
595  if (m_InvertLevelWindowDirection)
596  {
597  directionModifier = -1;
598  }
599 
600  // calculate adjustments from mouse movements
601  level += (m_CurrentDisplayCoordinate[levelIndex] - m_LastDisplayCoordinate[levelIndex]) * static_cast<ScalarType>(2) *
602  directionModifier;
603  window += (m_CurrentDisplayCoordinate[windowIndex] - m_LastDisplayCoordinate[windowIndex]) *
604  static_cast<ScalarType>(2) * directionModifier;
605 
606  lv.SetLevelWindow(level, window);
607  dynamic_cast<mitk::LevelWindowProperty *>(node->GetProperty("levelwindow"))->SetLevelWindow(lv);
608 
610 }
611 
613 {
614  this->SetMouseCursor(rotate_cursor_xpm, 0, 0);
615 }
616 
618 {
619  this->ResetMouseCursor();
620 }
621 
623 {
624  const auto *posEvent = dynamic_cast<const InteractionPositionEvent *>(event);
625  if (posEvent == nullptr)
626  return;
627 
628  Point3D cursor = posEvent->GetPositionInWorld();
629 
630  Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation;
631  Vector3D toCursor = cursor - m_CenterOfRotation;
632 
633  // cross product: | A x B | = |A| * |B| * sin(angle)
634  Vector3D axisOfRotation;
635  vnl_vector_fixed<ScalarType, 3> vnlDirection = vnl_cross_3d(toCursor.GetVnlVector(), toProjected.GetVnlVector());
636  axisOfRotation.SetVnlVector(vnlDirection);
637 
638  // scalar product: A * B = |A| * |B| * cos(angle)
639  // tan = sin / cos
640  ScalarType angle = -atan2((double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected));
641  angle *= 180.0 / vnl_math::pi;
642  m_LastCursorPosition = cursor;
643 
644  // create RotationOperation and apply to all SNCs that should be rotated
645  RotationOperation rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle);
646 
647  // iterate the OTHER slice navigation controllers: these are filled in DoDecideBetweenRotationAndSliceSelection
648  for (auto iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
649  {
650  TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry();
651  if (!timeGeometry)
652  continue;
653 
654  timeGeometry->ExecuteOperation(&rotationOperation);
655 
656  (*iter)->SendCreatedWorldGeometryUpdate();
657  }
658 
660 }
661 
663 {
664  const auto *posEvent = dynamic_cast<const InteractionPositionEvent *>(event);
665 
666  if (!posEvent)
667  return;
668 
669  // Determine relative mouse movement projected onto world space
670  Point2D cursor = posEvent->GetPointerPositionOnScreen();
671  Vector2D relativeCursor = cursor - m_ReferenceCursor;
672  Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1];
673 
674  // Determine rotation axis (perpendicular to rotation plane and cursor
675  // movement)
676  Vector3D rotationAxis = itk::CrossProduct(m_RotationPlaneNormal, relativeCursorAxis);
677 
678  ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0;
679 
680  // Restore the initial plane pose by undoing the previous rotation
681  // operation
682  RotationOperation op(OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle);
683 
684  SNCVector::iterator iter;
685  for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
686  {
687  if (!(*iter)->GetSliceRotationLocked())
688  {
689  TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry();
690  if (!timeGeometry)
691  continue;
692 
693  timeGeometry->ExecuteOperation(&op);
694  (*iter)->SendCreatedWorldGeometryUpdate();
695  }
696  }
697 
698  // Apply new rotation operation to all relevant SNCs
699  RotationOperation op2(OpROTATE, m_CenterOfRotation, rotationAxis, rotationAngle);
700 
701  for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
702  {
703  if (!(*iter)->GetSliceRotationLocked())
704  {
705  // Retrieve the TimeGeometry of this SliceNavigationController
706  TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry();
707  if (!timeGeometry)
708  continue;
709 
710  // Execute the new rotation
711  timeGeometry->ExecuteOperation(&op2);
712 
713  // Notify listeners
714  (*iter)->SendCreatedWorldGeometryUpdate();
715  }
716  }
717 
718  m_PreviousRotationAxis = rotationAxis;
719  m_PreviousRotationAngle = rotationAngle;
720 
722  return;
723 }
724 
726 {
727  const auto* posEvent = dynamic_cast<const InteractionPositionEvent *>(event);
728  if (nullptr == posEvent)
729  {
730  return;
731  }
732 
733  const mitk::BaseRenderer::Pointer baseRenderer = posEvent->GetSender();
735  auto globalCurrentTimePoint = baseRenderer->GetTime();
736  mitk::DataStorage::SetOfObjects::ConstPointer nodes = baseRenderer->GetDataStorage()->GetSubset(isImageData).GetPointer();
737  if (nodes.IsNull())
738  {
739  return;
740  }
741 
742  // posEvent->GetPositionInWorld() would return the world position at the
743  // time of initiating the interaction. However, we need to update the
744  // status bar with the position after changing slice. Therefore, we
745  // translate the same display position with the renderer again to
746  // get the new world position.
747  Point3D worldposition;
748  baseRenderer->DisplayToWorld(posEvent->GetPointerPositionOnScreen(), worldposition);
749 
750  mitk::Image::Pointer image3D;
752  mitk::DataNode::Pointer topSourceNode;
753 
754  int component = 0;
755 
756  node = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, baseRenderer);
757  if (node.IsNull())
758  {
759  return;
760  }
761 
762  bool isBinary(false);
763  node->GetBoolProperty("binary", isBinary);
764  if (isBinary)
765  {
766  mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = baseRenderer->GetDataStorage()->GetSources(node, nullptr, true);
767  if (!sourcenodes->empty())
768  {
769  topSourceNode = mitk::FindTopmostVisibleNode(sourcenodes, worldposition, globalCurrentTimePoint, baseRenderer);
770  }
771  if (topSourceNode.IsNotNull())
772  {
773  image3D = dynamic_cast<mitk::Image *>(topSourceNode->GetData());
774  topSourceNode->GetIntProperty("Image.Displayed Component", component);
775  }
776  else
777  {
778  image3D = dynamic_cast<mitk::Image *>(node->GetData());
779  node->GetIntProperty("Image.Displayed Component", component);
780  }
781  }
782  else
783  {
784  image3D = dynamic_cast<mitk::Image *>(node->GetData());
785  node->GetIntProperty("Image.Displayed Component", component);
786  }
787 
788  // get the position and gray value from the image and build up status bar text
789  auto statusBar = StatusBar::GetInstance();
790  if (image3D.IsNotNull() && statusBar != nullptr)
791  {
792  itk::Index<3> p;
793  image3D->GetGeometry()->WorldToIndex(worldposition, p);
794 
795  auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType();
796  if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA)
797  {
798  std::string pixelValue = "Pixel RGB(A) value: ";
799  pixelValue.append(ConvertCompositePixelValueToString(image3D, p));
800  statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue.c_str());
801  }
802  else if (pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR)
803  {
804  std::string pixelValue = "See ODF Details view. ";
805  statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue.c_str());
806  }
807  else
808  {
809  mitk::ScalarType pixelValue;
811  image3D->GetChannelDescriptor().GetPixelType(),
812  image3D,
813  image3D->GetVolumeData(image3D->GetTimeGeometry()->TimePointToTimeStep(globalCurrentTimePoint)),
814  p,
815  pixelValue,
816  component);
817  statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue);
818  }
819  }
820  else
821  {
822  statusBar->DisplayImageInfoInvalid();
823  }
824 }
825 
827 {
829  // auto repeat
830  std::string strAutoRepeat = "";
831  if (properties->GetStringProperty("autoRepeat", strAutoRepeat))
832  {
833  if (strAutoRepeat == "true")
834  {
835  m_AutoRepeat = true;
836  }
837  else
838  {
839  m_AutoRepeat = false;
840  }
841  }
842  // pixel movement for scrolling one slice
843  std::string strPixelPerSlice = "";
844  if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice))
845  {
846  m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str());
847  }
848  else
849  {
850  m_IndexToSliceModifier = 4;
851  }
852  // scroll direction
853  if (!properties->GetStringProperty("scrollDirection", m_ScrollDirection))
854  {
855  m_ScrollDirection = "updown";
856  }
857 
858  m_InvertScrollDirection = GetBoolProperty(properties, "invertScrollDirection", false);
859 
860  // zoom direction
861  if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection))
862  {
863  m_ZoomDirection = "updown";
864  }
865 
866  m_InvertZoomDirection = GetBoolProperty(properties, "invertZoomDirection", false);
867 
868  m_InvertMoveDirection = GetBoolProperty(properties, "invertMoveDirection", false);
869 
870  if (!properties->GetStringProperty("levelWindowDirection", m_LevelDirection))
871  {
872  m_LevelDirection = "leftright";
873  }
874 
875  m_InvertLevelWindowDirection = GetBoolProperty(properties, "invertLevelWindowDirection", false);
876 
877  // coupled rotation
878  std::string strCoupled = "";
879  if (properties->GetStringProperty("coupled", strCoupled))
880  {
881  if (strCoupled == "true")
882  m_LinkPlanes = true;
883  else
884  m_LinkPlanes = false;
885  }
886 
887  // zoom factor
888  std::string strZoomFactor = "";
889  properties->GetStringProperty("zoomFactor", strZoomFactor);
890  m_ZoomFactor = .05;
891  if (atoi(strZoomFactor.c_str()) > 0)
892  {
893  m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0);
894  }
895  // allwaysReact
896  std::string strAlwaysReact = "";
897  if (properties->GetStringProperty("alwaysReact", strAlwaysReact))
898  {
899  if (strAlwaysReact == "true")
900  {
901  m_AlwaysReact = true;
902  }
903  else
904  {
905  m_AlwaysReact = false;
906  }
907  }
908  else
909  {
910  m_AlwaysReact = false;
911  }
912 }
913 
914 bool mitk::DisplayInteractor::FilterEvents(InteractionEvent *interactionEvent, DataNode * /*dataNode*/)
915 {
916  if (interactionEvent->GetSender() == nullptr)
917  return false;
918  if (interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard3D)
919  return false;
920 
921  return true;
922 }
923 
925  const char *propertyName,
926  bool defaultValue)
927 {
928  std::string valueAsString;
929  if (!propertyList->GetStringProperty(propertyName, valueAsString))
930  {
931  return defaultValue;
932  }
933  else
934  {
935  if (valueAsString == "true")
936  {
937  return true;
938  }
939  else
940  {
941  return false;
942  }
943  }
944 }
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
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