Medical Imaging Interaction Toolkit  2018.4.99-b20efe7f
Medical Imaging Interaction Toolkit
mitkPlanarSubdivisionPolygon.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 
14 #include "mitkPlaneGeometry.h"
15 #include "mitkProperties.h"
16 
17 // stl related includes
18 #include <algorithm>
19 
20 mitk::PlanarSubdivisionPolygon::PlanarSubdivisionPolygon() : m_TensionParameter(0.0625), m_SubdivisionRounds(5)
21 {
22  // Polygon is subdivision (in contrast to parent class PlanarPolygon
23  this->SetProperty("closed", mitk::BoolProperty::New(true));
24  this->SetProperty("subdivision", mitk::BoolProperty::New(true));
25 
26  // Other properties are inherited / already initialized by parent class PlanarPolygon
27 }
28 
30 {
31  this->ClearPolyLines();
32  ControlPointListType subdivisionPoints;
33  ControlPointListType newSubdivisionPoints;
34  subdivisionPoints.clear();
35  subdivisionPoints = m_ControlPoints;
36 
38  {
39  for (unsigned int i = 0; i < GetSubdivisionRounds(); i++)
40  {
41  // Indices
42  unsigned int index, indexPrev, indexNext, indexNextNext;
43 
44  const unsigned int numberOfPoints = subdivisionPoints.size();
45 
46  Point2D newPoint;
47 
48  // Keep cycling our array indices forward until they wrap around at the end
49  for (index = 0; index < numberOfPoints; ++index)
50  {
51  // Create new subdivision point according to formula
52  // p_new = (0.5 + tension) * (p_here + p_next) - tension * (p_prev + p_nextnext)
53  indexPrev = (numberOfPoints + index - 1) % numberOfPoints;
54  indexNext = (index + 1) % numberOfPoints;
55  indexNextNext = (index + 2) % numberOfPoints;
56 
57  newPoint[0] =
58  (0.5 + GetTensionParameter()) * (double)(subdivisionPoints[index][0] + subdivisionPoints[indexNext][0]) -
59  GetTensionParameter() * (double)(subdivisionPoints[indexPrev][0] + subdivisionPoints[indexNextNext][0]);
60  newPoint[1] =
61  (0.5 + GetTensionParameter()) * (double)(subdivisionPoints[index][1] + subdivisionPoints[indexNext][1]) -
62  GetTensionParameter() * (double)(subdivisionPoints[indexPrev][1] + subdivisionPoints[indexNextNext][1]);
63 
64  newSubdivisionPoints.push_back(newPoint);
65  }
66 
67  ControlPointListType mergedSubdivisionPoints;
68  ControlPointListType::const_iterator it, itNew;
69 
70  for (it = subdivisionPoints.cbegin(), itNew = newSubdivisionPoints.cbegin(); it != subdivisionPoints.cend();
71  ++it, ++itNew)
72  {
73  mergedSubdivisionPoints.push_back(*it);
74  mergedSubdivisionPoints.push_back(*itNew);
75  }
76 
77  subdivisionPoints = mergedSubdivisionPoints;
78 
79  newSubdivisionPoints.clear();
80  }
81  }
82 
83  const bool isInitiallyPlaced = this->GetProperty("initiallyplaced");
84 
85  unsigned int i;
86  ControlPointListType::const_iterator it;
87  for (it = subdivisionPoints.cbegin(), i = 0; it != subdivisionPoints.cend(); ++it, ++i)
88  {
89  // Determine the index of the control point FOLLOWING this poly-line element
90  // (this is needed by PlanarFigureInteractor to insert new points at the correct position,
91  // namely BEFORE the next control point)
92  unsigned int nextIndex;
93  if (i == 0)
94  {
95  // For the FIRST polyline point, use the index of the LAST control point
96  // (it will used to check if the mouse is near the very last polyline element)
97  nextIndex = m_ControlPoints.size() - 1;
98  }
99  else
100  {
101  // For all other polyline points, use the index of the control point succeeding it
102  // (for polyline points lying on control points, the index of the previous control point
103  // is used)
104  nextIndex = (((i - 1) >> this->GetSubdivisionRounds()) + 1) % m_ControlPoints.size();
105  if (!isInitiallyPlaced && nextIndex > m_ControlPoints.size() - 2)
106  {
108  break;
109  }
110  }
111 
112  this->AppendPointToPolyLine(0, *it);
113  }
114  subdivisionPoints.clear();
115 }
116 
118 {
119  const auto *otherSubDivPoly = dynamic_cast<const mitk::PlanarSubdivisionPolygon *>(&other);
120  if (otherSubDivPoly)
121  {
122  if (this->m_SubdivisionRounds != otherSubDivPoly->m_SubdivisionRounds)
123  return false;
124  if (std::abs(this->m_TensionParameter - otherSubDivPoly->m_TensionParameter) > mitk::eps)
125  return false;
126  return Superclass::Equals(other);
127  }
128  else
129  {
130  return false;
131  }
132 }
133 
134 int mitk::PlanarSubdivisionPolygon::GetControlPointForPolylinePoint(int indexOfPolylinePoint, int polyLineIndex) const
135 {
136  const mitk::PlanarFigure::PolyLineType polyLine = GetPolyLine(polyLineIndex);
137 
138  if (indexOfPolylinePoint < 0 || indexOfPolylinePoint > static_cast<int>(polyLine.size()))
139  return -1;
140 
141  mitk::PlanarFigure::ControlPointListType::const_iterator elem;
142  auto first = m_ControlPoints.cbegin();
143  auto end = m_ControlPoints.cend();
144 
145  mitk::PlanarFigure::PolyLineType::const_iterator polyLineIter;
146  auto polyLineEnd = polyLine.cend();
147  auto polyLineStart = polyLine.cbegin();
148  polyLineStart += indexOfPolylinePoint;
149 
150  for (polyLineIter = polyLineStart; polyLineIter != polyLineEnd; ++polyLineIter)
151  {
152  elem = std::find(first, end, *polyLineIter);
153 
154  if (elem != end)
155  return std::distance(first, elem);
156  }
157 
158  return GetNumberOfControlPoints();
159 }
bool Equals(const mitk::PlanarFigure &other) const override
Compare two PlanarFigure objects Note: all subclasses have to implement the method on their own...
unsigned int GetSubdivisionRounds() const
How many times should we generate a round of subdivisions?
unsigned int GetMinimumNumberOfControlPoints() const override
Subdivision Polygon has 3 control points per definition.
void GeneratePolyLine() override
Generates the poly-line representation of the planar figure.
const PolyLineType GetPolyLine(unsigned int index)
Returns the polyline representing the planar figure (for rendering, measurements, etc...
void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName="", bool fallBackOnDefaultContext=false) override
Add new or change existent property.
unsigned int GetNumberOfControlPoints() const
Returns the current number of 2D control points defining this figure.
static Pointer New()
std::deque< Point2D > ControlPointListType
Implementation of PlanarFigure representing a polygon with two or more control points.
ControlPointListType m_ControlPoints
int GetControlPointForPolylinePoint(int indexOfPolylinePoint, int polyLineIndex) const override
Returns the id of the control-point that corresponds to the given polyline-point. ...
float GetTensionParameter() const
Parameter w_tension defines the tension. the higher w_tension, the lower the "tension" on points...
void AppendPointToPolyLine(unsigned int index, PolyLineElement element)
Append a point to the PolyLine # index.
mitk::BaseProperty::Pointer GetProperty(const char *propertyKey) const
Get the property (instance of BaseProperty) with key propertyKey from the PropertyList, and set it to this, respectively;.
Base-class for geometric planar (2D) figures, such as lines, circles, rectangles, polygons...
MITKCORE_EXPORT const ScalarType eps
std::vector< PolyLineElement > PolyLineType
void ClearPolyLines()
clears the list of PolyLines. Call before re-calculating a new Polyline.