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