HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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>
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:
164  using InterrupterType = InterrupterT;
165 
166  using SdfGridType = SdfGridT;
167  using SdfType = typename SdfGridT::ValueType;
168 
170  using AttGridType = typename SdfGridT::template ValueConverter<AttType>::Type;
171 
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
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>
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>
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>
840  getAtt(size_t, AttT&) const {}
841 
842  template<typename DisableType>
844  getAtt(size_t n, AttT& a) const { mParticles.getAtt(n, a); }
845 
846  template<typename T>
848  Merge(T s, const AttT&) const { return s; }
849 
850  template<typename T>
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 {
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 {
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 {
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 {
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 {
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 {
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
type
Definition: core.h:556
size_t getMaxCount() const
Return the number of particles that were ignored because they were larger than the maximum radius...
Real getHalfWidth() const
Return the half-width of the narrow band in voxel units.
int Ceil(float x)
Return the ceiling of x.
Definition: Math.h:856
MeshToVoxelEdgeData::EdgeData Abs(const MeshToVoxelEdgeData::EdgeData &x)
Functions to efficiently perform various compositing operations on grids.
math::Vec3< Real > Vec3R
Definition: Types.h:72
Tag dispatch class that distinguishes shallow copy constructors from deep copy constructors.
Definition: Types.h:680
Type Pow2(Type x)
Return x2.
Definition: Math.h:548
Real getRmax() const
Return the largest radius allowed in voxel units.
void
Definition: png.h:1083
AttGridType::Ptr attributeGrid()
Return a pointer to the grid containing the optional user-defined attribute.
Real getVoxelSize() const
Return the size of a voxel in world units.
const GLdouble * v
Definition: glcorearb.h:837
GLsizei const GLfloat * value
Definition: glcorearb.h:824
void finalize(bool prune=false)
This method syncs up the level set and attribute grids and therefore needs to be called before any of...
void particlesToSdf(const ParticleListT &, GridT &, InterrupterT *=nullptr)
Populate a scalar, floating-point grid with CSG-unioned level set spheres described by the given part...
IMATH_HOSTDEVICE constexpr Plane3< T > operator-(const Plane3< T > &plane) IMATH_NOEXCEPT
Reflect the pla.
Definition: ImathPlane.h:253
#define OPENVDB_LOG_WARN(mesg)
Definition: logging.h:277
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1222
GLdouble s
Definition: glad.h:3009
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
GLuint GLsizei GLsizei * length
Definition: glcorearb.h:795
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:245
void particlesToMask(const ParticleListT &, GridT &, InterrupterT *=nullptr)
Activate a boolean grid wherever it intersects the spheres described by the given particle positions ...
GA_API const UT_StringHolder P
void setGrainSize(int grainSize)
Set the grain size used for threading.
GLdouble GLdouble x2
Definition: glad.h:2349
ParticlesToLevelSet(SdfGridT &grid, InterrupterT *interrupt=nullptr)
Constructor using an existing boolean or narrow-band level set grid.
Spatially partitions points using a parallel radix-based sorting algorithm.
#define OPENVDB_ASSERT(X)
Definition: Assert.h:41
SYS_FORCE_INLINE const_iterator end() const
bool operator==(const BaseDimensions< T > &a, const BaseDimensions< Y > &b)
Definition: Dimensions.h:137
constexpr T zeroVal()
Return the value of type T that corresponds to zero.
Definition: Math.h:70
int getGrainSize() const
Return the grain size used for threading.
OIIO_FORCEINLINE vbool4 operator>(const vint4 &a, const vint4 &b)
Definition: simd.h:4718
GLdouble n
Definition: glcorearb.h:2008
GLint GLint GLsizei GLint GLenum GLenum type
Definition: glcorearb.h:108
Coord Abs(const Coord &xyz)
Definition: Coord.h:518
BBox< Coord > CoordBBox
Definition: NanoVDB.h:2516
void rasterizeSpheres(const ParticleListT &pa)
Rasterize each particle as a sphere with the particle's position and radius.
typename std::conditional< DisableT::value, size_t, AttributeT >::type AttType
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
void setRmax(Real Rmax)
Set the largest radius allowed in voxel units.
float Sqrt(float x)
Return the square root of a floating-point value.
Definition: Math.h:761
Defined various multi-threaded utility functions for trees.
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition: LeafManager.h:85
bool operator<(const GU_TetrahedronFacet &a, const GU_TetrahedronFacet &b)
GLuint id
Definition: glcorearb.h:655
#define OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN
Definition: Math.h:48
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
typename SdfGridT::template ValueConverter< AttType >::Type AttGridType
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1222
GA_API const UT_StringHolder transform
GLint GLenum GLint x
Definition: glcorearb.h:409
IMATH_HOSTDEVICE constexpr Quat< T > operator+(const Quat< T > &q1, const Quat< T > &q2) IMATH_NOEXCEPT
Quaterion addition.
Definition: ImathQuat.h:905
size_t getMinCount() const
Return the number of particles that were ignored because they were smaller than the minimum radius...
#define OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
Definition: Platform.h:140
Propagate the signs of distance values from the active voxels in the narrow band to the inactive valu...
bool ignoredParticles() const
Return true if any particles were ignored due to their size.
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 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...
void setRmin(Real Rmin)
Set the smallest radius allowed in voxel units.
void rasterizeTrails(const ParticleListT &pa, Real delta=1.0)
Rasterize each particle as a trail comprising the CSG union of spheres of decreasing radius...
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...
LeafData & operator=(const LeafData &)=delete
ImageBuf OIIO_API max(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
GA_API const UT_StringHolder N
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.
#define SIZE
Definition: simple.C:41
#define OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
Definition: Platform.h:141
int Floor(float x)
Return the floor of x.
Definition: Math.h:848
GLubyte GLubyte GLubyte GLubyte w
Definition: glcorearb.h:857
Tag dispatch class that distinguishes topology copy constructors from deep copy constructors.
Definition: Types.h:683
A LeafManager manages a linear array of pointers to a given tree's leaf nodes, as well as optional au...
GLboolean r
Definition: glcorearb.h:1222
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
void OIIO_UTIL_API split(string_view str, std::vector< string_view > &result, string_view sep=string_view(), int maxsplit=-1)
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
typename std::is_void< AttributeT >::type DisableT
bool wasInterrupted(T *i, int percent=-1)
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:119
bool ValueType
Definition: NanoVDB.h:5729
Real getRmin() const
Return the smallest radius allowed in voxel units.
const Type & Max(const Type &a, const Type &b)
Return the maximum of two values.
Definition: Math.h:595
GLint GLsizei count
Definition: glcorearb.h:405
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
auto join(It begin, Sentinel end, string_view sep) -> join_view< It, Sentinel >
Definition: format.h:4489
#define OPENVDB_NO_FP_EQUALITY_WARNING_END
Definition: Math.h:49