Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkGizmoInteractor.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 "mitkGizmoInteractor.h"
18 #include "mitkGizmoMapper2D.h"
19 
20 // MITK includes
21 #include <mitkInteractionConst.h>
23 #include <mitkInternalEvent.h>
24 #include <mitkOperationEvent.h>
25 #include <mitkRotationOperation.h>
26 #include <mitkScaleOperation.h>
27 #include <mitkSurface.h>
28 #include <mitkUndoController.h>
29 #include <mitkVtkMapper.h>
30 
31 // VTK includes
32 #include <vtkCamera.h>
33 #include <vtkInteractorObserver.h>
34 #include <vtkInteractorStyle.h>
35 #include <vtkMath.h>
36 #include <vtkPointData.h>
37 #include <vtkPolyData.h>
38 #include <vtkRenderWindowInteractor.h>
39 #include <vtkVector.h>
40 #include <vtkVectorOperators.h>
41 
42 mitk::GizmoInteractor::GizmoInteractor()
43 {
44 }
45 
46 mitk::GizmoInteractor::~GizmoInteractor()
47 {
48 }
49 
50 void mitk::GizmoInteractor::ConnectActionsAndFunctions()
51 {
52  CONNECT_CONDITION("PickedHandle", HasPickedHandle);
53 
54  CONNECT_FUNCTION("DecideInteraction", DecideInteraction);
55  CONNECT_FUNCTION("MoveAlongAxis", MoveAlongAxis);
56  CONNECT_FUNCTION("RotateAroundAxis", RotateAroundAxis);
57  CONNECT_FUNCTION("MoveFreely", MoveFreely);
58  CONNECT_FUNCTION("ScaleEqually", ScaleEqually);
59  CONNECT_FUNCTION("FeedUndoStack", FeedUndoStack);
60 }
61 
63 {
65 
66  m_Gizmo = dynamic_cast<Gizmo *>(node->GetData());
67 
68  // setup picking from just this object
69  m_Picker.clear();
70 }
71 
73 {
74  if (node && node->GetData())
75  {
76  m_ManipulatedObjectGeometry = node->GetData()->GetGeometry();
77  }
78 }
79 
80 bool mitk::GizmoInteractor::HasPickedHandle(const InteractionEvent *interactionEvent)
81 {
82  auto positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
83  if (positionEvent == NULL)
84  {
85  return false;
86  }
87 
88  DataNode::Pointer gizmoNode = this->GetDataNode();
89 
90  if (m_Gizmo.IsNull())
91  {
92  return false;
93  }
94 
95  if (m_ManipulatedObjectGeometry.IsNull())
96  {
97  return false;
98  }
99 
100  if (interactionEvent->GetSender()->GetRenderWindow()->GetNeverRendered())
101  {
102  return false;
103  }
104 
105  if (interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard2D)
106  {
107  m_PickedHandle = PickFrom2D(positionEvent);
108  }
109  else
110  {
111  m_PickedHandle = PickFrom3D(positionEvent);
112  }
113 
114  if (m_PickedHandle != Gizmo::NoHandle)
115  {
116  // if something relevant was picked, we calculate a number of
117  // important points and axes for the upcoming geometry manipulations
118 
119  // note initial state
120  m_InitialClickPosition2D = positionEvent->GetPointerPositionOnScreen();
121  m_InitialClickPosition3D = positionEvent->GetPositionInWorld();
122 
123  auto renderer = positionEvent->GetSender()->GetVtkRenderer();
124  renderer->SetWorldPoint(m_InitialClickPosition3D[0], m_InitialClickPosition3D[1], m_InitialClickPosition3D[2], 0);
125  renderer->WorldToDisplay();
126  m_InitialClickPosition2DZ = renderer->GetDisplayPoint()[2];
127 
128  m_InitialGizmoCenter3D = m_Gizmo->GetCenter();
129  positionEvent->GetSender()->WorldToDisplay(m_InitialGizmoCenter3D, m_InitialGizmoCenter2D);
130 
131  m_InitialManipulatedObjectGeometry = m_ManipulatedObjectGeometry->Clone();
132 
133  switch (m_PickedHandle)
134  {
137  case Gizmo::ScaleX:
138  m_AxisOfMovement = m_InitialManipulatedObjectGeometry->GetAxisVector(0);
139  break;
142  case Gizmo::ScaleY:
143  m_AxisOfMovement = m_InitialManipulatedObjectGeometry->GetAxisVector(1);
144  break;
147  case Gizmo::ScaleZ:
148  m_AxisOfMovement = m_InitialManipulatedObjectGeometry->GetAxisVector(2);
149  break;
150  default:
151  break;
152  }
153  m_AxisOfMovement.Normalize();
154  m_AxisOfRotation = m_AxisOfMovement;
155 
156  // for translation: test whether the user clicked into the "object's real" axis direction
157  // or into the other one
158  Vector3D intendedAxis = m_InitialClickPosition3D - m_InitialGizmoCenter3D;
159 
160  if (intendedAxis * m_AxisOfMovement < 0)
161  {
162  m_AxisOfMovement *= -1.0;
163  }
164 
165  // for rotation: test whether the axis of rotation is more looking in the direction
166  // of the camera or in the opposite
167  vtkCamera *camera = renderer->GetActiveCamera();
168  vtkVector3d cameraDirection(camera->GetDirectionOfProjection());
169 
170  double angle_rad = vtkMath::AngleBetweenVectors(cameraDirection.GetData(), m_AxisOfRotation.GetDataPointer());
171 
172  if (angle_rad < vtkMath::Pi() / 2.0)
173  {
174  m_AxisOfRotation *= -1.0;
175  }
176 
177  return true;
178  }
179  else
180  {
181  return false;
182  }
183 }
184 
185 void mitk::GizmoInteractor::DecideInteraction(StateMachineAction *, InteractionEvent *interactionEvent)
186 {
187  assert(m_PickedHandle != Gizmo::NoHandle);
188 
189  InternalEvent::Pointer decision;
190  switch (m_PickedHandle)
191  {
195  decision = InternalEvent::New(interactionEvent->GetSender(), this, "StartTranslationAlongAxis");
196  break;
200  decision = InternalEvent::New(interactionEvent->GetSender(), this, "StartRotationAroundAxis");
201  break;
202  case Gizmo::MoveFreely:
203  decision = InternalEvent::New(interactionEvent->GetSender(), this, "MoveFreely");
204  break;
205  case Gizmo::ScaleX:
206  case Gizmo::ScaleY:
207  case Gizmo::ScaleZ:
208  decision = InternalEvent::New(interactionEvent->GetSender(), this, "ScaleEqually");
209  break;
210  default:
211  break;
212  }
213 
214  interactionEvent->GetSender()->GetDispatcher()->QueueEvent(decision);
215 }
216 
217 void mitk::GizmoInteractor::MoveAlongAxis(StateMachineAction *, InteractionEvent *interactionEvent)
218 {
219  auto positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
220  if (positionEvent == NULL)
221  {
222  return;
223  }
224 
225  Vector2D axisVector2D = m_InitialClickPosition2D - m_InitialGizmoCenter2D;
226  axisVector2D.Normalize();
227 
228  Vector2D movement2D = positionEvent->GetPointerPositionOnScreen() - m_InitialClickPosition2D;
229  double relativeMovement = movement2D * axisVector2D; // projection
230 
231  Vector3D movement3D = relativeMovement * m_AxisOfMovement;
232 
233  ApplyTranslationToManipulatedObject(movement3D);
235 }
236 
237 void mitk::GizmoInteractor::RotateAroundAxis(StateMachineAction *, InteractionEvent *interactionEvent)
238 {
239  auto positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
240  if (positionEvent == NULL)
241  {
242  return;
243  }
244 
245  Vector2D originalVector = m_InitialClickPosition2D - m_InitialGizmoCenter2D;
246  Vector2D currentVector = positionEvent->GetPointerPositionOnScreen() - m_InitialGizmoCenter2D;
247 
248  originalVector.Normalize();
249  currentVector.Normalize();
250 
251  double angle_rad = std::atan2(currentVector[1], currentVector[0]) - std::atan2(originalVector[1], originalVector[0]);
252 
253  ApplyRotationToManipulatedObject(vtkMath::DegreesFromRadians(angle_rad));
255 }
256 
257 void mitk::GizmoInteractor::MoveFreely(StateMachineAction *, InteractionEvent *interactionEvent)
258 {
259  auto positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
260  if (positionEvent == NULL)
261  {
262  return;
263  }
264 
265  Point2D currentPosition2D = positionEvent->GetPointerPositionOnScreen();
266 
267  // re-use the initial z value to really move parallel to the camera plane
268  auto renderer = positionEvent->GetSender()->GetVtkRenderer();
269  renderer->SetDisplayPoint(currentPosition2D[0], currentPosition2D[1], m_InitialClickPosition2DZ);
270  renderer->DisplayToWorld();
271  vtkVector3d worldPointVTK(renderer->GetWorldPoint());
272  Point3D worldPointITK(worldPointVTK.GetData());
273  Vector3D movementITK(worldPointITK - m_InitialClickPosition3D);
274 
275  ApplyTranslationToManipulatedObject(movementITK);
277 }
278 
279 void mitk::GizmoInteractor::ScaleEqually(StateMachineAction *, InteractionEvent *interactionEvent)
280 {
281  auto positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
282  if (positionEvent == NULL)
283  {
284  return;
285  }
286 
287  Point2D currentPosition2D = positionEvent->GetPointerPositionOnScreen();
288  double relativeSize = (currentPosition2D - m_InitialGizmoCenter2D).GetNorm() /
289  (m_InitialClickPosition2D - m_InitialGizmoCenter2D).GetNorm();
290 
291  ApplyEqualScalingToManipulatedObject(relativeSize);
293 }
294 
295 void mitk::GizmoInteractor::ApplyTranslationToManipulatedObject(const Vector3D &translation)
296 {
297  assert(m_ManipulatedObjectGeometry.IsNotNull());
298 
299  auto manipulatedGeometry = m_InitialManipulatedObjectGeometry->Clone();
300  m_FinalDoOperation.reset(new PointOperation(OpMOVE, translation));
301  if (m_UndoEnabled)
302  {
303  m_FinalUndoOperation.reset(new PointOperation(OpMOVE, -translation));
304  }
305 
306  manipulatedGeometry->ExecuteOperation(m_FinalDoOperation.get());
307  m_ManipulatedObjectGeometry->SetIdentity();
308  m_ManipulatedObjectGeometry->Compose(manipulatedGeometry->GetIndexToWorldTransform());
309 }
310 
311 void mitk::GizmoInteractor::ApplyEqualScalingToManipulatedObject(double scalingFactor)
312 {
313  assert(m_ManipulatedObjectGeometry.IsNotNull());
314  auto manipulatedGeometry = m_InitialManipulatedObjectGeometry->Clone();
315 
316  m_FinalDoOperation.reset(new ScaleOperation(OpSCALE, scalingFactor - 1.0, m_InitialGizmoCenter3D));
317  if (m_UndoEnabled)
318  {
319  m_FinalUndoOperation.reset(new ScaleOperation(OpSCALE, -(scalingFactor - 1.0), m_InitialGizmoCenter3D));
320  }
321 
322  manipulatedGeometry->ExecuteOperation(m_FinalDoOperation.get());
323  m_ManipulatedObjectGeometry->SetIdentity();
324  m_ManipulatedObjectGeometry->Compose(manipulatedGeometry->GetIndexToWorldTransform());
325 }
326 
327 void mitk::GizmoInteractor::ApplyRotationToManipulatedObject(double angle_deg)
328 {
329  assert(m_ManipulatedObjectGeometry.IsNotNull());
330 
331  auto manipulatedGeometry = m_InitialManipulatedObjectGeometry->Clone();
332 
333  m_FinalDoOperation.reset(new RotationOperation(OpROTATE, m_InitialGizmoCenter3D, m_AxisOfRotation, angle_deg));
334  if (m_UndoEnabled)
335  {
336  m_FinalUndoOperation.reset(new RotationOperation(OpROTATE, m_InitialGizmoCenter3D, m_AxisOfRotation, -angle_deg));
337  }
338 
339  manipulatedGeometry->ExecuteOperation(m_FinalDoOperation.get());
340  m_ManipulatedObjectGeometry->SetIdentity();
341  m_ManipulatedObjectGeometry->Compose(manipulatedGeometry->GetIndexToWorldTransform());
342 }
343 
344 void mitk::GizmoInteractor::FeedUndoStack(StateMachineAction *, InteractionEvent *)
345 {
346  if (m_UndoEnabled)
347  {
348  OperationEvent *operationEvent = new OperationEvent(m_ManipulatedObjectGeometry,
349  // OperationEvent will destroy operations!
350  // --> release() and not get()
351  m_FinalDoOperation.release(),
352  m_FinalUndoOperation.release(),
353  "Direct geometry manipulation");
354  mitk::OperationEvent::IncCurrObjectEventId(); // save each modification individually
355  m_UndoController->SetOperationEvent(operationEvent);
356  }
357 }
358 
359 mitk::Gizmo::HandleType mitk::GizmoInteractor::PickFrom2D(const InteractionPositionEvent *positionEvent)
360 {
361  BaseRenderer *renderer = positionEvent->GetSender();
362 
363  auto mapper = GetDataNode()->GetMapper(BaseRenderer::Standard2D);
364  auto gizmo_mapper = dynamic_cast<GizmoMapper2D *>(mapper);
365  auto &picker = m_Picker[renderer];
366 
367  if (picker == nullptr)
368  {
370  picker->SetTolerance(0.005);
371 
372  if (gizmo_mapper)
373  { // doing this each time is bizarre
374  picker->AddPickList(gizmo_mapper->GetVtkProp(renderer));
375  picker->PickFromListOn();
376  }
377  }
378 
379  auto displayPosition = positionEvent->GetPointerPositionOnScreen();
380  picker->Pick(displayPosition[0], displayPosition[1], 0, positionEvent->GetSender()->GetVtkRenderer());
381 
382  vtkIdType pickedPointID = picker->GetPointId();
383  if (pickedPointID == -1)
384  {
385  return Gizmo::NoHandle;
386  }
387 
388  vtkPolyData *polydata = gizmo_mapper->GetVtkPolyData(renderer);
389 
390  if (polydata && polydata->GetPointData() && polydata->GetPointData()->GetScalars())
391  {
392  double dataValue = polydata->GetPointData()->GetScalars()->GetTuple1(pickedPointID);
393  return m_Gizmo->GetHandleFromPointDataValue(dataValue);
394  }
395 
396  return Gizmo::NoHandle;
397 }
398 
399 mitk::Gizmo::HandleType mitk::GizmoInteractor::PickFrom3D(const InteractionPositionEvent *positionEvent)
400 {
401  BaseRenderer *renderer = positionEvent->GetSender();
402  auto &picker = m_Picker[renderer];
403  if (picker == nullptr)
404  {
406  picker->SetTolerance(0.005);
407  auto mapper = GetDataNode()->GetMapper(BaseRenderer::Standard3D);
408  auto vtk_mapper = dynamic_cast<VtkMapper *>(mapper);
409  if (vtk_mapper)
410  { // doing this each time is bizarre
411  picker->AddPickList(vtk_mapper->GetVtkProp(renderer));
412  picker->PickFromListOn();
413  }
414  }
415 
416  auto displayPosition = positionEvent->GetPointerPositionOnScreen();
417  picker->Pick(displayPosition[0], displayPosition[1], 0, positionEvent->GetSender()->GetVtkRenderer());
418 
419  vtkIdType pickedPointID = picker->GetPointId();
420  if (pickedPointID == -1)
421  {
422  return Gizmo::NoHandle;
423  }
424 
425  // _something_ picked
426  return m_Gizmo->GetHandleFromPointID(pickedPointID);
427 }
void SetGizmoNode(DataNode *node)
Point< ScalarType, 2 > Point2D
Definition: mitkPoint.h:98
Super class for all position events.
void SetManipulatedObjectNode(DataNode *node)
BaseRenderer * GetSender() const
BaseData * GetData() const
Get the data object (instance of BaseData, e.g., an Image) managed by this DataNode.
Constants for most interaction classes, due to the generic StateMachines.
itk::SmartPointer< Self > Pointer
Definition: mitkDataNode.h:81
HandleType
Names for the different parts of the gizmo.
Definition: mitkGizmo.h:65
itk::SmartPointer< Self > Pointer
Vector< ScalarType, 2 > Vector2D
Definition: mitkVector.h:133
Vector< ScalarType, 3 > Vector3D
Definition: mitkVector.h:134
static RenderingManager * GetInstance()
virtual MapperSlotId GetMapperID()
Get the MapperSlotId to use.
Point< ScalarType, 3 > Point3D
Definition: mitkPoint.h:99
virtual void SetDataNode(DataNode *dataNode)
#define CONNECT_CONDITION(a, f)
void ForceImmediateUpdateAll(RequestType type=REQUEST_UPDATE_ALL)
static Pointer New(BaseRenderer *_arga, DataInteractor *_argb, const std::string &_argc)
static void IncCurrObjectEventId()
Increases the current ObjectEventId For example if a button click generates operations the ObjectEven...
#define CONNECT_FUNCTION(a, f)
mitk::BaseGeometry * GetGeometry(int t=0) const
Return the geometry, which is a TimeGeometry, of the data as non-const pointer.
Definition: mitkBaseData.h:129
vtkRenderWindow * GetRenderWindow() const
Access the RenderWindow into which this renderer renders.
Class for nodes of the DataTree.
Definition: mitkDataNode.h:66
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.