VTK  9.7.20261102
vtkStaticPointLocatorPrivate.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
2// SPDX-FileCopyrightText: Copyright 2011 Sandia Corporation
3// SPDX-License-Identifier: LicenseRef-BSD-3-Clause-Sandia-USGov
12
13#ifndef vtkStaticPointLocatorPrivate_h
14#define vtkStaticPointLocatorPrivate_h
15
16#include "vtkArrayDispatch.h"
18#include "vtkCellArray.h"
19#include "vtkDataArrayRange.h"
20#include "vtkDataSet.h"
21#include "vtkDoubleArray.h"
22#include "vtkMath.h"
23#include "vtkMathUtilities.h" // for SafeCastFromDouble
24#include "vtkPoints.h"
25#include "vtkSMPThreadLocal.h"
27#include "vtkSMPTools.h"
29#include "vtkStructuredData.h"
30
31#include <cassert>
32#include <cstdlib>
33
34VTK_ABI_NAMESPACE_BEGIN
35
36//------------------------------------------------------------------------------
37// The following code supports threaded point locator construction. The locator
38// is assumed to be constructed once (i.e., it does not allow incremental point
39// insertion). The algorithm proceeds in three steps:
40// 1) All points are assigned a bucket index (combined i-j-k bucket location).
41// The index is computed in parallel. This requires a one time allocation of an
42// index array (which is also associated with the originating point ids).
43// 2) vtkSMPTools::Sort() is used to sort the index array. Note that the sort
44// carries along the point ids as well. This creates contiguous runs of points
45// all resident in the same bucket.
46// 3) The bucket offsets are updated to refer to the right entry location into
47// the sorted point ids array. This enables quick access, and an indirect count
48// of the number of points in each bucket.
49
50// Forward declaration of lists of neighboring buckets.
51struct NeighborBuckets;
52
53//------------------------------------------------------------------------------
54// The bucketed points, including the sorted map. This is just a PIMPLd
55// wrapper around the classes that do the real work.
57{
59 vtkIdType NumPts; // the number of points to bucket
62
63 // These are internal data members used for performance reasons
65 int Divisions[3];
66 double Bounds[6];
67 double H[3];
68 double hX, hY, hZ, hX2, hY2, hZ2;
69 double fX, fY, fZ, bX, bY, bZ;
71
72 // Used for accelerated performance for certain methods
73 double* FastPoints; // fast path for accessing points
74 double BinRadius; // circumradius of a single bin/bucket
75 int MaxLevel; // the maximum possible level searches can proceed
76
77 // Construction
78 vtkBucketList(vtkStaticPointLocator* loc, vtkIdType numPts, int numBuckets)
79 {
80 this->Locator = loc;
81 this->NumPts = numPts;
82 this->NumBuckets = numBuckets;
83 this->BatchSize = 10000; // building the offset array
84 this->DataSet = loc->GetDataSet();
85 loc->GetDivisions(this->Divisions);
86
87 // Setup internal data members for more efficient processing.
88 double spacing[3], bounds[6];
89 loc->GetDivisions(this->Divisions);
90 loc->GetSpacing(spacing);
91 loc->GetBounds(bounds);
92 this->hX = this->H[0] = spacing[0];
93 this->hY = this->H[1] = spacing[1];
94 this->hZ = this->H[2] = spacing[2];
95 this->hX2 = this->hX / 2.0;
96 this->hY2 = this->hY / 2.0;
97 this->hZ2 = this->hZ / 2.0;
98 this->fX = 1.0 / spacing[0];
99 this->fY = 1.0 / spacing[1];
100 this->fZ = 1.0 / spacing[2];
101 this->bX = this->Bounds[0] = bounds[0];
102 this->Bounds[1] = bounds[1];
103 this->bY = this->Bounds[2] = bounds[2];
104 this->Bounds[3] = bounds[3];
105 this->bZ = this->Bounds[4] = bounds[4];
106 this->Bounds[5] = bounds[5];
107 this->xD = this->Divisions[0];
108 this->yD = this->Divisions[1];
109 this->zD = this->Divisions[2];
110 this->xyD = this->Divisions[0] * this->Divisions[1];
111
112 this->FastPoints = nullptr;
113 this->BinRadius = sqrt(hX * hX + hY * hY + hZ * hZ) / 2.0;
114 this->MaxLevel = std::max({ this->xD, this->yD, this->zD });
115 }
116
117 // Virtuals for templated subclasses
118 virtual ~vtkBucketList() = default;
119 virtual void BuildLocator() = 0;
120
121 // place points in appropriate buckets
123 NeighborBuckets* buckets, const int ijk[3], const int ndivs[3], int level);
124 void GenerateFace(int face, int i, int j, int k, vtkPoints* pts, vtkCellArray* polys);
125 double Distance2ToBucket(const double x[3], const int nei[3]);
126 double Distance2ToBounds(const double x[3], const double bounds[6]);
127
128 //-----------------------------------------------------------------------------
129 // Inlined for performance. These function invocations must be called after
130 // BuildLocator() is invoked, otherwise the output is indeterminate.
131 void GetBucketIndices(const double* x, int ijk[3]) const
132 {
133 // Compute point index. SafeCastFromDouble clamps to the integer type limits
134 // (mapping NaN to 0) so casting a coordinate far outside the locator bounds
135 // (e.g. VTK_DOUBLE_MAX) is not undefined behavior. Make sure it then lies
136 // within the range of the locator.
140
141 ijk[0] = tmp0 < 0 ? 0 : std::min(xD - 1, tmp0);
142 ijk[1] = tmp1 < 0 ? 0 : std::min(yD - 1, tmp1);
143 ijk[2] = tmp2 < 0 ? 0 : std::min(zD - 1, tmp2);
144 }
145
146 //-----------------------------------------------------------------------------
147 vtkIdType GetBucketIndex(const double* x) const
148 {
149 int ijk[3];
150 this->GetBucketIndices(x, ijk);
151 return ijk[0] + ijk[1] * xD + ijk[2] * xyD;
152 }
153
154 //-----------------------------------------------------------------------------
155 // Return the center of the bucket/bin at (i,j,k).
156 void GetBucketCenter(int i, int j, int k, double center[3])
157 {
158 center[0] = this->bX + this->hX2 + i * this->hX;
159 center[1] = this->bY + this->hY2 + j * this->hY;
160 center[2] = this->bZ + this->hZ2 + k * this->hZ;
161 }
162
163 //-----------------------------------------------------------------------------
164 // Return the bounding box (min,max) of a specified bucket (i,j,k).
165 void GetBucketBounds(int i, int j, int k, double min[3], double max[3])
166 {
167 min[0] = this->bX + i * this->hX;
168 min[1] = this->bY + j * this->hY;
169 min[2] = this->bZ + k * this->hZ;
170 max[0] = min[0] + this->hX;
171 max[1] = min[1] + this->hY;
172 max[2] = min[2] + this->hZ;
173 }
174
175 //-----------------------------------------------------------------------------
176 // Determine whether a bin/bucket specified by i,j,k is completely contained
177 // inside the sphere (center,r2). Return true if contained; false otherwise.
178 bool BucketInsideSphere(int i, int j, int k, const double center[3], double r2)
179 {
180 double min[3], max[3];
181 min[0] = this->bX + i * this->hX;
182 min[1] = this->bY + j * this->hY;
183 min[2] = this->bZ + k * this->hZ;
184 max[0] = min[0] + this->hX;
185 max[1] = min[1] + this->hY;
186 max[2] = min[2] + this->hZ;
187 return vtkBoundingBox::InsideSphere(min, max, center, r2);
188 }
189}; // vtkBucketList
190
191//------------------------------------------------------------------------------
192// This templates class manages the creation of the static locator
193// structures. It also implements the operator() functors which are supplied
194// to vtkSMPTools for threaded processesing.
195template <typename TIds>
197{
198 // Okay the various ivars
199 vtkLocatorTuple<TIds>* Map; // the map to be sorted
200 TIds* Offsets; // offsets for each bucket into the map
201
202 // Construction
203 BucketList(vtkStaticPointLocator* loc, vtkIdType numPts, int numBuckets)
204 : vtkBucketList(loc, numPts, numBuckets)
205 {
206 // one extra to simplify traversal
207 this->Map = new vtkLocatorTuple<TIds>[numPts + 1];
208 this->Map[numPts].Bucket = numBuckets;
209 this->Offsets = new TIds[numBuckets + 1];
210 this->Offsets[numBuckets] = numPts;
211 }
212
213 // Release allocated memory
214 ~BucketList() override
215 {
216 delete[] this->Map;
217 delete[] this->Offsets;
218 }
219
220 // The number of point ids in a bucket is determined by computing the
221 // difference between the offsets into the sorted points array.
223 {
224 return (this->Offsets[bucketNum + 1] - this->Offsets[bucketNum]);
225 }
226
227 // Given a bucket number, return the point ids in that bucket.
229 {
230 return this->Map + this->Offsets[bucketNum];
231 }
232
233 // Given a bucket number, return the point ids in that bucket.
234 void GetIds(vtkIdType bucketNum, vtkIdList* bList)
235 {
236 const vtkLocatorTuple<TIds>* ids = this->GetIds(bucketNum);
237 vtkIdType numIds = this->GetNumberOfIds(bucketNum);
238 bList->SetNumberOfIds(numIds);
239 for (int i = 0; i < numIds; i++)
240 {
241 bList->SetId(i, ids[i].PtId);
242 }
243 }
244
245 // Templated implementations of the locator
246 vtkIdType FindClosestPoint(const double x[3]);
248 double radius, const double x[3], double inputDataLength, double& dist2);
249 void FindClosestNPoints(int N, const double x[3], vtkIdList* result);
250 double FindNPointsInShell(int N, const double x[3], vtkDist2TupleArray& results,
251 double minDist2 = (-0.1), bool sort = true, vtkDoubleArray* petals = nullptr);
252 void FindPointsInShell(const double x[3], vtkDist2TupleArray& results, double minDist2,
253 double maxDist2, bool sort, vtkDoubleArray* petals);
254 void FindPointsWithinRadius(double R, const double x[3], vtkIdList* result);
255 int IntersectWithLine(double a0[3], double a1[3], double tol, double& t, double lineX[3],
256 double ptX[3], vtkIdType& ptId);
257 void MergePoints(double tol, vtkIdType* pointMap, int orderingMode);
259 void GenerateRepresentation(int vtkNotUsed(level), vtkPolyData* pd);
260
261 // Internal methods
263 NeighborBuckets* buckets, const double x[3], const int ijk[3], double dist, int level);
264 void GetOverlappingBuckets(NeighborBuckets* buckets, const double x[3], double dist,
265 int prevMinLevel[3], int prevMaxLevel[3]);
266
267 // Implicit point representation, slower path
268 template <typename T>
270 {
273
275 : BList(blist)
276 , DataSet(ds)
277 {
278 }
279
281 {
282 double p[3];
283 vtkLocatorTuple<T>* t = this->BList->Map + ptId;
284
285 for (; ptId < end; ++ptId, ++t)
286 {
287 this->DataSet->GetPoint(ptId, p);
288 t->Bucket = this->BList->GetBucketIndex(p);
289 t->PtId = ptId;
290 } // for all points in this batch
291 }
292 };
293
294 template <typename T, typename TPointsArray>
296 {
298 TPointsArray* Points;
299
300 MapPointsArray(BucketList<T>* blist, TPointsArray* pts)
301 : BList(blist)
302 , Points(pts)
303 {
304 }
305
307 {
308 double p[3];
309 auto x = vtk::DataArrayTupleRange<3>(this->Points, ptId, end).begin();
310 vtkLocatorTuple<T>* t = this->BList->Map + ptId;
311
312 for (; ptId < end; ++ptId, ++x, ++t)
313 {
314 x->GetTuple(p);
315 t->Bucket = this->BList->GetBucketIndex(p);
316 t->PtId = ptId;
317 } // for all points in this batch
318 }
319 };
320
322 {
323 template <typename TPointsArray>
324 void operator()(TPointsArray* points, BucketList* blist)
325 {
326 MapPointsArray<TIds, TPointsArray> mapper(blist, points);
327 vtkSMPTools::For(0, blist->NumPts, mapper);
328 }
329 };
330
331 // A clever way to build offsets in parallel. Basically each thread builds
332 // offsets across a range of the sorted map. Recall that offsets are an
333 // integral value referring to the locations of the sorted points that
334 // reside in each bucket.
335 template <typename T>
337 {
341
343 : BList(blist)
344 {
345 this->NumBuckets = this->BList->NumBuckets;
346 this->NumPts = this->BList->NumPts;
347 }
348
349 // Traverse sorted points (i.e., tuples) and update bucket offsets.
350 void operator()(vtkIdType batch, vtkIdType batchEnd)
351 {
352 T* offsets = this->BList->Offsets;
353 const vtkLocatorTuple<T>* curPt = this->BList->Map + batch * this->BList->BatchSize;
354 const vtkLocatorTuple<T>* endBatchPt = this->BList->Map + batchEnd * this->BList->BatchSize;
355 const vtkLocatorTuple<T>* endPt = this->BList->Map + this->NumPts;
356 const vtkLocatorTuple<T>* prevPt;
357 endBatchPt = (endBatchPt > endPt ? endPt : endBatchPt);
358
359 // Special case at the very beginning of the mapped points array. If
360 // the first point is in bucket# N, then all buckets up and including
361 // N must refer to the first point.
362 if (curPt == this->BList->Map)
363 {
364 prevPt = this->BList->Map;
365 std::fill_n(offsets, curPt->Bucket + 1, 0); // point to the first points
366 } // at the very beginning of the map (sorted points array)
367
368 // We are entering this functor somewhere in the interior of the
369 // mapped points array. All we need to do is point to the entry
370 // position because we are interested only in prevPt->Bucket.
371 else
372 {
373 prevPt = curPt;
374 } // else in the middle of a batch
375
376 // Okay we have a starting point for a bucket run. Now we can begin
377 // filling in the offsets in this batch. A previous thread should
378 // have/will have completed the previous and subsequent runs outside
379 // of the [batch,batchEnd) range
380 for (curPt = prevPt; curPt < endBatchPt;)
381 {
382 for (; curPt->Bucket == prevPt->Bucket && curPt <= endBatchPt; ++curPt)
383 {
384 // advance
385 }
386 // Fill in any gaps in the offset array
387 std::fill_n(
388 offsets + prevPt->Bucket + 1, curPt->Bucket - prevPt->Bucket, curPt - this->BList->Map);
389 prevPt = curPt;
390 } // for all batches in this range
391 } // operator()
392 };
393
394 // Merge points that are pecisely coincident. Operates in parallel on
395 // locator buckets. Does not need to check neighbor buckets.
396 template <typename T>
398 {
402
404 : BList(blist)
405 , MergeMap(mergeMap)
406 {
407 this->DataSet = blist->DataSet;
408 }
409
410 void operator()(vtkIdType bucket, vtkIdType endBucket)
411 {
412 BucketList<T>* bList = this->BList;
413 vtkIdType* mergeMap = this->MergeMap;
414 int i, j;
415 const vtkLocatorTuple<TIds>* ids;
416 double p[3], p2[3];
417 vtkIdType ptId, ptId2, numIds;
418
419 for (; bucket < endBucket; ++bucket)
420 {
421 if ((numIds = bList->GetNumberOfIds(bucket)) > 0)
422 {
423 ids = bList->GetIds(bucket);
424 for (i = 0; i < numIds; i++)
425 {
426 ptId = ids[i].PtId;
427 if (mergeMap[ptId] < 0)
428 {
429 mergeMap[ptId] = ptId;
430 this->DataSet->GetPoint(ptId, p);
431 for (j = i + 1; j < numIds; j++)
432 {
433 ptId2 = ids[j].PtId;
434 if (mergeMap[ptId2] < 0)
435 {
436 this->DataSet->GetPoint(ptId2, p2);
437 if (p[0] == p2[0] && p[1] == p2[1] && p[2] == p2[2])
438 {
439 mergeMap[ptId2] = ptId;
440 }
441 }
442 }
443 } // if point not yet visited
444 }
445 }
446 }
447 }
448 };
449
450 // Merge points that are coincident within a specified tolerance. Depending
451 // on the orderingMode, either a serialized ordering process is used (i.e.,
452 // POINT_ORDER) or a threaded ordering process is used (i.e.,
453 // BIN_ORDER). Note that due to the tolerance, the merging tolerance
454 // needs to check neighbor buckets which slows the algorithm down
455 // considerably. Note that merging is in one direction: larger ids are
456 // merged to lower ids.
457 template <typename T>
459 {
463 double Tol;
464
466
467 MergeClose(BucketList<T>* blist, double tol, vtkIdType* mergeMap)
468 : BList(blist)
469 , MergeMap(mergeMap)
470 , Tol(tol)
471 {
472 this->DataSet = blist->DataSet;
473 }
474
475 // The core merging process around the point ptId.
476 void MergePoint(vtkIdType ptId, vtkIdList* nearby)
477 {
478 vtkIdType* mergeMap = this->MergeMap;
479
480 // Make sure the point is not already merged
481 if (mergeMap[ptId] < 0)
482 {
483 mergeMap[ptId] = ptId;
484 double p[3];
485 this->DataSet->GetPoint(ptId, p);
486 this->BList->FindPointsWithinRadius(this->Tol, p, nearby);
487 vtkIdType numIds = nearby->GetNumberOfIds();
488 if (numIds > 0)
489 {
490 for (auto i = 0; i < numIds; ++i)
491 {
492 vtkIdType nearId = nearby->GetId(i);
493 if (mergeMap[nearId] < 0)
494 {
495 mergeMap[nearId] = ptId;
496 } // if eligible for merging and not yet merged
497 } // for all nearby points
498 } // if nearby points exist
499 } // if point not yet merged
500 } // MergePoint
501
502 // Just allocate a little bit of memory to get started.
504 {
505 vtkIdList*& pIds = this->PIds.Local();
506 pIds->Reserve(128); // allocate some memory
507 }
508
509 void Reduce() {}
510 };
511
512 // Merge points with non-zero tolerance. Order of point merging guarantees
513 // that any two merged point ids (p0,p1) are such that p0<p1. Consequently
514 // this is a completely serial algorithm.
515 template <typename T>
516 struct MergePointOrder : public MergeClose<T>
517 {
518 MergePointOrder(BucketList<T>* blist, double tol, vtkIdType* mergeMap)
519 : MergeClose<T>(blist, tol, mergeMap)
520 {
521 }
522
524
525 // Process serially, point by point.
527 {
528 vtkIdList*& nearby = this->PIds.Local();
529
530 // Serial operation over all points in the locator.
531 for (vtkIdType ptId = 0; ptId < numPts; ++ptId)
532 {
533 this->MergePoint(ptId, nearby);
534 } // for all points in the locator
535 } // operator()
536
537 void Reduce() { this->MergeClose<T>::Reduce(); }
538 }; // Merge points in point ordering
539
540 // Merge points with non-zero tolerance. The order of point merging depends
541 // on the order in which the bins are traversed (using a checkerboard
542 // pattern). While the algorithm is threaded, the checkerboarding acts as
543 // a barrier to full threading so the performance is not optimal (but at
544 // least deterministic / reproducible).
545 //
546 // Checkerboarding works as follows. The locator bin volume of dimensions
547 // Divisions[3] is divided into a collection of "blocks" which are
548 // subvolumes of bins of dimensions d^3. The algorithm makes multiple,
549 // threaded passes over the blocks (a total of d^3 threaded traversals),
550 // choosing one of the bins in each block to process via the current
551 // checkerboard index. The dimension d of the blocks is determined by the
552 // tolerance and locator bin size, and is chosen in such a way as to
553 // separate the point merging computation so as to avoid threading data
554 // races / write contention.
555 template <typename T>
556 struct MergeBinOrder : public MergeClose<T>
557 {
558 int CheckerboardDimension; // the dimension of the checkerboard block/subvolume
559 int NumBlocks; // how many blocks/subvolumes are in the binned locator
560 int BlockDims[3]; // the number of blocks in each coordinate direction
561 int CheckerboardIndex[3]; // which bin is being processed in the blocks
562
563 // The main function of the constructor is the setup the checkerboard
564 // traversal. This means configuring the checkerboard subvolume, and
565 // set up the traversal indices.
566 MergeBinOrder(BucketList<T>* blist, double tol, vtkIdType* mergeMap)
567 : MergeClose<T>(blist, tol, mergeMap)
568 {
569 BucketList<T>* bl = this->BList;
570 double hMin = std::min({ bl->hX, bl->hY, bl->hZ });
571 this->CheckerboardDimension =
572 1 + (hMin == 0.0 ? 1 : (1 + vtkMath::Floor(tol / (hMin / 2.0))));
573
574 // Determine how many blocks there are in the locater, and determine the
575 // dimensions of the blocks.
576 this->NumBlocks = 1;
577 for (auto i = 0; i < 3; ++i)
578 {
579 double numBlocks =
580 static_cast<double>(bl->Divisions[i]) / static_cast<double>(this->CheckerboardDimension);
581 this->BlockDims[i] = (bl->Divisions[i] <= 1 ? 1 : vtkMath::Ceil(numBlocks));
582 this->NumBlocks *= this->BlockDims[i];
583 }
585 }
586
587 // Initialize the checkerboard traversal process. A pointer to the
588 // current traversal state (within the checkerboard region) is returned.
590 {
591 // Control checkerboard traversal
592 this->CheckerboardIndex[0] = 0;
593 this->CheckerboardIndex[1] = 0;
594 this->CheckerboardIndex[2] = 0;
595 return this->CheckerboardIndex;
596 }
597
598 // Given a blockId and the current checkerboard index, compute the
599 // current locator bin/bucket id. May return <0 if no bin exists.
600 vtkIdType GetCurrentBin(int blockId, int cIdx[3])
601 {
602 // Which checkerboard block are we in?
603 int ijk[3];
604 vtkStructuredData::ComputePointStructuredCoords(blockId, this->BlockDims, ijk);
605
606 // Combine the block index with the checkerboard index. Make sure that
607 // we are still inside the locator bins (partial blocks may exist at
608 // the boundary). Recall that the blocks are composed of d^3 bins.
609 for (auto i = 0; i < 3; ++i)
610 {
611 ijk[i] = ijk[i] * this->CheckerboardDimension + cIdx[i];
612 if (ijk[i] >= this->BList->Divisions[i])
613 {
614 return (-1);
615 }
616 }
617
618 // Okay return the bin index
619 return (ijk[0] + ijk[1] * this->BList->Divisions[0] +
620 ijk[2] * this->BList->Divisions[0] * this->BList->Divisions[1]);
621 }
622
624
625 // Process locator blocks/subvolumes.
626 void operator()(vtkIdType blockId, vtkIdType endBlockId)
627 {
628 vtkIdList*& nearby = this->PIds.Local();
629 for (; blockId < endBlockId; ++blockId)
630 {
631 vtkIdType bin = this->GetCurrentBin(blockId, this->CheckerboardIndex);
632 vtkIdType numIds;
633
634 if (bin >= 0 && (numIds = this->BList->GetNumberOfIds(bin)) > 0)
635 {
636 const vtkLocatorTuple<TIds>* ids = this->BList->GetIds(bin);
637 for (auto i = 0; i < numIds; ++i)
638 {
639 vtkIdType ptId = ids[i].PtId;
640 this->MergePoint(ptId, nearby);
641 } // for all points in bin/bucket
642 } // if points exist in bin/bucket
643 } // for all blocks
644 } // operator()
645
646 void Reduce() { this->MergeClose<T>::Reduce(); }
647
648 // Coordinate the checkerboard threading process. Checkerboarding simply
649 // processes a subset of the locator bins to avoid write contention. The
650 // checkerboard footprint (its subvolume size) is a function of the
651 // tolerance, and is effectively a d^3 subvolume that is traversed
652 // (across all subvolumes) in a synchronized fashion. Hence there are d^3
653 // separate SMP traversals - if d becomes too large, the fallback is
654 // simply a serial (MergePointOrder()) to avoid thread thrashing.
655 void Execute()
656 {
657 int cDim = this->CheckerboardDimension;
658 int* cIdx = this->InitializeCheckerboardIndex();
659
660 // Coordinate the checkerboarding by synchronized traversal of the
661 // the checkerboard subblocks.
662 for (cIdx[2] = 0; cIdx[2] < cDim; ++cIdx[2])
663 {
664 for (cIdx[1] = 0; cIdx[1] < cDim; ++cIdx[1])
665 {
666 for (cIdx[0] = 0; cIdx[0] < cDim; ++cIdx[0])
667 {
668 vtkSMPTools::For(0, this->NumBlocks, *this);
669 }
670 }
671 }
672 } // Execute()
673
674 }; // MergeBinOrder
675
676 // Merge points that are geometrically coincident and have matching data
677 // values. Operates in parallel on locator buckets. Does not need to check
678 // neighbor buckets.
679 template <typename T>
681 {
688
690 : BList(blist)
691 , DataArray(da)
692 , MergeMap(mergeMap)
693 {
694 this->DataSet = blist->DataSet;
695 }
696
697 bool TuplesEqual(int tupleSize, double* t1, double* t2)
698 {
699 for (auto i = 0; i < tupleSize; ++i)
700 {
701 if (t1[i] != t2[i])
702 {
703 return false;
704 }
705 }
706 return true;
707 }
708
710 {
711 vtkIdType numComp = this->DataArray->GetNumberOfComponents();
712 this->Tuple.Local().resize(numComp);
713 this->Tuple2.Local().resize(numComp);
714 }
715
716 void operator()(vtkIdType bucket, vtkIdType endBucket)
717 {
718 BucketList<T>* bList = this->BList;
719 vtkIdType* mergeMap = this->MergeMap;
720 int i, j;
721 const vtkLocatorTuple<TIds>* ids;
722 double p[3], p2[3];
723 vtkIdType ptId, ptId2, numIds;
724 int tupleSize = static_cast<int>(this->Tuple.Local().size());
725 double* t = this->Tuple.Local().data();
726 double* t2 = this->Tuple2.Local().data();
727
728 for (; bucket < endBucket; ++bucket)
729 {
730 if ((numIds = bList->GetNumberOfIds(bucket)) > 0)
731 {
732 ids = bList->GetIds(bucket);
733 for (i = 0; i < numIds; i++)
734 {
735 ptId = ids[i].PtId;
736 if (mergeMap[ptId] < 0)
737 {
738 mergeMap[ptId] = ptId;
739 this->DataSet->GetPoint(ptId, p);
740 this->DataArray->GetTuple(ptId, t);
741 for (j = i + 1; j < numIds; j++)
742 {
743 ptId2 = ids[j].PtId;
744 if (mergeMap[ptId2] < 0)
745 {
746 this->DataSet->GetPoint(ptId2, p2);
747 if (p[0] == p2[0] && p[1] == p2[1] && p[2] == p2[2])
748 {
749 this->DataArray->GetTuple(ptId2, t2);
750 if (this->TuplesEqual(tupleSize, t, t2))
751 {
752 mergeMap[ptId2] = ptId;
753 } // if point's data match
754 } // if points geometrically coincident
755 } // if point not yet visited
756 } // for the remaining points in the bin
757 } // if point not yet merged
758 } // for all points in bucket
759 } // if bucket contains points
760 } // for all buckets
761 } // operator()
762
763 void Reduce() {}
764 }; // MergePointsWithData
765
766 // Build the map and other structures to support locator operations
767 void BuildLocator() override
768 {
769 // Place each point in a bucket
770 auto points = this->DataSet->GetPoints()->GetData();
773 points, worker, this))
774 {
775 worker(points, this);
776 }
777
778 // Provide accelerated access to points. Needed for Voronoi bin iterators.
780 {
781 this->FastPoints = static_cast<double*>(vtkDoubleArray::SafeDownCast(points)->GetPointer(0));
782 }
783
784 // Now group the points into contiguous runs within buckets (recall that
785 // sorting is occurring based on bin/bucket id).
786 vtkSMPTools::Sort(this->Map, this->Map + this->NumPts);
787
788 // Build the offsets into the Map. The offsets are the positions of
789 // each bucket into the sorted list. They mark the beginning of the
790 // list of points in each bucket. Amazingly, this can be done in
791 // parallel.
792 int numBatches = static_cast<int>(ceil(static_cast<double>(this->NumPts) / this->BatchSize));
793 MapOffsets<TIds> offMapper(this);
794 vtkSMPTools::For(0, numBatches, offMapper);
795 }
796};
797
798//------------------------------------------------------------------------------
799// Iteration over the bins of a vtkStaticPointLocator in blocks of increasing
800// levels around a center bin: the block of level L is the cube of bins
801// (2L+1)^3 around the center bin, and only its boundary bins (the bins not in
802// the block of level L-1) are visited, each once. Used by
803// vtkStaticPointLocator::FindPointsInShell() and vtkShellBinIterator.
804//------------------------------------------------------------------------------
805// Iterate over a *specified face* of a block of bins. Iteration occurs by
806// looping around the face center point at increasing face levels. Looping
807// continues until the face level is equal to the block level. This struct is
808// used in conjunction with vtkBucketBlockIterator to visit bins closer to a
809// generating point, and to ping-pong iterate over -/+ face points. The block
810// size, and therefore the sizes of the x, y, z faces, is specified by
811// blockLevel.
813{
814 // The current state of iteration.
815 int BlockLevel; // the current block over which iteration is occuring
816 int FaceCenter[3]; // the center of iteration of the current block face
817 int FaceNum; // the face being iterated over: 0-xface,1-yface,2-zface
818 int FaceLevel; // the current face level over which we are iterating
819 int Span; // the topological width of the block at the current face level
820 int EdgeNum; // four edges of each face form a loop, the current edge
821 int NumIncs; // the number of iteration steps over the current edge
822 int IncNum; // the current position along a block face edge
823 int Inc0[3]; // the index increment in the face "i" direction
824 int Inc1[3]; // the index increment in the face "j" direction
825 int IJK[3]; // the current IJK index of iteration
826
827 // Constructor requires block face type: i, j, or k.
829 {
830 // The face being iterated over: -i, -j, or -k. Ping-ponging
831 // is used to iterate over the +i, +j, or +k block faces.
832 this->FaceNum = faceNum;
833
834 // Specify the increments to move to the next bin during face looping.
835 if (this->FaceNum == 0) // -i face
836 {
837 this->Inc0[0] = 0;
838 this->Inc0[1] = 1;
839 this->Inc0[2] = 0;
840 this->Inc1[0] = 0;
841 this->Inc1[1] = 0;
842 this->Inc1[2] = 1;
843 }
844 else if (this->FaceNum == 1) // -j face
845 {
846 this->Inc0[0] = 1;
847 this->Inc0[1] = 0;
848 this->Inc0[2] = 0;
849 this->Inc1[0] = 0;
850 this->Inc1[1] = 0;
851 this->Inc1[2] = 1;
852 }
853 else // if ( this->FaceNum == 0 ) // -k face
854 {
855 this->Inc0[0] = 1;
856 this->Inc0[1] = 0;
857 this->Inc0[2] = 0;
858 this->Inc1[0] = 0;
859 this->Inc1[1] = 1;
860 this->Inc1[2] = 0;
861 }
862 }
863
864 // Update the IJK index.
865 void GetIJK(int ijk[3])
866 {
867 ijk[0] = this->IJK[0];
868 ijk[1] = this->IJK[1];
869 ijk[2] = this->IJK[2];
870 }
871
872 // The following methods are used for looping around the face center point in
873 // a counterclockwise direction.
875 {
876 this->IJK[0] += this->Inc0[0];
877 this->IJK[1] += this->Inc0[1];
878 this->IJK[2] += this->Inc0[2];
879 }
880 void MoveUp()
881 {
882 this->IJK[0] += this->Inc1[0];
883 this->IJK[1] += this->Inc1[1];
884 this->IJK[2] += this->Inc1[2];
885 }
886 void MoveLeft()
887 {
888 this->IJK[0] -= this->Inc0[0];
889 this->IJK[1] -= this->Inc0[1];
890 this->IJK[2] -= this->Inc0[2];
891 }
892 void MoveDown()
893 {
894 this->IJK[0] -= this->Inc1[0];
895 this->IJK[1] -= this->Inc1[1];
896 this->IJK[2] -= this->Inc1[2];
897 }
898
899 // The i,j,k faces of the block overlap along edges and at vertices. To
900 // prevent retrieval of bins more than once, we make sure that potentially
901 // overlapping vertices are only retrieved once.
902 bool IsValid()
903 {
904 // The i-face always produces a valid IJK. Any i,j,k face loops not
905 // touching the block boundary edges are always valid as well.
906 if (this->FaceNum == 0 || this->FaceLevel < this->BlockLevel)
907 {
908 return true;
909 }
910
911 // The j-face may revisit some edge bins (due to overlap with the
912 // +/- i face edges). So check the +/- i extremes for overlap.
913 else if (this->FaceNum == 1)
914 {
915 if (this->IJK[0] == (this->FaceCenter[0] - this->FaceLevel) ||
916 this->IJK[0] == (this->FaceCenter[0] + this->FaceLevel))
917 {
918 return false;
919 }
920 }
921
922 // The k-face may revisit some edge bins (due to overlap with the
923 // +/- i and +/- j face edges). Note: this code should not be
924 // visited since during iteration we prevent processing the outer
925 // k-face loop altogether, it's included for debugging purposes.
926 else // if ( this->FaceNum == 2 )
927 {
928 if (this->IJK[0] == (this->FaceCenter[0] - this->FaceLevel) ||
929 this->IJK[0] == (this->FaceCenter[0] + this->FaceLevel) ||
930 this->IJK[1] == (this->FaceCenter[1] - this->FaceLevel) ||
931 this->IJK[1] == (this->FaceCenter[1] + this->FaceLevel))
932 {
933 return false;
934 }
935 }
936
937 return true;
938 }
939
940 // Initialize the block face iteration process. Indicate the
941 // center of the block, and the block level. This is used to
942 // specify the face center of iteration.
943 void Initialize(int blockLevel, int blockCenter[3])
944 {
945 this->BlockLevel = blockLevel;
946 if (this->FaceNum == 0) // -i face
947 {
948 this->FaceCenter[0] = blockCenter[0] - blockLevel;
949 this->FaceCenter[1] = blockCenter[1];
950 this->FaceCenter[2] = blockCenter[2];
951 }
952 else if (this->FaceNum == 1) // -j face
953 {
954 this->FaceCenter[0] = blockCenter[0];
955 this->FaceCenter[1] = blockCenter[1] - blockLevel;
956 this->FaceCenter[2] = blockCenter[2];
957 }
958 else // if ( this->FaceNum == 0 ) // -k face
959 {
960 this->FaceCenter[0] = blockCenter[0];
961 this->FaceCenter[1] = blockCenter[1];
962 this->FaceCenter[2] = blockCenter[2] - blockLevel;
963 }
964 }
965
966 // Internal methods for the face iteration process follow below. These just
967 // visit the bins on the face of a block without considering topological
968 // validity. The iteration starts at the face center (level==0), and then
969 // grows outward one level at a time until the all bins associated with the
970 // face are visited. This method updates IJK[3] which is the current bin.
971 void BeginBin(int faceLevel)
972 {
973 // The current level of face iteration
974 this->FaceLevel = faceLevel;
975
976 // Special case: starting at the center of the current face.
977 if (faceLevel == 0)
978 {
979 this->IJK[0] = this->FaceCenter[0];
980 this->IJK[1] = this->FaceCenter[1];
981 this->IJK[2] = this->FaceCenter[2];
982 return;
983 }
984
985 // General case: iterate over the four edges of the face (basically
986 // traverse loops around the face center). We start at the lower left of the
987 // current level, moving horizontally NumIncs steps before turning vertically
988 // along the next edge.
989 this->Span = 2 * faceLevel + 1;
990
991 // Specify various iteration parameters, and the initial starting position
992 this->EdgeNum = 0;
993 this->IncNum = 0;
994 this->NumIncs = this->Span - 1;
995
996 if (this->FaceNum == 0) // -i face
997 {
998 this->IJK[0] = this->FaceCenter[0];
999 this->IJK[1] = this->FaceCenter[1] - faceLevel + 1;
1000 this->IJK[2] = this->FaceCenter[2] - faceLevel;
1001 }
1002 else if (this->FaceNum == 1) // -j face
1003 {
1004 this->IJK[0] = this->FaceCenter[0] - faceLevel + 1;
1005 this->IJK[1] = this->FaceCenter[1];
1006 this->IJK[2] = this->FaceCenter[2] - faceLevel;
1007 }
1008 else // if ( this->FaceNum == 2 ) // -k face
1009 {
1010 this->IJK[0] = this->FaceCenter[0] - faceLevel + 1;
1011 this->IJK[1] = this->FaceCenter[1] - faceLevel;
1012 this->IJK[2] = this->FaceCenter[2];
1013 }
1014 }
1015
1016 // Return false when iteration over the current faceLevel loop is complete.
1017 bool NextBin()
1018 {
1019 // Special case for face level == 0.
1020 if (this->FaceLevel == 0)
1021 {
1022 return false;
1023 }
1024
1025 // The k-face special case for skipping the whole outer loop on
1026 // the k block face.
1027 if (this->FaceNum == 2 && this->FaceLevel == this->BlockLevel)
1028 {
1029 return false;
1030 }
1031
1032 // Now for the general case. We traverse the four edges that
1033 // form a loop of bins around the face center point.
1034 this->IncNum++; // advance along current edge
1035 switch (this->EdgeNum)
1036 {
1037 case 0: // lower edge
1038 if (this->IncNum < this->NumIncs)
1039 {
1040 this->MoveRight();
1041 }
1042 else // move to next edge
1043 {
1044 this->EdgeNum = 1;
1045 this->IncNum = 0;
1046 this->MoveUp();
1047 }
1048 break;
1049 case 1: // right edge
1050 if (this->IncNum < this->NumIncs)
1051 {
1052 this->MoveUp();
1053 }
1054 else // move to next edge
1055 {
1056 this->EdgeNum = 2;
1057 this->IncNum = 0;
1058 this->MoveLeft();
1059 }
1060 break;
1061 case 2: // upper edge
1062 if (this->IncNum < this->NumIncs)
1063 {
1064 this->MoveLeft();
1065 }
1066 else // move to next edge
1067 {
1068 this->EdgeNum = 3;
1069 this->IncNum = 0;
1070 this->MoveDown();
1071 }
1072 break;
1073 case 3: // left edge
1074 if (this->IncNum < this->NumIncs)
1075 {
1076 this->MoveDown();
1077 }
1078 else // move to next edge
1079 {
1080 return false;
1081 }
1082 break;
1083 default: // terminate loop
1084 return false;
1085 }
1086
1087 return true;
1088 }
1089
1090 // Basically just sets the iteration starting point for the face loops
1091 // at the i face level==0.
1092 void Begin(int ijk[3])
1093 {
1094 this->BeginBin(0);
1095 this->GetIJK(ijk);
1096 }
1097
1098 // Return false when iteration over the entirety of the current face
1099 // completes. Rectangular rings of nested block face loops are processed
1100 // until all bins on the face are visited. When all face loops have been
1101 // processed, then return false;
1102 bool Next(int ijk[3])
1103 {
1104 // Loop until valid IJK is produced
1105 while (this->NextBin()) // returns false when level is complete
1106 {
1107 if (this->IsValid())
1108 {
1109 this->GetIJK(ijk);
1110 return true;
1111 }
1112 }
1113
1114 // If here, the current loop at face level has been exhausted. Start the next
1115 // face loop at the next level, or check and see if all face loops have been
1116 // visited.
1117 if (this->FaceLevel < this->BlockLevel)
1118 {
1119 this->BeginBin(this->FaceLevel + 1);
1120 this->GetIJK(ijk);
1121 return true;
1122 }
1123 else
1124 {
1125 return false;
1126 }
1127 } // Next()
1128}; // vtkBucketBlockFaceIterator
1129
1130//------------------------------------------------------------------------------
1131// Hard coded block traversals for lower levels. Note the ping-pong
1132// order (traversal of opposite bins).
1133inline constexpr int vtkBucketBlockLevel1[26][3] = { { -1, 0, 0 }, { 1, 0, 0 }, { 0, -1, 0 },
1134 { 0, 1, 0 }, { 0, 0, -1 }, { 0, 0, 1 }, // six face centers
1135 { 0, -1, -1 }, { 0, 1, 1 }, { 0, 1, -1 }, { 0, -1, 1 }, // centers of x-edges
1136 { -1, 0, -1 }, { 1, 0, 1 }, { 1, 0, -1 }, { -1, 0, 1 }, // centers of y-edges
1137 { -1, -1, 0 }, { 1, 1, 0 }, { 1, -1, 0 }, { -1, 1, 0 }, // centers of z-edges
1138 { -1, -1, -1 }, { 1, 1, 1 }, { 1, -1, -1 }, { -1, 1, 1 }, // eight corners
1139 { -1, 1, -1 }, { 1, -1, 1 }, { 1, 1, -1 }, { -1, -1, 1 } }; // Level1
1140
1141// Face centers, spiraling out with ping-pong traversal.
1142inline constexpr int vtkBucketBlockLevel2[98][3] = { { -2, 0, 0 }, { 2, 0, 0 }, { 0, -2, 0 },
1143 { 0, 2, 0 }, { 0, 0, -2 }, { 0, 0, 2 }, { -2, 0, -1 }, { 2, 0, -1 }, { 0, -2, -1 }, { 0, 2, -1 },
1144 { 0, -1, -2 }, { 0, -1, 2 }, { -2, 1, -1 }, { 2, 1, -1 }, { 1, -2, -1 }, { 1, 2, -1 },
1145 { 1, -1, -2 }, { 1, -1, 2 }, { -2, 1, 0 }, { 2, 1, 0 }, { 1, -2, 0 }, { 1, 2, 0 }, { 1, 0, -2 },
1146 { 1, 0, 2 }, { -2, 1, 1 }, { 2, 1, 1 }, { 1, -2, 1 }, { 1, 2, 1 }, { 1, 1, -2 }, { 1, 1, 2 },
1147 { -2, 0, 1 }, { 2, 0, 1 }, { 0, -2, 1 }, { 0, 2, 1 }, { 0, 1, -2 }, { 0, 1, 2 }, { -2, -1, 1 },
1148 { 2, -1, 1 }, { -1, -2, 1 }, { -1, 2, 1 }, { -1, 1, -2 }, { -1, 1, 2 }, { -2, -1, 0 },
1149 { 2, -1, 0 }, { -1, -2, 0 }, { -1, 2, 0 }, { -1, 0, -2 }, { -1, 0, 2 }, { -2, -1, -1 },
1150 { 2, -1, -1 }, { -1, -2, -1 }, { -1, 2, -1 }, { -1, -1, -2 }, { -1, -1, 2 }, { -1, -2, -2 },
1151 { -1, 2, -2 }, { -2, -1, -2 }, { 2, -1, -2 }, { 0, -2, -2 }, { 0, 2, -2 }, { -2, 0, -2 },
1152 { 2, 0, -2 }, { 1, -2, -2 }, { 1, 2, -2 }, { -2, 1, -2 }, { 2, 1, -2 }, { 1, -2, 2 }, { 1, 2, 2 },
1153 { -2, 2, -2 }, { 2, 2, -2 }, { 0, -2, 2 }, { 0, 2, 2 }, { -2, 2, -1 }, { 2, 2, -1 },
1154 { -1, -2, 2 }, { -1, 2, 2 }, { -2, 2, 0 }, { 2, 2, 0 }, { -2, 2, 1 }, { 2, 2, 1 }, { -2, 2, 2 },
1155 { 2, 2, 2 }, { -2, 1, 2 }, { 2, 1, 2 }, { -2, 0, 2 }, { 2, 0, 2 }, { -2, -1, 2 }, { 2, -1, 2 },
1156 { -2, -2, 2 }, { 2, -2, 2 }, { -2, -2, 1 }, { 2, -2, 1 }, { -2, -2, 0 }, { 2, -2, 0 },
1157 { -2, -2, -1 }, { 2, -2, -1 }, { -2, -2, -2 }, { 2, -2, -2 } }; // Level2
1158
1159inline constexpr int vtkBucketBlockLevel3[218][3] = { { -3, 0, 0 }, { 3, 0, 0 }, { 0, -3, 0 },
1160 { 0, 3, 0 }, { 0, 0, -3 }, { 0, 0, 3 }, { -3, 0, -1 }, { 3, 0, -1 }, { 0, -3, -1 }, { 0, 3, -1 },
1161 { 0, -1, -3 }, { 0, -1, 3 }, { -3, 1, -1 }, { 3, 1, -1 }, { 1, -3, -1 }, { 1, 3, -1 },
1162 { 1, -1, -3 }, { 1, -1, 3 }, { -3, 1, 0 }, { 3, 1, 0 }, { 1, -3, 0 }, { 1, 3, 0 }, { 1, 0, -3 },
1163 { 1, 0, 3 }, { -3, 1, 1 }, { 3, 1, 1 }, { 1, -3, 1 }, { 1, 3, 1 }, { 1, 1, -3 }, { 1, 1, 3 },
1164 { -3, 0, 1 }, { 3, 0, 1 }, { 0, -3, 1 }, { 0, 3, 1 }, { 0, 1, -3 }, { 0, 1, 3 }, { -3, -1, 1 },
1165 { 3, -1, 1 }, { -1, -3, 1 }, { -1, 3, 1 }, { -1, 1, -3 }, { -1, 1, 3 }, { -3, -1, 0 },
1166 { 3, -1, 0 }, { -1, -3, 0 }, { -1, 3, 0 }, { -1, 0, -3 }, { -1, 0, 3 }, { -3, -1, -1 },
1167 { 3, -1, -1 }, { -1, -3, -1 }, { -1, 3, -1 }, { -1, -1, -3 }, { -1, -1, 3 }, { -3, -1, -2 },
1168 { 3, -1, -2 }, { -1, -3, -2 }, { -1, 3, -2 }, { -1, -2, -3 }, { -1, -2, 3 }, { -3, 0, -2 },
1169 { 3, 0, -2 }, { 0, -3, -2 }, { 0, 3, -2 }, { 0, -2, -3 }, { 0, -2, 3 }, { -3, 1, -2 },
1170 { 3, 1, -2 }, { 1, -3, -2 }, { 1, 3, -2 }, { 1, -2, -3 }, { 1, -2, 3 }, { -3, 2, -2 },
1171 { 3, 2, -2 }, { 2, -3, -2 }, { 2, 3, -2 }, { 2, -2, -3 }, { 2, -2, 3 }, { -3, 2, -1 },
1172 { 3, 2, -1 }, { 2, -3, -1 }, { 2, 3, -1 }, { 2, -1, -3 }, { 2, -1, 3 }, { -3, 2, 0 }, { 3, 2, 0 },
1173 { 2, -3, 0 }, { 2, 3, 0 }, { 2, 0, -3 }, { 2, 0, 3 }, { -3, 2, 1 }, { 3, 2, 1 }, { 2, -3, 1 },
1174 { 2, 3, 1 }, { 2, 1, -3 }, { 2, 1, 3 }, { -3, 2, 2 }, { 3, 2, 2 }, { 2, -3, 2 }, { 2, 3, 2 },
1175 { 2, 2, -3 }, { 2, 2, 3 }, { -3, 1, 2 }, { 3, 1, 2 }, { 1, -3, 2 }, { 1, 3, 2 }, { 1, 2, -3 },
1176 { 1, 2, 3 }, { -3, 0, 2 }, { 3, 0, 2 }, { 0, -3, 2 }, { 0, 3, 2 }, { 0, 2, -3 }, { 0, 2, 3 },
1177 { -3, -1, 2 }, { 3, -1, 2 }, { -1, -3, 2 }, { -1, 3, 2 }, { -1, 2, -3 }, { -1, 2, 3 },
1178 { -3, -2, 2 }, { 3, -2, 2 }, { -2, -3, 2 }, { -2, 3, 2 }, { -2, 2, -3 }, { -2, 2, 3 },
1179 { -3, -2, 1 }, { 3, -2, 1 }, { -2, -3, 1 }, { -2, 3, 1 }, { -2, 1, -3 }, { -2, 1, 3 },
1180 { -3, -2, 0 }, { 3, -2, 0 }, { -2, -3, 0 }, { -2, 3, 0 }, { -2, 0, -3 }, { -2, 0, 3 },
1181 { -3, -2, -1 }, { 3, -2, -1 }, { -2, -3, -1 }, { -2, 3, -1 }, { -2, -1, -3 }, { -2, -1, 3 },
1182 { -3, -2, -2 }, { 3, -2, -2 }, { -2, -3, -2 }, { -2, 3, -2 }, { -2, -2, -3 }, { -2, -2, 3 },
1183 { -2, -3, -3 }, { -2, 3, -3 }, { -3, -2, -3 }, { 3, -2, -3 }, { -1, -3, -3 }, { -1, 3, -3 },
1184 { -3, -1, -3 }, { 3, -1, -3 }, { 0, -3, -3 }, { 0, 3, -3 }, { -3, 0, -3 }, { 3, 0, -3 },
1185 { 1, -3, -3 }, { 1, 3, -3 }, { -3, 1, -3 }, { 3, 1, -3 }, { 2, -3, -3 }, { 2, 3, -3 },
1186 { -3, 2, -3 }, { 3, 2, -3 }, { 2, -3, 3 }, { 2, 3, 3 }, { -3, 3, -3 }, { 3, 3, -3 }, { 1, -3, 3 },
1187 { 1, 3, 3 }, { -3, 3, -2 }, { 3, 3, -2 }, { 0, -3, 3 }, { 0, 3, 3 }, { -3, 3, -1 }, { 3, 3, -1 },
1188 { -1, -3, 3 }, { -1, 3, 3 }, { -3, 3, 0 }, { 3, 3, 0 }, { -2, -3, 3 }, { -2, 3, 3 }, { -3, 3, 1 },
1189 { 3, 3, 1 }, { -3, 3, 2 }, { 3, 3, 2 }, { -3, 3, 3 }, { 3, 3, 3 }, { -3, 2, 3 }, { 3, 2, 3 },
1190 { -3, 1, 3 }, { 3, 1, 3 }, { -3, 0, 3 }, { 3, 0, 3 }, { -3, -1, 3 }, { 3, -1, 3 }, { -3, -2, 3 },
1191 { 3, -2, 3 }, { -3, -3, 3 }, { 3, -3, 3 }, { -3, -3, 2 }, { 3, -3, 2 }, { -3, -3, 1 },
1192 { 3, -3, 1 }, { -3, -3, 0 }, { 3, -3, 0 }, { -3, -3, -1 }, { 3, -3, -1 }, { -3, -3, -2 },
1193 { 3, -3, -2 }, { -3, -3, -3 }, { 3, -3, -3 } }; // Level3
1194
1195//------------------------------------------------------------------------------
1196// Iterate over the boundary bins of a block of bins. The block is centered
1197// at Center, with width/height/depth of 2*Level+1. Note that this iterator
1198// deals strictly with topological concerns (iteration, and inclusion in
1199// topological space). For performance, block levels==0 and ==1 are hard
1200// coded to return the bins in near-optimal order (i.e., bins closer to the
1201// block center are returned first). The general iteration case (level>1) is
1202// performed using the vtkBucketBlockFaceIterator.
1204{
1205 int Divs[3]; // the topology of a regular (locator) binning
1206 vtkIdType BinSlice; // the size of a slice of bins
1207 int MaxLevel; // maximum level of iteration for the block+center
1208
1209 // The current state of iteration.
1210 int Level; // the current level over which we are iterating
1211 int Center[3]; // the center of iteration of the block
1212 int Span; // the topological width of the block at current level
1213 int NumBins; // total number of bins to iterate over at current level
1214 int BinNum; // the current iteration position
1215 int Interior; // indicate whether (current bin+level) is interior to the block
1216 int IJK[3]; // the current IJK index of iteration
1217
1218 // Traversal info to support level>1 general iteration over bins on the
1219 // faces of the block. We need only the -i, -j, and -k block faces; using
1220 // ping-ponging (to opposite face) we visit the +i, +j, and +k faces as well.
1223
1224 // Constructor defines three face types (-i, -j, -k).
1226 : IFace(0)
1227 , JFace(1)
1228 , KFace(2)
1229 {
1230 }
1231
1232 // Convenience method to get the current bin id from the current bin IJK.
1234 {
1235 return (this->IJK[0] + this->IJK[1] * this->Divs[0] + this->IJK[2] * this->BinSlice);
1236 }
1237
1238 // Determine if the current bin is within the locator binning.
1239 bool IsValid()
1240 {
1241 if (this->Interior)
1242 {
1243 return true;
1244 }
1245
1246 if (this->IJK[0] < 0 || this->IJK[0] >= this->Divs[0] || this->IJK[1] < 0 ||
1247 this->IJK[1] >= this->Divs[1] || this->IJK[2] < 0 || this->IJK[2] >= this->Divs[2])
1248 {
1249 return false;
1250 }
1251 return true;
1252 }
1253
1254 // Initialize the iterator. It is required that the center is
1255 // within the topological binning specified by divs[3].
1256 void Initialize(int divs[3], int maxLevel)
1257 {
1258 this->Divs[0] = divs[0];
1259 this->Divs[1] = divs[1];
1260 this->Divs[2] = divs[2];
1261 this->BinSlice = divs[0] * divs[1];
1262 this->MaxLevel = maxLevel;
1263 }
1264
1265 // Internal methods for the iteration process follow below. These just
1266 // visit the surface of the block without considering topological validity.
1267 // Begin processing the block at the given level. Bins are returned in
1268 // this->IJK[3]. The level must be >=0, and the center within the specified
1269 // Divs[3].
1270 void BeginBin(int level, int center[3])
1271 {
1272 // The level being iterated over
1273 this->Level = level;
1274
1275 // The center of iteration in topological coordinates.
1276 this->Center[0] = center[0];
1277 this->Center[1] = center[1];
1278 this->Center[2] = center[2];
1279
1280 // Starting iteration bin is always 0
1281 this->BinNum = 0;
1282
1283 // Special case for level 0.
1284 if (level == 0)
1285 {
1286 this->NumBins = 1;
1287 this->IJK[0] = this->Center[0];
1288 this->IJK[1] = this->Center[1];
1289 this->IJK[2] = this->Center[2];
1290 return;
1291 }
1292
1293 // The span. This comes into play later to process edge and face bins
1294 // when the level>1.
1295 this->Span = 2 * level + 1;
1296
1297 // The total number of bins to process
1298 this->NumBins = (level == 0 ? 1
1299 : (this->Span * this->Span * this->Span) -
1300 ((2 * (level - 1) + 1) * (2 * (level - 1) + 1) * (2 * (level - 1) + 1)));
1301
1302 // Determine whether the iteration region is inside of the locator. If
1303 // the iteration region overlaps the boundary (i.e., is not interior),
1304 // then bin validity checks have to be performed.
1305 this->Interior = true;
1306 if ((this->Center[0] - level) < 0 || (this->Center[0] + level) >= this->Divs[0] ||
1307 (this->Center[1] - level) < 0 || (this->Center[1] + level) >= this->Divs[1] ||
1308 (this->Center[2] - level) < 0 || (this->Center[2] + level) >= this->Divs[2])
1309 {
1310 this->Interior = false;
1311 }
1312
1313 // Special case for levels 1-3. The bins are traversed in ping-pong order
1314 // (-/+ face centers, edges, and then corner points).
1315 if (level == 1)
1316 {
1317 // Iteration always begins at the center of the (-i)-face.
1318 this->IJK[0] = this->Center[0] + vtkBucketBlockLevel1[0][0];
1319 this->IJK[1] = this->Center[1] + vtkBucketBlockLevel1[0][1];
1320 this->IJK[2] = this->Center[2] + vtkBucketBlockLevel1[0][2];
1321 return;
1322 }
1323 else if (level == 2)
1324 {
1325 // Iteration always begins at the center of the (-i)-face.
1326 this->IJK[0] = this->Center[0] + vtkBucketBlockLevel2[0][0];
1327 this->IJK[1] = this->Center[1] + vtkBucketBlockLevel2[0][1];
1328 this->IJK[2] = this->Center[2] + vtkBucketBlockLevel2[0][2];
1329 return;
1330 }
1331 else if (level == 3)
1332 {
1333 // Iteration always begins at the center of the (-i)-face.
1334 this->IJK[0] = this->Center[0] + vtkBucketBlockLevel3[0][0];
1335 this->IJK[1] = this->Center[1] + vtkBucketBlockLevel3[0][1];
1336 this->IJK[2] = this->Center[2] + vtkBucketBlockLevel3[0][2];
1337 return;
1338 }
1339
1340 // Otherwise general case for level>3. Process the three i, j, k boundary
1341 // bins of the current cuboid iteration sublock using ping-pong
1342 // traversal. Begin by setting up some iteration ranges for each of the
1343 // i, j, k boundary planes.
1344 this->BinNumRange[0] = 0;
1345 this->BinNumRange[1] = 2 * (this->Span * this->Span);
1346 this->BinNumRange[2] = this->BinNumRange[1] + (2 * this->Span * (this->Span - 2));
1347 this->BinNumRange[3] = this->BinNumRange[2] + (2 * (this->Span - 2) * (this->Span - 2));
1348 assert(this->BinNumRange[3] == this->NumBins);
1349
1350 // Initialize the block face iteration process (for the general case).
1351 this->IFace.Initialize(level, center);
1352 this->JFace.Initialize(level, center);
1353 this->KFace.Initialize(level, center);
1354
1355 // Start in the center of the i-face.
1356 this->IFace.Begin(this->IJK);
1357 } // BeginBin
1358
1359 // Return false when iteration over the entire block is complete. We do not worry
1360 // at this point if the returned IJK is valid, this will be checked by the calling
1361 // Next() method.
1362 bool NextBin()
1363 {
1364 // Advance the current bin number
1365 this->BinNum++;
1366
1367 // Special case for level 0, or if visited all bins in the current level
1368 int level = this->Level;
1369 if (level == 0 || this->BinNum >= this->NumBins || level > this->MaxLevel)
1370 {
1371 return false;
1372 }
1373
1374 // Special case for levels 1-3
1375 if (level == 1)
1376 {
1377 this->IJK[0] = this->Center[0] + vtkBucketBlockLevel1[this->BinNum][0];
1378 this->IJK[1] = this->Center[1] + vtkBucketBlockLevel1[this->BinNum][1];
1379 this->IJK[2] = this->Center[2] + vtkBucketBlockLevel1[this->BinNum][2];
1380 return true;
1381 }
1382 else if (level == 2)
1383 {
1384 this->IJK[0] = this->Center[0] + vtkBucketBlockLevel2[this->BinNum][0];
1385 this->IJK[1] = this->Center[1] + vtkBucketBlockLevel2[this->BinNum][1];
1386 this->IJK[2] = this->Center[2] + vtkBucketBlockLevel2[this->BinNum][2];
1387 return true;
1388 }
1389 else if (level == 3)
1390 {
1391 this->IJK[0] = this->Center[0] + vtkBucketBlockLevel3[this->BinNum][0];
1392 this->IJK[1] = this->Center[1] + vtkBucketBlockLevel3[this->BinNum][1];
1393 this->IJK[2] = this->Center[2] + vtkBucketBlockLevel3[this->BinNum][2];
1394 return true;
1395 }
1396
1397 // Otherwise The general case with level>3: process the points on the
1398 // boundary of the block. We visit the -/+i, -/+j, -/+k planes in "round
1399 // robin" order, at the same time ping ponging from bins on the "-" plane
1400 // to opposite bins on the "+" plane.
1401
1402 // The total number of bins on i+j+k faces; and the i+j faces.
1403 vtkIdType nijk = 3 * (this->BinNumRange[3] - this->BinNumRange[2]);
1404 vtkIdType nij = 2 * (2 * this->BinNumRange[2] - this->BinNumRange[1] - this->BinNumRange[3]);
1405
1406 // Process the interior face bins on the +/- i,j, and k faces.
1407 // These are bins strictly interior to the i,j,k faces.
1408 const int binNum = this->BinNum;
1409 if (binNum < nijk)
1410 {
1411 const int idx = binNum % 6;
1412 switch (idx)
1413 {
1414 case 0: // The Begin() method has initialized IFace traversal
1415 this->IFace.Next(this->IJK);
1416 break;
1417 case 1:
1418 this->IJK[0] = this->Center[0] + level; // i-pong
1419 break;
1420 case 2:
1421 if (binNum == 2)
1422 {
1423 this->JFace.Begin(this->IJK); // Initialize JFace traversal
1424 }
1425 else
1426 {
1427 this->JFace.Next(this->IJK);
1428 }
1429 break;
1430 case 3:
1431 this->IJK[1] = this->Center[1] + level; // j-pong
1432 break;
1433 case 4:
1434 if (binNum == 4)
1435 {
1436 this->KFace.Begin(this->IJK); // Initialize KFace traversal
1437 }
1438 else
1439 {
1440 this->KFace.Next(this->IJK);
1441 }
1442 break;
1443 case 5:
1444 this->IJK[2] = this->Center[2] + level; // k-pong
1445 break;
1446 }
1447 }
1448
1449 // Process the remaining bins on +/- i and j faces. These
1450 // remaining faces are along i,j face edges.
1451 else if (binNum < (nij + nijk))
1452 {
1453 const int idx = binNum % 4;
1454 switch (idx)
1455 {
1456 case 0:
1457 this->IFace.Next(this->IJK);
1458 break;
1459 case 1:
1460 this->IJK[0] = this->Center[0] + level; // i-pong
1461 break;
1462 case 2:
1463 this->JFace.Next(this->IJK);
1464 break;
1465 case 3:
1466 this->IJK[1] = this->Center[1] + level; // j-pong
1467 break;
1468 }
1469 }
1470
1471 // Process the remaining bins on +/- i faces. These remaining bins
1472 // are along i face edges.
1473 else // if ( this->BinNum < (ni+nij+nijk) )
1474 {
1475 if (!(binNum % 2))
1476 {
1477 this->IFace.Next(this->IJK);
1478 }
1479 else
1480 {
1481 this->IJK[0] = this->Center[0] + level; // i-pong
1482 }
1483 }
1484 return true;
1485 } // NextBin
1486
1487 // Return a valid binId at the iteration starting point.
1488 vtkIdType Begin(int blockLevel, int blockCenter[3])
1489 {
1490 // Now iterate over this block
1491 this->BeginBin(blockLevel, blockCenter);
1492 while (!this->IsValid())
1493 {
1494 this->NextBin();
1495 }
1496 return this->GetBinId();
1497 }
1498
1499 // Return a valid binId, or a -1 if the iteration for this level is
1500 // complete. Note that the iteration occurs in a prescribed order, so
1501 // intermediate information is computed incrementally as needed.
1503 {
1504 // Get the next bin and check whether it is valid. If so,
1505 // return it. Otherwise, get the next valid bin.
1506 while (this->BinNum < this->NumBins)
1507 {
1508 if (!this->NextBin())
1509 {
1510 return (-1);
1511 }
1512 if (this->IsValid())
1513 {
1514 return this->GetBinId();
1515 }
1516 }
1517 return (-1); // end of bins at current level
1518 }
1519
1520}; // vtkBucketBlockIterator
1521
1522VTK_ABI_NAMESPACE_END
1523#endif // vtkStaticPointLocatorPrivate_h
1524// VTK-HeaderTest-Exclude: vtkStaticPointLocatorPrivate.h
int idx
Definition HexCnBasis.h:79
RealT r2
Definition PyrC2Basis.h:20
RealT t2
Definition PyrC2Basis.h:22
ValueType * GetPointer(vtkIdType valueIdx)
Get the address of a particular data index.
int GetNumberOfComponents() const
Set/Get the dimension (n) of the components.
static bool InsideSphere(const double min[3], const double max[3], const double center[3], double r2)
Determine if a box is fully inside a sphere.
object to represent cell connectivity
virtual double * GetTuple(vtkIdType tupleIdx)=0
Get the data tuple at tupleIdx.
abstract class to specify dataset behavior
Definition vtkDataSet.h:165
virtual double * GetPoint(vtkIdType ptId)=0
Get point coordinates with ptId such that: 0 <= ptId < NumberOfPoints.
dynamic, self-adjusting array of double
static vtkDoubleArray * SafeDownCast(vtkObjectBase *o)
list of point or cell ids
Definition vtkIdList.h:135
void SetNumberOfIds(vtkIdType number)
Specify the number of ids for this object to hold.
vtkIdType GetNumberOfIds() const noexcept
Return the number of id's in the list.
Definition vtkIdList.h:185
vtkIdType GetId(vtkIdType i)
Return the id at location i.
Definition vtkIdList.h:190
void SetId(vtkIdType i, vtkIdType id)
Set the id at location i.
Definition vtkIdList.h:222
vtkTypeBool Reserve(vtkIdType size)
Reserve the id list to the requested number of ids and preserve data.
virtual vtkDataSet * GetDataSet()
Build the locator from the points/cells defining this dataset.
static int Ceil(double x)
Rounds a double to the nearest integer not less than itself.
Definition vtkMath.h:1982
static int Floor(double x)
Rounds a double to the nearest integer not greater than itself.
Definition vtkMath.h:1973
represent and manipulate 3D points
Definition vtkPoints.h:140
concrete dataset represents vertices, lines, polygons, and triangle strips
Thread local storage for VTK objects.
T *& Local()
Returns an object local to the current thread.
Thread local storage for VTK objects.
T & Local()
This needs to be called mainly within a threaded execution path.
static void Sort(RandomAccessIterator begin, RandomAccessIterator end)
A convenience method for sorting data.
static void For(vtkIdType first, vtkIdType last, vtkIdType grain, Functor &f)
Execute a for operation in parallel.
quickly locate points in 3-space
virtual double * GetBounds()
Provide an accessor to the bounds.
virtual double * GetSpacing()
Provide an accessor to the bucket spacing.
virtual int * GetDivisions()
Set the number of divisions in x-y-z directions.
static void ComputePointStructuredCoords(vtkIdType ptId, const int dim[3], int ijk[3], int dataDescription=vtkStructuredData::VTK_STRUCTURED_EMPTY)
Given a pointId and grid dimensions 'dim', get the structured coordinates (i-j-k).
A SafeCastFromDouble(double value)
Cast from double, and clamp to output type limits to avoid overlow.
VTK_ITER_INLINE auto DataArrayTupleRange(const ArrayTypePtr &array, TupleIdType start=-1, TupleIdType end=-1) -> typename detail::SelectTupleRange< ArrayTypePtr, TupleSize >::type
Generate an stl and for-range compatible range of tuple iterators from a vtkDataArray.
MapDataSet(BucketList< T > *blist, vtkDataSet *ds)
void operator()(vtkIdType ptId, vtkIdType end)
void operator()(vtkIdType batch, vtkIdType batchEnd)
void operator()(TPointsArray *points, BucketList *blist)
void operator()(vtkIdType ptId, vtkIdType end)
MapPointsArray(BucketList< T > *blist, TPointsArray *pts)
void operator()(vtkIdType blockId, vtkIdType endBlockId)
MergeBinOrder(BucketList< T > *blist, double tol, vtkIdType *mergeMap)
vtkIdType GetCurrentBin(int blockId, int cIdx[3])
vtkSMPThreadLocalObject< vtkIdList > PIds
void MergePoint(vtkIdType ptId, vtkIdList *nearby)
MergeClose(BucketList< T > *blist, double tol, vtkIdType *mergeMap)
MergePointOrder(BucketList< T > *blist, double tol, vtkIdType *mergeMap)
MergePointsAndData(BucketList< T > *blist, vtkDataArray *da, vtkIdType *mergeMap)
bool TuplesEqual(int tupleSize, double *t1, double *t2)
vtkSMPThreadLocal< std::vector< double > > Tuple
void operator()(vtkIdType bucket, vtkIdType endBucket)
vtkSMPThreadLocal< std::vector< double > > Tuple2
MergePrecise(BucketList< T > *blist, vtkIdType *mergeMap)
void operator()(vtkIdType bucket, vtkIdType endBucket)
void GetIds(vtkIdType bucketNum, vtkIdList *bList)
BucketList(vtkStaticPointLocator *loc, vtkIdType numPts, int numBuckets)
void GetOverlappingBuckets(NeighborBuckets *buckets, const double x[3], double dist, int prevMinLevel[3], int prevMaxLevel[3])
double FindNPointsInShell(int N, const double x[3], vtkDist2TupleArray &results, double minDist2=(-0.1), bool sort=true, vtkDoubleArray *petals=nullptr)
void GenerateRepresentation(int level, vtkPolyData *pd)
void MergePointsWithData(vtkDataArray *data, vtkIdType *pointMap)
void FindPointsInShell(const double x[3], vtkDist2TupleArray &results, double minDist2, double maxDist2, bool sort, vtkDoubleArray *petals)
vtkIdType FindClosestPoint(const double x[3])
vtkIdType GetNumberOfIds(vtkIdType bucketNum)
void FindPointsWithinRadius(double R, const double x[3], vtkIdList *result)
vtkLocatorTuple< TIds > * Map
void BuildLocator() override
const vtkLocatorTuple< TIds > * GetIds(vtkIdType bucketNum)
vtkIdType FindClosestPointWithinRadius(double radius, const double x[3], double inputDataLength, double &dist2)
int IntersectWithLine(double a0[3], double a1[3], double tol, double &t, double lineX[3], double ptX[3], vtkIdType &ptId)
void GetOverlappingBuckets(NeighborBuckets *buckets, const double x[3], const int ijk[3], double dist, int level)
void MergePoints(double tol, vtkIdType *pointMap, int orderingMode)
void FindClosestNPoints(int N, const double x[3], vtkIdList *result)
Dispatch a single array against all array types mentioned in the ArrayList template parameter.
void Initialize(int blockLevel, int blockCenter[3])
vtkIdType Begin(int blockLevel, int blockCenter[3])
vtkBucketBlockFaceIterator JFace
vtkBucketBlockFaceIterator IFace
void Initialize(int divs[3], int maxLevel)
vtkBucketBlockFaceIterator KFace
void BeginBin(int level, int center[3])
void GenerateFace(int face, int i, int j, int k, vtkPoints *pts, vtkCellArray *polys)
vtkIdType GetBucketIndex(const double *x) const
virtual void BuildLocator()=0
void GetBucketNeighbors(NeighborBuckets *buckets, const int ijk[3], const int ndivs[3], int level)
vtkBucketList(vtkStaticPointLocator *loc, vtkIdType numPts, int numBuckets)
double Distance2ToBucket(const double x[3], const int nei[3])
virtual ~vtkBucketList()=default
vtkStaticPointLocator * Locator
double Distance2ToBounds(const double x[3], const double bounds[6])
bool BucketInsideSphere(int i, int j, int k, const double center[3], double r2)
void GetBucketIndices(const double *x, int ijk[3]) const
void GetBucketBounds(int i, int j, int k, double min[3], double max[3])
void GetBucketCenter(int i, int j, int k, double center[3])
Represent an array of vtkDist2Tuples.
#define vtkDataArray
STL-compatible iterable ranges that provide access to vtkDataArray elements.
constexpr int vtkBucketBlockLevel2[98][3]
constexpr int vtkBucketBlockLevel1[26][3]
constexpr int vtkBucketBlockLevel3[218][3]
int vtkIdType
Definition vtkType.h:363
#define max(a, b)