OpenVDB  6.1.0
DenseSparseTools.h
Go to the documentation of this file.
1 //
3 // Copyright (c) 2012-2019 DreamWorks Animation LLC
4 //
5 // All rights reserved. This software is distributed under the
6 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
7 //
8 // Redistributions of source code must retain the above copyright
9 // and license notice and the following restrictions and disclaimer.
10 //
11 // * Neither the name of DreamWorks Animation nor the names of
12 // its contributors may be used to endorse or promote products derived
13 // from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
20 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 // IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
27 // LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
28 //
30 
31 #ifndef OPENVDB_TOOLS_DENSESPARSETOOLS_HAS_BEEN_INCLUDED
32 #define OPENVDB_TOOLS_DENSESPARSETOOLS_HAS_BEEN_INCLUDED
33 
34 #include <tbb/parallel_reduce.h>
35 #include <tbb/blocked_range3d.h>
36 #include <tbb/blocked_range2d.h>
37 #include <tbb/blocked_range.h>
38 #include <openvdb/Types.h>
40 #include "Dense.h"
41 #include <algorithm> // for std::min()
42 #include <vector>
43 
44 
45 namespace openvdb {
47 namespace OPENVDB_VERSION_NAME {
48 namespace tools {
49 
116 template<typename OpType, typename DenseType>
117 typename OpType::ResultTreeType::Ptr
118 extractSparseTree(const DenseType& dense, const OpType& functor,
119  const typename OpType::ResultValueType& background,
120  bool threaded = true);
121 
125 template<typename DenseType, typename TreeType>
127 {
128  using ValueType = typename DenseType::ValueType;
129  using Type = typename TreeType::template ValueConverter<ValueType>::Type;
130 };
131 
132 
142 template<typename DenseType, typename MaskTreeType>
144 extractSparseTreeWithMask(const DenseType& dense,
145  const MaskTreeType& mask,
146  const typename DenseType::ValueType& background,
147  bool threaded = true);
148 
149 
170 template<typename ValueT, typename OpType>
172  const openvdb::CoordBBox& bbox, const OpType& op, bool parallel=true);
173 
178 };
179 
187 template<DSCompositeOp, typename TreeT>
189  const TreeT& source,
190  const TreeT& alpha,
191  const typename TreeT::ValueType beta,
192  const typename TreeT::ValueType strength,
193  bool threaded = true);
194 
195 
199 template<typename OpType, typename DenseType>
201 {
202 public:
203  using Index = openvdb::math::Coord::ValueType;
204 
205  using DenseValueType = typename DenseType::ValueType;
206  using ResultTreeType = typename OpType::ResultTreeType;
207  using ResultValueType = typename ResultTreeType::ValueType;
208  using ResultLeafNodeType = typename ResultTreeType::LeafNodeType;
209  using MaskTree = typename ResultTreeType::template ValueConverter<ValueMask>::Type;
210 
211  using Range3d = tbb::blocked_range3d<Index, Index, Index>;
212 
213 private:
214  const DenseType& mDense;
215  const OpType& mFunctor;
216  const ResultValueType mBackground;
217  const openvdb::math::CoordBBox mBBox;
218  const Index mWidth;
219  typename ResultTreeType::Ptr mMask;
220  openvdb::math::Coord mMin;
221 
222 public:
223  SparseExtractor(const DenseType& dense, const OpType& functor,
224  const ResultValueType background) :
225  mDense(dense), mFunctor(functor),
226  mBackground(background),
227  mBBox(dense.bbox()),
228  mWidth(ResultLeafNodeType::DIM),
229  mMask( new ResultTreeType(mBackground))
230  {}
231 
232  SparseExtractor(const DenseType& dense,
233  const openvdb::math::CoordBBox& bbox,
234  const OpType& functor,
235  const ResultValueType background) :
236  mDense(dense), mFunctor(functor),
237  mBackground(background),
238  mBBox(bbox),
239  mWidth(ResultLeafNodeType::DIM),
240  mMask( new ResultTreeType(mBackground))
241  {
242  // mBBox must be inside the coordinate rage of the dense grid
243  if (!dense.bbox().isInside(mBBox)) {
244  OPENVDB_THROW(ValueError, "Data extraction window out of bound");
245  }
246  }
247 
248  SparseExtractor(SparseExtractor& other, tbb::split):
249  mDense(other.mDense), mFunctor(other.mFunctor),
250  mBackground(other.mBackground), mBBox(other.mBBox),
251  mWidth(other.mWidth),
252  mMask(new ResultTreeType(mBackground)),
253  mMin(other.mMin)
254  {}
255 
256  typename ResultTreeType::Ptr extract(bool threaded = true)
257  {
258  // Construct 3D range of leaf nodes that
259  // intersect mBBox.
260 
261  // Snap the bbox to nearest leaf nodes min and max
262 
263  openvdb::math::Coord padded_min = mBBox.min();
264  openvdb::math::Coord padded_max = mBBox.max();
265 
266 
267  padded_min &= ~(mWidth - 1);
268  padded_max &= ~(mWidth - 1);
269 
270  padded_max[0] += mWidth - 1;
271  padded_max[1] += mWidth - 1;
272  padded_max[2] += mWidth - 1;
273 
274 
275  // number of leaf nodes in each direction
276  // division by leaf width, e.g. 8 in most cases
277 
278  const Index xleafCount = ( padded_max.x() - padded_min.x() + 1 ) / mWidth;
279  const Index yleafCount = ( padded_max.y() - padded_min.y() + 1 ) / mWidth;
280  const Index zleafCount = ( padded_max.z() - padded_min.z() + 1 ) / mWidth;
281 
282  mMin = padded_min;
283 
284  Range3d leafRange(0, xleafCount, 1,
285  0, yleafCount, 1,
286  0, zleafCount, 1);
287 
288  // Iterate over the leafnodes applying *this as a functor.
289  if (threaded) {
290  tbb::parallel_reduce(leafRange, *this);
291  } else {
292  (*this)(leafRange);
293  }
294 
295  return mMask;
296  }
297 
298  void operator()(const Range3d& range)
299  {
300  ResultLeafNodeType* leaf = nullptr;
301 
302  // Unpack the range3d item.
303  const Index imin = range.pages().begin();
304  const Index imax = range.pages().end();
305 
306  const Index jmin = range.rows().begin();
307  const Index jmax = range.rows().end();
308 
309  const Index kmin = range.cols().begin();
310  const Index kmax = range.cols().end();
311 
312 
313  // loop over all the candidate leafs. Adding only those with 'true' values
314  // to the tree
315 
316  for (Index i = imin; i < imax; ++i) {
317  for (Index j = jmin; j < jmax; ++j) {
318  for (Index k = kmin; k < kmax; ++k) {
319 
320  // Calculate the origin of candidate leaf
321  const openvdb::math::Coord origin =
322  mMin + openvdb::math::Coord(mWidth * i,
323  mWidth * j,
324  mWidth * k );
325 
326  if (leaf == nullptr) {
327  leaf = new ResultLeafNodeType(origin, mBackground);
328  } else {
329  leaf->setOrigin(origin);
330  leaf->fill(mBackground);
331  leaf->setValuesOff();
332  }
333 
334  // The bounding box for this leaf
335 
336  openvdb::math::CoordBBox localBBox = leaf->getNodeBoundingBox();
337 
338  // Shrink to the intersection with mBBox (i.e. the dense
339  // volume)
340 
341  localBBox.intersect(mBBox);
342 
343  // Early out for non-intersecting leafs
344 
345  if (localBBox.empty()) continue;
346 
347 
348  const openvdb::math::Coord start = localBBox.getStart();
349  const openvdb::math::Coord end = localBBox.getEnd();
350 
351  // Order the looping to respect the memory layout in
352  // the Dense source
353 
354  if (mDense.memoryLayout() == openvdb::tools::LayoutZYX) {
355 
356  openvdb::math::Coord ijk;
357  Index offset;
358  const DenseValueType* dp;
359  for (ijk[0] = start.x(); ijk[0] < end.x(); ++ijk[0] ) {
360  for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1] ) {
361  for (ijk[2] = start.z(),
362  offset = ResultLeafNodeType::coordToOffset(ijk),
363  dp = &mDense.getValue(ijk);
364  ijk[2] < end.z(); ++ijk[2], ++offset, ++dp) {
365 
366  mFunctor(*dp, offset, leaf);
367  }
368  }
369  }
370 
371  } else {
372 
373  openvdb::math::Coord ijk;
374  const DenseValueType* dp;
375  for (ijk[2] = start.z(); ijk[2] < end.z(); ++ijk[2]) {
376  for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1]) {
377  for (ijk[0] = start.x(),
378  dp = &mDense.getValue(ijk);
379  ijk[0] < end.x(); ++ijk[0], ++dp) {
380 
381  mFunctor(*dp, ijk, leaf);
382 
383  }
384  }
385  }
386  }
387 
388  // Only add non-empty leafs (empty is defined as all inactive)
389 
390  if (!leaf->isEmpty()) {
391  mMask->addLeaf(leaf);
392  leaf = nullptr;
393  }
394 
395  }
396  }
397  }
398 
399  // Clean up an unused leaf.
400 
401  if (leaf != nullptr) delete leaf;
402  }
403 
404  void join(SparseExtractor& rhs) {
405  mMask->merge(*rhs.mMask);
406  }
407 }; // class SparseExtractor
408 
409 
410 template<typename OpType, typename DenseType>
411 typename OpType::ResultTreeType::Ptr
412 extractSparseTree(const DenseType& dense, const OpType& functor,
413  const typename OpType::ResultValueType& background,
414  bool threaded)
415 {
416  // Construct the mask using a parallel reduce pattern.
417  // Each thread computes disjoint mask-trees. The join merges
418  // into a single tree.
419 
420  SparseExtractor<OpType, DenseType> extractor(dense, functor, background);
421 
422  return extractor.extract(threaded);
423 }
424 
425 
429 template<typename DenseType, typename MaskTreeType>
431 {
432 public:
435  using ResultLeafNodeType = typename ResultTreeType::LeafNodeType;
436  using ResultValueType = typename ResultTreeType::ValueType;
438 
439  using MaskTree = typename ResultTreeType::template ValueConverter<ValueMask>::Type;
441  using MaskLeafVec = std::vector<const typename MaskTree::LeafNodeType*>;
442 
443 
444  SparseMaskedExtractor(const DenseType& dense,
445  const ResultValueType& background,
446  const MaskLeafVec& leafVec
447  ):
448  mDense(dense), mBackground(background), mBBox(dense.bbox()),
449  mLeafVec(leafVec),
450  mResult(new ResultTreeType(mBackground))
451  {}
452 
453  SparseMaskedExtractor(const SparseMaskedExtractor& other, tbb::split):
454  mDense(other.mDense), mBackground(other.mBackground), mBBox(other.mBBox),
455  mLeafVec(other.mLeafVec), mResult( new ResultTreeType(mBackground))
456  {}
457 
458  typename ResultTreeType::Ptr extract(bool threaded = true)
459  {
460  tbb::blocked_range<size_t> range(0, mLeafVec.size());
461 
462  if (threaded) {
463  tbb::parallel_reduce(range, *this);
464  } else {
465  (*this)(range);
466  }
467 
468  return mResult;
469  }
470 
471  // Used in looping over leaf nodes in the masked grid
472  // and using the active mask to select data to
473  void operator()(const tbb::blocked_range<size_t>& range)
474  {
475  ResultLeafNodeType* leaf = nullptr;
476 
477  // loop over all the candidate leafs. Adding only those with 'true' values
478  // to the tree
479 
480  for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
481 
482  const typename MaskTree::LeafNodeType* maskLeaf = mLeafVec[idx];
483 
484  // The bounding box for this leaf
485 
486  openvdb::math::CoordBBox localBBox = maskLeaf->getNodeBoundingBox();
487 
488  // Shrink to the intersection with the dense volume
489 
490  localBBox.intersect(mBBox);
491 
492  // Early out if there was no intersection
493 
494  if (localBBox.empty()) continue;
495 
496  // Reset or allocate the target leaf
497 
498  if (leaf == nullptr) {
499  leaf = new ResultLeafNodeType(maskLeaf->origin(), mBackground);
500  } else {
501  leaf->setOrigin(maskLeaf->origin());
502  leaf->fill(mBackground);
503  leaf->setValuesOff();
504  }
505 
506  // Iterate over the intersecting bounding box
507  // copying active values to the result tree
508 
509  const openvdb::math::Coord start = localBBox.getStart();
510  const openvdb::math::Coord end = localBBox.getEnd();
511 
512  openvdb::math::Coord ijk;
513 
514  if (mDense.memoryLayout() == openvdb::tools::LayoutZYX
515  && maskLeaf->isDense()) {
516 
517  Index offset;
518  const DenseValueType* src;
519  for (ijk[0] = start.x(); ijk[0] < end.x(); ++ijk[0] ) {
520  for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1] ) {
521  for (ijk[2] = start.z(),
522  offset = ResultLeafNodeType::coordToOffset(ijk),
523  src = &mDense.getValue(ijk);
524  ijk[2] < end.z(); ++ijk[2], ++offset, ++src) {
525 
526  // copy into leaf
527  leaf->setValueOn(offset, *src);
528  }
529 
530  }
531  }
532 
533  } else {
534 
535  Index offset;
536  for (ijk[0] = start.x(); ijk[0] < end.x(); ++ijk[0] ) {
537  for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1] ) {
538  for (ijk[2] = start.z(),
539  offset = ResultLeafNodeType::coordToOffset(ijk);
540  ijk[2] < end.z(); ++ijk[2], ++offset) {
541 
542  if (maskLeaf->isValueOn(offset)) {
543  const ResultValueType denseValue = mDense.getValue(ijk);
544  leaf->setValueOn(offset, denseValue);
545  }
546  }
547  }
548  }
549  }
550  // Only add non-empty leafs (empty is defined as all inactive)
551 
552  if (!leaf->isEmpty()) {
553  mResult->addLeaf(leaf);
554  leaf = nullptr;
555  }
556  }
557 
558  // Clean up an unused leaf.
559 
560  if (leaf != nullptr) delete leaf;
561  }
562 
564  mResult->merge(*rhs.mResult);
565  }
566 
567 
568 private:
569  const DenseType& mDense;
570  const ResultValueType mBackground;
571  const openvdb::math::CoordBBox& mBBox;
572  const MaskLeafVec& mLeafVec;
573 
574  typename ResultTreeType::Ptr mResult;
575 
576 }; // class SparseMaskedExtractor
577 
578 
580 template<typename _ResultTreeType, typename DenseValueType>
582 {
583  using ResultTreeType = _ResultTreeType;
584  using ResultLeafNodeType = typename ResultTreeType::LeafNodeType;
585 
586  template<typename CoordOrIndex> inline void
587  operator()(const DenseValueType& a, const CoordOrIndex& offset, ResultLeafNodeType* leaf) const
588  {
589  leaf->setValueOn(offset, a);
590  }
591 };
592 
593 
594 template<typename DenseType, typename MaskTreeType>
596 extractSparseTreeWithMask(const DenseType& dense,
597  const MaskTreeType& maskProxy,
598  const typename DenseType::ValueType& background,
599  bool threaded)
600 {
602  using DenseValueType = typename LeafExtractor::DenseValueType;
603  using ResultTreeType = typename LeafExtractor::ResultTreeType;
604  using MaskLeafVec = typename LeafExtractor::MaskLeafVec;
605  using MaskTree = typename LeafExtractor::MaskTree;
606  using MaskLeafCIter = typename LeafExtractor::MaskLeafCIter;
607  using ExtractionRule = ExtractAll<ResultTreeType, DenseValueType>;
608 
609  // Use Mask tree to hold the topology
610 
611  MaskTree maskTree(maskProxy, false, TopologyCopy());
612 
613  // Construct an array of pointers to the mask leafs.
614 
615  const size_t leafCount = maskTree.leafCount();
616  MaskLeafVec leafarray(leafCount);
617  MaskLeafCIter leafiter = maskTree.cbeginLeaf();
618  for (size_t n = 0; n != leafCount; ++n, ++leafiter) {
619  leafarray[n] = leafiter.getLeaf();
620  }
621 
622 
623  // Extract the data that is masked leaf nodes in the mask.
624 
625  LeafExtractor leafextractor(dense, background, leafarray);
626  typename ResultTreeType::Ptr resultTree = leafextractor.extract(threaded);
627 
628 
629  // Extract data that is masked by tiles in the mask.
630 
631 
632  // Loop over the mask tiles, extracting the data into new trees.
633  // These trees will be leaf-orthogonal to the leafTree (i.e. no leaf
634  // nodes will overlap). Merge these trees into the result.
635 
636  typename MaskTreeType::ValueOnCIter tileIter(maskProxy);
637  tileIter.setMaxDepth(MaskTreeType::ValueOnCIter::LEAF_DEPTH - 1);
638 
639  // Return the leaf tree if the mask had no tiles
640 
641  if (!tileIter) return resultTree;
642 
643  ExtractionRule allrule;
644 
645  // Loop over the tiles in series, but the actual data extraction
646  // is in parallel.
647 
648  CoordBBox bbox;
649  for ( ; tileIter; ++tileIter) {
650 
651  // Find the intersection of the tile with the dense grid.
652 
653  tileIter.getBoundingBox(bbox);
654  bbox.intersect(dense.bbox());
655 
656  if (bbox.empty()) continue;
657 
658  SparseExtractor<ExtractionRule, DenseType> copyData(dense, bbox, allrule, background);
659  typename ResultTreeType::Ptr fromTileTree = copyData.extract(threaded);
660  resultTree->merge(*fromTileTree);
661  }
662 
663  return resultTree;
664 }
665 
666 
670 template<typename _ValueT, typename OpType>
672 {
673 public:
674  using ValueT = _ValueT;
676  using IntType = openvdb::math::Coord::ValueType;
677  using RangeType = tbb::blocked_range2d<IntType, IntType>;
678 
679 private:
680  DenseT& mDense;
681  const OpType& mOp;
682  openvdb::math::CoordBBox mBBox;
683 
684 public:
685  DenseTransformer(DenseT& dense, const openvdb::math::CoordBBox& bbox, const OpType& functor):
686  mDense(dense), mOp(functor), mBBox(dense.bbox())
687  {
688  // The iteration space is the intersection of the
689  // input bbox and the index-space covered by the dense grid
690  mBBox.intersect(bbox);
691  }
692 
694  mDense(other.mDense), mOp(other.mOp), mBBox(other.mBBox) {}
695 
696  void apply(bool threaded = true) {
697 
698  // Early out if the iteration space is empty
699 
700  if (mBBox.empty()) return;
701 
702 
703  const openvdb::math::Coord start = mBBox.getStart();
704  const openvdb::math::Coord end = mBBox.getEnd();
705 
706  // The iteration range only the slower two directions.
707  const RangeType range(start.x(), end.x(), 1,
708  start.y(), end.y(), 1);
709 
710  if (threaded) {
711  tbb::parallel_for(range, *this);
712  } else {
713  (*this)(range);
714  }
715  }
716 
717  void operator()(const RangeType& range) const {
718 
719  // The stride in the z-direction.
720  // Note: the bbox is [inclusive, inclusive]
721 
722  const size_t zlength = size_t(mBBox.max().z() - mBBox.min().z() + 1);
723 
724  const IntType imin = range.rows().begin();
725  const IntType imax = range.rows().end();
726  const IntType jmin = range.cols().begin();
727  const IntType jmax = range.cols().end();
728 
729 
730  openvdb::math::Coord xyz(imin, jmin, mBBox.min().z());
731  for (xyz[0] = imin; xyz[0] != imax; ++xyz[0]) {
732  for (xyz[1] = jmin; xyz[1] != jmax; ++xyz[1]) {
733 
734  mOp.transform(mDense, xyz, zlength);
735  }
736  }
737  }
738 }; // class DenseTransformer
739 
740 
744 template<typename ValueT, typename PointWiseOp>
746 {
747  ContiguousOp(const PointWiseOp& op) : mOp(op){}
748 
750  inline void transform(DenseT& dense, openvdb::math::Coord& ijk, size_t size) const
751  {
752  ValueT* dp = const_cast<ValueT*>(&dense.getValue(ijk));
753 
754  for (size_t offset = 0; offset < size; ++offset) {
755  dp[offset] = mOp(dp[offset]);
756  }
757  }
758 
759  const PointWiseOp mOp;
760 };
761 
762 
764 template<typename ValueT, typename PointwiseOpT>
765 void
767  const openvdb::CoordBBox& bbox,
768  const PointwiseOpT& functor, bool parallel)
769 {
771 
772  // Convert the Op so it operates on a contiguous line in memory
773 
774  OpT op(functor);
775 
776  // Apply to the index space intersection in the dense grid
777  DenseTransformer<ValueT, OpT> transformer(dense, bbox, op);
778  transformer.apply(parallel);
779 }
780 
781 
782 template<typename CompositeMethod, typename _TreeT>
784 {
785 public:
786  using TreeT = _TreeT;
787  using ValueT = typename TreeT::ValueType;
788  using LeafT = typename TreeT::LeafNodeType;
789  using MaskTreeT = typename TreeT::template ValueConverter<ValueMask>::Type;
790  using MaskLeafT = typename MaskTreeT::LeafNodeType;
792  using Index = openvdb::math::Coord::ValueType;
793  using Range3d = tbb::blocked_range3d<Index, Index, Index>;
794 
795  SparseToDenseCompositor(DenseT& dense, const TreeT& source, const TreeT& alpha,
796  const ValueT beta, const ValueT strength) :
797  mDense(dense), mSource(source), mAlpha(alpha), mBeta(beta), mStrength(strength)
798  {}
799 
801  mDense(other.mDense), mSource(other.mSource), mAlpha(other.mAlpha),
802  mBeta(other.mBeta), mStrength(other.mStrength) {}
803 
804 
805  void sparseComposite(bool threaded)
806  {
807  const ValueT beta = mBeta;
808  const ValueT strength = mStrength;
809 
810  // construct a tree that defines the iteration space
811 
812  MaskTreeT maskTree(mSource, false /*background*/, openvdb::TopologyCopy());
813  maskTree.topologyUnion(mAlpha);
814 
815  // Composite regions that are represented by leafnodes in either mAlpha or mSource
816  // Parallelize over bool-leafs
817 
818  openvdb::tree::LeafManager<const MaskTreeT> maskLeafs(maskTree);
819  maskLeafs.foreach(*this, threaded);
820 
821  // Composite regions that are represented by tiles
822  // Parallelize within each tile.
823 
824  typename MaskTreeT::ValueOnCIter citer = maskTree.cbeginValueOn();
825  citer.setMaxDepth(MaskTreeT::ValueOnCIter::LEAF_DEPTH - 1);
826 
827  if (!citer) return;
828 
829  typename tree::ValueAccessor<const TreeT> alphaAccessor(mAlpha);
830  typename tree::ValueAccessor<const TreeT> sourceAccessor(mSource);
831 
832  for (; citer; ++citer) {
833 
834  const openvdb::math::Coord org = citer.getCoord();
835 
836  // Early out if both alpha and source are zero in this tile.
837 
838  const ValueT alphaValue = alphaAccessor.getValue(org);
839  const ValueT sourceValue = sourceAccessor.getValue(org);
840 
841  if (openvdb::math::isZero(alphaValue) &&
842  openvdb::math::isZero(sourceValue)) continue;
843 
844  // Compute overlap of tile with the dense grid
845 
846  openvdb::math::CoordBBox localBBox = citer.getBoundingBox();
847  localBBox.intersect(mDense.bbox());
848 
849  // Early out if there is no intersection
850 
851  if (localBBox.empty()) continue;
852 
853  // Composite the tile-uniform values into the dense grid.
854  compositeFromTile(mDense, localBBox, sourceValue,
855  alphaValue, beta, strength, threaded);
856  }
857  }
858 
859  // Composites leaf values where the alpha values are active.
860  // Used in sparseComposite
861  void inline operator()(const MaskLeafT& maskLeaf, size_t /*i*/) const
862  {
863  using ULeaf = UniformLeaf;
864  openvdb::math::CoordBBox localBBox = maskLeaf.getNodeBoundingBox();
865  localBBox.intersect(mDense.bbox());
866 
867  // Early out for non-overlapping leafs
868 
869  if (localBBox.empty()) return;
870 
871  const openvdb::math::Coord org = maskLeaf.origin();
872  const LeafT* alphaLeaf = mAlpha.probeLeaf(org);
873  const LeafT* sourceLeaf = mSource.probeLeaf(org);
874 
875  if (!sourceLeaf) {
876 
877  // Create a source leaf proxy with the correct value
878  ULeaf uniformSource(mSource.getValue(org));
879 
880  if (!alphaLeaf) {
881 
882  // Create an alpha leaf proxy with the correct value
883  ULeaf uniformAlpha(mAlpha.getValue(org));
884 
885  compositeFromLeaf(mDense, localBBox, uniformSource, uniformAlpha,
886  mBeta, mStrength);
887  } else {
888 
889  compositeFromLeaf(mDense, localBBox, uniformSource, *alphaLeaf,
890  mBeta, mStrength);
891  }
892  } else {
893  if (!alphaLeaf) {
894 
895  // Create an alpha leaf proxy with the correct value
896  ULeaf uniformAlpha(mAlpha.getValue(org));
897 
898  compositeFromLeaf(mDense, localBBox, *sourceLeaf, uniformAlpha,
899  mBeta, mStrength);
900  } else {
901 
902  compositeFromLeaf(mDense, localBBox, *sourceLeaf, *alphaLeaf,
903  mBeta, mStrength);
904  }
905  }
906  }
907  // i.e. it assumes that all valueOff Alpha voxels have value 0.
908 
909  template<typename LeafT1, typename LeafT2>
910  inline static void compositeFromLeaf(DenseT& dense, const openvdb::math::CoordBBox& bbox,
911  const LeafT1& source, const LeafT2& alpha,
912  const ValueT beta, const ValueT strength)
913  {
914  using IntType = openvdb::math::Coord::ValueType;
915 
916  const ValueT sbeta = strength * beta;
917  openvdb::math::Coord ijk = bbox.min();
918 
919 
920  if (alpha.isDense() /*all active values*/) {
921 
922  // Optimal path for dense alphaLeaf
923  const IntType size = bbox.max().z() + 1 - bbox.min().z();
924 
925  for (ijk[0] = bbox.min().x(); ijk[0] < bbox.max().x() + 1; ++ijk[0]) {
926  for (ijk[1] = bbox.min().y(); ijk[1] < bbox.max().y() + 1; ++ijk[1]) {
927 
928  ValueT* d = const_cast<ValueT*>(&dense.getValue(ijk));
929  const ValueT* a = &alpha.getValue(ijk);
930  const ValueT* s = &source.getValue(ijk);
931 
932  for (IntType idx = 0; idx < size; ++idx) {
933  d[idx] = CompositeMethod::apply(d[idx], a[idx], s[idx],
934  strength, beta, sbeta);
935  }
936  }
937  }
938  } else {
939 
940  // AlphaLeaf has non-active cells.
941 
942  for (ijk[0] = bbox.min().x(); ijk[0] < bbox.max().x() + 1; ++ijk[0]) {
943  for (ijk[1] = bbox.min().y(); ijk[1] < bbox.max().y() + 1; ++ijk[1]) {
944  for (ijk[2] = bbox.min().z(); ijk[2] < bbox.max().z() + 1; ++ijk[2]) {
945 
946  if (alpha.isValueOn(ijk)) {
947  dense.setValue(ijk, CompositeMethod::apply(dense.getValue(ijk),
948  alpha.getValue(ijk), source.getValue(ijk), strength, beta, sbeta));
949  }
950  }
951  }
952  }
953  }
954  }
955 
956  inline static void compositeFromTile(DenseT& dense, openvdb::math::CoordBBox& bbox,
957  const ValueT& sourceValue, const ValueT& alphaValue,
958  const ValueT& beta, const ValueT& strength,
959  bool threaded)
960  {
961  using TileTransformer = UniformTransformer;
962  TileTransformer functor(sourceValue, alphaValue, beta, strength);
963 
964  // Transform the data inside the bbox according to the TileTranformer.
965 
966  transformDense(dense, bbox, functor, threaded);
967  }
968 
969  void denseComposite(bool threaded)
970  {
973  const openvdb::math::CoordBBox& bbox = mDense.bbox();
974 
975  Range3d range(bbox.min().x(), bbox.max().x(), LeafT::DIM,
976  bbox.min().y(), bbox.max().y(), LeafT::DIM,
977  bbox.min().z(), bbox.max().z(), LeafT::DIM);
978 
979  // Iterate over the range, compositing into
980  // the dense grid using value accessors for
981  // sparse the grids.
982  if (threaded) {
983  tbb::parallel_for(range, *this);
984  } else {
985  (*this)(range);
986  }
987  }
988 
989  // Composites a dense region using value accessors
990  // into a dense grid
991  void operator()(const Range3d& range) const
992  {
993  // Use value accessors to alpha and source
994 
995  typename tree::ValueAccessor<const TreeT> alphaAccessor(mAlpha);
996  typename tree::ValueAccessor<const TreeT> sourceAccessor(mSource);
997 
998  const ValueT strength = mStrength;
999  const ValueT beta = mBeta;
1000  const ValueT sbeta = strength * beta;
1001 
1002  // Unpack the range3d item.
1003  const Index imin = range.pages().begin();
1004  const Index imax = range.pages().end();
1005 
1006  const Index jmin = range.rows().begin();
1007  const Index jmax = range.rows().end();
1008 
1009  const Index kmin = range.cols().begin();
1010  const Index kmax = range.cols().end();
1011 
1012  openvdb::Coord ijk;
1013  for (ijk[0] = imin; ijk[0] < imax; ++ijk[0]) {
1014  for (ijk[1] = jmin; ijk[1] < jmax; ++ijk[1]) {
1015  for (ijk[2] = kmin; ijk[2] < kmax; ++ijk[2]) {
1016  const ValueT d_old = mDense.getValue(ijk);
1017  const ValueT& alpha = alphaAccessor.getValue(ijk);
1018  const ValueT& src = sourceAccessor.getValue(ijk);
1019 
1020  mDense.setValue(ijk,
1021  CompositeMethod::apply(d_old, alpha, src, strength, beta, sbeta));
1022  }
1023  }
1024  }
1025  }
1026 
1027 private:
1028  // Internal class that wraps the templated composite method
1029  // for use when both alpha and source are uniform over
1030  // a prescribed bbox (e.g. a tile).
1031  class UniformTransformer
1032  {
1033  public:
1034  UniformTransformer(const ValueT& source, const ValueT& alpha, const ValueT& _beta,
1035  const ValueT& _strength) :
1036  mSource(source), mAlpha(alpha), mBeta(_beta),
1037  mStrength(_strength), mSBeta(_strength * _beta)
1038  {}
1039 
1040  ValueT operator()(const ValueT& input) const
1041  {
1042  return CompositeMethod::apply(input, mAlpha, mSource, mStrength, mBeta, mSBeta);
1043  }
1044 
1045  private:
1046  const ValueT mSource;
1047  const ValueT mAlpha;
1048  const ValueT mBeta;
1049  const ValueT mStrength;
1050  const ValueT mSBeta;
1051  };
1052 
1053 
1054  // Simple Class structure that mimics a leaf
1055  // with uniform values. Holds LeafT::DIM copies
1056  // of a value in an array.
1057  struct Line { ValueT mValues[LeafT::DIM]; };
1058  class UniformLeaf : private Line
1059  {
1060  public:
1061  using ValueT = typename LeafT::ValueType;
1062 
1063  using BaseT = Line;
1064  UniformLeaf(const ValueT& value) : BaseT(init(value)) {}
1065 
1066  static const BaseT init(const ValueT& value) {
1067  BaseT tmp;
1068  for (openvdb::Index i = 0; i < LeafT::DIM; ++i) {
1069  tmp.mValues[i] = value;
1070  }
1071  return tmp;
1072  }
1073 
1074  bool isDense() const { return true; }
1075  bool isValueOn(openvdb::math::Coord&) const { return true; }
1076 
1077  const ValueT& getValue(const openvdb::math::Coord&) const { return BaseT::mValues[0]; }
1078  };
1079 
1080 private:
1081  DenseT& mDense;
1082  const TreeT& mSource;
1083  const TreeT& mAlpha;
1084  ValueT mBeta;
1085  ValueT mStrength;
1086 }; // class SparseToDenseCompositor
1087 
1088 
1089 namespace ds
1090 {
1092  template<typename ValueT>
1094  struct OpOver
1095  {
1096  static inline ValueT apply(const ValueT u, const ValueT alpha,
1097  const ValueT v,
1098  const ValueT strength,
1099  const ValueT beta,
1100  const ValueT /*sbeta*/)
1101  { return (u + strength * alpha * (beta * v - u)); }
1102  };
1103 
1104  template<typename ValueT>
1105  struct OpAdd
1106  {
1107  static inline ValueT apply(const ValueT u, const ValueT alpha,
1108  const ValueT v,
1109  const ValueT /*strength*/,
1110  const ValueT /*beta*/,
1111  const ValueT sbeta)
1112  { return (u + sbeta * alpha * v); }
1113  };
1114 
1115  template<typename ValueT>
1116  struct OpSub
1117  {
1118  static inline ValueT apply(const ValueT u, const ValueT alpha,
1119  const ValueT v,
1120  const ValueT /*strength*/,
1121  const ValueT /*beta*/,
1122  const ValueT sbeta)
1123  { return (u - sbeta * alpha * v); }
1124  };
1125 
1126  template<typename ValueT>
1127  struct OpMin
1128  {
1129  static inline ValueT apply(const ValueT u, const ValueT alpha,
1130  const ValueT v,
1131  const ValueT s /*trength*/,
1132  const ValueT beta,
1133  const ValueT /*sbeta*/)
1134  { return ( ( 1 - s * alpha) * u + s * alpha * std::min(u, beta * v) ); }
1135  };
1136 
1137  template<typename ValueT>
1138  struct OpMax
1139  {
1140  static inline ValueT apply(const ValueT u, const ValueT alpha,
1141  const ValueT v,
1142  const ValueT s/*trength*/,
1143  const ValueT beta,
1144  const ValueT /*sbeta*/)
1145  { return ( ( 1 - s * alpha ) * u + s * alpha * std::min(u, beta * v) ); }
1146  };
1147 
1148  template<typename ValueT>
1149  struct OpMult
1150  {
1151  static inline ValueT apply(const ValueT u, const ValueT alpha,
1152  const ValueT v,
1153  const ValueT s/*trength*/,
1154  const ValueT /*beta*/,
1155  const ValueT sbeta)
1156  { return ( ( 1 + alpha * (sbeta * v - s)) * u ); }
1157  };
1159 
1161  template<DSCompositeOp OP, typename ValueT>
1164 
1165  template<typename ValueT>
1167 
1168  template<typename ValueT>
1170 
1171  template<typename ValueT>
1173 
1174  template<typename ValueT>
1176 
1177  template<typename ValueT>
1179 
1180  template<typename ValueT>
1183 
1184 } // namespace ds
1185 
1186 
1187 template<DSCompositeOp OpT, typename TreeT>
1188 inline void
1191  const TreeT& source, const TreeT& alpha,
1192  const typename TreeT::ValueType beta,
1193  const typename TreeT::ValueType strength,
1194  bool threaded)
1195 {
1196  using ValueT = typename TreeT::ValueType;
1198  using Method = typename Translator::OpT;
1199 
1200  if (openvdb::math::isZero(strength)) return;
1201 
1202  SparseToDenseCompositor<Method, TreeT> tool(dense, source, alpha, beta, strength);
1203 
1204  if (openvdb::math::isZero(alpha.background()) &&
1205  openvdb::math::isZero(source.background()))
1206  {
1207  // Use the sparsity of (alpha U source) as the iteration space.
1208  tool.sparseComposite(threaded);
1209  } else {
1210  // Use the bounding box of dense as the iteration space.
1211  tool.denseComposite(threaded);
1212  }
1213 }
1214 
1215 } // namespace tools
1216 } // namespace OPENVDB_VERSION_NAME
1217 } // namespace openvdb
1218 
1219 #endif //OPENVDB_TOOLS_DENSESPARSETOOLS_HAS_BEEN_INCLUDED
1220 
1221 // Copyright (c) 2012-2019 DreamWorks Animation LLC
1222 // All rights reserved. This software is distributed under the
1223 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
void operator()(const Range3d &range)
Definition: DenseSparseTools.h:298
SparseExtractor(const DenseType &dense, const openvdb::math::CoordBBox &bbox, const OpType &functor, const ResultValueType background)
Definition: DenseSparseTools.h:232
LeafNodeT * getLeaf() const
Return the leaf node to which the iterator is pointing.
Definition: TreeIterator.h:1261
DenseTransformer(const DenseTransformer &other)
Definition: DenseSparseTools.h:693
static ValueT apply(const ValueT u, const ValueT alpha, const ValueT v, const ValueT s, const ValueT, const ValueT sbeta)
Definition: DenseSparseTools.h:1151
Definition: DenseSparseTools.h:1138
typename ResultTreeType::template ValueConverter< ValueMask >::Type MaskTree
Definition: DenseSparseTools.h:439
static void compositeFromTile(DenseT &dense, openvdb::math::CoordBBox &bbox, const ValueT &sourceValue, const ValueT &alphaValue, const ValueT &beta, const ValueT &strength, bool threaded)
Definition: DenseSparseTools.h:956
openvdb::math::Coord::ValueType IntType
Definition: DenseSparseTools.h:676
Definition: DenseSparseTools.h:783
typename ResultTreeType::LeafNodeType ResultLeafNodeType
Definition: DenseSparseTools.h:584
void join(SparseMaskedExtractor &rhs)
Definition: DenseSparseTools.h:563
ResultTreeType::Ptr extract(bool threaded=true)
Definition: DenseSparseTools.h:458
Definition: DenseSparseTools.h:177
void operator()(const MaskLeafT &maskLeaf, size_t) const
Definition: DenseSparseTools.h:861
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:109
ResultTreeType::Ptr extract(bool threaded=true)
Definition: DenseSparseTools.h:256
static ValueT apply(const ValueT u, const ValueT alpha, const ValueT v, const ValueT s, const ValueT beta, const ValueT)
Definition: DenseSparseTools.h:1129
static ValueT apply(const ValueT u, const ValueT alpha, const ValueT v, const ValueT strength, const ValueT beta, const ValueT)
Definition: DenseSparseTools.h:1096
LeafCIter cbeginLeaf() const
Return an iterator over all leaf nodes in this tree.
Definition: Tree.h:1181
DSConverter< DenseType, MaskTreeType >::Type::Ptr extractSparseTreeWithMask(const DenseType &dense, const MaskTreeType &mask, const typename DenseType::ValueType &background, bool threaded=true)
Copy data from the intersection of a sparse tree and a dense input grid. The resulting tree has the s...
Definition: DenseSparseTools.h:596
typename MaskTree::LeafCIter MaskLeafCIter
Definition: DenseSparseTools.h:440
void compositeToDense(Dense< typename TreeT::ValueType, LayoutZYX > &dense, const TreeT &source, const TreeT &alpha, const typename TreeT::ValueType beta, const typename TreeT::ValueType strength, bool threaded=true)
Composite data from a sparse tree into a dense array of the same value type.
Definition: DenseSparseTools.h:1189
SparseExtractor(SparseExtractor &other, tbb::split)
Definition: DenseSparseTools.h:248
This file defines a simple dense grid and efficient converters to and from VDB grids.
typename TreeT::ValueType ValueT
Definition: DenseSparseTools.h:787
static ValueT apply(const ValueT u, const ValueT alpha, const ValueT v, const ValueT, const ValueT, const ValueT sbeta)
Definition: DenseSparseTools.h:1118
Definition: DenseSparseTools.h:1105
ResultValueType DenseValueType
Definition: DenseSparseTools.h:437
Definition: DenseSparseTools.h:177
OpType::ResultTreeType::Ptr extractSparseTree(const DenseType &dense, const OpType &functor, const typename OpType::ResultValueType &background, bool threaded=true)
Selectively extract and transform data from a dense grid, producing a sparse tree with leaf nodes onl...
Definition: DenseSparseTools.h:412
Definition: DenseSparseTools.h:1116
typename ResultTreeType::ValueType ResultValueType
Definition: DenseSparseTools.h:436
typename ResultTreeType::LeafNodeType ResultLeafNodeType
Definition: DenseSparseTools.h:435
a wrapper struct used to avoid unnecessary computation of memory access from Coord when all offsets a...
Definition: DenseSparseTools.h:745
Functor-based class used to extract data that satisfies some criteria defined by the embedded OpType ...
Definition: DenseSparseTools.h:200
typename DSConverter< DenseType, MaskTreeType >::Type _ResultTreeType
Definition: DenseSparseTools.h:433
Definition: DenseSparseTools.h:177
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:125
openvdb::math::Coord::ValueType Index
Definition: DenseSparseTools.h:203
Index32 leafCount() const override
Return the number of leaf nodes.
Definition: Tree.h:368
typename DenseType::ValueType DenseValueType
Definition: DenseSparseTools.h:205
void operator()(const DenseValueType &a, const CoordOrIndex &offset, ResultLeafNodeType *leaf) const
Definition: DenseSparseTools.h:587
void transform(DenseT &dense, openvdb::math::Coord &ijk, size_t size) const
Definition: DenseSparseTools.h:750
typename OpType::ResultTreeType ResultTreeType
Definition: DenseSparseTools.h:206
Index32 Index
Definition: Types.h:61
SparseMaskedExtractor(const SparseMaskedExtractor &other, tbb::split)
Definition: DenseSparseTools.h:453
SparseToDenseCompositor(DenseT &dense, const TreeT &source, const TreeT &alpha, const ValueT beta, const ValueT strength)
Definition: DenseSparseTools.h:795
SparseMaskedExtractor(const DenseType &dense, const ResultValueType &background, const MaskLeafVec &leafVec)
Definition: DenseSparseTools.h:444
static ValueT apply(const ValueT u, const ValueT alpha, const ValueT v, const ValueT s, const ValueT beta, const ValueT)
Definition: DenseSparseTools.h:1140
Definition: DenseSparseTools.h:177
a simple utility class used by extractSparseTreeWithMask
Definition: DenseSparseTools.h:581
DenseTransformer(DenseT &dense, const openvdb::math::CoordBBox &bbox, const OpType &functor)
Definition: DenseSparseTools.h:685
Definition: Exceptions.h:40
_ResultTreeType ResultTreeType
Definition: DenseSparseTools.h:583
typename TreeType::template ValueConverter< ValueType >::Type Type
Definition: DenseSparseTools.h:129
void operator()(const tbb::blocked_range< size_t > &range)
Definition: DenseSparseTools.h:473
const ValueType & getValue(const Coord &xyz) const
Return the value of the voxel at the given coordinates.
Definition: ValueAccessor.h:257
void setValue(size_t offset, const ValueT &value)
Set the value of the voxel at the given array offset.
Definition: Dense.h:287
void sparseComposite(bool threaded)
Definition: DenseSparseTools.h:805
Class that applies a functor to the index space intersection of a prescribed bounding box and the den...
Definition: DenseSparseTools.h:671
tbb::blocked_range3d< Index, Index, Index > Range3d
Definition: DenseSparseTools.h:211
std::vector< const typename MaskTree::LeafNodeType * > MaskLeafVec
Definition: DenseSparseTools.h:441
typename ResultTreeType::ValueType ResultValueType
Definition: DenseSparseTools.h:207
tree::Tree4< ValueMask, 5, 4, 3 >::Type MaskTree
Definition: openvdb.h:54
typename DenseType::ValueType ValueType
Definition: DenseSparseTools.h:128
Definition: DenseSparseTools.h:126
static void compositeFromLeaf(DenseT &dense, const openvdb::math::CoordBBox &bbox, const LeafT1 &source, const LeafT2 &alpha, const ValueT beta, const ValueT strength)
Definition: DenseSparseTools.h:910
void operator()(const Range3d &range) const
Definition: DenseSparseTools.h:991
LeafIteratorBase< const Tree, typename RootNodeType::ChildOnCIter > LeafCIter
Iterator over all leaf nodes in this tree.
Definition: Tree.h:1167
DSCompositeOp
Definition: DenseSparseTools.h:176
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:129
openvdb::math::Coord::ValueType Index
Definition: DenseSparseTools.h:792
Definition: DenseSparseTools.h:177
typename RootNodeType::LeafNodeType LeafNodeType
Definition: Tree.h:212
const ValueT & getValue(size_t offset) const
Return a const reference to the value of the voxel at the given array offset.
Definition: Dense.h:290
Definition: Tree.h:203
bool isZero(const Type &x)
Return true if x is exactly equal to zero.
Definition: Math.h:308
tbb::blocked_range3d< Index, Index, Index > Range3d
Definition: DenseSparseTools.h:793
void apply(bool threaded=true)
Definition: DenseSparseTools.h:696
Tag dispatch class that distinguishes topology copy constructors from deep copy constructors.
Definition: Types.h:750
typename ResultTreeType::template ValueConverter< ValueMask >::Type MaskTree
Definition: DenseSparseTools.h:209
void join(SparseExtractor &rhs)
Definition: DenseSparseTools.h:404
void denseComposite(bool threaded)
Definition: DenseSparseTools.h:969
Definition: Dense.h:95
SparseExtractor(const DenseType &dense, const OpType &functor, const ResultValueType background)
Definition: DenseSparseTools.h:223
Translator that converts an enum to compositing functor types.
Definition: DenseSparseTools.h:1163
Definition: Exceptions.h:92
tbb::blocked_range2d< IntType, IntType > RangeType
Definition: DenseSparseTools.h:677
SparseToDenseCompositor(const SparseToDenseCompositor &other)
Definition: DenseSparseTools.h:800
Definition: DenseSparseTools.h:1127
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:177
typename MaskTreeT::LeafNodeType MaskLeafT
Definition: DenseSparseTools.h:790
A LeafManager manages a linear array of pointers to a given tree&#39;s leaf nodes, as well as optional au...
typename TreeT::template ValueConverter< ValueMask >::Type MaskTreeT
Definition: DenseSparseTools.h:789
Definition: DenseSparseTools.h:177
static ValueT apply(const ValueT u, const ValueT alpha, const ValueT v, const ValueT, const ValueT, const ValueT sbeta)
Definition: DenseSparseTools.h:1107
Dense is a simple dense grid API used by the CopyToDense and CopyFromDense classes defined below...
Definition: Dense.h:209
typename ResultTreeType::LeafNodeType ResultLeafNodeType
Definition: DenseSparseTools.h:208
Functor-based class used to extract data from a dense grid, at the index-space intersection with a su...
Definition: DenseSparseTools.h:430
void operator()(const RangeType &range) const
Definition: DenseSparseTools.h:717
void transformDense(Dense< ValueT, openvdb::tools::LayoutZYX > &dense, const openvdb::CoordBBox &bbox, const PointwiseOpT &functor, bool parallel)
Apply a point-wise functor to the intersection of a dense grid and a given bounding box...
Definition: DenseSparseTools.h:766
typename TreeT::LeafNodeType LeafT
Definition: DenseSparseTools.h:788
_ValueT ValueT
Definition: DenseSparseTools.h:674
Point wise methods used to apply various compositing operations.
Definition: DenseSparseTools.h:1094
Definition: DenseSparseTools.h:1149
Definition: DenseSparseTools.h:177
const PointWiseOp mOp
Definition: DenseSparseTools.h:759
_TreeT TreeT
Definition: DenseSparseTools.h:786
_ResultTreeType ResultTreeType
Definition: DenseSparseTools.h:434
ContiguousOp(const PointWiseOp &op)
Definition: DenseSparseTools.h:747