1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkPolyDataNormals.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 "vtkPolyDataNormals.h"
16 
17 #include "vtkCellArray.h"
18 #include "vtkCellData.h"
19 #include "vtkFloatArray.h"
20 #include "vtkMath.h"
21 #include "vtkInformation.h"
22 #include "vtkInformationVector.h"
23 #include "vtkObjectFactory.h"
24 #include "vtkPointData.h"
25 #include "vtkPolyData.h"
26 #include "vtkPolygon.h"
27 #include "vtkTriangleStrip.h"
28 #include "vtkPriorityQueue.h"
29 
30 #include "vtkNew.h"
31 
32 vtkStandardNewMacro(vtkPolyDataNormals);
33 
34 // Construct with feature angle=30, splitting and consistency turned on,
35 // flipNormals turned off, and non-manifold traversal turned on.
vtkPolyDataNormals()36 vtkPolyDataNormals::vtkPolyDataNormals()
37 {
38   this->FeatureAngle = 30.0;
39   this->Splitting = 1;
40   this->Consistency = 1;
41   this->FlipNormals = 0;
42   this->ComputePointNormals = 1;
43   this->ComputeCellNormals = 0;
44   this->NonManifoldTraversal = 1;
45   this->AutoOrientNormals = 0;
46   // some internal data
47   this->NumFlips = 0;
48   this->OutputPointsPrecision = vtkAlgorithm::DEFAULT_PRECISION;
49   this->Wave = nullptr;
50   this->Wave2 = nullptr;
51   this->CellIds = nullptr;
52   this->Map = nullptr;
53   this->OldMesh = nullptr;
54   this->NewMesh = nullptr;
55   this->Visited = nullptr;
56   this->PolyNormals = nullptr;
57   this->CosAngle = 0.0;
58 }
59 
60 #define VTK_CELL_NOT_VISITED     0
61 #define VTK_CELL_VISITED         1
62 
63 // Generate normals for polygon meshes
RequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * outputVector)64 int vtkPolyDataNormals::RequestData(
65   vtkInformation *vtkNotUsed(request),
66   vtkInformationVector **inputVector,
67   vtkInformationVector *outputVector)
68 {
69   // get the info objects
70   vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
71   vtkInformation *outInfo = outputVector->GetInformationObject(0);
72 
73   // get the input and output
74   vtkPolyData *input = vtkPolyData::SafeDownCast(
75     inInfo->Get(vtkDataObject::DATA_OBJECT()));
76   vtkPolyData *output = vtkPolyData::SafeDownCast(
77     outInfo->Get(vtkDataObject::DATA_OBJECT()));
78 
79   vtkIdType npts = 0;
80   vtkIdType *pts = nullptr;
81   vtkIdType numNewPts;
82   double flipDirection=1.0;
83   vtkIdType numPolys, numStrips;
84   vtkIdType cellId;
85   vtkIdType numPts;
86   vtkPoints *inPts;
87   vtkCellArray *inPolys, *inStrips, *polys;
88   vtkPoints *newPts = nullptr;
89   vtkFloatArray *newNormals;
90   vtkPointData *pd, *outPD;
91   vtkDataSetAttributes* outCD = output->GetCellData();
92   double n[3];
93   vtkCellArray *newPolys;
94   vtkIdType ptId, oldId;
95 
96   vtkDebugMacro(<<"Generating surface normals");
97 
98   numPolys=input->GetNumberOfPolys();
99   numStrips=input->GetNumberOfStrips();
100   if ( (numPts=input->GetNumberOfPoints()) < 1 )
101   {
102     vtkDebugMacro(<<"No data to generate normals for!");
103     return 1;
104   }
105 
106 
107   // If there is nothing to do, pass the data through
108   if ( (this->ComputePointNormals == 0 && this->ComputeCellNormals == 0) ||
109        (numPolys < 1 && numStrips < 1) )
110   { //don't do anything! pass data through
111     output->CopyStructure(input);
112     output->GetPointData()->PassData(input->GetPointData());
113     output->GetCellData()->PassData(input->GetCellData());
114     return 1;
115   }
116 
117   if (numStrips < 1)
118   {
119     output->GetCellData()->PassData(input->GetCellData());
120   }
121 
122   // Load data into cell structure.  We need two copies: one is a
123   // non-writable mesh used to perform topological queries.  The other
124   // is used to write into and modify the connectivity of the mesh.
125   //
126   inPts = input->GetPoints();
127   inPolys = input->GetPolys();
128   inStrips = input->GetStrips();
129 
130   this->OldMesh = vtkPolyData::New();
131   this->OldMesh->SetPoints(inPts);
132   if ( numStrips > 0 ) //have to decompose strips into triangles
133   {
134     vtkDataSetAttributes* inCD = input->GetCellData();
135     // When we have triangle strips, make sure to create and copy
136     // the cell data appropriately. Since strips are broken into
137     // triangles, cell data cannot be passed as it is and needs to
138     // be copied tuple by tuple.
139     outCD->CopyAllocate(inCD);
140     if ( numPolys > 0 )
141     {
142       polys = vtkCellArray::New();
143       polys->DeepCopy(inPolys);
144       vtkNew<vtkIdList> ids;
145       ids->SetNumberOfIds(numPolys);
146       for (vtkIdType i=0; i<numPolys; i++)
147       {
148         ids->SetId(i, i);
149       }
150       outCD->CopyData(inCD, ids, ids);
151     }
152     else
153     {
154       polys = vtkCellArray::New();
155       polys->Allocate(polys->EstimateSize(numStrips,5));
156     }
157     vtkIdType inCellIdx = numPolys;
158     vtkIdType outCellIdx = numPolys;
159     for ( inStrips->InitTraversal(); inStrips->GetNextCell(npts,pts); inCellIdx++)
160     {
161       vtkTriangleStrip::DecomposeStrip(npts, pts, polys);
162       // Copy the cell data for the strip to each triangle.
163       for (vtkIdType i=0; i<npts-2; i++)
164       {
165         outCD->CopyData(inCD, inCellIdx, outCellIdx++);
166       }
167     }
168     this->OldMesh->SetPolys(polys);
169     polys->Delete();
170     numPolys = polys->GetNumberOfCells();//added some new triangles
171   }
172   else
173   {
174     this->OldMesh->SetPolys(inPolys);
175     polys = inPolys;
176   }
177   this->OldMesh->BuildLinks();
178   this->UpdateProgress(0.10);
179 
180   pd = input->GetPointData();
181   outPD = output->GetPointData();
182 
183   this->NewMesh = vtkPolyData::New();
184   this->NewMesh->SetPoints(inPts);
185   // create a copy because we're modifying it
186   newPolys = vtkCellArray::New();
187   newPolys->DeepCopy(polys);
188   this->NewMesh->SetPolys(newPolys);
189   this->NewMesh->BuildCells(); //builds connectivity
190 
191   // The visited array keeps track of which polygons have been visited.
192   //
193   if ( this->Consistency || this->Splitting || this->AutoOrientNormals )
194   {
195     this->Visited = new int[numPolys];
196     memset(this->Visited, VTK_CELL_NOT_VISITED, numPolys*sizeof(int));
197     this->CellIds = vtkIdList::New();
198     this->CellIds->Allocate(VTK_CELL_SIZE);
199   }
200   else
201   {
202     this->Visited = nullptr;
203   }
204 
205   //  Traverse all polygons insuring proper direction of ordering.  This
206   //  works by propagating a wave from a seed polygon to the polygon's
207   //  edge neighbors. Each neighbor may be reordered to maintain consistency
208   //  with its (already checked) neighbors.
209   //
210   this->NumFlips = 0;
211   if (this->AutoOrientNormals)
212   {
213     // No need to check this->Consistency. It's implied.
214 
215     // Ok, here's the basic idea: the "left-most" polygon should
216     // have its outward pointing normal facing left. If it doesn't,
217     // reverse the vertex order. Then use it as the seed for other
218     // connected polys. To find left-most polygon, first find left-most
219     // point, and examine neighboring polys and see which one
220     // has a normal that's "most aligned" with the X-axis. This process
221     // will need to be repeated to handle all connected components in
222     // the mesh. Report bugs/issues to cvolpe@ara.com.
223     int foundLeftmostCell;
224     vtkIdType leftmostCellID=-1, currentPointID, currentCellID;
225     vtkIdType *leftmostCells;
226     unsigned short nleftmostCells;
227     vtkIdType *cellPts;
228     vtkIdType nCellPts;
229     int cIdx;
230     double bestNormalAbsXComponent;
231     int bestReverseFlag;
232     vtkPriorityQueue *leftmostPoints = vtkPriorityQueue::New();
233     this->Wave = vtkIdList::New();
234     this->Wave->Allocate(numPolys/4+1,numPolys);
235     this->Wave2 = vtkIdList::New();
236     this->Wave2->Allocate(numPolys/4+1,numPolys);
237 
238     // Put all the points in the priority queue, based on x coord
239     // So that we can find leftmost point
240     leftmostPoints->Allocate(numPts);
241     for (ptId=0; ptId < numPts; ptId++)
242     {
243       leftmostPoints->Insert(inPts->GetPoint(ptId)[0],ptId);
244     }
245 
246     // Repeat this while loop as long as the queue is not empty,
247     // because there may be multiple connected components, each of
248     // which needs to be seeded independently with a correctly
249     // oriented polygon.
250     while (leftmostPoints->GetNumberOfItems())
251     {
252       foundLeftmostCell = 0;
253       // Keep iterating through leftmost points and cells located at
254       // those points until I've got a leftmost point with
255       // unvisited cells attached and I've found the best cell
256       // at that point
257       do {
258         currentPointID = leftmostPoints->Pop();
259         this->OldMesh->GetPointCells(currentPointID, nleftmostCells, leftmostCells);
260         bestNormalAbsXComponent = 0.0;
261         bestReverseFlag = 0;
262         for (cIdx = 0; cIdx < nleftmostCells; cIdx++)
263         {
264           currentCellID = leftmostCells[cIdx];
265           if (this->Visited[currentCellID] == VTK_CELL_VISITED)
266           {
267             continue;
268           }
269           this->OldMesh->GetCellPoints(currentCellID, nCellPts, cellPts);
270           vtkPolygon::ComputeNormal(inPts, nCellPts, cellPts, n);
271           // Ok, see if this leftmost cell candidate is the best
272           // so far
273           if (fabs(n[0]) > bestNormalAbsXComponent)
274           {
275             bestNormalAbsXComponent = fabs(n[0]);
276             leftmostCellID = currentCellID;
277             // If the current leftmost cell's normal is pointing to the
278             // right, then the vertex ordering is wrong
279             bestReverseFlag = (n[0] > 0);
280             foundLeftmostCell = 1;
281           } // if this normal is most x-aligned so far
282         } // for each cell at current leftmost point
283       } while (leftmostPoints->GetNumberOfItems() && !foundLeftmostCell);
284       if (foundLeftmostCell)
285       {
286         // We've got the seed for a connected component! But do
287         // we need to flip it first? We do, if it was pointed the wrong
288         // way to begin with, or if the user requested flipping all
289         // normals, but if both are true, then we leave it as it is.
290         if (bestReverseFlag ^ this->FlipNormals)
291         {
292           this->NewMesh->ReverseCell(leftmostCellID);
293           this->NumFlips++;
294         }
295         this->Wave->InsertNextId(leftmostCellID);
296         this->Visited[leftmostCellID] = VTK_CELL_VISITED;
297         this->TraverseAndOrder();
298         this->Wave->Reset();
299         this->Wave2->Reset();
300       } // if found leftmost cell
301     } // Still some points in the queue
302     this->Wave->Delete();
303     this->Wave2->Delete();
304     leftmostPoints->Delete();
305     vtkDebugMacro(<<"Reversed ordering of " << this->NumFlips << " polygons");
306   } // automatically orient normals
307   else
308   {
309     if ( this->Consistency )
310     {
311       this->Wave = vtkIdList::New();
312       this->Wave->Allocate(numPolys/4+1,numPolys);
313       this->Wave2 = vtkIdList::New();
314       this->Wave2->Allocate(numPolys/4+1,numPolys);
315       for (cellId=0; cellId < numPolys; cellId++)
316       {
317         if ( this->Visited[cellId] == VTK_CELL_NOT_VISITED)
318         {
319           if ( this->FlipNormals )
320           {
321             this->NumFlips++;
322             this->NewMesh->ReverseCell(cellId);
323           }
324           this->Wave->InsertNextId(cellId);
325           this->Visited[cellId] = VTK_CELL_VISITED;
326           this->TraverseAndOrder();
327         }
328 
329         this->Wave->Reset();
330         this->Wave2->Reset();
331       }
332 
333       this->Wave->Delete();
334       this->Wave2->Delete();
335       vtkDebugMacro(<<"Reversed ordering of " << this->NumFlips << " polygons");
336     }//Consistent ordering
337   } // don't automatically orient normals
338 
339   this->UpdateProgress(0.333);
340 
341   //  Initial pass to compute polygon normals without effects of neighbors
342   //
343   this->PolyNormals = vtkFloatArray::New();
344   this->PolyNormals->SetNumberOfComponents(3);
345   this->PolyNormals->Allocate(3*numPolys);
346   this->PolyNormals->SetName("Normals");
347   this->PolyNormals->SetNumberOfTuples(numPolys);
348 
349   for (cellId=0, newPolys->InitTraversal(); newPolys->GetNextCell(npts,pts);
350        cellId++ )
351   {
352     if ((cellId % 1000) == 0)
353     {
354       this->UpdateProgress (0.333 + 0.333 * (double) cellId / (double) numPolys);
355       if (this->GetAbortExecute())
356       {
357         break;
358       }
359     }
360     vtkPolygon::ComputeNormal(inPts, npts, pts, n);
361     this->PolyNormals->SetTuple(cellId,n);
362   }
363 
364   // Split mesh if sharp features
365   if ( this->Splitting )
366   {
367     //  Traverse all nodes; evaluate loops and feature edges.  If feature
368     //  edges found, split mesh creating new nodes.  Update polygon
369     // connectivity.
370     //
371       this->CosAngle = cos( vtkMath::RadiansFromDegrees( this->FeatureAngle) );
372     //  Splitting will create new points.  We have to create index array
373     // to map new points into old points.
374     //
375     this->Map = vtkIdList::New();
376     this->Map->SetNumberOfIds(numPts);
377     for (vtkIdType i=0; i < numPts; i++)
378     {
379       this->Map->SetId(i,i);
380     }
381 
382     for (ptId=0; ptId < numPts; ptId++)
383     {
384       this->MarkAndSplit(ptId);
385     }//for all input points
386 
387     numNewPts = this->Map->GetNumberOfIds();
388 
389     vtkDebugMacro(<<"Created " << numNewPts-numPts << " new points");
390 
391     //  Now need to map attributes of old points into new points.
392     //
393     outPD->CopyNormalsOff();
394     outPD->CopyAllocate(pd,numNewPts);
395 
396     newPts = vtkPoints::New();
397 
398     // set precision for the points in the output
399     if(this->OutputPointsPrecision == vtkAlgorithm::DEFAULT_PRECISION)
400     {
401       vtkPointSet *inputPointSet = vtkPointSet::SafeDownCast(input);
402       if(inputPointSet)
403       {
404         newPts->SetDataType(inputPointSet->GetPoints()->GetDataType());
405       }
406       else
407       {
408         newPts->SetDataType(VTK_FLOAT);
409       }
410     }
411     else if(this->OutputPointsPrecision == vtkAlgorithm::SINGLE_PRECISION)
412     {
413       newPts->SetDataType(VTK_FLOAT);
414     }
415     else if(this->OutputPointsPrecision == vtkAlgorithm::DOUBLE_PRECISION)
416     {
417       newPts->SetDataType(VTK_DOUBLE);
418     }
419 
420     newPts->SetNumberOfPoints(numNewPts);
421     for (ptId=0; ptId < numNewPts; ptId++)
422     {
423       oldId = this->Map->GetId(ptId);
424       newPts->SetPoint(ptId,inPts->GetPoint(oldId));
425       outPD->CopyData(pd,oldId,ptId);
426     }
427     this->Map->Delete();
428   } //splitting
429 
430   else //no splitting, so no new points
431   {
432     numNewPts = numPts;
433     outPD->CopyNormalsOff();
434     outPD->PassData(pd);
435   }
436 
437   if ( this->Consistency || this->Splitting )
438   {
439     delete [] this->Visited;
440     this->CellIds->Delete();
441   }
442 
443   this->UpdateProgress(0.80);
444 
445   //  Finally, traverse all elements, computing polygon normals and
446   //  accumulating them at the vertices.
447   //
448   if ( this->FlipNormals && ! this->Consistency )
449   {
450     flipDirection = -1.0;
451   }
452 
453   newNormals = vtkFloatArray::New();
454   newNormals->SetNumberOfComponents(3);
455   newNormals->SetNumberOfTuples(numNewPts);
456   newNormals->SetName("Normals");
457   float *fNormals = newNormals->WritePointer(0, 3 * numNewPts);
458   std::fill_n(fNormals, 3 * numNewPts, 0);
459 
460   float *fPolyNormals = this->PolyNormals->WritePointer(0, 3 * numPolys);
461 
462   if (this->ComputePointNormals)
463   {
464     for (cellId=0, newPolys->InitTraversal(); newPolys->GetNextCell(npts, pts);
465          ++cellId)
466     {
467       for (vtkIdType i = 0; i < npts; ++i)
468       {
469         fNormals[3 * pts[i]] += fPolyNormals[3 * cellId];
470         fNormals[3 * pts[i] + 1] += fPolyNormals[3 * cellId + 1];
471         fNormals[3 * pts[i] + 2] += fPolyNormals[3 * cellId + 2];
472       }
473     }
474 
475     for (vtkIdType i = 0; i < numNewPts; ++i)
476     {
477       const double length = sqrt(fNormals[3 * i] * fNormals[3 * i] +
478                                  fNormals[3 * i + 1] * fNormals[3 * i + 1] +
479                                  fNormals[3 * i + 2] * fNormals[3 * i + 2]
480                                  ) * flipDirection;
481       if (length != 0.0)
482       {
483         fNormals[3 * i] /= length;
484         fNormals[3 * i + 1] /= length;
485         fNormals[3 * i + 2] /= length;
486       }
487     }
488   }
489 
490   //  Update ourselves.  If no new nodes have been created (i.e., no
491   //  splitting), we can simply pass data through.
492   //
493   if ( ! this->Splitting )
494   {
495     output->SetPoints(inPts);
496   }
497 
498   //  If there is splitting, then have to send down the new data.
499   //
500   else
501   {
502     output->SetPoints(newPts);
503     newPts->Delete();
504   }
505 
506   if (this->ComputeCellNormals)
507   {
508     outCD->SetNormals(this->PolyNormals);
509   }
510   this->PolyNormals->Delete();
511 
512   if (this->ComputePointNormals)
513   {
514     outPD->SetNormals(newNormals);
515   }
516   newNormals->Delete();
517 
518   output->SetPolys(newPolys);
519   newPolys->Delete();
520 
521   // copy the original vertices and lines to the output
522   output->SetVerts(input->GetVerts());
523   output->SetLines(input->GetLines());
524 
525   this->OldMesh->Delete();
526   this->NewMesh->Delete();
527 
528   return 1;
529 }
530 
531 //  Propagate wave of consistently ordered polygons.
532 //
TraverseAndOrder(void)533 void vtkPolyDataNormals::TraverseAndOrder (void)
534 {
535   vtkIdType i, k;
536   int j, l, j1;
537   vtkIdType numIds, cellId;
538   vtkIdType *pts, *neiPts, npts, numNeiPts;
539   vtkIdType neighbor;
540   vtkIdList *tmpWave;
541 
542   // propagate wave until nothing left in wave
543   while ( (numIds=this->Wave->GetNumberOfIds()) > 0 )
544   {
545     for ( i=0; i < numIds; i++ )
546     {
547       cellId = this->Wave->GetId(i);
548 
549       this->NewMesh->GetCellPoints(cellId, npts, pts);
550 
551       for (j = 0, j1 = 1; j < npts; ++j, (j1 = (++j1 < npts) ? j1 : 0)) //for each edge neighbor
552       {
553         this->OldMesh->GetCellEdgeNeighbors(cellId, pts[j], pts[j1], this->CellIds);
554 
555         //  Check the direction of the neighbor ordering.  Should be
556         //  consistent with us (i.e., if we are n1->n2,
557         // neighbor should be n2->n1).
558         if ( this->CellIds->GetNumberOfIds() == 1 ||
559              this->NonManifoldTraversal )
560         {
561           for (k=0; k < this->CellIds->GetNumberOfIds(); k++)
562           {
563             if (this->Visited[this->CellIds->GetId(k)]==VTK_CELL_NOT_VISITED)
564             {
565               neighbor = this->CellIds->GetId(k);
566               this->NewMesh->GetCellPoints(neighbor,numNeiPts,neiPts);
567               for (l=0; l < numNeiPts; l++)
568               {
569                 if (neiPts[l] == pts[j1])
570                 {
571                   break;
572                 }
573               }
574 
575               //  Have to reverse ordering if neighbor not consistent
576               //
577               if ( neiPts[(l+1)%numNeiPts] != pts[j] )
578               {
579                 this->NumFlips++;
580                 this->NewMesh->ReverseCell(neighbor);
581               }
582               this->Visited[neighbor] = VTK_CELL_VISITED;
583               this->Wave2->InsertNextId(neighbor);
584             }// if cell not visited
585           } // for each edge neighbor
586         } //for manifold or non-manifold traversal allowed
587       } // for all edges of this polygon
588     } //for all cells in wave
589 
590     //swap wave and proceed with propagation
591     tmpWave = this->Wave;
592     this->Wave = this->Wave2;
593     this->Wave2 = tmpWave;
594     this->Wave2->Reset();
595   } //while wave still propagating
596 }
597 
598 //
599 //  Mark polygons around vertex.  Create new vertex (if necessary) and
600 //  replace (i.e., split mesh).
601 //
MarkAndSplit(vtkIdType ptId)602 void vtkPolyDataNormals::MarkAndSplit (vtkIdType ptId)
603 {
604   int i,j;
605 
606   // Get the cells using this point and make sure that we have to do something
607   unsigned short ncells;
608   vtkIdType *cells;
609   this->OldMesh->GetPointCells(ptId,ncells,cells);
610   if ( ncells <= 1 )
611   {
612     return; //point does not need to be further disconnected
613   }
614 
615   // Start moving around the "cycle" of points using the point. Label
616   // each point as requiring a visit. Then label each subregion of cells
617   // connected to this point that are connected (and not separated by
618   // a feature edge) with a given region number. For each N regions
619   // created, N-1 duplicate (split) points are created. The split point
620   // replaces the current point ptId in the polygons connectivity array.
621   //
622   // Start by initializing the cells as unvisited
623   for (i=0; i<ncells; i++)
624   {
625     this->Visited[cells[i]] = -1;
626   }
627 
628   // Loop over all cells and mark the region that each is in.
629   //
630   vtkIdType numPts;
631   vtkIdType *pts;
632   int numRegions = 0;
633   vtkIdType spot, neiPt[2], nei, cellId, neiCellId;
634   double thisNormal[3], neiNormal[3];
635   for (j=0; j<ncells; j++) //for all cells connected to point
636   {
637     if ( this->Visited[cells[j]] < 0 ) //for all unvisited cells
638     {
639       this->Visited[cells[j]] = numRegions;
640       //okay, mark all the cells connected to this seed cell and using ptId
641       this->OldMesh->GetCellPoints(cells[j],numPts,pts);
642 
643       //find the two edges
644       for (spot=0; spot < numPts; spot++)
645       {
646         if ( pts[spot] == ptId )
647         {
648           break;
649         }
650       }
651 
652       if ( spot == 0 )
653       {
654         neiPt[0] = pts[spot+1];
655         neiPt[1] = pts[numPts-1];
656       }
657       else if ( spot == (numPts-1) )
658       {
659         neiPt[0] = pts[spot-1];
660         neiPt[1] = pts[0];
661       }
662       else
663       {
664         neiPt[0] = pts[spot+1];
665         neiPt[1] = pts[spot-1];
666       }
667 
668       for (i=0; i<2; i++) //for each of the two edges of the seed cell
669       {
670         cellId = cells[j];
671         nei = neiPt[i];
672         while ( cellId >= 0 ) //while we can grow this region
673         {
674           this->OldMesh->GetCellEdgeNeighbors(cellId,ptId,nei,this->CellIds);
675           if ( this->CellIds->GetNumberOfIds() == 1 &&
676                this->Visited[(neiCellId=this->CellIds->GetId(0))] < 0 )
677           {
678             this->PolyNormals->GetTuple(cellId, thisNormal);
679             this->PolyNormals->GetTuple(neiCellId, neiNormal);
680 
681             if ( vtkMath::Dot(thisNormal,neiNormal) > CosAngle )
682             {
683               //visit and arrange to visit next edge neighbor
684               this->Visited[neiCellId] = numRegions;
685               cellId = neiCellId;
686               this->OldMesh->GetCellPoints(cellId,numPts,pts);
687 
688               for (spot=0; spot < numPts; spot++)
689               {
690                 if ( pts[spot] == ptId )
691                 {
692                   break;
693                 }
694               }
695 
696               if (spot == 0)
697               {
698                 nei = (pts[spot+1] != nei ? pts[spot+1] : pts[numPts-1]);
699               }
700               else if (spot == (numPts-1))
701               {
702                 nei = (pts[spot-1] != nei ? pts[spot-1] : pts[0]);
703               }
704               else
705               {
706                 nei = (pts[spot+1] != nei ? pts[spot+1] : pts[spot-1]);
707               }
708 
709             }//if not separated by edge angle
710             else
711             {
712               cellId = -1; //separated by edge angle
713             }
714           }//if can move to edge neighbor
715           else
716           {
717             cellId = -1;//separated by previous visit, boundary, or non-manifold
718           }
719         }//while visit wave is propagating
720       }//for each of the two edges of the starting cell
721       numRegions++;
722     }//if cell is unvisited
723   }//for all cells connected to point ptId
724 
725   if ( numRegions <=1 )
726   {
727     return; //a single region, no splitting ever required
728   }
729 
730   // Okay, for all cells not in the first region, the ptId is
731   // replaced with a new ptId, which is a duplicate of the first
732   // point, but disconnected topologically.
733   //
734   vtkIdType lastId = this->Map->GetNumberOfIds();
735   vtkIdType replacementPoint;
736   for (j=0; j<ncells; j++)
737   {
738     if (this->Visited[cells[j]] > 0 ) //replace point if splitting needed
739     {
740       replacementPoint = lastId + this->Visited[cells[j]] - 1;
741 
742       this->Map->InsertId(replacementPoint, ptId);
743 
744       this->NewMesh->GetCellPoints(cells[j],numPts,pts);
745       for (i=0; i < numPts; i++)
746       {
747         if ( pts[i] == ptId )
748         {
749           pts[i] = replacementPoint; // this is very nasty! direct write!
750           break;
751         }
752       }//replace ptId with split point
753     }//if not in first regions and requiring splitting
754   }//for all cells connected to ptId
755 }
756 
PrintSelf(ostream & os,vtkIndent indent)757 void vtkPolyDataNormals::PrintSelf(ostream& os, vtkIndent indent)
758 {
759   this->Superclass::PrintSelf(os,indent);
760 
761   os << indent << "Feature Angle: " << this->FeatureAngle << "\n";
762   os << indent << "Splitting: " << (this->Splitting ? "On\n" : "Off\n");
763   os << indent << "Consistency: " << (this->Consistency ? "On\n" : "Off\n");
764   os << indent << "Flip Normals: " << (this->FlipNormals ? "On\n" : "Off\n");
765   os << indent << "Auto Orient Normals: " << (this->AutoOrientNormals ? "On\n" : "Off\n");
766   os << indent << "Num Flips: " << this->NumFlips << endl;
767   os << indent << "Compute Point Normals: "
768      << (this->ComputePointNormals ? "On\n" : "Off\n");
769   os << indent << "Compute Cell Normals: "
770      << (this->ComputeCellNormals ? "On\n" : "Off\n");
771   os << indent << "Non-manifold Traversal: "
772      << (this->NonManifoldTraversal ? "On\n" : "Off\n");
773   os << indent << "Precision of the output points: "
774      << this->OutputPointsPrecision << "\n";
775 }
776 
777