OpenVDB 13.2.0
Loading...
Searching...
No Matches
FastSweeping.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-2.0
3//
4/// @file FastSweeping.h
5///
6/// @author Ken Museth
7///
8/// @brief Defined the six functions {fog,sdf}To{Sdf,Ext,SdfAndExt} in
9/// addition to the two functions maskSdf and dilateSdf. Sdf denotes
10/// a signed-distance field (i.e. negative values are inside), fog
11/// is a scalar fog volume (i.e. higher values are inside), and Ext is
12/// a field (of arbitrary type) that is extended off the iso-surface.
13/// All these functions are implemented with the methods in the class
14/// named FastSweeping.
15///
16/// @note Solves the (simplified) Eikonal Eq: @f$|\nabla \phi|^2 = 1@f$ and
17/// performs velocity extension, @f$\nabla f\nabla \phi = 0@f$, both
18/// by means of the fast sweeping algorithm detailed in:
19/// "A Fast Sweeping Method For Eikonal Equations"
20/// by H. Zhao, Mathematics of Computation, Vol 74(230), pp 603-627, 2004
21///
22/// @details The algorithm used below for parallel fast sweeping was first published in:
23/// "New Algorithm for Sparse and Parallel Fast Sweeping: Efficient
24/// Computation of Sparse Distance Fields" by K. Museth, ACM SIGGRAPH Talk,
25/// 2017, http://www.museth.org/Ken/Publications_files/Museth_SIG17.pdf
26
27#ifndef OPENVDB_TOOLS_FASTSWEEPING_HAS_BEEN_INCLUDED
28#define OPENVDB_TOOLS_FASTSWEEPING_HAS_BEEN_INCLUDED
29
30//#define BENCHMARK_FAST_SWEEPING
31
32#include <openvdb/openvdb.h>
33#include <openvdb/Platform.h>
34#include <openvdb/math/Math.h> // for Abs() and isExactlyEqual()
35#include <openvdb/math/Stencils.h> // for GradStencil
37#include <openvdb/tree/NodeManager.h> // for PruneMinMaxFltKernel
38#include <openvdb/util/Assert.h>
39
40#include "LevelSetUtil.h"
41#include "Morphology.h"
42
43#include "Statistics.h"
44#ifdef BENCHMARK_FAST_SWEEPING
46#endif
47
48#include <tbb/parallel_for.h>
49#include <tbb/enumerable_thread_specific.h>
50#include <tbb/task_group.h>
51
52#include <type_traits>// for static_assert
53#include <cmath>
54#include <limits>
55#include <deque>
56#include <unordered_map>
57#include <utility>// for std::make_pair
58
59namespace openvdb {
61namespace OPENVDB_VERSION_NAME {
62namespace tools {
63
64/// @brief Fast Sweeping update mode. This is useful to determine
65/// narrow-band extension or field extension in one side
66/// of a signed distance field.
68 /// Update all voxels affected by the sweeping algorithm
70 // Update voxels corresponding to an sdf/fog values that are greater than a given isovalue
72 // Update voxels corresponding to an sdf/fog values that are less than a given isovalue
74};
75
76/// @brief Converts a scalar fog volume into a signed distance function. Active input voxels
77/// with scalar values above the given isoValue will have NEGATIVE distance
78/// values on output, i.e. they are assumed to be INSIDE the iso-surface.
79///
80/// @return A shared pointer to a signed-distance field defined on the active values
81/// of the input fog volume.
82///
83/// @param fogGrid Scalar (floating-point) volume from which an
84/// iso-surface can be defined.
85///
86/// @param isoValue A value which defines a smooth iso-surface that
87/// intersects active voxels in @a fogGrid.
88///
89/// @param nIter Number of iterations of the fast sweeping algorithm.
90/// Each iteration performs 2^3 = 8 individual sweeps.
91///
92/// @note Strictly speaking a fog volume is normalized to the range [0,1] but this
93/// method accepts a scalar volume with an arbitrary range, as long as the it
94/// includes the @a isoValue.
95///
96/// @details Topology of output grid is identical to that of the input grid, except
97/// active tiles in the input grid will be converted to active voxels
98/// in the output grid!
99///
100/// @warning If @a isoValue does not intersect any active values in
101/// @a fogGrid then the returned grid has all its active values set to
102/// plus or minus infinity, depending on if the input values are larger or
103/// smaller than @a isoValue.
104template<typename GridT>
105typename GridT::Ptr
106fogToSdf(const GridT &fogGrid,
107 typename GridT::ValueType isoValue,
108 int nIter = 1);
109
110/// @brief Given an existing approximate SDF it solves the Eikonal equation for all its
111/// active voxels. Active input voxels with a signed distance value above the
112/// given isoValue will have POSITIVE distance values on output, i.e. they are
113/// assumed to be OUTSIDE the iso-surface.
114///
115/// @return A shared pointer to a signed-distance field defined on the active values
116/// of the input sdf volume.
117///
118/// @param sdfGrid An approximate signed distance field to the specified iso-surface.
119///
120/// @param isoValue A value which defines a smooth iso-surface that
121/// intersects active voxels in @a sdfGrid.
122///
123/// @param nIter Number of iterations of the fast sweeping algorithm.
124/// Each iteration performs 2^3 = 8 individual sweeps.
125///
126/// @note The only difference between this method and fogToSdf, defined above, is the
127/// convention of the sign of the output distance field.
128///
129/// @details Topology of output grid is identical to that of the input grid, except
130/// active tiles in the input grid will be converted to active voxels
131/// in the output grid!
132///
133/// @warning If @a isoValue does not intersect any active values in
134/// @a sdfGrid then the returned grid has all its active values set to
135/// plus or minus infinity, depending on if the input values are larger or
136/// smaller than @a isoValue.
137template<typename GridT>
138typename GridT::Ptr
139sdfToSdf(const GridT &sdfGrid,
140 typename GridT::ValueType isoValue = 0,
141 int nIter = 1);
142
143/// @brief Computes the extension of a field (scalar, vector, or int are supported), defined
144/// by the specified functor, off an iso-surface from an input FOG volume.
145///
146/// @return A shared pointer to the extension field defined from the active values in
147/// the input fog volume.
148///
149/// @param fogGrid Scalar (floating-point) volume from which an
150/// iso-surface can be defined.
151///
152/// @param op Functor with signature [](const Vec3R &xyz)->ExtValueT that
153/// defines the Dirichlet boundary condition, on the iso-surface,
154/// of the field to be extended.
155///
156/// @param background Background value of return grid with the extension field.
157///
158/// @param isoValue A value which defines a smooth iso-surface that
159/// intersects active voxels in @a fogGrid.
160///
161/// @param nIter Number of iterations of the fast sweeping algorithm.
162/// Each iteration performs 2^3 = 8 individual sweeps.
163///
164/// @param mode Determines the mode of updating the extension field. SWEEP_ALL
165/// will update all voxels of the extension field affected by the
166/// fast sweeping algorithm. SWEEP_GREATER_THAN_ISOVALUE will update
167/// all voxels corresponding to fog values that are greater than a given
168/// isovalue. SWEEP_LESS_THAN_ISOVALUE will update all voxels corresponding
169/// to fog values that are less than a given isovalue. If a mode other
170/// than SWEEP_ALL is chosen, a user needs to supply @a extGrid.
171///
172/// @param extGrid Optional parameter required to supply a default value for the extension
173/// field when SWEEP_GREATER_THAN_ISOVALUE or SWEEP_LESS_THAN_ISOVALUE
174/// mode is picked for @a mode. When SWEEP_GREATER_THAN_ISOVALUE is supplied
175/// as an argument for @a mode, the extension field voxel will default
176/// to the value of the @a extGrid in that position if it corresponds to a fog
177/// value that is less than the isovalue. Otherwise, the extension
178/// field voxel value will be computed by the Fast Sweeping algorithm.
179/// The opposite convention is implemented when SWEEP_LESS_THAN_ISOVALUE
180/// is supplied as an argument for @a mode.
181///
182/// @note Strictly speaking a fog volume is normalized to the range [0,1] but this
183/// method accepts a scalar volume with an arbitrary range, as long as the it
184/// includes the @a isoValue.
185///
186/// @details Topology of output grid is identical to that of the input grid, except
187/// active tiles in the input grid will be converted to active voxels
188/// in the output grid!
189///
190/// @warning If @a isoValue does not intersect any active values in
191/// @a fogGrid then the returned grid has all its active values set to
192/// @a background.
193template<typename FogGridT, typename ExtOpT, typename ExtValueT>
194typename FogGridT::template ValueConverter<ExtValueT>::Type::Ptr
195fogToExt(const FogGridT &fogGrid,
196 const ExtOpT &op,
197 const ExtValueT& background,
198 typename FogGridT::ValueType isoValue,
199 int nIter = 1,
200 FastSweepingDomain mode = FastSweepingDomain::SWEEP_ALL,
201 const typename FogGridT::template ValueConverter<ExtValueT>::Type::ConstPtr extGrid = nullptr);
202
203/// @brief Computes the extension of a field (scalar, vector, or int are supported), defined
204/// by the specified functor, off an iso-surface from an input SDF volume.
205///
206/// @return A shared pointer to the extension field defined on the active values in the
207/// input signed distance field.
208///
209/// @param sdfGrid An approximate signed distance field to the specified iso-surface.
210///
211/// @param op Functor with signature [](const Vec3R &xyz)->ExtValueT that
212/// defines the Dirichlet boundary condition, on the iso-surface,
213/// of the field to be extended.
214///
215/// @param background Background value of return grid with the extension field.
216///
217/// @param isoValue A value which defines a smooth iso-surface that
218/// intersects active voxels in @a sdfGrid.
219///
220/// @param nIter Number of iterations of the fast sweeping algorithm.
221/// Each iteration performs 2^3 = 8 individual sweeps.
222///
223/// @param mode Determines the mode of updating the extension field. SWEEP_ALL
224/// will update all voxels of the extension field affected by the
225/// fast sweeping algorithm. SWEEP_GREATER_THAN_ISOVALUE will update
226/// all voxels corresponding to level set values that are greater than a given
227/// isovalue. SWEEP_LESS_THAN_ISOVALUE will update all voxels corresponding
228/// to level set values that are less than a given isovalue. If a mode other
229/// than SWEEP_ALL is chosen, a user needs to supply @a extGrid.
230///
231/// @param extGrid Optional parameter required to supply a default value for the extension
232/// field when SWEEP_GREATER_THAN_ISOVALUE or SWEEP_LESS_THAN_ISOVALUE
233/// mode is picked for @a mode. When SWEEP_GREATER_THAN_ISOVALUE is supplied
234/// as an argument for @a mode, the extension field voxel will default
235/// to the value of the @a extGrid in that position if it corresponds to a level-set
236/// value that is less than the isovalue. Otherwise, the extension
237/// field voxel value will be computed by the Fast Sweeping algorithm.
238/// The opposite convention is implemented when SWEEP_LESS_THAN_ISOVALUE
239/// is supplied as an argument for @a mode.
240///
241/// @note The only difference between this method and fogToExt, defined above, is the
242/// convention of the sign of the signed distance field.
243///
244/// @details Topology of output grid is identical to that of the input grid, except
245/// active tiles in the input grid will be converted to active voxels
246/// in the output grid!
247///
248/// @warning If @a isoValue does not intersect any active values in
249/// @a sdfGrid then the returned grid has all its active values set to
250/// @a background.
251template<typename SdfGridT, typename ExtOpT, typename ExtValueT>
252typename SdfGridT::template ValueConverter<ExtValueT>::Type::Ptr
253sdfToExt(const SdfGridT &sdfGrid,
254 const ExtOpT &op,
255 const ExtValueT &background,
256 typename SdfGridT::ValueType isoValue = 0,
257 int nIter = 1,
259 const typename SdfGridT::template ValueConverter<ExtValueT>::Type::ConstPtr extGrid = nullptr);
260
261/// @brief Computes the signed distance field and the extension of a field (scalar, vector, or
262/// int are supported), defined by the specified functor, off an iso-surface from an input
263/// FOG volume.
264///
265/// @return An pair of two shared pointers to respectively the SDF and extension field
266///
267/// @param fogGrid Scalar (floating-point) volume from which an
268/// iso-surface can be defined.
269///
270/// @param op Functor with signature [](const Vec3R &xyz)->ExtValueT that
271/// defines the Dirichlet boundary condition, on the iso-surface,
272/// of the field to be extended.
273///
274/// @param background Background value of return grid with the extension field.
275///
276/// @param isoValue A value which defines a smooth iso-surface that
277/// intersects active voxels in @a fogGrid.
278///
279/// @param nIter Number of iterations of the fast sweeping algorithm.
280/// Each iteration performs 2^3 = 8 individual sweeps.
281///
282/// @param mode Determines the mode of updating the extension field. SWEEP_ALL
283/// will update all voxels of the extension field affected by the
284/// fast sweeping algorithm. SWEEP_GREATER_THAN_ISOVALUE will update
285/// all voxels corresponding to fog values that are greater than a given
286/// isovalue. SWEEP_LESS_THAN_ISOVALUE will update all voxels corresponding
287/// to fog values that are less than a given isovalue. If a mode other
288/// than SWEEP_ALL is chosen, a user needs to supply @a extGrid.
289///
290/// @param extGrid Optional parameter required to supply a default value for the extension
291/// field when SWEEP_GREATER_THAN_ISOVALUE or SWEEP_LESS_THAN_ISOVALUE
292/// mode is picked for @a mode. When SWEEP_GREATER_THAN_ISOVALUE is supplied
293/// as an argument for @a mode, the extension field voxel will default
294/// to the value of the @a extGrid in that position if it corresponds to a fog
295/// value that is less than the isovalue. Otherwise, the extension
296/// field voxel value will be computed by the Fast Sweeping algorithm.
297/// The opposite convention is implemented when SWEEP_LESS_THAN_ISOVALUE
298/// is supplied as an argument for @a mode.
299///
300/// @note Strictly speaking a fog volume is normalized to the range [0,1] but this
301/// method accepts a scalar volume with an arbitrary range, as long as the it
302/// includes the @a isoValue.
303///
304/// @details Topology of output grids are identical to that of the input grid, except
305/// active tiles in the input grid will be converted to active voxels
306/// in the output grids!
307///
308/// @warning If @a isoValue does not intersect any active values in
309/// @a fogGrid then a pair of the following grids is returned: The first
310/// is a signed distance grid with its active values set to plus or minus
311/// infinity depending of whether its input values are above or below @a isoValue.
312/// The second grid, which represents the extension field, has all its active
313/// values set to @a background.
314template<typename FogGridT, typename ExtOpT, typename ExtValueT>
315std::pair<typename FogGridT::Ptr, typename FogGridT::template ValueConverter<ExtValueT>::Type::Ptr>
316fogToSdfAndExt(const FogGridT &fogGrid,
317 const ExtOpT &op,
318 const ExtValueT &background,
319 typename FogGridT::ValueType isoValue,
320 int nIter = 1,
322 const typename FogGridT::template ValueConverter<ExtValueT>::Type::ConstPtr extGrid = nullptr);
323
324/// @brief Computes the signed distance field and the extension of a field (scalar, vector, or
325/// int are supported), defined by the specified functor, off an iso-surface from an input
326/// SDF volume.
327///
328/// @return A pair of two shared pointers to respectively the SDF and extension field
329///
330/// @param sdfGrid Scalar (floating-point) volume from which an
331/// iso-surface can be defined.
332///
333/// @param op Functor with signature [](const Vec3R &xyz)->ExtValueT that
334/// defines the Dirichlet boundary condition, on the iso-surface,
335/// of the field to be extended.
336///
337/// @param background Background value of return grid with the extension field.
338///
339/// @param isoValue A value which defines a smooth iso-surface that
340/// intersects active voxels in @a sdfGrid.
341///
342/// @param nIter Number of iterations of the fast sweeping algorithm.
343/// Each iteration performs 2^3 = 8 individual sweeps.
344///
345/// @param mode Determines the mode of updating the extension field. SWEEP_ALL
346/// will update all voxels of the extension field affected by the
347/// fast sweeping algorithm. SWEEP_GREATER_THAN_ISOVALUE will update
348/// all voxels corresponding to level set values that are greater than a given
349/// isovalue. SWEEP_LESS_THAN_ISOVALUE will update all voxels corresponding
350/// to level set values that are less than a given isovalue. If a mode other
351/// than SWEEP_ALL is chosen, a user needs to supply @a extGrid.
352///
353/// @param extGrid Optional parameter required to supply a default value for the extension
354/// field when SWEEP_GREATER_THAN_ISOVALUE or SWEEP_LESS_THAN_ISOVALUE
355/// mode is picked for @a mode. When SWEEP_GREATER_THAN_ISOVALUE is supplied
356/// as an argument for @a mode, the extension field voxel will default
357/// to the value of the @a extGrid in that position if it corresponds to a level-set
358/// value that is less than the isovalue. Otherwise, the extension
359/// field voxel value will be computed by the Fast Sweeping algorithm.
360/// The opposite convention is implemented when SWEEP_LESS_THAN_ISOVALUE
361/// is supplied as an argument for @a mode.
362///
363/// @note Strictly speaking a fog volume is normalized to the range [0,1] but this
364/// method accepts a scalar volume with an arbitrary range, as long as the it
365/// includes the @a isoValue.
366///
367/// @details Topology of output grids are identical to that of the input grid, except
368/// active tiles in the input grid will be converted to active voxels
369/// in the output grids!
370///
371/// @warning If @a isoValue does not intersect any active values in
372/// @a sdfGrid then a pair of the following grids is returned: The first
373/// is a signed distance grid with its active values set to plus or minus
374/// infinity depending of whether its input values are above or below @a isoValue.
375/// The second grid, which represents the extension field, has all its active
376/// values set to @a background.
377template<typename SdfGridT, typename ExtOpT, typename ExtValueT>
378std::pair<typename SdfGridT::Ptr, typename SdfGridT::template ValueConverter<ExtValueT>::Type::Ptr>
379sdfToSdfAndExt(const SdfGridT &sdfGrid,
380 const ExtOpT &op,
381 const ExtValueT &background,
382 typename SdfGridT::ValueType isoValue = 0,
383 int nIter = 1,
385 const typename SdfGridT::template ValueConverter<ExtValueT>::Type::ConstPtr extGrid = nullptr);
386
387/// @brief Dilates the narrow band of an existing signed distance field by
388/// a specified number of voxels (like adding "onion-rings").
389///
390/// @note This operation is not to be confused with morphological dilation
391/// of a level set, which is implemented in LevelSetFilter::offset,
392/// and involves actual interface tracking of the narrow band.
393///
394/// @return A shared pointer to the dilated signed distance field.
395///
396/// @param sdfGrid Input signed distance field to be dilated.
397///
398/// @param dilation Numer of voxels that the narrow band of the input SDF will be dilated.
399///
400/// @param nn Stencil-pattern used for dilation
401///
402/// @param nIter Number of iterations of the fast sweeping algorithm.
403/// Each iteration performs 2^3 = 8 individual sweeps.
404///
405/// @param mode Determines the direction of the dilation. SWEEP_ALL
406/// will dilate in both sides of the signed distance function,
407/// SWEEP_GREATER_THAN_ISOVALUE will dilate in the positive
408/// side of the iso-surface, SWEEP_LESS_THAN_ISOVALUE will dilate
409/// in the negative side of the iso-surface.
410///
411/// @details Topology will change as a result of this dilation. E.g. if
412/// sdfGrid has a width of 3 and @a dilation = 6 then the grid
413/// returned by this method is a narrow band signed distance field
414/// with a total width of 9 units.
415template<typename GridT>
416typename GridT::Ptr
417dilateSdf(const GridT &sdfGrid,
418 int dilation,
420 int nIter = 1,
422
423/// @brief Fills mask by extending an existing signed distance field into
424/// the active values of this input ree of arbitrary value type.
425///
426/// @return A shared pointer to the masked signed distance field.
427///
428/// @param sdfGrid Input signed distance field to be extended into the mask.
429///
430/// @param mask Mask used to identify the topology of the output SDF.
431/// Note this mask is assume to overlap with the sdfGrid.
432///
433/// @param ignoreActiveTiles If false, active tiles in the mask are treated
434/// as active voxels. Else they are ignored.
435///
436/// @param nIter Number of iterations of the fast sweeping algorithm.
437/// Each iteration performs 2^3 = 8 individual sweeps.
438///
439/// @details Topology of the output SDF is determined by the union of the active
440/// voxels (or optionally values) in @a sdfGrid and @a mask.
441template<typename GridT, typename MaskTreeT>
442typename GridT::Ptr
443maskSdf(const GridT &sdfGrid,
444 const Grid<MaskTreeT> &mask,
445 bool ignoreActiveTiles = false,
446 int nIter = 1);
447
448////////////////////////////////////////////////////////////////////////////////
449/// @brief Computes signed distance values from an initial iso-surface and
450/// optionally performs velocity extension at the same time. This is
451/// done by means of a novel sparse and parallel fast sweeping
452/// algorithm based on a first order Godunov's scheme.
453///
454/// Solves: @f$|\nabla \phi|^2 = 1 @f$
455///
456/// @warning Note, it is important to call one of the initialization methods before
457/// called the sweep function. Failure to do so will throw a RuntimeError.
458/// Consider instead call one of the many higher-level free-standing functions
459/// defined above!
460template<typename SdfGridT, typename ExtValueT = typename SdfGridT::ValueType>
462{
463 static_assert(std::is_floating_point<typename SdfGridT::ValueType>::value,
464 "FastSweeping requires SdfGridT to have floating-point values");
465 // Defined types related to the signed distance (or fog) grid
466 using SdfValueT = typename SdfGridT::ValueType;
467 using SdfTreeT = typename SdfGridT::TreeType;
468 using SdfAccT = tree::ValueAccessor<SdfTreeT, false>;//don't register accessors
469 using SdfConstAccT = typename tree::ValueAccessor<const SdfTreeT, false>;//don't register accessors
470
471 // define types related to the extension field
472 using ExtGridT = typename SdfGridT::template ValueConverter<ExtValueT>::Type;
473 using ExtTreeT = typename ExtGridT::TreeType;
475
476 // define types related to the tree that masks out the active voxels to be solved for
477 using SweepMaskTreeT = typename SdfTreeT::template ValueConverter<ValueMask>::Type;
478 using SweepMaskAccT = tree::ValueAccessor<SweepMaskTreeT, false>;//don't register accessors
479
480public:
481
482 /// @brief Constructor
483 FastSweeping();
484
485 /// @brief Destructor.
486 ~FastSweeping() { this->clear(); }
487
488 /// @brief Disallow copy construction.
489 FastSweeping(const FastSweeping&) = delete;
490
491 /// @brief Disallow copy assignment.
493
494 /// @brief Returns a shared pointer to the signed distance field computed
495 /// by this class.
496 ///
497 /// @warning This shared pointer might point to NULL if the grid has not been
498 /// initialize (by one of the init methods) or computed (by the sweep
499 /// method).
500 typename SdfGridT::Ptr sdfGrid() { return mSdfGrid; }
501
502 /// @brief Returns a shared pointer to the extension field computed
503 /// by this class.
504 ///
505 /// @warning This shared pointer might point to NULL if the grid has not been
506 /// initialize (by one of the init methods) or computed (by the sweep
507 /// method).
508 typename ExtGridT::Ptr extGrid() { return mExtGrid; }
509
510 /// @brief Returns a shared pointer to the extension grid input. This is non-NULL
511 /// if this class is used to extend a field with a non-default sweep direction.
512 ///
513 /// @warning This shared pointer might point to NULL. This is non-NULL
514 /// if this class is used to extend a field with a non-default sweep direction,
515 /// i.e. SWEEP_LESS_THAN_ISOVALUE or SWEEP_GREATER_THAN_ISOVALUE.
516 typename ExtGridT::Ptr extGridInput() { return mExtGridInput; }
517
518 /// @brief Initializer for input grids that are either a signed distance
519 /// field or a scalar fog volume.
520 ///
521 /// @return True if the initialization succeeded.
522 ///
523 /// @param sdfGrid Input scalar grid that represents an existing signed distance
524 /// field or a fog volume (signified by @a isInputSdf).
525 ///
526 /// @param isoValue Iso-value to be used to define the Dirichlet boundary condition
527 /// of the fast sweeping algorithm (typically 0 for sdfs and a
528 /// positive value for fog volumes).
529 ///
530 /// @param isInputSdf Used to determine if @a sdfGrid is a sigend distance field (true)
531 /// or a scalar fog volume (false).
532 ///
533 /// @details This, or any of ther other initialization methods, should be called
534 /// before any call to sweep(). Failure to do so will throw a RuntimeError.
535 ///
536 /// @warning Note, if this method fails, i.e. returns false, a subsequent call
537 /// to sweep will trow a RuntimeError. Instead call clear and try again.
538 bool initSdf(const SdfGridT &sdfGrid, SdfValueT isoValue, bool isInputSdf);
539
540 /// @brief Initializer used whenever velocity extension is performed in addition
541 /// to the computation of signed distance fields.
542 ///
543 /// @return True if the initialization succeeded.
544 ///
545 ///
546 /// @param sdfGrid Input scalar grid that represents an existing signed distance
547 /// field or a fog volume (signified by @a isInputSdf).
548 ///
549 /// @param op Functor with signature [](const Vec3R &xyz)->ExtValueT that
550 /// defines the Dirichlet boundary condition, on the iso-surface,
551 /// of the field to be extended. Strictly the return type of this functor
552 /// is only required to be convertible to ExtValueT!
553 ///
554 /// @param background Background value of return grid with the extension field.
555 ///
556 /// @param isoValue Iso-value to be used for the boundary condition of the fast
557 /// sweeping algorithm (typically 0 for sdfs and a positive value
558 /// for fog volumes).
559 ///
560 /// @param isInputSdf Used to determine if @a sdfGrid is a sigend distance field (true)
561 /// or a scalar fog volume (false).
562 ///
563 /// @param mode Determines the mode of updating the extension field. SWEEP_ALL
564 /// will update all voxels of the extension field affected by the
565 /// fast sweeping algorithm. SWEEP_GREATER_THAN_ISOVALUE will update
566 /// all voxels corresponding to fog values that are greater than a given
567 /// isovalue. SWEEP_LESS_THAN_ISOVALUE will update all voxels corresponding
568 /// to fog values that are less than a given isovalue. If a mode other
569 /// than SWEEP_ALL is chosen, a user needs to supply @a extGrid.
570 ///
571 /// @param extGrid Optional parameter required to supply a default value for the extension
572 /// field when SWEEP_GREATER_THAN_ISOVALUE or SWEEP_LESS_THAN_ISOVALUE
573 /// mode is picked for @a mode. When SWEEP_GREATER_THAN_ISOVALUE is supplied
574 /// as an argument for @a mode, the extension field voxel will default
575 /// to the value of the @a extGrid in that position if it corresponds to a level-set
576 /// value that is less than the isovalue. Otherwise, the extension
577 /// field voxel value will be computed by the Fast Sweeping algorithm.
578 /// The opposite convention is implemented when SWEEP_LESS_THAN_ISOVALUE
579 /// is supplied as an argument for @a mode.
580 ///
581 /// @details This, or any of ther other initialization methods, should be called
582 /// before any call to sweep(). Failure to do so will throw a RuntimeError.
583 ///
584 /// @warning Note, if this method fails, i.e. returns false, a subsequent call
585 /// to sweep will trow a RuntimeError. Instead call clear and try again.
586 template <typename ExtOpT>
587 bool initExt(const SdfGridT &sdfGrid,
588 const ExtOpT &op,
589 const ExtValueT &background,
590 SdfValueT isoValue,
591 bool isInputSdf,
593 const typename ExtGridT::ConstPtr extGrid = nullptr);
594
595 /// @brief Initializer used when dilating an existing signed distance field.
596 ///
597 /// @return True if the initialization succeeded.
598 ///
599 /// @param sdfGrid Input signed distance field to to be dilated.
600 ///
601 /// @param dilation Numer of voxels that the input SDF will be dilated.
602 ///
603 /// @param nn Stencil-pattern used for dilation
604 ///
605 /// @param mode Determines the direction of the dilation. SWEEP_ALL
606 /// will dilate in both sides of the signed distance function,
607 /// SWEEP_GREATER_THAN_ISOVALUE will dilate in the positive
608 /// side of the iso-surface, SWEEP_LESS_THAN_ISOVALUE will dilate
609 /// in the negative side of the iso-surface.
610 ///
611 /// @details This, or any of ther other initialization methods, should be called
612 /// before any call to sweep(). Failure to do so will throw a RuntimeError.
613 ///
614 /// @warning Note, if this method fails, i.e. returns false, a subsequent call
615 /// to sweep will trow a RuntimeError. Instead call clear and try again.
616 bool initDilate(const SdfGridT &sdfGrid,
617 int dilation,
620
621 /// @brief Initializer used for the extension of an existing signed distance field
622 /// into the active values of an input mask of arbitrary value type.
623 ///
624 /// @return True if the initialization succeeded.
625 ///
626 /// @param sdfGrid Input signed distance field to be extended into the mask.
627 ///
628 /// @param mask Mask used to identify the topology of the output SDF.
629 /// Note this mask is assume to overlap with the sdfGrid.
630 ///
631 /// @param ignoreActiveTiles If false, active tiles in the mask are treated
632 /// as active voxels. Else they are ignored.
633 ///
634 /// @details This, or any of ther other initialization methods, should be called
635 /// before any call to sweep(). Failure to do so will throw a RuntimeError.
636 ///
637 /// @warning Note, if this method fails, i.e. returns false, a subsequent call
638 /// to sweep will trow a RuntimeError. Instead call clear and try again.
639 template<typename MaskTreeT>
640 bool initMask(const SdfGridT &sdfGrid, const Grid<MaskTreeT> &mask, bool ignoreActiveTiles = false);
641
642 /// @brief Perform @a nIter iterations of the fast sweeping algorithm.
643 ///
644 /// @param nIter Number of iterations of the fast sweeping algorithm.
645 /// Each iteration performs 2^3 = 8 individual sweeps.
646 ///
647 /// @param finalize If true the (possibly asymmetric) inside and outside values of the
648 /// resulting signed distance field are properly set. Unless you're
649 /// an expert this should remain true!
650 ///
651 /// @throw RuntimeError if sweepingVoxelCount() or boundaryVoxelCount() return zero.
652 /// This might happen if none of the initialization methods above were called
653 /// or if that initialization failed.
654 void sweep(int nIter = 1,
655 bool finalize = true);
656
657 /// @brief Clears all the grids and counters so initialization can be called again.
658 void clear();
659
660 /// @brief Return the number of voxels that will be solved for.
661 size_t sweepingVoxelCount() const { return mSweepingVoxelCount; }
662
663 /// @brief Return the number of voxels that defined the boundary condition.
664 size_t boundaryVoxelCount() const { return mBoundaryVoxelCount; }
665
666 /// @brief Return true if there are voxels and boundaries to solve for
667 bool isValid() const { return mSweepingVoxelCount > 0 && mBoundaryVoxelCount > 0; }
668
669 /// @brief Return whether the sweep update is in all direction (SWEEP_ALL),
670 /// greater than isovalue (SWEEP_GREATER_THAN_ISOVALUE), or less than isovalue
671 /// (SWEEP_LESS_THAN_ISOVALUE).
672 ///
673 /// @note SWEEP_GREATER_THAN_ISOVALUE and SWEEP_LESS_THAN_ISOVALUE modes are used
674 /// in dilating the narrow-band of a levelset or in extending a field.
675 FastSweepingDomain sweepDirection() const { return mSweepDirection; }
676
677 /// @brief Return whether the fast-sweeping input grid a signed distance function or not (fog).
678 bool isInputSdf() { return mIsInputSdf; }
679
680private:
681
682 /// @brief Private method to prune the sweep mask and cache leaf origins.
683 void computeSweepMaskLeafOrigins();
684
685 // Private utility classes
686 template<typename>
687 struct MaskKernel;// initialization to extend a SDF into a mask
688 template<typename>
689 struct InitExt;
690 struct InitSdf;
691 struct DilateKernel;// initialization to dilate a SDF
692 struct MinMaxKernel;
693 struct PruneMinMaxFltKernel;// prune sdf if it has min float or max float
694 struct SweepingKernel;// performs the actual concurrent sparse fast sweeping
695
696 // Define the topology (i.e. stencil) of the neighboring grid points
697 static const Coord mOffset[6];// = {{-1,0,0},{1,0,0},{0,-1,0},{0,1,0},{0,0,-1},{0,0,1}};
698
699 // Private member data of FastSweeping
700 typename SdfGridT::Ptr mSdfGrid;
701 typename ExtGridT::Ptr mExtGrid;
702 typename ExtGridT::Ptr mExtGridInput; // optional: only used in extending a field in one direction
703 SweepMaskTreeT mSweepMask; // mask tree containing all non-boundary active voxels, in the case of dilation, does not include active voxel
704 std::vector<Coord> mSweepMaskLeafOrigins; // cache of leaf node origins for mask tree
705 size_t mSweepingVoxelCount, mBoundaryVoxelCount;
706 FastSweepingDomain mSweepDirection; // only used in dilate and extending a field
707 bool mIsInputSdf;
708};// FastSweeping
709
710////////////////////////////////////////////////////////////////////////////////
711
712// Static member data initialization
713template <typename SdfGridT, typename ExtValueT>
714const Coord FastSweeping<SdfGridT, ExtValueT>::mOffset[6] = {{-1,0,0},{1,0,0},
715 {0,-1,0},{0,1,0},
716 {0,0,-1},{0,0,1}};
717
718template <typename SdfGridT, typename ExtValueT>
720 : mSdfGrid(nullptr), mExtGrid(nullptr), mSweepingVoxelCount(0), mBoundaryVoxelCount(0), mSweepDirection(FastSweepingDomain::SWEEP_ALL), mIsInputSdf(true)
721{
722}
723
724template <typename SdfGridT, typename ExtValueT>
726{
727 mSdfGrid.reset();
728 mExtGrid.reset();
729 mSweepMask.clear();
730 if (mExtGridInput) mExtGridInput.reset();
731 mSweepingVoxelCount = mBoundaryVoxelCount = 0;
732 mSweepDirection = FastSweepingDomain::SWEEP_ALL;
733 mIsInputSdf = true;
734}
735
736template <typename SdfGridT, typename ExtValueT>
737void FastSweeping<SdfGridT, ExtValueT>::computeSweepMaskLeafOrigins()
738{
739 // replace any inactive leaf nodes with tiles and voxelize any active tiles
740
741 pruneInactive(mSweepMask);
742 mSweepMask.voxelizeActiveTiles();
743
744 using LeafManagerT = tree::LeafManager<SweepMaskTreeT>;
745 using LeafT = typename SweepMaskTreeT::LeafNodeType;
746 LeafManagerT leafManager(mSweepMask);
747
748 mSweepMaskLeafOrigins.resize(leafManager.leafCount());
749 std::atomic<size_t> sweepingVoxelCount{0};
750 auto kernel = [&](const LeafT& leaf, size_t leafIdx) {
751 mSweepMaskLeafOrigins[leafIdx] = leaf.origin();
752 sweepingVoxelCount += leaf.onVoxelCount();
753 };
754 leafManager.foreach(kernel, /*threaded=*/true, /*grainsize=*/1024);
755
756 mBoundaryVoxelCount = 0;
757 mSweepingVoxelCount = sweepingVoxelCount;
758 if (mSdfGrid) {
759 const size_t totalCount = mSdfGrid->constTree().activeVoxelCount();
760 OPENVDB_ASSERT( totalCount >= mSweepingVoxelCount );
761 mBoundaryVoxelCount = totalCount - mSweepingVoxelCount;
762 }
763}// FastSweeping::computeSweepMaskLeafOrigins
764
765template <typename SdfGridT, typename ExtValueT>
766bool FastSweeping<SdfGridT, ExtValueT>::initSdf(const SdfGridT &fogGrid, SdfValueT isoValue, bool isInputSdf)
767{
768 this->clear();
769 mSdfGrid = fogGrid.deepCopy();//very fast
770 mIsInputSdf = isInputSdf;
771 InitSdf kernel(*this);
772 kernel.run(isoValue);
773 return this->isValid();
774}
775
776template <typename SdfGridT, typename ExtValueT>
777template <typename OpT>
778bool FastSweeping<SdfGridT, ExtValueT>::initExt(const SdfGridT &fogGrid, const OpT &op, const ExtValueT &background, SdfValueT isoValue, bool isInputSdf, FastSweepingDomain mode, const typename ExtGridT::ConstPtr extGrid)
779{
780 if (mode != FastSweepingDomain::SWEEP_ALL) {
781 if (!extGrid)
782 OPENVDB_THROW(RuntimeError, "FastSweeping::initExt Calling initExt with mode != SWEEP_ALL requires an extension grid!");
783 if (extGrid->transform() != fogGrid.transform())
784 OPENVDB_THROW(RuntimeError, "FastSweeping::initExt extension grid input should have the same transform as Fog/SDF grid!");
785 }
786
787 this->clear();
788 mSdfGrid = fogGrid.deepCopy();//very fast
789 mExtGrid = createGrid<ExtGridT>( background );
790 mSweepDirection = mode;
791 mIsInputSdf = isInputSdf;
792 if (mSweepDirection != FastSweepingDomain::SWEEP_ALL) {
793 mExtGridInput = extGrid->deepCopy();
794 }
795 mExtGrid->topologyUnion( *mSdfGrid );//very fast
796 InitExt<OpT> kernel(*this);
797 kernel.run(isoValue, op);
798 return this->isValid();
799}
800
801
802template <typename SdfGridT, typename ExtValueT>
804{
805 this->clear();
806 mSdfGrid = sdfGrid.deepCopy();//very fast
807 mSweepDirection = mode;
808 DilateKernel kernel(*this);
809 kernel.run(dilate, nn);
810 return this->isValid();
811}
812
813template <typename SdfGridT, typename ExtValueT>
814template<typename MaskTreeT>
815bool FastSweeping<SdfGridT, ExtValueT>::initMask(const SdfGridT &sdfGrid, const Grid<MaskTreeT> &mask, bool ignoreActiveTiles)
816{
817 this->clear();
818 mSdfGrid = sdfGrid.deepCopy();//very fast
819
820 if (mSdfGrid->transform() != mask.transform()) {
821 OPENVDB_THROW(RuntimeError, "FastSweeping: Mask not aligned with the grid!");
822 }
823
825 {
826 if (mask.getGridClass() == GRID_LEVEL_SET) {
827 using T = typename MaskTreeT::template ValueConverter<bool>::Type;
828 typename Grid<T>::Ptr tmp = sdfInteriorMask(mask);//might have active tiles
829 tmp->tree().voxelizeActiveTiles();//multi-threaded
830 MaskKernel<T> kernel(*this);
831 kernel.run(tmp->tree());//multi-threaded
832 return this->isValid();
833 }
834 }
835 if (ignoreActiveTiles || !mask.tree().hasActiveTiles()) {
836 MaskKernel<MaskTreeT> kernel(*this);
837 kernel.run(mask.tree());//multi-threaded
838 } else {
839 using T = typename MaskTreeT::template ValueConverter<ValueMask>::Type;
840 T tmp(mask.tree(), false, TopologyCopy());//multi-threaded
841 tmp.voxelizeActiveTiles(true);//multi-threaded
842 MaskKernel<T> kernel(*this);
843 kernel.run(tmp);//multi-threaded
844 }
845 return this->isValid();
846}// FastSweeping::initMask
847
848template <typename SdfGridT, typename ExtValueT>
849void FastSweeping<SdfGridT, ExtValueT>::sweep(int nIter, bool finalize)
850{
851 if (!mSdfGrid) {
852 OPENVDB_THROW(RuntimeError, "FastSweeping::sweep called before initialization!");
853 }
854 if (mExtGrid && mSweepDirection != FastSweepingDomain::SWEEP_ALL && !mExtGridInput) {
855 OPENVDB_THROW(RuntimeError, "FastSweeping: Trying to extend a field in one direction needs"
856 " a non-null reference extension grid input.");
857 }
858 if (this->boundaryVoxelCount() == 0) {
859 OPENVDB_THROW(RuntimeError, "FastSweeping: No boundary voxels found!");
860 } else if (this->sweepingVoxelCount() == 0) {
861 OPENVDB_THROW(RuntimeError, "FastSweeping: No computing voxels found!");
862 }
863
864 // note: Sweeping kernel is non copy-constructible, so use a deque instead of a vector
865 std::deque<SweepingKernel> kernels;
866 for (int i = 0; i < 4; i++) kernels.emplace_back(*this);
867
868 { // compute voxel slices
869#ifdef BENCHMARK_FAST_SWEEPING
870 util::CpuTimer timer("Computing voxel slices");
871#endif
872
873 // Exploiting nested parallelism - all voxel slice data is precomputed
874 tbb::task_group tasks;
875 tasks.run([&] { kernels[0].computeVoxelSlices([](const Coord &a){ return a[0]+a[1]+a[2]; });/*+++ & ---*/ });
876 tasks.run([&] { kernels[1].computeVoxelSlices([](const Coord &a){ return a[0]+a[1]-a[2]; });/*++- & --+*/ });
877 tasks.run([&] { kernels[2].computeVoxelSlices([](const Coord &a){ return a[0]-a[1]+a[2]; });/*+-+ & -+-*/ });
878 tasks.run([&] { kernels[3].computeVoxelSlices([](const Coord &a){ return a[0]-a[1]-a[2]; });/*+-- & -++*/ });
879 tasks.wait();
880
881#ifdef BENCHMARK_FAST_SWEEPING
882 timer.stop();
883#endif
884 }
885
886 // perform nIter iterations of bi-directional sweeping in all directions
887 for (int i = 0; i < nIter; ++i) {
888 for (SweepingKernel& kernel : kernels) kernel.sweep();
889 }
890
891 if (finalize) {
892#ifdef BENCHMARK_FAST_SWEEPING
893 util::CpuTimer timer("Computing extrema values");
894#endif
895 MinMaxKernel kernel;
896 auto e = kernel.run(*mSdfGrid);//multi-threaded
897 //auto e = extrema(mGrid->beginValueOn());// 100x slower!!!!
898 if (kernel.mFltMinExists || kernel.mFltMaxExists) {
899 tree::NodeManager<SdfTreeT> nodeManager(mSdfGrid->tree());
900 PruneMinMaxFltKernel op(e.min(), e.max());
901 nodeManager.foreachTopDown(op, true /* = threaded*/, 1 /* = grainSize*/);
902 }
903#ifdef BENCHMARK_FAST_SWEEPING
904 std::cerr << "Min = " << e.min() << " Max = " << e.max() << std::endl;
905 timer.restart("Changing asymmetric background value");
906#endif
907 changeAsymmetricLevelSetBackground(mSdfGrid->tree(), e.max(), e.min());//multi-threaded
908
909#ifdef BENCHMARK_FAST_SWEEPING
910 timer.stop();
911#endif
912 }
913}// FastSweeping::sweep
914
915/// Private class of FastSweeping to quickly compute the extrema
916/// values of the active voxels in the leaf nodes. Several orders
917/// of magnitude faster than tools::extrema!
918/// Also determines whether there is float max or float min stored
919/// in a voxel.
920template <typename SdfGridT, typename ExtValueT>
921struct FastSweeping<SdfGridT, ExtValueT>::MinMaxKernel
922{
924 using LeafRange = typename LeafMgr::LeafRange;
925 MinMaxKernel() : mMin(std::numeric_limits<SdfValueT>::max()), mMax(-mMin), mFltMinExists(false), mFltMaxExists(true) {}
926 MinMaxKernel(MinMaxKernel& other, tbb::split) : mMin(other.mMin), mMax(other.mMax), mFltMinExists(other.mFltMinExists), mFltMaxExists(other.mFltMaxExists) {}
927
928 math::MinMax<SdfValueT> run(const SdfGridT &grid)
929 {
930 LeafMgr mgr(grid.tree());// super fast
931 tbb::parallel_reduce(mgr.leafRange(), *this);
933 }
934
935 void operator()(const LeafRange& r)
936 {
937 for (auto leafIter = r.begin(); leafIter; ++leafIter) {
938 for (auto voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) {
939 const SdfValueT v = *voxelIter;
940 const bool vEqFltMin = v == -std::numeric_limits<SdfValueT>::max();
941 const bool vEqFltMax = v == std::numeric_limits<SdfValueT>::max();
942 if (v < mMin && !vEqFltMin) mMin = v;
943 if (v > mMax && !vEqFltMax) mMax = v;
944 if (vEqFltMin) mFltMinExists = true;
945 if (vEqFltMax) mFltMaxExists = true;
946 }
947 }
948 }
949
950 void join(const MinMaxKernel& other)
951 {
952 if (other.mMin < mMin) mMin = other.mMin;
953 if (other.mMax > mMax) mMax = other.mMax;
956 }
957
958 SdfValueT mMin, mMax;
960};// FastSweeping::MinMaxKernel
961
962/// Private class of FastSweeping to prune sdf value that is equal to float max or
963/// float min.
964template <typename SdfGridT, typename ExtValueT>
965struct FastSweeping<SdfGridT, ExtValueT>::PruneMinMaxFltKernel {
966 PruneMinMaxFltKernel(SdfValueT min, SdfValueT max) : mMin(min), mMax(max) {}
967
968 // Root node
969 void operator()(typename SdfTreeT::RootNodeType& node, size_t = 1) const {
970 for (auto iter = node.beginValueAll(); iter; ++iter) {
971 if (*iter == -std::numeric_limits<SdfValueT>::max()) {
972 iter.setValue(mMin);
973 }
974 if (*iter == std::numeric_limits<SdfValueT>::max()) {
975 iter.setValue(mMax);
976 }
977 }
978 }
979
980 // Internal nodes
981 template<typename NodeT>
982 void operator()(NodeT& node, size_t = 1) const
983 {
984 for (auto iter = node.beginValueAll(); iter; ++iter) {
985 if (*iter == -std::numeric_limits<SdfValueT>::max()) {
986 iter.setValue(mMin);
987 }
988 if (*iter == std::numeric_limits<SdfValueT>::max()) {
989 iter.setValue(mMax);
990 }
991 }
992 }
993
994 // Leaf nodes
995 void operator()(typename SdfTreeT::LeafNodeType& leaf, size_t = 1) const
996 {
997 for (auto iter = leaf.beginValueOn(); iter; ++iter) {
998 if (*iter == -std::numeric_limits<SdfValueT>::max()) {
999 iter.setValue(mMin);
1000 }
1001 if (*iter == std::numeric_limits<SdfValueT>::max()) {
1002 iter.setValue(mMax);
1003 }
1004 }
1005 }
1006 SdfValueT mMin, mMax;
1007};// FastSweeping::PruneMinMaxFltKernel
1008
1009////////////////////////////////////////////////////////////////////////////////
1010
1011/// Private class of FastSweeping to perform multi-threaded initialization
1012template <typename SdfGridT, typename ExtValueT>
1013struct FastSweeping<SdfGridT, ExtValueT>::DilateKernel
1014{
1017 : mParent(&parent),
1018 mBackground(parent.mSdfGrid->background())
1019 {
1020 mSdfGridInput = mParent->mSdfGrid->deepCopy();
1021 }
1022 DilateKernel(const DilateKernel &parent) = default;// for tbb::parallel_for
1024
1025 void run(int dilation, NearestNeighbors nn)
1026 {
1027#ifdef BENCHMARK_FAST_SWEEPING
1028 util::CpuTimer timer("Construct LeafManager");
1029#endif
1030 tree::LeafManager<SdfTreeT> mgr(mParent->mSdfGrid->tree());// super fast
1031
1032#ifdef BENCHMARK_FAST_SWEEPING
1033 timer.restart("Changing background value");
1034#endif
1035 static const SdfValueT Unknown = std::numeric_limits<SdfValueT>::max();
1036 changeLevelSetBackground(mgr, Unknown);//multi-threaded
1037
1038 #ifdef BENCHMARK_FAST_SWEEPING
1039 timer.restart("Dilating and updating mgr (parallel)");
1040 //timer.restart("Dilating and updating mgr (serial)");
1041#endif
1042
1043 const int delta = 5;
1044 for (int i=0, d = dilation/delta; i<d; ++i) dilateActiveValues(mgr, delta, nn, IGNORE_TILES);
1045 dilateActiveValues(mgr, dilation % delta, nn, IGNORE_TILES);
1046 //for (int i=0, n=5, d=dilation/n; i<d; ++i) dilateActiveValues(mgr, n, nn, IGNORE_TILES);
1047 //dilateVoxels(mgr, dilation, nn);
1048
1049#ifdef BENCHMARK_FAST_SWEEPING
1050 timer.restart("Initializing grid and sweep mask");
1051#endif
1052
1053 mParent->mSweepMask.clear();
1054 mParent->mSweepMask.topologyUnion(mParent->mSdfGrid->constTree());
1055
1057 using LeafT = typename SdfGridT::TreeType::LeafNodeType;
1058
1059 const FastSweepingDomain mode = mParent->mSweepDirection;
1060
1061 LeafManagerT leafManager(mParent->mSdfGrid->tree());
1062
1063 auto kernel = [&](LeafT& leaf, size_t /*leafIdx*/) {
1064 static const SdfValueT Unknown = std::numeric_limits<SdfValueT>::max();
1065 const SdfValueT background = mBackground;//local copy
1066 auto* maskLeaf = mParent->mSweepMask.probeLeaf(leaf.origin());
1067 SdfConstAccT sdfInputAcc(mSdfGridInput->tree());
1068 OPENVDB_ASSERT(maskLeaf);
1069 for (auto voxelIter = leaf.beginValueOn(); voxelIter; ++voxelIter) {
1070 const SdfValueT value = *voxelIter;
1071 SdfValueT inputValue;
1072 const Coord ijk = voxelIter.getCoord();
1073
1074 if (math::Abs(value) < background) {// disable boundary voxels from the mask tree
1075 maskLeaf->setValueOff(voxelIter.pos());
1076 } else {
1077 switch (mode) {
1079 voxelIter.setValue(value > 0 ? Unknown : -Unknown);
1080 break;
1082 if (value > 0) voxelIter.setValue(Unknown);
1083 else {
1084 maskLeaf->setValueOff(voxelIter.pos());
1085 bool isInputOn = sdfInputAcc.probeValue(ijk, inputValue);
1086 if ( !isInputOn ) voxelIter.setValueOff();
1087 else voxelIter.setValue(inputValue);
1088 }
1089 break;
1091 if (value < 0) voxelIter.setValue(-Unknown);
1092 else {
1093 maskLeaf->setValueOff(voxelIter.pos());
1094 bool isInputOn = sdfInputAcc.probeValue(ijk, inputValue);
1095 if ( !isInputOn ) voxelIter.setValueOff();
1096 else voxelIter.setValue(inputValue);
1097 }
1098 break;
1099 }
1100 }
1101 }
1102 };
1103
1104 leafManager.foreach( kernel );
1105
1106 // cache the leaf node origins for fast lookup in the sweeping kernels
1107 mParent->computeSweepMaskLeafOrigins();
1108
1109#ifdef BENCHMARK_FAST_SWEEPING
1110 timer.stop();
1111#endif
1112 }// FastSweeping::DilateKernel::run
1113
1114 // Private member data of DilateKernel
1116 const SdfValueT mBackground;
1117 typename SdfGridT::ConstPtr mSdfGridInput;
1118};// FastSweeping::DilateKernel
1119
1120////////////////////////////////////////////////////////////////////////////////
1121
1122template <typename SdfGridT, typename ExtValueT>
1123struct FastSweeping<SdfGridT, ExtValueT>::InitSdf
1124{
1126 InitSdf(FastSweeping &parent): mParent(&parent),
1127 mSdfGrid(parent.mSdfGrid.get()), mIsoValue(0), mAboveSign(0) {}
1128 InitSdf(const InitSdf&) = default;// for tbb::parallel_for
1129 InitSdf& operator=(const InitSdf&) = delete;
1130
1131 void run(SdfValueT isoValue)
1132 {
1133 mIsoValue = isoValue;
1134 mAboveSign = mParent->mIsInputSdf ? SdfValueT(1) : SdfValueT(-1);
1135 SdfTreeT &tree = mSdfGrid->tree();//sdf
1136 const bool hasActiveTiles = tree.hasActiveTiles();
1137
1138 if (mParent->mIsInputSdf && hasActiveTiles) {
1139 OPENVDB_THROW(RuntimeError, "FastSweeping: A SDF should not have active tiles!");
1140 }
1141
1142#ifdef BENCHMARK_FAST_SWEEPING
1143 util::CpuTimer timer("Initialize voxels");
1144#endif
1145 mParent->mSweepMask.clear();
1146 mParent->mSweepMask.topologyUnion(mParent->mSdfGrid->constTree());
1147
1148 {// Process all voxels
1149 tree::LeafManager<SdfTreeT> mgr(tree, 1);// we need one auxiliary buffer
1150 tbb::parallel_for(mgr.leafRange(32), *this);//multi-threaded
1151 mgr.swapLeafBuffer(1);//swap voxel values
1152 }
1153
1154#ifdef BENCHMARK_FAST_SWEEPING
1155 timer.restart("Initialize tiles - new");
1156#endif
1157 // Process all tiles
1158 tree::NodeManager<SdfTreeT, SdfTreeT::RootNodeType::LEVEL-1> mgr(tree);
1159 mgr.foreachBottomUp(*this);//multi-threaded
1160 tree.root().setBackground(std::numeric_limits<SdfValueT>::max(), false);
1161 if (hasActiveTiles) tree.voxelizeActiveTiles();//multi-threaded
1162
1163 // cache the leaf node origins for fast lookup in the sweeping kernels
1164
1165 mParent->computeSweepMaskLeafOrigins();
1166 }// FastSweeping::InitSdf::run
1167
1168 void operator()(const LeafRange& r) const
1169 {
1170 SweepMaskAccT sweepMaskAcc(mParent->mSweepMask);
1172 const SdfValueT isoValue = mIsoValue, above = mAboveSign*std::numeric_limits<SdfValueT>::max();//local copy
1173 const SdfValueT h = mAboveSign*static_cast<SdfValueT>(mSdfGrid->voxelSize()[0]);//Voxel size
1174 for (auto leafIter = r.begin(); leafIter; ++leafIter) {
1175 SdfValueT* sdf = leafIter.buffer(1).data();
1176 for (auto voxelIter = leafIter->beginValueAll(); voxelIter; ++voxelIter) {
1177 const SdfValueT value = *voxelIter;
1178 const bool isAbove = value > isoValue;
1179 if (!voxelIter.isValueOn()) {// inactive voxels
1180 sdf[voxelIter.pos()] = isAbove ? above : -above;
1181 } else {// active voxels
1182 const Coord ijk = voxelIter.getCoord();
1183 stencil.moveTo(ijk, value);
1184 const auto mask = stencil.intersectionMask( isoValue );
1185 if (mask.none()) {// most common case
1186 sdf[voxelIter.pos()] = isAbove ? above : -above;
1187 } else {// compute distance to iso-surface
1188 // disable boundary voxels from the mask tree
1189 sweepMaskAcc.setValueOff(ijk);
1190 const SdfValueT delta = value - isoValue;//offset relative to iso-value
1191 if (math::isApproxZero(delta)) {//voxel is on the iso-surface
1192 sdf[voxelIter.pos()] = 0;
1193 } else {//voxel is neighboring the iso-surface
1194 SdfValueT sum = 0;
1195 for (int i=0; i<6;) {
1196 SdfValueT d = std::numeric_limits<SdfValueT>::max(), d2;
1197 if (mask.test(i++)) d = math::Abs(delta/(value-stencil.getValue(i)));
1198 if (mask.test(i++)) {
1199 d2 = math::Abs(delta/(value-stencil.getValue(i)));
1200 if (d2 < d) d = d2;
1201 }
1202 if (d < std::numeric_limits<SdfValueT>::max()) sum += 1/(d*d);
1203 }
1204 sdf[voxelIter.pos()] = isAbove ? h / math::Sqrt(sum) : -h / math::Sqrt(sum);
1205 }// voxel is neighboring the iso-surface
1206 }// intersecting voxels
1207 }// active voxels
1208 }// loop over voxels
1209 }// loop over leaf nodes
1210 }// FastSweeping::InitSdf::operator(const LeafRange&)
1211
1212 template<typename RootOrInternalNodeT>
1213 void operator()(const RootOrInternalNodeT& node) const
1214 {
1215 const SdfValueT isoValue = mIsoValue, above = mAboveSign*std::numeric_limits<SdfValueT>::max();
1216 for (auto it = node.cbeginValueAll(); it; ++it) {
1217 SdfValueT& v = const_cast<SdfValueT&>(*it);
1218 v = v > isoValue ? above : -above;
1219 }//loop over all tiles
1220 }// FastSweeping::InitSdf::operator()(const RootOrInternalNodeT&)
1221
1222 // Public member data
1224 SdfGridT *mSdfGrid;//raw pointer, i.e. lock free
1225 SdfValueT mIsoValue;
1226 SdfValueT mAboveSign;//sign of distance values above the iso-value
1227};// FastSweeping::InitSdf
1228
1229
1230/// Private class of FastSweeping to perform multi-threaded initialization
1231template <typename SdfGridT, typename ExtValueT>
1232template <typename OpT>
1233struct FastSweeping<SdfGridT, ExtValueT>::InitExt
1234{
1235 using LeafRange = typename tree::LeafManager<SdfTreeT>::LeafRange;
1236 using OpPoolT = tbb::enumerable_thread_specific<OpT>;
1237 InitExt(FastSweeping &parent) : mParent(&parent),
1238 mOpPool(nullptr), mSdfGrid(parent.mSdfGrid.get()),
1239 mExtGrid(parent.mExtGrid.get()), mIsoValue(0), mAboveSign(0) {}
1240 InitExt(const InitExt&) = default;// for tbb::parallel_for
1241 InitExt& operator=(const InitExt&) = delete;
1242 void run(SdfValueT isoValue, const OpT &opPrototype)
1243 {
1244 static_assert(std::is_convertible<decltype(opPrototype(Vec3d(0))),ExtValueT>::value, "Invalid return type of functor");
1245 if (!mExtGrid) {
1246 OPENVDB_THROW(RuntimeError, "FastSweeping::InitExt expected an extension grid!");
1247 }
1248
1249 mAboveSign = mParent->mIsInputSdf ? SdfValueT(1) : SdfValueT(-1);
1250 mIsoValue = isoValue;
1251 auto &tree1 = mSdfGrid->tree();
1252 auto &tree2 = mExtGrid->tree();
1253 const bool hasActiveTiles = tree1.hasActiveTiles();//very fast
1254
1255 if (mParent->mIsInputSdf && hasActiveTiles) {
1256 OPENVDB_THROW(RuntimeError, "FastSweeping: A SDF should not have active tiles!");
1257 }
1258
1259#ifdef BENCHMARK_FAST_SWEEPING
1260 util::CpuTimer timer("Initialize voxels");
1261#endif
1262
1263 mParent->mSweepMask.clear();
1264 mParent->mSweepMask.topologyUnion(mParent->mSdfGrid->constTree());
1265
1266 {// Process all voxels
1267 // Define thread-local operators
1268 OpPoolT opPool(opPrototype);
1269 mOpPool = &opPool;
1270
1271 tree::LeafManager<SdfTreeT> mgr(tree1, 1);// we need one auxiliary buffer
1272 tbb::parallel_for(mgr.leafRange(32), *this);//multi-threaded
1273 mgr.swapLeafBuffer(1);//swap out auxiliary buffer
1274 }
1275
1276#ifdef BENCHMARK_FAST_SWEEPING
1277 timer.restart("Initialize tiles");
1278#endif
1279 {// Process all tiles
1280 tree::NodeManager<SdfTreeT, SdfTreeT::RootNodeType::LEVEL-1> mgr(tree1);
1281 mgr.foreachBottomUp(*this);//multi-threaded
1282 tree1.root().setBackground(std::numeric_limits<SdfValueT>::max(), false);
1283 if (hasActiveTiles) {
1284#ifdef BENCHMARK_FAST_SWEEPING
1285 timer.restart("Voxelizing active tiles");
1286#endif
1287 tree1.voxelizeActiveTiles();//multi-threaded
1288 tree2.voxelizeActiveTiles();//multi-threaded
1289 }
1290 }
1291
1292 // cache the leaf node origins for fast lookup in the sweeping kernels
1293
1294 mParent->computeSweepMaskLeafOrigins();
1295
1296#ifdef BENCHMARK_FAST_SWEEPING
1297 timer.stop();
1298#endif
1299 }// FastSweeping::InitExt::run
1300
1301 // int implements an update if minD needs to be updated
1302 template<typename ExtT = ExtValueT, typename SdfT = SdfValueT, typename std::enable_if<std::is_same<ExtT, int>::value, int>::type = 0>
1303 void sumHelper(ExtT& sum2, ExtT ext, bool update, const SdfT& /* d2 */) const { if (update) sum2 = ext; }// int implementation
1304
1305 // non-int implements a weighted sum
1306 template<typename ExtT = ExtValueT, typename SdfT = SdfValueT, typename std::enable_if<!std::is_same<ExtT, int>::value, int>::type = 0>
1307 void sumHelper(ExtT& sum2, ExtT ext, bool /* update */, const SdfT& d2) const { sum2 += static_cast<ExtValueT>(d2 * ext); }// non-int implementation
1308
1309 template<typename ExtT = ExtValueT, typename SdfT = SdfValueT, typename std::enable_if<std::is_same<ExtT, int>::value, int>::type = 0>
1310 ExtT extValHelper(ExtT extSum, const SdfT& /* sdfSum */) const { return extSum; }// int implementation
1311
1312 template<typename ExtT = ExtValueT, typename SdfT = SdfValueT, typename std::enable_if<!std::is_same<ExtT, int>::value, int>::type = 0>
1313 ExtT extValHelper(ExtT extSum, const SdfT& sdfSum) const {return ExtT((SdfT(1) / sdfSum) * extSum); }// non-int implementation
1314
1315 void operator()(const LeafRange& r) const
1316 {
1317 ExtAccT acc(mExtGrid->tree());
1318 SweepMaskAccT sweepMaskAcc(mParent->mSweepMask);
1319 math::GradStencil<SdfGridT, false> stencil(*mSdfGrid);
1320 const math::Transform& xform = mExtGrid->transform();
1321 typename OpPoolT::reference op = mOpPool->local();
1322 const SdfValueT isoValue = mIsoValue, above = mAboveSign*std::numeric_limits<SdfValueT>::max();//local copy
1323 const SdfValueT h = mAboveSign*static_cast<SdfValueT>(mSdfGrid->voxelSize()[0]);//Voxel size
1324 for (auto leafIter = r.begin(); leafIter; ++leafIter) {
1325 SdfValueT *sdf = leafIter.buffer(1).data();
1326 ExtValueT *ext = acc.probeLeaf(leafIter->origin())->buffer().data();//should be safe!
1327 for (auto voxelIter = leafIter->beginValueAll(); voxelIter; ++voxelIter) {
1328 const SdfValueT value = *voxelIter;
1329 const bool isAbove = value > isoValue;
1330 if (!voxelIter.isValueOn()) {// inactive voxels
1331 sdf[voxelIter.pos()] = isAbove ? above : -above;
1332 } else {// active voxels
1333 const Coord ijk = voxelIter.getCoord();
1334 stencil.moveTo(ijk, value);
1335 const auto mask = stencil.intersectionMask( isoValue );
1336 if (mask.none()) {// no zero-crossing neighbors, most common case
1337 sdf[voxelIter.pos()] = isAbove ? above : -above;
1338 // the ext grid already has its active values set to the background value
1339 } else {// compute distance to iso-surface
1340 // disable boundary voxels from the mask tree
1341 sweepMaskAcc.setValueOff(ijk);
1342 const SdfValueT delta = value - isoValue;//offset relative to iso-value
1343 if (math::isApproxZero(delta)) {//voxel is on the iso-surface
1344 sdf[voxelIter.pos()] = 0;
1345 ext[voxelIter.pos()] = ExtValueT(op(xform.indexToWorld(ijk)));
1346 } else {//voxel is neighboring the iso-surface
1347 SdfValueT sum1 = 0;
1348 ExtValueT sum2 = zeroVal<ExtValueT>();
1349 // minD is used to update sum2 in the integer case,
1350 // where we choose the value of the extension grid corresponding to
1351 // the smallest value of d in the 6 direction neighboring the current
1352 // voxel.
1353 SdfValueT minD = std::numeric_limits<SdfValueT>::max();
1354 for (int n=0, i=0; i<6;) {
1355 SdfValueT d = std::numeric_limits<SdfValueT>::max(), d2;
1356 if (mask.test(i++)) {
1357 d = math::Abs(delta/(value-stencil.getValue(i)));
1358 n = i - 1;
1359 }
1360 if (mask.test(i++)) {
1361 d2 = math::Abs(delta/(value-stencil.getValue(i)));
1362 if (d2 < d) {
1363 d = d2;
1364 n = i - 1;
1365 }
1366 }
1367 if (d < std::numeric_limits<SdfValueT>::max()) {
1368 d2 = 1/(d*d);
1369 sum1 += d2;
1370 const Vec3R xyz(static_cast<SdfValueT>(ijk[0])+d*static_cast<SdfValueT>(FastSweeping::mOffset[n][0]),
1371 static_cast<SdfValueT>(ijk[1])+d*static_cast<SdfValueT>(FastSweeping::mOffset[n][1]),
1372 static_cast<SdfValueT>(ijk[2])+d*static_cast<SdfValueT>(FastSweeping::mOffset[n][2]));
1373 // If current d is less than minD, update minD
1374 sumHelper(sum2, ExtValueT(op(xform.indexToWorld(xyz))), d < minD, d2);
1375 if (d < minD) minD = d;
1376 }
1377 }//look over six cases
1378 ext[voxelIter.pos()] = extValHelper(sum2, sum1);
1379 sdf[voxelIter.pos()] = isAbove ? h / math::Sqrt(sum1) : -h / math::Sqrt(sum1);
1380 }// voxel is neighboring the iso-surface
1381 }// intersecting voxels
1382 }// active voxels
1383 }// loop over voxels
1384 }// loop over leaf nodes
1385 }// FastSweeping::InitExt::operator(const LeafRange& r)
1386
1387 template<typename RootOrInternalNodeT>
1388 void operator()(const RootOrInternalNodeT& node) const
1389 {
1390 const SdfValueT isoValue = mIsoValue, above = mAboveSign*std::numeric_limits<SdfValueT>::max();
1391 for (auto it = node.cbeginValueAll(); it; ++it) {
1392 SdfValueT& v = const_cast<SdfValueT&>(*it);
1393 v = v > isoValue ? above : -above;
1394 }//loop over all tiles
1395 }
1396 // Public member data
1397 FastSweeping *mParent;
1398 OpPoolT *mOpPool;
1399 SdfGridT *mSdfGrid;
1400 ExtGridT *mExtGrid;
1401 SdfValueT mIsoValue;
1402 SdfValueT mAboveSign;//sign of distance values above the iso-value
1403};// FastSweeping::InitExt
1404
1405/// Private class of FastSweeping to perform multi-threaded initialization
1406template <typename SdfGridT, typename ExtValueT>
1407template <typename MaskTreeT>
1408struct FastSweeping<SdfGridT, ExtValueT>::MaskKernel
1409{
1410 using LeafRange = typename tree::LeafManager<const MaskTreeT>::LeafRange;
1411 MaskKernel(FastSweeping &parent) : mParent(&parent),
1412 mSdfGrid(parent.mSdfGrid.get()) {}
1413 MaskKernel(const MaskKernel &parent) = default;// for tbb::parallel_for
1414 MaskKernel& operator=(const MaskKernel&) = delete;
1415
1416 void run(const MaskTreeT &mask)
1417 {
1418#ifdef BENCHMARK_FAST_SWEEPING
1419 util::CpuTimer timer;
1420#endif
1421 auto &lsTree = mSdfGrid->tree();
1422
1423 static const SdfValueT Unknown = std::numeric_limits<SdfValueT>::max();
1424
1425#ifdef BENCHMARK_FAST_SWEEPING
1426 timer.restart("Changing background value");
1427#endif
1428 changeLevelSetBackground(lsTree, Unknown);//multi-threaded
1429
1430#ifdef BENCHMARK_FAST_SWEEPING
1431 timer.restart("Union with mask");//multi-threaded
1432#endif
1433 lsTree.topologyUnion(mask);//multi-threaded
1434
1435 // ignore active tiles since the input grid is assumed to be a level set
1436 tree::LeafManager<const MaskTreeT> mgr(mask);// super fast
1437
1438#ifdef BENCHMARK_FAST_SWEEPING
1439 timer.restart("Initializing grid and sweep mask");
1440#endif
1441
1442 mParent->mSweepMask.clear();
1443 mParent->mSweepMask.topologyUnion(mParent->mSdfGrid->constTree());
1444
1445 using LeafManagerT = tree::LeafManager<SweepMaskTreeT>;
1446 using LeafT = typename SweepMaskTreeT::LeafNodeType;
1447 LeafManagerT leafManager(mParent->mSweepMask);
1448
1449 auto kernel = [&](LeafT& leaf, size_t /*leafIdx*/) {
1450 static const SdfValueT Unknown = std::numeric_limits<SdfValueT>::max();
1451 SdfAccT acc(mSdfGrid->tree());
1452 // The following hack is safe due to the topology union in
1453 // init and the fact that SdfValueT is known to be a floating point!
1454 SdfValueT *data = acc.probeLeaf(leaf.origin())->buffer().data();
1455 for (auto voxelIter = leaf.beginValueOn(); voxelIter; ++voxelIter) {// mask voxels
1456 if (math::Abs( data[voxelIter.pos()] ) < Unknown ) {
1457 // disable boundary voxels from the mask tree
1458 voxelIter.setValue(false);
1459 }
1460 }
1461 };
1462 leafManager.foreach( kernel );
1463
1464 // cache the leaf node origins for fast lookup in the sweeping kernels
1465 mParent->computeSweepMaskLeafOrigins();
1466
1467#ifdef BENCHMARK_FAST_SWEEPING
1468 timer.stop();
1469#endif
1470 }// FastSweeping::MaskKernel::run
1471
1472 // Private member data of MaskKernel
1473 FastSweeping *mParent;
1474 SdfGridT *mSdfGrid;//raw pointer, i.e. lock free
1475};// FastSweeping::MaskKernel
1476
1477/// @brief Private class of FastSweeping to perform concurrent fast sweeping in two directions
1478template <typename SdfGridT, typename ExtValueT>
1479struct FastSweeping<SdfGridT, ExtValueT>::SweepingKernel
1480{
1481 SweepingKernel(FastSweeping &parent) : mParent(&parent) {}
1484
1485 /// Main method that performs concurrent bi-directional sweeps
1486 template<typename HashOp>
1488 {
1489#ifdef BENCHMARK_FAST_SWEEPING
1490 util::CpuTimer timer;
1491#endif
1492
1493 // mask of the active voxels to be solved for, i.e. excluding boundary voxels
1494 const SweepMaskTreeT& maskTree = mParent->mSweepMask;
1495
1496 using LeafManagerT = typename tree::LeafManager<const SweepMaskTreeT>;
1497 using LeafT = typename SweepMaskTreeT::LeafNodeType;
1498 LeafManagerT leafManager(maskTree);
1499
1500 // compute the leaf node slices that have active voxels in them
1501 // the sliding window of the has keys is -14 to 21 (based on an 8x8x8 leaf node
1502 // and the extrema hash values i-j-k and i+j+k), but we use a larger mask window here to
1503 // easily accommodate any leaf dimension. The mask offset is used to be able to
1504 // store this in a fixed-size byte array
1505 constexpr int maskOffset = LeafT::DIM * 3;
1506 constexpr int maskRange = maskOffset * 2;
1507
1508 // mark each possible slice in each leaf node that has one or more active voxels in it
1509 std::vector<int8_t> leafSliceMasks(leafManager.leafCount()*maskRange);
1510 auto kernel1 = [&](const LeafT& leaf, size_t leafIdx) {
1511 const size_t leafOffset = leafIdx * maskRange;
1512 for (auto voxelIter = leaf.cbeginValueOn(); voxelIter; ++voxelIter) {
1513 const Coord ijk = LeafT::offsetToLocalCoord(voxelIter.pos());
1514 leafSliceMasks[leafOffset + hash(ijk) + maskOffset] = uint8_t(1);
1515 }
1516 };
1517 leafManager.foreach( kernel1 );
1518
1519 // compute the voxel slice map using a thread-local-storage hash map
1520 // the key of the hash map is the slice index of the voxel coord (ijk.x() + ijk.y() + ijk.z())
1521 // the values are an array of indices for every leaf that has active voxels with this slice index
1522 using ThreadLocalMap = std::unordered_map</*voxelSliceKey=*/int64_t, /*leafIdx=*/std::deque<size_t>>;
1523 tbb::enumerable_thread_specific<ThreadLocalMap> pool;
1524 auto kernel2 = [&](const LeafT& leaf, size_t leafIdx) {
1525 ThreadLocalMap& map = pool.local();
1526 const Coord& origin = leaf.origin();
1527 const int64_t leafKey = hash(origin);
1528 const size_t leafOffset = leafIdx * maskRange;
1529 for (int sliceIdx = 0; sliceIdx < maskRange; sliceIdx++) {
1530 if (leafSliceMasks[leafOffset + sliceIdx] == uint8_t(1)) {
1531 const int64_t voxelSliceKey = leafKey+sliceIdx-maskOffset;
1532 map[voxelSliceKey].emplace_back(leafIdx);
1533 }
1534 }
1535 };
1536 leafManager.foreach( kernel2 );
1537
1538 // combine into a single ordered map keyed by the voxel slice key
1539 // note that this is now stored in a map ordered by voxel slice key,
1540 // so sweep slices can be processed in order
1541 for (auto poolIt = pool.begin(); poolIt != pool.end(); ++poolIt) {
1542 const ThreadLocalMap& map = *poolIt;
1543 for (const auto& it : map) {
1544 for (const size_t leafIdx : it.second) {
1545 mVoxelSliceMap[it.first].emplace_back(leafIdx, NodeMaskPtrT());
1546 }
1547 }
1548 }
1549
1550 // extract the voxel slice keys for random access into the map
1551 mVoxelSliceKeys.reserve(mVoxelSliceMap.size());
1552 for (const auto& it : mVoxelSliceMap) {
1553 mVoxelSliceKeys.push_back(it.first);
1554 }
1555
1556 // allocate the node masks in parallel, as the map is populated in serial
1557 auto kernel3 = [&](tbb::blocked_range<size_t>& range) {
1558 for (size_t i = range.begin(); i < range.end(); i++) {
1559 const int64_t key = mVoxelSliceKeys[i];
1560 for (auto& it : mVoxelSliceMap[key]) {
1561 it.second = std::make_unique<NodeMaskT>();
1562 }
1563 }
1564 };
1565 tbb::parallel_for(tbb::blocked_range<size_t>(0, mVoxelSliceKeys.size()), kernel3);
1566
1567 // each voxel slice contains a leafIdx-nodeMask pair,
1568 // this routine populates these node masks to select only the active voxels
1569 // from the mask tree that have the same voxel slice key
1570 // TODO: a small optimization here would be to union this leaf node mask with
1571 // a pre-computed one for this particular slice pattern
1572 auto kernel4 = [&](tbb::blocked_range<size_t>& range) {
1573 for (size_t i = range.begin(); i < range.end(); i++) {
1574 const int64_t voxelSliceKey = mVoxelSliceKeys[i];
1575 LeafSliceArray& leafSliceArray = mVoxelSliceMap[voxelSliceKey];
1576 for (LeafSlice& leafSlice : leafSliceArray) {
1577 const size_t leafIdx = leafSlice.first;
1578 NodeMaskPtrT& nodeMask = leafSlice.second;
1579 const LeafT& leaf = leafManager.leaf(leafIdx);
1580 const Coord& origin = leaf.origin();
1581 const int64_t leafKey = hash(origin);
1582 for (auto voxelIter = leaf.cbeginValueOn(); voxelIter; ++voxelIter) {
1583 const Index voxelIdx = voxelIter.pos();
1584 const Coord ijk = LeafT::offsetToLocalCoord(voxelIdx);
1585 const int64_t key = leafKey + hash(ijk);
1586 if (key == voxelSliceKey) {
1587 nodeMask->setOn(voxelIdx);
1588 }
1589 }
1590 }
1591 }
1592 };
1593 tbb::parallel_for(tbb::blocked_range<size_t>(0, mVoxelSliceKeys.size()), kernel4);
1594 }// FastSweeping::SweepingKernel::computeVoxelSlices
1595
1596 // Private struct for nearest neighbor grid points (very memory light!)
1597 struct NN {
1598 SdfValueT v;
1599 int n;
1600 inline static Coord ijk(const Coord &p, int i) { return p + FastSweeping::mOffset[i]; }
1601 NN() : v(), n() {}
1602 NN(const SdfAccT &a, const Coord &p, int i) : v(math::Abs(a.getValue(ijk(p,i)))), n(i) {}
1603 inline Coord operator()(const Coord &p) const { return ijk(p, n); }
1604 inline bool operator<(const NN &rhs) const { return v < rhs.v; }
1605 inline operator bool() const { return v < SdfValueT(1000); }
1606 };// NN
1607
1608 /// @note Extending an integer field is based on the nearest-neighbor interpolation
1609 template<typename ExtT = ExtValueT, typename SdfT = SdfValueT, typename std::enable_if<std::is_same<ExtT, int>::value, int>::type = 0>
1610 ExtT twoNghbr(const NN& d1, const NN& d2, const SdfT& /* w */, const ExtT& v1, const ExtT& v2) const { return d1.v < d2.v ? v1 : v2; }// int implementation
1611
1612 /// @note Extending a non-integer field is based on a weighted interpolation
1613 template<typename ExtT = ExtValueT, typename SdfT = SdfValueT, typename std::enable_if<!std::is_same<ExtT, int>::value, int>::type = 0>
1614 ExtT twoNghbr(const NN& d1, const NN& d2, const SdfT& w, const ExtT& v1, const ExtT& v2) const { return ExtT(w*(d1.v*v1 + d2.v*v2)); }// non-int implementation
1615
1616 /// @note Extending an integer field is based on the nearest-neighbor interpolation
1617 template<typename ExtT = ExtValueT, typename SdfT = SdfValueT, typename std::enable_if<std::is_same<ExtT, int>::value, int>::type = 0>
1618 ExtT threeNghbr(const NN& d1, const NN& d2, const NN& d3, const SdfT& /* w */, const ExtT& v1, const ExtT& v2, const ExtT& v3) const {
1619 math::Vec3<SdfT> d(d1.v, d2.v, d3.v);
1620 math::Vec3<ExtT> v(v1, v2, v3);
1621 return v[static_cast<int>(math::MinIndex(d))];
1622 }// int implementation
1623
1624 /// @note Extending a non-integer field is based on a weighted interpolation
1625 template<typename ExtT = ExtValueT, typename SdfT = SdfValueT, typename std::enable_if<!std::is_same<ExtT, int>::value, int>::type = 0>
1626 ExtT threeNghbr(const NN& d1, const NN& d2, const NN& d3, const SdfT& w, const ExtT& v1, const ExtT& v2, const ExtT& v3) const {
1627 return ExtT(w*(d1.v*v1 + d2.v*v2 + d3.v*v3));
1628 }// non-int implementation
1629
1630 void sweep()
1631 {
1632 typename ExtGridT::TreeType *tree2 = mParent->mExtGrid ? &mParent->mExtGrid->tree() : nullptr;
1633 typename ExtGridT::TreeType *tree3 = mParent->mExtGridInput ? &mParent->mExtGridInput->tree() : nullptr;
1634
1635 const SdfValueT h = static_cast<SdfValueT>(mParent->mSdfGrid->voxelSize()[0]);
1636 const SdfValueT sqrt2h = math::Sqrt(SdfValueT(2))*h;
1637 const FastSweepingDomain mode = mParent->mSweepDirection;
1638 const bool isInputSdf = mParent->mIsInputSdf;
1639
1640 // If we are using an extension in one direction, we need a reference grid
1641 // for the default value of the extension for the voxels that are not
1642 // intended to be updated by the sweeping algorithm.
1643 if (tree2 && mode != FastSweepingDomain::SWEEP_ALL) OPENVDB_ASSERT(tree3);
1644
1645 const std::vector<Coord>& leafNodeOrigins = mParent->mSweepMaskLeafOrigins;
1646
1647 int64_t voxelSliceIndex(0);
1648
1649 auto kernel = [&](const tbb::blocked_range<size_t>& range) {
1650 using LeafT = typename SdfGridT::TreeType::LeafNodeType;
1651
1652 SdfAccT acc1(mParent->mSdfGrid->tree());
1653 auto acc2 = std::unique_ptr<ExtAccT>(tree2 ? new ExtAccT(*tree2) : nullptr);
1654 auto acc3 = std::unique_ptr<ExtAccT>(tree3 ? new ExtAccT(*tree3) : nullptr);
1655 SdfValueT absV, sign, update, D;
1656 NN d1, d2, d3;//distance values and coordinates of closest neighbor points
1657
1658 const LeafSliceArray& leafSliceArray = mVoxelSliceMap[voxelSliceIndex];
1659
1660 // Solves Godunov's scheme: [x-d1]^2 + [x-d2]^2 + [x-d3]^2 = h^2
1661 // where [X] = (X>0?X:0) and ai=min(di+1,di-1)
1662 for (size_t i = range.begin(); i < range.end(); ++i) {
1663
1664 // iterate over all leafs in the slice and extract the leaf
1665 // and node mask for each slice pattern
1666
1667 const LeafSlice& leafSlice = leafSliceArray[i];
1668 const size_t leafIdx = leafSlice.first;
1669 const NodeMaskPtrT& nodeMask = leafSlice.second;
1670
1671 const Coord& origin = leafNodeOrigins[leafIdx];
1672
1673 Coord ijk;
1674 for (auto indexIter = nodeMask->beginOn(); indexIter; ++indexIter) {
1675
1676 // Get coordinate of center point of the FD stencil
1677 ijk = origin + LeafT::offsetToLocalCoord(indexIter.pos());
1678
1679 // Find the closes neighbors in the three axial directions
1680 d1 = std::min(NN(acc1, ijk, 0), NN(acc1, ijk, 1));
1681 d2 = std::min(NN(acc1, ijk, 2), NN(acc1, ijk, 3));
1682 d3 = std::min(NN(acc1, ijk, 4), NN(acc1, ijk, 5));
1683
1684 if (!(d1 || d2 || d3)) continue;//no valid neighbors
1685
1686 // Get the center point of the FD stencil (assumed to be an active voxel)
1687 // Note this const_cast is normally unsafe but by design we know the tree
1688 // to be static, of floating-point type and containing active voxels only!
1689 SdfValueT &value = const_cast<SdfValueT&>(acc1.getValue(ijk));
1690
1691 // Extract the sign
1692 sign = value >= SdfValueT(0) ? SdfValueT(1) : SdfValueT(-1);
1693
1694 // Absolute value
1695 absV = math::Abs(value);
1696
1697 // sort values so d1 <= d2 <= d3
1698 if (d2 < d1) std::swap(d1, d2);
1699 if (d3 < d2) std::swap(d2, d3);
1700 if (d2 < d1) std::swap(d1, d2);
1701
1702 // Test if there is a solution depending on ONE of the neighboring voxels
1703 // if d2 - d1 >= h => d2 >= d1 + h then:
1704 // (x-d1)^2=h^2 => x = d1 + h
1705 update = d1.v + h;
1706 if (update <= d2.v) {
1707 if (update < absV) {
1708 value = sign * update;
1709 if (acc2) {
1710 // There is an assert upstream to check if mExtGridInput exists if mode != SWEEP_ALL
1711 ExtValueT updateExt = acc2->getValue(d1(ijk));
1713 if (isInputSdf) updateExt = (value >= SdfValueT(0)) ? acc2->getValue(d1(ijk)) : acc3->getValue(ijk);
1714 else updateExt = (value <= SdfValueT(0)) ? acc2->getValue(d1(ijk)) : acc3->getValue(ijk);
1715 } // SWEEP_GREATER_THAN_ISOVALUE
1717 if (isInputSdf) updateExt = (value <= SdfValueT(0)) ? acc2->getValue(d1(ijk)) : acc3->getValue(ijk);
1718 else updateExt = (value >= SdfValueT(0)) ? acc2->getValue(d1(ijk)) : acc3->getValue(ijk);
1719 } // SWEEP_LESS_THAN_ISOVALUE
1720 acc2->setValue(ijk, updateExt);
1721 }//update ext?
1722 }//update sdf?
1723 continue;
1724 }// one neighbor case
1725
1726 // Test if there is a solution depending on TWO of the neighboring voxels
1727 // (x-d1)^2 + (x-d2)^2 = h^2
1728 //D = SdfValueT(2) * h * h - math::Pow2(d1.v - d2.v);// = 2h^2-(d1-d2)^2
1729 //if (D >= SdfValueT(0)) {// non-negative discriminant
1730 if (d2.v <= sqrt2h + d1.v) {
1731 D = SdfValueT(2) * h * h - math::Pow2(d1.v - d2.v);// = 2h^2-(d1-d2)^2
1732 update = SdfValueT(0.5) * (d1.v + d2.v + std::sqrt(D));
1733 if (update > d2.v && update <= d3.v) {
1734 if (update < absV) {
1735 value = sign * update;
1736 if (acc2) {
1737 d1.v -= update;
1738 d2.v -= update;
1739 // affine combination of two neighboring extension values
1740 const SdfValueT w = SdfValueT(1)/(d1.v+d2.v);
1741 const ExtValueT v1 = acc2->getValue(d1(ijk));
1742 const ExtValueT v2 = acc2->getValue(d2(ijk));
1743 const ExtValueT extVal = twoNghbr(d1, d2, w, v1, v2);
1744
1745 ExtValueT updateExt = extVal;
1747 if (isInputSdf) updateExt = (value >= SdfValueT(0)) ? extVal : acc3->getValue(ijk);
1748 else updateExt = (value <= SdfValueT(0)) ? extVal : acc3->getValue(ijk);
1749 } // SWEEP_GREATER_THAN_ISOVALUE
1751 if (isInputSdf) updateExt = (value <= SdfValueT(0)) ? extVal : acc3->getValue(ijk);
1752 else updateExt = (value >= SdfValueT(0)) ? extVal : acc3->getValue(ijk);
1753 } // SWEEP_LESS_THAN_ISOVALUE
1754 acc2->setValue(ijk, updateExt);
1755 }//update ext?
1756 }//update sdf?
1757 continue;
1758 }//test for two neighbor case
1759 }//test for non-negative determinant
1760
1761 // Test if there is a solution depending on THREE of the neighboring voxels
1762 // (x-d1)^2 + (x-d2)^2 + (x-d3)^2 = h^2
1763 // 3x^2 - 2(d1 + d2 + d3)x + d1^2 + d2^2 + d3^2 = h^2
1764 // ax^2 + bx + c=0, a=3, b=-2(d1+d2+d3), c=d1^2 + d2^2 + d3^2 - h^2
1765 const SdfValueT d123 = d1.v + d2.v + d3.v;
1766 D = d123*d123 - SdfValueT(3)*(d1.v*d1.v + d2.v*d2.v + d3.v*d3.v - h * h);
1767 if (D >= SdfValueT(0)) {// non-negative discriminant
1768 update = SdfValueT(1.0/3.0) * (d123 + std::sqrt(D));//always passes test
1769 //if (update > d3.v) {//disabled due to round-off errors
1770 if (update < absV) {
1771 value = sign * update;
1772 if (acc2) {
1773 d1.v -= update;
1774 d2.v -= update;
1775 d3.v -= update;
1776 // affine combination of three neighboring extension values
1777 const SdfValueT w = SdfValueT(1)/(d1.v+d2.v+d3.v);
1778 const ExtValueT v1 = acc2->getValue(d1(ijk));
1779 const ExtValueT v2 = acc2->getValue(d2(ijk));
1780 const ExtValueT v3 = acc2->getValue(d3(ijk));
1781 const ExtValueT extVal = threeNghbr(d1, d2, d3, w, v1, v2, v3);
1782
1783 ExtValueT updateExt = extVal;
1785 if (isInputSdf) updateExt = (value >= SdfValueT(0)) ? extVal : acc3->getValue(ijk);
1786 else updateExt = (value <= SdfValueT(0)) ? extVal : acc3->getValue(ijk);
1787 } // SWEEP_GREATER_THAN_ISOVALUE
1789 if (isInputSdf) updateExt = (value <= SdfValueT(0)) ? extVal : acc3->getValue(ijk);
1790 else updateExt = (value >= SdfValueT(0)) ? extVal : acc3->getValue(ijk);
1791 } // SWEEP_LESS_THAN_ISOVALUE
1792 acc2->setValue(ijk, updateExt);
1793 }//update ext?
1794 }//update sdf?
1795 }//test for non-negative determinant
1796 }//loop over coordinates
1797 }
1798 };
1799
1800#ifdef BENCHMARK_FAST_SWEEPING
1801 util::CpuTimer timer("Forward sweep");
1802#endif
1803
1804 for (size_t i = 0; i < mVoxelSliceKeys.size(); i++) {
1805 voxelSliceIndex = mVoxelSliceKeys[i];
1806 tbb::parallel_for(tbb::blocked_range<size_t>(0, mVoxelSliceMap[voxelSliceIndex].size()), kernel);
1807 }
1808
1809#ifdef BENCHMARK_FAST_SWEEPING
1810 timer.restart("Backward sweeps");
1811#endif
1812 for (size_t i = mVoxelSliceKeys.size(); i > 0; i--) {
1813 voxelSliceIndex = mVoxelSliceKeys[i-1];
1814 tbb::parallel_for(tbb::blocked_range<size_t>(0, mVoxelSliceMap[voxelSliceIndex].size()), kernel);
1815 }
1816
1817#ifdef BENCHMARK_FAST_SWEEPING
1818 timer.stop();
1819#endif
1820 }// FastSweeping::SweepingKernel::sweep
1821
1822private:
1823 using NodeMaskT = typename SweepMaskTreeT::LeafNodeType::NodeMaskType;
1824 using NodeMaskPtrT = std::unique_ptr<NodeMaskT>;
1825 // using a unique ptr for the NodeMask allows for parallel allocation,
1826 // but makes this class not copy-constructible
1827 using LeafSlice = std::pair</*leafIdx=*/size_t, /*leafMask=*/NodeMaskPtrT>;
1828 using LeafSliceArray = std::deque<LeafSlice>;
1829 using VoxelSliceMap = std::map</*voxelSliceKey=*/int64_t, LeafSliceArray>;
1830
1831 // Private member data of SweepingKernel
1832 FastSweeping *mParent;
1833 VoxelSliceMap mVoxelSliceMap;
1834 std::vector<int64_t> mVoxelSliceKeys;
1835};// FastSweeping::SweepingKernel
1836
1837////////////////////////////////////////////////////////////////////////////////
1838
1839template<typename GridT>
1840typename GridT::Ptr
1841fogToSdf(const GridT &fogGrid,
1842 typename GridT::ValueType isoValue,
1843 int nIter)
1844{
1846 if (fs.initSdf(fogGrid, isoValue, /*isInputSdf*/false)) fs.sweep(nIter);
1847 return fs.sdfGrid();
1848}
1849
1850template<typename GridT>
1851typename GridT::Ptr
1852sdfToSdf(const GridT &sdfGrid,
1853 typename GridT::ValueType isoValue,
1854 int nIter)
1855{
1857 if (fs.initSdf(sdfGrid, isoValue, /*isInputSdf*/true)) fs.sweep(nIter);
1858 return fs.sdfGrid();
1859}
1860
1861template<typename FogGridT, typename ExtOpT, typename ExtValueT>
1862typename FogGridT::template ValueConverter<ExtValueT>::Type::Ptr
1863fogToExt(const FogGridT &fogGrid,
1864 const ExtOpT &op,
1865 const ExtValueT& background,
1866 typename FogGridT::ValueType isoValue,
1867 int nIter,
1868 FastSweepingDomain mode,
1869 const typename FogGridT::template ValueConverter<ExtValueT>::Type::ConstPtr extGrid)
1870{
1872 if (fs.initExt(fogGrid, op, background, isoValue, /*isInputSdf*/false, mode, extGrid))
1873 fs.sweep(nIter, /*finalize*/true);
1874 return fs.extGrid();
1875}
1876
1877template<typename SdfGridT, typename OpT, typename ExtValueT>
1878typename SdfGridT::template ValueConverter<ExtValueT>::Type::Ptr
1879sdfToExt(const SdfGridT &sdfGrid,
1880 const OpT &op,
1881 const ExtValueT &background,
1882 typename SdfGridT::ValueType isoValue,
1883 int nIter,
1884 FastSweepingDomain mode,
1885 const typename SdfGridT::template ValueConverter<ExtValueT>::Type::ConstPtr extGrid)
1886{
1888 if (fs.initExt(sdfGrid, op, background, isoValue, /*isInputSdf*/true, mode, extGrid))
1889 fs.sweep(nIter, /*finalize*/true);
1890 return fs.extGrid();
1891}
1892
1893template<typename FogGridT, typename ExtOpT, typename ExtValueT>
1894std::pair<typename FogGridT::Ptr, typename FogGridT::template ValueConverter<ExtValueT>::Type::Ptr>
1895fogToSdfAndExt(const FogGridT &fogGrid,
1896 const ExtOpT &op,
1897 const ExtValueT &background,
1898 typename FogGridT::ValueType isoValue,
1899 int nIter,
1900 FastSweepingDomain mode,
1901 const typename FogGridT::template ValueConverter<ExtValueT>::Type::ConstPtr extGrid)
1902{
1904 if (fs.initExt(fogGrid, op, background, isoValue, /*isInputSdf*/false, mode, extGrid))
1905 fs.sweep(nIter, /*finalize*/true);
1906 return std::make_pair(fs.sdfGrid(), fs.extGrid());
1907}
1908
1909template<typename SdfGridT, typename ExtOpT, typename ExtValueT>
1910std::pair<typename SdfGridT::Ptr, typename SdfGridT::template ValueConverter<ExtValueT>::Type::Ptr>
1911sdfToSdfAndExt(const SdfGridT &sdfGrid,
1912 const ExtOpT &op,
1913 const ExtValueT &background,
1914 typename SdfGridT::ValueType isoValue,
1915 int nIter,
1916 FastSweepingDomain mode,
1917 const typename SdfGridT::template ValueConverter<ExtValueT>::Type::ConstPtr extGrid)
1918{
1920 if (fs.initExt(sdfGrid, op, background, isoValue, /*isInputSdf*/true, mode, extGrid))
1921 fs.sweep(nIter, /*finalize*/true);
1922 return std::make_pair(fs.sdfGrid(), fs.extGrid());
1923}
1924
1925template<typename GridT>
1926typename GridT::Ptr
1927dilateSdf(const GridT &sdfGrid,
1928 int dilation,
1930 int nIter,
1931 FastSweepingDomain mode)
1932{
1934 if (fs.initDilate(sdfGrid, dilation, nn, /*sweep direction*/ mode)) fs.sweep(nIter);
1935 return fs.sdfGrid();
1936}
1937
1938template<typename GridT, typename MaskTreeT>
1939typename GridT::Ptr
1940maskSdf(const GridT &sdfGrid,
1941 const Grid<MaskTreeT> &mask,
1942 bool ignoreActiveTiles,
1943 int nIter)
1944{
1946 if (fs.initMask(sdfGrid, mask, ignoreActiveTiles)) fs.sweep(nIter);
1947 return fs.sdfGrid();
1948}
1949
1950
1951////////////////////////////////////////
1952
1953
1954// Explicit Template Instantiation
1955
1956#ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
1957
1958#ifdef OPENVDB_INSTANTIATE_FASTSWEEPING
1960#endif
1961
1962#define _FUNCTION(TreeT) \
1963 Grid<TreeT>::Ptr fogToSdf(const Grid<TreeT>&, TreeT::ValueType, int)
1965#undef _FUNCTION
1966
1967#define _FUNCTION(TreeT) \
1968 Grid<TreeT>::Ptr sdfToSdf(const Grid<TreeT>&, TreeT::ValueType, int)
1970#undef _FUNCTION
1971
1972#define _FUNCTION(TreeT) \
1973 Grid<TreeT>::Ptr dilateSdf(const Grid<TreeT>&, int, NearestNeighbors, int, FastSweepingDomain)
1975#undef _FUNCTION
1976
1977#endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
1978
1979
1980} // namespace tools
1981} // namespace OPENVDB_VERSION_NAME
1982} // namespace openvdb
1983
1984#endif // OPENVDB_TOOLS_FASTSWEEPING_HAS_BEEN_INCLUDED
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
A LeafManager manages a linear array of pointers to a given tree's leaf nodes, as well as optional au...
Miscellaneous utility methods that operate primarily or exclusively on level set grids.
Implementation of morphological dilation and erosion.
Functions to efficiently compute histograms, extrema (min/max) and statistics (mean,...
math::Transform & transform()
Return a reference to this grid's transform, which might be shared with other grids.
Definition Grid.h:412
GridClass getGridClass() const
Return the class of volumetric data (level set, fog volume, etc.) that is stored in this grid.
Container class that associates a tree with a transform and metadata.
Definition Grid.h:571
TreeType & tree()
Return a reference to this grid's tree, which might be shared with other grids.
Definition Grid.h:906
SharedPtr< Grid > Ptr
Definition Grid.h:573
Definition Exceptions.h:63
Tag dispatch class that distinguishes topology copy constructors from deep copy constructors.
Definition Types.h:754
const ValueType & getValue(unsigned int pos=0) const
Return the value from the stencil buffer with linear offset pos.
Definition Stencils.h:97
std::bitset< 6 > intersectionMask(const ValueType &isoValue=zeroVal< ValueType >()) const
Return true a bit-mask where the 6 bits indicates if the center of the stencil intersects the iso-con...
Definition Stencils.h:185
void moveTo(const Coord &ijk)
Initialize the stencil buffer with the values of voxel (i, j, k) and its neighbors.
Definition Stencils.h:47
Signed (x, y, z) 32-bit integer coordinates.
Definition Coord.h:26
Definition Stencils.h:1229
Templated class to compute the minimum and maximum values.
Definition Stats.h:32
Definition Vec3.h:25
Computes signed distance values from an initial iso-surface and optionally performs velocity extensio...
Definition FastSweeping.h:462
bool initMask(const SdfGridT &sdfGrid, const Grid< MaskTreeT > &mask, bool ignoreActiveTiles=false)
Initializer used for the extension of an existing signed distance field into the active values of an ...
Definition FastSweeping.h:815
size_t boundaryVoxelCount() const
Return the number of voxels that defined the boundary condition.
Definition FastSweeping.h:664
FastSweepingDomain sweepDirection() const
Return whether the sweep update is in all direction (SWEEP_ALL), greater than isovalue (SWEEP_GREATER...
Definition FastSweeping.h:675
bool isInputSdf()
Return whether the fast-sweeping input grid a signed distance function or not (fog).
Definition FastSweeping.h:678
ExtGridT::Ptr extGridInput()
Returns a shared pointer to the extension grid input. This is non-NULL if this class is used to exten...
Definition FastSweeping.h:516
FastSweeping()
Constructor.
Definition FastSweeping.h:719
bool isValid() const
Return true if there are voxels and boundaries to solve for.
Definition FastSweeping.h:667
size_t sweepingVoxelCount() const
Return the number of voxels that will be solved for.
Definition FastSweeping.h:661
~FastSweeping()
Destructor.
Definition FastSweeping.h:486
bool initSdf(const SdfGridT &sdfGrid, SdfValueT isoValue, bool isInputSdf)
Initializer for input grids that are either a signed distance field or a scalar fog volume.
Definition FastSweeping.h:766
FastSweeping & operator=(const FastSweeping &)=delete
Disallow copy assignment.
SdfGridT::Ptr sdfGrid()
Returns a shared pointer to the signed distance field computed by this class.
Definition FastSweeping.h:500
void sweep(int nIter=1, bool finalize=true)
Perform nIter iterations of the fast sweeping algorithm.
Definition FastSweeping.h:849
ExtGridT::Ptr extGrid()
Returns a shared pointer to the extension field computed by this class.
Definition FastSweeping.h:508
void clear()
Clears all the grids and counters so initialization can be called again.
Definition FastSweeping.h:725
FastSweeping(const FastSweeping &)=delete
Disallow copy construction.
bool initDilate(const SdfGridT &sdfGrid, int dilation, NearestNeighbors nn=NN_FACE, FastSweepingDomain mode=FastSweepingDomain::SWEEP_ALL)
Initializer used when dilating an existing signed distance field.
Definition FastSweeping.h:803
bool initExt(const SdfGridT &sdfGrid, const ExtOpT &op, const ExtValueT &background, SdfValueT isoValue, bool isInputSdf, FastSweepingDomain mode=FastSweepingDomain::SWEEP_ALL, const typename ExtGridT::ConstPtr extGrid=nullptr)
Initializer used whenever velocity extension is performed in addition to the computation of signed di...
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition LeafManager.h:86
bool swapLeafBuffer(size_t bufferIdx, bool serial=false)
Swap each leaf node's buffer with the nth corresponding auxiliary buffer, where n = bufferIdx.
Definition LeafManager.h:360
LeafRange leafRange(size_t grainsize=1) const
Return a TBB-compatible LeafRange.
Definition LeafManager.h:346
To facilitate threading over the nodes of a tree, cache node pointers in linear arrays,...
Definition NodeManager.h:532
void foreachTopDown(const NodeOp &op, bool threaded=true, size_t grainSize=1)
Definition NodeManager.h:632
void setValueOff(const Coord &xyz, const ValueType &value)
Set a particular value at the given coordinate and mark the coordinate as inactive.
Definition ValueAccessor.h:607
const ValueType & getValue(const Coord &xyz) const
Return the value of the voxel at the given coordinates.
Definition ValueAccessor.h:455
Simple timer for basic profiling.
Definition CpuTimer.h:67
double stop() const
Returns and prints time in milliseconds since construction or start was called.
Definition CpuTimer.h:128
double restart()
Re-start timer.
Definition CpuTimer.h:150
__hostdev__ uint32_t hash(uint32_t x)
Definition common.h:16
@ Unknown
Definition NanoVDB.h:223
OPENVDB_AX_API void run(const char *ax, openvdb::GridBase &grid, const AttributeBindings &bindings={})
Run a full AX pipeline (parse, compile and execute) on a single OpenVDB Grid.
Definition Types.h:763
bool isApproxZero(const Type &x, const Type &tolerance)
Return true if x is equal to zero to within the given tolerance.
Definition Math.h:360
float Sqrt(float x)
Return the square root of a floating-point value.
Definition Math.h:829
size_t MinIndex(const Vec3T &v)
Return the index [0,1,2] of the smallest value in a 3D vector.
Definition Math.h:999
Vec3< double > Vec3d
Definition Vec3.h:708
Coord Abs(const Coord &xyz)
Definition Coord.h:518
Type Pow2(Type x)
Return x2.
Definition Math.h:570
GridT::Ptr fogToSdf(const GridT &fogGrid, typename GridT::ValueType isoValue, int nIter=1)
Converts a scalar fog volume into a signed distance function. Active input voxels with scalar values ...
Definition FastSweeping.h:1841
std::pair< typename SdfGridT::Ptr, typename SdfGridT::template ValueConverter< ExtValueT >::Type::Ptr > sdfToSdfAndExt(const SdfGridT &sdfGrid, const ExtOpT &op, const ExtValueT &background, typename SdfGridT::ValueType isoValue=0, int nIter=1, FastSweepingDomain mode=FastSweepingDomain::SWEEP_ALL, const typename SdfGridT::template ValueConverter< ExtValueT >::Type::ConstPtr extGrid=nullptr)
Computes the signed distance field and the extension of a field (scalar, vector, or int are supported...
Definition FastSweeping.h:1911
NearestNeighbors
Voxel topology of nearest neighbors.
Definition Morphology.h:60
@ NN_FACE
Definition Morphology.h:60
SdfGridT::template ValueConverter< ExtValueT >::Type::Ptr sdfToExt(const SdfGridT &sdfGrid, const ExtOpT &op, const ExtValueT &background, typename SdfGridT::ValueType isoValue=0, int nIter=1, FastSweepingDomain mode=FastSweepingDomain::SWEEP_ALL, const typename SdfGridT::template ValueConverter< ExtValueT >::Type::ConstPtr extGrid=nullptr)
Computes the extension of a field (scalar, vector, or int are supported), defined by the specified fu...
GridT::Ptr dilateSdf(const GridT &sdfGrid, int dilation, NearestNeighbors nn=NN_FACE, int nIter=1, FastSweepingDomain mode=FastSweepingDomain::SWEEP_ALL)
Dilates the narrow band of an existing signed distance field by a specified number of voxels (like ad...
Definition FastSweeping.h:1927
MeshToVoxelEdgeData::EdgeData Abs(const MeshToVoxelEdgeData::EdgeData &x)
Definition MeshToVolume.h:3924
const Coord FastSweeping< SdfGridT, ExtValueT >::mOffset[6]
Definition FastSweeping.h:714
GridOrTreeType::template ValueConverter< bool >::Type::Ptr sdfInteriorMask(const GridOrTreeType &volume, typename GridOrTreeType::ValueType isovalue=lsutilGridZero< GridOrTreeType >())
Threaded method to construct a boolean mask that represents interior regions in a signed distance fie...
Definition LevelSetUtil.h:2306
GridT::Ptr sdfToSdf(const GridT &sdfGrid, typename GridT::ValueType isoValue=0, int nIter=1)
Given an existing approximate SDF it solves the Eikonal equation for all its active voxels....
Definition FastSweeping.h:1852
FastSweepingDomain
Fast Sweeping update mode. This is useful to determine narrow-band extension or field extension in on...
Definition FastSweeping.h:67
@ SWEEP_GREATER_THAN_ISOVALUE
Definition FastSweeping.h:71
@ SWEEP_ALL
Update all voxels affected by the sweeping algorithm.
Definition FastSweeping.h:69
@ SWEEP_LESS_THAN_ISOVALUE
Definition FastSweeping.h:73
void changeAsymmetricLevelSetBackground(TreeOrLeafManagerT &tree, const typename TreeOrLeafManagerT::ValueType &outsideWidth, const typename TreeOrLeafManagerT::ValueType &insideWidth, bool threaded=true, size_t grainSize=32)
Replace the background values in all the nodes of a floating-point tree containing a possibly asymmet...
Definition ChangeBackground.h:218
void dilateActiveValues(TreeOrLeafManagerT &tree, const int iterations=1, const NearestNeighbors nn=NN_FACE, const TilePolicy mode=PRESERVE_TILES, const bool threaded=true)
Topologically dilate all active values (i.e. both voxels and tiles) in a tree using one of three near...
Definition Morphology.h:1057
std::pair< typename FogGridT::Ptr, typename FogGridT::template ValueConverter< ExtValueT >::Type::Ptr > fogToSdfAndExt(const FogGridT &fogGrid, const ExtOpT &op, const ExtValueT &background, typename FogGridT::ValueType isoValue, int nIter=1, FastSweepingDomain mode=FastSweepingDomain::SWEEP_ALL, const typename FogGridT::template ValueConverter< ExtValueT >::Type::ConstPtr extGrid=nullptr)
Computes the signed distance field and the extension of a field (scalar, vector, or int are supported...
Definition FastSweeping.h:1895
GridT::Ptr maskSdf(const GridT &sdfGrid, const Grid< MaskTreeT > &mask, bool ignoreActiveTiles=false, int nIter=1)
Fills mask by extending an existing signed distance field into the active values of this input ree of...
Definition FastSweeping.h:1940
FogGridT::template ValueConverter< ExtValueT >::Type::Ptr fogToExt(const FogGridT &fogGrid, const ExtOpT &op, const ExtValueT &background, typename FogGridT::ValueType isoValue, int nIter=1, FastSweepingDomain mode=FastSweepingDomain::SWEEP_ALL, const typename FogGridT::template ValueConverter< ExtValueT >::Type::ConstPtr extGrid=nullptr)
Computes the extension of a field (scalar, vector, or int are supported), defined by the specified fu...
Definition FastSweeping.h:1863
@ IGNORE_TILES
Definition Morphology.h:82
void pruneInactive(TreeT &tree, bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing with background tiles any nodes whose values are a...
Definition Prune.h:357
void changeLevelSetBackground(TreeOrLeafManagerT &tree, const typename TreeOrLeafManagerT::ValueType &halfWidth, bool threaded=true, size_t grainSize=32)
Replace the background value in all the nodes of a floating-point tree containing a symmetric narrow-...
Definition ChangeBackground.h:234
Definition PointDataGrid.h:48
ValueAccessorImpl< TreeType, IsSafe, MutexType, openvdb::make_index_sequence< CacheLevels > > ValueAccessor
Default alias for a ValueAccessor. This is simply a helper alias for the generic definition but takes...
Definition ValueAccessor.h:86
Index32 Index
Definition Types.h:34
@ GRID_LEVEL_SET
Definition Types.h:526
constexpr T zeroVal()
Return the value of type T that corresponds to zero.
Definition Math.h:71
math::Vec3< Real > Vec3R
Definition Types.h:53
GridType::Ptr createGrid()
Create a new grid of type GridType with background value zero.
Definition Grid.h:1795
Definition Exceptions.h:13
Definition Coord.h:590
#define OPENVDB_THROW(exception, message)
Definition Exceptions.h:74
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
Defines various finite difference stencils by means of the "curiously recurring template pattern" on ...
NodeManager produces linear arrays of all tree nodes allowing for efficient threading and bottom-up p...
Definition Types.h:507
Private class of FastSweeping to perform multi-threaded initialization.
Definition FastSweeping.h:1014
const SdfValueT mBackground
Definition FastSweeping.h:1116
typename tree::LeafManager< SdfTreeT >::LeafRange LeafRange
Definition FastSweeping.h:1015
DilateKernel & operator=(const DilateKernel &)=delete
SdfGridT::ConstPtr mSdfGridInput
Definition FastSweeping.h:1117
void run(int dilation, NearestNeighbors nn)
Definition FastSweeping.h:1025
DilateKernel(const DilateKernel &parent)=default
DilateKernel(FastSweeping &parent)
Definition FastSweeping.h:1016
FastSweeping * mParent
Definition FastSweeping.h:1115
Definition FastSweeping.h:1124
typename tree::LeafManager< SdfTreeT >::LeafRange LeafRange
Definition FastSweeping.h:1125
void operator()(const RootOrInternalNodeT &node) const
Definition FastSweeping.h:1213
void operator()(const LeafRange &r) const
Definition FastSweeping.h:1168
void run(SdfValueT isoValue)
Definition FastSweeping.h:1131
SdfGridT * mSdfGrid
Definition FastSweeping.h:1224
InitSdf & operator=(const InitSdf &)=delete
SdfValueT mAboveSign
Definition FastSweeping.h:1226
SdfValueT mIsoValue
Definition FastSweeping.h:1225
InitSdf(FastSweeping &parent)
Definition FastSweeping.h:1126
FastSweeping * mParent
Definition FastSweeping.h:1223
SdfValueT mMin
Definition FastSweeping.h:958
tree::LeafManager< const SdfTreeT > LeafMgr
Definition FastSweeping.h:923
MinMaxKernel(MinMaxKernel &other, tbb::split)
Definition FastSweeping.h:926
math::MinMax< SdfValueT > run(const SdfGridT &grid)
Definition FastSweeping.h:928
void operator()(const LeafRange &r)
Definition FastSweeping.h:935
void join(const MinMaxKernel &other)
Definition FastSweeping.h:950
typename LeafMgr::LeafRange LeafRange
Definition FastSweeping.h:924
bool mFltMinExists
Definition FastSweeping.h:959
SdfValueT mMax
Definition FastSweeping.h:958
bool mFltMaxExists
Definition FastSweeping.h:959
MinMaxKernel()
Definition FastSweeping.h:925
SdfValueT mMin
Definition FastSweeping.h:1006
void operator()(NodeT &node, size_t=1) const
Definition FastSweeping.h:982
PruneMinMaxFltKernel(SdfValueT min, SdfValueT max)
Definition FastSweeping.h:966
void operator()(typename SdfTreeT::LeafNodeType &leaf, size_t=1) const
Definition FastSweeping.h:995
SdfValueT mMax
Definition FastSweeping.h:1006
void operator()(typename SdfTreeT::RootNodeType &node, size_t=1) const
Definition FastSweeping.h:969
SdfValueT v
Definition FastSweeping.h:1598
Coord operator()(const Coord &p) const
Definition FastSweeping.h:1603
bool operator<(const NN &rhs) const
Definition FastSweeping.h:1604
NN(const SdfAccT &a, const Coord &p, int i)
Definition FastSweeping.h:1602
static Coord ijk(const Coord &p, int i)
Definition FastSweeping.h:1600
Private class of FastSweeping to perform concurrent fast sweeping in two directions.
Definition FastSweeping.h:1480
SweepingKernel & operator=(const SweepingKernel &)=delete
ExtT threeNghbr(const NN &d1, const NN &d2, const NN &d3, const SdfT &, const ExtT &v1, const ExtT &v2, const ExtT &v3) const
Definition FastSweeping.h:1618
ExtT twoNghbr(const NN &d1, const NN &d2, const SdfT &w, const ExtT &v1, const ExtT &v2) const
Definition FastSweeping.h:1614
void sweep()
Definition FastSweeping.h:1630
SweepingKernel(const SweepingKernel &)=delete
SweepingKernel(FastSweeping &parent)
Definition FastSweeping.h:1481
ExtT twoNghbr(const NN &d1, const NN &d2, const SdfT &, const ExtT &v1, const ExtT &v2) const
Definition FastSweeping.h:1610
ExtT threeNghbr(const NN &d1, const NN &d2, const NN &d3, const SdfT &w, const ExtT &v1, const ExtT &v2, const ExtT &v3) const
Definition FastSweeping.h:1626
void computeVoxelSlices(HashOp hash)
Main method that performs concurrent bi-directional sweeps.
Definition FastSweeping.h:1487
#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:286
#define OPENVDB_REAL_TREE_INSTANTIATE(Function)
Definition version.h.in:230