VTK  9.7.1
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
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"
28 #include "vtkStaticPointLocator.h"
29 #include "vtkStructuredData.h"
30 
31 VTK_ABI_NAMESPACE_BEGIN
32 
33 //------------------------------------------------------------------------------
34 // The following code supports threaded point locator construction. The locator
35 // is assumed to be constructed once (i.e., it does not allow incremental point
36 // insertion). The algorithm proceeds in three steps:
37 // 1) All points are assigned a bucket index (combined i-j-k bucket location).
38 // The index is computed in parallel. This requires a one time allocation of an
39 // index array (which is also associated with the originating point ids).
40 // 2) vtkSMPTools::Sort() is used to sort the index array. Note that the sort
41 // carries along the point ids as well. This creates contiguous runs of points
42 // all resident in the same bucket.
43 // 3) The bucket offsets are updated to refer to the right entry location into
44 // the sorted point ids array. This enables quick access, and an indirect count
45 // of the number of points in each bucket.
46 
47 // Forward declaration of lists of neighboring buckets.
48 struct NeighborBuckets;
49 
50 //------------------------------------------------------------------------------
51 // The bucketed points, including the sorted map. This is just a PIMPLd
52 // wrapper around the classes that do the real work.
54 {
56  vtkIdType NumPts; // the number of points to bucket
58  int BatchSize;
59 
60  // These are internal data members used for performance reasons
62  int Divisions[3];
63  double Bounds[6];
64  double H[3];
65  double hX, hY, hZ, hX2, hY2, hZ2;
66  double fX, fY, fZ, bX, bY, bZ;
68 
69  // Used for accelerated performance for certain methods
70  double* FastPoints; // fast path for accessing points
71  double BinRadius; // circumradius of a single bin/bucket
72  int MaxLevel; // the maximum possible level searches can proceed
73 
74  // Construction
75  vtkBucketList(vtkStaticPointLocator* loc, vtkIdType numPts, int numBuckets)
76  {
77  this->Locator = loc;
78  this->NumPts = numPts;
79  this->NumBuckets = numBuckets;
80  this->BatchSize = 10000; // building the offset array
81  this->DataSet = loc->GetDataSet();
82  loc->GetDivisions(this->Divisions);
83 
84  // Setup internal data members for more efficient processing.
85  double spacing[3], bounds[6];
86  loc->GetDivisions(this->Divisions);
87  loc->GetSpacing(spacing);
88  loc->GetBounds(bounds);
89  this->hX = this->H[0] = spacing[0];
90  this->hY = this->H[1] = spacing[1];
91  this->hZ = this->H[2] = spacing[2];
92  this->hX2 = this->hX / 2.0;
93  this->hY2 = this->hY / 2.0;
94  this->hZ2 = this->hZ / 2.0;
95  this->fX = 1.0 / spacing[0];
96  this->fY = 1.0 / spacing[1];
97  this->fZ = 1.0 / spacing[2];
98  this->bX = this->Bounds[0] = bounds[0];
99  this->Bounds[1] = bounds[1];
100  this->bY = this->Bounds[2] = bounds[2];
101  this->Bounds[3] = bounds[3];
102  this->bZ = this->Bounds[4] = bounds[4];
103  this->Bounds[5] = bounds[5];
104  this->xD = this->Divisions[0];
105  this->yD = this->Divisions[1];
106  this->zD = this->Divisions[2];
107  this->xyD = this->Divisions[0] * this->Divisions[1];
108 
109  this->FastPoints = nullptr;
110  this->BinRadius = sqrt(hX * hX + hY * hY + hZ * hZ) / 2.0;
111  this->MaxLevel = std::max({ this->xD, this->yD, this->zD });
112  }
113 
114  // Virtuals for templated subclasses
115  virtual ~vtkBucketList() = default;
116  virtual void BuildLocator() = 0;
117 
118  // place points in appropriate buckets
119  void GetBucketNeighbors(
120  NeighborBuckets* buckets, const int ijk[3], const int ndivs[3], int level);
121  void GenerateFace(int face, int i, int j, int k, vtkPoints* pts, vtkCellArray* polys);
122  double Distance2ToBucket(const double x[3], const int nei[3]);
123  double Distance2ToBounds(const double x[3], const double bounds[6]);
124 
125  //-----------------------------------------------------------------------------
126  // Inlined for performance. These function invocations must be called after
127  // BuildLocator() is invoked, otherwise the output is indeterminate.
128  void GetBucketIndices(const double* x, int ijk[3]) const
129  {
130  // Compute point index. SafeCastFromDouble clamps to the integer type limits
131  // (mapping NaN to 0) so casting a coordinate far outside the locator bounds
132  // (e.g. VTK_DOUBLE_MAX) is not undefined behavior. Make sure it then lies
133  // within the range of the locator.
134  vtkIdType tmp0 = vtkMathUtilities::SafeCastFromDouble<vtkIdType>((x[0] - bX) * fX);
135  vtkIdType tmp1 = vtkMathUtilities::SafeCastFromDouble<vtkIdType>((x[1] - bY) * fY);
136  vtkIdType tmp2 = vtkMathUtilities::SafeCastFromDouble<vtkIdType>((x[2] - bZ) * fZ);
137 
138  ijk[0] = tmp0 < 0 ? 0 : std::min(xD - 1, tmp0);
139  ijk[1] = tmp1 < 0 ? 0 : std::min(yD - 1, tmp1);
140  ijk[2] = tmp2 < 0 ? 0 : std::min(zD - 1, tmp2);
141  }
142 
143  //-----------------------------------------------------------------------------
144  vtkIdType GetBucketIndex(const double* x) const
145  {
146  int ijk[3];
147  this->GetBucketIndices(x, ijk);
148  return ijk[0] + ijk[1] * xD + ijk[2] * xyD;
149  }
150 
151  //-----------------------------------------------------------------------------
152  // Return the center of the bucket/bin at (i,j,k).
153  void GetBucketCenter(int i, int j, int k, double center[3])
154  {
155  center[0] = this->bX + this->hX2 + i * this->hX;
156  center[1] = this->bY + this->hY2 + j * this->hY;
157  center[2] = this->bZ + this->hZ2 + k * this->hZ;
158  }
159 
160  //-----------------------------------------------------------------------------
161  // Return the bounding box (min,max) of a specified bucket (i,j,k).
162  void GetBucketBounds(int i, int j, int k, double min[3], double max[3])
163  {
164  min[0] = this->bX + i * this->hX;
165  min[1] = this->bY + j * this->hY;
166  min[2] = this->bZ + k * this->hZ;
167  max[0] = min[0] + this->hX;
168  max[1] = min[1] + this->hY;
169  max[2] = min[2] + this->hZ;
170  }
171 
172  //-----------------------------------------------------------------------------
173  // Determine whether a bin/bucket specified by i,j,k is completely contained
174  // inside the sphere (center,r2). Return true if contained; false otherwise.
175  bool BucketInsideSphere(int i, int j, int k, const double center[3], double r2)
176  {
177  double min[3], max[3];
178  min[0] = this->bX + i * this->hX;
179  min[1] = this->bY + j * this->hY;
180  min[2] = this->bZ + k * this->hZ;
181  max[0] = min[0] + this->hX;
182  max[1] = min[1] + this->hY;
183  max[2] = min[2] + this->hZ;
184  return vtkBoundingBox::InsideSphere(min, max, center, r2);
185  }
186 }; // vtkBucketList
187 
188 //------------------------------------------------------------------------------
189 // This templates class manages the creation of the static locator
190 // structures. It also implements the operator() functors which are supplied
191 // to vtkSMPTools for threaded processesing.
192 template <typename TIds>
193 struct BucketList : public vtkBucketList
194 {
195  // Okay the various ivars
196  vtkLocatorTuple<TIds>* Map; // the map to be sorted
197  TIds* Offsets; // offsets for each bucket into the map
198 
199  // Construction
200  BucketList(vtkStaticPointLocator* loc, vtkIdType numPts, int numBuckets)
201  : vtkBucketList(loc, numPts, numBuckets)
202  {
203  // one extra to simplify traversal
204  this->Map = new vtkLocatorTuple<TIds>[numPts + 1];
205  this->Map[numPts].Bucket = numBuckets;
206  this->Offsets = new TIds[numBuckets + 1];
207  this->Offsets[numBuckets] = numPts;
208  }
209 
210  // Release allocated memory
211  ~BucketList() override
212  {
213  delete[] this->Map;
214  delete[] this->Offsets;
215  }
216 
217  // The number of point ids in a bucket is determined by computing the
218  // difference between the offsets into the sorted points array.
220  {
221  return (this->Offsets[bucketNum + 1] - this->Offsets[bucketNum]);
222  }
223 
224  // Given a bucket number, return the point ids in that bucket.
226  {
227  return this->Map + this->Offsets[bucketNum];
228  }
229 
230  // Given a bucket number, return the point ids in that bucket.
231  void GetIds(vtkIdType bucketNum, vtkIdList* bList)
232  {
233  const vtkLocatorTuple<TIds>* ids = this->GetIds(bucketNum);
234  vtkIdType numIds = this->GetNumberOfIds(bucketNum);
235  bList->SetNumberOfIds(numIds);
236  for (int i = 0; i < numIds; i++)
237  {
238  bList->SetId(i, ids[i].PtId);
239  }
240  }
241 
242  // Templated implementations of the locator
243  vtkIdType FindClosestPoint(const double x[3]);
245  double radius, const double x[3], double inputDataLength, double& dist2);
246  void FindClosestNPoints(int N, const double x[3], vtkIdList* result);
247  double FindNPointsInShell(int N, const double x[3], vtkDist2TupleArray& results,
248  double minDist2 = (-0.1), bool sort = true, vtkDoubleArray* petals = nullptr);
249  void FindPointsWithinRadius(double R, const double x[3], vtkIdList* result);
250  int IntersectWithLine(double a0[3], double a1[3], double tol, double& t, double lineX[3],
251  double ptX[3], vtkIdType& ptId);
252  void MergePoints(double tol, vtkIdType* pointMap, int orderingMode);
254  void GenerateRepresentation(int vtkNotUsed(level), vtkPolyData* pd);
255 
256  // Internal methods
258  NeighborBuckets* buckets, const double x[3], const int ijk[3], double dist, int level);
259  void GetOverlappingBuckets(NeighborBuckets* buckets, const double x[3], double dist,
260  int prevMinLevel[3], int prevMaxLevel[3]);
261 
262  // Implicit point representation, slower path
263  template <typename T>
264  struct MapDataSet
265  {
268 
270  : BList(blist)
271  , DataSet(ds)
272  {
273  }
274 
276  {
277  double p[3];
278  vtkLocatorTuple<T>* t = this->BList->Map + ptId;
279 
280  for (; ptId < end; ++ptId, ++t)
281  {
282  this->DataSet->GetPoint(ptId, p);
283  t->Bucket = this->BList->GetBucketIndex(p);
284  t->PtId = ptId;
285  } // for all points in this batch
286  }
287  };
288 
289  template <typename T, typename TPointsArray>
291  {
293  TPointsArray* Points;
294 
295  MapPointsArray(BucketList<T>* blist, TPointsArray* pts)
296  : BList(blist)
297  , Points(pts)
298  {
299  }
300 
302  {
303  double p[3];
304  auto x = vtk::DataArrayTupleRange<3>(this->Points, ptId, end).begin();
305  vtkLocatorTuple<T>* t = this->BList->Map + ptId;
306 
307  for (; ptId < end; ++ptId, ++x, ++t)
308  {
309  x->GetTuple(p);
310  t->Bucket = this->BList->GetBucketIndex(p);
311  t->PtId = ptId;
312  } // for all points in this batch
313  }
314  };
315 
317  {
318  template <typename TPointsArray>
319  void operator()(TPointsArray* points, BucketList* blist)
320  {
321  MapPointsArray<TIds, TPointsArray> mapper(blist, points);
322  vtkSMPTools::For(0, blist->NumPts, mapper);
323  }
324  };
325 
326  // A clever way to build offsets in parallel. Basically each thread builds
327  // offsets across a range of the sorted map. Recall that offsets are an
328  // integral value referring to the locations of the sorted points that
329  // reside in each bucket.
330  template <typename T>
331  struct MapOffsets
332  {
336 
338  : BList(blist)
339  {
340  this->NumBuckets = this->BList->NumBuckets;
341  this->NumPts = this->BList->NumPts;
342  }
343 
344  // Traverse sorted points (i.e., tuples) and update bucket offsets.
345  void operator()(vtkIdType batch, vtkIdType batchEnd)
346  {
347  T* offsets = this->BList->Offsets;
348  const vtkLocatorTuple<T>* curPt = this->BList->Map + batch * this->BList->BatchSize;
349  const vtkLocatorTuple<T>* endBatchPt = this->BList->Map + batchEnd * this->BList->BatchSize;
350  const vtkLocatorTuple<T>* endPt = this->BList->Map + this->NumPts;
351  const vtkLocatorTuple<T>* prevPt;
352  endBatchPt = (endBatchPt > endPt ? endPt : endBatchPt);
353 
354  // Special case at the very beginning of the mapped points array. If
355  // the first point is in bucket# N, then all buckets up and including
356  // N must refer to the first point.
357  if (curPt == this->BList->Map)
358  {
359  prevPt = this->BList->Map;
360  std::fill_n(offsets, curPt->Bucket + 1, 0); // point to the first points
361  } // at the very beginning of the map (sorted points array)
362 
363  // We are entering this functor somewhere in the interior of the
364  // mapped points array. All we need to do is point to the entry
365  // position because we are interested only in prevPt->Bucket.
366  else
367  {
368  prevPt = curPt;
369  } // else in the middle of a batch
370 
371  // Okay we have a starting point for a bucket run. Now we can begin
372  // filling in the offsets in this batch. A previous thread should
373  // have/will have completed the previous and subsequent runs outside
374  // of the [batch,batchEnd) range
375  for (curPt = prevPt; curPt < endBatchPt;)
376  {
377  for (; curPt->Bucket == prevPt->Bucket && curPt <= endBatchPt; ++curPt)
378  {
379  // advance
380  }
381  // Fill in any gaps in the offset array
382  std::fill_n(
383  offsets + prevPt->Bucket + 1, curPt->Bucket - prevPt->Bucket, curPt - this->BList->Map);
384  prevPt = curPt;
385  } // for all batches in this range
386  } // operator()
387  };
388 
389  // Merge points that are pecisely coincident. Operates in parallel on
390  // locator buckets. Does not need to check neighbor buckets.
391  template <typename T>
393  {
397 
399  : BList(blist)
400  , MergeMap(mergeMap)
401  {
402  this->DataSet = blist->DataSet;
403  }
404 
405  void operator()(vtkIdType bucket, vtkIdType endBucket)
406  {
407  BucketList<T>* bList = this->BList;
408  vtkIdType* mergeMap = this->MergeMap;
409  int i, j;
410  const vtkLocatorTuple<TIds>* ids;
411  double p[3], p2[3];
412  vtkIdType ptId, ptId2, numIds;
413 
414  for (; bucket < endBucket; ++bucket)
415  {
416  if ((numIds = bList->GetNumberOfIds(bucket)) > 0)
417  {
418  ids = bList->GetIds(bucket);
419  for (i = 0; i < numIds; i++)
420  {
421  ptId = ids[i].PtId;
422  if (mergeMap[ptId] < 0)
423  {
424  mergeMap[ptId] = ptId;
425  this->DataSet->GetPoint(ptId, p);
426  for (j = i + 1; j < numIds; j++)
427  {
428  ptId2 = ids[j].PtId;
429  if (mergeMap[ptId2] < 0)
430  {
431  this->DataSet->GetPoint(ptId2, p2);
432  if (p[0] == p2[0] && p[1] == p2[1] && p[2] == p2[2])
433  {
434  mergeMap[ptId2] = ptId;
435  }
436  }
437  }
438  } // if point not yet visited
439  }
440  }
441  }
442  }
443  };
444 
445  // Merge points that are coincident within a specified tolerance. Depending
446  // on the orderingMode, either a serialized ordering process is used (i.e.,
447  // POINT_ORDER) or a threaded ordering process is used (i.e.,
448  // BIN_ORDER). Note that due to the tolerance, the merging tolerance
449  // needs to check neighbor buckets which slows the algorithm down
450  // considerably. Note that merging is in one direction: larger ids are
451  // merged to lower ids.
452  template <typename T>
453  struct MergeClose
454  {
458  double Tol;
459 
461 
462  MergeClose(BucketList<T>* blist, double tol, vtkIdType* mergeMap)
463  : BList(blist)
464  , MergeMap(mergeMap)
465  , Tol(tol)
466  {
467  this->DataSet = blist->DataSet;
468  }
469 
470  // The core merging process around the point ptId.
471  void MergePoint(vtkIdType ptId, vtkIdList* nearby)
472  {
473  vtkIdType* mergeMap = this->MergeMap;
474 
475  // Make sure the point is not already merged
476  if (mergeMap[ptId] < 0)
477  {
478  mergeMap[ptId] = ptId;
479  double p[3];
480  this->DataSet->GetPoint(ptId, p);
481  this->BList->FindPointsWithinRadius(this->Tol, p, nearby);
482  vtkIdType numIds = nearby->GetNumberOfIds();
483  if (numIds > 0)
484  {
485  for (auto i = 0; i < numIds; ++i)
486  {
487  vtkIdType nearId = nearby->GetId(i);
488  if (mergeMap[nearId] < 0)
489  {
490  mergeMap[nearId] = ptId;
491  } // if eligible for merging and not yet merged
492  } // for all nearby points
493  } // if nearby points exist
494  } // if point not yet merged
495  } // MergePoint
496 
497  // Just allocate a little bit of memory to get started.
498  void Initialize()
499  {
500  vtkIdList*& pIds = this->PIds.Local();
501  pIds->Reserve(128); // allocate some memory
502  }
503 
504  void Reduce() {}
505  };
506 
507  // Merge points with non-zero tolerance. Order of point merging guarantees
508  // that any two merged point ids (p0,p1) are such that p0<p1. Consequently
509  // this is a completely serial algorithm.
510  template <typename T>
511  struct MergePointOrder : public MergeClose<T>
512  {
513  MergePointOrder(BucketList<T>* blist, double tol, vtkIdType* mergeMap)
514  : MergeClose<T>(blist, tol, mergeMap)
515  {
516  }
517 
519 
520  // Process serially, point by point.
521  void operator()(vtkIdType numPts)
522  {
523  vtkIdList*& nearby = this->PIds.Local();
524 
525  // Serial operation over all points in the locator.
526  for (vtkIdType ptId = 0; ptId < numPts; ++ptId)
527  {
528  this->MergePoint(ptId, nearby);
529  } // for all points in the locator
530  } // operator()
531 
532  void Reduce() { this->MergeClose<T>::Reduce(); }
533  }; // Merge points in point ordering
534 
535  // Merge points with non-zero tolerance. The order of point merging depends
536  // on the order in which the bins are traversed (using a checkerboard
537  // pattern). While the algorithm is threaded, the checkerboarding acts as
538  // a barrier to full threading so the performance is not optimal (but at
539  // least deterministic / reproducible).
540  //
541  // Checkerboarding works as follows. The locator bin volume of dimensions
542  // Divisions[3] is divided into a collection of "blocks" which are
543  // subvolumes of bins of dimensions d^3. The algorithm makes multiple,
544  // threaded passes over the blocks (a total of d^3 threaded traversals),
545  // choosing one of the bins in each block to process via the current
546  // checkerboard index. The dimension d of the blocks is determined by the
547  // tolerance and locator bin size, and is chosen in such a way as to
548  // separate the point merging computation so as to avoid threading data
549  // races / write contention.
550  template <typename T>
551  struct MergeBinOrder : public MergeClose<T>
552  {
553  int CheckerboardDimension; // the dimension of the checkerboard block/subvolume
554  int NumBlocks; // how many blocks/subvolumes are in the binned locator
555  int BlockDims[3]; // the number of blocks in each coordinate direction
556  int CheckerboardIndex[3]; // which bin is being processed in the blocks
557 
558  // The main function of the constructor is the setup the checkerboard
559  // traversal. This means configuring the checkerboard subvolume, and
560  // set up the traversal indices.
561  MergeBinOrder(BucketList<T>* blist, double tol, vtkIdType* mergeMap)
562  : MergeClose<T>(blist, tol, mergeMap)
563  {
564  BucketList<T>* bl = this->BList;
565  double hMin = std::min({ bl->hX, bl->hY, bl->hZ });
566  this->CheckerboardDimension =
567  1 + (hMin == 0.0 ? 1 : (1 + vtkMath::Floor(tol / (hMin / 2.0))));
568 
569  // Determine how many blocks there are in the locater, and determine the
570  // dimensions of the blocks.
571  this->NumBlocks = 1;
572  for (auto i = 0; i < 3; ++i)
573  {
574  double numBlocks =
575  static_cast<double>(bl->Divisions[i]) / static_cast<double>(this->CheckerboardDimension);
576  this->BlockDims[i] = (bl->Divisions[i] <= 1 ? 1 : vtkMath::Ceil(numBlocks));
577  this->NumBlocks *= this->BlockDims[i];
578  }
580  }
581 
582  // Initialize the checkerboard traversal process. A pointer to the
583  // current traversal state (within the checkerboard region) is returned.
585  {
586  // Control checkerboard traversal
587  this->CheckerboardIndex[0] = 0;
588  this->CheckerboardIndex[1] = 0;
589  this->CheckerboardIndex[2] = 0;
590  return this->CheckerboardIndex;
591  }
592 
593  // Given a blockId and the current checkerboard index, compute the
594  // current locator bin/bucket id. May return <0 if no bin exists.
595  vtkIdType GetCurrentBin(int blockId, int cIdx[3])
596  {
597  // Which checkerboard block are we in?
598  int ijk[3];
599  vtkStructuredData::ComputePointStructuredCoords(blockId, this->BlockDims, ijk);
600 
601  // Combine the block index with the checkerboard index. Make sure that
602  // we are still inside the locator bins (partial blocks may exist at
603  // the boundary). Recall that the blocks are composed of d^3 bins.
604  for (auto i = 0; i < 3; ++i)
605  {
606  ijk[i] = ijk[i] * this->CheckerboardDimension + cIdx[i];
607  if (ijk[i] >= this->BList->Divisions[i])
608  {
609  return (-1);
610  }
611  }
612 
613  // Okay return the bin index
614  return (ijk[0] + ijk[1] * this->BList->Divisions[0] +
615  ijk[2] * this->BList->Divisions[0] * this->BList->Divisions[1]);
616  }
617 
619 
620  // Process locator blocks/subvolumes.
621  void operator()(vtkIdType blockId, vtkIdType endBlockId)
622  {
623  vtkIdList*& nearby = this->PIds.Local();
624  for (; blockId < endBlockId; ++blockId)
625  {
626  vtkIdType bin = this->GetCurrentBin(blockId, this->CheckerboardIndex);
627  vtkIdType numIds;
628 
629  if (bin >= 0 && (numIds = this->BList->GetNumberOfIds(bin)) > 0)
630  {
631  const vtkLocatorTuple<TIds>* ids = this->BList->GetIds(bin);
632  for (auto i = 0; i < numIds; ++i)
633  {
634  vtkIdType ptId = ids[i].PtId;
635  this->MergePoint(ptId, nearby);
636  } // for all points in bin/bucket
637  } // if points exist in bin/bucket
638  } // for all blocks
639  } // operator()
640 
641  void Reduce() { this->MergeClose<T>::Reduce(); }
642 
643  // Coordinate the checkerboard threading process. Checkerboarding simply
644  // processes a subset of the locator bins to avoid write contention. The
645  // checkerboard footprint (its subvolume size) is a function of the
646  // tolerance, and is effectively a d^3 subvolume that is traversed
647  // (across all subvolumes) in a synchronized fashion. Hence there are d^3
648  // separate SMP traversals - if d becomes too large, the fallback is
649  // simply a serial (MergePointOrder()) to avoid thread thrashing.
650  void Execute()
651  {
652  int cDim = this->CheckerboardDimension;
653  int* cIdx = this->InitializeCheckerboardIndex();
654 
655  // Coordinate the checkerboarding by synchronized traversal of the
656  // the checkerboard subblocks.
657  for (cIdx[2] = 0; cIdx[2] < cDim; ++cIdx[2])
658  {
659  for (cIdx[1] = 0; cIdx[1] < cDim; ++cIdx[1])
660  {
661  for (cIdx[0] = 0; cIdx[0] < cDim; ++cIdx[0])
662  {
663  vtkSMPTools::For(0, this->NumBlocks, *this);
664  }
665  }
666  }
667  } // Execute()
668 
669  }; // MergeBinOrder
670 
671  // Merge points that are geometrically coincident and have matching data
672  // values. Operates in parallel on locator buckets. Does not need to check
673  // neighbor buckets.
674  template <typename T>
676  {
683 
685  : BList(blist)
686  , DataArray(da)
687  , MergeMap(mergeMap)
688  {
689  this->DataSet = blist->DataSet;
690  }
691 
692  bool TuplesEqual(int tupleSize, double* t1, double* t2)
693  {
694  for (auto i = 0; i < tupleSize; ++i)
695  {
696  if (t1[i] != t2[i])
697  {
698  return false;
699  }
700  }
701  return true;
702  }
703 
704  void Initialize()
705  {
706  vtkIdType numComp = this->DataArray->GetNumberOfComponents();
707  this->Tuple.Local().resize(numComp);
708  this->Tuple2.Local().resize(numComp);
709  }
710 
711  void operator()(vtkIdType bucket, vtkIdType endBucket)
712  {
713  BucketList<T>* bList = this->BList;
714  vtkIdType* mergeMap = this->MergeMap;
715  int i, j;
716  const vtkLocatorTuple<TIds>* ids;
717  double p[3], p2[3];
718  vtkIdType ptId, ptId2, numIds;
719  int tupleSize = static_cast<int>(this->Tuple.Local().size());
720  double* t = this->Tuple.Local().data();
721  double* t2 = this->Tuple2.Local().data();
722 
723  for (; bucket < endBucket; ++bucket)
724  {
725  if ((numIds = bList->GetNumberOfIds(bucket)) > 0)
726  {
727  ids = bList->GetIds(bucket);
728  for (i = 0; i < numIds; i++)
729  {
730  ptId = ids[i].PtId;
731  if (mergeMap[ptId] < 0)
732  {
733  mergeMap[ptId] = ptId;
734  this->DataSet->GetPoint(ptId, p);
735  this->DataArray->GetTuple(ptId, t);
736  for (j = i + 1; j < numIds; j++)
737  {
738  ptId2 = ids[j].PtId;
739  if (mergeMap[ptId2] < 0)
740  {
741  this->DataSet->GetPoint(ptId2, p2);
742  if (p[0] == p2[0] && p[1] == p2[1] && p[2] == p2[2])
743  {
744  this->DataArray->GetTuple(ptId2, t2);
745  if (this->TuplesEqual(tupleSize, t, t2))
746  {
747  mergeMap[ptId2] = ptId;
748  } // if point's data match
749  } // if points geometrically coincident
750  } // if point not yet visited
751  } // for the remaining points in the bin
752  } // if point not yet merged
753  } // for all points in bucket
754  } // if bucket contains points
755  } // for all buckets
756  } // operator()
757 
758  void Reduce() {}
759  }; // MergePointsWithData
760 
761  // Build the map and other structures to support locator operations
762  void BuildLocator() override
763  {
764  // Place each point in a bucket
765  auto points = this->DataSet->GetPoints()->GetData();
766  MapPointsArrayWorker worker;
768  points, worker, this))
769  {
770  worker(points, this);
771  }
772 
773  // Provide accelerated access to points. Needed for Voronoi bin iterators.
775  {
776  this->FastPoints = static_cast<double*>(vtkDoubleArray::SafeDownCast(points)->GetPointer(0));
777  }
778 
779  // Now group the points into contiguous runs within buckets (recall that
780  // sorting is occurring based on bin/bucket id).
781  vtkSMPTools::Sort(this->Map, this->Map + this->NumPts);
782 
783  // Build the offsets into the Map. The offsets are the positions of
784  // each bucket into the sorted list. They mark the beginning of the
785  // list of points in each bucket. Amazingly, this can be done in
786  // parallel.
787  int numBatches = static_cast<int>(ceil(static_cast<double>(this->NumPts) / this->BatchSize));
788  MapOffsets<TIds> offMapper(this);
789  vtkSMPTools::For(0, numBatches, offMapper);
790  }
791 };
792 
793 VTK_ABI_NAMESPACE_END
794 #endif // vtkStaticPointLocatorPrivate_h
795 // VTK-HeaderTest-Exclude: vtkStaticPointLocatorPrivate.h
RealT r2
Definition: PyrC2Basis.h:20
vtkIdType GetNumberOfIds(vtkIdType bucketNum)
T & Local()
This needs to be called mainly within a threaded execution path.
void MergePoints(double tol, vtkIdType *pointMap, int orderingMode)
vtkIdType FindClosestPoint(const double x[3])
void GetOverlappingBuckets(NeighborBuckets *buckets, const double x[3], const int ijk[3], double dist, int level)
virtual double * GetSpacing()
Provide an accessor to the bucket spacing.
vtkLocatorTuple< TIds > * Map
virtual vtkPoints * GetPoints()
If the subclass has (implicit/explicit) points, then return them.
bool BucketInsideSphere(int i, int j, int k, const double center[3], double r2)
MapPointsArray(BucketList< T > *blist, TPointsArray *pts)
void GetIds(vtkIdType bucketNum, vtkIdList *bList)
virtual ~vtkBucketList()=default
bool TuplesEqual(int tupleSize, double *t1, double *t2)
const vtkLocatorTuple< TIds > * GetIds(vtkIdType bucketNum)
int GetNumberOfComponents() const
Set/Get the dimension (n) of the components.
MergePointOrder(BucketList< T > *blist, double tol, vtkIdType *mergeMap)
abstract class to specify dataset behavior
Definition: vtkDataSet.h:56
ValueType * GetPointer(vtkIdType valueIdx)
Get the address of a particular data index.
virtual double * GetTuple(vtkIdType tupleIdx)=0
Get the data tuple at tupleIdx.
double Distance2ToBounds(const double x[3], const double bounds[6])
MapOffsets(BucketList< T > *blist)
void FindClosestNPoints(int N, const double x[3], vtkIdList *result)
virtual double * GetBounds()
Provide an accessor to the bounds.
void FindPointsWithinRadius(double R, const double x[3], vtkIdList *result)
void GenerateRepresentation(int level, vtkPolyData *pd)
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).
void operator()(vtkIdType bucket, vtkIdType endBucket)
void GetBucketBounds(int i, int j, int k, double min[3], double max[3])
MergePointsAndData(BucketList< T > *blist, vtkDataArray *da, vtkIdType *mergeMap)
double FindNPointsInShell(int N, const double x[3], vtkDist2TupleArray &results, double minDist2=(-0.1), bool sort=true, vtkDoubleArray *petals=nullptr)
int vtkIdType
Definition: vtkType.h:363
concrete dataset represents vertices, lines, polygons, and triangle strips
Definition: vtkPolyData.h:71
vtkIdType FindClosestPointWithinRadius(double radius, const double x[3], double inputDataLength, double &dist2)
vtkBucketList(vtkStaticPointLocator *loc, vtkIdType numPts, int numBuckets)
int IntersectWithLine(double a0[3], double a1[3], double tol, double &t, double lineX[3], double ptX[3], vtkIdType &ptId)
void operator()(vtkIdType blockId, vtkIdType endBlockId)
static vtkDoubleArray * SafeDownCast(vtkObjectBase *o)
dynamic, self-adjusting array of double
MapDataSet(BucketList< T > *blist, vtkDataSet *ds)
virtual void BuildLocator()=0
MergeBinOrder(BucketList< T > *blist, double tol, vtkIdType *mergeMap)
vtkIdType GetId(vtkIdType i)
Return the id at location i.
Definition: vtkIdList.h:81
void SetId(vtkIdType i, vtkIdType id)
Set the id at location i.
Definition: vtkIdList.h:113
void operator()(vtkIdType ptId, vtkIdType end)
virtual vtkDataSet * GetDataSet()
Build the locator from the points/cells defining this dataset.
void GetBucketNeighbors(NeighborBuckets *buckets, const int ijk[3], const int ndivs[3], int level)
double Distance2ToBucket(const double x[3], const int nei[3])
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.
void MergePointsWithData(vtkDataArray *data, vtkIdType *pointMap)
list of point or cell ids
Definition: vtkIdList.h:25
static int Floor(double x)
Rounds a double to the nearest integer not greater than itself.
Definition: vtkMath.h:1833
abstract superclass for arrays of numeric data
Definition: vtkDataArray.h:44
static void Sort(RandomAccessIterator begin, RandomAccessIterator end)
A convenience method for sorting data.
Definition: vtkSMPTools.h:615
void MergePoint(vtkIdType ptId, vtkIdList *nearby)
void BuildLocator() override
vtkIdType GetCurrentBin(int blockId, int cIdx[3])
Thread local storage for VTK objects.
vtkIdType GetNumberOfIds() const noexcept
Return the number of id's in the list.
Definition: vtkIdList.h:76
virtual int * GetDivisions()
Set the number of divisions in x-y-z directions.
void GetBucketIndices(const double *x, int ijk[3]) const
static void For(vtkIdType first, vtkIdType last, vtkIdType grain, Functor &f)
Execute a for operation in parallel.
Definition: vtkSMPTools.h:238
vtkIdType GetBucketIndex(const double *x) const
quickly locate points in 3-space
vtkTypeBool Reserve(vtkIdType size)
Reserve the id list to the requested number of ids and preserve data.
static int Ceil(double x)
Rounds a double to the nearest integer not less than itself.
Definition: vtkMath.h:1842
void operator()(vtkIdType bucket, vtkIdType endBucket)
void GenerateFace(int face, int i, int j, int k, vtkPoints *pts, vtkCellArray *polys)
object to represent cell connectivity
Definition: vtkCellArray.h:177
void GetBucketCenter(int i, int j, int k, double center[3])
MergeClose(BucketList< T > *blist, double tol, vtkIdType *mergeMap)
BucketList(vtkStaticPointLocator *loc, vtkIdType numPts, int numBuckets)
Represent an array of vtkDist2Tuples.
vtkSMPThreadLocal< std::vector< double > > Tuple
MergePrecise(BucketList< T > *blist, vtkIdType *mergeMap)
vtkSMPThreadLocalObject< vtkIdList > PIds
vtkSMPThreadLocal< std::vector< double > > Tuple2
Private declarations for 3D binned spatial locator.
virtual double * GetPoint(vtkIdType ptId)=0
Get point coordinates with ptId such that: 0 <= ptId < NumberOfPoints.
void operator()(vtkIdType batch, vtkIdType batchEnd)
vtkDataArray * GetData()
Definition: vtkPoints.h:60
T *& Local()
Returns an object local to the current thread.
vtkStaticPointLocator * Locator
RealT t2
Definition: PyrC2Basis.h:22
void SetNumberOfIds(vtkIdType number)
Specify the number of ids for this object to hold.
#define max(a, b)
represent and manipulate 3D points
Definition: vtkPoints.h:30
Dispatch a single array against all array types mentioned in the ArrayList template parameter...
STL-compatible iterable ranges that provide access to vtkDataArray elements.
void operator()(TPointsArray *points, BucketList *blist)
void operator()(vtkIdType ptId, vtkIdType end)