1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 /// @file tree/ValueAccessor.h
5 ///
6 /// When traversing a grid in a spatially coherent pattern (e.g., iterating
7 /// over neighboring voxels), request a @c ValueAccessor from the grid
8 /// (with Grid::getAccessor()) and use the accessor's @c getValue() and
9 /// @c setValue() methods.  These will typically be significantly faster
10 /// than accessing voxels directly in the grid's tree.
11 ///
12 /// @par Example:
13 ///
14 /// @code
15 /// FloatGrid grid;
16 /// FloatGrid::Accessor acc = grid.getAccessor();
17 /// // First access is slow:
18 /// acc.setValue(Coord(0, 0, 0), 100);
19 /// // Subsequent nearby accesses are fast, since the accessor now holds pointers
20 /// // to nodes that contain (0, 0, 0) along the path from the root of the grid's
21 /// // tree to the leaf:
22 /// acc.setValue(Coord(0, 0, 1), 100);
23 /// acc.getValue(Coord(0, 2, 0), 100);
24 /// // Slow, because the accessor must be repopulated:
25 /// acc.getValue(Coord(-1, -1, -1));
26 /// // Fast:
27 /// acc.getValue(Coord(-1, -1, -2));
28 /// acc.setValue(Coord(-1, -2, 0), -100);
29 /// @endcode
30 
31 #ifndef OPENVDB_TREE_VALUEACCESSOR_HAS_BEEN_INCLUDED
32 #define OPENVDB_TREE_VALUEACCESSOR_HAS_BEEN_INCLUDED
33 
34 #include <tbb/null_mutex.h>
35 #include <tbb/spin_mutex.h>
36 #include <openvdb/version.h>
37 #include <openvdb/Types.h>
38 #include <cassert>
39 #include <limits>
40 #include <type_traits>
41 
42 namespace openvdb {
43 OPENVDB_USE_VERSION_NAMESPACE
44 namespace OPENVDB_VERSION_NAME {
45 namespace tree {
46 
47 // Forward declarations of local classes that are not intended for general use
48 // The IsSafe template parameter is explained in the warning below.
49 template<typename TreeType, bool IsSafe = true>
50 class ValueAccessor0;
51 template<typename TreeType, bool IsSafe = true, Index L0 = 0>
52 class ValueAccessor1;
53 template<typename TreeType, bool IsSafe = true, Index L0 = 0, Index L1 = 1>
54 class ValueAccessor2;
55 template<typename TreeType, bool IsSafe = true, Index L0 = 0, Index L1 = 1, Index L2 = 2>
56 class ValueAccessor3;
57 template<typename TreeCacheT, typename NodeVecT, bool AtRoot> class CacheItem;
58 
59 
60 /// @brief This base class for ValueAccessors manages registration of an accessor
61 /// with a tree so that the tree can automatically clear the accessor whenever
62 /// one of its nodes is deleted.
63 ///
64 /// @internal A base class is needed because ValueAccessor is templated on both
65 /// a Tree type and a mutex type.  The various instantiations of the template
66 /// are distinct, unrelated types, so they can't easily be stored in a container
67 /// such as the Tree's CacheRegistry.  This base class, in contrast, is templated
68 /// only on the Tree type, so for any given Tree, only two distinct instantiations
69 /// are possible, ValueAccessorBase<Tree> and ValueAccessorBase<const Tree>.
70 ///
71 /// @warning If IsSafe = false then the ValueAccessor will not register itself
72 /// with the tree from which it is constructed. While in some rare cases this can
73 /// lead to better performance (since it avoids the small overhead of insertion
74 /// on creation and deletion on destruction) it is also unsafe if the tree is
75 /// modified. So unless you're an expert it is highly recommended to set
76 /// IsSafe = true, which is the default in all derived ValueAccessors defined
77 /// below. However if you know that the tree is no being modifed for the lifespan
78 /// of the ValueAccessor AND the work performed per ValueAccessor is small relative
79 /// to overhead of registering it you should consider setting IsSafe = false. If
80 /// this turns out to improve performance you should really rewrite your code so as
81 /// to better amortize the construction of the ValueAccessor, i.e. reuse it as much
82 /// as possible!
83 template<typename TreeType, bool IsSafe>
84 class ValueAccessorBase
85 {
86 public:
87     static const bool IsConstTree = std::is_const<TreeType>::value;
88 
89     /// @brief Return true if this accessor is safe, i.e. registered
90     /// by the tree from which it is constructed. Un-registered
91     /// accessors can in rare cases be faster because it avoids the
92     /// (small) overhead of registration, but they are unsafe if the
93     /// tree is modified. So unless you're an expert it is highly
94     /// recommended to set IsSafe = true (which is the default).
isSafe()95     static bool isSafe() { return IsSafe; }
96 
ValueAccessorBase(TreeType & tree)97     ValueAccessorBase(TreeType& tree): mTree(&tree)
98     {
99         if (IsSafe) tree.attachAccessor(*this);
100     }
101 
~ValueAccessorBase()102     virtual ~ValueAccessorBase() { if (IsSafe && mTree) mTree->releaseAccessor(*this); }
103 
104     /// @brief Return a pointer to the tree associated with this accessor.
105     /// @details The pointer will be null only if the tree from which this accessor
106     /// was constructed was subsequently deleted (which generally leaves the
107     /// accessor in an unsafe state).
getTree()108     TreeType* getTree() const { return mTree; }
109     /// Return a reference to the tree associated with this accessor.
tree()110     TreeType& tree() const { assert(mTree); return *mTree; }
111 
ValueAccessorBase(const ValueAccessorBase & other)112     ValueAccessorBase(const ValueAccessorBase& other): mTree(other.mTree)
113     {
114         if (IsSafe && mTree) mTree->attachAccessor(*this);
115     }
116 
117     ValueAccessorBase& operator=(const ValueAccessorBase& other)
118     {
119         if (&other != this) {
120             if (IsSafe && mTree) mTree->releaseAccessor(*this);
121             mTree = other.mTree;
122             if (IsSafe && mTree) mTree->attachAccessor(*this);
123         }
124         return *this;
125     }
126 
127     virtual void clear() = 0;
128 
129 protected:
130     // Allow trees to deregister themselves.
131     template<typename> friend class Tree;
132 
release()133     virtual void release() { mTree = nullptr; }
134 
135     TreeType* mTree;
136 }; // class ValueAccessorBase
137 
138 
139 ////////////////////////////////////////
140 
141 
142 /// When traversing a grid in a spatially coherent pattern (e.g., iterating
143 /// over neighboring voxels), request a @c ValueAccessor from the grid
144 /// (with Grid::getAccessor()) and use the accessor's @c getValue() and
145 /// @c setValue() methods.  These will typically be significantly faster
146 /// than accessing voxels directly in the grid's tree.
147 ///
148 /// A ValueAccessor caches pointers to tree nodes along the path to a voxel (x, y, z).
149 /// A subsequent access to voxel (x', y', z') starts from the cached leaf node and
150 /// moves up until a cached node that encloses (x', y', z') is found, then traverses
151 /// down the tree from that node to a leaf, updating the cache with the new path.
152 /// This leads to significant acceleration of spatially-coherent accesses.
153 ///
154 /// @param _TreeType    the type of the tree to be accessed [required]
155 /// @param IsSafe       if IsSafe = false then the ValueAccessor will
156 ///                     not register itself with the tree from which
157 ///                     it is constructed (see warning).
158 /// @param CacheLevels  the number of nodes to be cached, starting from the leaf level
159 ///                     and not including the root (i.e., CacheLevels < DEPTH),
160 ///                     and defaulting to all non-root nodes
161 /// @param MutexType    the type of mutex to use (see note)
162 ///
163 /// @warning If IsSafe = false then the ValueAccessor will not register itself
164 /// with the tree from which it is constructed. While in some rare cases this can
165 /// lead to better performance (since it avoids the small overhead of insertion
166 /// on creation and deletion on destruction) it is also unsafe if the tree is
167 /// modified. So unless you're an expert it is highly recommended to set
168 /// IsSafe = true, which is the default. However if you know that the tree is no
169 /// being modifed for the lifespan of the ValueAccessor AND the work performed
170 /// per ValueAccessor is small relative to overhead of registering it you should
171 /// consider setting IsSafe = false. If this improves performance you should
172 /// really rewrite your code so as to better amortize the construction of the
173 /// ValueAccessor, i.e. reuse it as much as possible!
174 ///
175 /// @note If @c MutexType is a TBB-compatible mutex, then multiple threads may
176 /// safely access a single, shared accessor.  However, it is highly recommended
177 /// that, instead, each thread be assigned its own, non-mutex-protected accessor.
178 template<typename _TreeType,
179          bool IsSafe = true,
180          Index CacheLevels = _TreeType::DEPTH-1,
181          typename MutexType = tbb::null_mutex>
182 class ValueAccessor: public ValueAccessorBase<_TreeType, IsSafe>
183 {
184 public:
185     static_assert(CacheLevels < _TreeType::DEPTH, "cache size exceeds tree depth");
186 
187     using TreeType = _TreeType;
188     using RootNodeT = typename TreeType::RootNodeType;
189     using LeafNodeT = typename TreeType::LeafNodeType;
190     using ValueType = typename RootNodeT::ValueType;
191     using BaseT = ValueAccessorBase<TreeType, IsSafe>;
192     using LockT = typename MutexType::scoped_lock;
193     using BaseT::IsConstTree;
194 
ValueAccessor(TreeType & tree)195     ValueAccessor(TreeType& tree): BaseT(tree), mCache(*this)
196     {
197         mCache.insert(Coord(), &tree.root());
198     }
199 
ValueAccessor(const ValueAccessor & other)200     ValueAccessor(const ValueAccessor& other): BaseT(other), mCache(*this, other.mCache) {}
201 
202     ValueAccessor& operator=(const ValueAccessor& other)
203     {
204         if (&other != this) {
205             this->BaseT::operator=(other);
206             mCache.copy(*this, other.mCache);
207         }
208         return *this;
209     }
210     ~ValueAccessor() override = default;
211 
212     /// Return the number of cache levels employed by this accessor.
numCacheLevels()213     static Index numCacheLevels() { return CacheLevels; }
214 
215     /// Return @c true if nodes along the path to the given voxel have been cached.
isCached(const Coord & xyz)216     bool isCached(const Coord& xyz) const { LockT lock(mMutex); return mCache.isCached(xyz); }
217 
218     /// Return the value of the voxel at the given coordinates.
getValue(const Coord & xyz)219     const ValueType& getValue(const Coord& xyz) const
220     {
221         LockT lock(mMutex);
222         return mCache.getValue(xyz);
223     }
224 
225     /// Return the active state of the voxel at the given coordinates.
isValueOn(const Coord & xyz)226     bool isValueOn(const Coord& xyz) const { LockT lock(mMutex); return mCache.isValueOn(xyz); }
227 
228     /// Return the active state of the voxel as well as its value
probeValue(const Coord & xyz,ValueType & value)229     bool probeValue(const Coord& xyz, ValueType& value) const
230     {
231         LockT lock(mMutex);
232         return mCache.probeValue(xyz,value);
233     }
234 
235     /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides,
236     /// or -1 if (x, y, z) isn't explicitly represented in the tree (i.e., if it is
237     /// implicitly a background voxel).
getValueDepth(const Coord & xyz)238     int getValueDepth(const Coord& xyz) const
239     {
240         LockT lock(mMutex);
241         return mCache.getValueDepth(xyz);
242     }
243 
244     /// Return @c true if the value of voxel (x, y, z) resides at the leaf level
245     /// of the tree, i.e., if it is not a tile value.
isVoxel(const Coord & xyz)246     bool isVoxel(const Coord& xyz) const { LockT lock(mMutex); return mCache.isVoxel(xyz); }
247 
248     //@{
249     /// Set the value of the voxel at the given coordinates and mark the voxel as active.
setValue(const Coord & xyz,const ValueType & value)250     void setValue(const Coord& xyz, const ValueType& value)
251     {
252         LockT lock(mMutex);
253         mCache.setValue(xyz, value);
254     }
setValueOn(const Coord & xyz,const ValueType & value)255     void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); }
256     //@}
257 
258     /// Set the value of the voxel at the given coordinate but don't change its active state.
setValueOnly(const Coord & xyz,const ValueType & value)259     void setValueOnly(const Coord& xyz, const ValueType& value)
260     {
261         LockT lock(mMutex);
262         mCache.setValueOnly(xyz, value);
263     }
264 
265     /// Set the value of the voxel at the given coordinates and mark the voxel as inactive.
setValueOff(const Coord & xyz,const ValueType & value)266     void setValueOff(const Coord& xyz, const ValueType& value)
267     {
268         LockT lock(mMutex);
269         mCache.setValueOff(xyz, value);
270     }
271 
272     /// @brief Apply a functor to the value of the voxel at the given coordinates
273     /// and mark the voxel as active.
274     /// @details See Tree::modifyValue() for details.
275     template<typename ModifyOp>
modifyValue(const Coord & xyz,const ModifyOp & op)276     void modifyValue(const Coord& xyz, const ModifyOp& op)
277     {
278         LockT lock(mMutex);
279         mCache.modifyValue(xyz, op);
280     }
281 
282     /// @brief Apply a functor to the voxel at the given coordinates.
283     /// @details See Tree::modifyValueAndActiveState() for details.
284     template<typename ModifyOp>
modifyValueAndActiveState(const Coord & xyz,const ModifyOp & op)285     void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op)
286     {
287         LockT lock(mMutex);
288         mCache.modifyValueAndActiveState(xyz, op);
289     }
290 
291     /// Set the active state of the voxel at the given coordinates but don't change its value.
292     void setActiveState(const Coord& xyz, bool on = true)
293     {
294         LockT lock(mMutex);
295         mCache.setActiveState(xyz, on);
296     }
297     /// Mark the voxel at the given coordinates as active but don't change its value.
setValueOn(const Coord & xyz)298     void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); }
299     /// Mark the voxel at the given coordinates as inactive but don't change its value.
setValueOff(const Coord & xyz)300     void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); }
301 
302     /// Return the cached node of type @a NodeType.  [Mainly for internal use]
303     template<typename NodeType>
getNode()304     NodeType* getNode()
305     {
306         LockT lock(mMutex);
307         NodeType* node = nullptr;
308         mCache.getNode(node);
309         return node;
310     }
311 
312     /// Cache the given node, which should lie along the path from the root node to
313     /// the node containing voxel (x, y, z).  [Mainly for internal use]
314     template<typename NodeType>
insertNode(const Coord & xyz,NodeType & node)315     void insertNode(const Coord& xyz, NodeType& node)
316     {
317         LockT lock(mMutex);
318         mCache.insert(xyz, &node);
319     }
320 
321     /// If a node of the given type exists in the cache, remove it, so that
322     /// isCached(xyz) returns @c false for any voxel (x, y, z) contained in
323     /// that node.  [Mainly for internal use]
324     template<typename NodeType>
eraseNode()325     void eraseNode() { LockT lock(mMutex); NodeType* node = nullptr; mCache.erase(node); }
326 
327     /// @brief Add the specified leaf to this tree, possibly creating a child branch
328     /// in the process.  If the leaf node already exists, replace it.
addLeaf(LeafNodeT * leaf)329     void addLeaf(LeafNodeT* leaf)
330     {
331         LockT lock(mMutex);
332         mCache.addLeaf(leaf);
333     }
334 
335     /// @brief Add a tile at the specified tree level that contains voxel (x, y, z),
336     /// possibly deleting existing nodes or creating new nodes in the process.
addTile(Index level,const Coord & xyz,const ValueType & value,bool state)337     void addTile(Index level, const Coord& xyz, const ValueType& value, bool state)
338     {
339         LockT lock(mMutex);
340         mCache.addTile(level, xyz, value, state);
341     }
342 
343     /// @brief Return a pointer to the leaf node that contains voxel (x, y, z).
344     /// If no such node exists, create one, but preserve the values and
345     /// active states of all voxels.
346     /// @details Use this method to preallocate a static tree topology
347     /// over which to safely perform multithreaded processing.
touchLeaf(const Coord & xyz)348     LeafNodeT* touchLeaf(const Coord& xyz)
349     {
350         LockT lock(mMutex);
351         return mCache.touchLeaf(xyz);
352     }
353 
354     //@{
355     /// @brief Return a pointer to the node of the specified type that contains
356     /// voxel (x, y, z), or @c nullptr if no such node exists.
357     template<typename NodeT>
probeNode(const Coord & xyz)358     NodeT* probeNode(const Coord& xyz)
359     {
360         LockT lock(mMutex);
361         return mCache.template probeNode<NodeT>(xyz);
362     }
363     template<typename NodeT>
probeConstNode(const Coord & xyz)364     const NodeT* probeConstNode(const Coord& xyz) const
365     {
366         LockT lock(mMutex);
367         return mCache.template probeConstNode<NodeT>(xyz);
368     }
369     template<typename NodeT>
probeNode(const Coord & xyz)370     const NodeT* probeNode(const Coord& xyz) const
371     {
372         return this->template probeConstNode<NodeT>(xyz);
373     }
374     //@}
375 
376     //@{
377     /// @brief Return a pointer to the leaf node that contains voxel (x, y, z),
378     /// or @c nullptr if no such node exists.
probeLeaf(const Coord & xyz)379     LeafNodeT* probeLeaf(const Coord& xyz)
380     {
381         LockT lock(mMutex);
382         return mCache.probeLeaf(xyz);
383     }
probeConstLeaf(const Coord & xyz)384     const LeafNodeT* probeConstLeaf(const Coord& xyz) const
385     {
386         LockT lock(mMutex);
387         return mCache.probeConstLeaf(xyz);
388     }
probeLeaf(const Coord & xyz)389     const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); }
390     //@}
391 
392     /// Remove all nodes from this cache, then reinsert the root node.
clear()393     void clear() override
394     {
395         LockT lock(mMutex);
396         mCache.clear();
397         if (this->mTree) mCache.insert(Coord(), &(this->mTree->root()));
398     }
399 
400 private:
401     // Allow nodes to insert themselves into the cache.
402     template<typename> friend class RootNode;
403     template<typename, Index> friend class InternalNode;
404     template<typename, Index> friend class LeafNode;
405     // Allow trees to deregister themselves.
406     template<typename> friend class Tree;
407 
408     /// Prevent this accessor from calling Tree::releaseCache() on a tree that
409     /// no longer exists.  (Called by mTree when it is destroyed.)
release()410     void release() override
411     {
412         LockT lock(mMutex);
413         this->BaseT::release();
414         mCache.clear();
415     }
416 
417     /// Cache the given node, which should lie along the path from the root node to
418     /// the node containing voxel (x, y, z).
419     /// @note This operation is not mutex-protected and is intended to be called
420     /// only by nodes and only in the context of a getValue() or setValue() call.
421     template<typename NodeType>
insert(const Coord & xyz,NodeType * node)422     void insert(const Coord& xyz, NodeType* node) { mCache.insert(xyz, node); }
423 
424     // Define a list of all tree node types from LeafNode to RootNode
425     using InvTreeT = typename RootNodeT::NodeChainType;
426     // Remove all tree node types that are excluded from the cache
427     static constexpr int64_t First = CacheLevels;
428     static constexpr int64_t Last = InvTreeT::template Index<RootNodeT>;
429     using SubtreeT = typename InvTreeT::template RemoveByIndex<First, Last-1>;
430     using CacheItemT = CacheItem<ValueAccessor, SubtreeT, SubtreeT::Size==1>;
431 
432     // Private member data
433     mutable CacheItemT mCache;
434     mutable MutexType  mMutex;
435 
436 }; // class ValueAccessor
437 
438 
439 /// @brief Template specialization of the ValueAccessor with no mutex and no cache levels
440 /// @details This specialization is provided mainly for benchmarking.
441 /// Accessors with caching will almost always be faster.
442 template<typename TreeType, bool IsSafe>
443 class ValueAccessor<TreeType, IsSafe, 0, tbb::null_mutex>
444     : public ValueAccessor0<TreeType, IsSafe>
445 {
446 public:
ValueAccessor(TreeType & tree)447     ValueAccessor(TreeType& tree): ValueAccessor0<TreeType, IsSafe>(tree) {}
ValueAccessor(const ValueAccessor & other)448     ValueAccessor(const ValueAccessor& other): ValueAccessor0<TreeType, IsSafe>(other) {}
449     ~ValueAccessor() override = default;
450 };
451 
452 
453 /// Template specialization of the ValueAccessor with no mutex and one cache level
454 template<typename TreeType, bool IsSafe>
455 class ValueAccessor<TreeType, IsSafe, 1, tbb::null_mutex>
456     : public ValueAccessor1<TreeType, IsSafe>
457 {
458 public:
ValueAccessor(TreeType & tree)459     ValueAccessor(TreeType& tree): ValueAccessor1<TreeType, IsSafe>(tree) {}
ValueAccessor(const ValueAccessor & other)460     ValueAccessor(const ValueAccessor& other): ValueAccessor1<TreeType, IsSafe>(other) {}
461     ~ValueAccessor() override = default;
462 };
463 
464 
465 /// Template specialization of the ValueAccessor with no mutex and two cache levels
466 template<typename TreeType, bool IsSafe>
467 class ValueAccessor<TreeType, IsSafe, 2, tbb::null_mutex>
468     : public ValueAccessor2<TreeType, IsSafe>
469 {
470 public:
ValueAccessor(TreeType & tree)471     ValueAccessor(TreeType& tree): ValueAccessor2<TreeType, IsSafe>(tree) {}
ValueAccessor(const ValueAccessor & other)472     ValueAccessor(const ValueAccessor& other): ValueAccessor2<TreeType, IsSafe>(other) {}
473     ~ValueAccessor() override = default;
474 };
475 
476 
477 /// Template specialization of the ValueAccessor with no mutex and three cache levels
478 template<typename TreeType, bool IsSafe>
479 class ValueAccessor<TreeType, IsSafe, 3, tbb::null_mutex>: public ValueAccessor3<TreeType, IsSafe>
480 {
481 public:
ValueAccessor(TreeType & tree)482     ValueAccessor(TreeType& tree): ValueAccessor3<TreeType, IsSafe>(tree) {}
483     ValueAccessor(const ValueAccessor&) = default;
484     ValueAccessor& operator=(const ValueAccessor&) = default;
485     ~ValueAccessor() override = default;
486 };
487 
488 
489 ////////////////////////////////////////
490 
491 
492 /// @brief This accessor is thread-safe (at the cost of speed) for both reading and
493 /// writing to a tree.  That is, multiple threads may safely access a single,
494 /// shared ValueAccessorRW.
495 ///
496 /// @warning Since the mutex-locking employed by the ValueAccessorRW
497 /// can seriously impair performance of multithreaded applications, it
498 /// is recommended that, instead, each thread be assigned its own
499 /// (non-mutex protected) accessor.
500 template<typename TreeType, bool IsSafe = true>
501 class ValueAccessorRW: public ValueAccessor<TreeType, IsSafe, TreeType::DEPTH-1, tbb::spin_mutex>
502 {
503 public:
ValueAccessorRW(TreeType & tree)504     ValueAccessorRW(TreeType& tree)
505         : ValueAccessor<TreeType, IsSafe, TreeType::DEPTH-1, tbb::spin_mutex>(tree)
506     {
507     }
508 };
509 
510 
511 ////////////////////////////////////////
512 
513 
514 //
515 // The classes below are for internal use and should rarely be used directly.
516 //
517 
518 // An element of a compile-time linked list of node pointers, ordered from LeafNode to RootNode
519 template<typename TreeCacheT, typename NodeVecT, bool AtRoot>
520 class CacheItem
521 {
522 public:
523     using NodeType = typename NodeVecT::Front;
524     using ValueType = typename NodeType::ValueType;
525     using LeafNodeType = typename NodeType::LeafNodeType;
526     using CoordLimits = std::numeric_limits<Int32>;
527 
CacheItem(TreeCacheT & parent)528     CacheItem(TreeCacheT& parent):
529         mParent(&parent),
530         mHash(CoordLimits::max()),
531         mNode(nullptr),
532         mNext(parent)
533     {
534     }
535 
536     //@{
537     /// Copy another CacheItem's node pointers and hash keys, but not its parent pointer.
CacheItem(TreeCacheT & parent,const CacheItem & other)538     CacheItem(TreeCacheT& parent, const CacheItem& other):
539         mParent(&parent),
540         mHash(other.mHash),
541         mNode(other.mNode),
542         mNext(parent, other.mNext)
543     {
544     }
545 
copy(TreeCacheT & parent,const CacheItem & other)546     CacheItem& copy(TreeCacheT& parent, const CacheItem& other)
547     {
548         mParent = &parent;
549         mHash = other.mHash;
550         mNode = other.mNode;
551         mNext.copy(parent, other.mNext);
552         return *this;
553     }
554     //@}
555 
isCached(const Coord & xyz)556     bool isCached(const Coord& xyz) const
557     {
558         return (this->isHashed(xyz) || mNext.isCached(xyz));
559     }
560 
561     /// Cache the given node at this level.
insert(const Coord & xyz,const NodeType * node)562     void insert(const Coord& xyz, const NodeType* node)
563     {
564         mHash = (node != nullptr) ? xyz & ~(NodeType::DIM-1) : Coord::max();
565         mNode = node;
566     }
567     /// Forward the given node to another level of the cache.
568     template<typename OtherNodeType>
insert(const Coord & xyz,const OtherNodeType * node)569     void insert(const Coord& xyz, const OtherNodeType* node) { mNext.insert(xyz, node); }
570 
571     /// Erase the node at this level.
erase(const NodeType *)572     void erase(const NodeType*) { mHash = Coord::max(); mNode = nullptr; }
573     /// Erase the node at another level of the cache.
574     template<typename OtherNodeType>
erase(const OtherNodeType * node)575     void erase(const OtherNodeType* node) { mNext.erase(node); }
576 
577     /// Erase the nodes at this and lower levels of the cache.
clear()578     void clear() { mHash = Coord::max(); mNode = nullptr; mNext.clear(); }
579 
580     /// Return the cached node (if any) at this level.
getNode(const NodeType * & node)581     void getNode(const NodeType*& node) const { node = mNode; }
getNode(const NodeType * & node)582     void getNode(const NodeType*& node) { node = mNode; }
getNode(NodeType * & node)583     void getNode(NodeType*& node)
584     {
585         // This combination of a static assertion and a const_cast might not be elegant,
586         // but it is a lot simpler than specializing TreeCache for const Trees.
587         static_assert(!TreeCacheT::IsConstTree, "can't get a non-const node from a const tree");
588         node = const_cast<NodeType*>(mNode);
589     }
590     /// Forward the request to another level of the cache.
591     template<typename OtherNodeType>
getNode(OtherNodeType * & node)592     void getNode(OtherNodeType*& node) { mNext.getNode(node); }
593 
594     /// Return the value of the voxel at the given coordinates.
getValue(const Coord & xyz)595     const ValueType& getValue(const Coord& xyz)
596     {
597         if (this->isHashed(xyz)) {
598             assert(mNode);
599             return mNode->getValueAndCache(xyz, *mParent);
600         }
601         return mNext.getValue(xyz);
602     }
603 
addLeaf(LeafNodeType * leaf)604     void addLeaf(LeafNodeType* leaf)
605     {
606         static_assert(!TreeCacheT::IsConstTree, "can't add a node to a const tree");
607         if (NodeType::LEVEL == 0) return;
608         if (this->isHashed(leaf->origin())) {
609             assert(mNode);
610             return const_cast<NodeType*>(mNode)->addLeafAndCache(leaf, *mParent);
611         }
612         mNext.addLeaf(leaf);
613     }
614 
addTile(Index level,const Coord & xyz,const ValueType & value,bool state)615     void addTile(Index level, const Coord& xyz, const ValueType& value, bool state)
616     {
617         static_assert(!TreeCacheT::IsConstTree, "can't add a tile to a const tree");
618         if (NodeType::LEVEL < level) return;
619         if (this->isHashed(xyz)) {
620             assert(mNode);
621             return const_cast<NodeType*>(mNode)->addTileAndCache(
622                 level, xyz, value, state, *mParent);
623         }
624         mNext.addTile(level, xyz, value, state);
625     }
626 
touchLeaf(const Coord & xyz)627     LeafNodeType* touchLeaf(const Coord& xyz)
628     {
629         static_assert(!TreeCacheT::IsConstTree, "can't get a non-const node from a const tree");
630         if (this->isHashed(xyz)) {
631             assert(mNode);
632             return const_cast<NodeType*>(mNode)->touchLeafAndCache(xyz, *mParent);
633         }
634         return mNext.touchLeaf(xyz);
635     }
636 
probeLeaf(const Coord & xyz)637     LeafNodeType* probeLeaf(const Coord& xyz)
638     {
639         static_assert(!TreeCacheT::IsConstTree, "can't get a non-const node from a const tree");
640         if (this->isHashed(xyz)) {
641             assert(mNode);
642             return const_cast<NodeType*>(mNode)->probeLeafAndCache(xyz, *mParent);
643         }
644         return mNext.probeLeaf(xyz);
645     }
646 
probeConstLeaf(const Coord & xyz)647     const LeafNodeType* probeConstLeaf(const Coord& xyz)
648     {
649         if (this->isHashed(xyz)) {
650             assert(mNode);
651             return mNode->probeConstLeafAndCache(xyz, *mParent);
652         }
653         return mNext.probeConstLeaf(xyz);
654     }
655 
656     template<typename NodeT>
probeNode(const Coord & xyz)657     NodeT* probeNode(const Coord& xyz)
658     {
659         static_assert(!TreeCacheT::IsConstTree, "can't get a non-const node from a const tree");
660         OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
661         if (this->isHashed(xyz)) {
662             if ((std::is_same<NodeT, NodeType>::value)) {
663                 assert(mNode);
664                 return reinterpret_cast<NodeT*>(const_cast<NodeType*>(mNode));
665             }
666             return const_cast<NodeType*>(mNode)->template probeNodeAndCache<NodeT>(xyz, *mParent);
667         }
668         return mNext.template probeNode<NodeT>(xyz);
669         OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
670     }
671 
672     template<typename NodeT>
probeConstNode(const Coord & xyz)673     const NodeT* probeConstNode(const Coord& xyz)
674     {
675         OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
676         if (this->isHashed(xyz)) {
677             if ((std::is_same<NodeT, NodeType>::value)) {
678                 assert(mNode);
679                 return reinterpret_cast<const NodeT*>(mNode);
680             }
681             return mNode->template probeConstNodeAndCache<NodeT>(xyz, *mParent);
682         }
683         return mNext.template probeConstNode<NodeT>(xyz);
684         OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
685     }
686 
687     /// Return the active state of the voxel at the given coordinates.
isValueOn(const Coord & xyz)688     bool isValueOn(const Coord& xyz)
689     {
690         if (this->isHashed(xyz)) {
691             assert(mNode);
692             return mNode->isValueOnAndCache(xyz, *mParent);
693         }
694         return mNext.isValueOn(xyz);
695     }
696 
697     /// Return the active state and value of the voxel at the given coordinates.
probeValue(const Coord & xyz,ValueType & value)698     bool probeValue(const Coord& xyz, ValueType& value)
699     {
700         if (this->isHashed(xyz)) {
701             assert(mNode);
702             return mNode->probeValueAndCache(xyz, value, *mParent);
703         }
704         return mNext.probeValue(xyz, value);
705     }
706 
getValueDepth(const Coord & xyz)707      int getValueDepth(const Coord& xyz)
708     {
709         if (this->isHashed(xyz)) {
710             assert(mNode);
711             return static_cast<int>(TreeCacheT::RootNodeT::LEVEL) -
712                    static_cast<int>(mNode->getValueLevelAndCache(xyz, *mParent));
713         } else {
714             return mNext.getValueDepth(xyz);
715         }
716     }
717 
isVoxel(const Coord & xyz)718     bool isVoxel(const Coord& xyz)
719     {
720         if (this->isHashed(xyz)) {
721             assert(mNode);
722             return mNode->getValueLevelAndCache(xyz, *mParent)==0;
723         } else {
724             return mNext.isVoxel(xyz);
725         }
726     }
727 
728     /// Set the value of the voxel at the given coordinates and mark the voxel as active.
setValue(const Coord & xyz,const ValueType & value)729     void setValue(const Coord& xyz, const ValueType& value)
730     {
731         if (this->isHashed(xyz)) {
732             assert(mNode);
733             static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values");
734             const_cast<NodeType*>(mNode)->setValueAndCache(xyz, value, *mParent);
735         } else {
736             mNext.setValue(xyz, value);
737         }
738     }
setValueOnly(const Coord & xyz,const ValueType & value)739     void setValueOnly(const Coord& xyz, const ValueType& value)
740     {
741         if (this->isHashed(xyz)) {
742             assert(mNode);
743             static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values");
744             const_cast<NodeType*>(mNode)->setValueOnlyAndCache(xyz, value, *mParent);
745         } else {
746             mNext.setValueOnly(xyz, value);
747         }
748     }
setValueOn(const Coord & xyz,const ValueType & value)749     void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); }
750 
751     /// @brief Apply a functor to the value of the voxel at the given coordinates
752     /// and mark the voxel as active.
753     /// @details See Tree::modifyValue() for details.
754     template<typename ModifyOp>
modifyValue(const Coord & xyz,const ModifyOp & op)755     void modifyValue(const Coord& xyz, const ModifyOp& op)
756     {
757         if (this->isHashed(xyz)) {
758             assert(mNode);
759             static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values");
760             const_cast<NodeType*>(mNode)->modifyValueAndCache(xyz, op, *mParent);
761         } else {
762             mNext.modifyValue(xyz, op);
763         }
764     }
765 
766     /// @brief Apply a functor to the voxel at the given coordinates.
767     /// @details See Tree::modifyValueAndActiveState() for details.
768     template<typename ModifyOp>
modifyValueAndActiveState(const Coord & xyz,const ModifyOp & op)769     void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op)
770     {
771         if (this->isHashed(xyz)) {
772             assert(mNode);
773             static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values");
774             const_cast<NodeType*>(mNode)->modifyValueAndActiveStateAndCache(xyz, op, *mParent);
775         } else {
776             mNext.modifyValueAndActiveState(xyz, op);
777         }
778     }
779 
780     /// Set the value of the voxel at the given coordinates and mark the voxel as inactive.
setValueOff(const Coord & xyz,const ValueType & value)781     void setValueOff(const Coord& xyz, const ValueType& value)
782     {
783         if (this->isHashed(xyz)) {
784             assert(mNode);
785             static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values");
786             const_cast<NodeType*>(mNode)->setValueOffAndCache(xyz, value, *mParent);
787         } else {
788             mNext.setValueOff(xyz, value);
789         }
790     }
791 
792     /// Set the active state of the voxel at the given coordinates.
setActiveState(const Coord & xyz,bool on)793     void setActiveState(const Coord& xyz, bool on)
794     {
795         if (this->isHashed(xyz)) {
796             assert(mNode);
797             static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values");
798             const_cast<NodeType*>(mNode)->setActiveStateAndCache(xyz, on, *mParent);
799         } else {
800             mNext.setActiveState(xyz, on);
801         }
802     }
803 
804 private:
805     CacheItem(const CacheItem&);
806     CacheItem& operator=(const CacheItem&);
807 
isHashed(const Coord & xyz)808     bool isHashed(const Coord& xyz) const
809     {
810         return (xyz[0] & ~Coord::ValueType(NodeType::DIM-1)) == mHash[0]
811             && (xyz[1] & ~Coord::ValueType(NodeType::DIM-1)) == mHash[1]
812             && (xyz[2] & ~Coord::ValueType(NodeType::DIM-1)) == mHash[2];
813     }
814 
815     TreeCacheT* mParent;
816     Coord mHash;
817     const NodeType* mNode;
818     using RestT = typename NodeVecT::PopFront;
819     CacheItem<TreeCacheT, RestT, /*AtRoot=*/RestT::Size == 1> mNext;
820 };// end of CacheItem
821 
822 
823 /// The tail of a compile-time list of cached node pointers, ordered from LeafNode to RootNode
824 template<typename TreeCacheT, typename NodeVecT>
825 class CacheItem<TreeCacheT, NodeVecT, /*AtRoot=*/true>
826 {
827 public:
828     using RootNodeType = typename NodeVecT::Front;
829     using ValueType = typename RootNodeType::ValueType;
830     using LeafNodeType = typename RootNodeType::LeafNodeType;
831 
CacheItem(TreeCacheT & parent)832     CacheItem(TreeCacheT& parent): mParent(&parent), mRoot(nullptr) {}
CacheItem(TreeCacheT & parent,const CacheItem & other)833     CacheItem(TreeCacheT& parent, const CacheItem& other): mParent(&parent), mRoot(other.mRoot) {}
834 
copy(TreeCacheT & parent,const CacheItem & other)835     CacheItem& copy(TreeCacheT& parent, const CacheItem& other)
836     {
837         mParent = &parent;
838         mRoot = other.mRoot;
839         return *this;
840     }
841 
isCached(const Coord & xyz)842     bool isCached(const Coord& xyz) const { return this->isHashed(xyz); }
843 
insert(const Coord &,const RootNodeType * root)844     void insert(const Coord&, const RootNodeType* root) { mRoot = root; }
845 
846     // Needed for node types that are not cached
847     template<typename OtherNodeType>
insert(const Coord &,const OtherNodeType *)848     void insert(const Coord&, const OtherNodeType*) {}
849 
erase(const RootNodeType *)850     void erase(const RootNodeType*) { mRoot = nullptr; }
851 
clear()852     void clear() { mRoot = nullptr; }
853 
getNode(RootNodeType * & node)854     void getNode(RootNodeType*& node)
855     {
856         static_assert(!TreeCacheT::IsConstTree, "can't get a non-const node from a const tree");
857         node = const_cast<RootNodeType*>(mRoot);
858     }
getNode(const RootNodeType * & node)859     void getNode(const RootNodeType*& node) const { node = mRoot; }
860 
addLeaf(LeafNodeType * leaf)861     void addLeaf(LeafNodeType* leaf)
862     {
863         assert(mRoot);
864         static_assert(!TreeCacheT::IsConstTree, "can't add a node to a const tree");
865         const_cast<RootNodeType*>(mRoot)->addLeafAndCache(leaf, *mParent);
866     }
867 
addTile(Index level,const Coord & xyz,const ValueType & value,bool state)868     void addTile(Index level, const Coord& xyz, const ValueType& value, bool state)
869     {
870         assert(mRoot);
871         static_assert(!TreeCacheT::IsConstTree, "can't add a tile to a const tree");
872         const_cast<RootNodeType*>(mRoot)->addTileAndCache(level, xyz, value, state, *mParent);
873     }
874 
touchLeaf(const Coord & xyz)875     LeafNodeType* touchLeaf(const Coord& xyz)
876     {
877         assert(mRoot);
878         static_assert(!TreeCacheT::IsConstTree, "can't get a non-const node from a const tree");
879         return const_cast<RootNodeType*>(mRoot)->touchLeafAndCache(xyz, *mParent);
880     }
881 
probeLeaf(const Coord & xyz)882     LeafNodeType* probeLeaf(const Coord& xyz)
883     {
884         assert(mRoot);
885         static_assert(!TreeCacheT::IsConstTree, "can't get a non-const node from a const tree");
886         return const_cast<RootNodeType*>(mRoot)->probeLeafAndCache(xyz, *mParent);
887     }
888 
probeConstLeaf(const Coord & xyz)889     const LeafNodeType* probeConstLeaf(const Coord& xyz)
890     {
891         assert(mRoot);
892         return mRoot->probeConstLeafAndCache(xyz, *mParent);
893     }
894 
895     template<typename NodeType>
probeNode(const Coord & xyz)896     NodeType* probeNode(const Coord& xyz)
897     {
898         assert(mRoot);
899         static_assert(!TreeCacheT::IsConstTree, "can't get a non-const node from a const tree");
900         return const_cast<RootNodeType*>(mRoot)->
901             template probeNodeAndCache<NodeType>(xyz, *mParent);
902     }
903 
904     template<typename NodeType>
probeConstNode(const Coord & xyz)905     const NodeType* probeConstNode(const Coord& xyz)
906     {
907         assert(mRoot);
908         return mRoot->template probeConstNodeAndCache<NodeType>(xyz, *mParent);
909     }
910 
getValueDepth(const Coord & xyz)911     int getValueDepth(const Coord& xyz)
912     {
913         assert(mRoot);
914         return mRoot->getValueDepthAndCache(xyz, *mParent);
915     }
isValueOn(const Coord & xyz)916     bool isValueOn(const Coord& xyz)
917     {
918         assert(mRoot);
919         return mRoot->isValueOnAndCache(xyz, *mParent);
920     }
921 
probeValue(const Coord & xyz,ValueType & value)922     bool probeValue(const Coord& xyz, ValueType& value)
923     {
924         assert(mRoot);
925         return mRoot->probeValueAndCache(xyz, value, *mParent);
926     }
isVoxel(const Coord & xyz)927     bool isVoxel(const Coord& xyz)
928     {
929         assert(mRoot);
930         return mRoot->getValueDepthAndCache(xyz, *mParent) ==
931                static_cast<int>(RootNodeType::LEVEL);
932     }
getValue(const Coord & xyz)933     const ValueType& getValue(const Coord& xyz)
934     {
935         assert(mRoot);
936         return mRoot->getValueAndCache(xyz, *mParent);
937     }
938 
setValue(const Coord & xyz,const ValueType & value)939     void setValue(const Coord& xyz, const ValueType& value)
940     {
941         assert(mRoot);
942         static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values");
943         const_cast<RootNodeType*>(mRoot)->setValueAndCache(xyz, value, *mParent);
944     }
setValueOnly(const Coord & xyz,const ValueType & value)945     void setValueOnly(const Coord& xyz, const ValueType& value)
946     {
947         assert(mRoot);
948         static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values");
949         const_cast<RootNodeType*>(mRoot)->setValueOnlyAndCache(xyz, value, *mParent);
950     }
setValueOn(const Coord & xyz,const ValueType & value)951     void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); }
952 
953     template<typename ModifyOp>
modifyValue(const Coord & xyz,const ModifyOp & op)954     void modifyValue(const Coord& xyz, const ModifyOp& op)
955     {
956         assert(mRoot);
957         static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values");
958         const_cast<RootNodeType*>(mRoot)->modifyValueAndCache(xyz, op, *mParent);
959     }
960 
961     template<typename ModifyOp>
modifyValueAndActiveState(const Coord & xyz,const ModifyOp & op)962     void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op)
963     {
964         assert(mRoot);
965         static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values");
966         const_cast<RootNodeType*>(mRoot)->modifyValueAndActiveStateAndCache(xyz, op, *mParent);
967     }
968 
setValueOff(const Coord & xyz,const ValueType & value)969     void setValueOff(const Coord& xyz, const ValueType& value)
970     {
971         assert(mRoot);
972         static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values");
973         const_cast<RootNodeType*>(mRoot)->setValueOffAndCache(xyz, value, *mParent);
974     }
975 
setActiveState(const Coord & xyz,bool on)976     void setActiveState(const Coord& xyz, bool on)
977     {
978         assert(mRoot);
979         static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values");
980         const_cast<RootNodeType*>(mRoot)->setActiveStateAndCache(xyz, on, *mParent);
981     }
982 
983 private:
984     CacheItem(const CacheItem&);
985     CacheItem& operator=(const CacheItem&);
986 
isHashed(const Coord &)987     bool isHashed(const Coord&) const { return false; }
988 
989     TreeCacheT* mParent;
990     const RootNodeType* mRoot;
991 };// end of CacheItem specialized for RootNode
992 
993 
994 ////////////////////////////////////////
995 
996 
997 /// @brief ValueAccessor with no mutex and no node caching.
998 /// @details This specialization is provided mainly for benchmarking.
999 /// Accessors with caching will almost always be faster.
1000 template<typename _TreeType, bool IsSafe>
1001 class ValueAccessor0: public ValueAccessorBase<_TreeType, IsSafe>
1002 {
1003 public:
1004     using TreeType = _TreeType;
1005     using ValueType = typename TreeType::ValueType;
1006     using RootNodeT = typename TreeType::RootNodeType;
1007     using LeafNodeT = typename TreeType::LeafNodeType;
1008     using BaseT = ValueAccessorBase<TreeType, IsSafe>;
1009 
ValueAccessor0(TreeType & tree)1010     ValueAccessor0(TreeType& tree): BaseT(tree) {}
1011 
ValueAccessor0(const ValueAccessor0 & other)1012     ValueAccessor0(const ValueAccessor0& other): BaseT(other) {}
1013 
1014     /// Return the number of cache levels employed by this accessor.
numCacheLevels()1015     static Index numCacheLevels() { return 0; }
1016 
1017     ValueAccessor0& operator=(const ValueAccessor0& other)
1018     {
1019         if (&other != this) this->BaseT::operator=(other);
1020         return *this;
1021     }
1022 
1023     ~ValueAccessor0() override = default;
1024 
1025     /// Return @c true if nodes along the path to the given voxel have been cached.
isCached(const Coord &)1026     bool isCached(const Coord&) const { return false; }
1027 
1028     /// Return the value of the voxel at the given coordinates.
getValue(const Coord & xyz)1029     const ValueType& getValue(const Coord& xyz) const
1030     {
1031         assert(BaseT::mTree);
1032         return BaseT::mTree->getValue(xyz);
1033     }
1034 
1035     /// Return the active state of the voxel at the given coordinates.
isValueOn(const Coord & xyz)1036     bool isValueOn(const Coord& xyz) const
1037     {
1038         assert(BaseT::mTree);
1039         return BaseT::mTree->isValueOn(xyz);
1040     }
1041 
1042     /// Return the active state and, in @a value, the value of the voxel at the given coordinates.
probeValue(const Coord & xyz,ValueType & value)1043     bool probeValue(const Coord& xyz, ValueType& value) const
1044     {
1045         assert(BaseT::mTree);
1046         return BaseT::mTree->probeValue(xyz, value);
1047     }
1048 
1049     /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides,
1050     /// or -1 if (x, y, z) isn't explicitly represented in the tree (i.e., if it is
1051     /// implicitly a background voxel).
getValueDepth(const Coord & xyz)1052     int getValueDepth(const Coord& xyz) const
1053     {
1054         assert(BaseT::mTree);
1055         return BaseT::mTree->getValueDepth(xyz);
1056     }
1057 
1058     /// Return @c true if the value of voxel (x, y, z) resides at the leaf level
1059     /// of the tree, i.e., if it is not a tile value.
isVoxel(const Coord & xyz)1060     bool isVoxel(const Coord& xyz) const
1061     {
1062         assert(BaseT::mTree);
1063         return BaseT::mTree->getValueDepth(xyz) == static_cast<int>(RootNodeT::LEVEL);
1064     }
1065 
1066     //@{
1067     /// Set the value of the voxel at the given coordinates and mark the voxel as active.
setValue(const Coord & xyz,const ValueType & value)1068     void setValue(const Coord& xyz, const ValueType& value)
1069     {
1070         assert(BaseT::mTree);
1071         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1072         BaseT::mTree->setValue(xyz, value);
1073     }
setValueOn(const Coord & xyz,const ValueType & value)1074     void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); }
1075     //@}
1076 
1077     /// Set the value of the voxel at the given coordinate but don't change its active state.
setValueOnly(const Coord & xyz,const ValueType & value)1078     void setValueOnly(const Coord& xyz, const ValueType& value)
1079     {
1080         assert(BaseT::mTree);
1081         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1082         BaseT::mTree->setValueOnly(xyz, value);
1083     }
1084 
1085     /// Set the value of the voxel at the given coordinates and mark the voxel as inactive.
setValueOff(const Coord & xyz,const ValueType & value)1086     void setValueOff(const Coord& xyz, const ValueType& value)
1087     {
1088         assert(BaseT::mTree);
1089         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1090         BaseT::mTree->root().setValueOff(xyz, value);
1091     }
1092 
1093     /// @brief Apply a functor to the value of the voxel at the given coordinates
1094     /// and mark the voxel as active.
1095     /// @details See Tree::modifyValue() for details.
1096     template<typename ModifyOp>
modifyValue(const Coord & xyz,const ModifyOp & op)1097     void modifyValue(const Coord& xyz, const ModifyOp& op)
1098     {
1099         assert(BaseT::mTree);
1100         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1101         BaseT::mTree->modifyValue(xyz, op);
1102     }
1103 
1104     /// @brief Apply a functor to the voxel at the given coordinates.
1105     /// @details See Tree::modifyValueAndActiveState() for details.
1106     template<typename ModifyOp>
modifyValueAndActiveState(const Coord & xyz,const ModifyOp & op)1107     void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op)
1108     {
1109         assert(BaseT::mTree);
1110         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1111         BaseT::mTree->modifyValueAndActiveState(xyz, op);
1112     }
1113 
1114     /// Set the active state of the voxel at the given coordinates but don't change its value.
1115     void setActiveState(const Coord& xyz, bool on = true)
1116     {
1117         assert(BaseT::mTree);
1118         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1119         BaseT::mTree->setActiveState(xyz, on);
1120     }
1121     /// Mark the voxel at the given coordinates as active but don't change its value.
setValueOn(const Coord & xyz)1122     void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); }
1123     /// Mark the voxel at the given coordinates as inactive but don't change its value.
setValueOff(const Coord & xyz)1124     void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); }
1125 
1126     /// Return the cached node of type @a NodeType.  [Mainly for internal use]
getNode()1127     template<typename NodeT> NodeT* getNode() { return nullptr; }
1128 
1129     /// Cache the given node, which should lie along the path from the root node to
1130     /// the node containing voxel (x, y, z).  [Mainly for internal use]
insertNode(const Coord &,NodeT &)1131     template<typename NodeT> void insertNode(const Coord&, NodeT&) {}
1132 
1133     /// @brief Add the specified leaf to this tree, possibly creating a child branch
1134     /// in the process.  If the leaf node already exists, replace it.
addLeaf(LeafNodeT * leaf)1135     void addLeaf(LeafNodeT* leaf)
1136     {
1137         assert(BaseT::mTree);
1138         static_assert(!BaseT::IsConstTree, "can't add a node to a const tree");
1139         BaseT::mTree->root().addLeaf(leaf);
1140     }
1141 
1142     /// @brief Add a tile at the specified tree level that contains voxel (x, y, z),
1143     /// possibly deleting existing nodes or creating new nodes in the process.
addTile(Index level,const Coord & xyz,const ValueType & value,bool state)1144     void addTile(Index level, const Coord& xyz, const ValueType& value, bool state)
1145     {
1146         assert(BaseT::mTree);
1147         static_assert(!BaseT::IsConstTree, "can't add a tile to a const tree");
1148         BaseT::mTree->root().addTile(level, xyz, value, state);
1149     }
1150 
1151     /// If a node of the given type exists in the cache, remove it, so that
1152     /// isCached(xyz) returns @c false for any voxel (x, y, z) contained in
1153     /// that node.  [Mainly for internal use]
eraseNode()1154     template<typename NodeT> void eraseNode() {}
1155 
touchLeaf(const Coord & xyz)1156     LeafNodeT* touchLeaf(const Coord& xyz)
1157     {
1158         assert(BaseT::mTree);
1159         static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree");
1160         return BaseT::mTree->touchLeaf(xyz);
1161     }
1162 
1163     template<typename NodeT>
probeNode(const Coord & xyz)1164     NodeT* probeNode(const Coord& xyz)
1165     {
1166         assert(BaseT::mTree);
1167         static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree");
1168         return BaseT::mTree->template probeNode<NodeT>(xyz);
1169     }
1170 
1171     template<typename NodeT>
probeConstNode(const Coord & xyz)1172     const NodeT* probeConstNode(const Coord& xyz) const
1173     {
1174         assert(BaseT::mTree);
1175         return BaseT::mTree->template probeConstNode<NodeT>(xyz);
1176     }
1177 
probeLeaf(const Coord & xyz)1178     LeafNodeT* probeLeaf(const Coord& xyz)
1179     {
1180         return this->template probeNode<LeafNodeT>(xyz);
1181     }
1182 
probeConstLeaf(const Coord & xyz)1183     const LeafNodeT* probeConstLeaf(const Coord& xyz) const
1184     {
1185         return this->template probeConstNode<LeafNodeT>(xyz);
1186     }
1187 
probeLeaf(const Coord & xyz)1188     const LeafNodeT* probeLeaf(const Coord& xyz) const
1189     {
1190         return this->probeConstLeaf(xyz);
1191     }
1192 
1193     /// Remove all nodes from this cache, then reinsert the root node.
clear()1194     void clear() override {}
1195 
1196 private:
1197     // Allow trees to deregister themselves.
1198     template<typename> friend class Tree;
1199 
1200     /// Prevent this accessor from calling Tree::releaseCache() on a tree that
1201     /// no longer exists.  (Called by mTree when it is destroyed.)
release()1202     void release() override { this->BaseT::release(); }
1203 
1204 }; // ValueAccessor0
1205 
1206 
1207 /// @brief Value accessor with one level of node caching.
1208 /// @details The node cache level is specified by L0 with the default value 0
1209 /// (defined in the forward declaration) corresponding to a LeafNode.
1210 ///
1211 /// @note This class is for experts only and should rarely be used
1212 /// directly. Instead use ValueAccessor with its default template arguments.
1213 template<typename _TreeType, bool IsSafe, Index L0>
1214 class ValueAccessor1 : public ValueAccessorBase<_TreeType, IsSafe>
1215 {
1216 public:
1217     static_assert(_TreeType::DEPTH >= 2, "cache size exceeds tree depth");
1218     static_assert(L0 < _TreeType::RootNodeType::LEVEL, "invalid cache level");
1219     using TreeType = _TreeType;
1220     using ValueType = typename TreeType::ValueType;
1221     using RootNodeT = typename TreeType::RootNodeType;
1222     using LeafNodeT = typename TreeType::LeafNodeType;
1223     using BaseT = ValueAccessorBase<TreeType, IsSafe>;
1224     using InvTreeT = typename RootNodeT::NodeChainType;
1225     using NodeT0 = typename InvTreeT::template Get<L0>;
1226 
1227     /// Constructor from a tree
ValueAccessor1(TreeType & tree)1228     ValueAccessor1(TreeType& tree) : BaseT(tree), mKey0(Coord::max()), mNode0(nullptr)
1229     {
1230     }
1231 
1232     /// Copy constructor
ValueAccessor1(const ValueAccessor1 & other)1233     ValueAccessor1(const ValueAccessor1& other) : BaseT(other) { this->copy(other); }
1234 
1235     /// Return the number of cache levels employed by this ValueAccessor
numCacheLevels()1236     static Index numCacheLevels() { return 1; }
1237 
1238     /// Assignment operator
1239     ValueAccessor1& operator=(const ValueAccessor1& other)
1240     {
1241         if (&other != this) {
1242             this->BaseT::operator=(other);
1243             this->copy(other);
1244         }
1245         return *this;
1246     }
1247 
1248     /// Virtual destructor
1249     ~ValueAccessor1() override = default;
1250 
1251     /// Return @c true if any of the nodes along the path to the given
1252     /// voxel have been cached.
isCached(const Coord & xyz)1253     bool isCached(const Coord& xyz) const
1254     {
1255         assert(BaseT::mTree);
1256         return this->isHashed(xyz);
1257     }
1258 
1259     /// Return the value of the voxel at the given coordinates.
getValue(const Coord & xyz)1260     const ValueType& getValue(const Coord& xyz) const
1261     {
1262         assert(BaseT::mTree);
1263         if (this->isHashed(xyz)) {
1264             assert(mNode0);
1265             return mNode0->getValueAndCache(xyz, this->self());
1266         }
1267         return BaseT::mTree->root().getValueAndCache(xyz, this->self());
1268     }
1269 
1270     /// Return the active state of the voxel at the given coordinates.
isValueOn(const Coord & xyz)1271     bool isValueOn(const Coord& xyz) const
1272     {
1273         assert(BaseT::mTree);
1274         if (this->isHashed(xyz)) {
1275             assert(mNode0);
1276             return mNode0->isValueOnAndCache(xyz, this->self());
1277         }
1278         return BaseT::mTree->root().isValueOnAndCache(xyz, this->self());
1279     }
1280 
1281     /// Return the active state of the voxel as well as its value
probeValue(const Coord & xyz,ValueType & value)1282     bool probeValue(const Coord& xyz, ValueType& value) const
1283     {
1284         assert(BaseT::mTree);
1285         if (this->isHashed(xyz)) {
1286             assert(mNode0);
1287             return mNode0->probeValueAndCache(xyz, value, this->self());
1288         }
1289         return BaseT::mTree->root().probeValueAndCache(xyz, value, this->self());
1290     }
1291 
1292     /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides,
1293     /// or -1 if (x, y, z) isn't explicitly represented in the tree (i.e., if it is
1294     /// implicitly a background voxel).
getValueDepth(const Coord & xyz)1295     int getValueDepth(const Coord& xyz) const
1296     {
1297         assert(BaseT::mTree);
1298         if (this->isHashed(xyz)) {
1299             assert(mNode0);
1300             return RootNodeT::LEVEL - mNode0->getValueLevelAndCache(xyz, this->self());
1301         }
1302         return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self());
1303     }
1304 
1305     /// Return @c true if the value of voxel (x, y, z) resides at the leaf level
1306     /// of the tree, i.e., if it is not a tile value.
isVoxel(const Coord & xyz)1307     bool isVoxel(const Coord& xyz) const
1308     {
1309         assert(BaseT::mTree);
1310         if (this->isHashed(xyz)) {
1311             assert(mNode0);
1312             return mNode0->getValueLevelAndCache(xyz, this->self()) == 0;
1313         }
1314         return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self()) ==
1315                static_cast<int>(RootNodeT::LEVEL);
1316     }
1317 
1318     //@{
1319     /// Set the value of the voxel at the given coordinates and mark the voxel as active.
setValue(const Coord & xyz,const ValueType & value)1320     void setValue(const Coord& xyz, const ValueType& value)
1321     {
1322         assert(BaseT::mTree);
1323         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1324         if (this->isHashed(xyz)) {
1325             assert(mNode0);
1326             const_cast<NodeT0*>(mNode0)->setValueAndCache(xyz, value, *this);
1327         } else {
1328             BaseT::mTree->root().setValueAndCache(xyz, value, *this);
1329         }
1330     }
setValueOn(const Coord & xyz,const ValueType & value)1331     void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); }
1332     //@}
1333 
1334     /// Set the value of the voxel at the given coordinate but preserves its active state.
setValueOnly(const Coord & xyz,const ValueType & value)1335     void setValueOnly(const Coord& xyz, const ValueType& value)
1336     {
1337         assert(BaseT::mTree);
1338         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1339         if (this->isHashed(xyz)) {
1340             assert(mNode0);
1341             const_cast<NodeT0*>(mNode0)->setValueOnlyAndCache(xyz, value, *this);
1342         } else {
1343             BaseT::mTree->root().setValueOnlyAndCache(xyz, value, *this);
1344         }
1345     }
1346 
1347     /// Set the value of the voxel at the given coordinates and mark the voxel as inactive.
setValueOff(const Coord & xyz,const ValueType & value)1348     void setValueOff(const Coord& xyz, const ValueType& value)
1349     {
1350         assert(BaseT::mTree);
1351         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1352         if (this->isHashed(xyz)) {
1353             assert(mNode0);
1354             const_cast<NodeT0*>(mNode0)->setValueOffAndCache(xyz, value, *this);
1355         } else {
1356             BaseT::mTree->root().setValueOffAndCache(xyz, value, *this);
1357         }
1358     }
1359 
1360     /// @brief Apply a functor to the value of the voxel at the given coordinates
1361     /// and mark the voxel as active.
1362     /// @details See Tree::modifyValue() for details.
1363     template<typename ModifyOp>
modifyValue(const Coord & xyz,const ModifyOp & op)1364     void modifyValue(const Coord& xyz, const ModifyOp& op)
1365     {
1366         assert(BaseT::mTree);
1367         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1368         if (this->isHashed(xyz)) {
1369             assert(mNode0);
1370             const_cast<NodeT0*>(mNode0)->modifyValueAndCache(xyz, op, *this);
1371         } else {
1372             BaseT::mTree->root().modifyValueAndCache(xyz, op, *this);
1373         }
1374     }
1375 
1376     /// @brief Apply a functor to the voxel at the given coordinates.
1377     /// @details See Tree::modifyValueAndActiveState() for details.
1378     template<typename ModifyOp>
modifyValueAndActiveState(const Coord & xyz,const ModifyOp & op)1379     void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op)
1380     {
1381         assert(BaseT::mTree);
1382         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1383         if (this->isHashed(xyz)) {
1384             assert(mNode0);
1385             const_cast<NodeT0*>(mNode0)->modifyValueAndActiveStateAndCache(xyz, op, *this);
1386         } else {
1387             BaseT::mTree->root().modifyValueAndActiveStateAndCache(xyz, op, *this);
1388         }
1389     }
1390 
1391     /// Set the active state of the voxel at the given coordinates but don't change its value.
1392     void setActiveState(const Coord& xyz, bool on = true)
1393     {
1394         assert(BaseT::mTree);
1395         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1396         if (this->isHashed(xyz)) {
1397             assert(mNode0);
1398             const_cast<NodeT0*>(mNode0)->setActiveStateAndCache(xyz, on, *this);
1399         } else {
1400             BaseT::mTree->root().setActiveStateAndCache(xyz, on, *this);
1401         }
1402     }
1403     /// Mark the voxel at the given coordinates as active but don't change its value.
setValueOn(const Coord & xyz)1404     void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); }
1405     /// Mark the voxel at the given coordinates as inactive but don't change its value.
setValueOff(const Coord & xyz)1406     void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); }
1407 
1408     /// Return the cached node of type @a NodeType.  [Mainly for internal use]
1409     template<typename NodeT>
getNode()1410     NodeT* getNode()
1411     {
1412         const NodeT* node = nullptr;
1413         this->getNode(node);
1414         return const_cast<NodeT*>(node);
1415     }
1416 
1417     /// Cache the given node, which should lie along the path from the root node to
1418     /// the node containing voxel (x, y, z).  [Mainly for internal use]
1419     template<typename NodeT>
insertNode(const Coord & xyz,NodeT & node)1420     void insertNode(const Coord& xyz, NodeT& node) { this->insert(xyz, &node); }
1421 
1422     /// If a node of the given type exists in the cache, remove it, so that
1423     /// isCached(xyz) returns @c false for any voxel (x, y, z) contained in
1424     /// that node.  [Mainly for internal use]
1425     template<typename NodeT>
eraseNode()1426     void eraseNode()
1427     {
1428         const NodeT* node = nullptr;
1429         this->eraseNode(node);
1430     }
1431 
1432     /// @brief Add the specified leaf to this tree, possibly creating a child branch
1433     /// in the process.  If the leaf node already exists, replace it.
addLeaf(LeafNodeT * leaf)1434     void addLeaf(LeafNodeT* leaf)
1435     {
1436         assert(BaseT::mTree);
1437         static_assert(!BaseT::IsConstTree, "can't add a node to a const tree");
1438         BaseT::mTree->root().addLeaf(leaf);
1439     }
1440 
1441     /// @brief Add a tile at the specified tree level that contains voxel (x, y, z),
1442     /// possibly deleting existing nodes or creating new nodes in the process.
addTile(Index level,const Coord & xyz,const ValueType & value,bool state)1443     void addTile(Index level, const Coord& xyz, const ValueType& value, bool state)
1444     {
1445         assert(BaseT::mTree);
1446         static_assert(!BaseT::IsConstTree, "can't add a tile to a const tree");
1447         BaseT::mTree->root().addTile(level, xyz, value, state);
1448     }
1449 
1450     /// @brief @return the leaf node that contains voxel (x, y, z) and
1451     /// if it doesn't exist, create it, but preserve the values and
1452     /// active states of all voxels.
1453     ///
1454     /// Use this method to preallocate a static tree topology over which to
1455     /// safely perform multithreaded processing.
touchLeaf(const Coord & xyz)1456     LeafNodeT* touchLeaf(const Coord& xyz)
1457     {
1458         assert(BaseT::mTree);
1459         static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree");
1460         if (this->isHashed(xyz)) {
1461             assert(mNode0);
1462             return const_cast<NodeT0*>(mNode0)->touchLeafAndCache(xyz, *this);
1463         }
1464         return BaseT::mTree->root().touchLeafAndCache(xyz, *this);
1465     }
1466 
1467     /// @brief @return a pointer to the node of the specified type that contains
1468     /// voxel (x, y, z) and if it doesn't exist, return @c nullptr.
1469     template<typename NodeT>
probeNode(const Coord & xyz)1470     NodeT* probeNode(const Coord& xyz)
1471     {
1472         assert(BaseT::mTree);
1473         static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree");
1474         OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
1475         if ((std::is_same<NodeT, NodeT0>::value)) {
1476             if (this->isHashed(xyz)) {
1477                 assert(mNode0);
1478                 return reinterpret_cast<NodeT*>(const_cast<NodeT0*>(mNode0));
1479             }
1480             return BaseT::mTree->root().template probeNodeAndCache<NodeT>(xyz, *this);
1481         }
1482         return nullptr;
1483         OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
1484     }
probeLeaf(const Coord & xyz)1485     LeafNodeT* probeLeaf(const Coord& xyz)
1486     {
1487         return this->template probeNode<LeafNodeT>(xyz);
1488     }
1489 
1490     /// @brief @return a const pointer to the nodeof the specified type that contains
1491     /// voxel (x, y, z) and if it doesn't exist, return @c nullptr.
1492     template<typename NodeT>
probeConstNode(const Coord & xyz)1493     const NodeT* probeConstNode(const Coord& xyz) const
1494     {
1495         assert(BaseT::mTree);
1496         OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
1497         if ((std::is_same<NodeT, NodeT0>::value)) {
1498             if (this->isHashed(xyz)) {
1499                 assert(mNode0);
1500                 return reinterpret_cast<const NodeT*>(mNode0);
1501             }
1502             return BaseT::mTree->root().template probeConstNodeAndCache<NodeT>(xyz, this->self());
1503         }
1504         return nullptr;
1505         OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
1506     }
probeConstLeaf(const Coord & xyz)1507     const LeafNodeT* probeConstLeaf(const Coord& xyz) const
1508     {
1509         return this->template probeConstNode<LeafNodeT>(xyz);
1510     }
probeLeaf(const Coord & xyz)1511     const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); }
1512 
1513     /// Remove all the cached nodes and invalidate the corresponding hash-keys.
clear()1514     void clear() override
1515     {
1516         mKey0  = Coord::max();
1517         mNode0 = nullptr;
1518     }
1519 
1520 private:
1521     // Allow nodes to insert themselves into the cache.
1522     template<typename> friend class RootNode;
1523     template<typename, Index> friend class InternalNode;
1524     template<typename, Index> friend class LeafNode;
1525     // Allow trees to deregister themselves.
1526     template<typename> friend class Tree;
1527 
1528     // This private method is merely for convenience.
self()1529     inline ValueAccessor1& self() const { return const_cast<ValueAccessor1&>(*this); }
1530 
getNode(const NodeT0 * & node)1531     void getNode(const NodeT0*& node) { node = mNode0; }
getNode(const RootNodeT * & node)1532     void getNode(const RootNodeT*& node)
1533     {
1534         node = (BaseT::mTree ? &BaseT::mTree->root() : nullptr);
1535     }
getNode(const OtherNodeType * & node)1536     template<typename OtherNodeType> void getNode(const OtherNodeType*& node) { node = nullptr; }
eraseNode(const NodeT0 *)1537     void eraseNode(const NodeT0*) { mKey0 = Coord::max(); mNode0 = nullptr; }
eraseNode(const OtherNodeType *)1538     template<typename OtherNodeType> void eraseNode(const OtherNodeType*) {}
1539 
1540     /// Private copy method
copy(const ValueAccessor1 & other)1541     inline void copy(const ValueAccessor1& other)
1542     {
1543         mKey0  = other.mKey0;
1544         mNode0 = other.mNode0;
1545     }
1546 
1547     /// Prevent this accessor from calling Tree::releaseCache() on a tree that
1548     /// no longer exists.  (Called by mTree when it is destroyed.)
release()1549     void release() override
1550     {
1551         this->BaseT::release();
1552         this->clear();
1553     }
1554     /// Cache the given node, which should lie along the path from the root node to
1555     /// the node containing voxel (x, y, z).
1556     /// @note This operation is not mutex-protected and is intended to be called
1557     /// only by nodes and only in the context of a getValue() or setValue() call.
insert(const Coord & xyz,const NodeT0 * node)1558     inline void insert(const Coord& xyz, const NodeT0* node)
1559     {
1560         assert(node);
1561         mKey0  = xyz & ~(NodeT0::DIM-1);
1562         mNode0 = node;
1563     }
1564 
1565     /// No-op in case a tree traversal attemps to insert a node that
1566     /// is not cached by the ValueAccessor
insert(const Coord &,const OtherNodeType *)1567     template<typename OtherNodeType> inline void insert(const Coord&, const OtherNodeType*) {}
1568 
isHashed(const Coord & xyz)1569     inline bool isHashed(const Coord& xyz) const
1570     {
1571         return (xyz[0] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[0]
1572             && (xyz[1] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[1]
1573             && (xyz[2] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[2];
1574     }
1575     mutable Coord mKey0;
1576     mutable const NodeT0* mNode0;
1577 }; // ValueAccessor1
1578 
1579 
1580 /// @brief Value accessor with two levels of node caching.
1581 /// @details The node cache levels are specified by L0 and L1
1582 /// with the default values 0 and 1 (defined in the forward declaration)
1583 /// corresponding to a LeafNode and its parent InternalNode.
1584 ///
1585 /// @note This class is for experts only and should rarely be used directly.
1586 /// Instead use ValueAccessor with its default template arguments.
1587 template<typename _TreeType, bool IsSafe, Index L0, Index L1>
1588 class ValueAccessor2 : public ValueAccessorBase<_TreeType, IsSafe>
1589 {
1590 public:
1591     static_assert(_TreeType::DEPTH >= 3, "cache size exceeds tree depth");
1592     static_assert(L0 < L1, "invalid cache level");
1593     static_assert(L1 < _TreeType::RootNodeType::LEVEL, "invalid cache level");
1594 
1595     using TreeType = _TreeType;
1596     using ValueType = typename TreeType::ValueType;
1597     using RootNodeT = typename TreeType::RootNodeType;
1598     using LeafNodeT = typename TreeType::LeafNodeType;
1599     using BaseT = ValueAccessorBase<TreeType, IsSafe>;
1600     using InvTreeT = typename RootNodeT::NodeChainType;
1601     using NodeT0 = typename InvTreeT::template Get<L0>;
1602     using NodeT1 = typename InvTreeT::template Get<L1>;
1603 
1604     /// Constructor from a tree
ValueAccessor2(TreeType & tree)1605     ValueAccessor2(TreeType& tree) : BaseT(tree),
1606                                      mKey0(Coord::max()), mNode0(nullptr),
1607                                      mKey1(Coord::max()), mNode1(nullptr) {}
1608 
1609     /// Copy constructor
ValueAccessor2(const ValueAccessor2 & other)1610     ValueAccessor2(const ValueAccessor2& other) : BaseT(other) { this->copy(other); }
1611 
1612     /// Return the number of cache levels employed by this ValueAccessor
numCacheLevels()1613     static Index numCacheLevels() { return 2; }
1614 
1615     /// Assignment operator
1616     ValueAccessor2& operator=(const ValueAccessor2& other)
1617     {
1618         if (&other != this) {
1619             this->BaseT::operator=(other);
1620             this->copy(other);
1621         }
1622         return *this;
1623     }
1624 
1625     /// Virtual destructor
1626     ~ValueAccessor2() override = default;
1627 
1628     /// Return @c true if any of the nodes along the path to the given
1629     /// voxel have been cached.
isCached(const Coord & xyz)1630     bool isCached(const Coord& xyz) const
1631     {
1632         assert(BaseT::mTree);
1633         return this->isHashed1(xyz) || this->isHashed0(xyz);
1634     }
1635 
1636     /// Return the value of the voxel at the given coordinates.
getValue(const Coord & xyz)1637     const ValueType& getValue(const Coord& xyz) const
1638     {
1639         assert(BaseT::mTree);
1640         if (this->isHashed0(xyz)) {
1641             assert(mNode0);
1642             return mNode0->getValueAndCache(xyz, this->self());
1643         } else if (this->isHashed1(xyz)) {
1644             assert(mNode1);
1645             return mNode1->getValueAndCache(xyz, this->self());
1646         }
1647         return BaseT::mTree->root().getValueAndCache(xyz, this->self());
1648     }
1649 
1650     /// Return the active state of the voxel at the given coordinates.
isValueOn(const Coord & xyz)1651     bool isValueOn(const Coord& xyz) const
1652     {
1653         assert(BaseT::mTree);
1654         if (this->isHashed0(xyz)) {
1655             assert(mNode0);
1656             return mNode0->isValueOnAndCache(xyz, this->self());
1657         } else if (this->isHashed1(xyz)) {
1658             assert(mNode1);
1659             return mNode1->isValueOnAndCache(xyz, this->self());
1660         }
1661         return BaseT::mTree->root().isValueOnAndCache(xyz, this->self());
1662     }
1663 
1664     /// Return the active state of the voxel as well as its value
probeValue(const Coord & xyz,ValueType & value)1665     bool probeValue(const Coord& xyz, ValueType& value) const
1666     {
1667         assert(BaseT::mTree);
1668         if (this->isHashed0(xyz)) {
1669             assert(mNode0);
1670             return mNode0->probeValueAndCache(xyz, value, this->self());
1671         } else if (this->isHashed1(xyz)) {
1672             assert(mNode1);
1673             return mNode1->probeValueAndCache(xyz, value, this->self());
1674         }
1675         return BaseT::mTree->root().probeValueAndCache(xyz, value, this->self());
1676     }
1677 
1678     /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides,
1679     /// or -1 if (x, y, z) isn't explicitly represented in the tree (i.e., if it is
1680     /// implicitly a background voxel).
getValueDepth(const Coord & xyz)1681     int getValueDepth(const Coord& xyz) const
1682     {
1683         assert(BaseT::mTree);
1684         if (this->isHashed0(xyz)) {
1685             assert(mNode0);
1686             return RootNodeT::LEVEL - mNode0->getValueLevelAndCache(xyz, this->self());
1687         } else if (this->isHashed1(xyz)) {
1688             assert(mNode1);
1689             return RootNodeT::LEVEL - mNode1->getValueLevelAndCache(xyz, this->self());
1690         }
1691         return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self());
1692     }
1693 
1694     /// Return @c true if the value of voxel (x, y, z) resides at the leaf level
1695     /// of the tree, i.e., if it is not a tile value.
isVoxel(const Coord & xyz)1696     bool isVoxel(const Coord& xyz) const
1697     {
1698         assert(BaseT::mTree);
1699         if (this->isHashed0(xyz)) {
1700             assert(mNode0);
1701             return mNode0->getValueLevelAndCache(xyz, this->self())==0;
1702         } else if (this->isHashed1(xyz)) {
1703             assert(mNode1);
1704             return mNode1->getValueLevelAndCache(xyz, this->self())==0;
1705         }
1706         return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self()) ==
1707                static_cast<int>(RootNodeT::LEVEL);
1708     }
1709 
1710     //@{
1711     /// Set the value of the voxel at the given coordinates and mark the voxel as active.
setValue(const Coord & xyz,const ValueType & value)1712     void setValue(const Coord& xyz, const ValueType& value)
1713     {
1714         assert(BaseT::mTree);
1715         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1716         if (this->isHashed0(xyz)) {
1717             assert(mNode0);
1718             const_cast<NodeT0*>(mNode0)->setValueAndCache(xyz, value, *this);
1719         } else if (this->isHashed1(xyz)) {
1720             assert(mNode1);
1721             const_cast<NodeT1*>(mNode1)->setValueAndCache(xyz, value, *this);
1722         } else {
1723             BaseT::mTree->root().setValueAndCache(xyz, value, *this);
1724         }
1725     }
setValueOn(const Coord & xyz,const ValueType & value)1726     void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); }
1727     //@}
1728 
1729     /// Set the value of the voxel at the given coordinate but preserves its active state.
setValueOnly(const Coord & xyz,const ValueType & value)1730     void setValueOnly(const Coord& xyz, const ValueType& value)
1731     {
1732         assert(BaseT::mTree);
1733         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1734         if (this->isHashed0(xyz)) {
1735             assert(mNode0);
1736             const_cast<NodeT0*>(mNode0)->setValueOnlyAndCache(xyz, value, *this);
1737         } else if (this->isHashed1(xyz)) {
1738             assert(mNode1);
1739             const_cast<NodeT1*>(mNode1)->setValueOnlyAndCache(xyz, value, *this);
1740         } else {
1741             BaseT::mTree->root().setValueOnlyAndCache(xyz, value, *this);
1742         }
1743     }
1744 
1745     /// Set the value of the voxel at the given coordinates and mark the voxel as inactive.
setValueOff(const Coord & xyz,const ValueType & value)1746     void setValueOff(const Coord& xyz, const ValueType& value)
1747     {
1748         assert(BaseT::mTree);
1749         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1750         if (this->isHashed0(xyz)) {
1751             assert(mNode0);
1752             const_cast<NodeT0*>(mNode0)->setValueOffAndCache(xyz, value, *this);
1753         } else if (this->isHashed1(xyz)) {
1754             assert(mNode1);
1755             const_cast<NodeT1*>(mNode1)->setValueOffAndCache(xyz, value, *this);
1756         } else {
1757             BaseT::mTree->root().setValueOffAndCache(xyz, value, *this);
1758         }
1759     }
1760 
1761     /// @brief Apply a functor to the value of the voxel at the given coordinates
1762     /// and mark the voxel as active.
1763     /// @details See Tree::modifyValue() for details.
1764     template<typename ModifyOp>
modifyValue(const Coord & xyz,const ModifyOp & op)1765     void modifyValue(const Coord& xyz, const ModifyOp& op)
1766     {
1767         assert(BaseT::mTree);
1768         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1769         if (this->isHashed0(xyz)) {
1770             assert(mNode0);
1771             const_cast<NodeT0*>(mNode0)->modifyValueAndCache(xyz, op, *this);
1772         } else if (this->isHashed1(xyz)) {
1773             assert(mNode1);
1774             const_cast<NodeT1*>(mNode1)->modifyValueAndCache(xyz, op, *this);
1775         } else {
1776             BaseT::mTree->root().modifyValueAndCache(xyz, op, *this);
1777         }
1778     }
1779 
1780     /// @brief Apply a functor to the voxel at the given coordinates.
1781     /// @details See Tree::modifyValueAndActiveState() for details.
1782     template<typename ModifyOp>
modifyValueAndActiveState(const Coord & xyz,const ModifyOp & op)1783     void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op)
1784     {
1785         assert(BaseT::mTree);
1786         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1787         if (this->isHashed0(xyz)) {
1788             assert(mNode0);
1789             const_cast<NodeT0*>(mNode0)->modifyValueAndActiveStateAndCache(xyz, op, *this);
1790         } else if (this->isHashed1(xyz)) {
1791             assert(mNode1);
1792             const_cast<NodeT1*>(mNode1)->modifyValueAndActiveStateAndCache(xyz, op, *this);
1793         } else {
1794             BaseT::mTree->root().modifyValueAndActiveStateAndCache(xyz, op, *this);
1795         }
1796     }
1797 
1798     /// Set the active state of the voxel at the given coordinates without changing its value.
1799     void setActiveState(const Coord& xyz, bool on = true)
1800     {
1801         assert(BaseT::mTree);
1802         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
1803         if (this->isHashed0(xyz)) {
1804             assert(mNode0);
1805             const_cast<NodeT0*>(mNode0)->setActiveStateAndCache(xyz, on, *this);
1806         } else if (this->isHashed1(xyz)) {
1807             assert(mNode1);
1808             const_cast<NodeT1*>(mNode1)->setActiveStateAndCache(xyz, on, *this);
1809         } else {
1810             BaseT::mTree->root().setActiveStateAndCache(xyz, on, *this);
1811         }
1812     }
1813     /// Mark the voxel at the given coordinates as active without changing its value.
setValueOn(const Coord & xyz)1814     void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); }
1815     /// Mark the voxel at the given coordinates as inactive without changing its value.
setValueOff(const Coord & xyz)1816     void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); }
1817 
1818     /// Return the cached node of type @a NodeType.  [Mainly for internal use]
1819     template<typename NodeT>
getNode()1820     NodeT* getNode()
1821     {
1822         const NodeT* node = nullptr;
1823         this->getNode(node);
1824         return const_cast<NodeT*>(node);
1825     }
1826 
1827     /// Cache the given node, which should lie along the path from the root node to
1828     /// the node containing voxel (x, y, z).  [Mainly for internal use]
1829     template<typename NodeT>
insertNode(const Coord & xyz,NodeT & node)1830     void insertNode(const Coord& xyz, NodeT& node) { this->insert(xyz, &node); }
1831 
1832     /// If a node of the given type exists in the cache, remove it, so that
1833     /// isCached(xyz) returns @c false for any voxel (x, y, z) contained in
1834     /// that node.  [Mainly for internal use]
1835     template<typename NodeT>
eraseNode()1836     void eraseNode()
1837     {
1838         const NodeT* node = nullptr;
1839         this->eraseNode(node);
1840     }
1841 
1842     /// @brief Add the specified leaf to this tree, possibly creating a child branch
1843     /// in the process.  If the leaf node already exists, replace it.
addLeaf(LeafNodeT * leaf)1844     void addLeaf(LeafNodeT* leaf)
1845     {
1846         assert(BaseT::mTree);
1847         static_assert(!BaseT::IsConstTree, "can't add a node to a const tree");
1848         if (this->isHashed1(leaf->origin())) {
1849             assert(mNode1);
1850             return const_cast<NodeT1*>(mNode1)->addLeafAndCache(leaf, *this);
1851         }
1852         BaseT::mTree->root().addLeafAndCache(leaf, *this);
1853     }
1854 
1855     /// @brief Add a tile at the specified tree level that contains voxel (x, y, z),
1856     /// possibly deleting existing nodes or creating new nodes in the process.
addTile(Index level,const Coord & xyz,const ValueType & value,bool state)1857     void addTile(Index level, const Coord& xyz, const ValueType& value, bool state)
1858     {
1859         assert(BaseT::mTree);
1860         static_assert(!BaseT::IsConstTree, "can't add a tile to a const tree");
1861         if (this->isHashed1(xyz)) {
1862             assert(mNode1);
1863             return const_cast<NodeT1*>(mNode1)->addTileAndCache(level, xyz, value, state, *this);
1864         }
1865         BaseT::mTree->root().addTileAndCache(level, xyz, value, state, *this);
1866     }
1867 
1868     /// @brief @return the leaf node that contains voxel (x, y, z) and
1869     /// if it doesn't exist, create it, but preserve the values and
1870     /// active states of all voxels.
1871     ///
1872     /// Use this method to preallocate a static tree topology over which to
1873     /// safely perform multithreaded processing.
touchLeaf(const Coord & xyz)1874     LeafNodeT* touchLeaf(const Coord& xyz)
1875     {
1876         assert(BaseT::mTree);
1877         static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree");
1878         if (this->isHashed0(xyz)) {
1879             assert(mNode0);
1880             return const_cast<NodeT0*>(mNode0)->touchLeafAndCache(xyz, *this);
1881         } else if (this->isHashed1(xyz)) {
1882             assert(mNode1);
1883             return const_cast<NodeT1*>(mNode1)->touchLeafAndCache(xyz, *this);
1884         }
1885         return BaseT::mTree->root().touchLeafAndCache(xyz, *this);
1886     }
1887     /// @brief @return a pointer to the node of the specified type that contains
1888     /// voxel (x, y, z) and if it doesn't exist, return @c nullptr.
1889     template<typename NodeT>
probeNode(const Coord & xyz)1890     NodeT* probeNode(const Coord& xyz)
1891     {
1892         assert(BaseT::mTree);
1893         static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree");
1894         OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
1895         if ((std::is_same<NodeT, NodeT0>::value)) {
1896             if (this->isHashed0(xyz)) {
1897                 assert(mNode0);
1898                 return reinterpret_cast<NodeT*>(const_cast<NodeT0*>(mNode0));
1899             } else if (this->isHashed1(xyz)) {
1900                 assert(mNode1);
1901                 return const_cast<NodeT1*>(mNode1)->template probeNodeAndCache<NodeT>(xyz, *this);
1902             }
1903             return BaseT::mTree->root().template probeNodeAndCache<NodeT>(xyz, *this);
1904         } else if ((std::is_same<NodeT, NodeT1>::value)) {
1905             if (this->isHashed1(xyz)) {
1906                 assert(mNode1);
1907                 return reinterpret_cast<NodeT*>(const_cast<NodeT1*>(mNode1));
1908             }
1909             return BaseT::mTree->root().template probeNodeAndCache<NodeT>(xyz, *this);
1910         }
1911         return nullptr;
1912         OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
1913     }
1914     /// @brief @return a pointer to the leaf node that contains
1915     /// voxel (x, y, z) and if it doesn't exist, return @c nullptr.
probeLeaf(const Coord & xyz)1916     LeafNodeT* probeLeaf(const Coord& xyz) { return this->template probeNode<LeafNodeT>(xyz); }
1917 
1918     /// @brief @return a const pointer to the node of the specified type that contains
1919     /// voxel (x, y, z) and if it doesn't exist, return @c nullptr.
1920     template<typename NodeT>
probeConstLeaf(const Coord & xyz)1921     const NodeT* probeConstLeaf(const Coord& xyz) const
1922     {
1923         OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
1924         if ((std::is_same<NodeT, NodeT0>::value)) {
1925             if (this->isHashed0(xyz)) {
1926                 assert(mNode0);
1927                 return reinterpret_cast<const NodeT*>(mNode0);
1928             } else if (this->isHashed1(xyz)) {
1929                 assert(mNode1);
1930                 return mNode1->template probeConstNodeAndCache<NodeT>(xyz, this->self());
1931             }
1932             return BaseT::mTree->root().template probeConstNodeAndCache<NodeT>(xyz, this->self());
1933         } else if ((std::is_same<NodeT, NodeT1>::value)) {
1934             if (this->isHashed1(xyz)) {
1935                 assert(mNode1);
1936                 return reinterpret_cast<const NodeT*>(mNode1);
1937             }
1938             return BaseT::mTree->root().template probeConstNodeAndCache<NodeT>(xyz, this->self());
1939         }
1940         return nullptr;
1941         OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
1942     }
1943     /// @brief @return a const pointer to the leaf node that contains
1944     /// voxel (x, y, z) and if it doesn't exist, return @c nullptr.
probeConstLeaf(const Coord & xyz)1945     const LeafNodeT* probeConstLeaf(const Coord& xyz) const
1946     {
1947         return this->template probeConstNode<LeafNodeT>(xyz);
1948     }
probeLeaf(const Coord & xyz)1949     const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); }
1950 
1951     /// @brief @return a const pointer to the node of the specified type that contains
1952     /// voxel (x, y, z) and if it doesn't exist, return @c nullptr.
1953     template<typename NodeT>
probeConstNode(const Coord & xyz)1954     const NodeT* probeConstNode(const Coord& xyz) const
1955     {
1956         assert(BaseT::mTree);
1957         OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
1958         if ((std::is_same<NodeT, NodeT0>::value)) {
1959             if (this->isHashed0(xyz)) {
1960                 assert(mNode0);
1961                 return reinterpret_cast<const NodeT*>(mNode0);
1962             } else if (this->isHashed1(xyz)) {
1963                 assert(mNode1);
1964                 return mNode1->template probeConstNodeAndCache<NodeT>(xyz, this->self());
1965             }
1966             return BaseT::mTree->root().template probeConstNodeAndCache<NodeT>(xyz, this->self());
1967         } else if ((std::is_same<NodeT, NodeT1>::value)) {
1968             if (this->isHashed1(xyz)) {
1969                 assert(mNode1);
1970                 return reinterpret_cast<const NodeT*>(mNode1);
1971             }
1972             return BaseT::mTree->root().template probeConstNodeAndCache<NodeT>(xyz, this->self());
1973         }
1974         return nullptr;
1975         OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
1976     }
1977 
1978     /// Remove all the cached nodes and invalidate the corresponding hash-keys.
clear()1979     void clear() override
1980     {
1981         mKey0  = Coord::max();
1982         mNode0 = nullptr;
1983         mKey1  = Coord::max();
1984         mNode1 = nullptr;
1985     }
1986 
1987 private:
1988     // Allow nodes to insert themselves into the cache.
1989     template<typename> friend class RootNode;
1990     template<typename, Index> friend class InternalNode;
1991     template<typename, Index> friend class LeafNode;
1992     // Allow trees to deregister themselves.
1993     template<typename> friend class Tree;
1994 
1995     // This private method is merely for convenience.
self()1996     inline ValueAccessor2& self() const { return const_cast<ValueAccessor2&>(*this); }
1997 
getNode(const NodeT0 * & node)1998     void getNode(const NodeT0*& node) { node = mNode0; }
getNode(const NodeT1 * & node)1999     void getNode(const NodeT1*& node) { node = mNode1; }
getNode(const RootNodeT * & node)2000     void getNode(const RootNodeT*& node)
2001     {
2002         node = (BaseT::mTree ? &BaseT::mTree->root() : nullptr);
2003     }
getNode(const OtherNodeType * & node)2004     template<typename OtherNodeType> void getNode(const OtherNodeType*& node) { node = nullptr; }
2005 
eraseNode(const NodeT0 *)2006     void eraseNode(const NodeT0*) { mKey0 = Coord::max(); mNode0 = nullptr; }
eraseNode(const NodeT1 *)2007     void eraseNode(const NodeT1*) { mKey1 = Coord::max(); mNode1 = nullptr; }
eraseNode(const OtherNodeType *)2008     template<typename OtherNodeType> void eraseNode(const OtherNodeType*) {}
2009 
2010     /// Private copy method
copy(const ValueAccessor2 & other)2011     inline void copy(const ValueAccessor2& other)
2012     {
2013         mKey0  = other.mKey0;
2014         mNode0 = other.mNode0;
2015         mKey1  = other.mKey1;
2016         mNode1 = other.mNode1;
2017     }
2018 
2019     /// Prevent this accessor from calling Tree::releaseCache() on a tree that
2020     /// no longer exists.  (Called by mTree when it is destroyed.)
release()2021     void release() override
2022     {
2023         this->BaseT::release();
2024         this->clear();
2025     }
2026 
2027     /// Cache the given node, which should lie along the path from the root node to
2028     /// the node containing voxel (x, y, z).
2029     /// @note This operation is not mutex-protected and is intended to be called
2030     /// only by nodes and only in the context of a getValue() or setValue() call.
insert(const Coord & xyz,const NodeT0 * node)2031     inline void insert(const Coord& xyz, const NodeT0* node)
2032     {
2033         assert(node);
2034         mKey0  = xyz & ~(NodeT0::DIM-1);
2035         mNode0 = node;
2036     }
insert(const Coord & xyz,const NodeT1 * node)2037     inline void insert(const Coord& xyz, const NodeT1* node)
2038     {
2039         assert(node);
2040         mKey1  = xyz & ~(NodeT1::DIM-1);
2041         mNode1 = node;
2042     }
2043     /// No-op in case a tree traversal attemps to insert a node that
2044     /// is not cached by the ValueAccessor
insert(const Coord &,const NodeT *)2045     template<typename NodeT> inline void insert(const Coord&, const NodeT*) {}
2046 
isHashed0(const Coord & xyz)2047     inline bool isHashed0(const Coord& xyz) const
2048     {
2049         return (xyz[0] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[0]
2050             && (xyz[1] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[1]
2051             && (xyz[2] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[2];
2052     }
isHashed1(const Coord & xyz)2053     inline bool isHashed1(const Coord& xyz) const
2054     {
2055         return (xyz[0] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[0]
2056             && (xyz[1] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[1]
2057             && (xyz[2] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[2];
2058     }
2059     mutable Coord mKey0;
2060     mutable const NodeT0* mNode0;
2061     mutable Coord mKey1;
2062     mutable const NodeT1* mNode1;
2063 }; // ValueAccessor2
2064 
2065 
2066 /// @brief Value accessor with three levels of node caching.
2067 /// @details The node cache levels are specified by L0, L1, and L2
2068 /// with the default values 0, 1 and 2 (defined in the forward declaration)
2069 /// corresponding to a LeafNode, its parent InternalNode, and its parent InternalNode.
2070 /// Since the default configuration of all typed trees and grids, e.g.,
2071 /// FloatTree or FloatGrid, has a depth of four, this value accessor is the one
2072 /// used by default.
2073 ///
2074 /// @note This class is for experts only and should rarely be used
2075 /// directly. Instead use ValueAccessor with its default template arguments
2076 template<typename _TreeType, bool IsSafe, Index L0, Index L1, Index L2>
2077 class ValueAccessor3 : public ValueAccessorBase<_TreeType, IsSafe>
2078 {
2079 public:
2080     static_assert(_TreeType::DEPTH >= 4, "cache size exceeds tree depth");
2081     static_assert(L0 < L1, "invalid cache level");
2082     static_assert(L1 < L2, "invalid cache level");
2083     static_assert(L2 < _TreeType::RootNodeType::LEVEL, "invalid cache level");
2084 
2085     using TreeType = _TreeType;
2086     using ValueType = typename TreeType::ValueType;
2087     using RootNodeT = typename TreeType::RootNodeType;
2088     using LeafNodeT = typename TreeType::LeafNodeType;
2089     using BaseT = ValueAccessorBase<TreeType, IsSafe>;
2090     using InvTreeT = typename RootNodeT::NodeChainType;
2091     using NodeT0 = typename InvTreeT::template Get<L0>;
2092     using NodeT1 = typename InvTreeT::template Get<L1>;
2093     using NodeT2 = typename InvTreeT::template Get<L2>;
2094 
2095     /// Constructor from a tree
ValueAccessor3(TreeType & tree)2096     ValueAccessor3(TreeType& tree) : BaseT(tree),
2097                                      mKey0(Coord::max()), mNode0(nullptr),
2098                                      mKey1(Coord::max()), mNode1(nullptr),
2099                                      mKey2(Coord::max()), mNode2(nullptr) {}
2100 
2101     /// Copy constructor
ValueAccessor3(const ValueAccessor3 & other)2102     ValueAccessor3(const ValueAccessor3& other) : BaseT(other) { this->copy(other); }
2103 
2104     /// Assignment operator
2105     ValueAccessor3& operator=(const ValueAccessor3& other)
2106     {
2107         if (&other != this) {
2108             this->BaseT::operator=(other);
2109             this->copy(other);
2110         }
2111         return *this;
2112     }
2113 
2114     /// Return the number of cache levels employed by this ValueAccessor
numCacheLevels()2115     static Index numCacheLevels() { return 3; }
2116 
2117     /// Virtual destructor
2118     ~ValueAccessor3() override = default;
2119 
2120     /// Return @c true if any of the nodes along the path to the given
2121     /// voxel have been cached.
isCached(const Coord & xyz)2122     bool isCached(const Coord& xyz) const
2123     {
2124         assert(BaseT::mTree);
2125         return this->isHashed2(xyz) || this->isHashed1(xyz) || this->isHashed0(xyz);
2126     }
2127 
2128     /// Return the value of the voxel at the given coordinates.
getValue(const Coord & xyz)2129     const ValueType& getValue(const Coord& xyz) const
2130     {
2131         assert(BaseT::mTree);
2132         if (this->isHashed0(xyz)) {
2133             assert(mNode0);
2134             return mNode0->getValueAndCache(xyz, this->self());
2135         } else if (this->isHashed1(xyz)) {
2136             assert(mNode1);
2137             return mNode1->getValueAndCache(xyz, this->self());
2138         } else if (this->isHashed2(xyz)) {
2139             assert(mNode2);
2140             return mNode2->getValueAndCache(xyz, this->self());
2141         }
2142         return BaseT::mTree->root().getValueAndCache(xyz, this->self());
2143     }
2144 
2145     /// Return the active state of the voxel at the given coordinates.
isValueOn(const Coord & xyz)2146     bool isValueOn(const Coord& xyz) const
2147     {
2148         assert(BaseT::mTree);
2149         if (this->isHashed0(xyz)) {
2150             assert(mNode0);
2151             return mNode0->isValueOnAndCache(xyz, this->self());
2152         } else if (this->isHashed1(xyz)) {
2153             assert(mNode1);
2154             return mNode1->isValueOnAndCache(xyz, this->self());
2155         } else if (this->isHashed2(xyz)) {
2156             assert(mNode2);
2157             return mNode2->isValueOnAndCache(xyz, this->self());
2158         }
2159         return BaseT::mTree->root().isValueOnAndCache(xyz, this->self());
2160     }
2161 
2162     /// Return the active state of the voxel as well as its value
probeValue(const Coord & xyz,ValueType & value)2163     bool probeValue(const Coord& xyz, ValueType& value) const
2164     {
2165         assert(BaseT::mTree);
2166         if (this->isHashed0(xyz)) {
2167             assert(mNode0);
2168             return mNode0->probeValueAndCache(xyz, value, this->self());
2169         } else if (this->isHashed1(xyz)) {
2170             assert(mNode1);
2171             return mNode1->probeValueAndCache(xyz, value, this->self());
2172         } else if (this->isHashed2(xyz)) {
2173             assert(mNode2);
2174             return mNode2->probeValueAndCache(xyz, value, this->self());
2175         }
2176         return BaseT::mTree->root().probeValueAndCache(xyz, value, this->self());
2177     }
2178 
2179     /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides,
2180     /// or -1 if (x, y, z) isn't explicitly represented in the tree (i.e., if it is
2181     /// implicitly a background voxel).
getValueDepth(const Coord & xyz)2182     int getValueDepth(const Coord& xyz) const
2183     {
2184         assert(BaseT::mTree);
2185         if (this->isHashed0(xyz)) {
2186             assert(mNode0);
2187             return RootNodeT::LEVEL - mNode0->getValueLevelAndCache(xyz, this->self());
2188         } else if (this->isHashed1(xyz)) {
2189             assert(mNode1);
2190             return RootNodeT::LEVEL - mNode1->getValueLevelAndCache(xyz, this->self());
2191         } else if (this->isHashed2(xyz)) {
2192             assert(mNode2);
2193             return RootNodeT::LEVEL - mNode2->getValueLevelAndCache(xyz, this->self());
2194         }
2195         return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self());
2196     }
2197 
2198     /// Return @c true if the value of voxel (x, y, z) resides at the leaf level
2199     /// of the tree, i.e., if it is not a tile value.
isVoxel(const Coord & xyz)2200     bool isVoxel(const Coord& xyz) const
2201     {
2202         assert(BaseT::mTree);
2203         if (this->isHashed0(xyz)) {
2204             assert(mNode0);
2205             return mNode0->getValueLevelAndCache(xyz, this->self())==0;
2206         } else if (this->isHashed1(xyz)) {
2207             assert(mNode1);
2208             return mNode1->getValueLevelAndCache(xyz, this->self())==0;
2209         } else if (this->isHashed2(xyz)) {
2210             assert(mNode2);
2211             return mNode2->getValueLevelAndCache(xyz, this->self())==0;
2212         }
2213         return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self()) ==
2214                static_cast<int>(RootNodeT::LEVEL);
2215     }
2216 
2217     //@{
2218     /// Set the value of the voxel at the given coordinates and mark the voxel as active.
setValue(const Coord & xyz,const ValueType & value)2219     void setValue(const Coord& xyz, const ValueType& value)
2220     {
2221         assert(BaseT::mTree);
2222         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
2223         if (this->isHashed0(xyz)) {
2224             assert(mNode0);
2225             const_cast<NodeT0*>(mNode0)->setValueAndCache(xyz, value, *this);
2226         } else if (this->isHashed1(xyz)) {
2227             assert(mNode1);
2228             const_cast<NodeT1*>(mNode1)->setValueAndCache(xyz, value, *this);
2229         } else if (this->isHashed2(xyz)) {
2230             assert(mNode2);
2231             const_cast<NodeT2*>(mNode2)->setValueAndCache(xyz, value, *this);
2232         } else {
2233             BaseT::mTree->root().setValueAndCache(xyz, value, *this);
2234         }
2235     }
setValueOn(const Coord & xyz,const ValueType & value)2236     void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); }
2237     //@}
2238 
2239     /// Set the value of the voxel at the given coordinate but preserves its active state.
setValueOnly(const Coord & xyz,const ValueType & value)2240     void setValueOnly(const Coord& xyz, const ValueType& value)
2241     {
2242         assert(BaseT::mTree);
2243         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
2244         if (this->isHashed0(xyz)) {
2245             assert(mNode0);
2246             const_cast<NodeT0*>(mNode0)->setValueOnlyAndCache(xyz, value, *this);
2247         } else if (this->isHashed1(xyz)) {
2248             assert(mNode1);
2249             const_cast<NodeT1*>(mNode1)->setValueOnlyAndCache(xyz, value, *this);
2250         } else if (this->isHashed2(xyz)) {
2251             assert(mNode2);
2252             const_cast<NodeT2*>(mNode2)->setValueOnlyAndCache(xyz, value, *this);
2253         } else {
2254             BaseT::mTree->root().setValueOnlyAndCache(xyz, value, *this);
2255         }
2256     }
2257 
2258     /// Set the value of the voxel at the given coordinates and mark the voxel as inactive.
setValueOff(const Coord & xyz,const ValueType & value)2259     void setValueOff(const Coord& xyz, const ValueType& value)
2260     {
2261         assert(BaseT::mTree);
2262         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
2263         if (this->isHashed0(xyz)) {
2264             assert(mNode0);
2265             const_cast<NodeT0*>(mNode0)->setValueOffAndCache(xyz, value, *this);
2266         } else if (this->isHashed1(xyz)) {
2267             assert(mNode1);
2268             const_cast<NodeT1*>(mNode1)->setValueOffAndCache(xyz, value, *this);
2269         } else if (this->isHashed2(xyz)) {
2270             assert(mNode2);
2271             const_cast<NodeT2*>(mNode2)->setValueOffAndCache(xyz, value, *this);
2272         } else {
2273             BaseT::mTree->root().setValueOffAndCache(xyz, value, *this);
2274         }
2275     }
2276 
2277     /// @brief Apply a functor to the value of the voxel at the given coordinates
2278     /// and mark the voxel as active.
2279     /// @details See Tree::modifyValue() for details.
2280     template<typename ModifyOp>
modifyValue(const Coord & xyz,const ModifyOp & op)2281     void modifyValue(const Coord& xyz, const ModifyOp& op)
2282     {
2283         assert(BaseT::mTree);
2284         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
2285         if (this->isHashed0(xyz)) {
2286             assert(mNode0);
2287             const_cast<NodeT0*>(mNode0)->modifyValueAndCache(xyz, op, *this);
2288         } else if (this->isHashed1(xyz)) {
2289             assert(mNode1);
2290             const_cast<NodeT1*>(mNode1)->modifyValueAndCache(xyz, op, *this);
2291         } else if (this->isHashed2(xyz)) {
2292             assert(mNode2);
2293             const_cast<NodeT2*>(mNode2)->modifyValueAndCache(xyz, op, *this);
2294         } else {
2295             BaseT::mTree->root().modifyValueAndCache(xyz, op, *this);
2296         }
2297     }
2298 
2299     /// @brief Apply a functor to the voxel at the given coordinates.
2300     /// @details See Tree::modifyValueAndActiveState() for details.
2301     template<typename ModifyOp>
modifyValueAndActiveState(const Coord & xyz,const ModifyOp & op)2302     void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op)
2303     {
2304         assert(BaseT::mTree);
2305         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
2306         if (this->isHashed0(xyz)) {
2307             assert(mNode0);
2308             const_cast<NodeT0*>(mNode0)->modifyValueAndActiveStateAndCache(xyz, op, *this);
2309         } else if (this->isHashed1(xyz)) {
2310             assert(mNode1);
2311             const_cast<NodeT1*>(mNode1)->modifyValueAndActiveStateAndCache(xyz, op, *this);
2312         } else if (this->isHashed2(xyz)) {
2313             assert(mNode2);
2314             const_cast<NodeT2*>(mNode2)->modifyValueAndActiveStateAndCache(xyz, op, *this);
2315         } else {
2316             BaseT::mTree->root().modifyValueAndActiveStateAndCache(xyz, op, *this);
2317         }
2318     }
2319 
2320     /// Set the active state of the voxel at the given coordinates without changing its value.
2321     void setActiveState(const Coord& xyz, bool on = true)
2322     {
2323         assert(BaseT::mTree);
2324         static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
2325         if (this->isHashed0(xyz)) {
2326             assert(mNode0);
2327             const_cast<NodeT0*>(mNode0)->setActiveStateAndCache(xyz, on, *this);
2328         } else if (this->isHashed1(xyz)) {
2329             assert(mNode1);
2330             const_cast<NodeT1*>(mNode1)->setActiveStateAndCache(xyz, on, *this);
2331         } else if (this->isHashed2(xyz)) {
2332             assert(mNode2);
2333             const_cast<NodeT2*>(mNode2)->setActiveStateAndCache(xyz, on, *this);
2334         } else {
2335             BaseT::mTree->root().setActiveStateAndCache(xyz, on, *this);
2336         }
2337     }
2338     /// Mark the voxel at the given coordinates as active without changing its value.
setValueOn(const Coord & xyz)2339     void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); }
2340     /// Mark the voxel at the given coordinates as inactive without changing its value.
setValueOff(const Coord & xyz)2341     void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); }
2342 
2343     /// Return the cached node of type @a NodeType.  [Mainly for internal use]
2344     template<typename NodeT>
getNode()2345     NodeT* getNode()
2346     {
2347         const NodeT* node = nullptr;
2348         this->getNode(node);
2349         return const_cast<NodeT*>(node);
2350     }
2351 
2352     /// Cache the given node, which should lie along the path from the root node to
2353     /// the node containing voxel (x, y, z).  [Mainly for internal use]
2354     template<typename NodeT>
insertNode(const Coord & xyz,NodeT & node)2355     void insertNode(const Coord& xyz, NodeT& node) { this->insert(xyz, &node); }
2356 
2357     /// If a node of the given type exists in the cache, remove it, so that
2358     /// isCached(xyz) returns @c false for any voxel (x, y, z) contained in
2359     /// that node.  [Mainly for internal use]
2360     template<typename NodeT>
eraseNode()2361     void eraseNode()
2362     {
2363         const NodeT* node = nullptr;
2364         this->eraseNode(node);
2365     }
2366 
2367     /// @brief Add the specified leaf to this tree, possibly creating a child branch
2368     /// in the process.  If the leaf node already exists, replace it.
addLeaf(LeafNodeT * leaf)2369     void addLeaf(LeafNodeT* leaf)
2370     {
2371         assert(BaseT::mTree);
2372         static_assert(!BaseT::IsConstTree, "can't add a node to a const tree");
2373         if (this->isHashed1(leaf->origin())) {
2374             assert(mNode1);
2375             return const_cast<NodeT1*>(mNode1)->addLeafAndCache(leaf, *this);
2376         } else if (this->isHashed2(leaf->origin())) {
2377             assert(mNode2);
2378             return const_cast<NodeT2*>(mNode2)->addLeafAndCache(leaf, *this);
2379         }
2380         BaseT::mTree->root().addLeafAndCache(leaf, *this);
2381     }
2382 
2383     /// @brief Add a tile at the specified tree level that contains voxel (x, y, z),
2384     /// possibly deleting existing nodes or creating new nodes in the process.
addTile(Index level,const Coord & xyz,const ValueType & value,bool state)2385     void addTile(Index level, const Coord& xyz, const ValueType& value, bool state)
2386     {
2387         assert(BaseT::mTree);
2388         static_assert(!BaseT::IsConstTree, "can't add a tile to a const tree");
2389         if (this->isHashed1(xyz)) {
2390             assert(mNode1);
2391             return const_cast<NodeT1*>(mNode1)->addTileAndCache(level, xyz, value, state, *this);
2392         } if (this->isHashed2(xyz)) {
2393             assert(mNode2);
2394             return const_cast<NodeT2*>(mNode2)->addTileAndCache(level, xyz, value, state, *this);
2395         }
2396         BaseT::mTree->root().addTileAndCache(level, xyz, value, state, *this);
2397     }
2398 
2399     /// @brief @return the leaf node that contains voxel (x, y, z) and
2400     /// if it doesn't exist, create it, but preserve the values and
2401     /// active states of all voxels.
2402     ///
2403     /// Use this method to preallocate a static tree topology over which to
2404     /// safely perform multithreaded processing.
touchLeaf(const Coord & xyz)2405     LeafNodeT* touchLeaf(const Coord& xyz)
2406     {
2407         assert(BaseT::mTree);
2408         static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree");
2409         if (this->isHashed0(xyz)) {
2410             assert(mNode0);
2411             return const_cast<NodeT0*>(mNode0);
2412         } else if (this->isHashed1(xyz)) {
2413             assert(mNode1);
2414             return const_cast<NodeT1*>(mNode1)->touchLeafAndCache(xyz, *this);
2415         } else if (this->isHashed2(xyz)) {
2416             assert(mNode2);
2417             return const_cast<NodeT2*>(mNode2)->touchLeafAndCache(xyz, *this);
2418         }
2419         return BaseT::mTree->root().touchLeafAndCache(xyz, *this);
2420     }
2421     /// @brief @return a pointer to the node of the specified type that contains
2422     /// voxel (x, y, z) and if it doesn't exist, return @c nullptr.
2423     template<typename NodeT>
probeNode(const Coord & xyz)2424     NodeT* probeNode(const Coord& xyz)
2425     {
2426         assert(BaseT::mTree);
2427         static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree");
2428         OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
2429         if ((std::is_same<NodeT, NodeT0>::value)) {
2430             if (this->isHashed0(xyz)) {
2431                 assert(mNode0);
2432                 return reinterpret_cast<NodeT*>(const_cast<NodeT0*>(mNode0));
2433             } else if (this->isHashed1(xyz)) {
2434                 assert(mNode1);
2435                 return const_cast<NodeT1*>(mNode1)->template probeNodeAndCache<NodeT>(xyz, *this);
2436             } else if (this->isHashed2(xyz)) {
2437                 assert(mNode2);
2438                 return const_cast<NodeT2*>(mNode2)->template probeNodeAndCache<NodeT>(xyz, *this);
2439             }
2440             return BaseT::mTree->root().template probeNodeAndCache<NodeT>(xyz, *this);
2441         } else if ((std::is_same<NodeT, NodeT1>::value)) {
2442             if (this->isHashed1(xyz)) {
2443                 assert(mNode1);
2444                 return reinterpret_cast<NodeT*>(const_cast<NodeT1*>(mNode1));
2445             } else if (this->isHashed2(xyz)) {
2446                 assert(mNode2);
2447                 return const_cast<NodeT2*>(mNode2)->template probeNodeAndCache<NodeT>(xyz, *this);
2448             }
2449             return BaseT::mTree->root().template probeNodeAndCache<NodeT>(xyz, *this);
2450         } else if ((std::is_same<NodeT, NodeT2>::value)) {
2451             if (this->isHashed2(xyz)) {
2452                 assert(mNode2);
2453                 return reinterpret_cast<NodeT*>(const_cast<NodeT2*>(mNode2));
2454             }
2455             return BaseT::mTree->root().template probeNodeAndCache<NodeT>(xyz, *this);
2456         }
2457         return nullptr;
2458         OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
2459     }
2460     /// @brief @return a pointer to the leaf node that contains
2461     /// voxel (x, y, z) and if it doesn't exist, return @c nullptr.
probeLeaf(const Coord & xyz)2462     LeafNodeT* probeLeaf(const Coord& xyz) { return this->template probeNode<LeafNodeT>(xyz); }
2463 
2464     /// @brief @return a const pointer to the node of the specified type that contains
2465     /// voxel (x, y, z) and if it doesn't exist, return @c nullptr.
2466     template<typename NodeT>
probeConstNode(const Coord & xyz)2467     const NodeT* probeConstNode(const Coord& xyz) const
2468     {
2469         assert(BaseT::mTree);
2470         OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
2471         if ((std::is_same<NodeT, NodeT0>::value)) {
2472             if (this->isHashed0(xyz)) {
2473                 assert(mNode0);
2474                 return reinterpret_cast<const NodeT*>(mNode0);
2475             } else if (this->isHashed1(xyz)) {
2476                 assert(mNode1);
2477                 return mNode1->template probeConstNodeAndCache<NodeT>(xyz, this->self());
2478             } else if (this->isHashed2(xyz)) {
2479                 assert(mNode2);
2480                 return mNode2->template probeConstNodeAndCache<NodeT>(xyz, this->self());
2481             }
2482             return BaseT::mTree->root().template probeConstNodeAndCache<NodeT>(xyz, this->self());
2483         } else if ((std::is_same<NodeT, NodeT1>::value)) {
2484             if (this->isHashed1(xyz)) {
2485                 assert(mNode1);
2486                 return reinterpret_cast<const NodeT*>(mNode1);
2487             } else if (this->isHashed2(xyz)) {
2488                 assert(mNode2);
2489                 return mNode2->template probeConstNodeAndCache<NodeT>(xyz, this->self());
2490             }
2491             return BaseT::mTree->root().template probeConstNodeAndCache<NodeT>(xyz, this->self());
2492         } else if ((std::is_same<NodeT, NodeT2>::value)) {
2493             if (this->isHashed2(xyz)) {
2494                 assert(mNode2);
2495                 return reinterpret_cast<const NodeT*>(mNode2);
2496             }
2497             return BaseT::mTree->root().template probeConstNodeAndCache<NodeT>(xyz, this->self());
2498         }
2499         return nullptr;
2500         OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
2501     }
2502     /// @brief @return a const pointer to the leaf node that contains
2503     /// voxel (x, y, z) and if it doesn't exist, return @c nullptr.
probeConstLeaf(const Coord & xyz)2504     const LeafNodeT* probeConstLeaf(const Coord& xyz) const
2505     {
2506         return this->template probeConstNode<LeafNodeT>(xyz);
2507     }
probeLeaf(const Coord & xyz)2508     const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); }
2509 
2510     /// Remove all the cached nodes and invalidate the corresponding hash-keys.
clear()2511     void clear() override
2512     {
2513         mKey0  = Coord::max();
2514         mNode0 = nullptr;
2515         mKey1  = Coord::max();
2516         mNode1 = nullptr;
2517         mKey2  = Coord::max();
2518         mNode2 = nullptr;
2519     }
2520 
2521 private:
2522     // Allow nodes to insert themselves into the cache.
2523     template<typename> friend class RootNode;
2524     template<typename, Index> friend class InternalNode;
2525     template<typename, Index> friend class LeafNode;
2526     // Allow trees to deregister themselves.
2527     template<typename> friend class Tree;
2528 
2529     // This private method is merely for convenience.
self()2530     inline ValueAccessor3& self() const { return const_cast<ValueAccessor3&>(*this); }
2531 
2532     /// Private copy method
copy(const ValueAccessor3 & other)2533     inline void copy(const ValueAccessor3& other)
2534     {
2535         mKey0  = other.mKey0;
2536         mNode0 = other.mNode0;
2537         mKey1  = other.mKey1;
2538         mNode1 = other.mNode1;
2539         mKey2  = other.mKey2;
2540         mNode2 = other.mNode2;
2541     }
2542 
2543     /// Prevent this accessor from calling Tree::releaseCache() on a tree that
2544     /// no longer exists.  (Called by mTree when it is destroyed.)
release()2545     void release() override
2546     {
2547         this->BaseT::release();
2548         this->clear();
2549     }
getNode(const NodeT0 * & node)2550     void getNode(const NodeT0*& node) { node = mNode0; }
getNode(const NodeT1 * & node)2551     void getNode(const NodeT1*& node) { node = mNode1; }
getNode(const NodeT2 * & node)2552     void getNode(const NodeT2*& node) { node = mNode2; }
getNode(const RootNodeT * & node)2553     void getNode(const RootNodeT*& node)
2554     {
2555         node = (BaseT::mTree ? &BaseT::mTree->root() : nullptr);
2556     }
getNode(const OtherNodeType * & node)2557     template<typename OtherNodeType> void getNode(const OtherNodeType*& node) { node = nullptr; }
2558 
eraseNode(const NodeT0 *)2559     void eraseNode(const NodeT0*) { mKey0 = Coord::max(); mNode0 = nullptr; }
eraseNode(const NodeT1 *)2560     void eraseNode(const NodeT1*) { mKey1 = Coord::max(); mNode1 = nullptr; }
eraseNode(const NodeT2 *)2561     void eraseNode(const NodeT2*) { mKey2 = Coord::max(); mNode2 = nullptr; }
eraseNode(const OtherNodeType *)2562     template<typename OtherNodeType> void eraseNode(const OtherNodeType*) {}
2563 
2564     /// Cache the given node, which should lie along the path from the root node to
2565     /// the node containing voxel (x, y, z).
2566     /// @note This operation is not mutex-protected and is intended to be called
2567     /// only by nodes and only in the context of a getValue() or setValue() call.
insert(const Coord & xyz,const NodeT0 * node)2568     inline void insert(const Coord& xyz, const NodeT0* node)
2569     {
2570         assert(node);
2571         mKey0  = xyz & ~(NodeT0::DIM-1);
2572         mNode0 = node;
2573     }
insert(const Coord & xyz,const NodeT1 * node)2574     inline void insert(const Coord& xyz, const NodeT1* node)
2575     {
2576         assert(node);
2577         mKey1  = xyz & ~(NodeT1::DIM-1);
2578         mNode1 = node;
2579     }
insert(const Coord & xyz,const NodeT2 * node)2580     inline void insert(const Coord& xyz, const NodeT2* node)
2581     {
2582         assert(node);
2583         mKey2  = xyz & ~(NodeT2::DIM-1);
2584         mNode2 = node;
2585     }
2586     /// No-op in case a tree traversal attemps to insert a node that
2587     /// is not cached by the ValueAccessor
2588     template<typename OtherNodeType>
insert(const Coord &,const OtherNodeType *)2589     inline void insert(const Coord&, const OtherNodeType*)
2590     {
2591     }
isHashed0(const Coord & xyz)2592     inline bool isHashed0(const Coord& xyz) const
2593     {
2594         return (xyz[0] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[0]
2595             && (xyz[1] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[1]
2596             && (xyz[2] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[2];
2597     }
isHashed1(const Coord & xyz)2598     inline bool isHashed1(const Coord& xyz) const
2599     {
2600         return (xyz[0] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[0]
2601             && (xyz[1] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[1]
2602             && (xyz[2] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[2];
2603     }
isHashed2(const Coord & xyz)2604     inline bool isHashed2(const Coord& xyz) const
2605     {
2606         return (xyz[0] & ~Coord::ValueType(NodeT2::DIM-1)) == mKey2[0]
2607             && (xyz[1] & ~Coord::ValueType(NodeT2::DIM-1)) == mKey2[1]
2608             && (xyz[2] & ~Coord::ValueType(NodeT2::DIM-1)) == mKey2[2];
2609     }
2610     mutable Coord mKey0;
2611     mutable const NodeT0* mNode0;
2612     mutable Coord mKey1;
2613     mutable const NodeT1* mNode1;
2614     mutable Coord mKey2;
2615     mutable const NodeT2* mNode2;
2616 }; // ValueAccessor3
2617 
2618 } // namespace tree
2619 } // namespace OPENVDB_VERSION_NAME
2620 } // namespace openvdb
2621 
2622 #endif // OPENVDB_TREE_VALUEACCESSOR_HAS_BEEN_INCLUDED
2623