Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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.