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