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