OpenVDB  12.1.0
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>
18 #include <openvdb/thread/Threading.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 
188  static_assert(std::is_floating_point<ValueT>::value);
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 infinte line.
314  /// @param[in] y The y ordinate of the infinte 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
547  clearYRange(const ValueT& x)
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: circumsribed 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
ValueT start() const
Retrieves the starting x value in the range.
Definition: ConvexVoxelizer.h:603
ValueT voxelSize() const
Return the voxel size of the grid.
Definition: ConvexVoxelizer.h:215
void iterate()
The function the derived class calls to create the level set, working in index space other than setti...
Definition: ConvexVoxelizer.h:236
ValueT end() const
Retrieves the ending x value in the range.
Definition: ConvexVoxelizer.h:607
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.
Definition: ConvexVoxelizer.h:327
int Floor(float x)
Return the floor of x.
Definition: Math.h:848
void clearYRange(const ValueT &x)
Clears the y range for a given x value, setting it to an empty interval.
Definition: ConvexVoxelizer.h:547
Coord Abs(const Coord &xyz)
Definition: Coord.h:518
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
float Sqrt(float x)
Return the square root of a floating-point value.
Definition: Math.h:761
Index size() const
Returns the number of x points in the current range.
Definition: ConvexVoxelizer.h:599
ValueT tilePointSignedDistance(const Vec3T &p) const
Computes the signed distance for tiles in index space, considering the center of the tile...
Definition: ConvexVoxelizer.h:303
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.
Definition: ConvexVoxelizer.h:357
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...
Definition: ConvexVoxelizer.h:537
bool isEmpty() const
Returns true if the container has no x values or if all y ranges are empty.
Definition: ConvexVoxelizer.h:650
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.
Definition: ConvexVoxelizer.h:389
ValueT getYMax(const ValueT &x) const
Gets the maximum y value for a given x value.
Definition: ConvexVoxelizer.h:634
ValueT getYMax(const Index &i) const
Gets the maximum y value for a given index.
Definition: ConvexVoxelizer.h:622
bool tileCanFit(const Index &) const
Checks if the tile of a given dimension can possibly fit within the region.
Definition: ConvexVoxelizer.h:284
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.
Definition: ConvexVoxelizer.h:373
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...
Definition: ConvexVoxelizer.h:507
OutGridT XformOp & op
Definition: ValueTransformer.h:139
Index32 Index
Definition: Types.h:54
A list of types (not necessarily unique)
Definition: TypeList.h:577
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.
Definition: ConvexVoxelizer.h:402
openvdb::GridBase::Ptr GridPtr
Definition: Utils.h:44
Class that stores endpoints of a y range for each x value within a specified range and step size...
Definition: ConvexVoxelizer.h:445
ValueT getYMin(const ValueT &x) const
Gets the minimum y value for a given x value.
Definition: ConvexVoxelizer.h:628
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.
Definition: ConvexVoxelizer.h:343
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...
Definition: ConvexVoxelizer.h:481
ValueT getYMin(const Index &i) const
Gets the minimum y value for a given index.
Definition: ConvexVoxelizer.h:617
void clear()
Clears the data container.
Definition: ConvexVoxelizer.h:557
PointArray(const std::vector< VectorType > &vec)
Definition: ConvexVoxelizer.h:47
Base class used to generate a grid of type GridType containing a narrow-band level set representation...
Definition: ConvexVoxelizer.h:170
OutGridT XformOp bool bool MergePolicy merge
Definition: ValueTransformer.h:141
OutGridT XformOp bool threaded
Definition: ValueTransformer.h:140
Propagate the signs of distance values from the active voxels in the narrow band to the inactive valu...
int Ceil(float x)
Return the ceiling of x.
Definition: Math.h:856
Type Pow2(Type x)
Return x2.
Definition: Math.h:548
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...
Definition: SignedFloodFill.h:267
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.
Definition: ConvexVoxelizer.h:272
Definition: Exceptions.h:13
float Round(float x)
Return x rounded to the nearest integer.
Definition: Math.h:819
ValueT halfWidth() const
Return the half width of the narrow-band level set.
Definition: ConvexVoxelizer.h:218
Definition: Mat.h:165
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.
Definition: ConvexVoxelizer.h:417
void getPos(size_t n, VectorType &xyz) const
Definition: ConvexVoxelizer.h:56
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:49
Functions to efficiently merge grids.
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...
Definition: ConvexVoxelizer.h:494
Internal class used by derived ConvexVoxelizer classes that make use of PointPartitioner.
Definition: ConvexVoxelizer.h:41
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 ...
Definition: ConvexVoxelizer.h:470
size_t size() const
Definition: ConvexVoxelizer.h:54
const VectorType & operator[](const Index &i)
Definition: ConvexVoxelizer.h:52
ConvexVoxelizer(GridPtr &grid, const bool &threaded=false, InterruptType *interrupter=nullptr)
Constructor.
Definition: ConvexVoxelizer.h:201
XYRangeData mXYData
Definition: ConvexVoxelizer.h:780
Definition: Vec2.h:23
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.
Definition: ConvexVoxelizer.h:457
void XYData(ValueT &x, ValueT &ymin, ValueT &ymax, const Index &i) const
Retrieves the x, ymin, and ymax values for a given index.
Definition: ConvexVoxelizer.h:642
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.
Definition: ConvexVoxelizer.h:702
void merge(const XYRangeData &xydata)
Merges another XYRangeData into the current instance by combining y ranges over the overlapping x ran...
Definition: ConvexVoxelizer.h:667
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...
Definition: ConvexVoxelizer.h:525
VectorType PosType
Definition: ConvexVoxelizer.h:43
ValueT signedDistance(const Vec3T &) const
Computes the signed distance from a point to the convex region in index space.
Definition: ConvexVoxelizer.h:290
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
ValueT getX(const Index &i) const
Converts an index to its corresponding x value.
Definition: ConvexVoxelizer.h:612
void split(ContainerT &out, const std::string &in, const char delim)
Definition: Name.h:43
Index step() const
Retrieves the step size used for the x values.
Definition: ConvexVoxelizer.h:595
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:110
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 ...
Definition: ConvexVoxelizer.h:577
#define EPS
Definition: ConvexVoxelizer.h:784
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:218
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.
Definition: ConvexVoxelizer.h:432