OpenVDB 13.1.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
824 if (mask.getGridClass() == GRID_LEVEL_SET) {
825 using T = typename MaskTreeT::template ValueConverter<bool>::Type;
826 typename Grid<T>::Ptr tmp = sdfInteriorMask(mask);//might have active tiles
827 tmp->tree().voxelizeActiveTiles();//multi-threaded
828 MaskKernel<T> kernel(*this);
829 kernel.run(tmp->tree());//multi-threaded
830 } else {
831 if (ignoreActiveTiles || !mask.tree().hasActiveTiles()) {
832 MaskKernel<MaskTreeT> kernel(*this);
833 kernel.run(mask.tree());//multi-threaded
834 } else {
835 using T = typename MaskTreeT::template ValueConverter<ValueMask>::Type;
836 T tmp(mask.tree(), false, TopologyCopy());//multi-threaded
837 tmp.voxelizeActiveTiles(true);//multi-threaded
838 MaskKernel<T> kernel(*this);
839 kernel.run(tmp);//multi-threaded
840 }
841 }
842 return this->isValid();
843}// FastSweeping::initMask
844
845template <typename SdfGridT, typename ExtValueT>
846void FastSweeping<SdfGridT, ExtValueT>::sweep(int nIter, bool finalize)
847{
848 if (!mSdfGrid) {
849 OPENVDB_THROW(RuntimeError, "FastSweeping::sweep called before initialization!");
850 }
851 if (mExtGrid && mSweepDirection != FastSweepingDomain::SWEEP_ALL && !mExtGridInput) {
852 OPENVDB_THROW(RuntimeError, "FastSweeping: Trying to extend a field in one direction needs"
853 " a non-null reference extension grid input.");
854 }
855 if (this->boundaryVoxelCount() == 0) {
856 OPENVDB_THROW(RuntimeError, "FastSweeping: No boundary voxels found!");
857 } else if (this->sweepingVoxelCount() == 0) {
858 OPENVDB_THROW(RuntimeError, "FastSweeping: No computing voxels found!");
859 }
860
861 // note: Sweeping kernel is non copy-constructible, so use a deque instead of a vector
862 std::deque<SweepingKernel> kernels;
863 for (int i = 0; i < 4; i++) kernels.emplace_back(*this);
864
865 { // compute voxel slices
866#ifdef BENCHMARK_FAST_SWEEPING
867 util::CpuTimer timer("Computing voxel slices");
868#endif
869
870 // Exploiting nested parallelism - all voxel slice data is precomputed
871 tbb::task_group tasks;
872 tasks.run([&] { kernels[0].computeVoxelSlices([](const Coord &a){ return a[0]+a[1]+a[2]; });/*+++ & ---*/ });
873 tasks.run([&] { kernels[1].computeVoxelSlices([](const Coord &a){ return a[0]+a[1]-a[2]; });/*++- & --+*/ });
874 tasks.run([&] { kernels[2].computeVoxelSlices([](const Coord &a){ return a[0]-a[1]+a[2]; });/*+-+ & -+-*/ });
875 tasks.run([&] { kernels[3].computeVoxelSlices([](const Coord &a){ return a[0]-a[1]-a[2]; });/*+-- & -++*/ });
876 tasks.wait();
877
878#ifdef BENCHMARK_FAST_SWEEPING
879 timer.stop();
880#endif
881 }
882
883 // perform nIter iterations of bi-directional sweeping in all directions
884 for (int i = 0; i < nIter; ++i) {
885 for (SweepingKernel& kernel : kernels) kernel.sweep();
886 }
887
888 if (finalize) {
889#ifdef BENCHMARK_FAST_SWEEPING
890 util::CpuTimer timer("Computing extrema values");
891#endif
892 MinMaxKernel kernel;
893 auto e = kernel.run(*mSdfGrid);//multi-threaded
894 //auto e = extrema(mGrid->beginValueOn());// 100x slower!!!!
895 if (kernel.mFltMinExists || kernel.mFltMaxExists) {
896 tree::NodeManager<SdfTreeT> nodeManager(mSdfGrid->tree());
897 PruneMinMaxFltKernel op(e.min(), e.max());
898 nodeManager.foreachTopDown(op, true /* = threaded*/, 1 /* = grainSize*/);
899 }
900#ifdef BENCHMARK_FAST_SWEEPING
901 std::cerr << "Min = " << e.min() << " Max = " << e.max() << std::endl;
902 timer.restart("Changing asymmetric background value");
903#endif
904 changeAsymmetricLevelSetBackground(mSdfGrid->tree(), e.max(), e.min());//multi-threaded
905
906#ifdef BENCHMARK_FAST_SWEEPING
907 timer.stop();
908#endif
909 }
910}// FastSweeping::sweep
911
912/// Private class of FastSweeping to quickly compute the extrema
913/// values of the active voxels in the leaf nodes. Several orders
914/// of magnitude faster than tools::extrema!
915/// Also determines whether there is float max or float min stored
916/// in a voxel.
917template <typename SdfGridT, typename ExtValueT>
918struct FastSweeping<SdfGridT, ExtValueT>::MinMaxKernel
919{
921 using LeafRange = typename LeafMgr::LeafRange;
922 MinMaxKernel() : mMin(std::numeric_limits<SdfValueT>::max()), mMax(-mMin), mFltMinExists(false), mFltMaxExists(true) {}
923 MinMaxKernel(MinMaxKernel& other, tbb::split) : mMin(other.mMin), mMax(other.mMax), mFltMinExists(other.mFltMinExists), mFltMaxExists(other.mFltMaxExists) {}
924
925 math::MinMax<SdfValueT> run(const SdfGridT &grid)
926 {
927 LeafMgr mgr(grid.tree());// super fast
928 tbb::parallel_reduce(mgr.leafRange(), *this);
930 }
931
932 void operator()(const LeafRange& r)
933 {
934 for (auto leafIter = r.begin(); leafIter; ++leafIter) {
935 for (auto voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) {
936 const SdfValueT v = *voxelIter;
937 const bool vEqFltMin = v == -std::numeric_limits<SdfValueT>::max();
938 const bool vEqFltMax = v == std::numeric_limits<SdfValueT>::max();
939 if (v < mMin && !vEqFltMin) mMin = v;
940 if (v > mMax && !vEqFltMax) mMax = v;
941 if (vEqFltMin) mFltMinExists = true;
942 if (vEqFltMax) mFltMaxExists = true;
943 }
944 }
945 }
946
947 void join(const MinMaxKernel& other)
948 {
949 if (other.mMin < mMin) mMin = other.mMin;
950 if (other.mMax > mMax) mMax = other.mMax;
953 }
954
955 SdfValueT mMin, mMax;
957};// FastSweeping::MinMaxKernel
958
959/// Private class of FastSweeping to prune sdf value that is equal to float max or
960/// float min.
961template <typename SdfGridT, typename ExtValueT>
962struct FastSweeping<SdfGridT, ExtValueT>::PruneMinMaxFltKernel {
963 PruneMinMaxFltKernel(SdfValueT min, SdfValueT max) : mMin(min), mMax(max) {}
964
965 // Root node
966 void operator()(typename SdfTreeT::RootNodeType& node, size_t = 1) const {
967 for (auto iter = node.beginValueAll(); iter; ++iter) {
968 if (*iter == -std::numeric_limits<SdfValueT>::max()) {
969 iter.setValue(mMin);
970 }
971 if (*iter == std::numeric_limits<SdfValueT>::max()) {
972 iter.setValue(mMax);
973 }
974 }
975 }
976
977 // Internal nodes
978 template<typename NodeT>
979 void operator()(NodeT& node, size_t = 1) const
980 {
981 for (auto iter = node.beginValueAll(); iter; ++iter) {
982 if (*iter == -std::numeric_limits<SdfValueT>::max()) {
983 iter.setValue(mMin);
984 }
985 if (*iter == std::numeric_limits<SdfValueT>::max()) {
986 iter.setValue(mMax);
987 }
988 }
989 }
990
991 // Leaf nodes
992 void operator()(typename SdfTreeT::LeafNodeType& leaf, size_t = 1) const
993 {
994 for (auto iter = leaf.beginValueOn(); iter; ++iter) {
995 if (*iter == -std::numeric_limits<SdfValueT>::max()) {
996 iter.setValue(mMin);
997 }
998 if (*iter == std::numeric_limits<SdfValueT>::max()) {
999 iter.setValue(mMax);
1000 }
1001 }
1002 }
1003 SdfValueT mMin, mMax;
1004};// FastSweeping::PruneMinMaxFltKernel
1005
1006////////////////////////////////////////////////////////////////////////////////
1007
1008/// Private class of FastSweeping to perform multi-threaded initialization
1009template <typename SdfGridT, typename ExtValueT>
1010struct FastSweeping<SdfGridT, ExtValueT>::DilateKernel
1011{
1014 : mParent(&parent),
1015 mBackground(parent.mSdfGrid->background())
1016 {
1017 mSdfGridInput = mParent->mSdfGrid->deepCopy();
1018 }
1019 DilateKernel(const DilateKernel &parent) = default;// for tbb::parallel_for
1021
1022 void run(int dilation, NearestNeighbors nn)
1023 {
1024#ifdef BENCHMARK_FAST_SWEEPING
1025 util::CpuTimer timer("Construct LeafManager");
1026#endif
1027 tree::LeafManager<SdfTreeT> mgr(mParent->mSdfGrid->tree());// super fast
1028
1029#ifdef BENCHMARK_FAST_SWEEPING
1030 timer.restart("Changing background value");
1031#endif
1032 static const SdfValueT Unknown = std::numeric_limits<SdfValueT>::max();
1033 changeLevelSetBackground(mgr, Unknown);//multi-threaded
1034
1035 #ifdef BENCHMARK_FAST_SWEEPING
1036 timer.restart("Dilating and updating mgr (parallel)");
1037 //timer.restart("Dilating and updating mgr (serial)");
1038#endif
1039
1040 const int delta = 5;
1041 for (int i=0, d = dilation/delta; i<d; ++i) dilateActiveValues(mgr, delta, nn, IGNORE_TILES);
1042 dilateActiveValues(mgr, dilation % delta, nn, IGNORE_TILES);
1043 //for (int i=0, n=5, d=dilation/n; i<d; ++i) dilateActiveValues(mgr, n, nn, IGNORE_TILES);
1044 //dilateVoxels(mgr, dilation, nn);
1045
1046#ifdef BENCHMARK_FAST_SWEEPING
1047 timer.restart("Initializing grid and sweep mask");
1048#endif
1049
1050 mParent->mSweepMask.clear();
1051 mParent->mSweepMask.topologyUnion(mParent->mSdfGrid->constTree());
1052
1054 using LeafT = typename SdfGridT::TreeType::LeafNodeType;
1055
1056 const FastSweepingDomain mode = mParent->mSweepDirection;
1057
1058 LeafManagerT leafManager(mParent->mSdfGrid->tree());
1059
1060 auto kernel = [&](LeafT& leaf, size_t /*leafIdx*/) {
1061 static const SdfValueT Unknown = std::numeric_limits<SdfValueT>::max();
1062 const SdfValueT background = mBackground;//local copy
1063 auto* maskLeaf = mParent->mSweepMask.probeLeaf(leaf.origin());
1064 SdfConstAccT sdfInputAcc(mSdfGridInput->tree());
1065 OPENVDB_ASSERT(maskLeaf);
1066 for (auto voxelIter = leaf.beginValueOn(); voxelIter; ++voxelIter) {
1067 const SdfValueT value = *voxelIter;
1068 SdfValueT inputValue;
1069 const Coord ijk = voxelIter.getCoord();
1070
1071 if (math::Abs(value) < background) {// disable boundary voxels from the mask tree
1072 maskLeaf->setValueOff(voxelIter.pos());
1073 } else {
1074 switch (mode) {
1076 voxelIter.setValue(value > 0 ? Unknown : -Unknown);
1077 break;
1079 if (value > 0) voxelIter.setValue(Unknown);
1080 else {
1081 maskLeaf->setValueOff(voxelIter.pos());
1082 bool isInputOn = sdfInputAcc.probeValue(ijk, inputValue);
1083 if ( !isInputOn ) voxelIter.setValueOff();
1084 else voxelIter.setValue(inputValue);
1085 }
1086 break;
1088 if (value < 0) voxelIter.setValue(-Unknown);
1089 else {
1090 maskLeaf->setValueOff(voxelIter.pos());
1091 bool isInputOn = sdfInputAcc.probeValue(ijk, inputValue);
1092 if ( !isInputOn ) voxelIter.setValueOff();
1093 else voxelIter.setValue(inputValue);
1094 }
1095 break;
1096 }
1097 }
1098 }
1099 };
1100
1101 leafManager.foreach( kernel );
1102
1103 // cache the leaf node origins for fast lookup in the sweeping kernels
1104 mParent->computeSweepMaskLeafOrigins();
1105
1106#ifdef BENCHMARK_FAST_SWEEPING
1107 timer.stop();
1108#endif
1109 }// FastSweeping::DilateKernel::run
1110
1111 // Private member data of DilateKernel
1113 const SdfValueT mBackground;
1114 typename SdfGridT::ConstPtr mSdfGridInput;
1115};// FastSweeping::DilateKernel
1116
1117////////////////////////////////////////////////////////////////////////////////
1118
1119template <typename SdfGridT, typename ExtValueT>
1120struct FastSweeping<SdfGridT, ExtValueT>::InitSdf
1121{
1123 InitSdf(FastSweeping &parent): mParent(&parent),
1124 mSdfGrid(parent.mSdfGrid.get()), mIsoValue(0), mAboveSign(0) {}
1125 InitSdf(const InitSdf&) = default;// for tbb::parallel_for
1126 InitSdf& operator=(const InitSdf&) = delete;
1127
1128 void run(SdfValueT isoValue)
1129 {
1130 mIsoValue = isoValue;
1131 mAboveSign = mParent->mIsInputSdf ? SdfValueT(1) : SdfValueT(-1);
1132 SdfTreeT &tree = mSdfGrid->tree();//sdf
1133 const bool hasActiveTiles = tree.hasActiveTiles();
1134
1135 if (mParent->mIsInputSdf && hasActiveTiles) {
1136 OPENVDB_THROW(RuntimeError, "FastSweeping: A SDF should not have active tiles!");
1137 }
1138
1139#ifdef BENCHMARK_FAST_SWEEPING
1140 util::CpuTimer timer("Initialize voxels");
1141#endif
1142 mParent->mSweepMask.clear();
1143 mParent->mSweepMask.topologyUnion(mParent->mSdfGrid->constTree());
1144
1145 {// Process all voxels
1146 tree::LeafManager<SdfTreeT> mgr(tree, 1);// we need one auxiliary buffer
1147 tbb::parallel_for(mgr.leafRange(32), *this);//multi-threaded
1148 mgr.swapLeafBuffer(1);//swap voxel values
1149 }
1150
1151#ifdef BENCHMARK_FAST_SWEEPING
1152 timer.restart("Initialize tiles - new");
1153#endif
1154 // Process all tiles
1155 tree::NodeManager<SdfTreeT, SdfTreeT::RootNodeType::LEVEL-1> mgr(tree);
1156 mgr.foreachBottomUp(*this);//multi-threaded
1157 tree.root().setBackground(std::numeric_limits<SdfValueT>::max(), false);
1158 if (hasActiveTiles) tree.voxelizeActiveTiles();//multi-threaded
1159
1160 // cache the leaf node origins for fast lookup in the sweeping kernels
1161
1162 mParent->computeSweepMaskLeafOrigins();
1163 }// FastSweeping::InitSdf::run
1164
1165 void operator()(const LeafRange& r) const
1166 {
1167 SweepMaskAccT sweepMaskAcc(mParent->mSweepMask);
1169 const SdfValueT isoValue = mIsoValue, above = mAboveSign*std::numeric_limits<SdfValueT>::max();//local copy
1170 const SdfValueT h = mAboveSign*static_cast<SdfValueT>(mSdfGrid->voxelSize()[0]);//Voxel size
1171 for (auto leafIter = r.begin(); leafIter; ++leafIter) {
1172 SdfValueT* sdf = leafIter.buffer(1).data();
1173 for (auto voxelIter = leafIter->beginValueAll(); voxelIter; ++voxelIter) {
1174 const SdfValueT value = *voxelIter;
1175 const bool isAbove = value > isoValue;
1176 if (!voxelIter.isValueOn()) {// inactive voxels
1177 sdf[voxelIter.pos()] = isAbove ? above : -above;
1178 } else {// active voxels
1179 const Coord ijk = voxelIter.getCoord();
1180 stencil.moveTo(ijk, value);
1181 const auto mask = stencil.intersectionMask( isoValue );
1182 if (mask.none()) {// most common case
1183 sdf[voxelIter.pos()] = isAbove ? above : -above;
1184 } else {// compute distance to iso-surface
1185 // disable boundary voxels from the mask tree
1186 sweepMaskAcc.setValueOff(ijk);
1187 const SdfValueT delta = value - isoValue;//offset relative to iso-value
1188 if (math::isApproxZero(delta)) {//voxel is on the iso-surface
1189 sdf[voxelIter.pos()] = 0;
1190 } else {//voxel is neighboring the iso-surface
1191 SdfValueT sum = 0;
1192 for (int i=0; i<6;) {
1193 SdfValueT d = std::numeric_limits<SdfValueT>::max(), d2;
1194 if (mask.test(i++)) d = math::Abs(delta/(value-stencil.getValue(i)));
1195 if (mask.test(i++)) {
1196 d2 = math::Abs(delta/(value-stencil.getValue(i)));
1197 if (d2 < d) d = d2;
1198 }
1199 if (d < std::numeric_limits<SdfValueT>::max()) sum += 1/(d*d);
1200 }
1201 sdf[voxelIter.pos()] = isAbove ? h / math::Sqrt(sum) : -h / math::Sqrt(sum);
1202 }// voxel is neighboring the iso-surface
1203 }// intersecting voxels
1204 }// active voxels
1205 }// loop over voxels
1206 }// loop over leaf nodes
1207 }// FastSweeping::InitSdf::operator(const LeafRange&)
1208
1209 template<typename RootOrInternalNodeT>
1210 void operator()(const RootOrInternalNodeT& node) const
1211 {
1212 const SdfValueT isoValue = mIsoValue, above = mAboveSign*std::numeric_limits<SdfValueT>::max();
1213 for (auto it = node.cbeginValueAll(); it; ++it) {
1214 SdfValueT& v = const_cast<SdfValueT&>(*it);
1215 v = v > isoValue ? above : -above;
1216 }//loop over all tiles
1217 }// FastSweeping::InitSdf::operator()(const RootOrInternalNodeT&)
1218
1219 // Public member data
1221 SdfGridT *mSdfGrid;//raw pointer, i.e. lock free
1222 SdfValueT mIsoValue;
1223 SdfValueT mAboveSign;//sign of distance values above the iso-value
1224};// FastSweeping::InitSdf
1225
1226
1227/// Private class of FastSweeping to perform multi-threaded initialization
1228template <typename SdfGridT, typename ExtValueT>
1229template <typename OpT>
1230struct FastSweeping<SdfGridT, ExtValueT>::InitExt
1231{
1232 using LeafRange = typename tree::LeafManager<SdfTreeT>::LeafRange;
1233 using OpPoolT = tbb::enumerable_thread_specific<OpT>;
1234 InitExt(FastSweeping &parent) : mParent(&parent),
1235 mOpPool(nullptr), mSdfGrid(parent.mSdfGrid.get()),
1236 mExtGrid(parent.mExtGrid.get()), mIsoValue(0), mAboveSign(0) {}
1237 InitExt(const InitExt&) = default;// for tbb::parallel_for
1238 InitExt& operator=(const InitExt&) = delete;
1239 void run(SdfValueT isoValue, const OpT &opPrototype)
1240 {
1241 static_assert(std::is_convertible<decltype(opPrototype(Vec3d(0))),ExtValueT>::value, "Invalid return type of functor");
1242 if (!mExtGrid) {
1243 OPENVDB_THROW(RuntimeError, "FastSweeping::InitExt expected an extension grid!");
1244 }
1245
1246 mAboveSign = mParent->mIsInputSdf ? SdfValueT(1) : SdfValueT(-1);
1247 mIsoValue = isoValue;
1248 auto &tree1 = mSdfGrid->tree();
1249 auto &tree2 = mExtGrid->tree();
1250 const bool hasActiveTiles = tree1.hasActiveTiles();//very fast
1251
1252 if (mParent->mIsInputSdf && hasActiveTiles) {
1253 OPENVDB_THROW(RuntimeError, "FastSweeping: A SDF should not have active tiles!");
1254 }
1255
1256#ifdef BENCHMARK_FAST_SWEEPING
1257 util::CpuTimer timer("Initialize voxels");
1258#endif
1259
1260 mParent->mSweepMask.clear();
1261 mParent->mSweepMask.topologyUnion(mParent->mSdfGrid->constTree());
1262
1263 {// Process all voxels
1264 // Define thread-local operators
1265 OpPoolT opPool(opPrototype);
1266 mOpPool = &opPool;
1267
1268 tree::LeafManager<SdfTreeT> mgr(tree1, 1);// we need one auxiliary buffer
1269 tbb::parallel_for(mgr.leafRange(32), *this);//multi-threaded
1270 mgr.swapLeafBuffer(1);//swap out auxiliary buffer
1271 }
1272
1273#ifdef BENCHMARK_FAST_SWEEPING
1274 timer.restart("Initialize tiles");
1275#endif
1276 {// Process all tiles
1277 tree::NodeManager<SdfTreeT, SdfTreeT::RootNodeType::LEVEL-1> mgr(tree1);
1278 mgr.foreachBottomUp(*this);//multi-threaded
1279 tree1.root().setBackground(std::numeric_limits<SdfValueT>::max(), false);
1280 if (hasActiveTiles) {
1281#ifdef BENCHMARK_FAST_SWEEPING
1282 timer.restart("Voxelizing active tiles");
1283#endif
1284 tree1.voxelizeActiveTiles();//multi-threaded
1285 tree2.voxelizeActiveTiles();//multi-threaded
1286 }
1287 }
1288
1289 // cache the leaf node origins for fast lookup in the sweeping kernels
1290
1291 mParent->computeSweepMaskLeafOrigins();
1292
1293#ifdef BENCHMARK_FAST_SWEEPING
1294 timer.stop();
1295#endif
1296 }// FastSweeping::InitExt::run
1297
1298 // int implements an update if minD needs to be updated
1299 template<typename ExtT = ExtValueT, typename SdfT = SdfValueT, typename std::enable_if<std::is_same<ExtT, int>::value, int>::type = 0>
1300 void sumHelper(ExtT& sum2, ExtT ext, bool update, const SdfT& /* d2 */) const { if (update) sum2 = ext; }// int implementation
1301
1302 // non-int implements a weighted sum
1303 template<typename ExtT = ExtValueT, typename SdfT = SdfValueT, typename std::enable_if<!std::is_same<ExtT, int>::value, int>::type = 0>
1304 void sumHelper(ExtT& sum2, ExtT ext, bool /* update */, const SdfT& d2) const { sum2 += static_cast<ExtValueT>(d2 * ext); }// non-int implementation
1305
1306 template<typename ExtT = ExtValueT, typename SdfT = SdfValueT, typename std::enable_if<std::is_same<ExtT, int>::value, int>::type = 0>
1307 ExtT extValHelper(ExtT extSum, const SdfT& /* sdfSum */) const { return extSum; }// 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 ExtT((SdfT(1) / sdfSum) * extSum); }// non-int implementation
1311
1312 void operator()(const LeafRange& r) const
1313 {
1314 ExtAccT acc(mExtGrid->tree());
1315 SweepMaskAccT sweepMaskAcc(mParent->mSweepMask);
1316 math::GradStencil<SdfGridT, false> stencil(*mSdfGrid);
1317 const math::Transform& xform = mExtGrid->transform();
1318 typename OpPoolT::reference op = mOpPool->local();
1319 const SdfValueT isoValue = mIsoValue, above = mAboveSign*std::numeric_limits<SdfValueT>::max();//local copy
1320 const SdfValueT h = mAboveSign*static_cast<SdfValueT>(mSdfGrid->voxelSize()[0]);//Voxel size
1321 for (auto leafIter = r.begin(); leafIter; ++leafIter) {
1322 SdfValueT *sdf = leafIter.buffer(1).data();
1323 ExtValueT *ext = acc.probeLeaf(leafIter->origin())->buffer().data();//should be safe!
1324 for (auto voxelIter = leafIter->beginValueAll(); voxelIter; ++voxelIter) {
1325 const SdfValueT value = *voxelIter;
1326 const bool isAbove = value > isoValue;
1327 if (!voxelIter.isValueOn()) {// inactive voxels
1328 sdf[voxelIter.pos()] = isAbove ? above : -above;
1329 } else {// active voxels
1330 const Coord ijk = voxelIter.getCoord();
1331 stencil.moveTo(ijk, value);
1332 const auto mask = stencil.intersectionMask( isoValue );
1333 if (mask.none()) {// no zero-crossing neighbors, most common case
1334 sdf[voxelIter.pos()] = isAbove ? above : -above;
1335 // the ext grid already has its active values set to the background value
1336 } else {// compute distance to iso-surface
1337 // disable boundary voxels from the mask tree
1338 sweepMaskAcc.setValueOff(ijk);
1339 const SdfValueT delta = value - isoValue;//offset relative to iso-value
1340 if (math::isApproxZero(delta)) {//voxel is on the iso-surface
1341 sdf[voxelIter.pos()] = 0;
1342 ext[voxelIter.pos()] = ExtValueT(op(xform.indexToWorld(ijk)));
1343 } else {//voxel is neighboring the iso-surface
1344 SdfValueT sum1 = 0;
1345 ExtValueT sum2 = zeroVal<ExtValueT>();
1346 // minD is used to update sum2 in the integer case,
1347 // where we choose the value of the extension grid corresponding to
1348 // the smallest value of d in the 6 direction neighboring the current
1349 // voxel.
1350 SdfValueT minD = std::numeric_limits<SdfValueT>::max();
1351 for (int n=0, i=0; i<6;) {
1352 SdfValueT d = std::numeric_limits<SdfValueT>::max(), d2;
1353 if (mask.test(i++)) {
1354 d = math::Abs(delta/(value-stencil.getValue(i)));
1355 n = i - 1;
1356 }
1357 if (mask.test(i++)) {
1358 d2 = math::Abs(delta/(value-stencil.getValue(i)));
1359 if (d2 < d) {
1360 d = d2;
1361 n = i - 1;
1362 }
1363 }
1364 if (d < std::numeric_limits<SdfValueT>::max()) {
1365 d2 = 1/(d*d);
1366 sum1 += d2;
1367 const Vec3R xyz(static_cast<SdfValueT>(ijk[0])+d*static_cast<SdfValueT>(FastSweeping::mOffset[n][0]),
1368 static_cast<SdfValueT>(ijk[1])+d*static_cast<SdfValueT>(FastSweeping::mOffset[n][1]),
1369 static_cast<SdfValueT>(ijk[2])+d*static_cast<SdfValueT>(FastSweeping::mOffset[n][2]));
1370 // If current d is less than minD, update minD
1371 sumHelper(sum2, ExtValueT(op(xform.indexToWorld(xyz))), d < minD, d2);
1372 if (d < minD) minD = d;
1373 }
1374 }//look over six cases
1375 ext[voxelIter.pos()] = extValHelper(sum2, sum1);
1376 sdf[voxelIter.pos()] = isAbove ? h / math::Sqrt(sum1) : -h / math::Sqrt(sum1);
1377 }// voxel is neighboring the iso-surface
1378 }// intersecting voxels
1379 }// active voxels
1380 }// loop over voxels
1381 }// loop over leaf nodes
1382 }// FastSweeping::InitExt::operator(const LeafRange& r)
1383
1384 template<typename RootOrInternalNodeT>
1385 void operator()(const RootOrInternalNodeT& node) const
1386 {
1387 const SdfValueT isoValue = mIsoValue, above = mAboveSign*std::numeric_limits<SdfValueT>::max();
1388 for (auto it = node.cbeginValueAll(); it; ++it) {
1389 SdfValueT& v = const_cast<SdfValueT&>(*it);
1390 v = v > isoValue ? above : -above;
1391 }//loop over all tiles
1392 }
1393 // Public member data
1394 FastSweeping *mParent;
1395 OpPoolT *mOpPool;
1396 SdfGridT *mSdfGrid;
1397 ExtGridT *mExtGrid;
1398 SdfValueT mIsoValue;
1399 SdfValueT mAboveSign;//sign of distance values above the iso-value
1400};// FastSweeping::InitExt
1401
1402/// Private class of FastSweeping to perform multi-threaded initialization
1403template <typename SdfGridT, typename ExtValueT>
1404template <typename MaskTreeT>
1405struct FastSweeping<SdfGridT, ExtValueT>::MaskKernel
1406{
1407 using LeafRange = typename tree::LeafManager<const MaskTreeT>::LeafRange;
1408 MaskKernel(FastSweeping &parent) : mParent(&parent),
1409 mSdfGrid(parent.mSdfGrid.get()) {}
1410 MaskKernel(const MaskKernel &parent) = default;// for tbb::parallel_for
1411 MaskKernel& operator=(const MaskKernel&) = delete;
1412
1413 void run(const MaskTreeT &mask)
1414 {
1415#ifdef BENCHMARK_FAST_SWEEPING
1416 util::CpuTimer timer;
1417#endif
1418 auto &lsTree = mSdfGrid->tree();
1419
1420 static const SdfValueT Unknown = std::numeric_limits<SdfValueT>::max();
1421
1422#ifdef BENCHMARK_FAST_SWEEPING
1423 timer.restart("Changing background value");
1424#endif
1425 changeLevelSetBackground(lsTree, Unknown);//multi-threaded
1426
1427#ifdef BENCHMARK_FAST_SWEEPING
1428 timer.restart("Union with mask");//multi-threaded
1429#endif
1430 lsTree.topologyUnion(mask);//multi-threaded
1431
1432 // ignore active tiles since the input grid is assumed to be a level set
1433 tree::LeafManager<const MaskTreeT> mgr(mask);// super fast
1434
1435#ifdef BENCHMARK_FAST_SWEEPING
1436 timer.restart("Initializing grid and sweep mask");
1437#endif
1438
1439 mParent->mSweepMask.clear();
1440 mParent->mSweepMask.topologyUnion(mParent->mSdfGrid->constTree());
1441
1442 using LeafManagerT = tree::LeafManager<SweepMaskTreeT>;
1443 using LeafT = typename SweepMaskTreeT::LeafNodeType;
1444 LeafManagerT leafManager(mParent->mSweepMask);
1445
1446 auto kernel = [&](LeafT& leaf, size_t /*leafIdx*/) {
1447 static const SdfValueT Unknown = std::numeric_limits<SdfValueT>::max();
1448 SdfAccT acc(mSdfGrid->tree());
1449 // The following hack is safe due to the topology union in
1450 // init and the fact that SdfValueT is known to be a floating point!
1451 SdfValueT *data = acc.probeLeaf(leaf.origin())->buffer().data();
1452 for (auto voxelIter = leaf.beginValueOn(); voxelIter; ++voxelIter) {// mask voxels
1453 if (math::Abs( data[voxelIter.pos()] ) < Unknown ) {
1454 // disable boundary voxels from the mask tree
1455 voxelIter.setValue(false);
1456 }
1457 }
1458 };
1459 leafManager.foreach( kernel );
1460
1461 // cache the leaf node origins for fast lookup in the sweeping kernels
1462 mParent->computeSweepMaskLeafOrigins();
1463
1464#ifdef BENCHMARK_FAST_SWEEPING
1465 timer.stop();
1466#endif
1467 }// FastSweeping::MaskKernel::run
1468
1469 // Private member data of MaskKernel
1470 FastSweeping *mParent;
1471 SdfGridT *mSdfGrid;//raw pointer, i.e. lock free
1472};// FastSweeping::MaskKernel
1473
1474/// @brief Private class of FastSweeping to perform concurrent fast sweeping in two directions
1475template <typename SdfGridT, typename ExtValueT>
1476struct FastSweeping<SdfGridT, ExtValueT>::SweepingKernel
1477{
1478 SweepingKernel(FastSweeping &parent) : mParent(&parent) {}
1481
1482 /// Main method that performs concurrent bi-directional sweeps
1483 template<typename HashOp>
1485 {
1486#ifdef BENCHMARK_FAST_SWEEPING
1487 util::CpuTimer timer;
1488#endif
1489
1490 // mask of the active voxels to be solved for, i.e. excluding boundary voxels
1491 const SweepMaskTreeT& maskTree = mParent->mSweepMask;
1492
1493 using LeafManagerT = typename tree::LeafManager<const SweepMaskTreeT>;
1494 using LeafT = typename SweepMaskTreeT::LeafNodeType;
1495 LeafManagerT leafManager(maskTree);
1496
1497 // compute the leaf node slices that have active voxels in them
1498 // the sliding window of the has keys is -14 to 21 (based on an 8x8x8 leaf node
1499 // and the extrema hash values i-j-k and i+j+k), but we use a larger mask window here to
1500 // easily accommodate any leaf dimension. The mask offset is used to be able to
1501 // store this in a fixed-size byte array
1502 constexpr int maskOffset = LeafT::DIM * 3;
1503 constexpr int maskRange = maskOffset * 2;
1504
1505 // mark each possible slice in each leaf node that has one or more active voxels in it
1506 std::vector<int8_t> leafSliceMasks(leafManager.leafCount()*maskRange);
1507 auto kernel1 = [&](const LeafT& leaf, size_t leafIdx) {
1508 const size_t leafOffset = leafIdx * maskRange;
1509 for (auto voxelIter = leaf.cbeginValueOn(); voxelIter; ++voxelIter) {
1510 const Coord ijk = LeafT::offsetToLocalCoord(voxelIter.pos());
1511 leafSliceMasks[leafOffset + hash(ijk) + maskOffset] = uint8_t(1);
1512 }
1513 };
1514 leafManager.foreach( kernel1 );
1515
1516 // compute the voxel slice map using a thread-local-storage hash map
1517 // the key of the hash map is the slice index of the voxel coord (ijk.x() + ijk.y() + ijk.z())
1518 // the values are an array of indices for every leaf that has active voxels with this slice index
1519 using ThreadLocalMap = std::unordered_map</*voxelSliceKey=*/int64_t, /*leafIdx=*/std::deque<size_t>>;
1520 tbb::enumerable_thread_specific<ThreadLocalMap> pool;
1521 auto kernel2 = [&](const LeafT& leaf, size_t leafIdx) {
1522 ThreadLocalMap& map = pool.local();
1523 const Coord& origin = leaf.origin();
1524 const int64_t leafKey = hash(origin);
1525 const size_t leafOffset = leafIdx * maskRange;
1526 for (int sliceIdx = 0; sliceIdx < maskRange; sliceIdx++) {
1527 if (leafSliceMasks[leafOffset + sliceIdx] == uint8_t(1)) {
1528 const int64_t voxelSliceKey = leafKey+sliceIdx-maskOffset;
1529 map[voxelSliceKey].emplace_back(leafIdx);
1530 }
1531 }
1532 };
1533 leafManager.foreach( kernel2 );
1534
1535 // combine into a single ordered map keyed by the voxel slice key
1536 // note that this is now stored in a map ordered by voxel slice key,
1537 // so sweep slices can be processed in order
1538 for (auto poolIt = pool.begin(); poolIt != pool.end(); ++poolIt) {
1539 const ThreadLocalMap& map = *poolIt;
1540 for (const auto& it : map) {
1541 for (const size_t leafIdx : it.second) {
1542 mVoxelSliceMap[it.first].emplace_back(leafIdx, NodeMaskPtrT());
1543 }
1544 }
1545 }
1546
1547 // extract the voxel slice keys for random access into the map
1548 mVoxelSliceKeys.reserve(mVoxelSliceMap.size());
1549 for (const auto& it : mVoxelSliceMap) {
1550 mVoxelSliceKeys.push_back(it.first);
1551 }
1552
1553 // allocate the node masks in parallel, as the map is populated in serial
1554 auto kernel3 = [&](tbb::blocked_range<size_t>& range) {
1555 for (size_t i = range.begin(); i < range.end(); i++) {
1556 const int64_t key = mVoxelSliceKeys[i];
1557 for (auto& it : mVoxelSliceMap[key]) {
1558 it.second = std::make_unique<NodeMaskT>();
1559 }
1560 }
1561 };
1562 tbb::parallel_for(tbb::blocked_range<size_t>(0, mVoxelSliceKeys.size()), kernel3);
1563
1564 // each voxel slice contains a leafIdx-nodeMask pair,
1565 // this routine populates these node masks to select only the active voxels
1566 // from the mask tree that have the same voxel slice key
1567 // TODO: a small optimization here would be to union this leaf node mask with
1568 // a pre-computed one for this particular slice pattern
1569 auto kernel4 = [&](tbb::blocked_range<size_t>& range) {
1570 for (size_t i = range.begin(); i < range.end(); i++) {
1571 const int64_t voxelSliceKey = mVoxelSliceKeys[i];
1572 LeafSliceArray& leafSliceArray = mVoxelSliceMap[voxelSliceKey];
1573 for (LeafSlice& leafSlice : leafSliceArray) {
1574 const size_t leafIdx = leafSlice.first;
1575 NodeMaskPtrT& nodeMask = leafSlice.second;
1576 const LeafT& leaf = leafManager.leaf(leafIdx);
1577 const Coord& origin = leaf.origin();
1578 const int64_t leafKey = hash(origin);
1579 for (auto voxelIter = leaf.cbeginValueOn(); voxelIter; ++voxelIter) {
1580 const Index voxelIdx = voxelIter.pos();
1581 const Coord ijk = LeafT::offsetToLocalCoord(voxelIdx);
1582 const int64_t key = leafKey + hash(ijk);
1583 if (key == voxelSliceKey) {
1584 nodeMask->setOn(voxelIdx);
1585 }
1586 }
1587 }
1588 }
1589 };
1590 tbb::parallel_for(tbb::blocked_range<size_t>(0, mVoxelSliceKeys.size()), kernel4);
1591 }// FastSweeping::SweepingKernel::computeVoxelSlices
1592
1593 // Private struct for nearest neighbor grid points (very memory light!)
1594 struct NN {
1595 SdfValueT v;
1596 int n;
1597 inline static Coord ijk(const Coord &p, int i) { return p + FastSweeping::mOffset[i]; }
1598 NN() : v(), n() {}
1599 NN(const SdfAccT &a, const Coord &p, int i) : v(math::Abs(a.getValue(ijk(p,i)))), n(i) {}
1600 inline Coord operator()(const Coord &p) const { return ijk(p, n); }
1601 inline bool operator<(const NN &rhs) const { return v < rhs.v; }
1602 inline operator bool() const { return v < SdfValueT(1000); }
1603 };// NN
1604
1605 /// @note Extending an integer field is based on the nearest-neighbor interpolation
1606 template<typename ExtT = ExtValueT, typename SdfT = SdfValueT, typename std::enable_if<std::is_same<ExtT, int>::value, int>::type = 0>
1607 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
1608
1609 /// @note Extending a non-integer field is based on a weighted interpolation
1610 template<typename ExtT = ExtValueT, typename SdfT = SdfValueT, typename std::enable_if<!std::is_same<ExtT, int>::value, int>::type = 0>
1611 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
1612
1613 /// @note Extending an integer field is based on the nearest-neighbor interpolation
1614 template<typename ExtT = ExtValueT, typename SdfT = SdfValueT, typename std::enable_if<std::is_same<ExtT, int>::value, int>::type = 0>
1615 ExtT threeNghbr(const NN& d1, const NN& d2, const NN& d3, const SdfT& /* w */, const ExtT& v1, const ExtT& v2, const ExtT& v3) const {
1616 math::Vec3<SdfT> d(d1.v, d2.v, d3.v);
1617 math::Vec3<ExtT> v(v1, v2, v3);
1618 return v[static_cast<int>(math::MinIndex(d))];
1619 }// int implementation
1620
1621 /// @note Extending a non-integer field is based on a weighted interpolation
1622 template<typename ExtT = ExtValueT, typename SdfT = SdfValueT, typename std::enable_if<!std::is_same<ExtT, int>::value, int>::type = 0>
1623 ExtT threeNghbr(const NN& d1, const NN& d2, const NN& d3, const SdfT& w, const ExtT& v1, const ExtT& v2, const ExtT& v3) const {
1624 return ExtT(w*(d1.v*v1 + d2.v*v2 + d3.v*v3));
1625 }// non-int implementation
1626
1627 void sweep()
1628 {
1629 typename ExtGridT::TreeType *tree2 = mParent->mExtGrid ? &mParent->mExtGrid->tree() : nullptr;
1630 typename ExtGridT::TreeType *tree3 = mParent->mExtGridInput ? &mParent->mExtGridInput->tree() : nullptr;
1631
1632 const SdfValueT h = static_cast<SdfValueT>(mParent->mSdfGrid->voxelSize()[0]);
1633 const SdfValueT sqrt2h = math::Sqrt(SdfValueT(2))*h;
1634 const FastSweepingDomain mode = mParent->mSweepDirection;
1635 const bool isInputSdf = mParent->mIsInputSdf;
1636
1637 // If we are using an extension in one direction, we need a reference grid
1638 // for the default value of the extension for the voxels that are not
1639 // intended to be updated by the sweeping algorithm.
1640 if (tree2 && mode != FastSweepingDomain::SWEEP_ALL) OPENVDB_ASSERT(tree3);
1641
1642 const std::vector<Coord>& leafNodeOrigins = mParent->mSweepMaskLeafOrigins;
1643
1644 int64_t voxelSliceIndex(0);
1645
1646 auto kernel = [&](const tbb::blocked_range<size_t>& range) {
1647 using LeafT = typename SdfGridT::TreeType::LeafNodeType;
1648
1649 SdfAccT acc1(mParent->mSdfGrid->tree());
1650 auto acc2 = std::unique_ptr<ExtAccT>(tree2 ? new ExtAccT(*tree2) : nullptr);
1651 auto acc3 = std::unique_ptr<ExtAccT>(tree3 ? new ExtAccT(*tree3) : nullptr);
1652 SdfValueT absV, sign, update, D;
1653 NN d1, d2, d3;//distance values and coordinates of closest neighbor points
1654
1655 const LeafSliceArray& leafSliceArray = mVoxelSliceMap[voxelSliceIndex];
1656
1657 // Solves Godunov's scheme: [x-d1]^2 + [x-d2]^2 + [x-d3]^2 = h^2
1658 // where [X] = (X>0?X:0) and ai=min(di+1,di-1)
1659 for (size_t i = range.begin(); i < range.end(); ++i) {
1660
1661 // iterate over all leafs in the slice and extract the leaf
1662 // and node mask for each slice pattern
1663
1664 const LeafSlice& leafSlice = leafSliceArray[i];
1665 const size_t leafIdx = leafSlice.first;
1666 const NodeMaskPtrT& nodeMask = leafSlice.second;
1667
1668 const Coord& origin = leafNodeOrigins[leafIdx];
1669
1670 Coord ijk;
1671 for (auto indexIter = nodeMask->beginOn(); indexIter; ++indexIter) {
1672
1673 // Get coordinate of center point of the FD stencil
1674 ijk = origin + LeafT::offsetToLocalCoord(indexIter.pos());
1675
1676 // Find the closes neighbors in the three axial directions
1677 d1 = std::min(NN(acc1, ijk, 0), NN(acc1, ijk, 1));
1678 d2 = std::min(NN(acc1, ijk, 2), NN(acc1, ijk, 3));
1679 d3 = std::min(NN(acc1, ijk, 4), NN(acc1, ijk, 5));
1680
1681 if (!(d1 || d2 || d3)) continue;//no valid neighbors
1682
1683 // Get the center point of the FD stencil (assumed to be an active voxel)
1684 // Note this const_cast is normally unsafe but by design we know the tree
1685 // to be static, of floating-point type and containing active voxels only!
1686 SdfValueT &value = const_cast<SdfValueT&>(acc1.getValue(ijk));
1687
1688 // Extract the sign
1689 sign = value >= SdfValueT(0) ? SdfValueT(1) : SdfValueT(-1);
1690
1691 // Absolute value
1692 absV = math::Abs(value);
1693
1694 // sort values so d1 <= d2 <= d3
1695 if (d2 < d1) std::swap(d1, d2);
1696 if (d3 < d2) std::swap(d2, d3);
1697 if (d2 < d1) std::swap(d1, d2);
1698
1699 // Test if there is a solution depending on ONE of the neighboring voxels
1700 // if d2 - d1 >= h => d2 >= d1 + h then:
1701 // (x-d1)^2=h^2 => x = d1 + h
1702 update = d1.v + h;
1703 if (update <= d2.v) {
1704 if (update < absV) {
1705 value = sign * update;
1706 if (acc2) {
1707 // There is an assert upstream to check if mExtGridInput exists if mode != SWEEP_ALL
1708 ExtValueT updateExt = acc2->getValue(d1(ijk));
1710 if (isInputSdf) updateExt = (value >= SdfValueT(0)) ? acc2->getValue(d1(ijk)) : acc3->getValue(ijk);
1711 else updateExt = (value <= SdfValueT(0)) ? acc2->getValue(d1(ijk)) : acc3->getValue(ijk);
1712 } // SWEEP_GREATER_THAN_ISOVALUE
1714 if (isInputSdf) updateExt = (value <= SdfValueT(0)) ? acc2->getValue(d1(ijk)) : acc3->getValue(ijk);
1715 else updateExt = (value >= SdfValueT(0)) ? acc2->getValue(d1(ijk)) : acc3->getValue(ijk);
1716 } // SWEEP_LESS_THAN_ISOVALUE
1717 acc2->setValue(ijk, updateExt);
1718 }//update ext?
1719 }//update sdf?
1720 continue;
1721 }// one neighbor case
1722
1723 // Test if there is a solution depending on TWO of the neighboring voxels
1724 // (x-d1)^2 + (x-d2)^2 = h^2
1725 //D = SdfValueT(2) * h * h - math::Pow2(d1.v - d2.v);// = 2h^2-(d1-d2)^2
1726 //if (D >= SdfValueT(0)) {// non-negative discriminant
1727 if (d2.v <= sqrt2h + d1.v) {
1728 D = SdfValueT(2) * h * h - math::Pow2(d1.v - d2.v);// = 2h^2-(d1-d2)^2
1729 update = SdfValueT(0.5) * (d1.v + d2.v + std::sqrt(D));
1730 if (update > d2.v && update <= d3.v) {
1731 if (update < absV) {
1732 value = sign * update;
1733 if (acc2) {
1734 d1.v -= update;
1735 d2.v -= update;
1736 // affine combination of two neighboring extension values
1737 const SdfValueT w = SdfValueT(1)/(d1.v+d2.v);
1738 const ExtValueT v1 = acc2->getValue(d1(ijk));
1739 const ExtValueT v2 = acc2->getValue(d2(ijk));
1740 const ExtValueT extVal = twoNghbr(d1, d2, w, v1, v2);
1741
1742 ExtValueT updateExt = extVal;
1744 if (isInputSdf) updateExt = (value >= SdfValueT(0)) ? extVal : acc3->getValue(ijk);
1745 else updateExt = (value <= SdfValueT(0)) ? extVal : acc3->getValue(ijk);
1746 } // SWEEP_GREATER_THAN_ISOVALUE
1748 if (isInputSdf) updateExt = (value <= SdfValueT(0)) ? extVal : acc3->getValue(ijk);
1749 else updateExt = (value >= SdfValueT(0)) ? extVal : acc3->getValue(ijk);
1750 } // SWEEP_LESS_THAN_ISOVALUE
1751 acc2->setValue(ijk, updateExt);
1752 }//update ext?
1753 }//update sdf?
1754 continue;
1755 }//test for two neighbor case
1756 }//test for non-negative determinant
1757
1758 // Test if there is a solution depending on THREE of the neighboring voxels
1759 // (x-d1)^2 + (x-d2)^2 + (x-d3)^2 = h^2
1760 // 3x^2 - 2(d1 + d2 + d3)x + d1^2 + d2^2 + d3^2 = h^2
1761 // ax^2 + bx + c=0, a=3, b=-2(d1+d2+d3), c=d1^2 + d2^2 + d3^2 - h^2
1762 const SdfValueT d123 = d1.v + d2.v + d3.v;
1763 D = d123*d123 - SdfValueT(3)*(d1.v*d1.v + d2.v*d2.v + d3.v*d3.v - h * h);
1764 if (D >= SdfValueT(0)) {// non-negative discriminant
1765 update = SdfValueT(1.0/3.0) * (d123 + std::sqrt(D));//always passes test
1766 //if (update > d3.v) {//disabled due to round-off errors
1767 if (update < absV) {
1768 value = sign * update;
1769 if (acc2) {
1770 d1.v -= update;
1771 d2.v -= update;
1772 d3.v -= update;
1773 // affine combination of three neighboring extension values
1774 const SdfValueT w = SdfValueT(1)/(d1.v+d2.v+d3.v);
1775 const ExtValueT v1 = acc2->getValue(d1(ijk));
1776 const ExtValueT v2 = acc2->getValue(d2(ijk));
1777 const ExtValueT v3 = acc2->getValue(d3(ijk));
1778 const ExtValueT extVal = threeNghbr(d1, d2, d3, w, v1, v2, v3);
1779
1780 ExtValueT updateExt = extVal;
1782 if (isInputSdf) updateExt = (value >= SdfValueT(0)) ? extVal : acc3->getValue(ijk);
1783 else updateExt = (value <= SdfValueT(0)) ? extVal : acc3->getValue(ijk);
1784 } // SWEEP_GREATER_THAN_ISOVALUE
1786 if (isInputSdf) updateExt = (value <= SdfValueT(0)) ? extVal : acc3->getValue(ijk);
1787 else updateExt = (value >= SdfValueT(0)) ? extVal : acc3->getValue(ijk);
1788 } // SWEEP_LESS_THAN_ISOVALUE
1789 acc2->setValue(ijk, updateExt);
1790 }//update ext?
1791 }//update sdf?
1792 }//test for non-negative determinant
1793 }//loop over coordinates
1794 }
1795 };
1796
1797#ifdef BENCHMARK_FAST_SWEEPING
1798 util::CpuTimer timer("Forward sweep");
1799#endif
1800
1801 for (size_t i = 0; i < mVoxelSliceKeys.size(); i++) {
1802 voxelSliceIndex = mVoxelSliceKeys[i];
1803 tbb::parallel_for(tbb::blocked_range<size_t>(0, mVoxelSliceMap[voxelSliceIndex].size()), kernel);
1804 }
1805
1806#ifdef BENCHMARK_FAST_SWEEPING
1807 timer.restart("Backward sweeps");
1808#endif
1809 for (size_t i = mVoxelSliceKeys.size(); i > 0; i--) {
1810 voxelSliceIndex = mVoxelSliceKeys[i-1];
1811 tbb::parallel_for(tbb::blocked_range<size_t>(0, mVoxelSliceMap[voxelSliceIndex].size()), kernel);
1812 }
1813
1814#ifdef BENCHMARK_FAST_SWEEPING
1815 timer.stop();
1816#endif
1817 }// FastSweeping::SweepingKernel::sweep
1818
1819private:
1820 using NodeMaskT = typename SweepMaskTreeT::LeafNodeType::NodeMaskType;
1821 using NodeMaskPtrT = std::unique_ptr<NodeMaskT>;
1822 // using a unique ptr for the NodeMask allows for parallel allocation,
1823 // but makes this class not copy-constructible
1824 using LeafSlice = std::pair</*leafIdx=*/size_t, /*leafMask=*/NodeMaskPtrT>;
1825 using LeafSliceArray = std::deque<LeafSlice>;
1826 using VoxelSliceMap = std::map</*voxelSliceKey=*/int64_t, LeafSliceArray>;
1827
1828 // Private member data of SweepingKernel
1829 FastSweeping *mParent;
1830 VoxelSliceMap mVoxelSliceMap;
1831 std::vector<int64_t> mVoxelSliceKeys;
1832};// FastSweeping::SweepingKernel
1833
1834////////////////////////////////////////////////////////////////////////////////
1835
1836template<typename GridT>
1837typename GridT::Ptr
1838fogToSdf(const GridT &fogGrid,
1839 typename GridT::ValueType isoValue,
1840 int nIter)
1841{
1843 if (fs.initSdf(fogGrid, isoValue, /*isInputSdf*/false)) fs.sweep(nIter);
1844 return fs.sdfGrid();
1845}
1846
1847template<typename GridT>
1848typename GridT::Ptr
1849sdfToSdf(const GridT &sdfGrid,
1850 typename GridT::ValueType isoValue,
1851 int nIter)
1852{
1854 if (fs.initSdf(sdfGrid, isoValue, /*isInputSdf*/true)) fs.sweep(nIter);
1855 return fs.sdfGrid();
1856}
1857
1858template<typename FogGridT, typename ExtOpT, typename ExtValueT>
1859typename FogGridT::template ValueConverter<ExtValueT>::Type::Ptr
1860fogToExt(const FogGridT &fogGrid,
1861 const ExtOpT &op,
1862 const ExtValueT& background,
1863 typename FogGridT::ValueType isoValue,
1864 int nIter,
1865 FastSweepingDomain mode,
1866 const typename FogGridT::template ValueConverter<ExtValueT>::Type::ConstPtr extGrid)
1867{
1869 if (fs.initExt(fogGrid, op, background, isoValue, /*isInputSdf*/false, mode, extGrid))
1870 fs.sweep(nIter, /*finalize*/true);
1871 return fs.extGrid();
1872}
1873
1874template<typename SdfGridT, typename OpT, typename ExtValueT>
1875typename SdfGridT::template ValueConverter<ExtValueT>::Type::Ptr
1876sdfToExt(const SdfGridT &sdfGrid,
1877 const OpT &op,
1878 const ExtValueT &background,
1879 typename SdfGridT::ValueType isoValue,
1880 int nIter,
1881 FastSweepingDomain mode,
1882 const typename SdfGridT::template ValueConverter<ExtValueT>::Type::ConstPtr extGrid)
1883{
1885 if (fs.initExt(sdfGrid, op, background, isoValue, /*isInputSdf*/true, mode, extGrid))
1886 fs.sweep(nIter, /*finalize*/true);
1887 return fs.extGrid();
1888}
1889
1890template<typename FogGridT, typename ExtOpT, typename ExtValueT>
1891std::pair<typename FogGridT::Ptr, typename FogGridT::template ValueConverter<ExtValueT>::Type::Ptr>
1892fogToSdfAndExt(const FogGridT &fogGrid,
1893 const ExtOpT &op,
1894 const ExtValueT &background,
1895 typename FogGridT::ValueType isoValue,
1896 int nIter,
1897 FastSweepingDomain mode,
1898 const typename FogGridT::template ValueConverter<ExtValueT>::Type::ConstPtr extGrid)
1899{
1901 if (fs.initExt(fogGrid, op, background, isoValue, /*isInputSdf*/false, mode, extGrid))
1902 fs.sweep(nIter, /*finalize*/true);
1903 return std::make_pair(fs.sdfGrid(), fs.extGrid());
1904}
1905
1906template<typename SdfGridT, typename ExtOpT, typename ExtValueT>
1907std::pair<typename SdfGridT::Ptr, typename SdfGridT::template ValueConverter<ExtValueT>::Type::Ptr>
1908sdfToSdfAndExt(const SdfGridT &sdfGrid,
1909 const ExtOpT &op,
1910 const ExtValueT &background,
1911 typename SdfGridT::ValueType isoValue,
1912 int nIter,
1913 FastSweepingDomain mode,
1914 const typename SdfGridT::template ValueConverter<ExtValueT>::Type::ConstPtr extGrid)
1915{
1917 if (fs.initExt(sdfGrid, op, background, isoValue, /*isInputSdf*/true, mode, extGrid))
1918 fs.sweep(nIter, /*finalize*/true);
1919 return std::make_pair(fs.sdfGrid(), fs.extGrid());
1920}
1921
1922template<typename GridT>
1923typename GridT::Ptr
1924dilateSdf(const GridT &sdfGrid,
1925 int dilation,
1927 int nIter,
1928 FastSweepingDomain mode)
1929{
1931 if (fs.initDilate(sdfGrid, dilation, nn, /*sweep direction*/ mode)) fs.sweep(nIter);
1932 return fs.sdfGrid();
1933}
1934
1935template<typename GridT, typename MaskTreeT>
1936typename GridT::Ptr
1937maskSdf(const GridT &sdfGrid,
1938 const Grid<MaskTreeT> &mask,
1939 bool ignoreActiveTiles,
1940 int nIter)
1941{
1943 if (fs.initMask(sdfGrid, mask, ignoreActiveTiles)) fs.sweep(nIter);
1944 return fs.sdfGrid();
1945}
1946
1947
1948////////////////////////////////////////
1949
1950
1951// Explicit Template Instantiation
1952
1953#ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
1954
1955#ifdef OPENVDB_INSTANTIATE_FASTSWEEPING
1957#endif
1958
1959#define _FUNCTION(TreeT) \
1960 Grid<TreeT>::Ptr fogToSdf(const Grid<TreeT>&, TreeT::ValueType, int)
1962#undef _FUNCTION
1963
1964#define _FUNCTION(TreeT) \
1965 Grid<TreeT>::Ptr sdfToSdf(const Grid<TreeT>&, TreeT::ValueType, int)
1967#undef _FUNCTION
1968
1969#define _FUNCTION(TreeT) \
1970 Grid<TreeT>::Ptr dilateSdf(const Grid<TreeT>&, int, NearestNeighbors, int, FastSweepingDomain)
1972#undef _FUNCTION
1973
1974#endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
1975
1976
1977} // namespace tools
1978} // namespace OPENVDB_VERSION_NAME
1979} // namespace openvdb
1980
1981#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:411
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:846
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:219
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:1838
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:1908
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:1924
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:1849
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:1892
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:1937
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:1860
@ 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:170
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:1757
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...
Private class of FastSweeping to perform multi-threaded initialization.
Definition FastSweeping.h:1011
const SdfValueT mBackground
Definition FastSweeping.h:1113
typename tree::LeafManager< SdfTreeT >::LeafRange LeafRange
Definition FastSweeping.h:1012
DilateKernel & operator=(const DilateKernel &)=delete
SdfGridT::ConstPtr mSdfGridInput
Definition FastSweeping.h:1114
void run(int dilation, NearestNeighbors nn)
Definition FastSweeping.h:1022
DilateKernel(const DilateKernel &parent)=default
DilateKernel(FastSweeping &parent)
Definition FastSweeping.h:1013
FastSweeping * mParent
Definition FastSweeping.h:1112
Definition FastSweeping.h:1121
typename tree::LeafManager< SdfTreeT >::LeafRange LeafRange
Definition FastSweeping.h:1122
void operator()(const RootOrInternalNodeT &node) const
Definition FastSweeping.h:1210
void operator()(const LeafRange &r) const
Definition FastSweeping.h:1165
void run(SdfValueT isoValue)
Definition FastSweeping.h:1128
SdfGridT * mSdfGrid
Definition FastSweeping.h:1221
InitSdf & operator=(const InitSdf &)=delete
SdfValueT mAboveSign
Definition FastSweeping.h:1223
SdfValueT mIsoValue
Definition FastSweeping.h:1222
InitSdf(FastSweeping &parent)
Definition FastSweeping.h:1123
FastSweeping * mParent
Definition FastSweeping.h:1220
SdfValueT mMin
Definition FastSweeping.h:955
tree::LeafManager< const SdfTreeT > LeafMgr
Definition FastSweeping.h:920
MinMaxKernel(MinMaxKernel &other, tbb::split)
Definition FastSweeping.h:923
math::MinMax< SdfValueT > run(const SdfGridT &grid)
Definition FastSweeping.h:925
void operator()(const LeafRange &r)
Definition FastSweeping.h:932
void join(const MinMaxKernel &other)
Definition FastSweeping.h:947
typename LeafMgr::LeafRange LeafRange
Definition FastSweeping.h:921
bool mFltMinExists
Definition FastSweeping.h:956
SdfValueT mMax
Definition FastSweeping.h:955
bool mFltMaxExists
Definition FastSweeping.h:956
MinMaxKernel()
Definition FastSweeping.h:922
SdfValueT mMin
Definition FastSweeping.h:1003
void operator()(NodeT &node, size_t=1) const
Definition FastSweeping.h:979
PruneMinMaxFltKernel(SdfValueT min, SdfValueT max)
Definition FastSweeping.h:963
void operator()(typename SdfTreeT::LeafNodeType &leaf, size_t=1) const
Definition FastSweeping.h:992
SdfValueT mMax
Definition FastSweeping.h:1003
void operator()(typename SdfTreeT::RootNodeType &node, size_t=1) const
Definition FastSweeping.h:966
SdfValueT v
Definition FastSweeping.h:1595
Coord operator()(const Coord &p) const
Definition FastSweeping.h:1600
bool operator<(const NN &rhs) const
Definition FastSweeping.h:1601
NN(const SdfAccT &a, const Coord &p, int i)
Definition FastSweeping.h:1599
static Coord ijk(const Coord &p, int i)
Definition FastSweeping.h:1597
Private class of FastSweeping to perform concurrent fast sweeping in two directions.
Definition FastSweeping.h:1477
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:1615
ExtT twoNghbr(const NN &d1, const NN &d2, const SdfT &w, const ExtT &v1, const ExtT &v2) const
Definition FastSweeping.h:1611
void sweep()
Definition FastSweeping.h:1627
SweepingKernel(const SweepingKernel &)=delete
SweepingKernel(FastSweeping &parent)
Definition FastSweeping.h:1478
ExtT twoNghbr(const NN &d1, const NN &d2, const SdfT &, const ExtT &v1, const ExtT &v2) const
Definition FastSweeping.h:1607
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:1623
void computeVoxelSlices(HashOp hash)
Main method that performs concurrent bi-directional sweeps.
Definition FastSweeping.h:1484
#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:291
#define OPENVDB_REAL_TREE_INSTANTIATE(Function)
Definition version.h.in:235