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