HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ConvexVoxelizer.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: Apache-2.0
3 ///
4 /// @author Greg Hurst
5 ///
6 /// @file ConvexVoxelizer.h
7 ///
8 /// @brief Base class used to generate the narrow-band level set of a convex region.
9 ///
10 /// @note By definition a level set has a fixed narrow band width
11 /// (the half width is defined by LEVEL_SET_HALF_WIDTH in Types.h),
12 /// whereas an SDF can have a variable narrow band width.
13 
14 #ifndef OPENVDB_TOOLS_CONVEXVOXELIZER_HAS_BEEN_INCLUDED
15 #define OPENVDB_TOOLS_CONVEXVOXELIZER_HAS_BEEN_INCLUDED
16 
17 #include <openvdb/math/Math.h>
20 
21 #include <openvdb/tools/Merge.h>
23 
24 #include <tbb/enumerable_thread_specific.h>
25 #include <tbb/parallel_for.h>
26 #include <tbb/parallel_reduce.h>
27 #include <tbb/blocked_range.h>
28 
29 #include <type_traits>
30 #include <vector>
31 
32 namespace openvdb {
34 namespace OPENVDB_VERSION_NAME {
35 namespace tools {
36 
37 namespace lvlset {
38 
39 /// @brief Internal class used by derived @c ConvexVoxelizer classes that make use of PointPartitioner.
40 template<typename VectorType>
41 struct PointArray
42 {
43  using PosType = VectorType;
44 
45  PointArray() = default;
46 
47  PointArray(const std::vector<VectorType>& vec)
48  : mVec(vec)
49  {
50  }
51 
52  inline const VectorType& operator[](const Index& i) { return mVec[i]; }
53 
54  inline size_t size() const { return mVec.size(); };
55 
56  inline void getPos(size_t n, VectorType& xyz) const { xyz = mVec[n]; }
57 
58 private:
59 
60  const std::vector<VectorType>& mVec;
61 
62 }; // struct PointArray
63 
64 } // namespace lvlset
65 
66 /// @brief Base class used to generate a grid of type @c GridType containing a narrow-band level set
67 /// representation of a convex region.
68 ///
69 /// @note @c GridType::ValueType must be a floating-point scalar.
70 /// @note @c Derived is the derived class that implements the base class (curiously recurring template pattern).
71 ///
72 /// @par Example of derived level set sphere class
73 /// @code
74 /// template <typename GridType>
75 /// class SphereVoxelizer : public ConvexVoxelizer<GridType, SphereVoxelizer<GridType>>
76 /// {
77 /// using GridPtr = typename GridType::Ptr;
78 ///
79 /// using BaseT = ConvexVoxelizer<GridType, SphereVoxelizer<GridType>>;
80 /// using BaseT::mXYData;
81 /// using BaseT::tileCeil;
82 ///
83 /// using ValueT = typename BaseT::ValueT;
84 /// using Vec3T = typename BaseT::Vec3T;
85 ///
86 /// public:
87 ///
88 /// friend class ConvexVoxelizer<GridType, SphereVoxelizer<GridType>>;
89 ///
90 /// SphereVoxelizer(GridPtr& grid, const bool& threaded = true)
91 /// : BaseT(grid, threaded)
92 /// {
93 /// }
94 ///
95 /// template <typename ScalarT>
96 /// void
97 /// operator()(const math::Vec3<ScalarT>& pt, const ScalarT& r)
98 /// {
99 /// static_assert(std::is_floating_point<ScalarT>::value);
100 ///
101 /// if (r <= 0)
102 /// return;
103 ///
104 /// initialize<ScalarT>(pt, r);
105 ///
106 /// BaseT::iterate();
107 /// }
108 ///
109 /// private:
110 ///
111 /// inline ValueT
112 /// signedDistance(const Vec3T& p) const
113 /// {
114 /// return (p - mPt).length() - mRad;
115 /// }
116 ///
117 /// inline void
118 /// setXYRangeData(const Index& step = 1)
119 /// {
120 /// mXYData.reset(mX - mORad, mX + mORad, step);
121 ///
122 /// for (ValueT x = tileCeil(mX - mORad, step); x <= mX + mORad; x += step)
123 /// mXYData.expandYRange(x, BaseT::circleBottom(mX, mY, mORad, x),
124 /// BaseT::circleTop(mX, mY, mORad, x));
125 /// }
126 ///
127 /// std::function<bool(ValueT&, ValueT&, const ValueT&, const ValueT&)> sphereBottomTop =
128 /// [this](ValueT& zb, ValueT& zt, const ValueT& x, const ValueT& y)
129 /// {
130 /// zb = BaseT::sphereBottom(mX, mY, mZ, mORad, x, y);
131 /// zt = BaseT::sphereTop(mX, mY, mZ, mORad, x, y);
132 ///
133 /// return std::isfinite(zb) && std::isfinite(zt);
134 /// };
135 ///
136 /// template <typename ScalarT>
137 /// inline void
138 /// initialize(const math::Vec3<ScalarT>& pt, const ScalarT& r)
139 /// {
140 /// const ValueT vx = BaseT::voxelSize(),
141 /// hw = BaseT::halfWidth();
142 ///
143 /// // sphere data in index space
144 /// mPt = Vec3T(pt)/vx;
145 /// mRad = ValueT(r)/vx;
146 ///
147 /// mX = mPt.x(); mY = mPt.y(); mZ = mPt.z();
148 ///
149 /// // padded radius used to populate the outer halfwidth of the sdf
150 /// mORad = mRad + hw;
151 ///
152 /// BaseT::bottomTop = sphereBottomTop;
153 /// }
154 ///
155 /// Vec3T mPt;
156 /// ValueT mRad, mORad, mX, mY, mZ;
157 /// };
158 ///
159 /// // usage:
160 ///
161 /// // initialize level set grid with voxel size 0.1 and half width 3.0
162 /// FloatGrid::Ptr grid = createLevelSet<GridT>(0.1f, 3.0f);
163 ///
164 /// // populate grid with a sphere centered at (0, 1, 2) and radius 5
165 /// SphereVoxelizer<FloatGrid> op(grid);
166 /// op(Vec3s(0.0f, 1.0f, 2.0f), 5.0f);
167 ///
168 /// @endcode
169 template <typename GridType, typename Derived, typename InterruptType = util::NullInterrupter>
171 {
172  using GridPtr = typename GridType::Ptr;
173 
174  using TreeT = typename GridType::TreeType;
175  using RootT = typename TreeT::RootNodeType;
176  using LeafT = typename TreeT::LeafNodeType;
177 
178  using NodeChainT = typename RootT::NodeChainType;
179 
180  using AccessorT = typename GridType::Accessor;
181 
182 protected:
183 
184  using ValueT = typename GridType::ValueType;
187 
189 
190 public:
191 
192  /// @brief Constructor
193  ///
194  /// @param grid scalar grid to populate the level set in
195  /// @param threaded center of the sphere in world units
196  /// @param interrupter pointer to optional interrupter. Use template
197  /// argument util::NullInterrupter if no interruption is desired.
198  ///
199  /// @note The voxel size and half width are determined from the input grid,
200  /// meaning the voxel size and background value need to be set prior to voxelization
201  ConvexVoxelizer(GridPtr& grid, const bool& threaded = false, InterruptType* interrupter = nullptr)
202  : mGrid(grid)
203  , mVox(ValueT((grid->voxelSize())[0]))
204  , mHw(ValueT(grid->background()/(grid->voxelSize())[0]))
205  , mBg(grid->background())
206  , mNegBg(-(grid->background()))
207  , mSerial(!threaded)
208  , mInterrupter(interrupter)
209  {
210  }
211 
212  virtual ~ConvexVoxelizer() = default;
213 
214  /// @brief Return the voxel size of the grid.
215  inline ValueT voxelSize() const { return mVox; }
216 
217  /// @brief Return the half width of the narrow-band level set.
218  inline ValueT halfWidth() const { return mHw; }
219 
220 private:
221 
222  class CacheLastLeafAccessor;
223 
224 protected:
225 
226  // ------------ Main APIs for derived classes ------------
227 
228  /// @brief The function the derived class calls to create the level set,
229  /// working in index space other than setting signed distance values.
230  ///
231  /// @note This function handles both parallel and serial iterations. If running in serial mode,
232  /// it flood fills the tile topology immediately; otherwise, it avoids duplicating nontrivial
233  /// tree topology over multiple threads. This method also checks for background tiles
234  /// that are too thin to fit and delegates accordingly.
235  inline void
237  {
238  static const Index LEAFDIM = LeafT::DIM;
239 
240  // objects too thin to have negative background tiles
241  if (!invokeTileCanFit(LEAFDIM)) {
242  thinIterate();
243  return;
244  }
245 
246  // iterate over all non root nodes
247  using ChainT = typename NodeChainT::PopBack;
248 
249  // if we're working in parallel, we avoid flood filling the tile topology until the end
250  // this avoids duplicating nontrivial tree topology over multiple threads
251  if (mSerial)
252  iterateTile<ChainT>();
253 
254  iterateLeaf();
255 
256  if (!checkInterrupter())
257  return;
258 
259  if (!mSerial)
260  tools::signedFloodFill(getTree());
261  }
262 
263  // ------------ derived classes need to implement these ------------
264 
265  /// @brief Determines the x bounds in index space of the convex region dilated by the half width.
266  /// For each x value in index space, the y range in index space of the dilated region is computed.
267  /// This function should store the data in @c mXYData.
268  ///
269  /// @param step The step size for setting the XY range data, defaults to 1.
270  /// @note Function to be implemented by derived classes to set XY range data.
271  /// This function is called at most 4 times within @c iterate().
272  void setXYRangeData(const Index&) {}
273 
274  /// @brief Checks if the tile of a given dimension can possibly fit within the region.
275  ///
276  /// The derived class does not need to implement it if the default behavior is acceptable,
277  /// which assumes a tile can always possibly fit.
278  ///
279  /// @param dim The dimension of the tile in which to check if the tile fits.
280  /// @note This is meant as a short-circuting method: if a tile of a given dimension
281  /// can't fit then @c iterate will not try to populate the level set with background
282  /// tiles of this dimension.
283  /// @return true if the tile can possibly fit; otherwise false.
284  inline bool tileCanFit(const Index&) const { return true; }
285 
286  // distance in index space
287  /// @brief Computes the signed distance from a point to the convex region in index space.
288  ///
289  /// @param p The point in 3D space for which to compute the signed distance.
290  inline ValueT signedDistance(const Vec3T&) const { return ValueT(0); }
291 
292  /// @brief Computes the signed distance for tiles in index space,
293  /// considering the center of the tile.
294  /// This method is optional to override and defaults to @c signedDistance.
295  ///
296  /// @param p The point at the center of the tile in 3D space.
297  /// @note This can be useful for cases that build objects from multiple primitives, e.g.
298  /// dilated mesh is built by constructing and unioning _open_ prisms and _open_ tube wedges.
299  /// A tile might not fully fit in an open prism but might fit in the union of a prism and wedge,
300  /// and so in this case it might make sense to use the sdf for an offset triangle on tiles
301  /// during the open prism scan.
302  inline ValueT
304  {
305  return static_cast<const Derived*>(this)->signedDistance(p);
306  }
307 
308  /// @brief Find where a vertical infinite line intersects
309  /// a convex region dilated by the half width.
310  ///
311  /// @param[out] zb Reference to the z ordinate where the bottom intersection occurs.
312  /// @param[out] zt Reference to the z ordinate where the top intersection occurs.
313  /// @param[in] x The x ordinate of the infinite line.
314  /// @param[in] y The y ordinate of the infinite line.
315  /// @return true if an intersection occurs; otherwise false.
316  /// @note The derived class can override this lambda to implement different behavior for degenerate cases.
317  std::function<bool(ValueT&, ValueT&, const ValueT&, const ValueT&)> bottomTop =
318  [](ValueT&, ValueT&, const ValueT&, const ValueT&) { return false; };
319 
320  // ------------ utilities ------------
321 
322  /// @brief Rounds an input scalar up to the nearest valid ordinate of tile of a specified size.
323  /// @param x Input value.
324  /// @param step Tile step size.
325  /// @return The ceiling of the value based on the tile size.
326  inline static ValueT
327  tileCeil(const ValueT& x, const ValueT& step)
328  {
329  const ValueT offset = ValueT(0.5) * (step - ValueT(1));
330 
331  return step == ValueT(1)
332  ? static_cast<ValueT>(math::Ceil(perturbDown(x)))
333  : step * static_cast<ValueT>(math::Ceil(perturbDown((x - offset)/step))) + offset;
334  }
335 
336  /// @brief Rounds an input scalar up to the nearest valid ordinate of tile of a specified size.
337  /// @tparam T Any integral type (int, unsigned int, size_t, etc.)
338  /// @param x Input value.
339  /// @param step Tile step size.
340  /// @return The ceiling of the value based on the tile size.
341  template <typename T>
342  inline static ValueT
343  tileCeil(const ValueT& x, const T& step)
344  {
345  static_assert(std::is_integral<T>::value, "Index must be an integral type");
346 
347  const ValueT s = static_cast<ValueT>(step);
348 
349  return tileCeil(x, s);
350  }
351 
352  /// @brief Rounds an input scalar down to the nearest valid ordinate of tile of a specified size.
353  /// @param x Input value.
354  /// @param step Tile step size.
355  /// @return The ceiling of the value based on the tile size.
356  inline static ValueT
357  tileFloor(const ValueT& x, const ValueT& step)
358  {
359  const ValueT offset = ValueT(0.5) * (step - ValueT(1));
360 
361  return step == ValueT(1)
362  ? static_cast<ValueT>(math::Floor(perturbUp(x)))
363  : step * static_cast<ValueT>(math::Floor(perturbUp((x - offset)/step))) + offset;
364  }
365 
366  /// @brief Rounds an input scalar down to the nearest valid ordinate of tile of a specified size.
367  /// @tparam T Any integral type (int, unsigned int, size_t, etc.)
368  /// @param x Input value.
369  /// @param step Tile step size.
370  /// @return The ceiling of the value based on the tile size.
371  template <typename T>
372  inline static ValueT
373  tileFloor(const ValueT& x, const T& step)
374  {
375  static_assert(std::is_integral<T>::value, "Index must be an integral type");
376 
377  const ValueT s = static_cast<ValueT>(step);
378 
379  return tileFloor(x, s);
380  }
381 
382  /// @brief Computes the bottom y-coordinate of a circle at a given x position.
383  /// @param x0 X-coordinate of the circle's center.
384  /// @param y0 Y-coordinate of the circle's center.
385  /// @param r Radius of the circle.
386  /// @param x X-coordinate for which to compute the bottom y-coordinate.
387  /// @return The y-coordinate at the bottom of the circle for the given x position.
388  inline static ValueT
389  circleBottom(const ValueT& x0, const ValueT& y0,
390  const ValueT& r, const ValueT& x)
391  {
392  return y0 - math::Sqrt(math::Pow2(r) - math::Pow2(x-x0));
393  }
394 
395  /// @brief Computes the top y-coordinate of a circle at a given x position.
396  /// @param x0 X-coordinate of the circle's center.
397  /// @param y0 Y-coordinate of the circle's center.
398  /// @param r Radius of the circle.
399  /// @param x X-coordinate for which to compute the top y-coordinate.
400  /// @return The y-coordinate at the top of the circle for the given x position.
401  inline static ValueT
402  circleTop(const ValueT& x0, const ValueT& y0,
403  const ValueT& r, const ValueT& x)
404  {
405  return y0 + math::Sqrt(math::Pow2(r) - math::Pow2(x-x0));
406  }
407 
408  /// @brief Computes the bottom z-coordinate of a sphere at a given (x, y) position.
409  /// @param x0 X-coordinate of the sphere's center.
410  /// @param y0 Y-coordinate of the sphere's center.
411  /// @param z0 Z-coordinate of the sphere's center.
412  /// @param r Radius of the sphere.
413  /// @param x X-coordinate for which to compute the bottom z-coordinate.
414  /// @param y Y-coordinate for which to compute the bottom z-coordinate.
415  /// @return The z-coordinate at the bottom of the sphere for the given (x, y) position.
416  inline static ValueT
417  sphereBottom(const ValueT& x0, const ValueT& y0, const ValueT& z0,
418  const ValueT& r, const ValueT& x, const ValueT& y)
419  {
420  return z0 - math::Sqrt(math::Pow2(r) - math::Pow2(x-x0) - math::Pow2(y-y0));
421  }
422 
423  /// @brief Computes the top z-coordinate of a sphere at a given (x, y) position.
424  /// @param x0 X-coordinate of the sphere's center.
425  /// @param y0 Y-coordinate of the sphere's center.
426  /// @param z0 Z-coordinate of the sphere's center.
427  /// @param r Radius of the sphere.
428  /// @param x X-coordinate for which to compute the top z-coordinate.
429  /// @param y Y-coordinate for which to compute the top z-coordinate.
430  /// @return The z-coordinate at the top of the sphere for the given (x, y) position.
431  inline static ValueT
432  sphereTop(const ValueT& x0, const ValueT& y0, const ValueT& z0,
433  const ValueT& r, const ValueT& x, const ValueT& y)
434  {
435  return z0 + math::Sqrt(math::Pow2(r) - math::Pow2(x-x0) - math::Pow2(y-y0));
436  }
437 
438  // ------------ nested classes ------------
439 
440  /// @brief Class that stores endpoints of a y range for each x value within a specified range and step size.
441  /// @details This class tracks y ranges (defined by ymin and ymax) for each x value over a defined interval,
442  /// using a configurable step size.
443  /// It allows updating, expanding, and resetting the y ranges, as well as merging data from other instances
444  /// and trimming invalid entries.
446  {
447 
448  public:
449 
450  XYRangeData() = default;
451 
452  /// @brief Constructor that sets the x range to span a given interval with a specific step size.
453  /// This initializes all y ranges as empty.
454  /// @param xmin The lower bound of the x range.
455  /// @param xmax The upper bound of the x range.
456  /// @param step The step size between x values. Defaults to 1.
457  XYRangeData(const ValueT& xmin, const ValueT& xmax, const Index& step = 1)
458  {
459  reset(xmin, xmax, step);
460  }
461 
462  /// @brief Expands the y range for a given x value by updating the minimum and maximum
463  /// y values if the new ones extend the current range.
464  /// @param x The x value.
465  /// @param ymin The new minimum y value to compare with and possibly update
466  /// the current minimum at x.
467  /// @param ymax The new maximum y value to compare with and possibly update
468  /// the current maximum at x.
469  inline void
470  expandYRange(const ValueT& x, const ValueT& ymin, const ValueT& ymax)
471  {
472  expandYMin(x, ymin);
473  expandYMax(x, ymax);
474  }
475 
476  /// @brief Sets the minimum y value for a given x value, if the provided ymin
477  /// is smaller than the current value.
478  /// @param x The x value.
479  /// @param ymin The minimum y value to possibly be set.
480  inline void
481  expandYMin(const ValueT& x, const ValueT& ymin)
482  {
483  const Index i = worldToIndex(x);
484 
485  if (std::isfinite(ymin) && ymin < mYMins[i])
486  mYMins[i] = ymin;
487  }
488 
489  /// @brief Sets the maximum y value for a given x value, if the provided ymax
490  /// is larger than the current value.
491  /// @param x The x value.
492  /// @param ymax The maximum y value to possibly be set.
493  inline void
494  expandYMax(const ValueT& x, const ValueT& ymax)
495  {
496  const Index i = worldToIndex(x);
497 
498  if (std::isfinite(ymax) && ymax > mYMaxs[i])
499  mYMaxs[i] = ymax;
500  }
501 
502  /// @brief Expands the y range for a given x by adjusting ymin or ymax if the
503  /// given y is smaller or larger.
504  /// @param x The x value.
505  /// @param y The y value to use for expanding the range.
506  inline void
507  expandYRange(const ValueT& x, const ValueT& y)
508  {
509  if (std::isfinite(y)) {
510  const Index i = worldToIndex(x);
511 
512  if (y < mYMins[i])
513  mYMins[i] = y;
514 
515  if (y > mYMaxs[i])
516  mYMaxs[i] = y;
517  }
518  }
519 
520  /// @brief Set the minimum y value for a given x value,
521  /// even if it is larger than the current value.
522  /// @param x The x value.
523  /// @param ymin The minimum y value to reset.
524  inline void
525  setYMin(const ValueT& x, const ValueT& ymin)
526  {
527  const Index i = worldToIndex(x);
528 
529  mYMins[i] = ymin;
530  }
531 
532  /// @brief Set the maximum y value for a given x value,
533  /// even if it is smaller than the current value.
534  /// @param x The x value.
535  /// @param ymax The maximum y value to reset.
536  inline void
537  setYMax(const ValueT& x, const ValueT& ymax)
538  {
539  const Index i = worldToIndex(x);
540 
541  mYMaxs[i] = ymax;
542  }
543 
544  /// @brief Clears the y range for a given x value, setting it to an empty interval.
545  /// @param x The x value.
546  inline void
548  {
549  const Index i = worldToIndex(x);
550 
551  mYMins[i] = MAXVALUE;
552  mYMaxs[i] = MINVALUE;
553  }
554 
555  /// @brief Clears the data container
556  inline void
558  {
559  mStep = 1;
560  mStepInv = ValueT(1);
561 
562  mXStart = MAXVALUE;
563  mXEnd = MINVALUE;
564 
565  mSize = 0;
566 
567  mYMins.assign(mSize, MAXVALUE);
568  mYMaxs.assign(mSize, MINVALUE);
569  }
570 
571  /// @brief Resets the x range to span a given interval with a specific step size.
572  /// This initializes all y ranges as empty.
573  /// @param xmin The lower bound of the x range.
574  /// @param xmax The upper bound of the x range.
575  /// @param step The step size between x values. Defaults to 1.
576  inline void
577  reset(const ValueT& xmin, const ValueT& xmax, const Index& step = 1)
578  {
579  assert(step != 0);
580 
581  mStep = step;
582  mStepInv = ValueT(1)/static_cast<ValueT>(mStep);
583 
584  mXStart = tileCeil(xmin, mStep);
585  mXEnd = tileFloor(xmax, mStep);
586 
587  mSize = 1 + indexDistance(mXEnd, mXStart);
588 
589  mYMins.assign(mSize, MAXVALUE);
590  mYMaxs.assign(mSize, MINVALUE);
591  }
592 
593  /// @brief Retrieves the step size used for the x values.
594  /// @return The step size.
595  inline Index step() const { return mStep; }
596 
597  /// @brief Returns the number of x points in the current range.
598  /// @return The size of the x range.
599  inline Index size() const { return mSize; }
600 
601  /// @brief Retrieves the starting x value in the range.
602  /// @return The start of the x range.
603  inline ValueT start() const { return mXStart; }
604 
605  /// @brief Retrieves the ending x value in the range.
606  /// @return The end of the x range.
607  inline ValueT end() const { return mXEnd; }
608 
609  /// @brief Converts an index to its corresponding x value.
610  /// @param i The index value.
611  /// @return The corresponding x value.
612  inline ValueT getX(const Index& i) const { return indexToWorld(i); }
613 
614  /// @brief Gets the minimum y value for a given index.
615  /// @param i The index value.
616  /// @return The minimum y value.
617  inline ValueT getYMin(const Index& i) const { assert(i < mSize); return mYMins[i]; }
618 
619  /// @brief Gets the maximum y value for a given index.
620  /// @param i The index value.
621  /// @return The maximum y value.
622  inline ValueT getYMax(const Index& i) const { assert(i < mSize); return mYMaxs[i]; }
623 
624  /// @brief Gets the minimum y value for a given x value.
625  /// @param x The x value.
626  /// @return The minimum y value at the given x.
627  /// @note @c x is rounded to the nearest value in the x range.
628  inline ValueT getYMin(const ValueT& x) const { return mYMins[worldToIndex(x)]; }
629 
630  /// @brief Gets the maximum y value for a given x value.
631  /// @param x The x value.
632  /// @return The maximum y value at the given x.
633  /// @note @c x is rounded to the nearest value in the x range.
634  inline ValueT getYMax(const ValueT& x) const { return mYMaxs[worldToIndex(x)]; }
635 
636  /// @brief Retrieves the x, ymin, and ymax values for a given index.
637  /// @param x Output parameter for the x value.
638  /// @param ymin Output parameter for the minimum y value.
639  /// @param ymax Output parameter for the maximum y value.
640  /// @param i The index to query.
641  inline void
642  XYData(ValueT& x, ValueT& ymin, ValueT& ymax, const Index& i) const
643  {
644  x = indexToWorld(i);
645  ymin = mYMins[i];
646  ymax = mYMaxs[i];
647  }
648 
649  /// @brief Returns @c true if the container has no x values or if all y ranges are empty.
650  inline bool isEmpty() const
651  {
652  if (mSize == 0)
653  return true;
654 
655  for (Index i = 0; i < mSize; ++i) {
656  if (mYMins[i] <= mYMaxs[i])
657  return false;
658  }
659 
660  return true;
661  }
662 
663  /// @brief Merges another XYRangeData into the current instance by combining y ranges
664  /// over the overlapping x range.
665  /// @param xydata The XYRangeData to merge with.
666  inline void
667  merge(const XYRangeData& xydata)
668  {
669  assert(mStep == xydata.step());
670 
671  const ValueT start = xydata.start(), end = xydata.end();
672 
673  const std::vector<ValueT>& ymins = xydata.mYMins;
674  const std::vector<ValueT>& ymaxs = xydata.mYMaxs;
675 
676  if (start < mXStart) {
677  const Index n = indexDistance(mXStart, start);
678  mYMins.insert(mYMins.begin(), n, MAXVALUE);
679  mYMaxs.insert(mYMaxs.begin(), n, MINVALUE);
680  mXStart = start;
681  }
682 
683  if (mXEnd < end) {
684  const Index m = indexDistance(end, mXEnd);
685  mYMins.insert(mYMins.end(), m, MAXVALUE);
686  mYMaxs.insert(mYMaxs.end(), m, MINVALUE);
687  mXEnd = end;
688  }
689 
690  mSize = 1 + indexDistance(mXEnd, mXStart);
691 
692  const Index offset = start < mXStart ? 0 : indexDistance(start, mXStart);
693  for (Index i = 0, j = offset; i < ymins.size(); ++i, ++j) {
694  if (mYMins[j] > ymins[i]) { mYMins[j] = ymins[i]; }
695  if (mYMaxs[j] < ymaxs[i]) { mYMaxs[j] = ymaxs[i]; }
696  }
697  }
698 
699  /// @brief Trims the x range by removing empty or invalid y ranges from the beginning and end.
700  /// Truncates the range if all values are invalid.
701  inline void
703  {
704  Index i = 0;
705  while(i < mSize) {
706  if (mYMins[i] > mYMaxs[i]) i++;
707  else break;
708  }
709 
710  if (i == mSize) {
711  mSize = 0; mXStart = ValueT(0); mXEnd = ValueT(0);
712  mYMins.clear(); mYMaxs.clear();
713  return;
714  }
715 
716  Index j = 0;
717  while(j < mSize) {
718  const Index k = mSize - (j + 1);
719  if (mYMins[k] > mYMaxs[k]) j++;
720  else break;
721  }
722 
723  if (i == 0 && j == 0)
724  return;
725 
726  mSize -= i + j;
727  mXStart += ValueT(i * mStep);
728  mXEnd -= ValueT(j * mStep);
729 
730  if (i > 0) {
731  mYMins.erase(mYMins.begin(), mYMins.begin() + i);
732  mYMaxs.erase(mYMaxs.begin(), mYMaxs.begin() + i);
733  }
734 
735  if (j > 0) {
736  mYMins.erase(mYMins.end() - j, mYMins.end());
737  mYMaxs.erase(mYMaxs.end() - j, mYMaxs.end());
738  }
739  }
740 
741  private:
742 
743  inline static const ValueT
744  MINVALUE = std::numeric_limits<ValueT>::lowest(),
746 
747  inline Index
748  indexDistance(const ValueT& a, const ValueT& b)
749  {
750  return Index(math::Round(mStepInv*math::Abs(a - b)));
751  }
752 
753  inline Index
754  worldToIndex(const ValueT& x) const
755  {
756  const Index i = Index(math::Round(mStepInv*(x - mXStart)));
757  assert(i < mSize);
758 
759  return i;
760  }
761 
762  inline ValueT
763  indexToWorld(const Index i) const
764  {
765  assert(i < mSize);
766 
767  return mXStart + ValueT(i * mStep);
768  }
769 
770  Index mStep, mSize;
771 
772  ValueT mStepInv, mXStart, mXEnd;
773 
774  std::vector<ValueT> mYMins, mYMaxs;
775 
776  }; // class XYRangeData
777 
778  // ------------ protected members ------------
779 
780  XYRangeData mXYData;
781 
782 private:
783 
784 #define EPS 0.0005f
785  inline static ValueT perturbDown(const ValueT& x) { return x - ValueT(EPS); }
786  inline static ValueT perturbUp(const ValueT& x) { return x + ValueT(EPS); }
787 #undef EPS
788 
789  inline static ValueT
790  voxelCeil(const ValueT& x)
791  {
792  return static_cast<ValueT>(math::Ceil(perturbDown(x)));
793  }
794 
795  inline static ValueT
796  voxelFloor(const ValueT& x)
797  {
798  return static_cast<ValueT>(math::Floor(perturbUp(x)));
799  }
800 
801  // skips the need for negative tile population and internal leap frogging
802  inline void thinIterate()
803  {
804  invokeSetXYRangeData();
805 
806  // false means disable internal leap frogging
807  iterateXYZ<false>();
808  }
809 
810  template <typename ChainT>
811  inline void iterateTile()
812  {
813  using NodeT = typename ChainT::Back;
814  using PoppedChainT = typename ChainT::PopBack;
815 
816  static const Index DIM = NodeT::DIM;
817 
818  // only attempt to add negative background tiles at this level if they can fit
819  if (invokeTileCanFit(DIM)) {
820  invokeSetXYRangeData(DIM);
821 
822  tileIterateXYZ<NodeT>();
823  }
824 
825  if constexpr (!std::is_same_v<PoppedChainT, openvdb::TypeList<>>) {
826  iterateTile<PoppedChainT>();
827  }
828  }
829 
830  inline void
831  iterateLeaf()
832  {
833  invokeSetXYRangeData();
834 
835  // true means enable internal leap frogging
836  iterateXYZ<true>();
837  }
838 
839  template <bool LeapFrog = false>
840  void
841  iterateXYZ()
842  {
843  if (mXYData.isEmpty())
844  return;
845 
846  // borrowing parallel logic from tools/LevelSetSphere.h
847 
848  const Index n = mXYData.size();
849 
850  if (mSerial) {
851  CacheLastLeafAccessor acc(getTree());
852  for (Index i = 0; i < n; ++i) {
853  if (mInterrupter && !(i & ((1 << 7) - 1)) && !checkInterrupter())
854  return;
855 
856  iterateYZ<LeapFrog>(i, acc);
857  }
858  } else {
859  tbb::enumerable_thread_specific<TreeT> pool(getTree());
860 
861  auto kernel = [&](const tbb::blocked_range<Index>& rng) {
862  TreeT &tree = pool.local();
863  CacheLastLeafAccessor acc(tree);
864 
865  if (!checkInterrupter())
866  return;
867 
868  for (Index i = rng.begin(); i != rng.end(); ++i) {
869  if constexpr (LeapFrog)
870  iterateNoTilesYZ(i, acc);
871  else
872  iterateYZ<false>(i, acc);
873  }
874  };
875 
876  tbb::parallel_for(tbb::blocked_range<Index>(Index(0), n, Index(128)), kernel);
877  using RangeT = tbb::blocked_range<typename tbb::enumerable_thread_specific<TreeT>::iterator>;
878  struct Op {
879  const bool mDelete;
880  TreeT *mTree;
881  Op(TreeT &tree) : mDelete(false), mTree(&tree) {}
882  Op(const Op& other, tbb::split) : mDelete(true), mTree(new TreeT(other.mTree->background())) {}
883  ~Op() { if (mDelete) delete mTree; }
884  void operator()(const RangeT &r) { for (auto i=r.begin(); i!=r.end(); ++i) this->merge(*i);}
885  void join(Op &other) { this->merge(*(other.mTree)); }
886  void merge(TreeT &tree) { mTree->merge(tree, MERGE_ACTIVE_STATES); }
887  } op( getTree() );
888  tbb::parallel_reduce(RangeT(pool.begin(), pool.end(), 4), op);
889  }
890  }
891 
892 
893  // for a given x value, create a filled slice of the object by populating
894  // active voxels and inactive negative background voxels
895  // if we're leap frogging, we may assume the negative background tiles are already populated
896  // for each x ordinate and y-scan range
897  // find the z-range for each y and then populate the grid with distance values
898  template <bool LeapFrog = false>
899  inline void
900  iterateYZ(const Index& i, CacheLastLeafAccessor& acc)
901  {
902  // initialize x value and y-range
903  ValueT x, yb, yt;
904  mXYData.XYData(x, yb, yt, i);
905 
906  if (!std::isfinite(yb) || !std::isfinite(yt))
907  return;
908 
909  ValueT zb, zt;
910 
911  for (ValueT y = voxelCeil(yb); y <= perturbUp(yt); ++y) {
912  if (!bottomTop(zb, zt, x, y))
913  continue;
914 
915  Coord ijk(Int32(x), Int32(y), Int32(0));
916  Vec3T p(x, y, ValueT(0));
917 
918  ijk[2] = Int32(voxelCeil(zb))-1;
919  acc.reset(ijk);
920 
921  for (ValueT z = voxelCeil(zb); z <= perturbUp(zt); ++z) {
922  ijk[2] = Int32(z);
923  const ValueT val = acc.template getValue<1>(ijk);
924 
925  if (val == mNegBg) {
926  if constexpr (LeapFrog) acc.template leapUp<false>(ijk, z);
927  continue;
928  }
929 
930  p[2] = z;
931  const ValueT dist = mVox * invokeSignedDistance(p);
932 
933  if (dist <= mNegBg) {
934  acc.template setValueOff<1,false>(ijk, mNegBg);
935  } else if (dist < val) {
936  acc.template setValueOn<1,false>(ijk, dist);
937  } else { // dist >= val
938  acc.template checkReset<1>(ijk);
939  }
940  }
941  }
942  }
943 
944  // for a given x value, create a hollow slice of the object by only populating active voxels
945  // for each x ordinate and y-scan range
946  // find the z-range for each y and then populate the grid with distance values
947  inline void
948  iterateNoTilesYZ(const Index& i, CacheLastLeafAccessor& acc)
949  {
950  // initialize x value and y-range
951  ValueT x, yb, yt;
952  mXYData.XYData(x, yb, yt, i);
953 
954  if (!std::isfinite(yb) || !std::isfinite(yt))
955  return;
956 
957  ValueT zb, zt;
958 
959  for (ValueT y = voxelCeil(yb); y <= perturbUp(yt); ++y) {
960  if (!bottomTop(zb, zt, x, y))
961  continue;
962 
963  Coord ijk(Int32(x), Int32(y), Int32(0));
964  Vec3T p(x, y, ValueT(0));
965 
966  bool early_break = false;
967  ValueT z_stop;
968 
969  ijk[2] = Int32(voxelCeil(zb))-1;
970  acc.reset(ijk);
971  for (ValueT z = voxelCeil(zb); z <= perturbUp(zt); ++z) {
972  ijk[2] = Int32(z);
973  p[2] = z;
974  const ValueT dist = mVox * invokeSignedDistance(p);
975 
976  if (dist <= mNegBg) {
977  early_break = true;
978  z_stop = z;
979  break;
980  } else if (dist < mBg) {
981  acc.template setValueOn<1>(ijk, dist);
982  } else { // dist >= mBg
983  acc.template checkReset<1>(ijk);
984  }
985  }
986  if (early_break) {
987  ijk[2] = Int32(voxelFloor(zt))+1;
988  acc.reset(ijk);
989  for (ValueT z = voxelFloor(zt); z > z_stop; --z) {
990  ijk[2] = Int32(z);
991  p[2] = z;
992  const ValueT dist = mVox * invokeSignedDistance(p);
993 
994  if (dist <= mNegBg) {
995  break;
996  } else if (dist < mBg) {
997  acc.template setValueOn<-1>(ijk, dist);
998  } else { // dist >= mBg
999  acc.template checkReset<-1>(ijk);
1000  }
1001  }
1002  }
1003  }
1004  }
1005 
1006  template <typename NodeT>
1007  void
1008  tileIterateXYZ()
1009  {
1010  if (mXYData.isEmpty())
1011  return;
1012 
1013  AccessorT acc(getTree());
1014  for (Index i = 0; i < mXYData.size(); ++i) {
1015  if (mInterrupter && !(i & ((1 << 7) - 1)) && !checkInterrupter())
1016  return;
1017 
1018  tileIterateYZ<NodeT>(i, acc);
1019  }
1020  }
1021 
1022  template <typename NodeT>
1023  inline void
1024  tileIterateYZ(const Index& i, AccessorT& acc)
1025  {
1026  // initialize x value and y-range
1027  ValueT x, yb, yt;
1028  mXYData.XYData(x, yb, yt, i);
1029 
1030  if (!std::isfinite(yb) || !std::isfinite(yt))
1031  return;
1032 
1033  static const Index TILESIZE = NodeT::DIM;
1034 
1035  ValueT zb, zt;
1036 
1037  for (ValueT y = tileCeil(yb, TILESIZE); y <= perturbUp(yt); y += TILESIZE) {
1038  if (!bottomTop(zb, zt, x, y))
1039  continue;
1040 
1041  Coord ijk(Int32(x), Int32(y), Int32(0));
1042  Vec3T p(x, y, ValueT(0));
1043 
1044  bool tiles_added = false;
1045  ValueT z = tileCeil(zb, TILESIZE) - 2*TILESIZE;
1046  while (z <= tileFloor(zt, TILESIZE) + TILESIZE) {
1047  ijk[2] = Int32(z);
1048  p[2] = z;
1049 
1050  if (leapFrogToNextTile<NodeT, 1>(ijk, z, acc))
1051  continue;
1052 
1053  if (addTile<NodeT>(p, ijk, acc)) tiles_added = true;
1054  else if (tiles_added) break;
1055  }
1056  }
1057  }
1058 
1059  template <typename NodeT, int dir>
1060  inline bool
1061  leapFrogToNextTile(const Coord& ijk, ValueT& z, AccessorT& acc) const
1062  {
1063  static const int offset = NodeT::DIM;
1064  static const int nodeDepth = int(TreeT::DEPTH - NodeT::LEVEL - 1);
1065 
1066  // we have not encountered an already populated tile
1067  if (acc.getValue(ijk) != mNegBg) {
1068  z += dir*offset;
1069  return false;
1070  }
1071 
1072  const int depth = acc.getValueDepth(ijk);
1073 
1074  // tile is not larger than current node
1075  if (depth >= nodeDepth) {
1076  z += dir*offset;
1077  return false;
1078  }
1079 
1080  const ValueT sz = ValueT(mTileSizes[depth]);
1081 
1082  z = dir > 0
1083  ? sz * ValueT(math::Ceil(z/sz)) + ValueT(0.5)*(offset-1)
1084  : sz * ValueT(math::Floor(z/sz)) - ValueT(0.5)*(offset+1);
1085 
1086  return true;
1087  }
1088 
1089  // add negative background tile inside the object if it fits and return true iff it was added
1090  template<typename NodeT>
1091  inline bool
1092  addTile(const Vec3T& p, const Coord& ijk, AccessorT& acc)
1093  {
1094  static const Index LEVEL = NodeT::LEVEL + 1;
1095 
1096  if (tileFits<NodeT>(p)) {
1097  acc.addTile(LEVEL, ijk, mNegBg, false);
1098  return true;
1099  } else {
1100  return false;
1101  }
1102  }
1103 
1104  template <typename NodeT>
1105  inline bool
1106  tileFits(const Vec3T& p) const
1107  {
1108  static const Index TILESIZE = NodeT::DIM;
1109 
1110  static const ValueT R1 = ValueT(0.500)*(TILESIZE-1),
1111  R2 = ValueT(0.866)*(TILESIZE-1);
1112 
1113  const ValueT dist = invokeTilePointSignedDistance(p);
1114 
1115  // fast positive criterion: circumscribed ball is in the object
1116  if (dist <= -R2-mHw)
1117  return true;
1118 
1119  // fast negative criterion: inscribed ball is not in the object
1120  else if (dist >= -R1-mHw)
1121  return false;
1122 
1123  // convexity: the tile is in the object iff all corners are in the object
1124  return invokeTilePointSignedDistance(p + Vec3T(-R1, -R1, -R1)) < -mHw
1125  && invokeTilePointSignedDistance(p + Vec3T(-R1, -R1, R1)) < -mHw
1126  && invokeTilePointSignedDistance(p + Vec3T(-R1, R1, -R1)) < -mHw
1127  && invokeTilePointSignedDistance(p + Vec3T(-R1, R1, R1)) < -mHw
1128  && invokeTilePointSignedDistance(p + Vec3T(R1, -R1, -R1)) < -mHw
1129  && invokeTilePointSignedDistance(p + Vec3T(R1, -R1, R1)) < -mHw
1130  && invokeTilePointSignedDistance(p + Vec3T(R1, R1, -R1)) < -mHw
1131  && invokeTilePointSignedDistance(p + Vec3T(R1, R1, R1)) < -mHw;
1132  }
1133 
1134  inline void
1135  invokeSetXYRangeData(const Index& step = 1)
1136  {
1137  mXYData.clear();
1138  static_cast<Derived*>(this)->setXYRangeData(step);
1139  }
1140 
1141  inline bool
1142  invokeTileCanFit(const Index& dim) const
1143  {
1144  return static_cast<const Derived*>(this)->tileCanFit(dim);
1145  }
1146 
1147  inline ValueT
1148  invokeSignedDistance(const Vec3T& p) const
1149  {
1150  return static_cast<const Derived*>(this)->signedDistance(p);
1151  }
1152 
1153  inline ValueT
1154  invokeTilePointSignedDistance(const Vec3T& p) const
1155  {
1156  return static_cast<const Derived*>(this)->tilePointSignedDistance(p);
1157  }
1158 
1159  // misc
1160 
1161  static inline std::vector<int>
1162  treeTileSizes()
1163  {
1164  // iterate over all non root nodes
1165  using ChainT = typename NodeChainT::PopBack;
1166 
1167  std::vector<int> sizes;
1168  doTreeTileSizes<ChainT>(sizes);
1169 
1170  return sizes;
1171  }
1172 
1173  template <typename ChainT>
1174  static inline void
1175  doTreeTileSizes(std::vector<int>& sizes)
1176  {
1177  using NodeT = typename ChainT::Back;
1178  using PoppedChainT = typename ChainT::PopBack;
1179 
1180  sizes.push_back(NodeT::DIM);
1181 
1182  if constexpr (!std::is_same_v<PoppedChainT, openvdb::TypeList<>>) {
1183  doTreeTileSizes<PoppedChainT>(sizes);
1184  }
1185  }
1186 
1187  inline bool
1188  checkInterrupter()
1189  {
1190  if (util::wasInterrupted(mInterrupter)) {
1191  openvdb::thread::cancelGroupExecution();
1192  return false;
1193  }
1194  return true;
1195  }
1196 
1197  inline TreeT& getTree() { return mGrid->tree(); }
1198 
1199  // ------------ private nested classes ------------
1200 
1201  /// @brief A class that caches access to the last leaf node.
1202  /// @tparam TreeT The type of the tree being accessed.
1203  /// @note This class optimizes repeated accesses to the same
1204  /// leaf node by caching the last accessed leaf.
1205  class CacheLastLeafAccessor
1206  {
1207  using NodeMaskType = util::NodeMask<LeafT::LOG2DIM>;
1208 
1209  public:
1210 
1211  /// @brief Constructs the CacheLastLeafAccessor and initializes
1212  /// the internal accessor with the provided tree.
1213  /// @param tree Reference to the tree being accessed.
1214  CacheLastLeafAccessor(TreeT& tree)
1215  : mAcc(tree), mTileSizes(treeTileSizes())
1216  {
1217  }
1218 
1219  /// @brief Resets the cache by caching new leaf data for the given coordinates.
1220  /// @param ijk The coordinate for which to cache the new leaf data.
1221  inline void reset(const Coord& ijk) { cacheNewLeafData(ijk); }
1222 
1223  /// @brief Checks if the given coordinates are at a new leaf position,
1224  /// and resets the cache if needed.
1225  /// @tparam ZDir The direction in the Z-axis (default: 0, other choices are -1 and 1).
1226  /// @param ijk The coordinate to check and potentially reset.
1227  template<int ZDir = 0>
1228  inline void checkReset(const Coord& ijk)
1229  {
1230  if (atNewLeafPos<ZDir>(ijk))
1231  cacheNewLeafData(ijk);
1232  }
1233 
1234  /// @brief Retrieves the value at the given coordinate,
1235  /// checking and resetting the cache if needed.
1236  /// @tparam ZDir The direction in the Z-axis (default: 0, other choices are -1 and 1).
1237  /// @tparam Check If true, checks if the coordinate is at a new leaf position (default: true).
1238  /// @param ijk The coordinate for which to retrieve the value.
1239  /// @return The value at the specified coordinate.
1240  template<int ZDir = 0, bool Check = true>
1241  inline ValueT getValue(const Coord& ijk)
1242  {
1243  if (Check && atNewLeafPos<ZDir>(ijk))
1244  cacheNewLeafData(ijk);
1245 
1246  return mLeaf
1247  ? mLeafData[coordToOffset(ijk)]
1248  : mAcc.getValue(ijk);
1249  }
1250 
1251  /// @brief Sets the value at the given coordinates and marks the voxel as active,
1252  /// checking the cache if needed.
1253  /// @tparam ZDir The direction in the Z-axis (default: 0, other choices are -1 and 1).
1254  /// @tparam Check If true, checks if the coordinate is at a new leaf position (default: true).
1255  /// @param ijk The coordinate where the value is to be set.
1256  /// @param val The value to set at the specified coordinate.
1257  template<int ZDir = 0, bool Check = true>
1258  inline void setValueOn(const Coord& ijk, const ValueT& val)
1259  {
1260  if (Check && atNewLeafPos<ZDir>(ijk))
1261  cacheNewLeafData(ijk);
1262 
1263  if (mLeaf) {
1264  const Index n = coordToOffset(ijk);
1265  mLeafData[n] = val;
1266  mValueMask->setOn(n);
1267  } else {
1268  mAcc.setValueOn(ijk, val);
1269  cacheNewLeafData(ijk);
1270  }
1271  }
1272 
1273  /// @brief Sets the value at the given coordinate and marks the voxel as inactive,
1274  /// checking the cache if needed.
1275  /// @tparam ZDir The direction in the Z-axis (default: 0, other choices are -1 and 1).
1276  /// @tparam Check If true, checks if the coordinate is at a new leaf position (default: true).
1277  /// @param ijk The coordinates where the value is to be set.
1278  /// @param val The value to set at the specified coordinate.
1279  template<int ZDir = 0, bool Check = true>
1280  inline void setValueOff(const Coord& ijk, const ValueT& val)
1281  {
1282  if (Check && atNewLeafPos<ZDir>(ijk))
1283  cacheNewLeafData(ijk);
1284 
1285  if (mLeaf) {
1286  const Index n = coordToOffset(ijk);
1287  mLeafData[n] = val;
1288  mValueMask->setOff(n);
1289  } else {
1290  mAcc.setValueOff(ijk, val);
1291  cacheNewLeafData(ijk);
1292  }
1293  }
1294 
1295  /// @brief Return @c true if the value of a voxel resides at the (possibly cached)
1296  /// leaf level of the tree, i.e., if it is not a tile value.
1297  /// @tparam ZDir The direction in the Z-axis (default: 0, other choices are -1 and 1).
1298  /// @tparam Check If true, checks if the coordinate is at a new leaf position (default: true).
1299  /// @param ijk The coordinate of the voxel to check.
1300  /// @return True if the voxel exists, otherwise false.
1301  template<int ZDir = 0, bool Check = true>
1302  inline bool isVoxel(const Coord& ijk)
1303  {
1304  return Check && atNewLeafPos<ZDir>(ijk) ? mLeaf != nullptr : mAcc.isVoxel(ijk);
1305  }
1306 
1307  /// @brief If the input coordinate lies within a tile,
1308  /// the input z value is set to the top of the tile bounding box, in index space.
1309  /// @tparam Check If true, checks if the voxel exists before leaping (default: true).
1310  /// @param ijk The coordinate to be examined.
1311  /// @param z The Z-coordinate to be adjusted.
1312  template<bool Check = true>
1313  inline void leapUp(const Coord& ijk, ValueT& z)
1314  {
1315  if (isVoxel<1,Check>(ijk))
1316  return;
1317 
1318  const int depth = mAcc.getValueDepth(ijk);
1319  const ValueT sz = ValueT(mTileSizes[depth]);
1320 
1321  z = sz * ValueT(math::Ceil((z+ValueT(1))/sz)) - ValueT(1);
1322  }
1323 
1324  private:
1325 
1326  static const Index
1327  DIM = LeafT::DIM,
1328  LOG2DIM = LeafT::LOG2DIM;
1329 
1330  template<int ZDir>
1331  bool atNewLeafPos(const Coord& ijk) const
1332  {
1333  if constexpr (ZDir == -1) {
1334  // assumes value just above has been cached already!
1335  return (ijk[2] & (DIM-1u)) == DIM-1u;
1336  } else if constexpr (ZDir == 1) {
1337  // assumes value just below has been cached already!
1338  return (ijk[2] & (DIM-1u)) == 0;
1339  } else {
1340  return Coord::lessThan(ijk, mOrigin)
1341  || Coord::lessThan(mOrigin.offsetBy(DIM-1u), ijk);
1342  }
1343  }
1344 
1345  inline void cacheNewLeafData(const Coord& ijk)
1346  {
1347  mLeaf = mAcc.probeLeaf(ijk);
1348  mOrigin = ijk & (~(DIM - 1));
1349 
1350  if (mLeaf) {
1351  mLeafData = mLeaf->buffer().data();
1352  mValueMask = &(mLeaf->getValueMask());
1353  } else {
1354  mLeafData = nullptr;
1355  }
1356  }
1357 
1358  inline static Index coordToOffset(const Coord& ijk)
1359  {
1360  return ((ijk[0] & (DIM-1u)) << 2*LOG2DIM)
1361  + ((ijk[1] & (DIM-1u)) << LOG2DIM)
1362  + (ijk[2] & (DIM-1u));
1363  }
1364 
1365  AccessorT mAcc;
1366  LeafT* mLeaf;
1367 
1368  ValueT* mLeafData;
1369  NodeMaskType* mValueMask;
1370  Coord mOrigin;
1371 
1372  const std::vector<int> mTileSizes;
1373 
1374  }; // class CacheLastLeafAccessor
1375 
1376  // ------------ private members ------------
1377 
1378  GridPtr mGrid;
1379 
1380  const std::vector<int> mTileSizes = treeTileSizes();
1381 
1382  const ValueT mVox, mHw, mBg, mNegBg;
1383 
1384  // misc
1385 
1386  const bool mSerial;
1387 
1388  InterruptType* mInterrupter;
1389 
1390 }; // class ConvexVoxelizer
1391 
1392 
1393 } // namespace tools
1394 } // namespace OPENVDB_VERSION_NAME
1395 } // namespace openvdb
1396 
1397 #endif // OPENVDB_TOOLS_CONVEXVOXELIZER_HAS_BEEN_INCLUDED
GLuint GLsizei const GLuint const GLintptr const GLsizeiptr * sizes
Definition: glcorearb.h:2621
typedef int(APIENTRYP RE_PFNGLXSWAPINTERVALSGIPROC)(int)
GA_API const UT_StringHolder dist
ValueT start() const
Retrieves the starting x value in the range.
static ValueT sphereTop(const ValueT &x0, const ValueT &y0, const ValueT &z0, const ValueT &r, const ValueT &x, const ValueT &y)
Computes the top z-coordinate of a sphere at a given (x, y) position.
ValueT getYMin(const ValueT &x) const
Gets the minimum y value for a given x value.
void expandYRange(const ValueT &x, const ValueT &ymin, const ValueT &ymax)
Expands the y range for a given x value by updating the minimum and maximum y values if the new ones ...
int Ceil(float x)
Return the ceiling of x.
Definition: Math.h:927
void expandYRange(const ValueT &x, const ValueT &y)
Expands the y range for a given x by adjusting ymin or ymax if the given y is smaller or larger...
Type Pow2(Type x)
Return x2.
Definition: Math.h:573
ConvexVoxelizer(GridPtr &grid, const bool &threaded=false, InterruptType *interrupter=nullptr)
Constructor.
GLuint start
Definition: glcorearb.h:475
GLsizei const GLfloat * value
Definition: glcorearb.h:824
Index step() const
Retrieves the step size used for the x values.
GLdouble GLdouble GLdouble z
Definition: glcorearb.h:848
void trim()
Trims the x range by removing empty or invalid y ranges from the beginning and end. Truncates the range if all values are invalid.
static ValueT circleBottom(const ValueT &x0, const ValueT &y0, const ValueT &r, const ValueT &x)
Computes the bottom y-coordinate of a circle at a given x position.
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1222
GLdouble s
Definition: glad.h:3009
void expandYMin(const ValueT &x, const ValueT &ymin)
Sets the minimum y value for a given x value, if the provided ymin is smaller than the current value...
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:246
void reset(const ValueT &xmin, const ValueT &xmax, const Index &step=1)
Resets the x range to span a given interval with a specific step size. This initializes all y ranges ...
ValueT tilePointSignedDistance(const Vec3T &p) const
Computes the signed distance for tiles in index space, considering the center of the tile...
ValueT voxelSize() const
Return the voxel size of the grid.
GLint y
Definition: glcorearb.h:103
Base class used to generate a grid of type GridType containing a narrow-band level set representation...
void getPos(size_t n, VectorType &xyz) const
Internal class used by derived ConvexVoxelizer classes that make use of PointPartitioner.
__hostdev__ float getValue(uint32_t i) const
Definition: NanoVDB.h:5578
#define EPS
GLdouble n
Definition: glcorearb.h:2008
GLintptr offset
Definition: glcorearb.h:665
static ValueT circleTop(const ValueT &x0, const ValueT &y0, const ValueT &r, const ValueT &x)
Computes the top y-coordinate of a circle at a given x position.
GLboolean reset
Definition: glad.h:5138
Coord Abs(const Coord &xyz)
Definition: Coord.h:518
float Round(float x)
Return x rounded to the nearest integer.
Definition: Math.h:890
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
void expandYMax(const ValueT &x, const ValueT &ymax)
Sets the maximum y value for a given x value, if the provided ymax is larger than the current value...
GLuint GLuint end
Definition: glcorearb.h:475
OIIO_UTIL_API void parallel_for(int32_t begin, int32_t end, function_view< void(int32_t)> task, paropt opt=0)
float Sqrt(float x)
Return the square root of a floating-point value.
Definition: Math.h:832
void setYMax(const ValueT &x, const ValueT &ymax)
Set the maximum y value for a given x value, even if it is smaller than the current value...
bool isEmpty() const
Returns true if the container has no x values or if all y ranges are empty.
ValueT getYMax(const Index &i) const
Gets the maximum y value for a given index.
void clearYRange(const ValueT &x)
Clears the y range for a given x value, setting it to an empty interval.
static ValueT tileFloor(const ValueT &x, const T &step)
Rounds an input scalar down to the nearest valid ordinate of tile of a specified size.
ValueT getYMax(const ValueT &x) const
Gets the maximum y value for a given x value.
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1222
GLint GLenum GLint x
Definition: glcorearb.h:409
Class that stores endpoints of a y range for each x value within a specified range and step size...
MaskT< LOG2DIM > mValueMask
Definition: NanoVDB.h:5738
std::function< bool(ValueT &, ValueT &, const ValueT &, const ValueT &)> bottomTop
Find where a vertical infinite line intersects a convex region dilated by the half width...
static ValueT tileFloor(const ValueT &x, const ValueT &step)
Rounds an input scalar down to the nearest valid ordinate of tile of a specified size.
Propagate the signs of distance values from the active voxels in the narrow band to the inactive valu...
GLint GLint GLsizei GLsizei GLsizei depth
Definition: glcorearb.h:476
GLint j
Definition: glad.h:2733
void merge(const XYRangeData &xydata)
Merges another XYRangeData into the current instance by combining y ranges over the overlapping x ran...
static ValueT tileCeil(const ValueT &x, const ValueT &step)
Rounds an input scalar up to the nearest valid ordinate of tile of a specified size.
ValueT signedDistance(const Vec3T &) const
Computes the signed distance from a point to the convex region in index space.
void signedFloodFill(TreeOrLeafManagerT &tree, bool threaded=true, size_t grainSize=1, Index minLevel=0)
Set the values of all inactive voxels and tiles of a narrow-band level set from the signs of the acti...
void iterate()
The function the derived class calls to create the level set, working in index space other than setti...
void setYMin(const ValueT &x, const ValueT &ymin)
Set the minimum y value for a given x value, even if it is larger than the current value...
GLuint GLfloat * val
Definition: glcorearb.h:1608
ImageBuf OIIO_API max(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
static ValueT tileCeil(const ValueT &x, const T &step)
Rounds an input scalar up to the nearest valid ordinate of tile of a specified size.
void XYData(ValueT &x, ValueT &ymin, ValueT &ymax, const Index &i) const
Retrieves the x, ymin, and ymax values for a given index.
ValueT getX(const Index &i) const
Converts an index to its corresponding x value.
int Floor(float x)
Return the floor of x.
Definition: Math.h:919
OutGridT XformOp bool bool MergePolicy merge
ValueT end() const
Retrieves the ending x value in the range.
GLboolean r
Definition: glcorearb.h:1222
Functions to efficiently merge grids.
ValueT halfWidth() const
Return the half width of the narrow-band level set.
void OIIO_UTIL_API split(string_view str, std::vector< string_view > &result, string_view sep=string_view(), int maxsplit=-1)
bool wasInterrupted(T *i, int percent=-1)
ValueT getYMin(const Index &i) const
Gets the minimum y value for a given index.
PointArray(const std::vector< VectorType > &vec)
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:119
void setXYRangeData(const Index &)
Determines the x bounds in index space of the convex region dilated by the half width. For each x value in index space, the y range in index space of the dilated region is computed. This function should store the data in mXYData.
bool ValueType
Definition: NanoVDB.h:5729
bool tileCanFit(const Index &) const
Checks if the tile of a given dimension can possibly fit within the region.
#define R2(n)
XYRangeData(const ValueT &xmin, const ValueT &xmax, const Index &step=1)
Constructor that sets the x range to span a given interval with a specific step size. This initializes all y ranges as empty.
auto join(It begin, Sentinel end, string_view sep) -> join_view< It, Sentinel >
Definition: format.h:4489
static ValueT sphereBottom(const ValueT &x0, const ValueT &y0, const ValueT &z0, const ValueT &r, const ValueT &x, const ValueT &y)
Computes the bottom z-coordinate of a sphere at a given (x, y) position.
**Note that the tasks the is the thread number *for the pool
Definition: thread.h:646
Index size() const
Returns the number of x points in the current range.