1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkStaticCleanPolyData.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 "vtkStaticCleanPolyData.h"
16
17 #include "vtkCellArray.h"
18 #include "vtkCellData.h"
19 #include "vtkMergePoints.h"
20 #include "vtkInformation.h"
21 #include "vtkInformationVector.h"
22 #include "vtkObjectFactory.h"
23 #include "vtkPointData.h"
24 #include "vtkPolyData.h"
25 #include "vtkStreamingDemandDrivenPipeline.h"
26 #include "vtkStaticPointLocator.h"
27 #include "vtkArrayListTemplate.h" // For processing attribute data
28 #include "vtkSMPTools.h"
29
30 #include <algorithm>
31
32 vtkStandardNewMacro(vtkStaticCleanPolyData);
33
34 // These are created to support a (float,double) fast path. They work in
35 // tandem with vtkTemplate2Macro found in vtkSetGet.h.
36 #define vtkTemplate2MacroCP(call) \
37 vtkTemplate2MacroCase1CP(VTK_DOUBLE, double, call); \
38 vtkTemplate2MacroCase1CP(VTK_FLOAT, float, call); \
39 vtkTemplate2MacroCase1CP(VTK_LONG_LONG, long long, call); \
40 vtkTemplate2MacroCase1CP(VTK_UNSIGNED_LONG_LONG, unsigned long long, call); \
41 vtkTemplate2MacroCase1CP(VTK_ID_TYPE, vtkIdType, call); \
42 vtkTemplate2MacroCase1CP(VTK_LONG, long, call); \
43 vtkTemplate2MacroCase1CP(VTK_UNSIGNED_LONG, unsigned long, call); \
44 vtkTemplate2MacroCase1CP(VTK_INT, int, call); \
45 vtkTemplate2MacroCase1CP(VTK_UNSIGNED_INT, unsigned int, call); \
46 vtkTemplate2MacroCase1CP(VTK_SHORT, short, call); \
47 vtkTemplate2MacroCase1CP(VTK_UNSIGNED_SHORT, unsigned short, call); \
48 vtkTemplate2MacroCase1CP(VTK_CHAR, char, call); \
49 vtkTemplate2MacroCase1CP(VTK_SIGNED_CHAR, signed char, call); \
50 vtkTemplate2MacroCase1CP(VTK_UNSIGNED_CHAR, unsigned char, call)
51 #define vtkTemplate2MacroCase1CP(type1N, type1, call) \
52 vtkTemplate2MacroCase2(type1N, type1, VTK_DOUBLE, double, call); \
53 vtkTemplate2MacroCase2(type1N, type1, VTK_FLOAT, float, call);
54
55 namespace { //anonymous
56
57 //----------------------------------------------------------------------------
58 // Fast, threaded way to copy new points and attribute data to output.
59 template <typename TPIn, typename TPOut>
60 struct CopyPoints
61 {
62 vtkIdType *PtMap;
63 TPIn *InPts;
64 TPOut *OutPts;
65 ArrayList Arrays;
66
CopyPoints__anona17c15e50111::CopyPoints67 CopyPoints(vtkIdType *ptMap, TPIn *inPts, vtkPointData *inPD,
68 vtkIdType numNewPts, TPOut* outPts, vtkPointData *outPD) :
69 PtMap(ptMap), InPts(inPts), OutPts(outPts)
70 {
71 this->Arrays.AddArrays(numNewPts,inPD,outPD);
72 }
73
operator ()__anona17c15e50111::CopyPoints74 void operator() (vtkIdType ptId, vtkIdType endPtId)
75 {
76 const TPIn *inP=this->InPts + 3*ptId;
77 TPOut *outP;
78 const vtkIdType *ptMap=this->PtMap;
79 vtkIdType outPtId;
80
81 for ( ; ptId < endPtId; ++ptId, inP+=3)
82 {
83 outPtId = ptMap[ptId];
84 if ( outPtId != -1 )
85 {
86 outP = this->OutPts + 3*outPtId;
87 *outP++ = static_cast<TPOut>(inP[0]);
88 *outP++ = static_cast<TPOut>(inP[1]);
89 *outP = static_cast<TPOut>(inP[2]);
90 this->Arrays.Copy(ptId,outPtId);
91 }
92 }
93 }
94
Execute__anona17c15e50111::CopyPoints95 static void Execute(vtkIdType numPts, vtkIdType *ptMap, TPIn *inPts,
96 vtkPointData *inPD, vtkIdType numNewPts,
97 TPOut *outPts, vtkPointData *outPD)
98 {
99 CopyPoints copyPts(ptMap, inPts, inPD, numNewPts, outPts, outPD);
100 vtkSMPTools::For(0,numPts, copyPts);
101 }
102
103 };
104
105 } //anonymous namespace
106
107
108
109 //---------------------------------------------------------------------------
110 // Construct object with initial Tolerance of 0.0
vtkStaticCleanPolyData()111 vtkStaticCleanPolyData::vtkStaticCleanPolyData()
112 {
113 this->ToleranceIsAbsolute = 0;
114 this->Tolerance = 0.0;
115 this->AbsoluteTolerance = 1.0;
116 this->ConvertPolysToLines = 1;
117 this->ConvertLinesToPoints = 1;
118 this->ConvertStripsToPolys = 1;
119 this->Locator = vtkStaticPointLocator::New();
120 this->PieceInvariant = 1;
121 this->OutputPointsPrecision = vtkAlgorithm::DEFAULT_PRECISION;
122 }
123
124 //--------------------------------------------------------------------------
~vtkStaticCleanPolyData()125 vtkStaticCleanPolyData::~vtkStaticCleanPolyData()
126 {
127 this->Locator->Delete();
128 this->Locator = nullptr;
129 }
130
131
132 //--------------------------------------------------------------------------
RequestUpdateExtent(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * outputVector)133 int vtkStaticCleanPolyData::RequestUpdateExtent(
134 vtkInformation *vtkNotUsed(request),
135 vtkInformationVector **inputVector,
136 vtkInformationVector *outputVector)
137 {
138 // get the info objects
139 vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
140 vtkInformation *outInfo = outputVector->GetInformationObject(0);
141
142 if (this->PieceInvariant)
143 {
144 // Although piece > 1 is handled by superclass, we should be thorough.
145 if (outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER()) == 0)
146 {
147 inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER(), 0);
148 inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES(),
149 1);
150 }
151 else
152 {
153 inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER(), 0);
154 inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES(),
155 0);
156 }
157 }
158 else
159 {
160 inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES(),
161 outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES()));
162 inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER(),
163 outInfo->Get(
164 vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER()));
165 inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS(),
166 outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS()));
167 }
168
169 return 1;
170 }
171
172 //--------------------------------------------------------------------------
RequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * outputVector)173 int vtkStaticCleanPolyData::RequestData(
174 vtkInformation *vtkNotUsed(request),
175 vtkInformationVector **inputVector,
176 vtkInformationVector *outputVector)
177 {
178 // get the info objects
179 vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
180 vtkInformation *outInfo = outputVector->GetInformationObject(0);
181
182 // get the input and output
183 vtkPolyData *input = vtkPolyData::SafeDownCast(
184 inInfo->Get(vtkDataObject::DATA_OBJECT()));
185 vtkPolyData *output = vtkPolyData::SafeDownCast(
186 outInfo->Get(vtkDataObject::DATA_OBJECT()));
187
188 vtkPoints *inPts = input->GetPoints();
189 vtkIdType numPts = input->GetNumberOfPoints();
190
191 vtkDebugMacro(<<"Beginning PolyData clean");
192 if ( (numPts<1) || (inPts == nullptr ) )
193 {
194 vtkDebugMacro(<<"No data to Operate On!");
195 return 1;
196 }
197 vtkIdType *updatedPts = new vtkIdType[input->GetMaxCellSize()];
198
199 // we'll be needing these
200 vtkIdType inCellID, newId;
201 vtkIdType i;
202 vtkIdType ptId;
203 vtkIdType npts = 0;
204 vtkIdType *pts = nullptr;
205
206 vtkCellArray *inVerts = input->GetVerts(), *newVerts = nullptr;
207 vtkCellArray *inLines = input->GetLines(), *newLines = nullptr;
208 vtkCellArray *inPolys = input->GetPolys(), *newPolys = nullptr;
209 vtkCellArray *inStrips = input->GetStrips(), *newStrips = nullptr;
210
211 vtkPointData *inPD = input->GetPointData();
212 vtkCellData *inCD = input->GetCellData();
213
214 // The merge map indicates which points are merged with what points
215 vtkIdType *mergeMap = new vtkIdType [numPts];
216 this->Locator->SetDataSet(input);
217 this->Locator->BuildLocator();
218 double tol = ( this->ToleranceIsAbsolute ? this->AbsoluteTolerance :
219 this->Tolerance*input->GetLength() );
220 this->Locator->MergePoints(tol,mergeMap);
221
222 vtkPointData *outPD = output->GetPointData();
223 vtkCellData *outCD = output->GetCellData();
224 outPD->CopyAllocate(inPD);
225 outCD->CopyAllocate(inCD);
226
227 // Prefix sum: count the number of new points; allocate memory. Populate the
228 // point map (old points to new).
229 vtkIdType *pointMap = new vtkIdType [numPts];
230 vtkIdType id, numNewPts=0;
231 // Count and map points to new points
232 for ( id=0; id < numPts; ++id )
233 {
234 if ( mergeMap[id] == id )
235 {
236 pointMap[id] = numNewPts++;
237 }
238 }
239 // Now map old merged points to new points
240 for ( id=0; id < numPts; ++id )
241 {
242 if ( mergeMap[id] != id )
243 {
244 pointMap[id] = pointMap[mergeMap[id]];
245 }
246 }
247 delete [] mergeMap;
248
249 vtkPoints *newPts = inPts->NewInstance();
250 if(this->OutputPointsPrecision == vtkAlgorithm::DEFAULT_PRECISION)
251 {
252 newPts->SetDataType(inPts->GetDataType());
253 }
254 else if(this->OutputPointsPrecision == vtkAlgorithm::SINGLE_PRECISION)
255 {
256 newPts->SetDataType(VTK_FLOAT);
257 }
258 else if(this->OutputPointsPrecision == vtkAlgorithm::DOUBLE_PRECISION)
259 {
260 newPts->SetDataType(VTK_DOUBLE);
261 }
262 newPts->SetNumberOfPoints(numNewPts);
263
264 // Now copy points and point data (in parallel)
265 void *inPtr = inPts->GetVoidPointer(0);
266 int inPtsType = inPts->GetDataType();
267 void *outPtr = newPts->GetVoidPointer(0);
268 int outPtsType = newPts->GetDataType();
269
270 switch (vtkTemplate2PackMacro(inPtsType, outPtsType))
271 {
272 vtkTemplate2MacroCP((CopyPoints<VTK_T1,VTK_T2>::Execute(numPts, pointMap,
273 (VTK_T1*)inPtr, inPD, numNewPts, (VTK_T2*)outPtr, outPD)));
274 default:
275 vtkErrorMacro(<<"Type not supported");
276 return 0;
277 }
278
279 // Finally, remap the topology to use new point ids. Celldata needs to be
280 // copied correctly. If a poly is converted to a line, or a line to a
281 // point, then using a CellCounter will not do, as the cells should be
282 // ordered verts, lines, polys, strips. We need to maintain separate cell
283 // data lists so we can copy them all correctly. Tedious but easy to
284 // implement. We can use outCD for vertex cell data, then add the rest
285 // at the end.
286 vtkCellData *outLineData = nullptr;
287 vtkCellData *outPolyData = nullptr;
288 vtkCellData *outStrpData = nullptr;
289 vtkIdType vertIDcounter = 0, lineIDcounter = 0;
290 vtkIdType polyIDcounter = 0, strpIDcounter = 0;
291
292 // Begin to adjust topology.
293 //
294 // Vertices are renumbered and we remove duplicates
295 vtkIdType numCellPts;
296 inCellID = 0;
297 if ( !this->GetAbortExecute() && inVerts->GetNumberOfCells() > 0 )
298 {
299 newVerts = vtkCellArray::New();
300 newVerts->Allocate(inVerts->GetSize());
301
302 vtkDebugMacro(<<"Starting Verts "<<inCellID);
303 for (inVerts->InitTraversal(); inVerts->GetNextCell(npts,pts);
304 inCellID++)
305 {
306 for ( numCellPts=0, i=0; i < npts; i++ )
307 {
308 ptId = pointMap[pts[i]];
309 updatedPts[numCellPts++] = ptId;
310 }//for all points of vertex cell
311
312 if ( numCellPts > 0 )
313 {
314 newId = newVerts->InsertNextCell(numCellPts,updatedPts);
315 outCD->CopyData(inCD, inCellID, newId);
316 if ( vertIDcounter != newId)
317 {
318 vtkErrorMacro(<<"Vertex ID fault in vertex test");
319 }
320 vertIDcounter++;
321 }
322 }
323 }
324 this->UpdateProgress(0.25);
325
326 // lines reduced to one point are eliminated or made into verts
327 if ( !this->GetAbortExecute() && inLines->GetNumberOfCells() > 0 )
328 {
329 newLines = vtkCellArray::New();
330 newLines->Allocate(inLines->GetSize());
331 outLineData = vtkCellData::New();
332 outLineData->CopyAllocate(inCD);
333 //
334 vtkDebugMacro(<<"Starting Lines "<<inCellID);
335 for (inLines->InitTraversal(); inLines->GetNextCell(npts,pts); inCellID++)
336 {
337 for ( numCellPts=0, i=0; i<npts; i++ )
338 {
339 ptId = pointMap[pts[i]];
340 updatedPts[numCellPts++] = ptId;
341 }//for all cell points
342
343 if ( (numCellPts>1) || !this->ConvertLinesToPoints )
344 {
345 newId = newLines->InsertNextCell(numCellPts,updatedPts);
346 outLineData->CopyData(inCD, inCellID, newId);
347 if ( lineIDcounter != newId)
348 {
349 vtkErrorMacro(<<"Line ID fault in line test");
350 }
351 lineIDcounter++;
352 }
353 else if ( numCellPts==1 )
354 {
355 if (!newVerts)
356 {
357 newVerts = vtkCellArray::New();
358 newVerts->Allocate(5);
359 }
360 newId = newVerts->InsertNextCell(numCellPts,updatedPts);
361 outCD->CopyData(inCD, inCellID, newId);
362 if (vertIDcounter!=newId)
363 {
364 vtkErrorMacro(<<"Vertex ID fault in line test");
365 }
366 vertIDcounter++;
367 }
368 }
369 vtkDebugMacro(<<"Removed "
370 << inLines->GetNumberOfCells() - newLines->GetNumberOfCells()
371 << " lines");
372
373 }
374 this->UpdateProgress(0.50);
375
376 // polygons reduced to two points or less are either eliminated
377 // or converted to lines or points if enabled
378 if ( !this->GetAbortExecute() && inPolys->GetNumberOfCells() > 0 )
379 {
380 newPolys = vtkCellArray::New();
381 newPolys->Allocate(inPolys->GetSize());
382 outPolyData = vtkCellData::New();
383 outPolyData->CopyAllocate(inCD);
384
385 vtkDebugMacro(<<"Starting Polys "<<inCellID);
386 for (inPolys->InitTraversal(); inPolys->GetNextCell(npts,pts); inCellID++)
387 {
388 for ( numCellPts=0, i=0; i<npts; i++ )
389 {
390 ptId = pointMap[pts[i]];
391 updatedPts[numCellPts++] = ptId;
392 } //for points in cell
393
394 if ( numCellPts>2 && updatedPts[0] == updatedPts[numCellPts-1] )
395 {
396 numCellPts--;
397 }
398 if ( (numCellPts > 2) || !this->ConvertPolysToLines )
399 {
400 newId = newPolys->InsertNextCell(numCellPts,updatedPts);
401 outPolyData->CopyData(inCD, inCellID, newId);
402 if (polyIDcounter!=newId)
403 {
404 vtkErrorMacro(<<"Poly ID fault in poly test");
405 }
406 polyIDcounter++;
407 }
408 else if ( (numCellPts==2) || !this->ConvertLinesToPoints )
409 {
410 if (!newLines)
411 {
412 newLines = vtkCellArray::New();
413 newLines->Allocate(5);
414 outLineData = vtkCellData::New();
415 outLineData->CopyAllocate(inCD);
416 }
417 newId = newLines->InsertNextCell(numCellPts,updatedPts);
418 outLineData->CopyData(inCD, inCellID, newId);
419 if (lineIDcounter!=newId)
420 {
421 vtkErrorMacro(<<"Line ID fault in poly test");
422 }
423 lineIDcounter++;
424 }
425 else if ( numCellPts==1 )
426 {
427 if (!newVerts)
428 {
429 newVerts = vtkCellArray::New();
430 newVerts->Allocate(5);
431 }
432 newId = newVerts->InsertNextCell(numCellPts,updatedPts);
433 outCD->CopyData(inCD, inCellID, newId);
434 if (vertIDcounter!=newId)
435 {
436 vtkErrorMacro(<<"Vertex ID fault in poly test");
437 }
438 vertIDcounter++;
439 }
440 }
441 vtkDebugMacro(<<"Removed "
442 << inPolys->GetNumberOfCells() - newPolys->GetNumberOfCells()
443 << " polys");
444 }
445 this->UpdateProgress(0.75);
446
447 // triangle strips can reduced to polys/lines/points etc
448 if ( !this->GetAbortExecute() && inStrips->GetNumberOfCells() > 0 )
449 {
450 newStrips = vtkCellArray::New();
451 newStrips->Allocate(inStrips->GetSize());
452 outStrpData = vtkCellData::New();
453 outStrpData->CopyAllocate(inCD);
454
455 for (inStrips->InitTraversal(); inStrips->GetNextCell(npts,pts);
456 inCellID++)
457 {
458 for ( numCellPts=0, i=0; i < npts; i++ )
459 {
460 ptId = pointMap[pts[i]];
461 updatedPts[numCellPts++] = ptId;
462 }
463 if ( (numCellPts > 3) || !this->ConvertStripsToPolys )
464 {
465 newId = newStrips->InsertNextCell(numCellPts,updatedPts);
466 outStrpData->CopyData(inCD, inCellID, newId);
467 if (strpIDcounter!=newId)
468 {
469 vtkErrorMacro(<<"Strip ID fault in strip test");
470 }
471 strpIDcounter++;
472 }
473 else if ( (numCellPts==3) || !this->ConvertPolysToLines )
474 {
475 if (!newPolys)
476 {
477 newPolys = vtkCellArray::New();
478 newPolys->Allocate(5);
479 outPolyData = vtkCellData::New();
480 outPolyData->CopyAllocate(inCD);
481 }
482 newId = newPolys->InsertNextCell(numCellPts,updatedPts);
483 outPolyData->CopyData(inCD, inCellID, newId);
484 if (polyIDcounter!=newId)
485 {
486 vtkErrorMacro(<<"Poly ID fault in strip test");
487 }
488 polyIDcounter++;
489 }
490 else if ( (numCellPts==2) || !this->ConvertLinesToPoints )
491 {
492 if (!newLines)
493 {
494 newLines = vtkCellArray::New();
495 newLines->Allocate(5);
496 outLineData = vtkCellData::New();
497 outLineData->CopyAllocate(inCD);
498 }
499 newId = newLines->InsertNextCell(numCellPts,updatedPts);
500 outLineData->CopyData(inCD, inCellID, newId);
501 if (lineIDcounter!=newId)
502 {
503 vtkErrorMacro(<<"Line ID fault in strip test");
504 }
505 lineIDcounter++;
506 }
507 else if ( numCellPts==1 )
508 {
509 if (!newVerts)
510 {
511 newVerts = vtkCellArray::New();
512 newVerts->Allocate(5);
513 }
514 newId = newVerts->InsertNextCell(numCellPts,updatedPts);
515 outCD->CopyData(inCD, inCellID, newId);
516 if (vertIDcounter!=newId)
517 {
518 vtkErrorMacro(<<"Vertex ID fault in strip test");
519 }
520 vertIDcounter++;
521 }
522 }
523 vtkDebugMacro(<<"Removed "
524 << inStrips->GetNumberOfCells() - newStrips->GetNumberOfCells()
525 << " strips");
526 }
527
528 vtkDebugMacro(<<"Removed "
529 << numNewPts - newPts->GetNumberOfPoints() << " points");
530
531 // Update ourselves and release memory
532 //
533 this->Locator->Initialize(); //release memory.
534 delete [] updatedPts;
535 delete [] pointMap;
536
537 // Now transfer all CellData from Lines/Polys/Strips into final
538 // Cell data output
539 vtkIdType combinedCellID = vertIDcounter;
540 if (newLines)
541 {
542 for (i=0; i<lineIDcounter; ++i, ++combinedCellID)
543 {
544 outCD->CopyData(outLineData, i, combinedCellID);
545 }
546 outLineData->Delete();
547 }
548 if (newPolys)
549 {
550 for (i=0; i<polyIDcounter; ++i, ++combinedCellID)
551 {
552 outCD->CopyData(outPolyData, i, combinedCellID);
553 }
554 outPolyData->Delete();
555 }
556 if (newStrips)
557 {
558 for (i=0; i<strpIDcounter;++ i, ++combinedCellID)
559 {
560 outCD->CopyData(outStrpData, i, combinedCellID);
561 }
562 outStrpData->Delete();
563 }
564
565 output->SetPoints(newPts);
566 newPts->Delete();
567 if (newVerts)
568 {
569 output->SetVerts(newVerts);
570 newVerts->Delete();
571 }
572 if (newLines)
573 {
574 output->SetLines(newLines);
575 newLines->Delete();
576 }
577 if (newPolys)
578 {
579 output->SetPolys(newPolys);
580 newPolys->Delete();
581 }
582 if (newStrips)
583 {
584 output->SetStrips(newStrips);
585 newStrips->Delete();
586 }
587
588 return 1;
589 }
590
591 //--------------------------------------------------------------------------
GetMTime()592 vtkMTimeType vtkStaticCleanPolyData::GetMTime()
593 {
594 vtkMTimeType mTime=this->vtkObject::GetMTime();
595 vtkMTimeType time = this->Locator->GetMTime();
596 return ( time > mTime ? time : mTime );
597 }
598
599 //--------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)600 void vtkStaticCleanPolyData::PrintSelf(ostream& os, vtkIndent indent)
601 {
602 this->Superclass::PrintSelf(os,indent);
603
604 os << indent << "ToleranceIsAbsolute: "
605 << (this->ToleranceIsAbsolute ? "On\n" : "Off\n");
606 os << indent << "Tolerance: "
607 << (this->Tolerance ? "On\n" : "Off\n");
608 os << indent << "AbsoluteTolerance: "
609 << (this->AbsoluteTolerance ? "On\n" : "Off\n");
610 os << indent << "ConvertPolysToLines: "
611 << (this->ConvertPolysToLines ? "On\n" : "Off\n");
612 os << indent << "ConvertLinesToPoints: "
613 << (this->ConvertLinesToPoints ? "On\n" : "Off\n");
614 os << indent << "ConvertStripsToPolys: "
615 << (this->ConvertStripsToPolys ? "On\n" : "Off\n");
616 if ( this->Locator )
617 {
618 os << indent << "Locator: " << this->Locator << "\n";
619 }
620 else
621 {
622 os << indent << "Locator: (none)\n";
623 }
624 os << indent << "PieceInvariant: "
625 << (this->PieceInvariant ? "On\n" : "Off\n");
626 os << indent << "Output Points Precision: " << this->OutputPointsPrecision
627 << "\n";
628 }
629