GCC Code Coverage Report


Directory: ./
File: openvdb/openvdb/tools/DenseSparseTools.h
Date: 2022-07-25 17:40:05
Exec Total Coverage
Lines: 173 240 72.1%
Functions: 29 41 70.7%
Branches: 105 202 52.0%

Line Branch Exec Source
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3
4 #ifndef OPENVDB_TOOLS_DENSESPARSETOOLS_HAS_BEEN_INCLUDED
5 #define OPENVDB_TOOLS_DENSESPARSETOOLS_HAS_BEEN_INCLUDED
6
7 #include <tbb/parallel_reduce.h>
8 #include <tbb/blocked_range3d.h>
9 #include <tbb/blocked_range2d.h>
10 #include <tbb/blocked_range.h>
11 #include <openvdb/Types.h>
12 #include <openvdb/tree/LeafManager.h>
13 #include "Dense.h"
14 #include <algorithm> // for std::min()
15 #include <vector>
16
17
18 namespace openvdb {
19 OPENVDB_USE_VERSION_NAMESPACE
20 namespace OPENVDB_VERSION_NAME {
21 namespace tools {
22
23 /// @brief Selectively extract and transform data from a dense grid, producing a
24 /// sparse tree with leaf nodes only (e.g. create a tree from the square
25 /// of values greater than a cutoff.)
26 /// @param dense A dense grid that acts as a data source
27 /// @param functor A functor that selects and transforms data for output
28 /// @param background The background value of the resulting sparse grid
29 /// @param threaded Option to use threaded or serial code path
30 /// @return @c Ptr to tree with the valuetype and configuration defined
31 /// by typedefs in the @c functor.
32 /// @note To achieve optimal sparsity consider calling the prune()
33 /// method on the result.
34 /// @note To simply copy the all the data from a Dense grid to a
35 /// OpenVDB Grid, use tools::copyFromDense() for better performance.
36 ///
37 /// The type of the sparse tree is determined by the specified OtpType
38 /// functor by means of the typedef OptType::ResultTreeType
39 ///
40 /// The OptType function is responsible for the the transformation of
41 /// dense grid data to sparse grid data on a per-voxel basis.
42 ///
43 /// Only leaf nodes with active values will be added to the sparse grid.
44 ///
45 /// The OpType must struct that defines a the minimal form
46 /// @code
47 /// struct ExampleOp
48 /// {
49 /// using ResultTreeType = DesiredTreeType;
50 ///
51 /// template<typename IndexOrCoord>
52 /// void OpType::operator() (const DenseValueType a, const IndexOrCoord& ijk,
53 /// ResultTreeType::LeafNodeType* leaf);
54 /// };
55 /// @endcode
56 ///
57 /// For example, to generate a <ValueType, 5, 4, 3> tree with valuesOn
58 /// at locations greater than a given maskvalue
59 /// @code
60 /// template<typename ValueType>
61 /// class Rule
62 /// {
63 /// public:
64 /// // Standard tree type (e.g. MaskTree or FloatTree in openvdb.h)
65 /// using ResultTreeType = typename openvdb::tree::Tree4<ValueType, 5, 4, 3>::Type;
66 ///
67 /// using ResultLeafNodeType = typename ResultTreeType::LeafNodeType;
68 /// using ResultValueType = typename ResultTreeType::ValueType;
69 ///
70 /// using DenseValueType = float;
71 ///
72 /// using Index = openvdb::Coord::ValueType;
73 ///
74 /// Rule(const DenseValueType& value): mMaskValue(value){};
75 ///
76 /// template<typename IndexOrCoord>
77 /// void operator()(const DenseValueType& a, const IndexOrCoord& offset,
78 /// ResultLeafNodeType* leaf) const
79 /// {
80 /// if (a > mMaskValue) {
81 /// leaf->setValueOn(offset, a);
82 /// }
83 /// }
84 ///
85 /// private:
86 /// const DenseValueType mMaskValue;
87 /// };
88 /// @endcode
89 template<typename OpType, typename DenseType>
90 typename OpType::ResultTreeType::Ptr
91 extractSparseTree(const DenseType& dense, const OpType& functor,
92 const typename OpType::ResultValueType& background,
93 bool threaded = true);
94
95 /// This struct that aids template resolution of a new tree type
96 /// has the same configuration at TreeType, but the ValueType from
97 /// DenseType.
98 template<typename DenseType, typename TreeType>
99 struct DSConverter
100 {
101 using ValueType = typename DenseType::ValueType;
102 using Type = typename TreeType::template ValueConverter<ValueType>::Type;
103 };
104
105
106 /// @brief Copy data from the intersection of a sparse tree and a dense input grid.
107 /// The resulting tree has the same configuration as the sparse tree, but holds
108 /// the data type specified by the dense input.
109 /// @param dense A dense grid that acts as a data source
110 /// @param mask The active voxels and tiles intersected with dense define iteration mask
111 /// @param background The background value of the resulting sparse grid
112 /// @param threaded Option to use threaded or serial code path
113 /// @return @c Ptr to tree with the same configuration as @c mask but of value type
114 /// defined by @c dense.
115 template<typename DenseType, typename MaskTreeType>
116 typename DSConverter<DenseType, MaskTreeType>::Type::Ptr
117 extractSparseTreeWithMask(const DenseType& dense,
118 const MaskTreeType& mask,
119 const typename DenseType::ValueType& background,
120 bool threaded = true);
121
122
123 /// Apply a point-wise functor to the intersection of a dense grid and a given bounding box
124 /// @param dense A dense grid to be transformed
125 /// @param bbox Index space bounding box, define region where the transformation is applied
126 /// @param op A functor that acts on the dense grid value type
127 /// @param parallel Used to select multithreaded or single threaded
128 /// Minimally, the @c op class has to support a @c operator() method,
129 /// @code
130 /// // Square values in a grid
131 /// struct Op
132 /// {
133 /// ValueT operator()(const ValueT& in) const
134 /// {
135 /// // do work
136 /// ValueT result = in * in;
137 ///
138 /// return result;
139 /// }
140 /// };
141 /// @endcode
142 /// NB: only Dense grids with memory layout zxy are supported
143 template<typename ValueT, typename OpType>
144 void transformDense(Dense<ValueT, openvdb::tools::LayoutZYX>& dense,
145 const openvdb::CoordBBox& bbox, const OpType& op, bool parallel=true);
146
147 /// We currrently support the following operations when compositing sparse
148 /// data into a dense grid.
149 enum DSCompositeOp {
150 DS_OVER, DS_ADD, DS_SUB, DS_MIN, DS_MAX, DS_MULT, DS_SET
151 };
152
153 /// @brief Composite data from a sparse tree into a dense array of the same value type.
154 /// @param dense Dense grid to be altered by the operation
155 /// @param source Sparse data to composite into @c dense
156 /// @param alpha Sparse Alpha mask used in compositing operations.
157 /// @param beta Constant multiplier on src
158 /// @param strength Constant multiplier on alpha
159 /// @param threaded Enable threading for this operation.
160 template<DSCompositeOp, typename TreeT>
161 void compositeToDense(Dense<typename TreeT::ValueType, LayoutZYX>& dense,
162 const TreeT& source,
163 const TreeT& alpha,
164 const typename TreeT::ValueType beta,
165 const typename TreeT::ValueType strength,
166 bool threaded = true);
167
168
169 /// @brief Functor-based class used to extract data that satisfies some
170 /// criteria defined by the embedded @c OpType functor. The @c extractSparseTree
171 /// function wraps this class.
172 template<typename OpType, typename DenseType>
173 17 class SparseExtractor
174 {
175 public:
176 using Index = openvdb::math::Coord::ValueType;
177
178 using DenseValueType = typename DenseType::ValueType;
179 using ResultTreeType = typename OpType::ResultTreeType;
180 using ResultValueType = typename ResultTreeType::ValueType;
181 using ResultLeafNodeType = typename ResultTreeType::LeafNodeType;
182 using MaskTree = typename ResultTreeType::template ValueConverter<ValueMask>::Type;
183
184 using Range3d = tbb::blocked_range3d<Index, Index, Index>;
185
186 private:
187 const DenseType& mDense;
188 const OpType& mFunctor;
189 const ResultValueType mBackground;
190 const openvdb::math::CoordBBox mBBox;
191 const Index mWidth;
192 typename ResultTreeType::Ptr mMask;
193 openvdb::math::Coord mMin;
194
195 public:
196 6 SparseExtractor(const DenseType& dense, const OpType& functor,
197 const ResultValueType background) :
198 mDense(dense), mFunctor(functor),
199 mBackground(background),
200 mBBox(dense.bbox()),
201 mWidth(ResultLeafNodeType::DIM),
202 6 mMask( new ResultTreeType(mBackground))
203 6 {}
204
205 SparseExtractor(const DenseType& dense,
206 const openvdb::math::CoordBBox& bbox,
207 const OpType& functor,
208 const ResultValueType background) :
209 mDense(dense), mFunctor(functor),
210 mBackground(background),
211 mBBox(bbox),
212 mWidth(ResultLeafNodeType::DIM),
213 mMask( new ResultTreeType(mBackground))
214 {
215 // mBBox must be inside the coordinate rage of the dense grid
216 if (!dense.bbox().isInside(mBBox)) {
217 OPENVDB_THROW(ValueError, "Data extraction window out of bound");
218 }
219 }
220
221 34 SparseExtractor(SparseExtractor& other, tbb::split):
222 34 mDense(other.mDense), mFunctor(other.mFunctor),
223 34 mBackground(other.mBackground), mBBox(other.mBBox),
224 34 mWidth(other.mWidth),
225 34 mMask(new ResultTreeType(mBackground)),
226 34 mMin(other.mMin)
227 34 {}
228
229 6 typename ResultTreeType::Ptr extract(bool threaded = true)
230 {
231 // Construct 3D range of leaf nodes that
232 // intersect mBBox.
233
234 // Snap the bbox to nearest leaf nodes min and max
235
236 6 openvdb::math::Coord padded_min = mBBox.min();
237 6 openvdb::math::Coord padded_max = mBBox.max();
238
239
240
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
6 padded_min &= ~(mWidth - 1);
241 padded_max &= ~(mWidth - 1);
242
243 6 padded_max[0] += mWidth - 1;
244 6 padded_max[1] += mWidth - 1;
245 6 padded_max[2] += mWidth - 1;
246
247
248 // number of leaf nodes in each direction
249 // division by leaf width, e.g. 8 in most cases
250
251 6 const Index xleafCount = ( padded_max.x() - padded_min.x() + 1 ) / mWidth;
252 6 const Index yleafCount = ( padded_max.y() - padded_min.y() + 1 ) / mWidth;
253 6 const Index zleafCount = ( padded_max.z() - padded_min.z() + 1 ) / mWidth;
254
255
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
6 mMin = padded_min;
256
257 Range3d leafRange(0, xleafCount, 1,
258 0, yleafCount, 1,
259 0, zleafCount, 1);
260
261 // Iterate over the leafnodes applying *this as a functor.
262
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
6 if (threaded) {
263 6 tbb::parallel_reduce(leafRange, *this);
264 } else {
265 (*this)(leafRange);
266 }
267
268 6 return mMask;
269 }
270
271 1014 void operator()(const Range3d& range)
272 {
273 ResultLeafNodeType* leaf = nullptr;
274
275 // Unpack the range3d item.
276 const Index imin = range.pages().begin();
277 const Index imax = range.pages().end();
278
279 const Index jmin = range.rows().begin();
280 const Index jmax = range.rows().end();
281
282 const Index kmin = range.cols().begin();
283 const Index kmax = range.cols().end();
284
285
286 // loop over all the candidate leafs. Adding only those with 'true' values
287 // to the tree
288
289
2/2
✓ Branch 0 taken 1349 times.
✓ Branch 1 taken 507 times.
3712 for (Index i = imin; i < imax; ++i) {
290
2/2
✓ Branch 0 taken 4562 times.
✓ Branch 1 taken 1349 times.
11822 for (Index j = jmin; j < jmax; ++j) {
291
2/2
✓ Branch 0 taken 13500 times.
✓ Branch 1 taken 4562 times.
36124 for (Index k = kmin; k < kmax; ++k) {
292
293 // Calculate the origin of candidate leaf
294 const openvdb::math::Coord origin =
295 27000 mMin + openvdb::math::Coord(mWidth * i,
296 mWidth * j,
297
2/2
✓ Branch 0 taken 510 times.
✓ Branch 1 taken 12990 times.
27000 mWidth * k );
298
299
2/2
✓ Branch 0 taken 510 times.
✓ Branch 1 taken 12990 times.
27000 if (leaf == nullptr) {
300
1/2
✓ Branch 2 taken 307 times.
✗ Branch 3 not taken.
1020 leaf = new ResultLeafNodeType(origin, mBackground);
301 } else {
302 leaf->setOrigin(origin);
303 leaf->fill(mBackground);
304 leaf->setValuesOff();
305 }
306
307 // The bounding box for this leaf
308
309 27000 openvdb::math::CoordBBox localBBox = leaf->getNodeBoundingBox();
310
311 // Shrink to the intersection with mBBox (i.e. the dense
312 // volume)
313
314 27000 localBBox.intersect(mBBox);
315
316 // Early out for non-intersecting leafs
317
318 if (localBBox.empty()) continue;
319
320
321 27000 const openvdb::math::Coord start = localBBox.getStart();
322 const openvdb::math::Coord end = localBBox.getEnd();
323
324 // Order the looping to respect the memory layout in
325 // the Dense source
326
327 if (mDense.memoryLayout() == openvdb::tools::LayoutZYX) {
328
329 openvdb::math::Coord ijk;
330 Index offset;
331 const DenseValueType* dp;
332
2/2
✓ Branch 0 taken 68760 times.
✓ Branch 1 taken 9000 times.
155520 for (ijk[0] = start.x(); ijk[0] < end.x(); ++ijk[0] ) {
333
2/2
✓ Branch 0 taken 550080 times.
✓ Branch 1 taken 68760 times.
1237680 for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1] ) {
334 8159520 for (ijk[2] = start.z(),
335 1100160 offset = ResultLeafNodeType::coordToOffset(ijk),
336 1100160 dp = &mDense.getValue(ijk);
337
2/2
✓ Branch 0 taken 4079760 times.
✓ Branch 1 taken 550080 times.
9259680 ijk[2] < end.z(); ++ijk[2], ++offset, ++dp) {
338
339
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2039879 times.
8159520 mFunctor(*dp, offset, leaf);
340 }
341 }
342 }
343
344 } else {
345
346 openvdb::math::Coord ijk;
347 const DenseValueType* dp;
348
2/2
✓ Branch 0 taken 33375 times.
✓ Branch 1 taken 4500 times.
75750 for (ijk[2] = start.z(); ijk[2] < end.z(); ++ijk[2]) {
349
2/2
✓ Branch 0 taken 267000 times.
✓ Branch 1 taken 33375 times.
600750 for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1]) {
350 4079760 for (ijk[0] = start.x(),
351 534000 dp = &mDense.getValue(ijk);
352
2/2
✓ Branch 0 taken 2039880 times.
✓ Branch 1 taken 267000 times.
4613760 ijk[0] < end.x(); ++ijk[0], ++dp) {
353
354
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2039879 times.
4079760 mFunctor(*dp, ijk, leaf);
355
356 }
357 }
358 }
359 }
360
361 // Only add non-empty leafs (empty is defined as all inactive)
362
363
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 13497 times.
27000 if (!leaf->isEmpty()) {
364 6 mMask->addLeaf(leaf);
365 leaf = nullptr;
366 }
367
368 }
369 }
370 }
371
372 // Clean up an unused leaf.
373
374
1/2
✓ Branch 0 taken 507 times.
✗ Branch 1 not taken.
1624 if (leaf != nullptr) delete leaf;
375 }
376
377 void join(SparseExtractor& rhs) {
378 17 mMask->merge(*rhs.mMask);
379 17 }
380 }; // class SparseExtractor
381
382
383 template<typename OpType, typename DenseType>
384 typename OpType::ResultTreeType::Ptr
385 6 extractSparseTree(const DenseType& dense, const OpType& functor,
386 const typename OpType::ResultValueType& background,
387 bool threaded)
388 {
389 // Construct the mask using a parallel reduce pattern.
390 // Each thread computes disjoint mask-trees. The join merges
391 // into a single tree.
392
393 6 SparseExtractor<OpType, DenseType> extractor(dense, functor, background);
394
395
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
12 return extractor.extract(threaded);
396 }
397
398
399 /// @brief Functor-based class used to extract data from a dense grid, at
400 /// the index-space intersection with a supplied mask in the form of a sparse tree.
401 /// The @c extractSparseTreeWithMask function wraps this class.
402 template<typename DenseType, typename MaskTreeType>
403
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 class SparseMaskedExtractor
404 {
405 public:
406 using _ResultTreeType = typename DSConverter<DenseType, MaskTreeType>::Type;
407 using ResultTreeType = _ResultTreeType;
408 using ResultLeafNodeType = typename ResultTreeType::LeafNodeType;
409 using ResultValueType = typename ResultTreeType::ValueType;
410 using DenseValueType = ResultValueType;
411
412 using MaskTree = typename ResultTreeType::template ValueConverter<ValueMask>::Type;
413 using MaskLeafCIter = typename MaskTree::LeafCIter;
414 using MaskLeafVec = std::vector<const typename MaskTree::LeafNodeType*>;
415
416
417 1 SparseMaskedExtractor(const DenseType& dense,
418 const ResultValueType& background,
419 const MaskLeafVec& leafVec
420 ):
421 mDense(dense), mBackground(background), mBBox(dense.bbox()),
422 mLeafVec(leafVec),
423 1 mResult(new ResultTreeType(mBackground))
424 1 {}
425
426 SparseMaskedExtractor(const SparseMaskedExtractor& other, tbb::split):
427 mDense(other.mDense), mBackground(other.mBackground), mBBox(other.mBBox),
428 mLeafVec(other.mLeafVec), mResult( new ResultTreeType(mBackground))
429 {}
430
431 1 typename ResultTreeType::Ptr extract(bool threaded = true)
432 {
433
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 tbb::blocked_range<size_t> range(0, mLeafVec.size());
434
435
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (threaded) {
436 1 tbb::parallel_reduce(range, *this);
437 } else {
438 (*this)(range);
439 }
440
441 1 return mResult;
442 }
443
444 // Used in looping over leaf nodes in the masked grid
445 // and using the active mask to select data to
446 2 void operator()(const tbb::blocked_range<size_t>& range)
447 {
448 ResultLeafNodeType* leaf = nullptr;
449
450 // loop over all the candidate leafs. Adding only those with 'true' values
451 // to the tree
452
453
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
454
455 2 const typename MaskTree::LeafNodeType* maskLeaf = mLeafVec[idx];
456
457 // The bounding box for this leaf
458
459 2 openvdb::math::CoordBBox localBBox = maskLeaf->getNodeBoundingBox();
460
461 // Shrink to the intersection with the dense volume
462
463 2 localBBox.intersect(mBBox);
464
465 // Early out if there was no intersection
466
467 1 if (localBBox.empty()) continue;
468
469 // Reset or allocate the target leaf
470
471
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (leaf == nullptr) {
472
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 leaf = new ResultLeafNodeType(maskLeaf->origin(), mBackground);
473 } else {
474 leaf->setOrigin(maskLeaf->origin());
475 leaf->fill(mBackground);
476 leaf->setValuesOff();
477 }
478
479 // Iterate over the intersecting bounding box
480 // copying active values to the result tree
481
482 1 const openvdb::math::Coord start = localBBox.getStart();
483 const openvdb::math::Coord end = localBBox.getEnd();
484
485 openvdb::math::Coord ijk;
486
487 if (mDense.memoryLayout() == openvdb::tools::LayoutZYX
488
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 && maskLeaf->isDense()) {
489
490 Index offset;
491 const DenseValueType* src;
492 for (ijk[0] = start.x(); ijk[0] < end.x(); ++ijk[0] ) {
493 for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1] ) {
494 for (ijk[2] = start.z(),
495 offset = ResultLeafNodeType::coordToOffset(ijk),
496 src = &mDense.getValue(ijk);
497 ijk[2] < end.z(); ++ijk[2], ++offset, ++src) {
498
499 // copy into leaf
500 leaf->setValueOn(offset, *src);
501 }
502
503 }
504 }
505
506 } else {
507
508 Index offset;
509
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
9 for (ijk[0] = start.x(); ijk[0] < end.x(); ++ijk[0] ) {
510
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 8 times.
72 for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1] ) {
511 320 for (ijk[2] = start.z(),
512 offset = ResultLeafNodeType::coordToOffset(ijk);
513
2/2
✓ Branch 0 taken 256 times.
✓ Branch 1 taken 64 times.
320 ijk[2] < end.z(); ++ijk[2], ++offset) {
514
515
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 255 times.
256 if (maskLeaf->isValueOn(offset)) {
516 1 const ResultValueType denseValue = mDense.getValue(ijk);
517 leaf->setValueOn(offset, denseValue);
518 }
519 }
520 }
521 }
522 }
523 // Only add non-empty leafs (empty is defined as all inactive)
524
525
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!leaf->isEmpty()) {
526 1 mResult->addLeaf(leaf);
527 leaf = nullptr;
528 }
529 }
530
531 // Clean up an unused leaf.
532
533
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (leaf != nullptr) delete leaf;
534 2 }
535
536 void join(SparseMaskedExtractor& rhs) {
537 mResult->merge(*rhs.mResult);
538 }
539
540
541 private:
542 const DenseType& mDense;
543 const ResultValueType mBackground;
544 const openvdb::math::CoordBBox& mBBox;
545 const MaskLeafVec& mLeafVec;
546
547 typename ResultTreeType::Ptr mResult;
548
549 }; // class SparseMaskedExtractor
550
551
552 /// @brief a simple utility class used by @c extractSparseTreeWithMask
553 template<typename _ResultTreeType, typename DenseValueType>
554 struct ExtractAll
555 {
556 using ResultTreeType = _ResultTreeType;
557 using ResultLeafNodeType = typename ResultTreeType::LeafNodeType;
558
559 template<typename CoordOrIndex> inline void
560 operator()(const DenseValueType& a, const CoordOrIndex& offset, ResultLeafNodeType* leaf) const
561 {
562 leaf->setValueOn(offset, a);
563 }
564 };
565
566
567 template<typename DenseType, typename MaskTreeType>
568 typename DSConverter<DenseType, MaskTreeType>::Type::Ptr
569 1 extractSparseTreeWithMask(const DenseType& dense,
570 const MaskTreeType& maskProxy,
571 const typename DenseType::ValueType& background,
572 bool threaded)
573 {
574 using LeafExtractor = SparseMaskedExtractor<DenseType, MaskTreeType>;
575 using DenseValueType = typename LeafExtractor::DenseValueType;
576 using ResultTreeType = typename LeafExtractor::ResultTreeType;
577 using MaskLeafVec = typename LeafExtractor::MaskLeafVec;
578 using MaskTree = typename LeafExtractor::MaskTree;
579 using MaskLeafCIter = typename LeafExtractor::MaskLeafCIter;
580 using ExtractionRule = ExtractAll<ResultTreeType, DenseValueType>;
581
582 // Use Mask tree to hold the topology
583
584
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 MaskTree maskTree(maskProxy, false, TopologyCopy());
585
586 // Construct an array of pointers to the mask leafs.
587
588 1 const size_t leafCount = maskTree.leafCount();
589
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 MaskLeafVec leafarray(leafCount);
590 MaskLeafCIter leafiter = maskTree.cbeginLeaf();
591
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (size_t n = 0; n != leafCount; ++n, ++leafiter) {
592
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 leafarray[n] = leafiter.getLeaf();
593 }
594
595
596 // Extract the data that is masked leaf nodes in the mask.
597
598
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 LeafExtractor leafextractor(dense, background, leafarray);
599
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 typename ResultTreeType::Ptr resultTree = leafextractor.extract(threaded);
600
601
602 // Extract data that is masked by tiles in the mask.
603
604
605 // Loop over the mask tiles, extracting the data into new trees.
606 // These trees will be leaf-orthogonal to the leafTree (i.e. no leaf
607 // nodes will overlap). Merge these trees into the result.
608
609
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 typename MaskTreeType::ValueOnCIter tileIter(maskProxy);
610 1 tileIter.setMaxDepth(MaskTreeType::ValueOnCIter::LEAF_DEPTH - 1);
611
612 // Return the leaf tree if the mask had no tiles
613
614
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!tileIter) return resultTree;
615
616 ExtractionRule allrule;
617
618 // Loop over the tiles in series, but the actual data extraction
619 // is in parallel.
620
621 CoordBBox bbox;
622 for ( ; tileIter; ++tileIter) {
623
624 // Find the intersection of the tile with the dense grid.
625
626 tileIter.getBoundingBox(bbox);
627 bbox.intersect(dense.bbox());
628
629 if (bbox.empty()) continue;
630
631 SparseExtractor<ExtractionRule, DenseType> copyData(dense, bbox, allrule, background);
632 typename ResultTreeType::Ptr fromTileTree = copyData.extract(threaded);
633 resultTree->merge(*fromTileTree);
634 }
635
636 return resultTree;
637 }
638
639
640 /// @brief Class that applies a functor to the index space intersection
641 /// of a prescribed bounding box and the dense grid.
642 /// NB: This class only supports DenseGrids with ZYX memory layout.
643 template<typename _ValueT, typename OpType>
644 class DenseTransformer
645 {
646 public:
647 using ValueT = _ValueT;
648 using DenseT = Dense<ValueT, openvdb::tools::LayoutZYX>;
649 using IntType = openvdb::math::Coord::ValueType;
650 using RangeType = tbb::blocked_range2d<IntType, IntType>;
651
652 private:
653 DenseT& mDense;
654 const OpType& mOp;
655 openvdb::math::CoordBBox mBBox;
656
657 public:
658 1 DenseTransformer(DenseT& dense, const openvdb::math::CoordBBox& bbox, const OpType& functor):
659 1 mDense(dense), mOp(functor), mBBox(dense.bbox())
660 {
661 // The iteration space is the intersection of the
662 // input bbox and the index-space covered by the dense grid
663 1 mBBox.intersect(bbox);
664 }
665
666 20 DenseTransformer(const DenseTransformer& other) :
667 2 mDense(other.mDense), mOp(other.mOp), mBBox(other.mBBox) {}
668
669
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 void apply(bool threaded = true) {
670
671 // Early out if the iteration space is empty
672
673 if (mBBox.empty()) return;
674
675
676
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 const openvdb::math::Coord start = mBBox.getStart();
677 const openvdb::math::Coord end = mBBox.getEnd();
678
679 // The iteration range only the slower two directions.
680 const RangeType range(start.x(), end.x(), 1,
681 start.y(), end.y(), 1);
682
683
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 if (threaded) {
684 2 tbb::parallel_for(range, *this);
685 } else {
686 (*this)(range);
687 }
688 }
689
690 70 void operator()(const RangeType& range) const {
691
692 // The stride in the z-direction.
693 // Note: the bbox is [inclusive, inclusive]
694
695 70 const size_t zlength = size_t(mBBox.max().z() - mBBox.min().z() + 1);
696
697 const IntType imin = range.rows().begin();
698 const IntType imax = range.rows().end();
699 const IntType jmin = range.cols().begin();
700 const IntType jmax = range.cols().end();
701
702
703 openvdb::math::Coord xyz(imin, jmin, mBBox.min().z());
704
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 35 times.
140 for (xyz[0] = imin; xyz[0] != imax; ++xyz[0]) {
705
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 35 times.
140 for (xyz[1] = jmin; xyz[1] != jmax; ++xyz[1]) {
706
707 70 mOp.transform(mDense, xyz, zlength);
708 }
709 }
710 }
711 }; // class DenseTransformer
712
713
714 /// @brief a wrapper struct used to avoid unnecessary computation of
715 /// memory access from @c Coord when all offsets are guaranteed to be
716 /// within the dense grid.
717 template<typename ValueT, typename PointWiseOp>
718 struct ContiguousOp
719 {
720 1 ContiguousOp(const PointWiseOp& op) : mOp(op){}
721
722 using DenseT = Dense<ValueT, openvdb::tools::LayoutZYX>;
723 70 inline void transform(DenseT& dense, openvdb::math::Coord& ijk, size_t size) const
724 {
725 ValueT* dp = const_cast<ValueT*>(&dense.getValue(ijk));
726
727
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 35 times.
280 for (size_t offset = 0; offset < size; ++offset) {
728 210 dp[offset] = mOp(dp[offset]);
729 }
730 }
731
732 const PointWiseOp mOp;
733 };
734
735
736 /// Apply a point-wise functor to the intersection of a dense grid and a given bounding box
737 template<typename ValueT, typename PointwiseOpT>
738 void
739 2 transformDense(Dense<ValueT, openvdb::tools::LayoutZYX>& dense,
740 const openvdb::CoordBBox& bbox,
741 const PointwiseOpT& functor, bool parallel)
742 {
743 using OpT = ContiguousOp<ValueT, PointwiseOpT>;
744
745 // Convert the Op so it operates on a contiguous line in memory
746
747 OpT op(functor);
748
749 // Apply to the index space intersection in the dense grid
750 DenseTransformer<ValueT, OpT> transformer(dense, bbox, op);
751 2 transformer.apply(parallel);
752 }
753
754
755 template<typename CompositeMethod, typename _TreeT>
756 class SparseToDenseCompositor
757 {
758 public:
759 using TreeT = _TreeT;
760 using ValueT = typename TreeT::ValueType;
761 using LeafT = typename TreeT::LeafNodeType;
762 using MaskTreeT = typename TreeT::template ValueConverter<ValueMask>::Type;
763 using MaskLeafT = typename MaskTreeT::LeafNodeType;
764 using DenseT = Dense<ValueT, openvdb::tools::LayoutZYX>;
765 using Index = openvdb::math::Coord::ValueType;
766 using Range3d = tbb::blocked_range3d<Index, Index, Index>;
767
768 3 SparseToDenseCompositor(DenseT& dense, const TreeT& source, const TreeT& alpha,
769 const ValueT beta, const ValueT strength) :
770
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
3 mDense(dense), mSource(source), mAlpha(alpha), mBeta(beta), mStrength(strength)
771 {}
772
773 8 SparseToDenseCompositor(const SparseToDenseCompositor& other):
774 8 mDense(other.mDense), mSource(other.mSource), mAlpha(other.mAlpha),
775
1/2
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
5 mBeta(other.mBeta), mStrength(other.mStrength) {}
776
777
778 2 void sparseComposite(bool threaded)
779 {
780 2 const ValueT beta = mBeta;
781 2 const ValueT strength = mStrength;
782
783 // construct a tree that defines the iteration space
784
785 2 MaskTreeT maskTree(mSource, false /*background*/, openvdb::TopologyCopy());
786
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 maskTree.topologyUnion(mAlpha);
787
788 // Composite regions that are represented by leafnodes in either mAlpha or mSource
789 // Parallelize over bool-leafs
790
791
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 openvdb::tree::LeafManager<const MaskTreeT> maskLeafs(maskTree);
792
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 maskLeafs.foreach(*this, threaded);
793
794 // Composite regions that are represented by tiles
795 // Parallelize within each tile.
796
797 typename MaskTreeT::ValueOnCIter citer = maskTree.cbeginValueOn();
798 2 citer.setMaxDepth(MaskTreeT::ValueOnCIter::LEAF_DEPTH - 1);
799
800
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!citer) return;
801
802 typename tree::ValueAccessor<const TreeT> alphaAccessor(mAlpha);
803 typename tree::ValueAccessor<const TreeT> sourceAccessor(mSource);
804
805 for (; citer; ++citer) {
806
807 const openvdb::math::Coord org = citer.getCoord();
808
809 // Early out if both alpha and source are zero in this tile.
810
811 const ValueT alphaValue = alphaAccessor.getValue(org);
812 const ValueT sourceValue = sourceAccessor.getValue(org);
813
814 if (openvdb::math::isZero(alphaValue) &&
815 openvdb::math::isZero(sourceValue)) continue;
816
817 // Compute overlap of tile with the dense grid
818
819 openvdb::math::CoordBBox localBBox = citer.getBoundingBox();
820 localBBox.intersect(mDense.bbox());
821
822 // Early out if there is no intersection
823
824 if (localBBox.empty()) continue;
825
826 // Composite the tile-uniform values into the dense grid.
827 compositeFromTile(mDense, localBBox, sourceValue,
828 alphaValue, beta, strength, threaded);
829 }
830 }
831
832 // Composites leaf values where the alpha values are active.
833 // Used in sparseComposite
834 2 void inline operator()(const MaskLeafT& maskLeaf, size_t /*i*/) const
835 {
836 using ULeaf = UniformLeaf;
837 2 openvdb::math::CoordBBox localBBox = maskLeaf.getNodeBoundingBox();
838 2 localBBox.intersect(mDense.bbox());
839
840 // Early out for non-overlapping leafs
841
842 if (localBBox.empty()) return;
843
844 2 const openvdb::math::Coord org = maskLeaf.origin();
845 2 const LeafT* alphaLeaf = mAlpha.probeLeaf(org);
846 2 const LeafT* sourceLeaf = mSource.probeLeaf(org);
847
848
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!sourceLeaf) {
849
850 // Create a source leaf proxy with the correct value
851 ULeaf uniformSource(mSource.getValue(org));
852
853 if (!alphaLeaf) {
854
855 // Create an alpha leaf proxy with the correct value
856 ULeaf uniformAlpha(mAlpha.getValue(org));
857
858 compositeFromLeaf(mDense, localBBox, uniformSource, uniformAlpha,
859 mBeta, mStrength);
860 } else {
861
862 compositeFromLeaf(mDense, localBBox, uniformSource, *alphaLeaf,
863 mBeta, mStrength);
864 }
865 } else {
866
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!alphaLeaf) {
867
868 // Create an alpha leaf proxy with the correct value
869 ULeaf uniformAlpha(mAlpha.getValue(org));
870
871 compositeFromLeaf(mDense, localBBox, *sourceLeaf, uniformAlpha,
872 mBeta, mStrength);
873 } else {
874
875 2 compositeFromLeaf(mDense, localBBox, *sourceLeaf, *alphaLeaf,
876 2 mBeta, mStrength);
877 }
878 }
879 }
880 // i.e. it assumes that all valueOff Alpha voxels have value 0.
881
882 template<typename LeafT1, typename LeafT2>
883 4 inline static void compositeFromLeaf(DenseT& dense, const openvdb::math::CoordBBox& bbox,
884 const LeafT1& source, const LeafT2& alpha,
885 const ValueT beta, const ValueT strength)
886 {
887 using IntType = openvdb::math::Coord::ValueType;
888
889 const ValueT sbeta = strength * beta;
890 4 openvdb::math::Coord ijk = bbox.min();
891
892
893
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
4 if (alpha.isDense() /*all active values*/) {
894
895 // Optimal path for dense alphaLeaf
896 const IntType size = bbox.max().z() + 1 - bbox.min().z();
897
898 for (ijk[0] = bbox.min().x(); ijk[0] < bbox.max().x() + 1; ++ijk[0]) {
899 for (ijk[1] = bbox.min().y(); ijk[1] < bbox.max().y() + 1; ++ijk[1]) {
900
901 ValueT* d = const_cast<ValueT*>(&dense.getValue(ijk));
902 const ValueT* a = &alpha.getValue(ijk);
903 const ValueT* s = &source.getValue(ijk);
904
905 for (IntType idx = 0; idx < size; ++idx) {
906 d[idx] = CompositeMethod::apply(d[idx], a[idx], s[idx],
907 strength, beta, sbeta);
908 }
909 }
910 }
911 } else {
912
913 // AlphaLeaf has non-active cells.
914
915
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
12 for (ijk[0] = bbox.min().x(); ijk[0] < bbox.max().x() + 1; ++ijk[0]) {
916
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 4 times.
56 for (ijk[1] = bbox.min().y(); ijk[1] < bbox.max().y() + 1; ++ijk[1]) {
917
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 24 times.
192 for (ijk[2] = bbox.min().z(); ijk[2] < bbox.max().z() + 1; ++ijk[2]) {
918
919
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 70 times.
144 if (alpha.isValueOn(ijk)) {
920 4 dense.setValue(ijk, CompositeMethod::apply(dense.getValue(ijk),
921 alpha.getValue(ijk), source.getValue(ijk), strength, beta, sbeta));
922 }
923 }
924 }
925 }
926 }
927 }
928
929 inline static void compositeFromTile(DenseT& dense, openvdb::math::CoordBBox& bbox,
930 const ValueT& sourceValue, const ValueT& alphaValue,
931 const ValueT& beta, const ValueT& strength,
932 bool threaded)
933 {
934 using TileTransformer = UniformTransformer;
935 TileTransformer functor(sourceValue, alphaValue, beta, strength);
936
937 // Transform the data inside the bbox according to the TileTranformer.
938
939 transformDense(dense, bbox, functor, threaded);
940 }
941
942 1 void denseComposite(bool threaded)
943 {
944 /// Construct a range that corresponds to the
945 /// bounding box of the dense volume
946
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 const openvdb::math::CoordBBox& bbox = mDense.bbox();
947
948 Range3d range(bbox.min().x(), bbox.max().x(), LeafT::DIM,
949 bbox.min().y(), bbox.max().y(), LeafT::DIM,
950 bbox.min().z(), bbox.max().z(), LeafT::DIM);
951
952 // Iterate over the range, compositing into
953 // the dense grid using value accessors for
954 // sparse the grids.
955
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (threaded) {
956 1 tbb::parallel_for(range, *this);
957 } else {
958 (*this)(range);
959 }
960 1 }
961
962 // Composites a dense region using value accessors
963 // into a dense grid
964 4 void operator()(const Range3d& range) const
965 {
966 // Use value accessors to alpha and source
967
968 4 typename tree::ValueAccessor<const TreeT> alphaAccessor(mAlpha);
969
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 typename tree::ValueAccessor<const TreeT> sourceAccessor(mSource);
970
971 4 const ValueT strength = mStrength;
972 4 const ValueT beta = mBeta;
973 const ValueT sbeta = strength * beta;
974
975 // Unpack the range3d item.
976 const Index imin = range.pages().begin();
977 const Index imax = range.pages().end();
978
979 const Index jmin = range.rows().begin();
980 const Index jmax = range.rows().end();
981
982 const Index kmin = range.cols().begin();
983 const Index kmax = range.cols().end();
984
985 openvdb::Coord ijk;
986
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4 times.
24 for (ijk[0] = imin; ijk[0] < imax; ++ijk[0]) {
987
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 20 times.
120 for (ijk[1] = jmin; ijk[1] < jmax; ++ijk[1]) {
988
2/2
✓ Branch 0 taken 500 times.
✓ Branch 1 taken 100 times.
600 for (ijk[2] = kmin; ijk[2] < kmax; ++ijk[2]) {
989 500 const ValueT d_old = mDense.getValue(ijk);
990
1/2
✓ Branch 1 taken 500 times.
✗ Branch 2 not taken.
500 const ValueT& alpha = alphaAccessor.getValue(ijk);
991
1/2
✓ Branch 1 taken 500 times.
✗ Branch 2 not taken.
500 const ValueT& src = sourceAccessor.getValue(ijk);
992
993 500 mDense.setValue(ijk,
994 500 CompositeMethod::apply(d_old, alpha, src, strength, beta, sbeta));
995 }
996 }
997 }
998 4 }
999
1000 private:
1001 // Internal class that wraps the templated composite method
1002 // for use when both alpha and source are uniform over
1003 // a prescribed bbox (e.g. a tile).
1004 class UniformTransformer
1005 {
1006 public:
1007 UniformTransformer(const ValueT& source, const ValueT& alpha, const ValueT& _beta,
1008 const ValueT& _strength) :
1009 mSource(source), mAlpha(alpha), mBeta(_beta),
1010 mStrength(_strength), mSBeta(_strength * _beta)
1011 {}
1012
1013 ValueT operator()(const ValueT& input) const
1014 {
1015 return CompositeMethod::apply(input, mAlpha, mSource, mStrength, mBeta, mSBeta);
1016 }
1017
1018 private:
1019 const ValueT mSource;
1020 const ValueT mAlpha;
1021 const ValueT mBeta;
1022 const ValueT mStrength;
1023 const ValueT mSBeta;
1024 };
1025
1026
1027 // Simple Class structure that mimics a leaf
1028 // with uniform values. Holds LeafT::DIM copies
1029 // of a value in an array.
1030 struct Line { ValueT mValues[LeafT::DIM]; };
1031 class UniformLeaf : private Line
1032 {
1033 public:
1034 using ValueT = typename LeafT::ValueType;
1035
1036 using BaseT = Line;
1037 UniformLeaf(const ValueT& value) : BaseT(init(value)) {}
1038
1039 static const BaseT init(const ValueT& value) {
1040 BaseT tmp;
1041 for (openvdb::Index i = 0; i < LeafT::DIM; ++i) {
1042 tmp.mValues[i] = value;
1043 }
1044 return tmp;
1045 }
1046
1047 bool isDense() const { return true; }
1048 bool isValueOn(openvdb::math::Coord&) const { return true; }
1049
1050 const ValueT& getValue(const openvdb::math::Coord&) const { return BaseT::mValues[0]; }
1051 };
1052
1053 private:
1054 DenseT& mDense;
1055 const TreeT& mSource;
1056 const TreeT& mAlpha;
1057 ValueT mBeta;
1058 ValueT mStrength;
1059 }; // class SparseToDenseCompositor
1060
1061
1062 namespace ds
1063 {
1064 //@{
1065 /// @brief Point wise methods used to apply various compositing operations.
1066 template<typename ValueT>
1067 struct OpOver
1068 {
1069 static inline ValueT apply(const ValueT u, const ValueT alpha,
1070 const ValueT v,
1071 const ValueT strength,
1072 const ValueT beta,
1073 const ValueT /*sbeta*/)
1074
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
503 { return (u + strength * alpha * (beta * v - u)); }
1075 };
1076
1077 template<typename ValueT>
1078 struct OpAdd
1079 {
1080 static inline ValueT apply(const ValueT u, const ValueT alpha,
1081 const ValueT v,
1082 const ValueT /*strength*/,
1083 const ValueT /*beta*/,
1084 const ValueT sbeta)
1085 { return (u + sbeta * alpha * v); }
1086 };
1087
1088 template<typename ValueT>
1089 struct OpSub
1090 {
1091 static inline ValueT apply(const ValueT u, const ValueT alpha,
1092 const ValueT v,
1093 const ValueT /*strength*/,
1094 const ValueT /*beta*/,
1095 const ValueT sbeta)
1096 { return (u - sbeta * alpha * v); }
1097 };
1098
1099 template<typename ValueT>
1100 struct OpMin
1101 {
1102 static inline ValueT apply(const ValueT u, const ValueT alpha,
1103 const ValueT v,
1104 const ValueT s /*trength*/,
1105 const ValueT beta,
1106 const ValueT /*sbeta*/)
1107 { return ( ( 1 - s * alpha) * u + s * alpha * std::min(u, beta * v) ); }
1108 };
1109
1110 template<typename ValueT>
1111 struct OpMax
1112 {
1113 static inline ValueT apply(const ValueT u, const ValueT alpha,
1114 const ValueT v,
1115 const ValueT s/*trength*/,
1116 const ValueT beta,
1117 const ValueT /*sbeta*/)
1118 { return ( ( 1 - s * alpha ) * u + s * alpha * std::min(u, beta * v) ); }
1119 };
1120
1121 template<typename ValueT>
1122 struct OpMult
1123 {
1124 static inline ValueT apply(const ValueT u, const ValueT alpha,
1125 const ValueT v,
1126 const ValueT s/*trength*/,
1127 const ValueT /*beta*/,
1128 const ValueT sbeta)
1129 { return ( ( 1 + alpha * (sbeta * v - s)) * u ); }
1130 };
1131 //@}
1132
1133 //@{
1134 /// Translator that converts an enum to compositing functor types
1135 template<DSCompositeOp OP, typename ValueT>
1136 struct CompositeFunctorTranslator{};
1137
1138 template<typename ValueT>
1139 struct CompositeFunctorTranslator<DS_OVER, ValueT>{ using OpT = OpOver<ValueT>; };
1140
1141 template<typename ValueT>
1142 struct CompositeFunctorTranslator<DS_ADD, ValueT>{ using OpT = OpAdd<ValueT>; };
1143
1144 template<typename ValueT>
1145 struct CompositeFunctorTranslator<DS_SUB, ValueT>{ using OpT = OpSub<ValueT>; };
1146
1147 template<typename ValueT>
1148 struct CompositeFunctorTranslator<DS_MIN, ValueT>{ using OpT = OpMin<ValueT>; };
1149
1150 template<typename ValueT>
1151 struct CompositeFunctorTranslator<DS_MAX, ValueT>{ using OpT = OpMax<ValueT>; };
1152
1153 template<typename ValueT>
1154 struct CompositeFunctorTranslator<DS_MULT, ValueT>{ using OpT = OpMult<ValueT>; };
1155 //@}
1156
1157 } // namespace ds
1158
1159
1160 template<DSCompositeOp OpT, typename TreeT>
1161 inline void
1162 1 compositeToDense(
1163 Dense<typename TreeT::ValueType, LayoutZYX>& dense,
1164 const TreeT& source, const TreeT& alpha,
1165 const typename TreeT::ValueType beta,
1166 const typename TreeT::ValueType strength,
1167 bool threaded)
1168 {
1169 using ValueT = typename TreeT::ValueType;
1170 using Translator = ds::CompositeFunctorTranslator<OpT, ValueT>;
1171 using Method = typename Translator::OpT;
1172
1173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (openvdb::math::isZero(strength)) return;
1174
1175 SparseToDenseCompositor<Method, TreeT> tool(dense, source, alpha, beta, strength);
1176
1177
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 if (openvdb::math::isZero(alpha.background()) &&
1178 openvdb::math::isZero(source.background()))
1179 {
1180 // Use the sparsity of (alpha U source) as the iteration space.
1181 1 tool.sparseComposite(threaded);
1182 } else {
1183 // Use the bounding box of dense as the iteration space.
1184 tool.denseComposite(threaded);
1185 }
1186 }
1187
1188 } // namespace tools
1189 } // namespace OPENVDB_VERSION_NAME
1190 } // namespace openvdb
1191
1192 #endif //OPENVDB_TOOLS_DENSESPARSETOOLS_HAS_BEEN_INCLUDED
1193