PointSetUtils.h
1 /*
2  * Medical Image Registration ToolKit (MIRTK)
3  *
4  * Copyright 2013-2016 Imperial College London
5  * Copyright 2013-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_PointSetUtils_H
21 #define MIRTK_PointSetUtils_H
22 
23 #include "mirtk/UnorderedSet.h"
24 #include "mirtk/Array.h"
25 #include "mirtk/List.h"
26 #include "mirtk/Pair.h"
27 
28 #include "mirtk/Math.h"
29 #include "mirtk/Vtk.h"
30 #include "mirtk/VtkMath.h"
31 
32 class vtkDataSet;
33 class vtkDataSetAttributes;
34 class vtkPointSet;
35 class vtkPolyData;
36 class vtkImageData;
37 class vtkImageStencilData;
38 class vtkDataArray;
39 class vtkCell;
40 
41 
42 namespace mirtk {
43 
44 
45 struct ImageAttributes;
46 class BaseImage;
47 class PointSet;
48 class EdgeTable;
49 class Vector;
50 
51 template <typename> struct Vector3D;
52 
53 /// List of pairs of edge end point IDs
54 typedef List<Pair<int, int>> EdgeList;
55 
56 // =============================================================================
57 // VTK / MIRTK type conversion
58 // =============================================================================
59 
60 /// Add points of vtkPointSet to mirtk::PointSet
61 ///
62 /// \param[out] oset Point set to which the vtkPointSet points are added.
63 /// \param[in] iset VTK point set.
64 void AddPoints(PointSet &oset, vtkPointSet *iset);
65 
66 // =============================================================================
67 // Point set domain
68 // =============================================================================
69 
70 /// Determine dimension of data set
71 int Dimension(vtkDataSet *);
72 
73 /// Determine bounding box of point set
74 ///
75 /// \param[in] data Data set.
76 /// \param[in] dx Desired lattice spacing along x axis.
77 /// If non-positive, the average interval between the x
78 /// coordinates of the input points is used.
79 /// \param[in] dy Desired lattice spacing along y axis.
80 /// If non-positive, the average interval between the y
81 /// coordinates of the input points is used.
82 /// \param[in] dz Desired lattice spacing along z axis.
83 /// If non-positive, the average interval between the z
84 /// coordinates of the input points is used.
85 ///
86 /// \returns Attributes of oriented minimum-volume bounding box.
87 ImageAttributes PointSetDomain(vtkPointSet *data, double dx = -1, double dy = -1, double dz = -1);
88 
89 /// Determine bounding box of point set
90 ///
91 /// \param[in] data Data set.
92 /// \param[in] ds Desired lattice spacing.
93 /// If an entry is non-positive, the average interval between
94 /// the coordinates of the input points along this axis is used.
95 ///
96 /// \returns Attributes of oriented minimum-volume bounding box.
97 ImageAttributes PointSetDomain(vtkPointSet *data, const Vector3D<double> &ds);
98 
99 /// Determine bounding box of polydata points
100 ///
101 /// \param[in] data Data set.
102 /// \param[in] dx Desired lattice spacing along x axis.
103 /// If non-positive, the average interval between the x
104 /// coordinates of the input points is used.
105 /// \param[in] dy Desired lattice spacing along y axis.
106 /// If non-positive, the average interval between the y
107 /// coordinates of the input points is used.
108 /// \param[in] dz Desired lattice spacing along z axis.
109 /// If non-positive, the average interval between the z
110 /// coordinates of the input points is used.
111 ///
112 /// \returns Attributes of oriented minimum-volume bounding box.
113 ImageAttributes PolyDataDomain(vtkPolyData *data, double dx = -1, double dy = -1, double dz = -1);
114 
115 /// Determine bounding box of polydata points
116 ///
117 /// \param[in] data Data set.
118 /// \param[in] ds Desired lattice spacing.
119 /// If an entry is non-positive, the average interval between
120 /// the coordinates of the input points along this axis is used.
121 ///
122 /// \returns Attributes of oriented minimum-volume bounding box.
123 ImageAttributes PolyDataDomain(vtkPolyData *data, const Vector3D<double> &ds);
124 
125 // =============================================================================
126 // Point/cell data
127 // =============================================================================
128 
129 /// Map string to vtkDataSetAttributes::AttributeTypes enumeration value
130 ///
131 /// @param type Case insensitive attribute type name (e.g. "scalars", "NORMALS")
132 ///
133 /// @return VTK data set attribute type ID (e.g., vtkDataSetAttributes::SCALARS)
134 /// or -1 if string does not name a known attribute type.
135 int PolyDataAttributeType(const char *type);
136 
137 /// Get point data array using case insensitive name
138 ///
139 /// @param[in] data Point or cell data attributes (cf. vtkPolyData::GetPointData, vtkPolyData::GetCellData).
140 /// @param[in] name Case insenitive name of data array.
141 /// @param[out] loc Set to array index if not @c NULL.
142 ///
143 /// @return Pointer to data array or @c NULL if not found.
144 vtkDataArray *GetArrayByCaseInsensitiveName(vtkDataSetAttributes *data, const char *name, int *loc = NULL);
145 
146 /// Copy named data array from one dataset attributes to another
147 ///
148 /// If an array with the given name exists in the destination dataset attributes,
149 /// it's data is overridden using the vtkDataSetAttributes::DeepCopy function.
150 /// Otherwise, a new array is added.
151 ///
152 /// @param dst Destination dataset attributes.
153 /// @param src Source dataset attributes.
154 /// @param name Case insensitive name of data array.
155 ///
156 /// @return Index of array in destination or -1 if source array not found.
157 int DeepCopyArrayUsingCaseInsensitiveName(vtkDataSetAttributes *dst,
158  vtkDataSetAttributes *src,
159  const char *name);
160 
161 /// Determine whether data array name suggests it contains categorical values
162 ///
163 /// \param[in] name Data array name.
164 bool IsCategoricalArrayName(const string &name);
165 
166 // =============================================================================
167 // Cell attributes
168 // =============================================================================
169 
170 /// Compute area of cell
171 ///
172 /// @return Area of cell or NaN if cell type is not supported.
173 double ComputeArea(vtkCell *cell);
174 
175 /// Compute volume of cell
176 ///
177 /// @return Volume of cell or NaN if cell type is not supported.
178 double ComputeVolume(vtkCell *cell);
179 
180 /// Compute orthogonal vectors spanning the tangent plane of a cell
181 ///
182 /// @param[in] n Cell normal vector.
183 /// @param[out] e1 First normal vector in tangent plane.
184 /// @param[out] e2 Second normal vector in tangent plane.
185 ///
186 /// @returns Whether tangent vectors are valid.
187 inline bool ComputeTangents(const double n[3], double e1[3], double e2[3])
188 {
189  double v[3] = {n[1], n[2], n[0]};
190  vtkMath::Cross(n, v, e1);
191  if (vtkMath::Dot(e1, e1) < 1e-6) {
192  v[1] *= -1.0;
193  vtkMath::Cross(n, v, e1);
194  if (vtkMath::Dot(e1, e1) < 1e-6) return false;
195  }
196  vtkMath::Cross(n, e1, e2);
197  vtkMath::Normalize(e1);
198  vtkMath::Normalize(e2);
199  return true;
200 }
201 
202 // -----------------------------------------------------------------------------
203 /// Compute 4 (8) equally spaced tangent vectors (angular sampling of 45 degrees)
204 inline bool ComputeTangents(const double n[3], double e1[3], double e2[3], double e3[3], double e4[3])
205 {
206  if (!ComputeTangents(n, e1, e3)) return false;
207  const double sqrt2 = sqrt(2.0);
208  e2[0] = (e1[0] + e3[0]) / sqrt2;
209  e2[1] = (e1[1] + e3[1]) / sqrt2;
210  e2[2] = (e1[2] + e3[2]) / sqrt2;
211  e4[0] = (e1[0] - e3[0]) / sqrt2;
212  e4[1] = (e1[1] - e3[1]) / sqrt2;
213  e4[2] = (e1[2] - e3[2]) / sqrt2;
214  return true;
215 }
216 
217 // =============================================================================
218 // Basic point set manipulation
219 // =============================================================================
220 
221 /// Get boundary surface mesh
222 ///
223 /// @param[in] dataset Dataset whose boundary surface is extracted.
224 /// @param[in] passPtIds Whether to pass point array with IDs of points in @p dataset.
225 /// @param[in] passCellIds Whether to pass cell array with IDs of cells in @p dataset.
226 ///
227 /// @return Boundary surface mesh.
228 vtkSmartPointer<vtkPolyData> DataSetSurface(vtkSmartPointer<vtkDataSet> dataset,
229  bool passPtIds = false,
230  bool passCellIds = false);
231 
232 /// Translate point set such that center is at origin
233 void Center(vtkSmartPointer<vtkPointSet> pointset);
234 
235 /// Scale point set around center
236 void Scale(vtkSmartPointer<vtkPointSet> pointset, double);
237 
238 // =============================================================================
239 // Surface meshes
240 // =============================================================================
241 
242 /// Calculate are of triangle with given edge length
243 inline double EdgeLengthToTriangleArea(double l)
244 {
245  return 0.25 * sqrt(3.0) * l * l;
246 }
247 
248 /// Determine whether a point set is a surface mesh
249 bool IsSurfaceMesh(vtkDataSet *);
250 
251 /// Check whether given point set is a triangular mesh
252 bool IsTriangularMesh(vtkDataSet *);
253 
254 /// Check whether given point set is a tetrahedral mesh
255 bool IsTetrahedralMesh(vtkDataSet *);
256 
257 /// Get IDs of end points of boundary edges
258 UnorderedSet<int> BoundaryPoints(vtkDataSet *, const EdgeTable * = nullptr);
259 
260 /// Get list of all boundary edges
261 EdgeList BoundaryEdges(vtkDataSet *);
262 
263 /// Get list of all boundary edges
264 EdgeList BoundaryEdges(vtkDataSet *, const EdgeTable &);
265 
266 /// Get list of edges with given end point
267 ///
268 /// \param[in] edges List of edges.
269 /// \param[in] ptId Edge end point.
270 ///
271 /// \returns List of edges, where ptId is always the first entry.
272 EdgeList GetPointEdges(const EdgeList &edges, int ptId);
273 
274 /// Get list of edges with given end point and remove them from input list
275 ///
276 /// \param[in] edges List of edges.
277 /// \param[in] ptId Edge end point.
278 ///
279 /// \returns List of edges, where ptId is always the first entry.
280 EdgeList PopPointEdges(EdgeList &edges, int ptId);
281 
282 /// Get connected boundary segments as (closed) line strips
283 Array<Array<int> > BoundarySegments(vtkDataSet *, const EdgeTable * = nullptr);
284 
285 /// Number of points
286 int NumberOfPoints(vtkDataSet *);
287 
288 /// Number of edges
289 int NumberOfEdges(vtkDataSet *, const EdgeTable * = nullptr);
290 
291 /// Number of faces
292 int NumberOfFaces(vtkDataSet *);
293 
294 /// Number of empty/deleted cells
295 int NumberOfEmptyCells(vtkDataSet *);
296 
297 /// Number of connected components
298 int NumberOfConnectedComponents(vtkDataSet *);
299 
300 /// Number of connected boundary components
301 int NumberOfBoundarySegments(vtkDataSet *, const EdgeTable * = nullptr);
302 
303 /// Euler characeteristic, i.e., V - E + F
304 int EulerCharacteristic(vtkDataSet *dataset,
305  const EdgeTable &,
306  int *npoints = nullptr,
307  int *nedges = nullptr,
308  int *nfaces = nullptr);
309 
310 /// Euler characeteristic, i.e., V - E + F
311 int EulerCharacteristic(vtkDataSet *dataset,
312  int *npoints = nullptr,
313  int *nedges = nullptr,
314  int *nfaces = nullptr);
315 
316 /// Genus of surface mesh
317 double Genus(vtkDataSet *dataset,
318  const EdgeTable &,
319  int *npoints = nullptr,
320  int *nedges = nullptr,
321  int *nfaces = nullptr,
322  int *nbounds = nullptr,
323  int *ncomps = nullptr,
324  int *euler = nullptr);
325 
326 /// Genus of surface mesh
327 double Genus(vtkDataSet *dataset,
328  int *npoints = nullptr,
329  int *nedges = nullptr,
330  int *nfaces = nullptr,
331  int *nbounds = nullptr,
332  int *ncomps = nullptr,
333  int *euler = nullptr);
334 
335 /// Area of surface mesh
336 double Area(vtkPolyData *, bool per_cell = false);
337 
338 /// Area of surface mesh
339 inline double Area(vtkSmartPointer<vtkPolyData> surface, bool per_cell = false)
340 {
341  return Area(surface.GetPointer(), per_cell);
342 }
343 
344 
345 /// Area of point set surface
346 double Area(vtkSmartPointer<vtkPointSet>);
347 
348 /// Compute edge lengths of point set given a precomputed edge table
349 Vector EdgeLengths(vtkSmartPointer<vtkPoints>, const EdgeTable &);
350 
351 /// Compute squared edge lengths of point set given a precomputed edge table
352 Vector SquaredEdgeLengths(vtkSmartPointer<vtkPoints>, const EdgeTable &);
353 
354 /// Determine average edge length of point set given a precomputed edge table
355 double AverageEdgeLength(vtkSmartPointer<vtkPoints>, const EdgeTable &);
356 
357 /// Determine average edge length of point set
358 double AverageEdgeLength(vtkSmartPointer<vtkPointSet>);
359 
360 /// Determine median edge length of point set given a precomputed edge table
361 double MedianEdgeLength(vtkSmartPointer<vtkPoints>, const EdgeTable &);
362 
363 /// Determine median edge length of point set
364 double MedianEdgeLength(vtkSmartPointer<vtkPointSet>);
365 
366 /// Determine average edge length of point set given a precomputed edge table
367 ///
368 /// This function only considers edges with a length within in the 5th and 95th
369 /// percentile of all edge lengths. It thus ignores extrem short/long edges.
370 double RobustAverageEdgeLength(vtkSmartPointer<vtkPoints>, const EdgeTable &);
371 
372 /// Determine average edge length of point set
373 double RobustAverageEdgeLength(vtkSmartPointer<vtkPointSet>);
374 
375 /// Determine minimum and maximum edge length
376 ///
377 /// \param[in] points Points.
378 /// \param[in] edgeTable Edge table.
379 /// \param[out] min Minimum edge length.
380 /// \param[out] max Maximum edge length.
381 void GetMinMaxEdgeLength(vtkSmartPointer<vtkPoints> points, const EdgeTable &edgeTable, double &min, double &max);
382 
383 /// Determine minimum and maximum edge length
384 ///
385 /// \param[in] pointset Point set.
386 /// \param[out] min Minimum edge length.
387 /// \param[out] max Maximum edge length.
388 void GetMinMaxEdgeLength(vtkSmartPointer<vtkPointSet> pointset, double &min, double &max);
389 
390 /// Determine minimum edge length
391 ///
392 /// \param[in] points Points.
393 /// \param[in] edgeTable Edge table.
394 ///
395 /// \return Minimum edge length.
396 double MinEdgeLength(vtkSmartPointer<vtkPoints> points, const EdgeTable &edgeTable);
397 
398 /// Determine minimum edge length
399 ///
400 /// \param[in] pointset Point set.
401 ///
402 /// \return Minimum edge length.
403 double MinEdgeLength(vtkSmartPointer<vtkPointSet> pointset);
404 
405 /// Determine maximum edge length
406 ///
407 /// \param[in] points Points.
408 /// \param[in] edgeTable Edge table.
409 ///
410 /// \return Maximum edge length.
411 double MaxEdgeLength(vtkSmartPointer<vtkPoints> points, const EdgeTable &edgeTable);
412 
413 /// Determine maximum edge length
414 ///
415 /// \param[in] pointset Point set.
416 ///
417 /// \return Maximum edge length.
418 double MaxEdgeLength(vtkSmartPointer<vtkPointSet> pointset);
419 
420 /// Compute statistics of edge lengths
421 ///
422 /// \param[in] points Points.
423 /// \param[in] edgeTable Edge table.
424 /// \param[out] mean Average edge length.
425 /// \param[out] sigma Standard deviation of edge length.
426 void EdgeLengthNormalDistribution(vtkSmartPointer<vtkPoints> points, const EdgeTable &edgeTable, double &mean, double &sigma);
427 
428 /// Compute statistics of edge lengths
429 ///
430 /// \param[in] pointset Point set.
431 /// \param[out] mean Average edge length.
432 /// \param[out] sigma Standard deviation of edge length.
433 void EdgeLengthNormalDistribution(vtkSmartPointer<vtkPointSet> pointset, double &mean, double &sigma);
434 
435 /// Get approximate volume enclosed by polygonal mesh
436 double Volume(vtkSmartPointer<vtkPolyData>);
437 
438 /// Get convex hull of point set
439 ///
440 /// @param[in] pointset Input point set.
441 /// @param[in] levels Parameter of vtkHull::AddRecursiveSpherePlanes.
442 ///
443 /// @return Convex hull of input point set.
444 vtkSmartPointer<vtkPolyData> ConvexHull(vtkSmartPointer<vtkPointSet> pointset, int levels = 3);
445 
446 /// Triangulate surface mesh
447 vtkSmartPointer<vtkPolyData> Triangulate(vtkSmartPointer<vtkPolyData>);
448 
449 /// Tetrahedralize the interior of a piecewise linear complex (PLC)
450 vtkSmartPointer<vtkPointSet> Tetrahedralize(vtkSmartPointer<vtkPointSet>);
451 
452 /// Instantiate new VTK image mask
453 ///
454 /// Note that vtkImageData has no implicit orientation. Therefore we just
455 /// convert the image in voxel coordinates (origin at 0 and voxel size 1x1x1)
456 /// and instead convert points to voxel coordinates.
457 vtkSmartPointer<vtkImageData> NewVtkMask(int nx, int ny, int nz);
458 
459 /// Map point set points to voxel coordinates
460 vtkSmartPointer<vtkPointSet> WorldToImage(vtkSmartPointer<vtkPointSet> pointset,
461  const BaseImage *image);
462 
463 /// Get inside surface image stencil
464 vtkSmartPointer<vtkImageStencilData> ImageStencil(vtkSmartPointer<vtkImageData> image,
465  vtkSmartPointer<vtkPointSet> pointset);
466 
467 /// Convert surface image stencil to binary mask
468 ///
469 /// \param[out] image Image data with voxel type IRTK_VOXEL_BINARY.
470 /// \param[in] stencil Image stencil.
471 void ImageStencilToMask(vtkSmartPointer<vtkImageStencilData> stencil,
472  vtkSmartPointer<vtkImageData> image);
473 
474 
475 } // namespace mirtk
476 
477 #endif // MIRTK_PointSetUtils_H
bool ComputeTangents(const double n[3], double e1[3], double e2[3])
EdgeList PopPointEdges(EdgeList &edges, int ptId)
double MinEdgeLength(vtkSmartPointer< vtkPoints > points, const EdgeTable &edgeTable)
Vector SquaredEdgeLengths(vtkSmartPointer< vtkPoints >, const EdgeTable &)
Compute squared edge lengths of point set given a precomputed edge table.
double EdgeLengthToTriangleArea(double l)
Calculate are of triangle with given edge length.
bool IsTetrahedralMesh(vtkDataSet *)
Check whether given point set is a tetrahedral mesh.
double RobustAverageEdgeLength(vtkSmartPointer< vtkPoints >, const EdgeTable &)
UnorderedSet< int > BoundaryPoints(vtkDataSet *, const EdgeTable *=nullptr)
Get IDs of end points of boundary edges.
double ComputeVolume(vtkCell *cell)
void ImageStencilToMask(vtkSmartPointer< vtkImageStencilData > stencil, vtkSmartPointer< vtkImageData > image)
double Genus(vtkDataSet *dataset, const EdgeTable &, int *npoints=nullptr, int *nedges=nullptr, int *nfaces=nullptr, int *nbounds=nullptr, int *ncomps=nullptr, int *euler=nullptr)
Genus of surface mesh.
bool IsCategoricalArrayName(const string &name)
vtkSmartPointer< vtkPolyData > DataSetSurface(vtkSmartPointer< vtkDataSet > dataset, bool passPtIds=false, bool passCellIds=false)
int NumberOfEmptyCells(vtkDataSet *)
Number of empty/deleted cells.
int NumberOfEdges(vtkDataSet *, const EdgeTable *=nullptr)
Number of edges.
vtkSmartPointer< vtkImageData > NewVtkMask(int nx, int ny, int nz)
List< Pair< int, int > > EdgeList
List of pairs of edge end point IDs.
Definition: PointSetUtils.h:51
void Scale(vtkSmartPointer< vtkPointSet > pointset, double)
Scale point set around center.
double Volume(vtkSmartPointer< vtkPolyData >)
Get approximate volume enclosed by polygonal mesh.
vtkSmartPointer< vtkPointSet > Tetrahedralize(vtkSmartPointer< vtkPointSet >)
Tetrahedralize the interior of a piecewise linear complex (PLC)
int EulerCharacteristic(vtkDataSet *dataset, const EdgeTable &, int *npoints=nullptr, int *nedges=nullptr, int *nfaces=nullptr)
Euler characeteristic, i.e., V - E + F.
vtkSmartPointer< vtkImageStencilData > ImageStencil(vtkSmartPointer< vtkImageData > image, vtkSmartPointer< vtkPointSet > pointset)
Get inside surface image stencil.
Array< Array< int > > BoundarySegments(vtkDataSet *, const EdgeTable *=nullptr)
Get connected boundary segments as (closed) line strips.
vtkSmartPointer< vtkPolyData > Triangulate(vtkSmartPointer< vtkPolyData >)
Triangulate surface mesh.
double ComputeArea(vtkCell *cell)
Definition: IOConfig.h:41
ImageAttributes PolyDataDomain(vtkPolyData *data, double dx=-1, double dy=-1, double dz=-1)
int Dimension(vtkDataSet *)
Determine dimension of data set.
void EdgeLengthNormalDistribution(vtkSmartPointer< vtkPoints > points, const EdgeTable &edgeTable, double &mean, double &sigma)
int DeepCopyArrayUsingCaseInsensitiveName(vtkDataSetAttributes *dst, vtkDataSetAttributes *src, const char *name)
vtkSmartPointer< vtkPolyData > ConvexHull(vtkSmartPointer< vtkPointSet > pointset, int levels=3)
EdgeList BoundaryEdges(vtkDataSet *)
Get list of all boundary edges.
void Center(vtkSmartPointer< vtkPointSet > pointset)
Translate point set such that center is at origin.
vtkDataArray * GetArrayByCaseInsensitiveName(vtkDataSetAttributes *data, const char *name, int *loc=NULL)
Vector EdgeLengths(vtkSmartPointer< vtkPoints >, const EdgeTable &)
Compute edge lengths of point set given a precomputed edge table.
double MaxEdgeLength(vtkSmartPointer< vtkPoints > points, const EdgeTable &edgeTable)
int NumberOfFaces(vtkDataSet *)
Number of faces.
vtkSmartPointer< vtkPointSet > WorldToImage(vtkSmartPointer< vtkPointSet > pointset, const BaseImage *image)
Map point set points to voxel coordinates.
double MedianEdgeLength(vtkSmartPointer< vtkPoints >, const EdgeTable &)
Determine median edge length of point set given a precomputed edge table.
double Area(vtkPolyData *, bool per_cell=false)
Area of surface mesh.
int NumberOfConnectedComponents(vtkDataSet *)
Number of connected components.
void AddPoints(PointSet &oset, vtkPointSet *iset)
void GetMinMaxEdgeLength(vtkSmartPointer< vtkPoints > points, const EdgeTable &edgeTable, double &min, double &max)
EdgeList GetPointEdges(const EdgeList &edges, int ptId)
int NumberOfPoints(vtkDataSet *)
Number of points.
bool IsSurfaceMesh(vtkDataSet *)
Determine whether a point set is a surface mesh.
int PolyDataAttributeType(const char *type)
bool IsTriangularMesh(vtkDataSet *)
Check whether given point set is a triangular mesh.
ImageAttributes PointSetDomain(vtkPointSet *data, double dx=-1, double dy=-1, double dz=-1)
double AverageEdgeLength(vtkSmartPointer< vtkPoints >, const EdgeTable &)
Determine average edge length of point set given a precomputed edge table.
int NumberOfBoundarySegments(vtkDataSet *, const EdgeTable *=nullptr)
Number of connected boundary components.