OpenVDB 12.0.0
 
Loading...
Searching...
No Matches
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
23namespace openvdb {
25namespace OPENVDB_VERSION_NAME {
26namespace tree {
27
28// Produce linear arrays of all tree nodes, to facilitate efficient threading
29// and bottom-up processing.
30template<typename TreeOrLeafManagerT, Index LEVELS = TreeOrLeafManagerT::RootNodeType::LEVEL>
31class NodeManager;
32
33
34// Produce linear arrays of all tree nodes lazily, to facilitate efficient threading
35// of topology-changing top-down workflows.
36template<typename TreeOrLeafManagerT, Index _LEVELS = TreeOrLeafManagerT::RootNodeType::LEVEL>
37class DynamicNodeManager;
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.
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.
54template<typename NodeT>
56{
57public:
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 }
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
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
195 NodeRange(NodeRange& r, tbb::split):
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
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
299private:
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
388protected:
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.
402template<typename NodeT, Index LEVEL>
404{
405public:
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
462protected:
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.
474template<typename NodeT>
475class NodeManagerLink<NodeT, 0>
476{
477public:
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
517protected:
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.
530template<typename TreeOrLeafManagerT, Index _LEVELS>
532{
533public:
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
713protected:
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
724template <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
746private:
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
755template <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
767 ReduceFilterOp(const ReduceFilterOp& other, tbb::split)
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
790private:
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.
802template<typename NodeT, Index LEVEL>
804{
805public:
806 using NonConstChildNodeType = typename NodeT::ChildNodeType;
808
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
853protected:
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.
862template<typename NodeT>
863class DynamicNodeManagerLink<NodeT, 0>
864{
865public:
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
884protected:
885 NodeList<NodeT> mList;
886};// DynamicNodeManagerLink class
887
888
889template<typename TreeOrLeafManagerT, Index _LEVELS>
891{
892public:
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
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
1050protected:
1053};// DynamicNodeManager class
1054
1055
1056
1057////////////////////////////////////////////
1058
1059
1060/// @private
1061/// Template specialization of the NodeManager with no caching of nodes
1062template<typename TreeOrLeafManagerT>
1063class NodeManager<TreeOrLeafManagerT, 0>
1064{
1065public:
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
1101protected:
1102 RootNodeType& mRoot;
1103}; // NodeManager<0>
1104
1105
1106////////////////////////////////////////////
1107
1108
1109/// @private
1110/// Template specialization of the NodeManager with one level of nodes
1111template<typename TreeOrLeafManagerT>
1112class NodeManager<TreeOrLeafManagerT, 1>
1113{
1114public:
1115 using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1116 using RootNodeType = typename CopyConstness<TreeOrLeafManagerT, NonConstRootNodeType>::Type;
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
1173protected:
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
1189template<typename TreeOrLeafManagerT>
1190class NodeManager<TreeOrLeafManagerT, 2>
1191{
1192public:
1193 using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1194 using RootNodeType = typename CopyConstness<TreeOrLeafManagerT, NonConstRootNodeType>::Type;
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
1261protected:
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
1282template<typename TreeOrLeafManagerT>
1283class NodeManager<TreeOrLeafManagerT, 3>
1284{
1285public:
1286 using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1287 using RootNodeType = typename CopyConstness<TreeOrLeafManagerT, NonConstRootNodeType>::Type;
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
1360protected:
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
1385template<typename TreeOrLeafManagerT>
1386class NodeManager<TreeOrLeafManagerT, 4>
1387{
1388public:
1389 using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1390 using RootNodeType = typename CopyConstness<TreeOrLeafManagerT, NonConstRootNodeType>::Type;
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
1472protected:
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
1501template<typename TreeOrLeafManagerT>
1502class DynamicNodeManager<TreeOrLeafManagerT, 0>
1503{
1504public:
1505 using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1506 using RootNodeType = typename CopyConstness<TreeOrLeafManagerT, NonConstRootNodeType>::Type;
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
1531protected:
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
1543template<typename TreeOrLeafManagerT>
1544class DynamicNodeManager<TreeOrLeafManagerT, 1>
1545{
1546public:
1547 using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1548 using RootNodeType = typename CopyConstness<TreeOrLeafManagerT, NonConstRootNodeType>::Type;
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
1583protected:
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
1599template<typename TreeOrLeafManagerT>
1600class DynamicNodeManager<TreeOrLeafManagerT, 2>
1601{
1602public:
1603 using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1604 using RootNodeType = typename CopyConstness<TreeOrLeafManagerT, NonConstRootNodeType>::Type;
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
1645protected:
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
1666template<typename TreeOrLeafManagerT>
1667class DynamicNodeManager<TreeOrLeafManagerT, 3>
1668{
1669public:
1670 using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1671 using RootNodeType = typename CopyConstness<TreeOrLeafManagerT, NonConstRootNodeType>::Type;
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
1720protected:
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
1745template<typename TreeOrLeafManagerT>
1746class DynamicNodeManager<TreeOrLeafManagerT, 4>
1747{
1748public:
1749 using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType;
1750 using RootNodeType = typename CopyConstness<TreeOrLeafManagerT, NonConstRootNodeType>::Type;
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
1807protected:
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
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
RootNodeType & mRoot
Definition NodeManager.h:1051
typename RootNodeType::ChildNodeType NonConstChildNodeType
Definition NodeManager.h:898
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
const NonConstRootNodeType & root() const
Return a reference to the root node.
Definition NodeManager.h:907
DynamicNodeManagerLink< ChildNodeType, LEVELS-1 > mChain
Definition NodeManager.h:1052
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
static const Index LEVELS
Definition NodeManager.h:893
typename CopyConstness< TreeOrLeafManagerT, NonConstRootNodeType >::Type RootNodeType
Definition NodeManager.h:897
typename CopyConstness< TreeOrLeafManagerT, NonConstChildNodeType >::Type ChildNodeType
Definition NodeManager.h:899
DynamicNodeManager(const DynamicNodeManager &)=delete
typename TreeOrLeafManagerT::RootNodeType NonConstRootNodeType
Definition NodeManager.h:896
DynamicNodeManager(TreeOrLeafManagerT &tree)
Definition NodeManager.h:902
bool test() const
Return true if this iterator is not yet exhausted.
Definition NodeManager.h:228
Iterator & operator=(const Iterator &)=default
bool isValid() const
Definition NodeManager.h:226
bool empty() const
Return true if this iterator is exhausted.
Definition NodeManager.h:232
bool operator!=(const Iterator &other) const
Definition NodeManager.h:233
NodeT & operator*() const
Return a reference to the node to which this iterator is pointing.
Definition NodeManager.h:221
const NodeRange & nodeRange() const
Definition NodeManager.h:238
size_t pos() const
Return the index into the list of the current node.
Definition NodeManager.h:225
Iterator(const NodeRange &range, size_t pos)
Definition NodeManager.h:212
NodeT * operator->() const
Return a pointer to the node to which this iterator is pointing.
Definition NodeManager.h:223
Iterator & operator++()
Advance to the next node.
Definition NodeManager.h:219
bool operator==(const Iterator &other) const
Definition NodeManager.h:237
Definition NodeManager.h:189
Iterator begin() const
Definition NodeManager.h:245
size_t size() const
Definition NodeManager.h:199
const NodeList & nodeList() const
Definition NodeManager.h:203
bool is_divisible() const
Definition NodeManager.h:207
NodeRange(size_t begin, size_t end, const NodeList &nodeList, size_t grainSize=1)
Definition NodeManager.h:192
Iterator end() const
Definition NodeManager.h:247
bool empty() const
Definition NodeManager.h:205
size_t grainsize() const
Definition NodeManager.h:201
NodeRange(NodeRange &r, tbb::split)
Definition NodeManager.h:195
This class caches tree nodes of a specific type in a linear array.
Definition NodeManager.h:56
void reduce(NodeOp &op, bool threaded=true, size_t grainSize=1)
Definition NodeManager.h:276
Index64 nodeCount() const
Definition NodeManager.h:64
NodeRange nodeRange(size_t grainsize=1) const
Return a TBB-compatible NodeRange.
Definition NodeManager.h:263
NodeT & operator()(size_t n) const
Definition NodeManager.h:60
void reduceWithIndex(NodeOp &op, bool threaded=true, size_t grainSize=1)
Definition NodeManager.h:293
std::unique_ptr< NodeT0 *[]> mNodePtrs
Definition NodeManager.h:390
NodeT0 ** mNodes
Definition NodeManager.h:391
bool initNodeChildren(ParentsT &parents, const NodeFilterT &nodeFilter=NodeFilterT(), bool serial=false)
Definition NodeManager.h:106
void foreachWithIndex(const NodeOp &op, bool threaded=true, size_t grainSize=1)
Definition NodeManager.h:285
NodeT *& operator[](size_t n)
Definition NodeManager.h:62
void clear()
Definition NodeManager.h:66
size_t mNodeCount
Definition NodeManager.h:389
bool initRootChildren(RootT &root)
Definition NodeManager.h:75
To facilitate threading over the nodes of a tree, cache node pointers in linear arrays,...
Definition NodeManager.h:532
void reduceTopDown(NodeOp &op, bool threaded=true, size_t grainSize=1)
Definition NodeManager.h:706
RootNodeType & mRoot
Definition NodeManager.h:714
NodeManager(TreeOrLeafManagerT &tree, bool serial=false)
Definition NodeManager.h:543
typename RootNodeType::ChildNodeType NonConstChildNodeType
Definition NodeManager.h:539
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
Index64 nodeCount() const
Return the total number of cached nodes (excluding the root node)
Definition NodeManager.h:562
NodeManager(const NodeManager &)=delete
void foreachTopDown(const NodeOp &op, bool threaded=true, size_t grainSize=1)
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
NodeManagerLink< ChildNodeType, LEVELS-1 > mChain
Definition NodeManager.h:715
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
static const Index LEVELS
Definition NodeManager.h:534
typename CopyConstness< TreeOrLeafManagerT, NonConstRootNodeType >::Type RootNodeType
Definition NodeManager.h:538
typename CopyConstness< TreeOrLeafManagerT, NonConstChildNodeType >::Type ChildNodeType
Definition NodeManager.h:540
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
typename TreeOrLeafManagerT::RootNodeType NonConstRootNodeType
Definition NodeManager.h:537
void clear()
Clear all the cached tree nodes.
Definition NodeManager.h:552
const RootNodeType & root() const
Return a reference to the root node.
Definition NodeManager.h:559
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.
void split(ContainerT &out, const std::string &in, const char delim)
Definition Name.h:43
OutGridT XformOp & op
Definition ValueTransformer.h:140
OutGridT XformOp bool threaded
Definition ValueTransformer.h:140
Definition TreeIterator.h:30
Definition PointDataGrid.h:170
Index32 Index
Definition Types.h:54
uint64_t Index64
Definition Types.h:53
Definition Exceptions.h:13
Definition Coord.h:590
typename std::remove_const< ToType >::type Type
Definition Types.h:439
Definition NodeManager.h:726
const OpT & op() const
Definition NodeManager.h:744
void operator()(NodeT &node, size_t idx) const
Definition NodeManager.h:737
ForeachFilterOp(const OpT &op, openvdb::Index64 size)
Definition NodeManager.h:727
bool valid(size_t idx) const
Definition NodeManager.h:742
ForeachFilterOp(const ForeachFilterOp &other)
Definition NodeManager.h:732
Definition NodeManager.h:46
static bool valid(size_t)
Definition NodeManager.h:47
Definition NodeManager.h:757
void operator()(NodeT &node, size_t idx) const
Definition NodeManager.h:773
bool valid(size_t idx) const
Definition NodeManager.h:783
ReduceFilterOp(const ReduceFilterOp &other)
Definition NodeManager.h:763
ReduceFilterOp(const ReduceFilterOp &other, tbb::split)
Definition NodeManager.h:767
ReduceFilterOp(OpT &op, openvdb::Index64 size)
Definition NodeManager.h:758
OpT & op()
Definition NodeManager.h:788
void join(const ReduceFilterOp &other)
Definition NodeManager.h:778
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:218