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