HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ValueAccessor.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 /// @file tree/ValueAccessor.h
5 ///
6 /// @brief ValueAccessors are designed to help accelerate accesses into the
7 /// OpenVDB Tree structures by storing caches to Tree branches. When
8 /// traversing a grid in a spatially coherent pattern (e.g., iterating over
9 /// neighboring voxels), the same branches and nodes of the underlying tree
10 /// can be hit. If you do this using the Tree/RootNode methods directly,
11 /// traversal will occur at O(log(n)) (or O(n) depending on the hash map
12 /// implementation) for every access. However, using a ValueAccessor allows
13 /// for the Accessor to cache previously visited Nodes, providing possible
14 /// subsequent access speeds of O(1) if the next access is close to a
15 /// previously cached Node. Accessors are lightweight and can be configured
16 /// to cache any number of arbitrary Tree levels.
17 ///
18 /// The ValueAccessor interfaces matches that of compatible OpenVDB Tree
19 /// nodes. You can request an Accessor from a Grid (with Grid::getAccessor())
20 /// or construct one directly from a Tree. You can use, for example, the
21 /// accessor's @c getValue() and @c setValue() methods in place of those on
22 /// OpenVDB Nodes/Trees.
23 ///
24 /// @par Example:
25 /// @code
26 /// FloatGrid grid;
27 /// FloatGrid::Accessor acc = grid.getAccessor();
28 /// // First access is slow:
29 /// acc.setValue(Coord(0, 0, 0), 100);
30 ///
31 /// // Subsequent nearby accesses are fast, since the accessor now holds pointers
32 /// // to nodes that contain (0, 0, 0) along the path from the root of the grid's
33 /// // tree to the leaf:
34 /// acc.setValue(Coord(0, 0, 1), 100);
35 /// acc.getValue(Coord(0, 2, 0), 100);
36 ///
37 /// // Slow, because the accessor must be repopulated:
38 /// acc.getValue(Coord(-1, -1, -1));
39 ///
40 /// // Fast:
41 /// acc.getValue(Coord(-1, -1, -2));
42 /// acc.setValue(Coord(-1, -2, 0), -100);
43 /// @endcode
44 
45 #ifndef OPENVDB_TREE_VALUEACCESSOR_HAS_BEEN_INCLUDED
46 #define OPENVDB_TREE_VALUEACCESSOR_HAS_BEEN_INCLUDED
47 
48 #include <openvdb/version.h>
49 #include <openvdb/Types.h>
50 
51 #include <tbb/spin_mutex.h>
52 
53 #include <cassert>
54 #include <limits>
55 #include <type_traits>
56 #include <mutex>
57 
58 namespace openvdb {
60 namespace OPENVDB_VERSION_NAME {
61 namespace tree {
62 
63 // Forward declaration of the generic ValueAccessor API.
64 template<typename TreeType,
65  bool IsSafe = true,
66  typename MutexT = void,
67  typename IndexSequence = openvdb::make_index_sequence<std::max(size_t(1),TreeType::DEPTH)-1>>
69 
70 /// @brief Default alias for a ValueAccessor. This is simply a helper alias
71 /// for the generic definition but takes a single Index specifying the number
72 /// of nodes to cache. This is expanded into an index sequence (required for
73 /// backward compatibility).
74 /// @tparam TreeType The tree type
75 /// @tparam IsSafe Whether this accessor registers itself to the tree. See
76 /// the base class definition for more information on this parameter.
77 /// @tparam CacheLevels The number of node levels to cache _excluding_ the
78 /// Root node. The Root node is implicitly always included, even if this
79 /// value is zero.
80 /// @tparam MutexType An optional std compatible mutex to use which ensures
81 /// every call to the ValueAccessor API is thread safe. If void (the default)
82 /// no locking takes place. In general it's not advised to mutex lock
83 /// ValueAccessor methods (instead consider creating a accessor per thread).
84 template<typename TreeType, bool IsSafe = true,
85  size_t CacheLevels = std::max(Index(1),TreeType::DEPTH)-1, typename MutexType = void>
86 using ValueAccessor =
87  ValueAccessorImpl<TreeType, IsSafe, MutexType,
88  openvdb::make_index_sequence<CacheLevels>>;
89 
90 /// @brief Helper alias for a ValueAccessor which doesn't cache any Internal
91 /// or Leaf nodes.
92 template <typename TreeType, bool IsSafe>
93 using ValueAccessor0 =
95 /// @brief Helper alias for a ValueAccessor which caches a single node level.
96 /// By default, the node level is 0, which corresponds to the lowest node
97 /// level, typically LeafNodes.
98 template <typename TreeType, bool IsSafe, size_t L0 = 0>
99 using ValueAccessor1 =
101 /// @brief Helper alias for a ValueAccessor which caches two node levels. By
102 /// default the two lowest node levels are selected (0, 1) which typically
103 /// correspond to an InternalNode and its child LeafNodes. This instantiation
104 /// will only be valid for TreeTypes which have at least two levels of nodes
105 /// (excluding the Root node).
106 template <typename TreeType, bool IsSafe, size_t L0 = 0, size_t L1 = 1>
107 using ValueAccessor2 =
109 /// @brief Helper alias for a ValueAccessor which caches three node levels. By
110 /// default the three lowest node levels are selected (0, 1, 2) which
111 /// typically correspond to two InternalNodes followed by the bottom
112 /// LeafNodes. This instantiation will only be valid for TreeTypes which have
113 /// at least three levels of nodes (excluding the Root node).
114 template <typename TreeType, bool IsSafe, size_t L0 = 0, size_t L1 = 1, size_t L2 = 2>
115 using ValueAccessor3 =
117 
118 /// @brief Helper alias for a ValueAccesor which spin locks every API call.
119 template<typename TreeType, bool IsSafe = true,
120  size_t CacheLevels = std::max(Index(1),TreeType::DEPTH)-1>
121 using ValueAccessorRW =
122  ValueAccessorImpl<TreeType, IsSafe, tbb::spin_mutex,
123  openvdb::make_index_sequence<CacheLevels>>;
124 
125 
126 /// @brief This base class for ValueAccessors manages registration of an
127 /// accessor with a tree so that the tree can automatically clear the
128 /// accessor whenever one of its nodes is deleted.
129 ///
130 /// @internal A base class is needed because ValueAccessor is templated on both
131 /// a Tree type and a mutex type. The various instantiations of the template
132 /// are distinct, unrelated types, so they can't easily be stored in a
133 /// container (mainly the Tree's CacheRegistry). This base class, in contrast,
134 /// is templated only on the Tree type, so for any given Tree, only two
135 /// distinct instantiations are possible, ValueAccessorBase<Tree> and
136 /// ValueAccessorBase<const Tree>.
137 ///
138 /// @warning If IsSafe = false then the ValueAccessor will not register itself
139 /// with the tree from which it is constructed. While in some rare cases this
140 /// can lead to better performance (since it avoids the small overhead of
141 /// insertion on creation and deletion on destruction) it is also unsafe if
142 /// the tree is modified. So unless you're an expert it is highly recommended
143 /// to set IsSafe = true, which is the default in all derived ValueAccessors
144 /// defined below. However if you know that the tree is no being modifed for
145 /// the lifespan of the ValueAccessor AND the work performed per
146 /// ValueAccessor is small relative to overhead of registering it you should
147 /// consider setting IsSafe = false. If this turns out to improve performance
148 /// you should really rewrite your code so as to better amortize the
149 /// construction of the ValueAccessor, i.e. reuse it as much as possible!
150 template<typename TreeType, bool IsSafe>
152 {
153 public:
154  /// @brief Returns true if this accessor is operating on a const tree type.
156 
157  /// @brief Return true if this accessor is safe, i.e. registered by the
158  /// tree from which it is constructed. Un-registered accessors can in
159  /// rare cases be faster because it avoids the (small) overhead of
160  /// registration, but they are unsafe if the tree is modified. So unless
161  /// you're an expert it is highly recommended to set IsSafe = true (which
162  /// is the default).
163  static constexpr bool isSafe() { return IsSafe; }
164 
165  /// @brief Construct from a tree. Should rarely be invoked directly, the
166  /// drived implementation class calls this. Remains public for backwards
167  /// compatibility.
169  : mTree(&tree)
170  {
171  if (IsSafe) tree.attachAccessor(*this);
172  }
173 
174  virtual ~ValueAccessorBase() { if (IsSafe && mTree) mTree->releaseAccessor(*this); }
175 
176  /// @brief Copy constructor - if IsSafe, then the copy also registers
177  /// itself against the tree it is accessing.
179  : mTree(other.mTree)
180  {
181  if (IsSafe && mTree) mTree->attachAccessor(*this);
182  }
183 
185  {
186  if (&other != this) {
187  if (IsSafe && mTree) mTree->releaseAccessor(*this);
188  mTree = other.mTree;
189  if (IsSafe && mTree) mTree->attachAccessor(*this);
190  }
191  return *this;
192  }
193 
194  /// @brief Return a pointer to the tree associated with this accessor.
195  /// @details The pointer will be null only if the tree from which this
196  /// accessor was constructed was subsequently deleted (which generally
197  /// leaves the accessor in an unsafe state).
198  TreeType* getTree() const { return mTree; }
199 
200  /// @brief Return a reference to the tree associated with this accessor.
201  TreeType& tree() const { assert(mTree); return *mTree; }
202 
203  /// @brief Pure virtual method, clears the derived accessor
204  virtual void clear() = 0;
205 
206 protected:
207  // Allow trees to deregister themselves.
208  template<typename> friend class Tree;
209  virtual void release() { mTree = nullptr; }
210  TreeType* mTree;
211 }; // class ValueAccessorBase
212 
213 ///////////////////////////////////////////////////////////////////////////////
214 
215 /// @cond OPENVDB_DOCS_INTERNAL
216 
217 namespace value_accessor_internal
218 {
219 
220 template<typename ListT, size_t... Ts> struct NodeListBuilderImpl;
221 
222 template <typename NodeChainT>
223 struct NodeListBuilderImpl<NodeChainT>
224 {
225  using ListT = TypeList<>;
226 };
227 
228 template <typename NodeChainT, size_t Idx>
229 struct NodeListBuilderImpl<NodeChainT, Idx>
230 {
231  using NodeT = typename NodeChainT::template Get<Idx>;
232  using ListT = TypeList<NodeT>;
233 };
234 
235 template <typename NodeChainT, size_t ThisIdx, size_t NextIdx, size_t... Idxs>
236 struct NodeListBuilderImpl<NodeChainT, ThisIdx, NextIdx, Idxs...>
237 {
238  static_assert(ThisIdx < NextIdx,
239  "Invalid cache level - Cache levels must be in increasing ascending order");
240  static_assert(ThisIdx < NodeChainT::Size,
241  "Invalid cache level - Cache level is larger than the number of tree nodes");
242  static_assert(ThisIdx < NodeChainT::Back::LEVEL,
243  "Invalid cache level - Cache level is larger than the number of tree nodes");
244 
245  using NodeT = typename NodeChainT::template Get<ThisIdx>;
246  using ListT = typename TypeList<NodeT>::template Append<
247  typename NodeListBuilderImpl<NodeChainT, NextIdx, Idxs...>::ListT>;
248 };
249 
250 template<typename NodeChainT, size_t RootLevel, typename IntegerSequence>
251 struct NodeListBuilder;
252 
253 template<typename NodeChainT, size_t RootLevel, size_t... Is>
254 struct NodeListBuilder<NodeChainT, RootLevel, std::integer_sequence<size_t, Is...>>
255 {
256  using ListT = typename NodeListBuilderImpl<NodeChainT, Is..., RootLevel>::ListT;
257 };
258 
259 template<typename NodeChainT, size_t RootLevel, size_t... Is>
260 struct NodeListBuilder<NodeChainT, RootLevel, openvdb::index_sequence<Is...>>
261 {
262  using ListT = typename NodeListBuilderImpl<NodeChainT, Is..., RootLevel>::ListT;
263 };
264 
265 
266 template<typename TreeTypeT, typename NodeT>
267 struct EnableLeafBuffer
268 {
269  using LeafNodeT = typename TreeTypeT::LeafNodeType;
270  static constexpr bool value =
272  std::is_same<typename LeafNodeT::Buffer::StorageType,
273  typename LeafNodeT::ValueType>::value;
274 };
275 
276 template<typename TreeTypeT, size_t... Is>
277 struct EnableLeafBuffer<TreeTypeT, openvdb::index_sequence<Is...>>
278 {
279  // Empty integer seq, no nodes being caches
280  static constexpr bool value = false;
281 };
282 
283 template<typename TreeTypeT, size_t First, size_t... Is>
284 struct EnableLeafBuffer<TreeTypeT, openvdb::index_sequence<First, Is...>>
285 {
286 private:
287  using NodeChainT = typename TreeTypeT::RootNodeType::NodeChainType;
288  using FirstNodeT = typename NodeChainT::template Get<First>;
289 public:
291 };
292 
293 } // namespace value_accessor_internal
294 
295 /// @endcond
296 
297 ///////////////////////////////////////////////////////////////////////////////
298 
299 /// The following classes exist to perform empty base class optimizations
300 /// with the final ValueAccessor implementation. Depending on the template
301 /// types provided to the derived implementation, some member variables may not
302 /// be necessary (mutex, leaf buffer cache, etc). These classes allow for these
303 /// variables to be compiled out. Note that from C++20 we can switch to
304 /// [[no_unique_address]] member annotations instead.
305 
306 /// @brief A small class that contains a Mutex which is derived from by the
307 /// internal Value Accessor Implementation. This allows for the empty base
308 /// class optimization to be performed in the case where a Mutex/Lock is not
309 /// in use. From C++20 we can instead switch to [[no_unique_address]].
310 template <typename MutexT>
312 {
313  inline auto lock() const { return std::scoped_lock(m); }
314 private:
315  mutable MutexT m;
316 };
317 
318 /// @brief Specialization for the case where no Mutex is in use. See above.
319 template <>
321 {
322  inline constexpr auto lock() const { return 0; }
323 };
324 
325 /// @brief A small class that contains a cached pointer to a LeafNode data
326 /// buffer which is derived from by the internal Value Accessor
327 /// Implementation. This allows for the empty base class optimization to be
328 /// performed in the case where a LeafNode does not store a contiguous
329 /// index-able buffer. From C++20 we can instead switch to
330 /// [[no_unique_address]].
331 template<typename TreeTypeT, typename IntegerSequence, typename Enable = void>
333 {
334  template <typename NodeT>
335  static constexpr bool BypassLeafAPI =
337  inline const typename TreeTypeT::ValueType* buffer() { assert(mBuffer); return mBuffer; }
338  inline const typename TreeTypeT::ValueType* buffer() const { assert(mBuffer); return mBuffer; }
339  inline void setBuffer(const typename TreeTypeT::ValueType* b) const { mBuffer = b; }
340 private:
341  mutable const typename TreeTypeT::ValueType* mBuffer;
342 };
343 
344 /// @brief Specialization for the case where a Leaf Buffer cannot be cached.
345 // These methods should never be invoked. See above.
346 template<typename TreeTypeT, typename IntegerSequence>
347 struct ValueAccessorLeafBuffer<TreeTypeT, IntegerSequence,
348  typename std::enable_if<
349  !value_accessor_internal::EnableLeafBuffer<TreeTypeT, IntegerSequence>::value
350  >::type>
351 {
352  template <typename> static constexpr bool BypassLeafAPI = false;
353  inline constexpr typename TreeTypeT::ValueType* buffer() { assert(false); return nullptr; }
354  inline constexpr typename TreeTypeT::ValueType* buffer() const { assert(false); return nullptr; }
355  inline constexpr void setBuffer(const typename TreeTypeT::ValueType*) const { assert(false); }
356 };
357 
358 ///////////////////////////////////////////////////////////////////////////////
359 
360 /// @brief The Value Accessor Implementation and API methods. The majoirty of
361 /// the API matches the API of a compatible OpenVDB Tree Node.
362 template<typename _TreeType, bool IsSafe, typename MutexT, typename IntegerSequence>
363 class ValueAccessorImpl final :
364  public ValueAccessorBase<_TreeType, IsSafe>,
365  public ValueAccessorLeafBuffer<_TreeType, IntegerSequence>,
366  public ValueAccessorLock<MutexT>
367 {
368 public:
369  /// @note Not strictly the only Base Type but provided for backwards
370  /// compatibility.
374 
375  using TreeType = _TreeType;
376  using ValueType = typename TreeType::ValueType;
377  using RootNodeT = typename TreeType::RootNodeType;
378  using LeafNodeT = typename TreeType::LeafNodeType;
379  using NodeChainT = typename RootNodeT::NodeChainType;
380 
381  /// @brief A resolved, flattened TypeList of node types which this
382  /// accessor is caching. The nodes index in this list does not
383  /// necessarily correspond to the nodes level in the tree.
384  using NodeLevelList =
385  typename value_accessor_internal::NodeListBuilder
386  <NodeChainT, RootNodeT::LEVEL, IntegerSequence>::ListT;
387  using NodePtrList = typename NodeLevelList::template Transform<std::add_pointer_t>;
388 
389  /// @brief Return a node type at a particular cache level in the Value
390  /// accessor. The node type at a given cache level does not necessarily
391  /// equal the same node type in the TreeType as this depends entirely on
392  /// which tree levels this Accessor is caching. For example:
393  /// @par Example:
394  /// @code
395  /// // Cache tree levels 0 and 2
396  /// using Impl = ValueAccessorImpl<FloatTree, true, void, 0, 2>
397  /// using CacheLevel1 = Impl::template NodeTypeAtLevel<1>
398  /// using TreeLevel2 = TreeType::RootNodeType::NodeChainType::Get<2>;
399  /// static_assert(std::is_same<CacheLevel1, TreeLevel2>::value);
400  /// @endcode
401  template <size_t Level>
402  using NodeTypeAtLevel = typename NodeLevelList::template Get<Level>;
403 
404  /// @brief Given a node type, return whether this Accessor can perform
405  /// optimized value buffer accesses. This is only possible for LeafNodes
406  /// so will always return false for any non LeafNode type. It also
407  /// depends on the value type - if the value buffer is a contiguous
408  /// index-able array of values then this returns true.
409  template <typename NodeT>
410  static constexpr bool IsLeafAndBypassLeafAPI =
411  LeafCacheT::template BypassLeafAPI<NodeT>;
412 
413  /// @brief Helper alias which is true if the lowest cached node level is
414  /// a LeafNode type and has a compatible value type for optimized access.
415  static constexpr bool BypassLeafAPI =
416  IsLeafAndBypassLeafAPI<NodeTypeAtLevel<0>>;
417 
418  /// @brief The number of node levels that this accessor can cache,
419  /// excluding the RootNode.
420  static constexpr size_t NumCacheLevels = NodeLevelList::Size-1;
421  static_assert(TreeType::DEPTH >= NodeLevelList::Size-1, "cache size exceeds tree depth");
422  static_assert(NodeLevelList::Size > 0, "unexpected cache size");
423 
424  /// @brief Constructor from a tree
426  : BaseT(tree)
427  , LeafCacheT()
428  , LockT()
429  , mKeys()
430  , mNodes() {
431  this->clear();
432  }
433 
434  ~ValueAccessorImpl() override final = default;
436  ValueAccessorImpl& operator=(const ValueAccessorImpl&) = default;
437 
438  /// @brief Return @c true if any of the nodes along the path to the given
439  /// coordinate have been cached.
440  /// @param xyz The index space coordinate to query
441  bool isCached(const Coord& xyz) const
442  {
443  return this->evalFirstIndex([&](const auto Idx) -> bool
444  {
445  using NodeType = typename NodeLevelList::template Get<Idx>;
446  // @warning Putting this exp in the if statement crashes GCC9
447  constexpr bool IsRoot = std::is_same<RootNodeT, NodeType>::value;
448  if constexpr(IsRoot) return false;
449  else return (this->isHashed<NodeType>(xyz));
450  });
451  }
452 
453  /// @brief Return the value of the voxel at the given coordinates.
454  /// @param xyz The index space coordinate to query
455  const ValueType& getValue(const Coord& xyz) const
456  {
457  // Don't use evalFirstCached as we don't access the node when
458  // IsLeafAndBypassLeafAPI<NodeType> is true.
459  return *this->evalFirstIndex([&](const auto Idx) -> const ValueType*
460  {
461  using NodeType = typename NodeLevelList::template Get<Idx>;
462  // If not cached return a nullptr. Note that this operator is
463  // guaranteed to return a value as the last node in the chain
464  // is a RootNode and isHashed always returns true for this case
465  if (!this->isHashed<NodeType>(xyz)) return nullptr;
466 
467  if constexpr(IsLeafAndBypassLeafAPI<NodeType>) {
468  return &(LeafCacheT::buffer()[LeafNodeT::coordToOffset(xyz)]);
469  }
470  else {
471  auto node = mNodes.template get<Idx>();
472  assert(node);
473  return &(node->getValueAndCache(xyz, *this));
474  }
475  });
476  }
477 
478  /// @brief Return the active state of the voxel at the given coordinates.
479  /// @param xyz The index space coordinate to query
480  bool isValueOn(const Coord& xyz) const
481  {
482  return this->evalFirstCached(xyz, [&](const auto node) -> bool {
483  assert(node);
484  return node->isValueOnAndCache(xyz, *this);
485  });
486  }
487 
488  /// @brief Return the active state of the value at a given coordinate as
489  /// well as its value.
490  /// @param xyz The index space coordinate to query
491  /// @param value The value to get
492  bool probeValue(const Coord& xyz, ValueType& value) const
493  {
494  return this->evalFirstCached(xyz, [&](const auto node) -> bool
495  {
496  using NodeType = std::remove_pointer_t<decltype(node)>;
497  assert(node);
498 
499  if constexpr(IsLeafAndBypassLeafAPI<NodeType>) {
500  const auto offset = LeafNodeT::coordToOffset(xyz);
501  value = LeafCacheT::buffer()[offset];
502  return node->isValueOn(offset);
503  }
504  else {
505  return node->probeValueAndCache(xyz, value, *this);
506  }
507  });
508  }
509 
510  /// @brief Return the tree depth (0 = root) at which the value of voxel
511  /// (x, y, z) resides, or -1 if (x, y, z) isn't explicitly represented in
512  /// the tree (i.e., if it is implicitly a background voxel).
513  /// @note This is the inverse of the node LEVEL (where the RootNode level
514  /// is the highest in the tree).
515  /// @param xyz The index space coordinate to query
516  int getValueDepth(const Coord& xyz) const
517  {
518  return this->evalFirstCached(xyz, [&](const auto node) -> int
519  {
520  using NodeType = std::remove_pointer_t<decltype(node)>;
521  assert(node);
522 
524  return node->getValueDepthAndCache(xyz, *this);
525  }
526  else {
527  return int(RootNodeT::LEVEL - node->getValueLevelAndCache(xyz, *this));
528  }
529  });
530  }
531 
532  /// @brief Return @c true if the value of voxel (x, y, z) resides at the
533  /// leaf level of the tree, i.e., if it is not a tile value.
534  /// @param xyz The index space coordinate to query
535  bool isVoxel(const Coord& xyz) const
536  {
537  assert(BaseT::mTree);
538  return this->getValueDepth(xyz) ==
539  static_cast<int>(RootNodeT::LEVEL);
540  }
541 
542  //@{
543  /// @brief Set a particular value at the given coordinate and mark the
544  /// coordinate as active
545  /// @note This method will densify branches of the tree if the coordinate
546  /// points to a tile and if the provided value or active state is
547  /// different to the tiles
548  /// @param xyz The index space coordinate to set
549  /// @param value The value to set
550  void setValue(const Coord& xyz, const ValueType& value)
551  {
552  static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
553  this->evalFirstCached(xyz, [&](const auto node) -> void
554  {
555  using NodeType = std::remove_pointer_t<decltype(node)>;
556  assert(node);
557 
558  if constexpr(IsLeafAndBypassLeafAPI<NodeType>) {
559  const auto offset = LeafNodeT::coordToOffset(xyz);
560  const_cast<ValueType&>(LeafCacheT::buffer()[offset]) = value;
561  const_cast<NodeType*>(node)->setValueOn(offset);
562  }
563  else {
564  const_cast<NodeType*>(node)->setValueAndCache(xyz, value, *this);
565  }
566  });
567  }
568 
569  void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); }
570  //@}
571 
572  /// @brief Set a particular value at the given coordinate but preserve its
573  /// active state
574  /// @note This method will densify branches of the tree if the coordinate
575  /// points to a tile and if the provided value is different to the tiles
576  /// @param xyz The index space coordinate to set
577  /// @param value The value to set
578  void setValueOnly(const Coord& xyz, const ValueType& value)
579  {
580  static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
581  // Don't use evalFirstCached as we don't access the node when
582  // IsLeafAndBypassLeafAPI<NodeType> is true.
583  this->evalFirstIndex([&](const auto Idx) -> bool
584  {
585  using NodeType = typename NodeLevelList::template Get<Idx>;
586  if (!this->isHashed<NodeType>(xyz)) return false;
587 
588  if constexpr(IsLeafAndBypassLeafAPI<NodeType>) {
589  const_cast<ValueType&>(LeafCacheT::buffer()[LeafNodeT::coordToOffset(xyz)]) = value;
590  }
591  else {
592  auto node = mNodes.template get<Idx>();
593  assert(node);
594  const_cast<NodeType*>(node)->setValueOnlyAndCache(xyz, value, *this);
595  }
596  return true;
597  });
598  }
599 
600  /// @brief Set a particular value at the given coordinate and mark the
601  /// coordinate as inactive.
602  /// @note This method will densify branches of the tree if the coordinate
603  /// points to a tile and if the provided value or active state is
604  /// different to the tiles
605  /// @param xyz The index space coordinate to set
606  /// @param value The value to set
607  void setValueOff(const Coord& xyz, const ValueType& value)
608  {
609  static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
610  this->evalFirstCached(xyz, [&](const auto node) -> void
611  {
612  using NodeType = std::remove_pointer_t<decltype(node)>;
613  assert(node);
614 
615  if constexpr(IsLeafAndBypassLeafAPI<NodeType>) {
616  const auto offset = LeafNodeT::coordToOffset(xyz);
617  const_cast<ValueType&>(LeafCacheT::buffer()[offset]) = value;
618  const_cast<NodeType*>(node)->setValueOff(offset);
619  }
620  else {
621  const_cast<NodeType*>(node)->setValueOffAndCache(xyz, value, *this);
622  }
623  });
624  }
625 
626  /// @brief Apply a functor to the value at the given coordinate and mark
627  /// mark the coordinate as active
628  /// @details See Tree::modifyValue() for details.
629  /// @param xyz The index space coordinate to modify
630  /// @param op The modify operation
631  template<typename ModifyOp>
632  void modifyValue(const Coord& xyz, const ModifyOp& op)
633  {
634  static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
635  this->evalFirstCached(xyz, [&](const auto node) -> void
636  {
637  using NodeType = std::remove_pointer_t<decltype(node)>;
638  assert(node);
639 
640  if constexpr(IsLeafAndBypassLeafAPI<NodeType>) {
641  const auto offset = LeafNodeT::coordToOffset(xyz);
642  op(const_cast<ValueType&>(LeafCacheT::buffer()[offset]));
643  const_cast<NodeType*>(node)->setActiveState(offset, true);
644  }
645  else {
646  const_cast<NodeType*>(node)->modifyValueAndCache(xyz, op, *this);
647  }
648  });
649  }
650 
651  /// @brief Apply a functor to the voxel at the given coordinates.
652  /// @details See Tree::modifyValueAndActiveState() for details.
653  /// @param xyz The index space coordinate to modify
654  /// @param op The modify operation
655  template<typename ModifyOp>
656  void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op)
657  {
658  static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
659  this->evalFirstCached(xyz, [&](const auto node) -> void
660  {
661  using NodeType = std::remove_pointer_t<decltype(node)>;
662  assert(node);
663 
664  if constexpr(IsLeafAndBypassLeafAPI<NodeType>) {
665  const auto offset = LeafNodeT::coordToOffset(xyz);
666  bool state = node->isValueOn(offset);
667  op(const_cast<ValueType&>(LeafCacheT::buffer()[offset]), state);
668  const_cast<NodeType*>(node)->setActiveState(offset, state);
669  }
670  else {
671  const_cast<NodeType*>(node)->modifyValueAndActiveStateAndCache(xyz, op, *this);
672  }
673  });
674  }
675 
676  /// @brief Set the active state of the voxel at the given coordinates
677  /// without changing its value.
678  /// @note This method will densify branches of the tree if the coordinate
679  /// points to a tile and if the provided activate state flag is different
680  /// to the tiles
681  /// @param xyz The index space coordinate to modify
682  /// @param on Whether to set the active state to on (true) or off (false)
683  void setActiveState(const Coord& xyz, bool on = true)
684  {
685  static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
686  this->evalFirstCached(xyz, [&](const auto node) -> void
687  {
688  using NodeType = std::remove_pointer_t<decltype(node)>;
689  assert(node);
690  const_cast<NodeType*>(node)->setActiveStateAndCache(xyz, on, *this);
691  });
692  }
693  /// @brief Mark the voxel at the given coordinates as active without
694  /// changing its value.
695  /// @note This method will densify branches of the tree if the coordinate
696  /// points to a tile and if the tiles active state is off.
697  /// @param xyz The index space coordinate to modify
698  void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); }
699  /// @brief Mark the voxel at the given coordinates as inactive without
700  /// changing its value.
701  /// @note This method will densify branches of the tree if the coordinate
702  /// points to a tile and if the tiles active state is on.
703  /// @param xyz The index space coordinate to modify
704  void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); }
705 
706  /// @brief Returns the leaf node that contains voxel (x, y, z) and if it
707  /// doesn't exist, create it, but preserve the values and active states
708  /// of the pre-existing branch.
709  /// @note You can use this method to preallocate a static tree topology
710  /// over which to safely perform multithreaded processing.
711  /// @param xyz The index space coordinate at which to create a LeafNode.
712  /// Note that if this coordinate is not a LeafNode origin then the
713  /// LeafNode that would otherwise contain this coordinate is created and
714  /// returned.
715  LeafNodeT* touchLeaf(const Coord& xyz)
716  {
717  static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree");
718  return this->evalFirstCached(xyz, [&](const auto node) -> LeafNodeT*
719  {
720  using NodeType = std::remove_pointer_t<decltype(node)>;
721  assert(node);
722  return const_cast<NodeType*>(node)->touchLeafAndCache(xyz, *this);
723  });
724  }
725 
726  /// @brief Add the specified leaf to this tree, possibly creating a child
727  /// branch in the process. If the leaf node already exists, replace it.
728  /// @param leaf The LeafNode to insert into the tree. Must not be a nullptr.
729  void addLeaf(LeafNodeT* leaf)
730  {
731  constexpr int64_t Start = NodeLevelList::template Index<LeafNodeT> + 1;
732  static_assert(!BaseT::IsConstTree, "can't add a node to a const tree");
733  static_assert(Start >= 0);
734  assert(leaf);
735  this->evalFirstCached<Start>(leaf->origin(), [&](const auto node) -> void
736  {
737  using NodeType = std::remove_pointer_t<decltype(node)>;
738  assert(node);
739  const_cast<NodeType*>(node)->addLeafAndCache(leaf, *this);
740  });
741  }
742 
743  /// @brief Add a tile at the specified tree level that contains the
744  /// coordinate xyz, possibly deleting existing nodes or creating new
745  /// nodes in the process.
746  /// @note Calling this with a level of 0 will modify voxel values. This
747  /// function will always densify a tree branch up to the requested level
748  /// (regardless if the value and active state match).
749  /// @param level The level of the tree to add a tile. Level 0 refers to
750  /// voxels (and is similar to ::setValue, except will always density).
751  /// @param xyz The index space coordinate to add a tile
752  /// @param value The value of the tile
753  /// @param state The active state to set on the new tile
754  void addTile(Index level, const Coord& xyz, const ValueType& value, bool state)
755  {
756  constexpr int64_t Start = NodeLevelList::template Index<LeafNodeT> + 1;
757  static_assert(!BaseT::IsConstTree, "can't add a tile to a const tree");
758  static_assert(Start >= 0);
759  this->evalFirstCached<Start>(xyz, [&](const auto node) -> void
760  {
761  using NodeType = std::remove_pointer_t<decltype(node)>;
762  assert(node);
763  const_cast<NodeType*>(node)->addTileAndCache(level, xyz, value, state, *this);
764  });
765  }
766 
767  ///@{
768  /// @brief Return a pointer to the node of the specified type that contains
769  /// the value located at xyz. If no node of the given NodeT exists which
770  /// contains the value, a nullptr is returned.
771  /// @brief This function may return a nullptr even if the coordinate xyz is
772  /// represented in tree, as it depends on the type NodeT provided. For
773  /// example, the value may exist as a tile in an InternalNode but note as
774  /// a LeafNode.
775  /// @param xyz The index space coordinate to query
776  template<typename NodeT>
777  NodeT* probeNode(const Coord& xyz)
778  {
779  static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree");
780  return this->evalFirstPred([&](const auto Idx) -> bool
781  {
782  using NodeType = typename NodeLevelList::template Get<Idx>;
783  // @warning Putting this exp in the if statement crashes GCC9
784  constexpr bool NodeMayBeCached =
785  std::is_same<NodeT, NodeType>::value || NodeT::LEVEL < NodeType::LEVEL;
786 
787  if constexpr(NodeMayBeCached) return this->isHashed<NodeType>(xyz);
788  else return false;
789  },
790  [&](const auto node) -> NodeT*
791  {
792  using NodeType = std::remove_pointer_t<decltype(node)>;
793  assert(node);
795  return const_cast<NodeT*>(node);
796  }
797  else {
798  assert(NodeT::LEVEL < NodeType::LEVEL);
799  return const_cast<NodeType*>(node)->template probeNodeAndCache<NodeT>(xyz, *this);
800  }
801  });
802  }
803 
804  template<typename NodeT>
805  const NodeT* probeConstNode(const Coord& xyz) const
806  {
807  return this->evalFirstPred([&](const auto Idx) -> bool
808  {
809  using NodeType = typename NodeLevelList::template Get<Idx>;
810  // @warning Putting this exp in the if statement crashes GCC9
811  constexpr bool NodeMayBeCached =
812  std::is_same<NodeT, NodeType>::value || NodeT::LEVEL < NodeType::LEVEL;
813 
814  if constexpr(NodeMayBeCached) return this->isHashed<NodeType>(xyz);
815  else return false;
816  },
817  [&](const auto node) -> const NodeT*
818  {
819  using NodeType = std::remove_pointer_t<decltype(node)>;
820  assert(node);
822  return node;
823  }
824  else {
825  assert(NodeT::LEVEL < NodeType::LEVEL);
826  return const_cast<NodeType*>(node)->template probeConstNodeAndCache<NodeT>(xyz, *this);
827  }
828  });
829  }
830  /// @}
831 
832  ///@{
833  /// @brief Return a pointer to the leaf node that contains the voxel
834  /// coordinate xyz. If no LeafNode exists, returns a nullptr.
835  /// @param xyz The index space coordinate to query
836  LeafNodeT* probeLeaf(const Coord& xyz) { return this->template probeNode<LeafNodeT>(xyz); }
837  const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); }
838  const LeafNodeT* probeConstLeaf(const Coord& xyz) const
839  {
840  return this->template probeConstNode<LeafNodeT>(xyz);
841  }
842  /// @}
843 
844  /// @brief Return the node of type @a NodeT that has been cached on this
845  /// accessor. If this accessor does not cache this NodeT, or if no
846  /// node of this type has been cached, returns a nullptr.
847  template<typename NodeT>
848  NodeT* getNode()
849  {
850  using NodeType = typename std::decay<NodeT>::type;
851  static constexpr int64_t Idx = NodeLevelList::template Index<NodeType>;
852  if constexpr (Idx >= 0) return mNodes.template get<Idx>();
853  else return nullptr;
854  }
855 
856  /// @brief Explicitly insert a node of the type @a NodeT into this Value
857  /// Accessors cache.
858  /// @todo deprecate?
859  template<typename NodeT>
860  void insertNode(const Coord& xyz, NodeT& node)
861  {
862  this->insert(xyz, &node);
863  }
864 
865  /// @brief Explicitly remove this Value Accessors cached node of the given
866  /// NodeT. If this Value Accessor does not support the caching of the
867  /// provided NodeT, this method does nothing.
868  template<typename NodeT>
869  void eraseNode()
870  {
871  static constexpr int64_t Idx = NodeLevelList::template Index<NodeT>;
872  if constexpr (Idx >= 0) {
873  mKeys[Idx] = Coord::max();
874  mNodes.template get<Idx>() = nullptr;
875  }
876  }
877 
878  /// @brief Remove all the cached nodes and invalidate the corresponding
879  /// hash-keys.
880  void clear() override final
881  {
882  mKeys.fill(Coord::max());
883  mNodes.foreach([](auto& node) { node = nullptr; });
884  if constexpr (BypassLeafAPI) {
885  LeafCacheT::setBuffer(nullptr);
886  }
887  if (BaseT::mTree) {
888  static constexpr int64_t Idx = NodeLevelList::template Index<RootNodeT>;
889  mNodes.template get<Idx>() = const_cast<RootNodeT*>(&(BaseT::mTree->root()));
890  }
891  }
892 
893 public:
894  // Backwards compatible support. Use NodeTypeAtLevel<> instead
895  using NodeT0 OPENVDB_DEPRECATED_MESSAGE("Use NodeTypeAtLevel<0>") =
896  typename std::conditional<(NumCacheLevels > 0), NodeTypeAtLevel<0>, void>::type;
897  using NodeT1 OPENVDB_DEPRECATED_MESSAGE("Use NodeTypeAtLevel<1>") =
898  typename std::conditional<(NumCacheLevels > 1), NodeTypeAtLevel<1>, void>::type;
899  using NodeT2 OPENVDB_DEPRECATED_MESSAGE("Use NodeTypeAtLevel<2>") =
900  typename std::conditional<(NumCacheLevels > 2), NodeTypeAtLevel<2>, void>::type;
901  /// @brief Return the number of cache levels employed by this ValueAccessor
902  OPENVDB_DEPRECATED_MESSAGE("Use the static NumCacheLevels constant")
903  static constexpr Index numCacheLevels() { return NumCacheLevels; }
904 
905 protected:
906  // Allow nodes to insert themselves into the cache.
907  template<typename> friend class RootNode;
908  template<typename, Index> friend class InternalNode;
909  template<typename, Index> friend class LeafNode;
910  // Allow trees to deregister themselves.
911  template<typename> friend class Tree;
912 
913  /// @brief Release this accessor from the tree, set the tree to null and
914  /// clear the accessor cache. After calling this method the accessor
915  /// will be completely invalid.
916  void release() override final
917  {
918  this->BaseT::release();
919  this->clear();
920  }
921 
922  /// ******************************* WARNING *******************************
923  /// Methods here must be force inline otherwise compilers do not optimize
924  /// out the function call due to recursive templates and performance
925  /// degradation is significant.
926  /// ***********************************************************************
927 
928  /// @brief Insert a node into this ValueAccessor's cache
929  template<typename NodeT>
931  [[maybe_unused]] const Coord& xyz,
932  [[maybe_unused]] const NodeT* node) const
933  {
934  // Early exit if NodeT isn't part of this ValueAccessors cache
935  if constexpr(!NodeLevelList::template Contains<NodeT>) return;
936  else {
937  constexpr uint64_t Idx = uint64_t(NodeLevelList::template Index<NodeT>);
938  static_assert(NodeLevelList::template Contains<NodeT>);
939  static_assert(Idx < NumCacheLevels);
940  mKeys[Idx] = xyz & ~(NodeT::DIM-1);
941  mNodes.template get<Idx>() = const_cast<NodeT*>(node);
942  if constexpr(IsLeafAndBypassLeafAPI<NodeT>) {
943  LeafCacheT::setBuffer(node->buffer().data());
944  }
945  }
946  }
947 
948  template<typename NodeT>
949  OPENVDB_FORCE_INLINE bool isHashed([[maybe_unused]] const Coord& xyz) const
950  {
951  if constexpr(!NodeLevelList::template Contains<NodeT>) return false;
953  return true;
954  }
955  else {
956  constexpr uint64_t Idx = uint64_t(NodeLevelList::template Index<NodeT>);
957  static_assert(NodeLevelList::template Contains<NodeT>);
958  static_assert(Idx < NumCacheLevels + 1);
959  return (xyz[0] & ~Coord::ValueType(NodeT::DIM-1)) == mKeys[Idx][0]
960  && (xyz[1] & ~Coord::ValueType(NodeT::DIM-1)) == mKeys[Idx][1]
961  && (xyz[2] & ~Coord::ValueType(NodeT::DIM-1)) == mKeys[Idx][2];
962  }
963  }
964 
965 private:
966  /// @brief Evaluate a function on each node until its returns value is not
967  /// null or false.
968  /// @param op The function to run
969  template <typename OpT>
970  OPENVDB_FORCE_INLINE auto evalFirstIndex(OpT&& op) const
971  {
972  assert(BaseT::mTree);
973  // Mutex lock the accessor. Does nothing if no mutex if in place
974  [[maybe_unused]] const auto lock = this->lock();
975  // Get the return type of the provided operation OpT
976  using IndexT = std::integral_constant<std::size_t, 0>;
977  using RetT = typename std::invoke_result<OpT, IndexT>::type;
978  return openvdb::evalFirstIndex<0, NumCacheLevels+1>(op, RetT(NULL));
979  }
980 
981  /// @brief Evaluate a predicate on each index I from [0,Size] until it
982  /// returns true, then executes the provided op function on the resolved
983  /// node type. Helps in cases where std::get may be unecessarily invoked.
984  /// @param pred The predicate to run on the node index
985  /// @param op The function to run on the node where the pred returns true
986  template <typename PredT, typename OpT>
987  OPENVDB_FORCE_INLINE auto evalFirstPred(PredT&& pred, OpT&& op) const
988  {
989  assert(BaseT::mTree);
990  // Mutex lock the accessor. Does nothing if no mutex if in place
991  [[maybe_unused]] const auto lock = this->lock();
992  using RetT = typename std::invoke_result<OpT, RootNodeT*>::type;
993  if constexpr(!std::is_same<RetT, void>::value) {
994  return mNodes.evalFirstPred(pred, op, RetT(false));
995  }
996  else {
997  return mNodes.evalFirstPred(pred, op);
998  }
999  }
1000 
1001  /// @brief Helper alias to call this->evalFirstPred(), but with a default
1002  /// predicate set to return true when the node at the given index is
1003  /// cached
1004  /// @param xyz The coord to hash
1005  /// @param op The function to run on the node where the pred returns true
1006  template <size_t Start = 0, typename OpT = void>
1007  OPENVDB_FORCE_INLINE auto evalFirstCached([[maybe_unused]] const Coord& xyz, OpT&& op) const
1008  {
1009  return this->evalFirstPred([&](const auto Idx) -> bool
1010  {
1011  if constexpr(Idx < Start) return false;
1012  if constexpr(Idx > NumCacheLevels+1) return false;
1013  using NodeType = typename NodeLevelList::template Get<Idx>;
1014  return this->isHashed<NodeType>(xyz);
1015  }, op);
1016  }
1017 
1018 private:
1019  mutable std::array<Coord, NumCacheLevels> mKeys;
1020  mutable typename NodePtrList::AsTupleList mNodes;
1021 }; // ValueAccessorImpl
1022 
1023 } // namespace tree
1024 } // namespace OPENVDB_VERSION_NAME
1025 } // namespace openvdb
1026 
1027 #endif // OPENVDB_TREE_VALUEACCESSOR_HAS_BEEN_INCLUDED
typedef int(APIENTRYP RE_PFNGLXSWAPINTERVALSGIPROC)(int)
void setActiveState(const Coord &xyz, bool on=true)
Set the active state of the voxel at the given coordinates without changing its value.
#define OPENVDB_FORCE_INLINE
Definition: Platform.h:109
bool isValueOn(const Coord &xyz) const
Return the active state of the voxel at the given coordinates.
void release() overridefinal
Release this accessor from the tree, set the tree to null and clear the accessor cache. After calling this method the accessor will be completely invalid.
void modifyValue(const Coord &xyz, const ModifyOp &op)
Apply a functor to the value at the given coordinate and mark mark the coordinate as active...
const NodeT * probeConstNode(const Coord &xyz) const
Return a pointer to the node of the specified type that contains the value located at xyz...
void
Definition: png.h:1083
virtual void clear()=0
Pure virtual method, clears the derived accessor.
GLsizei const GLfloat * value
Definition: glcorearb.h:824
void setValueOff(const Coord &xyz)
Mark the voxel at the given coordinates as inactive without changing its value.
GLint level
Definition: glcorearb.h:108
ValueAccessorBase & operator=(const ValueAccessorBase &other)
typename NodeLevelList::template Get< Level > NodeTypeAtLevel
Return a node type at a particular cache level in the Value accessor. The node type at a given cache ...
#define OPENVDB_DEPRECATED_MESSAGE(msg)
Definition: Platform.h:154
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:239
**But if you need a or simply need to know when the task has note that the like this
Definition: thread.h:617
The Value Accessor Implementation and API methods. The majoirty of the API matches the API of a compa...
Definition: ValueAccessor.h:68
ValueAccessorBase(const ValueAccessorBase &other)
Copy constructor - if IsSafe, then the copy also registers itself against the tree it is accessing...
A small class that contains a Mutex which is derived from by the internal Value Accessor Implementati...
Specialization for the case where no Mutex is in use. See above.
void setValue(const Coord &xyz, const ValueType &value)
Set a particular value at the given coordinate and mark the coordinate as active. ...
std::decay_t< decltype(make_index_sequence_impl< N >())> make_index_sequence
Definition: Types.h:234
ValueAccessorBase(TreeType &tree)
Construct from a tree. Should rarely be invoked directly, the drived implementation class calls this...
OPENVDB_FORCE_INLINE void insert([[maybe_unused]] const Coord &xyz, [[maybe_unused]] const NodeT *node) const
Insert a node into this ValueAccessor's cache.
OPENVDB_FORCE_INLINE bool isHashed([[maybe_unused]] const Coord &xyz) const
typename value_accessor_internal::NodeListBuilder< NodeChainT, RootNodeT::LEVEL, IntegerSequence >::ListT NodeLevelList
A resolved, flattened TypeList of node types which this accessor is caching. The nodes index in this ...
bool probeValue(const Coord &xyz, ValueType &value) const
Return the active state of the value at a given coordinate as well as its value.
GLintptr offset
Definition: glcorearb.h:665
void modifyValueAndActiveState(const Coord &xyz, const ModifyOp &op)
Apply a functor to the voxel at the given coordinates.
int getValueDepth(const Coord &xyz) const
Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides, or -1 if (x...
void setValueOn(const Coord &xyz, const ValueType &value)
Set a particular value at the given coordinate and mark the coordinate as active. ...
static constexpr bool isSafe()
Return true if this accessor is safe, i.e. registered by the tree from which it is constructed...
typename NodeLevelList::template Transform< std::add_pointer_t > NodePtrList
ValueAccessorImpl(TreeType &tree)
Constructor from a tree.
NodeT * getNode()
Return the node of type NodeT that has been cached on this accessor. If this accessor does not cache ...
bool isVoxel(const Coord &xyz) const
Return true if the value of voxel (x, y, z) resides at the leaf level of the tree, i.e., if it is not a tile value.
A small class that contains a cached pointer to a LeafNode data buffer which is derived from by the i...
const LeafNodeT * probeConstLeaf(const Coord &xyz) const
Return a pointer to the leaf node that contains the voxel coordinate xyz. If no LeafNode exists...
integer_sequence< std::size_t, Ints...> index_sequence
Definition: invoke.hpp:52
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1222
const LeafNodeT * probeLeaf(const Coord &xyz) const
Return a pointer to the leaf node that contains the voxel coordinate xyz. If no LeafNode exists...
static constexpr bool IsConstTree
Returns true if this accessor is operating on a const tree type.
This base class for ValueAccessors manages registration of an accessor with a tree so that the tree c...
TreeType * getTree() const
Return a pointer to the tree associated with this accessor.
void setValueOn(const Coord &xyz)
Mark the voxel at the given coordinates as active without changing its value.
void setBuffer(const typename TreeTypeT::ValueType *b) const
LeafNodeT * touchLeaf(const Coord &xyz)
Returns the leaf node that contains voxel (x, y, z) and if it doesn't exist, create it...
Library and file format version numbers.
static constexpr Index numCacheLevels()
Return the number of cache levels employed by this ValueAccessor.
static constexpr bool IsLeafAndBypassLeafAPI
Given a node type, return whether this Accessor can perform optimized value buffer accesses...
LeafNodeT * probeLeaf(const Coord &xyz)
Return a pointer to the leaf node that contains the voxel coordinate xyz. If no LeafNode exists...
ImageBuf OIIO_API max(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
if(num_boxed_items<=0)
Definition: UT_RTreeImpl.h:697
void insertNode(const Coord &xyz, NodeT &node)
Explicitly insert a node of the type NodeT into this Value Accessors cache.
Definition: core.h:1131
void eraseNode()
Explicitly remove this Value Accessors cached node of the given NodeT. If this Value Accessor does no...
#define const
Definition: zconf.h:214
static constexpr size_t NumCacheLevels
The number of node levels that this accessor can cache, excluding the RootNode.
NodeT * probeNode(const Coord &xyz)
Return a pointer to the node of the specified type that contains the value located at xyz...
void setValueOff(const Coord &xyz, const ValueType &value)
Set a particular value at the given coordinate and mark the coordinate as inactive.
static constexpr bool BypassLeafAPI
Helper alias which is true if the lowest cached node level is a LeafNode type and has a compatible va...
type
Definition: core.h:1059
const ValueType & getValue(const Coord &xyz) const
Return the value of the voxel at the given coordinates.
bool isCached(const Coord &xyz) const
Return true if any of the nodes along the path to the given coordinate have been cached.
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:119
A list of types (not necessarily unique)
Definition: TypeList.h:577
void setValueOnly(const Coord &xyz, const ValueType &value)
Set a particular value at the given coordinate but preserve its active state.
void addLeaf(LeafNodeT *leaf)
Add the specified leaf to this tree, possibly creating a child branch in the process. If the leaf node already exists, replace it.
void addTile(Index level, const Coord &xyz, const ValueType &value, bool state)
Add a tile at the specified tree level that contains the coordinate xyz, possibly deleting existing n...
TreeType & tree() const
Return a reference to the tree associated with this accessor.
void clear() overridefinal
Remove all the cached nodes and invalidate the corresponding hash-keys.