1 /*=========================================================================
2 
3 Program:   Visualization Toolkit
4 Module:    vtkHyperTreeGridToDualGrid.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 "vtkHyperTreeGridToDualGrid.h"
16 
17 #include "vtkBitArray.h"
18 #include "vtkCellData.h"
19 #include "vtkCellType.h"
20 #include "vtkHyperTree.h"
21 #include "vtkHyperTreeGrid.h"
22 #include "vtkHyperTreeGridNonOrientedMooreSuperCursor.h"
23 #include "vtkHyperTreeGridOrientedGeometryCursor.h"
24 #include "vtkIdTypeArray.h"
25 #include "vtkInformation.h"
26 #include "vtkInformationVector.h"
27 #include "vtkObjectFactory.h"
28 #include "vtkPointData.h"
29 #include "vtkPoints.h"
30 #include "vtkUnstructuredGrid.h"
31 
32 vtkStandardNewMacro(vtkHyperTreeGridToDualGrid);
33 
34 static const unsigned int CornerNeighborCursorsTable3D0[8] = { 0, 1, 3, 4, 9, 10, 12, 13 };
35 static const unsigned int CornerNeighborCursorsTable3D1[8] = { 1, 2, 4, 5, 10, 11, 13, 14 };
36 static const unsigned int CornerNeighborCursorsTable3D2[8] = { 3, 4, 6, 7, 12, 13, 15, 16 };
37 static const unsigned int CornerNeighborCursorsTable3D3[8] = { 4, 5, 7, 8, 13, 14, 16, 17 };
38 static const unsigned int CornerNeighborCursorsTable3D4[8] = { 9, 10, 12, 13, 18, 19, 21, 22 };
39 static const unsigned int CornerNeighborCursorsTable3D5[8] = { 10, 11, 13, 14, 19, 20, 22, 23 };
40 static const unsigned int CornerNeighborCursorsTable3D6[8] = { 12, 13, 15, 16, 21, 22, 24, 25 };
41 static const unsigned int CornerNeighborCursorsTable3D7[8] = { 13, 14, 16, 17, 22, 23, 25, 26 };
42 static const unsigned int* CornerNeighborCursorsTable3D[8] = {
43   CornerNeighborCursorsTable3D0,
44   CornerNeighborCursorsTable3D1,
45   CornerNeighborCursorsTable3D2,
46   CornerNeighborCursorsTable3D3,
47   CornerNeighborCursorsTable3D4,
48   CornerNeighborCursorsTable3D5,
49   CornerNeighborCursorsTable3D6,
50   CornerNeighborCursorsTable3D7,
51 };
52 
53 //------------------------------------------------------------------------------
vtkHyperTreeGridToDualGrid()54 vtkHyperTreeGridToDualGrid::vtkHyperTreeGridToDualGrid()
55 {
56   // Dual grid corners (primal grid leaf centers)
57   this->Points = nullptr;
58   this->Connectivity = nullptr;
59 }
60 
61 //------------------------------------------------------------------------------
~vtkHyperTreeGridToDualGrid()62 vtkHyperTreeGridToDualGrid::~vtkHyperTreeGridToDualGrid()
63 {
64   if (this->Points)
65   {
66     this->Points->Delete();
67   }
68 
69   if (this->Connectivity)
70   {
71     this->Connectivity->Delete();
72   }
73 }
74 
75 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)76 void vtkHyperTreeGridToDualGrid::PrintSelf(ostream& os, vtkIndent indent)
77 {
78   this->Superclass::PrintSelf(os, indent);
79   os << indent << "Points: " << this->Points << endl;
80   os << indent << "Connectivity: " << this->Connectivity << endl;
81 }
82 
83 //------------------------------------------------------------------------------
FillOutputPortInformation(int,vtkInformation * info)84 int vtkHyperTreeGridToDualGrid::FillOutputPortInformation(int, vtkInformation* info)
85 {
86   info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkUnstructuredGrid");
87   return 1;
88 }
89 
90 //------------------------------------------------------------------------------
ProcessTrees(vtkHyperTreeGrid * input,vtkDataObject * outputDO)91 int vtkHyperTreeGridToDualGrid::ProcessTrees(vtkHyperTreeGrid* input, vtkDataObject* outputDO)
92 {
93   // Downcast output data object to hyper tree grid
94   vtkUnstructuredGrid* output = vtkUnstructuredGrid::SafeDownCast(outputDO);
95   if (!output)
96   {
97     vtkErrorMacro("Incorrect type of output: " << outputDO->GetClassName());
98     return 0;
99   }
100 
101   // Check if we can break out early
102   // TODO: this isn't right, we need to trust the pipeline tell us when
103   // we need to reexecute, or upstream grid will change and output of this filter will be stale.
104   if (this->Points)
105   {
106     return 1;
107   }
108 
109   // TODO: we shouldn't be using persistent internal Points and Connectivity, just populate the
110   // output.
111   // Create arrays needed by dual mesh
112   this->Points = vtkPoints::New();
113   this->Connectivity = vtkIdTypeArray::New();
114 
115   // Primal cell centers are dual points
116   // JB On ne peut pas se reduire a dimensionner le tableau de points au nombre
117   // de Vertices, en effet, surtout si l'on veut conserver le mapping 1:1
118   // entre les noeuds de l'HTG et ces points du maillage dual.
119   // En effet, si l'on definit un GlobalIndex ou un IndexStart specifique
120   // cette ecriture simpliste ne fonctionnait plus... tableau trop petit
121   // car GetGlobalIndex retourne une valeur > this->GetNumberOfVertices().
122   this->Points->SetNumberOfPoints(input->GetGlobalNodeIndexMax() + 1);
123 
124   // TODO: find out why we get some uninitialized point coords instead
125   this->Points->GetData()->Fill(0.0);
126 
127   int numVerts = 1 << input->GetDimension();
128   this->Connectivity->SetNumberOfComponents(numVerts);
129 
130   // Initialize grid depth
131   unsigned int gridDepth = 0;
132 
133   // Compute and assign scales of all tree roots
134   // double scale[3] = { 1., 1., 1. };
135 
136   // Check whether coordinate arrays match grid size
137   // If coordinates array are complete, compute all tree scales
138   // SEB: int* dims = input->GetDimensions();
139   // SEB: if ( dims[0] == input->GetXCoordinates()->GetNumberOfTuples()
140   // SEB:      && dims[1] == input->GetYCoordinates()->GetNumberOfTuples()
141   // SEB:      && dims[2] == input->GetZCoordinates()->GetNumberOfTuples() )
142   if (static_cast<int>(input->GetDimensions()[0]) ==
143       input->GetXCoordinates()->GetNumberOfTuples() &&
144     static_cast<int>(input->GetDimensions()[1]) == input->GetYCoordinates()->GetNumberOfTuples() &&
145     static_cast<int>(input->GetDimensions()[2]) == input->GetZCoordinates()->GetNumberOfTuples())
146   {
147     gridDepth = input->GetNumberOfLevels();
148   }
149 
150   // Compute and store reduction factors for speed
151   double factor = 1.;
152   for (unsigned int p = 0; p < gridDepth; ++p)
153   {
154     this->ReductionFactors[p] = .5 * factor;
155     factor /= input->GetBranchFactor();
156   } // p
157 
158   // Retrieve material mask
159   vtkBitArray* mask = input->HasMask() ? input->GetMask() : nullptr;
160 
161   // Iterate over all hyper trees
162   vtkIdType index;
163   vtkHyperTreeGrid::vtkHyperTreeGridIterator it;
164   input->InitializeTreeIterator(it);
165   vtkNew<vtkHyperTreeGridNonOrientedMooreSuperCursor> cursor;
166   while (it.GetNextTree(index))
167   {
168     // Initialize new Moore cursor at root of current tree
169     input->InitializeNonOrientedMooreSuperCursor(cursor, index);
170     // Convert hyper tree into unstructured mesh recursively
171     if (mask)
172     {
173       this->TraverseDualRecursively(cursor, mask, input);
174     }
175     else
176     {
177       this->TraverseDualRecursively(cursor, input);
178     }
179   } // it
180 
181   // Adjust dual points as needed to fit the primal boundary
182   for (unsigned int d = 0; d < input->GetDimension(); ++d)
183   {
184     // Iterate over all adjustments for current dimension
185     for (std::map<vtkIdType, double>::const_iterator _it = this->PointShifts[d].begin();
186          _it != this->PointShifts[d].end(); ++_it)
187     {
188       double pt[3];
189 
190       assert(_it->first < input->GetNumberOfVertices());
191       this->Points->GetPoint(_it->first, pt);
192 
193       pt[d] += _it->second;
194 
195       assert(_it->first < input->GetNumberOfVertices());
196       this->Points->SetPoint(_it->first, pt);
197     } // it
198     this->PointShifts[d].clear();
199   } // d
200   this->PointShifted.clear();
201 
202   // now populate my output from the mesh internals made above
203   output->SetPoints(this->Points);
204   output->GetPointData()->ShallowCopy(input->GetCellData());
205 
206   int numPts = 1 << input->GetDimension();
207   int type = VTK_VOXEL;
208   if (numPts == 4)
209   {
210     type = VTK_PIXEL;
211   }
212   if (numPts == 2)
213   {
214     type = VTK_LINE;
215   }
216   output->Allocate();
217   int numCells = this->Connectivity->GetNumberOfTuples();
218   for (int cellIdx = 0; cellIdx < numCells; cellIdx++)
219   {
220     vtkIdType* ptr = this->Connectivity->GetPointer(0) + cellIdx * numPts;
221     output->InsertNextCell(type, numPts, ptr);
222   }
223 
224   return 1;
225 }
226 
227 //------------------------------------------------------------------------------
TraverseDualRecursively(vtkHyperTreeGridNonOrientedMooreSuperCursor * cursor,vtkHyperTreeGrid * input)228 void vtkHyperTreeGridToDualGrid::TraverseDualRecursively(
229   vtkHyperTreeGridNonOrientedMooreSuperCursor* cursor, vtkHyperTreeGrid* input)
230 {
231   // TODO - code duplication is evil, fold this with other TraverseDual
232   // Create cell corner if cursor is at leaf
233   if (cursor->IsLeaf())
234   {
235     // Center is a leaf, create dual items depending on dimension
236     switch (input->GetDimension())
237     {
238       case 1:
239         this->GenerateDualCornerFromLeaf1D(cursor, input);
240         break;
241       case 2:
242         this->GenerateDualCornerFromLeaf2D(cursor, input);
243         break;
244       case 3:
245         this->GenerateDualCornerFromLeaf3D(cursor, input);
246         break;
247     } // switch ( this->Dimension )
248   }   // if ( cursor->IsLeaf() )
249   else
250   {
251     // Cursor is not at leaf, recurse to all children
252     int numChildren = input->GetNumberOfChildren();
253     for (int child = 0; child < numChildren; ++child)
254     {
255       cursor->ToChild(child);
256       // Recurse
257       this->TraverseDualRecursively(cursor, input);
258       cursor->ToParent();
259     } // child
260   }   // else
261 }
262 
263 //------------------------------------------------------------------------------
GenerateDualCornerFromLeaf1D(vtkHyperTreeGridNonOrientedMooreSuperCursor * cursor,vtkHyperTreeGrid * input)264 void vtkHyperTreeGridToDualGrid::GenerateDualCornerFromLeaf1D(
265   vtkHyperTreeGridNonOrientedMooreSuperCursor* cursor, vtkHyperTreeGrid* input)
266 {
267   // With d=1:
268   //   (d-0)-faces are corners, neighbor cursors are 0 and 2
269   //   (d-1)-faces do not exist
270   //   (d-2)-faces do not exist
271 
272   // Retrieve neighbor (left/right) cursors
273   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorL =
274     cursor->GetOrientedGeometryCursor(0);
275   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorR =
276     cursor->GetOrientedGeometryCursor(2);
277 
278   // Retrieve cursor center coordinates
279   double pt[3];
280   cursor->GetPoint(pt);
281 
282   // Check across d-face neighbors whether point must be adjusted
283   if (!cursorL->HasTree())
284   {
285     // Move to left corner
286     pt[input->GetOrientation()] -= .5 * cursor->GetSize()[input->GetOrientation()];
287     ;
288   }
289   if (!cursorR->HasTree())
290   {
291     // Move to right corner
292     pt[input->GetOrientation()] += .5 * cursor->GetSize()[input->GetOrientation()];
293     ;
294   }
295 
296   // Retrieve global index of center cursor
297   vtkIdType id = cursor->GetGlobalNodeIndex();
298 
299   // Insert dual point at center of leaf cell
300   this->Points->SetPoint(id, pt);
301 
302   // Storage for edge vertex IDs: dual cell ownership to cursor with higher index
303   vtkIdType ids[2];
304   ids[0] = id;
305 
306   // Check whether a dual edge to left neighbor exists
307   if (cursorL->HasTree() && cursorL->IsLeaf())
308   {
309     // If left neighbor is a leaf, always create an edge
310     ids[1] = cursorL->GetGlobalNodeIndex();
311     this->Connectivity->InsertNextTypedTuple(ids);
312   }
313 
314   // Check whether a dual edge to right neighbor exists
315   if ((cursorR->HasTree() && cursorR->IsLeaf()) && cursorR->GetLevel() != cursor->GetLevel())
316   {
317     // If right neighbor is a leaf, create an edge only if right cell at higher level
318     ids[1] = cursorR->GetGlobalNodeIndex();
319     this->Connectivity->InsertNextTypedTuple(ids);
320   }
321 }
322 
323 //------------------------------------------------------------------------------
GenerateDualCornerFromLeaf2D(vtkHyperTreeGridNonOrientedMooreSuperCursor * cursor,vtkHyperTreeGrid * input)324 void vtkHyperTreeGridToDualGrid::GenerateDualCornerFromLeaf2D(
325   vtkHyperTreeGridNonOrientedMooreSuperCursor* cursor, vtkHyperTreeGrid* input)
326 {
327   // With d=2:
328   //   (d-0)-faces are edges, neighbor cursors are 1, 3, 5, 7
329   //   (d-1)-faces are corners, neighbor cursors are 0, 2, 6, 8
330   //   (d-2)-faces do not exist
331 
332   // Retrieve (d-0)-neighbor (south/east/west/north) cursors
333   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorS =
334     cursor->GetOrientedGeometryCursor(1);
335   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorW =
336     cursor->GetOrientedGeometryCursor(3);
337   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorE =
338     cursor->GetOrientedGeometryCursor(5);
339   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorN =
340     cursor->GetOrientedGeometryCursor(7);
341 
342   // Retrieve (d-1)-neighbor (southwest/southeast/northwest/northeast) cursors
343   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorSW =
344     cursor->GetOrientedGeometryCursor(0);
345   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorSE =
346     cursor->GetOrientedGeometryCursor(2);
347   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorNW =
348     cursor->GetOrientedGeometryCursor(6);
349   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorNE =
350     cursor->GetOrientedGeometryCursor(8);
351 
352   // Retrieve 2D axes (east-west/south-north)
353   unsigned int axisWE = input->GetOrientation() ? 0 : 1;
354   unsigned int axisSN = input->GetOrientation() == 2 ? 1 : 2;
355 
356   // Retrieve cursor center coordinates
357   double pt[3];
358   cursor->GetPoint(pt);
359 
360   // Compute potential shifts
361   double shift[2];
362   shift[0] = .5 * cursor->GetSize()[axisWE];
363   shift[1] = .5 * cursor->GetSize()[axisSN];
364 
365   // Check across edge neighbors whether point must be adjusted
366   if (!cursorS->HasTree())
367   {
368     // Move to south edge
369     pt[axisSN] -= shift[1];
370   }
371   if (!cursorW->HasTree())
372   {
373     // Move to west edge
374     pt[axisWE] -= shift[0];
375   }
376   if (!cursorE->HasTree())
377   {
378     // Move to east edge
379     pt[axisWE] += shift[0];
380   }
381   if (!cursorN->HasTree())
382   {
383     // Move to north edge
384     pt[axisSN] += shift[1];
385   }
386 
387   // Retrieve global index of center cursor
388   vtkIdType id = cursor->GetGlobalNodeIndex();
389 
390   // Insert dual point at center of leaf cell
391   this->Points->SetPoint(id, pt);
392 
393   // Storage for edge vertex IDs: dual cell ownership to cursor with higher index
394   vtkIdType ids[4];
395   ids[0] = id;
396 
397   // Retrieve current level to break corner ownership ties
398   unsigned int level = cursor->GetLevel();
399 
400   // Check whether a dual cell around SW corner exists
401   if (cursorSW->HasTree() && cursorSW->IsLeaf() && cursorS->HasTree() && cursorS->IsLeaf() &&
402     cursorW->HasTree() && cursorW->IsLeaf())
403   {
404     // If SW, S, and W neighbors are leaves, always create a face
405     ids[1] = cursorW->GetGlobalNodeIndex();
406     ids[2] = cursorS->GetGlobalNodeIndex();
407     ids[3] = cursorSW->GetGlobalNodeIndex();
408     this->Connectivity->InsertNextTypedTuple(ids);
409   }
410 
411   // Check whether a dual cell around SE corner exists
412   if (cursorS->HasTree() && cursorS->IsLeaf() && cursorSE->HasTree() && cursorSE->IsLeaf() &&
413     cursorE->HasTree() && cursorE->IsLeaf() && level != cursorE->GetLevel())
414   {
415     // If S, SE, and E neighbors are leaves, create a face if E at higher level
416     ids[1] = cursorE->GetGlobalNodeIndex();
417     ids[2] = cursorS->GetGlobalNodeIndex();
418     ids[3] = cursorSE->GetGlobalNodeIndex();
419     this->Connectivity->InsertNextTypedTuple(ids);
420   }
421 
422   // Check whether a dual cell around NE corner exists
423   if (cursorE->HasTree() && cursorE->IsLeaf() && cursorNE->HasTree() && cursorNE->IsLeaf() &&
424     cursorN->HasTree() && cursorN->IsLeaf() && level != cursorE->GetLevel() &&
425     level != cursorNE->GetLevel() && level != cursorN->GetLevel())
426   {
427     // If E, NE, and N neighbors are leaves, create a face if E, NE, N at higher level
428     ids[1] = cursorE->GetGlobalNodeIndex();
429     ids[2] = cursorN->GetGlobalNodeIndex();
430     ids[3] = cursorNE->GetGlobalNodeIndex();
431     this->Connectivity->InsertNextTypedTuple(ids);
432   }
433 
434   // Check whether a dual cell around NW corner exists
435   if (cursorW->HasTree() && cursorW->IsLeaf() && cursorN->HasTree() && cursorN->IsLeaf() &&
436     cursorNW->HasTree() && cursorNW->IsLeaf() && level != cursorNW->GetLevel() &&
437     level != cursorN->GetLevel())
438   {
439     // If W, N, and NW neighbors are leaves, create a face if NW and N at higher level
440     ids[1] = cursorW->GetGlobalNodeIndex();
441     ids[2] = cursorN->GetGlobalNodeIndex();
442     ids[3] = cursorNW->GetGlobalNodeIndex();
443     this->Connectivity->InsertNextTypedTuple(ids);
444   }
445 }
446 
447 //------------------------------------------------------------------------------
GenerateDualCornerFromLeaf3D(vtkHyperTreeGridNonOrientedMooreSuperCursor * cursor,vtkHyperTreeGrid * vtkNotUsed (input))448 void vtkHyperTreeGridToDualGrid::GenerateDualCornerFromLeaf3D(
449   vtkHyperTreeGridNonOrientedMooreSuperCursor* cursor, vtkHyperTreeGrid* vtkNotUsed(input))
450 {
451   // With d=3:
452   //   (d-0)-faces are faces, neighbor cursors are 4, 10, 12, 14, 16, 22
453   //   (d-1)-faces are edges, neighbor cursors are 1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25
454   //   (d-2)-faces are corners, neighbor cursors are 0, 2, 6, 8, 18, 20, 24, 26
455 
456   // Retrieve cursors
457   std::vector<vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor>> cursors;
458   cursors.resize(27);
459   for (unsigned int c = 0; c < 27; ++c)
460   {
461     cursors[c] = cursor->GetOrientedGeometryCursor(c);
462   }
463 
464   // Retrieve cursor center coordinates
465   double pt[3];
466   cursor->GetPoint(pt);
467 
468   // Compute potential shifts
469   double shift[3];
470   shift[0] = .5 * cursor->GetSize()[0];
471   shift[1] = .5 * cursor->GetSize()[1];
472   shift[2] = .5 * cursor->GetSize()[2];
473 
474   // Index offset relative to center cursor (13)
475   unsigned int offset = 1;
476 
477   // Check across face neighbors whether point must be adjusted
478   for (unsigned int axis = 0; axis < 3; ++axis, offset *= 3)
479   {
480     if (!cursors[13 - offset]->HasTree())
481     {
482       // Move to negative side along axis
483       pt[axis] -= shift[axis];
484     }
485     if (!cursors[13 + offset]->HasTree())
486     {
487       // Move to positive side along axis
488       pt[axis] += shift[axis];
489     }
490   } // axis
491 
492   // Retrieve global index of center cursor
493   vtkIdType id = cursor->GetGlobalNodeIndex();
494 
495   // Insert dual point at center of leaf cell
496   this->Points->SetPoint(id, pt);
497 
498   // Storage for edge vertex IDs: dual cell ownership to cursor with higher index
499   vtkIdType ids[8];
500 
501   // Retrieve current level to break corner ownership ties
502   unsigned int level = cursor->GetLevel();
503 
504   // Iterate over leaf corners
505   for (unsigned int c = 0; c < 8; ++c)
506   {
507     // Assume center cursor leaf owns the corner
508     bool owner = true;
509 
510     // Iterate over every leaf touching the corner
511     for (unsigned int l = 0; l < 8 && owner; ++l)
512     {
513       // Retrieve cursor index of touching leaf
514       unsigned int index = CornerNeighborCursorsTable3D[c][l];
515 
516       // Compute whether corner is owned by another leaf
517       if (index != 13)
518       {
519         if (!cursors[index]->HasTree() || !cursors[index]->IsLeaf() ||
520           (cursors[index]->GetLevel() == level && index > 13))
521         {
522           // If neighbor leaf is out of bounds or has not been
523           // refined to a leaf, this leaf does not own the corner
524           // A level tie is broken in favor of the largest index
525           owner = false;
526         }
527         else
528         {
529           // Collect the leaf indices for the dual cell
530           ids[l] = cursors[index]->GetGlobalNodeIndex();
531         }
532       }
533       else
534       { // if ( index != 13 )
535         // Collect the leaf indices for the dual cell
536         ids[l] = cursors[index]->GetGlobalNodeIndex();
537       } // else
538     }   // l
539 
540     // If leaf owns the corner, create dual cell
541     if (owner)
542     {
543       this->Connectivity->InsertNextTypedTuple(ids);
544     } // if ( owner )
545   }   // c
546 }
547 
548 //------------------------------------------------------------------------------
TraverseDualRecursively(vtkHyperTreeGridNonOrientedMooreSuperCursor * cursor,vtkBitArray * mask,vtkHyperTreeGrid * input)549 void vtkHyperTreeGridToDualGrid::TraverseDualRecursively(
550   vtkHyperTreeGridNonOrientedMooreSuperCursor* cursor, vtkBitArray* mask, vtkHyperTreeGrid* input)
551 {
552   // TODO - code duplication is evil, fold this with other TraverseDual
553   // Create cell corner if cursor is at leaf
554   if (cursor->IsLeaf())
555   {
556     // Cursor is at leaf, retrieve its global index
557     vtkIdType id = cursor->GetGlobalNodeIndex();
558 
559     // Center is a leaf, create dual items depending on dimension
560     if (mask->GetValue(id))
561     {
562       switch (input->GetDimension())
563       {
564         case 2:
565           this->ShiftDualCornerFromMaskedLeaf2D(cursor, mask, input);
566           break;
567         case 3:
568           this->ShiftDualCornerFromMaskedLeaf3D(cursor, mask, input);
569       } // switch ( this->Dimension )
570     }
571     else
572     {
573       switch (input->GetDimension())
574       {
575         case 1:
576           this->GenerateDualCornerFromLeaf1D(cursor, input);
577           break;
578         case 2:
579           this->GenerateDualCornerFromLeaf2D(cursor, mask, input);
580           break;
581         case 3:
582           this->GenerateDualCornerFromLeaf3D(cursor, mask, input);
583       } // switch ( this->Dimension )
584     }   // else
585   }     // if ( cursor->IsLeaf() )
586   else
587   {
588     // Cursor is not at leaf, recurse to all children
589     int numChildren = input->GetNumberOfChildren();
590     for (int child = 0; child < numChildren; ++child)
591     {
592       cursor->ToChild(child);
593       // Recurse
594       this->TraverseDualRecursively(cursor, mask, input);
595       cursor->ToParent();
596     } // child
597   }   // else
598 }
599 
600 //------------------------------------------------------------------------------
ShiftDualCornerFromMaskedLeaf2D(vtkHyperTreeGridNonOrientedMooreSuperCursor * cursor,vtkBitArray * mask,vtkHyperTreeGrid * input)601 void vtkHyperTreeGridToDualGrid::ShiftDualCornerFromMaskedLeaf2D(
602   vtkHyperTreeGridNonOrientedMooreSuperCursor* cursor, vtkBitArray* mask, vtkHyperTreeGrid* input)
603 {
604   // With d=2:
605   //   (d-0)-faces are edges, neighbor cursors are 1, 3, 5, 7
606   //   (d-1)-faces are corners, neighbor cursors are 0, 2, 6, 8
607   //   (d-2)-faces do not exist
608 
609   // Retrieve (d-0)-neighbor (south/east/west/north) cursors
610   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorS =
611     cursor->GetOrientedGeometryCursor(1);
612   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorW =
613     cursor->GetOrientedGeometryCursor(3);
614   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorE =
615     cursor->GetOrientedGeometryCursor(5);
616   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorN =
617     cursor->GetOrientedGeometryCursor(7);
618 
619   // Retrieve (d-1)-neighbor (southwest/southeast/northwest/northeast) cursors
620   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorSW =
621     cursor->GetOrientedGeometryCursor(0);
622   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorSE =
623     cursor->GetOrientedGeometryCursor(2);
624   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorNW =
625     cursor->GetOrientedGeometryCursor(6);
626   vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorNE =
627     cursor->GetOrientedGeometryCursor(8);
628 
629   // Retrieve global indices of non-center cursors
630 
631   // Retrieve 2D axes (east-west/south-north)
632   unsigned int axisWE = input->GetOrientation() ? 0 : 1;
633   unsigned int axisSN = input->GetOrientation() == 2 ? 1 : 2;
634 
635   // Retrieve current level to break corner ownership ties
636   unsigned int level = cursor->GetLevel();
637 
638   // Check whether dual point across S edge must be adjusted
639   if (cursorS->HasTree() && cursorS->IsLeaf() && cursorS->GetLevel() < level)
640   {
641     vtkIdType idS = cursorS->GetGlobalNodeIndex();
642     if (!mask->GetValue(idS))
643     {
644       // Dual point must be adjusted
645       this->PointShifted[idS] = true;
646       this->PointShifts[axisSN][idS] =
647         cursorS->GetTree()->GetScale(axisSN) * this->ReductionFactors[cursorS->GetLevel()];
648     }
649   }
650 
651   // Check whether dual point across W edge must be adjusted
652   if (cursorW->HasTree() && cursorW->IsLeaf() && cursorW->GetLevel() < level)
653   {
654     vtkIdType idW = cursorW->GetGlobalNodeIndex();
655     if (!mask->GetValue(idW))
656     {
657       // Dual point must be adjusted
658       this->PointShifted[idW] = true;
659       this->PointShifts[axisWE][idW] =
660         cursorW->GetTree()->GetScale(axisWE) * this->ReductionFactors[cursorW->GetLevel()];
661     }
662   }
663 
664   // Check whether dual point across E face must be adjusted
665   if (cursorE->HasTree() && cursorE->IsLeaf() && cursorE->GetLevel() < level)
666   {
667     vtkIdType idE = cursorE->GetGlobalNodeIndex();
668     if (!mask->GetValue(idE))
669     {
670       // Dual point must be adjusted
671       this->PointShifted[idE] = true;
672       this->PointShifts[axisWE][idE] =
673         -cursorE->GetTree()->GetScale(axisWE) * this->ReductionFactors[cursorE->GetLevel()];
674     }
675   }
676 
677   // Check whether dual point across N edge must be adjusted
678   if (cursorN->HasTree() && cursorN->IsLeaf() && cursorN->GetLevel() < level)
679   {
680     vtkIdType idN = cursorN->GetGlobalNodeIndex();
681     if (!mask->GetValue(idN))
682     {
683       // Dual point must be adjusted
684       this->PointShifted[idN] = true;
685       this->PointShifts[axisSN][idN] =
686         -cursorN->GetTree()->GetScale(axisSN) * this->ReductionFactors[cursorN->GetLevel()];
687     }
688   }
689 
690   // Check whether dual point across SE corner must be adjusted
691   if (cursorSE->HasTree() && cursorSE->IsLeaf() && cursorSE->GetLevel() < level)
692   {
693     vtkIdType idSE = cursorSE->GetGlobalNodeIndex();
694     if (!mask->GetValue(idSE) && !this->PointShifted[idSE])
695     {
696       // Dual point must be adjusted
697       double shift[3];
698       cursorSE->GetTree()->GetScale(shift);
699       double factor = this->ReductionFactors[cursorSE->GetLevel()];
700       this->PointShifts[axisWE][idSE] = factor * shift[axisWE];
701       this->PointShifts[axisSN][idSE] = factor * shift[axisSN];
702     }
703   }
704 
705   // Check whether dual point across SW corner must be adjusted
706   if (cursorSW->HasTree() && cursorSW->IsLeaf() && cursorSW->GetLevel() < level)
707   {
708     vtkIdType idSW = cursorSW->GetGlobalNodeIndex();
709     if (!mask->GetValue(idSW) && !this->PointShifted[idSW])
710     {
711       // Dual point must be adjusted
712       double shift[3];
713       cursorSW->GetTree()->GetScale(shift);
714       double factor = this->ReductionFactors[cursorSW->GetLevel()];
715       this->PointShifts[axisWE][idSW] = -factor * shift[axisWE];
716       this->PointShifts[axisSN][idSW] = factor * shift[axisSN];
717     }
718   }
719 
720   // Check whether dual point across NW corner must be adjusted
721   if (cursorNW->HasTree() && cursorNW->IsLeaf() && cursorNW->GetLevel() < level)
722   {
723     vtkIdType idNW = cursorNW->GetGlobalNodeIndex();
724     if (!mask->GetValue(idNW) && !this->PointShifted[idNW])
725     {
726       // Dual point must be adjusted
727       double shift[3];
728       cursorNW->GetTree()->GetScale(shift);
729       double factor = this->ReductionFactors[cursorNW->GetLevel()];
730       this->PointShifts[axisWE][idNW] = factor * shift[axisWE];
731       this->PointShifts[axisSN][idNW] = -factor * shift[axisSN];
732     }
733   }
734 
735   // Check whether dual point across NE corner must be adjusted
736   if (cursorNE->HasTree() && cursorNE->IsLeaf() && cursorNE->GetLevel() < level)
737   {
738     vtkIdType idNE = cursorNE->GetGlobalNodeIndex();
739     if (!mask->GetValue(idNE) && !this->PointShifted[idNE])
740     {
741       // Dual point must be adjusted
742       double shift[3];
743       cursorNE->GetTree()->GetScale(shift);
744       double factor = this->ReductionFactors[cursorNE->GetLevel()];
745       this->PointShifts[axisWE][idNE] = -factor * shift[axisWE];
746       this->PointShifts[axisSN][idNE] = -factor * shift[axisSN];
747     }
748   }
749 }
750 
751 //------------------------------------------------------------------------------
ShiftDualCornerFromMaskedLeaf3D(vtkHyperTreeGridNonOrientedMooreSuperCursor * cursor,vtkBitArray * mask,vtkHyperTreeGrid * vtkNotUsed (input))752 void vtkHyperTreeGridToDualGrid::ShiftDualCornerFromMaskedLeaf3D(
753   vtkHyperTreeGridNonOrientedMooreSuperCursor* cursor, vtkBitArray* mask,
754   vtkHyperTreeGrid* vtkNotUsed(input))
755 {
756   // With d=3:
757   //   (d-0)-faces are faces, neighbor cursors are 4, 10, 12, 14, 16, 22
758   //   (d-1)-faces are edges, neighbor cursors are 1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25
759   //   (d-2)-faces are corners, neighbor cursors are 0, 2, 6, 8, 18, 20, 24, 26
760 
761   // Retrieve current level to break corner ownership ties
762   unsigned int level = cursor->GetLevel();
763 
764   // Check whether dual points across face neighbors must be adjusted
765   int offset = 1;
766   for (unsigned int axis = 0; axis < 3; ++axis, offset *= 3)
767   {
768     vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorM =
769       cursor->GetOrientedGeometryCursor(13 - offset);
770     if (cursorM->HasTree() && cursorM->IsLeaf() && cursorM->GetLevel() < level)
771     {
772       vtkIdType idM = cursorM->GetGlobalNodeIndex();
773       if (!mask->GetValue(idM))
774       {
775         // Dual point must be adjusted
776         this->PointShifted[idM] = true;
777         this->PointShifts[axis][idM] =
778           cursorM->GetTree()->GetScale(axis) * this->ReductionFactors[cursorM->GetLevel()];
779       }
780     }
781     vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorP =
782       cursor->GetOrientedGeometryCursor(13 + offset);
783     if (cursorP->HasTree() && cursorP->IsLeaf() && cursorP->GetLevel() < level)
784     {
785       vtkIdType idP = cursorP->GetGlobalNodeIndex();
786       if (!mask->GetValue(idP))
787       {
788         // Dual point must be adjusted
789         this->PointShifted[idP] = true;
790         this->PointShifts[axis][idP] =
791           -cursorP->GetTree()->GetScale(axis) * this->ReductionFactors[cursorP->GetLevel()];
792       }
793     }
794   } // axis
795 
796   // Check whether dual points across edge neighbors must be adjusted
797   int i = 1;
798   for (int axis1 = 0; axis1 < 2; ++axis1, i *= 3)
799   {
800     int j = 3 * i;
801     for (int axis2 = axis1 + 1; axis2 < 3; ++axis2, j *= 3)
802     {
803       for (int o2 = -1; o2 < 2; o2 += 2)
804       {
805         for (int o1 = -1; o1 < 2; o1 += 2)
806         {
807           int index = 13 + o1 * (i * o2 + j);
808           vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorE =
809             cursor->GetOrientedGeometryCursor(static_cast<unsigned int>(index));
810           if (cursorE->HasTree() && cursorE->IsLeaf() && cursorE->GetLevel() < level)
811           {
812             vtkIdType idE = cursorE->GetGlobalNodeIndex();
813             if (!mask->GetValue(idE) && !this->PointShifted[idE])
814             {
815               // Dual point must be adjusted
816               this->PointShifted[idE] = true;
817               double shift[3];
818               cursorE->GetTree()->GetScale(shift);
819               double factor = this->ReductionFactors[cursorE->GetLevel()];
820               this->PointShifts[axis1][idE] = -o1 * o2 * factor * shift[axis1];
821               this->PointShifts[axis2][idE] = -o1 * factor * shift[axis2];
822             }
823           }
824         } // o1
825       }   // o2
826     }     // axis2
827   }       // axis1
828 
829   // Check whether dual points across corner neighbors must be adjusted
830   for (int o3 = -1; o3 < 2; o3 += 2)
831   {
832     for (int o2 = -1; o2 < 2; o2 += 2)
833     {
834       offset = o2 * (o3 + 3) + 9;
835       for (int o1 = -1; o1 < 2; o1 += 2)
836       {
837         int index = 13 + o1 * offset;
838         vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorC =
839           cursor->GetOrientedGeometryCursor(index);
840         if (cursorC->HasTree() && cursorC->IsLeaf() && cursorC->GetLevel() < level)
841         {
842           vtkIdType idC = cursorC->GetGlobalNodeIndex();
843           if (!mask->GetValue(idC) && !this->PointShifted[idC])
844           {
845             // Dual point must be adjusted
846             this->PointShifted[idC] = true;
847             double shift[3];
848             cursorC->GetTree()->GetScale(shift);
849             double factor = this->ReductionFactors[cursorC->GetLevel()];
850             this->PointShifts[0][idC] = -o1 * o2 * o3 * factor * shift[0];
851             this->PointShifts[1][idC] = -o1 * o2 * factor * shift[1];
852             this->PointShifts[2][idC] = -o1 * factor * shift[2];
853           }
854         }
855       } // o1
856     }   // o2
857   }     // o3
858 }
859 
860 //------------------------------------------------------------------------------
GenerateDualCornerFromLeaf2D(vtkHyperTreeGridNonOrientedMooreSuperCursor * cursor,vtkBitArray * mask,vtkHyperTreeGrid * input)861 void vtkHyperTreeGridToDualGrid::GenerateDualCornerFromLeaf2D(
862   vtkHyperTreeGridNonOrientedMooreSuperCursor* cursor, vtkBitArray* mask, vtkHyperTreeGrid* input)
863 {
864   // With d=2:
865   //   (d-0)-faces are edges, neighbor cursors are 1, 3, 5, 7
866   //   (d-1)-faces are corners, neighbor cursors are 0, 2, 6, 8
867   //   (d-2)-faces do not exist
868 
869   const unsigned int dS = 1;
870   const unsigned int dW = 3;
871   const unsigned int dE = 5;
872   const unsigned int dN = 7;
873   const unsigned int dSW = 0;
874   const unsigned int dSE = 2;
875   const unsigned int dNW = 6;
876   const unsigned int dNE = 8;
877 
878   // Retrieve 2D axes (east-west/south-north)
879   unsigned int axisWE = input->GetOrientation() ? 0 : 1;
880   unsigned int axisSN = input->GetOrientation() == 2 ? 1 : 2;
881 
882   // Retrieve cursor center coordinates
883   double pt[3];
884   cursor->GetPoint(pt);
885 
886   // Compute potential shifts
887   double shift[2];
888   shift[0] = .5 * cursor->GetSize()[axisWE];
889   shift[1] = .5 * cursor->GetSize()[axisSN];
890 
891   // When a mask is present, edge as well as face shifts are possible
892   bool shifted = false;
893 
894   // Check across edge neighbors whether point must be adjusted
895   if (!cursor->HasTree(dS))
896   {
897     // Move to south edge
898     pt[axisSN] -= shift[1];
899     shifted = true;
900   }
901   else if (cursor->IsLeaf(dS) && mask->GetValue(cursor->GetGlobalNodeIndex(dS)))
902   {
903     // Move to south edge
904     pt[axisSN] -= shift[1];
905     shifted = true;
906   }
907 
908   if (!cursor->HasTree(dW))
909   {
910     // Move to west edge
911     pt[axisWE] -= shift[0];
912     shifted = true;
913   }
914   else if (cursor->IsLeaf(dW) && mask->GetValue(cursor->GetGlobalNodeIndex(dW)))
915   {
916     // Move to west edge
917     pt[axisWE] -= shift[0];
918     shifted = true;
919   }
920 
921   if (!cursor->HasTree(dE))
922   {
923     // Move to east edge
924     pt[axisWE] += shift[0];
925     shifted = true;
926   }
927   else if (cursor->IsLeaf(dE) && mask->GetValue(cursor->GetGlobalNodeIndex(dE)))
928   {
929     // Move to east edge
930     pt[axisWE] += shift[0];
931     shifted = true;
932   }
933 
934   if (!cursor->HasTree(dN))
935   {
936     // Move to north edge
937     pt[axisSN] += shift[1];
938     shifted = true;
939   }
940   else if (cursor->IsLeaf(dN) && mask->GetValue(cursor->GetGlobalNodeIndex(dN)))
941   {
942     // Move to north edge
943     pt[axisSN] += shift[1];
944     shifted = true;
945   }
946 
947   // Only when point was not moved to edge, check corner neighbors
948   if (!shifted)
949   {
950     if (!cursor->HasTree(dSW))
951     {
952       // Move to southwest corner
953       pt[axisWE] -= shift[0];
954       pt[axisSN] -= shift[1];
955     }
956     else if (cursor->IsLeaf(dSW) && mask->GetValue(cursor->GetGlobalNodeIndex(dSW)))
957     {
958       // Move to southwest corner
959       pt[axisWE] -= shift[0];
960       pt[axisSN] -= shift[1];
961     }
962 
963     if (!cursor->HasTree(dSE))
964     {
965       // Move to southeast corner
966       pt[axisWE] += shift[0];
967       pt[axisSN] -= shift[1];
968     }
969     else if (cursor->IsLeaf(dSE) && mask->GetValue(cursor->GetGlobalNodeIndex(dSE)))
970     {
971       // Move to southeast corner
972       pt[axisWE] += shift[0];
973       pt[axisSN] -= shift[1];
974     }
975 
976     if (!cursor->HasTree(dNW))
977     {
978       // Move to northwest corner
979       pt[axisWE] -= shift[0];
980       pt[axisSN] += shift[1];
981     }
982     else if (cursor->IsLeaf(dNW) && mask->GetValue(cursor->GetGlobalNodeIndex(dNW)))
983     {
984       // Move to northwest corner
985       pt[axisWE] -= shift[0];
986       pt[axisSN] += shift[1];
987     }
988 
989     if (!cursor->HasTree(dNE))
990     {
991       // Move to northeast corner
992       pt[axisWE] += shift[0];
993       pt[axisSN] += shift[1];
994     }
995     else if (cursor->IsLeaf(dNE) && mask->GetValue(cursor->GetGlobalNodeIndex(dNE)))
996     {
997       // Move to northeast corner
998       pt[axisWE] += shift[0];
999       pt[axisSN] += shift[1];
1000     }
1001   } // if ( ! shifted )
1002 
1003   // Retrieve global index of center cursor
1004   vtkIdType id = cursor->GetGlobalNodeIndex();
1005 
1006   // Insert dual point at center of leaf cell
1007   assert(id < input->GetNumberOfVertices());
1008   this->Points->SetPoint(id, pt);
1009 
1010   // If cell is masked, terminate recursion, no dual cell will be generated
1011   if (mask->GetValue(id))
1012   {
1013     return;
1014   }
1015 
1016   // Storage for edge vertex IDs: dual cell ownership to cursor with higher index
1017   vtkIdType ids[4];
1018   ids[0] = id;
1019 
1020   // Retrieve current level to break corner ownership ties
1021   unsigned int level = cursor->GetLevel();
1022 
1023   // Check whether a dual cell around SW corner exists
1024   if (cursor->HasTree(dSW) && cursor->HasTree(dS) && cursor->HasTree(dW) && cursor->IsLeaf(dSW) &&
1025     cursor->IsLeaf(dS) && cursor->IsLeaf(dW))
1026   {
1027     vtkIdType idSW, idS, idW;
1028     if (!mask->GetValue(idSW = cursor->GetGlobalNodeIndex(dSW)) &&
1029       !mask->GetValue(idS = cursor->GetGlobalNodeIndex(dS)) &&
1030       !mask->GetValue(idW = cursor->GetGlobalNodeIndex(dW)))
1031     {
1032       // If SW, S, and W neighbors are leaves, always create a face
1033       ids[1] = idW;
1034       ids[2] = idS;
1035       ids[3] = idSW;
1036       this->Connectivity->InsertNextTypedTuple(ids);
1037     }
1038   }
1039 
1040   // Check whether a dual cell around SE corner exists
1041   if (cursor->HasTree(dS) && cursor->HasTree(dSE) && cursor->HasTree(dE) && cursor->IsLeaf(dS) &&
1042     cursor->IsLeaf(dSE) && cursor->IsLeaf(dE))
1043   {
1044     vtkIdType idS, idSE, idE;
1045     if (!mask->GetValue(idS = cursor->GetGlobalNodeIndex(dS)) &&
1046       !mask->GetValue(idSE = cursor->GetGlobalNodeIndex(dSE)) &&
1047       !mask->GetValue(idE = cursor->GetGlobalNodeIndex(dE)) && level != cursor->GetLevel(dE))
1048     {
1049       // If S, SE, and E neighbors are leaves, create a face if E at higher level
1050       ids[1] = idE;
1051       ids[2] = idS;
1052       ids[3] = idSE;
1053       this->Connectivity->InsertNextTypedTuple(ids);
1054     }
1055   }
1056 
1057   // Check whether a dual cell around NE corner exists
1058   if (cursor->HasTree(dE) && cursor->HasTree(dNE) && cursor->HasTree(dN) && cursor->IsLeaf(dE) &&
1059     cursor->IsLeaf(dNE) && cursor->IsLeaf(dN))
1060   {
1061     vtkIdType idE, idNE, idN;
1062     if (!mask->GetValue(idE = cursor->GetGlobalNodeIndex(dE)) &&
1063       !mask->GetValue(idNE = cursor->GetGlobalNodeIndex(dNE)) &&
1064       !mask->GetValue(idN = cursor->GetGlobalNodeIndex(dN)) && level != cursor->GetLevel(dE) &&
1065       level != cursor->GetLevel(dNE) && level != cursor->GetLevel(dN))
1066     {
1067       // If E, NE, and N neighbors are leaves, create a face if E, NE, N at higher level
1068       ids[1] = idE;
1069       ids[2] = idN;
1070       ids[3] = idNE;
1071       this->Connectivity->InsertNextTypedTuple(ids);
1072     }
1073   }
1074 
1075   // Check whether a dual cell around NW corner exists
1076   if (cursor->HasTree(dW) && cursor->HasTree(dN) && cursor->HasTree(dNW) && cursor->IsLeaf(dW) &&
1077     cursor->IsLeaf(dN) && cursor->IsLeaf(dNW))
1078   {
1079     vtkIdType idW, idN, idNW;
1080     if (!mask->GetValue(idW = cursor->GetGlobalNodeIndex(dW)) &&
1081       !mask->GetValue(idN = cursor->GetGlobalNodeIndex(dN)) &&
1082       !mask->GetValue(idNW = cursor->GetGlobalNodeIndex(dNW)) && level != cursor->GetLevel(dNW) &&
1083       level != cursor->GetLevel(dN))
1084     {
1085       // If W, N, and NW neighbors are leaves, create a face if NW and N at higher level
1086       ids[1] = idW;
1087       ids[2] = idN;
1088       ids[3] = idNW;
1089       this->Connectivity->InsertNextTypedTuple(ids);
1090     }
1091   }
1092 }
1093 
1094 //------------------------------------------------------------------------------
GenerateDualCornerFromLeaf3D(vtkHyperTreeGridNonOrientedMooreSuperCursor * cursor,vtkBitArray * mask,vtkHyperTreeGrid * vtkNotUsed (input))1095 void vtkHyperTreeGridToDualGrid::GenerateDualCornerFromLeaf3D(
1096   vtkHyperTreeGridNonOrientedMooreSuperCursor* cursor, vtkBitArray* mask,
1097   vtkHyperTreeGrid* vtkNotUsed(input))
1098 {
1099   // With d=3:
1100   //   (d-0)-faces are faces, neighbor cursors are 4, 10, 12, 14, 16, 22
1101   //   (d-1)-faces are edges, neighbor cursors are 1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25
1102   //   (d-2)-faces are corners, neighbor cursors are 0, 2, 6, 8, 18, 20, 24, 26
1103 
1104   // Retrieve cursor center coordinates
1105   double pt[3];
1106   cursor->GetPoint(pt);
1107 
1108   // Compute potential shifts
1109   double shift[3];
1110   shift[0] = .5 * cursor->GetSize()[0];
1111   shift[1] = .5 * cursor->GetSize()[1];
1112   shift[2] = .5 * cursor->GetSize()[2];
1113 
1114   // When a mask is present, corner, edge, and face shifts are possible
1115   bool shifted = false;
1116 
1117   // Check across face neighbors whether point must be adjusted
1118   unsigned int offset = 1;
1119   for (unsigned int axis = 0; axis < 3; ++axis, offset *= 3)
1120   {
1121     vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorM =
1122       cursor->GetOrientedGeometryCursor(13 - offset);
1123     if (!cursorM->HasTree())
1124     {
1125       // Move to negative side along axis
1126       pt[axis] -= shift[axis];
1127       shifted = true;
1128     }
1129     else
1130     {
1131       vtkIdType idM = cursorM->GetGlobalNodeIndex();
1132       if (cursorM->IsLeaf() && mask->GetValue(idM))
1133       {
1134         // Move to negative side along axis
1135         pt[axis] -= shift[axis];
1136         shifted = true;
1137       }
1138     }
1139     vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorP =
1140       cursor->GetOrientedGeometryCursor(13 + offset);
1141     if (!cursorP->HasTree())
1142     {
1143       // Move to positive side along axis
1144       pt[axis] += shift[axis];
1145       shifted = true;
1146     }
1147     else
1148     {
1149       vtkIdType idP = cursorP->GetGlobalNodeIndex();
1150       if (cursorP->IsLeaf() && mask->GetValue(idP))
1151       {
1152         // Move to positive side along axis
1153         pt[axis] += shift[axis];
1154         shifted = true;
1155       }
1156     }
1157   } // axis
1158 
1159   // Only when point was not moved to face, check edge neighbors
1160   if (!shifted)
1161   {
1162     int i = 1;
1163     for (int axis1 = 0; axis1 < 2; ++axis1, i *= 3)
1164     {
1165       int j = 3 * i;
1166       for (int axis2 = axis1 + 1; axis2 < 3; ++axis2, j *= 3)
1167       {
1168         for (int o2 = -1; o2 < 2; o2 += 2)
1169         {
1170           for (int o1 = -1; o1 < 2; o1 += 2)
1171           {
1172             int index = 13 + o1 * (i * o2 + j);
1173             vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorE =
1174               cursor->GetOrientedGeometryCursor(static_cast<unsigned int>(index));
1175             if (!cursorE->HasTree())
1176             {
1177               // Move to corresponding edge
1178               pt[axis1] += o1 * o2 * shift[axis1];
1179               pt[axis2] += o1 * shift[axis2];
1180               shifted = true;
1181             }
1182             else
1183             {
1184               vtkIdType idE = cursorE->GetGlobalNodeIndex();
1185               if (cursorE->IsLeaf() && mask->GetValue(idE))
1186               {
1187                 // Move to corresponding edge
1188                 pt[axis1] += o1 * o2 * shift[axis1];
1189                 pt[axis2] += o1 * shift[axis2];
1190                 shifted = true;
1191               }
1192             }
1193           } // o1
1194         }   // o2
1195       }     // axis2
1196     }       // axis1
1197   }         // if ( ! shifted )
1198 
1199   // Only when point was neither moved to face nor to edge, check corners neighbors
1200   if (!shifted)
1201   {
1202     // Iterate over all 8 corners
1203     for (int o3 = -1; o3 < 2; o3 += 2)
1204     {
1205       for (int o2 = -1; o2 < 2; o2 += 2)
1206       {
1207         offset = o2 * (o3 + 3) + 9;
1208         for (int o1 = -1; o1 < 2; o1 += 2)
1209         {
1210           int index = 13 + o1 * static_cast<int>(offset);
1211           vtkSmartPointer<vtkHyperTreeGridOrientedGeometryCursor> cursorC =
1212             cursor->GetOrientedGeometryCursor(static_cast<unsigned int>(index));
1213           if (!cursorC->HasTree())
1214           {
1215             // Move to corresponding corner
1216             pt[0] += o1 * o2 * o3 * shift[0];
1217             pt[1] += o1 * o2 * shift[1];
1218             pt[2] += o1 * shift[2];
1219           }
1220           else
1221           { // if cursor
1222             vtkIdType idC = cursorC->GetGlobalNodeIndex();
1223             if (cursorC->IsLeaf() && mask->GetValue(idC))
1224             {
1225               // Move to corresponding corner
1226               pt[0] += o1 * o2 * o3 * shift[0];
1227               pt[1] += o1 * o2 * shift[1];
1228               pt[2] += o1 * shift[2];
1229             }
1230           } // if cursor
1231         }   // o1
1232       }     // o2
1233     }       // o3
1234   }         // if ( ! shifted )
1235 
1236   // Retrieve global index of center cursor
1237   vtkIdType id = cursor->GetGlobalNodeIndex();
1238 
1239   // Insert dual point at center of leaf cell
1240   // assert ( id < input->GetGlobalNodeIndexMax() + 1 );
1241   this->Points->SetPoint(id, pt);
1242 
1243   // Storage for edge vertex IDs: dual cell ownership to cursor with higher index
1244   vtkIdType ids[8];
1245 
1246   // Retrieve current level to break corner ownership ties
1247   unsigned int level = cursor->GetLevel();
1248 
1249   // Iterate over leaf corners
1250   for (unsigned int c = 0; c < 8; ++c)
1251   {
1252     // Assume center cursor leaf owns the corner
1253     bool owner = true;
1254     unsigned int real_l = 0;
1255 
1256     // Iterate over every leaf touching the corner
1257     for (unsigned int l = 0; l < 8 && owner; ++l)
1258     {
1259       // Retrieve cursor index of touching leaf
1260       unsigned int index = CornerNeighborCursorsTable3D[c][l];
1261 
1262       // Compute whether corner is owned by another leaf
1263       if (index != 13)
1264       {
1265         if (!cursor->HasTree(index) || !cursor->IsLeaf(index) ||
1266           (cursor->GetLevel(index) == level && index > 13))
1267         {
1268           // If neighbor leaf is out of bounds or has not been
1269           // refined to a leaf, this leaf does not own the corner
1270           // A level tie is broken in favor of the largest index
1271           owner = false;
1272         }
1273         else
1274         {
1275           vtkIdType idglobal = cursor->GetGlobalNodeIndex(index);
1276           if (!mask->GetValue(idglobal))
1277           {
1278             // Collect the leaf indices for the dual cell
1279             ids[real_l++] = cursor->GetGlobalNodeIndex(index);
1280           }
1281           else
1282           {
1283             owner = false;
1284           }
1285         }
1286       }
1287       else
1288       { // if ( index != 13 )
1289         // Collect the leaf indices for the dual cell
1290         ids[real_l++] = id;
1291       } // else
1292     }   // l
1293 
1294     // If leaf owns the corner, create dual cell
1295     if (owner)
1296     {
1297       if (real_l != 8)
1298       {
1299         if (real_l == 0)
1300         {
1301           continue;
1302         }
1303         vtkIdType last = ids[real_l - 1];
1304         for (; real_l < 8; ++real_l)
1305         {
1306           ids[real_l] = last;
1307         }
1308       }
1309       this->Connectivity->InsertNextTypedTuple(ids);
1310     } // if ( owner )
1311   }   // c
1312 }
1313