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