VTK  9.6.20251231
vtkStaticPointLocator2DPrivate.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 vtkStaticPointLocator2DPrivate_h
14#define vtkStaticPointLocator2DPrivate_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 "vtkPoints.h"
25#include "vtkSMPTools.h"
27#include "vtkStructuredData.h"
28
29VTK_ABI_NAMESPACE_BEGIN
30
31#define Distance2BetweenPoints2D(p1, p2) \
32 ((p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]))
33
39inline bool IntersectsCircle(
40 const double min[2], const double max[2], const double center[2], double r2)
41{
42 double d2 = 0.0;
43 for (int i = 0; i < 2; ++i)
44 {
45 if (center[i] < min[i])
46 {
47 d2 += (center[i] - min[i]) * (center[i] - min[i]);
48 }
49 else if (center[i] > max[i])
50 {
51 d2 += (center[i] - max[i]) * (center[i] - max[i]);
52 }
53 }
54 return (d2 <= r2);
55}
56
62inline bool InsideCircle(
63 const double min[2], const double max[2], const double center[2], double r2)
64{
65 double dmin = 0.0, dmax = 0.0;
66 for (int i = 0; i < 2; ++i)
67 {
68 double a = (center[i] - min[i]) * (center[i] - min[i]);
69 double b = (center[i] - max[i]) * (center[i] - max[i]);
70 dmax += std::max(a, b);
71 if (min[i] <= center[i] && center[i] <= max[i])
72 {
73 dmin += std::min(a, b);
74 }
75 }
76 return (!(dmin <= r2 && r2 <= dmax));
77}
78
79//------------------------------------------------------------------------------
80// The following code supports threaded point locator construction. The locator
81// is assumed to be constructed once (i.e., it does not allow incremental point
82// insertion). The algorithm proceeds in three steps:
83// 1) All points are assigned a bucket index (combined i-j bucket location).
84// The index is computed in parallel. This requires a one time allocation of an
85// index array (which is also associated with the originating point ids).
86// 2) vtkSMPTools::Sort() is used to sort the index array. Note that the sort
87// carries along the point ids as well. This creates contiguous runs of points
88// all resident in the same bucket.
89// 3) The bucket offsets are updated to refer to the right entry location into
90// the sorted point ids array. This enables quick access, and an indirect count
91// of the number of points in each bucket.
92
93struct NeighborBuckets2D;
94
95//------------------------------------------------------------------------------
96// The bucketed points, including the sorted map. This is just a PIMPLd
97// wrapper around the classes that do the real work.
99{
101 vtkIdType NumPts; // the number of points to bucket
104
105 // These are internal data members used for performance reasons
107 int Divisions[3];
108 double Bounds[6];
109 double H[3];
110 double hX, hY, hX2, hY2;
111 double fX, fY, bX, bY;
113
114 // Used for accelerated performance for certain methods
115 double* FastPoints; // fast path for accessing points
116 double BinRadius; // circumradius of a single bin/bucket
117 int MaxLevel; // the maximum possible level searches can proceed
118
119 // Construction
121 {
122 this->Locator = loc;
123 this->NumPts = numPts;
124 this->NumBuckets = numBuckets;
125 this->BatchSize = 10000; // building the offset array
126 this->DataSet = loc->GetDataSet();
127
128 // Setup internal data members for more efficient processing. Remember this is
129 // a 2D locator so just processing (x,y) points.
130 double spacing[3], bounds[6];
131 loc->GetDivisions(this->Divisions);
132 loc->GetSpacing(spacing);
133 loc->GetBounds(bounds);
134 this->hX = this->H[0] = spacing[0];
135 this->hY = this->H[1] = spacing[1];
136 this->hX2 = this->hX / 2.0;
137 this->hY2 = this->hY / 2.0;
138 this->fX = 1.0 / spacing[0];
139 this->fY = 1.0 / spacing[1];
140 this->bX = this->Bounds[0] = bounds[0];
141 this->Bounds[1] = bounds[1];
142 this->bY = this->Bounds[2] = bounds[2];
143 this->Bounds[3] = bounds[3];
144 this->xD = this->Divisions[0];
145 this->yD = this->Divisions[1];
146 this->zD = 1;
147
148 this->FastPoints = nullptr;
149 this->BinRadius = sqrt(hX * hX + hY * hY) / 2.0;
150 this->MaxLevel = std::max({ this->xD, this->yD, this->zD });
151 }
152
153 // Virtuals for templated subclasses
154 virtual ~vtkBucketList2D() = default;
155 virtual void BuildLocator() = 0;
156
157 // place points in appropriate buckets
159 NeighborBuckets2D* buckets, const int ij[2], const int ndivs[2], int level);
160 void GenerateFace(int face, int i, int j, int k, vtkPoints* pts, vtkCellArray* polys);
161 double Distance2ToBucket(const double x[3], const int nei[3]);
162 double Distance2ToBounds(const double x[3], const double bounds[6]);
163
164 //-----------------------------------------------------------------------------
165 // Inlined for performance. These function invocations must be called after
166 // BuildLocator() is invoked, otherwise the output is indeterminate.
167 void GetBucketIndices(const double* x, int ij[2]) const
168 {
169 // Compute point index. Make sure it lies within range of locator.
170 vtkIdType tmp0 = static_cast<vtkIdType>(((x[0] - bX) * fX));
171 vtkIdType tmp1 = static_cast<vtkIdType>(((x[1] - bY) * fY));
172
173 ij[0] = std::min(std::max<vtkIdType>(tmp0, 0), xD - 1);
174 ij[1] = std::min(std::max<vtkIdType>(tmp1, 0), yD - 1);
175 }
176
177 //-----------------------------------------------------------------------------
178 vtkIdType GetBucketIndex(const double* x) const
179 {
180 int ij[2];
181 this->GetBucketIndices(x, ij);
182 return ij[0] + ij[1] * xD;
183 }
184
185 //-----------------------------------------------------------------------------
186 // Return the center of the bucket/bin at (i,j). Note, returns a 2D point
187 // center[2].
188 void GetBucketCenter(int i, int j, double center[3])
189 {
190 center[0] = this->bX + this->hX2 + i * this->hX;
191 center[1] = this->bY + this->hY2 + j * this->hY;
192 center[2] = 0.0;
193 }
194
195 //-----------------------------------------------------------------------------
196 // Return the bounding box (min,max) of a specified bucket (i,j,k).
197 void GetBucketBounds(int i, int j, double min[3], double max[3])
198 {
199 min[0] = this->bX + i * this->hX;
200 min[1] = this->bY + j * this->hY;
201 min[2] = 0.0;
202 max[0] = min[0] + this->hX;
203 max[1] = min[1] + this->hY;
204 max[2] = 0.0;
205 }
206}; // vtkBucketList2D
207
208//------------------------------------------------------------------------------
209// This templated class manages the creation of the static locator
210// structures. It also implements the operator() functors which are supplied
211// to vtkSMPTools for threaded processesing.
212template <typename TIds>
214{
215 // Okay the various ivars
216 vtkLocatorTuple<TIds>* Map; // the map to be sorted
217 TIds* Offsets; // offsets for each bucket into the map
218
219 // Construction
220 BucketList2D(vtkStaticPointLocator2D* loc, vtkIdType numPts, int numBuckets)
221 : vtkBucketList2D(loc, numPts, numBuckets)
222 {
223 // one extra to simplify traversal
224 this->Map = new vtkLocatorTuple<TIds>[numPts + 1];
225 this->Map[numPts].Bucket = numBuckets;
226 this->Offsets = new TIds[numBuckets + 1];
227 this->Offsets[numBuckets] = numPts;
228 }
229
230 // Release allocated memory
231 ~BucketList2D() override
232 {
233 delete[] this->Map;
234 delete[] this->Offsets;
235 }
236
237 // The number of point ids in a bucket is determined by computing the
238 // difference between the offsets into the sorted points array.
240 {
241 return (this->Offsets[bucketNum + 1] - this->Offsets[bucketNum]);
242 }
243
244 // Given a bucket number, return the point ids in that bucket.
246 {
247 return this->Map + this->Offsets[bucketNum];
248 }
249
250 // Given a bucket number, return the point ids in that bucket.
251 void GetIds(vtkIdType bucketNum, vtkIdList* bList)
252 {
253 const vtkLocatorTuple<TIds>* ids = this->GetIds(bucketNum);
254 vtkIdType numIds = this->GetNumberOfIds(bucketNum);
255 bList->SetNumberOfIds(numIds);
256 for (int i = 0; i < numIds; i++)
257 {
258 bList->SetId(i, ids[i].PtId);
259 }
260 }
261
262 // Templated implementations of the locator
263 vtkIdType FindClosestPoint(const double x[3]);
265 double radius, const double x[3], double inputDataLength, double& dist2);
266 void FindClosestNPoints(int N, const double x[3], vtkIdList* result);
267 double FindNPointsInAnnulus(int N, const double x[3], vtkDist2TupleArray& results,
268 double minDist2 = (-0.1), bool sort = true, vtkDoubleArray* petals = nullptr);
269 void FindPointsWithinRadius(double R, const double x[3], vtkIdList* result);
270 int IntersectWithLine(double a0[3], double a1[3], double tol, double& t, double lineX[3],
271 double ptX[3], vtkIdType& ptId);
272 double FindCloseNBoundedPoints(int N, const double x[3], vtkIdList* result);
273
274 void MergePoints(double tol, vtkIdType* pointMap);
275 void GenerateRepresentation(int vtkNotUsed(level), vtkPolyData* pd);
276
277 // Internal methods
278 bool BucketIntersectsCircle(int i, int j, const double center[3], double R2);
280 NeighborBuckets2D* buckets, const double x[3], const int ij[2], double dist, int level);
281 void GetOverlappingBuckets(NeighborBuckets2D* buckets, const double x[3], double dist,
282 int prevMinLevel[2], int prevMaxLevel[2]);
283
284 // Implicit point representation, slower path
285 template <typename T>
287 {
290
292 : BList(blist)
293 , DataSet(ds)
294 {
295 }
296
298 {
299 double p[3];
300 vtkLocatorTuple<T>* t = this->BList->Map + ptId;
301 for (; ptId < end; ++ptId, ++t)
302 {
303 this->DataSet->GetPoint(ptId, p);
304 t->PtId = ptId;
305 t->Bucket = this->BList->GetBucketIndex(p);
306 } // for all points in this batch
307 }
308 };
309
310 template <typename T, typename TPointsArray>
312 {
314 TPointsArray* Points;
315
316 MapPointsArray(BucketList2D<T>* blist, TPointsArray* pts)
317 : BList(blist)
318 , Points(pts)
319 {
320 }
321
323 {
324 double p[3];
325 auto x = vtk::DataArrayTupleRange<3>(this->Points, ptId, end).begin();
326 vtkLocatorTuple<T>* t = this->BList->Map + ptId;
327 for (; ptId < end; ++ptId, ++x, ++t)
328 {
329 x->GetTuple(p);
330 t->PtId = ptId;
331 t->Bucket = this->BList->GetBucketIndex(p);
332 } // for all points in this batch
333 }
334 };
335
337 {
338 template <typename TPointsArray>
339 void operator()(TPointsArray* points, BucketList2D* blist)
340 {
341 MapPointsArray<TIds, TPointsArray> mapper(blist, points);
342 vtkSMPTools::For(0, blist->NumPts, mapper);
343 }
344 };
345
346 // A clever way to build offsets in parallel. Basically each thread builds
347 // offsets across a range of the sorted map. Recall that offsets are an
348 // integral value referring to the locations of the sorted points that
349 // reside in each bucket.
350 template <typename T>
352 {
356
358 : BList(blist)
359 {
360 this->NumPts = this->BList->NumPts;
361 this->NumBuckets = this->BList->NumBuckets;
362 }
363
364 // Traverse sorted points (i.e., tuples) and update bucket offsets.
365 void operator()(vtkIdType batch, vtkIdType batchEnd)
366 {
367 T* offsets = this->BList->Offsets;
368 const vtkLocatorTuple<T>* curPt = this->BList->Map + batch * this->BList->BatchSize;
369 const vtkLocatorTuple<T>* endBatchPt = this->BList->Map + batchEnd * this->BList->BatchSize;
370 const vtkLocatorTuple<T>* endPt = this->BList->Map + this->NumPts;
371 const vtkLocatorTuple<T>* prevPt;
372 endBatchPt = (endBatchPt > endPt ? endPt : endBatchPt);
373
374 // Special case at the very beginning of the mapped points array. If
375 // the first point is in bucket# N, then all buckets up and including
376 // N must refer to the first point.
377 if (curPt == this->BList->Map)
378 {
379 prevPt = this->BList->Map;
380 std::fill_n(offsets, curPt->Bucket + 1, 0); // point to the first points
381 } // at the very beginning of the map (sorted points array)
382
383 // We are entering this functor somewhere in the interior of the
384 // mapped points array. All we need to do is point to the entry
385 // position because we are interested only in prevPt->Bucket.
386 else
387 {
388 prevPt = curPt;
389 } // else in the middle of a batch
390
391 // Okay we have a starting point for a bucket run. Now we can begin
392 // filling in the offsets in this batch. A previous thread should
393 // have/will have completed the previous and subsequent runs outside
394 // of the [batch,batchEnd) range
395 for (curPt = prevPt; curPt < endBatchPt;)
396 {
397 for (; curPt->Bucket == prevPt->Bucket && curPt <= endBatchPt; ++curPt)
398 {
399 // advance
400 }
401 // Fill in any gaps in the offset array
402 std::fill_n(
403 offsets + prevPt->Bucket + 1, curPt->Bucket - prevPt->Bucket, curPt - this->BList->Map);
404 prevPt = curPt;
405 } // for all batches in this range
406 } // operator()
407 };
408
409 // Merge points that are pecisely coincident. Operates in parallel on
410 // locator buckets. Does not need to check neighbor buckets.
411 template <typename T>
413 {
417
419 : BList(blist)
420 , MergeMap(mergeMap)
421 {
422 this->DataSet = blist->DataSet;
423 }
424
425 void operator()(vtkIdType bucket, vtkIdType endBucket)
426 {
427 BucketList2D<T>* bList = this->BList;
428 vtkIdType* mergeMap = this->MergeMap;
429 int i, j;
430 const vtkLocatorTuple<TIds>* ids;
431 double p[3], p2[3];
432 vtkIdType ptId, ptId2, numIds;
433
434 for (; bucket < endBucket; ++bucket)
435 {
436 if ((numIds = bList->GetNumberOfIds(bucket)) > 0)
437 {
438 ids = bList->GetIds(bucket);
439 for (i = 0; i < numIds; i++)
440 {
441 ptId = ids[i].PtId;
442 if (mergeMap[ptId] < 0)
443 {
444 mergeMap[ptId] = ptId;
445 this->DataSet->GetPoint(ptId, p);
446 for (j = i + 1; j < numIds; j++)
447 {
448 ptId2 = ids[j].PtId;
449 if (mergeMap[ptId2] < 0)
450 {
451 this->DataSet->GetPoint(ptId2, p2);
452 if (p[0] == p2[0] && p[1] == p2[1])
453 {
454 mergeMap[ptId2] = ptId;
455 }
456 }
457 }
458 } // if point not yet visited
459 }
460 }
461 }
462 }
463 };
464
465 // Merge points that are coincident within a tolerance. Operates in
466 // parallel on points. Needs to check neighbor buckets which slows it down
467 // considerably. Note that merging is one direction: larger ids are merged
468 // to lower.
469 template <typename T>
471 {
475 double Tol;
476
478
479 MergeClose(BucketList2D<T>* blist, double tol, vtkIdType* mergeMap)
480 : BList(blist)
481 , MergeMap(mergeMap)
482 , Tol(tol)
483 {
484 this->DataSet = blist->DataSet;
485 }
486
487 // Just allocate a little bit of memory to get started.
489 {
490 vtkIdList*& pIds = this->PIds.Local();
491 pIds->Allocate(128); // allocate some memory
492 }
493
494 void operator()(vtkIdType ptId, vtkIdType endPtId)
495 {
496 BucketList2D<T>* bList = this->BList;
497 vtkIdType* mergeMap = this->MergeMap;
498 int i;
499 double p[3];
500 vtkIdType nearId, numIds;
501 vtkIdList*& nearby = this->PIds.Local();
502
503 for (; ptId < endPtId; ++ptId)
504 {
505 if (mergeMap[ptId] < 0)
506 {
507 mergeMap[ptId] = ptId;
508 this->DataSet->GetPoint(ptId, p);
509 bList->FindPointsWithinRadius(this->Tol, p, nearby);
510 if ((numIds = nearby->GetNumberOfIds()) > 0)
511 {
512 for (i = 0; i < numIds; i++)
513 {
514 nearId = nearby->GetId(i);
515 if (ptId < nearId && (mergeMap[nearId] < 0 || ptId < mergeMap[nearId]))
516 {
517 mergeMap[nearId] = ptId;
518 }
519 }
520 }
521 } // if point not yet processed
522 } // for all points in this batch
523 }
524
525 void Reduce() {}
526 };
527
528 // Build the map and other structures to support locator operations
529 void BuildLocator() override
530 {
531 // Place each point in a bucket
532 auto points = this->DataSet->GetPoints()->GetData();
535 points, worker, this))
536 {
537 worker(points, this);
538 }
539
540 // Now gather the points into contiguous runs in buckets
541 //
542 vtkSMPTools::Sort(this->Map, this->Map + this->NumPts);
543
544 // Build the offsets into the Map. The offsets are the positions of
545 // each bucket into the sorted list. They mark the beginning of the
546 // list of points in each bucket. Amazingly, this can be done in
547 // parallel.
548 //
549 int numBatches = static_cast<int>(ceil(static_cast<double>(this->NumPts) / this->BatchSize));
550 MapOffsets<TIds> offMapper(this);
551 vtkSMPTools::For(0, numBatches, offMapper);
552 }
553}; // BucketList2D
554
555VTK_ABI_NAMESPACE_END
556#endif // vtkStaticPointLocator2DPrivate_h
557// VTK-HeaderTest-Exclude: vtkStaticPointLocator2DPrivate.h
RealT r2
Definition PyrC2Basis.h:20
object to represent cell connectivity
abstract class to specify dataset behavior
Definition vtkDataSet.h:166
virtual double * GetPoint(vtkIdType ptId)=0
Get point coordinates with ptId such that: 0 <= ptId < NumberOfPoints.
dynamic, self-adjusting array of double
list of point or cell ids
Definition vtkIdList.h:133
void SetNumberOfIds(vtkIdType number)
Specify the number of ids for this object to hold.
int Allocate(vtkIdType sz, int strategy=0)
Allocate a capacity for sz ids in the list and set the number of stored ids in the list to 0.
vtkIdType GetNumberOfIds() const noexcept
Return the number of id's in the list.
Definition vtkIdList.h:159
void SetId(vtkIdType i, vtkIdType vtkid)
Set the id at location i.
Definition vtkIdList.h:188
vtkIdType GetId(vtkIdType i)
Return the id at location i.
Definition vtkIdList.h:164
virtual vtkDataSet * GetDataSet()
Build the locator from the points/cells defining this dataset.
represent and manipulate 3D points
Definition vtkPoints.h:139
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.
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 2-space
void GetBounds(double *bounds) override
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 directions.
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(BucketList2D< T > *blist, vtkDataSet *ds)
void operator()(vtkIdType ptId, vtkIdType end)
void operator()(vtkIdType batch, vtkIdType batchEnd)
void operator()(TPointsArray *points, BucketList2D *blist)
void operator()(vtkIdType ptId, vtkIdType end)
MapPointsArray(BucketList2D< T > *blist, TPointsArray *pts)
MergeClose(BucketList2D< T > *blist, double tol, vtkIdType *mergeMap)
vtkSMPThreadLocalObject< vtkIdList > PIds
void operator()(vtkIdType ptId, vtkIdType endPtId)
void operator()(vtkIdType bucket, vtkIdType endBucket)
MergePrecise(BucketList2D< T > *blist, vtkIdType *mergeMap)
void FindClosestNPoints(int N, const double x[3], vtkIdList *result)
double FindCloseNBoundedPoints(int N, const double x[3], vtkIdList *result)
const vtkLocatorTuple< TIds > * GetIds(vtkIdType bucketNum)
BucketList2D(vtkStaticPointLocator2D *loc, vtkIdType numPts, int numBuckets)
void MergePoints(double tol, vtkIdType *pointMap)
void GetOverlappingBuckets(NeighborBuckets2D *buckets, const double x[3], const int ij[2], double dist, int level)
void GetOverlappingBuckets(NeighborBuckets2D *buckets, const double x[3], double dist, int prevMinLevel[2], int prevMaxLevel[2])
vtkLocatorTuple< TIds > * Map
void FindPointsWithinRadius(double R, const double x[3], vtkIdList *result)
int IntersectWithLine(double a0[3], double a1[3], double tol, double &t, double lineX[3], double ptX[3], vtkIdType &ptId)
vtkIdType GetNumberOfIds(vtkIdType bucketNum)
bool BucketIntersectsCircle(int i, int j, const double center[3], double R2)
void GetIds(vtkIdType bucketNum, vtkIdList *bList)
vtkIdType FindClosestPointWithinRadius(double radius, const double x[3], double inputDataLength, double &dist2)
vtkIdType FindClosestPoint(const double x[3])
double FindNPointsInAnnulus(int N, const double x[3], vtkDist2TupleArray &results, double minDist2=(-0.1), bool sort=true, vtkDoubleArray *petals=nullptr)
void GenerateRepresentation(int level, vtkPolyData *pd)
Dispatch a single array against all array types mentioned in the ArrayList template parameter.
double Distance2ToBucket(const double x[3], const int nei[3])
void GetBucketBounds(int i, int j, double min[3], double max[3])
double Distance2ToBounds(const double x[3], const double bounds[6])
void GetBucketIndices(const double *x, int ij[2]) const
void GenerateFace(int face, int i, int j, int k, vtkPoints *pts, vtkCellArray *polys)
vtkIdType GetBucketIndex(const double *x) const
virtual ~vtkBucketList2D()=default
vtkStaticPointLocator2D * Locator
virtual void BuildLocator()=0
void GetBucketCenter(int i, int j, double center[3])
void GetBucketNeighbors(NeighborBuckets2D *buckets, const int ij[2], const int ndivs[2], int level)
vtkBucketList2D(vtkStaticPointLocator2D *loc, vtkIdType numPts, int numBuckets)
Represent an array of vtkDist2Tuples.
STL-compatible iterable ranges that provide access to vtkDataArray elements.
bool InsideCircle(const double min[2], const double max[2], const double center[2], double r2)
Performant method to determine if a box if fully inside a circle.
bool IntersectsCircle(const double min[2], const double max[2], const double center[2], double r2)
Performant method to intersect a box with a circle.
int vtkIdType
Definition vtkType.h:368
#define max(a, b)