1 /*
2  * Medical Image Registration ToolKit (MIRTK)
3  *
4  * Copyright 2016 Imperial College London
5  * Copyright 2016 Andreas Schuh
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 
20 #ifndef MIRTK_BoundarySegment_H
21 #define MIRTK_BoundarySegment_H
22 
23 #include "mirtk/Object.h"
24 #include "mirtk/Array.h"
25 #include "mirtk/Vector.h"
26 #include "mirtk/Point.h"
27 #include "mirtk/UnorderedMap.h"
28 
29 #include "vtkSmartPointer.h"
30 #include "vtkPolyData.h"
31 
32 
33 namespace mirtk {
34 
35 
36 /**
37  * Closed boundary segment of surface mesh
38  *
39  * A boundary segment is a closed line strip defined by an ordered list of point
40  * indices with an edge between each consecutive pair of points. The boundary
41  * loop is closed by the edge connecting the last point with the first point.
42  * A surface mesh may have any number of boundary segments, including no boundary
43  * at all in case of a closed genus-0 surface mesh which is topologically
44  * equivalent to a sphere. This class represents a single boundary segment.
45  */
46 class BoundarySegment : public Object
47 {
48   mirtkObjectMacro(BoundarySegment);
49 
50   // ---------------------------------------------------------------------------
51   // Types
52 
53   /// Type of surface point ID to boundary segment point index map
54   typedef UnorderedMap<int, int> PointIdToIndexMap;
55 
56   // ---------------------------------------------------------------------------
57   // Attributes
58 
59   /// Surface mesh which this boundary segment belongs to
60   mirtkReadOnlyAttributeMacro(vtkSmartPointer<vtkPolyData>, Surface);
61 
62   /// IDs of boundary segment points
63   mirtkReadOnlyAttributeMacro(Array<int>, PointIds);
64 
65   /// Pre-computed map from surface point ID to boundary segment point index
66   mirtkAttributeMacro(PointIdToIndexMap, Index);
67 
68   /// Indices of selected points
69   mirtkAttributeMacro(Array<int>, Selection);
70 
71   /// Pre-computed boundary segment edge lengths
72   Vector _EdgeLengths;
73 
74   /// Length of boundary segment
75   double _Length;
76 
77   /// Copy attributes of this class from another instance
78   void CopyAttributes(const BoundarySegment &);
79 
80   // ---------------------------------------------------------------------------
81   // Construction/Destruction
82 
83 public:
84 
85   /// Default constructor
86   BoundarySegment();
87 
88   /// Constructor
89   ///
90   /// \param[in] surface Surface mesh.
91   /// \param[in] ptIds   Ordered list of IDs of surface points making up this
92   ///                    boundary segment.
93   BoundarySegment(vtkPolyData *surface, const Array<int> &ptIds);
94 
95   /// Copy constructor
96   BoundarySegment(const BoundarySegment &);
97 
98   /// Assignment operator
99   BoundarySegment &operator =(const BoundarySegment &);
100 
101   /// Destructor
102   virtual ~BoundarySegment();
103 
104   // ---------------------------------------------------------------------------
105   // Initialization
106 
107   /// Pre-compute map of surface point ID to boundary segment point index
108   void InitializeIndex();
109 
110   // Pre-compute edge lengths and total length
111   void InitializeLengths();
112 
113   /// Compute edge lengths
114   Vector ComputeEdgeLengths() const;
115 
116   /// Compute length of boundary segment
117   double ComputeLength() const;
118 
119   // ---------------------------------------------------------------------------
120   // Boundary points
121 
122   /// Number of boundary segment points
123   int NumberOfPoints() const;
124 
125   /// Get boundary segment point index in range [0, N)
126   int IndexModuloNumberOfPoints(int i) const;
127 
128   /// Get surface point ID of i-th boundary segment point
129   ///
130   /// \param[in] i Index of boundary segment point.
131   ///
132   /// \returns Surface point ID of i-th boundary segment point.
133   int PointId(int i) const;
134 
135   /// Get boundary segment point coordinates
136   ///
137   /// \param[in]  i Index of boundary segment point.
138   /// \param[out] p Coordinates of i-th boundary segment point.
139   void GetPoint(int i, double p[3]) const;
140 
141   /// Set boundary segment point coordinates
142   ///
143   /// \param[in] i Index of boundary segment point.
144   /// \param[in] p New coordinates of i-th boundary segment point.
145   void SetPoint(int i, const double p[3]);
146 
147   /// Get boundary segment point coordinates
148   ///
149   /// \param[in] i Index of boundary segment point.
150   ///
151   /// \returns Coordinates of i-th boundary segment point.
152   class Point Point(int i) const;
153 
154   /// Set boundary segment point coordinates
155   ///
156   /// \param[in] i Index of boundary segment point.
157   /// \param[in] p New coordinates of i-th boundary segment point.
158   void Point(int i, const class Point &p);
159 
160   /// Find surface point in boundary segment
161   ///
162   /// \param[in] ptId Surface point ID.
163   ///
164   /// \returns Boundary segment point index or -1 if segment does not contain this point.
165   int Find(int ptId) const;
166 
167   /// Find closest boundary point
168   ///
169   /// \param[in] x     Point coordinates.
170   /// \param[in] dist2 Squared distance of closest boundary point.
171   ///
172   /// \returns Index of closest boundary segment point.
173   int FindClosestPoint(const class Point &x, double *dist2 = nullptr) const;
174 
175   /// Whether this boundary segment contains a given surface point
176   ///
177   /// \param[in] ptId Surface point ID.
178   ///
179   /// \returns Whether surface point ID is part of this boundary segment.
180   bool Contains(int ptId) const;
181 
182   // ---------------------------------------------------------------------------
183   // Boundary edges
184 
185   /// Get total length of boundary segment
186   double Length() const;
187 
188   /// Get lengths of boundary segment edges
189   Vector EdgeLengths() const;
190 
191   /// Length of boundary segment edge
192   ///
193   /// \param[in] i  Index of first boundary segment point.
194   /// \param[in] di Index increment +1 or -1, i.e., traversal direction.
195   ///
196   /// \returns Boundary segment edge lengths.
197   double EdgeLength(int i, int di = +1) const;
198 
199   // ---------------------------------------------------------------------------
200   // Selected points
201 
202   /// Reserve space for n selected points
203   ///
204   /// \param[in] n Number of points to be selected.
205   void ReserveSelection(int n);
206 
207   /// Remove the i-th selected point
208   ///
209   /// \param[in] i Selected point index.
210   void RemoveSelection(int i);
211 
212   /// Deselect all points
213   void ClearSelection();
214 
215   /// Add i-th boundary segment point to selection
216   ///
217   /// \param[in] i Boundary segment point index.
218   void SelectPoint(int i);
219 
220   /// Remove i-th boundary segment point from selection
221   ///
222   /// \param[in] i Boundary segment point index.
223   void DeselectPoint(int i);
224 
225   /// Number of selected curve points
226   int NumberOfSelectedPoints() const;
227 
228   /// Get boundary segment point index of i-th selected boundary point
229   ///
230   /// \param[in] i Index of selected point.
231   ///
232   /// \return Index of corresponding boundary segment point.
233   int SelectedPointIndex(int i) const;
234 
235   /// Get surface point ID of i-th selected boundary point
236   ///
237   /// \param[in] i Index of selected point.
238   ///
239   /// \return Index of corresponding surface point.
240   int SelectedPointId(int i) const;
241 
242   /// Get selected boundary segment point coordinates
243   ///
244   /// \param[in]  i Index of selected boundary segment point.
245   /// \param[out] p Coordinates of i-th selected boundary segment point.
246   void GetSelectedPoint(int i, double p[3]) const;
247 
248   /// Get selected boundary segment point coordinates
249   ///
250   /// \param[in] i Index of selected boundary segment point.
251   ///
252   /// \returns Coordinates of i-th selected boundary segment point.
253   class Point SelectedPoint(int i) const;
254 
255   /// Check if boundary segment point is selected
256   ///
257   /// \param[in] i Index of boundary segment point.
258   ///              The index is taken modulo the number of boundary
259   ///              points and negative values count from the end of
260   ///              the list of boundary points.
261   ///
262   /// \return Whether boundary segment point is selected.
263   bool IsSelected(int i) const;
264 
265 };
266 
267 ////////////////////////////////////////////////////////////////////////////////
268 // Inline definitions
269 ////////////////////////////////////////////////////////////////////////////////
270 
271 // =============================================================================
272 // Initialization
273 // =============================================================================
274 
275 // -----------------------------------------------------------------------------
ComputeLength()276 inline double BoundarySegment::ComputeLength() const
277 {
278   if (_EdgeLengths) return _EdgeLengths.Sum();
279   return ComputeEdgeLengths().Sum();
280 }
281 
282 // =============================================================================
283 // Boundary points
284 // =============================================================================
285 
286 // -----------------------------------------------------------------------------
NumberOfPoints()287 inline int BoundarySegment::NumberOfPoints() const
288 {
289   return static_cast<int>(_PointIds.size());
290 }
291 
292 // -----------------------------------------------------------------------------
IndexModuloNumberOfPoints(int i)293 inline int BoundarySegment::IndexModuloNumberOfPoints(int i) const
294 {
295   const int n = NumberOfPoints();
296   if      (i <  0) i = (i + 1) % n + n - 1;
297   else if (i >= n) i =  i      % n;
298   return i;
299 }
300 
301 // -----------------------------------------------------------------------------
PointId(int i)302 inline int BoundarySegment::PointId(int i) const
303 {
304   i = IndexModuloNumberOfPoints(i);
305   return _PointIds[i];
306 }
307 
308 // -----------------------------------------------------------------------------
GetPoint(int i,double p[3])309 inline void BoundarySegment::GetPoint(int i, double p[3]) const
310 {
311   i = IndexModuloNumberOfPoints(i);
312   _Surface->GetPoint(static_cast<vtkIdType>(PointId(i)), p);
313 }
314 
315 // -----------------------------------------------------------------------------
SetPoint(int i,const double p[3])316 inline void BoundarySegment::SetPoint(int i, const double p[3])
317 {
318   i = IndexModuloNumberOfPoints(i);
319   _Surface->GetPoints()->SetPoint(static_cast<vtkIdType>(PointId(i)), const_cast<double *>(p));
320 }
321 
322 // -----------------------------------------------------------------------------
Point(int i)323 inline Point BoundarySegment::Point(int i) const
324 {
325   double p[3];
326   GetPoint(i, p);
327   return p;
328 }
329 
330 // -----------------------------------------------------------------------------
Point(int i,const class Point & p)331 inline void BoundarySegment::Point(int i, const class Point &p)
332 {
333   SetPoint(i, p);
334 }
335 
336 // -----------------------------------------------------------------------------
Contains(int ptId)337 inline bool BoundarySegment::Contains(int ptId) const
338 {
339   return Find(ptId) != -1;
340 }
341 
342 // =============================================================================
343 // Boundary edges
344 // =============================================================================
345 
346 // -----------------------------------------------------------------------------
Length()347 inline double BoundarySegment::Length() const
348 {
349   if (_Length > 0.) return _Length;
350   return ComputeLength();
351 }
352 
353 // -----------------------------------------------------------------------------
EdgeLengths()354 inline Vector BoundarySegment::EdgeLengths() const
355 {
356   if (_EdgeLengths) return _EdgeLengths;
357   return ComputeEdgeLengths();
358 }
359 
360 // -----------------------------------------------------------------------------
EdgeLength(int i,int di)361 inline double BoundarySegment::EdgeLength(int i, int di) const
362 {
363   if (_EdgeLengths) {
364     return _EdgeLengths(IndexModuloNumberOfPoints(di < 0 ? i-1 : i));
365   } else {
366     return Point(i).Distance(Point(i + di));
367   }
368 }
369 
370 // =============================================================================
371 // Selected points
372 // =============================================================================
373 
374 // -----------------------------------------------------------------------------
NumberOfSelectedPoints()375 inline int BoundarySegment::NumberOfSelectedPoints() const
376 {
377   return static_cast<int>(_Selection.size());
378 }
379 
380 // -----------------------------------------------------------------------------
SelectedPointIndex(int i)381 inline int BoundarySegment::SelectedPointIndex(int i) const
382 {
383   return _Selection[i];
384 }
385 
386 // -----------------------------------------------------------------------------
SelectedPointId(int i)387 inline int BoundarySegment::SelectedPointId(int i) const
388 {
389   return _PointIds[_Selection[i]];
390 }
391 
392 // -----------------------------------------------------------------------------
GetSelectedPoint(int i,double p[3])393 inline void BoundarySegment::GetSelectedPoint(int i, double p[3]) const
394 {
395   GetPoint(_Selection[i], p);
396 }
397 
398 // -----------------------------------------------------------------------------
SelectedPoint(int i)399 inline Point BoundarySegment::SelectedPoint(int i) const
400 {
401   return Point(_Selection[i]);
402 }
403 
404 // -----------------------------------------------------------------------------
IsSelected(int i)405 inline bool BoundarySegment::IsSelected(int i) const
406 {
407   for (auto it = _Selection.begin(); it != _Selection.end(); ++it) {
408     if (*it == i) return true;
409   }
410   return false;
411 }
412 
413 
414 } // namespace mirtk
415 
416 #endif // MIRTK_BoundarySegment_H
417