OpenVDB  12.0.0
ParticlesToLevelSet.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: Apache-2.0
3 
4 /// @author Ken Museth
5 ///
6 /// @file tools/ParticlesToLevelSet.h
7 ///
8 /// @brief Rasterize particles with position, radius and velocity
9 /// into either a boolean mask grid or a narrow-band level set grid.
10 ///
11 /// @details Optionally, arbitrary attributes on the particles can be transferred,
12 /// resulting in additional output grids with the same topology as the main grid.
13 ///
14 /// @note Particle to level set conversion is intended to be combined with
15 /// some kind of surface postprocessing, using
16 /// @vdblink::tools::LevelSetFilter LevelSetFilter@endlink, for example.
17 /// Without such postprocessing the generated surface is typically too noisy and blobby.
18 /// However, it serves as a great and fast starting point for subsequent
19 /// level set surface processing and convolution.
20 ///
21 /// @details For particle access, any class with the following interface may be used
22 /// (see the unit test or the From Particles Houdini SOP for practical examples):
23 /// @code
24 /// struct ParticleList
25 /// {
26 /// // Return the total number of particles in the list.
27 /// // Always required!
28 /// size_t size() const;
29 ///
30 /// // Get the world-space position of the nth particle.
31 /// // Required by rasterizeSpheres().
32 /// void getPos(size_t n, Vec3R& xyz) const;
33 ///
34 /// // Get the world-space position and radius of the nth particle.
35 /// // Required by rasterizeSpheres().
36 /// void getPosRad(size_t n, Vec3R& xyz, Real& radius) const;
37 ///
38 /// // Get the world-space position, radius and velocity of the nth particle.
39 /// // Required by rasterizeTrails().
40 /// void getPosRadVel(size_t n, Vec3R& xyz, Real& radius, Vec3R& velocity) const;
41 ///
42 /// // Get the value of the nth particle's user-defined attribute (of type @c AttributeType).
43 /// // Required only if attribute transfer is enabled in ParticlesToLevelSet.
44 /// void getAtt(size_t n, AttributeType& att) const;
45 /// };
46 /// @endcode
47 ///
48 /// Some functions accept an interrupter argument. This refers to any class
49 /// with the following interface:
50 /// @code
51 /// struct Interrupter
52 /// {
53 /// void start(const char* name = nullptr) // called when computations begin
54 /// void end() // called when computations end
55 /// bool wasInterrupted(int percent=-1) // return true to abort computation
56 /// };
57 /// @endcode
58 ///
59 /// The default interrupter is @vdblink::util::NullInterrupter NullInterrupter@endlink,
60 /// for which all calls are no-ops that incur no computational overhead.
61 
62 #ifndef OPENVDB_TOOLS_PARTICLES_TO_LEVELSET_HAS_BEEN_INCLUDED
63 #define OPENVDB_TOOLS_PARTICLES_TO_LEVELSET_HAS_BEEN_INCLUDED
64 
65 #include <openvdb/Types.h>
66 #include <openvdb/Grid.h>
67 #include <openvdb/math/Math.h>
68 #include <openvdb/math/Transform.h>
70 #include <openvdb/util/logging.h>
72 #include <openvdb/util/Assert.h>
73 #include <openvdb/thread/Threading.h>
74 
75 #include "Composite.h" // for csgUnion()
76 #include "PointPartitioner.h"
77 #include "Prune.h"
78 #include "SignedFloodFill.h"
79 
80 #include <tbb/parallel_reduce.h>
81 #include <tbb/blocked_range.h>
82 
83 #include <functional>
84 #include <iostream>
85 #include <type_traits>
86 #include <vector>
87 
88 
89 namespace openvdb {
91 namespace OPENVDB_VERSION_NAME {
92 namespace tools {
93 
94 /// @brief Populate a scalar, floating-point grid with CSG-unioned level set spheres
95 /// described by the given particle positions and radii.
96 /// @details For more control over the output, including attribute transfer,
97 /// use the ParticlesToLevelSet class directly.
98 template<typename GridT, typename ParticleListT, typename InterrupterT = util::NullInterrupter>
99 inline void particlesToSdf(const ParticleListT&, GridT&, InterrupterT* = nullptr);
100 
101 /// @brief Populate a scalar, floating-point grid with fixed-size, CSG-unioned
102 /// level set spheres described by the given particle positions and the specified radius.
103 /// @details For more control over the output, including attribute transfer,
104 /// use the ParticlesToLevelSet class directly.
105 template<typename GridT, typename ParticleListT, typename InterrupterT = util::NullInterrupter>
106 inline void particlesToSdf(const ParticleListT&, GridT&, Real radius, InterrupterT* = nullptr);
107 
108 /// @brief Populate a scalar, floating-point grid with CSG-unioned trails
109 /// of level set spheres with decreasing radius, where the starting position and radius
110 /// and the direction of each trail is given by particle attributes.
111 /// @details For more control over the output, including attribute transfer,
112 /// use the ParticlesToLevelSet class directly.
113 /// @note The @a delta parameter controls the distance between spheres in a trail.
114 /// Be careful not to use too small a value.
115 template<typename GridT, typename ParticleListT, typename InterrupterT = util::NullInterrupter>
116 inline void particleTrailsToSdf(const ParticleListT&, GridT&, Real delta=1, InterrupterT* =nullptr);
117 
118 /// @brief Activate a boolean grid wherever it intersects the spheres
119 /// described by the given particle positions and radii.
120 /// @details For more control over the output, including attribute transfer,
121 /// use the ParticlesToLevelSet class directly.
122 template<typename GridT, typename ParticleListT, typename InterrupterT = util::NullInterrupter>
123 inline void particlesToMask(const ParticleListT&, GridT&, InterrupterT* = nullptr);
124 
125 /// @brief Activate a boolean grid wherever it intersects the fixed-size spheres
126 /// described by the given particle positions and the specified radius.
127 /// @details For more control over the output, including attribute transfer,
128 /// use the ParticlesToLevelSet class directly.
129 template<typename GridT, typename ParticleListT, typename InterrupterT = util::NullInterrupter>
130 inline void particlesToMask(const ParticleListT&, GridT&, Real radius, InterrupterT* = nullptr);
131 
132 /// @brief Activate a boolean grid wherever it intersects trails of spheres
133 /// with decreasing radius, where the starting position and radius and the direction
134 /// of each trail is given by particle attributes.
135 /// @details For more control over the output, including attribute transfer,
136 /// use the ParticlesToLevelSet class directly.
137 /// @note The @a delta parameter controls the distance between spheres in a trail.
138 /// Be careful not to use too small a value.
139 template<typename GridT, typename ParticleListT, typename InterrupterT = util::NullInterrupter>
140 inline void particleTrailsToMask(const ParticleListT&, GridT&,Real delta=1,InterrupterT* =nullptr);
141 
142 
143 ////////////////////////////////////////
144 
145 /// @cond OPENVDB_DOCS_INTERNAL
146 
147 namespace p2ls_internal {
148 // This is a simple type that combines a distance value and a particle
149 // attribute. It's required for attribute transfer which is performed
150 // in the ParticlesToLevelSet::Raster member class defined below.
151 /// @private
152 template<typename VisibleT, typename BlindT> class BlindData;
153 }
154 
155 /// @endcond
156 
157 template<typename SdfGridT,
158  typename AttributeT = void,
159  typename InterrupterT = util::NullInterrupter>
161 {
162 public:
163  using DisableT = typename std::is_void<AttributeT>::type;
164  using InterrupterType = InterrupterT;
165 
166  using SdfGridType = SdfGridT;
167  using SdfType = typename SdfGridT::ValueType;
168 
169  using AttType = typename std::conditional<DisableT::value, size_t, AttributeT>::type;
170  using AttGridType = typename SdfGridT::template ValueConverter<AttType>::Type;
171 
172  static const bool OutputIsMask = std::is_same<SdfType, bool>::value;
173 
174  /// @brief Constructor using an existing boolean or narrow-band level set grid
175  ///
176  /// @param grid grid into which particles are rasterized
177  /// @param interrupt callback to interrupt a long-running process
178  ///
179  /// @details If the input grid is already populated with signed distances,
180  /// particles are unioned onto the existing level set surface.
181  ///
182  /// @details The width in voxel units of the generated narrow band level set
183  /// is given by 2&times;<I>background</I>/<I>dx</I>, where @a background
184  /// is the background value stored in the grid and @a dx is the voxel size
185  /// derived from the transform associated with the grid.
186  /// Also note that &minus;<I>background</I> corresponds to the constant value
187  /// inside the generated narrow-band level set.
188  ///
189  /// @note If attribute transfer is enabled, i.e., if @c AttributeT is not @c void,
190  /// attributes are generated only for voxels that overlap with particles,
191  /// not for any other preexisting voxels (for which no attributes exist!).
192  explicit ParticlesToLevelSet(SdfGridT& grid, InterrupterT* interrupt = nullptr);
193 
194  ~ParticlesToLevelSet() { delete mBlindGrid; }
195 
196  /// @brief This method syncs up the level set and attribute grids
197  /// and therefore needs to be called before any of those grids are
198  /// used and after the last call to any of the rasterizer methods.
199  /// @details It has no effect or overhead if attribute transfer is disabled
200  /// (i.e., if @c AttributeT is @c void) and @a prune is @c false.
201  ///
202  /// @note Avoid calling this method more than once, and call it only after
203  /// all the particles have been rasterized.
204  void finalize(bool prune = false);
205 
206  /// @brief Return a pointer to the grid containing the optional user-defined attribute.
207  /// @warning If attribute transfer is disabled (i.e., if @c AttributeT is @c void)
208  /// or if @link finalize() finalize@endlink is not called, the pointer will be null.
209  typename AttGridType::Ptr attributeGrid() { return mAttGrid; }
210 
211  /// @brief Return the size of a voxel in world units.
212  Real getVoxelSize() const { return mDx; }
213 
214  /// @brief Return the half-width of the narrow band in voxel units.
215  Real getHalfWidth() const { return mHalfWidth; }
216 
217  /// @brief Return the smallest radius allowed in voxel units.
218  Real getRmin() const { return mRmin; }
219  /// @brief Set the smallest radius allowed in voxel units.
220  void setRmin(Real Rmin) { mRmin = math::Max(Real(0),Rmin); }
221 
222  /// @brief Return the largest radius allowed in voxel units.
223  Real getRmax() const { return mRmax; }
224  /// @brief Set the largest radius allowed in voxel units.
225  void setRmax(Real Rmax) { mRmax = math::Max(mRmin,Rmax); }
226 
227  /// @brief Return @c true if any particles were ignored due to their size.
228  bool ignoredParticles() const { return mMinCount>0 || mMaxCount>0; }
229  /// @brief Return the number of particles that were ignored because they were
230  /// smaller than the minimum radius.
231  size_t getMinCount() const { return mMinCount; }
232  /// @brief Return the number of particles that were ignored because they were
233  /// larger than the maximum radius.
234  size_t getMaxCount() const { return mMaxCount; }
235 
236  /// @brief Return the grain size used for threading
237  int getGrainSize() const { return mGrainSize; }
238  /// @brief Set the grain size used for threading.
239  /// @note A grain size of zero or less disables threading.
240  void setGrainSize(int grainSize) { mGrainSize = grainSize; }
241 
242  /// @brief Rasterize each particle as a sphere with the particle's position and radius.
243  /// @details For level set output, all spheres are CSG-unioned.
244  template<typename ParticleListT>
245  void rasterizeSpheres(const ParticleListT& pa);
246 
247  /// @brief Rasterize each particle as a sphere with the particle's position
248  /// and a fixed radius.
249  /// @details For level set output, all spheres are CSG-unioned.
250  ///
251  /// @param pa particles with positions
252  /// @param radius fixed sphere radius in world units.
253  template<typename ParticleListT>
254  void rasterizeSpheres(const ParticleListT& pa, Real radius);
255 
256  /// @brief Rasterize each particle as a trail comprising the CSG union
257  /// of spheres of decreasing radius.
258  ///
259  /// @param pa particles with position, radius and velocity.
260  /// @param delta controls the distance between sphere instances
261  ///
262  /// @warning Be careful not to use too small values for @a delta,
263  /// since this can lead to excessive computation per trail (which the
264  /// interrupter can't stop).
265  ///
266  /// @note The direction of a trail is opposite to that of the velocity vector,
267  /// and its length is given by the magnitude of the velocity.
268  /// The radius at the head of the trail is given by the radius of the particle,
269  /// and the radius at the tail is @a Rmin voxel units, which has
270  /// a default value of 1.5 corresponding to the Nyquist frequency!
271  template<typename ParticleListT>
272  void rasterizeTrails(const ParticleListT& pa, Real delta=1.0);
273 
274 private:
275  using BlindType = p2ls_internal::BlindData<SdfType, AttType>;
276  using BlindGridType = typename SdfGridT::template ValueConverter<BlindType>::Type;
277 
278  /// Class with multi-threaded implementation of particle rasterization
279  template<typename ParticleListT, typename GridT> struct Raster;
280 
281  SdfGridType* mSdfGrid;
282  typename AttGridType::Ptr mAttGrid;
283  BlindGridType* mBlindGrid;
284  InterrupterT* mInterrupter;
285  Real mDx, mHalfWidth;
286  Real mRmin, mRmax; // ignore particles outside this range of radii in voxel
287  size_t mMinCount, mMaxCount; // counters for ignored particles
288  int mGrainSize;
289 }; // class ParticlesToLevelSet
290 
291 
292 template<typename SdfGridT, typename AttributeT, typename InterrupterT>
294 ParticlesToLevelSet(SdfGridT& grid, InterrupterT* interrupter) :
295  mSdfGrid(&grid),
296  mBlindGrid(nullptr),
297  mInterrupter(interrupter),
298  mDx(grid.voxelSize()[0]),
299  mHalfWidth(grid.background()/mDx),
300  mRmin(1.5),// corresponds to the Nyquist grid sampling frequency
301  mRmax(100.0),// corresponds to a huge particle (probably too large!)
302  mMinCount(0),
303  mMaxCount(0),
304  mGrainSize(1)
305 {
306  if (!mSdfGrid->hasUniformVoxels()) {
307  OPENVDB_THROW(RuntimeError, "ParticlesToLevelSet only supports uniform voxels!");
308  }
309  if (!DisableT::value) {
310  mBlindGrid = new BlindGridType(BlindType(grid.background()));
311  mBlindGrid->setTransform(mSdfGrid->transform().copy());
312  }
313 }
314 
315 template<typename SdfGridT, typename AttributeT, typename InterrupterT>
316 template<typename ParticleListT>
318 rasterizeSpheres(const ParticleListT& pa)
319 {
320  if (DisableT::value) {
321  Raster<ParticleListT, SdfGridT> r(*this, mSdfGrid, pa);
322  r.rasterizeSpheres();
323  } else {
324  Raster<ParticleListT, BlindGridType> r(*this, mBlindGrid, pa);
325  r.rasterizeSpheres();
326  }
327 }
328 
329 template<typename SdfGridT, typename AttributeT, typename InterrupterT>
330 template<typename ParticleListT>
332 rasterizeSpheres(const ParticleListT& pa, Real radius)
333 {
334  if (DisableT::value) {
335  Raster<ParticleListT, SdfGridT> r(*this, mSdfGrid, pa);
336  r.rasterizeSpheres(radius/mDx);
337  } else {
338  Raster<ParticleListT, BlindGridType> r(*this, mBlindGrid, pa);
339  r.rasterizeSpheres(radius/mDx);
340  }
341 }
342 
343 template<typename SdfGridT, typename AttributeT, typename InterrupterT>
344 template<typename ParticleListT>
346 rasterizeTrails(const ParticleListT& pa, Real delta)
347 {
348  if (DisableT::value) {
349  Raster<ParticleListT, SdfGridT> r(*this, mSdfGrid, pa);
350  r.rasterizeTrails(delta);
351  } else {
352  Raster<ParticleListT, BlindGridType> r(*this, mBlindGrid, pa);
353  r.rasterizeTrails(delta);
354  }
355 }
356 
357 
358 template<typename SdfGridT, typename AttributeT, typename InterrupterT>
359 inline void
361 {
363 
364  if (!mBlindGrid) {
365  if (prune) {
366  if (OutputIsMask) {
367  tools::prune(mSdfGrid->tree());
368  } else {
369  tools::pruneLevelSet(mSdfGrid->tree());
370  }
371  }
372  return;
373  }
374 
375  if (prune) tools::prune(mBlindGrid->tree());
376 
377  using AttTreeT = typename AttGridType::TreeType;
378  using AttLeafT = typename AttTreeT::LeafNodeType;
379  using BlindTreeT = typename BlindGridType::TreeType;
380  using BlindLeafIterT = typename BlindTreeT::LeafCIter;
381  using BlindLeafT = typename BlindTreeT::LeafNodeType;
382  using SdfTreeT = typename SdfGridType::TreeType;
383  using SdfLeafT = typename SdfTreeT::LeafNodeType;
384 
385  // Use topology copy constructors since output grids have the same topology as mBlindDataGrid
386  const BlindTreeT& blindTree = mBlindGrid->tree();
387 
388  // Create the output attribute grid.
389  typename AttTreeT::Ptr attTree(new AttTreeT(
390  blindTree, blindTree.background().blind(), openvdb::TopologyCopy()));
391  // Note this overwrites any existing attribute grids!
392  mAttGrid = typename AttGridType::Ptr(new AttGridType(attTree));
393  mAttGrid->setTransform(mBlindGrid->transform().copy());
394 
395  typename SdfTreeT::Ptr sdfTree; // the output mask or level set tree
396 
397  // Extract the attribute grid and the mask or level set grid from mBlindDataGrid.
398  if (OutputIsMask) {
399  sdfTree.reset(new SdfTreeT(blindTree,
400  /*off=*/SdfType(0), /*on=*/SdfType(1), TopologyCopy()));
401 
402  // Copy leaf voxels in parallel.
403  tree::LeafManager<AttTreeT> leafNodes(*attTree);
404  leafNodes.foreach([&](AttLeafT& attLeaf, size_t /*leafIndex*/) {
405  if (const auto* blindLeaf = blindTree.probeConstLeaf(attLeaf.origin())) {
406  for (auto iter = attLeaf.beginValueOn(); iter; ++iter) {
407  const auto pos = iter.pos();
408  attLeaf.setValueOnly(pos, blindLeaf->getValue(pos).blind());
409  }
410  }
411  });
412  // Copy tiles serially.
413  const auto blindAcc = mBlindGrid->getConstAccessor();
414  auto iter = attTree->beginValueOn();
415  iter.setMaxDepth(AttTreeT::ValueOnIter::LEAF_DEPTH - 1);
416  for ( ; iter; ++iter) {
417  iter.modifyValue([&](AttType& v) { v = blindAcc.getValue(iter.getCoord()).blind(); });
418  }
419  } else {
420  // Here we exploit the fact that by design level sets have no active tiles.
421  // Only leaf voxels can be active.
422  sdfTree.reset(new SdfTreeT(blindTree, blindTree.background().visible(), TopologyCopy()));
423  for (BlindLeafIterT n = blindTree.cbeginLeaf(); n; ++n) {
424  const BlindLeafT& leaf = *n;
425  const openvdb::Coord xyz = leaf.origin();
426  // Get leafnodes that were allocated during topology construction!
427  SdfLeafT* sdfLeaf = sdfTree->probeLeaf(xyz);
428  AttLeafT* attLeaf = attTree->probeLeaf(xyz);
429  // Use linear offset (vs coordinate) access for better performance!
430  typename BlindLeafT::ValueOnCIter m=leaf.cbeginValueOn();
431  if (!m) {//no active values in leaf node so copy everything
432  for (openvdb::Index k = 0; k!=BlindLeafT::SIZE; ++k) {
433  const BlindType& v = leaf.getValue(k);
434  sdfLeaf->setValueOnly(k, v.visible());
435  attLeaf->setValueOnly(k, v.blind());
436  }
437  } else {//only copy active values (using flood fill for the inactive values)
438  for(; m; ++m) {
439  const openvdb::Index k = m.pos();
440  const BlindType& v = *m;
441  sdfLeaf->setValueOnly(k, v.visible());
442  attLeaf->setValueOnly(k, v.blind());
443  }
444  }
445  }
446  tools::signedFloodFill(*sdfTree);//required since we only transferred active voxels!
447  }
448 
449  if (mSdfGrid->empty()) {
450  mSdfGrid->setTree(sdfTree);
451  } else {
452  if (OutputIsMask) {
453  mSdfGrid->tree().topologyUnion(*sdfTree);
454  tools::prune(mSdfGrid->tree());
455  } else {
456  tools::csgUnion(mSdfGrid->tree(), *sdfTree, /*prune=*/true);
457  }
458  }
459 
461 }
462 
463 
464 ///////////////////////////////////////////////////////////
465 
466 
467 template<typename SdfGridT, typename AttributeT, typename InterrupterT>
468 template<typename ParticleListT, typename GridT>
469 struct ParticlesToLevelSet<SdfGridT, AttributeT, InterrupterT>::Raster
470 {
471  using DisableT = typename std::is_void<AttributeT>::type;
472  using ParticlesToLevelSetT = ParticlesToLevelSet<SdfGridT, AttributeT, InterrupterT>;
473  using SdfT = typename ParticlesToLevelSetT::SdfType; // type of signed distance values
474  using AttT = typename ParticlesToLevelSetT::AttType; // type of particle attribute
475  using ValueT = typename GridT::ValueType;
476  using AccessorT = typename GridT::Accessor;
477  using TreeT = typename GridT::TreeType;
478  using LeafNodeT = typename TreeT::LeafNodeType;
479  using PointPartitionerT = PointPartitioner<Index32, LeafNodeT::LOG2DIM>;
480 
481  static const bool
482  OutputIsMask = std::is_same<SdfT, bool>::value,
483  DoAttrXfer = !DisableT::value;
484 
485  /// @brief Main constructor
486  Raster(ParticlesToLevelSetT& parent, GridT* grid, const ParticleListT& particles)
487  : mParent(parent)
488  , mParticles(particles)
489  , mGrid(grid)
490  , mMap(*(mGrid->transform().baseMap()))
491  , mMinCount(0)
492  , mMaxCount(0)
493  , mIsCopy(false)
494  {
495  mPointPartitioner = new PointPartitionerT;
496  mPointPartitioner->construct(particles, mGrid->transform());
497  }
498 
499  /// @brief Copy constructor called by tbb threads
500  Raster(Raster& other, tbb::split)
501  : mParent(other.mParent)
502  , mParticles(other.mParticles)
503  , mGrid(new GridT(*other.mGrid, openvdb::ShallowCopy()))
504  , mMap(other.mMap)
505  , mMinCount(0)
506  , mMaxCount(0)
507  , mTask(other.mTask)
508  , mIsCopy(true)
509  , mPointPartitioner(other.mPointPartitioner)
510  {
511  mGrid->newTree();
512  }
513 
514  virtual ~Raster()
515  {
516  // Copy-constructed Rasters own temporary grids that have to be deleted,
517  // while the original has ownership of the bucket array.
518  if (mIsCopy) {
519  delete mGrid;
520  } else {
521  delete mPointPartitioner;
522  }
523  }
524 
525  void rasterizeSpheres()
526  {
527  mMinCount = mMaxCount = 0;
528  if (mParent.mInterrupter) {
529  mParent.mInterrupter->start("Rasterizing particles to level set using spheres");
530  }
531  mTask = std::bind(&Raster::rasterSpheres, std::placeholders::_1, std::placeholders::_2);
532  this->cook();
533  if (mParent.mInterrupter) mParent.mInterrupter->end();
534  }
535 
536  void rasterizeSpheres(Real radius)
537  {
538  mMinCount = radius < mParent.mRmin ? mParticles.size() : 0;
539  mMaxCount = radius > mParent.mRmax ? mParticles.size() : 0;
540  if (mMinCount>0 || mMaxCount>0) {//skipping all particles!
541  mParent.mMinCount = mMinCount;
542  mParent.mMaxCount = mMaxCount;
543  } else {
544  if (mParent.mInterrupter) {
545  mParent.mInterrupter->start(
546  "Rasterizing particles to level set using const spheres");
547  }
548  mTask = std::bind(&Raster::rasterFixedSpheres,
549  std::placeholders::_1, std::placeholders::_2, radius);
550  this->cook();
551  if (mParent.mInterrupter) mParent.mInterrupter->end();
552  }
553  }
554 
555  void rasterizeTrails(Real delta=1.0)
556  {
557  mMinCount = mMaxCount = 0;
558  if (mParent.mInterrupter) {
559  mParent.mInterrupter->start("Rasterizing particles to level set using trails");
560  }
561  mTask = std::bind(&Raster::rasterTrails,
562  std::placeholders::_1, std::placeholders::_2, delta);
563  this->cook();
564  if (mParent.mInterrupter) mParent.mInterrupter->end();
565  }
566 
567  /// @brief Kick off the optionally multithreaded computation.
568  void operator()(const tbb::blocked_range<size_t>& r)
569  {
570  OPENVDB_ASSERT(mTask);
571  mTask(this, r);
572  mParent.mMinCount = mMinCount;
573  mParent.mMaxCount = mMaxCount;
574  }
575 
576  /// @brief Required by tbb::parallel_reduce
577  void join(Raster& other)
578  {
580  if (OutputIsMask) {
581  if (DoAttrXfer) {
582  tools::compMax(*mGrid, *other.mGrid);
583  } else {
584  mGrid->topologyUnion(*other.mGrid);
585  }
586  } else {
587  tools::csgUnion(*mGrid, *other.mGrid, /*prune=*/true);
588  }
590  mMinCount += other.mMinCount;
591  mMaxCount += other.mMaxCount;
592  }
593 
594 private:
595  /// Disallow assignment since some of the members are references
596  Raster& operator=(const Raster&) { return *this; }
597 
598  /// @return true if the particle is too small or too large
599  bool ignoreParticle(Real R)
600  {
601  if (R < mParent.mRmin) {// below the cutoff radius
602  ++mMinCount;
603  return true;
604  }
605  if (R > mParent.mRmax) {// above the cutoff radius
606  ++mMaxCount;
607  return true;
608  }
609  return false;
610  }
611 
612  /// @brief Threaded rasterization of particles as spheres with variable radius
613  /// @param r range of indices into the list of particles
614  void rasterSpheres(const tbb::blocked_range<size_t>& r)
615  {
616  AccessorT acc = mGrid->getAccessor(); // local accessor
617  bool run = true;
618  const Real invDx = 1 / mParent.mDx;
619  AttT att;
620  Vec3R pos;
621  Real rad;
622 
623  // Loop over buckets
624  for (size_t n = r.begin(), N = r.end(); n < N; ++n) {
625  // Loop over particles in bucket n.
626  typename PointPartitionerT::IndexIterator iter = mPointPartitioner->indices(n);
627  for ( ; run && iter; ++iter) {
628  const Index32& id = *iter;
629  mParticles.getPosRad(id, pos, rad);
630  const Real R = invDx * rad;// in voxel units
631  if (this->ignoreParticle(R)) continue;
632  const Vec3R P = mMap.applyInverseMap(pos);
633  this->getAtt<DisableT>(id, att);
634  run = this->makeSphere(P, R, att, acc);
635  }//end loop over particles
636  }//end loop over buckets
637  }
638 
639  /// @brief Threaded rasterization of particles as spheres with a fixed radius
640  /// @param r range of indices into the list of particles
641  /// @param R radius of fixed-size spheres
642  void rasterFixedSpheres(const tbb::blocked_range<size_t>& r, Real R)
643  {
644  AccessorT acc = mGrid->getAccessor(); // local accessor
645  AttT att;
646  Vec3R pos;
647 
648  // Loop over buckets
649  for (size_t n = r.begin(), N = r.end(); n < N; ++n) {
650  // Loop over particles in bucket n.
651  for (auto iter = mPointPartitioner->indices(n); iter; ++iter) {
652  const Index32& id = *iter;
653  this->getAtt<DisableT>(id, att);
654  mParticles.getPos(id, pos);
655  const Vec3R P = mMap.applyInverseMap(pos);
656  this->makeSphere(P, R, att, acc);
657  }
658  }
659  }
660 
661  /// @brief Threaded rasterization of particles as spheres with velocity trails
662  /// @param r range of indices into the list of particles
663  /// @param delta inter-sphere spacing
664  void rasterTrails(const tbb::blocked_range<size_t>& r, Real delta)
665  {
666  AccessorT acc = mGrid->getAccessor(); // local accessor
667  bool run = true;
668  AttT att;
669  Vec3R pos, vel;
670  Real rad;
671  const Vec3R origin = mMap.applyInverseMap(Vec3R(0,0,0));
672  const Real Rmin = mParent.mRmin, invDx = 1 / mParent.mDx;
673 
674  // Loop over buckets
675  for (size_t n = r.begin(), N = r.end(); n < N; ++n) {
676  // Loop over particles in bucket n.
677  typename PointPartitionerT::IndexIterator iter = mPointPartitioner->indices(n);
678  for ( ; run && iter; ++iter) {
679  const Index32& id = *iter;
680  mParticles.getPosRadVel(id, pos, rad, vel);
681  const Real R0 = invDx * rad;
682  if (this->ignoreParticle(R0)) continue;
683  this->getAtt<DisableT>(id, att);
684  const Vec3R P0 = mMap.applyInverseMap(pos);
685  const Vec3R V = mMap.applyInverseMap(vel) - origin; // exclude translation
686  const Real speed = V.length(), invSpeed = 1.0 / speed;
687  const Vec3R Nrml = -V * invSpeed; // inverse normalized direction
688  Vec3R P = P0; // local position of instance
689  Real R = R0, d = 0; // local radius and length of trail
690  while (run && d <= speed) {
691  run = this->makeSphere(P, R, att, acc);
692  P += 0.5 * delta * R * Nrml; // adaptive offset along inverse velocity direction
693  d = (P - P0).length(); // current length of trail
694  R = R0 - (R0 - Rmin) * d * invSpeed; // R = R0 -> mRmin(e.g. 1.5)
695  }//end loop over sphere instances
696  }//end loop over particles
697  }//end loop over buckets
698  }
699 
700  void cook()
701  {
702  // parallelize over the point buckets
703  const Index32 bucketCount = Index32(mPointPartitioner->size());
704 
705  if (mParent.mGrainSize>0) {
706  tbb::parallel_reduce(
707  tbb::blocked_range<size_t>(0, bucketCount, mParent.mGrainSize), *this);
708  } else {
709  (*this)(tbb::blocked_range<size_t>(0, bucketCount));
710  }
711  }
712 
713  /// @brief Rasterize sphere at position P and radius R into
714  /// a narrow-band level set with half-width, mHalfWidth.
715  /// @return @c false if rasterization was interrupted
716  ///
717  /// @param P coordinates of the particle position in voxel units
718  /// @param R radius of particle in voxel units
719  /// @param att an optional user-defined attribute value to be associated with voxels
720  /// @param acc grid accessor with a private copy of the grid
721  ///
722  /// @note For best performance all computations are performed in voxel space,
723  /// with the important exception of the final level set value that is converted
724  /// to world units (the grid stores the closest Euclidean signed distances
725  /// measured in world units). Also note we use the convention of positive distances
726  /// outside the surface and negative distances inside the surface.
727  template <bool IsMaskT = OutputIsMask>
728  typename std::enable_if<!IsMaskT, bool>::type
729  makeSphere(const Vec3R& P, Real R, const AttT& att, AccessorT& acc)
730  {
731  const Real
732  dx = mParent.mDx,
733  w = mParent.mHalfWidth,
734  max = R + w, // maximum distance in voxel units
735  max2 = math::Pow2(max), // square of maximum distance in voxel units
736  min2 = math::Pow2(math::Max(Real(0), R - w)); // square of minimum distance
737  // Bounding box of the sphere
738  const Coord
739  lo(math::Floor(P[0]-max),math::Floor(P[1]-max),math::Floor(P[2]-max)),
740  hi(math::Ceil( P[0]+max),math::Ceil( P[1]+max),math::Ceil( P[2]+max));
741  const ValueT inside = -mGrid->background();
742 
743  ValueT v;
744  size_t count = 0;
745  for (Coord c = lo; c.x() <= hi.x(); ++c.x()) {
746  //only check interrupter every 32'th scan in x
747  if (!(count++ & ((1<<5)-1)) && util::wasInterrupted(mParent.mInterrupter)) {
748  thread::cancelGroupExecution();
749  return false;
750  }
751  const Real x2 = math::Pow2(c.x() - P[0]);
752  for (c.y() = lo.y(); c.y() <= hi.y(); ++c.y()) {
753  const Real x2y2 = x2 + math::Pow2(c.y() - P[1]);
754  for (c.z() = lo.z(); c.z() <= hi.z(); ++c.z()) {
755  const Real x2y2z2 = x2y2 + math::Pow2(c.z()-P[2]); // squared dist from c to P
756 #if defined __INTEL_COMPILER
757  _Pragma("warning (push)")
758  _Pragma("warning (disable:186)") // "pointless comparison of unsigned integer with zero"
759 #endif
760  if (x2y2z2 >= max2 || (!acc.probeValue(c, v) && (v < ValueT(0))))
761  continue;//outside narrow band of the particle or inside existing level set
762 #if defined __INTEL_COMPILER
763  _Pragma("warning (pop)")
764 #endif
765  if (x2y2z2 <= min2) {//inside narrow band of the particle.
766  acc.setValueOff(c, inside);
767  continue;
768  }
769  // convert signed distance from voxel units to world units
770  //const ValueT d=dx*(math::Sqrt(x2y2z2) - R);
771  const ValueT d = Merge(static_cast<SdfT>(dx*(math::Sqrt(x2y2z2)-R)), att);
772  if (d < v) acc.setValue(c, d);//CSG union
773  }//end loop over z
774  }//end loop over y
775  }//end loop over x
776  return true;
777  }
778 
779  /// @brief Rasterize a sphere of radius @a r at position @a p into a boolean mask grid.
780  /// @return @c false if rasterization was interrupted
781  template <bool IsMaskT = OutputIsMask>
782  typename std::enable_if<IsMaskT, bool>::type
783  makeSphere(const Vec3R& p, Real r, const AttT& att, AccessorT& acc)
784  {
785  const Real
786  rSquared = r * r, // sphere radius squared, in voxel units
787  inW = r / math::Sqrt(6.0); // half the width in voxel units of an inscribed cube
788  const Coord
789  // Bounding box of the sphere
790  outLo(math::Floor(p[0] - r), math::Floor(p[1] - r), math::Floor(p[2] - r)),
791  outHi(math::Ceil(p[0] + r), math::Ceil(p[1] + r), math::Ceil(p[2] + r)),
792  // Bounds of the inscribed cube
793  inLo(math::Ceil(p[0] - inW), math::Ceil(p[1] - inW), math::Ceil(p[2] - inW)),
794  inHi(math::Floor(p[0] + inW), math::Floor(p[1] + inW), math::Floor(p[2] + inW));
795  // Bounding boxes of regions comprising out - in
796  /// @todo These could be divided further into sparsely- and densely-filled subregions.
797  const std::vector<CoordBBox> padding{
798  CoordBBox(outLo.x(), outLo.y(), outLo.z(), inLo.x()-1, outHi.y(), outHi.z()),
799  CoordBBox(inHi.x()+1, outLo.y(), outLo.z(), outHi.x(), outHi.y(), outHi.z()),
800  CoordBBox(outLo.x(), outLo.y(), outLo.z(), outHi.x(), inLo.y()-1, outHi.z()),
801  CoordBBox(outLo.x(), inHi.y()+1, outLo.z(), outHi.x(), outHi.y(), outHi.z()),
802  CoordBBox(outLo.x(), outLo.y(), outLo.z(), outHi.x(), outHi.y(), inLo.z()-1),
803  CoordBBox(outLo.x(), outLo.y(), inHi.z()+1, outHi.x(), outHi.y(), outHi.z()),
804  };
805  const ValueT onValue = Merge(SdfT(1), att);
806 
807  // Sparsely fill the inscribed cube.
808  /// @todo Use sparse fill only if 2r > leaf width?
809  acc.tree().sparseFill(CoordBBox(inLo, inHi), onValue);
810 
811  // Densely fill the remaining regions.
812  for (const auto& bbox: padding) {
813  if (util::wasInterrupted(mParent.mInterrupter)) {
814  thread::cancelGroupExecution();
815  return false;
816  }
817  const Coord &bmin = bbox.min(), &bmax = bbox.max();
818  Coord c;
819  Real cx, cy, cz;
820  for (c = bmin, cx = c.x(); c.x() <= bmax.x(); ++c.x(), cx += 1) {
821  const Real x2 = math::Pow2(cx - p[0]);
822  for (c.y() = bmin.y(), cy = c.y(); c.y() <= bmax.y(); ++c.y(), cy += 1) {
823  const Real x2y2 = x2 + math::Pow2(cy - p[1]);
824  for (c.z() = bmin.z(), cz = c.z(); c.z() <= bmax.z(); ++c.z(), cz += 1) {
825  const Real x2y2z2 = x2y2 + math::Pow2(cz - p[2]);
826  if (x2y2z2 < rSquared) {
827  acc.setValue(c, onValue);
828  }
829  }
830  }
831  }
832  }
833  return true;
834  }
835 
836  using FuncType = typename std::function<void (Raster*, const tbb::blocked_range<size_t>&)>;
837 
838  template<typename DisableType>
839  typename std::enable_if<DisableType::value>::type
840  getAtt(size_t, AttT&) const {}
841 
842  template<typename DisableType>
843  typename std::enable_if<!DisableType::value>::type
844  getAtt(size_t n, AttT& a) const { mParticles.getAtt(n, a); }
845 
846  template<typename T>
847  typename std::enable_if<std::is_same<T, ValueT>::value, ValueT>::type
848  Merge(T s, const AttT&) const { return s; }
849 
850  template<typename T>
851  typename std::enable_if<!std::is_same<T, ValueT>::value, ValueT>::type
852  Merge(T s, const AttT& a) const { return ValueT(s,a); }
853 
854  ParticlesToLevelSetT& mParent;
855  const ParticleListT& mParticles;//list of particles
856  GridT* mGrid;
857  const math::MapBase& mMap;
858  size_t mMinCount, mMaxCount;//counters for ignored particles!
859  FuncType mTask;
860  const bool mIsCopy;
861  PointPartitionerT* mPointPartitioner;
862 }; // struct ParticlesToLevelSet::Raster
863 
864 
865 ///////////////////// YOU CAN SAFELY IGNORE THIS SECTION /////////////////////
866 
867 /// @cond OPENVDB_DOCS_INTERNAL
868 
869 namespace p2ls_internal {
870 
871 // This is a simple type that combines a distance value and a particle
872 // attribute. It's required for attribute transfer which is defined in the
873 // Raster class above.
874 /// @private
875 template<typename VisibleT, typename BlindT>
876 class BlindData
877 {
878 public:
879  using type = VisibleT;
880  using VisibleType = VisibleT;
881  using BlindType = BlindT;
882 
883  BlindData() {}
884  explicit BlindData(VisibleT v) : mVisible(v), mBlind(zeroVal<BlindType>()) {}
885  BlindData(VisibleT v, BlindT b) : mVisible(v), mBlind(b) {}
886  BlindData(const BlindData&) = default;
887  BlindData& operator=(const BlindData&) = default;
888  const VisibleT& visible() const { return mVisible; }
889  const BlindT& blind() const { return mBlind; }
891  bool operator==(const BlindData& rhs) const { return mVisible == rhs.mVisible; }
893  bool operator< (const BlindData& rhs) const { return mVisible < rhs.mVisible; }
894  bool operator> (const BlindData& rhs) const { return mVisible > rhs.mVisible; }
895  BlindData operator+(const BlindData& rhs) const { return BlindData(mVisible + rhs.mVisible); }
896  BlindData operator-(const BlindData& rhs) const { return BlindData(mVisible - rhs.mVisible); }
897  BlindData operator-() const { return BlindData(-mVisible, mBlind); }
898 
899 protected:
900  VisibleT mVisible;
901  BlindT mBlind;
902 };
903 
904 /// @private
905 // Required by several of the tree nodes
906 template<typename VisibleT, typename BlindT>
907 inline std::ostream& operator<<(std::ostream& ostr, const BlindData<VisibleT, BlindT>& rhs)
908 {
909  ostr << rhs.visible();
910  return ostr;
911 }
912 
913 /// @private
914 // Required by math::Abs
915 template<typename VisibleT, typename BlindT>
916 inline BlindData<VisibleT, BlindT> Abs(const BlindData<VisibleT, BlindT>& x)
917 {
918  return BlindData<VisibleT, BlindT>(math::Abs(x.visible()), x.blind());
919 }
920 
921 /// @private
922 // Required to support the (zeroVal<BlindData>() + val) idiom.
923 template<typename VisibleT, typename BlindT, typename T>
924 inline BlindData<VisibleT, BlindT>
925 operator+(const BlindData<VisibleT, BlindT>& x, const T& rhs)
926 {
927  return BlindData<VisibleT, BlindT>(x.visible() + static_cast<VisibleT>(rhs), x.blind());
928 }
929 
930 } // namespace p2ls_internal
931 
932 /// @endcond
933 
934 //////////////////////////////////////////////////////////////////////////////
935 
936 
937 // The following are convenience functions for common use cases.
938 
939 template<typename GridT, typename ParticleListT, typename InterrupterT>
940 inline void
941 particlesToSdf(const ParticleListT& plist, GridT& grid, InterrupterT* interrupt)
942 {
943  static_assert(std::is_floating_point<typename GridT::ValueType>::value,
944  "particlesToSdf requires an SDF grid with floating-point values");
945 
946  if (grid.getGridClass() != GRID_LEVEL_SET) {
947  OPENVDB_LOG_WARN("particlesToSdf requires a level set grid;"
948  " try Grid::setGridClass(openvdb::GRID_LEVEL_SET)");
949  }
950 
951  ParticlesToLevelSet<GridT> p2ls(grid, interrupt);
952  p2ls.rasterizeSpheres(plist);
953  tools::pruneLevelSet(grid.tree());
954 }
955 
956 template<typename GridT, typename ParticleListT, typename InterrupterT>
957 inline void
958 particlesToSdf(const ParticleListT& plist, GridT& grid, Real radius, InterrupterT* interrupt)
959 {
960  static_assert(std::is_floating_point<typename GridT::ValueType>::value,
961  "particlesToSdf requires an SDF grid with floating-point values");
962 
963  if (grid.getGridClass() != GRID_LEVEL_SET) {
964  OPENVDB_LOG_WARN("particlesToSdf requires a level set grid;"
965  " try Grid::setGridClass(openvdb::GRID_LEVEL_SET)");
966  }
967 
968  ParticlesToLevelSet<GridT> p2ls(grid, interrupt);
969  p2ls.rasterizeSpheres(plist, radius);
970  tools::pruneLevelSet(grid.tree());
971 }
972 
973 template<typename GridT, typename ParticleListT, typename InterrupterT>
974 inline void
975 particleTrailsToSdf(const ParticleListT& plist, GridT& grid, Real delta, InterrupterT* interrupt)
976 {
977  static_assert(std::is_floating_point<typename GridT::ValueType>::value,
978  "particleTrailsToSdf requires an SDF grid with floating-point values");
979 
980  if (grid.getGridClass() != GRID_LEVEL_SET) {
981  OPENVDB_LOG_WARN("particlesToSdf requires a level set grid;"
982  " try Grid::setGridClass(openvdb::GRID_LEVEL_SET)");
983  }
984 
985  ParticlesToLevelSet<GridT> p2ls(grid, interrupt);
986  p2ls.rasterizeTrails(plist, delta);
987  tools::pruneLevelSet(grid.tree());
988 }
989 
990 template<typename GridT, typename ParticleListT, typename InterrupterT>
991 inline void
992 particlesToMask(const ParticleListT& plist, GridT& grid, InterrupterT* interrupt)
993 {
994  static_assert(std::is_same<bool, typename GridT::ValueType>::value,
995  "particlesToMask requires a boolean-valued grid");
996  ParticlesToLevelSet<GridT> p2ls(grid, interrupt);
997  p2ls.rasterizeSpheres(plist);
998  tools::prune(grid.tree());
999 }
1000 
1001 template<typename GridT, typename ParticleListT, typename InterrupterT>
1002 inline void
1003 particlesToMask(const ParticleListT& plist, GridT& grid, Real radius, InterrupterT* interrupt)
1004 {
1005  static_assert(std::is_same<bool, typename GridT::ValueType>::value,
1006  "particlesToMask requires a boolean-valued grid");
1007  ParticlesToLevelSet<GridT> p2ls(grid, interrupt);
1008  p2ls.rasterizeSpheres(plist, radius);
1009  tools::prune(grid.tree());
1010 }
1011 
1012 template<typename GridT, typename ParticleListT, typename InterrupterT>
1013 inline void
1014 particleTrailsToMask(const ParticleListT& plist, GridT& grid, Real delta, InterrupterT* interrupt)
1015 {
1016  static_assert(std::is_same<bool, typename GridT::ValueType>::value,
1017  "particleTrailsToMask requires a boolean-valued grid");
1018  ParticlesToLevelSet<GridT> p2ls(grid, interrupt);
1019  p2ls.rasterizeTrails(plist, delta);
1020  tools::prune(grid.tree());
1021 }
1022 
1023 } // namespace tools
1024 } // namespace OPENVDB_VERSION_NAME
1025 } // namespace openvdb
1026 
1027 #endif // OPENVDB_TOOLS_PARTICLES_TO_LEVELSET_HAS_BEEN_INCLUDED
void rasterizeTrails(const ParticleListT &pa, Real delta=1.0)
Rasterize each particle as a trail comprising the CSG union of spheres of decreasing radius...
Definition: ParticlesToLevelSet.h:346
InterrupterT InterrupterType
Definition: ParticlesToLevelSet.h:164
Type Pow2(Type x)
Return x2.
Definition: Math.h:548
SdfT::Ptr rasterizeSpheres(const PointDataGridT &points, const Real radius, const Real halfband=LEVEL_SET_HALF_WIDTH, math::Transform::Ptr transform=nullptr, const FilterT &filter=NullFilter(), InterrupterT *interrupter=nullptr)
Narrow band sphere stamping with a uniform radius.
Definition: PointRasterizeSDFImpl.h:1061
Definition: ParticlesToLevelSet.h:160
static const bool OutputIsMask
Definition: ParticlesToLevelSet.h:172
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
Coord Abs(const Coord &xyz)
Definition: Coord.h:518
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
void csgUnion(GridOrTreeT &a, GridOrTreeT &b, bool prune=true, bool pruneCancelledTiles=false)
Given two level set grids, replace the A grid with the union of A and B.
Definition: Composite.h:886
void pruneLevelSet(TreeT &tree, bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing nodes whose values are all inactive with inactive ...
Definition: Prune.h:390
Functions to efficiently perform various compositing operations on grids.
void compMax(GridOrTreeT &a, GridOrTreeT &b)
Given grids A and B, compute max(a, b) per voxel (using sparse traversal). Store the result in the A ...
Definition: Composite.h:748
void setGrainSize(int grainSize)
Set the grain size used for threading.
Definition: ParticlesToLevelSet.h:240
bool operator>(const Tuple< SIZE, T0 > &t0, const Tuple< SIZE, T1 > &t1)
Definition: Tuple.h:187
void particlesToMask(const ParticleListT &, GridT &, Real radius, InterrupterT *=nullptr)
Activate a boolean grid wherever it intersects the fixed-size spheres described by the given particle...
Definition: ParticlesToLevelSet.h:1003
void particleTrailsToMask(const ParticleListT &, GridT &, Real delta=1, InterrupterT *=nullptr)
Activate a boolean grid wherever it intersects trails of spheres with decreasing radius, where the starting position and radius and the direction of each trail is given by particle attributes.
Definition: ParticlesToLevelSet.h:1014
Tag dispatch class that distinguishes shallow copy constructors from deep copy constructors.
Definition: Types.h:680
const Type & Max(const Type &a, const Type &b)
Return the maximum of two values.
Definition: Math.h:595
typename SdfGridT::template ValueConverter< AttType >::Type AttGridType
Definition: ParticlesToLevelSet.h:170
Index32 Index
Definition: Types.h:54
Abstract base class for maps.
Definition: Maps.h:134
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:110
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:49
void particleTrailsToSdf(const ParticleListT &, GridT &, Real delta=1, InterrupterT *=nullptr)
Populate a scalar, floating-point grid with CSG-unioned trails of level set spheres with decreasing r...
Definition: ParticlesToLevelSet.h:975
typename SdfGridT::ValueType SdfType
Definition: ParticlesToLevelSet.h:167
void setRmin(Real Rmin)
Set the smallest radius allowed in voxel units.
Definition: ParticlesToLevelSet.h:220
Spatially partitions points using a parallel radix-based sorting algorithm.
Defined various multi-threaded utility functions for trees.
Real getHalfWidth() const
Return the half-width of the narrow band in voxel units.
Definition: ParticlesToLevelSet.h:215
bool operator==(const Vec3< T0 > &v0, const Vec3< T1 > &v1)
Equality operator, does exact floating point comparisons.
Definition: Vec3.h:474
void split(ContainerT &out, const std::string &in, const char delim)
Definition: Name.h:43
bool ignoredParticles() const
Return true if any particles were ignored due to their size.
Definition: ParticlesToLevelSet.h:228
Definition: Types.h:455
Vec3< typename promote< T, typename Coord::ValueType >::type > operator+(const Vec3< T > &v0, const Coord &v1)
Allow a Coord to be added to or subtracted from a Vec3.
Definition: Coord.h:528
#define OPENVDB_ASSERT(X)
Definition: Assert.h:41
MeshToVoxelEdgeData::EdgeData Abs(const MeshToVoxelEdgeData::EdgeData &x)
Definition: MeshToVolume.h:3921
Propagate the signs of distance values from the active voxels in the narrow band to the inactive valu...
Real getVoxelSize() const
Return the size of a voxel in world units.
Definition: ParticlesToLevelSet.h:212
Definition: PointPartitioner.h:77
int Floor(float x)
Return the floor of x.
Definition: Math.h:848
SdfGridT SdfGridType
Definition: ParticlesToLevelSet.h:166
Definition: Exceptions.h:13
bool operator<(const Tuple< SIZE, T0 > &t0, const Tuple< SIZE, T1 > &t1)
Definition: Tuple.h:175
#define OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
Definition: Platform.h:140
~ParticlesToLevelSet()
Definition: ParticlesToLevelSet.h:194
#define OPENVDB_NO_FP_EQUALITY_WARNING_END
Definition: Math.h:49
void particlesToSdf(const ParticleListT &, GridT &, Real radius, InterrupterT *=nullptr)
Populate a scalar, floating-point grid with fixed-size, CSG-unioned level set spheres described by th...
Definition: ParticlesToLevelSet.h:958
double Real
Definition: Types.h:60
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.
int Ceil(float x)
Return the ceiling of x.
Definition: Math.h:856
Vec3< typename promote< T, Coord::ValueType >::type > operator-(const Vec3< T > &v0, const Coord &v1)
Allow a Coord to be subtracted from a Vec3.
Definition: Coord.h:554
void signedFloodFill(TreeOrLeafManagerT &tree, bool threaded=true, size_t grainSize=1, Index minLevel=0)
Set the values of all inactive voxels and tiles of a narrow-band level set from the signs of the acti...
Definition: SignedFloodFill.h:267
void particlesToSdf(const ParticleListT &, GridT &, InterrupterT *=nullptr)
Populate a scalar, floating-point grid with CSG-unioned level set spheres described by the given part...
Definition: ParticlesToLevelSet.h:941
#define OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN
Definition: Math.h:48
uint32_t Index32
Definition: Types.h:52
typename std::conditional< DisableT::value, size_t, AttributeT >::type AttType
Definition: ParticlesToLevelSet.h:169
void prune(TreeT &tree, typename TreeT::ValueType tolerance=zeroVal< typename TreeT::ValueType >(), bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing with tiles any nodes whose values are all the same...
Definition: Prune.h:335
#define OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
Definition: Platform.h:141
int getGrainSize() const
Return the grain size used for threading.
Definition: ParticlesToLevelSet.h:237
void foreach(const LeafOp &op, bool threaded=true, size_t grainSize=1)
Threaded method that applies a user-supplied functor to each leaf node in the LeafManager.
Definition: LeafManager.h:484
#define OPENVDB_LOG_WARN(message)
Log a warning message of the form &#39;someVar << "some text" << ...&#39;.
Definition: logging.h:256
Real getRmin() const
Return the smallest radius allowed in voxel units.
Definition: ParticlesToLevelSet.h:218
void particlesToMask(const ParticleListT &, GridT &, InterrupterT *=nullptr)
Activate a boolean grid wherever it intersects the spheres described by the given particle positions ...
Definition: ParticlesToLevelSet.h:992
This class manages a linear array of pointers to a given tree&#39;s leaf nodes, as well as optional auxil...
Definition: LeafManager.h:85
typename std::is_void< AttributeT >::type DisableT
Definition: ParticlesToLevelSet.h:163
AttGridType::Ptr attributeGrid()
Return a pointer to the grid containing the optional user-defined attribute.
Definition: ParticlesToLevelSet.h:209
void setRmax(Real Rmax)
Set the largest radius allowed in voxel units.
Definition: ParticlesToLevelSet.h:225
void rasterizeSpheres(const ParticleListT &pa)
Rasterize each particle as a sphere with the particle&#39;s position and radius.
Definition: ParticlesToLevelSet.h:318
Tag dispatch class that distinguishes topology copy constructors from deep copy constructors.
Definition: Types.h:683
T length() const
Length of the vector.
Definition: Vec3.h:201
A LeafManager manages a linear array of pointers to a given tree&#39;s leaf nodes, as well as optional au...
void finalize(bool prune=false)
This method syncs up the level set and attribute grids and therefore needs to be called before any of...
Definition: ParticlesToLevelSet.h:360
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
Real getRmax() const
Return the largest radius allowed in voxel units.
Definition: ParticlesToLevelSet.h:223
float Sqrt(float x)
Return the square root of a floating-point value.
Definition: Math.h:761
size_t getMaxCount() const
Return the number of particles that were ignored because they were larger than the maximum radius...
Definition: ParticlesToLevelSet.h:234
math::Vec3< Real > Vec3R
Definition: Types.h:72
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:218
size_t getMinCount() const
Return the number of particles that were ignored because they were smaller than the minimum radius...
Definition: ParticlesToLevelSet.h:231
Definition: Exceptions.h:63