Medical Imaging Interaction Toolkit  2018.4.99-064ad45c
Medical Imaging Interaction Toolkit
mitkDisplayActionEventBroadcast.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 
14 
15  // us
16 #include "usGetModuleContext.h"
17 #include "usModuleContext.h"
18 
19 // mitk core module
22 #include <mitkImage.h>
24 #include <mitkInteractionConst.h>
26 #include <mitkLine.h>
28 #include <mitkPixelTypeMultiplex.h>
29 #include <mitkRotationOperation.h>
30 #include <mitkStatusBar.h>
31 
32 #include <rotate_cursor.xpm>
33 
35  : m_AlwaysReact(false)
36  , m_AutoRepeat(false)
37  , m_IndexToSliceModifier(4)
38  , m_InvertScrollDirection(false)
39  , m_InvertZoomDirection(false)
40  , m_ZoomFactor(2)
41  , m_InvertMoveDirection(false)
42  , m_InvertLevelWindowDirection(false)
43  , m_LinkPlanes(true)
44 {
45  m_StartCoordinateInMM.Fill(0);
46  m_LastDisplayCoordinate.Fill(0);
47  m_LastCoordinateInMM.Fill(0);
48  m_CurrentDisplayCoordinate.Fill(0);
49 
50  // register the broadcast class (itself) as an interaction event observer via micro services
52  props["name"] = std::string("DisplayActionEventBroadcast");
53  m_ServiceRegistration = us::GetModuleContext()->RegisterService<InteractionEventObserver>(this, props);
54 }
55 
57 {
58  m_ServiceRegistration.Unregister();
59 }
60 
61 void mitk::DisplayActionEventBroadcast::Notify(InteractionEvent* interactionEvent, bool isHandled)
62 {
63  // the event is passed to the state machine interface to be handled
64  if (!isHandled || m_AlwaysReact)
65  {
66  HandleEvent(interactionEvent, nullptr);
67  }
68 }
69 
71 {
72  CONNECT_CONDITION("check_position_event", CheckPositionEvent);
73  CONNECT_CONDITION("check_can_rotate", CheckRotationPossible);
74  CONNECT_CONDITION("check_can_swivel", CheckSwivelPossible);
75 
76  CONNECT_FUNCTION("init", Init);
77  CONNECT_FUNCTION("move", Move);
78  CONNECT_FUNCTION("zoom", Zoom);
79  CONNECT_FUNCTION("scroll", Scroll);
80  CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp);
81  CONNECT_FUNCTION("ScrollOneDown", ScrollOneDown);
82  CONNECT_FUNCTION("levelWindow", AdjustLevelWindow);
83  CONNECT_FUNCTION("setCrosshair", SetCrosshair);
84 
85  CONNECT_FUNCTION("updateStatusbar", UpdateStatusbar)
86 
87  CONNECT_FUNCTION("startRotation", StartRotation);
88  CONNECT_FUNCTION("endRotation", EndRotation);
89  CONNECT_FUNCTION("rotate", Rotate);
90 
91  CONNECT_FUNCTION("swivel", Swivel);
92 
93  CONNECT_FUNCTION("IncreaseTimeStep", IncreaseTimeStep);
94  CONNECT_FUNCTION("DecreaseTimeStep", DecreaseTimeStep);
95 }
96 
98 {
99  PropertyList::Pointer properties = GetAttributes();
100 
101  // allwaysReact
102  std::string strAlwaysReact = "";
103  m_AlwaysReact = false;
104  if (properties->GetStringProperty("alwaysReact", strAlwaysReact))
105  {
106  if (strAlwaysReact == "true")
107  {
108  m_AlwaysReact = true;
109  }
110  }
111 
112  // auto repeat
113  std::string strAutoRepeat = "";
114  m_AutoRepeat = false;
115  if (properties->GetStringProperty("autoRepeat", strAutoRepeat))
116  {
117  if (strAutoRepeat == "true")
118  {
119  m_AutoRepeat = true;
120  }
121  }
122 
123  // pixel movement for scrolling one slice
124  std::string strPixelPerSlice = "";
125  m_IndexToSliceModifier = 4;
126  if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice))
127  {
128  m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str());
129  }
130 
131  // scroll direction
132  if (!properties->GetStringProperty("scrollDirection", m_ScrollDirection))
133  {
134  m_ScrollDirection = "updown";
135  }
136 
137  m_InvertScrollDirection = GetBoolProperty(properties, "invertScrollDirection", false);
138 
139  // zoom direction
140  if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection))
141  {
142  m_ZoomDirection = "updown";
143  }
144 
145  m_InvertZoomDirection = GetBoolProperty(properties, "invertZoomDirection", false);
146  m_InvertMoveDirection = GetBoolProperty(properties, "invertMoveDirection", false);
147 
148  if (!properties->GetStringProperty("levelWindowDirection", m_LevelDirection))
149  {
150  m_LevelDirection = "leftright";
151  }
152 
153  m_InvertLevelWindowDirection = GetBoolProperty(properties, "invertLevelWindowDirection", false);
154 
155  // coupled rotation
156  std::string strCoupled = "";
157  m_LinkPlanes = false;
158  if (properties->GetStringProperty("coupled", strCoupled))
159  {
160  if (strCoupled == "true")
161  {
162  m_LinkPlanes = true;
163  }
164  }
165 
166  // zoom factor
167  std::string strZoomFactor = "";
168  properties->GetStringProperty("zoomFactor", strZoomFactor);
169  m_ZoomFactor = .05;
170  if (atoi(strZoomFactor.c_str()) > 0)
171  {
172  m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0);
173  }
174 }
175 
177 {
178  BaseRenderer* sendingRenderer = interactionEvent->GetSender();
179  if (nullptr == sendingRenderer)
180  {
181  return false;
182  }
183 
184  if (BaseRenderer::Standard3D == sendingRenderer->GetMapperID())
185  {
186  return false;
187  }
188 
189  return true;
190 }
191 
193 {
194  const auto* positionEvent = dynamic_cast<const InteractionPositionEvent*>(interactionEvent);
195  if (nullptr == positionEvent)
196  {
197  return false;
198  }
199 
200  return true;
201 }
202 
204 {
205  // Decide between moving and rotation slices.
206  /*
207  Detailed logic:
208 
209  1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be
210  rotated. Needs not even be counted or checked.
211  2. Inspect every other SliceNavigationController
212  - calculate the line intersection of this SliceNavigationController's plane with our rendering plane
213  - if there is NO intersection, ignore and continue
214  - IF there is an intersection
215  - check the mouse cursor's distance from that line.
216  0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in
217  "locked" mode)
218  1. on first line near the cursor, just remember this intersection line as THE other plane that we want to rotate
219  2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to
220  rotate
221  - if yes, we just push this line to the "other" lines and rotate it along
222  - if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to
223  rotate
224  */
225  const auto* positionEvent = dynamic_cast<const InteractionPositionEvent*>(interactionEvent);
226  if (nullptr == positionEvent)
227  {
228  return false;
229  }
230 
231  BaseRenderer* renderer = positionEvent->GetSender();
232  if (nullptr == renderer)
233  {
234  return false;
235  }
236 
237  const PlaneGeometry* rendererWorldPlaneGeometry = renderer->GetCurrentWorldPlaneGeometry();
238  if (nullptr == rendererWorldPlaneGeometry)
239  {
240  return false;
241  }
242 
243  Point3D position = positionEvent->GetPositionInWorld();
244  const auto spacing = rendererWorldPlaneGeometry->GetSpacing();
245  const PlaneGeometry *geometryToBeRotated = nullptr; // this one is under the mouse cursor
246  const PlaneGeometry *anyOtherGeometry = nullptr; // this is also visible (for calculation of intersection ONLY)
247  Line3D intersectionLineWithGeometryToBeRotated;
248 
249  bool hitMultipleLines(false);
250  m_SNCsToBeRotated.clear();
251 
252  const ScalarType threshholdDistancePixels = 12.0;
253 
255  for (auto renderWindow : allRenderWindows)
256  {
258 
259  // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes.
260  if (BaseRenderer::Standard3D == BaseRenderer::GetInstance(renderWindow)->GetMapperID())
261  {
262  continue;
263  }
264 
265  const PlaneGeometry* rendererPlaneGeometry = snc->GetCurrentPlaneGeometry();
266  if (nullptr == rendererPlaneGeometry)
267  {
268  continue; // ignore, we don't see a plane
269  }
270 
271  // check if there is an intersection between rendered / clicked geometry and the one being analyzed
272  Line3D intersectionLine;
273  if (!rendererWorldPlaneGeometry->IntersectionLine(rendererPlaneGeometry, intersectionLine))
274  {
275  continue; // we ignore this plane, it's parallel to our plane
276  }
277 
278  // check distance from intersection line
279  const double distanceFromIntersectionLine = intersectionLine.Distance(position) / spacing[snc->GetDefaultViewDirection()];
280 
281  // far away line, only remember for linked rotation if necessary
282  if (distanceFromIntersectionLine > threshholdDistancePixels)
283  {
284  // we just take the last one, so overwrite each iteration (we just need some crossing point)
285  // TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used
286  anyOtherGeometry = rendererPlaneGeometry;
287  if (m_LinkPlanes)
288  {
289  // if planes are linked, apply rotation to all planes
290  m_SNCsToBeRotated.push_back(snc);
291  }
292  }
293  else // close to cursor
294  {
295  if (nullptr == geometryToBeRotated) // first one close to the cursor
296  {
297  geometryToBeRotated = rendererPlaneGeometry;
298  intersectionLineWithGeometryToBeRotated = intersectionLine;
299  m_SNCsToBeRotated.push_back(snc);
300  }
301  else
302  {
303  // compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane
304  // together with the primary one
305  // if different, DON'T rotate
306  if (intersectionLine.IsParallel(intersectionLineWithGeometryToBeRotated)
307  && intersectionLine.Distance(intersectionLineWithGeometryToBeRotated.GetPoint1()) < eps)
308  {
309  m_SNCsToBeRotated.push_back(snc);
310  }
311  else
312  {
313  hitMultipleLines = true;
314  }
315  }
316  }
317  }
318 
319  bool moveSlices(true);
320 
321  if (geometryToBeRotated && anyOtherGeometry && rendererWorldPlaneGeometry && !hitMultipleLines)
322  {
323  // assure all three are valid, so calculation of center of rotation can be done
324  moveSlices = false;
325  }
326  // question in state machine is: "rotate?"
327  if (moveSlices) // i.e. NOT rotate
328  {
329  return false;
330  }
331  else
332  {
333  // we have enough information for rotation
334  // remember where the last cursor position ON THE LINE has been observed
335  m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project(position);
336 
337  // find center of rotation by intersection with any of the OTHER lines
338  if (anyOtherGeometry->IntersectionPoint(intersectionLineWithGeometryToBeRotated, m_CenterOfRotation))
339  {
340  return true;
341  }
342  else
343  {
344  return false;
345  }
346  }
347  return false;
348 }
349 
351 {
352  // Decide between moving and rotation: if we're close to the crossing
353  // point of the planes, moving mode is entered, otherwise
354  // rotation/swivel mode
355  const auto* positionEvent = dynamic_cast<const InteractionPositionEvent*>(interactionEvent);
356  if (nullptr == positionEvent)
357  {
358  return false;
359  }
360 
361  BaseRenderer* renderer = positionEvent->GetSender();
362  if (nullptr == renderer)
363  {
364  return false;
365  }
366 
367  const Point3D& position = positionEvent->GetPositionInWorld();
368 
369  m_SNCsToBeRotated.clear();
370 
371  const PlaneGeometry* clickedGeometry(nullptr);
372  const PlaneGeometry* otherGeometry1(nullptr);
373  const PlaneGeometry* otherGeometry2(nullptr);
374 
375  const ScalarType threshholdDistancePixels = 6.0;
376 
378  for (auto renderWindow : allRenderWindows)
379  {
381 
382  // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes.
383  if (BaseRenderer::Standard3D == BaseRenderer::GetInstance(renderWindow)->GetMapperID())
384  {
385  continue;
386  }
387 
388  const PlaneGeometry* rendererPlaneGeometry = snc->GetCurrentPlaneGeometry();
389  if (nullptr == rendererPlaneGeometry)
390  {
391  continue; // ignore, we don't see a plane
392  }
393 
394  if (snc == renderer->GetSliceNavigationController())
395  {
396  clickedGeometry = rendererPlaneGeometry;
397  m_SNCsToBeRotated.push_back(snc);
398  }
399  else
400  {
401  if (nullptr == otherGeometry1)
402  {
403  otherGeometry1 = rendererPlaneGeometry;
404  }
405  else
406  {
407  otherGeometry2 = rendererPlaneGeometry;
408  }
409  if (m_LinkPlanes)
410  {
411  // if planes are linked, apply rotation to all planes
412  m_SNCsToBeRotated.push_back(snc);
413  }
414  }
415  }
416 
417  Line3D line;
418  Point3D point;
419  if ((nullptr != clickedGeometry) && (nullptr != otherGeometry1) && (nullptr != otherGeometry2)
420  && clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point))
421  {
422  m_CenterOfRotation = point;
423  if (m_CenterOfRotation.EuclideanDistanceTo(position) < threshholdDistancePixels)
424  {
425  return false;
426  }
427  else
428  {
429  m_ReferenceCursor = positionEvent->GetPointerPositionOnScreen();
430 
431  // Get main axes of rotation plane and store it for rotation step
432  m_RotationPlaneNormal = clickedGeometry->GetNormal();
433 
434  ScalarType xVector[] = { 1.0, 0.0, 0.0 };
435  ScalarType yVector[] = { 0.0, 1.0, 0.0 };
436  clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(xVector), m_RotationPlaneXVector);
437  clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(yVector), m_RotationPlaneYVector);
438 
439  m_RotationPlaneNormal.Normalize();
440  m_RotationPlaneXVector.Normalize();
441  m_RotationPlaneYVector.Normalize();
442 
443  m_PreviousRotationAxis.Fill(0.0);
444  m_PreviousRotationAxis[2] = 1.0;
445  m_PreviousRotationAngle = 0.0;
446 
447  return true;
448  }
449  }
450  else
451  {
452  return false;
453  }
454  return false;
455 }
456 
457 void mitk::DisplayActionEventBroadcast::Init(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
458 {
459  const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
460  if (nullptr == positionEvent)
461  {
462  return;
463  }
464 
465  m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
466  m_CurrentDisplayCoordinate = m_LastDisplayCoordinate;
467  positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate, m_StartCoordinateInMM);
468  m_LastCoordinateInMM = m_StartCoordinateInMM;
469 }
470 
471 void mitk::DisplayActionEventBroadcast::Move(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
472 {
473  const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
474  if (nullptr == positionEvent)
475  {
476  return;
477  }
478 
479  BaseRenderer* sender = interactionEvent->GetSender();
480  Vector2D moveVector = m_LastDisplayCoordinate - positionEvent->GetPointerPositionOnScreen();
481 
482  if (m_InvertMoveDirection)
483  {
484  moveVector *= -1.0;
485  }
486 
487  moveVector *= sender->GetScaleFactorMMPerDisplayUnit(); // #TODO: put here?
488 
489  // store new display coordinate
490  m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
491 
492  // propagate move event with computed geometry values
493  InvokeEvent(DisplayMoveEvent(interactionEvent, moveVector));
494 }
495 
497 {
498  const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
499  if (nullptr == positionEvent)
500  {
501  return;
502  }
503 
504  Point3D position = positionEvent->GetPositionInWorld();
505 
506  // propagate set crosshair event with computed geometry values
507  InvokeEvent(DisplaySetCrosshairEvent(interactionEvent, position));
508 }
509 
510 void mitk::DisplayActionEventBroadcast::Zoom(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
511 {
512  const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
513  if (nullptr == positionEvent)
514  {
515  return;
516  }
517 
518  float factor = 1.0;
519  float distance = 0;
520 
521  if (m_ZoomDirection == "updown")
522  {
523  distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1];
524  }
525  else
526  {
527  distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0];
528  }
529 
530  if (m_InvertZoomDirection)
531  {
532  distance *= -1.0;
533  }
534 
535  // set zooming speed
536  if (distance < 0.0)
537  {
538  factor = 1.0 / m_ZoomFactor;
539  }
540  else if (distance > 0.0)
541  {
542  factor = 1.0 * m_ZoomFactor;
543  }
544 
545  // store new display coordinates
546  m_LastDisplayCoordinate = m_CurrentDisplayCoordinate;
547  m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
548 
549  // propagate zoom event with computed geometry values
550  InvokeEvent(DisplayZoomEvent(interactionEvent, factor, m_StartCoordinateInMM));
551 }
552 
553 void mitk::DisplayActionEventBroadcast::Scroll(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
554 {
555  const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
556  if (nullptr == positionEvent)
557  {
558  return;
559  }
560 
561  int sliceDelta = 0;
562 
563  // scroll direction
564  if (m_ScrollDirection == "updown")
565  {
566  sliceDelta = static_cast<int>(m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]);
567  }
568  else
569  {
570  sliceDelta = static_cast<int>(m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]);
571  }
572 
573  if (m_InvertScrollDirection)
574  {
575  sliceDelta *= -1;
576  }
577 
578  // set how many pixels the mouse has to be moved to scroll one slice
579  // if the mouse has been moved less than 'm_IndexToSliceModifier', pixels slice ONE slice only
580  if (sliceDelta > 0 && sliceDelta < m_IndexToSliceModifier)
581  {
582  sliceDelta = m_IndexToSliceModifier;
583  }
584  else if (sliceDelta < 0 && sliceDelta > -m_IndexToSliceModifier)
585  {
586  sliceDelta = -m_IndexToSliceModifier;
587  }
588  sliceDelta /= m_IndexToSliceModifier;
589 
590  // store new display coordinates
591  m_LastDisplayCoordinate = m_CurrentDisplayCoordinate;
592  m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
593 
594  // propagate scroll event with computed geometry values
595  InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta));
596 }
597 
599 {
600  int sliceDelta = 1;
601  if (m_InvertScrollDirection)
602  {
603  sliceDelta = -1;
604  }
605 
606  // propagate scroll event with a single slice delta (increase)
607  InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta));
608 }
609 
611 {
612  int sliceDelta = -1;
613  if (m_InvertScrollDirection)
614  {
615  sliceDelta = 1;
616  }
617 
618  // propagate scroll event with a single slice delta (decrease)
619  InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta));
620 }
621 
623 {
624  const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
625  if (nullptr == positionEvent)
626  {
627  return;
628  }
629 
630  ScalarType level;
631  ScalarType window;
632 
633  if (m_LevelDirection == "leftright")
634  {
635  level = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0];
636  window = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1];
637  }
638  else
639  {
640  level = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1];
641  window = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0];
642  }
643 
644  if (m_InvertLevelWindowDirection)
645  {
646  level *= -1;
647  window *= -1;
648  }
649 
650  level *= static_cast<ScalarType>(2);
651  window *= static_cast<ScalarType>(2);
652 
653  // store new display coordinates
654  m_LastDisplayCoordinate = m_CurrentDisplayCoordinate;
655  m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
656 
657  // propagate set level window event with the level and window delta
658  InvokeEvent(DisplaySetLevelWindowEvent(interactionEvent, level, window));
659 }
660 
662 {
663  SetMouseCursor(rotate_cursor_xpm, 0, 0);
664 }
665 
666 void mitk::DisplayActionEventBroadcast::EndRotation(StateMachineAction* /*stateMachineAction*/, InteractionEvent* /*interactionEvent*/)
667 {
669 }
670 
671 void mitk::DisplayActionEventBroadcast::Rotate(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
672 {
673  const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
674  if (nullptr == positionEvent)
675  {
676  return;
677  }
678 
679  Point3D position = positionEvent->GetPositionInWorld();
680 
681  Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation;
682  Vector3D toCursor = position - m_CenterOfRotation;
683 
684  // cross product: | A x B | = |A| * |B| * sin(angle)
685  Vector3D axisOfRotation;
686  vnl_vector_fixed<ScalarType, 3> vnlDirection = vnl_cross_3d(toCursor.GetVnlVector(), toProjected.GetVnlVector());
687  axisOfRotation.SetVnlVector(vnlDirection);
688 
689  // scalar product: A * B = |A| * |B| * cos(angle)
690  // tan = sin / cos
691  ScalarType angle = -atan2((double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected));
692  angle *= 180.0 / vnl_math::pi;
693  m_LastCursorPosition = position;
694 
695  // create RotationOperation and apply to all SNCs that should be rotated
696  RotationOperation rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle);
697 
698  // iterate the OTHER slice navigation controllers
699  for (auto iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
700  {
701  TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry();
702  if (nullptr == timeGeometry)
703  {
704  continue;
705  }
706 
707  timeGeometry->ExecuteOperation(&rotationOperation);
708 
709  (*iter)->SendCreatedWorldGeometryUpdate();
710  }
711 
713 }
714 
715 void mitk::DisplayActionEventBroadcast::Swivel(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
716 {
717  const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
718  if (nullptr == positionEvent)
719  {
720  return;
721  }
722 
723  // Determine relative mouse movement projected onto world space
724  Point2D position = positionEvent->GetPointerPositionOnScreen();
725 
726  Vector2D relativeCursor = position - m_ReferenceCursor;
727  Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1];
728 
729  // Determine rotation axis (perpendicular to rotation plane and cursor movement)
730  Vector3D rotationAxis = itk::CrossProduct(m_RotationPlaneNormal, relativeCursorAxis);
731 
732  ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0;
733 
734  // Restore the initial plane pose by undoing the previous rotation operation
735  RotationOperation op(OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle);
736 
737  SNCVector::iterator iter;
738  for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
739  {
740  if (!(*iter)->GetSliceRotationLocked())
741  {
742  TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry();
743  if (nullptr == timeGeometry)
744  {
745  continue;
746  }
747 
748  timeGeometry->ExecuteOperation(&op);
749  (*iter)->SendCreatedWorldGeometryUpdate();
750  }
751  }
752 
753  // Apply new rotation operation to all relevant SNCs
754  RotationOperation op2(OpROTATE, m_CenterOfRotation, rotationAxis, rotationAngle);
755 
756  for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
757  {
758  if (!(*iter)->GetSliceRotationLocked())
759  {
760  // Retrieve the TimeGeometry of this SliceNavigationController
761  TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry();
762  if (nullptr == timeGeometry)
763  {
764  continue;
765  }
766 
767  // Execute the new rotation
768  timeGeometry->ExecuteOperation(&op2);
769 
770  // Notify listeners
771  (*iter)->SendCreatedWorldGeometryUpdate();
772  }
773  }
774 
775  m_PreviousRotationAxis = rotationAxis;
776  m_PreviousRotationAngle = rotationAngle;
777 
779  return;
780 }
781 
783 {
784  auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController();
785  auto stepper = sliceNaviController->GetTime();
786  stepper->SetAutoRepeat(true);
787  stepper->Next();
788 }
789 
791 {
792  auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController();
793  auto stepper = sliceNaviController->GetTime();
794  stepper->SetAutoRepeat(true);
795  stepper->Previous();
796 }
797 
798 void mitk::DisplayActionEventBroadcast::UpdateStatusbar(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
799 {
800  const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
801  if (nullptr == positionEvent)
802  {
803  return;
804  }
805 
806  BaseRenderer::Pointer renderer = positionEvent->GetSender();
807 
809  DataStorage::SetOfObjects::ConstPointer nodes = renderer->GetDataStorage()->GetSubset(isImageData).GetPointer();
810  if (nodes.IsNull())
811  {
812  return;
813  }
814 
815  Point3D worldposition;
816  renderer->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), worldposition);
817  auto globalCurrentTimePoint = renderer->GetTime();
818 
819  Image::Pointer image3D;
820  DataNode::Pointer node;
821  DataNode::Pointer topSourceNode;
822 
823  int component = 0;
824 
825  node = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, renderer);
826  if (node.IsNull())
827  {
828  return;
829  }
830 
831  bool isBinary(false);
832  node->GetBoolProperty("binary", isBinary);
833  if (isBinary)
834  {
835  DataStorage::SetOfObjects::ConstPointer sourcenodes = renderer->GetDataStorage()->GetSources(node, nullptr, true);
836  if (!sourcenodes->empty())
837  {
838  topSourceNode = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, renderer);
839  }
840  if (topSourceNode.IsNotNull())
841  {
842  image3D = dynamic_cast<Image*>(topSourceNode->GetData());
843  topSourceNode->GetIntProperty("Image.Displayed Component", component);
844  }
845  else
846  {
847  image3D = dynamic_cast<Image*>(node->GetData());
848  node->GetIntProperty("Image.Displayed Component", component);
849  }
850  }
851  else
852  {
853  image3D = dynamic_cast<Image *>(node->GetData());
854  node->GetIntProperty("Image.Displayed Component", component);
855  }
856 
857  // get the position and pixel value from the image and build up status bar text
858  auto statusBar = StatusBar::GetInstance();
859  if (image3D.IsNotNull() && statusBar != nullptr)
860  {
861  itk::Index<3> p;
862  image3D->GetGeometry()->WorldToIndex(worldposition, p);
863 
864  auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType();
865  if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA)
866  {
867  std::string pixelValue = "Pixel RGB(A) value: ";
868  pixelValue.append(ConvertCompositePixelValueToString(image3D, p));
869  statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue.c_str());
870  }
871  else if (pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR)
872  {
873  std::string pixelValue = "See ODF Details view. ";
874  statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue.c_str());
875  }
876  else
877  {
878  ScalarType pixelValue;
881  image3D->GetChannelDescriptor().GetPixelType(),
882  image3D,
883  image3D->GetVolumeData(renderer->GetTimeStep()),
884  p,
885  pixelValue,
886  component);
887  statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue);
888  }
889  }
890  else
891  {
892  statusBar->DisplayImageInfoInvalid();
893  }
894 }
895 
896 bool mitk::DisplayActionEventBroadcast::GetBoolProperty(PropertyList::Pointer propertyList, const char* propertyName, bool defaultValue)
897 {
898  std::string valueAsString;
899  if (!propertyList->GetStringProperty(propertyName, valueAsString))
900  {
901  return defaultValue;
902  }
903  else
904  {
905  if (valueAsString == "true")
906  {
907  return true;
908  }
909  else
910  {
911  return false;
912  }
913  }
914 }
void Zoom(StateMachineAction *stateMachineAction, InteractionEvent *interactionEvent)
mitk::ScalarType FastSinglePixelAccess(mitk::PixelType, mitk::Image::Pointer im, ImageDataItem *item, itk::Index< 3 > idx, mitk::ScalarType &val, int component=0)
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 Move(StateMachineAction *stateMachineAction, InteractionEvent *interactionEvent)
Base class to implement InteractionEventObservers.
void StartRotation(StateMachineAction *stateMachineAction, InteractionEvent *interactionEvent)
static BaseRenderer * GetInstance(vtkRenderWindow *renWin)
ServiceRegistrationU RegisterService(const InterfaceMap &service, const ServiceProperties &properties=ServiceProperties())
void SetCrosshair(StateMachineAction *stateMachineAction, InteractionEvent *interactionEvent)
double ScalarType
bool HandleEvent(InteractionEvent *event, DataNode *dataNode)
void Swivel(StateMachineAction *stateMachineAction, InteractionEvent *interactionEvent)
Organizes the rendering process.
double GetScaleFactorMMPerDisplayUnit() const
void Notify(InteractionEvent *interactionEvent, bool isHandled) override
bool FilterEvents(InteractionEvent *interactionEvent, DataNode *dataNode) override
Filters the event resp. the sender of the event.
bool IntersectionPoint(const Line3D &line, Point3D &intersectionPoint) const
Calculate intersection point between the plane and a line.
void EndRotation(StateMachineAction *stateMachineAction, InteractionEvent *interactionEvent)
virtual const PlaneGeometry * GetCurrentWorldPlaneGeometry()
Get the current 2D-worldgeometry (m_CurrentWorldPlaneGeometry) used for 2D-rendering.
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
BaseRenderer * GetSender() const
void ConnectActionsAndFunctions() override
Connects the action names used in the state machine pattern with functions implemented within this In...
void Init(StateMachineAction *stateMachineAction, InteractionEvent *interactionEvent)
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.
Vector< ScalarType, 3 > Vector3D
Definition: mitkVector.h:130
bool CheckRotationPossible(const InteractionEvent *interactionEvent)
static RenderingManager * GetInstance()
virtual void SetAutoRepeat(bool _arg)
Represents an action, that is executed after a certain event (in statemachine-mechanism) TODO: implem...
virtual MapperSlotId GetMapperID()
Get the MapperSlotId to use.
Image class for storing images.
Definition: mitkImage.h:72
void Scroll(StateMachineAction *stateMachineAction, InteractionEvent *interactionEvent)
const mitk::PlaneGeometry * GetCurrentPlaneGeometry()
Returns the currently selected Plane in the current BaseGeometry (if existent).
void DecreaseTimeStep(StateMachineAction *stateMachineAction, InteractionEvent *interactionEvent)
void ScrollOneUp(StateMachineAction *stateMachineAction, InteractionEvent *interactionEvent)
void ResetMouseCursor()
Resets the mouse cursor (if modified by the SlicesCoordinator) to its original state.
void ScrollOneDown(StateMachineAction *stateMachineAction, InteractionEvent *interactionEvent)
bool CheckSwivelPossible(const InteractionEvent *interactionEvent)
void SetMouseCursor(const char *xpm[], int hotspotX, int hotspotY)
Sets the specified mouse cursor.
US_UNORDERED_MAP_TYPE< std::string, Any > ServiceProperties
void IncreaseTimeStep(StateMachineAction *stateMachineAction, InteractionEvent *interactionEvent)
void ConfigurationChanged() override
This function is executed when a config object is set / changed (via &#39;SetEventConfig&#39; or &#39;AddEventCon...
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!
#define CONNECT_CONDITION(a, f)
Vector3D GetNormal() const
Normal of the plane.
void Rotate(StateMachineAction *stateMachineAction, InteractionEvent *interactionEvent)
const mitk::Vector3D GetSpacing() const
Get the spacing (size of a pixel).
MITKCORE_EXPORT const ScalarType eps
virtual SliceNavigationController * GetSliceNavigationController()
PropertyList::Pointer GetAttributes() const
Stepper * GetTime()
Get the Stepper through the time.
#define mitkPixelTypeMultiplex5(function, ptype, param1, param2, param3, param4, param5)
Describes a two-dimensional, rectangular plane.
#define CONNECT_FUNCTION(a, f)
void AdjustLevelWindow(StateMachineAction *stateMachineAction, InteractionEvent *interactionEvent)
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.
static ModuleContext * GetModuleContext()
Returns the module context of the calling module.
const RenderWindowVector & GetAllRegisteredRenderWindows()
void RequestUpdateAll(RequestType type=REQUEST_UPDATE_ALL)
Class for nodes of the DataTree.
Definition: mitkDataNode.h:57
bool CheckPositionEvent(const InteractionEvent *interactionEvent)
Check if the given interaction event is actually an &#39;InteractionPositionEvent&#39;.