VTK  9.3.20240419
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"
30 #include "vtkCellArrayIterator.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 
39 namespace
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
51 struct 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)
81 unsigned char BaseCell::Mask[MAX_CELL_VERTS] = { 1, 2, 4, 8, 16, 32, 64, 128 };
82 // Build repackaged case table and place into cases array.
83 inline 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.
109 struct 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)
127 unsigned 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).
130 inline 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------------------------------------------------------
151 struct 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)
168 unsigned 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).
171 inline 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  {
182  cases[i] = vtkHexahedron::GetTriangleCases(i);
183  }
184 
185  BaseCell::BuildCases(numCases, edges, cases, HexahedronCell::HexahedronCases);
186 
187  delete[] edges;
188  delete[] cases;
189 }
190 
191 // Contour wedge cell ------------------------------------------------------
192 struct 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)
209 unsigned 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).
212 inline 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------------------------------------------------------
233 struct 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)
250 unsigned 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).
253 inline 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------------------------------------------------------
274 struct 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)
291 unsigned 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.
296 inline 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.---------------------------------
334 struct 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)
350 unsigned 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.
355 struct 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
bool IsDoneWithTraversal()
Returns true if the iterator has completed the traversal.
void GoToCell(vtkIdType cellId)
Initialize the iterator to a specific cell.
void GoToNextCell()
Advance the forward iterator to the next cell.
vtkIdType GetCurrentCellId() const
Returns the id of the current cell during forward iteration.
void GetCurrentCell(vtkIdType &cellSize, vtkIdType const *&cellPoints)
Returns the definition of the current cell during forward traversal.
object to represent cell connectivity
Definition: vtkCellArray.h:286
vtkCellArrayIterator * NewIterator()
NewIterator returns a new instance of vtkCellArrayIterator that is initialized to point at the first ...
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).
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).
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:67
@ VTK_PYRAMID
Definition: vtkCellType.h:70
@ VTK_EMPTY_CELL
Definition: vtkCellType.h:56
@ VTK_TETRA
Definition: vtkCellType.h:66
@ VTK_WEDGE
Definition: vtkCellType.h:69
@ VTK_HEXAHEDRON
Definition: vtkCellType.h:68
int vtkIdType
Definition: vtkType.h:315