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