VTK  9.5.20251210
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 "vtkCellArray.h"
17#include "vtkDataSet.h"
18#include "vtkDoubleArray.h"
19#include "vtkMath.h"
20#include "vtkPointSet.h"
21#include "vtkPoints.h"
22#include "vtkPolyData.h"
23#include "vtkSMPThreadLocal.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 // Explicit point representation (e.g., vtkPointSet), faster path
311 template <typename T, typename TPts>
313 {
315 const TPts* Points;
316
317 MapPointsArray(BucketList2D<T>* blist, const TPts* pts)
318 : BList(blist)
319 , Points(pts)
320 {
321 }
322
324 {
325 double p[3];
326 const TPts* x = this->Points + 3 * ptId;
327 vtkLocatorTuple<T>* t = this->BList->Map + ptId;
328 for (; ptId < end; ++ptId, x += 3, ++t)
329 {
330 p[0] = static_cast<double>(x[0]);
331 p[1] = static_cast<double>(x[1]);
332 t->PtId = ptId;
333 t->Bucket = this->BList->GetBucketIndex(p);
334 } // for all points in this batch
335 }
336 };
337
338 // A clever way to build offsets in parallel. Basically each thread builds
339 // offsets across a range of the sorted map. Recall that offsets are an
340 // integral value referring to the locations of the sorted points that
341 // reside in each bucket.
342 template <typename T>
344 {
348
350 : BList(blist)
351 {
352 this->NumPts = this->BList->NumPts;
353 this->NumBuckets = this->BList->NumBuckets;
354 }
355
356 // Traverse sorted points (i.e., tuples) and update bucket offsets.
357 void operator()(vtkIdType batch, vtkIdType batchEnd)
358 {
359 T* offsets = this->BList->Offsets;
360 const vtkLocatorTuple<T>* curPt = this->BList->Map + batch * this->BList->BatchSize;
361 const vtkLocatorTuple<T>* endBatchPt = this->BList->Map + batchEnd * this->BList->BatchSize;
362 const vtkLocatorTuple<T>* endPt = this->BList->Map + this->NumPts;
363 const vtkLocatorTuple<T>* prevPt;
364 endBatchPt = (endBatchPt > endPt ? endPt : endBatchPt);
365
366 // Special case at the very beginning of the mapped points array. If
367 // the first point is in bucket# N, then all buckets up and including
368 // N must refer to the first point.
369 if (curPt == this->BList->Map)
370 {
371 prevPt = this->BList->Map;
372 std::fill_n(offsets, curPt->Bucket + 1, 0); // point to the first points
373 } // at the very beginning of the map (sorted points array)
374
375 // We are entering this functor somewhere in the interior of the
376 // mapped points array. All we need to do is point to the entry
377 // position because we are interested only in prevPt->Bucket.
378 else
379 {
380 prevPt = curPt;
381 } // else in the middle of a batch
382
383 // Okay we have a starting point for a bucket run. Now we can begin
384 // filling in the offsets in this batch. A previous thread should
385 // have/will have completed the previous and subsequent runs outside
386 // of the [batch,batchEnd) range
387 for (curPt = prevPt; curPt < endBatchPt;)
388 {
389 for (; curPt->Bucket == prevPt->Bucket && curPt <= endBatchPt; ++curPt)
390 {
391 // advance
392 }
393 // Fill in any gaps in the offset array
394 std::fill_n(
395 offsets + prevPt->Bucket + 1, curPt->Bucket - prevPt->Bucket, curPt - this->BList->Map);
396 prevPt = curPt;
397 } // for all batches in this range
398 } // operator()
399 };
400
401 // Merge points that are pecisely coincident. Operates in parallel on
402 // locator buckets. Does not need to check neighbor buckets.
403 template <typename T>
405 {
409
411 : BList(blist)
412 , MergeMap(mergeMap)
413 {
414 this->DataSet = blist->DataSet;
415 }
416
417 void operator()(vtkIdType bucket, vtkIdType endBucket)
418 {
419 BucketList2D<T>* bList = this->BList;
420 vtkIdType* mergeMap = this->MergeMap;
421 int i, j;
422 const vtkLocatorTuple<TIds>* ids;
423 double p[3], p2[3];
424 vtkIdType ptId, ptId2, numIds;
425
426 for (; bucket < endBucket; ++bucket)
427 {
428 if ((numIds = bList->GetNumberOfIds(bucket)) > 0)
429 {
430 ids = bList->GetIds(bucket);
431 for (i = 0; i < numIds; i++)
432 {
433 ptId = ids[i].PtId;
434 if (mergeMap[ptId] < 0)
435 {
436 mergeMap[ptId] = ptId;
437 this->DataSet->GetPoint(ptId, p);
438 for (j = i + 1; j < numIds; j++)
439 {
440 ptId2 = ids[j].PtId;
441 if (mergeMap[ptId2] < 0)
442 {
443 this->DataSet->GetPoint(ptId2, p2);
444 if (p[0] == p2[0] && p[1] == p2[1])
445 {
446 mergeMap[ptId2] = ptId;
447 }
448 }
449 }
450 } // if point not yet visited
451 }
452 }
453 }
454 }
455 };
456
457 // Merge points that are coincident within a tolerance. Operates in
458 // parallel on points. Needs to check neighbor buckets which slows it down
459 // considerably. Note that merging is one direction: larger ids are merged
460 // to lower.
461 template <typename T>
463 {
467 double Tol;
468
470
471 MergeClose(BucketList2D<T>* blist, double tol, vtkIdType* mergeMap)
472 : BList(blist)
473 , MergeMap(mergeMap)
474 , Tol(tol)
475 {
476 this->DataSet = blist->DataSet;
477 }
478
479 // Just allocate a little bit of memory to get started.
481 {
482 vtkIdList*& pIds = this->PIds.Local();
483 pIds->Allocate(128); // allocate some memory
484 }
485
486 void operator()(vtkIdType ptId, vtkIdType endPtId)
487 {
488 BucketList2D<T>* bList = this->BList;
489 vtkIdType* mergeMap = this->MergeMap;
490 int i;
491 double p[3];
492 vtkIdType nearId, numIds;
493 vtkIdList*& nearby = this->PIds.Local();
494
495 for (; ptId < endPtId; ++ptId)
496 {
497 if (mergeMap[ptId] < 0)
498 {
499 mergeMap[ptId] = ptId;
500 this->DataSet->GetPoint(ptId, p);
501 bList->FindPointsWithinRadius(this->Tol, p, nearby);
502 if ((numIds = nearby->GetNumberOfIds()) > 0)
503 {
504 for (i = 0; i < numIds; i++)
505 {
506 nearId = nearby->GetId(i);
507 if (ptId < nearId && (mergeMap[nearId] < 0 || ptId < mergeMap[nearId]))
508 {
509 mergeMap[nearId] = ptId;
510 }
511 }
512 }
513 } // if point not yet processed
514 } // for all points in this batch
515 }
516
517 void Reduce() {}
518 };
519
520 // Build the map and other structures to support locator operations
521 void BuildLocator() override
522 {
523 // Place each point in a bucket
524 //
525 vtkPointSet* ps = static_cast<vtkPointSet*>(this->DataSet);
526 if (ps)
527 { // map points array: explicit points representation
528 int dataType = ps->GetPoints()->GetDataType();
529 void* pts = ps->GetPoints()->GetVoidPointer(0);
530 if (dataType == VTK_FLOAT)
531 {
532 MapPointsArray<TIds, float> mapper(this, static_cast<float*>(pts));
533 vtkSMPTools::For(0, this->NumPts, mapper);
534 }
535 else if (dataType == VTK_DOUBLE)
536 {
537 this->FastPoints = static_cast<double*>(pts);
538 MapPointsArray<TIds, double> mapper(this, static_cast<double*>(pts));
539 vtkSMPTools::For(0, this->NumPts, mapper);
540 }
541 }
542
543 else // if (!mapped)
544 { // map dataset points: non-float points or implicit points representation
545 MapDataSet<TIds> mapper(this, this->DataSet);
546 vtkSMPTools::For(0, this->NumPts, mapper);
547 }
548
549 // Now gather the points into contiguous runs in buckets
550 //
551 vtkSMPTools::Sort(this->Map, this->Map + this->NumPts);
552
553 // Build the offsets into the Map. The offsets are the positions of
554 // each bucket into the sorted list. They mark the beginning of the
555 // list of points in each bucket. Amazingly, this can be done in
556 // parallel.
557 //
558 int numBatches = static_cast<int>(ceil(static_cast<double>(this->NumPts) / this->BatchSize));
559 MapOffsets<TIds> offMapper(this);
560 vtkSMPTools::For(0, numBatches, offMapper);
561 }
562}; // BucketList2D
563
564VTK_ABI_NAMESPACE_END
565#endif // vtkStaticPointLocator2DPrivate_h
566// 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.
concrete class for storing a set of points
Definition vtkPointSet.h:98
vtkPoints * GetPoints() override
Specify point array to define point coordinates.
represent and manipulate 3D points
Definition vtkPoints.h:139
void * GetVoidPointer(const int id)
Return a void pointer.
Definition vtkPoints.h:196
virtual int GetDataType() const
Return the underlying data type.
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.
MapDataSet(BucketList2D< T > *blist, vtkDataSet *ds)
void operator()(vtkIdType ptId, vtkIdType end)
void operator()(vtkIdType batch, vtkIdType batchEnd)
MapPointsArray(BucketList2D< T > *blist, const TPts *pts)
void operator()(vtkIdType ptId, vtkIdType end)
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)
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.
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:367
#define VTK_DOUBLE
Definition vtkType.h:44
#define VTK_FLOAT
Definition vtkType.h:43
#define max(a, b)