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 |