VTK  9.4.20241217
vtk3DLinearGridInternal.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
2// SPDX-License-Identifier: BSD-3-Clause
25#ifndef vtk3DLinearGridInternal_h
26#define vtk3DLinearGridInternal_h
27
28// Include appropriate cell types
29#include "vtkCellArray.h"
31#include "vtkHexahedron.h"
32#include "vtkPyramid.h"
33#include "vtkSmartPointer.h"
34#include "vtkTetra.h"
35#include "vtkVoxel.h"
36#include "vtkWedge.h"
37#include <cmath>
38
39namespace
40{ // anonymous namespace
41
42//========================= CELL MACHINERY ====================================
43
44// Implementation note: this filter currently handles 3D linear cells. It
45// could be extended to handle other 3D cell types.
46
47// The maximum number of verts per cell (hexahedron)
48#define MAX_CELL_VERTS 8
49
50// Base class to represent cells
51struct BaseCell
52{
53 unsigned char CellType;
54 unsigned char NumVerts;
55 unsigned char NumEdges;
56 unsigned short* Cases;
57 static unsigned char Mask[MAX_CELL_VERTS];
58
59 BaseCell(int cellType)
60 : CellType(cellType)
61 , NumVerts(0)
62 , NumEdges(0)
63 , Cases(nullptr)
64 {
65 }
66 virtual ~BaseCell() = default;
67
68 // Set up the case table. This is done by accessing standard VTK cells and
69 // repackaging the case table for efficiency. The format of the case table
70 // is as follows: a linear array, organized into two parts: 1) offsets into
71 // the second part, and 2) the cases. The first 2^NumVerts entries are the
72 // offsets which refer to the 2^NumVerts cases in the second part. Each
73 // case is represented by the number of edges, followed by pairs of
74 // vertices (v0,v1) for each edge. Note that groups of three contiguous
75 // edges form a triangle.
76 virtual void BuildCases() = 0;
77 void BuildCases(int numCases, const vtkIdType** edges, int** cases, unsigned short* caseArray);
78};
79// Used to generate case mask
80// NOLINTNEXTLINE(misc-definitions-in-headers)
81unsigned char BaseCell::Mask[MAX_CELL_VERTS] = { 1, 2, 4, 8, 16, 32, 64, 128 };
82// Build repackaged case table and place into cases array.
83inline void BaseCell::BuildCases(
84 int numCases, const vtkIdType** edges, int** cases, unsigned short* caseArray)
85{
86 int caseOffset = numCases;
87 for (int caseNum = 0; caseNum < numCases; ++caseNum)
88 {
89 caseArray[caseNum] = caseOffset;
90 int* triCases = cases[caseNum];
91
92 // Count the number of edges
93 const int count = std::find(triCases, triCases + numCases, -1) - triCases;
94 caseArray[caseOffset++] = count;
95
96 // Now populate the edges
97 const vtkIdType* edge;
98 for (int i = 0; triCases[i] != -1; ++i)
99 {
100 edge = edges[triCases[i]];
101 caseArray[caseOffset++] = edge[0];
102 caseArray[caseOffset++] = edge[1];
103 }
104 } // for all cases
105}
106
107// Contour tetrahedral cell------------------------------------------------------
108// Repackages case table for more efficient processing.
109struct TetraCell : public BaseCell
110{
111 static unsigned short TetraCases[152];
112
113 TetraCell()
114 : BaseCell(VTK_TETRA)
115 {
116 this->NumVerts = 4;
117 this->NumEdges = 6;
118 this->BuildCases();
119 this->Cases = TetraCell::TetraCases;
120 }
121 ~TetraCell() override = default;
122 void BuildCases() override;
123};
124// Dummy initialization filled in later at initialization. The lengtth of the
125// array is determined from the equation length=(2*NumCases + 3*2*NumTris).
126// NOLINTNEXTLINE(misc-definitions-in-headers)
127unsigned short TetraCell::TetraCases[152] = { 0 };
128// Load and transform vtkTetra case table. The case tables are repackaged for
129// efficiency (e.g., support the GetCase() method).
130inline void TetraCell::BuildCases()
131{
132 const vtkIdType** edges = new const vtkIdType*[this->NumEdges];
133 int numCases = std::pow(2, this->NumVerts);
134 int** cases = new int*[numCases];
135 for (int i = 0; i < this->NumEdges; ++i)
136 {
138 }
139 for (int i = 0; i < numCases; ++i)
140 {
141 cases[i] = vtkTetra::GetTriangleCases(i);
142 }
143
144 BaseCell::BuildCases(numCases, edges, cases, TetraCell::TetraCases);
145
146 delete[] edges;
147 delete[] cases;
148}
149
150// Contour hexahedral cell------------------------------------------------------
151struct HexahedronCell : public BaseCell
152{
153 static unsigned short HexahedronCases[5432];
154
155 HexahedronCell()
156 : BaseCell(VTK_HEXAHEDRON)
157 {
158 this->NumVerts = 8;
159 this->NumEdges = 12;
160 this->BuildCases();
161 this->Cases = HexahedronCell::HexahedronCases;
162 }
163 ~HexahedronCell() override = default;
164 void BuildCases() override;
165};
166// Dummy initialization filled in later at instantiation
167// NOLINTNEXTLINE(misc-definitions-in-headers)
168unsigned short HexahedronCell::HexahedronCases[5432] = { 0 };
169// Load and transform marching cubes case table. The case tables are
170// repackaged for efficiency (e.g., support the GetCase() method).
171inline void HexahedronCell::BuildCases()
172{
173 const vtkIdType** edges = new const vtkIdType*[this->NumEdges];
174 int numCases = std::pow(2, this->NumVerts);
175 int** cases = new int*[numCases];
176 for (int i = 0; i < this->NumEdges; ++i)
177 {
179 }
180 for (int i = 0; i < numCases; ++i)
181 {
183 }
184
185 BaseCell::BuildCases(numCases, edges, cases, HexahedronCell::HexahedronCases);
186
187 delete[] edges;
188 delete[] cases;
189}
190
191// Contour wedge cell ------------------------------------------------------
192struct WedgeCell : public BaseCell
193{
194 static unsigned short WedgeCases[968];
195
196 WedgeCell()
197 : BaseCell(VTK_WEDGE)
198 {
199 this->NumVerts = 6;
200 this->NumEdges = 9;
201 this->BuildCases();
202 this->Cases = WedgeCell::WedgeCases;
203 }
204 ~WedgeCell() override = default;
205 void BuildCases() override;
206};
207// Dummy initialization filled in later at instantiation
208// NOLINTNEXTLINE(misc-definitions-in-headers)
209unsigned short WedgeCell::WedgeCases[968] = { 0 };
210// Load and transform marching cubes case table. The case tables are
211// repackaged for efficiency (e.g., support the GetCase() method).
212inline void WedgeCell::BuildCases()
213{
214 const vtkIdType** edges = new const vtkIdType*[this->NumEdges];
215 int numCases = std::pow(2, this->NumVerts);
216 int** cases = new int*[numCases];
217 for (int i = 0; i < this->NumEdges; ++i)
218 {
220 }
221 for (int i = 0; i < numCases; ++i)
222 {
223 cases[i] = vtkWedge::GetTriangleCases(i);
224 }
225
226 BaseCell::BuildCases(numCases, edges, cases, WedgeCell::WedgeCases);
227
228 delete[] edges;
229 delete[] cases;
230}
231
232// Contour pyramid cell------------------------------------------------------
233struct PyramidCell : public BaseCell
234{
235 static unsigned short PyramidCases[448];
236
237 PyramidCell()
238 : BaseCell(VTK_PYRAMID)
239 {
240 this->NumVerts = 5;
241 this->NumEdges = 8;
242 this->BuildCases();
243 this->Cases = PyramidCell::PyramidCases;
244 }
245 ~PyramidCell() override = default;
246 void BuildCases() override;
247};
248// Dummy initialization filled in later at instantiation
249// NOLINTNEXTLINE(misc-definitions-in-headers)
250unsigned short PyramidCell::PyramidCases[448] = { 0 };
251// Load and transform marching cubes case table. The case tables are
252// repackaged for efficiency (e.g., support the GetCase() method).
253inline void PyramidCell::BuildCases()
254{
255 const vtkIdType** edges = new const vtkIdType*[this->NumEdges];
256 int numCases = std::pow(2, this->NumVerts);
257 int** cases = new int*[numCases];
258 for (int i = 0; i < this->NumEdges; ++i)
259 {
261 }
262 for (int i = 0; i < numCases; ++i)
263 {
264 cases[i] = vtkPyramid::GetTriangleCases(i);
265 }
266
267 BaseCell::BuildCases(numCases, edges, cases, PyramidCell::PyramidCases);
268
269 delete[] edges;
270 delete[] cases;
271}
272
273// Contour voxel cell------------------------------------------------------
274struct VoxelCell : public BaseCell
275{
276 static unsigned short VoxCases[5432];
277
278 VoxelCell()
279 : BaseCell(VTK_VOXEL)
280 {
281 this->NumVerts = 8;
282 this->NumEdges = 12;
283 this->BuildCases();
284 this->Cases = VoxelCell::VoxCases;
285 }
286 ~VoxelCell() override = default;
287 void BuildCases() override;
288};
289// Dummy initialization filled in later at instantiation
290// NOLINTNEXTLINE(misc-definitions-in-headers)
291unsigned short VoxelCell::VoxCases[5432] = { 0 };
292// Load and transform marching cubes case table. The case tables are
293// repackaged for efficiency (e.g., support the GetCase() method). Note that
294// the MC cases (vtkMarchingCubesTriangleCases) are specified for the
295// hexahedron; voxels require a transformation to produce correct output.
296inline void VoxelCell::BuildCases()
297{
298 // Map the voxel points consistent with the hex edges and cases, Basically
299 // the hex points (2,3,6,7) are ordered (3,2,7,6) on the voxel.
300 const vtkIdType** edges = new const vtkIdType*[this->NumEdges];
301 constexpr vtkIdType voxEdges[12][2] = { { 0, 1 }, { 1, 3 }, { 2, 3 }, { 0, 2 }, { 4, 5 },
302 { 5, 7 }, { 6, 7 }, { 4, 6 }, { 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 } };
303
304 for (int i = 0; i < this->NumEdges; ++i)
305 {
306 edges[i] = voxEdges[i];
307 }
308
309 // Build the voxel cases. Have to shuffle them around due to different
310 // vertex ordering.
311 unsigned int numCases = std::pow(2, this->NumVerts);
312 int** cases = new int*[numCases];
313 unsigned int hexCase, voxCase;
314 for (hexCase = 0; hexCase < numCases; ++hexCase)
315 {
316 voxCase = ((hexCase & BaseCell::Mask[0]) ? 1 : 0) << 0;
317 voxCase |= ((hexCase & BaseCell::Mask[1]) ? 1 : 0) << 1;
318 voxCase |= ((hexCase & BaseCell::Mask[2]) ? 1 : 0) << 3;
319 voxCase |= ((hexCase & BaseCell::Mask[3]) ? 1 : 0) << 2;
320 voxCase |= ((hexCase & BaseCell::Mask[4]) ? 1 : 0) << 4;
321 voxCase |= ((hexCase & BaseCell::Mask[5]) ? 1 : 0) << 5;
322 voxCase |= ((hexCase & BaseCell::Mask[6]) ? 1 : 0) << 7;
323 voxCase |= ((hexCase & BaseCell::Mask[7]) ? 1 : 0) << 6;
324 cases[voxCase] = vtkHexahedron::GetTriangleCases(hexCase);
325 }
326
327 BaseCell::BuildCases(numCases, edges, cases, VoxelCell::VoxCases);
328
329 delete[] edges;
330 delete[] cases;
331}
332
333// Contour empty cell. These cells are skipped.---------------------------------
334struct EmptyCell : public BaseCell
335{
336 static unsigned short EmptyCases[2];
337
338 EmptyCell()
339 : BaseCell(VTK_EMPTY_CELL)
340 {
341 this->NumVerts = 0;
342 this->NumEdges = 0;
343 this->Cases = EmptyCell::EmptyCases;
344 }
345 ~EmptyCell() override = default;
346 void BuildCases() override {}
347};
348// No triangles generated
349// NOLINTNEXTLINE(misc-definitions-in-headers)
350unsigned short EmptyCell::EmptyCases[2] = { 0, 0 };
351
352// This is a general iterator which assumes that the unstructured grid has a
353// mix of cells. Any cell that is not processed by this contouring algorithm
354// (i.e., not one of tet, hex, pyr, wedge, voxel) is skipped.
355struct CellIter
356{
357 // Current active cell, and whether it is a copy (which controls
358 // the destruction process).
359 bool Copy;
360 BaseCell* Cell;
361
362 // The iteration state.
363 unsigned char NumVerts;
364 const unsigned short* Cases;
365
366 // References to unstructured grid for cell traversal.
367 vtkIdType NumCells;
368 const unsigned char* Types;
371
372 // All possible cell types. The iterator switches between them when
373 // processing. All unsupported cells are of type EmptyCell.
374 TetraCell* Tetra;
375 HexahedronCell* Hexahedron;
376 PyramidCell* Pyramid;
377 WedgeCell* Wedge;
378 VoxelCell* Voxel;
379 EmptyCell* Empty;
380
381 CellIter()
382 : Copy(true)
383 , Cell(nullptr)
384 , NumVerts(0)
385 , Cases(nullptr)
386 , NumCells(0)
387 , Types(nullptr)
388 , Tetra(nullptr)
389 , Hexahedron(nullptr)
390 , Pyramid(nullptr)
391 , Wedge(nullptr)
392 , Voxel(nullptr)
393 , Empty(nullptr)
394 {
395 }
396
397 CellIter(vtkIdType numCells, unsigned char* types, vtkCellArray* cellArray)
398 : Copy(false)
399 , Cell(nullptr)
400 , NumVerts(0)
401 , Cases(nullptr)
402 , NumCells(numCells)
403 , Types(types)
404 , CellArray(cellArray)
405 , ConnIter(vtk::TakeSmartPointer(cellArray->NewIterator()))
406 {
407 this->Tetra = new TetraCell;
408 this->Hexahedron = new HexahedronCell;
409 this->Pyramid = new PyramidCell;
410 this->Wedge = new WedgeCell;
411 this->Voxel = new VoxelCell;
412 this->Empty = new EmptyCell;
413 }
414
415 ~CellIter()
416 {
417 if (!this->Copy)
418 {
419 delete this->Tetra;
420 delete this->Hexahedron;
421 delete this->Pyramid;
422 delete this->Wedge;
423 delete this->Voxel;
424 delete this->Empty;
425 }
426 }
427
428 CellIter(const CellIter&) = default; // remove compiler warnings
429
430 // Shallow copy to avoid new/delete.
431 CellIter& operator=(const CellIter& cellIter)
432 {
433 this->Copy = true;
434 this->Cell = nullptr;
435
436 this->NumVerts = cellIter.NumVerts;
437 this->Cases = cellIter.Cases;
438
439 this->NumCells = cellIter.NumCells;
440 this->Types = cellIter.Types;
441 this->CellArray = cellIter.CellArray;
442
443 // This class is passed around by pointer and only copied deliberately
444 // to create thread-local copies. Since we don't want to share state,
445 // create a new iterator here:
446 if (cellIter.ConnIter)
447 {
448 this->ConnIter = vtk::TakeSmartPointer(this->CellArray->NewIterator());
449 this->ConnIter->GoToCell(cellIter.ConnIter->GetCurrentCellId());
450 }
451 else
452 {
453 this->ConnIter = nullptr;
454 }
455
456 this->Tetra = cellIter.Tetra;
457 this->Hexahedron = cellIter.Hexahedron;
458 this->Pyramid = cellIter.Pyramid;
459 this->Wedge = cellIter.Wedge;
460 this->Voxel = cellIter.Voxel;
461 this->Empty = cellIter.Empty;
462
463 return *this;
464 }
465
466 // Decode the case table. (See previous documentation of case table
467 // organization.) Note that bounds/range chacking is not performed
468 // for efficiency.
469 const unsigned short* GetCase(unsigned char caseNum)
470 {
471 return (this->Cases + this->Cases[caseNum]);
472 }
473
474 // Methods for caching traversal. Initialize() sets up the traversal
475 // process; Next() advances to the next cell. Note that the public data
476 // members representing the iteration state (NumVerts, Cases, ConnIter) are
477 // modified by these methods, and then subsequently read during iteration.
478 const vtkIdType* Initialize(vtkIdType cellId)
479 {
480 this->Cell = this->GetCell(this->Types[cellId]);
481 this->NumVerts = this->Cell->NumVerts;
482 this->Cases = this->Cell->Cases;
483 this->ConnIter->GoToCell(cellId);
484
485 vtkIdType dummy;
486 const vtkIdType* conn;
487 this->ConnIter->GetCurrentCell(dummy, conn);
488 return conn;
489 }
490
491 const vtkIdType* Next()
492 {
493 this->ConnIter->GoToNextCell();
494
495 if (this->ConnIter->IsDoneWithTraversal())
496 {
497 return nullptr;
498 }
499
500 const vtkIdType currentCellId = this->ConnIter->GetCurrentCellId();
501
502 // Only update information if the cell type changes. Note however that
503 // empty cells may have to be treated specially.
504 if (this->Cell->CellType == VTK_EMPTY_CELL ||
505 this->Cell->CellType != this->Types[currentCellId])
506 {
507 this->Cell = this->GetCell(this->Types[currentCellId]);
508 this->NumVerts = this->Cell->NumVerts;
509 this->Cases = this->Cell->Cases;
510 }
511
512 vtkIdType dummy;
513 const vtkIdType* conn;
514 this->ConnIter->GetCurrentCell(dummy, conn);
515 return conn;
516 }
517
518 // Method for random access of cell, no caching
519 unsigned char GetCellType(vtkIdType cellId) { return this->Types[cellId]; }
520
521 // Method for random access of cell, no caching
522 const vtkIdType* GetCellIds(vtkIdType cellId)
523 {
524 this->Cell = this->GetCell(this->Types[cellId]);
525 this->NumVerts = this->Cell->NumVerts;
526 this->Cases = this->Cell->Cases;
527 this->ConnIter->GoToCell(cellId);
528
529 vtkIdType dummy;
530 const vtkIdType* conn;
531 this->ConnIter->GetCurrentCell(dummy, conn);
532 return conn;
533 }
534
535 // Switch to the appropriate cell type.
536 BaseCell* GetCell(int cellType)
537 {
538 switch (cellType)
539 {
540 case VTK_TETRA:
541 return this->Tetra;
542 case VTK_HEXAHEDRON:
543 return this->Hexahedron;
544 case VTK_WEDGE:
545 return this->Wedge;
546 case VTK_PYRAMID:
547 return this->Pyramid;
548 case VTK_VOXEL:
549 return this->Voxel;
550 default:
551 return this->Empty;
552 }
553 }
554};
555
556} // anonymous namespace
557
558#endif // vtk3DLinearGridInternal_h
559// VTK-HeaderTest-Exclude: vtk3DLinearGridInternal.h
object to represent cell connectivity
static int * GetTriangleCases(int caseId)
Return the case table for table-based isocontouring (aka marching cubes style implementations).
static const vtkIdType * GetEdgeArray(vtkIdType edgeId)
Return the ids of the vertices defining edge/face (edgeId/‘faceId’).
static const vtkIdType * GetEdgeArray(vtkIdType edgeId)
Return the ids of the vertices defining edge/face (edgeId/‘faceId’).
static int * GetTriangleCases(int caseId)
Return the case table for table-based isocontouring (aka marching cubes style implementations).
Hold a reference to a vtkObjectBase instance.
static int * GetTriangleCases(int caseId)
Return the case table for table-based isocontouring (aka marching cubes style implementations).
static const vtkIdType * GetEdgeArray(vtkIdType edgeId)
Return the ids of the vertices defining edge/face (edgeId/‘faceId’).
static int * GetTriangleCases(int caseId)
Return the case table for table-based isocontouring (aka marching cubes style implementations).
static const vtkIdType * GetEdgeArray(vtkIdType edgeId)
Return the ids of the vertices defining edge/face (edgeId/‘faceId’).
VTKIOCATALYSTCONDUIT_EXPORT int GetCellType(const std::string &shape)
Get vtk cell type from conduit shape name throw a runtime_error on unsupported type.
Specialization of tuple ranges and iterators for vtkAOSDataArrayTemplate.
vtkSmartPointer< T > TakeSmartPointer(T *obj)
Construct a vtkSmartPointer<T> containing obj.
#define MAX_CELL_VERTS
std::pair< boost::graph_traits< vtkGraph * >::edge_iterator, boost::graph_traits< vtkGraph * >::edge_iterator > edges(vtkGraph *g)
@ VTK_VOXEL
Definition vtkCellType.h:48
@ VTK_PYRAMID
Definition vtkCellType.h:51
@ VTK_EMPTY_CELL
Definition vtkCellType.h:37
@ VTK_TETRA
Definition vtkCellType.h:47
@ VTK_WEDGE
Definition vtkCellType.h:50
@ VTK_HEXAHEDRON
Definition vtkCellType.h:49
int vtkIdType
Definition vtkType.h:315