Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkPlanarEllipse.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 "mitkPlanarEllipse.h"
18 #include "mitkPlaneGeometry.h"
19 #include "mitkProperties.h"
20 
21 #include <algorithm>
22 
23 #define _USE_MATH_DEFINES
24 #include <math.h>
25 
27  : FEATURE_ID_MAJOR_AXIS(Superclass::AddFeature("Major Axis", "mm")),
28  FEATURE_ID_MINOR_AXIS(Superclass::AddFeature("Minor Axis", "mm")),
29  FEATURE_ID_AREA(Superclass::AddFeature("Area", "mm2")),
30  m_MinRadius(0),
31  m_MaxRadius(100),
32  m_MinMaxRadiusContraintsActive(false),
33  m_TreatAsCircle(true)
34 {
35  // Ellipse has three control points
37  this->SetNumberOfPolyLines(2);
38  this->SetProperty("closed", mitk::BoolProperty::New(true));
39 }
40 
41 bool mitk::PlanarEllipse::SetControlPoint(unsigned int index, const Point2D &point, bool createIfDoesNotExist)
42 {
43  if (index == 0) // moving center point and control points accordingly
44  {
45  const Point2D &centerPoint = GetControlPoint(0);
46  Point2D boundaryPoint1 = GetControlPoint(1);
47  Point2D boundaryPoint2 = GetControlPoint(2);
48  Point2D boundaryPoint3 = GetControlPoint(3);
49  const vnl_vector<ScalarType> vec = (point.GetVnlVector() - centerPoint.GetVnlVector());
50 
51  boundaryPoint1[0] += vec[0];
52  boundaryPoint1[1] += vec[1];
53  boundaryPoint2[0] += vec[0];
54  boundaryPoint2[1] += vec[1];
55  boundaryPoint3[0] += vec[0];
56  boundaryPoint3[1] += vec[1];
57  PlanarFigure::SetControlPoint(0, point, createIfDoesNotExist);
58  PlanarFigure::SetControlPoint(1, boundaryPoint1, createIfDoesNotExist);
59  PlanarFigure::SetControlPoint(2, boundaryPoint2, createIfDoesNotExist);
60  PlanarFigure::SetControlPoint(3, boundaryPoint3, createIfDoesNotExist);
61  return true;
62  }
63  else if (index < 3)
64  {
65  PlanarFigure::SetControlPoint(index, point, createIfDoesNotExist);
66  int otherIndex = index + 1;
67  if (otherIndex > 2)
68  otherIndex = 1;
69 
70  const Point2D &centerPoint = GetControlPoint(0);
71  Point2D otherPoint = GetControlPoint(otherIndex);
72  Point2D point3 = GetControlPoint(3);
73 
74  const Vector2D vec1 = point - centerPoint;
75  Vector2D vec2;
76 
77  if (index == 1 && m_TreatAsCircle)
78  {
79  const float x = vec1[0];
80  vec2[0] = vec1[1];
81  vec2[1] = x;
82 
83  if (index == 1)
84  vec2[0] *= -1;
85  else
86  vec2[1] *= -1;
87 
88  otherPoint = centerPoint + vec2;
89  PlanarFigure::SetControlPoint(otherIndex, otherPoint, createIfDoesNotExist);
90  const float r = centerPoint.EuclideanDistanceTo(otherPoint);
91 
92  // adjust additional third control point
93  const Point2D p3 = this->GetControlPoint(3);
94  Vector2D vec3;
95  vec3[0] = p3[0] - centerPoint[0];
96  vec3[1] = p3[1] - centerPoint[1];
97  if (vec3[0] != 0 || vec3[1] != 0)
98  {
99  vec3.Normalize();
100  vec3 *= r;
101  }
102  else
103  {
104  vec3[0] = r;
105  vec3[1] = 0;
106  }
107  point3 = centerPoint + vec3;
108  PlanarFigure::SetControlPoint(3, point3, createIfDoesNotExist);
109  }
110  else if (vec1.GetNorm() > 0)
111  {
112  const float r = centerPoint.EuclideanDistanceTo(otherPoint);
113  const float x = vec1[0];
114  vec2[0] = vec1[1];
115  vec2[1] = x;
116 
117  if (index == 1)
118  vec2[0] *= -1;
119  else
120  vec2[1] *= -1;
121 
122  vec2.Normalize();
123  vec2 *= r;
124 
125  if (vec2.GetNorm() > 0)
126  {
127  otherPoint = centerPoint + vec2;
128  PlanarFigure::SetControlPoint(otherIndex, otherPoint, createIfDoesNotExist);
129  }
130 
131  // adjust third control point
132  Vector2D vec3 = point3 - centerPoint;
133  vec3.Normalize();
134  const double r1 = centerPoint.EuclideanDistanceTo(GetControlPoint(1));
135  const double r2 = centerPoint.EuclideanDistanceTo(GetControlPoint(2));
136  const Point2D newPoint = centerPoint + vec3 * std::max(r1, r2);
137  PlanarFigure::SetControlPoint(3, newPoint, createIfDoesNotExist);
138 
139  m_TreatAsCircle = false;
140  }
141  return true;
142  }
143  else if (index == 3)
144  {
145  const Point2D centerPoint = GetControlPoint(0);
146  Vector2D vec3 = point - centerPoint;
147  vec3.Normalize();
148  const double r1 = centerPoint.EuclideanDistanceTo(GetControlPoint(1));
149  const double r2 = centerPoint.EuclideanDistanceTo(GetControlPoint(2));
150  const Point2D newPoint = centerPoint + vec3 * std::max(r1, r2);
151  PlanarFigure::SetControlPoint(index, newPoint, createIfDoesNotExist);
152  m_TreatAsCircle = false;
153  return true;
154  }
155  return false;
156 }
157 
159 {
161  m_SelectedControlPoint = 1;
162 }
163 
165 {
166  return point;
167 
168  Point2D indexPoint;
169  this->GetPlaneGeometry()->WorldToIndex(point, indexPoint);
170 
171  BoundingBox::BoundsArrayType bounds = this->GetPlaneGeometry()->GetBounds();
172  if (indexPoint[0] < bounds[0])
173  {
174  indexPoint[0] = bounds[0];
175  }
176  if (indexPoint[0] > bounds[1])
177  {
178  indexPoint[0] = bounds[1];
179  }
180  if (indexPoint[1] < bounds[2])
181  {
182  indexPoint[1] = bounds[2];
183  }
184  if (indexPoint[1] > bounds[3])
185  {
186  indexPoint[1] = bounds[3];
187  }
188 
189  Point2D constrainedPoint;
190  this->GetPlaneGeometry()->IndexToWorld(indexPoint, constrainedPoint);
191 
192  if (m_MinMaxRadiusContraintsActive)
193  {
194  if (index != 0)
195  {
196  const Point2D &centerPoint = this->GetControlPoint(0);
197  const double euclideanDinstanceFromCenterToPoint1 = centerPoint.EuclideanDistanceTo(point);
198 
199  Vector2D vectorProjectedPoint = point - centerPoint;
200  vectorProjectedPoint.Normalize();
201 
202  if (euclideanDinstanceFromCenterToPoint1 > m_MaxRadius)
203  {
204  vectorProjectedPoint *= m_MaxRadius;
205  constrainedPoint = centerPoint;
206  constrainedPoint += vectorProjectedPoint;
207  }
208  else if (euclideanDinstanceFromCenterToPoint1 < m_MinRadius)
209  {
210  vectorProjectedPoint *= m_MinRadius;
211  constrainedPoint = centerPoint;
212  constrainedPoint += vectorProjectedPoint;
213  }
214  }
215  }
216 
217  return constrainedPoint;
218 }
219 
221 {
222  // clear the PolyLine-Contrainer, it will be reconstructed soon enough...
223  this->ClearPolyLines();
224 
225  const Point2D &centerPoint = GetControlPoint(0);
226  const Point2D &boundaryPoint1 = GetControlPoint(1);
227  const Point2D &boundaryPoint2 = GetControlPoint(2);
228 
229  Vector2D dir = boundaryPoint1 - centerPoint;
230  dir.Normalize();
231  vnl_matrix_fixed<float, 2, 2> rot;
232 
233  // differentiate between clockwise and counterclockwise rotation
234  int start = 0;
235  int end = 64;
236  if (dir[1] < 0)
237  {
238  dir[0] = -dir[0];
239  start = -32;
240  end = 32;
241  }
242  // construct rotation matrix to align ellipse with control point vector
243  rot[0][0] = dir[0];
244  rot[1][1] = rot[0][0];
245  rot[1][0] = sin(acos(rot[0][0]));
246  rot[0][1] = -rot[1][0];
247 
248  const double radius1 = centerPoint.EuclideanDistanceTo(boundaryPoint1);
249  const double radius2 = centerPoint.EuclideanDistanceTo(boundaryPoint2);
250 
251  // Generate poly-line with 64 segments
252  for (int t = start; t < end; ++t)
253  {
254  const double alpha = (double)t * vnl_math::pi / 32.0;
255 
256  // construct the new polyline point ...
257  vnl_vector_fixed<float, 2> vec;
258  vec[0] = radius1 * cos(alpha);
259  vec[1] = radius2 * sin(alpha);
260  vec = rot * vec;
261 
262  Point2D polyLinePoint;
263  polyLinePoint[0] = centerPoint[0] + vec[0];
264  polyLinePoint[1] = centerPoint[1] + vec[1];
265 
266  // ... and append it to the PolyLine.
267  // No extending supported here, so we can set the index of the PolyLineElement to '0'
268  this->AppendPointToPolyLine(0, polyLinePoint);
269  }
270 
271  this->AppendPointToPolyLine(1, centerPoint);
272  this->AppendPointToPolyLine(1, this->GetControlPoint(3));
273 }
274 
275 void mitk::PlanarEllipse::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/)
276 {
277  // A circle does not require a helper object
278 }
279 
281 {
282  const Point2D centerPoint = this->GetControlPoint(0);
283 
284  const auto longAxisLength = centerPoint.EuclideanDistanceTo(this->GetControlPoint(1));
285  const auto shortAxisLength = centerPoint.EuclideanDistanceTo(this->GetControlPoint(2));
286 
287  this->SetQuantity(FEATURE_ID_MAJOR_AXIS, 2 * longAxisLength);
288  this->SetQuantity(FEATURE_ID_MINOR_AXIS, 2 * shortAxisLength);
289  this->SetQuantity(FEATURE_ID_AREA, longAxisLength * shortAxisLength * M_PI);
290 }
291 
292 void mitk::PlanarEllipse::PrintSelf(std::ostream &os, itk::Indent indent) const
293 {
294  Superclass::PrintSelf(os, indent);
295 }
296 
298 {
299  const mitk::PlanarEllipse *otherEllipse = dynamic_cast<const mitk::PlanarEllipse *>(&other);
300  if (otherEllipse)
301  {
302  if (this->m_TreatAsCircle != otherEllipse->m_TreatAsCircle)
303  return false;
304 
305  return Superclass::Equals(other);
306  }
307  else
308  {
309  return false;
310  }
311 }
bool SetControlPoint(unsigned int index, const Point2D &point, bool createIfDoesNotExist=true) override
Implementation of PlanarFigure representing a circle through two control points.
virtual void PlaceFigure(const Point2D &point) override
Place figure in its minimal configuration (a point at least) onto the given 2D geometry.
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.
itk::DataObject Superclass
Definition: mitkBaseData.h:42
virtual bool Equals(const mitk::PlanarFigure &other) const override
Compare two PlanarFigure objects Note: all subclasses have to implement the method on their own...
static Pointer New()
void SetProperty(const char *propertyKey, BaseProperty *property)
virtual void EvaluateFeaturesInternal() override
Calculates feature quantities of the planar figure.
virtual void PlaceFigure(const Point2D &point)
Place figure at the given point (in 2D index coordinates) onto the given 2D geometry.
static T max(T x, T y)
Definition: svm.cpp:70
virtual void GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight) override
Generates the poly-lines that should be drawn the same size regardless of zoom.
void SetNumberOfPolyLines(unsigned int numberOfPolyLines)
defines the number of PolyLines that will be available
virtual bool SetControlPoint(unsigned int index, const Point2D &point, bool createIfDoesNotExist=false)
Base-class for geometric planar (2D) figures, such as lines, circles, rectangles, polygons...
virtual Point2D ApplyControlPointConstraints(unsigned int index, const Point2D &point) override
Spatially constrain control points of second (orthogonal) line.
virtual void GeneratePolyLine() override
Generates the poly-line representation of the planar figure.
BoundingBoxType::BoundsArrayType BoundsArrayType