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