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 #include "mirtk/Common.h"
21 #include "mirtk/Options.h"
22
23 #include "mirtk/PointSetIO.h"
24 #include "mirtk/PointSetUtils.h"
25 #include "mirtk/GenericImage.h"
26 #include "mirtk/Stripper.h"
27 #include "mirtk/SurfaceBoundary.h"
28 #include "mirtk/ConnectedComponents.h"
29 #include "mirtk/SurfacePatches.h"
30 #include "mirtk/SurfaceRemeshing.h"
31 #include "mirtk/MeshSmoothing.h"
32 #include "mirtk/Matrix3x3.h"
33 #include "mirtk/Vector3.h"
34 #include "mirtk/Triangle.h"
35
36 #include "mirtk/Vtk.h"
37 #include "vtkSmartPointer.h"
38 #include "vtkPolyData.h"
39 #include "vtkPointData.h"
40 #include "vtkCellData.h"
41 #include "vtkCleanPolyData.h"
42 #include "vtkAppendPolyData.h"
43 #include "vtkImageData.h"
44 #include "vtkImageStencilData.h"
45 #include "vtkIdList.h"
46 #include "vtkCellArray.h"
47 #include "vtkPolygon.h"
48 #include "vtkPolyDataNormals.h"
49 #include "vtkCellDataToPointData.h"
50 #include "vtkPointLocator.h"
51 #include "vtkCellLocator.h"
52 #include "vtkPlaneSource.h"
53 #include "vtkIntersectionPolyDataFilter.h"
54 #include "vtkGenericCell.h"
55 #include "vtkMergePoints.h"
56 #include "vtkPolyDataConnectivityFilter.h"
57 #include "vtkLine.h"
58 #include "vtkDelaunay2D.h"
59 #include "vtkMatrix4x4.h"
60 #include "vtkMatrixToLinearTransform.h"
61
62 using namespace mirtk;
63
64
65 // =============================================================================
66 // Help
67 // =============================================================================
68
69 // -----------------------------------------------------------------------------
PrintHelp(const char * name)70 void PrintHelp(const char *name)
71 {
72 cout << "\n";
73 cout << "Usage: " << name << " [options]\n";
74 cout << " " << name << " [<input>...] <output> [options]\n";
75 cout << "\n";
76 cout << "Description:\n";
77 cout << " Merge surface meshes at segmentation label boundaries. The input\n";
78 cout << " surfaces must follow closely the boundary of a segment in the given\n";
79 cout << " image segmentation. Two surfaces are then merged at the longest\n";
80 cout << " common intersection boundary. When the two surfaces share no such\n";
81 cout << " segmentation boundary, the surfaces are not connected.\n";
82 cout << "\n";
83 cout << "Arguments:\n";
84 cout << " input File name of input surface mesh.\n";
85 cout << " output File name of output surface mesh.\n";
86 cout << "\n";
87 cout << "Options:\n";
88 cout << " -i, -input <file>...\n";
89 cout << " Input surfaces. These are added to the list of input surfaces\n";
90 cout << " to be merged after the <input> arguments.\n";
91 cout << " -o, -output <file>\n";
92 cout << " Output surface file. When this option is given, the <output>\n";
93 cout << " argument is appended to the <input> surfaces before any surface\n";
94 cout << " specified using :option:`-i`. Either this option or an <output>\n";
95 cout << " argument is required.\n";
96 cout << " -labels <file>\n";
97 cout << " Segmentation labels image. Currently required.\n";
98 cout << " -labels-percentage <value>\n";
99 cout << " Percentage of voxels within an input surface that must have the same label\n";
100 cout << " to be considered for determining boundaries between segments.\n";
101 cout << " -source-array <name>\n";
102 cout << " Add point/cell data array with the specified name with one-based\n";
103 cout << " labels corresponding to the input point set from which an output\n";
104 cout << " point/cell originates from. When the first input point set has\n";
105 cout << " a scalar array with the specified name, the labels of this first\n";
106 cout << " surface are preserved, while successive labels are offset by the\n";
107 cout << " maximum integer value of the input data array. This is useful when\n";
108 cout << " successively merging surface meshes instead of with a single execution\n";
109 cout << " of this command. (default: none)\n";
110 cout << " -point-source-array <name>\n";
111 cout << " Name of output point data :option:`-source-array`.\n";
112 cout << " -cell-source-array <name>\n";
113 cout << " Name of output cell data :option:`-source-array`.\n";
114 cout << " -join [on|off], -nojoin\n";
115 cout << " Whether to join surfaces at intersection boundaries.\n";
116 cout << " When off, the output data set has unconnected components.\n";
117 cout << " (default: on)\n";
118 cout << " -largest [on|off]\n";
119 cout << " Retain only largest surface component after :option:`-join` of boundaries. (default: off)\n";
120 cout << " -tolerance, -tol <float>\n";
121 cout << " Maximum distance of an input cell from the segmentation\n";
122 cout << " boundary to be removed. The resulting intersection boundary\n";
123 cout << " edges are joined again after the removal of these cells.\n";
124 cout << " (default: max voxel size)\n";
125 cout << " -smooth-boundaries, -boundary-smoothing [<n>]\n";
126 cout << " Number of intersection boundary edge smoothing iterations.\n";
127 cout << " (default: 3)\n";
128 cout << " -nosmooth-boundaries, -noboundary-smoothing\n";
129 cout << " Disable smoothing of boundaries, same as :option:`-smooth-boundaries` 0.\n";
130 cout << " -remeshing, -remesh [<n>]\n";
131 cout << " Enable remeshing of newly inserted triangles at intersection\n";
132 cout << " boundaries. The default number of remeshing iterations <n> is 3.\n";
133 cout << " A different value can be specified using this option or\n";
134 cout << " :option:`-remeshing-iterations`. (default: 3)\n";
135 cout << " -remeshing-iterations, -remesh-iterations\n";
136 cout << " Number of intersection triangle remeshing iterations. (default: 3)\n";
137 cout << " -noremeshing, -noremesh\n";
138 cout << " Disable remeshing of newly inserted intersection triangles,\n";
139 cout << " same as :option:`-remeshing` or :option:`-remeshing-iterations` 0.\n";
140 cout << " -edge-length <min> [<max>]\n";
141 cout << " Edge length range for remeshing newly inserted triangles at\n";
142 cout << " intersection boundaries. When min and/or max edge length not\n";
143 cout << " specified, the range is chosen based on the mean local edge\n";
144 cout << " length of the input meshes at the intersection boundaries\n";
145 cout << " minus/plus :option:`-edge-length-sigma` times the local\n";
146 cout << " standard deviation of input mesh edge lengths.\n";
147 cout << " -min-edge-length <min>\n";
148 cout << " Minimum edge length for remeshing of newly inserted triangles.\n";
149 cout << " -max-edge-length <min>\n";
150 cout << " Maximum edge length for remeshing of newly inserted triangles.\n";
151 cout << " -edge-length-sigma <scale>\n";
152 cout << " Standard deviation scaling factor of local edge length standard\n";
153 cout << " deviation used to set min/max edge length range for local remeshing\n";
154 cout << " of newly inserted triangles at intersection boundaries. (default: 2)\n";
155 cout << " -smoothing, -smooth [<n>]\n";
156 cout << " Enable smoothing of merged surface points nearby intersection boundaries.\n";
157 cout << " When :option:`-smoothing-mu` is zero or NaN, Gaussian weighting function\n";
158 cout << " is used for the mesh smoothing. Otherwise, a uniform weighting is used. (default: 0)\n";
159 cout << " -smoothing-iterations, -smooth-iterations\n";
160 cout << " Number of intersection points smoothing iterations. (default: 0)\n";
161 cout << " -nosmoothing, -nosmooth\n";
162 cout << " Disable smoothing of nearby intersection points, same as\n";
163 cout << " :option:`-smoothing` or :option:`-smoothing-iterations` 0.\n";
164 cout << " -smoothing-lambda, -smooth-lambda <float>\n";
165 cout << " Lambda parameter used for odd iterations of Laplacian point :option:`-smoothing`.\n";
166 cout << " The default value is set based on the :option:`-smooth-mu` parameter. (default: abs(mu) - .01)\n";
167 cout << " When only :option:`-smoothing-lambda` is given, :option:`-smoothing-mu` is set to\n";
168 cout << " to the same value and a Gaussian weighting function is used.\n";
169 cout << " -smoothing-mu, -smooth-mu <float>\n";
170 cout << " Lambda parameter used for even iterations of Laplacian point :option:`-smoothing`.\n";
171 cout << " The default smoothing parameters avoid shrinking of the surface mesh\n";
172 cout << " and serve mainly to relax any small self-intersections that may have\n";
173 cout << " been introduced while joining the split input surfaces. (default: -.75)\n";
174 cout << " -neighborhood, -neighbourhood, -radius <int>\n";
175 cout << " Edge connectivity radius around intersection border cells/points used\n";
176 cout << " to define the local neighborhood for which to apply the remeshing and\n";
177 cout << " smoothing operations. See also :option:`-remeshing-neighborhood`.\n";
178 cout << " -remeshing-neighborhood, -remeshing-radius <int>\n";
179 cout << " Edge connectivity radius around intersection border cells/points used\n";
180 cout << " to define neighborhood for which to apply the :option:`-remeshing`. (default: 3)\n";
181 cout << " -smoothing-neighborhood, -smoothing-radius <int>\n";
182 cout << " Edge connectivity radius around intersection border cells/points used\n";
183 cout << " to define neighborhood for which to apply the :option:`-smoothing`. (default: 2)\n";
184 cout << " -smooth-source, source-smoothing [<n>]\n";
185 cout << " Number of iterations for which to smooth the :option:`-source-array`\n";
186 cout << " labels to form smoother boundaries between intersection borders.\n";
187 cout << " -dividers [on|off], -nodividers\n";
188 cout << " Enable/disable insertion of segmentation cutting planes at mesh intersections.\n";
189 cout << " The :option:`-source-array` has unique negative labels for each such inserted\n";
190 cout << " divider surface patch which enable the extraction or removal of these\n";
191 cout << " dividers again when a genus-0 surface is required again. (default: off)\n";
192 cout << " -normals [on|off], -nonormals\n";
193 cout << " Enable/disable output of surface point and cell normals. (default: on)\n";
194 cout << " -point-normals [on|off], -nopoint-normals\n";
195 cout << " Enable/disable output of surface point normals. (default: on)\n";
196 cout << " -cell-normals [on|off], -nocell-normals\n";
197 cout << " Enable/disable output of surface cell normals. (default: on)\n";
198 PrintStandardOptions(cout);
199 cout << endl;
200 }
201
202 // =============================================================================
203 // Constants
204 // =============================================================================
205
206 const char * const SOURCE_ARRAY_NAME = "_SourceId";
207 const char * const INTERSECTION_ARRAY_NAME = "_IntersectionMask";
208 const char * const MIN_EDGE_LENGTH_ARRAY_NAME = "_MinEdgeLength";
209 const char * const MAX_EDGE_LENGTH_ARRAY_NAME = "_MaxEdgeLength";
210
211 // =============================================================================
212 // Auxiliaries
213 // =============================================================================
214
215 // -----------------------------------------------------------------------------
216 /// Get binary mask of region enclosed by surface
InsideMask(const ImageAttributes & attr,vtkSmartPointer<vtkPolyData> surface)217 BinaryImage InsideMask(const ImageAttributes &attr, vtkSmartPointer<vtkPolyData> surface)
218 {
219 BinaryImage mask(attr, 1);
220 surface = vtkPolyData::SafeDownCast(WorldToImage(surface, &mask));
221 vtkSmartPointer<vtkImageData> vtkmask = NewVtkMask(mask.X(), mask.Y(), mask.Z());
222 vtkSmartPointer<vtkImageStencilData> stencil = ImageStencil(vtkmask, surface);
223 ImageStencilToMask(stencil, vtkmask);
224 mask.CopyFrom(reinterpret_cast<BinaryPixel *>(vtkmask->GetScalarPointer()));
225 return mask;
226 }
227
228 // -----------------------------------------------------------------------------
229 /// Determine dominant surface label
InsideLabels(const GreyImage & labels,double percentage,vtkPolyData * surface)230 UnorderedSet<int> InsideLabels(const GreyImage &labels, double percentage, vtkPolyData *surface)
231 {
232 OrderedMap<int, int> hist;
233 BinaryImage mask = InsideMask(labels.Attributes(), surface);
234 const int nvox = labels.NumberOfSpatialVoxels();
235 int size = 0;
236 for (int vox = 0; vox < nvox; ++vox) {
237 if (mask(vox) != 0) {
238 ++hist[labels(vox)];
239 ++size;
240 }
241 }
242 UnorderedSet<int> label_set;
243 if (size == 0) return label_set;
244 const int min_count = ifloor(percentage * static_cast<double>(size) / 100.);
245 for (const auto &bin : hist) {
246 if (bin.second > min_count) {
247 label_set.insert(bin.first);
248 }
249 }
250 return label_set;
251 }
252
253 // -----------------------------------------------------------------------------
254 /// Add set of segmentation boundary points
255 vtkSmartPointer<vtkPolyData>
LabelBoundary(const GreyImage & labels,const UnorderedSet<int> & label_set1,const UnorderedSet<int> & label_set2)256 LabelBoundary(const GreyImage &labels, const UnorderedSet<int> &label_set1, const UnorderedSet<int> &label_set2)
257 {
258 const NeighborhoodOffsets offsets(&labels, labels.Z() > 1 ? CONNECTIVITY_6 : CONNECTIVITY_4);
259 const int nvox = labels.NumberOfSpatialVoxels();
260
261 const GreyPixel *l1, *l2;
262 const GreyPixel * const begin = labels.Data();
263 const GreyPixel * const end = labels.Data() + nvox;
264
265 l1 = begin;
266
267 bool is_boundary_voxel;
268 ByteImage boundary_voxels(labels.Attributes());
269 for (int vox = 0; vox < nvox; ++vox, ++l1) {
270 if (label_set1.find(*l1) != label_set1.end()) {
271 is_boundary_voxel = false;
272 for (int n = 0; n < offsets.Size(); ++n) {
273 l2 = l1 + offsets(n);
274 if (begin <= l2 && l2 < end) {
275 if (*l2 != *l1 && label_set2.find(*l2) != label_set2.end()) {
276 is_boundary_voxel = true;
277 break;
278 }
279 }
280 }
281 if (is_boundary_voxel) {
282 boundary_voxels(vox) = 1;
283 }
284 }
285 }
286
287 ConnectedComponents<BytePixel> cc;
288 cc.Input (&boundary_voxels);
289 cc.Output(&boundary_voxels);
290 cc.Run();
291
292 vtkSmartPointer<vtkPoints> points;
293 vtkSmartPointer<vtkCellArray> polys;
294 points = vtkSmartPointer<vtkPoints>::New();
295 polys = vtkSmartPointer<vtkCellArray>::New();
296
297 l1 = begin;
298 double p[3], c[3];
299 vtkIdType ptIds[4];
300 int i1, j1, k1, i2, j2, k2;
301 for (int vox = 0; vox < nvox; ++vox, ++l1) {
302 if (boundary_voxels(vox) == 1) {
303 for (int n = 0; n < offsets.Size(); ++n) {
304 l2 = l1 + offsets(n);
305 if (begin <= l2 && l2 < end) {
306 if (*l2 != *l1 && label_set2.find(*l2) != label_set2.end()) {
307 labels.IndexToVoxel(static_cast<int>(l1 - begin), i1, j1, k1);
308 labels.IndexToVoxel(static_cast<int>(l2 - begin), i2, j2, k2);
309 c[0] = i1, c[1] = j1, c[2] = k1;
310 if (i1 != i2) {
311 p[0] = .5 * (i1 + i2);
312 p[1] = c[1] - .5;
313 p[2] = c[2] - .5;
314 labels.ImageToWorld(p[0], p[1], p[2]);
315 ptIds[0] = points->InsertNextPoint(p);
316 p[0] = .5 * (i1 + i2);
317 p[1] = c[1] - .5;
318 p[2] = c[2] + .5;
319 labels.ImageToWorld(p[0], p[1], p[2]);
320 ptIds[1] = points->InsertNextPoint(p);
321 p[0] = .5 * (i1 + i2);
322 p[1] = c[1] + .5;
323 p[2] = c[2] + .5;
324 labels.ImageToWorld(p[0], p[1], p[2]);
325 ptIds[2] = points->InsertNextPoint(p);
326 p[0] = .5 * (i1 + i2);
327 p[1] = c[1] + .5;
328 p[2] = c[2] - .5;
329 labels.ImageToWorld(p[0], p[1], p[2]);
330 ptIds[3] = points->InsertNextPoint(p);
331 } else if (j1 != j2) {
332 p[0] = c[0] - .5;
333 p[1] = .5 * (j1 + j2);
334 p[2] = c[2] - .5;
335 labels.ImageToWorld(p[0], p[1], p[2]);
336 ptIds[0] = points->InsertNextPoint(p);
337 p[0] = c[0] - .5;
338 p[1] = .5 * (j1 + j2);
339 p[2] = c[2] + .5;
340 labels.ImageToWorld(p[0], p[1], p[2]);
341 ptIds[1] = points->InsertNextPoint(p);
342 p[0] = c[0] + .5;
343 p[1] = .5 * (j1 + j2);
344 p[2] = c[2] + .5;
345 labels.ImageToWorld(p[0], p[1], p[2]);
346 ptIds[2] = points->InsertNextPoint(p);
347 p[0] = c[0] + .5;
348 p[1] = .5 * (j1 + j2);
349 p[2] = c[2] - .5;
350 labels.ImageToWorld(p[0], p[1], p[2]);
351 ptIds[3] = points->InsertNextPoint(p);
352 } else {
353 p[0] = c[0] - .5;
354 p[1] = c[1] - .5;
355 p[2] = .5 * (k1 + k2);
356 labels.ImageToWorld(p[0], p[1], p[2]);
357 ptIds[0] = points->InsertNextPoint(p);
358 p[0] = c[0] - .5;
359 p[1] = c[1] + .5;
360 p[2] = .5 * (k1 + k2);
361 labels.ImageToWorld(p[0], p[1], p[2]);
362 ptIds[1] = points->InsertNextPoint(p);
363 p[0] = c[0] + .5;
364 p[1] = c[1] + .5;
365 p[2] = .5 * (k1 + k2);
366 labels.ImageToWorld(p[0], p[1], p[2]);
367 ptIds[2] = points->InsertNextPoint(p);
368 p[0] = c[0] + .5;
369 p[1] = c[1] - .5;
370 p[2] = .5 * (k1 + k2);
371 labels.ImageToWorld(p[0], p[1], p[2]);
372 ptIds[3] = points->InsertNextPoint(p);
373 }
374 polys->InsertNextCell(4, ptIds);
375 }
376 }
377 }
378 }
379 }
380
381 vtkSmartPointer<vtkPolyData> boundary;
382 boundary = vtkSmartPointer<vtkPolyData>::New();
383 boundary->SetPoints(points);
384 boundary->SetPolys(polys);
385
386 vtkNew<vtkCleanPolyData> cleaner;
387 SetVTKInput(cleaner, boundary);
388 cleaner->ConvertPolysToLinesOff();
389 cleaner->ConvertLinesToPointsOff();
390 cleaner->ConvertStripsToPolysOff();
391 cleaner->PointMergingOn();
392 cleaner->ToleranceIsAbsoluteOn();
393 cleaner->SetAbsoluteTolerance(1e-9);
394 cleaner->Update();
395 boundary = cleaner->GetOutput();
396
397 if (debug) {
398 static int ncall = 0; ++ncall;
399 char fname[64];
400 snprintf(fname, 64, "debug_boundary_voxels_%d.nii.gz", ncall);
401 boundary_voxels.Write(fname);
402 snprintf(fname, 64, "debug_boundary_%d.vtp", ncall);
403 WritePolyData(fname, boundary);
404 }
405
406 return boundary;
407 }
408
409 // -----------------------------------------------------------------------------
410 /// Minimum squared distance of point to segmentation boundary surface
MinSquaredDistance(vtkAbstractCellLocator * cut,const Point & p)411 inline double MinSquaredDistance(vtkAbstractCellLocator *cut, const Point &p)
412 {
413 double x[3], dist2;
414 vtkIdType cellId;
415 int subId;
416 cut->FindClosestPoint(const_cast<Point &>(p), x, cellId, subId, dist2);
417 return dist2;
418 }
419
420 // -----------------------------------------------------------------------------
421 /// Minimum squared distance of point to surface boundary
MinSquaredDistance(const BoundarySegment & seg,const Point & p)422 inline double MinSquaredDistance(const BoundarySegment &seg, const Point &p)
423 {
424 const auto i = seg.FindClosestPoint(p);
425 return p.SquaredDistance(seg.Point(i));
426 }
427
428 // -----------------------------------------------------------------------------
429 /// Minimum squared distance of surface boundary segment to segmentation boundary surface
MinSquaredDistance(const BoundarySegment & seg,vtkAbstractCellLocator * cut)430 double MinSquaredDistance(const BoundarySegment &seg, vtkAbstractCellLocator *cut)
431 {
432 double x[3], dist2, min_dist2 = inf;
433 vtkIdType cellId;
434 int subId;
435 for (int i = 0; i < seg.NumberOfPoints(); ++i) {
436 cut->FindClosestPoint(seg.Point(i), x, cellId, subId, dist2);
437 if (dist2 < min_dist2) min_dist2 = dist2;
438 }
439 return min_dist2;
440 }
441
442 // -----------------------------------------------------------------------------
443 /// Hausdorff distance of two boundary segments
HausdorffDistance(const BoundarySegment & a,const BoundarySegment & b)444 double HausdorffDistance(const BoundarySegment &a, const BoundarySegment &b)
445 {
446 double d_ab = 0., d_ba = 0.;
447 for (int i = 0; i < a.NumberOfPoints(); ++i) {
448 d_ab = max(d_ab, MinSquaredDistance(b, a.Point(i)));
449 }
450 for (int i = 0; i < b.NumberOfPoints(); ++i) {
451 d_ba = max(d_ba, MinSquaredDistance(a, b.Point(i)));
452 }
453 return sqrt(max(d_ab, d_ba));
454 }
455
456 // -----------------------------------------------------------------------------
457 /// Delete boundary points with only one cell
DeleteSingleBoundaryPointCells(SurfaceBoundary & boundary,vtkAbstractCellLocator * cut,double max_dist2)458 void DeleteSingleBoundaryPointCells(SurfaceBoundary &boundary, vtkAbstractCellLocator *cut, double max_dist2)
459 {
460 vtkPolyData * const surface = boundary.Surface();
461
462 vtkNew<vtkIdList> cellIds;
463
464 while (true) {
465 int ndel = 0;
466 surface->BuildLinks();
467 for (int j = 0; j < boundary.NumberOfSegments(); ++j) {
468 auto &seg = boundary.Segment(j);
469 if (MinSquaredDistance(seg, cut) < max_dist2) {
470 for (int i = 0; i < seg.NumberOfPoints(); ++i) {
471 surface->GetPointCells(seg.PointId(i), cellIds.GetPointer());
472 if (cellIds->GetNumberOfIds() == 1) {
473 surface->RemoveCellReference(cellIds->GetId(0));
474 surface->DeleteCell(cellIds->GetId(0));
475 ++ndel;
476 }
477 }
478 }
479 }
480 if (ndel == 0) break;
481 surface->RemoveDeletedCells();
482 boundary = SurfaceBoundary(surface);
483 }
484 }
485
486 // -----------------------------------------------------------------------------
487 /// Smooth intersection boundaries
SmoothBoundaries(SurfaceBoundary & boundary,vtkAbstractCellLocator * cut,double max_dist2,int niter=1)488 void SmoothBoundaries(SurfaceBoundary &boundary, vtkAbstractCellLocator *cut, double max_dist2, int niter = 1)
489 {
490 if (niter <= 0) return;
491 PointSet new_points;
492 for (int j = 0; j < boundary.NumberOfSegments(); ++j) {
493 auto &seg = boundary.Segment(j);
494 if (MinSquaredDistance(seg, cut) < max_dist2) {
495 new_points.Resize(seg.NumberOfPoints());
496 for (int iter = 0; iter < niter; ++iter) {
497 for (int i = 0; i < seg.NumberOfPoints(); ++i) {
498 new_points(i) = (seg.Point(i-1) + seg.Point(i) + seg.Point(i+1)) / 3.;
499 }
500 for (int i = 0; i < seg.NumberOfPoints(); ++i) {
501 seg.Point(i, new_points(i));
502 }
503 }
504 }
505 }
506 }
507
508 // -----------------------------------------------------------------------------
InterpolateCellData(vtkCellData * cd,vtkPointData * pd,const Array<int> & type,vtkIdType cellId,vtkIdType ptId1,vtkIdType ptId2,vtkIdType ptId3)509 void InterpolateCellData(vtkCellData *cd, vtkPointData *pd, const Array<int> &type,
510 vtkIdType cellId, vtkIdType ptId1, vtkIdType ptId2, vtkIdType ptId3)
511 {
512 double v1, v2, v3;
513 Array<double> tuple;
514 for (int i = 0; i < cd->GetNumberOfArrays(); ++i) {
515 vtkDataArray * const dst = cd->GetArray(i);
516 vtkDataArray * const src = pd->GetArray(i);
517 tuple.resize(src->GetNumberOfComponents());
518 if (type[i] == 2) {
519 for (int j = 0; j < src->GetNumberOfComponents(); ++j) {
520 tuple[j] = 0.;
521 }
522 } else if (type[i] == 1) {
523 for (int j = 0; j < src->GetNumberOfComponents(); ++j) {
524 v1 = src->GetComponent(ptId1, j);
525 v2 = src->GetComponent(ptId2, j);
526 v3 = src->GetComponent(ptId3, j);
527 tuple[j] = (v2 == v3 ? v2 : v1);
528 }
529 } else {
530 for (int j = 0; j < src->GetNumberOfComponents(); ++j) {
531 v1 = src->GetComponent(ptId1, j);
532 v2 = src->GetComponent(ptId2, j);
533 v3 = src->GetComponent(ptId3, j);
534 tuple[j] = (v1 + v2 + v3) / 3.;
535 }
536 }
537 dst->InsertTuple(cellId, tuple.data());
538 }
539 }
540
541 // -----------------------------------------------------------------------------
542 /// Convert cell data to point data
CellToPointData(vtkDataSet * dataset,const Array<int> & type)543 vtkSmartPointer<vtkPointData> CellToPointData(vtkDataSet *dataset, const Array<int> &type)
544 {
545 vtkCellData * const cd = dataset->GetCellData();
546 // Use VTK filter to convert cell data to point data using averaging
547 vtkNew<vtkCellDataToPointData> cd_to_pd;
548 SetVTKInput(cd_to_pd, dataset);
549 cd_to_pd->PassCellDataOff();
550 cd_to_pd->Update();
551 vtkSmartPointer<vtkPointData> pd = cd_to_pd->GetOutput()->GetPointData();
552 // Fix label data
553 double v;
554 UnorderedMap<double, int> bins;
555 UnorderedMap<double, int>::iterator bin;
556 vtkNew<vtkIdList> cellIds;
557 for (int i = 0; i < pd->GetNumberOfArrays(); ++i) {
558 if (type[i] == 1) {
559 vtkDataArray * const src = cd->GetArray(i);
560 vtkDataArray * const dst = pd->GetArray(i);
561 for (vtkIdType ptId = 0; ptId < dataset->GetNumberOfPoints(); ++ptId) {
562 dataset->GetPointCells(ptId, cellIds.GetPointer());
563 for (int j = 0; j < src->GetNumberOfComponents(); ++j) {
564 bins.clear();
565 for (vtkIdType k = 0; k < cellIds->GetNumberOfIds(); ++k) {
566 v = src->GetComponent(cellIds->GetId(k), j);
567 bin = bins.find(v);
568 if (bin == bins.end()) bins[v] = 1;
569 else bin->second += 1;
570 }
571 int max_cnt = 0;
572 double max_val = 0.;
573 for (bin = bins.begin(); bin != bins.end(); ++bin) {
574 if (bin->second > max_cnt) {
575 max_val = bin->first;
576 max_cnt = bin->second;
577 }
578 }
579 dst->SetComponent(ptId, j, max_val);
580 }
581 }
582 }
583 }
584 return pd;
585 }
586
587 // -----------------------------------------------------------------------------
588 /// Join intersected components at the segmentation boundary
JoinBoundaries(SurfaceBoundary & boundary,vtkAbstractCellLocator * cut,double max_dist2,double max_hdist)589 void JoinBoundaries(SurfaceBoundary &boundary, vtkAbstractCellLocator *cut, double max_dist2, double max_hdist)
590 {
591 vtkIdType cellId, ptIds[3];
592
593 vtkPolyData * const surface = boundary.Surface();
594 vtkCellArray * const polys = surface->GetPolys();
595 vtkCellData * const cd = surface->GetCellData();
596
597 Array<int> cd_type(cd->GetNumberOfArrays(), 0);
598 for (int i = 0; i < cd->GetNumberOfArrays(); ++i) {
599 vtkDataArray * const arr = cd->GetArray(i);
600 if (arr->GetName()) {
601 if (strcmp(arr->GetName(), SOURCE_ARRAY_NAME) == 0) {
602 cd_type[i] = 2;
603 } else if (IsCategoricalArrayName(arr->GetName())) {
604 cd_type[i] = 1;
605 }
606 }
607 }
608 vtkSmartPointer<vtkPointData> cd_as_pd = CellToPointData(surface, cd_type);
609
610 while (boundary.NumberOfSegments() > 1) {
611
612 Array<double> dist2(boundary.NumberOfSegments());
613 for (int j = 0; j < boundary.NumberOfSegments(); ++j) {
614 dist2[j] = MinSquaredDistance(boundary.Segment(j), cut);
615 }
616
617 int idx1, idx2;
618 Array<Array<double>> hdist(boundary.NumberOfSegments());
619 for (idx1 = 0; idx1 < boundary.NumberOfSegments(); ++idx1) {
620 hdist[idx1].resize(boundary.NumberOfSegments(), inf);
621 if (dist2[idx1] <= max_dist2) {
622 const auto &seg1 = boundary.Segment(idx1);
623 if (seg1.NumberOfPoints() > 1) {
624 for (idx2 = idx1 + 1; idx2 < boundary.NumberOfSegments(); ++idx2) {
625 if (dist2[idx2] <= max_dist2) {
626 const auto &seg2 = boundary.Segment(idx2);
627 if (seg2.NumberOfPoints() > 1) {
628 hdist[idx1][idx2] = HausdorffDistance(seg1, seg2);
629 }
630 }
631 }
632 }
633 }
634 }
635 Array<double> min_hdist(boundary.NumberOfSegments());
636 Array<int> min_idx2 (boundary.NumberOfSegments());
637 for (idx1 = 0; idx1 < boundary.NumberOfSegments(); ++idx1) {
638 min_idx2 [idx1] = IncreasingOrder(hdist[idx1]).front();
639 min_hdist[idx1] = hdist[idx1][min_idx2[idx1]];
640 }
641 idx1 = IncreasingOrder(min_hdist).front();
642 if (min_hdist[idx1] > max_hdist) break;
643 idx2 = min_idx2[idx1];
644
645 if (verbose > 1) {
646 cout << " Joining boundary segments " << idx1+1 << " and " << idx2+1
647 << " out of " << boundary.NumberOfSegments() << " remaining surface boundaries"
648 << " (Hausdorff distance = " << hdist[idx1][idx2] << ")" << endl;
649 }
650
651 // Get boundary segments to be joined
652 auto &seg1 = boundary.Segment(idx1);
653 auto &seg2 = boundary.Segment(idx2);
654
655 const int i0 = 0;
656 const int j0 = seg2.FindClosestPoint(seg1.Point(i0));
657
658 // TODO: Determine in which direction to traverse each boundary segment
659 // such that orientation of newly added triangles is consistent with the
660 // orientation of the boundary triangles
661 const int di = 1;
662
663 // Construct list of new triangles based on either dj=-1 or dj=+1 traversal
664 // direction and select those triangles with the least cost/error
665 const int max_iter = seg1.NumberOfPoints() + seg2.NumberOfPoints();
666 double l1, l2, cost1 = 0., cost2 = 0.;
667 Array<Vector3D<vtkIdType>> tris1, tris2;
668 for (int dj = -1; dj <= 1; dj += 2) {
669 int i = i0, j = j0;
670 seg1.ClearSelection();
671 seg2.ClearSelection();
672 auto &tris = (dj == -1 ? tris1 : tris2);
673 auto &cost = (dj == -1 ? cost1 : cost2);
674 tris.reserve(max_iter);
675 for (int iter = 0; iter < max_iter; ++iter) {
676 l1 = (seg1.IsSelected(i + di) ? inf : seg2.Point(j).SquaredDistance(seg1.Point(i + di)));
677 l2 = (seg2.IsSelected(j + dj) ? inf : seg1.Point(i).SquaredDistance(seg2.Point(j + dj)));
678 if (IsInf(l1) && IsInf(l2)) break;
679 if (l1 <= l2) {
680 cost += l1;
681 ptIds[0] = seg1.PointId(i);
682 ptIds[1] = seg1.PointId(i + di);
683 ptIds[2] = seg2.PointId(j);
684 i = seg1.IndexModuloNumberOfPoints(i + di);
685 seg1.SelectPoint(i);
686 } else {
687 cost += l2;
688 ptIds[0] = seg1.PointId(i);
689 ptIds[1] = seg2.PointId(j + dj);
690 ptIds[2] = seg2.PointId(j);
691 j = seg2.IndexModuloNumberOfPoints(j + dj);
692 seg2.SelectPoint(j);
693 }
694 tris.push_back(Vector3D<vtkIdType>(ptIds));
695 }
696 }
697
698 // Add triangles joining the two boundary segments
699 for (auto &tri : (cost1 <= cost2 ? tris1 : tris2)) {
700 cellId = polys->InsertNextCell(3);
701 polys->InsertCellPoint(tri._x);
702 polys->InsertCellPoint(tri._y);
703 polys->InsertCellPoint(tri._z);
704 InterpolateCellData(cd, cd_as_pd, cd_type, cellId, tri._x, tri._y, tri._z);
705 }
706
707 surface->DeleteLinks();
708 surface->DeleteCells();
709
710 #if 0
711 boundary = SurfaceBoundary(surface);
712 #else
713 break;
714 #endif
715 }
716
717 // Free excessive memory
718 polys->Squeeze();
719 cd->Squeeze();
720 }
721
722 // -----------------------------------------------------------------------------
723 /// Merge two surface meshes at given segmentation boundary
724 vtkSmartPointer<vtkPolyData>
Merge(vtkPolyData * s1,vtkPolyData * s2,vtkPolyData * label_boundary,double tol,int smooth,bool join)725 Merge(vtkPolyData *s1, vtkPolyData *s2, vtkPolyData *label_boundary, double tol, int smooth, bool join)
726 {
727 const double tol2 = tol * tol;
728 const double max_dist2 = 4. * tol2;
729 const double max_hdist = 10. * tol;
730
731 double p[3], x[3], dist2;
732 vtkIdType cellId;
733 int subId;
734
735 vtkNew<vtkIdList> cellIds;
736
737 // Build links
738 s1->BuildLinks();
739 s2->BuildLinks();
740
741 // Locator to find distance to segmentation boundary
742 vtkSmartPointer<vtkAbstractCellLocator> cut;
743 cut = vtkSmartPointer<vtkCellLocator>::New();
744 cut->SetDataSet(label_boundary);
745 cut->BuildLocator();
746
747 // Mark cells close to segmentation boundary as deleted
748 for (vtkIdType ptId = 0; ptId < s1->GetNumberOfPoints(); ++ptId) {
749 s1->GetPoint(ptId, p);
750 cut->FindClosestPoint(p, x, cellId, subId, dist2);
751 if (dist2 < tol2) {
752 s1->GetPointCells(ptId, cellIds.GetPointer());
753 for (vtkIdType i = 0; i < cellIds->GetNumberOfIds(); ++i) {
754 s1->DeleteCell(cellIds->GetId(i));
755 }
756 s1->DeletePoint(ptId);
757 }
758 }
759
760 for (vtkIdType ptId = 0; ptId < s2->GetNumberOfPoints(); ++ptId) {
761 s2->GetPoint(ptId, p);
762 cut->FindClosestPoint(p, x, cellId, subId, dist2);
763 if (dist2 < tol2) {
764 s2->GetPointCells(ptId, cellIds.GetPointer());
765 for (vtkIdType i = 0; i < cellIds->GetNumberOfIds(); ++i) {
766 s2->DeleteCell(cellIds->GetId(i));
767 }
768 s2->DeletePoint(ptId);
769 }
770 }
771
772 // Remove deleted cells
773 s1->RemoveDeletedCells();
774 s2->RemoveDeletedCells();
775
776 // Combine surfaces into one polygonal data set
777 vtkNew<vtkAppendPolyData> appender;
778 AddVTKInput(appender, s1);
779 AddVTKInput(appender, s2);
780 appender->Update();
781
782 // Get combined output data set
783 vtkSmartPointer<vtkPolyData> output;
784 output = appender->GetOutput();
785
786 // Clean intersection boundaries
787 //
788 // This is in particular needed to avoid issues with these cells when
789 // smoothing and/or joining the intersection boundaries...
790 SurfaceBoundary boundary(output);
791 DeleteSingleBoundaryPointCells(boundary, cut, max_dist2);
792
793 // Smooth intersection boundaries
794 SmoothBoundaries(boundary, cut, max_dist2, smooth);
795
796 // Join intersection boundaries
797 if (join) JoinBoundaries(boundary, cut, max_dist2, max_hdist);
798
799 return output;
800 }
801
802 // -----------------------------------------------------------------------------
803 /// Add array with given point source label
AddPointSourceArray(vtkPolyData * const surface,size_t idx)804 int AddPointSourceArray(vtkPolyData * const surface, size_t idx)
805 {
806 vtkSmartPointer<vtkDataArray> arr;
807 arr = NewVtkDataArray(VTK_SHORT, surface->GetNumberOfPoints(), 1, SOURCE_ARRAY_NAME);
808 arr->FillComponent(0, static_cast<double>(idx));
809 surface->GetPointData()->RemoveArray(SOURCE_ARRAY_NAME);
810 return surface->GetPointData()->AddArray(arr);
811 }
812
813 // -----------------------------------------------------------------------------
814 /// Add array with given cell source label
AddCellSourceArray(vtkPolyData * const surface,size_t idx)815 int AddCellSourceArray(vtkPolyData * const surface, size_t idx)
816 {
817 vtkSmartPointer<vtkDataArray> arr;
818 arr = NewVtkDataArray(VTK_SHORT, surface->GetNumberOfCells(), 1, SOURCE_ARRAY_NAME);
819 arr->FillComponent(0, static_cast<double>(idx));
820 surface->GetCellData()->RemoveArray(SOURCE_ARRAY_NAME);
821 return surface->GetCellData()->AddArray(arr);
822 }
823
824 // -----------------------------------------------------------------------------
825 /// Calculate point normals of surface mesh while fixing vertex order if necessary
826 vtkSmartPointer<vtkPolyData>
CalculateNormals(vtkPolyData * surface,bool point_normals,bool cell_normals)827 CalculateNormals(vtkPolyData *surface, bool point_normals, bool cell_normals)
828 {
829 vtkNew<vtkPolyDataNormals> normals;
830 SetVTKInput(normals, surface);
831 normals->AutoOrientNormalsOff();
832 normals->SplittingOff();
833 normals->NonManifoldTraversalOff();
834 normals->ConsistencyOn();
835 normals->SetComputePointNormals(point_normals);
836 normals->SetComputeCellNormals(cell_normals);
837 normals->Update();
838 return normals->GetOutput();
839 }
840
841 // -----------------------------------------------------------------------------
842 /// Fix normals of border edge points
FixBorderPointNormals(vtkPolyData * surface)843 void FixBorderPointNormals(vtkPolyData *surface)
844 {
845 vtkDataArray * const cnormals = surface->GetCellData ()->GetNormals();
846 vtkDataArray * const pnormals = surface->GetPointData()->GetNormals();
847 vtkDataArray * const csource = surface->GetCellData ()->GetArray(SOURCE_ARRAY_NAME);
848 vtkDataArray * const psource = surface->GetPointData()->GetArray(SOURCE_ARRAY_NAME);
849
850 vtkIdType n;
851 vtkNew<vtkIdList> cellIds;
852 Vector3 pn, cn;
853
854 for (vtkIdType ptId = 0; ptId < surface->GetNumberOfPoints(); ++ptId) {
855 if (psource->GetComponent(ptId, 0) == 0.) {
856 surface->GetPointCells(ptId, cellIds.GetPointer());
857 n = 0;
858 pn = 0.;
859 for (vtkIdType i = 0; i < cellIds->GetNumberOfIds(); ++i) {
860 if (csource->GetComponent(cellIds->GetId(i), 0) >= 0.) {
861 cnormals->GetTuple(cellIds->GetId(i), cn);
862 pn += cn, ++n;
863 }
864 }
865 if (n > 0) {
866 pn /= n;
867 pn.Normalize();
868 pnormals->SetTuple(ptId, pn);
869 }
870 }
871 }
872 }
873
874 // -----------------------------------------------------------------------------
875 /// Get cell data array with cells within vicinity of joint boundaries masked
IntersectionCellMask(vtkPolyData * const surface,int nconn=3)876 vtkSmartPointer<vtkDataArray> IntersectionCellMask(vtkPolyData * const surface, int nconn = 3)
877 {
878 const vtkIdType n = surface->GetNumberOfCells();
879
880 vtkCellData * const cd = surface->GetCellData();
881 vtkDataArray * const source = cd->GetArray(SOURCE_ARRAY_NAME);
882 vtkNew<vtkIdList> ptIds, cellIds;
883
884 vtkSmartPointer<vtkDataArray> mask;
885 mask = NewVtkDataArray(VTK_UNSIGNED_CHAR, n, 1, INTERSECTION_ARRAY_NAME);
886 for (vtkIdType cellId = 0; cellId < n; ++cellId) {
887 mask->SetComponent(cellId, 0, source->GetComponent(cellId, 0) == 0. ? 1. : 0.);
888 }
889 for (int iter = 0; iter < nconn; ++iter) {
890 vtkSmartPointer<vtkDataArray> next;
891 next.TakeReference(mask->NewInstance());
892 next->DeepCopy(mask);
893 for (vtkIdType cellId = 0; cellId < n; ++cellId) {
894 if (mask->GetComponent(cellId, 0) != 0.) {
895 surface->GetCellPoints(cellId, ptIds.GetPointer());
896 for (vtkIdType i = 0; i < ptIds->GetNumberOfIds(); ++i) {
897 surface->GetPointCells(ptIds->GetId(i), cellIds.GetPointer());
898 for (vtkIdType j = 0; j < cellIds->GetNumberOfIds(); ++j) {
899 next->SetComponent(cellIds->GetId(j), 0, 1.);
900 }
901 }
902 }
903 }
904 mask = next;
905 }
906
907 return mask;
908 }
909
910 // -----------------------------------------------------------------------------
911 /// Get point data array with points within vicinity of joint boundaries masked
IntersectionPointMask(vtkPolyData * const surface,int nconn=3)912 vtkSmartPointer<vtkDataArray> IntersectionPointMask(vtkPolyData * const surface, int nconn = 3)
913 {
914 const vtkIdType n = surface->GetNumberOfPoints();
915
916 vtkCellData * const cd = surface->GetCellData();
917 vtkDataArray * const source = cd->GetArray(SOURCE_ARRAY_NAME);
918 vtkNew<vtkIdList> ptIds, cellIds;
919
920 vtkSmartPointer<vtkDataArray> mask;
921 mask = NewVtkDataArray(VTK_UNSIGNED_CHAR, n, 1, INTERSECTION_ARRAY_NAME);
922 mask->FillComponent(0, 0.);
923 for (vtkIdType ptId = 0; ptId < n; ++ptId) {
924 surface->GetPointCells(ptId, cellIds.GetPointer());
925 for (vtkIdType i = 0; i < cellIds->GetNumberOfIds(); ++i) {
926 if (source->GetComponent(cellIds->GetId(i), 0) == 0.) {
927 mask->SetComponent(ptId, 0, 1.);
928 break;
929 }
930 }
931 }
932 for (int iter = 0; iter < nconn; ++iter) {
933 vtkSmartPointer<vtkDataArray> next;
934 next.TakeReference(mask->NewInstance());
935 next->DeepCopy(mask);
936 for (vtkIdType ptId = 0; ptId < n; ++ptId) {
937 if (mask->GetComponent(ptId, 0) != 0.) {
938 surface->GetPointCells(ptId, cellIds.GetPointer());
939 for (vtkIdType i = 0; i < cellIds->GetNumberOfIds(); ++i) {
940 surface->GetCellPoints(cellIds->GetId(i), ptIds.GetPointer());
941 for (vtkIdType j = 0; j < ptIds->GetNumberOfIds(); ++j) {
942 next->SetComponent(ptIds->GetId(j), 0, 1.);
943 }
944 }
945 }
946 }
947 mask = next;
948 }
949
950 return mask;
951 }
952
953 // -----------------------------------------------------------------------------
954 /// Determine edge length range within surface intersection
GetEdgeLengthRange(vtkPolyData * const surface,vtkDataArray * const mask,double & lmin,double & lmax,double sd=2.5)955 void GetEdgeLengthRange(vtkPolyData * const surface, vtkDataArray * const mask,
956 double &lmin, double &lmax, double sd = 2.5)
957 {
958 vtkCellData * const cd = surface->GetCellData();
959 vtkDataArray * const source = cd->GetArray(SOURCE_ARRAY_NAME);
960
961 double min_edge_length = +inf;
962 double max_edge_length = -inf;
963
964 int n = 0;
965 double a[3], b[3], ab[3], l2, lsum = 0., l2sum = 0.;
966 vtkNew<vtkIdList> ptIds;
967
968 for (vtkIdType cellId = 0; cellId < surface->GetNumberOfCells(); ++cellId) {
969 if (mask->GetComponent(cellId, 0) != 0. && source->GetComponent(cellId, 0) != 0.) {
970 surface->GetCellPoints(cellId, ptIds.GetPointer());
971 for (vtkIdType i = 0, j; i < ptIds->GetNumberOfIds(); ++i) {
972 j = (i + 1) % ptIds->GetNumberOfIds();
973 surface->GetPoint(ptIds->GetId(i), a);
974 surface->GetPoint(ptIds->GetId(j), b);
975 vtkMath::Subtract(b, a, ab);
976 l2 = vtkMath::Dot(ab, ab);
977 lsum += sqrt(l2);
978 l2sum += l2;
979 ++n;
980 }
981 }
982 }
983 if (n > 0) {
984 const double mean = lsum / n;
985 const double sigma = sqrt(l2sum / n - mean * mean);
986 min_edge_length = max(0., mean - sd * sigma);
987 max_edge_length = mean + sd * sigma;
988 }
989
990 if (IsNaN(lmin)) {
991 lmin = (min_edge_length > max_edge_length ? 0. : min_edge_length);
992 }
993 if (IsNaN(lmax)) {
994 lmax = (min_edge_length > max_edge_length ? inf : max_edge_length);
995 }
996 }
997
998 // -----------------------------------------------------------------------------
999 /// Principal component analysis of point set
PrincipalDirections(vtkPointSet * const points,Vector3 dir[3],bool twod=false)1000 bool PrincipalDirections(vtkPointSet * const points, Vector3 dir[3], bool twod = false)
1001 {
1002 if (points->GetNumberOfPoints() == 0) return false;
1003
1004 Point c, p;
1005 points->GetCenter(c);
1006
1007 Matrix3x3 covar(0.);
1008 for (vtkIdType ptId = 0; ptId < points->GetNumberOfPoints(); ++ptId) {
1009 points->GetPoint(ptId, p);
1010 vtkMath::Subtract(p, c, p);
1011 for (int r = 0; r < 3; ++r)
1012 for (int c = 0; c < 3; ++c) {
1013 covar[r][c] += p[r] * p[c];
1014 }
1015 }
1016
1017 Vector3 axis[3];
1018 Array<double> eigval(3);
1019 covar.EigenSolveSymmetric(eigval.data(), axis);
1020
1021 eigval[0] = abs(eigval[0]);
1022 eigval[1] = abs(eigval[1]);
1023 eigval[2] = abs(eigval[2]);
1024 Array<int> order = DecreasingOrder(eigval);
1025 if (eigval[order[twod ? 1 : 2]] < 1e-6) return false;
1026
1027 dir[0] = axis[order[0]], dir[0].Normalize();
1028 dir[1] = axis[order[1]], dir[1].Normalize();
1029 dir[2] = axis[order[2]], dir[2].Normalize();
1030 return true;
1031 }
1032
1033 // -----------------------------------------------------------------------------
1034 /// Structure with cutting plane parameters
1035 struct PlaneAttributes
1036 {
1037 Point o; ///< Origin/Center point
1038 Vector3 n; ///< Normal vector
1039 Vector3 x; ///< In-plane x axis
1040 Vector3 y; ///< In-plane y axis
1041 Vector3 &z = n; ///< Out of plane z axis, i.e., plane normal
1042 };
1043
1044 // -----------------------------------------------------------------------------
1045 /// Get cutting plane from segmentation boundary
CuttingPlaneAttributes(vtkSmartPointer<vtkPointSet> points)1046 PlaneAttributes CuttingPlaneAttributes(vtkSmartPointer<vtkPointSet> points)
1047 {
1048 MIRTK_START_TIMING();
1049 Vector3 dir[3], p;
1050 if (!PrincipalDirections(points, dir)) {
1051 FatalError("Failed to compute principal directions of point set");
1052 }
1053 double u, v, minu = +inf, maxu = -inf, minv = +inf, maxv = -inf;
1054 for (vtkIdType ptId = 0; ptId < points->GetNumberOfPoints(); ++ptId) {
1055 points->GetPoint(ptId, p);
1056 u = dir[0].Dot(p);
1057 v = dir[1].Dot(p);
1058 if (u < minu) minu = u;
1059 if (u > maxu) maxu = u;
1060 if (v < minv) minv = v;
1061 if (v > maxv) maxv = v;
1062 }
1063 dir[0] *= .5 * (maxu - minu);
1064 dir[1] *= .5 * (maxv - minv);
1065 dir[2].Normalize();
1066 PlaneAttributes attr;
1067 points->GetCenter(attr.o);
1068 attr.n = dir[2];
1069 attr.x = dir[0];
1070 attr.y = dir[1];
1071 MIRTK_DEBUG_TIMING(2, "CuttingPlaneAttributes");
1072 return attr;
1073 }
1074
1075 // -----------------------------------------------------------------------------
1076 /// Get cutting plane from segmentation boundary
CuttingPlane(const PlaneAttributes & attr,double tn,double rx,double ry,double margin)1077 vtkSmartPointer<vtkPolyData> CuttingPlane(const PlaneAttributes &attr, double tn, double rx, double ry, double margin)
1078 {
1079 MIRTK_START_TIMING();
1080
1081 // Calculate rotation matrices in local plane coordinates
1082 const double cx = cos(rx * rad_per_deg), sx = sin(rx * rad_per_deg);
1083 const double cy = cos(ry * rad_per_deg), sy = sin(ry * rad_per_deg);
1084 const Matrix3x3 Rx(1., 0., 0., 0., cx, -sx, 0., sx, cx);
1085 const Matrix3x3 Ry(cy, 0., sy, 0., 1., 0., -sy, 0., cy);
1086
1087 // Rotate local plane axes vectors
1088 Vector3 dx(1., 0., 0.), dy(0., 1., 0.), n(0., 0., 1.);
1089 dx = Rx * Ry * dx;
1090 dy = Rx * Ry * dy;
1091 n = Rx * Ry * n;
1092
1093 // Map to world vectors of unit length
1094 Vector3 e1(attr.x), e2(attr.y), e3(attr.z);
1095 e1.Normalize(), e2.Normalize(), e3.Normalize();
1096 dx = dx._x * e1 + dx._y * e2 + dx._z * e3;
1097 dy = dy._x * e1 + dy._y * e2 + dy._z * e3;
1098 n = n ._x * e1 + n ._y * e2 + n ._z * e3;
1099
1100 // Add plane margin to initial plane extend
1101 dx *= (attr.x.Length() + 2. * margin);
1102 dy *= (attr.y.Length() + 2. * margin);
1103
1104 // Translate plane along original normal vector
1105 Point o = attr.o + tn * attr.n;
1106
1107 // vtkPlaneSource origin is in corner, not center
1108 o -= dx;
1109 o -= dy;
1110 dx *= 2.;
1111 dy *= 2.;
1112
1113 // Tesselate finite plane
1114 vtkSmartPointer<vtkPolyData> plane;
1115 vtkNew<vtkPlaneSource> source;
1116 source->SetXResolution(1);
1117 source->SetYResolution(1);
1118 source->SetOrigin(o);
1119 source->SetPoint1(o + dx);
1120 source->SetPoint2(o + dy);
1121 source->Update();
1122 plane = Triangulate(source->GetOutput());
1123
1124 MIRTK_DEBUG_TIMING(2, "CuttingPlane");
1125 return plane;
1126 }
1127
1128 // -----------------------------------------------------------------------------
1129 /// Intersect two polygonal data sets and return single intersection polygon
LargestClosedIntersection(vtkPolyData * s1,vtkPolyData * s2)1130 vtkSmartPointer<vtkPolyData> LargestClosedIntersection(vtkPolyData *s1, vtkPolyData *s2)
1131 {
1132 MIRTK_START_TIMING();
1133
1134 vtkSmartPointer<vtkPolyData> cut;
1135 {
1136 MIRTK_START_TIMING();
1137 vtkNew<vtkIntersectionPolyDataFilter> intersection;
1138 intersection->SplitFirstOutputOff();
1139 intersection->SplitSecondOutputOff();
1140 SetNthVTKInput(intersection, 0, s1);
1141 SetNthVTKInput(intersection, 1, s2);
1142
1143 vtkNew<vtkCleanPolyData> merger;
1144 SetVTKConnection(merger, intersection);
1145 merger->ConvertStripsToPolysOff();
1146 merger->ConvertPolysToLinesOff();
1147 merger->ConvertLinesToPointsOff();
1148 merger->PointMergingOn();
1149 merger->ToleranceIsAbsoluteOn();
1150 merger->SetAbsoluteTolerance(1e-12);
1151
1152 merger->Update();
1153 cut = merger->GetOutput();
1154 MIRTK_DEBUG_TIMING(3, "LargestClosedIntersection (intersect)");
1155 }
1156
1157 if (debug > 2) {
1158 static int iter = 0; ++iter;
1159 char fname[64];
1160 snprintf(fname, 64, "debug_cutting_lines_%d.vtp", iter);
1161 WritePolyData(fname, cut);
1162 }
1163
1164 {
1165 MIRTK_START_TIMING();
1166 cut->BuildLinks();
1167
1168 vtkIdType ptId, nbrId, cellId;
1169 vtkNew<vtkIdList> ptIds, cellIds;
1170 ptIds->Allocate(2);
1171
1172 Stack<vtkIdType> activePtIds;
1173 for (ptId = 0; ptId < cut->GetNumberOfPoints(); ++ptId) {
1174 cut->GetPointCells(ptId, cellIds.GetPointer());
1175 if (cellIds->GetNumberOfIds() == 1) activePtIds.push(ptId);
1176 }
1177 while (!activePtIds.empty()) {
1178 ptId = activePtIds.top(), activePtIds.pop();
1179 cut->GetPointCells(ptId, cellIds.GetPointer());
1180 if (cellIds->GetNumberOfIds() == 1) {
1181 cellId = cellIds->GetId(0);
1182 cut->GetCellPoints(cellId, ptIds.GetPointer());
1183 cut->RemoveCellReference(cellId);
1184 cut->DeleteCell(cellId);
1185 for (vtkIdType i = 0; i < ptIds->GetNumberOfIds(); ++i) {
1186 nbrId = ptIds->GetId(i);
1187 if (nbrId != ptId) {
1188 cut->GetPointCells(nbrId, cellIds.GetPointer());
1189 if (cellIds->GetNumberOfIds() == 1) activePtIds.push(nbrId);
1190 }
1191 }
1192 }
1193 }
1194 cut->RemoveDeletedCells();
1195 vtkNew<vtkCleanPolyData> merger;
1196 SetVTKInput(merger, cut);
1197 merger->ConvertStripsToPolysOff();
1198 merger->ConvertPolysToLinesOff();
1199 merger->ConvertLinesToPointsOn();
1200 merger->PointMergingOn();
1201 merger->ToleranceIsAbsoluteOn();
1202 merger->SetAbsoluteTolerance(1e-12);
1203 merger->Update();
1204 merger->GetOutput()->SetVerts(nullptr);
1205 cut = merger->GetOutput();
1206 MIRTK_DEBUG_TIMING(3, "LargestClosedIntersection (clean)");
1207 }
1208
1209 {
1210 MIRTK_START_TIMING();
1211 vtkNew<vtkPolyDataConnectivityFilter> cc;
1212 SetVTKInput(cc, cut);
1213 cc->ScalarConnectivityOff();
1214 cc->SetExtractionModeToLargestRegion();
1215 cc->Update();
1216 cut = cc->GetOutput();
1217 MIRTK_DEBUG_TIMING(3, "LargestClosedIntersection (largest)");
1218 }
1219
1220 MIRTK_DEBUG_TIMING(2, "LargestClosedIntersection");
1221 return cut;
1222 }
1223
1224 // -----------------------------------------------------------------------------
1225 /// Merge points and make line strips
LineStrips(vtkSmartPointer<vtkPolyData> cut)1226 vtkSmartPointer<vtkPolyData> LineStrips(vtkSmartPointer<vtkPolyData> cut)
1227 {
1228 vtkNew<vtkCleanPolyData> cut_merger;
1229 SetVTKInput(cut_merger, cut);
1230 cut_merger->ConvertStripsToPolysOff();
1231 cut_merger->ConvertPolysToLinesOff();
1232 cut_merger->ConvertLinesToPointsOn();
1233 cut_merger->PointMergingOn();
1234 cut_merger->ToleranceIsAbsoluteOn();
1235 cut_merger->SetAbsoluteTolerance(1e-12);
1236 cut_merger->Update();
1237 cut_merger->GetOutput()->SetVerts(nullptr);
1238 cut = cut_merger->GetOutput();
1239
1240 Stripper stripper;
1241 stripper.Input(cut);
1242 stripper.Run();
1243 return stripper.Output();
1244 }
1245
1246 // -----------------------------------------------------------------------------
NonBoundaryPointsMask(vtkSmartPointer<vtkPolyData> input)1247 vtkSmartPointer<vtkDataArray> NonBoundaryPointsMask(vtkSmartPointer<vtkPolyData> input)
1248 {
1249 vtkSmartPointer<vtkDataArray> mask;
1250 mask = NewVtkDataArray(VTK_UNSIGNED_CHAR, input->GetNumberOfPoints(), 1);
1251 mask->FillComponent(0, 1.);
1252 UnorderedSet<int> ptIds = BoundaryPoints(input);
1253 for (auto ptId : ptIds) mask->SetComponent(ptId, 0, 0.);
1254 return mask;
1255 }
1256
1257 // -----------------------------------------------------------------------------
1258 /// Make divider surface from intersection curve
Divider(vtkSmartPointer<vtkPolyData> cut)1259 vtkSmartPointer<vtkPolyData> Divider(vtkSmartPointer<vtkPolyData> cut)
1260 {
1261 vtkSmartPointer<vtkPolyData> divider = LineStrips(cut);
1262 if (debug) {
1263 static int callId = 0; ++callId;
1264 char fname[64];
1265 snprintf(fname, 64, "debug_split_surface_lines_%d.vtp", callId);
1266 WritePolyData(fname, divider);
1267 }
1268 vtkSmartPointer<vtkIdList> ptIds = vtkIdList::New();
1269 vtkSmartPointer<vtkIdList> stripPtIds = vtkIdList::New();
1270 for (vtkIdType cellId = 0; cellId < divider->GetLines()->GetNumberOfCells(); ++cellId) {
1271 divider->GetLines()->GetCell(cellId, ptIds);
1272 if (ptIds->GetNumberOfIds() > stripPtIds->GetNumberOfIds()) {
1273 stripPtIds = ptIds;
1274 }
1275 }
1276 if (stripPtIds->GetNumberOfIds() == 0) {
1277 Throw(ERR_LogicError, __FUNCTION__, "Expected at least one contiguous intersection line");
1278 }
1279 if (stripPtIds->GetNumberOfIds() <= 2) {
1280 Throw(ERR_LogicError, __FUNCTION__, "Expected polygon with more than two points");
1281 }
1282 if (stripPtIds->GetId(0) != stripPtIds->GetId(stripPtIds->GetNumberOfIds() - 1)) {
1283 Throw(ERR_LogicError, __FUNCTION__, "Expected closed intersection polygon");
1284 }
1285 vtkSmartPointer<vtkCellArray> polys = vtkSmartPointer<vtkCellArray>::New();
1286 polys->Allocate(polys->EstimateSize(1, stripPtIds->GetNumberOfIds() - 1));
1287 polys->InsertNextCell(stripPtIds);
1288 divider->SetLines(nullptr);
1289 divider->SetPolys(polys);
1290 divider->DeleteCells();
1291 return divider;
1292 }
1293
1294 // -----------------------------------------------------------------------------
1295 /// Compute Delaunay triangulation of interior of divider polygon after projection to 2D plane
TesselateDivider(vtkSmartPointer<vtkPolyData> divider,double ds)1296 vtkSmartPointer<vtkPolyData> TesselateDivider(vtkSmartPointer<vtkPolyData> divider, double ds)
1297 {
1298 if (divider->GetNumberOfPoints() < 2) return divider;
1299
1300 const double min_dist2 = pow(.2 * ds, 2);
1301
1302 int subId;
1303 double dist2, reverse_dist2;
1304 vtkIdType otherId;
1305 Vector3 c, p, q, x, dir[3];
1306
1307 // Compute principle directions of divider polygon plane
1308 const bool twod = true;
1309 PrincipalDirections(divider, dir, twod);
1310
1311 // Get center of divider polygon
1312 divider->GetCenter(c);
1313 double offset = 0.;
1314 for (vtkIdType ptId = 0; ptId < divider->GetNumberOfPoints(); ++ptId) {
1315 divider->GetPoint(ptId, p);
1316 offset += dir[2].Dot(p - c);
1317 }
1318 offset /= divider->GetNumberOfPoints();
1319 c += offset * dir[2];
1320
1321 // Set up homogeneous transformation from 3D world to divider plane
1322 vtkSmartPointer<vtkMatrix4x4> matrix;
1323 matrix = vtkSmartPointer<vtkMatrix4x4>::New();
1324 matrix->Identity();
1325 for (int i = 0; i < 3; ++i) {
1326 matrix->SetElement(i, 0, dir[i]._x);
1327 matrix->SetElement(i, 1, dir[i]._y);
1328 matrix->SetElement(i, 2, dir[i]._z);
1329 matrix->SetElement(i, 3, -dir[i].Dot(c));
1330 }
1331
1332 vtkSmartPointer<vtkMatrixToLinearTransform> transform;
1333 transform = vtkSmartPointer<vtkMatrixToLinearTransform>::New();
1334 transform->SetInput(matrix);
1335 transform->Update();
1336
1337 // Determine extent of divider polygon within divider plane
1338 Point minq, maxq;
1339 divider->GetPoint(0, minq);
1340 divider->GetPoint(0, maxq);
1341 for (vtkIdType ptId = 1; ptId < divider->GetNumberOfPoints(); ++ptId) {
1342 divider->GetPoint(ptId, p);
1343 transform->TransformPoint(p, q);
1344 if (q._x < minq._x) minq._x = q._x;
1345 if (q._y < minq._y) minq._y = q._y;
1346 if (q._z < minq._z) minq._z = q._z;
1347 if (q._x > maxq._x) maxq._x = q._x;
1348 if (q._y > maxq._y) maxq._y = q._y;
1349 if (q._z > maxq._z) maxq._z = q._z;
1350 }
1351
1352 const double margin = ds;
1353 minq._x -= margin, maxq._x += margin;
1354 minq._y -= margin, maxq._y += margin;
1355
1356 int nx = iceil((maxq._x - minq._x) / ds) + 1;
1357 int ny = iceil((maxq._y - minq._y) / ds) + 1;
1358
1359 if (nx % 2 == 0) ++nx;
1360 if (ny % 2 == 0) ++ny;
1361
1362 minq._x = minq._x + .5 * (maxq._x - minq._x) - ((nx - 1) / 2) * ds;
1363 maxq._x = minq._x + (nx - 1) * ds + 1e-12;
1364
1365 minq._y = minq._y + .5 * (maxq._y - minq._y) - ((ny - 1) / 2) * ds;
1366 maxq._y = minq._y + (ny - 1) * ds + 1e-12;
1367
1368 // Add additional discrete divider polygon plane grid points
1369 vtkNew<vtkPointLocator> locator;
1370 locator->SetDataSet(divider);
1371 locator->BuildLocator();
1372
1373 vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
1374 points->SetDataTypeToDouble();
1375 points->Allocate(divider->GetNumberOfPoints() + nx * ny);
1376
1377 for (vtkIdType ptId = 0; ptId < divider->GetNumberOfPoints(); ++ptId) {
1378 divider->GetPoint(ptId, p);
1379 points->InsertNextPoint(p);
1380 }
1381
1382 q._z = 0.;
1383 for (q._y = minq._y; q._y <= maxq._y; q._y += ds)
1384 for (q._x = minq._x; q._x <= maxq._x; q._x += ds) {
1385 p = c + q._x * dir[0] + q._y * dir[1];
1386 otherId = locator->FindClosestPoint(p);
1387 if (otherId >= 0) {
1388 divider->GetPoint(otherId, x);
1389 if ((p - x).SquaredLength() < min_dist2) {
1390 p = x;
1391 }
1392 }
1393 points->InsertNextPoint(p);
1394 }
1395
1396 // Compute constrained Delanauy triangulation of divider polygon
1397 vtkSmartPointer<vtkPolyData> pointset;
1398 pointset = vtkSmartPointer<vtkPolyData>::New();
1399 pointset->SetPoints(points);
1400
1401 vtkSmartPointer<vtkPolyData> boundary;
1402 boundary = vtkSmartPointer<vtkPolyData>::New();
1403 boundary->SetPoints(points);
1404 boundary->SetPolys(divider->GetPolys());
1405
1406 vtkNew<vtkDelaunay2D> delaunay;
1407 delaunay->SetInputData(pointset);
1408 delaunay->SetSourceData(boundary);
1409 delaunay->SetAlpha(0.);
1410 delaunay->SetOffset(1.);
1411 delaunay->SetTolerance(0.);
1412 delaunay->SetTransform(transform);
1413 delaunay->Update();
1414
1415 vtkSmartPointer<vtkPolyData> output;
1416 output = vtkSmartPointer<vtkPolyData>::New();
1417 output->DeepCopy(delaunay->GetOutput());
1418
1419 // TODO: Determine correct order of polygon points before vtkDelaunay2D::Update
1420 vtkNew<vtkCellLocator> cell_locator;
1421 cell_locator->SetDataSet(delaunay->GetOutput());
1422 cell_locator->BuildLocator();
1423 cell_locator->FindClosestPoint(c, x, otherId, subId, dist2);
1424
1425 vtkNew<vtkIdList> ptIds, reverse_pts;
1426 boundary->GetPolys()->GetCell(0, ptIds.GetPointer());
1427 reverse_pts->Allocate(ptIds->GetNumberOfIds());
1428 for (vtkIdType i = ptIds->GetNumberOfIds() - 1; i >= 0; --i) {
1429 reverse_pts->InsertNextId(ptIds->GetId(i));
1430 }
1431 vtkSmartPointer<vtkCellArray> reverse_polys;
1432 reverse_polys = vtkSmartPointer<vtkCellArray>::New();
1433 reverse_polys->Allocate(reverse_polys->EstimateSize(1, reverse_pts->GetNumberOfIds()));
1434 reverse_polys->InsertNextCell(reverse_pts.GetPointer());
1435
1436 vtkSmartPointer<vtkPolyData> reverse_boundary;
1437 reverse_boundary = vtkSmartPointer<vtkPolyData>::New();
1438 reverse_boundary->SetPoints(points);
1439 reverse_boundary->SetPolys(reverse_polys);
1440
1441 delaunay->SetSourceData(reverse_boundary);
1442 delaunay->Update();
1443
1444 cell_locator->SetDataSet(delaunay->GetOutput());
1445 cell_locator->BuildLocator();
1446 cell_locator->FindClosestPoint(c, x, otherId, subId, reverse_dist2);
1447
1448 if (reverse_dist2 < dist2) {
1449 output = delaunay->GetOutput();
1450 }
1451
1452 // Remove unused points
1453 vtkNew<vtkCleanPolyData> cleaner;
1454 SetVTKInput(cleaner, output);
1455 cleaner->ConvertStripsToPolysOff();
1456 cleaner->ConvertPolysToLinesOff();
1457 cleaner->ConvertLinesToPointsOff();
1458 cleaner->PointMergingOff();
1459 cleaner->Update();
1460 output = cleaner->GetOutput();
1461
1462 // Smooth divider
1463 // (needed when boundary points were snapped to surface mesh)
1464 MeshSmoothing smoother;
1465 smoother.Input(output);
1466 smoother.Mask(NonBoundaryPointsMask(output));
1467 smoother.Weighting(MeshSmoothing::Combinatorial);
1468 smoother.AdjacentValuesOnlyOn();
1469 smoother.SmoothPointsOn();
1470 smoother.NumberOfIterations(3);
1471 smoother.Lambda(1.);
1472 smoother.Run();
1473 output = smoother.Output();
1474
1475 return output;
1476 }
1477
1478 // -----------------------------------------------------------------------------
1479 /// Check if given intersection curve is acceptable
IsValidIntersection(vtkSmartPointer<vtkPolyData> cut,double tol)1480 bool IsValidIntersection(vtkSmartPointer<vtkPolyData> cut, double tol)
1481 {
1482 if (cut->GetNumberOfCells() == 0) return false;
1483 #if 1
1484 return true;
1485 #else
1486 vtkNew<vtkCleanPolyData> merger;
1487 SetVTKInput(merger, cut);
1488 merger->ConvertStripsToPolysOff();
1489 merger->ConvertPolysToLinesOff();
1490 merger->ConvertLinesToPointsOn();
1491 merger->PointMergingOn();
1492 merger->ToleranceIsAbsoluteOn();
1493 merger->SetAbsoluteTolerance(2. * tol);
1494 merger->Update();
1495 merger->GetOutput()->SetVerts(nullptr);
1496 cut = merger->GetOutput();
1497
1498 cut->BuildLinks();
1499
1500 vtkNew<vtkIdList> cellIds;
1501 for (vtkIdType ptId = 0; ptId < cut->GetNumberOfPoints(); ++ptId) {
1502 cut->GetPointCells(ptId, cellIds.GetPointer());
1503 if (cellIds->GetNumberOfIds() != 2 ||
1504 cut->GetCellType(cellIds->GetId(0)) != VTK_LINE ||
1505 cut->GetCellType(cellIds->GetId(1)) != VTK_LINE) {
1506 return false;
1507 }
1508 }
1509
1510 return true;
1511 #endif
1512 }
1513
1514 // -----------------------------------------------------------------------------
1515 /// Smooth line strip
SmoothLineStrip(vtkSmartPointer<vtkPolyData> cut,int niter=1)1516 void SmoothLineStrip(vtkSmartPointer<vtkPolyData> cut, int niter = 1)
1517 {
1518 vtkNew<vtkIdList> ptIds1, ptIds2, cellIds;
1519
1520 int i1, i2;
1521 Point p1, p2, p3;
1522
1523 cut->BuildLinks();
1524
1525 vtkSmartPointer<vtkPoints> points;
1526 points = vtkSmartPointer<vtkPoints>::New();
1527 points->SetNumberOfPoints(cut->GetNumberOfPoints());
1528
1529 for (int iter = 0; iter < niter; ++iter) {
1530 for (vtkIdType ptId = 0; ptId < cut->GetNumberOfPoints(); ++ptId) {
1531 cut->GetPointCells(ptId, cellIds.GetPointer());
1532 if (cellIds->GetNumberOfIds() == 2 &&
1533 cut->GetCellType(cellIds->GetId(0)) == VTK_LINE &&
1534 cut->GetCellType(cellIds->GetId(1)) == VTK_LINE) {
1535 cut->GetCellPoints(cellIds->GetId(0), ptIds1.GetPointer());
1536 cut->GetCellPoints(cellIds->GetId(1), ptIds2.GetPointer());
1537 i1 = (ptIds1->GetId(0) == ptId ? 1 : 0);
1538 i2 = (ptIds2->GetId(0) == ptId ? 1 : 0);
1539 cut->GetPoint(ptIds1->GetId(i1), p1);
1540 cut->GetPoint(ptIds2->GetId(i2), p2);
1541 cut->GetPoint(ptId, p3);
1542 points->SetPoint(ptId, (p1 + p2 + p3) / 3.);
1543 }
1544 }
1545 vtkSmartPointer<vtkPoints> tmp = cut->GetPoints();
1546 cut->SetPoints(points);
1547 points = tmp;
1548 }
1549 }
1550
1551 // -----------------------------------------------------------------------------
1552 /// Intersection incircle radius
LineStripIncircleRadius(vtkSmartPointer<vtkPolyData> cut)1553 double LineStripIncircleRadius(vtkSmartPointer<vtkPolyData> cut)
1554 {
1555 Point p, c;
1556 cut->GetCenter(c);
1557 double r = inf;
1558 for (vtkIdType ptId = 0; ptId < cut->GetNumberOfPoints(); ++ptId) {
1559 cut->GetPoint(ptId, p);
1560 r = min(r, p.Distance(c));
1561 }
1562 return r;
1563 }
1564
1565 // -----------------------------------------------------------------------------
1566 /// Intersection incircle radius
AverageLineStripRadius(vtkSmartPointer<vtkPolyData> cut)1567 double AverageLineStripRadius(vtkSmartPointer<vtkPolyData> cut)
1568 {
1569 Point p, c;
1570 cut->GetCenter(c);
1571 double r = 0.;
1572 for (vtkIdType ptId = 0; ptId < cut->GetNumberOfPoints(); ++ptId) {
1573 cut->GetPoint(ptId, p);
1574 r += p.Distance(c);
1575 }
1576 return r / cut->GetNumberOfPoints();
1577 }
1578
1579 // -----------------------------------------------------------------------------
1580 /// Length of line strip
LineStripLength(vtkSmartPointer<vtkPolyData> cut)1581 double LineStripLength(vtkSmartPointer<vtkPolyData> cut)
1582 {
1583 vtkNew<vtkIdList> ptIds;
1584 Point p1, p2;
1585 double l = 0.;
1586
1587 cut->BuildLinks();
1588
1589 for (vtkIdType cellId = 0; cellId < cut->GetNumberOfCells(); ++cellId) {
1590 cut->GetCellPoints(cellId, ptIds.GetPointer());
1591 if (cut->GetCellType(cellId) == VTK_LINE && ptIds->GetNumberOfIds() == 2) {
1592 cut->GetPoint(ptIds->GetId(0), p1);
1593 cut->GetPoint(ptIds->GetId(1), p2);
1594 l += p1.Distance(p2);
1595 }
1596 }
1597
1598 return l;
1599 }
1600
1601 // -----------------------------------------------------------------------------
1602 /// Average curvature of line strip
LineStripCurvature(vtkSmartPointer<vtkPolyData> cut)1603 double LineStripCurvature(vtkSmartPointer<vtkPolyData> cut)
1604 {
1605 vtkNew<vtkIdList> cellIds, ptIds1, ptIds2;
1606
1607 int i1, i2, n = 0;
1608 double l1, l2, angle, curv = 0.;
1609 Vector3 e1, e2;
1610 Point p1, p2, p3;
1611
1612 cut->GetPoint(cut->GetNumberOfPoints()-1, p1);
1613 cut->GetPoint(0, p2);
1614 e1 = p2 - p1;
1615 l1 = e1.Length();
1616
1617 cut->BuildLinks();
1618
1619 for (vtkIdType ptId = 0; ptId < cut->GetNumberOfPoints(); ++ptId) {
1620 cut->GetPointCells(ptId, cellIds.GetPointer());
1621 if (cellIds->GetNumberOfIds() == 2 &&
1622 cut->GetCellType(cellIds->GetId(0)) == VTK_LINE &&
1623 cut->GetCellType(cellIds->GetId(1)) == VTK_LINE) {
1624 cut->GetCellPoints(cellIds->GetId(0), ptIds1.GetPointer());
1625 cut->GetCellPoints(cellIds->GetId(1), ptIds2.GetPointer());
1626 i1 = (ptIds1->GetId(0) == ptId ? 1 : 0);
1627 i2 = (ptIds2->GetId(0) == ptId ? 1 : 0);
1628 cut->GetPoint(ptIds1->GetId(i1), p1);
1629 cut->GetPoint(ptIds2->GetId(i2), p2);
1630 cut->GetPoint(ptId, p3);
1631 e1 = p3 - p1;
1632 e2 = p2 - p3;
1633 l1 = e1.Length();
1634 l2 = e2.Length();
1635 angle = acos(e1.Dot(e2) / (l1 * l2));
1636 curv += angle * angle / (l1 + l2);
1637 ++n;
1638 }
1639 }
1640
1641 return curv / n;
1642 }
1643
1644 // -----------------------------------------------------------------------------
1645 /// Maximum angle made up by adjacent line segments
MaxLineStripAngle(vtkSmartPointer<vtkPolyData> cut)1646 double MaxLineStripAngle(vtkSmartPointer<vtkPolyData> cut)
1647 {
1648 vtkNew<vtkIdList> cellIds, ptIds1, ptIds2;
1649
1650 double angle = 0.;
1651 int i1, i2;
1652 Vector3 e1, e2;
1653 Point p1, p2, p3;
1654
1655 cut->BuildLinks();
1656
1657 for (vtkIdType ptId = 0; ptId < cut->GetNumberOfPoints(); ++ptId) {
1658 cut->GetPointCells(ptId, cellIds.GetPointer());
1659 if (cellIds->GetNumberOfIds() == 2 &&
1660 cut->GetCellType(cellIds->GetId(0)) == VTK_LINE &&
1661 cut->GetCellType(cellIds->GetId(1)) == VTK_LINE) {
1662 cut->GetCellPoints(cellIds->GetId(0), ptIds1.GetPointer());
1663 cut->GetCellPoints(cellIds->GetId(1), ptIds2.GetPointer());
1664 i1 = (ptIds1->GetId(0) == ptId ? 1 : 0);
1665 i2 = (ptIds2->GetId(0) == ptId ? 1 : 0);
1666 cut->GetPoint(ptIds1->GetId(i1), p1);
1667 cut->GetPoint(ptIds2->GetId(i2), p2);
1668 cut->GetPoint(ptId, p3);
1669 e1 = p3 - p1;
1670 e2 = p2 - p3;
1671 e1.Normalize();
1672 e2.Normalize();
1673 angle = max(angle, acos(e1.Dot(e2)));
1674 }
1675 }
1676
1677 return angle;
1678 }
1679
1680 // -----------------------------------------------------------------------------
DividerArea(vtkSmartPointer<vtkPolyData> divider)1681 double DividerArea(vtkSmartPointer<vtkPolyData> divider)
1682 {
1683 vtkPolygon *polygon = vtkPolygon::SafeDownCast(divider->GetCell(0));
1684 return polygon->ComputeArea();
1685 }
1686
1687 // -----------------------------------------------------------------------------
DividerError(vtkSmartPointer<vtkPolyData> divider,vtkSmartPointer<vtkPoints> points)1688 double DividerError(vtkSmartPointer<vtkPolyData> divider, vtkSmartPointer<vtkPoints> points)
1689 {
1690 vtkIdType cellId;
1691 int subId;
1692 double sum = 0., dist2;
1693 Point p, x;
1694 vtkNew<vtkCellLocator> locator;
1695 locator->SetDataSet(divider);
1696 locator->BuildLocator();
1697 for (vtkIdType ptId = 0; ptId < points->GetNumberOfPoints(); ++ptId) {
1698 points->GetPoint(ptId, p);
1699 locator->FindClosestPoint(p, x, cellId, subId, dist2);
1700 sum += dist2;
1701 }
1702 return sqrt(sum / points->GetNumberOfPoints());
1703 }
1704
1705 // -----------------------------------------------------------------------------
1706 /// Find cutting plane which intersects surface with one closed intersection boundary
FindCuttingPlane(vtkSmartPointer<vtkPolyData> surface,vtkSmartPointer<vtkPointSet> boundary,vtkSmartPointer<vtkPolyData> & plane,vtkSmartPointer<vtkPolyData> & cut,double max_offset,double ds=0.,double tol=0.)1707 bool FindCuttingPlane(vtkSmartPointer<vtkPolyData> surface,
1708 vtkSmartPointer<vtkPointSet> boundary,
1709 vtkSmartPointer<vtkPolyData> &plane,
1710 vtkSmartPointer<vtkPolyData> &cut,
1711 double max_offset, double ds = 0., double tol = 0.)
1712 {
1713 static int call = 0; ++call;
1714
1715 MIRTK_START_TIMING();
1716
1717 if (ds <= 0.) ds = AverageEdgeLength(surface);
1718
1719 // Abort search after the specified number of suitable planes were found
1720 const int max_suitable_planes = 10;
1721 const bool prefer_default_plane = false;
1722
1723 // Compute cutting plane from segmentation boundary points
1724 const PlaneAttributes attr = CuttingPlaneAttributes(boundary);
1725
1726 const double max_angle = 20.;
1727 const double delta_angle = 5.;
1728
1729 const double delta_offset = max_offset / 4.;
1730
1731 const double margin = 5. * ds;
1732 const double bbmargin = 20. * ds;
1733
1734 double r, l;
1735 double best_tn, best_rx, best_ry, best_l = inf;
1736 vtkIdType best_ct = 0;
1737
1738 // Remove cells which are never cut to speed up vtkPolyDataIntersectionFilter
1739 Point center;
1740 double bounds[6], cell_bounds[6];
1741 plane = CuttingPlane(attr, 0., 0., 0., 0.);
1742 plane->GetBounds(bounds);
1743 plane->GetCenter(center);
1744 for (int i = 0; i < 6; i += 2) {
1745 bounds[i ] -= bbmargin;
1746 bounds[i+1] += bbmargin;
1747 }
1748 plane = nullptr;
1749
1750 vtkSmartPointer<vtkPolyData> cells;
1751 cells.TakeReference(surface->NewInstance());
1752 cells->ShallowCopy(surface);
1753 cells->DeleteCells();
1754 cells->BuildCells();
1755 for (vtkIdType cellId = 0; cellId < surface->GetNumberOfCells(); ++cellId) {
1756 surface->GetCell(cellId)->GetBounds(cell_bounds);
1757 if (cell_bounds[0] > bounds[1] || cell_bounds[1] < bounds[0] ||
1758 cell_bounds[2] > bounds[3] || cell_bounds[3] < bounds[2] ||
1759 cell_bounds[4] > bounds[5] || cell_bounds[5] < bounds[4]) {
1760 cells->DeleteCell(cellId);
1761 }
1762 }
1763 cells->RemoveDeletedCells();
1764
1765 if (debug > 1) {
1766 char fname[64];
1767 snprintf(fname, 64, "debug_cutting_cells_%d.vtp", call);
1768 WritePolyData(fname, cells);
1769 }
1770
1771 const auto prev_cout_precision = cout.precision();
1772 const auto prev_cout_flags = cout.flags();
1773
1774 const streamsize offset_precision = 3;
1775 const streamsize angle_precision = 0;
1776
1777 // Try different normal offsets and plane rotations in the order of preference
1778 int n = 0, m = 0;
1779 bool abort = false;
1780 for (double angle = 0., rx, ry; angle <= max_angle && !abort; angle += delta_angle) {
1781 for (int dim = (angle > 0. ? 0 : 1); dim < 2 && !abort; ++dim) {
1782 if (dim == 0) {
1783 rx = angle;
1784 ry = 0.;
1785 } else {
1786 rx = 0.;
1787 ry = angle;
1788 }
1789 for (double tn = 0.; tn <= max_offset && !abort; tn += delta_offset) {
1790 for (double sry = (ry > 0. ? -1. : 1.); sry <= 1. && !abort; sry += 2.)
1791 for (double srx = (rx > 0. ? -1. : 1.); srx <= 1. && !abort; srx += 2.)
1792 for (double stn = (tn > 0. ? -1. : 1.); stn <= 1. && !abort; stn += 2.) {
1793 if (verbose > 4) {
1794 cout << " Cutting plane with: offset = " << fixed
1795 << setprecision(offset_precision) << setw(offset_precision+3) << stn * tn
1796 << ", alpha = " << setprecision(angle_precision) << setw(angle_precision+3) << srx * rx
1797 << ", beta = " << setprecision(angle_precision) << setw(angle_precision+3) << sry * ry
1798 << " ...";
1799 }
1800 plane = CuttingPlane(attr, stn * tn, srx * rx, sry * ry, margin);
1801 cut = LargestClosedIntersection(cells, plane);
1802 if (IsValidIntersection(cut, tol)) {
1803 if (verbose == 4) {
1804 cout << " Cutting plane with: offset = " << fixed
1805 << setprecision(offset_precision) << setw(offset_precision+3) << stn * tn
1806 << ", alpha = " << setprecision(angle_precision) << setw(angle_precision+3) << srx * rx
1807 << ", beta = " << setprecision(angle_precision) << setw(angle_precision+3) << sry * ry
1808 << " ...";
1809 }
1810 bool accept = false;
1811 SmoothLineStrip(cut, 2);
1812 r = LineStripIncircleRadius(cut);
1813 if (center.Distance(cut->GetCenter()) < r) {
1814 ++m;
1815 if (verbose == 3) {
1816 cout << " Cutting plane with: offset = " << fixed
1817 << setprecision(offset_precision) << setw(offset_precision+3) << stn * tn
1818 << ", alpha = " << setprecision(angle_precision) << setw(angle_precision+3) << srx * rx
1819 << ", beta = " << setprecision(angle_precision) << setw(angle_precision+3) << sry * ry
1820 << " ...";
1821 }
1822 l = LineStripLength(cut);
1823 if (best_ct == 0 || l < .95 * best_l) {
1824 accept = true;
1825 }
1826 if (accept) {
1827 if (verbose > 2) cout << " accepted";
1828 best_tn = stn * tn;
1829 best_rx = srx * rx;
1830 best_ry = sry * ry;
1831 best_l = l;
1832 best_ct = cut->GetNumberOfCells();
1833 if (prefer_default_plane && best_tn == 0. && best_rx == 0. && best_ry == 0.) {
1834 abort = true;
1835 }
1836 } else {
1837 if (verbose > 2) cout << " rejected";
1838 }
1839 if (verbose > 2) {
1840 vtkSmartPointer<vtkPolyData> divider = Divider(cut);
1841 double area = DividerArea(divider);
1842 double error = DividerError(divider, boundary->GetPoints());
1843 cout << ": perimeter = " << setprecision(5) << setw(8) << l;
1844 cout << ", area = " << setprecision(5) << setw(8) << area;
1845 cout << ", RMS = " << setprecision(5) << setw(8) << error;
1846 cout << endl;
1847 }
1848 if (m >= max_suitable_planes && best_ct > 0) {
1849 abort = true;
1850 }
1851 } else {
1852 if (verbose > 3) cout << " rejected" << endl;
1853 }
1854 if (debug > 1) {
1855 if (debug > 2 || center.Distance(cut->GetCenter()) < r) {
1856 ++n;
1857 char fname[64];
1858 const char *suffix = accept ? "accepted" : "rejected";
1859 snprintf(fname, 64, "debug_cutting_plane_%d_%d_%s.vtp", call, n, suffix);
1860 WritePolyData(fname, plane);
1861 snprintf(fname, 64, "debug_cutting_lines_%d_%d_%s.vtp", call, n, suffix);
1862 WritePolyData(fname, cut);
1863 }
1864 }
1865 } else {
1866 if (verbose > 4) cout << " invalid" << endl;
1867 }
1868 }
1869 }
1870 }
1871 }
1872
1873 // Pick best plane
1874 if (best_ct > 0) {
1875 if (verbose > 1) {
1876 cout << " Found cutting plane with: offset = " << fixed
1877 << setprecision(offset_precision) << setw(offset_precision+3) << best_tn
1878 << ", alpha = " << setprecision(angle_precision) << setw(angle_precision+3) << best_rx
1879 << ", beta = " << setprecision(angle_precision) << setw(angle_precision+3) << best_ry << endl;
1880 }
1881 plane = CuttingPlane(attr, best_tn, best_rx, best_ry, margin);
1882 cut = LargestClosedIntersection(surface, plane);
1883 }
1884
1885 cout.precision(prev_cout_precision);
1886 cout.flags(prev_cout_flags);
1887
1888 MIRTK_DEBUG_TIMING(2, "FindCuttingPlane");
1889 return best_ct > 0;
1890 }
1891
1892 // -----------------------------------------------------------------------------
1893 struct EdgeIntersection
1894 {
1895 vtkIdType i; // Edge intersection point index
1896 double t; // Edge interpolation parameter
1897
EdgeIntersectionEdgeIntersection1898 EdgeIntersection() : i(-1), t(0.) {}
EdgeIntersectionEdgeIntersection1899 EdgeIntersection(vtkIdType i, double t) : i(i), t(t) {}
1900 };
1901
1902 // -----------------------------------------------------------------------------
1903 struct TriangleIntersections
1904 {
1905 EdgeIntersection edge[3];
1906 };
1907
1908 typedef UnorderedMap<vtkIdType, TriangleIntersections> TriangleIntersectionsMap;
1909
1910 // -----------------------------------------------------------------------------
InsertEdgeIntersection(TriangleIntersectionsMap & intersections,vtkPolyData * surface,vtkIdType cellId,int edge,Vector3 & x,double t)1911 inline void InsertEdgeIntersection(TriangleIntersectionsMap &intersections,
1912 vtkPolyData *surface, vtkIdType cellId,
1913 int edge, Vector3 &x, double t)
1914 {
1915 vtkNew<vtkIdList> cellIds, cellPtIds, nbrPtIds;
1916 vtkIdType newPtId, nbrId;
1917 int nbrEdge;
1918 double nbrParam;
1919
1920 auto it = intersections.find(cellId);
1921 if (it == intersections.end()) {
1922 it = intersections.insert(MakePair(cellId, TriangleIntersections())).first;
1923 }
1924 auto &intersection = it->second.edge[edge];
1925 if (intersection.i < 0) {
1926 newPtId = surface->GetPoints()->InsertNextPoint(const_cast<Vector3 &>(x));
1927 intersection.i = newPtId;
1928 intersection.t = t;
1929 } else {
1930 newPtId = intersection.i;
1931 surface->GetPoint(newPtId, x);
1932 }
1933
1934 surface->GetCellPoints(cellId, cellPtIds.GetPointer());
1935 const vtkIdType ptId1 = cellPtIds->GetId(edge);
1936 const vtkIdType ptId2 = cellPtIds->GetId((edge + 1) % cellPtIds->GetNumberOfIds());
1937 surface->GetCellEdgeNeighbors(cellId, ptId1, ptId2, cellIds.GetPointer());
1938
1939 for (vtkIdType j = 0; j < cellIds->GetNumberOfIds(); ++j) {
1940 nbrId = cellIds->GetId(j);
1941 nbrEdge = -1;
1942 surface->GetCellPoints(nbrId, nbrPtIds.GetPointer());
1943 for (vtkIdType k = 0; k < nbrPtIds->GetNumberOfIds(); ++k) {
1944 if (nbrPtIds->GetId(k) == ptId1 && nbrPtIds->GetId((k + 1) % nbrPtIds->GetNumberOfIds()) == ptId2) {
1945 nbrEdge = k;
1946 nbrParam = t;
1947 break;
1948 }
1949 if (nbrPtIds->GetId(k) == ptId2 && nbrPtIds->GetId((k + 1) % nbrPtIds->GetNumberOfIds()) == ptId1) {
1950 nbrEdge = k;
1951 nbrParam = 1. - t;
1952 break;
1953 }
1954 }
1955 if (nbrEdge != -1) {
1956 auto it = intersections.find(nbrId);
1957 if (it == intersections.end()) {
1958 it = intersections.insert(MakePair(nbrId, TriangleIntersections())).first;
1959 }
1960 auto &intersection = it->second.edge[nbrEdge];
1961 if (intersection.i < 0) {
1962 intersection.i = newPtId;
1963 intersection.t = nbrParam;
1964 }
1965 }
1966 }
1967 }
1968
1969 // -----------------------------------------------------------------------------
InterpolateEdge(vtkPointData * outputPD,vtkPointData * inputPD,vtkIdType ptId1,vtkIdType ptId2,const EdgeIntersection & intersection)1970 inline void InterpolateEdge(vtkPointData *outputPD, vtkPointData *inputPD,
1971 vtkIdType ptId1, vtkIdType ptId2,
1972 const EdgeIntersection &intersection)
1973 {
1974 outputPD->InterpolateEdge(inputPD, intersection.i, ptId1, ptId2, intersection.t);
1975 }
1976
1977 // -----------------------------------------------------------------------------
1978 /// Bisect first edge of triangle
Bisect(vtkPolyData * input,vtkIdType cellId,vtkIdType ptId1,vtkIdType ptId2,vtkIdType ptId3,const EdgeIntersection & intersection,vtkPolyData * output)1979 void Bisect(vtkPolyData *input, vtkIdType cellId,
1980 vtkIdType ptId1, vtkIdType ptId2, vtkIdType ptId3,
1981 const EdgeIntersection &intersection, vtkPolyData *output)
1982 {
1983 vtkCellArray * const polys = output->GetPolys();
1984 vtkPointData * const inputPD = input ->GetPointData();
1985 vtkPointData * const outputPD = output->GetPointData();
1986 vtkCellData * const inputCD = input ->GetCellData();
1987 vtkCellData * const outputCD = output->GetCellData();
1988
1989 const vtkIdType &newPtId1 = ptId1;
1990 const vtkIdType &newPtId2 = intersection.i;
1991 const vtkIdType &newPtId3 = ptId2;
1992 const vtkIdType &newPtId4 = ptId3;
1993 vtkIdType newCellId;
1994
1995 InterpolateEdge(outputPD, inputPD, ptId1, ptId2, intersection);
1996
1997 newCellId = polys->InsertNextCell(3);
1998 polys->InsertCellPoint(newPtId1);
1999 polys->InsertCellPoint(newPtId2);
2000 polys->InsertCellPoint(newPtId4);
2001 outputCD->CopyData(inputCD, cellId, newCellId);
2002
2003 newCellId = polys->InsertNextCell(3);
2004 polys->InsertCellPoint(newPtId2);
2005 polys->InsertCellPoint(newPtId3);
2006 polys->InsertCellPoint(newPtId4);
2007 outputCD->CopyData(inputCD, cellId, newCellId);
2008
2009 input->DeleteCell(cellId);
2010 }
2011
2012 // -----------------------------------------------------------------------------
2013 /// Bisect first and second edge of triangle
Trisect(vtkPolyData * input,vtkIdType cellId,vtkIdType ptId1,vtkIdType ptId2,vtkIdType ptId3,const EdgeIntersection & intersection1,const EdgeIntersection & intersection2,vtkPolyData * output)2014 void Trisect(vtkPolyData *input, vtkIdType cellId,
2015 vtkIdType ptId1, vtkIdType ptId2, vtkIdType ptId3,
2016 const EdgeIntersection &intersection1,
2017 const EdgeIntersection &intersection2,
2018 vtkPolyData *output)
2019 {
2020 vtkCellArray * const polys = output->GetPolys();
2021 vtkPointData * const inputPD = input ->GetPointData();
2022 vtkPointData * const outputPD = output->GetPointData();
2023 vtkCellData * const inputCD = input ->GetCellData();
2024 vtkCellData * const outputCD = output->GetCellData();
2025
2026 const vtkIdType &newPtId1 = ptId1;
2027 const vtkIdType &newPtId2 = intersection1.i;
2028 const vtkIdType &newPtId3 = ptId2;
2029 const vtkIdType &newPtId4 = intersection2.i;
2030 const vtkIdType &newPtId5 = ptId3;
2031 vtkIdType newCellId;
2032
2033 InterpolateEdge(outputPD, inputPD, ptId1, ptId2, intersection1);
2034 InterpolateEdge(outputPD, inputPD, ptId2, ptId3, intersection2);
2035
2036 newCellId = polys->InsertNextCell(3);
2037 polys->InsertCellPoint(newPtId1);
2038 polys->InsertCellPoint(newPtId2);
2039 polys->InsertCellPoint(newPtId5);
2040 outputCD->CopyData(inputCD, cellId, newCellId);
2041
2042 newCellId = polys->InsertNextCell(3);
2043 polys->InsertCellPoint(newPtId2);
2044 polys->InsertCellPoint(newPtId3);
2045 polys->InsertCellPoint(newPtId4);
2046 outputCD->CopyData(inputCD, cellId, newCellId);
2047
2048 newCellId = polys->InsertNextCell(3);
2049 polys->InsertCellPoint(newPtId4);
2050 polys->InsertCellPoint(newPtId5);
2051 polys->InsertCellPoint(newPtId2);
2052 outputCD->CopyData(inputCD, cellId, newCellId);
2053
2054 input->DeleteCell(cellId);
2055 }
2056
2057 // -----------------------------------------------------------------------------
2058 vtkSmartPointer<vtkPolyData>
AddClosedIntersectionDivider(vtkPolyData * surface,vtkPolyData * cut,double tol=0.)2059 AddClosedIntersectionDivider(vtkPolyData *surface, vtkPolyData *cut, double tol = 0.)
2060 {
2061 const double tol2 = tol * tol;
2062
2063 int bisect[3], subId, snapId;
2064 double pcoords[3], weights[3], dist2, t1, t2;
2065 vtkIdType lineId, cellId, ptId;
2066 Vector3 pt[3], x[2], p, q, closestPt;
2067
2068 Array<double> dists;
2069 Array<int> order;
2070 vtkNew<vtkIdList> ptIds, cellIds, cellPtIds, edge1, edge2, linePtIds;
2071 vtkNew<vtkGenericCell> cell;
2072 vtkSmartPointer<vtkDataArray> arr;
2073
2074 ptIds->Allocate(10);
2075 edge1->Allocate(2);
2076 edge2->Allocate(2);
2077
2078 // Determine edge length for tesselation of divider polygon
2079 const double mean_edge_length = AverageEdgeLength(surface);
2080
2081 // Build needed auxiliary structures
2082 cut->BuildCells();
2083 surface->BuildLinks();
2084
2085 // Polygonal data set of intersected surface cells
2086 vtkPoints * const points = surface->GetPoints();
2087
2088 vtkSmartPointer<vtkPolyData> split;
2089 vtkSmartPointer<vtkCellArray> polys;
2090
2091 polys = vtkSmartPointer<vtkCellArray>::New();
2092 polys->Allocate(polys->EstimateSize(3 * cut->GetNumberOfCells(), 3));
2093
2094 split = vtkSmartPointer<vtkPolyData>::New();
2095 split->SetPoints(points);
2096 split->SetPolys(polys);
2097
2098 split->GetPointData()->InterpolateAllocate(surface->GetPointData());
2099 split->GetCellData()->CopyAllocate(surface->GetCellData());
2100
2101 // Map (intersected) surface cell ID to intersection line ID(s)
2102 UnorderedMap<vtkIdType, List<vtkIdType>> cellIdToLineIdsMap;
2103 vtkDataArray * const inputIds = cut->GetCellData()->GetArray("Input0CellID");
2104 for (lineId = 0; lineId < cut->GetNumberOfCells(); ++lineId) {
2105 cellId = static_cast<vtkIdType>(inputIds->GetComponent(lineId, 0));
2106 cellIdToLineIdsMap[cellId].push_back(lineId);
2107 }
2108 cut->GetCellData()->RemoveArray("Input0CellID");
2109 cut->GetCellData()->RemoveArray("Input1CellID");
2110
2111 // Determine and insert new edge intersection points
2112 TriangleIntersectionsMap intersections;
2113 for (auto &entry : cellIdToLineIdsMap) {
2114
2115 // Get intersected surface cell
2116 cellId = entry.first;
2117 surface->GetCell(cellId, cell.GetPointer());
2118
2119 // Merge intersection lines at non-edge cell points, these are resulting
2120 // from the triangular tesselation of the intersection plane needed by
2121 // the vtkIntersectionPolyDataFilter
2122 if (entry.second.size() > 1) {
2123 ptIds->Reset();
2124 for (const auto &lineId : entry.second) {
2125 cut->GetCellPoints(lineId, linePtIds.GetPointer());
2126 for (int i = 0; i < linePtIds->GetNumberOfIds(); ++i) {
2127 ptIds->InsertUniqueId(linePtIds->GetId(i));
2128 }
2129 }
2130 dists.resize(ptIds->GetNumberOfIds());
2131 for (vtkIdType i = 0; i < ptIds->GetNumberOfIds(); ++i) {
2132 ptId = ptIds->GetId(i);
2133 cut->GetPoint(ptId, p);
2134 cell->EvaluatePosition(p, nullptr, subId, pcoords, dist2, weights);
2135 cell->CellBoundary(subId, pcoords, edge1.GetPointer());
2136 surface->GetPoint(edge1->GetId(0), x[0]);
2137 surface->GetPoint(edge1->GetId(1), x[1]);
2138 dists[i] = vtkLine::DistanceToLine(p, x[0], x[1], t1, closestPt);
2139 }
2140 order = IncreasingOrder(dists);
2141 edge1->SetNumberOfIds(2);
2142 edge1->SetId(0, ptIds->GetId(order[0]));
2143 edge1->SetId(1, ptIds->GetId(order[1]));
2144 auto lineIdIt = entry.second.begin();
2145 cut->ReplaceCell(*lineIdIt, 2, edge1->GetPointer(0));
2146 ++lineIdIt;
2147 while (lineIdIt != entry.second.end()) {
2148 cut->DeleteCell(*lineIdIt);
2149 ++lineIdIt;
2150 }
2151 entry.second.resize(1);
2152 }
2153
2154 // Determine which edges are being intersected
2155 surface->GetCellPoints(cellId, cellPtIds.GetPointer());
2156 surface->GetPoint(cellPtIds->GetId(0), pt[0]);
2157 surface->GetPoint(cellPtIds->GetId(1), pt[1]);
2158 surface->GetPoint(cellPtIds->GetId(2), pt[2]);
2159
2160 lineId = entry.second.front();
2161 cut->GetCellPoints(lineId, linePtIds.GetPointer());
2162 cut->GetPoint(linePtIds->GetId(0), p);
2163 cut->GetPoint(linePtIds->GetId(1), q);
2164
2165 // Snap first intersection line point to either edge point or node
2166 cell->EvaluatePosition(p, nullptr, subId, pcoords, dist2, weights);
2167 cell->CellBoundary(subId, pcoords, edge1.GetPointer());
2168 surface->GetPoint(edge1->GetId(0), x[0]);
2169 surface->GetPoint(edge1->GetId(1), x[1]);
2170
2171 snapId = -1;
2172 if (tol2 > 0.) {
2173 dists.resize(2);
2174 dists[0] = (p - x[0]).SquaredLength();
2175 dists[1] = (p - x[1]).SquaredLength();
2176 snapId = (dists[0] <= dists[1] ? 0 : 1);
2177 if (dists[snapId] < tol2) {
2178 p = x[snapId], t1 = 0.;
2179 edge1->SetId(snapId == 0 ? 1 : 0, edge1->GetId(snapId));
2180 }
2181 }
2182 if (snapId == -1) {
2183 vtkLine::DistanceToLine(p, x[0], x[1], t1, closestPt);
2184 t1 = clamp(t1, 0., 1.);
2185 p = closestPt;
2186 }
2187
2188 // Snap second intersection line point to either edge point or node
2189 cell->EvaluatePosition(q, nullptr, subId, pcoords, dist2, weights);
2190 cell->CellBoundary(subId, pcoords, edge2.GetPointer());
2191 surface->GetPoint(edge2->GetId(0), x[0]);
2192 surface->GetPoint(edge2->GetId(1), x[1]);
2193
2194 snapId = -1;
2195 if (tol2 > 0.) {
2196 dists.resize(2);
2197 dists[0] = (q - x[0]).SquaredLength();
2198 dists[1] = (q - x[1]).SquaredLength();
2199 snapId = (dists[0] <= dists[1] ? 0 : 1);
2200 if (dists[snapId] < tol2) {
2201 q = x[snapId], t2 = 0.;
2202 edge2->SetId(snapId == 0 ? 1 : 0, edge2->GetId(snapId));
2203 }
2204 }
2205 if (snapId == -1) {
2206 vtkLine::DistanceToLine(q, x[0], x[1], t2, closestPt);
2207 t2 = clamp(t2, 0., 1.);
2208 q = closestPt;
2209 }
2210
2211 // Insert (new) edge intersection points
2212 //
2213 // This also updates the edge intersections of neighboring cells,
2214 // including in particular those that are not themselves intersected
2215 // by any of the intersection lines. Edge intersection points are
2216 // inserted only once into the points list, thus ensuring that
2217 // intersection line segments share a common point.
2218 if (cellPtIds->GetNumberOfIds() != 3) {
2219 Throw(ERR_LogicError, __FUNCTION__, "Surface must have triangular faces");
2220 }
2221 if (edge1->GetId(0) == cellPtIds->GetId(0) && edge1->GetId(1) == cellPtIds->GetId(1)) {
2222 InsertEdgeIntersection(intersections, surface, cellId, 0, p, t1);
2223 } else if (edge2->GetId(0) == cellPtIds->GetId(0) && edge2->GetId(1) == cellPtIds->GetId(1)) {
2224 InsertEdgeIntersection(intersections, surface, cellId, 0, q, t2);
2225 }
2226 if (edge1->GetId(0) == cellPtIds->GetId(1) && edge1->GetId(1) == cellPtIds->GetId(2)) {
2227 InsertEdgeIntersection(intersections, surface, cellId, 1, p, t1);
2228 } else if (edge2->GetId(0) == cellPtIds->GetId(1) && edge2->GetId(1) == cellPtIds->GetId(2)) {
2229 InsertEdgeIntersection(intersections, surface, cellId, 1, q, t2);
2230 }
2231 if (edge1->GetId(0) == cellPtIds->GetId(2) && edge1->GetId(1) == cellPtIds->GetId(0)) {
2232 InsertEdgeIntersection(intersections, surface, cellId, 2, p, t1);
2233 } else if (edge2->GetId(0) == cellPtIds->GetId(2) && edge2->GetId(1) == cellPtIds->GetId(0)) {
2234 InsertEdgeIntersection(intersections, surface, cellId, 2, q, t2);
2235 }
2236 if (debug && (t1 != 0. || t2 != 0.) && intersections.find(cellId) == intersections.end()) {
2237 Throw(ERR_LogicError, __FUNCTION__, "No edge intersection recorded for intersected cell ", cellId);
2238 }
2239
2240 // Update line end points (**after** InsertEdgeIntersection)
2241 cut->GetPoints()->SetPoint(linePtIds->GetId(0), p);
2242 cut->GetPoints()->SetPoint(linePtIds->GetId(1), q);
2243 }
2244 cut->RemoveDeletedCells();
2245
2246 // Split surface cells at edge intersection points
2247 for (const auto &intersection : intersections) {
2248 cellId = intersection.first;
2249 const auto &edge = intersection.second.edge;
2250 bisect[0] = (edge[0].i >= 0 ? 1 : 0);
2251 bisect[1] = (edge[1].i >= 0 ? 1 : 0);
2252 bisect[2] = (edge[2].i >= 0 ? 1 : 0);
2253 surface->GetCellPoints(cellId, cellPtIds.GetPointer());
2254 switch (bisect[0] + bisect[1] + bisect[2]) {
2255 case 0: {
2256 Throw(ERR_LogicError, __FUNCTION__, "Exptected at least one edge intersection");
2257 } break;
2258 case 1: {
2259 if (bisect[0]) Bisect(surface, cellId, cellPtIds->GetId(0), cellPtIds->GetId(1), cellPtIds->GetId(2), edge[0], split);
2260 else if (bisect[1]) Bisect(surface, cellId, cellPtIds->GetId(1), cellPtIds->GetId(2), cellPtIds->GetId(0), edge[1], split);
2261 else Bisect(surface, cellId, cellPtIds->GetId(2), cellPtIds->GetId(0), cellPtIds->GetId(1), edge[2], split);
2262 } break;
2263 case 2: {
2264 if (bisect[0] && bisect[1]) Trisect(surface, cellId, cellPtIds->GetId(0), cellPtIds->GetId(1), cellPtIds->GetId(2), edge[0], edge[1], split);
2265 else if (bisect[1] && bisect[2]) Trisect(surface, cellId, cellPtIds->GetId(1), cellPtIds->GetId(2), cellPtIds->GetId(0), edge[1], edge[2], split);
2266 else Trisect(surface, cellId, cellPtIds->GetId(2), cellPtIds->GetId(0), cellPtIds->GetId(1), edge[2], edge[0], split);
2267 } break;
2268 case 3: {
2269 Throw(ERR_LogicError, __FUNCTION__, "Quadsection not possible when intersecting triangle with one line");
2270 } break;
2271 }
2272 }
2273 surface->RemoveDeletedCells();
2274
2275 if (debug) {
2276 static int callId = 0; ++callId;
2277 char fname[64];
2278 snprintf(fname, 64, "debug_split_surface_other_%d.vtp", callId);
2279 WritePolyData(fname, surface);
2280 snprintf(fname, 64, "debug_split_surface_cells_%d.vtp", callId);
2281 WritePolyData(fname, split);
2282 snprintf(fname, 64, "debug_split_surface_cut_%d.vtp", callId);
2283 WritePolyData(fname, cut);
2284 }
2285
2286 // Create polygonal mesh tesselation of closed intersection polygon
2287 vtkSmartPointer<vtkPolyData> divider = Divider(cut);
2288 if (debug) {
2289 static int callId = 0; ++callId;
2290 char fname[64];
2291 snprintf(fname, 64, "debug_split_surface_polygon_%d.vtp", callId);
2292 WritePolyData(fname, divider);
2293 }
2294 divider = TesselateDivider(divider, mean_edge_length);
2295
2296 // Prepare point/cell data before appending polygonal data sets
2297 vtkCellData * const surfaceCD = surface->GetCellData();
2298 vtkCellData * const dividerCD = divider->GetCellData();
2299 vtkCellData * const splitCD = split->GetCellData();
2300
2301 arr = surfaceCD->GetArray(SOURCE_ARRAY_NAME);
2302 int divider_source_index = min(0, static_cast<int>(arr->GetRange(0)[0])) - 1;
2303
2304 arr = splitCD->GetArray(SOURCE_ARRAY_NAME);
2305 if (!arr) {
2306 arr = NewVtkDataArray(VTK_SHORT, split->GetNumberOfCells(), 1, SOURCE_ARRAY_NAME);
2307 splitCD->AddArray(arr);
2308 }
2309 arr->FillComponent(0, 0.);
2310
2311 arr = dividerCD->GetArray(SOURCE_ARRAY_NAME);
2312 if (arr == nullptr) {
2313 arr = NewVtkDataArray(VTK_SHORT, divider->GetNumberOfCells(), 1, SOURCE_ARRAY_NAME);
2314 dividerCD->AddArray(arr);
2315 }
2316 arr->FillComponent(0, static_cast<double>(divider_source_index));
2317
2318 divider = CalculateNormals(divider, surface->GetPointData()->GetNormals() != nullptr,
2319 surface->GetCellData ()->GetNormals() != nullptr);
2320
2321 if (debug) {
2322 static int callId = 0; ++callId;
2323 char fname[64];
2324 snprintf(fname, 64, "debug_split_surface_divider_%d.vtp", callId);
2325 WritePolyData(fname, divider);
2326 }
2327
2328 // Merge surface with intersected cells and tesselated divider polygon
2329 vtkNew<vtkAppendPolyData> appender;
2330 AddVTKInput(appender, surface);
2331 AddVTKInput(appender, split);
2332 // divider added as input below
2333
2334 vtkNew<vtkCleanPolyData> merger;
2335 SetVTKConnection(merger, appender);
2336 merger->ConvertStripsToPolysOff();
2337 merger->ConvertPolysToLinesOff();
2338 merger->ConvertLinesToPointsOff();
2339 merger->PointMergingOn();
2340 merger->ToleranceIsAbsoluteOn();
2341 merger->SetAbsoluteTolerance(1e-12);
2342
2343 if (debug) {
2344 static int callId = 0; ++callId;
2345 char fname[64];
2346 snprintf(fname, 64, "debug_split_surface_interim_%d.vtp", callId);
2347 merger->Update();
2348 WritePolyData(fname, merger->GetOutput());
2349 }
2350
2351 AddVTKInput(appender, divider);
2352 merger->Update();
2353 vtkSmartPointer<vtkPolyData> output = merger->GetOutput();
2354
2355 // Remove those triangles from resulting mesh originating from the tesselated
2356 // divider whose three vertices are all on the divider boundary and that have
2357 // a corresponding cell in the split surface mesh, i.e., a duplicate triangle
2358 output->BuildLinks();
2359 vtkNew<vtkIdList> ptIds1, ptIds2;
2360 for (vtkIdType cellId = 0; cellId < output->GetNumberOfCells(); ++cellId) {
2361 if (output->GetCellType(cellId) != VTK_EMPTY_CELL) {
2362 output->GetCellPoints(cellId, ptIds1.GetPointer());
2363 for (vtkIdType i = 0; i < ptIds1->GetNumberOfIds(); ++i) {
2364 output->GetPointCells(ptIds1->GetId(i), cellIds.GetPointer());
2365 for (vtkIdType j = 0; j < cellIds->GetNumberOfIds(); ++j) {
2366 if (cellIds->GetId(j) > cellId && output->GetCellType(cellIds->GetId(j)) != VTK_EMPTY_CELL) {
2367 output->GetCellPoints(cellIds->GetId(j), ptIds2.GetPointer());
2368 if (ptIds1->GetNumberOfIds() == ptIds2->GetNumberOfIds()) {
2369 ptIds2->IntersectWith(ptIds1.GetPointer());
2370 if (ptIds1->GetNumberOfIds() == ptIds2->GetNumberOfIds()) {
2371 output->DeleteCell(cellIds->GetId(j));
2372 }
2373 }
2374 }
2375 }
2376 }
2377 }
2378 }
2379 output->RemoveDeletedCells();
2380
2381 return output;
2382 }
2383
2384 // -----------------------------------------------------------------------------
LabelSurfacePatches(vtkPolyData * surface)2385 void LabelSurfacePatches(vtkPolyData *surface)
2386 {
2387 SurfacePatches cc;
2388 cc.Input(surface);
2389 cc.Ordering(SurfacePatches::LargestFirst);
2390 cc.Run();
2391
2392 vtkDataArray * const patch_labels = cc.GetLabelsArray();
2393 vtkDataArray * const source_labels = surface->GetCellData()->GetArray(SOURCE_ARRAY_NAME);
2394
2395 UnorderedSet<double> used;
2396 UnorderedMap<double, int> bins;
2397 decltype(bins)::iterator bin;
2398 double patch_label, label;
2399 int count;
2400
2401 double min_label = source_labels->GetRange(0)[0];
2402 double max_label = source_labels->GetRange(0)[1];
2403
2404 for (int i = 0; i < cc.NumberOfPatches(); ++i) {
2405 bins.clear();
2406 patch_label = static_cast<double>(i + 1);
2407 for (vtkIdType cellId = 0; cellId < surface->GetNumberOfCells(); ++cellId) {
2408 if (patch_labels->GetComponent(cellId, 0) == patch_label) {
2409 label = source_labels->GetComponent(cellId, 0);
2410 if (label != 0.) {
2411 bin = bins.find(label);
2412 if (bin == bins.end()) bins[label] = 1;
2413 else bin->second += 1;
2414 }
2415 }
2416 }
2417 label = 0.;
2418 count = 0;
2419 for (bin = bins.begin(); bin != bins.end(); ++bin) {
2420 if (bin->second > count) {
2421 label = bin->first;
2422 count = bin->second;
2423 }
2424 }
2425 if (label == 0. || used.find(label) != used.end()) {
2426 if (label < 0.) {
2427 min_label -= 1.;
2428 label = min_label;
2429 } else {
2430 max_label += 1.;
2431 label = max_label;
2432 }
2433 }
2434 for (vtkIdType cellId = 0; cellId < surface->GetNumberOfCells(); ++cellId) {
2435 if (patch_labels->GetComponent(cellId, 0) == patch_label) {
2436 source_labels->SetComponent(cellId, 0, label);
2437 }
2438 }
2439 used.insert(label);
2440 }
2441 }
2442
2443 // -----------------------------------------------------------------------------
GrowSourceRegion(vtkPolyData * surface,bool ignore_border_edges)2444 void GrowSourceRegion(vtkPolyData *surface, bool ignore_border_edges)
2445 {
2446 vtkDataArray * const source = surface->GetCellData()->GetArray(SOURCE_ARRAY_NAME);
2447
2448 Queue<vtkIdType> active;
2449 UnorderedMap<double, int> bins;
2450 decltype(bins)::iterator bin;
2451 double label;
2452 int count;
2453 vtkIdType cellId, nbrId;
2454 vtkNew<vtkIdList> cellIds, ptIds;
2455
2456 for (cellId = 0; cellId < surface->GetNumberOfCells(); ++cellId) {
2457 if (source->GetComponent(cellId, 0) == 0.) {
2458 surface->GetCellPoints(cellId, ptIds.GetPointer());
2459 for (vtkIdType i = 0; i < ptIds->GetNumberOfIds(); ++i) {
2460 surface->GetCellEdgeNeighbors(cellId, ptIds->GetId(i), ptIds->GetId((i + 1) % ptIds->GetNumberOfIds()), cellIds.GetPointer());
2461 if (ignore_border_edges || cellIds->GetNumberOfIds() == 1) {
2462 for (vtkIdType j = 0; j < cellIds->GetNumberOfIds(); ++j) {
2463 nbrId = cellIds->GetId(j);
2464 label = source->GetComponent(nbrId, 0);
2465 if (label != 0.) {
2466 active.push(cellId);
2467 i = ptIds->GetNumberOfIds(); // break also cell points loop
2468 break;
2469 }
2470 }
2471 }
2472 }
2473 }
2474 }
2475 while (!active.empty()) {
2476 cellId = active.front();
2477 active.pop();
2478 if (source->GetComponent(cellId, 0) == 0.) {
2479 bins.clear();
2480 surface->GetCellPoints(cellId, ptIds.GetPointer());
2481 for (vtkIdType i = 0; i < ptIds->GetNumberOfIds(); ++i) {
2482 surface->GetCellEdgeNeighbors(cellId, ptIds->GetId(i), ptIds->GetId((i + 1) % ptIds->GetNumberOfIds()), cellIds.GetPointer());
2483 if (ignore_border_edges || cellIds->GetNumberOfIds() == 1) {
2484 for (vtkIdType j = 0; j < cellIds->GetNumberOfIds(); ++j) {
2485 nbrId = cellIds->GetId(j);
2486 label = source->GetComponent(nbrId, 0);
2487 if (label == 0.) {
2488 active.push(nbrId);
2489 } else {
2490 bin = bins.find(label);
2491 if (bin == bins.end()) bins[label] = 1;
2492 else bin->second += 1;
2493 }
2494 }
2495 }
2496 }
2497 label = 0.;
2498 count = 0;
2499 for (bin = bins.begin(); bin != bins.end(); ++bin) {
2500 if (bin->second > count) {
2501 label = bin->first;
2502 count = bin->second;
2503 }
2504 }
2505 source->SetComponent(cellId, 0, label);
2506 }
2507 }
2508 }
2509
2510 // =============================================================================
2511 // Main
2512 // =============================================================================
2513
2514 // -----------------------------------------------------------------------------
main(int argc,char * argv[])2515 int main(int argc, char *argv[])
2516 {
2517 REQUIRES_POSARGS(0);
2518
2519 Array<const char *> input_names;
2520 const char *output_name = nullptr;
2521 const char *labels_name = nullptr;
2522
2523 double labels_percentage = 33.;
2524 double tolerance = NaN;
2525 double snap_tolerance = 0.;
2526 int smooth_boundaries = -1;
2527 bool join_boundaries = true;
2528 double min_edge_length = NaN;
2529 double max_edge_length = NaN;
2530 double edge_length_sd = 2.; // SD factor of edge length range about mean
2531 int max_remesh_steps = 3; // max no. of remeshing steps
2532 int max_smooth_steps = 0; // (max) no. of smoothing steps
2533 int remesh_radius = 3; // intersection cell neighborhood connectivity radius
2534 int smooth_radius = 2; // intersection cell neighborhood connectivity radius
2535 double smooth_lambda = NaN;
2536 double smooth_mu = NaN;
2537 bool fill_source_label = true;
2538 int max_smooth_source = 0;
2539 bool output_largest_comp = false;
2540 bool add_dividers = false;
2541 bool output_point_normals = true;
2542 bool output_cell_normals = true;
2543
2544 const char *point_source_name = nullptr;
2545 const char *cell_source_name = nullptr;
2546
2547 if (NUM_POSARGS == 1) {
2548 output_name = POSARG(1);
2549 } else if (NUM_POSARGS > 1) {
2550 input_names.reserve(NUM_POSARGS-1);
2551 for (int i = 1; i < NUM_POSARGS; ++i) {
2552 input_names.push_back(POSARG(i));
2553 }
2554 output_name = POSARG(NUM_POSARGS);
2555 }
2556
2557 for (ALL_OPTIONS) {
2558 if (OPTION("-labels")) {
2559 labels_name = ARGUMENT;
2560 }
2561 else if (OPTION("-labels-percentage")) {
2562 PARSE_ARGUMENT(labels_percentage);
2563 }
2564 else if (OPTION("-i") || OPTION("-input")) {
2565 do {
2566 input_names.push_back(ARGUMENT);
2567 } while (HAS_ARGUMENT);
2568 }
2569 else if (OPTION("-o") || OPTION("-output")) {
2570 if (output_name != nullptr) {
2571 input_names.insert(input_names.begin() + NUM_POSARGS - 1, output_name);
2572 }
2573 output_name = ARGUMENT;
2574 }
2575 else if (OPTION("-source-array")) {
2576 point_source_name = cell_source_name = ARGUMENT;
2577 }
2578 else if (OPTION("-point-source-array")) {
2579 point_source_name = ARGUMENT;
2580 }
2581 else if (OPTION("-cell-source-array")) {
2582 cell_source_name = ARGUMENT;
2583 }
2584 else if (OPTION("-tolerance") || OPTION("-tol")) {
2585 PARSE_ARGUMENT(tolerance);
2586 }
2587 else if (OPTION("-snap-tolerance") || OPTION("-snap-tol")) {
2588 PARSE_ARGUMENT(snap_tolerance);
2589 }
2590 else if (OPTION("-smooth-boundaries") || OPTION("-boundary-smoothing")) {
2591 if (HAS_ARGUMENT) PARSE_ARGUMENT(smooth_boundaries);
2592 else smooth_boundaries = 3;
2593 }
2594 else if (OPTION("-nosmooth-boundaries") || OPTION("-noboundary-smoothing")) {
2595 smooth_boundaries = 0;
2596 }
2597 else if (OPTION("-edge-length")) {
2598 PARSE_ARGUMENT(min_edge_length);
2599 if (HAS_ARGUMENT) PARSE_ARGUMENT(max_edge_length);
2600 else max_edge_length = min_edge_length;
2601 if (min_edge_length < max_edge_length) {
2602 swap(min_edge_length, max_edge_length);
2603 }
2604 }
2605 else if (OPTION("-min-edge-length")) {
2606 PARSE_ARGUMENT(min_edge_length);
2607 }
2608 else if (OPTION("-max-edge-length")) {
2609 PARSE_ARGUMENT(max_edge_length);
2610 }
2611 else if (OPTION("-edge-length-sigma") || OPTION("-edge-length-sd")) {
2612 PARSE_ARGUMENT(edge_length_sd);
2613 }
2614 else if (OPTION("-remesh") || OPTION("-remeshing")) {
2615 if (HAS_ARGUMENT) PARSE_ARGUMENT(max_remesh_steps);
2616 else max_remesh_steps = 3;
2617 }
2618 else if (OPTION("-remeshing-iterations") || OPTION("-remesh-iterations")) {
2619 PARSE_ARGUMENT(max_remesh_steps);
2620 }
2621 else if (OPTION("-noremeshing") || OPTION("-noremesh")) {
2622 max_remesh_steps = 0;
2623 }
2624 else if (OPTION("-smooth") || OPTION("-smoothing")) {
2625 if (HAS_ARGUMENT) PARSE_ARGUMENT(max_smooth_steps);
2626 else max_smooth_steps = 100;
2627 }
2628 else if (OPTION("-smoothing-iterations") || OPTION("-smooth-iterations")) {
2629 PARSE_ARGUMENT(max_smooth_steps);
2630 }
2631 else if (OPTION("-smoothing-lambda") || OPTION("-smooth-lambda")) {
2632 PARSE_ARGUMENT(smooth_lambda);
2633 }
2634 else if (OPTION("-smoothing-mu") || OPTION("-smooth-mu")) {
2635 PARSE_ARGUMENT(smooth_mu);
2636 }
2637 else if (OPTION("-nosmoothing") || OPTION("-nosmooth")) {
2638 max_smooth_steps = 0;
2639 }
2640 else if (OPTION("-neighborhood") || OPTION("-neighbourhood") || OPTION("-radius")) {
2641 PARSE_ARGUMENT(remesh_radius);
2642 smooth_radius = remesh_radius;
2643 }
2644 else if (OPTION("-remeshing-neighborhood") || OPTION("-remeshing-neighbourhood") || OPTION("-remeshing-radius") ||
2645 OPTION("-remesh-neighborhood") || OPTION("-remesh-neighbourhood") || OPTION("-remesh-radius")) {
2646 PARSE_ARGUMENT(remesh_radius);
2647 }
2648 else if (OPTION("-smoothing-neighborhood") || OPTION("-smoothing-neighbourhood") || OPTION("-smoothing-radius") ||
2649 OPTION("-smooth-neighborhood") || OPTION("-smooth-neighbourhood") || OPTION("-smooth-radius")) {
2650 PARSE_ARGUMENT(smooth_radius);
2651 }
2652 else if (OPTION("-smooth-source") || OPTION("-source-smoothing")) {
2653 if (HAS_ARGUMENT) PARSE_ARGUMENT(max_smooth_source);
2654 else max_smooth_source = 10;
2655 }
2656 else if (OPTION("-nosmooth-source") || OPTION("-nosource-smoothing")) {
2657 max_smooth_source = 0;
2658 }
2659 else if (OPTION("-normals")) {
2660 if (HAS_ARGUMENT) {
2661 PARSE_ARGUMENT(output_point_normals);
2662 output_cell_normals = output_point_normals;
2663 } else {
2664 output_cell_normals = output_point_normals = true;
2665 }
2666 }
2667 else if (OPTION("-nonormals")) {
2668 output_cell_normals = output_point_normals = false;
2669 }
2670 else HANDLE_BOOLEAN_OPTION("point-normals", output_point_normals);
2671 else HANDLE_BOOLEAN_OPTION("cell-normals", output_point_normals);
2672 else HANDLE_BOOLEAN_OPTION("join", join_boundaries);
2673 else HANDLE_BOOLEAN_OPTION("largest", output_largest_comp);
2674 else HANDLE_BOOLEAN_OPTION("dividers", add_dividers);
2675 else HANDLE_COMMON_OR_UNKNOWN_OPTION();
2676 }
2677
2678 if (input_names.size() < 2) {
2679 FatalError("At least two -input surfaces required!");
2680 }
2681 if (output_name == nullptr) {
2682 FatalError("No -output surface file name specified!");
2683 }
2684
2685 if (join_boundaries) {
2686 if (smooth_boundaries < 0) smooth_boundaries = 3;
2687 } else {
2688 smooth_boundaries = max(smooth_boundaries, 0);
2689 remesh_radius = smooth_radius = 0;
2690 max_remesh_steps = max_smooth_steps = 0;
2691 }
2692 if (!IsNaN(min_edge_length) && min_edge_length < 0. &&
2693 !IsNaN(max_edge_length) && IsInf(max_edge_length)) {
2694 remesh_radius = 0;
2695 max_remesh_steps = 0;
2696 }
2697 if (IsNaN(smooth_lambda)) {
2698 if (IsNaN(smooth_mu)) smooth_mu = -.75;
2699 if (-1. < smooth_mu && smooth_mu < 0.) {
2700 smooth_lambda = abs(smooth_mu) - .01;
2701 } else {
2702 smooth_lambda = smooth_mu;
2703 }
2704 }
2705 if (cell_source_name == nullptr && point_source_name == nullptr) {
2706 fill_source_label = false;
2707 max_smooth_source = 0;
2708 }
2709
2710 // Read segmentation labels
2711 GreyImage labels_image;
2712 if (labels_name) {
2713 InitializeIOLibrary();
2714 labels_image.Read(labels_name);
2715 } else {
2716 FatalError("Can only merge surfaces with corresponding -labels image at the moment!");
2717 }
2718
2719 // Read input surfaces
2720 Array<vtkSmartPointer<vtkPolyData> > surfaces;
2721 surfaces.resize(input_names.size());
2722 for (size_t i = 0; i < input_names.size(); ++i) {
2723 surfaces[i] = ReadPolyData(input_names[i]);
2724 }
2725
2726 // Default tolerance
2727 if (IsNaN(tolerance)) {
2728 tolerance = max(max(labels_image.XSize(),
2729 labels_image.YSize()),
2730 labels_image.ZSize());
2731 }
2732
2733 // Add cell source arrays
2734 size_t source_offset = 1;
2735 vtkSmartPointer<vtkDataArray> cell_source;
2736 if (point_source_name) {
2737 cell_source = surfaces[0]->GetPointData()->GetArray(point_source_name);
2738 if (cell_source && cell_source->GetNumberOfComponents() == 1) {
2739 source_offset = max(source_offset, static_cast<size_t>(ceil(cell_source->GetRange(0)[1])));
2740 }
2741 cell_source = nullptr; // not a cell data array!
2742 }
2743 if (cell_source_name) {
2744 cell_source = surfaces[0]->GetCellData()->GetArray(cell_source_name);
2745 if (cell_source && cell_source->GetNumberOfComponents() == 1) {
2746 source_offset = max(source_offset, static_cast<size_t>(ceil(cell_source->GetRange(0)[1])));
2747 }
2748 }
2749 if (cell_source) {
2750 cell_source->SetName(SOURCE_ARRAY_NAME);
2751 cell_source = nullptr;
2752 } else {
2753 source_offset = 1;
2754 AddCellSourceArray(surfaces[0], source_offset);
2755 }
2756 for (size_t i = 1; i < surfaces.size(); ++i) {
2757 AddCellSourceArray(surfaces[i], source_offset + i);
2758 }
2759
2760 // Merge surfaces
2761 if (verbose > 0) {
2762 cout << "Merging surfaces at segmentation boundaries...";
2763 if (verbose > 1) cout << "\n";
2764 cout.flush();
2765 }
2766 vtkSmartPointer<vtkPolyData> output = surfaces[0];
2767 Array<vtkSmartPointer<vtkPolyData>> boundaries;
2768 {
2769 vtkSmartPointer<vtkPolyData> boundary;
2770 UnorderedSet<int> output_labels = InsideLabels(labels_image, labels_percentage, output);
2771 if (output_labels.empty()) {
2772 FatalError("Could not determine labels corresponding to the inside of the first input surface!");
2773 }
2774 for (size_t i = 1; i < surfaces.size(); ++i) {
2775 UnorderedSet<int> surface_labels = InsideLabels(labels_image, labels_percentage, surfaces[i]);
2776 if (surface_labels.empty()) {
2777 FatalError("Could not determine labels corresponding to the inside of the "
2778 << (i == 1 ? "1st" : (i == 2 ? "2nd" : (i == 3 ? "3rd" : ((ToString(i) + "th").c_str()))))
2779 << " input surface!");
2780 }
2781 boundary = LabelBoundary(labels_image, output_labels, surface_labels);
2782 output = Merge(output, surfaces[i], boundary, tolerance, smooth_boundaries, join_boundaries);
2783 output_labels.insert(surface_labels.begin(), surface_labels.end());
2784 boundaries.push_back(boundary);
2785 }
2786 surfaces.clear();
2787 }
2788 if (verbose > 0) {
2789 if (verbose > 1) cout << "Merging surfaces at segmentation boundaries...";
2790 cout << " done" << endl;
2791 }
2792
2793 if (join_boundaries) {
2794
2795 // Extract largest connected component
2796 if (output_largest_comp) {
2797 if (verbose > 0) {
2798 cout << "Extracting largest surface component...";
2799 cout.flush();
2800 }
2801 vtkNew<vtkPolyDataConnectivityFilter> lcc;
2802 SetVTKInput(lcc, output);
2803 lcc->SetExtractionModeToLargestRegion();
2804 lcc->Update();
2805 output = lcc->GetOutput();
2806 if (verbose > 0) cout << " done" << endl;
2807 }
2808
2809 // Recalculate surface normals and fix vertex order if necessary
2810 if (verbose > 0) {
2811 cout << "Calculating surface normals...";
2812 cout.flush();
2813 }
2814 const bool calc_cell_normals = true;
2815 output = CalculateNormals(output, output_point_normals, calc_cell_normals);
2816 if (verbose > 0) cout << " done" << endl;
2817
2818 if (debug > 0) {
2819 WritePolyData("debug_joined_boundaries.vtp", output);
2820 }
2821
2822 // Remesh newly inserted cells which are joining the intersection boundaries
2823 if (remesh_radius > 0 && max_remesh_steps > 0) {
2824 if (verbose > 0) {
2825 cout << "Remeshing surface near intersection boundaries...";
2826 cout.flush();
2827 }
2828
2829 output = Triangulate(output);
2830 output->BuildLinks();
2831
2832 vtkSmartPointer<vtkDataArray> mask;
2833 mask = IntersectionCellMask(output, remesh_radius);
2834
2835 double min_global_length, max_global_length;
2836 GetMinMaxEdgeLength(output, min_global_length, max_global_length);
2837 GetEdgeLengthRange(output, mask, min_edge_length, max_edge_length, edge_length_sd);
2838
2839 min_global_length -= 1e-12;
2840 max_global_length += 1e-12;
2841
2842 const vtkIdType ncells = output->GetNumberOfCells();
2843 vtkSmartPointer<vtkDataArray> min_edge_length_arr, max_edge_length_arr;
2844 min_edge_length_arr = NewVtkDataArray(VTK_FLOAT, ncells, 1, MIN_EDGE_LENGTH_ARRAY_NAME);
2845 max_edge_length_arr = NewVtkDataArray(VTK_FLOAT, ncells, 1, MAX_EDGE_LENGTH_ARRAY_NAME);
2846
2847 int n = 0;
2848 for (vtkIdType cellId = 0; cellId < ncells; ++cellId) {
2849 if (mask->GetComponent(cellId, 0) != 0.) {
2850 min_edge_length_arr->SetComponent(cellId, 0, min_edge_length);
2851 max_edge_length_arr->SetComponent(cellId, 0, max_edge_length);
2852 ++n;
2853 } else {
2854 min_edge_length_arr->SetComponent(cellId, 0, min_global_length);
2855 max_edge_length_arr->SetComponent(cellId, 0, max_global_length);
2856 }
2857 }
2858 mask = nullptr;
2859
2860 if (n > 0) {
2861 if (verbose > 1) {
2862 cout << "\n";
2863 cout << " No. of boundary cells = " << n << "\n";
2864 cout << " Edge length range = [" << min_edge_length << ", " << max_edge_length << "]\n";
2865 if (debug_time > 0) cout << "\n";
2866 cout.flush();
2867 }
2868 int nmelt = 0, ninvs = 0, nsubd = 0;
2869 for (int iter = 0; iter < max_remesh_steps; ++iter) {
2870 SurfaceRemeshing remesher;
2871 remesher.MeltTrianglesOff();
2872 remesher.MeltNodesOff();
2873 remesher.InvertTrianglesToIncreaseMinHeightOff();
2874 remesher.Input(output);
2875 if (iter == 0) {
2876 remesher.MinCellEdgeLengthArray(min_edge_length_arr);
2877 remesher.MaxCellEdgeLengthArray(max_edge_length_arr);
2878 min_edge_length_arr = nullptr;
2879 max_edge_length_arr = nullptr;
2880 }
2881 remesher.Run();
2882 if (remesher.NumberOfChanges() == 0) break;
2883 output = remesher.Output();
2884 if (debug > 0) {
2885 char fname[64];
2886 snprintf(fname, 64, "debug_remeshed_%d.vtp", iter+1);
2887 WritePolyData(fname, output);
2888 }
2889 nmelt += remesher.NumberOfMeltedEdges();
2890 ninvs += remesher.NumberOfInversions();
2891 nsubd += remesher.NumberOfBisections();
2892 nsubd += 2 * remesher.NumberOfTrisections();
2893 nsubd += 3 * remesher.NumberOfQuadsections();
2894 }
2895 if (verbose > 1) {
2896 if (debug_time > 0) cout << "\n";
2897 cout << " No. of edge-meltings = " << nmelt << "\n";
2898 cout << " No. of inversions = " << ninvs << "\n";
2899 cout << " No. of subdivisions = " << nsubd << "\n";
2900 cout.flush();
2901 }
2902 output->GetPointData()->RemoveArray(SurfaceRemeshing::MIN_EDGE_LENGTH);
2903 output->GetPointData()->RemoveArray(SurfaceRemeshing::MAX_EDGE_LENGTH);
2904 }
2905
2906 if (verbose > 0) {
2907 if (verbose > 1) {
2908 cout << "Remeshing surface near intersection boundaries...";
2909 }
2910 cout << " done" << endl;
2911 }
2912 }
2913
2914 // Smooth surface in vicinity of joined intersection boundaries to remove
2915 // small self-intersections introduced by joining and/or remeshing steps
2916 if (smooth_radius > 0 && max_smooth_steps > 0) {
2917 if (verbose > 0) {
2918 cout << "Smoothing surface near intersection boundaries...";
2919 cout.flush();
2920 }
2921 output->BuildLinks();
2922 MeshSmoothing smoother;
2923 smoother.Input(output);
2924 smoother.Mask(IntersectionPointMask(output, smooth_radius));
2925 smoother.Lambda(smooth_lambda);
2926 smoother.Mu(smooth_mu);
2927 smoother.NumberOfIterations(max_smooth_steps);
2928 if (IsNaN(smooth_mu) || fequal(smooth_lambda, smooth_mu)) {
2929 smoother.Weighting(MeshSmoothing::Gaussian);
2930 smoother.AdjacentValuesOnlyOff();
2931 } else {
2932 smoother.Weighting(MeshSmoothing::Combinatorial);
2933 smoother.AdjacentValuesOnlyOn();
2934 }
2935 smoother.SmoothPointsOn();
2936 smoother.Run();
2937 output->SetPoints(smoother.Output()->GetPoints());
2938 if (verbose > 0) cout << " done" << endl;
2939 if (debug > 0) {
2940 const int i = output->GetPointData()->AddArray(smoother.Mask());
2941 WritePolyData("debug_smoothed_transition.vtp", output);
2942 output->GetPointData()->RemoveArray(i);
2943 }
2944 }
2945
2946 // Insert cutting planes in reverse order
2947 if (add_dividers) {
2948 if (verbose > 0) {
2949 cout << "Adding internal division planes...";
2950 if (verbose > 1) cout << "\n";
2951 cout.flush();
2952 }
2953 const EdgeTable edgeTable(output);
2954 const double ds = AverageEdgeLength(output->GetPoints(), edgeTable);
2955 vtkSmartPointer<vtkPolyData> plane, polygon, cut;
2956 for (int i = static_cast<int>(boundaries.size()-1); i >= 0; --i) {
2957 string msg;
2958 if (verbose > 1) {
2959 const int n = i + 1;
2960 msg = " Adding division plane for ";
2961 if (n == 1) msg += "1st";
2962 else if (n == 2) msg += "2nd";
2963 else if (n == 3) msg += "3rd";
2964 else msg += ToString(n) + "th";
2965 msg += " boundary";
2966 cout << msg << "..." << endl;
2967 }
2968 if (FindCuttingPlane(output, boundaries[i], plane, cut, 2. * tolerance, ds, snap_tolerance)) {
2969 if (debug > 0) {
2970 char fname[64];
2971 snprintf(fname, 64, "debug_cutting_plane_%d.vtp", static_cast<int>(boundaries.size()) - i);
2972 WritePolyData(fname, plane);
2973 }
2974 if (debug > 0) {
2975 char fname[64];
2976 snprintf(fname, 64, "debug_cutting_polygon_%d.vtp", static_cast<int>(boundaries.size()) - i);
2977 WritePolyData(fname, cut);
2978 }
2979 output = AddClosedIntersectionDivider(output, cut, snap_tolerance);
2980 if (debug > 0) {
2981 char fname[64];
2982 snprintf(fname, 64, "debug_output+divider_%d.vtp", static_cast<int>(boundaries.size()) - i);
2983 WritePolyData(fname, output);
2984 }
2985 if (!msg.empty()) cout << msg << "... done" << endl;
2986 } else {
2987 if (verbose > 0) {
2988 if (!msg.empty()) cout << msg << "...";
2989 cout << " failed\n" << endl;
2990 }
2991 FatalError("Could not find a closed intersection with finite cutting plane near segmentation boundary!");
2992 }
2993 }
2994 if (verbose > 0) {
2995 if (verbose > 1) cout << "Adding internal division planes...";
2996 cout << " done" << endl;
2997 }
2998 }
2999
3000 } // if (join_boundaries)
3001
3002 // Clean up and rename or remove data source arrays
3003 output->BuildLinks();
3004
3005 if (fill_source_label) {
3006 if (verbose > 0) {
3007 cout << "Filling cell source labels...";
3008 cout.flush();
3009 }
3010 if (add_dividers) {
3011 LabelSurfacePatches(output);
3012 } else {
3013 GrowSourceRegion(output, false);
3014 GrowSourceRegion(output, true); // just in case... usually NOP
3015 }
3016 if (verbose > 0) cout << " done" << endl;
3017 }
3018
3019 if (max_smooth_source > 0) {
3020 if (verbose > 0) {
3021 cout << "Smoothing cell source labels...";
3022 cout.flush();
3023 }
3024
3025 UnorderedMap<double, int> bins;
3026 decltype(bins)::iterator bin;
3027 double label;
3028 int count;
3029
3030 vtkNew<vtkIdList> cellIds, ptIds;
3031 vtkSmartPointer<vtkDataArray> cell_source;
3032 cell_source = output->GetCellData()->GetArray(SOURCE_ARRAY_NAME);
3033
3034 for (int iter = 0; iter < max_smooth_source; ++iter) {
3035 bool modified = false;
3036 for (vtkIdType cellId = 0; cellId < output->GetNumberOfCells(); ++cellId) {
3037 bins.clear();
3038 output->GetCellPoints(cellId, ptIds.GetPointer());
3039 for (vtkIdType i = 0; i < ptIds->GetNumberOfIds(); ++i) {
3040 output->GetPointCells(ptIds->GetId(i), cellIds.GetPointer());
3041 for (vtkIdType j = 0; j < cellIds->GetNumberOfIds(); ++j) {
3042 if (cellIds->GetId(j) != cellId) {
3043 label = cell_source->GetComponent(cellIds->GetId(j), 0);
3044 bin = bins.find(label);
3045 if (bin == bins.end()) bins[label] = 1;
3046 else bin->second += 1;
3047 }
3048 }
3049 }
3050 label = cell_source->GetComponent(cellId, 0);
3051 bin = bins.find(label);
3052 count = (bin == bins.end() ? 0 : bin->second);
3053 for (bin = bins.begin(); bin != bins.end(); ++bin) {
3054 if (bin->second > count) {
3055 label = bin->first;
3056 count = bin->second;
3057 modified = true;
3058 }
3059 }
3060 cell_source->SetComponent(cellId, 0, label);
3061 }
3062 if (!modified) break;
3063 }
3064 if (verbose > 0) cout << " done" << endl;
3065 }
3066
3067 if ((output_point_normals && add_dividers) || point_source_name) {
3068 if (verbose > 0) {
3069 cout << "Generating point source labels...";
3070 cout.flush();
3071 }
3072
3073 double label;
3074 vtkNew<vtkIdList> cellIds;
3075 vtkSmartPointer<vtkDataArray> cell_source, point_source;
3076
3077 cell_source = output->GetCellData()->GetArray(SOURCE_ARRAY_NAME);
3078 point_source = NewVtkDataArray(cell_source->GetDataType(),
3079 output->GetNumberOfPoints(), 1,
3080 SOURCE_ARRAY_NAME); // renamed later
3081 output->GetPointData()->AddArray(point_source);
3082
3083 for (vtkIdType ptId = 0; ptId < output->GetNumberOfPoints(); ++ptId) {
3084 output->GetPointCells(ptId, cellIds.GetPointer());
3085 if (cellIds->GetNumberOfIds() == 0) {
3086 label = 0.;
3087 } else {
3088 label = cell_source->GetComponent(cellIds->GetId(0), 0);
3089 for (vtkIdType i = 1; i < cellIds->GetNumberOfIds(); ++i) {
3090 if (cell_source->GetComponent(cellIds->GetId(i), 0) != label) {
3091 label = 0.;
3092 break;
3093 }
3094 }
3095 }
3096 point_source->SetComponent(ptId, 0, label);
3097 }
3098
3099 if (verbose > 0) cout << " done" << endl;
3100 }
3101
3102 // Only now rename/remove not requested data arrays
3103 if (output_point_normals) {
3104 if (add_dividers) FixBorderPointNormals(output); // **before** renaming the source arrays
3105 } else {
3106 output->GetPointData()->SetNormals(nullptr);
3107 }
3108 if (!output_cell_normals) {
3109 output->GetCellData()->SetNormals(nullptr);
3110 }
3111 if (cell_source_name) {
3112 output->GetCellData()->GetArray(SOURCE_ARRAY_NAME)->SetName(cell_source_name);
3113 } else {
3114 output->GetCellData()->RemoveArray(SOURCE_ARRAY_NAME);
3115 }
3116 if (point_source_name) {
3117 output->GetPointData()->GetArray(SOURCE_ARRAY_NAME)->SetName(point_source_name);
3118 }
3119
3120 // Write output surface mesh
3121 if (!WritePolyData(output_name, output)) {
3122 FatalError("Failed to write output surface to " << output_name);
3123 }
3124
3125 return 0;
3126 }
3127