GCC Code Coverage Report


Directory: ./
File: openvdb/openvdb/tools/ValueTransformer.h
Date: 2022-07-25 17:40:05
Exec Total Coverage
Lines: 116 126 92.1%
Functions: 315 3273 9.6%
Branches: 77 380 20.3%

Line Branch Exec Source
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 _WIN32
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;
239 2 MinOp(const ValueType& v): val(v) {}
240
4/66
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 72 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 75 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 78 not taken.
7 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;
246 520028190 MaxOp(const ValueType& v): val(v) {}
247
6/76
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✓ Branch 58 taken 5 times.
✓ Branch 59 taken 1 times.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 72 not taken.
✗ Branch 73 not taken.
✓ Branch 74 taken 10491837 times.
✓ Branch 75 taken 509536345 times.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 78 not taken.
530520033 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;
253
0/44
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 81 not taken.
✗ Branch 82 not taken.
✗ Branch 84 not taken.
✗ Branch 85 not taken.
19853882 SumOp(const ValueType& v): val(v) {}
254 7685484 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
0/12
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
29 SumOp(const ValueType& v): val(v) {}
262
6/32
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 15 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
29 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 3 setValueOnMin(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value)
286 {
287 3 tree.modifyValue(xyz, valxform::MinOp<typename TreeT::ValueType>(value));
288 3 }
289
290
291 template<typename TreeT>
292 void
293 3 setValueOnMax(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value)
294 {
295 3 tree.modifyValue(xyz, valxform::MaxOp<typename TreeT::ValueType>(value));
296 3 }
297
298
299 template<typename TreeT>
300 void
301 3 setValueOnSum(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value)
302 {
303 3 tree.modifyValue(xyz, valxform::SumOp<typename TreeT::ValueType>(value));
304 3 }
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 59968 class SharedOpApplier
322 {
323 public:
324 using IterRange = typename tree::IteratorRange<IterT>;
325
326 1610 SharedOpApplier(const IterT& iter, OpT& op): mIter(iter), mOp(op) {}
327
328 3228 void process(bool threaded = true)
329 {
330 3228 IterRange range(mIter);
331
2/2
✓ Branch 0 taken 1610 times.
✓ Branch 1 taken 4 times.
3228 if (threaded) {
332 3220 tbb::parallel_for(range, *this);
333 } else {
334 8 (*this)(range);
335 }
336 3228 }
337
338 609426240 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 23 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 930 CopyableOpApplier(const CopyableOpApplier& other):
357 930 mIter(other.mIter), mOp(*other.mOrigOp), mOrigOp(other.mOrigOp) {}
358
359 46 void process(bool threaded = true)
360 {
361 46 IterRange range(mIter);
362
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
46 if (threaded) {
363 46 tbb::parallel_for(range, *this);
364 } else {
365 (*this)(range);
366 }
367 46 }
368
369 9192184 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 63 foreach(const IterT& iter, XformOp& op, bool threaded, bool shared)
383 {
384
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 23 times.
63 if (shared) {
385 typename valxform::SharedOpApplier<IterT, XformOp> proc(iter, op);
386 17 proc.process(threaded);
387 } else {
388 using Processor = typename valxform::CopyableOpApplier<IterT, XformOp>;
389 Processor proc(iter, op);
390
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
46 proc.process(threaded);
391 }
392 63 }
393
394 template<typename IterT, typename XformOp>
395 inline void
396 8 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
15/30
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 2 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 2 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 1 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 1 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 1 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 1 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 1 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 1 times.
✗ Branch 44 not taken.
1609 proc.process(threaded);
401 8 }
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 1 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 1 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 5 SharedOpTransformer(SharedOpTransformer& other, tbb::split):
433 mIsRoot(false),
434 5 mInputIter(other.mInputIter),
435 5 mInputTree(other.mInputTree),
436 5 mOutputTree(new OutTreeT(zeroVal<OutValueT>())),
437 5 mOp(other.mOp),
438 5 mMergePolicy(other.mMergePolicy)
439 5 {}
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
2/8
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
6 if (!mIsRoot) {
446
1/8
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
5 delete mOutputTree;
447 mOutputTree = nullptr;
448 }
449 5 }
450
451 1 void process(bool threaded = true)
452 {
453
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!mInputTree || !mOutputTree) return;
454
455 1 IterRange range(mInputIter);
456
457 // Independently transform elements in the iterator range,
458 // either in parallel or serially.
459
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (threaded) {
460 1 tbb::parallel_reduce(range, *this);
461 } else {
462 (*this)(range);
463 }
464 }
465
466 /// Transform each element in the given range.
467 137 void operator()(IterRange& range) const
468 {
469
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 137 times.
137 if (!mOutputTree) return;
470 typename tree::ValueAccessor<OutTreeT> outAccessor(*mOutputTree);
471 for ( ; range; ++range) {
472
1/2
✓ Branch 1 taken 15628 times.
✗ Branch 2 not taken.
15628 mOp(range.iterator(), outAccessor);
473 }
474 }
475
476 void join(const SharedOpTransformer& other)
477 {
478
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 if (mOutputTree && other.mOutputTree) {
479 5 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 1 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 1 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 1 }
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 3 CopyableOpTransformer(CopyableOpTransformer& other, tbb::split):
520 mIsRoot(false),
521 3 mInputIter(other.mInputIter),
522 3 mInputTree(other.mInputTree),
523 3 mOutputTree(new OutTreeT(zeroVal<OutValueT>())),
524 3 mOp(*other.mOrigOp),
525 3 mOrigOp(other.mOrigOp),
526 3 mMergePolicy(other.mMergePolicy)
527 3 {}
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
2/8
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
4 if (!mIsRoot) {
534
1/8
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
3 delete mOutputTree;
535 mOutputTree = nullptr;
536 }
537 3 }
538
539 1 void process(bool threaded = true)
540 {
541
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!mInputTree || !mOutputTree) return;
542
543 1 IterRange range(mInputIter);
544
545 // Independently transform elements in the iterator range,
546 // either in parallel or serially.
547
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (threaded) {
548 1 tbb::parallel_reduce(range, *this);
549 } else {
550 (*this)(range);
551 }
552 }
553
554 /// Transform each element in the given range.
555 140 void operator()(IterRange& range)
556 {
557
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 140 times.
140 if (!mOutputTree) return;
558 typename tree::ValueAccessor<OutTreeT> outAccessor(*mOutputTree);
559 for ( ; range; ++range) {
560
1/2
✓ Branch 1 taken 15628 times.
✗ Branch 2 not taken.
15628 mOp(range.iterator(), outAccessor);
561 }
562 }
563
564 void join(const CopyableOpTransformer& other)
565 {
566
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 if (mOutputTree && other.mOutputTree) {
567 3 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 2 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
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (shared) {
595 using Processor = typename valxform::SharedOpTransformer<InIterT, OutTreeT, XformOp>;
596 Processor proc(inIter, Adapter::tree(outGrid), op, merge);
597
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 proc.process(threaded);
598 } else {
599 using Processor = typename valxform::CopyableOpTransformer<InIterT, OutTreeT, XformOp>;
600 1 Processor proc(inIter, Adapter::tree(outGrid), op, merge);
601
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 proc.process(threaded);
602 }
603 2 }
604
605 #ifndef _WIN32
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 96 OpAccumulator(const IterT& iter, OpT& op):
637 mIsRoot(true),
638 mIter(iter),
639 mOp(&op),
640
1/2
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
97 mOrigOp(new OpT(op))
641 95 {}
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 255 OpAccumulator(OpAccumulator& other, tbb::split):
646 mIsRoot(false),
647 252 mIter(other.mIter),
648
1/2
✓ Branch 2 taken 77 times.
✗ Branch 3 not taken.
258 mOp(new OpT(*other.mOrigOp)),
649 255 mOrigOp(other.mOrigOp)
650 252 {}
651
652
4/6
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 129 times.
✓ Branch 2 taken 51 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 129 times.
✗ Branch 6 not taken.
559 ~OpAccumulator() { if (mIsRoot) delete mOrigOp; else delete mOp; }
653
654 97 void process(bool threaded = true)
655 {
656 95 IterRange range(mIter);
657
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 19 times.
97 if (threaded) {
658 59 tbb::parallel_reduce(range, *this);
659 } else {
660 38 (*this)(range);
661 }
662 97 }
663
664 28735990 void operator()(IterRange& r) { for ( ; r; ++r) (*mOp)(r.iterator()); }
665
666
7/16
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 19 taken 9 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 9 times.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✓ Branch 24 taken 1 times.
129 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 97 accumulate(const IterT& iter, XformOp& op, bool threaded)
684 {
685 192 typename valxform::OpAccumulator<IterT, XformOp> proc(iter, op);
686
1/2
✓ Branch 1 taken 51 times.
✗ Branch 2 not taken.
97 proc.process(threaded);
687 97 }
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