OpenVDB  11.0.0
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>>
68 class ValueAccessorImpl;
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>
87  ValueAccessorImpl<TreeType, IsSafe, MutexType,
89 
90 /// @brief Helper alias for a ValueAccessor which doesn't cache any Internal
91 /// or Leaf nodes.
92 template <typename TreeType, bool IsSafe>
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>
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>
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>
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>
122  ValueAccessorImpl<TreeType, IsSafe, tbb::spin_mutex,
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.
155  static constexpr bool IsConstTree = std::is_const<TreeType>::value;
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.
168  ValueAccessorBase(TreeType& tree)
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 =
271  std::is_same<NodeT, LeafNodeT>::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:
290  static constexpr bool value = EnableLeafBuffer<TreeTypeT, FirstNodeT>::value;
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 <>
320 struct ValueAccessorLock<void>
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 =
336  std::is_same<NodeT, typename TreeTypeT::LeafNodeType>::value;
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.
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 
523  if constexpr(std::is_same<RootNodeT, NodeType>::value) {
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);
794  if constexpr(std::is_same<NodeT, NodeType>::value) {
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);
821  if constexpr(std::is_same<NodeT, NodeType>::value) {
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;
952  if constexpr(std::is_same<NodeT, RootNodeT>::value) {
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
#define OPENVDB_FORCE_INLINE
Definition: Platform.h:103
#define OPENVDB_DEPRECATED_MESSAGE(msg)
Definition: Platform.h:148
Signed (x, y, z) 32-bit integer coordinates.
Definition: Coord.h:25
Int32 ValueType
Definition: Coord.h:32
static Coord max()
Return the largest possible coordinate.
Definition: Coord.h:46
Definition: InternalNode.h:34
Templated block class to hold specific data types and a fixed number of values determined by Log2Dim....
Definition: LeafNode.h:38
Definition: RootNode.h:39
Definition: Tree.h:178
This base class for ValueAccessors manages registration of an accessor with a tree so that the tree c...
Definition: ValueAccessor.h:152
static constexpr bool isSafe()
Return true if this accessor is safe, i.e. registered by the tree from which it is constructed....
Definition: ValueAccessor.h:163
ValueAccessorBase(const ValueAccessorBase &other)
Copy constructor - if IsSafe, then the copy also registers itself against the tree it is accessing.
Definition: ValueAccessor.h:178
TreeType * mTree
Definition: ValueAccessor.h:210
virtual ~ValueAccessorBase()
Definition: ValueAccessor.h:174
virtual void clear()=0
Pure virtual method, clears the derived accessor.
ValueAccessorBase & operator=(const ValueAccessorBase &other)
Definition: ValueAccessor.h:184
ValueAccessorBase(TreeType &tree)
Construct from a tree. Should rarely be invoked directly, the drived implementation class calls this....
Definition: ValueAccessor.h:168
TreeType & tree() const
Return a reference to the tree associated with this accessor.
Definition: ValueAccessor.h:201
TreeType * getTree() const
Return a pointer to the tree associated with this accessor.
Definition: ValueAccessor.h:198
virtual void release()
Definition: ValueAccessor.h:209
The Value Accessor Implementation and API methods. The majoirty of the API matches the API of a compa...
Definition: ValueAccessor.h:367
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,...
Definition: ValueAccessor.h:516
LeafNodeT * probeLeaf(const Coord &xyz)
Return a pointer to the leaf node that contains the voxel coordinate xyz. If no LeafNode exists,...
Definition: ValueAccessor.h:836
typename RootNodeT::NodeChainType NodeChainT
Definition: ValueAccessor.h:379
LeafNodeT * touchLeaf(const Coord &xyz)
Returns the leaf node that contains voxel (x, y, z) and if it doesn't exist, create it,...
Definition: ValueAccessor.h:715
OPENVDB_FORCE_INLINE void insert([[maybe_unused]] const Coord &xyz, [[maybe_unused]] const NodeT *node) const
Insert a node into this ValueAccessor's cache.
Definition: ValueAccessor.h:930
void clear() override final
Remove all the cached nodes and invalidate the corresponding hash-keys.
Definition: ValueAccessor.h:880
bool isValueOn(const Coord &xyz) const
Return the active state of the voxel at the given coordinates.
Definition: ValueAccessor.h:480
void setActiveState(const Coord &xyz, bool on=true)
Set the active state of the voxel at the given coordinates without changing its value.
Definition: ValueAccessor.h:683
bool isVoxel(const Coord &xyz) const
Return true if the value of voxel (x, y, z) resides at the leaf level of the tree,...
Definition: ValueAccessor.h:535
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 ...
Definition: ValueAccessor.h:386
typename TreeType::ValueType ValueType
Definition: ValueAccessor.h:376
OPENVDB_FORCE_INLINE bool isHashed([[maybe_unused]] const Coord &xyz) const
Definition: ValueAccessor.h:949
void addLeaf(LeafNodeT *leaf)
Add the specified leaf to this tree, possibly creating a child branch in the process....
Definition: ValueAccessor.h:729
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 ...
Definition: ValueAccessor.h:402
NodeT * getNode()
Return the node of type NodeT that has been cached on this accessor. If this accessor does not cache ...
Definition: ValueAccessor.h:848
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...
Definition: ValueAccessor.h:754
void modifyValueAndActiveState(const Coord &xyz, const ModifyOp &op)
Apply a functor to the voxel at the given coordinates.
Definition: ValueAccessor.h:656
void setValueOnly(const Coord &xyz, const ValueType &value)
Set a particular value at the given coordinate but preserve its active state.
Definition: ValueAccessor.h:578
typename TreeType::RootNodeType RootNodeT
Definition: ValueAccessor.h:377
void setValueOff(const Coord &xyz, const ValueType &value)
Set a particular value at the given coordinate and mark the coordinate as inactive.
Definition: ValueAccessor.h:607
bool probeValue(const Coord &xyz, ValueType &value) const
Return the active state of the value at a given coordinate as well as its value.
Definition: ValueAccessor.h:492
typename TreeType::LeafNodeType LeafNodeT
Definition: ValueAccessor.h:378
void setValue(const Coord &xyz, const ValueType &value)
Set a particular value at the given coordinate and mark the coordinate as active.
Definition: ValueAccessor.h:550
typename NodeLevelList::template Transform< std::add_pointer_t > NodePtrList
Definition: ValueAccessor.h:387
void eraseNode()
Explicitly remove this Value Accessors cached node of the given NodeT. If this Value Accessor does no...
Definition: ValueAccessor.h:869
void setValueOff(const Coord &xyz)
Mark the voxel at the given coordinates as inactive without changing its value.
Definition: ValueAccessor.h:704
typename std::conditional<(NumCacheLevels > 1), NodeTypeAtLevel< 1 >, void >::type NodeT1
Definition: ValueAccessor.h:898
const NodeT * probeConstNode(const Coord &xyz) const
Return a pointer to the node of the specified type that contains the value located at xyz....
Definition: ValueAccessor.h:805
const LeafNodeT * probeConstLeaf(const Coord &xyz) const
Return a pointer to the leaf node that contains the voxel coordinate xyz. If no LeafNode exists,...
Definition: ValueAccessor.h:838
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.
Definition: ValueAccessor.h:632
NodeT * probeNode(const Coord &xyz)
Return a pointer to the node of the specified type that contains the value located at xyz....
Definition: ValueAccessor.h:777
ValueAccessorImpl(TreeType &tree)
Constructor from a tree.
Definition: ValueAccessor.h:425
const LeafNodeT * probeLeaf(const Coord &xyz) const
Return a pointer to the leaf node that contains the voxel coordinate xyz. If no LeafNode exists,...
Definition: ValueAccessor.h:837
typename std::conditional<(NumCacheLevels > 2), NodeTypeAtLevel< 2 >, void >::type NodeT2
Definition: ValueAccessor.h:900
void insertNode(const Coord &xyz, NodeT &node)
Explicitly insert a node of the type NodeT into this Value Accessors cache.
Definition: ValueAccessor.h:860
~ValueAccessorImpl() override final=default
void setValueOn(const Coord &xyz)
Mark the voxel at the given coordinates as active without changing its value.
Definition: ValueAccessor.h:698
void setValueOn(const Coord &xyz, const ValueType &value)
Definition: ValueAccessor.h:569
_TreeType TreeType
Definition: ValueAccessor.h:375
typename std::conditional<(NumCacheLevels > 0), NodeTypeAtLevel< 0 >, void >::type NodeT0
Definition: ValueAccessor.h:896
const ValueType & getValue(const Coord &xyz) const
Return the value of the voxel at the given coordinates.
Definition: ValueAccessor.h:455
void release() override final
Release this accessor from the tree, set the tree to null and clear the accessor cache....
Definition: ValueAccessor.h:916
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:110
Index32 Index
Definition: Types.h:54
OPENVDB_FORCE_INLINE RetT evalFirstIndex(OpT op, const RetT def=RetT())
Definition: TypeList.h:566
std::decay_t< decltype(make_index_sequence_impl< N >())> make_index_sequence
Definition: Types.h:234
Definition: Exceptions.h:13
Definition: Coord.h:589
A list of types (not necessarily unique)
Definition: TypeList.h:578
A small class that contains a cached pointer to a LeafNode data buffer which is derived from by the i...
Definition: ValueAccessor.h:333
const TreeTypeT::ValueType * buffer() const
Definition: ValueAccessor.h:338
const TreeTypeT::ValueType * buffer()
Definition: ValueAccessor.h:337
void setBuffer(const typename TreeTypeT::ValueType *b) const
Definition: ValueAccessor.h:339
constexpr auto lock() const
Definition: ValueAccessor.h:322
A small class that contains a Mutex which is derived from by the internal Value Accessor Implementati...
Definition: ValueAccessor.h:312
auto lock() const
Definition: ValueAccessor.h:313
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:212