1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkHyperTreeGridContour.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 
16 // Hide VTK_DEPRECATED_IN_9_0_0() warnings for this class.
17 #define VTK_DEPRECATION_LEVEL 0
18 
19 #include "vtkHyperTreeGridContour.h"
20 
21 #include "vtkBitArray.h"
22 #include "vtkCellArray.h"
23 #include "vtkCellData.h"
24 #include "vtkContourHelper.h"
25 #include "vtkContourValues.h"
26 #include "vtkHyperTree.h"
27 #include "vtkHyperTreeGrid.h"
28 #include "vtkHyperTreeGridNonOrientedCursor.h"
29 #include "vtkHyperTreeGridNonOrientedGeometryCursor.h"
30 #include "vtkHyperTreeGridNonOrientedMooreSuperCursor.h"
31 #include "vtkIdTypeArray.h"
32 #include "vtkIncrementalPointLocator.h"
33 #include "vtkInformation.h"
34 #include "vtkInformationVector.h"
35 #include "vtkLine.h"
36 #include "vtkMergePoints.h"
37 #include "vtkObjectFactory.h"
38 #include "vtkPixel.h"
39 #include "vtkPointData.h"
40 #include "vtkPolyData.h"
41 #include "vtkUnsignedCharArray.h"
42 #include "vtkVoxel.h"
43 
44 static const unsigned int MooreCursors1D[2] = { 0, 2 };
45 static const unsigned int MooreCursors2D[8] = { 0, 1, 2, 3, 5, 6, 7, 8 };
46 static const unsigned int MooreCursors3D[26] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15,
47   16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 };
48 static const unsigned int* MooreCursors[3] = {
49   MooreCursors1D,
50   MooreCursors2D,
51   MooreCursors3D,
52 };
53 
54 vtkStandardNewMacro(vtkHyperTreeGridContour);
55 
56 //------------------------------------------------------------------------------
vtkHyperTreeGridContour()57 vtkHyperTreeGridContour::vtkHyperTreeGridContour()
58 {
59   // Initialize storage for contour values
60   this->ContourValues = vtkContourValues::New();
61 
62   // Initialize locator to null
63   this->Locator = nullptr;
64 
65   // Initialize list of selected cells
66   this->SelectedCells = nullptr;
67 
68   // Initialize per-cell quantities of interest
69   this->CellSigns = nullptr;
70   this->CellScalars = nullptr;
71 
72   // Initialize structures for isocontouring
73   this->Helper = nullptr;
74   this->Leaves = vtkIdList::New();
75   this->Line = vtkLine::New();
76   this->Pixel = vtkPixel::New();
77   this->Voxel = vtkVoxel::New();
78 
79   // Output indices begin at 0
80   this->CurrentId = 0;
81 
82   // Process active point scalars by default
83   this->SetInputArrayToProcess(
84     0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS_THEN_CELLS, vtkDataSetAttributes::SCALARS);
85 
86   // Input scalars point to null by default
87   this->InScalars = nullptr;
88 }
89 
90 //------------------------------------------------------------------------------
~vtkHyperTreeGridContour()91 vtkHyperTreeGridContour::~vtkHyperTreeGridContour()
92 {
93   if (this->ContourValues)
94   {
95     this->ContourValues->Delete();
96     this->ContourValues = nullptr;
97   }
98 
99   if (this->Locator)
100   {
101     this->Locator->Delete();
102     this->Locator = nullptr;
103   }
104 
105   if (this->Line)
106   {
107     this->Line->Delete();
108     this->Line = nullptr;
109   }
110 
111   if (this->Pixel)
112   {
113     this->Pixel->Delete();
114     this->Pixel = nullptr;
115   }
116 
117   if (this->Voxel)
118   {
119     this->Voxel->Delete();
120     this->Voxel = nullptr;
121   }
122 
123   if (this->Leaves)
124   {
125     this->Leaves->Delete();
126     this->Leaves = nullptr;
127   }
128 }
129 
130 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)131 void vtkHyperTreeGridContour::PrintSelf(ostream& os, vtkIndent indent)
132 {
133   this->Superclass::PrintSelf(os, indent);
134 
135   this->ContourValues->PrintSelf(os, indent.GetNextIndent());
136 
137   os << indent << "CurrentId: " << this->CurrentId << endl;
138 
139   if (this->InScalars)
140   {
141     os << indent << "InScalars:\n";
142     this->InScalars->PrintSelf(os, indent.GetNextIndent());
143   }
144   else
145   {
146     os << indent << "InScalars: ( none )\n";
147   }
148 
149   if (this->Locator)
150   {
151     os << indent << "Locator: " << this->Locator << "\n";
152   }
153   else
154   {
155     os << indent << "Locator: (none)\n";
156   }
157 
158   if (this->Line)
159   {
160     os << indent << ": " << this->Line << "\n";
161   }
162   else
163   {
164     os << indent << ": (none)\n";
165   }
166 
167   if (this->Pixel)
168   {
169     os << indent << ": " << this->Pixel << "\n";
170   }
171   else
172   {
173     os << indent << ": (none)\n";
174   }
175 
176   if (this->Voxel)
177   {
178     os << indent << ": " << this->Voxel << "\n";
179   }
180   else
181   {
182     os << indent << ": (none)\n";
183   }
184 
185   if (this->Leaves)
186   {
187     os << indent << ": " << this->Leaves << "\n";
188   }
189   else
190   {
191     os << indent << ": (none)\n";
192   }
193 }
194 
195 //------------------------------------------------------------------------------
FillOutputPortInformation(int,vtkInformation * info)196 int vtkHyperTreeGridContour::FillOutputPortInformation(int, vtkInformation* info)
197 {
198   info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkPolyData");
199   return 1;
200 }
201 
202 //------------------------------------------------------------------------------
SetLocator(vtkIncrementalPointLocator * locator)203 void vtkHyperTreeGridContour::SetLocator(vtkIncrementalPointLocator* locator)
204 {
205   // Check if proposed locator is identical to existing one
206   if (this->Locator == locator)
207   {
208     return;
209   }
210 
211   // Clean up existing locator instance variable
212   if (this->Locator)
213   {
214     this->Locator->Delete();
215     this->Locator = nullptr;
216   }
217 
218   // Register proposed locator and assign it
219   if (locator)
220   {
221     locator->Register(this);
222   }
223   this->Locator = locator;
224 
225   // Modify time
226   this->Modified();
227 }
228 
229 //------------------------------------------------------------------------------
CreateDefaultLocator()230 void vtkHyperTreeGridContour::CreateDefaultLocator()
231 {
232   // If no locator instance variable create a merge point one
233   if (!this->Locator)
234   {
235     this->Locator = vtkMergePoints::New();
236     this->Locator->Register(this);
237     this->Locator->Delete();
238   }
239 }
240 
241 //------------------------------------------------------------------------------
GetMTime()242 vtkMTimeType vtkHyperTreeGridContour::GetMTime()
243 {
244   vtkMTimeType mTime = this->Superclass::GetMTime();
245   vtkMTimeType time;
246 
247   if (this->ContourValues)
248   {
249     time = this->ContourValues->GetMTime();
250     mTime = (time > mTime ? time : mTime);
251   }
252   if (this->Locator)
253   {
254     time = this->Locator->GetMTime();
255     mTime = (time > mTime ? time : mTime);
256   }
257 
258   return mTime;
259 }
260 
261 //------------------------------------------------------------------------------
ProcessTrees(vtkHyperTreeGrid * input,vtkDataObject * outputDO)262 int vtkHyperTreeGridContour::ProcessTrees(vtkHyperTreeGrid* input, vtkDataObject* outputDO)
263 {
264   // Downcast output data object to polygonal data set
265   vtkPolyData* output = vtkPolyData::SafeDownCast(outputDO);
266   if (!output)
267   {
268     vtkErrorMacro("Incorrect type of output: " << outputDO->GetClassName());
269     return 0;
270   }
271 
272   // Retrieve scalar quantity of interest
273   this->InScalars = this->GetInputArrayToProcess(0, input);
274   if (!this->InScalars)
275   {
276     vtkWarningMacro(<< "No scalar data to contour");
277     return 1;
278   }
279 
280   // Initialize output point data
281   this->InData = input->GetCellData();
282   this->OutData = output->GetPointData();
283   this->OutData->CopyAllocate(this->InData);
284 
285   // Output indices begin at 0
286   this->CurrentId = 0;
287 
288   // Retrieve material mask
289   this->InMask = input->HasMask() ? input->GetMask() : nullptr;
290 
291   // Retrive ghost cells
292   this->InGhostArray = input->GetGhostCells();
293 
294   // Estimate output size as a multiple of 1024
295   vtkIdType numCells = input->GetNumberOfVertices();
296   vtkIdType numContours = this->ContourValues->GetNumberOfContours();
297   vtkIdType estimatedSize = static_cast<vtkIdType>(pow(static_cast<double>(numCells), .75));
298   estimatedSize *= numContours;
299   estimatedSize = estimatedSize / 1024 * 1024;
300   if (estimatedSize < 1024)
301   {
302     estimatedSize = 1024;
303   }
304 
305   // Create storage for output points
306   vtkPoints* newPts = vtkPoints::New();
307   newPts->Allocate(estimatedSize, estimatedSize);
308 
309   // Create storage for output vertices
310   vtkCellArray* newVerts = vtkCellArray::New();
311   newVerts->AllocateExact(estimatedSize, estimatedSize);
312 
313   // Create storage for output lines
314   vtkCellArray* newLines = vtkCellArray::New();
315   newLines->AllocateExact(estimatedSize, estimatedSize);
316 
317   // Create storage for output polygons
318   vtkCellArray* newPolys = vtkCellArray::New();
319   newPolys->AllocateExact(estimatedSize, estimatedSize);
320 
321   // Create storage for output scalar values
322   this->CellScalars = this->InScalars->NewInstance();
323   this->CellScalars->SetNumberOfComponents(this->InScalars->GetNumberOfComponents());
324   this->CellScalars->Allocate(this->CellScalars->GetNumberOfComponents() * 8);
325 
326   // Initialize point locator
327   if (!this->Locator)
328   {
329     // Create default locator if needed
330     this->CreateDefaultLocator();
331   }
332   this->Locator->InitPointInsertion(newPts, input->GetBounds(), estimatedSize);
333 
334   vtkNew<vtkPointData> inPointData;
335   inPointData->PassData(input->GetCellData());
336 
337   // Instantiate a contour helper for convenience, with triangle generation on
338   this->Helper = new vtkContourHelper(this->Locator, newVerts, newLines, newPolys, inPointData,
339     nullptr, output->GetPointData(), nullptr, estimatedSize, true);
340 
341   // Create storage to keep track of selected cells
342   this->SelectedCells = vtkBitArray::New();
343   this->SelectedCells->SetNumberOfTuples(numCells);
344 
345   // Initialize storage for signs and values
346   // NOLINTNEXTLINE(bugprone-sizeof-expression)
347   this->CellSigns = (vtkBitArray**)malloc(numContours * sizeof(*this->CellSigns));
348   this->Signs.resize(numContours, true);
349   for (int c = 0; c < numContours; ++c)
350   {
351     this->CellSigns[c] = vtkBitArray::New();
352     this->CellSigns[c]->SetNumberOfTuples(numCells);
353   }
354 
355   // First pass across tree roots to evince cells intersected by contours
356   vtkIdType index;
357   vtkHyperTreeGrid::vtkHyperTreeGridIterator it;
358   input->InitializeTreeIterator(it);
359   vtkNew<vtkHyperTreeGridNonOrientedCursor> cursor;
360   while (it.GetNextTree(index))
361   {
362     // Initialize new grid cursor at root of current input tree
363     input->InitializeNonOrientedCursor(cursor, index);
364     // Pre-process tree recursively
365     this->RecursivelyPreProcessTree(cursor);
366   } // it
367 
368   // Second pass across tree roots: now compute isocontours recursively
369   input->InitializeTreeIterator(it);
370   vtkNew<vtkHyperTreeGridNonOrientedMooreSuperCursor> supercursor;
371   while (it.GetNextTree(index))
372   {
373     // Initialize new Moore cursor at root of current tree
374     input->InitializeNonOrientedMooreSuperCursor(supercursor, index);
375     // Compute contours recursively
376     this->RecursivelyProcessTree(supercursor);
377   } // it
378 
379   // Set output
380   output->SetPoints(newPts);
381   if (newVerts->GetNumberOfCells())
382   {
383     output->SetVerts(newVerts);
384   }
385   if (newLines->GetNumberOfCells())
386   {
387     output->SetLines(newLines);
388   }
389   if (newPolys->GetNumberOfCells())
390   {
391     output->SetPolys(newPolys);
392   }
393   newPolys->Delete();
394 
395   // Clean up
396   this->SelectedCells->Delete();
397   for (vtkIdType c = 0; c < this->GetNumberOfContours(); ++c)
398   {
399     if (this->CellSigns[c])
400     {
401       this->CellSigns[c]->Delete();
402     }
403   } // c
404   free(this->CellSigns);
405   delete this->Helper;
406   this->CellScalars->Delete();
407   newPts->Delete();
408   newVerts->Delete();
409   newLines->Delete();
410   this->Locator->Initialize();
411 
412   // Squeeze output
413   output->Squeeze();
414 
415   return 1;
416 }
417 
418 //------------------------------------------------------------------------------
RecursivelyPreProcessTree(vtkHyperTreeGridNonOrientedCursor * cursor)419 bool vtkHyperTreeGridContour::RecursivelyPreProcessTree(vtkHyperTreeGridNonOrientedCursor* cursor)
420 {
421   // Retrieve global index of input cursor
422   vtkIdType id = cursor->GetGlobalNodeIndex();
423 
424   if (this->InGhostArray && this->InGhostArray->GetTuple1(id))
425   {
426     return false;
427   }
428 
429   // Retrieve number of contours
430   vtkIdType numContours = this->ContourValues->GetNumberOfContours();
431 
432   // Descend further into input trees only if cursor is not a leaf
433   bool selected = false;
434   if (!cursor->IsLeaf())
435   {
436     // Cursor is not at leaf, recurse to all all children
437     int numChildren = cursor->GetNumberOfChildren();
438     for (int child = 0; child < numChildren; ++child)
439     {
440       // Create storage for signs relative to contour values
441       std::vector<bool> signs(numContours);
442 
443       cursor->ToChild(child);
444 
445       // Recurse and keep track of whether this branch is selected
446       selected |= this->RecursivelyPreProcessTree(cursor);
447 
448       // Check if branch not completely selected
449       if (!selected)
450       {
451         // If not, update contour values
452         for (int c = 0; c < numContours; ++c)
453         {
454           // Retrieve global index of child
455           vtkIdType childId = cursor->GetGlobalNodeIndex();
456 
457           // Compute and store selection flags for current contour
458           if (!child)
459           {
460             // Initialize sign array with sign of first child
461             signs[c] = (this->CellSigns[c]->GetTuple1(childId) != 0.0);
462           } // if ( ! child )
463           else
464           {
465             // For subsequent children compare their sign with stored value
466             if (signs[c] != (this->CellSigns[c]->GetTuple1(childId) != 0.0))
467             {
468               // A change of sign occurred, therefore cell must selected
469               selected = true;
470             }
471           } // else
472         }   // c
473       }     // if( ! selected )
474 
475       cursor->ToParent();
476     } // child
477   }
478   else if (!this->InGhostArray || !this->InGhostArray->GetTuple1(id))
479   {
480     // Cursor is at leaf, retrieve its active scalar value
481     double val = this->InScalars->GetTuple1(id);
482 
483     // Iterate over all contours
484     double* values = this->ContourValues->GetValues();
485     for (int c = 0; c < numContours; ++c)
486     {
487       this->Signs[c] = val > values[c];
488     }
489   } // else
490 
491   // Update list of selected cells
492   this->SelectedCells->SetTuple1(id, selected);
493 
494   // Set signs for all contours
495   for (int c = 0; c < numContours; ++c)
496   {
497     // Parent cell has that of one of its children
498     this->CellSigns[c]->SetTuple1(id, this->Signs[c]);
499   }
500 
501   // Return whether current node was fully selected
502   return selected;
503 }
504 
505 //------------------------------------------------------------------------------
RecursivelyProcessTree(vtkHyperTreeGridNonOrientedMooreSuperCursor * supercursor)506 void vtkHyperTreeGridContour::RecursivelyProcessTree(
507   vtkHyperTreeGridNonOrientedMooreSuperCursor* supercursor)
508 {
509   // Retrieve global index of input cursor
510   vtkIdType id = supercursor->GetGlobalNodeIndex();
511 
512   if (this->InGhostArray && this->InGhostArray->GetTuple1(id))
513   {
514     return;
515   }
516   // Retrieve dimensionality
517   unsigned int dim = supercursor->GetDimension();
518 
519   // Descend further into input trees only if cursor is not a leaf
520   if (!supercursor->IsLeaf())
521   {
522     // Cell is not selected until proven otherwise
523     bool selected = false;
524 
525     // Iterate over contours
526     for (vtkIdType c = 0; c < this->ContourValues->GetNumberOfContours() && !selected; ++c)
527     {
528       // Retrieve sign with respect to contour value at current cursor
529       bool sign = (this->CellSigns[c]->GetTuple1(id) != 0.0);
530 
531       // Iterate over all cursors of Von Neumann neighborhood around center
532       unsigned int nn = supercursor->GetNumberOfCursors() - 1;
533       for (unsigned int neighbor = 0; neighbor < nn && !selected; ++neighbor)
534       {
535         // Retrieve global index of neighbor
536         unsigned int icursorN = MooreCursors[dim - 1][neighbor];
537         if (supercursor->HasTree(icursorN))
538         {
539           vtkIdType idN = supercursor->GetGlobalNodeIndex(icursorN);
540 
541           // Decide whether neighbor was selected or must be retained because of a sign change
542           selected = this->SelectedCells->GetTuple1(idN) == 1 ||
543             ((this->CellSigns[c]->GetTuple1(idN) != 0.0) != sign) ||
544             (this->InGhostArray && this->InGhostArray->GetTuple1(idN));
545         }
546         else
547         {
548           selected = false;
549         }
550       } // neighbor
551     }   // c
552 
553     if (selected)
554     {
555       // Node has at least one neighbor containing one contour, recurse to all children
556       unsigned int numChildren = supercursor->GetNumberOfChildren();
557       for (unsigned int child = 0; child < numChildren; ++child)
558       {
559         // Create child cursor from parent in input grid
560         supercursor->ToChild(child);
561         // Recurse
562         this->RecursivelyProcessTree(supercursor);
563         supercursor->ToParent();
564       }
565     }
566   }
567   else if ((!this->InMask || !this->InMask->GetTuple1(id)))
568   {
569     // Cell is not masked, iterate over its corners
570     unsigned int numLeavesCorners = 1 << dim;
571     for (unsigned int cornerIdx = 0; cornerIdx < numLeavesCorners; ++cornerIdx)
572     {
573       bool owner = true;
574       this->Leaves->SetNumberOfIds(numLeavesCorners);
575 
576       // Iterate over every leaf touching the corner and check ownership
577       for (unsigned int leafIdx = 0; leafIdx < numLeavesCorners && owner; ++leafIdx)
578       {
579         owner = supercursor->GetCornerCursors(cornerIdx, leafIdx, this->Leaves);
580       } // leafIdx
581 
582       // If cell owns dual cell, compute contours thereof
583       if (owner)
584       {
585         vtkIdType numContours = this->ContourValues->GetNumberOfContours();
586         double* values = this->ContourValues->GetValues();
587 
588         // Generate contour topology depending on dimensionality
589         vtkCell* cell = nullptr;
590         switch (dim)
591         {
592           case 1:
593             cell = this->Line;
594             break;
595           case 2:
596             cell = this->Pixel;
597             break;
598           case 3:
599             cell = this->Voxel;
600         } // switch ( dim )
601 
602         // Iterate over cell corners
603         double x[3];
604         supercursor->GetPoint(x);
605         for (unsigned int _cornerIdx = 0; _cornerIdx < numLeavesCorners; ++_cornerIdx)
606         {
607           // Get cursor corresponding to this corner
608           vtkIdType cursorId = this->Leaves->GetId(_cornerIdx);
609 
610           // Retrieve neighbor coordinates and store them
611           supercursor->GetPoint(cursorId, x);
612           cell->Points->SetPoint(_cornerIdx, x);
613 
614           // Retrieve neighbor index and add to list of cell vertices
615           vtkIdType idN = supercursor->GetGlobalNodeIndex(cursorId);
616           cell->PointIds->SetId(_cornerIdx, idN);
617 
618           // Assign scalar value attached to this contour item
619           this->CellScalars->SetTuple(_cornerIdx, this->InScalars->GetTuple(idN));
620         } // cornerIdx
621         // Compute cell isocontour for each isovalue
622         for (int c = 0; c < numContours; ++c)
623         {
624           this->Helper->Contour(cell, values[c], this->CellScalars, this->CurrentId);
625         } // c
626 
627         // Increment output cell counter
628         ++this->CurrentId;
629       } // if ( owner )
630     }   // cornerIdx
631   }     // else if ( ! this->InMask || this->InMask->GetTuple1( id ) )
632 }
633