Medical Imaging Interaction Toolkit  2018.4.99-9a29ffc6
Medical Imaging Interaction Toolkit
mitkPlanarFigureInteractor.cpp
Go to the documentation of this file.
1 /*============================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
13 #define PLANARFIGUREINTERACTOR_DBG MITK_DEBUG("PlanarFigureInteractor") << __LINE__ << ": "
14 
16 #include "mitkPlanarBezierCurve.h"
17 #include "mitkPlanarCircle.h"
18 #include "mitkPlanarFigure.h"
19 #include "mitkPlanarPolygon.h"
20 
22 #include "mitkInternalEvent.h"
23 
24 #include "mitkBaseRenderer.h"
25 #include "mitkRenderingManager.h"
26 
28 #include "mitkPlaneGeometry.h"
29 
31  : DataInteractor(), m_Precision(6.5), m_MinimumPointDistance(25.0), m_IsHovering(false), m_LastPointWasValid(false)
32 {
33 }
34 
36 {
37 }
38 
40 {
41  CONNECT_CONDITION("figure_is_on_current_slice", CheckFigureOnRenderingGeometry);
42  CONNECT_CONDITION("figure_is_placed", CheckFigurePlaced);
43  CONNECT_CONDITION("minimal_figure_is_finished", CheckMinimalFigureFinished);
44  CONNECT_CONDITION("hovering_above_figure", CheckFigureHovering);
45  CONNECT_CONDITION("hovering_above_point", CheckControlPointHovering);
46  CONNECT_CONDITION("figure_is_selected", CheckSelection);
47  CONNECT_CONDITION("point_is_valid", CheckPointValidity);
48  CONNECT_CONDITION("figure_is_finished", CheckFigureFinished);
49  CONNECT_CONDITION("reset_on_point_select_needed", CheckResetOnPointSelect);
50  CONNECT_CONDITION("points_can_be_added_or_removed", CheckFigureIsExtendable);
51  CONNECT_CONDITION("figure_can_be_deleted", CheckFigureIsDeletable);
52  CONNECT_CONDITION("figure_is_editable", CheckFigureIsEditable);
53 
54  CONNECT_FUNCTION("finalize_figure", FinalizeFigure);
55  CONNECT_FUNCTION("hide_preview_point", HidePreviewPoint)
56  CONNECT_FUNCTION("hide_control_points", HideControlPoints)
57  CONNECT_FUNCTION("set_preview_point_position", SetPreviewPointPosition)
58  CONNECT_FUNCTION("move_current_point", MoveCurrentPoint);
59  CONNECT_FUNCTION("deselect_point", DeselectPoint);
60  CONNECT_FUNCTION("add_new_point", AddPoint);
61  CONNECT_FUNCTION("add_initial_point", AddInitialPoint);
62  CONNECT_FUNCTION("remove_selected_point", RemoveSelectedPoint);
63  CONNECT_FUNCTION("request_context_menu", RequestContextMenu);
64  CONNECT_FUNCTION("select_figure", SelectFigure);
65  CONNECT_FUNCTION("select_point", SelectPoint);
66  CONNECT_FUNCTION("end_interaction", EndInteraction);
67  CONNECT_FUNCTION("start_hovering", StartHovering)
68  CONNECT_FUNCTION("end_hovering", EndHovering);
69  CONNECT_FUNCTION("delete_figure", DeleteFigure);
70  CONNECT_FUNCTION("reset_on_point_select", PerformPointResetOnSelect);
71 }
72 
74 {
75  const mitk::PlanarFigure *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
76 
77  bool isFigureFinished = false;
78  planarFigure->GetPropertyList()->GetBoolProperty("initiallyplaced", isFigureFinished);
79 
80  return planarFigure->IsPlaced() && isFigureFinished;
81 }
82 
84 {
85  auto *positionEvent = dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent);
86  if (positionEvent == nullptr)
87  return;
88 
89  bool isEditable = true;
90  GetDataNode()->GetBoolProperty("planarfigure.iseditable", isEditable);
91 
92  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
93 
94  const mitk::PlaneGeometry *planarFigureGeometry = planarFigure->GetPlaneGeometry();
95  const mitk::AbstractTransformGeometry *abstractTransformGeometry =
96  dynamic_cast<AbstractTransformGeometry *>(planarFigure->GetGeometry(0));
97 
98  if (abstractTransformGeometry != nullptr)
99  return;
100 
101  // Extract point in 2D world coordinates (relative to PlaneGeometry of
102  // PlanarFigure)
103  Point2D point2D;
104  if (!this->TransformPositionEventToPoint2D(positionEvent, planarFigureGeometry, point2D) || !isEditable)
105  {
106  return;
107  }
108 
109  planarFigure->InvokeEvent(StartInteractionPlanarFigureEvent());
110 
111  // check if the control points shall be hidden during interaction
112  bool hidecontrolpointsduringinteraction = false;
113  GetDataNode()->GetBoolProperty("planarfigure.hidecontrolpointsduringinteraction", hidecontrolpointsduringinteraction);
114 
115  // hide the control points if necessary
116  // interactionEvent->GetSender()->GetDataStorage()->BlockNodeModifiedEvents( true );
117  GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", !hidecontrolpointsduringinteraction);
118  // interactionEvent->GetSender()->GetDataStorage()->BlockNodeModifiedEvents( false );
119 
120  // Move current control point to this point
121  planarFigure->SetCurrentControlPoint(point2D);
122 
123  // Re-evaluate features
124  planarFigure->EvaluateFeatures();
125 
126  // Update rendered scene
128 }
129 
131 {
132  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
133 
134  planarFigure->Modified();
135  planarFigure->DeselectControlPoint();
136  planarFigure->RemoveLastControlPoint();
137  planarFigure->SetProperty("initiallyplaced", mitk::BoolProperty::New(true));
138  GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", true);
139  GetDataNode()->Modified();
140  planarFigure->InvokeEvent(EndPlacementPlanarFigureEvent());
141  planarFigure->InvokeEvent(EndInteractionPlanarFigureEvent());
142 
143  // Shape might change when figure is finalized, e.g., smoothing of subdivision polygon
144  planarFigure->EvaluateFeatures();
145 
147 }
148 
150 {
151  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
152  GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", true);
153  planarFigure->Modified();
154  planarFigure->InvokeEvent(EndInteractionPlanarFigureEvent());
156 }
157 
159 {
160  if (interactionEvent->GetSender() == nullptr)
161  return false;
162  if (interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard3D)
163  return false;
164 
165  return true;
166 }
167 
169 {
170  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
171  planarFigure->ResetPreviewContolPoint();
172 
173  // Invoke end-hover event once the mouse is exiting the figure area
174  m_IsHovering = false;
175  planarFigure->InvokeEvent(EndHoverPlanarFigureEvent());
176 
177  // Set bool property to indicate that planar figure is no longer in "hovering" mode
178  GetDataNode()->SetBoolProperty("planarfigure.ishovering", false);
179 
181 }
182 
184 {
185  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
186  planarFigure->RemoveAllObservers();
187  GetDataNode()->RemoveAllObservers();
188 
189  interactionEvent->GetSender()->GetDataStorage()->Remove(GetDataNode());
191 }
192 
194 {
195  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
196  planarFigure->ResetOnPointSelect();
197 }
198 
200 {
201  const mitk::PlanarFigure *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
202  return (planarFigure->GetNumberOfControlPoints() >= planarFigure->GetMinimumNumberOfControlPoints());
203 }
204 
206 {
207  const mitk::PlanarFigure *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
208  return (planarFigure->GetNumberOfControlPoints() >= planarFigure->GetMaximumNumberOfControlPoints());
209 }
210 
212 {
213  bool isExtendable(false);
214  GetDataNode()->GetBoolProperty("planarfigure.isextendable", isExtendable);
215 
216  return isExtendable;
217 }
218 
220 {
221  bool isDeletable(true);
222  GetDataNode()->GetBoolProperty("planarfigure.isdeletable", isDeletable);
223 
224  return isDeletable;
225 }
226 
228 {
229  bool isEditable(true);
230  GetDataNode()->GetBoolProperty("planarfigure.iseditable", isEditable);
231 
232  return isEditable;
233 }
234 
236 {
237  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
238 
239  const bool wasSelected = planarFigure->DeselectControlPoint();
240  if (wasSelected)
241  {
242  // Issue event so that listeners may update themselves
243  planarFigure->Modified();
244  planarFigure->InvokeEvent(EndInteractionPlanarFigureEvent());
245 
246  GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", true);
247  // GetDataNode()->SetBoolProperty( "planarfigure.ishovering", false );
248  GetDataNode()->Modified();
249  }
250 }
251 
253 {
254  const mitk::InteractionPositionEvent *positionEvent =
255  dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent);
256  if (positionEvent == nullptr)
257  return;
258 
259  const DataNode::Pointer node = this->GetDataNode();
260  const BaseData::Pointer data = node->GetData();
261 
262  /*
263  * Added check for "initiallyplaced" due to bug 13097:
264  *
265  * There are two possible cases in which a point can be inserted into a PlanarPolygon:
266  *
267  * 1. The figure is currently drawn -> the point will be appended at the end of the figure
268  * 2. A point is inserted at a userdefined position after the initial placement of the figure is finished
269  *
270  * In the second case we need to determine the proper insertion index. In the first case the index always has
271  * to be -1 so that the point is appended to the end.
272  *
273  * These changes are necessary because of a macOS specific issue: If a users draws a PlanarPolygon then the
274  * next point to be added moves according to the mouse position. If then the user left clicks in order to add
275  * a point one would assume the last move position is identical to the left click position. This is actually the
276  * case for windows and linux but somehow NOT for mac. Because of the insertion logic of a new point in the
277  * PlanarFigure then for mac the wrong current selected point is determined.
278  *
279  * With this check here this problem can be avoided. However a redesign of the insertion logic should be considered
280  */
281 
282  bool isFigureFinished = false;
283  data->GetPropertyList()->GetBoolProperty("initiallyplaced", isFigureFinished);
284 
285  bool selected = false;
286  bool isEditable = true;
287 
288  node->GetBoolProperty("selected", selected);
289  node->GetBoolProperty("planarfigure.iseditable", isEditable);
290 
291  if (!selected || !isEditable)
292  {
293  return;
294  }
295 
296  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(data.GetPointer());
297 
298  // We can't derive a new control point from a polyline of a Bezier curve
299  // as all control points contribute to each polyline point.
300  if (dynamic_cast<PlanarBezierCurve *>(planarFigure) != nullptr && isFigureFinished)
301  return;
302 
303  const mitk::PlaneGeometry *planarFigureGeometry = planarFigure->GetPlaneGeometry();
304  const mitk::AbstractTransformGeometry *abstractTransformGeometry =
305  dynamic_cast<AbstractTransformGeometry *>(planarFigure->GetGeometry(0));
306 
307  if (abstractTransformGeometry != nullptr)
308  return;
309 
310  // If the planarFigure already has reached the maximum number
311  if (planarFigure->GetNumberOfControlPoints() >= planarFigure->GetMaximumNumberOfControlPoints())
312  {
313  return;
314  }
315 
316  // Extract point in 2D world coordinates (relative to PlaneGeometry of
317  // PlanarFigure)
318  Point2D point2D, projectedPoint;
319  if (!this->TransformPositionEventToPoint2D(positionEvent, planarFigureGeometry, point2D))
320  {
321  return;
322  }
323 
324  // TODO: check segment of polyline we clicked in
325  int nextIndex = -1;
326 
327  // We only need to check which position to insert the control point
328  // when interacting with a PlanarPolygon. For all other types
329  // new control points will always be appended
330 
331  const mitk::BaseRenderer *renderer = interactionEvent->GetSender();
332  const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldPlaneGeometry();
333 
334  if (dynamic_cast<mitk::PlanarPolygon *>(planarFigure) && isFigureFinished)
335  {
336  nextIndex =
337  this->IsPositionOverFigure(positionEvent, planarFigure, planarFigureGeometry, projectionPlane, projectedPoint);
338  }
339 
340  // Add point as new control point
341 
342  if (planarFigure->IsPreviewControlPointVisible())
343  {
344  point2D = planarFigure->GetPreviewControlPoint();
345  }
346 
347  planarFigure->AddControlPoint(point2D, planarFigure->GetControlPointForPolylinePoint(nextIndex, 0));
348 
349  if (planarFigure->IsPreviewControlPointVisible())
350  {
351  planarFigure->SelectControlPoint(nextIndex);
352  planarFigure->ResetPreviewContolPoint();
353  }
354 
355  // Re-evaluate features
356  planarFigure->EvaluateFeatures();
357  // this->LogPrintPlanarFigureQuantities( planarFigure );
358 
359  // Update rendered scene
361 }
362 
364 {
365  const mitk::InteractionPositionEvent *positionEvent =
366  dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent);
367  if (positionEvent == nullptr)
368  return;
369 
370  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
371  mitk::BaseRenderer *renderer = interactionEvent->GetSender();
372  auto *planarFigureGeometry = dynamic_cast<PlaneGeometry *>(planarFigure->GetGeometry(0));
373  const mitk::AbstractTransformGeometry *abstractTransformGeometry =
374  dynamic_cast<AbstractTransformGeometry *>(planarFigure->GetGeometry(0));
375 
376  // Invoke event to notify listeners that placement of this PF starts now
377  planarFigure->InvokeEvent(StartPlacementPlanarFigureEvent());
378 
379  // Use PlaneGeometry of the renderer clicked on for this PlanarFigure
380  auto *planeGeometry = const_cast<mitk::PlaneGeometry *>(
381  dynamic_cast<const mitk::PlaneGeometry *>(renderer->GetSliceNavigationController()->GetCurrentPlaneGeometry()));
382  if (planeGeometry != nullptr && abstractTransformGeometry == nullptr)
383  {
384  planarFigureGeometry = planeGeometry;
385  planarFigure->SetPlaneGeometry(planeGeometry);
386  }
387  else
388  {
389  return;
390  }
391 
392  // Extract point in 2D world coordinates (relative to PlaneGeometry of
393  // PlanarFigure)
394  Point2D point2D;
395  if (!this->TransformPositionEventToPoint2D(positionEvent, planarFigureGeometry, point2D))
396  {
397  return;
398  }
399 
400  // Place PlanarFigure at this point
401  planarFigure->PlaceFigure(point2D);
402 
403  // Re-evaluate features
404  planarFigure->EvaluateFeatures();
405  // this->LogPrintPlanarFigureQuantities( planarFigure );
406 
407  // Set a bool property indicating that the figure has been placed in
408  // the current RenderWindow. This is required so that the same render
409  // window can be re-aligned to the PlaneGeometry of the PlanarFigure later
410  // on in an application.
411  GetDataNode()->SetBoolProperty("PlanarFigureInitializedWindow", true, renderer);
412 
413  // Update rendered scene
415 }
416 
418 {
419  const mitk::InteractionPositionEvent *positionEvent =
420  dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent);
421  if (positionEvent == nullptr)
422  return;
423 
424  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
425 
426  if (!m_IsHovering)
427  {
428  // Invoke hover event once when the mouse is entering the figure area
429  m_IsHovering = true;
430  planarFigure->InvokeEvent(StartHoverPlanarFigureEvent());
431 
432  // Set bool property to indicate that planar figure is currently in "hovering" mode
433  GetDataNode()->SetBoolProperty("planarfigure.ishovering", true);
434 
436  }
437 }
438 
440 {
441  const mitk::InteractionPositionEvent *positionEvent =
442  dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent);
443  if (positionEvent == nullptr)
444  return;
445 
446  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
447  const mitk::BaseRenderer *renderer = interactionEvent->GetSender();
448 
449  planarFigure->DeselectControlPoint();
450 
451  mitk::Point2D pointProjectedOntoLine = positionEvent->GetPointerPositionOnScreen();
452 
453  bool selected(false);
454  bool isExtendable(false);
455  bool isEditable(true);
456  GetDataNode()->GetBoolProperty("selected", selected);
457  GetDataNode()->GetBoolProperty("planarfigure.isextendable", isExtendable);
458  GetDataNode()->GetBoolProperty("planarfigure.iseditable", isEditable);
459 
460  if (selected && isExtendable && isEditable)
461  {
462  renderer->DisplayToPlane(pointProjectedOntoLine, pointProjectedOntoLine);
463  planarFigure->SetPreviewControlPoint(pointProjectedOntoLine);
464  }
465 
467 }
468 
470 {
471  GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", false);
472 }
473 
475 {
476  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
477  planarFigure->ResetPreviewContolPoint();
479 }
480 
482 {
483  const auto *positionEvent =
484  dynamic_cast<const mitk::InteractionPositionEvent *>(interactionEvent);
485  if (positionEvent == nullptr)
486  return false;
487 
488  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
489  const mitk::BaseRenderer *renderer = interactionEvent->GetSender();
490  const mitk::PlaneGeometry *planarFigureGeometry = planarFigure->GetPlaneGeometry();
491  auto *abstractTransformGeometry =
492  dynamic_cast<AbstractTransformGeometry *>(planarFigure->GetGeometry(0));
493  const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldPlaneGeometry();
494 
495  if (abstractTransformGeometry != nullptr)
496  return false;
497 
498  mitk::Point2D pointProjectedOntoLine;
499  int previousControlPoint = this->IsPositionOverFigure(
500  positionEvent, planarFigure, planarFigureGeometry, projectionPlane, pointProjectedOntoLine);
501 
502  bool isHovering = (previousControlPoint != -1);
503 
504  if (isHovering)
505  {
506  return true;
507  }
508  else
509  {
510  return false;
511  }
512 
513  return false;
514 }
515 
517 {
518  const auto *positionEvent =
519  dynamic_cast<const mitk::InteractionPositionEvent *>(interactionEvent);
520  if (positionEvent == nullptr)
521  return false;
522 
523  const mitk::PlanarFigure *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
524  const mitk::BaseRenderer *renderer = interactionEvent->GetSender();
525  const mitk::PlaneGeometry *planarFigureGeometry = dynamic_cast<PlaneGeometry *>(planarFigure->GetGeometry(0));
526  const mitk::AbstractTransformGeometry *abstractTransformGeometry =
527  dynamic_cast<AbstractTransformGeometry *>(planarFigure->GetGeometry(0));
528  const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldPlaneGeometry();
529 
530  if (abstractTransformGeometry != nullptr)
531  return false;
532 
533  int pointIndex = -1;
535  positionEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer);
536 
537  if (pointIndex >= 0)
538  {
539  return true;
540  }
541  else
542  {
543  return false;
544  }
545 }
546 
548 {
549  bool selected = false;
550  GetDataNode()->GetBoolProperty("selected", selected);
551 
552  return selected;
553 }
554 
556 {
557  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
558  planarFigure->InvokeEvent(SelectPlanarFigureEvent());
559 }
560 
562 {
563  const mitk::InteractionPositionEvent *positionEvent =
564  dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent);
565  if (positionEvent == nullptr)
566  return;
567 
568  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
569  const mitk::BaseRenderer *renderer = interactionEvent->GetSender();
570  const mitk::PlaneGeometry *planarFigureGeometry = dynamic_cast<PlaneGeometry *>(planarFigure->GetGeometry(0));
571  const mitk::AbstractTransformGeometry *abstractTransformGeometry =
572  dynamic_cast<AbstractTransformGeometry *>(planarFigure->GetGeometry(0));
573  const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldPlaneGeometry();
574 
575  if (abstractTransformGeometry != nullptr)
576  return;
577 
579  positionEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer);
580 
581  if (pointIndex >= 0)
582  {
583  // If mouse is above control point, mark it as selected
584  planarFigure->SelectControlPoint(pointIndex);
585  }
586  else
587  {
588  planarFigure->DeselectControlPoint();
589  }
590 }
591 
593 {
594  // Check if the distance of the current point to the previously set point in display coordinates
595  // is sufficient (if a previous point exists)
596 
597  // Extract display position
598  const auto *positionEvent =
599  dynamic_cast<const mitk::InteractionPositionEvent *>(interactionEvent);
600  if (positionEvent == nullptr)
601  return false;
602 
603  const mitk::PlanarFigure *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
604 
605  m_LastPointWasValid = IsMousePositionAcceptableAsNewControlPoint(positionEvent, planarFigure);
606  return m_LastPointWasValid;
607 }
608 
610 {
611  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
612  mitk::BaseRenderer *renderer = interactionEvent->GetSender();
613 
614  const int selectedControlPoint = planarFigure->GetSelectedControlPoint();
615  planarFigure->RemoveControlPoint(selectedControlPoint);
616 
617  // Re-evaluate features
618  planarFigure->EvaluateFeatures();
619  planarFigure->Modified();
620 
621  GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", true);
622  planarFigure->InvokeEvent(EndInteractionPlanarFigureEvent());
624 
625  HandleEvent(mitk::InternalEvent::New(renderer, this, "Dummy-Event"), GetDataNode());
626 }
627 
629 {
630  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
631 
632  bool selected = false;
633  GetDataNode()->GetBoolProperty("selected", selected);
634 
635  // no need to invoke this if the figure is already selected
636  if (!selected)
637  {
638  planarFigure->InvokeEvent(SelectPlanarFigureEvent());
639  }
640 
641  planarFigure->InvokeEvent(ContextMenuPlanarFigureEvent());
642 }
643 
645 {
646  auto *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
647 
648  bool isEditable = true;
649  GetDataNode()->GetBoolProperty("planarfigure.iseditable", isEditable);
650 
651  // Reset the PlanarFigure if required
652  return isEditable && planarFigure->ResetOnPointSelectNeeded();
653 }
654 
656 {
657  const auto *posEvent =
658  dynamic_cast<const mitk::InteractionPositionEvent *>(interactionEvent);
659 
660  if (posEvent == nullptr)
661  return false;
662 
663  const mitk::Point3D worldPoint3D = posEvent->GetPositionInWorld();
664  const mitk::PlanarFigure *planarFigure = dynamic_cast<mitk::PlanarFigure *>(GetDataNode()->GetData());
665 
666  const mitk::PlaneGeometry *planarFigurePlaneGeometry = dynamic_cast<PlaneGeometry *>(planarFigure->GetGeometry(0));
667  const mitk::AbstractTransformGeometry *abstractTransformGeometry =
668  dynamic_cast<AbstractTransformGeometry *>(planarFigure->GetGeometry(0));
669 
670  if (abstractTransformGeometry != nullptr)
671  return false;
672 
673  const double planeThickness = planarFigurePlaneGeometry->GetExtentInMM(2);
674  if (planarFigurePlaneGeometry->Distance(worldPoint3D) > planeThickness)
675  {
676  // don't react, when interaction is too far away
677  return false;
678  }
679  return true;
680 }
681 
683 {
684  m_Precision = precision;
685 }
686 
688 {
689  m_MinimumPointDistance = minimumDistance;
690 }
691 
693  const PlaneGeometry *planarFigureGeometry,
694  Point2D &point2D)
695 {
696  const mitk::Point3D worldPoint3D = positionEvent->GetPositionInWorld();
697 
698  // TODO: proper handling of distance tolerance
699  if (planarFigureGeometry->Distance(worldPoint3D) > 0.1)
700  {
701  return false;
702  }
703 
704  // Project point onto plane of this PlanarFigure
705  planarFigureGeometry->Map(worldPoint3D, point2D);
706  return true;
707 }
708 
710  mitk::Point2D &displayPoint,
711  const mitk::PlaneGeometry *objectGeometry,
712  const mitk::PlaneGeometry *rendererGeometry,
713  const mitk::BaseRenderer *renderer) const
714 {
715  mitk::Point3D point3D;
716 
717  // Map circle point from local 2D geometry into 3D world space
718  objectGeometry->Map(point2D, point3D);
719 
720  const double planeThickness = objectGeometry->GetExtentInMM(2);
721 
722  // TODO: proper handling of distance tolerance
723  if (rendererGeometry->Distance(point3D) < planeThickness / 3.0)
724  {
725  // Project 3D world point onto display geometry
726  renderer->WorldToDisplay(point3D, displayPoint);
727  return true;
728  }
729 
730  return false;
731 }
732 
734  const mitk::Point2D &startPoint,
735  const mitk::Point2D &endPoint,
736  mitk::Point2D &projectedPoint) const
737 {
738  mitk::Vector2D n1 = endPoint - startPoint;
739  n1.Normalize();
740 
741  // Determine dot products between line vector and startpoint-point / endpoint-point vectors
742  const double l1 = n1 * (point - startPoint);
743  const double l2 = -n1 * (point - endPoint);
744 
745  // Determine projection of specified point onto line defined by start / end point
746  const mitk::Point2D crossPoint = startPoint + n1 * l1;
747  projectedPoint = crossPoint;
748 
749  const float dist1 = crossPoint.SquaredEuclideanDistanceTo(point);
750  const float dist2 = endPoint.SquaredEuclideanDistanceTo(point);
751  const float dist3 = startPoint.SquaredEuclideanDistanceTo(point);
752 
753  // Point is inside encompassing rectangle IF
754  // - its distance to its projected point is small enough
755  // - it is not further outside of the line than the defined tolerance
756  if (((dist1 < 20.0) && (l1 > 0.0) && (l2 > 0.0)) || dist2 < 20.0 || dist3 < 20.0)
757  {
758  return true;
759  }
760 
761  return false;
762 }
763 
765  PlanarFigure *planarFigure,
766  const PlaneGeometry *planarFigureGeometry,
767  const PlaneGeometry *rendererGeometry,
768  Point2D &pointProjectedOntoLine) const
769 {
770  mitk::Point2D displayPosition = positionEvent->GetPointerPositionOnScreen();
771 
772  // Iterate over all polylines of planar figure, and check if
773  // any one is close to the current display position
774  typedef mitk::PlanarFigure::PolyLineType VertexContainerType;
775 
776  Point2D polyLinePoint;
777  Point2D firstPolyLinePoint;
778  Point2D previousPolyLinePoint;
779  for (unsigned short loop = 0; loop < planarFigure->GetPolyLinesSize(); ++loop)
780  {
781  const VertexContainerType polyLine = planarFigure->GetPolyLine(loop);
782 
783  bool firstPoint(true);
784  for (auto it = polyLine.begin(); it != polyLine.end(); ++it)
785  {
786  // Get plane coordinates of this point of polyline (if possible)
787  if (!this->TransformObjectToDisplay(
788  *it, polyLinePoint, planarFigureGeometry, rendererGeometry, positionEvent->GetSender()))
789  {
790  break; // Poly line invalid (not on current 2D plane) --> skip it
791  }
792 
793  if (firstPoint)
794  {
795  firstPolyLinePoint = polyLinePoint;
796  firstPoint = false;
797  }
798  else if (this->IsPointNearLine(displayPosition, previousPolyLinePoint, polyLinePoint, pointProjectedOntoLine))
799  {
800  // Point is close enough to line segment --> Return index of the segment
801  return std::distance(polyLine.begin(), it);
802  }
803  previousPolyLinePoint = polyLinePoint;
804  }
805 
806  // For closed figures, also check last line segment
807  if (planarFigure->IsClosed() &&
808  this->IsPointNearLine(displayPosition, polyLinePoint, firstPolyLinePoint, pointProjectedOntoLine))
809  {
810  return 0; // Return index of first control point
811  }
812  }
813  return -1;
814 }
815 
817  const PlanarFigure *planarFigure,
818  const PlaneGeometry *planarFigureGeometry,
819  const PlaneGeometry *rendererGeometry,
820  const BaseRenderer *renderer) const
821 {
822  const mitk::Point2D displayPosition = positionEvent->GetPointerPositionOnScreen();
823 
824  // Iterate over all control points of planar figure, and check if
825  // any one is close to the current display position
826  mitk::Point2D displayControlPoint;
827 
828  const int numberOfControlPoints = planarFigure->GetNumberOfControlPoints();
829  for (int i = 0; i < numberOfControlPoints; i++)
830  {
831  if (this->TransformObjectToDisplay(
832  planarFigure->GetControlPoint(i), displayControlPoint, planarFigureGeometry, rendererGeometry, renderer))
833  {
834  // TODO: variable size of markers
835  if (displayPosition.SquaredEuclideanDistanceTo(displayControlPoint) < 20.0)
836  {
837  return i;
838  }
839  }
840  }
841 
842  return -1;
843 }
844 
846 {
847  MITK_INFO << "PlanarFigure: " << planarFigure->GetNameOfClass();
848  for (unsigned int i = 0; i < planarFigure->GetNumberOfFeatures(); ++i)
849  {
850  MITK_INFO << "* " << planarFigure->GetFeatureName(i) << ": " << planarFigure->GetQuantity(i) << " "
851  << planarFigure->GetFeatureUnit(i);
852  }
853 }
854 
856  const mitk::InteractionPositionEvent *positionEvent, const PlanarFigure *planarFigure)
857 {
858  assert(positionEvent && planarFigure);
859 
860  const BaseRenderer *renderer = positionEvent->GetSender();
861  assert(renderer);
862 
863  // Get the timestep to support 3D+t
864  const int timeStep(renderer->GetTimeStep(planarFigure));
865 
866  bool tooClose(false);
867 
868  const mitk::PlaneGeometry *planarFigureGeometry =
869  dynamic_cast<mitk::PlaneGeometry *>(planarFigure->GetGeometry(timeStep));
870  const mitk::AbstractTransformGeometry *abstractTransformGeometry =
871  dynamic_cast<mitk::AbstractTransformGeometry *>(planarFigure->GetGeometry(timeStep));
872 
873  if (abstractTransformGeometry != nullptr)
874  return false;
875 
876  Point2D point2D;
877  // Get the point2D from the positionEvent
878  if (!this->TransformPositionEventToPoint2D(positionEvent, planarFigureGeometry, point2D))
879  {
880  return false;
881  }
882 
883  // apply the controlPoint constraints of the planarFigure to get the
884  // coordinates that would actually be used.
885  const Point2D correctedPoint = const_cast<PlanarFigure *>(planarFigure)->ApplyControlPointConstraints(0, point2D);
886 
887  // map the 2D coordinates of the new point to world-coordinates
888  // and transform those to display-coordinates
889  mitk::Point3D newPoint3D;
890  planarFigureGeometry->Map(correctedPoint, newPoint3D);
891  mitk::Point2D newDisplayPosition;
892  renderer->WorldToDisplay(newPoint3D, newDisplayPosition);
893 
894  const int selectedControlPoint = planarFigure->GetSelectedControlPoint();
895  for (int i = 0; i < (int)planarFigure->GetNumberOfControlPoints(); ++i)
896  {
897  if (i != selectedControlPoint)
898  {
899  // Try to convert previous point to current display coordinates
900  mitk::Point3D previousPoint3D;
901  // map the 2D coordinates of the control-point to world-coordinates
902  planarFigureGeometry->Map(planarFigure->GetControlPoint(i), previousPoint3D);
903 
904  if (renderer->GetCurrentWorldPlaneGeometry()->Distance(previousPoint3D) < 0.1) // ugly, but assert makes this work
905  {
906  mitk::Point2D previousDisplayPosition;
907  // transform the world-coordinates into display-coordinates
908  renderer->WorldToDisplay(previousPoint3D, previousDisplayPosition);
909 
910  // Calculate the distance. We use display-coordinates here to make
911  // the check independent of the zoom-level of the rendering scene.
912  const double a = newDisplayPosition[0] - previousDisplayPosition[0];
913  const double b = newDisplayPosition[1] - previousDisplayPosition[1];
914 
915  // If point is to close, do not set a new point
916  tooClose = (a * a + b * b < m_MinimumPointDistance);
917  }
918  if (tooClose)
919  return false; // abort loop early
920  }
921  }
922 
923  return !tooClose; // default
924 }
925 
927 {
928  const mitk::PropertyList::Pointer properties = GetAttributes();
929 
930  std::string precision = "";
931  if (properties->GetStringProperty("precision", precision))
932  {
933  m_Precision = atof(precision.c_str());
934  }
935  else
936  {
937  m_Precision = (ScalarType)6.5;
938  }
939 
940  std::string minPointDistance = "";
941  if (properties->GetStringProperty("minPointDistance", minPointDistance))
942  {
943  m_MinimumPointDistance = atof(minPointDistance.c_str());
944  }
945  else
946  {
947  m_MinimumPointDistance = (ScalarType)25.0;
948  }
949 }
bool TransformObjectToDisplay(const mitk::Point2D &point2D, mitk::Point2D &displayPoint, const mitk::PlaneGeometry *objectGeometry, const mitk::PlaneGeometry *rendererGeometry, const mitk::BaseRenderer *renderer) const
bool IsPointNearLine(const mitk::Point2D &point, const mitk::Point2D &startPoint, const mitk::Point2D &endPoint, mitk::Point2D &projectedPoint) const
Returns true if the first specified point is in proximity of the line defined the other two point; fa...
void SelectFigure(StateMachineAction *, InteractionEvent *interactionEvent)
Super class for all position events.
bool CheckResetOnPointSelect(const InteractionEvent *interactionEvent)
Point2D GetControlPoint(unsigned int index) const
Returns specified control point in 2D world coordinates.
#define MITK_INFO
Definition: mitkLogMacros.h:18
virtual unsigned int GetMaximumNumberOfControlPoints() const =0
Returns the maximum number of control points allowed for this figure (e.g. 3 for triangles).
double ScalarType
void EndInteraction(StateMachineAction *, InteractionEvent *interactionEvent)
void SelectPoint(StateMachineAction *, InteractionEvent *interactionEvent)
bool HandleEvent(InteractionEvent *event, DataNode *dataNode)
Organizes the rendering process.
bool CheckFigureIsEditable(const InteractionEvent *interactionEvent)
virtual unsigned int GetMinimumNumberOfControlPoints() const =0
Returns the minimum number of control points needed to represent this figure.
bool CheckFigurePlaced(const InteractionEvent *interactionEvent)
bool CheckFigureHovering(const InteractionEvent *interactionEvent)
bool CheckSelection(const InteractionEvent *interactionEvent)
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 const PlaneGeometry * GetCurrentWorldPlaneGeometry()
Get the current 2D-worldgeometry (m_CurrentWorldPlaneGeometry) used for 2D-rendering.
void EndHovering(StateMachineAction *, InteractionEvent *interactionEvent)
double GetQuantity(unsigned int index) const
bool CheckPointValidity(const InteractionEvent *interactionEvent)
void HideControlPoints(StateMachineAction *, InteractionEvent *interactionEvent)
void HidePreviewPoint(StateMachineAction *, InteractionEvent *interactionEvent)
bool CheckControlPointHovering(const InteractionEvent *interactionEvent)
virtual bool Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const
Project a 3D point given in mm (pt3d_mm) onto the 2D geometry. The result is a 2D point in mm (pt2d_m...
const PolyLineType GetPolyLine(unsigned int index)
Returns the polyline representing the planar figure (for rendering, measurements, etc...
bool GetBoolProperty(const char *propertyKey, bool &boolValue, const mitk::BaseRenderer *renderer=nullptr) const
Convenience access method for bool properties (instances of BoolProperty)
void WorldToDisplay(const Point3D &worldIndex, Point2D &displayPoint) const
This method converts a 3D world index to the display point using the geometry of the renderWindow...
bool FilterEvents(InteractionEvent *interactionEvent, DataNode *) override
void RemoveSelectedPoint(StateMachineAction *, InteractionEvent *interactionEvent)
bool CheckFigureFinished(const InteractionEvent *interactionEvent)
DataNode * GetDataNode() const
void SetPreviewPointPosition(StateMachineAction *, InteractionEvent *interactionEvent)
ScalarType GetExtentInMM(int direction) const
Get the extent of the bounding-box in the specified direction in mm.
bool IsMousePositionAcceptableAsNewControlPoint(const mitk::InteractionPositionEvent *positionEvent, const PlanarFigure *)
Used when clicking to determine if a point is too close to the previous point.
BaseRenderer * GetSender() const
virtual bool IsClosed() const
True if the planar figure is closed.
unsigned int GetNumberOfControlPoints() const
Returns the current number of 2D control points defining this figure.
Base class from with interactors that handle DataNodes are to be derived.
static Pointer New()
void AddPoint(StateMachineAction *, InteractionEvent *interactionEvent)
virtual bool IsPlaced() const
True if the planar figure has been placed (and can be displayed/interacted with). ...
void StartHovering(StateMachineAction *, InteractionEvent *interactionEvent)
BaseData * GetData() const
Get the data object (instance of BaseData, e.g., an Image) managed by this DataNode.
bool CheckFigureIsDeletable(const InteractionEvent *interactionEvent)
void ResetPreviewContolPoint()
Marks the PreviewControlPoint as invisible.
static RenderingManager * GetInstance()
Represents an action, that is executed after a certain event (in statemachine-mechanism) TODO: implem...
virtual unsigned int GetTimeStep() const
void DeselectPoint(StateMachineAction *, InteractionEvent *interactionEvent)
virtual bool ResetOnPointSelect()
Returns true if the planar figure is reset to "add points" mode when a point is selected.
bool CheckFigureIsExtendable(const InteractionEvent *interactionEvent)
virtual MapperSlotId GetMapperID()
Get the MapperSlotId to use.
void FinalizeFigure(StateMachineAction *, InteractionEvent *interactionEvent)
const mitk::PlaneGeometry * GetCurrentPlaneGeometry()
Returns the currently selected Plane in the current BaseGeometry (if existent).
virtual unsigned short GetPolyLinesSize()
Returns the current number of polylines.
void DeleteFigure(StateMachineAction *, InteractionEvent *interactionEvent)
int IsPositionInsideMarker(const InteractionPositionEvent *positionEvent, const PlanarFigure *planarFigure, const PlaneGeometry *planarFigureGeometry, const PlaneGeometry *rendererGeometry, const BaseRenderer *renderer) const
Returns the index of the marker (control point) over which the point contained in the passed event (i...
Describes a geometry defined by an vtkAbstractTransform and a plane.
void MoveCurrentPoint(StateMachineAction *, InteractionEvent *interactionEvent)
virtual unsigned int GetNumberOfFeatures() const
Returns the number of features available for this PlanarFigure (such as, radius, area, ...).
void ConnectActionsAndFunctions() override
Overwrite this function to connect actions from StateMachine description with functions.
bool CheckFigureOnRenderingGeometry(const InteractionEvent *interactionEvent)
void RequestContextMenu(StateMachineAction *, InteractionEvent *interactionEvent)
bool TransformPositionEventToPoint2D(const InteractionPositionEvent *positionEvent, const PlaneGeometry *planarFigureGeometry, Point2D &point2D)
mitk::PropertyList::Pointer GetPropertyList() const
Get the data&#39;s property list.
void SetBoolProperty(const char *propertyKey, bool boolValue, const mitk::BaseRenderer *renderer=nullptr)
Convenience method for setting boolean properties (instances of BoolProperty)
virtual bool DeselectControlPoint()
Deselect control point; no control point active.
#define CONNECT_CONDITION(a, f)
virtual int GetSelectedControlPoint() const
Return currently selected control point.
Base-class for geometric planar (2D) figures, such as lines, circles, rectangles, polygons...
void SetMinimumPointDistance(ScalarType minimumDistance)
Sets the minimal distance between two control points.
std::vector< PolyLineElement > PolyLineType
ScalarType Distance(const Point3D &pt3d_mm) const
Distance of the point from the geometry (bounding-box not considered)
virtual SliceNavigationController * GetSliceNavigationController()
PropertyList::Pointer GetAttributes() const
const char * GetFeatureName(unsigned int index) const
Returns the name (identifier) of the specified features.
const char * GetFeatureUnit(unsigned int index) const
Returns the physical unit of the specified features.
static Pointer New(BaseRenderer *_arga, DataInteractor *_argb, const std::string &_argc)
void SetPrecision(ScalarType precision)
Sets the amount of precision.
Describes a two-dimensional, rectangular plane.
#define CONNECT_FUNCTION(a, f)
void LogPrintPlanarFigureQuantities(const PlanarFigure *planarFigure)
bool CheckMinimalFigureFinished(const InteractionEvent *interactionEvent)
virtual DataStorage::Pointer GetDataStorage() const
mitk::BaseGeometry * GetGeometry(int t=0) const
Return the geometry, which is a TimeGeometry, of the data as non-const pointer.
Definition: mitkBaseData.h:143
int IsPositionOverFigure(const InteractionPositionEvent *positionEvent, PlanarFigure *planarFigure, const PlaneGeometry *planarFigureGeometry, const PlaneGeometry *rendererGeometry, Point2D &pointProjectedOntoLine) const
Returns true if the point contained in the passed event (in display coordinates) is over the planar f...
void PerformPointResetOnSelect(StateMachineAction *, InteractionEvent *interactionEvent)
void RequestUpdateAll(RequestType type=REQUEST_UPDATE_ALL)
Class for nodes of the DataTree.
Definition: mitkDataNode.h:57
void AddInitialPoint(StateMachineAction *, InteractionEvent *interactionEvent)