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
mitkPlanarCross.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 "mitkPlanarCross.h"
18 #include "mitkPlaneGeometry.h"
19 #include "mitkProperties.h"
20 
22  : FEATURE_ID_LONGESTDIAMETER(this->AddFeature("Longest Axis", "mm")),
23  FEATURE_ID_SHORTAXISDIAMETER(this->AddFeature("Short Axis", "mm"))
24 {
25  // Cross has two control points at the beginning
27 
28  // Create property for SingleLineMode (default: false)
29  this->SetProperty("SingleLineMode", mitk::BoolProperty::New(false));
30 
31  // Create helper polyline object (for drawing the orthogonal orientation line)
33  m_HelperPolyLinesToBePainted->InsertElement(0, false);
34 }
35 
36 void mitk::PlanarCross::SetSingleLineMode(bool singleLineMode)
37 {
38  this->SetProperty("SingleLineMode", mitk::BoolProperty::New(singleLineMode));
39  this->Modified();
40 }
41 
43 {
44  const mitk::BoolProperty *singleLineMode =
45  dynamic_cast<mitk::BoolProperty *>(this->GetProperty("SingleLineMode").GetPointer());
46 
47  if (singleLineMode != nullptr)
48  {
49  return singleLineMode->GetValue();
50  }
51  return false;
52 }
53 
55 {
56  return this->GetSingleLineMode() == false || (m_SelectedControlPoint >= 0 && m_SelectedControlPoint <= 3);
57 }
58 
60 {
61  if (this->GetSingleLineMode())
62  {
63  // In single line mode --> nothing to reset
64  return false;
65  }
66 
67  switch (m_SelectedControlPoint)
68  {
69  default:
70  // Nothing selected --> nothing to reset
71  return false;
72 
73  case 0:
74  {
75  // Control point 0 selected: exchange points 0 and 1
76  const Point2D tmpPoint = this->GetControlPoint(0);
77  this->SetControlPoint(0, this->GetControlPoint(1));
78  this->SetControlPoint(1, tmpPoint);
79  // FALLS THROUGH!
80  }
81 
82  case 1:
83  {
84  // Control point 0 or 1 selected: reset number of control points to two
85  this->ResetNumberOfControlPoints(2);
86  this->SelectControlPoint(1);
87  return true;
88  }
89 
90  case 2:
91  {
92  // Control point 2 selected: replace point 0 with point 3 and point 1 with point 2
93  this->SetControlPoint(0, this->GetControlPoint(3));
94  this->SetControlPoint(1, this->GetControlPoint(2));
95 
96  // Adjust selected control point, reset number of control points to two
97  this->ResetNumberOfControlPoints(2);
98  this->SelectControlPoint(1);
99  return true;
100  }
101 
102  case 3:
103  {
104  // Control point 3 selected: replace point 0 with point 2 and point 1 with point 3
105 
106  this->SetControlPoint(0, this->GetControlPoint(2));
107  this->SetControlPoint(1, this->GetControlPoint(3));
108 
109  // Adjust selected control point, reset number of control points to two
110  this->ResetNumberOfControlPoints(2);
111  this->SelectControlPoint(1);
112  return true;
113  }
114  }
115 }
116 
118 {
119  if (this->GetSingleLineMode() || (this->GetNumberOfControlPoints() < 4))
120  {
121  return 1;
122  }
123  else
124  {
125  return 2;
126  }
127 }
128 
130 {
131  // Apply spatial constraints from superclass and from this class until the resulting constrained
132  // point converges. Although not an optimal implementation, this iterative approach
133  // helps to respect both constraints from the superclass and from this class. Without this,
134  // situations may occur where control points are constrained by the superclass, but again
135  // moved out of the superclass bounds by the subclass, or vice versa.
136 
137  unsigned int count = 0; // ensures stop of approach if point does not converge in reasonable time
138  Point2D confinedPoint = point;
139  Point2D superclassConfinedPoint;
140  do
141  {
142  superclassConfinedPoint = Superclass::ApplyControlPointConstraints(index, confinedPoint);
143  confinedPoint = this->InternalApplyControlPointConstraints(index, superclassConfinedPoint);
144  ++count;
145  } while ((confinedPoint.EuclideanDistanceTo(superclassConfinedPoint) > mitk::eps) && (count < 32));
146 
147  return confinedPoint;
148 }
149 
150 mitk::Point2D mitk::PlanarCross::InternalApplyControlPointConstraints(unsigned int index, const Point2D &point)
151 {
152  // Apply constraints depending on current interaction state
153  switch (index)
154  {
155  case 2:
156  {
157  // Check if 3rd control point is outside of the range (2D area) defined by the first
158  // line (via the first two control points); if it is outside, clip it to the bounds
159  const Point2D p1 = this->GetControlPoint(0);
160  const Point2D p2 = this->GetControlPoint(1);
161 
162  Vector2D n1 = p2 - p1;
163  n1.Normalize();
164 
165  const Vector2D v1 = point - p1;
166  const double dotProduct = n1 * v1;
167  const Point2D crossPoint = p1 + n1 * dotProduct;
168  ;
169  const Vector2D crossVector = point - crossPoint;
170 
171  if (dotProduct < 0.0)
172  {
173  // Out-of-bounds on the left: clip point to left boundary
174  return (p1 + crossVector);
175  }
176  else if (dotProduct > p2.EuclideanDistanceTo(p1))
177  {
178  // Out-of-bounds on the right: clip point to right boundary
179  return (p2 + crossVector);
180  }
181  else
182  {
183  // Pass back original point
184  return point;
185  }
186  }
187 
188  case 3:
189  {
190  // Constrain 4th control point so that with the 3rd control point it forms
191  // a line orthogonal to the first line (constraint 1); the 4th control point
192  // must lie on the opposite side of the line defined by the first two control
193  // points than the 3rd control point (constraint 2)
194  const Point2D p1 = this->GetControlPoint(0);
195  const Point2D p2 = this->GetControlPoint(1);
196  const Point2D p3 = this->GetControlPoint(2);
197 
198  // Calculate distance of original point from orthogonal line the corrected
199  // point should lie on to project the point onto this line
200  Vector2D n1 = p2 - p1;
201  n1.Normalize();
202 
203  const Vector2D v1 = point - p3;
204  const double dotProduct1 = n1 * v1;
205 
206  const Point2D pointOnLine = point - n1 * dotProduct1;
207 
208  // Project new point onto line [p1, p2]
209  const Vector2D v2 = pointOnLine - p1;
210  double dotProduct2 = n1 * v2;
211 
212  const Point2D crossingPoint = p1 + n1 * dotProduct2;
213 
214  // Determine whether the projected point on the line, or the crossing point should be
215  // used (according to the second constraint in the comment above)
216  if ((pointOnLine.SquaredEuclideanDistanceTo(p3) > crossingPoint.SquaredEuclideanDistanceTo(p3)) &&
217  (pointOnLine.SquaredEuclideanDistanceTo(p3) > pointOnLine.SquaredEuclideanDistanceTo(crossingPoint)))
218  {
219  return pointOnLine;
220  }
221  else
222  {
223  return crossingPoint;
224  }
225  }
226 
227  default:
228  return point;
229  }
230 }
231 
233 {
234  this->SetNumberOfPolyLines(1);
235  this->ClearPolyLines();
236 
237  if (this->GetNumberOfControlPoints() > 2)
238  this->SetNumberOfPolyLines(2);
239 
240  for (unsigned int i = 0; i < this->GetNumberOfControlPoints(); ++i)
241  {
242  if (i < 2)
243  this->AppendPointToPolyLine(0, this->GetControlPoint(i));
244 
245  if (i > 1)
246  this->AppendPointToPolyLine(1, this->GetControlPoint(i));
247  }
248 }
249 
250 void mitk::PlanarCross::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/)
251 {
252  // Generate helper polyline (orientation line orthogonal to first line)
253  // if the third control point is currently being set
254  if (this->GetNumberOfControlPoints() != 3)
255  {
256  m_HelperPolyLinesToBePainted->SetElement(0, false);
257  return;
258  }
259 
260  m_HelperPolyLinesToBePainted->SetElement(0, true);
261 
262  this->ClearHelperPolyLines();
263 
264  // Calculate cross point of first line (p1 to p2) and orthogonal line through
265  // the third control point (p3)
266  const Point2D p1 = this->GetControlPoint(0);
267  const Point2D p2 = this->GetControlPoint(1);
268  const Point2D p3 = this->GetControlPoint(2);
269 
270  Vector2D n1 = p2 - p1;
271  n1.Normalize();
272 
273  const Vector2D v1 = p3 - p1;
274  const Point2D crossPoint = p1 + n1 * (n1 * v1);
275 
276  const Vector2D v2 = crossPoint - p3;
277  if (v2.GetNorm() < 1.0)
278  {
279  // If third point is on the first line, draw orthogonal "infinite" line
280  // through cross point on line
281  Vector2D v0;
282  v0[0] = n1[1];
283  v0[1] = -n1[0];
284  this->AppendPointToHelperPolyLine(0, Point2D(p3 - v0 * 10000.0));
285  this->AppendPointToHelperPolyLine(0, Point2D(p3 + v0 * 10000.0));
286  }
287  else
288  {
289  // Else, draw orthogonal line starting from third point and crossing the
290  // first line, open-ended only on the other side
291  this->AppendPointToHelperPolyLine(0, p3);
292  this->AppendPointToHelperPolyLine(0, Point2D(p3 + v2 * 10000.0));
293  }
294 }
295 
297 {
298  // Calculate length of first line
299  const Point3D &p0 = this->GetWorldControlPoint(0);
300  const Point3D &p1 = this->GetWorldControlPoint(1);
301  double l1 = p0.EuclideanDistanceTo(p1);
302 
303  // Calculate length of second line
304  double l2 = 0.0;
305  if (!this->GetSingleLineMode() && (this->GetNumberOfControlPoints() > 3))
306  {
307  const Point3D &p2 = this->GetWorldControlPoint(2);
308  const Point3D &p3 = this->GetWorldControlPoint(3);
309  l2 = p2.EuclideanDistanceTo(p3);
310  }
311 
312  double longestDiameter;
313  double shortAxisDiameter;
314  if (l1 > l2)
315  {
316  longestDiameter = l1;
317  shortAxisDiameter = l2;
318  }
319  else
320  {
321  longestDiameter = l2;
322  shortAxisDiameter = l1;
323  }
324 
325  this->SetQuantity(FEATURE_ID_LONGESTDIAMETER, longestDiameter);
326  this->SetQuantity(FEATURE_ID_SHORTAXISDIAMETER, shortAxisDiameter);
327 }
328 
329 void mitk::PlanarCross::PrintSelf(std::ostream &os, itk::Indent indent) const
330 {
331  Superclass::PrintSelf(os, indent);
332 }
333 
335 {
336  const mitk::PlanarCross *otherCross = dynamic_cast<const mitk::PlanarCross *>(&other);
337  if (otherCross)
338  {
339  return Superclass::Equals(other);
340  }
341  else
342  {
343  return false;
344  }
345 }
virtual bool Equals(const mitk::PlanarFigure &other) const override
Compare two PlanarFigure objects Note: all subclasses have to implement the method on their own...
Point< ScalarType, 2 > Point2D
Definition: mitkPoint.h:98
virtual Point2D ApplyControlPointConstraints(unsigned int index, const Point2D &point) override
Spatially constrain control points of second (orthogonal) line.
virtual unsigned int GetNumberOfFeatures() const override
Returns the number of features available for this PlanarCross (1 or 2).
virtual void PrintSelf(std::ostream &os, itk::Indent indent) const override
void ResetNumberOfControlPoints(int numberOfControlPoints)
Set the initial number of control points of the planar figure.
bool GetSingleLineMode() const
Indicates whether the PlanarFigure shall represent only a single line instead of an orthogonal cross...
static Pointer New()
void SetProperty(const char *propertyKey, BaseProperty *property)
Vector< ScalarType, 2 > Vector2D
Definition: mitkVector.h:133
virtual bool ResetOnPointSelect() override
The cross shall be reset to a single line when a control point is selected.
void SetNumberOfHelperPolyLines(unsigned int numberOfHelperPolyLines)
defines the number of HelperPolyLines that will be available
void SetSingleLineMode(bool singleLineMode)
Indicates whether the PlanarFigure shall represent only a single line instead of an orthogonal cross...
virtual bool ResetOnPointSelectNeeded() const override
Implementation of PlanarFigure modeling a cross with two orthogonal lines on a plane.
Base-class for geometric planar (2D) figures, such as lines, circles, rectangles, polygons...
BoolContainerType::Pointer m_HelperPolyLinesToBePainted
virtual void GeneratePolyLine() override
Generates the poly-line representation of the planar figure.
MITKCORE_EXPORT const ScalarType eps
virtual void GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight) override
Generates the poly-lines that should be drawn the same size regardless of zoom.
virtual void EvaluateFeaturesInternal() override
Calculates feature quantities of the planar figure.
virtual T GetValue() const