| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright Contributors to the OpenVDB Project | ||
| 2 | // SPDX-License-Identifier: MPL-2.0 | ||
| 3 | // | ||
| 4 | /// @author Nick Avramoussis | ||
| 5 | /// | ||
| 6 | /// @file PointRasterizeSDFImpl.h | ||
| 7 | /// | ||
| 8 | |||
| 9 | #ifndef OPENVDB_POINTS_RASTERIZE_SDF_IMPL_HAS_BEEN_INCLUDED | ||
| 10 | #define OPENVDB_POINTS_RASTERIZE_SDF_IMPL_HAS_BEEN_INCLUDED | ||
| 11 | |||
| 12 | namespace openvdb { | ||
| 13 | OPENVDB_USE_VERSION_NAMESPACE | ||
| 14 | namespace OPENVDB_VERSION_NAME { | ||
| 15 | namespace points { | ||
| 16 | |||
| 17 | /// @cond OPENVDB_DOCS_INTERNAL | ||
| 18 | |||
| 19 | namespace rasterize_sdf_internal | ||
| 20 | { | ||
| 21 | |||
| 22 | /// @brief Define a fixed index space radius for point rasterization | ||
| 23 | template <typename ValueT> | ||
| 24 | struct FixedRadius | ||
| 25 | { | ||
| 26 | 4600 | FixedRadius(const ValueT ris) : mR(ris) {} | |
| 27 | inline void reset(const PointDataTree::LeafNodeType&) const {} | ||
| 28 | inline const FixedRadius& eval(const Index) const { return *this; } | ||
| 29 | 160008 | inline ValueT get() const { return mR; } | |
| 30 | private: | ||
| 31 | const ValueT mR; | ||
| 32 | }; | ||
| 33 | |||
| 34 | /// @brief Define a fixed narrow band radius for point rasterization | ||
| 35 | /// @details Pass in an index space radius (relative to a PointDataGrid voxel | ||
| 36 | /// size) and the desired half band width of the target surface. The minimum | ||
| 37 | /// radius of influence is clamped to zero. | ||
| 38 | template <typename ValueT> | ||
| 39 | struct FixedBandRadius : public FixedRadius<ValueT> | ||
| 40 | { | ||
| 41 | 1370 | FixedBandRadius(const ValueT ris, const ValueT hb) | |
| 42 | : FixedRadius<ValueT>(ris) | ||
| 43 | 2753 | , mMinSearchIS(math::Max(ValueT(0.0), ris - hb)) | |
| 44 | 1383 | , mMaxSearchIS(ris + hb) | |
| 45 | 1370 | , mMinSearchSqIS(mMinSearchIS*mMinSearchIS) | |
| 46 | 21 | , mMaxSearchSqIS(mMaxSearchIS*mMaxSearchIS) {} | |
| 47 | |||
| 48 | inline void reset(const PointDataTree::LeafNodeType&) const {} | ||
| 49 | inline const FixedBandRadius& eval(const Index) const { return *this; } | ||
| 50 | inline ValueT min() const { return mMinSearchIS; } | ||
| 51 | 2836 | inline ValueT minSq() const { return mMinSearchSqIS; } | |
| 52 | 2328 | inline ValueT max() const { return mMaxSearchIS; } | |
| 53 | 2836 | inline ValueT maxSq() const { return mMaxSearchSqIS; } | |
| 54 | private: | ||
| 55 | const ValueT mMinSearchIS, mMaxSearchIS; | ||
| 56 | const ValueT mMinSearchSqIS, mMaxSearchSqIS; | ||
| 57 | }; | ||
| 58 | |||
| 59 | /// @brief A varying per point radius with an optional scale | ||
| 60 | template <typename ValueT, typename CodecT = UnknownCodec> | ||
| 61 | 8 | struct VaryingRadius | |
| 62 | { | ||
| 63 | using RadiusHandleT = AttributeHandle<ValueT, CodecT>; | ||
| 64 | 19 | VaryingRadius(const size_t ridx, const ValueT scale = 1.0) | |
| 65 |
4/8✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
|
8 | : mRIdx(ridx), mRHandle(), mScale(scale) {} |
| 66 | 175 | VaryingRadius(const VaryingRadius& other) | |
| 67 |
2/8✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 10 taken 5 times.
✗ Branch 11 not taken.
|
8 | : mRIdx(other.mRIdx), mRHandle(), mScale(other.mScale) {} |
| 68 | |||
| 69 | 3070 | inline void reset(const PointDataTree::LeafNodeType& leaf) | |
| 70 | { | ||
| 71 |
1/2✓ Branch 3 taken 3070 times.
✗ Branch 4 not taken.
|
3070 | mRHandle.reset(new RadiusHandleT(leaf.constAttributeArray(mRIdx))); |
| 72 | 3070 | } | |
| 73 | |||
| 74 | /// @brief Compute a fixed radius for a specific point | ||
| 75 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3219 times.
|
3219 | inline const FixedRadius<ValueT> eval(const Index id) const |
| 76 | { | ||
| 77 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3219 times.
|
3219 | assert(mRHandle); |
| 78 | 3219 | return FixedRadius<ValueT>(mRHandle->get(id) * mScale); | |
| 79 | } | ||
| 80 | |||
| 81 | private: | ||
| 82 | const size_t mRIdx; | ||
| 83 | typename RadiusHandleT::UniquePtr mRHandle; | ||
| 84 | const ValueT mScale; | ||
| 85 | }; | ||
| 86 | |||
| 87 | /// @brief A varying per point narrow band radius with an optional scale | ||
| 88 | template <typename ValueT, typename CodecT = UnknownCodec> | ||
| 89 |
2/8✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
|
22 | struct VaryingBandRadius : public VaryingRadius<ValueT, CodecT> |
| 90 | { | ||
| 91 | using BaseT = VaryingRadius<ValueT, CodecT>; | ||
| 92 | 11 | VaryingBandRadius(const size_t ridx, const ValueT halfband, | |
| 93 | const ValueT scale = 1.0) | ||
| 94 | 11 | : BaseT(ridx, scale), mHalfBand(halfband) {} | |
| 95 | |||
| 96 | 1349 | inline const FixedBandRadius<ValueT> eval(const Index id) const | |
| 97 | { | ||
| 98 | 1349 | const auto r = this->BaseT::eval(id).get(); | |
| 99 |
2/2✓ Branch 0 taken 753 times.
✓ Branch 1 taken 596 times.
|
1349 | return FixedBandRadius<ValueT>(ValueT(r), mHalfBand); |
| 100 | } | ||
| 101 | |||
| 102 | private: | ||
| 103 | const ValueT mHalfBand; | ||
| 104 | }; | ||
| 105 | |||
| 106 | /// @brief Base class for SDF transfers which consolidates member data and | ||
| 107 | /// some required interface methods. | ||
| 108 | /// @note Derives from TransformTransfer for automatic transformation support | ||
| 109 | /// and VolumeTransfer<...T> for automatic buffer setup | ||
| 110 | template <typename SdfT, | ||
| 111 | typename PositionCodecT, | ||
| 112 | typename RadiusType, | ||
| 113 | bool CPG> | ||
| 114 | struct SignedDistanceFieldTransfer : | ||
| 115 | public TransformTransfer, | ||
| 116 | public std::conditional<CPG, | ||
| 117 | VolumeTransfer<typename SdfT::TreeType, Int64Tree>, | ||
| 118 | VolumeTransfer<typename SdfT::TreeType>>::type | ||
| 119 | { | ||
| 120 | using TreeT = typename SdfT::TreeType; | ||
| 121 | using ValueT = typename TreeT::ValueType; | ||
| 122 | static_assert(std::is_floating_point<ValueT>::value, | ||
| 123 | "Spherical transfer only supports floating point values."); | ||
| 124 | static_assert(!std::is_reference<RadiusType>::value && !std::is_pointer<RadiusType>::value, | ||
| 125 | "Templated radius type must not be a reference or pointer"); | ||
| 126 | |||
| 127 | using VolumeTransferT = typename std::conditional<CPG, | ||
| 128 | VolumeTransfer<TreeT, Int64Tree>, | ||
| 129 | VolumeTransfer<TreeT>>::type; | ||
| 130 | |||
| 131 | using PositionHandleT = AttributeHandle<Vec3f, PositionCodecT>; | ||
| 132 | |||
| 133 | // typically the max radius of all points rounded up | ||
| 134 | 2395 | inline Int32 range(const Coord&, size_t) const { return Int32(mMaxKernelWidth); } | |
| 135 | |||
| 136 | 16398 | inline bool startPointLeaf(const PointDataTree::LeafNodeType& leaf) | |
| 137 | { | ||
| 138 |
1/2✓ Branch 3 taken 8199 times.
✗ Branch 4 not taken.
|
16398 | mPosition.reset(new PositionHandleT(leaf.constAttributeArray(mPIdx))); |
| 139 | 6140 | mRadius.reset(leaf); | |
| 140 | // if CPG, store leaf id in upper 32 bits of mask | ||
| 141 | 3138 | if (CPG) mPLeafMask = (Index64(mIds->find(&leaf)->second) << 32); | |
| 142 | 16398 | return true; | |
| 143 | } | ||
| 144 | |||
| 145 | protected: | ||
| 146 | /// @brief Constructor to use when a closet point grid is not in use | ||
| 147 | template <bool EnableT = CPG> | ||
| 148 | 24 | SignedDistanceFieldTransfer(const size_t pidx, | |
| 149 | const size_t width, | ||
| 150 | const RadiusType& rt, | ||
| 151 | const math::Transform& source, | ||
| 152 | SdfT& surface, | ||
| 153 | Int64Tree* cpg, | ||
| 154 | const std::unordered_map<const PointDataTree::LeafNodeType*, Index>* ids, | ||
| 155 | typename std::enable_if<EnableT>::type* = 0) | ||
| 156 | : TransformTransfer(source, surface.transform()) | ||
| 157 | , VolumeTransferT(&(surface.tree()), cpg) | ||
| 158 | , mPIdx(pidx) | ||
| 159 | , mPosition() | ||
| 160 | , mMaxKernelWidth(width) | ||
| 161 | , mRadius(rt) | ||
| 162 | , mBackground(surface.background()) | ||
| 163 | 24 | , mDx(surface.voxelSize()[0]) | |
| 164 | , mIds(ids) | ||
| 165 |
1/2✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
24 | , mPLeafMask(0) { |
| 166 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
24 | assert(cpg && ids); |
| 167 | } | ||
| 168 | |||
| 169 | /// @brief Constructor to use when a closet point grid is in use | ||
| 170 | template <bool EnableT = CPG> | ||
| 171 |
1/2✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
|
74 | SignedDistanceFieldTransfer(const size_t pidx, |
| 172 | const size_t width, | ||
| 173 | const RadiusType& rt, | ||
| 174 | const math::Transform& source, | ||
| 175 | SdfT& surface, | ||
| 176 | Int64Tree*, | ||
| 177 | const std::unordered_map<const PointDataTree::LeafNodeType*, Index>*, | ||
| 178 | typename std::enable_if<!EnableT>::type* = 0) | ||
| 179 | : TransformTransfer(source, surface.transform()) | ||
| 180 | , VolumeTransferT(surface.tree()) | ||
| 181 | , mPIdx(pidx) | ||
| 182 | , mPosition() | ||
| 183 | , mMaxKernelWidth(width) | ||
| 184 | , mRadius(rt) | ||
| 185 | , mBackground(surface.background()) | ||
| 186 | 74 | , mDx(surface.voxelSize()[0]) | |
| 187 | , mIds(nullptr) | ||
| 188 |
1/2✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
|
148 | , mPLeafMask(0) {} |
| 189 | |||
| 190 | 615 | SignedDistanceFieldTransfer(const SignedDistanceFieldTransfer& other) | |
| 191 | : TransformTransfer(other) | ||
| 192 | , VolumeTransferT(other) | ||
| 193 | 615 | , mPIdx(other.mPIdx) | |
| 194 | , mPosition() | ||
| 195 | 615 | , mMaxKernelWidth(other.mMaxKernelWidth) | |
| 196 | , mRadius(other.mRadius) | ||
| 197 | 615 | , mBackground(other.mBackground) | |
| 198 | 615 | , mDx(other.mDx) | |
| 199 | 615 | , mIds(other.mIds) | |
| 200 | 615 | , mPLeafMask(0) {} | |
| 201 | |||
| 202 | protected: | ||
| 203 | const size_t mPIdx; | ||
| 204 | typename PositionHandleT::UniquePtr mPosition; | ||
| 205 | const size_t mMaxKernelWidth; | ||
| 206 | RadiusType mRadius; | ||
| 207 | const ValueT mBackground; | ||
| 208 | const double mDx; | ||
| 209 | const std::unordered_map<const PointDataTree::LeafNodeType*, Index>* mIds; | ||
| 210 | Index64 mPLeafMask; | ||
| 211 | }; | ||
| 212 | |||
| 213 | /// @brief The transfer implementation for spherical stamping of narrow band | ||
| 214 | /// radius values. | ||
| 215 | template <typename SdfT, | ||
| 216 | typename PositionCodecT, | ||
| 217 | typename RadiusType, | ||
| 218 | bool CPG> | ||
| 219 | 17 | struct SphericalTransfer : | |
| 220 | public SignedDistanceFieldTransfer<SdfT, PositionCodecT, RadiusType, CPG> | ||
| 221 | { | ||
| 222 | using BaseT = SignedDistanceFieldTransfer<SdfT, PositionCodecT, RadiusType, CPG>; | ||
| 223 | using typename BaseT::TreeT; | ||
| 224 | using typename BaseT::ValueT; | ||
| 225 | static const Index DIM = TreeT::LeafNodeType::DIM; | ||
| 226 | static const Index LOG2DIM = TreeT::LeafNodeType::LOG2DIM; | ||
| 227 | // The precision of the kernel arithmetic | ||
| 228 | using RealT = double; | ||
| 229 | |||
| 230 | 30 | SphericalTransfer(const size_t pidx, | |
| 231 | const size_t width, | ||
| 232 | const RadiusType& rt, | ||
| 233 | const math::Transform& source, | ||
| 234 | SdfT& surface, | ||
| 235 | Int64Tree* cpg = nullptr, | ||
| 236 | const std::unordered_map<const PointDataTree::LeafNodeType*, Index>* ids = nullptr) | ||
| 237 | 30 | : BaseT(pidx, width, rt, source, surface, cpg, ids) {} | |
| 238 | |||
| 239 | /// @brief For each point, stamp a sphere with a given radius by running | ||
| 240 | /// over all intersecting voxels and calculating if this point is closer | ||
| 241 | /// than the currently held distance value. Note that the default value | ||
| 242 | /// of the surface buffer should be the background value of the surface. | ||
| 243 | 7354 | inline void rasterizePoint(const Coord& ijk, | |
| 244 | const Index id, | ||
| 245 | const CoordBBox& bounds) | ||
| 246 | { | ||
| 247 | // @note GCC 6.3 warns here in optimised builds | ||
| 248 | #if defined(__GNUC__) && !defined(__clang__) | ||
| 249 | #pragma GCC diagnostic push | ||
| 250 | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" | ||
| 251 | #endif | ||
| 252 | 7354 | Vec3d P = ijk.asVec3d() + Vec3d(this->mPosition->get(id)); | |
| 253 | #if defined(__GNUC__) && !defined(__clang__) | ||
| 254 | #pragma GCC diagnostic pop | ||
| 255 | #endif | ||
| 256 | 7354 | P = this->transformSourceToTarget(P); | |
| 257 | |||
| 258 | 2698 | const auto& r = this->mRadius.eval(id); | |
| 259 | 2698 | const RealT max = r.max(); | |
| 260 | |||
| 261 | 7354 | CoordBBox intersectBox(Coord::floor(P - max), Coord::ceil(P + max)); | |
| 262 | 7354 | intersectBox.intersect(bounds); | |
| 263 | 1682 | if (intersectBox.empty()) return; | |
| 264 | |||
| 265 | auto* const data = this->template buffer<0>(); | ||
| 266 | auto* const cpg = CPG ? this->template buffer<CPG ? 1 : 0>() : nullptr; | ||
| 267 | auto& mask = *(this->template mask<0>()); | ||
| 268 | |||
| 269 | // If min2 == 0.0, then the index space radius is equal to or less than | ||
| 270 | // the desired half band. In this case each sphere interior always needs | ||
| 271 | // to be filled with distance values as we won't ever reach the negative | ||
| 272 | // background value. If, however, a point overlaps a voxel coord exactly, | ||
| 273 | // x2y2z2 will be 0.0. Forcing min2 to be less than zero here avoids | ||
| 274 | // incorrectly setting these voxels to inactive -background values as | ||
| 275 | // x2y2z2 will never be < 0.0. We still want the lteq logic in the | ||
| 276 | // (x2y2z2 <= min2) check as this is valid when min2 > 0.0. | ||
| 277 |
2/2✓ Branch 0 taken 1484 times.
✓ Branch 1 taken 1352 times.
|
5672 | const RealT min2 = r.minSq() == 0.0 ? -1.0 : r.minSq(); |
| 278 | 1948 | const RealT max2 = r.maxSq(); | |
| 279 | |||
| 280 | const Coord& a(intersectBox.min()); | ||
| 281 | const Coord& b(intersectBox.max()); | ||
| 282 |
2/2✓ Branch 0 taken 15437 times.
✓ Branch 1 taken 2836 times.
|
36546 | for (Coord c = a; c.x() <= b.x(); ++c.x()) { |
| 283 | 30874 | const RealT x2 = static_cast<RealT>(math::Pow2(c.x() - P[0])); | |
| 284 | 30874 | const Index i = ((c.x() & (DIM-1u)) << 2*LOG2DIM); // unsigned bit shift mult | |
| 285 |
2/2✓ Branch 0 taken 86267 times.
✓ Branch 1 taken 15437 times.
|
203408 | for (c.y() = a.y(); c.y() <= b.y(); ++c.y()) { |
| 286 | 172534 | const RealT x2y2 = static_cast<RealT>(x2 + math::Pow2(c.y() - P[1])); | |
| 287 | 172534 | const Index ij = i + ((c.y() & (DIM-1u)) << LOG2DIM); | |
| 288 |
2/2✓ Branch 0 taken 487347 times.
✓ Branch 1 taken 86267 times.
|
1147228 | for (c.z() = a.z(); c.z() <= b.z(); ++c.z()) { |
| 289 | 974694 | const Index offset = ij + /*k*/(c.z() & (DIM-1u)); | |
| 290 |
2/2✓ Branch 1 taken 13932 times.
✓ Branch 2 taken 473415 times.
|
974694 | if (!mask.isOn(offset)) continue; // inside existing level set or not in range |
| 291 | |||
| 292 |
2/2✓ Branch 0 taken 319444 times.
✓ Branch 1 taken 153971 times.
|
946830 | const RealT x2y2z2 = static_cast<RealT>(x2y2 + math::Pow2(c.z() - P[2])); |
| 293 |
2/2✓ Branch 0 taken 319444 times.
✓ Branch 1 taken 153971 times.
|
946830 | if (x2y2z2 >= max2) continue; //outside narrow band of particle in positive direction |
| 294 |
2/2✓ Branch 0 taken 4219 times.
✓ Branch 1 taken 149752 times.
|
307942 | if (x2y2z2 <= min2) { //outside narrow band of the particle in negative direction. can disable this to fill interior |
| 295 | 8438 | data[offset] = -(this->mBackground); | |
| 296 | 8438 | mask.setOff(offset); | |
| 297 | 8438 | continue; | |
| 298 | } | ||
| 299 | |||
| 300 |
2/2✓ Branch 0 taken 94168 times.
✓ Branch 1 taken 55584 times.
|
299504 | const ValueT d = ValueT(this->mDx * (math::Sqrt(x2y2z2) - r.get())); // back to world space |
| 301 | 299504 | ValueT& v = data[offset]; | |
| 302 |
2/2✓ Branch 0 taken 94168 times.
✓ Branch 1 taken 55584 times.
|
299504 | if (d < v) { |
| 303 | 188336 | v = d; | |
| 304 | OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN | ||
| 305 | 23628 | if (CPG) cpg[offset] = Int64(this->mPLeafMask | Index64(id)); | |
| 306 | OPENVDB_NO_TYPE_CONVERSION_WARNING_END | ||
| 307 | // transfer attributes - we can't use this here as the exposed | ||
| 308 | // function signatures take vector of attributes (i.e. an unbounded | ||
| 309 | // size). If we instead clamped the attribute transfer to a fixed | ||
| 310 | // amount of attributes we could get rid of the closest point logic | ||
| 311 | // entirely. @todo consider adding another signature for increased | ||
| 312 | // performance | ||
| 313 | // this->foreach([&](auto* buffer, const size_t idx) { | ||
| 314 | // using Type = typename std::remove_pointer<decltype(buffer)>::type; | ||
| 315 | // buffer[offset] = mAttributes.template get<Type>(idx); | ||
| 316 | // }) | ||
| 317 | } | ||
| 318 | } | ||
| 319 | } | ||
| 320 | } // outer sdf voxel | ||
| 321 | } // point idx | ||
| 322 | |||
| 323 | /// @brief Allow early termination if all voxels in the surface have been | ||
| 324 | /// deactivated (all interior) | ||
| 325 | inline bool endPointLeaf(const PointDataTree::LeafNodeType&) | ||
| 326 | { | ||
| 327 | // If the mask is off, terminate rasterization | ||
| 328 | return !(this->template mask<0>()->isOff()); | ||
| 329 | } | ||
| 330 | |||
| 331 | 2666 | inline bool finalize(const Coord&, const size_t) | |
| 332 | { | ||
| 333 | // loop over voxels in the outer cube diagonals which won't have been | ||
| 334 | // hit by point rasterizations - these will be on because of the mask | ||
| 335 | // fill technique and need to be turned off. | ||
| 336 | auto& mask = *(this->template mask<0>()); | ||
| 337 | auto* const data = this->template buffer<0>(); | ||
| 338 |
2/2✓ Branch 1 taken 291426 times.
✓ Branch 2 taken 1333 times.
|
588184 | for (auto iter = mask.beginOn(); iter; ++iter) { |
| 339 |
2/2✓ Branch 0 taken 222447 times.
✓ Branch 1 taken 68979 times.
|
582852 | if (data[iter.pos()] == this->mBackground) mask.setOff(iter.pos()); |
| 340 | } | ||
| 341 | // apply sdf mask to other grids | ||
| 342 | if (CPG) *(this->template mask<CPG ? 1 : 0>()) = mask; | ||
| 343 | 2666 | return true; | |
| 344 | } | ||
| 345 | }; | ||
| 346 | |||
| 347 | /// @brief The transfer implementation for averaging of positions followed by | ||
| 348 | /// spherical stamping. | ||
| 349 | template <typename SdfT, | ||
| 350 | typename PositionCodecT, | ||
| 351 | typename RadiusType, | ||
| 352 | bool CPG> | ||
| 353 | struct AveragePositionTransfer : | ||
| 354 | public SignedDistanceFieldTransfer<SdfT, PositionCodecT, RadiusType, CPG> | ||
| 355 | { | ||
| 356 | using BaseT = SignedDistanceFieldTransfer<SdfT, PositionCodecT, RadiusType, CPG>; | ||
| 357 | using typename BaseT::TreeT; | ||
| 358 | using typename BaseT::ValueT; | ||
| 359 | |||
| 360 | using VolumeTransferT = typename std::conditional<CPG, | ||
| 361 | VolumeTransfer<typename SdfT::TreeType, Int64Tree>, | ||
| 362 | VolumeTransfer<typename SdfT::TreeType>>::type; | ||
| 363 | |||
| 364 | static const Index DIM = TreeT::LeafNodeType::DIM; | ||
| 365 | static const Index LOG2DIM = TreeT::LeafNodeType::LOG2DIM; | ||
| 366 | static const Index NUM_VALUES = TreeT::LeafNodeType::NUM_VALUES; | ||
| 367 | // The precision of the kernel arithmetic | ||
| 368 | using RealT = double; | ||
| 369 | |||
| 370 | struct PosRadPair | ||
| 371 | { | ||
| 372 | template <typename S> inline void addP(const math::Vec3<S>& v) { P += v; } | ||
| 373 | 222407 | template <typename S> inline void addR(const S r) { R += r; } | |
| 374 | 101607 | template <typename S> inline void multR(const S w) { R *= w; } | |
| 375 | template <typename S> inline void multP(const S w) { P *= w; } | ||
| 376 | 203214 | inline RealT length() const { return P.length() - R; } | |
| 377 | math::Vec3<RealT> P = math::Vec3<RealT>(0.0); | ||
| 378 | RealT R = 0.0; | ||
| 379 | }; | ||
| 380 | |||
| 381 | 19 | AveragePositionTransfer(const size_t pidx, | |
| 382 | const size_t width, | ||
| 383 | const RadiusType& rt, | ||
| 384 | const RealT search, | ||
| 385 | const math::Transform& source, | ||
| 386 | SdfT& surface, | ||
| 387 | Int64Tree* cpg = nullptr, | ||
| 388 | const std::unordered_map<const PointDataTree::LeafNodeType*, Index>* ids = nullptr) | ||
| 389 | : BaseT(pidx, width, rt, source, surface, cpg, ids) | ||
| 390 | , mMaxSearchIS(search) | ||
| 391 | 19 | , mMaxSearchSqIS(search*search) | |
| 392 | , mWeights() | ||
| 393 |
10/64✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✓ Branch 31 taken 1 times.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✓ Branch 43 taken 1 times.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✓ Branch 49 taken 4 times.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✓ Branch 55 taken 1 times.
✗ Branch 56 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✓ Branch 67 taken 1 times.
✗ Branch 68 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✓ Branch 79 taken 1 times.
✗ Branch 80 not taken.
✗ Branch 82 not taken.
✗ Branch 83 not taken.
✓ Branch 85 taken 7 times.
✗ Branch 86 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✓ Branch 91 taken 1 times.
✗ Branch 92 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
|
19 | , mDist() {} |
| 394 | |||
| 395 | 19 | AveragePositionTransfer(const AveragePositionTransfer& other) | |
| 396 | : BaseT(other) | ||
| 397 | 19 | , mMaxSearchIS(other.mMaxSearchIS) | |
| 398 | 19 | , mMaxSearchSqIS(other.mMaxSearchSqIS) | |
| 399 | , mWeights() | ||
| 400 | 6 | , mDist() {} | |
| 401 | |||
| 402 | 2124 | inline void initialize(const Coord& origin, const size_t idx, const CoordBBox& bounds) | |
| 403 | { | ||
| 404 | // init buffers | ||
| 405 | 2124 | this->BaseT::initialize(origin, idx, bounds); | |
| 406 |
2/2✓ Branch 0 taken 2124 times.
✓ Branch 1 taken 1062 times.
|
6372 | mWeights.assign(NUM_VALUES, PosRadPair()); |
| 407 | 768 | if (CPG) mDist.assign(NUM_VALUES, std::numeric_limits<float>::max()); | |
| 408 | // We use the surface buffer to store the intermediate weights as | ||
| 409 | // defined by the sum of k(|x−xj|/R), where k(s) = max(0,(1−s^2)^3) | ||
| 410 | // and R is the maximum search distance. The active buffer currently | ||
| 411 | // holds background values. We could simply subtract the background away | ||
| 412 | // from the final result - however if the background value increases | ||
| 413 | // beyond 1, progressively larger floating point instabilities can be | ||
| 414 | // observed with the weight calculation. Instead, reset all active | ||
| 415 | // values to zero | ||
| 416 | // @todo The surface buffer may not be at RealT precision. Should we | ||
| 417 | // enforce this by storing the weights in another vector? | ||
| 418 | auto* const data = this->template buffer<0>(); | ||
| 419 | const auto& mask = *(this->template mask<0>()); | ||
| 420 |
2/2✓ Branch 1 taken 216409 times.
✓ Branch 2 taken 1062 times.
|
437066 | for (auto iter = mask.beginOn(); iter; ++iter) { |
| 421 | 432818 | data[iter.pos()] = ValueT(0); | |
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | 5478 | inline void rasterizePoint(const Coord& ijk, | |
| 426 | const Index id, | ||
| 427 | const CoordBBox& bounds) | ||
| 428 | { | ||
| 429 | #if defined(__GNUC__) && !defined(__clang__) | ||
| 430 | #pragma GCC diagnostic push | ||
| 431 | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" | ||
| 432 | #endif | ||
| 433 | 10956 | const Vec3d PWS = this->sourceTransform().indexToWorld(ijk.asVec3d() + Vec3d(this->mPosition->get(id))); | |
| 434 | #if defined(__GNUC__) && !defined(__clang__) | ||
| 435 | #pragma GCC diagnostic pop | ||
| 436 | #endif | ||
| 437 | const Vec3d P = this->targetTransform().worldToIndex(PWS); | ||
| 438 | |||
| 439 | 5478 | CoordBBox intersectBox(Coord::floor(P - mMaxSearchIS), | |
| 440 | 5478 | Coord::ceil(P + mMaxSearchIS)); | |
| 441 | 5478 | intersectBox.intersect(bounds); | |
| 442 | ✗ | if (intersectBox.empty()) return; | |
| 443 | |||
| 444 | auto* const data = this->template buffer<0>(); | ||
| 445 | auto* const cpg = CPG ? this->template buffer<CPG ? 1 : 0>() : nullptr; | ||
| 446 | const auto& mask = *(this->template mask<0>()); | ||
| 447 | |||
| 448 | // index space radius | ||
| 449 | 3740 | const auto& r = this->mRadius.eval(id); | |
| 450 | 3740 | const RealT rad = r.get(); | |
| 451 | 5478 | const RealT invsq = 1.0 / mMaxSearchSqIS; | |
| 452 | |||
| 453 | const Coord& a(intersectBox.min()); | ||
| 454 | const Coord& b(intersectBox.max()); | ||
| 455 |
2/2✓ Branch 0 taken 14109 times.
✓ Branch 1 taken 2739 times.
|
33696 | for (Coord c = a; c.x() <= b.x(); ++c.x()) { |
| 456 | 28218 | const RealT x2 = static_cast<RealT>(math::Pow2(c.x() - P[0])); | |
| 457 | 28218 | const Index i = ((c.x() & (DIM-1u)) << 2*LOG2DIM); // unsigned bit shift mult | |
| 458 |
2/2✓ Branch 0 taken 74889 times.
✓ Branch 1 taken 14109 times.
|
177996 | for (c.y() = a.y(); c.y() <= b.y(); ++c.y()) { |
| 459 | 149778 | const RealT x2y2 = static_cast<RealT>(x2 + math::Pow2(c.y() - P[1])); | |
| 460 | 149778 | const Index ij = i + ((c.y() & (DIM-1u)) << LOG2DIM); | |
| 461 |
2/2✓ Branch 0 taken 410914 times.
✓ Branch 1 taken 74889 times.
|
971606 | for (c.z() = a.z(); c.z() <= b.z(); ++c.z()) { |
| 462 |
2/2✓ Branch 0 taken 182113 times.
✓ Branch 1 taken 228801 times.
|
821828 | RealT x2y2z2 = static_cast<RealT>(x2y2 + math::Pow2(c.z() - P[2])); |
| 463 |
2/2✓ Branch 0 taken 182113 times.
✓ Branch 1 taken 228801 times.
|
821828 | if (x2y2z2 >= mMaxSearchSqIS) continue; //outside search distance |
| 464 | 457602 | const Index offset = ij + /*k*/(c.z() & (DIM-1u)); | |
| 465 |
2/2✓ Branch 1 taken 6394 times.
✓ Branch 2 taken 222407 times.
|
457602 | if (!mask.isOn(offset)) continue; // inside existing level set or not in range |
| 466 | |||
| 467 | // @note this algorithm is unable to deactivate voxels within | ||
| 468 | // a computed narrow band during rasterization as all points must | ||
| 469 | // visit their affected voxels. | ||
| 470 | |||
| 471 | if (CPG) { | ||
| 472 | // CPG still computed directly with each individual point | ||
| 473 | // @note Because voxels can't be discarded, it may be faster to | ||
| 474 | // do this as a post process (and avoid the sqrt per lookup) | ||
| 475 | // @note No need to scale back to world space | ||
| 476 | 201216 | const float dist = static_cast<float>(math::Sqrt(x2y2z2) - r.get()); | |
| 477 |
2/2✓ Branch 0 taken 51999 times.
✓ Branch 1 taken 48609 times.
|
201216 | auto& d = mDist[offset]; |
| 478 |
2/2✓ Branch 0 taken 51999 times.
✓ Branch 1 taken 48609 times.
|
201216 | if (dist < d) { |
| 479 | 103998 | d = dist; | |
| 480 | OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN | ||
| 481 | 103998 | cpg[offset] = Int64(this->mPLeafMask | Index64(id)); | |
| 482 | OPENVDB_NO_TYPE_CONVERSION_WARNING_END | ||
| 483 | } | ||
| 484 | } | ||
| 485 | |||
| 486 | 444814 | x2y2z2 *= invsq; // x2y2z2 = (x - xi) / R | |
| 487 | // k(s) = max(0,(1−s^2)^3). note that the max is unecessary | ||
| 488 | // as we early terminate above with x2y2z2 >= mMaxSearchSqIS | ||
| 489 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 222407 times.
|
444814 | x2y2z2 = math::Pow3(1.0 - x2y2z2); |
| 490 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 222407 times.
|
444814 | assert(x2y2z2 >= 0.0); |
| 491 | // @todo The surface buffer may not be at RealT precision. Should we | ||
| 492 | // enforce this by storing the weights in another vector? | ||
| 493 | OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN | ||
| 494 | 444814 | data[offset] += x2y2z2; | |
| 495 | OPENVDB_NO_TYPE_CONVERSION_WARNING_END | ||
| 496 | auto& wt = mWeights[offset]; | ||
| 497 | 444814 | wt.addP(PWS * x2y2z2); | |
| 498 | 444814 | wt.addR(rad * x2y2z2); | |
| 499 | } | ||
| 500 | } | ||
| 501 | } // outer sdf voxel | ||
| 502 | } // point idx | ||
| 503 | |||
| 504 | inline bool endPointLeaf(const PointDataTree::LeafNodeType&) { return true; } | ||
| 505 | |||
| 506 | 2124 | inline bool finalize(const Coord& origin, const size_t) | |
| 507 | { | ||
| 508 | auto& mask = *(this->template mask<0>()); | ||
| 509 | auto* const data = this->template buffer<0>(); | ||
| 510 | |||
| 511 |
2/2✓ Branch 1 taken 216409 times.
✓ Branch 2 taken 1062 times.
|
437066 | for (auto iter = mask.beginOn(); iter; ++iter) { |
| 512 | const Index idx = iter.pos(); | ||
| 513 | 432818 | auto& w = data[idx]; | |
| 514 | // if background, voxel was out of range. Guaranteed to be outside as | ||
| 515 | // all interior voxels will have at least a single point contribution | ||
| 516 |
2/2✓ Branch 0 taken 114802 times.
✓ Branch 1 taken 101607 times.
|
432818 | if (w == 0.0f) { |
| 517 | 229604 | mask.setOff(idx); | |
| 518 | 229604 | w = this->mBackground; | |
| 519 | } | ||
| 520 | else { | ||
| 521 | 203214 | const Coord ijk = origin + TreeT::LeafNodeType::offsetToLocalCoord(idx); | |
| 522 | const Vec3d ws = this->targetTransform().indexToWorld(ijk); | ||
| 523 | 203214 | const RealT wi = RealT(1.0) / RealT(w); // wi | |
| 524 | auto& wt = mWeights[idx]; | ||
| 525 | wt.multP(wi); // sum of weighted positions | ||
| 526 | 203214 | wt.multR(wi * this->mDx); // sum of weighted radii (scale to ws) | |
| 527 | wt.addP(-ws); // (x - xi) (instead doing (-x + xi)) | ||
| 528 |
2/2✓ Branch 1 taken 63909 times.
✓ Branch 2 taken 37698 times.
|
203214 | w = static_cast<typename SdfT::ValueType>(wt.length()); // (x - xi) - r |
| 529 | // clamp active region and value range to requested narrow band | ||
| 530 |
2/2✓ Branch 0 taken 63909 times.
✓ Branch 1 taken 37698 times.
|
203214 | if (std::fabs(w) >= this->mBackground) { |
| 531 | 127818 | w = std::copysign(this->mBackground, w); | |
| 532 | 127818 | mask.setOff(idx); | |
| 533 | } | ||
| 534 | } | ||
| 535 | } | ||
| 536 | |||
| 537 | // apply sdf mask to other grids | ||
| 538 | if (CPG) *(this->template mask<CPG ? 1 : 0>()) = mask; | ||
| 539 | 2124 | return true; | |
| 540 | } | ||
| 541 | |||
| 542 | private: | ||
| 543 | const RealT mMaxSearchIS, mMaxSearchSqIS; | ||
| 544 | std::vector<PosRadPair> mWeights; | ||
| 545 | std::vector<float> mDist; | ||
| 546 | }; | ||
| 547 | |||
| 548 | /// @brief Base class for surfacing mask initialization | ||
| 549 | template <typename MaskTreeT = MaskTree, | ||
| 550 | typename InterrupterT = util::NullInterrupter> | ||
| 551 | struct SurfaceMaskOp | ||
| 552 | { | ||
| 553 | 56 | inline void join(SurfaceMaskOp& other) | |
| 554 | { | ||
| 555 |
2/2✓ Branch 2 taken 30 times.
✓ Branch 3 taken 26 times.
|
56 | if (mMask->leafCount() > other.mMask->leafCount()) { |
| 556 | mMask->topologyUnion(*other.mMask); | ||
| 557 | other.mMask.reset(); | ||
| 558 | } | ||
| 559 | else { | ||
| 560 | other.mMask->topologyUnion(*mMask); | ||
| 561 | mMask.reset(other.mMask.release()); | ||
| 562 | } | ||
| 563 | |||
| 564 |
2/2✓ Branch 2 taken 9 times.
✓ Branch 3 taken 47 times.
|
56 | if (mMaskOff->leafCount() > other.mMaskOff->leafCount()) { |
| 565 | mMaskOff->topologyUnion(*other.mMaskOff); | ||
| 566 | other.mMaskOff.reset(); | ||
| 567 | } | ||
| 568 | else { | ||
| 569 | other.mMaskOff->topologyUnion(*mMaskOff); | ||
| 570 | mMaskOff.reset(other.mMaskOff.release()); | ||
| 571 | } | ||
| 572 | 56 | } | |
| 573 | |||
| 574 | std::unique_ptr<MaskTreeT> mask() { return std::move(mMask); } | ||
| 575 | std::unique_ptr<MaskTreeT> maskoff() { return std::move(mMaskOff); } | ||
| 576 | |||
| 577 | protected: | ||
| 578 | 57 | SurfaceMaskOp(const math::Transform& points, | |
| 579 | const math::Transform& surface, | ||
| 580 | InterrupterT* interrupter = nullptr) | ||
| 581 | 57 | : mMask(new MaskTreeT) | |
| 582 |
1/2✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
|
57 | , mMaskOff(new MaskTreeT) |
| 583 | , mPointsTransform(points) | ||
| 584 | , mSurfaceTransform(surface) | ||
| 585 | , mInterrupter(interrupter) | ||
| 586 |
1/4✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
57 | , mVoxelOffset(static_cast<int>(math::RoundUp(points.voxelSize()[0] / |
| 587 |
1/2✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
|
114 | surface.voxelSize()[0]))) {} |
| 588 | |||
| 589 | 56 | SurfaceMaskOp(const SurfaceMaskOp& other) | |
| 590 | 56 | : mMask(new MaskTreeT) | |
| 591 |
1/2✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
|
56 | , mMaskOff(new MaskTreeT) |
| 592 | 56 | , mPointsTransform(other.mPointsTransform) | |
| 593 | 56 | , mSurfaceTransform(other.mSurfaceTransform) | |
| 594 | 56 | , mInterrupter(other.mInterrupter) | |
| 595 | 56 | , mVoxelOffset(other.mVoxelOffset) {} | |
| 596 | |||
| 597 | // @brief Sparse fill a tree with activated bounding boxes expanded from | ||
| 598 | // each active voxel. | ||
| 599 | // @note This method used to fill from each individual voxel. Whilst more | ||
| 600 | // accurate, this was slower in comparison to using the active node bounds. | ||
| 601 | // As the rasterization is so fast (discarding of voxels out of range) | ||
| 602 | // this overzealous activation results in far superior performance here. | ||
| 603 | template <typename LeafT> | ||
| 604 | 221 | inline void fill(const LeafT& leaf, const int dist, const bool active) | |
| 605 | { | ||
| 606 | 442 | auto doFill = [&](CoordBBox b) { | |
| 607 | 221 | b = mSurfaceTransform.worldToIndexCellCentered( | |
| 608 | 221 | mPointsTransform.indexToWorld(b)); | |
| 609 |
2/2✓ Branch 0 taken 189 times.
✓ Branch 1 taken 32 times.
|
221 | b.expand(dist); |
| 610 |
2/2✓ Branch 0 taken 189 times.
✓ Branch 1 taken 32 times.
|
410 | if (active) mMask->sparseFill(b, true, true); |
| 611 | 64 | else mMaskOff->sparseFill(b, true, true); | |
| 612 | }; | ||
| 613 | |||
| 614 | const auto& mask = leaf.getValueMask(); | ||
| 615 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 221 times.
|
221 | if (mask.isOn()) { |
| 616 | ✗ | doFill(leaf.getNodeBoundingBox()); | |
| 617 | } | ||
| 618 | else { | ||
| 619 | 221 | CoordBBox bounds; | |
| 620 |
2/2✓ Branch 1 taken 221 times.
✓ Branch 2 taken 221 times.
|
663 | for (auto iter = mask.beginOn(); iter; ++iter) { |
| 621 | 221 | bounds.expand(leaf.offsetToLocalCoord(iter.pos())); | |
| 622 | } | ||
| 623 | if (!bounds.empty()) { | ||
| 624 | bounds.translate(leaf.origin()); | ||
| 625 | 221 | doFill(bounds); | |
| 626 | } | ||
| 627 | } | ||
| 628 | 221 | } | |
| 629 | |||
| 630 | 189 | inline bool interrupted() | |
| 631 | { | ||
| 632 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 189 times.
|
189 | if (util::wasInterrupted(mInterrupter)) { |
| 633 | ✗ | thread::cancelGroupExecution(); | |
| 634 | ✗ | return true; | |
| 635 | } | ||
| 636 | return false; | ||
| 637 | } | ||
| 638 | |||
| 639 | protected: | ||
| 640 | std::unique_ptr<MaskTreeT> mMask; | ||
| 641 | std::unique_ptr<MaskTreeT> mMaskOff; | ||
| 642 | const math::Transform& mPointsTransform; | ||
| 643 | const math::Transform& mSurfaceTransform; | ||
| 644 | InterrupterT* mInterrupter; | ||
| 645 | // add the size of a single points voxel at the surface resolution | ||
| 646 | // to the distance limits - this is becasue we don't query exact point | ||
| 647 | // positions in SurfaceMaskOp, which means we have to | ||
| 648 | // actviate the "worst case scenario" bounds. The closer the points | ||
| 649 | // voxel size is to the target sdf voxel size the better the topology | ||
| 650 | // estimate | ||
| 651 | const int mVoxelOffset; | ||
| 652 | }; | ||
| 653 | |||
| 654 | /// @brief Initializes a fixed activity mask | ||
| 655 | template <typename MaskTreeT, | ||
| 656 | typename InterrupterT = util::NullInterrupter> | ||
| 657 | 73 | struct FixedSurfaceMaskOp | |
| 658 | : public SurfaceMaskOp<MaskTreeT, InterrupterT> | ||
| 659 | { | ||
| 660 | using BaseT = SurfaceMaskOp<MaskTreeT, InterrupterT>; | ||
| 661 | using LeafManagerT = | ||
| 662 | tree::LeafManager<const points::PointDataTree>; | ||
| 663 | |||
| 664 | 34 | FixedSurfaceMaskOp(const math::Transform& points, | |
| 665 | const math::Transform& surface, | ||
| 666 | const double minBandRadius, | ||
| 667 | const double maxBandRadius, | ||
| 668 | InterrupterT* interrupter = nullptr) | ||
| 669 | : BaseT(points, surface, interrupter) | ||
| 670 | 34 | , mMin(), mMax() { | |
| 671 | // calculate the min interior cube area of activity. this is the side | ||
| 672 | // of the largest possible cube that fits into the radius "min": | ||
| 673 | // d = 2r -> 2r = 3x^2 -> x = 2r / sqrt(3) | ||
| 674 | // finally half it to the new radius | ||
| 675 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 32 times.
|
34 | const Real min = ((2.0 * minBandRadius) / std::sqrt(3.0)) / 2.0; |
| 676 | 34 | const int d = static_cast<int>(math::RoundDown(min)) - this->mVoxelOffset; | |
| 677 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 32 times.
|
34 | mMin = math::Max(0, d); |
| 678 | 34 | mMax = static_cast<int>(math::RoundUp(maxBandRadius)); | |
| 679 | 34 | mMax += this->mVoxelOffset; | |
| 680 | 34 | } | |
| 681 | |||
| 682 | 39 | FixedSurfaceMaskOp(const FixedSurfaceMaskOp& other, tbb::split) | |
| 683 | 39 | : BaseT(other), mMin(other.mMin), mMax(other.mMax) {} | |
| 684 | |||
| 685 | 128 | void operator()(const typename LeafManagerT::LeafRange& range) | |
| 686 | { | ||
| 687 |
1/2✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
|
128 | if (this->interrupted()) return; |
| 688 |
2/2✓ Branch 1 taken 128 times.
✓ Branch 2 taken 128 times.
|
256 | for (auto leafIter = range.begin(); leafIter; ++leafIter) { |
| 689 | 128 | this->fill(*leafIter, mMax, true); | |
| 690 | } | ||
| 691 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 112 times.
|
128 | if (mMin <= 0) return; |
| 692 |
2/2✓ Branch 1 taken 16 times.
✓ Branch 2 taken 16 times.
|
32 | for (auto leafIter = range.begin(); leafIter; ++leafIter) { |
| 693 | 16 | this->fill(*leafIter, mMin, false); | |
| 694 | } | ||
| 695 | } | ||
| 696 | |||
| 697 | private: | ||
| 698 | int mMin, mMax; | ||
| 699 | }; | ||
| 700 | |||
| 701 | /// @brief Initializes a variable activity mask | ||
| 702 | template <typename RadiusTreeT, | ||
| 703 | typename MaskTreeT, | ||
| 704 | typename InterrupterT = util::NullInterrupter> | ||
| 705 | 40 | struct VariableSurfaceMaskOp | |
| 706 | : public SurfaceMaskOp<MaskTreeT, InterrupterT> | ||
| 707 | { | ||
| 708 | using BaseT = SurfaceMaskOp<MaskTreeT, InterrupterT>; | ||
| 709 | using LeafManagerT = | ||
| 710 | tree::LeafManager<const points::PointDataTree>; | ||
| 711 | |||
| 712 | 23 | VariableSurfaceMaskOp(const math::Transform& pointsTransform, | |
| 713 | const math::Transform& surfaceTransform, | ||
| 714 | const RadiusTreeT& min, | ||
| 715 | const RadiusTreeT& max, | ||
| 716 | const Real scale, | ||
| 717 | const Real halfband, | ||
| 718 | InterrupterT* interrupter = nullptr) | ||
| 719 | : BaseT(pointsTransform, surfaceTransform, interrupter) | ||
| 720 |
1/2✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
|
23 | , mMin(min), mMax(max), mScale(scale), mHalfband(halfband) {} |
| 721 | |||
| 722 | 17 | VariableSurfaceMaskOp(const VariableSurfaceMaskOp& other, tbb::split) | |
| 723 | 17 | : BaseT(other), mMin(other.mMin), mMax(other.mMax) | |
| 724 | 17 | , mScale(other.mScale), mHalfband(other.mHalfband) {} | |
| 725 | |||
| 726 | 61 | void operator()(const typename LeafManagerT::LeafRange& range) | |
| 727 | { | ||
| 728 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
|
61 | if (this->interrupted()) return; |
| 729 | 61 | const tree::ValueAccessor<const RadiusTreeT> maxacc(mMax); | |
| 730 |
2/2✓ Branch 1 taken 61 times.
✓ Branch 2 taken 61 times.
|
122 | for (auto leafIter = range.begin(); leafIter; ++leafIter) { |
| 731 |
1/2✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
|
61 | const int max = this->maxDist(maxacc.getValue(leafIter->origin())); |
| 732 |
1/2✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
|
61 | this->fill(*leafIter, max, true); |
| 733 | } | ||
| 734 |
1/2✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
|
61 | const tree::ValueAccessor<const RadiusTreeT> minacc(mMin); |
| 735 |
2/2✓ Branch 1 taken 61 times.
✓ Branch 2 taken 61 times.
|
122 | for (auto leafIter = range.begin(); leafIter; ++leafIter) { |
| 736 |
1/2✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
|
61 | const int min = this->minDist(minacc.getValue(leafIter->origin())); |
| 737 |
2/2✓ Branch 0 taken 45 times.
✓ Branch 1 taken 16 times.
|
61 | if (min <= 0) continue; |
| 738 |
1/2✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
|
16 | this->fill(*leafIter, min, false); |
| 739 | } | ||
| 740 | } | ||
| 741 | |||
| 742 | private: | ||
| 743 | inline int maxDist(const typename RadiusTreeT::ValueType& max) const | ||
| 744 | { | ||
| 745 | // max radius in index space | ||
| 746 | 61 | const Real v = (Real(max) * mScale) + mHalfband; | |
| 747 | 61 | int d = int(math::RoundUp(v)); // next voxel | |
| 748 | 61 | d += this->mVoxelOffset; // final offset | |
| 749 | return d; | ||
| 750 | } | ||
| 751 | |||
| 752 | 61 | inline int minDist(const typename RadiusTreeT::ValueType& min) const | |
| 753 | { | ||
| 754 | // min radius in index space | ||
| 755 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 37 times.
|
61 | Real v = math::Max(0.0, (Real(min) * mScale) - mHalfband); |
| 756 |
2/2✓ Branch 0 taken 23 times.
✓ Branch 1 taken 38 times.
|
61 | v = math::Max(0.0, Real(v - this->mVoxelOffset)); // final offset |
| 757 | // calculate the min interior cube area of activity. this is the side | ||
| 758 | // of the largest possible cube that fits into the radius "v": | ||
| 759 | // d = 2r -> 2r = 3x^2 -> x = 2r / sqrt(3) | ||
| 760 | // finally half it to the new radius | ||
| 761 | 61 | v = ((2.0 * v) / std::sqrt(3.0)) / 2.0; | |
| 762 | 61 | return static_cast<int>(v); // round down (v is always >= 0) | |
| 763 | } | ||
| 764 | |||
| 765 | private: | ||
| 766 | const RadiusTreeT& mMin; | ||
| 767 | const RadiusTreeT& mMax; | ||
| 768 | const Real mScale, mHalfband; | ||
| 769 | }; | ||
| 770 | |||
| 771 | template <typename SdfT, typename InterrupterT, typename PointDataGridT> | ||
| 772 | inline typename SdfT::Ptr | ||
| 773 | 34 | initFixedSdf(const PointDataGridT& points, | |
| 774 | math::Transform::Ptr transform, | ||
| 775 | const typename SdfT::ValueType bg, | ||
| 776 | const double minBandRadius, | ||
| 777 | const double maxBandRadius, | ||
| 778 | InterrupterT* interrupter) | ||
| 779 | { | ||
| 780 | using LeafManagerT = tree::LeafManager<const typename PointDataGridT::TreeType>; | ||
| 781 | using MaskTreeT = typename SdfT::TreeType::template ValueConverter<ValueMask>::Type; | ||
| 782 | |||
| 783 | 34 | typename SdfT::Ptr surface = SdfT::create(bg); | |
| 784 |
2/6✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 34 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
68 | surface->setTransform(transform); |
| 785 |
1/2✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
|
34 | surface->setGridClass(GRID_LEVEL_SET); |
| 786 | |||
| 787 |
1/2✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
|
34 | FixedSurfaceMaskOp<MaskTreeT, InterrupterT> op(points.transform(), |
| 788 | *transform, minBandRadius, maxBandRadius, interrupter); | ||
| 789 | |||
| 790 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
34 | if (interrupter) interrupter->start("Generating uniform surface topology"); |
| 791 | |||
| 792 |
1/2✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
|
68 | LeafManagerT leafManager(points.tree()); |
| 793 |
3/4✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 27 times.
|
34 | tbb::parallel_reduce(leafManager.leafRange(), op); |
| 794 | auto mask = op.mask(); | ||
| 795 | |||
| 796 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 27 times.
|
34 | if (minBandRadius > 0.0) { |
| 797 | auto maskoff = op.maskoff(); | ||
| 798 | mask->topologyDifference(*maskoff); | ||
| 799 | // union will copy empty nodes so prune them | ||
| 800 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | tools::pruneInactive(*mask); |
| 801 | surface->tree().topologyUnion(*mask); | ||
| 802 | mask.reset(); | ||
| 803 | // set maskoff values to -background | ||
| 804 | tree::ValueAccessor<const MaskTreeT> acc(*maskoff); | ||
| 805 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | auto setOffOp = [acc](auto& iter) { |
| 806 |
2/2✓ Branch 2 taken 432 times.
✓ Branch 3 taken 1909813 times.
|
1910245 | if (acc.isValueOn(iter.getCoord())) { |
| 807 | 864 | iter.modifyValue([](auto& v) { v = -v; }); | |
| 808 | } | ||
| 809 | }; | ||
| 810 |
2/6✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
7 | tools::foreach(surface->beginValueOff(), setOffOp, |
| 811 | /*thread=*/true, /*shared=*/false); | ||
| 812 | maskoff.reset(); | ||
| 813 | } | ||
| 814 | else { | ||
| 815 | surface->tree().topologyUnion(*mask); | ||
| 816 | mask.reset(); | ||
| 817 | } | ||
| 818 | |||
| 819 | surface->tree().voxelizeActiveTiles(); | ||
| 820 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
34 | if (interrupter) interrupter->end(); |
| 821 | 34 | return surface; | |
| 822 | } | ||
| 823 | |||
| 824 | template <typename SdfT, | ||
| 825 | typename InterrupterT, | ||
| 826 | typename PointDataGridT, | ||
| 827 | typename RadiusTreeT> | ||
| 828 | inline typename SdfT::Ptr | ||
| 829 | 23 | initVariableSdf(const PointDataGridT& points, | |
| 830 | math::Transform::Ptr transform, | ||
| 831 | const typename SdfT::ValueType bg, | ||
| 832 | const RadiusTreeT& min, | ||
| 833 | const RadiusTreeT& max, | ||
| 834 | const Real scale, | ||
| 835 | const Real halfband, | ||
| 836 | InterrupterT* interrupter) | ||
| 837 | { | ||
| 838 | using LeafManagerT = tree::LeafManager<const typename PointDataGridT::TreeType>; | ||
| 839 | using MaskTreeT = typename SdfT::TreeType::template ValueConverter<ValueMask>::Type; | ||
| 840 | |||
| 841 | 23 | typename SdfT::Ptr surface = SdfT::create(bg); | |
| 842 |
2/6✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 23 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
46 | surface->setTransform(transform); |
| 843 |
1/2✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
|
23 | surface->setGridClass(GRID_LEVEL_SET); |
| 844 | |||
| 845 | VariableSurfaceMaskOp<RadiusTreeT, MaskTreeT, InterrupterT> | ||
| 846 | op(points.transform(), *transform, min, max, scale, halfband, interrupter); | ||
| 847 | |||
| 848 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
23 | if (interrupter) interrupter->start("Generating variable surface topology"); |
| 849 | |||
| 850 |
1/2✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
|
46 | LeafManagerT leafManager(points.tree()); |
| 851 |
1/2✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
|
23 | tbb::parallel_reduce(leafManager.leafRange(), op); |
| 852 | auto mask = op.mask(); | ||
| 853 | auto maskoff = op.maskoff(); | ||
| 854 | |||
| 855 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 19 times.
|
23 | if (!maskoff->empty()) { |
| 856 | mask->topologyDifference(*maskoff); | ||
| 857 | // union will copy empty nodes so prune them | ||
| 858 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | tools::pruneInactive(*mask); |
| 859 | surface->tree().topologyUnion(*mask); | ||
| 860 | // set maskoff values to -background | ||
| 861 | tree::ValueAccessor<const MaskTreeT> acc(*maskoff); | ||
| 862 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | auto setOffOp = [acc](auto& iter) { |
| 863 |
2/2✓ Branch 2 taken 432 times.
✓ Branch 3 taken 1292454 times.
|
1292886 | if (acc.isValueOn(iter.getCoord())) { |
| 864 | 864 | iter.modifyValue([](auto& v) { v = -v; }); | |
| 865 | } | ||
| 866 | }; | ||
| 867 |
2/6✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
4 | tools::foreach(surface->beginValueOff(), setOffOp, |
| 868 | /*thread=*/true, /*shared=*/false); | ||
| 869 | } | ||
| 870 | else { | ||
| 871 | surface->tree().topologyUnion(*mask); | ||
| 872 | } | ||
| 873 | |||
| 874 | mask.reset(); | ||
| 875 | maskoff.reset(); | ||
| 876 | surface->tree().voxelizeActiveTiles(); | ||
| 877 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
23 | if (interrupter) interrupter->end(); |
| 878 | 23 | return surface; | |
| 879 | } | ||
| 880 | |||
| 881 | template <typename PointDataTreeT, | ||
| 882 | typename AttributeTypes> | ||
| 883 | inline GridPtrVec | ||
| 884 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
24 | transferAttributes(const tree::LeafManager<const PointDataTreeT>& manager, |
| 885 | const std::vector<std::string>& attributes, | ||
| 886 | const Int64Tree& cpg, | ||
| 887 | const math::Transform::Ptr transform) | ||
| 888 | { | ||
| 889 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
24 | assert(manager.leafCount() != 0); |
| 890 | // masking uses upper 32 bits for leaf node id | ||
| 891 | // @note we can use a point list impl to support larger counts | ||
| 892 | // if necessary but this is far faster | ||
| 893 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
24 | assert(manager.leafCount() < |
| 894 | size_t(std::numeric_limits<Index>::max())); | ||
| 895 | |||
| 896 | // linearise cpg to avoid having to probe data | ||
| 897 | 48 | const tree::LeafManager<const Int64Tree> cpmanager(cpg); | |
| 898 | |||
| 899 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
40 | auto transfer = [&](auto& tree, const size_t attrIdx) { |
| 900 | using TreeType = typename std::decay<decltype(tree)>::type; | ||
| 901 | using HandleT = AttributeHandle<typename TreeType::ValueType>; | ||
| 902 | |||
| 903 | // init topology | ||
| 904 | tree.topologyUnion(cpg); | ||
| 905 | 8 | tree::LeafManager<TreeType> lm(tree); | |
| 906 | |||
| 907 | // init values | ||
| 908 |
2/6✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
|
344 | lm.foreach([&manager, &cpmanager, attrIdx] |
| 909 | (auto& leaf, const size_t idx) { | ||
| 910 | auto voxel = leaf.beginValueOn(); | ||
| 911 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 56 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
112 | if (!voxel) return; |
| 912 | |||
| 913 | 112 | auto* data = leaf.buffer().data(); | |
| 914 | 112 | const Int64* ids = cpmanager.leaf(idx).buffer().data(); | |
| 915 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 56 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
112 | Index prev = Index(ids[voxel.pos()] >> 32); |
| 916 |
2/6✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 56 times.
✗ Branch 8 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
112 | typename HandleT::UniquePtr handle( |
| 917 | new HandleT(manager.leaf(prev).constAttributeArray(attrIdx))); | ||
| 918 | |||
| 919 |
4/6✓ Branch 0 taken 4307 times.
✓ Branch 1 taken 56 times.
✓ Branch 2 taken 4307 times.
✓ Branch 3 taken 56 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
8726 | for (; voxel; ++voxel) { |
| 920 | 8614 | const Int64 hash = ids[voxel.pos()]; | |
| 921 | 8614 | const Index lfid = Index(hash >> 32); // upper 32 bits to leaf id | |
| 922 | 8614 | const Index ptid = static_cast<Index>(hash); // lower | |
| 923 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 4307 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4307 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
8614 | if (lfid != prev) { |
| 924 | ✗ | handle.reset(new HandleT(manager.leaf(lfid).constAttributeArray(attrIdx))); | |
| 925 | prev = lfid; | ||
| 926 | } | ||
| 927 |
2/6✓ Branch 1 taken 4307 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 4307 times.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
8614 | data[voxel.pos()] = handle->get(ptid); |
| 928 | } | ||
| 929 | }); | ||
| 930 | }; | ||
| 931 | |||
| 932 | GridPtrVec grids; | ||
| 933 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
24 | grids.reserve(attributes.size()); |
| 934 | const auto& attrSet = manager.leaf(0).attributeSet(); | ||
| 935 | tbb::task_group tasks; | ||
| 936 | |||
| 937 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 4 times.
|
40 | for (const auto& name : attributes) { |
| 938 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
32 | const size_t attrIdx = attrSet.find(name); |
| 939 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
32 | if (attrIdx == points::AttributeSet::INVALID_POS) continue; |
| 940 |
3/6✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 16 times.
|
32 | if (attrSet.get(attrIdx)->stride() != 1) { |
| 941 | ✗ | OPENVDB_THROW(RuntimeError, "Transfer of attribute " + name + | |
| 942 | " not supported since it is strided"); | ||
| 943 | } | ||
| 944 | |||
| 945 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
32 | const std::string& type = attrSet.descriptor().valueType(attrIdx); |
| 946 | GridBase::Ptr grid = nullptr; | ||
| 947 |
3/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 8 times.
|
68 | AttributeTypes::foreach([&](const auto& v) { |
| 948 | using ValueType = typename std::remove_const<typename std::decay<decltype(v)>::type>::type; | ||
| 949 | using TreeT = typename PointDataTreeT::template ValueConverter<ValueType>::Type; | ||
| 950 |
8/12✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 4 times.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 4 times.
|
32 | if (!grid && typeNameAsString<ValueType>() == type) { |
| 951 | auto typed = Grid<TreeT>::create(); | ||
| 952 | grid = typed; | ||
| 953 |
2/6✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
8 | typed->setName(name); |
| 954 |
2/6✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
16 | typed->setTransform(transform); |
| 955 |
2/12✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 9 taken 4 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
|
16 | tasks.run([typed, attrIdx, transfer] { transfer(typed->tree(), attrIdx); }); |
| 956 |
2/6✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
8 | grids.emplace_back(grid); |
| 957 | } | ||
| 958 | }); | ||
| 959 | |||
| 960 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8 times.
|
24 | if (!grid) { |
| 961 |
4/10✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 8 times.
✗ Branch 12 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
|
80 | OPENVDB_THROW(RuntimeError, "No support for attribute type " + type + |
| 962 | " built during closest point surface transfer"); | ||
| 963 | } | ||
| 964 | } | ||
| 965 | |||
| 966 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
8 | tasks.wait(); |
| 967 | 8 | return grids; | |
| 968 | } | ||
| 969 | |||
| 970 | template <typename SdfT, | ||
| 971 | template <typename, typename, typename, bool> class TransferInterfaceT, | ||
| 972 | typename AttributeTypes, | ||
| 973 | typename InterrupterT, | ||
| 974 | typename PointDataGridT, | ||
| 975 | typename FilterT, | ||
| 976 | typename ...Args> | ||
| 977 | inline GridPtrVec | ||
| 978 |
1/2✓ Branch 1 taken 51 times.
✗ Branch 2 not taken.
|
102 | rasterizeSurface(const PointDataGridT& points, |
| 979 | const std::vector<std::string>& attributes, | ||
| 980 | const FilterT& filter, | ||
| 981 | SdfT& surface, | ||
| 982 | InterrupterT* interrupter, | ||
| 983 | Args&&... args) | ||
| 984 | { | ||
| 985 | using RadRefT = typename std::tuple_element<1, std::tuple<Args...>>::type; | ||
| 986 | using RadT = typename std::remove_reference<RadRefT>::type; | ||
| 987 | |||
| 988 | GridPtrVec grids; | ||
| 989 | auto leaf = points.constTree().cbeginLeaf(); | ||
| 990 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 2 times.
|
102 | if (!leaf) return grids; |
| 991 | |||
| 992 |
2/4✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
|
98 | const size_t pidx = leaf->attributeSet().find("P"); |
| 993 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
|
98 | if (pidx == AttributeSet::INVALID_POS) { |
| 994 | ✗ | OPENVDB_THROW(RuntimeError, "Failed to find position attribute"); | |
| 995 | } | ||
| 996 | |||
| 997 | // @note Can't split this out into a generic lambda yet as there | ||
| 998 | // are compiler issues with capturing variadic arguments | ||
| 999 |
1/2✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
|
98 | const NamePair& ptype = leaf->attributeSet().descriptor().type(pidx); |
| 1000 | |||
| 1001 |
2/2✓ Branch 0 taken 37 times.
✓ Branch 1 taken 12 times.
|
98 | if (attributes.empty()) { |
| 1002 |
1/2✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
|
74 | if (ptype.second == NullCodec::name()) { |
| 1003 | using TransferT = TransferInterfaceT<SdfT, NullCodec, RadT, false>; | ||
| 1004 |
1/2✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
|
100 | TransferT transfer(pidx, args...); |
| 1005 | rasterize<PointDataGridT, TransferT, FilterT, InterrupterT> | ||
| 1006 |
1/2✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
|
74 | (points, transfer, filter, interrupter); |
| 1007 | } | ||
| 1008 | else { | ||
| 1009 | using TransferT = TransferInterfaceT<SdfT, UnknownCodec, RadT, false>; | ||
| 1010 | ✗ | TransferT transfer(pidx, args...); | |
| 1011 | rasterize<PointDataGridT, TransferT, FilterT, InterrupterT> | ||
| 1012 | ✗ | (points, transfer, filter, interrupter); | |
| 1013 | } | ||
| 1014 | } | ||
| 1015 | else { | ||
| 1016 | 48 | Int64Tree cpg; | |
| 1017 | cpg.topologyUnion(surface.tree()); | ||
| 1018 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
48 | tree::LeafManager<const PointDataTree> manager(points.tree()); |
| 1019 | // map point leaf nodes to their linear id | ||
| 1020 | // @todo sorted vector of leaf ptr-> index pair then lookup with binary search? | ||
| 1021 | std::unordered_map<const PointDataTree::LeafNodeType*, Index> ids; | ||
| 1022 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
78 | manager.foreach([&](auto& leaf, size_t idx) { ids[&leaf] = Index(idx); }, false); |
| 1023 | |||
| 1024 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
24 | if (ptype.second == NullCodec::name()) { |
| 1025 | using TransferT = TransferInterfaceT<SdfT, NullCodec, RadT, true>; | ||
| 1026 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
36 | TransferT transfer(pidx, args..., &cpg, &ids); |
| 1027 | rasterize<PointDataGridT, TransferT, FilterT, InterrupterT> | ||
| 1028 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
24 | (points, transfer, filter, interrupter); |
| 1029 | } | ||
| 1030 | else { | ||
| 1031 | using TransferT = TransferInterfaceT<SdfT, UnknownCodec, RadT, true>; | ||
| 1032 | ✗ | TransferT transfer(pidx, args..., &cpg, &ids); | |
| 1033 | rasterize<PointDataGridT, TransferT, FilterT, InterrupterT> | ||
| 1034 | ✗ | (points, transfer, filter, interrupter); | |
| 1035 | } | ||
| 1036 | |||
| 1037 | ids.clear(); | ||
| 1038 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
24 | tools::pruneInactive(cpg); |
| 1039 | // Build attribute transfer grids | ||
| 1040 |
3/4✓ Branch 1 taken 4 times.
✓ Branch 2 taken 8 times.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
56 | grids = transferAttributes |
| 1041 | <typename PointDataGrid::TreeType, AttributeTypes> | ||
| 1042 | (manager, attributes, cpg, surface.transformPtr()); | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | return grids; | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | } // namespace rasterize_sdf_internal | ||
| 1049 | |||
| 1050 | /// @endcond | ||
| 1051 | |||
| 1052 | /////////////////////////////////////////////////// | ||
| 1053 | /////////////////////////////////////////////////// | ||
| 1054 | |||
| 1055 | |||
| 1056 | template <typename PointDataGridT, | ||
| 1057 | typename SdfT, | ||
| 1058 | typename FilterT, | ||
| 1059 | typename InterrupterT> | ||
| 1060 | typename SdfT::Ptr | ||
| 1061 | 34 | rasterizeSpheres(const PointDataGridT& points, | |
| 1062 | const Real radius, | ||
| 1063 | const Real halfband, | ||
| 1064 | math::Transform::Ptr transform, | ||
| 1065 | const FilterT& filter, | ||
| 1066 | InterrupterT* interrupter) | ||
| 1067 | { | ||
| 1068 |
2/6✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
102 | auto grids = |
| 1069 | rasterizeSpheres<PointDataGridT, TypeList<>, SdfT, FilterT, InterrupterT> | ||
| 1070 | (points, radius, {}, halfband, transform, filter, interrupter); | ||
| 1071 | 34 | return StaticPtrCast<SdfT>(grids.front()); | |
| 1072 | } | ||
| 1073 | |||
| 1074 | template <typename PointDataGridT, | ||
| 1075 | typename RadiusT, | ||
| 1076 | typename SdfT, | ||
| 1077 | typename FilterT, | ||
| 1078 | typename InterrupterT> | ||
| 1079 | typename SdfT::Ptr | ||
| 1080 | 18 | rasterizeSpheres(const PointDataGridT& points, | |
| 1081 | const std::string& radius, | ||
| 1082 | const Real scale, | ||
| 1083 | const Real halfband, | ||
| 1084 | math::Transform::Ptr transform, | ||
| 1085 | const FilterT& filter, | ||
| 1086 | InterrupterT* interrupter) | ||
| 1087 | { | ||
| 1088 |
2/6✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
54 | auto grids = |
| 1089 | rasterizeSpheres<PointDataGridT, TypeList<>, RadiusT, SdfT, FilterT, InterrupterT> | ||
| 1090 | (points, radius, {}, scale, halfband, transform, filter, interrupter); | ||
| 1091 | 18 | return StaticPtrCast<SdfT>(grids.front()); | |
| 1092 | } | ||
| 1093 | |||
| 1094 | template <typename PointDataGridT, | ||
| 1095 | typename AttributeTypes, | ||
| 1096 | typename SdfT, | ||
| 1097 | typename FilterT, | ||
| 1098 | typename InterrupterT> | ||
| 1099 | GridPtrVec | ||
| 1100 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 4 times.
|
42 | rasterizeSpheres(const PointDataGridT& points, |
| 1101 | const Real radius, | ||
| 1102 | const std::vector<std::string>& attributes, | ||
| 1103 | const Real halfband, | ||
| 1104 | math::Transform::Ptr transform, | ||
| 1105 | const FilterT& filter, | ||
| 1106 | InterrupterT* interrupter) | ||
| 1107 | { | ||
| 1108 | using namespace rasterize_sdf_internal; | ||
| 1109 | |||
| 1110 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 4 times.
|
76 | if (!transform) transform = points.transform().copy(); |
| 1111 | 42 | const Real vs = transform->voxelSize()[0]; | |
| 1112 | 42 | const float background = static_cast<float>(vs * halfband); | |
| 1113 | |||
| 1114 | // search distance at the SDF transform, including its half band | ||
| 1115 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 14 times.
|
42 | const Real radiusIndexSpace = radius / vs; |
| 1116 | const FixedBandRadius<Real> rad(radiusIndexSpace, halfband); | ||
| 1117 | const Real minBandRadius = rad.min(); | ||
| 1118 | const Real maxBandRadius = rad.max(); | ||
| 1119 | 42 | const size_t width = static_cast<size_t>(math::RoundUp(maxBandRadius)); | |
| 1120 | |||
| 1121 |
1/2✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
|
42 | typename SdfT::Ptr surface = |
| 1122 | initFixedSdf<SdfT, InterrupterT> | ||
| 1123 | (points, transform, background, minBandRadius, maxBandRadius, interrupter); | ||
| 1124 | |||
| 1125 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
42 | if (interrupter) interrupter->start("Rasterizing particles to level set using constant Spheres"); |
| 1126 | |||
| 1127 |
2/2✓ Branch 1 taken 19 times.
✓ Branch 2 taken 2 times.
|
42 | GridPtrVec grids = |
| 1128 | rasterizeSurface<SdfT, SphericalTransfer, AttributeTypes, InterrupterT> | ||
| 1129 | (points, attributes, filter, *surface, interrupter, | ||
| 1130 | width, rad, points.transform(), *surface); // args | ||
| 1131 | |||
| 1132 |
1/2✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
|
38 | tools::pruneLevelSet(surface->tree()); |
| 1133 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
38 | if (interrupter) interrupter->end(); |
| 1134 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
38 | grids.insert(grids.begin(), surface); |
| 1135 | 38 | return grids; | |
| 1136 | } | ||
| 1137 | |||
| 1138 | template <typename PointDataGridT, | ||
| 1139 | typename AttributeTypes, | ||
| 1140 | typename RadiusT, | ||
| 1141 | typename SdfT, | ||
| 1142 | typename FilterT, | ||
| 1143 | typename InterrupterT> | ||
| 1144 | GridPtrVec | ||
| 1145 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 5 times.
|
26 | rasterizeSpheres(const PointDataGridT& points, |
| 1146 | const std::string& radius, | ||
| 1147 | const std::vector<std::string>& attributes, | ||
| 1148 | const Real scale, | ||
| 1149 | const Real halfband, | ||
| 1150 | math::Transform::Ptr transform, | ||
| 1151 | const FilterT& filter, | ||
| 1152 | InterrupterT* interrupter) | ||
| 1153 | { | ||
| 1154 | using namespace rasterize_sdf_internal; | ||
| 1155 | using PointDataTreeT = typename PointDataGridT::TreeType; | ||
| 1156 | using RadTreeT = typename PointDataTreeT::template ValueConverter<RadiusT>::Type; | ||
| 1157 | |||
| 1158 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 5 times.
|
42 | if (!transform) transform = points.transform().copy(); |
| 1159 | 26 | const Real vs = transform->voxelSize()[0]; | |
| 1160 | 26 | const float background = static_cast<float>(vs * halfband); | |
| 1161 | |||
| 1162 | 26 | RadiusT min(0), max(0); | |
| 1163 |
1/2✓ Branch 3 taken 13 times.
✗ Branch 4 not taken.
|
52 | typename RadTreeT::Ptr mintree(new RadTreeT), maxtree(new RadTreeT); |
| 1164 | points::evalMinMax<RadiusT, UnknownCodec> | ||
| 1165 |
1/2✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
|
26 | (points.tree(), radius, min, max, filter, mintree.get(), maxtree.get()); |
| 1166 | |||
| 1167 | // search distance at the SDF transform | ||
| 1168 | 26 | const RadiusT indexSpaceScale = RadiusT(scale / vs); | |
| 1169 |
3/6✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
|
56 | typename SdfT::Ptr surface = |
| 1170 | initVariableSdf<SdfT, InterrupterT> | ||
| 1171 | (points, transform, background, *mintree, *maxtree, | ||
| 1172 | indexSpaceScale, halfband, interrupter); | ||
| 1173 | mintree.reset(); | ||
| 1174 | maxtree.reset(); | ||
| 1175 | |||
| 1176 | auto leaf = points.constTree().cbeginLeaf(); | ||
| 1177 |
4/6✓ Branch 0 taken 2 times.
✓ Branch 1 taken 11 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
|
38 | if (!leaf) return GridPtrVec(1, surface); |
| 1178 | |||
| 1179 | // max possible index space radius | ||
| 1180 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
22 | const size_t width = static_cast<size_t> |
| 1181 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
22 | (math::RoundUp((Real(max) * indexSpaceScale) + Real(halfband))); |
| 1182 | |||
| 1183 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
22 | const size_t ridx = leaf->attributeSet().find(radius); |
| 1184 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
22 | if (ridx == AttributeSet::INVALID_POS) { |
| 1185 | ✗ | OPENVDB_THROW(RuntimeError, "Failed to find radius attribute \"" + radius + "\""); | |
| 1186 | } | ||
| 1187 | |||
| 1188 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
22 | VaryingBandRadius<RadiusT> rad(ridx, RadiusT(halfband), indexSpaceScale); |
| 1189 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
22 | if (interrupter) interrupter->start("Rasterizing particles to level set using variable Spheres"); |
| 1190 | |||
| 1191 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 2 times.
|
40 | GridPtrVec grids = |
| 1192 | rasterizeSurface<SdfT, SphericalTransfer, AttributeTypes, InterrupterT> | ||
| 1193 | (points, attributes, filter, *surface, interrupter, | ||
| 1194 | width, rad, points.transform(), *surface); // args | ||
| 1195 | |||
| 1196 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
18 | if (interrupter) interrupter->end(); |
| 1197 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
18 | tools::pruneLevelSet(surface->tree()); |
| 1198 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
18 | grids.insert(grids.begin(), surface); |
| 1199 | return grids; | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | /////////////////////////////////////////////////// | ||
| 1203 | |||
| 1204 | template <typename PointDataGridT, | ||
| 1205 | typename SdfT, | ||
| 1206 | typename FilterT, | ||
| 1207 | typename InterrupterT> | ||
| 1208 | typename SdfT::Ptr | ||
| 1209 | 18 | rasterizeSmoothSpheres(const PointDataGridT& points, | |
| 1210 | const Real radius, | ||
| 1211 | const Real searchRadius, | ||
| 1212 | const Real halfband, | ||
| 1213 | math::Transform::Ptr transform, | ||
| 1214 | const FilterT& filter, | ||
| 1215 | InterrupterT* interrupter) | ||
| 1216 | { | ||
| 1217 |
2/6✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
54 | auto grids = |
| 1218 | rasterizeSmoothSpheres<PointDataGridT, TypeList<>, SdfT, FilterT, InterrupterT> | ||
| 1219 | (points, radius, searchRadius, {}, halfband, transform, filter, interrupter); | ||
| 1220 | 18 | return StaticPtrCast<SdfT>(grids.front()); | |
| 1221 | } | ||
| 1222 | |||
| 1223 | template <typename PointDataGridT, | ||
| 1224 | typename RadiusT, | ||
| 1225 | typename SdfT, | ||
| 1226 | typename FilterT, | ||
| 1227 | typename InterrupterT> | ||
| 1228 | typename SdfT::Ptr | ||
| 1229 | 12 | rasterizeSmoothSpheres(const PointDataGridT& points, | |
| 1230 | const std::string& radius, | ||
| 1231 | const Real radiusScale, | ||
| 1232 | const Real searchRadius, | ||
| 1233 | const Real halfband, | ||
| 1234 | math::Transform::Ptr transform, | ||
| 1235 | const FilterT& filter, | ||
| 1236 | InterrupterT* interrupter) | ||
| 1237 | { | ||
| 1238 |
2/6✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
36 | auto grids = |
| 1239 | rasterizeSmoothSpheres<PointDataGridT, TypeList<>, RadiusT, SdfT, FilterT, InterrupterT> | ||
| 1240 | (points, radius, radiusScale, searchRadius, {}, halfband, transform, filter, interrupter); | ||
| 1241 | 12 | return StaticPtrCast<SdfT>(grids.front()); | |
| 1242 | } | ||
| 1243 | |||
| 1244 | template <typename PointDataGridT, | ||
| 1245 | typename AttributeTypes, | ||
| 1246 | typename SdfT, | ||
| 1247 | typename FilterT, | ||
| 1248 | typename InterrupterT> | ||
| 1249 | GridPtrVec | ||
| 1250 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
|
26 | rasterizeSmoothSpheres(const PointDataGridT& points, |
| 1251 | const Real radius, | ||
| 1252 | const Real searchRadius, | ||
| 1253 | const std::vector<std::string>& attributes, | ||
| 1254 | const Real halfband, | ||
| 1255 | math::Transform::Ptr transform, | ||
| 1256 | const FilterT& filter, | ||
| 1257 | InterrupterT* interrupter) | ||
| 1258 | { | ||
| 1259 | using namespace rasterize_sdf_internal; | ||
| 1260 | |||
| 1261 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
|
48 | if (!transform) transform = points.transform().copy(); |
| 1262 | 26 | const Real vs = transform->voxelSize()[0]; | |
| 1263 | 26 | const float background = static_cast<float>(vs * halfband); | |
| 1264 | 26 | const Real indexSpaceSearch = searchRadius / vs; | |
| 1265 | 26 | const FixedBandRadius<Real> bands(radius / vs, halfband); | |
| 1266 | const Real max = bands.max(); | ||
| 1267 | |||
| 1268 |
2/4✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
|
52 | typename SdfT::Ptr surface = |
| 1269 | initFixedSdf<SdfT, InterrupterT> | ||
| 1270 | (points, transform, background, /*min*/0.0, max, interrupter); | ||
| 1271 | |||
| 1272 | auto leaf = points.constTree().cbeginLeaf(); | ||
| 1273 |
4/6✓ Branch 0 taken 2 times.
✓ Branch 1 taken 11 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
|
38 | if (!leaf) return GridPtrVec(1, surface); |
| 1274 | |||
| 1275 | // max possible index space search radius | ||
| 1276 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
22 | const size_t width = static_cast<size_t>(math::RoundUp(indexSpaceSearch)); |
| 1277 | |||
| 1278 | const FixedRadius<Real> rad(radius / vs); | ||
| 1279 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
22 | if (interrupter) interrupter->start("Rasterizing particles to level set using constant Zhu-Bridson"); |
| 1280 | |||
| 1281 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 2 times.
|
40 | GridPtrVec grids = |
| 1282 | rasterizeSurface<SdfT, AveragePositionTransfer, AttributeTypes, InterrupterT> | ||
| 1283 | (points, attributes, filter, *surface, interrupter, | ||
| 1284 | width, rad, indexSpaceSearch, points.transform(), *surface); // args | ||
| 1285 | |||
| 1286 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
18 | tools::pruneInactive(surface->tree()); |
| 1287 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
18 | if (interrupter) interrupter->end(); |
| 1288 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
18 | grids.insert(grids.begin(), surface); |
| 1289 | return grids; | ||
| 1290 | } | ||
| 1291 | |||
| 1292 | template <typename PointDataGridT, | ||
| 1293 | typename AttributeTypes, | ||
| 1294 | typename RadiusT, | ||
| 1295 | typename SdfT, | ||
| 1296 | typename FilterT, | ||
| 1297 | typename InterrupterT> | ||
| 1298 | GridPtrVec | ||
| 1299 | 10 | rasterizeSmoothSpheres(const PointDataGridT& points, | |
| 1300 | const std::string& radius, | ||
| 1301 | const Real radiusScale, | ||
| 1302 | const Real searchRadius, | ||
| 1303 | const std::vector<std::string>& attributes, | ||
| 1304 | const Real halfband, | ||
| 1305 | math::Transform::Ptr transform, | ||
| 1306 | const FilterT& filter, | ||
| 1307 | InterrupterT* interrupter) | ||
| 1308 | { | ||
| 1309 | using namespace rasterize_sdf_internal; | ||
| 1310 | using PointDataTreeT = typename PointDataGridT::TreeType; | ||
| 1311 | using RadTreeT = typename PointDataTreeT::template ValueConverter<RadiusT>::Type; | ||
| 1312 | |||
| 1313 | 8 | if (!transform) transform = points.transform().copy(); | |
| 1314 | 10 | const Real vs = transform->voxelSize()[0]; | |
| 1315 | 10 | const float background = static_cast<float>(vs * halfband); | |
| 1316 | 10 | const Real indexSpaceSearch = searchRadius / vs; | |
| 1317 | |||
| 1318 | 10 | RadiusT min(0), max(0); | |
| 1319 | 20 | typename RadTreeT::Ptr mintree(new RadTreeT), maxtree(new RadTreeT); | |
| 1320 | points::evalMinMax<RadiusT, UnknownCodec> | ||
| 1321 | 10 | (points.tree(), radius, min, max, filter, mintree.get(), maxtree.get()); | |
| 1322 | |||
| 1323 | // search distance at the SDF transform | ||
| 1324 | 10 | const RadiusT indexSpaceScale = RadiusT(radiusScale / vs); | |
| 1325 | 22 | typename SdfT::Ptr surface = | |
| 1326 | initVariableSdf<SdfT, InterrupterT> | ||
| 1327 | (points, transform, background, *mintree, *maxtree, | ||
| 1328 | indexSpaceScale, halfband, interrupter); | ||
| 1329 | mintree.reset(); | ||
| 1330 | maxtree.reset(); | ||
| 1331 | |||
| 1332 | auto leaf = points.constTree().cbeginLeaf(); | ||
| 1333 | 16 | if (!leaf) return GridPtrVec(1, surface); | |
| 1334 | |||
| 1335 | // max possible index space search radius | ||
| 1336 | 8 | const size_t width = static_cast<size_t>(math::RoundUp(indexSpaceSearch)); | |
| 1337 | |||
| 1338 | 8 | const size_t ridx = leaf->attributeSet().find(radius); | |
| 1339 | 8 | if (ridx == AttributeSet::INVALID_POS) { | |
| 1340 | ✗ | OPENVDB_THROW(RuntimeError, "Failed to find radius attribute"); | |
| 1341 | } | ||
| 1342 | |||
| 1343 | VaryingRadius<RadiusT> rad(ridx, indexSpaceScale); | ||
| 1344 | 8 | if (interrupter) interrupter->start("Rasterizing particles to level set using variable Zhu-Bridson"); | |
| 1345 | |||
| 1346 | 14 | GridPtrVec grids = | |
| 1347 | rasterizeSurface<SdfT, AveragePositionTransfer, AttributeTypes, InterrupterT> | ||
| 1348 | (points, attributes, filter, *surface, interrupter, | ||
| 1349 | width, rad, indexSpaceSearch, points.transform(), *surface); // args | ||
| 1350 | |||
| 1351 | 6 | tools::pruneInactive(surface->tree()); | |
| 1352 | 6 | if (interrupter) interrupter->end(); | |
| 1353 | 6 | grids.insert(grids.begin(), surface); | |
| 1354 | return grids; | ||
| 1355 | } | ||
| 1356 | |||
| 1357 | } // namespace points | ||
| 1358 | } // namespace OPENVDB_VERSION_NAME | ||
| 1359 | } // namespace openvdb | ||
| 1360 | |||
| 1361 | #endif //OPENVDB_POINTS_RASTERIZE_SDF_IMPL_HAS_BEEN_INCLUDED | ||
| 1362 |