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