OpenVDB 12.1.0
Loading...
Searching...
No Matches
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
32namespace openvdb {
34namespace OPENVDB_VERSION_NAME {
35namespace tools {
36
37namespace lvlset {
38
39/// @brief Internal class used by derived @c ConvexVoxelizer classes that make use of PointPartitioner.
40template<typename VectorType>
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
58private:
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
169template <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
182protected:
183
184 using ValueT = typename GridType::ValueType;
187
188 static_assert(std::is_floating_point<ValueT>::value);
189
190public:
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
220private:
221
222 class CacheLastLeafAccessor;
223
224protected:
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
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(),
745 MAXVALUE = std::numeric_limits<ValueT>::max();
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
782private:
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
#define EPS
Definition ConvexVoxelizer.h:784
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
Functions to efficiently merge grids.
Propagate the signs of distance values from the active voxels in the narrow band to the inactive valu...
Definition Vec2.h:24
Definition Vec3.h:25
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
Index size() const
Returns the number of x points in the current range.
Definition ConvexVoxelizer.h:599
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
void trim()
Trims the x range by removing empty or invalid y ranges from the beginning and end....
Definition ConvexVoxelizer.h:702
ValueT end() const
Retrieves the ending x value in the range.
Definition ConvexVoxelizer.h:607
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
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
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
ValueT start() const
Retrieves the starting x value in the range.
Definition ConvexVoxelizer.h:603
ValueT getX(const Index &i) const
Converts an index to its corresponding x value.
Definition ConvexVoxelizer.h:612
void clearYRange(const ValueT &x)
Clears the y range for a given x value, setting it to an empty interval.
Definition ConvexVoxelizer.h:547
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
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....
Definition ConvexVoxelizer.h:457
ValueT getYMax(const Index &i) const
Gets the maximum y value for a given index.
Definition ConvexVoxelizer.h:622
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
ValueT getYMin(const ValueT &x) const
Gets the minimum y value for a given x value.
Definition ConvexVoxelizer.h:628
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
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
ValueT getYMax(const ValueT &x) const
Gets the maximum y value for a given x value.
Definition ConvexVoxelizer.h:634
void clear()
Clears the data container.
Definition ConvexVoxelizer.h:557
bool isEmpty() const
Returns true if the container has no x values or if all y ranges are empty.
Definition ConvexVoxelizer.h:650
ValueT getYMin(const Index &i) const
Gets the minimum y value for a given index.
Definition ConvexVoxelizer.h:617
Index step() const
Retrieves the step size used for the x values.
Definition ConvexVoxelizer.h:595
ValueT halfWidth() const
Return the half width of the narrow-band level set.
Definition ConvexVoxelizer.h:218
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
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
void iterate()
The function the derived class calls to create the level set, working in index space other than setti...
Definition ConvexVoxelizer.h:236
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
math::Vec2< ValueT > Vec2T
Definition ConvexVoxelizer.h:186
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
math::Vec3< ValueT > Vec3T
Definition ConvexVoxelizer.h:185
bool tileCanFit(const Index &) const
Checks if the tile of a given dimension can possibly fit within the region.
Definition ConvexVoxelizer.h:284
typename GridType::ValueType ValueT
Definition ConvexVoxelizer.h:184
std::function< bool(ValueT &, ValueT &, const ValueT &, const ValueT &)> bottomTop
Definition ConvexVoxelizer.h:317
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 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
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
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 setXYRangeData(const Index &)
Determines the x bounds in index space of the convex region dilated by the half width....
Definition ConvexVoxelizer.h:272
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
ConvexVoxelizer(GridPtr &grid, const bool &threaded=false, InterruptType *interrupter=nullptr)
Constructor.
Definition ConvexVoxelizer.h:201
ValueT signedDistance(const Vec3T &) const
Computes the signed distance from a point to the convex region in index space.
Definition ConvexVoxelizer.h:290
int Ceil(float x)
Return the ceiling of x.
Definition Math.h:856
float Sqrt(float x)
Return the square root of a floating-point value.
Definition Math.h:761
Coord Abs(const Coord &xyz)
Definition Coord.h:518
float Round(float x)
Return x rounded to the nearest integer.
Definition Math.h:819
Type Pow2(Type x)
Return x2.
Definition Math.h:548
int Floor(float x)
Return the floor of x.
Definition Math.h:848
Definition ConvexVoxelizer.h:37
OutGridT const XformOp bool bool
Definition ValueTransformer.h:610
OutGridT XformOp & op
Definition ValueTransformer.h:140
OutGridT XformOp bool bool MergePolicy merge
Definition ValueTransformer.h:141
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
OutGridT XformOp bool threaded
Definition ValueTransformer.h:140
Index32 Index
Definition Types.h:54
int32_t Int32
Definition Types.h:56
openvdb::GridBase::Ptr GridPtr
Definition Utils.h:44
Definition Exceptions.h:13
void getPos(size_t n, VectorType &xyz) const
Definition ConvexVoxelizer.h:56
size_t size() const
Definition ConvexVoxelizer.h:54
VectorType PosType
Definition ConvexVoxelizer.h:43
PointArray(const std::vector< VectorType > &vec)
Definition ConvexVoxelizer.h:47
const VectorType & operator[](const Index &i)
Definition ConvexVoxelizer.h:52
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:218