VTK  9.7.1
vtkVoronoiCore.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
2 // SPDX-License-Identifier: BSD-3-Clause
19 #ifndef vtkVoronoiCore_h
20 #define vtkVoronoiCore_h
21 
22 #include "vtkAlgorithm.h" // check abort status if embedded in filter
23 #include "vtkIntArray.h" // for representing segmented region ids
24 #include "vtkSMPTools.h" // SMP parallel processing
25 
26 #include <algorithm> // for std::sort
27 #include <iostream>
28 #include <random> // random generation of colors
29 #include <vector>
30 
31 VTK_ABI_NAMESPACE_BEGIN
32 
39 {
40  NoIntersection = 0,
41  Intersection = 1,
42  Pruned = 2,
43  Numeric = 3
44 };
45 
52 {
53  BACKWARD_SPOKE = 0, // Bit 0: Backward spoke
54  FORWARD_SPOKE = 1, // Bit 0: Forward spoke
55  REGION_BOUNDARY = 2, // Bit 1: Region boundary spoke
56  DOMAIN_BOUNDARY = 4, // Bit 2: Domain boundary spoke
57  PRUNED = 8, // Bit 3: Spoke is pruned (deleted)
58 };
59 
64 {
65  vtkIdType NeiId; // Id of the wheel that the spoke is connected to (wheelId,NeiId)
66  unsigned char Classification; // Indicate the classification of this spoke
68  : NeiId(-1)
69  , Classification(0)
70  {
71  }
72  vtkVoronoiSpoke(vtkIdType neiId, unsigned char classification)
73  : NeiId(neiId)
74  , Classification(classification)
75  {
76  }
77 }; // vtkVoronoiSpoke
78 
86 using vtkVoronoiSpokesType = std::vector<vtkVoronoiSpoke>;
87 using vtkVoronoiSpokesIterator = vtkVoronoiSpokesType::iterator;
88 using vtkVoronoiWheelsType = std::vector<vtkIdType>;
89 
90 // Gather spokes into a wheel. Define some basic operators. Note that every
91 // wheel is associated with an input (tile/hull generating) point. So access
92 // to the wheel and its associated spokes is via point id.
94 {
95  vtkVoronoiWheelsType& Wheels; // The composited array of wheels
96  vtkVoronoiSpokesType& Spokes; // The composited array of spokes
97  vtkIdType Id; // The associated point/tile id: with wheelId == pointId
98  int NumSpokes; // The number of emanating spokes
99  vtkVoronoiSpoke* LocalSpokes; // The array of spokes connected to this wheel
100 
101  // Default instantiation.
103  : Wheels(wheels)
104  , Spokes(spokes)
105  , Id(-1)
106  , NumSpokes(0)
107  , LocalSpokes(nullptr)
108  {
109  }
110 
111  // Setup the wheel for queries: an efficient form that does not require
112  // repeated wheel instantiation.
113  vtkVoronoiSpoke* Initialize(vtkIdType id, int& numSpokes)
114  {
115  this->Id = id;
116  this->NumSpokes = numSpokes = (this->Wheels[id + 1] - this->Wheels[id]);
117  this->LocalSpokes =
118  ((size_t)this->Wheels[id] < this->Spokes.size() ? &(this->Spokes[this->Wheels[id]])
119  : nullptr);
120  return this->LocalSpokes;
121  }
122 }; // vtkVoronoiWheel
123 
132 {
133  vtkVoronoiWheelsType Wheels; // Wheel/spokes data structure: offset array into spokes
134  vtkVoronoiSpokesType Spokes; // Spokes / edges with classification
135 
136  void Initialize(vtkIdType numWheels, vtkIdType numSpokes);
137  vtkVoronoiWheelsType& GetWheels() { return this->Wheels; }
138  vtkVoronoiSpokesType& GetSpokes() { return this->Spokes; }
139  vtkIdType GetNumberOfWheels() { return (this->Wheels.size() - 1); }
140  vtkIdType GetNumberOfSpokes() { return this->Spokes.size(); }
141  bool IsSpoke(vtkIdType pt0, vtkIdType pt1);
142  vtkIdType GetNumberOfSpokes(vtkIdType ptId); // #spokes for a specified wheel
143  vtkVoronoiSpoke* GetSpokes(vtkIdType ptId, vtkIdType& numSpokes);
144  vtkIdType GetWheelOffset(vtkIdType ptId) { return this->Wheels[ptId]; }
145  static void CountFaces(const vtkVoronoiSpoke* spokes, int numSpokes, int& numDomainBoundaryFaces,
146  int& numRegionBoundaryFaces, int& numForwardFaces);
147 
155  bool Validate();
156 
161  {
164 
166  : Graph(graph)
167  , NumInvalid(0)
168  {
169  }
170 
171  // Keep track whether threads are non-degenerate.
173 
174  // vtkSMPTools threaded interface
175  void Initialize();
176  void operator()(vtkIdType wheelId, vtkIdType endWheelId);
177  void Reduce();
178  }; // ValidateAdjacencyGraph
179 }; // vtkVoronoiAdjacencyGraph
180 
185 struct vtkVoronoiHullVertex // 3D hull vertices
186 {
187  double X[3];
188  vtkVoronoiHullVertex(double x, double y, double z)
189  : X{ x, y, z }
190  {
191  }
192  vtkVoronoiHullVertex(const double x[3])
193  : X{ x[0], x[1], x[2] }
194  {
195  }
196 };
197 using vtkVoronoiHullVertexType = std::vector<vtkVoronoiHullVertex>;
198 
199 struct vtkVoronoiTileVertex // 2D tile vertices
200 {
201  double X[2];
202  vtkVoronoiTileVertex(double x, double y)
203  : X{ x, y }
204  {
205  }
206  vtkVoronoiTileVertex(const double x[2])
207  : X{ x[0], x[1] }
208  {
209  }
210 };
211 using vtkVoronoiTileVertexType = std::vector<vtkVoronoiTileVertex>;
212 
224 {
229  std::array<vtkIdType, 4> Ids;
230 
235  : Ids{ 0 }
236  {
237  }
243  : Ids{ p0, p1, p2, ptId }
244  {
245  std::sort(this->Ids.data(), this->Ids.data() + this->Ids.size());
246  }
250  vtkVoronoiTopoCoord3D(const vtkVoronoiTopoCoord3D& tt) { this->Ids = tt.Ids; }
251 
257  bool operator<(const vtkVoronoiTopoCoord3D& tuple) const { return this->Ids < tuple.Ids; }
258 };
259 using vtkVoronoiTopoCoords3DType = std::vector<vtkVoronoiTopoCoord3D>;
260 
262 {
267  std::array<vtkIdType, 3> Ids;
268 
273  : Ids{ 0 }
274  {
275  }
281  : Ids{ p0, p1, ptId }
282  {
283  std::sort(this->Ids.data(), this->Ids.data() + this->Ids.size());
284  }
288  vtkVoronoiTopoCoord2D(const vtkVoronoiTopoCoord2D& tt) { this->Ids = tt.Ids; }
289 
295  bool operator<(const vtkVoronoiTopoCoord2D& tuple) const { return this->Ids < tuple.Ids; }
296 };
297 using vtkVoronoiTopoCoords2DType = std::vector<vtkVoronoiTopoCoord2D>;
298 
307 {
308  vtkIdType PtId; // the id of the hull vertex
309 
311  : PtId(-1)
312  {
313  }
314  bool operator!=(const vtkVoronoiMergeTuple3D& mt) const { return (this->Ids != mt.Ids); }
315 }; // vtkVoronoiMergeTuple3D
316 
318 {
319  vtkIdType PtId; // the id of the tile vertex
320 
322  : PtId(-1)
323  {
324  }
325  bool operator!=(const vtkVoronoiMergeTuple2D& mt) const { return (this->Ids != mt.Ids); }
326 }; // vtkVoronoiMergeTuple2D
327 
334 using vtkMergeTupleOffsets = std::vector<vtkIdType>; // offsets into merged tuples
335 using vtkMergeTuples3DType = std::vector<vtkVoronoiMergeTuple3D>;
336 using vtkMergeTuples2DType = std::vector<vtkVoronoiMergeTuple2D>;
337 
343 using vtkMergeMapType = std::vector<vtkIdType>;
344 
348 using vtkVoronoiCellConnType = std::vector<vtkIdType>;
349 
355 {
356  vtkIdType Num; // Number of total items (e.g., points) to process
357  vtkIdType BatchSize; // The desired batch size (clamped by Num)
358  vtkIdType NumBatches; // The total number of batches to process
360  : Num(num)
361  , BatchSize(batchSize)
362  {
363  this->NumBatches = static_cast<vtkIdType>(ceil(static_cast<double>(num) / batchSize));
364  }
365  vtkIdType GetNumberOfBatches() const { return this->NumBatches; }
366  vtkIdType GetBatchItemRange(vtkIdType batchNum, vtkIdType& startId, vtkIdType& endId) const
367  {
368  startId = batchNum * this->BatchSize;
369  endId = startId + this->BatchSize;
370  endId = (endId > this->Num ? this->Num : endId);
371  return (endId - startId);
372  }
373 }; // vtkVoronoiBatchManager
374 
379 using vtkBatchIdsType = std::vector<vtkIdType>;
380 
381 // Convenience function: convert input labels/region ids/scalars to signed int.
382 // The Voronoi classes expect signed int region labels.
384 {
385  vtkNew<vtkIntArray> rIds;
386  rIds->SetNumberOfTuples(inScalars->GetNumberOfTuples());
387  rIds->DeepCopy(inScalars);
388 
389  return rIds;
390 }
391 
397 {
399  bool IsFirst;
401 
403  : Filter(filter)
404  {
405  this->IsFirst = vtkSMPTools::GetSingleThread();
406  this->CheckAbortInterval = std::min((end - start) / 10 + 1, (vtkIdType)1000);
407  }
408 
410  {
411  if (this->Filter && this->IsFirst && !(id % this->CheckAbortInterval))
412  {
413  this->Filter->CheckAbort();
414  return (this->Filter->GetAbortOutput() ? true : false);
415  }
416  return false;
417  }
418 }; // vtkVoronoiAbortCheck
419 
420 // Use system <random> - create a simple convenience class. This generates
421 // random color indices [0,64).
423 {
424  std::mt19937 RNG;
425  std::uniform_int_distribution<int> Dist;
426  vtkVoronoiRandomColors() { this->Dist.param(typename decltype(this->Dist)::param_type(0, 64)); }
427  void Seed(vtkIdType s) { this->RNG.seed(s); }
428  vtkIdType Next() { return this->Dist(RNG); }
429 };
430 
431 // Use system <random> - create a simple convenience class. This generates
432 // random real values [0,1).
434 {
435  std::mt19937 RNG;
436  std::uniform_real_distribution<double> Dist;
438  {
439  this->Dist.param(typename decltype(this->Dist)::param_type(0.0, 1.0));
440  }
441  void Seed(vtkIdType s) { this->RNG.seed(s); }
442  double Next() { return this->Dist(RNG); }
443 };
444 
445 // A convenience class and methods to randomly perturb (alternatively:
446 // joggle, jitter, or jiggle) point positions. Such jittering (even if very
447 // small) significantly improves the numerical stability of Voronoi and
448 // Delaunay computations. Terminology note: while joggle is not a widely used
449 // term (typically in computer graphics the words perturb, jitter, and jiggle
450 // are found more frequently), it is used by the popular QuickHull (and
451 // associated implementation QHull qhull.org)--so the terminology was
452 // adopted by this work. Implementation note: these methods might be better
453 // added to vtkMath since they can be used by other classes. Once they are
454 // demonstrated to be stable, they may be moved.
456 {
457  // Joggle a single point at input position xIn to produce the output position
458  // xOut (xIn and xOut may be computed in-place). The radius is the allowable
459  // range of joggle in the sphere. A sequence is provided, assumed properly
460  // initialized, to produce random (0,1) values. Note that if this method is
461  // invoked in a thread, separate sequence instantiations (one per thread)
462  // should be provided.
463  static void JoggleXYZ(
464  double xIn[3], double xOut[3], double radius, vtkVoronoiRandom01Range& sequence)
465  {
466  double cosphi = 1 - 2 * sequence.Next();
467  double sinphi = sqrt(1 - cosphi * cosphi);
468  double rho = radius * pow(sequence.Next(), 0.33333333);
469  double R = rho * sinphi;
470  double theta = 2.0 * vtkMath::Pi() * sequence.Next();
471  xOut[0] = xIn[0] + R * cos(theta);
472  xOut[1] = xIn[1] + R * sin(theta);
473  xOut[2] = xIn[2] + rho * cosphi;
474  }
475 
476  // Joggle a single point at input position xIn to produce the output
477  // position xOut (xIn and xOut may be computed in-place). The radius is the
478  // allowable range of joggle in the circle in the x-y plane. A sequence is
479  // provided, assumed properly initialized, to produce random (0,1) values.
480  // Note that if this method is invoked in a thread, separate sequence
481  // instantiations (one per thread) should be provided.
482  static void JoggleXY(
483  double xIn[3], double xOut[3], double radius, vtkVoronoiRandom01Range& sequence)
484  {
485  double R = radius * sequence.Next();
486  double theta = 2.0 * vtkMath::Pi() * sequence.Next();
487  xOut[0] = xIn[0] + R * cos(theta);
488  xOut[1] = xIn[1] + R * sin(theta);
489  xOut[2] = xIn[2];
490  }
491 
492  // Joggle a single point at input position xIn to produce the output
493  // position xOut (xIn and xOut may be computed in-place). The radius is the
494  // allowable range of joggle in the circle in the x-z plane. A sequence is
495  // provided, assumed properly initialized, to produce random (0,1) values.
496  // Note that if this method is invoked in a thread, separate sequence
497  // instantiations (one per thread) should be provided.
498  static void JoggleXZ(
499  double xIn[3], double xOut[3], double radius, vtkVoronoiRandom01Range& sequence)
500  {
501  double R = radius * sequence.Next();
502  double theta = 2.0 * vtkMath::Pi() * sequence.Next();
503  xOut[0] = xIn[0] + R * cos(theta);
504  xOut[1] = xIn[1];
505  xOut[2] = xIn[2] + R * sin(theta);
506  }
507 
508  // Joggle a single point at input position xIn to produce the output
509  // position xOut (xIn and xOut may be computed in-place). The radius is the
510  // allowable range of joggle in the circle in the y-z plane. A sequence is
511  // provided, assumed properly initialized, to produce random (0,1) values.
512  // Note that if this method is invoked in a thread, separate sequence
513  // instantiations (one per thread) should be provided.
514  static void JoggleYZ(
515  double xIn[3], double xOut[3], double radius, vtkVoronoiRandom01Range& sequence)
516  {
517  double R = radius * sequence.Next();
518  double theta = 2.0 * vtkMath::Pi() * sequence.Next();
519  xOut[0] = xIn[0];
520  xOut[1] = xIn[1] + R * cos(theta);
521  xOut[2] = xIn[2] + R * sin(theta);
522  }
523 
524  // Joggle a 3D vector. Specify an input normalized vector (vecIn), a angle
525  // of rotation theta (in radians, 0<theta<Pi/2), and a random
526  // sequence. Produce on output a normalized vector (vecOut) -- vecIn and
527  // vecOut may be computed in place. Note that if this method is invoked in
528  // a thread, separate sequence instantiations (one per thread) should be
529  // provided. The approach used here is to define a disk located at the tip
530  // of the (normalized) vecIn (the disc is normal to vecIn), and randomly
531  // select a vector from the edge/along the perimeter of the disc.
532  static void JoggleNormal(
533  double vecIn[3], double vecOut[3], double theta, vtkVoronoiRandom01Range& sequence)
534  {
535  // Find a vector orthogonal to the input vector.
536  double perp[3];
537  int iMax = (std::fabs(vecIn[0]) > std::fabs(vecIn[1]) ? 0 : 1);
538  iMax = (std::fabs(vecIn[iMax]) > std::fabs(vecIn[2]) ? iMax : 2);
539  perp[(iMax + 1) % 3] = 1.0;
540  perp[(iMax + 2) % 3] = 0.0;
541  perp[iMax] = -(vecIn[(iMax + 1) % 3] / vecIn[iMax]);
542  vtkMath::Normalize(perp);
543 
544  // Now find third orthogonal vector. Since vecIn and perp are normalized and
545  // orthogonal, the resultant cross also a unit vector.
546  double cross[3];
547  vtkMath::Cross(vecIn, perp, cross);
548 
549  // Compute a random vector
550  double radius = theta / (2.0 * vtkMath::Pi()); // small angle approximation
551  double t = 2.0 * vtkMath::Pi() * sequence.Next();
552  double x = radius * cos(t);
553  double y = radius * sin(t);
554 
555  // Construct a vector along the disc/cone edge
556  vecOut[0] = vecIn[0] + x * perp[0] + y * cross[0];
557  vecOut[1] = vecIn[1] + x * perp[1] + y * cross[1];
558  vecOut[2] = vecIn[2] + x * perp[2] + y * cross[2];
559  vtkMath::Normalize(vecOut);
560  }
561 }; // vtkVoronoiJoggle
562 
563 VTK_ABI_NAMESPACE_END
564 #include "vtkVoronoiCore.txx"
565 
566 #endif
567 // VTK-HeaderTest-Exclude: vtkVoronoiCore.h
unsigned char Classification
Threaded perform validity checking.
virtual void SetNumberOfTuples(vtkIdType numTuples)
Set the number of tuples (a component group) in the array.
bool operator<(const vtkVoronoiTopoCoord2D &tuple) const
Operator< used to support a subsequent sort operation of the n-tuples (used for uniquely identifying ...
std::vector< vtkIdType > vtkBatchIdsType
Keep track of batches of generating points.
Convenience class to check and interrupt processing aborts during threaded processing.
vtkVoronoiTopoCoord2D(vtkIdType p0, vtkIdType p1, vtkIdType ptId)
Define with the N+1 point generators: the N generators producing the hull vertex, plus the current po...
std::vector< vtkVoronoiMergeTuple2D > vtkMergeTuples2DType
std::uniform_real_distribution< double > Dist
vtkVoronoiHullVertex(const double x[3])
vtkVoronoiSpokesType & Spokes
vtkVoronoiAbortCheck(vtkIdType start, vtkIdType end, vtkAlgorithm *filter)
vtkVoronoiTopoCoord2D()
Various flavors of constructors.
std::array< vtkIdType, 4 > Ids
Points defining a topological coord tuple / Delaunay simplex.
vtkVoronoiSpoke * Initialize(vtkIdType id, int &numSpokes)
vtkVoronoiSpokesType & GetSpokes()
vtkVoronoiSpokesType Spokes
void Seed(vtkIdType s)
static void JoggleNormal(double vecIn[3], double vecOut[3], double theta, vtkVoronoiRandom01Range &sequence)
vtkVoronoiTopoCoord3D()
Various flavors of constructors.
vtkVoronoiWheelsType & Wheels
std::vector< vtkVoronoiTopoCoord2D > vtkVoronoiTopoCoords2DType
static constexpr double Pi()
A mathematical constant.
Definition: vtkMath.h:118
Merge tuples contain an additional point id, which is the global id of a tile/hull point...
void Seed(vtkIdType s)
vtkIdType GetNumberOfBatches() const
vtkVoronoiTileVertex(double x, double y)
vtkVoronoiTopoCoord2D(const vtkVoronoiTopoCoord2D &tt)
Copy constructor assumes that tuple ids are already sorted.
vtkVoronoiHullVertex(double x, double y, double z)
int vtkIdType
Definition: vtkType.h:363
Define hull/tile vertex types.
vtkAlgorithm * Filter
void DeepCopy(vtkAbstractArray *aa) override
Deep copy of data.
std::vector< vtkIdType > vtkVoronoiWheelsType
std::vector< vtkIdType > vtkMergeMapType
When merging points, the merge map is a vector that maps global tile/hull vertex ids (which contain d...
std::vector< vtkVoronoiTileVertex > vtkVoronoiTileVertexType
std::vector< vtkVoronoiTopoCoord3D > vtkVoronoiTopoCoords3DType
vtkVoronoiSpoke * LocalSpokes
vtkVoronoiSpokesType::iterator vtkVoronoiSpokesIterator
void operator()(vtkIdType wheelId, vtkIdType endWheelId)
vtkVoronoiTopoCoord3D(const vtkVoronoiTopoCoord3D &tt)
Copy constructor assumes that tuple ids are already sorted.
vtkVoronoiTopoCoord3D(vtkIdType p0, vtkIdType p1, vtkIdType p2, vtkIdType ptId)
Define with the N+1 point generators: the N generators producing the hull vertex, plus the current po...
vtkVoronoiBatchManager(vtkIdType num, vtkIdType batchSize)
Superclass for all sources, filters, and sinks in VTK.
Definition: vtkAlgorithm.h:53
std::vector< vtkIdType > vtkVoronoiCellConnType
Convenience type for representing cell connectivity during compositing.
vtkIdType GetBatchItemRange(vtkIdType batchNum, vtkIdType &startId, vtkIdType &endId) const
bool IsSpoke(vtkIdType pt0, vtkIdType pt1)
std::uniform_int_distribution< int > Dist
bool operator()(vtkIdType id)
Typedefs and classes in support of the adjacency graph.
vtkVoronoiWheelsType Wheels
std::vector< vtkVoronoiHullVertex > vtkVoronoiHullVertexType
A topological coordinate of dimension N are the N+1 ids of neighboring N+1 Voronoi generator points w...
abstract superclass for arrays of numeric data
Definition: vtkDataArray.h:44
static void JoggleXZ(double xIn[3], double xOut[3], double radius, vtkVoronoiRandom01Range &sequence)
vtkSMPThreadLocal< vtkIdType > ThreadNumInvalid
void Initialize(vtkIdType numWheels, vtkIdType numSpokes)
static void JoggleXYZ(double xIn[3], double xOut[3], double radius, vtkVoronoiRandom01Range &sequence)
bool CheckAbort()
Checks to see if this filter should abort.
RealT mt
Definition: PyrC2Basis.h:39
The adjacency graph, a collection of wheels and spokes, is a topological construct that connects Voro...
static void JoggleYZ(double xIn[3], double xOut[3], double radius, vtkVoronoiRandom01Range &sequence)
static void JoggleXY(double xIn[3], double xOut[3], double radius, vtkVoronoiRandom01Range &sequence)
static bool GetSingleThread()
Returns true if the given thread is specified thread for single scope.
vtkIdType GetNumberOfTuples() const
Get the number of complete tuples (a component group) in the array.
vtkSmartPointer< vtkIntArray > ConvertRegionLabels(vtkDataArray *inScalars)
static float Normalize(float v[3])
Normalize (in place) a 3-vector.
Definition: vtkMath.h:1863
ClipIntersectionStatus
Classes, structs, and typedefs in support of Voronoi processing.
bool Validate()
Return true if the graph meets the conditions necessary to form a valid Voronoi tessellation.
static void Cross(VectorT1 &&a, VectorT2 &&b, VectorT3 &c)
Cross product of two 3-vectors.
Definition: vtkMath.h:1970
bool operator!=(const vtkVoronoiMergeTuple3D &mt) const
std::vector< vtkVoronoiMergeTuple3D > vtkMergeTuples3DType
vtkSpokeClassification
Classification for Voronoi spokes (and associated faces).
vtkVoronoiTileVertex(const double x[2])
std::vector< vtkVoronoiSpoke > vtkVoronoiSpokesType
The vtkVoronoiWheelsType vector is used to keep track of the number of spokes (and equivalently...
vtkVoronoiWheel(vtkVoronoiWheelsType &wheels, vtkVoronoiSpokesType &spokes)
static void CountFaces(const vtkVoronoiSpoke *spokes, int numSpokes, int &numDomainBoundaryFaces, int &numRegionBoundaryFaces, int &numForwardFaces)
vtkVoronoiWheelsType & GetWheels()
Class to manage batches of points.
std::array< vtkIdType, 3 > Ids
Points defining a topological coord tuple / Delaunay simplex.
vtkIdType GetWheelOffset(vtkIdType ptId)
vtkVoronoiSpoke(vtkIdType neiId, unsigned char classification)
std::vector< vtkIdType > vtkMergeTupleOffsets
Global tile/hull vertices, with duplicates, that are assigned a global id (if point merging is perfor...
bool operator!=(const vtkVoronoiMergeTuple2D &mt) const
vtkIdType CheckAbortInterval
ValidateAdjacencyGraph(vtkVoronoiAdjacencyGraph &graph)
bool operator<(const vtkVoronoiTopoCoord3D &tuple) const
Operator< used to support a subsequent sort operation of the n-tuples (used for uniquely identifying ...
virtual bool GetAbortOutput()
Set/Get an internal variable used to communicate between the algorithm and executive.