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