1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 //
4 /// @file ValueTransformer.h
5 ///
6 /// @author Peter Cucka
7 ///
8 /// tools::foreach() and tools::transformValues() transform the values in a grid
9 /// by iterating over the grid with a user-supplied iterator and applying a
10 /// user-supplied functor at each step of the iteration.  With tools::foreach(),
11 /// the transformation is done in-place on the input grid, whereas with
12 /// tools::transformValues(), transformed values are written to an output grid
13 /// (which can, for example, have a different value type than the input grid).
14 /// Both functions can optionally transform multiple values of the grid in parallel.
15 ///
16 /// tools::accumulate() can be used to accumulate the results of applying a functor
17 /// at each step of a grid iteration.  (The functor is responsible for storing and
18 /// updating intermediate results.)  When the iteration is done serially the behavior is
19 /// the same as with tools::foreach(), but when multiple values are processed in parallel,
20 /// an additional step is performed: when any two threads finish processing,
21 /// @c op.join(otherOp) is called on one thread's functor to allow it to coalesce
22 /// its intermediate result with the other thread's.
23 ///
24 /// Finally, tools::setValueOnMin(), tools::setValueOnMax(), tools::setValueOnSum()
25 /// and tools::setValueOnMult() are wrappers around Tree::modifyValue() (or
26 /// ValueAccessor::modifyValue()) for some commmon in-place operations.
27 /// These are typically significantly faster than calling getValue() followed by setValue().
28 
29 #ifndef OPENVDB_TOOLS_VALUETRANSFORMER_HAS_BEEN_INCLUDED
30 #define OPENVDB_TOOLS_VALUETRANSFORMER_HAS_BEEN_INCLUDED
31 
32 #include <algorithm> // for std::min(), std::max()
33 #include <tbb/parallel_for.h>
34 #include <tbb/parallel_reduce.h>
35 #include <openvdb/Types.h>
36 #include <openvdb/Grid.h>
37 #include <openvdb/openvdb.h>
38 
39 
40 namespace openvdb {
41 OPENVDB_USE_VERSION_NAMESPACE
42 namespace OPENVDB_VERSION_NAME {
43 namespace tools {
44 
45 /// Iterate over a grid and at each step call @c op(iter).
46 /// @param iter      an iterator over a grid or its tree (@c Grid::ValueOnCIter,
47 ///                  @c Tree::NodeIter, etc.)
48 /// @param op        a functor of the form <tt>void op(const IterT&)</tt>, where @c IterT is
49 ///                  the type of @a iter
50 /// @param threaded  if true, transform multiple values of the grid in parallel
51 /// @param shareOp   if true and @a threaded is true, all threads use the same functor;
52 ///                  otherwise, each thread gets its own copy of the @e original functor
53 ///
54 /// @par Example:
55 /// Multiply all values (both set and unset) of a scalar, floating-point grid by two.
56 /// @code
57 /// struct Local {
58 ///     static inline void op(const FloatGrid::ValueAllIter& iter) {
59 ///         iter.setValue(*iter * 2);
60 ///     }
61 /// };
62 /// FloatGrid grid = ...;
63 /// tools::foreach(grid.beginValueAll(), Local::op);
64 /// @endcode
65 ///
66 /// @par Example:
67 /// Rotate all active vectors of a vector grid by 45 degrees about the y axis.
68 /// @code
69 /// namespace {
70 ///     struct MatMul {
71 ///         math::Mat3s M;
72 ///         MatMul(const math::Mat3s& mat): M(mat) {}
73 ///         inline void operator()(const VectorGrid::ValueOnIter& iter) const {
74 ///             iter.setValue(M.transform(*iter));
75 ///         }
76 ///     };
77 /// }
78 /// {
79 ///     VectorGrid grid = ...;
80 ///     tools::foreach(grid.beginValueOn(),
81 ///         MatMul(math::rotation<math::Mat3s>(math::Y, M_PI_4)));
82 /// }
83 /// @endcode
84 ///
85 /// @note For more complex operations that require finer control over threading,
86 /// consider using @c tbb::parallel_for() or @c tbb::parallel_reduce() in conjunction
87 /// with a tree::IteratorRange that wraps a grid or tree iterator.
88 template<typename IterT, typename XformOp>
89 inline void foreach(const IterT& iter, XformOp& op,
90     bool threaded = true, bool shareOp = true);
91 
92 template<typename IterT, typename XformOp>
93 inline void foreach(const IterT& iter, const XformOp& op,
94     bool threaded = true, bool shareOp = true);
95 
96 
97 /// Iterate over a grid and at each step call <tt>op(iter, accessor)</tt> to
98 /// populate (via the accessor) the given output grid, whose @c ValueType
99 /// need not be the same as the input grid's.
100 /// @param inIter    a non-<tt>const</tt> or (preferably) @c const iterator over an
101 ///                  input grid or its tree (@c Grid::ValueOnCIter, @c Tree::NodeIter, etc.)
102 /// @param outGrid   an empty grid to be populated
103 /// @param op        a functor of the form
104 ///                  <tt>void op(const InIterT&, OutGridT::ValueAccessor&)</tt>,
105 ///                  where @c InIterT is the type of @a inIter
106 /// @param threaded  if true, transform multiple values of the input grid in parallel
107 /// @param shareOp   if true and @a threaded is true, all threads use the same functor;
108 ///                  otherwise, each thread gets its own copy of the @e original functor
109 /// @param merge     how to merge intermediate results from multiple threads (see Types.h)
110 ///
111 /// @par Example:
112 /// Populate a scalar floating-point grid with the lengths of the vectors from all
113 /// active voxels of a vector-valued input grid.
114 /// @code
115 /// struct Local {
116 ///     static void op(
117 ///         const Vec3fGrid::ValueOnCIter& iter,
118 ///         FloatGrid::ValueAccessor& accessor)
119 ///     {
120 ///         if (iter.isVoxelValue()) { // set a single voxel
121 ///             accessor.setValue(iter.getCoord(), iter->length());
122 ///         } else { // fill an entire tile
123 ///             CoordBBox bbox;
124 ///             iter.getBoundingBox(bbox);
125 ///             accessor.getTree()->fill(bbox, iter->length());
126 ///         }
127 ///     }
128 /// };
129 /// Vec3fGrid inGrid = ...;
130 /// FloatGrid outGrid;
131 /// tools::transformValues(inGrid.cbeginValueOn(), outGrid, Local::op);
132 /// @endcode
133 ///
134 /// @note For more complex operations that require finer control over threading,
135 /// consider using @c tbb::parallel_for() or @c tbb::parallel_reduce() in conjunction
136 /// with a tree::IteratorRange that wraps a grid or tree iterator.
137 template<typename InIterT, typename OutGridT, typename XformOp>
138 inline void transformValues(const InIterT& inIter, OutGridT& outGrid,
139     XformOp& op, bool threaded = true, bool shareOp = true,
140     MergePolicy merge = MERGE_ACTIVE_STATES);
141 
142 #ifndef _MSC_VER
143 template<typename InIterT, typename OutGridT, typename XformOp>
144 inline void transformValues(const InIterT& inIter, OutGridT& outGrid,
145     const XformOp& op, bool threaded = true, bool shareOp = true,
146     MergePolicy merge = MERGE_ACTIVE_STATES);
147 #endif
148 
149 
150 /// Iterate over a grid and at each step call @c op(iter).  If threading is enabled,
151 /// call @c op.join(otherOp) to accumulate intermediate results from pairs of threads.
152 /// @param iter      an iterator over a grid or its tree (@c Grid::ValueOnCIter,
153 ///                  @c Tree::NodeIter, etc.)
154 /// @param op        a functor with a join method of the form <tt>void join(XformOp&)</tt>
155 ///                  and a call method of the form <tt>void op(const IterT&)</tt>,
156 ///                  where @c IterT is the type of @a iter
157 /// @param threaded  if true, transform multiple values of the grid in parallel
158 /// @note If @a threaded is true, each thread gets its own copy of the @e original functor.
159 /// The order in which threads are joined is unspecified.
160 /// @note If @a threaded is false, the join method is never called.
161 ///
162 /// @par Example:
163 /// Compute the average of the active values of a scalar, floating-point grid
164 /// using the math::Stats class.
165 /// @code
166 /// namespace {
167 ///     struct Average {
168 ///         math::Stats stats;
169 ///
170 ///         // Accumulate voxel and tile values into this functor's Stats object.
171 ///         inline void operator()(const FloatGrid::ValueOnCIter& iter) {
172 ///             if (iter.isVoxelValue()) stats.add(*iter);
173 ///             else stats.add(*iter, iter.getVoxelCount());
174 ///         }
175 ///
176 ///         // Accumulate another functor's Stats object into this functor's.
177 ///         inline void join(Average& other) { stats.add(other.stats); }
178 ///
179 ///         // Return the cumulative result.
180 ///         inline double average() const { return stats.mean(); }
181 ///     };
182 /// }
183 /// {
184 ///     FloatGrid grid = ...;
185 ///     Average op;
186 ///     tools::accumulate(grid.cbeginValueOn(), op);
187 ///     double average = op.average();
188 /// }
189 /// @endcode
190 ///
191 /// @note For more complex operations that require finer control over threading,
192 /// consider using @c tbb::parallel_for() or @c tbb::parallel_reduce() in conjunction
193 /// with a tree::IteratorRange that wraps a grid or tree iterator.
194 template<typename IterT, typename XformOp>
195 inline void accumulate(const IterT& iter, XformOp& op, bool threaded = true);
196 
197 
198 /// @brief Set the value of the voxel at the given coordinates in @a tree to
199 /// the minimum of its current value and @a value, and mark the voxel as active.
200 /// @details This is typically significantly faster than calling getValue()
201 /// followed by setValueOn().
202 /// @note @a TreeT can be either a Tree or a ValueAccessor.
203 template<typename TreeT>
204 void setValueOnMin(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value);
205 
206 /// @brief Set the value of the voxel at the given coordinates in @a tree to
207 /// the maximum of its current value and @a value, and mark the voxel as active.
208 /// @details This is typically significantly faster than calling getValue()
209 /// followed by setValueOn().
210 /// @note @a TreeT can be either a Tree or a ValueAccessor.
211 template<typename TreeT>
212 void setValueOnMax(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value);
213 
214 /// @brief Set the value of the voxel at the given coordinates in @a tree to
215 /// the sum of its current value and @a value, and mark the voxel as active.
216 /// @details This is typically significantly faster than calling getValue()
217 /// followed by setValueOn().
218 /// @note @a TreeT can be either a Tree or a ValueAccessor.
219 template<typename TreeT>
220 void setValueOnSum(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value);
221 
222 /// @brief Set the value of the voxel at the given coordinates in @a tree to
223 /// the product of its current value and @a value, and mark the voxel as active.
224 /// @details This is typically significantly faster than calling getValue()
225 /// followed by setValueOn().
226 /// @note @a TreeT can be either a Tree or a ValueAccessor.
227 template<typename TreeT>
228 void setValueOnMult(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value);
229 
230 
231 ////////////////////////////////////////
232 
233 
234 namespace valxform {
235 
236 template<typename ValueType>
237 struct MinOp {
238     const ValueType val;
MinOpMinOp239     MinOp(const ValueType& v): val(v) {}
operatorMinOp240     inline void operator()(ValueType& v) const { v = std::min<ValueType>(v, val); }
241 };
242 
243 template<typename ValueType>
244 struct MaxOp {
245     const ValueType val;
MaxOpMaxOp246     MaxOp(const ValueType& v): val(v) {}
operatorMaxOp247     inline void operator()(ValueType& v) const { v = std::max<ValueType>(v, val); }
248 };
249 
250 template<typename ValueType>
251 struct SumOp {
252     const ValueType val;
SumOpSumOp253     SumOp(const ValueType& v): val(v) {}
operatorSumOp254     inline void operator()(ValueType& v) const { v += val; }
255 };
256 
257 template<>
258 struct SumOp<bool> {
259     using ValueType = bool;
260     const ValueType val;
261     SumOp(const ValueType& v): val(v) {}
262     inline void operator()(ValueType& v) const { v = v || val; }
263 };
264 
265 template<typename ValueType>
266 struct MultOp {
267     const ValueType val;
268     MultOp(const ValueType& v): val(v) {}
269     inline void operator()(ValueType& v) const { v *= val; }
270 };
271 
272 template<>
273 struct MultOp<bool> {
274     using ValueType = bool;
275     const ValueType val;
276     MultOp(const ValueType& v): val(v) {}
277     inline void operator()(ValueType& v) const { v = v && val; }
278 };
279 
280 }
281 
282 
283 template<typename TreeT>
284 void
285 setValueOnMin(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value)
286 {
287     tree.modifyValue(xyz, valxform::MinOp<typename TreeT::ValueType>(value));
288 }
289 
290 
291 template<typename TreeT>
292 void
293 setValueOnMax(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value)
294 {
295     tree.modifyValue(xyz, valxform::MaxOp<typename TreeT::ValueType>(value));
296 }
297 
298 
299 template<typename TreeT>
300 void
301 setValueOnSum(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value)
302 {
303     tree.modifyValue(xyz, valxform::SumOp<typename TreeT::ValueType>(value));
304 }
305 
306 
307 template<typename TreeT>
308 void
309 setValueOnMult(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value)
310 {
311     tree.modifyValue(xyz, valxform::MultOp<typename TreeT::ValueType>(value));
312 }
313 
314 
315 ////////////////////////////////////////
316 
317 
318 namespace valxform {
319 
320 template<typename IterT, typename OpT>
321 class SharedOpApplier
322 {
323 public:
324     using IterRange = typename tree::IteratorRange<IterT>;
325 
326     SharedOpApplier(const IterT& iter, OpT& op): mIter(iter), mOp(op) {}
327 
328     void process(bool threaded = true)
329     {
330         IterRange range(mIter);
331         if (threaded) {
332             tbb::parallel_for(range, *this);
333         } else {
334             (*this)(range);
335         }
336     }
337 
338     void operator()(IterRange& r) const { for ( ; r; ++r) mOp(r.iterator()); }
339 
340 private:
341     IterT mIter;
342     OpT& mOp;
343 };
344 
345 
346 template<typename IterT, typename OpT>
347 class CopyableOpApplier
348 {
349 public:
350     using IterRange = typename tree::IteratorRange<IterT>;
351 
352     CopyableOpApplier(const IterT& iter, const OpT& op): mIter(iter), mOp(op), mOrigOp(&op) {}
353 
354     // When splitting this task, give the subtask a copy of the original functor,
355     // not of this task's functor, which might have been modified arbitrarily.
356     CopyableOpApplier(const CopyableOpApplier& other):
357         mIter(other.mIter), mOp(*other.mOrigOp), mOrigOp(other.mOrigOp) {}
358 
359     void process(bool threaded = true)
360     {
361         IterRange range(mIter);
362         if (threaded) {
363             tbb::parallel_for(range, *this);
364         } else {
365             (*this)(range);
366         }
367     }
368 
369     void operator()(IterRange& r) const { for ( ; r; ++r) mOp(r.iterator()); }
370 
371 private:
372     IterT mIter;
373     OpT mOp; // copy of original functor
374     OpT const * const mOrigOp; // pointer to original functor
375 };
376 
377 } // namespace valxform
378 
379 
380 template<typename IterT, typename XformOp>
381 inline void
382 foreach(const IterT& iter, XformOp& op, bool threaded, bool shared)
383 {
384     if (shared) {
385         typename valxform::SharedOpApplier<IterT, XformOp> proc(iter, op);
386         proc.process(threaded);
387     } else {
388         using Processor = typename valxform::CopyableOpApplier<IterT, XformOp>;
389         Processor proc(iter, op);
390         proc.process(threaded);
391     }
392 }
393 
394 template<typename IterT, typename XformOp>
395 inline void
396 foreach(const IterT& iter, const XformOp& op, bool threaded, bool /*shared*/)
397 {
398     // Const ops are shared across threads, not copied.
399     typename valxform::SharedOpApplier<IterT, const XformOp> proc(iter, op);
400     proc.process(threaded);
401 }
402 
403 
404 ////////////////////////////////////////
405 
406 
407 namespace valxform {
408 
409 template<typename InIterT, typename OutTreeT, typename OpT>
410 class SharedOpTransformer
411 {
412 public:
413     using InTreeT = typename InIterT::TreeT;
414     using IterRange = typename tree::IteratorRange<InIterT>;
415     using OutValueT = typename OutTreeT::ValueType;
416 
417     SharedOpTransformer(const InIterT& inIter, OutTreeT& outTree, OpT& op, MergePolicy merge):
418         mIsRoot(true),
419         mInputIter(inIter),
420         mInputTree(inIter.getTree()),
421         mOutputTree(&outTree),
422         mOp(op),
423         mMergePolicy(merge)
424     {
425         if (static_cast<const void*>(mInputTree) == static_cast<void*>(mOutputTree)) {
426             OPENVDB_LOG_INFO("use tools::foreach(), not transformValues(),"
427                 " to transform a grid in place");
428         }
429     }
430 
431     /// Splitting constructor
432     SharedOpTransformer(SharedOpTransformer& other, tbb::split):
433         mIsRoot(false),
434         mInputIter(other.mInputIter),
435         mInputTree(other.mInputTree),
436         mOutputTree(new OutTreeT(zeroVal<OutValueT>())),
437         mOp(other.mOp),
438         mMergePolicy(other.mMergePolicy)
439         {}
440 
441     ~SharedOpTransformer()
442     {
443         // Delete the output tree only if it was allocated locally
444         // (the top-level output tree was supplied by the caller).
445         if (!mIsRoot) {
446             delete mOutputTree;
447             mOutputTree = nullptr;
448         }
449     }
450 
451     void process(bool threaded = true)
452     {
453         if (!mInputTree || !mOutputTree) return;
454 
455         IterRange range(mInputIter);
456 
457         // Independently transform elements in the iterator range,
458         // either in parallel or serially.
459         if (threaded) {
460             tbb::parallel_reduce(range, *this);
461         } else {
462             (*this)(range);
463         }
464     }
465 
466     /// Transform each element in the given range.
467     void operator()(IterRange& range) const
468     {
469         if (!mOutputTree) return;
470         typename tree::ValueAccessor<OutTreeT> outAccessor(*mOutputTree);
471         for ( ; range; ++range) {
472             mOp(range.iterator(), outAccessor);
473         }
474     }
475 
476     void join(const SharedOpTransformer& other)
477     {
478         if (mOutputTree && other.mOutputTree) {
479             mOutputTree->merge(*other.mOutputTree, mMergePolicy);
480         }
481     }
482 
483 private:
484     bool mIsRoot;
485     InIterT mInputIter;
486     const InTreeT* mInputTree;
487     OutTreeT* mOutputTree;
488     OpT& mOp;
489     MergePolicy mMergePolicy;
490 }; // class SharedOpTransformer
491 
492 
493 template<typename InIterT, typename OutTreeT, typename OpT>
494 class CopyableOpTransformer
495 {
496 public:
497     using InTreeT = typename InIterT::TreeT;
498     using IterRange = typename tree::IteratorRange<InIterT>;
499     using OutValueT = typename OutTreeT::ValueType;
500 
501     CopyableOpTransformer(const InIterT& inIter, OutTreeT& outTree,
502         const OpT& op, MergePolicy merge):
503         mIsRoot(true),
504         mInputIter(inIter),
505         mInputTree(inIter.getTree()),
506         mOutputTree(&outTree),
507         mOp(op),
508         mOrigOp(&op),
509         mMergePolicy(merge)
510     {
511         if (static_cast<const void*>(mInputTree) == static_cast<void*>(mOutputTree)) {
512             OPENVDB_LOG_INFO("use tools::foreach(), not transformValues(),"
513                 " to transform a grid in place");
514         }
515     }
516 
517     // When splitting this task, give the subtask a copy of the original functor,
518     // not of this task's functor, which might have been modified arbitrarily.
519     CopyableOpTransformer(CopyableOpTransformer& other, tbb::split):
520         mIsRoot(false),
521         mInputIter(other.mInputIter),
522         mInputTree(other.mInputTree),
523         mOutputTree(new OutTreeT(zeroVal<OutValueT>())),
524         mOp(*other.mOrigOp),
525         mOrigOp(other.mOrigOp),
526         mMergePolicy(other.mMergePolicy)
527         {}
528 
529     ~CopyableOpTransformer()
530     {
531         // Delete the output tree only if it was allocated locally
532         // (the top-level output tree was supplied by the caller).
533         if (!mIsRoot) {
534             delete mOutputTree;
535             mOutputTree = nullptr;
536         }
537     }
538 
539     void process(bool threaded = true)
540     {
541         if (!mInputTree || !mOutputTree) return;
542 
543         IterRange range(mInputIter);
544 
545         // Independently transform elements in the iterator range,
546         // either in parallel or serially.
547         if (threaded) {
548             tbb::parallel_reduce(range, *this);
549         } else {
550             (*this)(range);
551         }
552     }
553 
554     /// Transform each element in the given range.
555     void operator()(IterRange& range)
556     {
557         if (!mOutputTree) return;
558         typename tree::ValueAccessor<OutTreeT> outAccessor(*mOutputTree);
559         for ( ; range; ++range) {
560             mOp(range.iterator(), outAccessor);
561         }
562     }
563 
564     void join(const CopyableOpTransformer& other)
565     {
566         if (mOutputTree && other.mOutputTree) {
567             mOutputTree->merge(*other.mOutputTree, mMergePolicy);
568         }
569     }
570 
571 private:
572     bool mIsRoot;
573     InIterT mInputIter;
574     const InTreeT* mInputTree;
575     OutTreeT* mOutputTree;
576     OpT mOp; // copy of original functor
577     OpT const * const mOrigOp; // pointer to original functor
578     MergePolicy mMergePolicy;
579 }; // class CopyableOpTransformer
580 
581 } // namespace valxform
582 
583 
584 ////////////////////////////////////////
585 
586 
587 template<typename InIterT, typename OutGridT, typename XformOp>
588 inline void
589 transformValues(const InIterT& inIter, OutGridT& outGrid, XformOp& op,
590     bool threaded, bool shared, MergePolicy merge)
591 {
592     using Adapter = TreeAdapter<OutGridT>;
593     using OutTreeT = typename Adapter::TreeType;
594     if (shared) {
595         using Processor = typename valxform::SharedOpTransformer<InIterT, OutTreeT, XformOp>;
596         Processor proc(inIter, Adapter::tree(outGrid), op, merge);
597         proc.process(threaded);
598     } else {
599         using Processor = typename valxform::CopyableOpTransformer<InIterT, OutTreeT, XformOp>;
600         Processor proc(inIter, Adapter::tree(outGrid), op, merge);
601         proc.process(threaded);
602     }
603 }
604 
605 #ifndef _MSC_VER
606 template<typename InIterT, typename OutGridT, typename XformOp>
607 inline void
608 transformValues(const InIterT& inIter, OutGridT& outGrid, const XformOp& op,
609     bool threaded, bool /*share*/, MergePolicy merge)
610 {
611     using Adapter = TreeAdapter<OutGridT>;
612     using OutTreeT = typename Adapter::TreeType;
613     // Const ops are shared across threads, not copied.
614     using Processor = typename valxform::SharedOpTransformer<InIterT, OutTreeT, const XformOp>;
615     Processor proc(inIter, Adapter::tree(outGrid), op, merge);
616     proc.process(threaded);
617 }
618 #endif
619 
620 
621 ////////////////////////////////////////
622 
623 
624 namespace valxform {
625 
626 template<typename IterT, typename OpT>
627 class OpAccumulator
628 {
629 public:
630     using IterRange = typename tree::IteratorRange<IterT>;
631 
632     // The root task makes a const copy of the original functor (mOrigOp)
633     // and keeps a pointer to the original functor (mOp), which it then modifies.
634     // Each subtask keeps a const pointer to the root task's mOrigOp
635     // and makes and then modifies a non-const copy (mOp) of it.
636     OpAccumulator(const IterT& iter, OpT& op):
637         mIsRoot(true),
638         mIter(iter),
639         mOp(&op),
640         mOrigOp(new OpT(op))
641     {}
642 
643     // When splitting this task, give the subtask a copy of the original functor,
644     // not of this task's functor, which might have been modified arbitrarily.
645     OpAccumulator(OpAccumulator& other, tbb::split):
646         mIsRoot(false),
647         mIter(other.mIter),
648         mOp(new OpT(*other.mOrigOp)),
649         mOrigOp(other.mOrigOp)
650     {}
651 
652     ~OpAccumulator() { if (mIsRoot) delete mOrigOp; else delete mOp; }
653 
654     void process(bool threaded = true)
655     {
656         IterRange range(mIter);
657         if (threaded) {
658             tbb::parallel_reduce(range, *this);
659         } else {
660             (*this)(range);
661         }
662     }
663 
664     void operator()(IterRange& r) { for ( ; r; ++r) (*mOp)(r.iterator()); }
665 
666     void join(OpAccumulator& other) { mOp->join(*other.mOp); }
667 
668 private:
669     const bool mIsRoot;
670     const IterT mIter;
671     OpT* mOp; // pointer to original functor, which might get modified
672     OpT const * const mOrigOp; // const copy of original functor
673 }; // class OpAccumulator
674 
675 } // namespace valxform
676 
677 
678 ////////////////////////////////////////
679 
680 
681 template<typename IterT, typename XformOp>
682 inline void
683 accumulate(const IterT& iter, XformOp& op, bool threaded)
684 {
685     typename valxform::OpAccumulator<IterT, XformOp> proc(iter, op);
686     proc.process(threaded);
687 }
688 
689 
690 ////////////////////////////////////////
691 
692 
693 // Explicit Template Instantiation
694 
695 #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
696 
697 #ifdef OPENVDB_INSTANTIATE_VALUETRANSFORMER
698 #include <openvdb/util/ExplicitInstantiation.h>
699 #endif
700 
701 #define _FUNCTION(TreeT) \
702     void setValueOnMin(TreeT&, const Coord&, const TreeT::ValueType&)
703 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
704 #undef _FUNCTION
705 
706 #define _FUNCTION(TreeT) \
707     void setValueOnMax(TreeT&, const Coord&, const TreeT::ValueType&)
708 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
709 #undef _FUNCTION
710 
711 #define _FUNCTION(TreeT) \
712     void setValueOnSum(TreeT&, const Coord&, const TreeT::ValueType&)
713 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
714 #undef _FUNCTION
715 
716 #define _FUNCTION(TreeT) \
717     void setValueOnMult(TreeT&, const Coord&, const TreeT::ValueType&)
718 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
719 #undef _FUNCTION
720 
721 #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
722 
723 
724 } // namespace tools
725 } // namespace OPENVDB_VERSION_NAME
726 } // namespace openvdb
727 
728 #endif // OPENVDB_TOOLS_VALUETRANSFORMER_HAS_BEEN_INCLUDED
729