1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkClipClosedSurface.cxx
5
6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7 All rights reserved.
8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9
10 This software is distributed WITHOUT ANY WARRANTY; without even
11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12 PURPOSE. See the above copyright notice for more information.
13
14 =========================================================================*/
15 #include "vtkClipClosedSurface.h"
16
17 #include "vtkDataSet.h"
18 #include "vtkInformation.h"
19 #include "vtkInformationVector.h"
20 #include "vtkObjectFactory.h"
21 #include "vtkImageData.h"
22 #include "vtkPolyData.h"
23 #include "vtkPoints.h"
24 #include "vtkCellArray.h"
25 #include "vtkCellData.h"
26 #include "vtkPointData.h"
27 #include "vtkUnsignedCharArray.h"
28 #include "vtkSignedCharArray.h"
29 #include "vtkDoubleArray.h"
30 #include "vtkPlaneCollection.h"
31 #include "vtkMath.h"
32 #include "vtkPolygon.h"
33 #include "vtkTriangleStrip.h"
34 #include "vtkLine.h"
35 #include "vtkMatrix4x4.h"
36 #include "vtkContourTriangulator.h"
37
38 #include "vtkIncrementalOctreePointLocator.h"
39
40 #include <vector>
41 #include <algorithm>
42 #include <map>
43 #include <utility>
44
45 vtkStandardNewMacro(vtkClipClosedSurface);
46
47 vtkCxxSetObjectMacro(vtkClipClosedSurface,ClippingPlanes,vtkPlaneCollection);
48
49 //----------------------------------------------------------------------------
vtkClipClosedSurface()50 vtkClipClosedSurface::vtkClipClosedSurface()
51 {
52 this->ClippingPlanes = nullptr;
53 this->Tolerance = 1e-6;
54 this->PassPointData = 0;
55
56 this->ScalarMode = VTK_CCS_SCALAR_MODE_NONE;
57 this->GenerateOutline = 0;
58 this->GenerateFaces = 1;
59 this->ActivePlaneId = -1;
60
61 this->BaseColor[0] = 1.0;
62 this->BaseColor[1] = 0.0;
63 this->BaseColor[2] = 0.0;
64
65 this->ClipColor[0] = 1.0;
66 this->ClipColor[1] = 0.5;
67 this->ClipColor[2] = 0.0;
68
69 this->ActivePlaneColor[0] = 1.0;
70 this->ActivePlaneColor[1] = 1.0;
71 this->ActivePlaneColor[2] = 0.0;
72
73 this->TriangulationErrorDisplay = 0;
74
75 // A whole bunch of objects needed during execution
76 this->IdList = nullptr;
77 }
78
79 //----------------------------------------------------------------------------
~vtkClipClosedSurface()80 vtkClipClosedSurface::~vtkClipClosedSurface()
81 {
82 if (this->ClippingPlanes) { this->ClippingPlanes->Delete(); }
83
84 if (this->IdList) { this->IdList->Delete(); }
85 }
86
87 //----------------------------------------------------------------------------
GetScalarModeAsString()88 const char *vtkClipClosedSurface::GetScalarModeAsString()
89 {
90 switch (this->ScalarMode)
91 {
92 case VTK_CCS_SCALAR_MODE_NONE:
93 return "None";
94 case VTK_CCS_SCALAR_MODE_COLORS:
95 return "Colors";
96 case VTK_CCS_SCALAR_MODE_LABELS:
97 return "Labels";
98 }
99 return "";
100 }
101
102 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)103 void vtkClipClosedSurface::PrintSelf(ostream& os, vtkIndent indent)
104 {
105 this->Superclass::PrintSelf(os,indent);
106
107 os << indent << "ClippingPlanes: ";
108 if (this->ClippingPlanes)
109 {
110 os << this->ClippingPlanes << "\n";
111 }
112 else
113 {
114 os << "(none)\n";
115 }
116
117 os << indent << "Tolerance: " << this->Tolerance << "\n";
118
119 os << indent << "PassPointData: "
120 << (this->PassPointData ? "On\n" : "Off\n" );
121
122 os << indent << "GenerateOutline: "
123 << (this->GenerateOutline ? "On\n" : "Off\n" );
124
125 os << indent << "GenerateFaces: "
126 << (this->GenerateFaces ? "On\n" : "Off\n" );
127
128 os << indent << "ScalarMode: "
129 << this->GetScalarModeAsString() << "\n";
130
131 os << indent << "BaseColor: " << this->BaseColor[0] << ", "
132 << this->BaseColor[1] << ", " << this->BaseColor[2] << "\n";
133
134 os << indent << "ClipColor: " << this->ClipColor[0] << ", "
135 << this->ClipColor[1] << ", " << this->ClipColor[2] << "\n";
136
137 os << indent << "ActivePlaneId: " << this->ActivePlaneId << "\n";
138
139 os << indent << "ActivePlaneColor: " << this->ActivePlaneColor[0] << ", "
140 << this->ActivePlaneColor[1] << ", " << this->ActivePlaneColor[2] << "\n";
141
142 os << indent << "TriangulationErrorDisplay: "
143 << (this->TriangulationErrorDisplay ? "On\n" : "Off\n" );
144 }
145
146 //----------------------------------------------------------------------------
ComputePipelineMTime(vtkInformation * vtkNotUsed (request),vtkInformationVector ** vtkNotUsed (inputVector),vtkInformationVector * vtkNotUsed (outputVector),int vtkNotUsed (requestFromOutputPort),vtkMTimeType * mtime)147 int vtkClipClosedSurface::ComputePipelineMTime(
148 vtkInformation* vtkNotUsed(request),
149 vtkInformationVector** vtkNotUsed(inputVector),
150 vtkInformationVector* vtkNotUsed(outputVector),
151 int vtkNotUsed(requestFromOutputPort),
152 vtkMTimeType* mtime)
153 {
154 vtkMTimeType mTime = this->GetMTime();
155
156 vtkPlaneCollection *planes = this->ClippingPlanes;
157 vtkPlane *plane = nullptr;
158
159 if (planes)
160 {
161 vtkMTimeType planesMTime = planes->GetMTime();
162 if (planesMTime > mTime)
163 {
164 mTime = planesMTime;
165 }
166
167 vtkCollectionSimpleIterator iter;
168 planes->InitTraversal(iter);
169 while ( (plane = planes->GetNextPlane(iter)) )
170 {
171 vtkMTimeType planeMTime = plane->GetMTime();
172 if (planeMTime > mTime)
173 {
174 mTime = planeMTime;
175 }
176 }
177 }
178
179 *mtime = mTime;
180
181 return 1;
182 }
183
184 //----------------------------------------------------------------------------
185 // A helper class to quickly locate an edge, given the endpoint ids.
186 // It uses an stl map rather than a table partitioning scheme, since
187 // we have no idea how many entries there will be when we start. So
188 // the performance is approximately log(n).
189
190 class vtkCCSEdgeLocatorNode
191 {
192 public:
vtkCCSEdgeLocatorNode()193 vtkCCSEdgeLocatorNode() :
194 ptId0(-1), ptId1(-1), edgeId(-1), next(nullptr) {};
195
FreeList()196 void FreeList() {
197 vtkCCSEdgeLocatorNode *ptr = this->next;
198 while (ptr)
199 {
200 vtkCCSEdgeLocatorNode *tmp = ptr;
201 ptr = ptr->next;
202 tmp->next = nullptr;
203 delete tmp;
204 }
205 };
206
207 vtkIdType ptId0;
208 vtkIdType ptId1;
209 vtkIdType edgeId;
210 vtkCCSEdgeLocatorNode *next;
211 };
212
213
214 class vtkCCSEdgeLocator
215 {
216 private:
217 typedef std::map<vtkIdType, vtkCCSEdgeLocatorNode> MapType;
218 MapType EdgeMap;
219
220 public:
New()221 static vtkCCSEdgeLocator *New() {
222 return new vtkCCSEdgeLocator; };
223
Delete()224 void Delete() {
225 this->Initialize();
226 delete this; };
227
228 // Description:
229 // Initialize the locator.
230 void Initialize();
231
232 // Description:
233 // If edge (i0, i1) is not in the list, then it will be added and
234 // a pointer for storing the new edgeId will be returned.
235 // If edge (i0, i1) is in the list, then edgeId will be set to the
236 // stored value and a null pointer will be returned.
237 vtkIdType *InsertUniqueEdge(vtkIdType i0, vtkIdType i1, vtkIdType &edgeId);
238 };
239
Initialize()240 void vtkCCSEdgeLocator::Initialize()
241 {
242 for (MapType::iterator i = this->EdgeMap.begin();
243 i != this->EdgeMap.end();
244 ++i)
245 {
246 i->second.FreeList();
247 }
248 this->EdgeMap.clear();
249 }
250
InsertUniqueEdge(vtkIdType i0,vtkIdType i1,vtkIdType & edgeId)251 vtkIdType *vtkCCSEdgeLocator::InsertUniqueEdge(
252 vtkIdType i0, vtkIdType i1, vtkIdType &edgeId)
253 {
254 // Ensure consistent ordering of edge
255 if (i1 < i0)
256 {
257 vtkIdType tmp = i0;
258 i0 = i1;
259 i1 = tmp;
260 }
261
262 // Generate a integer key, try to make it unique
263 #ifdef VTK_USE_64BIT_IDS
264 vtkIdType key = ((i1 << 32) ^ i0);
265 #else
266 vtkIdType key = ((i1 << 16) ^ i0);
267 #endif
268
269 vtkCCSEdgeLocatorNode *node = &this->EdgeMap[key];
270
271 if (node->ptId1 < 0)
272 {
273 // Didn't find key, so add a new edge entry
274 node->ptId0 = i0;
275 node->ptId1 = i1;
276 return &node->edgeId;
277 }
278
279 // Search through the list for i0 and i1
280 if (node->ptId0 == i0 && node->ptId1 == i1)
281 {
282 edgeId = node->edgeId;
283 return nullptr;
284 }
285
286 int i = 1;
287 while (node->next != nullptr)
288 {
289 i++;
290 node = node->next;
291
292 if (node->ptId0 == i0 && node->ptId1 == i1)
293 {
294 edgeId = node->edgeId;
295 return nullptr;
296 }
297 }
298
299 // No entry for i1, so make one and return
300 node->next = new vtkCCSEdgeLocatorNode;
301 node = node->next;
302 node->ptId0 = i0;
303 node->ptId1 = i1;
304 node->edgeId = static_cast<vtkIdType>(this->EdgeMap.size()-1);
305 return &node->edgeId;
306 }
307
308 //----------------------------------------------------------------------------
RequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * outputVector)309 int vtkClipClosedSurface::RequestData(
310 vtkInformation *vtkNotUsed(request),
311 vtkInformationVector **inputVector,
312 vtkInformationVector *outputVector)
313 {
314 // Get the info objects
315 vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
316 vtkInformation *outInfo = outputVector->GetInformationObject(0);
317
318 // Get the input and output
319 vtkPolyData *input = vtkPolyData::SafeDownCast(
320 inInfo->Get(vtkDataObject::DATA_OBJECT()));
321 vtkPolyData *output = vtkPolyData::SafeDownCast(
322 outInfo->Get(vtkDataObject::DATA_OBJECT()));
323
324 // Create objects needed for temporary storage
325 if (this->IdList == nullptr) { this->IdList = vtkIdList::New(); }
326
327 // Get the input points
328 vtkPoints *inputPoints = input->GetPoints();
329 vtkIdType numPts = 0;
330 int inputPointsType = VTK_FLOAT;
331 if (inputPoints)
332 {
333 numPts = inputPoints->GetNumberOfPoints();
334 inputPointsType = inputPoints->GetDataType();
335 }
336
337 // Force points to double precision, copy the point attributes
338 vtkPoints *points = vtkPoints::New();
339 points->SetDataTypeToDouble();
340 points->SetNumberOfPoints(numPts);
341
342 vtkPointData *pointData = vtkPointData::New();
343 vtkPointData *inPointData = nullptr;
344
345 if (this->PassPointData)
346 {
347 inPointData = input->GetPointData();
348 pointData->InterpolateAllocate(inPointData, numPts, 0);
349 }
350
351 for (vtkIdType ptId = 0; ptId < numPts; ptId++)
352 {
353 double point[3];
354 inputPoints->GetPoint(ptId, point);
355 points->SetPoint(ptId, point);
356 // Point data is not copied from input
357 if (inPointData)
358 {
359 pointData->CopyData(inPointData, ptId, ptId);
360 }
361 }
362
363 // An edge locator to avoid point duplication while clipping
364 vtkCCSEdgeLocator *edgeLocator = vtkCCSEdgeLocator::New();
365
366 // A temporary polydata for the contour lines that are triangulated
367 vtkPolyData *tmpContourData = vtkPolyData::New();
368
369 // The cell scalars
370 vtkUnsignedCharArray *lineScalars = nullptr;
371 vtkUnsignedCharArray *polyScalars = nullptr;
372 vtkUnsignedCharArray *inputScalars = nullptr;
373
374 // For input scalars: the offsets to the various cell types
375 vtkIdType firstLineScalar = 0;
376 vtkIdType firstPolyScalar = 0;
377 vtkIdType firstStripScalar = 0;
378
379 // Make the colors to be used on the data.
380 int numberOfScalarComponents = 1;
381 unsigned char colors[3][3];
382
383 if (this->ScalarMode == VTK_CCS_SCALAR_MODE_COLORS)
384 {
385 numberOfScalarComponents = 3;
386 this->CreateColorValues(this->BaseColor, this->ClipColor,
387 this->ActivePlaneColor, colors);
388 }
389 else if (this->ScalarMode == VTK_CCS_SCALAR_MODE_LABELS)
390 {
391 colors[0][0] = 0;
392 colors[1][0] = 1;
393 colors[2][0] = 2;
394 }
395
396 // This is set if we have to work with scalars. The input scalars
397 // will be copied if they are unsigned char with 3 components, otherwise
398 // new scalars will be generated.
399 if (this->ScalarMode)
400 {
401 // Make the scalars
402 lineScalars = vtkUnsignedCharArray::New();
403 lineScalars->SetNumberOfComponents(numberOfScalarComponents);
404
405 vtkDataArray *tryInputScalars = input->GetCellData()->GetScalars();
406 // Get input scalars if they are RGB color scalars
407 if (tryInputScalars && tryInputScalars->IsA("vtkUnsignedCharArray") &&
408 numberOfScalarComponents == 3 &&
409 tryInputScalars->GetNumberOfComponents() == 3)
410 {
411 inputScalars = static_cast<vtkUnsignedCharArray *>(
412 input->GetCellData()->GetScalars());
413
414 vtkIdType numVerts = 0;
415 vtkIdType numLines = 0;
416 vtkIdType numPolys = 0;
417 vtkCellArray *tmpCellArray = nullptr;
418 if ( (tmpCellArray = input->GetVerts()) )
419 {
420 numVerts = tmpCellArray->GetNumberOfCells();
421 }
422 if ( (tmpCellArray = input->GetLines()) )
423 {
424 numLines = tmpCellArray->GetNumberOfCells();
425 }
426 if ( (tmpCellArray = input->GetPolys()) )
427 {
428 numPolys = tmpCellArray->GetNumberOfCells();
429 }
430 firstLineScalar = numVerts;
431 firstPolyScalar = numVerts + numLines;
432 firstStripScalar = numVerts + numLines + numPolys;
433 }
434 }
435
436 // Break the input lines into segments, generate scalars for lines
437 vtkCellArray *lines = vtkCellArray::New();
438 if (input->GetLines() && input->GetLines()->GetNumberOfCells() > 0)
439 {
440 this->BreakPolylines(input->GetLines(), lines, inputScalars,
441 firstLineScalar, lineScalars, colors[0]);
442 }
443
444 // Copy the polygons, convert strips to triangles
445 vtkCellArray *polys = nullptr;
446 int polyMax = 3;
447 if ((input->GetPolys() && input->GetPolys()->GetNumberOfCells() > 0) ||
448 (input->GetStrips() && input->GetStrips()->GetNumberOfCells() > 0))
449 {
450 // If there are line scalars, then poly scalars are needed too
451 if (lineScalars)
452 {
453 polyScalars = vtkUnsignedCharArray::New();
454 polyScalars->SetNumberOfComponents(numberOfScalarComponents);
455 }
456
457 polys = vtkCellArray::New();
458 this->CopyPolygons(input->GetPolys(), polys, inputScalars,
459 firstPolyScalar, polyScalars, colors[0]);
460 this->BreakTriangleStrips(input->GetStrips(), polys, inputScalars,
461 firstStripScalar, polyScalars, colors[0]);
462
463 // Check if the input has polys and quads or just triangles
464 vtkIdType npts = 0;
465 vtkIdType *pts = nullptr;
466 vtkCellArray *inPolys = input->GetPolys();
467 inPolys->InitTraversal();
468 while (inPolys->GetNextCell(npts, pts))
469 {
470 if (npts > polyMax)
471 {
472 polyMax = npts;
473 }
474 }
475 }
476
477 // Get the clipping planes
478 vtkPlaneCollection *planes = this->ClippingPlanes;
479
480 // Arrays for storing the clipped lines and polys.
481 vtkCellArray *newLines = vtkCellArray::New();
482 vtkCellArray *newPolys = nullptr;
483 if (polys)
484 {
485 newPolys = vtkCellArray::New();
486 }
487
488 // The point scalars, needed for clipping (not for the output!)
489 vtkDoubleArray *pointScalars = vtkDoubleArray::New();
490
491 // The line scalars, for coloring the outline
492 vtkCellData *inLineData = vtkCellData::New();
493 inLineData->CopyScalarsOn();
494 inLineData->SetScalars(lineScalars);
495 if (lineScalars)
496 {
497 lineScalars->Delete();
498 lineScalars = nullptr;
499 }
500
501 // The poly scalars, for coloring the faces
502 vtkCellData *inPolyData = vtkCellData::New();
503 inPolyData->CopyScalarsOn();
504 inPolyData->SetScalars(polyScalars);
505 if (polyScalars)
506 {
507 polyScalars->Delete();
508 polyScalars = nullptr;
509 }
510
511 // Also create output attribute data
512 vtkCellData *outLineData = vtkCellData::New();
513 outLineData->CopyScalarsOn();
514
515 vtkCellData *outPolyData = vtkCellData::New();
516 outPolyData->CopyScalarsOn();
517
518 // Go through the clipping planes and clip the input with each plane
519 vtkCollectionSimpleIterator iter;
520 int numPlanes = 0;
521 if (planes)
522 {
523 planes->InitTraversal(iter);
524 numPlanes = planes->GetNumberOfItems();
525 }
526
527 vtkPlane *plane = nullptr;
528 for (int planeId = 0;
529 planes && (plane = planes->GetNextPlane(iter));
530 planeId++)
531 {
532 this->UpdateProgress((planeId + 1.0)/(numPlanes + 1.0));
533 if (this->GetAbortExecute())
534 {
535 break;
536 }
537
538 // Is this the last cut plane? If so, generate triangles.
539 int triangulate = 5;
540 if (planeId == numPlanes-1)
541 {
542 triangulate = polyMax;
543 }
544
545 // Is this the active plane?
546 int active = (planeId == this->ActivePlaneId);
547
548 // Convert the plane into an easy-to-evaluate function
549 double pc[4];
550 plane->GetNormal(pc);
551 pc[3] = -vtkMath::Dot(pc, plane->GetOrigin());
552
553 // Create the clip scalars by evaluating the plane at each point
554 vtkIdType numPoints = points->GetNumberOfPoints();
555 pointScalars->SetNumberOfValues(numPoints);
556 for (vtkIdType pointId = 0; pointId < numPoints; pointId++)
557 {
558 double p[3];
559 points->GetPoint(pointId, p);
560 double val = p[0]*pc[0] + p[1]*pc[1] + p[2]*pc[2] + pc[3];
561 pointScalars->SetValue(pointId, val);
562 }
563
564 // Prepare the output scalars
565 outLineData->CopyAllocate(inLineData, 0, 0);
566 outPolyData->CopyAllocate(inPolyData, 0, 0);
567
568 // Reset the locator
569 edgeLocator->Initialize();
570
571 // Clip the lines
572 this->ClipLines(points, pointScalars, pointData, edgeLocator,
573 lines, newLines, inLineData, outLineData);
574
575 // Clip the polys
576 if (polys)
577 {
578 // Get the number of lines remaining after the clipping
579 vtkIdType numClipLines = newLines->GetNumberOfCells();
580
581 // Cut the polys to generate more lines
582 this->ClipAndContourPolys(points, pointScalars, pointData, edgeLocator,
583 triangulate, polys, newPolys, newLines,
584 inPolyData, outPolyData, outLineData);
585
586 // Add scalars for the newly-created contour lines
587 vtkUnsignedCharArray *scalars =
588 vtkArrayDownCast<vtkUnsignedCharArray>(outLineData->GetScalars());
589
590 if (scalars)
591 {
592 // Set the color to the active color if plane is active
593 unsigned char *color = colors[1+active];
594 unsigned char *activeColor = colors[2];
595
596 vtkIdType numLines = newLines->GetNumberOfCells();
597 for (vtkIdType lineId = numClipLines; lineId < numLines; lineId++)
598 {
599 unsigned char oldColor[3];
600 scalars->GetTypedTuple(lineId, oldColor);
601 if (numberOfScalarComponents != 3 ||
602 oldColor[0] != activeColor[0] ||
603 oldColor[1] != activeColor[1] ||
604 oldColor[2] != activeColor[2])
605 {
606 scalars->SetTypedTuple(lineId, color);
607 }
608 }
609 }
610
611 // Generate new polys from the cut lines
612 vtkIdType cellId = newPolys->GetNumberOfCells();
613 vtkIdType numClipAndContourLines = newLines->GetNumberOfCells();
614
615 // Create a polydata for the lines
616 tmpContourData->SetPoints(points);
617 tmpContourData->SetLines(newLines);
618 tmpContourData->BuildCells();
619
620 this->TriangulateContours(tmpContourData, numClipLines,
621 numClipAndContourLines - numClipLines,
622 newPolys, pc);
623
624 // Add scalars for the newly-created polys
625 scalars = vtkArrayDownCast<vtkUnsignedCharArray>(outPolyData->GetScalars());
626
627 if (scalars)
628 {
629 unsigned char *color = colors[1+active];
630
631 vtkIdType numCells = newPolys->GetNumberOfCells();
632 if (numCells > cellId)
633 {
634 // The insert allocates space up to numCells-1
635 scalars->InsertTypedTuple(numCells-1, color);
636 for (;cellId < numCells; cellId++)
637 {
638 scalars->SetTypedTuple(cellId, color);
639 }
640 }
641 }
642
643 // Add scalars to any diagnostic lines that added by
644 // TriangulateContours(). In usual operation, no lines are added.
645 scalars = vtkArrayDownCast<vtkUnsignedCharArray>(outLineData->GetScalars());
646
647 if (scalars)
648 {
649 unsigned char color[3] = { 0, 255, 255 };
650
651 vtkIdType numCells = newLines->GetNumberOfCells();
652 if (numCells > numClipAndContourLines)
653 {
654 // The insert allocates space up to numCells-1
655 scalars->InsertTypedTuple(numCells-1, color);
656 for (vtkIdType lineCellId = numClipAndContourLines;
657 lineCellId < numCells; lineCellId++)
658 {
659 scalars->SetTypedTuple(lineCellId, color);
660 }
661 }
662 }
663 }
664
665 // Swap the lines, points, etcetera: old output becomes new input
666 vtkCellArray *tmp1 = lines;
667 lines = newLines;
668 newLines = tmp1;
669 newLines->Initialize();
670
671 if (polys)
672 {
673 vtkCellArray *tmp2 = polys;
674 polys = newPolys;
675 newPolys = tmp2;
676 newPolys->Initialize();
677 }
678
679 vtkCellData *tmp4 = inLineData;
680 inLineData = outLineData;
681 outLineData = tmp4;
682 outLineData->Initialize();
683
684 vtkCellData *tmp5 = inPolyData;
685 inPolyData = outPolyData;
686 outPolyData = tmp5;
687 outPolyData->Initialize();
688 }
689
690 // Delete the locator
691 edgeLocator->Delete();
692
693 // Delete the contour data container
694 tmpContourData->Delete();
695
696 // Delete the clip scalars
697 pointScalars->Delete();
698
699 // Get the line scalars
700 vtkUnsignedCharArray *scalars =
701 vtkArrayDownCast<vtkUnsignedCharArray>(inLineData->GetScalars());
702
703 if (this->GenerateOutline)
704 {
705 output->SetLines(lines);
706 }
707 else if (scalars)
708 {
709 // If not adding lines to output, clear the line scalars
710 scalars->Initialize();
711 }
712
713 if (this->GenerateFaces)
714 {
715 output->SetPolys(polys);
716
717 if (polys && scalars)
718 {
719 vtkUnsignedCharArray *pScalars =
720 vtkArrayDownCast<vtkUnsignedCharArray>(inPolyData->GetScalars());
721
722 vtkIdType m = scalars->GetNumberOfTuples();
723 vtkIdType n = pScalars->GetNumberOfTuples();
724
725 if (n > 0)
726 {
727 unsigned char color[3];
728 color[0] = color[1] = color[2] = 0;
729
730 // This is just to expand the array
731 scalars->InsertTypedTuple(n+m-1, color);
732
733 // Fill in the poly scalars
734 for (vtkIdType i = 0; i < n; i++)
735 {
736 pScalars->GetTypedTuple(i, color);
737 scalars->SetTypedTuple(i+m, color);
738 }
739 }
740 }
741 }
742
743 lines->Delete();
744
745 if (polys)
746 {
747 polys->Delete();
748 }
749
750 if (this->ScalarMode == VTK_CCS_SCALAR_MODE_COLORS)
751 {
752 scalars->SetName("Colors");
753 output->GetCellData()->SetScalars(scalars);
754 }
755 else if (this->ScalarMode == VTK_CCS_SCALAR_MODE_LABELS)
756 {
757 // Don't use VTK_UNSIGNED_CHAR or they will look like color scalars
758 vtkSignedCharArray *categories = vtkSignedCharArray::New();
759 categories->DeepCopy(scalars);
760 categories->SetName("Labels");
761 output->GetCellData()->SetScalars(categories);
762 categories->Delete();
763 }
764 else
765 {
766 output->GetCellData()->SetScalars(nullptr);
767 }
768
769 newLines->Delete();
770 if (newPolys)
771 {
772 newPolys->Delete();
773 }
774
775 inLineData->Delete();
776 outLineData->Delete();
777 inPolyData->Delete();
778 outPolyData->Delete();
779
780 // Finally, store the points in the output
781 this->SqueezeOutputPoints(output, points, pointData, inputPointsType);
782 output->Squeeze();
783
784 points->Delete();
785 pointData->Delete();
786
787 return 1;
788 }
789
790 //----------------------------------------------------------------------------
SqueezeOutputPoints(vtkPolyData * output,vtkPoints * points,vtkPointData * pointData,int outputPointDataType)791 void vtkClipClosedSurface::SqueezeOutputPoints(
792 vtkPolyData *output, vtkPoints *points, vtkPointData *pointData,
793 int outputPointDataType)
794 {
795 // Create a list of points used by cells
796 vtkIdType n = points->GetNumberOfPoints();
797 vtkIdType numNewPoints = 0;
798
799 // The point data
800 vtkPointData *outPointData = output->GetPointData();
801
802 // A mapping from old pointIds to new pointIds
803 vtkIdType *pointMap = new vtkIdType[n];
804 for (vtkIdType i = 0; i < n; i++)
805 {
806 pointMap[i] = -1;
807 }
808
809 vtkIdType npts, *pts;
810 vtkCellArray *cellArrays[4];
811 cellArrays[0] = output->GetVerts();
812 cellArrays[1] = output->GetLines();
813 cellArrays[2] = output->GetPolys();
814 cellArrays[3] = output->GetStrips();
815 int arrayId;
816
817 // Find all the newPoints that are used by cells
818 for (arrayId = 0; arrayId < 4; arrayId++)
819 {
820 vtkCellArray *cellArray = cellArrays[arrayId];
821 if (cellArray)
822 {
823 cellArray->InitTraversal();
824 while (cellArray->GetNextCell(npts, pts))
825 {
826 for (vtkIdType ii = 0; ii < npts; ii++)
827 {
828 vtkIdType pointId = pts[ii];
829 if (pointMap[pointId] < 0)
830 {
831 pointMap[pointId] = numNewPoints++;
832 }
833 }
834 }
835 }
836 }
837
838 // Create exactly the number of points that are required
839 vtkPoints *newPoints = vtkPoints::New();
840 newPoints->SetDataType(outputPointDataType);
841 newPoints->SetNumberOfPoints(numNewPoints);
842 outPointData->CopyAllocate(pointData, numNewPoints, 0);
843
844 for (vtkIdType pointId = 0; pointId < n; pointId++)
845 {
846 vtkIdType newPointId = pointMap[pointId];
847 if (newPointId >= 0)
848 {
849 double p[3];
850 points->GetPoint(pointId, p);
851 newPoints->SetPoint(newPointId, p);
852 outPointData->CopyData(pointData, pointId, newPointId);
853 }
854 }
855
856 // Change the cell pointIds to reflect the new point array
857 for (arrayId = 0; arrayId < 4; arrayId++)
858 {
859 vtkCellArray *cellArray = cellArrays[arrayId];
860 if (cellArray)
861 {
862 cellArray->InitTraversal();
863 while (cellArray->GetNextCell(npts, pts))
864 {
865 for (vtkIdType ii = 0; ii < npts; ii++)
866 {
867 vtkIdType pointId = pts[ii];
868 pts[ii] = pointMap[pointId];
869 }
870 }
871 }
872 }
873
874 output->SetPoints(newPoints);
875 newPoints->Delete();
876
877 delete [] pointMap;
878 }
879
880 //----------------------------------------------------------------------------
CreateColorValues(const double color1[3],const double color2[3],const double color3[3],unsigned char colors[3][3])881 void vtkClipClosedSurface::CreateColorValues(
882 const double color1[3], const double color2[3], const double color3[3],
883 unsigned char colors[3][3])
884 {
885 // Convert colors from "double" to "unsigned char"
886
887 const double *dcolors[3];
888 dcolors[0] = color1;
889 dcolors[1] = color2;
890 dcolors[2] = color3;
891
892 for (int i = 0; i < 3; i++)
893 {
894 for (int j = 0; j < 3; j++)
895 {
896 double val = dcolors[i][j];
897 if (val < 0) { val = 0; }
898 if (val > 1) { val = 1; }
899 colors[i][j] = static_cast<unsigned char>(val*255);
900 }
901 }
902 }
903
904 //----------------------------------------------------------------------------
905 // Point interpolation for clipping and contouring, given the scalar
906 // values (v0, v1) for the two endpoints (p0, p1). The use of this
907 // function guarantees perfect consistency in the results.
InterpolateEdge(vtkPoints * points,vtkPointData * pointData,vtkCCSEdgeLocator * locator,double tol,vtkIdType i0,vtkIdType i1,double v0,double v1,vtkIdType & i)908 int vtkClipClosedSurface::InterpolateEdge(
909 vtkPoints *points, vtkPointData *pointData, vtkCCSEdgeLocator *locator,
910 double tol, vtkIdType i0, vtkIdType i1, double v0, double v1,
911 vtkIdType &i)
912 {
913 // This swap guarantees that exactly the same point is computed
914 // for both line directions, as long as the endpoints are the same.
915 if (v1 > 0)
916 {
917 vtkIdType tmpi = i0;
918 i0 = i1;
919 i1 = tmpi;
920
921 double tmp = v0;
922 v0 = v1;
923 v1 = tmp;
924 }
925
926 // After the above swap, i0 will be kept, and i1 will be clipped
927
928 // Check to see if this point has already been computed
929 vtkIdType *iptr = locator->InsertUniqueEdge(i0, i1, i);
930 if (iptr == nullptr)
931 {
932 return 0;
933 }
934
935 // Get the edge and interpolate the new point
936 double p0[3], p1[3], p[3];
937 points->GetPoint(i0, p0);
938 points->GetPoint(i1, p1);
939
940 double f = v0/(v0 - v1);
941 double s = 1.0 - f;
942 double t = 1.0 - s;
943
944 p[0] = s*p0[0] + t*p1[0];
945 p[1] = s*p0[1] + t*p1[1];
946 p[2] = s*p0[2] + t*p1[2];
947
948 double tol2 = tol*tol;
949
950 // Make sure that new point is far enough from kept point
951 if (vtkMath::Distance2BetweenPoints(p, p0) < tol2)
952 {
953 i = i0;
954 *iptr = i0;
955 return 0;
956 }
957
958 if (vtkMath::Distance2BetweenPoints(p, p1) < tol2)
959 {
960 i = i1;
961 *iptr = i1;
962 return 0;
963 }
964
965 i = points->InsertNextPoint(p);
966 pointData->InterpolateEdge(pointData, i, i0, i1, t);
967
968 // Store the new index in the locator
969 *iptr = i;
970
971 return 1;
972 }
973
974 //----------------------------------------------------------------------------
ClipLines(vtkPoints * points,vtkDoubleArray * pointScalars,vtkPointData * pointData,vtkCCSEdgeLocator * edgeLocator,vtkCellArray * inputCells,vtkCellArray * outputLines,vtkCellData * inCellData,vtkCellData * outLineData)975 void vtkClipClosedSurface::ClipLines(
976 vtkPoints *points, vtkDoubleArray *pointScalars,
977 vtkPointData *pointData, vtkCCSEdgeLocator *edgeLocator,
978 vtkCellArray *inputCells, vtkCellArray *outputLines,
979 vtkCellData *inCellData, vtkCellData *outLineData)
980 {
981 vtkIdType numCells = inputCells->GetNumberOfCells();
982
983 inputCells->InitTraversal();
984 for (vtkIdType cellId = 0; cellId < numCells; cellId++)
985 {
986 vtkIdType numPts = 0;
987 vtkIdType *pts = nullptr;
988 inputCells->GetNextCell(numPts, pts);
989
990 vtkIdType i1 = pts[0];
991 double v1 = pointScalars->GetValue(i1);
992 int c1 = (v1 > 0);
993
994 for (vtkIdType i = 1; i < numPts; i++)
995 {
996 vtkIdType i0 = i1;
997 double v0 = v1;
998 int c0 = c1;
999
1000 i1 = pts[i];
1001 v1 = pointScalars->GetValue(i1);
1002 c1 = (v1 > 0);
1003
1004 // If at least one point wasn't clipped
1005 if ( (c0 | c1) )
1006 {
1007 vtkIdType linePts[2];
1008 linePts[0] = i0;
1009 linePts[1] = i1;
1010
1011 // If only one end was clipped, interpolate new point
1012 if ( (c0 ^ c1) )
1013 {
1014 vtkClipClosedSurface::InterpolateEdge(
1015 points, pointData, edgeLocator, this->Tolerance,
1016 i0, i1, v0, v1, linePts[c0]);
1017 }
1018
1019 // If endpoints are different, insert the line segment
1020 if (linePts[0] != linePts[1])
1021 {
1022 vtkIdType newCellId = outputLines->InsertNextCell(2, linePts);
1023 outLineData->CopyData(inCellData, cellId, newCellId);
1024 }
1025 }
1026 }
1027 }
1028 }
1029
1030 //----------------------------------------------------------------------------
ClipAndContourPolys(vtkPoints * points,vtkDoubleArray * pointScalars,vtkPointData * pointData,vtkCCSEdgeLocator * edgeLocator,int triangulate,vtkCellArray * inputCells,vtkCellArray * outputPolys,vtkCellArray * outputLines,vtkCellData * inCellData,vtkCellData * outPolyData,vtkCellData * outLineData)1031 void vtkClipClosedSurface::ClipAndContourPolys(
1032 vtkPoints *points, vtkDoubleArray *pointScalars, vtkPointData *pointData,
1033 vtkCCSEdgeLocator *edgeLocator, int triangulate,
1034 vtkCellArray *inputCells,
1035 vtkCellArray *outputPolys, vtkCellArray *outputLines,
1036 vtkCellData *inCellData,
1037 vtkCellData *outPolyData, vtkCellData *outLineData)
1038 {
1039 vtkIdList *idList = this->IdList;
1040
1041 // How many sides for output polygons?
1042 int polyMax = VTK_INT_MAX;
1043 if (triangulate)
1044 {
1045 if (triangulate < 4)
1046 { // triangles only
1047 polyMax = 3;
1048 }
1049 else if (triangulate == 4)
1050 { // allow triangles and quads
1051 polyMax = 4;
1052 }
1053 }
1054
1055 int triangulationFailure = 0;
1056
1057 // Go through all cells and clip them.
1058 vtkIdType numCells = inputCells->GetNumberOfCells();
1059
1060 inputCells->InitTraversal();
1061 for (vtkIdType cellId = 0; cellId < numCells; cellId++)
1062 {
1063 vtkIdType numPts = 0;
1064 vtkIdType *pts = nullptr;
1065 inputCells->GetNextCell(numPts, pts);
1066 idList->Reset();
1067
1068 vtkIdType i1 = pts[numPts-1];
1069 double v1 = pointScalars->GetValue(i1);
1070 int c1 = (v1 > 0);
1071
1072 // The ids for the current edge: init j0 to -1 if i1 will be clipped
1073 vtkIdType j0 = (c1 ? i1 : -1);
1074 vtkIdType j1 = 0;
1075
1076 // To store the ids of the contour line
1077 vtkIdType linePts[2];
1078 linePts[0] = 0;
1079 linePts[1] = 0;
1080
1081 for (vtkIdType i = 0; i < numPts; i++)
1082 {
1083 // Save previous point info
1084 vtkIdType i0 = i1;
1085 double v0 = v1;
1086 int c0 = c1;
1087
1088 // Generate new point info
1089 i1 = pts[i];
1090 v1 = pointScalars->GetValue(i1);
1091 c1 = (v1 > 0);
1092
1093 // If at least one edge end point wasn't clipped
1094 if ( (c0 | c1) )
1095 {
1096 // If only one end was clipped, interpolate new point
1097 if ( (c0 ^ c1) )
1098 {
1099 vtkClipClosedSurface::InterpolateEdge(
1100 points, pointData, edgeLocator, this->Tolerance,
1101 i0, i1, v0, v1, j1);
1102
1103 if (j1 != j0)
1104 {
1105 idList->InsertNextId(j1);
1106 j0 = j1;
1107 }
1108
1109 // Save as one end of the contour line
1110 linePts[c0] = j1;
1111 }
1112
1113 if (c1)
1114 {
1115 j1 = i1;
1116
1117 if (j1 != j0)
1118 {
1119 idList->InsertNextId(j1);
1120 j0 = j1;
1121 }
1122 }
1123 }
1124 }
1125
1126 // Insert the clipped poly
1127 vtkIdType numPoints = idList->GetNumberOfIds();
1128
1129 if (numPoints > polyMax)
1130 {
1131 vtkIdType newCellId = outputPolys->GetNumberOfCells();
1132
1133 // Triangulate the poly and insert triangles into output.
1134 if (!this->TriangulatePolygon(idList, points, outputPolys))
1135 {
1136 triangulationFailure = 1;
1137 }
1138
1139 // Copy the attribute data to the triangle cells
1140 vtkIdType nCells = outputPolys->GetNumberOfCells();
1141 for (; newCellId < nCells; newCellId++)
1142 {
1143 outPolyData->CopyData(inCellData, cellId, newCellId);
1144 }
1145 }
1146 else if (numPoints > 2)
1147 {
1148 // Insert the polygon without triangulating it
1149 vtkIdType newCellId = outputPolys->InsertNextCell(idList);
1150 outPolyData->CopyData(inCellData, cellId, newCellId);
1151 }
1152
1153 // Insert the contour line if one was created
1154 if (linePts[0] != linePts[1])
1155 {
1156 vtkIdType newCellId = outputLines->InsertNextCell(2, linePts);
1157 outLineData->CopyData(inCellData, cellId, newCellId);
1158 }
1159 }
1160
1161 if (triangulationFailure && this->TriangulationErrorDisplay)
1162 {
1163 vtkErrorMacro("Triangulation failed, output may not be watertight");
1164 }
1165
1166 // Free up the idList memory
1167 idList->Initialize();
1168 }
1169
1170 //----------------------------------------------------------------------------
BreakPolylines(vtkCellArray * inputLines,vtkCellArray * lines,vtkUnsignedCharArray * inputScalars,vtkIdType firstLineScalar,vtkUnsignedCharArray * scalars,const unsigned char color[3])1171 void vtkClipClosedSurface::BreakPolylines(
1172 vtkCellArray *inputLines, vtkCellArray *lines,
1173 vtkUnsignedCharArray *inputScalars, vtkIdType firstLineScalar,
1174 vtkUnsignedCharArray *scalars, const unsigned char color[3])
1175 {
1176 // The color for the lines
1177 unsigned char cellColor[3];
1178 cellColor[0] = color[0];
1179 cellColor[1] = color[1];
1180 cellColor[2] = color[2];
1181
1182 // Break the input lines into segments
1183 inputLines->InitTraversal();
1184 vtkIdType cellId = 0;
1185 vtkIdType npts, *pts;
1186 while (inputLines->GetNextCell(npts, pts))
1187 {
1188 if (inputScalars)
1189 {
1190 inputScalars->GetTypedTuple(firstLineScalar + cellId++, cellColor);
1191 }
1192
1193 for (vtkIdType i = 1; i < npts; i++)
1194 {
1195 lines->InsertNextCell(2);
1196 lines->InsertCellPoint(pts[i-1]);
1197 lines->InsertCellPoint(pts[i]);
1198
1199 if (scalars)
1200 {
1201 scalars->InsertNextTypedTuple(cellColor);
1202 }
1203 }
1204 }
1205 }
1206
1207 //----------------------------------------------------------------------------
CopyPolygons(vtkCellArray * inputPolys,vtkCellArray * polys,vtkUnsignedCharArray * inputScalars,vtkIdType firstPolyScalar,vtkUnsignedCharArray * polyScalars,const unsigned char color[3])1208 void vtkClipClosedSurface::CopyPolygons(
1209 vtkCellArray *inputPolys, vtkCellArray *polys,
1210 vtkUnsignedCharArray *inputScalars, vtkIdType firstPolyScalar,
1211 vtkUnsignedCharArray *polyScalars, const unsigned char color[3])
1212 {
1213 if (!inputPolys)
1214 {
1215 return;
1216 }
1217
1218 polys->DeepCopy(inputPolys);
1219
1220 if (polyScalars)
1221 {
1222 unsigned char scalarValue[3];
1223 scalarValue[0] = color[0];
1224 scalarValue[1] = color[1];
1225 scalarValue[2] = color[2];
1226
1227 vtkIdType n = polys->GetNumberOfCells();
1228 polyScalars->SetNumberOfTuples(n);
1229
1230 if (inputScalars)
1231 {
1232 for (vtkIdType i = 0; i < n; i++)
1233 {
1234 inputScalars->GetTypedTuple(i + firstPolyScalar, scalarValue);
1235 polyScalars->SetTypedTuple(i, scalarValue);
1236 }
1237 }
1238 else
1239 {
1240 for (vtkIdType i = 0; i < n; i++)
1241 {
1242 polyScalars->SetTypedTuple(i, scalarValue);
1243 }
1244 }
1245 }
1246 }
1247
1248 //----------------------------------------------------------------------------
BreakTriangleStrips(vtkCellArray * inputStrips,vtkCellArray * polys,vtkUnsignedCharArray * inputScalars,vtkIdType firstStripScalar,vtkUnsignedCharArray * polyScalars,const unsigned char color[3])1249 void vtkClipClosedSurface::BreakTriangleStrips(
1250 vtkCellArray *inputStrips, vtkCellArray *polys,
1251 vtkUnsignedCharArray *inputScalars, vtkIdType firstStripScalar,
1252 vtkUnsignedCharArray *polyScalars, const unsigned char color[3])
1253 {
1254 if (!inputStrips)
1255 {
1256 return;
1257 }
1258
1259 vtkIdType npts = 0;
1260 vtkIdType *pts = nullptr;
1261
1262 inputStrips->InitTraversal();
1263
1264 for (vtkIdType cellId = firstStripScalar;
1265 inputStrips->GetNextCell(npts, pts);
1266 cellId++)
1267 {
1268 vtkTriangleStrip::DecomposeStrip(npts, pts, polys);
1269
1270 if (polyScalars)
1271 {
1272 unsigned char scalarValue[3];
1273 scalarValue[0] = color[0];
1274 scalarValue[1] = color[1];
1275 scalarValue[2] = color[2];
1276
1277 if (inputScalars)
1278 {
1279 // If there are input scalars, use them instead of "color"
1280 inputScalars->GetTypedTuple(cellId, scalarValue);
1281 }
1282
1283 vtkIdType n = npts - 3;
1284 vtkIdType m = polyScalars->GetNumberOfTuples();
1285 if (n >= 0)
1286 {
1287 // First insert is just to allocate space
1288 polyScalars->InsertTypedTuple(m+n, scalarValue);
1289
1290 for (vtkIdType i = 0; i < n; i++)
1291 {
1292 polyScalars->SetTypedTuple(m+i, scalarValue);
1293 }
1294 }
1295 }
1296 }
1297 }
1298
1299 //----------------------------------------------------------------------------
TriangulateContours(vtkPolyData * data,vtkIdType firstLine,vtkIdType numLines,vtkCellArray * outputPolys,const double normal[3])1300 void vtkClipClosedSurface::TriangulateContours(
1301 vtkPolyData *data, vtkIdType firstLine, vtkIdType numLines,
1302 vtkCellArray *outputPolys, const double normal[3])
1303 {
1304 // If no cut lines were generated, there's nothing to do
1305 if (numLines <= 0)
1306 {
1307 return;
1308 }
1309
1310 double nnormal[3] = { -normal[0], -normal[1], -normal[2] };
1311 int rval = vtkContourTriangulator::TriangulateContours(
1312 data, firstLine, numLines, outputPolys, nnormal);
1313
1314 if (rval == 0 && this->TriangulationErrorDisplay)
1315 {
1316 vtkErrorMacro("Triangulation failed, data may not be watertight.");
1317 }
1318 }
1319
1320 // ---------------------------------------------------
TriangulatePolygon(vtkIdList * polygon,vtkPoints * points,vtkCellArray * triangles)1321 int vtkClipClosedSurface::TriangulatePolygon(
1322 vtkIdList *polygon, vtkPoints *points, vtkCellArray *triangles)
1323 {
1324 return vtkContourTriangulator::TriangulatePolygon(
1325 polygon, points, triangles);
1326 }
1327