Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkPlanarPolygon.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 "mitkPlanarPolygon.h"
18 #include "mitkPlaneGeometry.h"
19 #include "mitkProperties.h"
20 
21 // stl related includes
22 #include <algorithm>
23 
25  : FEATURE_ID_CIRCUMFERENCE(this->AddFeature("Circumference", "mm")), FEATURE_ID_AREA(this->AddFeature("Area", "mm2"))
26 {
27  // Polygon has at least two control points
29  this->SetNumberOfPolyLines(1);
30 
31  // Polygon is closed by default
32  this->SetProperty("closed", mitk::BoolProperty::New(true));
33  this->SetProperty("subdivision", mitk::BoolProperty::New(false));
34 }
35 
37 {
38  this->SetProperty("closed", mitk::BoolProperty::New(closed));
39 
40  if (!closed)
41  {
42  // For non-closed polygons: use "Length" as feature name; disable area
43  this->SetFeatureName(FEATURE_ID_CIRCUMFERENCE, "Length");
44  this->DeactivateFeature(FEATURE_ID_AREA);
45  }
46  else
47  {
48  // For closed polygons: use "Circumference" as feature name; enable area
49  this->SetFeatureName(FEATURE_ID_CIRCUMFERENCE, "Circumference");
50  this->ActivateFeature(FEATURE_ID_AREA);
51  }
52 
53  this->Modified();
54 }
55 
57 {
58  this->ClearPolyLines();
59 
60  for (ControlPointListType::size_type i = 0; i < m_ControlPoints.size(); ++i)
61  this->AppendPointToPolyLine(0, this->GetControlPoint(i));
62 }
63 
64 void mitk::PlanarPolygon::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/)
65 {
66  // A polygon does not require helper objects
67 }
68 
70 {
71  // Calculate circumference
72  double circumference = 0.0;
73  unsigned int i, j;
74 
75  const PolyLineType polyLine = m_PolyLines[0];
76 
77  if (polyLine.empty())
78  return;
79 
80  for (i = 0; i < (polyLine.size() - 1); ++i)
81  {
82  circumference += static_cast<Point2D>(polyLine[i]).EuclideanDistanceTo(static_cast<Point2D>(polyLine[i + 1]));
83  }
84 
85  if (this->IsClosed())
86  {
87  circumference += static_cast<Point2D>(polyLine[i]).EuclideanDistanceTo(static_cast<Point2D>(polyLine.front()));
88  }
89 
90  this->SetQuantity(FEATURE_ID_CIRCUMFERENCE, circumference);
91 
92  // Calculate polygon area (if closed)
93  double area = 0.0;
94  bool intersection = false;
95 
96  if (this->IsClosed() && (this->GetPlaneGeometry() != nullptr))
97  {
98  // does PlanarPolygon overlap/intersect itself?
99  const unsigned int numberOfPoints = polyLine.size();
100  if (numberOfPoints >= 4)
101  {
102  for (i = 0; i < (numberOfPoints - 1); ++i)
103  {
104  // line 1
105  const Point2D p0 = polyLine[i];
106  const Point2D p1 = polyLine[i + 1];
107 
108  // check for intersection with all other lines
109  for (j = i + 1; j < (numberOfPoints - 1); ++j)
110  {
111  const Point2D p2 = polyLine[j];
112  const Point2D p3 = polyLine[j + 1];
113  intersection = CheckForLineIntersection(p0, p1, p2, p3);
114  if (intersection)
115  break;
116  }
117  if (intersection)
118  break; // only because the inner loop might have changed "intersection"
119 
120  // last line from p_x to p_0
121  const Point2D p2 = polyLine.front();
122  const Point2D p3 = polyLine.back();
123 
124  intersection = CheckForLineIntersection(p0, p1, p2, p3);
125  if (intersection)
126  break;
127  }
128  }
129 
130  // calculate area
131  for (i = 0; i < polyLine.size(); ++i)
132  {
133  const Point2D p0 = polyLine[i];
134  const Point2D p1 = polyLine[(i + 1) % polyLine.size()];
135 
136  area += p0[0] * p1[1] - p1[0] * p0[1];
137  }
138  area /= 2.0;
139  }
140 
141  // set area if appropiate (i.e. closed and not intersected)
142  if (this->IsClosed() && !intersection)
143  {
144  SetQuantity(FEATURE_ID_AREA, fabs(area));
145  this->ActivateFeature(FEATURE_ID_AREA);
146  }
147  else
148  {
149  SetQuantity(FEATURE_ID_AREA, 0);
150  this->DeactivateFeature(FEATURE_ID_AREA);
151  }
152 }
153 
154 void mitk::PlanarPolygon::PrintSelf(std::ostream &os, itk::Indent indent) const
155 {
156  Superclass::PrintSelf(os, indent);
157 
158  if (this->IsClosed())
159  os << indent << "Polygon is closed\n";
160  else
161  os << indent << "Polygon is not closed\n";
162 }
163 
164 // based on
165 // http://flassari.is/2008/11/line-line-intersection-in-cplusplus/
167  const mitk::Point2D &p2,
168  const mitk::Point2D &p3,
169  const mitk::Point2D &p4,
170  Point2D &intersection) const
171 {
172  // do not check for intersections with control points
173  if (p1 == p2 || p1 == p3 || p1 == p4 || p2 == p3 || p2 == p4 || p3 == p4)
174  return false;
175 
176  // Store the values for fast access and easy
177  // equations-to-code conversion
178  const double x1 = p1[0], x2 = p2[0], x3 = p3[0], x4 = p4[0];
179  const double y1 = p1[1], y2 = p2[1], y3 = p3[1], y4 = p4[1];
180 
181  const double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
182  // If d is zero, there is no intersection
183  // if (d < mitk::eps) return false;
184  if (d == 0)
185  return false;
186 
187  // Get the x and y
188  const double pre = (x1 * y2 - y1 * x2);
189  const double post = (x3 * y4 - y3 * x4);
190  const double x = (pre * (x3 - x4) - (x1 - x2) * post) / d;
191  const double y = (pre * (y3 - y4) - (y1 - y2) * post) / d;
192 
193  double tolerance = 0.001;
194  // Check if the x coordinates are within both lines, including tolerance
195  if (x < (std::min(x1, x2) - tolerance) || x > (std::max(x1, x2) + tolerance) || x < (std::min(x3, x4) - tolerance) ||
196  x > (std::max(x3, x4) + tolerance))
197  {
198  return false;
199  }
200 
201  // Check if the y coordinates are within both lines, including tolerance
202  if (y < (std::min(y1, y2) - tolerance) || y > (std::max(y1, y2) + tolerance) || y < (std::min(y3, y4) - tolerance) ||
203  y > (std::max(y3, y4) + tolerance))
204  {
205  return false;
206  }
207 
208  // point of intersection
209  Point2D ret;
210  ret[0] = x;
211  ret[1] = y;
212  intersection = ret;
213  return true;
214 }
215 
217  const mitk::Point2D &p2,
218  const mitk::Point2D &p3,
219  const mitk::Point2D &p4) const
220 {
221  mitk::Point2D intersection;
222  return mitk::PlanarPolygon::CheckForLineIntersection(p1, p2, p3, p4, intersection);
223 }
224 
226  const mitk::Point2D &p2) const
227 {
228  std::vector<mitk::Point2D> intersectionList;
229 
230  ControlPointListType polyLinePoints;
231  const PolyLineType tempList = m_PolyLines[0];
232  for (auto iter = tempList.cbegin(); iter != tempList.cend(); ++iter)
233  {
234  polyLinePoints.push_back(*iter);
235  }
236 
237  for (ControlPointListType::size_type i = 0; i < polyLinePoints.size() - 1; i++)
238  {
239  const mitk::Point2D pnt1 = polyLinePoints[i];
240  const mitk::Point2D pnt2 = polyLinePoints[i + 1];
241  mitk::Point2D intersection;
242 
243  if (mitk::PlanarPolygon::CheckForLineIntersection(p1, p2, pnt1, pnt2, intersection))
244  {
245  intersectionList.push_back(intersection);
246  }
247  }
248 
249  if (this->IsClosed())
250  {
251  mitk::Point2D intersection;
252  const mitk::Point2D lastControlPoint = polyLinePoints.back();
253  const mitk::Point2D firstControlPoint = polyLinePoints.front();
254 
255  if (mitk::PlanarPolygon::CheckForLineIntersection(lastControlPoint, firstControlPoint, p1, p2, intersection))
256  {
257  intersectionList.push_back(intersection);
258  }
259  }
260 
261  return intersectionList;
262 }
263 
265 {
266  const mitk::PlanarPolygon *otherPolygon = dynamic_cast<const mitk::PlanarPolygon *>(&other);
267  if (otherPolygon)
268  {
269  return Superclass::Equals(other);
270  }
271  else
272  {
273  return false;
274  }
275 }
virtual void GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight) override
Generates the poly-lines that should be drawn the same size regardless of zoom.
virtual bool Equals(const mitk::PlanarFigure &other) const override
Compare two PlanarFigure objects Note: all subclasses have to implement the method on their own...
virtual void SetClosed(bool closed)
Set whether the polygon should be closed between first and last control point or not.
virtual void PrintSelf(std::ostream &os, itk::Indent indent) const override
virtual void GeneratePolyLine() override
Generates the poly-line representation of the planar figure.
void ResetNumberOfControlPoints(int numberOfControlPoints)
Set the initial number of control points of the planar figure.
Implementation of PlanarFigure representing a polygon with two or more control points.
static Pointer New()
std::deque< Point2D > ControlPointListType
void SetProperty(const char *propertyKey, BaseProperty *property)
static T max(T x, T y)
Definition: svm.cpp:70
static T min(T x, T y)
Definition: svm.cpp:67
void SetNumberOfPolyLines(unsigned int numberOfPolyLines)
defines the number of PolyLines that will be available
Base-class for geometric planar (2D) figures, such as lines, circles, rectangles, polygons...
std::vector< PolyLineElement > PolyLineType
virtual void EvaluateFeaturesInternal() override
Calculates feature quantities of the planar figure.
std::vector< mitk::Point2D > CheckForLineIntersection(const Point2D &p1, const Point2D &p2) const