OpenVDB  12.0.0
NodeManager.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: Apache-2.0
3 
4 /// @file tree/NodeManager.h
5 ///
6 /// @authors Ken Museth, Dan Bailey
7 ///
8 /// @brief NodeManager produces linear arrays of all tree nodes
9 /// allowing for efficient threading and bottom-up processing.
10 ///
11 /// @note A NodeManager can be constructed from a Tree or LeafManager.
12 
13 #ifndef OPENVDB_TREE_NODEMANAGER_HAS_BEEN_INCLUDED
14 #define OPENVDB_TREE_NODEMANAGER_HAS_BEEN_INCLUDED
15 
16 #include <openvdb/Types.h>
17 #include <openvdb/util/Assert.h>
18 #include <tbb/parallel_for.h>
19 #include <tbb/parallel_reduce.h>
20 #include <deque>
21 
22 
23 namespace openvdb {
25 namespace OPENVDB_VERSION_NAME {
26 namespace tree {
27 
28 // Produce linear arrays of all tree nodes, to facilitate efficient threading
29 // and bottom-up processing.
30 template<typename TreeOrLeafManagerT, Index LEVELS = TreeOrLeafManagerT::RootNodeType::LEVEL>
32 
33 
34 // Produce linear arrays of all tree nodes lazily, to facilitate efficient threading
35 // of topology-changing top-down workflows.
36 template<typename TreeOrLeafManagerT, Index _LEVELS = TreeOrLeafManagerT::RootNodeType::LEVEL>
38 
39 
40 ////////////////////////////////////////
41 
42 
43 // This is a dummy node filtering class used by the NodeManager class to match
44 // the internal filtering interface used by the DynamicNodeManager.
45 struct NodeFilter
46 {
47  static bool valid(size_t) { return true; }
48 }; // struct NodeFilter
49 
50 
51 /// @brief This class caches tree nodes of a specific type in a linear array.
52 ///
53 /// @note It is for internal use and should rarely be used directly.
54 template<typename NodeT>
55 class NodeList
56 {
57 public:
58  NodeList() = default;
59 
60  NodeT& operator()(size_t n) const { OPENVDB_ASSERT(n<mNodeCount); return *(mNodes[n]); }
61 
62  NodeT*& operator[](size_t n) { OPENVDB_ASSERT(n<mNodeCount); return mNodes[n]; }
63 
64  Index64 nodeCount() const { return mNodeCount; }
65 
66  void clear()
67  {
68  mNodePtrs.reset();
69  mNodes = nullptr;
70  mNodeCount = 0;
71  }
72 
73  // initialize this node list from the provided root node
74  template <typename RootT>
75  bool initRootChildren(RootT& root)
76  {
77  // Allocate (or deallocate) the node pointer array
78 
79  size_t nodeCount = root.childCount();
80 
81  if (nodeCount != mNodeCount) {
82  if (nodeCount > 0) {
83  mNodePtrs.reset(new NodeT*[nodeCount]);
84  mNodes = mNodePtrs.get();
85  } else {
86  mNodePtrs.reset();
87  mNodes = nullptr;
88  }
89  mNodeCount = nodeCount;
90  }
91 
92  if (mNodeCount == 0) return false;
93 
94  // Populate the node pointers
95 
96  NodeT** nodePtr = mNodes;
97  for (auto iter = root.beginChildOn(); iter; ++iter) {
98  *nodePtr++ = &iter.getValue();
99  }
100 
101  return true;
102  }
103 
104  // initialize this node list from another node list containing the parent nodes
105  template <typename ParentsT, typename NodeFilterT>
106  bool initNodeChildren(ParentsT& parents, const NodeFilterT& nodeFilter = NodeFilterT(), bool serial = false)
107  {
108  // Compute the node counts for each node
109 
110  std::vector<Index64> nodeCounts;
111  if (serial) {
112  nodeCounts.reserve(parents.nodeCount());
113  for (size_t i = 0; i < parents.nodeCount(); i++) {
114  if (!nodeFilter.valid(i)) nodeCounts.push_back(0);
115  else nodeCounts.push_back(parents(i).childCount());
116  }
117  } else {
118  nodeCounts.resize(parents.nodeCount());
119  tbb::parallel_for(
120  // with typical node sizes and SSE enabled, there are only a handful
121  // of instructions executed per-operation with a default grainsize
122  // of 1, so increase to 64 to reduce parallel scheduling overhead
123  tbb::blocked_range<Index64>(0, parents.nodeCount(), /*grainsize=*/64),
124  [&](tbb::blocked_range<Index64>& range)
125  {
126  for (Index64 i = range.begin(); i < range.end(); i++) {
127  if (!nodeFilter.valid(i)) nodeCounts[i] = 0;
128  else nodeCounts[i] = parents(i).childCount();
129  }
130  }
131  );
132  }
133 
134  // Turn node counts into a cumulative histogram and obtain total node count
135 
136  for (size_t i = 1; i < nodeCounts.size(); i++) {
137  nodeCounts[i] += nodeCounts[i-1];
138  }
139 
140  const size_t nodeCount = nodeCounts.empty() ? 0 : nodeCounts.back();
141 
142  // Allocate (or deallocate) the node pointer array
143 
144  if (nodeCount != mNodeCount) {
145  if (nodeCount > 0) {
146  mNodePtrs.reset(new NodeT*[nodeCount]);
147  mNodes = mNodePtrs.get();
148  } else {
149  mNodePtrs.reset();
150  mNodes = nullptr;
151  }
152  mNodeCount = nodeCount;
153  }
154 
155  if (mNodeCount == 0) return false;
156 
157  // Populate the node pointers
158 
159  if (serial) {
160  NodeT** nodePtr = mNodes;
161  for (size_t i = 0; i < parents.nodeCount(); i++) {
162  if (!nodeFilter.valid(i)) continue;
163  for (auto iter = parents(i).beginChildOn(); iter; ++iter) {
164  *nodePtr++ = &iter.getValue();
165  }
166  }
167  } else {
168  tbb::parallel_for(
169  tbb::blocked_range<Index64>(0, parents.nodeCount()),
170  [&](tbb::blocked_range<Index64>& range)
171  {
172  Index64 i = range.begin();
173  NodeT** nodePtr = mNodes;
174  if (i > 0) nodePtr += nodeCounts[i-1];
175  for ( ; i < range.end(); i++) {
176  if (!nodeFilter.valid(i)) continue;
177  for (auto iter = parents(i).beginChildOn(); iter; ++iter) {
178  *nodePtr++ = &iter.getValue();
179  }
180  }
181  }
182  );
183  }
184 
185  return true;
186  }
187 
188  class NodeRange
189  {
190  public:
191 
192  NodeRange(size_t begin, size_t end, const NodeList& nodeList, size_t grainSize=1):
193  mEnd(end), mBegin(begin), mGrainSize(grainSize), mNodeList(nodeList) {}
194 
196  mEnd(r.mEnd), mBegin(doSplit(r)), mGrainSize(r.mGrainSize),
197  mNodeList(r.mNodeList) {}
198 
199  size_t size() const { return mEnd - mBegin; }
200 
201  size_t grainsize() const { return mGrainSize; }
202 
203  const NodeList& nodeList() const { return mNodeList; }
204 
205  bool empty() const {return !(mBegin < mEnd);}
206 
207  bool is_divisible() const {return mGrainSize < this->size();}
208 
209  class Iterator
210  {
211  public:
212  Iterator(const NodeRange& range, size_t pos): mRange(range), mPos(pos)
213  {
214  OPENVDB_ASSERT(this->isValid());
215  }
216  Iterator(const Iterator&) = default;
217  Iterator& operator=(const Iterator&) = default;
218  /// Advance to the next node.
219  Iterator& operator++() { ++mPos; return *this; }
220  /// Return a reference to the node to which this iterator is pointing.
221  NodeT& operator*() const { return mRange.mNodeList(mPos); }
222  /// Return a pointer to the node to which this iterator is pointing.
223  NodeT* operator->() const { return &(this->operator*()); }
224  /// Return the index into the list of the current node.
225  size_t pos() const { return mPos; }
226  bool isValid() const { return mPos>=mRange.mBegin && mPos<=mRange.mEnd; }
227  /// Return @c true if this iterator is not yet exhausted.
228  bool test() const { return mPos < mRange.mEnd; }
229  /// Return @c true if this iterator is not yet exhausted.
230  operator bool() const { return this->test(); }
231  /// Return @c true if this iterator is exhausted.
232  bool empty() const { return !this->test(); }
233  bool operator!=(const Iterator& other) const
234  {
235  return (mPos != other.mPos) || (&mRange != &other.mRange);
236  }
237  bool operator==(const Iterator& other) const { return !(*this != other); }
238  const NodeRange& nodeRange() const { return mRange; }
239 
240  private:
241  const NodeRange& mRange;
242  size_t mPos;
243  };// NodeList::NodeRange::Iterator
244 
245  Iterator begin() const {return Iterator(*this, mBegin);}
246 
247  Iterator end() const {return Iterator(*this, mEnd);}
248 
249  private:
250  size_t mEnd, mBegin, mGrainSize;
251  const NodeList& mNodeList;
252 
253  static size_t doSplit(NodeRange& r)
254  {
256  size_t middle = r.mBegin + (r.mEnd - r.mBegin) / 2u;
257  r.mEnd = middle;
258  return middle;
259  }
260  };// NodeList::NodeRange
261 
262  /// Return a TBB-compatible NodeRange.
263  NodeRange nodeRange(size_t grainsize = 1) const
264  {
265  return NodeRange(0, this->nodeCount(), *this, grainsize);
266  }
267 
268  template<typename NodeOp>
269  void foreach(const NodeOp& op, bool threaded = true, size_t grainSize=1)
270  {
271  NodeTransformerCopy<NodeOp> transform(op); // always deep-copies the op
272  transform.run(this->nodeRange(grainSize), threaded);
273  }
274 
275  template<typename NodeOp>
276  void reduce(NodeOp& op, bool threaded = true, size_t grainSize=1)
277  {
278  NodeReducer<NodeOp> transform(op);
279  transform.run(this->nodeRange(grainSize), threaded);
280  }
281 
282  // identical to foreach except the operator() method has a node index and
283  // the operator is referenced instead of copied in NodeTransformer
284  template<typename NodeOp>
285  void foreachWithIndex(const NodeOp& op, bool threaded = true, size_t grainSize=1)
286  {
287  NodeTransformer<NodeOp, OpWithIndex> transform(op);
288  transform.run(this->nodeRange(grainSize), threaded);
289  }
290 
291  // identical to reduce except the operator() method has a node index
292  template<typename NodeOp>
293  void reduceWithIndex(NodeOp& op, bool threaded = true, size_t grainSize=1)
294  {
295  NodeReducer<NodeOp, OpWithIndex> transform(op);
296  transform.run(this->nodeRange(grainSize), threaded);
297  }
298 
299 private:
300 
301  // default execution in the NodeManager ignores the node index
302  // given by the iterator position
303  struct OpWithoutIndex
304  {
305  template <typename T>
306  static void eval(T& node, typename NodeRange::Iterator& iter) { node(*iter); }
307  };
308 
309  // execution in the DynamicNodeManager matches that of the LeafManager in
310  // passing through the node index given by the iterator position
311  struct OpWithIndex
312  {
313  template <typename T>
314  static void eval(T& node, typename NodeRange::Iterator& iter) { node(*iter, iter.pos()); }
315  };
316 
317  // Private struct of NodeList that performs parallel_for
318  template<typename NodeOp, typename OpT = OpWithoutIndex>
319  struct NodeTransformerCopy
320  {
321  NodeTransformerCopy(const NodeOp& nodeOp) : mNodeOp(nodeOp)
322  {
323  }
324  void run(const NodeRange& range, bool threaded = true)
325  {
326  threaded ? tbb::parallel_for(range, *this) : (*this)(range);
327  }
328  void operator()(const NodeRange& range) const
329  {
330  for (typename NodeRange::Iterator it = range.begin(); it; ++it) {
331  OpT::template eval(mNodeOp, it);
332  }
333  }
334  const NodeOp mNodeOp;
335  };// NodeList::NodeTransformerCopy
336 
337  // Private struct of NodeList that performs parallel_for
338  template<typename NodeOp, typename OpT = OpWithoutIndex>
339  struct NodeTransformer
340  {
341  NodeTransformer(const NodeOp& nodeOp) : mNodeOp(nodeOp)
342  {
343  }
344  void run(const NodeRange& range, bool threaded = true)
345  {
346  threaded ? tbb::parallel_for(range, *this) : (*this)(range);
347  }
348  void operator()(const NodeRange& range) const
349  {
350  for (typename NodeRange::Iterator it = range.begin(); it; ++it) {
351  OpT::template eval(mNodeOp, it);
352  }
353  }
354  const NodeOp& mNodeOp;
355  };// NodeList::NodeTransformer
356 
357  // Private struct of NodeList that performs parallel_reduce
358  template<typename NodeOp, typename OpT = OpWithoutIndex>
359  struct NodeReducer
360  {
361  NodeReducer(NodeOp& nodeOp) : mNodeOp(&nodeOp)
362  {
363  }
364  NodeReducer(const NodeReducer& other, tbb::split)
365  : mNodeOpPtr(std::make_unique<NodeOp>(*(other.mNodeOp), tbb::split()))
366  , mNodeOp(mNodeOpPtr.get())
367  {
368  }
369  void run(const NodeRange& range, bool threaded = true)
370  {
371  threaded ? tbb::parallel_reduce(range, *this) : (*this)(range);
372  }
373  void operator()(const NodeRange& range)
374  {
375  for (typename NodeRange::Iterator it = range.begin(); it; ++it) {
376  OpT::template eval(*mNodeOp, it);
377  }
378  }
379  void join(const NodeReducer& other)
380  {
381  mNodeOp->join(*(other.mNodeOp));
382  }
383  std::unique_ptr<NodeOp> mNodeOpPtr;
384  NodeOp *mNodeOp = nullptr;
385  };// NodeList::NodeReducer
386 
387 
388 protected:
389  size_t mNodeCount = 0;
390  std::unique_ptr<NodeT*[]> mNodePtrs;
391  NodeT** mNodes = nullptr;
392 };// NodeList
393 
394 
395 /////////////////////////////////////////////
396 
397 
398 /// @brief This class is a link in a chain that each caches tree nodes
399 /// of a specific type in a linear array.
400 ///
401 /// @note It is for internal use and should rarely be used directly.
402 template<typename NodeT, Index LEVEL>
404 {
405 public:
406  using NonConstChildNodeType = typename NodeT::ChildNodeType;
408 
409  NodeManagerLink() = default;
410 
411  void clear() { mList.clear(); mNext.clear(); }
412 
413  template <typename RootT>
414  void initRootChildren(RootT& root, bool serial = false)
415  {
416  mList.initRootChildren(root);
417  mNext.initNodeChildren(mList, serial);
418  }
419 
420  template<typename ParentsT>
421  void initNodeChildren(ParentsT& parents, bool serial = false)
422  {
423  mList.initNodeChildren(parents, NodeFilter(), serial);
424  mNext.initNodeChildren(mList, serial);
425  }
426 
427  Index64 nodeCount() const { return mList.nodeCount() + mNext.nodeCount(); }
428 
430  {
431  return i==NodeT::LEVEL ? mList.nodeCount() : mNext.nodeCount(i);
432  }
433 
434  template<typename NodeOp>
435  void foreachBottomUp(const NodeOp& op, bool threaded, size_t grainSize)
436  {
437  mNext.foreachBottomUp(op, threaded, grainSize);
438  mList.foreach(op, threaded, grainSize);
439  }
440 
441  template<typename NodeOp>
442  void foreachTopDown(const NodeOp& op, bool threaded, size_t grainSize)
443  {
444  mList.foreach(op, threaded, grainSize);
445  mNext.foreachTopDown(op, threaded, grainSize);
446  }
447 
448  template<typename NodeOp>
449  void reduceBottomUp(NodeOp& op, bool threaded, size_t grainSize)
450  {
451  mNext.reduceBottomUp(op, threaded, grainSize);
452  mList.reduce(op, threaded, grainSize);
453  }
454 
455  template<typename NodeOp>
456  void reduceTopDown(NodeOp& op, bool threaded, size_t grainSize)
457  {
458  mList.reduce(op, threaded, grainSize);
459  mNext.reduceTopDown(op, threaded, grainSize);
460  }
461 
462 protected:
465 };// NodeManagerLink class
466 
467 
468 ////////////////////////////////////////
469 
470 
471 /// @private
472 /// @brief Specialization that terminates the chain of cached tree nodes
473 /// @note It is for internal use and should rarely be used directly.
474 template<typename NodeT>
475 class NodeManagerLink<NodeT, 0>
476 {
477 public:
478  NodeManagerLink() = default;
479 
480  /// @brief Clear all the cached tree nodes
481  void clear() { mList.clear(); }
482 
483  template <typename RootT>
484  void initRootChildren(RootT& root, bool /*serial*/ = false) { mList.initRootChildren(root); }
485 
486  template<typename ParentsT>
487  void initNodeChildren(ParentsT& parents, bool serial = false) { mList.initNodeChildren(parents, NodeFilter(), serial); }
488 
489  Index64 nodeCount() const { return mList.nodeCount(); }
490 
491  Index64 nodeCount(Index) const { return mList.nodeCount(); }
492 
493  template<typename NodeOp>
494  void foreachBottomUp(const NodeOp& op, bool threaded, size_t grainSize)
495  {
496  mList.foreach(op, threaded, grainSize);
497  }
498 
499  template<typename NodeOp>
500  void foreachTopDown(const NodeOp& op, bool threaded, size_t grainSize)
501  {
502  mList.foreach(op, threaded, grainSize);
503  }
504 
505  template<typename NodeOp>
506  void reduceBottomUp(NodeOp& op, bool threaded, size_t grainSize)
507  {
508  mList.reduce(op, threaded, grainSize);
509  }
510 
511  template<typename NodeOp>
512  void reduceTopDown(NodeOp& op, bool threaded, size_t grainSize)
513  {
514  mList.reduce(op, threaded, grainSize);
515  }
516 
517 protected:
518  NodeList<NodeT> mList;
519 };// NodeManagerLink class
520 
521 
522 ////////////////////////////////////////
523 
524 
525 /// @brief To facilitate threading over the nodes of a tree, cache
526 /// node pointers in linear arrays, one for each level of the tree.
527 ///
528 /// @details This implementation works with trees of any depth, but
529 /// optimized specializations are provided for the most typical tree depths.
530 template<typename TreeOrLeafManagerT, Index _LEVELS>
531 class NodeManager
532 {
533 public:
534  static const Index LEVELS = _LEVELS;
535  static_assert(LEVELS > 0,
536  "expected instantiation of template specialization"); // see specialization below
537  using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
539  using NonConstChildNodeType = typename RootNodeType::ChildNodeType;
541  static_assert(RootNodeType::LEVEL >= LEVELS, "number of levels exceeds root node height");
542 
543  NodeManager(TreeOrLeafManagerT& tree, bool serial = false)
544  : mRoot(tree.root())
545  {
546  this->rebuild(serial);
547  }
548 
549  NodeManager(const NodeManager&) = delete;
550 
551  /// @brief Clear all the cached tree nodes
552  void clear() { mChain.clear(); }
553 
554  /// @brief Clear and recache all the tree nodes from the
555  /// tree. This is required if tree nodes have been added or removed.
556  void rebuild(bool serial = false) { mChain.initRootChildren(mRoot, serial); }
557 
558  /// @brief Return a reference to the root node.
559  const RootNodeType& root() const { return mRoot; }
560 
561  /// @brief Return the total number of cached nodes (excluding the root node)
562  Index64 nodeCount() const { return mChain.nodeCount(); }
563 
564  /// @brief Return the number of cached nodes at level @a i, where
565  /// 0 corresponds to the lowest level.
566  Index64 nodeCount(Index i) const { return mChain.nodeCount(i); }
567 
568  //@{
569  /// @brief Threaded method that applies a user-supplied functor
570  /// to all the nodes in the tree.
571  ///
572  /// @param op user-supplied functor, see examples for interface details.
573  /// @param threaded optional toggle to disable threading, on by default.
574  /// @param grainSize optional parameter to specify the grainsize
575  /// for threading, one by default.
576  ///
577  /// @warning The functor object is deep-copied to create TBB tasks.
578  ///
579  /// @par Example:
580  /// @code
581  /// // Functor to offset all the inactive values of a tree. Note
582  /// // this implementation also illustrates how different
583  /// // computation can be applied to the different node types.
584  /// template<typename TreeType>
585  /// struct OffsetOp
586  /// {
587  /// using ValueT = typename TreeT::ValueType;
588  /// using RootT = typename TreeT::RootNodeType;
589  /// using LeafT = typename TreeT::LeafNodeType;
590  /// OffsetOp(const ValueT& v) : mOffset(v) {}
591  ///
592  /// // Processes the root node. Required by the NodeManager
593  /// void operator()(RootT& root) const
594  /// {
595  /// for (typename RootT::ValueOffIter i = root.beginValueOff(); i; ++i) *i += mOffset;
596  /// }
597  /// // Processes the leaf nodes. Required by the NodeManager
598  /// void operator()(LeafT& leaf) const
599  /// {
600  /// for (typename LeafT::ValueOffIter i = leaf.beginValueOff(); i; ++i) *i += mOffset;
601  /// }
602  /// // Processes the internal nodes. Required by the NodeManager
603  /// template<typename NodeT>
604  /// void operator()(NodeT& node) const
605  /// {
606  /// for (typename NodeT::ValueOffIter i = node.beginValueOff(); i; ++i) *i += mOffset;
607  /// }
608  /// private:
609  /// const ValueT mOffset;
610  /// };
611  ///
612  /// // usage:
613  /// OffsetOp<FloatTree> op(3.0f);
614  /// tree::NodeManager<FloatTree> nodes(tree);
615  /// nodes.foreachBottomUp(op);
616  ///
617  /// // or if a LeafManager already exists
618  /// using T = tree::LeafManager<FloatTree>;
619  /// OffsetOp<T> op(3.0f);
620  /// tree::NodeManager<T> nodes(leafManager);
621  /// nodes.foreachBottomUp(op);
622  ///
623  /// @endcode
624  template<typename NodeOp>
625  void foreachBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1)
626  {
627  mChain.foreachBottomUp(op, threaded, grainSize);
628  op(mRoot);
629  }
630 
631  template<typename NodeOp>
632  void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1)
633  {
634  op(mRoot);
635  mChain.foreachTopDown(op, threaded, grainSize);
636  }
637 
638  //@}
639 
640  //@{
641  /// @brief Threaded method that processes nodes with a user supplied functor
642  ///
643  /// @param op user-supplied functor, see examples for interface details.
644  /// @param threaded optional toggle to disable threading, on by default.
645  /// @param grainSize optional parameter to specify the grainsize
646  /// for threading, one by default.
647  ///
648  /// @warning The functor object is deep-copied to create TBB tasks.
649  ///
650  /// @par Example:
651  /// @code
652  /// // Functor to count nodes in a tree
653  /// template<typename TreeType>
654  /// struct NodeCountOp
655  /// {
656  /// NodeCountOp() : nodeCount(TreeType::DEPTH, 0), totalCount(0)
657  /// {
658  /// }
659  /// NodeCountOp(const NodeCountOp& other, tbb::split) :
660  /// nodeCount(TreeType::DEPTH, 0), totalCount(0)
661  /// {
662  /// }
663  /// void join(const NodeCountOp& other)
664  /// {
665  /// for (size_t i = 0; i < nodeCount.size(); ++i) {
666  /// nodeCount[i] += other.nodeCount[i];
667  /// }
668  /// totalCount += other.totalCount;
669  /// }
670  /// // do nothing for the root node
671  /// void operator()(const typename TreeT::RootNodeType& node)
672  /// {
673  /// }
674  /// // count the internal and leaf nodes
675  /// template<typename NodeT>
676  /// void operator()(const NodeT& node)
677  /// {
678  /// ++(nodeCount[NodeT::LEVEL]);
679  /// ++totalCount;
680  /// }
681  /// std::vector<openvdb::Index64> nodeCount;
682  /// openvdb::Index64 totalCount;
683  /// };
684  ///
685  /// // usage:
686  /// NodeCountOp<FloatTree> op;
687  /// tree::NodeManager<FloatTree> nodes(tree);
688  /// nodes.reduceBottomUp(op);
689  ///
690  /// // or if a LeafManager already exists
691  /// NodeCountOp<FloatTree> op;
692  /// using T = tree::LeafManager<FloatTree>;
693  /// T leafManager(tree);
694  /// tree::NodeManager<T> nodes(leafManager);
695  /// nodes.reduceBottomUp(op);
696  ///
697  /// @endcode
698  template<typename NodeOp>
699  void reduceBottomUp(NodeOp& op, bool threaded = true, size_t grainSize=1)
700  {
701  mChain.reduceBottomUp(op, threaded, grainSize);
702  op(mRoot);
703  }
704 
705  template<typename NodeOp>
706  void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1)
707  {
708  op(mRoot);
709  mChain.reduceTopDown(op, threaded, grainSize);
710  }
711  //@}
712 
713 protected:
716 };// NodeManager class
717 
718 
719 ////////////////////////////////////////////
720 
721 
722 // Wraps a user-supplied DynamicNodeManager operator and stores the return
723 // value of the operator() method to the index of the node in a bool array
724 template <typename OpT>
726 {
727  explicit ForeachFilterOp(const OpT& op, openvdb::Index64 size)
728  : mOp(op)
729  , mValidPtr(std::make_unique<bool[]>(size))
730  , mValid(mValidPtr.get()) { }
731 
733  : mOp(other.mOp)
734  , mValid(other.mValid) { }
735 
736  template<typename NodeT>
737  void operator()(NodeT& node, size_t idx) const
738  {
739  mValid[idx] = mOp(node, idx);
740  }
741 
742  bool valid(size_t idx) const { return mValid[idx]; }
743 
744  const OpT& op() const { return mOp; }
745 
746 private:
747  const OpT& mOp;
748  std::unique_ptr<bool[]> mValidPtr;
749  bool* mValid = nullptr;
750 }; // struct ForeachFilterOp
751 
752 
753 // Wraps a user-supplied DynamicNodeManager operator and stores the return
754 // value of the operator() method to the index of the node in a bool array
755 template <typename OpT>
757 {
759  : mOp(&op)
760  , mValidPtr(std::make_unique<bool[]>(size))
761  , mValid(mValidPtr.get()) { }
762 
764  : mOp(other.mOp)
765  , mValid(other.mValid) { }
766 
768  : mOpPtr(std::make_unique<OpT>(*(other.mOp), tbb::split()))
769  , mOp(mOpPtr.get())
770  , mValid(other.mValid) { }
771 
772  template<typename NodeT>
773  void operator()(NodeT& node, size_t idx) const
774  {
775  mValid[idx] = (*mOp)(node, idx);
776  }
777 
778  void join(const ReduceFilterOp& other)
779  {
780  mOp->join(*(other.mOp));
781  }
782 
783  bool valid(size_t idx) const
784  {
785  return mValid[idx];
786  }
787 
788  OpT& op() { return *mOp; }
789 
790 private:
791  std::unique_ptr<OpT> mOpPtr;
792  OpT* mOp = nullptr;
793  std::unique_ptr<bool[]> mValidPtr;
794  bool* mValid = nullptr;
795 }; // struct ReduceFilterOp
796 
797 
798 /// @brief This class is a link in a chain that each caches tree nodes
799 /// of a specific type in a linear array.
800 ///
801 /// @note It is for internal use and should rarely be used directly.
802 template<typename NodeT, Index LEVEL>
804 {
805 public:
806  using NonConstChildNodeType = typename NodeT::ChildNodeType;
808 
809  DynamicNodeManagerLink() = default;
810 
811  template<typename NodeOpT, typename RootT>
812  void foreachTopDown(const NodeOpT& op, RootT& root, bool threaded,
813  size_t leafGrainSize, size_t nonLeafGrainSize)
814  {
815  if (!op(root, /*index=*/0)) return;
816  if (!mList.initRootChildren(root)) return;
817  ForeachFilterOp<NodeOpT> filterOp(op, mList.nodeCount());
818  mList.foreachWithIndex(filterOp, threaded, LEVEL == 0 ? leafGrainSize : nonLeafGrainSize);
819  mNext.foreachTopDownRecurse(filterOp, mList, threaded, leafGrainSize, nonLeafGrainSize);
820  }
821 
822  template<typename FilterOpT, typename ParentT>
823  void foreachTopDownRecurse(const FilterOpT& filterOp, ParentT& parent, bool threaded,
824  size_t leafGrainSize, size_t nonLeafGrainSize)
825  {
826  if (!mList.initNodeChildren(parent, filterOp, !threaded)) return;
827  FilterOpT childFilterOp(filterOp.op(), mList.nodeCount());
828  mList.foreachWithIndex(childFilterOp, threaded, LEVEL == 0 ? leafGrainSize : nonLeafGrainSize);
829  mNext.foreachTopDownRecurse(childFilterOp, mList, threaded, leafGrainSize, nonLeafGrainSize);
830  }
831 
832  template<typename NodeOpT, typename RootT>
833  void reduceTopDown(NodeOpT& op, RootT& root, bool threaded,
834  size_t leafGrainSize, size_t nonLeafGrainSize)
835  {
836  if (!op(root, /*index=*/0)) return;
837  if (!mList.initRootChildren(root)) return;
838  ReduceFilterOp<NodeOpT> filterOp(op, mList.nodeCount());
839  mList.reduceWithIndex(filterOp, threaded, LEVEL == 0 ? leafGrainSize : nonLeafGrainSize);
840  mNext.reduceTopDownRecurse(filterOp, mList, threaded, leafGrainSize, nonLeafGrainSize);
841  }
842 
843  template<typename FilterOpT, typename ParentT>
844  void reduceTopDownRecurse(FilterOpT& filterOp, ParentT& parent, bool threaded,
845  size_t leafGrainSize, size_t nonLeafGrainSize)
846  {
847  if (!mList.initNodeChildren(parent, filterOp, !threaded)) return;
848  FilterOpT childFilterOp(filterOp.op(), mList.nodeCount());
849  mList.reduceWithIndex(childFilterOp, threaded, LEVEL == 0 ? leafGrainSize : nonLeafGrainSize);
850  mNext.reduceTopDownRecurse(childFilterOp, mList, threaded, leafGrainSize, nonLeafGrainSize);
851  }
852 
853 protected:
856 };// DynamicNodeManagerLink class
857 
858 
859 /// @private
860 /// @brief Specialization that terminates the chain of cached tree nodes
861 /// @note It is for internal use and should rarely be used directly.
862 template<typename NodeT>
863 class DynamicNodeManagerLink<NodeT, 0>
864 {
865 public:
866  DynamicNodeManagerLink() = default;
867 
868  template<typename NodeFilterOp, typename ParentT>
869  void foreachTopDownRecurse(const NodeFilterOp& nodeFilterOp, ParentT& parent, bool threaded,
870  size_t leafGrainSize, size_t /*nonLeafGrainSize*/)
871  {
872  if (!mList.initNodeChildren(parent, nodeFilterOp, !threaded)) return;
873  mList.foreachWithIndex(nodeFilterOp.op(), threaded, leafGrainSize);
874  }
875 
876  template<typename NodeFilterOp, typename ParentT>
877  void reduceTopDownRecurse(NodeFilterOp& nodeFilterOp, ParentT& parent, bool threaded,
878  size_t leafGrainSize, size_t /*nonLeafGrainSize*/)
879  {
880  if (!mList.initNodeChildren(parent, nodeFilterOp, !threaded)) return;
881  mList.reduceWithIndex(nodeFilterOp.op(), threaded, leafGrainSize);
882  }
883 
884 protected:
885  NodeList<NodeT> mList;
886 };// DynamicNodeManagerLink class
887 
888 
889 template<typename TreeOrLeafManagerT, Index _LEVELS>
890 class DynamicNodeManager
891 {
892 public:
893  static const Index LEVELS = _LEVELS;
894  static_assert(LEVELS > 0,
895  "expected instantiation of template specialization"); // see specialization below
896  using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
898  using NonConstChildNodeType = typename RootNodeType::ChildNodeType;
900  static_assert(RootNodeType::LEVEL >= LEVELS, "number of levels exceeds root node height");
901 
902  explicit DynamicNodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) { }
903 
904  DynamicNodeManager(const DynamicNodeManager&) = delete;
905 
906  /// @brief Return a reference to the root node.
907  const NonConstRootNodeType& root() const { return mRoot; }
908 
909  /// @brief Threaded method that applies a user-supplied functor
910  /// to all the nodes in the tree.
911  ///
912  /// @param op user-supplied functor, see examples for interface details.
913  /// @param threaded optional toggle to disable threading, on by default.
914  /// @param leafGrainSize optional parameter to specify the grainsize
915  /// for threading over leaf nodes, one by default.
916  /// @param nonLeafGrainSize optional parameter to specify the grainsize
917  /// for threading over non-leaf nodes, one by default.
918  ///
919  /// @note There are two key differences to the interface of the
920  /// user-supplied functor to the NodeManager class - (1) the operator()
921  /// method aligns with the LeafManager class in expecting the index of the
922  /// node in a linear array of identical node types, (2) the operator()
923  /// method returns a boolean termination value with true indicating that
924  /// children of this node should be processed, false indicating the
925  /// early-exit termination should occur.
926  ///
927  /// @note Unlike the NodeManager, the foreach() method of the
928  /// DynamicNodeManager uses copy-by-reference for the user-supplied functor.
929  /// This can be an issue when using a shared Accessor or shared Sampler in
930  /// the operator as they are not inherently thread-safe. For these use
931  /// cases, it is recommended to create the Accessor or Sampler in the
932  /// operator execution itself.
933  ///
934  /// @par Example:
935  /// @code
936  /// // Functor to densify the first child node in a linear array. Note
937  /// // this implementation also illustrates how different
938  /// // computation can be applied to the different node types.
939  ///
940  /// template<typename TreeT>
941  /// struct DensifyOp
942  /// {
943  /// using RootT = typename TreeT::RootNodeType;
944  /// using LeafT = typename TreeT::LeafNodeType;
945  ///
946  /// DensifyOp() = default;
947  ///
948  /// // Processes the root node. Required by the DynamicNodeManager
949  /// bool operator()(RootT&, size_t) const { return true; }
950  ///
951  /// // Processes the internal nodes. Required by the DynamicNodeManager
952  /// template<typename NodeT>
953  /// bool operator()(NodeT& node, size_t idx) const
954  /// {
955  /// // densify child
956  /// for (auto iter = node.cbeginValueAll(); iter; ++iter) {
957  /// const openvdb::Coord ijk = iter.getCoord();
958  /// node.addChild(new typename NodeT::ChildNodeType(iter.getCoord(), NodeT::LEVEL, true));
959  /// }
960  /// // early-exit termination for all non-zero index children
961  /// return idx == 0;
962  /// }
963  /// // Processes the leaf nodes. Required by the DynamicNodeManager
964  /// bool operator()(LeafT&, size_t) const
965  /// {
966  /// return true;
967  /// }
968  /// };// DensifyOp
969  ///
970  /// // usage:
971  /// DensifyOp<FloatTree> op;
972  /// tree::DynamicNodeManager<FloatTree> nodes(tree);
973  /// nodes.foreachTopDown(op);
974  ///
975  /// @endcode
976  template<typename NodeOp>
977  void foreachTopDown(const NodeOp& op, bool threaded = true,
978  size_t leafGrainSize=1, size_t nonLeafGrainSize=1)
979  {
980  mChain.foreachTopDown(op, mRoot, threaded, leafGrainSize, nonLeafGrainSize);
981  }
982 
983  /// @brief Threaded method that processes nodes with a user supplied functor
984  ///
985  /// @param op user-supplied functor, see examples for interface details.
986  /// @param threaded optional toggle to disable threading, on by default.
987  /// @param leafGrainSize optional parameter to specify the grainsize
988  /// for threading over leaf nodes, one by default.
989  /// @param nonLeafGrainSize optional parameter to specify the grainsize
990  /// for threading over non-leaf nodes, one by default.
991  ///
992  /// @note There are two key differences to the interface of the
993  /// user-supplied functor to the NodeManager class - (1) the operator()
994  /// method aligns with the LeafManager class in expecting the index of the
995  /// node in a linear array of identical node types, (2) the operator()
996  /// method returns a boolean termination value with true indicating that
997  /// children of this node should be processed, false indicating the
998  /// early-exit termination should occur.
999  ///
1000  /// @par Example:
1001  /// @code
1002  /// // Functor to count nodes in a tree
1003  /// template<typename TreeType>
1004  /// struct NodeCountOp
1005  /// {
1006  /// NodeCountOp() : nodeCount(TreeType::DEPTH, 0), totalCount(0)
1007  /// {
1008  /// }
1009  /// NodeCountOp(const NodeCountOp& other, tbb::split) :
1010  /// nodeCount(TreeType::DEPTH, 0), totalCount(0)
1011  /// {
1012  /// }
1013  /// void join(const NodeCountOp& other)
1014  /// {
1015  /// for (size_t i = 0; i < nodeCount.size(); ++i) {
1016  /// nodeCount[i] += other.nodeCount[i];
1017  /// }
1018  /// totalCount += other.totalCount;
1019  /// }
1020  /// // do nothing for the root node
1021  /// bool operator()(const typename TreeT::RootNodeType& node, size_t)
1022  /// {
1023  /// return true;
1024  /// }
1025  /// // count the internal and leaf nodes
1026  /// template<typename NodeT>
1027  /// bool operator()(const NodeT& node, size_t)
1028  /// {
1029  /// ++(nodeCount[NodeT::LEVEL]);
1030  /// ++totalCount;
1031  /// return true;
1032  /// }
1033  /// std::vector<openvdb::Index64> nodeCount;
1034  /// openvdb::Index64 totalCount;
1035  /// };
1036  ///
1037  /// // usage:
1038  /// NodeCountOp<FloatTree> op;
1039  /// tree::DynamicNodeManager<FloatTree> nodes(tree);
1040  /// nodes.reduceTopDown(op);
1041  ///
1042  /// @endcode
1043  template<typename NodeOp>
1044  void reduceTopDown(NodeOp& op, bool threaded = true,
1045  size_t leafGrainSize=1, size_t nonLeafGrainSize=1)
1046  {
1047  mChain.reduceTopDown(op, mRoot, threaded, leafGrainSize, nonLeafGrainSize);
1048  }
1049 
1050 protected:
1053 };// DynamicNodeManager class
1054 
1055 
1056 
1057 ////////////////////////////////////////////
1058 
1059 
1060 /// @private
1061 /// Template specialization of the NodeManager with no caching of nodes
1062 template<typename TreeOrLeafManagerT>
1063 class NodeManager<TreeOrLeafManagerT, 0>
1064 {
1065 public:
1066  using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1068  static const Index LEVELS = 0;
1069 
1070  NodeManager(TreeOrLeafManagerT& tree, bool /*serial*/ = false) : mRoot(tree.root()) { }
1071 
1072  NodeManager(const NodeManager&) = delete;
1073 
1074  /// @brief Clear all the cached tree nodes
1075  void clear() {}
1076 
1077  /// @brief Clear and recache all the tree nodes from the
1078  /// tree. This is required if tree nodes have been added or removed.
1079  void rebuild(bool /*serial*/ = false) { }
1080 
1081  /// @brief Return a reference to the root node.
1082  const RootNodeType& root() const { return mRoot; }
1083 
1084  /// @brief Return the total number of cached nodes (excluding the root node)
1085  Index64 nodeCount() const { return 0; }
1086 
1087  Index64 nodeCount(Index) const { return 0; }
1088 
1089  template<typename NodeOp>
1090  void foreachBottomUp(const NodeOp& op, bool, size_t) { op(mRoot); }
1091 
1092  template<typename NodeOp>
1093  void foreachTopDown(const NodeOp& op, bool, size_t) { op(mRoot); }
1094 
1095  template<typename NodeOp>
1096  void reduceBottomUp(NodeOp& op, bool, size_t) { op(mRoot); }
1097 
1098  template<typename NodeOp>
1099  void reduceTopDown(NodeOp& op, bool, size_t) { op(mRoot); }
1100 
1101 protected:
1102  RootNodeType& mRoot;
1103 }; // NodeManager<0>
1104 
1105 
1106 ////////////////////////////////////////////
1107 
1108 
1109 /// @private
1110 /// Template specialization of the NodeManager with one level of nodes
1111 template<typename TreeOrLeafManagerT>
1112 class NodeManager<TreeOrLeafManagerT, 1>
1113 {
1114 public:
1115  using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1117  static_assert(RootNodeType::LEVEL > 0, "expected instantiation of template specialization");
1118  static const Index LEVELS = 1;
1119 
1120  NodeManager(TreeOrLeafManagerT& tree, bool serial = false)
1121  : mRoot(tree.root())
1122  {
1123  this->rebuild(serial);
1124  }
1125 
1126  NodeManager(const NodeManager&) = delete;
1127 
1128  /// @brief Clear all the cached tree nodes
1129  void clear() { mList0.clear(); }
1130 
1131  /// @brief Clear and recache all the tree nodes from the
1132  /// tree. This is required if tree nodes have been added or removed.
1133  void rebuild(bool /*serial*/ = false) { mList0.initRootChildren(mRoot); }
1134 
1135  /// @brief Return a reference to the root node.
1136  const RootNodeType& root() const { return mRoot; }
1137 
1138  /// @brief Return the total number of cached nodes (excluding the root node)
1139  Index64 nodeCount() const { return mList0.nodeCount(); }
1140 
1141  /// @brief Return the number of cached nodes at level @a i, where
1142  /// 0 corresponds to the lowest level.
1143  Index64 nodeCount(Index i) const { return i==0 ? mList0.nodeCount() : 0; }
1144 
1145  template<typename NodeOp>
1146  void foreachBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1)
1147  {
1148  mList0.foreach(op, threaded, grainSize);
1149  op(mRoot);
1150  }
1151 
1152  template<typename NodeOp>
1153  void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1)
1154  {
1155  op(mRoot);
1156  mList0.foreach(op, threaded, grainSize);
1157  }
1158 
1159  template<typename NodeOp>
1160  void reduceBottomUp(NodeOp& op, bool threaded = true, size_t grainSize=1)
1161  {
1162  mList0.reduce(op, threaded, grainSize);
1163  op(mRoot);
1164  }
1165 
1166  template<typename NodeOp>
1167  void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1)
1168  {
1169  op(mRoot);
1170  mList0.reduce(op, threaded, grainSize);
1171  }
1172 
1173 protected:
1174  using NodeT1 = RootNodeType;
1175  using NonConstNodeT0 = typename NodeT1::ChildNodeType;
1176  using NodeT0 = typename CopyConstness<RootNodeType, NonConstNodeT0>::Type;
1177  using ListT0 = NodeList<NodeT0>;
1178 
1179  NodeT1& mRoot;
1180  ListT0 mList0;
1181 }; // NodeManager<1>
1182 
1183 
1184 ////////////////////////////////////////////
1185 
1186 
1187 /// @private
1188 /// Template specialization of the NodeManager with two levels of nodes
1189 template<typename TreeOrLeafManagerT>
1190 class NodeManager<TreeOrLeafManagerT, 2>
1191 {
1192 public:
1193  using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1195  static_assert(RootNodeType::LEVEL > 1, "expected instantiation of template specialization");
1196  static const Index LEVELS = 2;
1197 
1198  NodeManager(TreeOrLeafManagerT& tree, bool serial = false) : mRoot(tree.root())
1199  {
1200  this->rebuild(serial);
1201  }
1202 
1203  NodeManager(const NodeManager&) = delete;
1204 
1205  /// @brief Clear all the cached tree nodes
1206  void clear() { mList0.clear(); mList1.clear(); }
1207 
1208  /// @brief Clear and recache all the tree nodes from the
1209  /// tree. This is required if tree nodes have been added or removed.
1210  void rebuild(bool serial = false)
1211  {
1212  mList1.initRootChildren(mRoot);
1213  mList0.initNodeChildren(mList1, NodeFilter(), serial);
1214  }
1215 
1216  /// @brief Return a reference to the root node.
1217  const RootNodeType& root() const { return mRoot; }
1218 
1219  /// @brief Return the total number of cached nodes (excluding the root node)
1220  Index64 nodeCount() const { return mList0.nodeCount() + mList1.nodeCount(); }
1221 
1222  /// @brief Return the number of cached nodes at level @a i, where
1223  /// 0 corresponds to the lowest level.
1224  Index64 nodeCount(Index i) const
1225  {
1226  return i==0 ? mList0.nodeCount() : i==1 ? mList1.nodeCount() : 0;
1227  }
1228 
1229  template<typename NodeOp>
1230  void foreachBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1)
1231  {
1232  mList0.foreach(op, threaded, grainSize);
1233  mList1.foreach(op, threaded, grainSize);
1234  op(mRoot);
1235  }
1236 
1237  template<typename NodeOp>
1238  void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1)
1239  {
1240  op(mRoot);
1241  mList1.foreach(op, threaded, grainSize);
1242  mList0.foreach(op, threaded, grainSize);
1243  }
1244 
1245  template<typename NodeOp>
1246  void reduceBottomUp(NodeOp& op, bool threaded = true, size_t grainSize=1)
1247  {
1248  mList0.reduce(op, threaded, grainSize);
1249  mList1.reduce(op, threaded, grainSize);
1250  op(mRoot);
1251  }
1252 
1253  template<typename NodeOp>
1254  void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1)
1255  {
1256  op(mRoot);
1257  mList1.reduce(op, threaded, grainSize);
1258  mList0.reduce(op, threaded, grainSize);
1259  }
1260 
1261 protected:
1262  using NodeT2 = RootNodeType;
1263  using NonConstNodeT1 = typename NodeT2::ChildNodeType;
1264  using NodeT1 = typename CopyConstness<RootNodeType, NonConstNodeT1>::Type; // upper level
1265  using NonConstNodeT0 = typename NodeT1::ChildNodeType;
1266  using NodeT0 = typename CopyConstness<RootNodeType, NonConstNodeT0>::Type; // lower level
1267 
1268  using ListT1 = NodeList<NodeT1>; // upper level
1269  using ListT0 = NodeList<NodeT0>; // lower level
1270 
1271  NodeT2& mRoot;
1272  ListT1 mList1;
1273  ListT0 mList0;
1274 }; // NodeManager<2>
1275 
1276 
1277 ////////////////////////////////////////////
1278 
1279 
1280 /// @private
1281 /// Template specialization of the NodeManager with three levels of nodes
1282 template<typename TreeOrLeafManagerT>
1283 class NodeManager<TreeOrLeafManagerT, 3>
1284 {
1285 public:
1286  using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1288  static_assert(RootNodeType::LEVEL > 2, "expected instantiation of template specialization");
1289  static const Index LEVELS = 3;
1290 
1291  NodeManager(TreeOrLeafManagerT& tree, bool serial = false) : mRoot(tree.root())
1292  {
1293  this->rebuild(serial);
1294  }
1295 
1296  NodeManager(const NodeManager&) = delete;
1297 
1298  /// @brief Clear all the cached tree nodes
1299  void clear() { mList0.clear(); mList1.clear(); mList2.clear(); }
1300 
1301  /// @brief Clear and recache all the tree nodes from the
1302  /// tree. This is required if tree nodes have been added or removed.
1303  void rebuild(bool serial = false)
1304  {
1305  mList2.initRootChildren(mRoot);
1306  mList1.initNodeChildren(mList2, NodeFilter(), serial);
1307  mList0.initNodeChildren(mList1, NodeFilter(), serial);
1308  }
1309 
1310  /// @brief Return a reference to the root node.
1311  const RootNodeType& root() const { return mRoot; }
1312 
1313  /// @brief Return the total number of cached nodes (excluding the root node)
1314  Index64 nodeCount() const { return mList0.nodeCount()+mList1.nodeCount()+mList2.nodeCount(); }
1315 
1316  /// @brief Return the number of cached nodes at level @a i, where
1317  /// 0 corresponds to the lowest level.
1318  Index64 nodeCount(Index i) const
1319  {
1320  return i==0 ? mList0.nodeCount() : i==1 ? mList1.nodeCount()
1321  : i==2 ? mList2.nodeCount() : 0;
1322  }
1323 
1324  template<typename NodeOp>
1325  void foreachBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1)
1326  {
1327  mList0.foreach(op, threaded, grainSize);
1328  mList1.foreach(op, threaded, grainSize);
1329  mList2.foreach(op, threaded, grainSize);
1330  op(mRoot);
1331  }
1332 
1333  template<typename NodeOp>
1334  void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1)
1335  {
1336  op(mRoot);
1337  mList2.foreach(op, threaded, grainSize);
1338  mList1.foreach(op, threaded, grainSize);
1339  mList0.foreach(op, threaded, grainSize);
1340  }
1341 
1342  template<typename NodeOp>
1343  void reduceBottomUp(NodeOp& op, bool threaded = true, size_t grainSize=1)
1344  {
1345  mList0.reduce(op, threaded, grainSize);
1346  mList1.reduce(op, threaded, grainSize);
1347  mList2.reduce(op, threaded, grainSize);
1348  op(mRoot);
1349  }
1350 
1351  template<typename NodeOp>
1352  void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1)
1353  {
1354  op(mRoot);
1355  mList2.reduce(op, threaded, grainSize);
1356  mList1.reduce(op, threaded, grainSize);
1357  mList0.reduce(op, threaded, grainSize);
1358  }
1359 
1360 protected:
1361  using NodeT3 = RootNodeType;
1362  using NonConstNodeT2 = typename NodeT3::ChildNodeType;
1363  using NodeT2 = typename CopyConstness<RootNodeType, NonConstNodeT2>::Type; // upper level
1364  using NonConstNodeT1 = typename NodeT2::ChildNodeType;
1365  using NodeT1 = typename CopyConstness<RootNodeType, NonConstNodeT1>::Type; // mid level
1366  using NonConstNodeT0 = typename NodeT1::ChildNodeType;
1367  using NodeT0 = typename CopyConstness<RootNodeType, NonConstNodeT0>::Type; // lower level
1368 
1369  using ListT2 = NodeList<NodeT2>; // upper level of internal nodes
1370  using ListT1 = NodeList<NodeT1>; // lower level of internal nodes
1371  using ListT0 = NodeList<NodeT0>; // lower level of internal nodes or leafs
1372 
1373  NodeT3& mRoot;
1374  ListT2 mList2;
1375  ListT1 mList1;
1376  ListT0 mList0;
1377 }; // NodeManager<3>
1378 
1379 
1380 ////////////////////////////////////////////
1381 
1382 
1383 /// @private
1384 /// Template specialization of the NodeManager with four levels of nodes
1385 template<typename TreeOrLeafManagerT>
1386 class NodeManager<TreeOrLeafManagerT, 4>
1387 {
1388 public:
1389  using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1391  static_assert(RootNodeType::LEVEL > 3, "expected instantiation of template specialization");
1392  static const Index LEVELS = 4;
1393 
1394  NodeManager(TreeOrLeafManagerT& tree, bool serial = false) : mRoot(tree.root())
1395  {
1396  this->rebuild(serial);
1397  }
1398 
1399  NodeManager(const NodeManager&) = delete; // disallow copy-construction
1400 
1401  /// @brief Clear all the cached tree nodes
1402  void clear() { mList0.clear(); mList1.clear(); mList2.clear(); mList3.clear(); }
1403 
1404  /// @brief Clear and recache all the tree nodes from the
1405  /// tree. This is required if tree nodes have been added or removed.
1406  void rebuild(bool serial = false)
1407  {
1408  mList3.initRootChildren(mRoot);
1409  mList2.initNodeChildren(mList3, NodeFilter(), serial);
1410  mList1.initNodeChildren(mList2, NodeFilter(), serial);
1411  mList0.initNodeChildren(mList1, NodeFilter(), serial);
1412  }
1413 
1414  /// @brief Return a reference to the root node.
1415  const RootNodeType& root() const { return mRoot; }
1416 
1417  /// @brief Return the total number of cached nodes (excluding the root node)
1418  Index64 nodeCount() const
1419  {
1420  return mList0.nodeCount() + mList1.nodeCount()
1421  + mList2.nodeCount() + mList3.nodeCount();
1422  }
1423 
1424  /// @brief Return the number of cached nodes at level @a i, where
1425  /// 0 corresponds to the lowest level.
1426  Index64 nodeCount(Index i) const
1427  {
1428  return i==0 ? mList0.nodeCount() : i==1 ? mList1.nodeCount() :
1429  i==2 ? mList2.nodeCount() : i==3 ? mList3.nodeCount() : 0;
1430  }
1431 
1432  template<typename NodeOp>
1433  void foreachBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1)
1434  {
1435  mList0.foreach(op, threaded, grainSize);
1436  mList1.foreach(op, threaded, grainSize);
1437  mList2.foreach(op, threaded, grainSize);
1438  mList3.foreach(op, threaded, grainSize);
1439  op(mRoot);
1440  }
1441 
1442  template<typename NodeOp>
1443  void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1)
1444  {
1445  op(mRoot);
1446  mList3.foreach(op, threaded, grainSize);
1447  mList2.foreach(op, threaded, grainSize);
1448  mList1.foreach(op, threaded, grainSize);
1449  mList0.foreach(op, threaded, grainSize);
1450  }
1451 
1452  template<typename NodeOp>
1453  void reduceBottomUp(NodeOp& op, bool threaded = true, size_t grainSize=1)
1454  {
1455  mList0.reduce(op, threaded, grainSize);
1456  mList1.reduce(op, threaded, grainSize);
1457  mList2.reduce(op, threaded, grainSize);
1458  mList3.reduce(op, threaded, grainSize);
1459  op(mRoot);
1460  }
1461 
1462  template<typename NodeOp>
1463  void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1)
1464  {
1465  op(mRoot);
1466  mList3.reduce(op, threaded, grainSize);
1467  mList2.reduce(op, threaded, grainSize);
1468  mList1.reduce(op, threaded, grainSize);
1469  mList0.reduce(op, threaded, grainSize);
1470  }
1471 
1472 protected:
1473  using NodeT4 = RootNodeType;
1474  using NonConstNodeT3 = typename NodeT4::ChildNodeType;
1475  using NodeT3 = typename CopyConstness<RootNodeType, NonConstNodeT3>::Type; // upper level
1476  using NonConstNodeT2 = typename NodeT3::ChildNodeType;
1477  using NodeT2 = typename CopyConstness<RootNodeType, NonConstNodeT2>::Type; // upper mid level
1478  using NonConstNodeT1 = typename NodeT2::ChildNodeType;
1479  using NodeT1 = typename CopyConstness<RootNodeType, NonConstNodeT1>::Type; // lower mid level
1480  using NonConstNodeT0 = typename NodeT1::ChildNodeType;
1481  using NodeT0 = typename CopyConstness<RootNodeType, NonConstNodeT0>::Type; // lower level
1482 
1483  using ListT3 = NodeList<NodeT3>; // upper level of internal nodes
1484  using ListT2 = NodeList<NodeT2>; // upper mid level of internal nodes
1485  using ListT1 = NodeList<NodeT1>; // lower mid level of internal nodes
1486  using ListT0 = NodeList<NodeT0>; // lower level of internal nodes or leafs
1487 
1488  NodeT4& mRoot;
1489  ListT3 mList3;
1490  ListT2 mList2;
1491  ListT1 mList1;
1492  ListT0 mList0;
1493 }; // NodeManager<4>
1494 
1495 
1496 ////////////////////////////////////////////
1497 
1498 
1499 /// @private
1500 /// Template specialization of the DynamicNodeManager with no caching of nodes
1501 template<typename TreeOrLeafManagerT>
1502 class DynamicNodeManager<TreeOrLeafManagerT, 0>
1503 {
1504 public:
1505  using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1507  static_assert(RootNodeType::LEVEL > 0, "expected instantiation of template specialization");
1508  static const Index LEVELS = 0;
1509 
1510  explicit DynamicNodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) { }
1511 
1512  DynamicNodeManager(const DynamicNodeManager&) = delete;
1513 
1514  /// @brief Return a reference to the root node.
1515  const RootNodeType& root() const { return mRoot; }
1516 
1517  template<typename NodeOp>
1518  void foreachTopDown(const NodeOp& op, bool /*threaded*/=true, size_t /*grainSize*/=1)
1519  {
1520  // root
1521  if (!op(mRoot, /*index=*/0)) return;
1522  }
1523 
1524  template<typename NodeOp>
1525  void reduceTopDown(NodeOp& op, bool /*threaded*/=true, size_t /*grainSize*/=1)
1526  {
1527  // root
1528  if (!op(mRoot, /*index=*/0)) return;
1529  }
1530 
1531 protected:
1532  using NodeT1 = RootNodeType;
1533 
1534  NodeT1& mRoot;
1535 };// DynamicNodeManager<0> class
1536 
1537 
1538 ////////////////////////////////////////////
1539 
1540 
1541 /// @private
1542 /// Template specialization of the DynamicNodeManager with one level of nodes
1543 template<typename TreeOrLeafManagerT>
1544 class DynamicNodeManager<TreeOrLeafManagerT, 1>
1545 {
1546 public:
1547  using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1549  static_assert(RootNodeType::LEVEL > 0, "expected instantiation of template specialization");
1550  static const Index LEVELS = 1;
1551 
1552  explicit DynamicNodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) { }
1553 
1554  DynamicNodeManager(const DynamicNodeManager&) = delete;
1555 
1556  /// @brief Return a reference to the root node.
1557  const RootNodeType& root() const { return mRoot; }
1558 
1559  template<typename NodeOp>
1560  void foreachTopDown(const NodeOp& op, bool threaded = true,
1561  size_t leafGrainSize=1, size_t /*nonLeafGrainSize*/ =1)
1562  {
1563  // root
1564  if (!op(mRoot, /*index=*/0)) return;
1565  // list0
1566  if (!mList0.initRootChildren(mRoot)) return;
1567  ForeachFilterOp<NodeOp> nodeOp(op, mList0.nodeCount());
1568  mList0.foreachWithIndex(nodeOp, threaded, leafGrainSize);
1569  }
1570 
1571  template<typename NodeOp>
1572  void reduceTopDown(NodeOp& op, bool threaded = true,
1573  size_t leafGrainSize=1, size_t /*nonLeafGrainSize*/ =1)
1574  {
1575  // root
1576  if (!op(mRoot, /*index=*/0)) return;
1577  // list0
1578  if (!mList0.initRootChildren(mRoot)) return;
1579  ReduceFilterOp<NodeOp> nodeOp(op, mList0.nodeCount());
1580  mList0.reduceWithIndex(nodeOp, threaded, leafGrainSize);
1581  }
1582 
1583 protected:
1584  using NodeT1 = RootNodeType;
1585  using NonConstNodeT0 = typename NodeT1::ChildNodeType;
1586  using NodeT0 = typename CopyConstness<RootNodeType, NonConstNodeT0>::Type;
1587  using ListT0 = NodeList<NodeT0>;
1588 
1589  NodeT1& mRoot;
1590  ListT0 mList0;
1591 };// DynamicNodeManager<1> class
1592 
1593 
1594 ////////////////////////////////////////////
1595 
1596 
1597 /// @private
1598 /// Template specialization of the DynamicNodeManager with two levels of nodes
1599 template<typename TreeOrLeafManagerT>
1600 class DynamicNodeManager<TreeOrLeafManagerT, 2>
1601 {
1602 public:
1603  using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1605  static_assert(RootNodeType::LEVEL > 1, "expected instantiation of template specialization");
1606  static const Index LEVELS = 2;
1607 
1608  explicit DynamicNodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) { }
1609 
1610  DynamicNodeManager(const DynamicNodeManager&) = delete;
1611 
1612  /// @brief Return a reference to the root node.
1613  const RootNodeType& root() const { return mRoot; }
1614 
1615  template<typename NodeOp>
1616  void foreachTopDown(const NodeOp& op, bool threaded = true,
1617  size_t leafGrainSize=1, size_t nonLeafGrainSize=1)
1618  {
1619  // root
1620  if (!op(mRoot, /*index=*/0)) return;
1621  // list1
1622  if (!mList1.initRootChildren(mRoot)) return;
1623  ForeachFilterOp<NodeOp> nodeOp(op, mList1.nodeCount());
1624  mList1.foreachWithIndex(nodeOp, threaded, nonLeafGrainSize);
1625  // list0
1626  if (!mList0.initNodeChildren(mList1, nodeOp, !threaded)) return;
1627  mList0.foreachWithIndex(op, threaded, leafGrainSize);
1628  }
1629 
1630  template<typename NodeOp>
1631  void reduceTopDown(NodeOp& op, bool threaded = true,
1632  size_t leafGrainSize=1, size_t nonLeafGrainSize=1)
1633  {
1634  // root
1635  if (!op(mRoot, /*index=*/0)) return;
1636  // list1
1637  if (!mList1.initRootChildren(mRoot)) return;
1638  ReduceFilterOp<NodeOp> nodeOp(op, mList1.nodeCount());
1639  mList1.reduceWithIndex(nodeOp, threaded, nonLeafGrainSize);
1640  // list0
1641  if (!mList0.initNodeChildren(mList1, nodeOp, !threaded)) return;
1642  mList0.reduceWithIndex(op, threaded, leafGrainSize);
1643  }
1644 
1645 protected:
1646  using NodeT2 = RootNodeType;
1647  using NonConstNodeT1 = typename NodeT2::ChildNodeType;
1648  using NodeT1 = typename CopyConstness<RootNodeType, NonConstNodeT1>::Type; // upper level
1649  using NonConstNodeT0 = typename NodeT1::ChildNodeType;
1650  using NodeT0 = typename CopyConstness<RootNodeType, NonConstNodeT0>::Type; // lower level
1651 
1652  using ListT1 = NodeList<NodeT1>; // upper level
1653  using ListT0 = NodeList<NodeT0>; // lower level
1654 
1655  NodeT2& mRoot;
1656  ListT1 mList1;
1657  ListT0 mList0;
1658 };// DynamicNodeManager<2> class
1659 
1660 
1661 ////////////////////////////////////////////
1662 
1663 
1664 /// @private
1665 /// Template specialization of the DynamicNodeManager with three levels of nodes
1666 template<typename TreeOrLeafManagerT>
1667 class DynamicNodeManager<TreeOrLeafManagerT, 3>
1668 {
1669 public:
1670  using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1672  static_assert(RootNodeType::LEVEL > 2, "expected instantiation of template specialization");
1673  static const Index LEVELS = 3;
1674 
1675  explicit DynamicNodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) { }
1676 
1677  DynamicNodeManager(const DynamicNodeManager&) = delete;
1678 
1679  /// @brief Return a reference to the root node.
1680  const RootNodeType& root() const { return mRoot; }
1681 
1682  template<typename NodeOp>
1683  void foreachTopDown(const NodeOp& op, bool threaded = true,
1684  size_t leafGrainSize=1, size_t nonLeafGrainSize=1)
1685  {
1686  // root
1687  if (!op(mRoot, /*index=*/0)) return;
1688  // list2
1689  if (!mList2.initRootChildren(mRoot)) return;
1690  ForeachFilterOp<NodeOp> nodeOp2(op, mList2.nodeCount());
1691  mList2.foreachWithIndex(nodeOp2, threaded, nonLeafGrainSize);
1692  // list1
1693  if (!mList1.initNodeChildren(mList2, nodeOp2, !threaded)) return;
1694  ForeachFilterOp<NodeOp> nodeOp1(op, mList1.nodeCount());
1695  mList1.foreachWithIndex(nodeOp1, threaded, nonLeafGrainSize);
1696  // list0
1697  if (!mList0.initNodeChildren(mList1, nodeOp1, !threaded)) return;
1698  mList0.foreachWithIndex(op, threaded, leafGrainSize);
1699  }
1700 
1701  template<typename NodeOp>
1702  void reduceTopDown(NodeOp& op, bool threaded = true,
1703  size_t leafGrainSize=1, size_t nonLeafGrainSize=1)
1704  {
1705  // root
1706  if (!op(mRoot, /*index=*/0)) return;
1707  // list2
1708  if (!mList2.initRootChildren(mRoot)) return;
1709  ReduceFilterOp<NodeOp> nodeOp2(op, mList2.nodeCount());
1710  mList2.reduceWithIndex(nodeOp2, threaded, nonLeafGrainSize);
1711  // list1
1712  if (!mList1.initNodeChildren(mList2, nodeOp2, !threaded)) return;
1713  ReduceFilterOp<NodeOp> nodeOp1(op, mList1.nodeCount());
1714  mList1.reduceWithIndex(nodeOp1, threaded, nonLeafGrainSize);
1715  // list0
1716  if (!mList0.initNodeChildren(mList1, nodeOp1, !threaded)) return;
1717  mList0.reduceWithIndex(op, threaded, leafGrainSize);
1718  }
1719 
1720 protected:
1721  using NodeT3 = RootNodeType;
1722  using NonConstNodeT2 = typename NodeT3::ChildNodeType;
1723  using NodeT2 = typename CopyConstness<RootNodeType, NonConstNodeT2>::Type; // upper level
1724  using NonConstNodeT1 = typename NodeT2::ChildNodeType;
1725  using NodeT1 = typename CopyConstness<RootNodeType, NonConstNodeT1>::Type; // mid level
1726  using NonConstNodeT0 = typename NodeT1::ChildNodeType;
1727  using NodeT0 = typename CopyConstness<RootNodeType, NonConstNodeT0>::Type; // lower level
1728 
1729  using ListT2 = NodeList<NodeT2>; // upper level of internal nodes
1730  using ListT1 = NodeList<NodeT1>; // lower level of internal nodes
1731  using ListT0 = NodeList<NodeT0>; // lower level of internal nodes or leafs
1732 
1733  NodeT3& mRoot;
1734  ListT2 mList2;
1735  ListT1 mList1;
1736  ListT0 mList0;
1737 };// DynamicNodeManager<3> class
1738 
1739 
1740 ////////////////////////////////////////////
1741 
1742 
1743 /// @private
1744 /// Template specialization of the DynamicNodeManager with four levels of nodes
1745 template<typename TreeOrLeafManagerT>
1746 class DynamicNodeManager<TreeOrLeafManagerT, 4>
1747 {
1748 public:
1749  using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1751  static_assert(RootNodeType::LEVEL > 3, "expected instantiation of template specialization");
1752  static const Index LEVELS = 4;
1753 
1754  explicit DynamicNodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) { }
1755 
1756  DynamicNodeManager(const DynamicNodeManager&) = delete;
1757 
1758  /// @brief Return a reference to the root node.
1759  const RootNodeType& root() const { return mRoot; }
1760 
1761  template<typename NodeOp>
1762  void foreachTopDown(const NodeOp& op, bool threaded = true,
1763  size_t leafGrainSize=1, size_t nonLeafGrainSize=1)
1764  {
1765  // root
1766  if (!op(mRoot, /*index=*/0)) return;
1767  // list3
1768  if (!mList3.initRootChildren(mRoot)) return;
1769  ForeachFilterOp<NodeOp> nodeOp3(op, mList3.nodeCount());
1770  mList3.foreachWithIndex(nodeOp3, threaded, nonLeafGrainSize);
1771  // list2
1772  if (!mList2.initNodeChildren(mList3, nodeOp3, !threaded)) return;
1773  ForeachFilterOp<NodeOp> nodeOp2(op, mList2.nodeCount());
1774  mList2.foreachWithIndex(nodeOp2, threaded, nonLeafGrainSize);
1775  // list1
1776  if (!mList1.initNodeChildren(mList2, nodeOp2, !threaded)) return;
1777  ForeachFilterOp<NodeOp> nodeOp1(op, mList1.nodeCount());
1778  mList1.foreachWithIndex(nodeOp1, threaded, nonLeafGrainSize);
1779  // list0
1780  if (!mList0.initNodeChildren(mList1, nodeOp1, !threaded)) return;
1781  mList0.foreachWithIndex(op, threaded, leafGrainSize);
1782  }
1783 
1784  template<typename NodeOp>
1785  void reduceTopDown(NodeOp& op, bool threaded = true,
1786  size_t leafGrainSize=1, size_t nonLeafGrainSize=1)
1787  {
1788  // root
1789  if (!op(mRoot, /*index=*/0)) return;
1790  // list3
1791  if (!mList3.initRootChildren(mRoot)) return;
1792  ReduceFilterOp<NodeOp> nodeOp3(op, mList3.nodeCount());
1793  mList3.reduceWithIndex(nodeOp3, threaded, nonLeafGrainSize);
1794  // list2
1795  if (!mList2.initNodeChildren(mList3, nodeOp3, !threaded)) return;
1796  ReduceFilterOp<NodeOp> nodeOp2(op, mList2.nodeCount());
1797  mList2.reduceWithIndex(nodeOp2, threaded, nonLeafGrainSize);
1798  // list1
1799  if (!mList1.initNodeChildren(mList2, nodeOp2, !threaded)) return;
1800  ReduceFilterOp<NodeOp> nodeOp1(op, mList1.nodeCount());
1801  mList1.reduceWithIndex(nodeOp1, threaded, nonLeafGrainSize);
1802  // list0
1803  if (!mList0.initNodeChildren(mList1, nodeOp1, !threaded)) return;
1804  mList0.reduceWithIndex(op, threaded, leafGrainSize);
1805  }
1806 
1807 protected:
1808  using NodeT4 = RootNodeType;
1809  using NonConstNodeT3 = typename NodeT4::ChildNodeType;
1810  using NodeT3 = typename CopyConstness<RootNodeType, NonConstNodeT3>::Type; // upper level
1811  using NonConstNodeT2 = typename NodeT3::ChildNodeType;
1812  using NodeT2 = typename CopyConstness<RootNodeType, NonConstNodeT2>::Type; // upper mid level
1813  using NonConstNodeT1 = typename NodeT2::ChildNodeType;
1814  using NodeT1 = typename CopyConstness<RootNodeType, NonConstNodeT1>::Type; // lower mid level
1815  using NonConstNodeT0 = typename NodeT1::ChildNodeType;
1816  using NodeT0 = typename CopyConstness<RootNodeType, NonConstNodeT0>::Type; // lower level
1817 
1818  using ListT3 = NodeList<NodeT3>; // upper level of internal nodes
1819  using ListT2 = NodeList<NodeT2>; // upper mid level of internal nodes
1820  using ListT1 = NodeList<NodeT1>; // lower mid level of internal nodes
1821  using ListT0 = NodeList<NodeT0>; // lower level of internal nodes or leafs
1822 
1823  NodeT4& mRoot;
1824  ListT3 mList3;
1825  ListT2 mList2;
1826  ListT1 mList1;
1827  ListT0 mList0;
1828 };// DynamicNodeManager<4> class
1829 
1830 
1831 } // namespace tree
1832 } // namespace OPENVDB_VERSION_NAME
1833 } // namespace openvdb
1834 
1835 #endif // OPENVDB_TREE_NODEMANAGER_HAS_BEEN_INCLUDED
typename RootNodeType::ChildNodeType NonConstChildNodeType
Definition: NodeManager.h:898
bool empty() const
Definition: NodeManager.h:205
To facilitate threading over the nodes of a tree, cache node pointers in linear arrays, one for each level of the tree.
Definition: NodeManager.h:31
void foreachTopDown(const NodeOp &op, bool threaded=true, size_t leafGrainSize=1, size_t nonLeafGrainSize=1)
Threaded method that applies a user-supplied functor to all the nodes in the tree.
Definition: NodeManager.h:977
void foreachWithIndex(const NodeOp &op, bool threaded=true, size_t grainSize=1)
Definition: NodeManager.h:285
NodeRange(NodeRange &r, tbb::split)
Definition: NodeManager.h:195
const NodeList & nodeList() const
Definition: NodeManager.h:203
bool operator==(const Iterator &other) const
Definition: NodeManager.h:237
Iterator & operator++()
Advance to the next node.
Definition: NodeManager.h:219
typename CopyConstness< TreeOrLeafManagerT, NonConstChildNodeType >::Type ChildNodeType
Definition: NodeManager.h:540
__hostdev__ bool isValid(GridType gridType, GridClass gridClass)
return true if the combination of GridType and GridClass is valid.
Definition: NanoVDB.h:613
bool valid(size_t idx) const
Definition: NodeManager.h:783
size_t pos() const
Return the index into the list of the current node.
Definition: NodeManager.h:225
const OpT & op() const
Definition: NodeManager.h:744
uint64_t Index64
Definition: Types.h:53
void reduceBottomUp(NodeOp &op, bool threaded=true, size_t grainSize=1)
Threaded method that processes nodes with a user supplied functor.
Definition: NodeManager.h:699
Definition: NodeManager.h:37
ForeachFilterOp(const ForeachFilterOp &other)
Definition: NodeManager.h:732
Definition: NodeManager.h:725
typename std::remove_const< ToType >::type Type
Definition: Types.h:439
Definition: NodeManager.h:756
Definition: Coord.h:590
OpT & op()
Definition: NodeManager.h:788
RootNodeType & mRoot
Definition: NodeManager.h:1051
NodeManagerLink< ChildNodeType, LEVELS-1 > mChain
Definition: NodeManager.h:715
bool operator!=(const Iterator &other) const
Definition: NodeManager.h:233
Index32 Index
Definition: Types.h:54
typename TreeOrLeafManagerT::RootNodeType NonConstRootNodeType
Definition: NodeManager.h:537
OutGridT XformOp & op
Definition: ValueTransformer.h:139
void clear()
Definition: NodeManager.h:66
typename CopyConstness< TreeOrLeafManagerT, NonConstChildNodeType >::Type ChildNodeType
Definition: NodeManager.h:899
Definition: NodeManager.h:45
void reduceTopDown(NodeOp &op, bool threaded=true, size_t leafGrainSize=1, size_t nonLeafGrainSize=1)
Threaded method that processes nodes with a user supplied functor.
Definition: NodeManager.h:1044
Mat3< typename promote< T0, T1 >::type > operator*(const Mat3< T0 > &m0, const Mat3< T1 > &m1)
Multiply m0 by m1 and return the resulting matrix.
Definition: Mat3.h:597
typename CopyConstness< TreeOrLeafManagerT, NonConstRootNodeType >::Type RootNodeType
Definition: NodeManager.h:897
void operator()(NodeT &node, size_t idx) const
Definition: NodeManager.h:737
Index64 nodeCount() const
Return the total number of cached nodes (excluding the root node)
Definition: NodeManager.h:562
Index64 nodeCount(Index i) const
Return the number of cached nodes at level i, where 0 corresponds to the lowest level.
Definition: NodeManager.h:566
Iterator(const NodeRange &range, size_t pos)
Definition: NodeManager.h:212
std::unique_ptr< NodeT *[]> mNodePtrs
Definition: NodeManager.h:390
void split(ContainerT &out, const std::string &in, const char delim)
Definition: Name.h:43
ReduceFilterOp(OpT &op, openvdb::Index64 size)
Definition: NodeManager.h:758
bool initRootChildren(RootT &root)
Definition: NodeManager.h:75
void clear()
Clear all the cached tree nodes.
Definition: NodeManager.h:552
#define OPENVDB_ASSERT(X)
Definition: Assert.h:41
void foreachTopDown(const NodeOp &op, bool threaded=true, size_t grainSize=1)
Threaded method that applies a user-supplied functor to all the nodes in the tree.
Definition: NodeManager.h:632
void foreachBottomUp(const NodeOp &op, bool threaded=true, size_t grainSize=1)
Threaded method that applies a user-supplied functor to all the nodes in the tree.
Definition: NodeManager.h:625
NodeT & operator*() const
Return a reference to the node to which this iterator is pointing.
Definition: NodeManager.h:221
OutGridT XformOp bool threaded
Definition: ValueTransformer.h:140
typename CopyConstness< TreeOrLeafManagerT, NonConstRootNodeType >::Type RootNodeType
Definition: NodeManager.h:538
Definition: NodeManager.h:188
ForeachFilterOp(const OpT &op, openvdb::Index64 size)
Definition: NodeManager.h:727
NodeManager(TreeOrLeafManagerT &tree, bool serial=false)
Definition: NodeManager.h:543
const NodeRange & nodeRange() const
Definition: NodeManager.h:238
void reduceTopDown(NodeOp &op, bool threaded=true, size_t grainSize=1)
Threaded method that processes nodes with a user supplied functor.
Definition: NodeManager.h:706
bool valid(size_t idx) const
Definition: NodeManager.h:742
Definition: Exceptions.h:13
const RootNodeType & root() const
Return a reference to the root node.
Definition: NodeManager.h:559
void reduce(NodeOp &op, bool threaded=true, size_t grainSize=1)
Definition: NodeManager.h:276
NodeRange(size_t begin, size_t end, const NodeList &nodeList, size_t grainSize=1)
Definition: NodeManager.h:192
size_t grainsize() const
Definition: NodeManager.h:201
size_t size() const
Definition: NodeManager.h:199
OutGridT const XformOp bool bool
Definition: ValueTransformer.h:609
bool test() const
Return true if this iterator is not yet exhausted.
Definition: NodeManager.h:228
OPENVDB_AX_API void run(const char *ax, openvdb::GridBase &grid, const AttributeBindings &bindings={})
Run a full AX pipeline (parse, compile and execute) on a single OpenVDB Grid.
Iterator begin() const
Definition: NodeManager.h:245
typename TreeOrLeafManagerT::RootNodeType NonConstRootNodeType
Definition: NodeManager.h:896
void join(const ReduceFilterOp &other)
Definition: NodeManager.h:778
bool is_divisible() const
Definition: NodeManager.h:207
void operator()(NodeT &node, size_t idx) const
Definition: NodeManager.h:773
DynamicNodeManager(TreeOrLeafManagerT &tree)
Definition: NodeManager.h:902
ReduceFilterOp(const ReduceFilterOp &other)
Definition: NodeManager.h:763
DynamicNodeManagerLink< ChildNodeType, LEVELS-1 > mChain
Definition: NodeManager.h:1052
NodeT & operator()(size_t n) const
Definition: NodeManager.h:60
ReduceFilterOp(const ReduceFilterOp &other, tbb::split)
Definition: NodeManager.h:767
Index64 nodeCount() const
Definition: NodeManager.h:64
void reduceWithIndex(NodeOp &op, bool threaded=true, size_t grainSize=1)
Definition: NodeManager.h:293
RootNodeType & mRoot
Definition: NodeManager.h:714
static bool valid(size_t)
Definition: NodeManager.h:47
NodeT *& operator[](size_t n)
Definition: NodeManager.h:62
NodeT * operator->() const
Return a pointer to the node to which this iterator is pointing.
Definition: NodeManager.h:223
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
Iterator end() const
Definition: NodeManager.h:247
This class caches tree nodes of a specific type in a linear array.
Definition: NodeManager.h:55
const NonConstRootNodeType & root() const
Return a reference to the root node.
Definition: NodeManager.h:907
bool empty() const
Return true if this iterator is exhausted.
Definition: NodeManager.h:232
bool isValid() const
Definition: NodeManager.h:226
NodeRange nodeRange(size_t grainsize=1) const
Return a TBB-compatible NodeRange.
Definition: NodeManager.h:263
bool initNodeChildren(ParentsT &parents, const NodeFilterT &nodeFilter=NodeFilterT(), bool serial=false)
Definition: NodeManager.h:106
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:218
void rebuild(bool serial=false)
Clear and recache all the tree nodes from the tree. This is required if tree nodes have been added or...
Definition: NodeManager.h:556
typename RootNodeType::ChildNodeType NonConstChildNodeType
Definition: NodeManager.h:539