OpenVDB  12.0.0
Merge.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: Apache-2.0
3 //
4 /// @file Merge.h
5 ///
6 /// @brief Functions to efficiently merge grids
7 ///
8 /// @author Dan Bailey
9 
10 #ifndef OPENVDB_TOOLS_MERGE_HAS_BEEN_INCLUDED
11 #define OPENVDB_TOOLS_MERGE_HAS_BEEN_INCLUDED
12 
13 #include <openvdb/Platform.h>
14 #include <openvdb/Exceptions.h>
15 #include <openvdb/Types.h>
16 #include <openvdb/Grid.h>
18 #include <openvdb/openvdb.h>
19 #include <openvdb/util/Assert.h>
20 
21 #include "NodeVisitor.h"
22 
23 #include <memory>
24 #include <unordered_map>
25 #include <unordered_set>
26 
27 namespace openvdb {
29 namespace OPENVDB_VERSION_NAME {
30 namespace tools {
31 
32 
33 /// @brief Convenience class that contains a pointer to a tree to be stolen or
34 /// deep copied depending on the tag dispatch class used and a subset of
35 /// methods to retrieve data from the tree.
36 ///
37 /// @details The primary purpose of this class is to be able to create an array
38 /// of TreeToMerge objects that each store a tree to be stolen or a tree to be
39 /// deep-copied in an arbitrary order. Certain operations such as floating-point
40 /// addition are non-associative so the order in which they are merged is
41 /// important for the operation to remain deterministic regardless of how the
42 /// data is being extracted from the tree.
43 ///
44 /// @note Stealing data requires a non-const tree pointer. There is a constructor
45 /// to pass in a tree shared pointer for cases where it is desirable for this class
46 /// to maintain shared ownership.
47 template <typename TreeT>
49 {
50  using TreeType = std::remove_const_t<TreeT>;
51  using RootNodeType = typename TreeType::RootNodeType;
52  using ValueType = typename TreeType::ValueType;
53  using MaskTreeType = typename TreeT::template ValueConverter<ValueMask>::Type;
54 
55  TreeToMerge() = delete;
56 
57  /// @brief Non-const pointer tree constructor for stealing data.
59  : mTree(&tree), mSteal(true) { }
60  /// @brief Non-const shared pointer tree constructor for stealing data.
61  TreeToMerge(typename TreeType::Ptr treePtr, Steal)
62  : mTreePtr(treePtr), mTree(mTreePtr.get()), mSteal(true) { }
63 
64  /// @brief Const tree pointer constructor for deep-copying data. As the
65  /// tree is not mutable and thus cannot be pruned, a lightweight mask tree
66  /// with the same topology is created that can be pruned to use as a
67  /// reference. Initialization of this mask tree can optionally be disabled
68  /// for delayed construction.
69  TreeToMerge(const TreeType& tree, DeepCopy, bool initialize = true)
70  : mTree(&tree), mSteal(false)
71  {
72  if (mTree && initialize) this->initializeMask();
73  }
74 
75  /// @brief Non-const tree pointer constructor for deep-copying data. The
76  /// tree is not intended to be modified so is not pruned, instead a
77  /// lightweight mask tree with the same topology is created that can be
78  /// pruned to use as a reference. Initialization of this mask tree can
79  /// optionally be disabled for delayed construction.
80  TreeToMerge(TreeType& tree, DeepCopy tag, bool initialize = true)
81  : TreeToMerge(static_cast<const TreeType&>(tree), tag, initialize) { }
82 
83  /// @brief Reset the non-const tree shared pointer. This is primarily
84  /// used to preserve the order of trees to merge in a container but have
85  /// the data in the tree be lazily loaded or resampled.
86  void reset(typename TreeType::Ptr treePtr, Steal);
87 
88  /// @brief Return a pointer to the tree to be stolen.
89  TreeType* treeToSteal() { return mSteal ? const_cast<TreeType*>(mTree) : nullptr; }
90  /// @brief Return a pointer to the tree to be deep-copied.
91  const TreeType* treeToDeepCopy() { return mSteal ? nullptr : mTree; }
92 
93  /// @brief Retrieve a const pointer to the root node.
94  const RootNodeType* rootPtr() const;
95 
96  /// @brief Return a pointer to the node of type @c NodeT that contains
97  /// voxel (x, y, z). If no such node exists, return @c nullptr.
98  template<typename NodeT>
99  const NodeT* probeConstNode(const Coord& ijk) const;
100 
101  /// @brief Prune the mask and remove the node associated with this coord.
102  void pruneMask(Index level, const Coord& ijk);
103 
104  /// @brief Return a pointer to the node of type @c NodeT that contains voxel (x, y, z).
105  /// If the tree is non-const, steal the node and replace it with the value provided.
106  /// If the tree is const, deep-copy the node and modify the mask tree to prune the node.
107  template <typename NodeT>
108  std::unique_ptr<NodeT> stealOrDeepCopyNode(const Coord& ijk, const ValueType& value);
109 
110  /// @brief Return a pointer to the node of type @c NodeT that contains voxel (x, y, z).
111  /// If the tree is non-const, steal the node and replace it with an inactive
112  /// background-value tile.
113  /// If the tree is const, deep-copy the node and modify the mask tree to prune the node.
114  template <typename NodeT>
115  std::unique_ptr<NodeT> stealOrDeepCopyNode(const Coord& ijk);
116 
117  /// @brief Add a tile containing voxel (x, y, z) at the level of NodeT,
118  /// deleting the existing branch if necessary.
119  template <typename NodeT>
120  void addTile(const Coord& ijk, const ValueType& value, bool active);
121 
122  // build a lightweight mask using a union of the const tree where leaf nodes
123  // are converted into active tiles
124  void initializeMask();
125 
126  // returns true if mask has been initialized
127  bool hasMask() const;
128 
129  // returns MaskTree pointer or nullptr
130  MaskTreeType* mask() { return mMaskTree.ptr.get(); }
131  const MaskTreeType* mask() const { return mMaskTree.ptr.get(); }
132 
133 private:
134  struct MaskPtr;
135  struct MaskUnionOp;
136 
137  typename TreeType::Ptr mTreePtr;
138  const TreeType* mTree;
139  MaskPtr mMaskTree;
140  bool mSteal;
141 }; // struct TreeToMerge
142 
143 
144 /// @brief Wrapper around unique_ptr that deep-copies mask on copy construction
145 template <typename TreeT>
146 struct TreeToMerge<TreeT>::MaskPtr
147 {
148  std::unique_ptr<MaskTreeType> ptr;
149 
150  MaskPtr() = default;
151  ~MaskPtr() = default;
152  MaskPtr(MaskPtr&& other) = default;
153  MaskPtr& operator=(MaskPtr&& other) = default;
154  MaskPtr(const MaskPtr& other)
155  : ptr(bool(other.ptr) ? std::make_unique<MaskTreeType>(*other.ptr) : nullptr) { }
156  MaskPtr& operator=(const MaskPtr& other)
157  {
158  if (bool(other.ptr)) ptr = std::make_unique<MaskTreeType>(*other.ptr);
159  else ptr.reset();
160  return *this;
161  }
162 };
163 
164 /// @brief DynamicNodeManager operator used to generate a mask of the input
165 /// tree, but with dense leaf nodes replaced with active tiles for compactness
166 template <typename TreeT>
167 struct TreeToMerge<TreeT>::MaskUnionOp
168 {
170  using RootT = typename MaskT::RootNodeType;
171  using LeafT = typename MaskT::LeafNodeType;
172 
173  explicit MaskUnionOp(const TreeT& tree) : mTree(tree) { }
174  bool operator()(RootT& root, size_t) const;
175  template<typename NodeT>
176  bool operator()(NodeT& node, size_t) const;
177  bool operator()(LeafT&, size_t) const { return false; }
178 private:
179  const TreeT& mTree;
180 }; // struct TreeToMerge<TreeT>::MaskUnionOp
181 
182 
183 ////////////////////////////////////////
184 
185 
186 /// @brief DynamicNodeManager operator to merge trees using a CSG union or intersection.
187 /// @note This class modifies the topology of the tree so is designed to be used
188 /// from DynamicNodeManager::foreachTopDown().
189 /// @details A union and an intersection are opposite operations to each other so
190 /// implemented in a combined class. Use the CsgUnionOp and CsgIntersectionOp aliases
191 /// for convenience.
192 template<typename TreeT, bool Union>
194 {
195  using ValueT = typename TreeT::ValueType;
196  using RootT = typename TreeT::RootNodeType;
197  using LeafT = typename TreeT::LeafNodeType;
198 
199  /// @brief Convenience constructor to CSG union or intersect a single
200  /// non-const tree with another. This constructor takes a Steal or DeepCopy
201  /// tag dispatch class.
202  template <typename TagT>
203  CsgUnionOrIntersectionOp(TreeT& tree, TagT tag) { mTreesToMerge.emplace_back(tree, tag); }
204 
205  /// @brief Convenience constructor to CSG union or intersect a single
206  /// const tree with another. This constructor requires a DeepCopy tag
207  /// dispatch class.
208  CsgUnionOrIntersectionOp(const TreeT& tree, DeepCopy tag) { mTreesToMerge.emplace_back(tree, tag); }
209 
210  /// @brief Constructor to CSG union or intersect a container of multiple
211  /// const or non-const tree pointers. A Steal tag requires a container of
212  /// non-const trees, a DeepCopy tag will accept either const or non-const
213  /// trees.
214  template <typename TreesT, typename TagT>
215  CsgUnionOrIntersectionOp(TreesT& trees, TagT tag)
216  {
217  for (auto* tree : trees) {
218  if (tree) {
219  mTreesToMerge.emplace_back(*tree, tag);
220  }
221  }
222  }
223 
224  /// @brief Constructor to accept a vector of TreeToMerge objects, primarily
225  /// used when mixing const/non-const trees.
226  /// @note Union/intersection order is preserved.
227  explicit CsgUnionOrIntersectionOp(const std::vector<TreeToMerge<TreeT>>& trees)
228  : mTreesToMerge(trees) { }
229 
230  /// @brief Constructor to accept a deque of TreeToMerge objects, primarily
231  /// used when mixing const/non-const trees.
232  /// @note Union/intersection order is preserved.
233  explicit CsgUnionOrIntersectionOp(const std::deque<TreeToMerge<TreeT>>& trees)
234  : mTreesToMerge(trees.cbegin(), trees.cend()) { }
235 
236  /// @brief Return true if no trees being merged
237  bool empty() const { return mTreesToMerge.empty(); }
238 
239  /// @brief Return the number of trees being merged
240  size_t size() const { return mTreesToMerge.size(); }
241 
242  /// Enables immediate pruning of tiles that cancel each other out.
243  void setPruneCancelledTiles(bool doprune) { mPruneCancelledTiles = doprune; }
244 
245  // Processes the root node. Required by the NodeManager
246  bool operator()(RootT& root, size_t idx) const;
247 
248  // Processes the internal nodes. Required by the NodeManager
249  template<typename NodeT>
250  bool operator()(NodeT& node, size_t idx) const;
251 
252  // Processes the leaf nodes. Required by the NodeManager
253  bool operator()(LeafT& leaf, size_t idx) const;
254 
255 private:
256  // on processing the root node, the background value is stored, retrieve it
257  // and check that the root node has already been processed
258  const ValueT& background() const;
259 
260  mutable std::vector<TreeToMerge<TreeT>> mTreesToMerge;
261  mutable const ValueT* mBackground = nullptr;
262  bool mPruneCancelledTiles = false;
263 }; // struct CsgUnionOrIntersectionOp
264 
265 
266 template <typename TreeT>
267 using CsgUnionOp = CsgUnionOrIntersectionOp<TreeT, /*Union=*/true>;
268 
269 template <typename TreeT>
270 using CsgIntersectionOp = CsgUnionOrIntersectionOp<TreeT, /*Union=*/false>;
271 
272 
273 /// @brief DynamicNodeManager operator to merge two trees using a CSG difference.
274 /// @note This class modifies the topology of the tree so is designed to be used
275 /// from DynamicNodeManager::foreachTopDown().
276 /// PruneCancelledTiles will set to background any leaf tile that matches
277 /// in the two trees, thus minimizing ghost banding when common borders
278 /// are differenced.
279 template<typename TreeT>
281 {
282  using ValueT = typename TreeT::ValueType;
283  using RootT = typename TreeT::RootNodeType;
284  using LeafT = typename TreeT::LeafNodeType;
285 
286  /// @brief Convenience constructor to CSG difference a single non-const
287  /// tree from another. This constructor takes a Steal or DeepCopy tag
288  /// dispatch class.
289  template <typename TagT>
290  CsgDifferenceOp(TreeT& tree, TagT tag) : mTree(tree, tag) { }
291  /// @brief Convenience constructor to CSG difference a single const
292  /// tree from another. This constructor requires an explicit DeepCopy tag
293  /// dispatch class.
294  CsgDifferenceOp(const TreeT& tree, DeepCopy tag) : mTree(tree, tag) { }
295 
296  /// @brief Constructor to CSG difference the tree in a TreeToMerge object
297  /// from another.
298  explicit CsgDifferenceOp(TreeToMerge<TreeT>& tree) : mTree(tree) { }
299 
300  /// Enables immediate pruning of tiles that cancel each other out.
301  void setPruneCancelledTiles(bool doprune) { mPruneCancelledTiles = doprune; }
302 
303  /// @brief Return the number of trees being merged (only ever 1)
304  size_t size() const { return 1; }
305 
306  // Processes the root node. Required by the NodeManager
307  bool operator()(RootT& root, size_t idx) const;
308 
309  // Processes the internal nodes. Required by the NodeManager
310  template<typename NodeT>
311  bool operator()(NodeT& node, size_t idx) const;
312 
313  // Processes the leaf nodes. Required by the NodeManager
314  bool operator()(LeafT& leaf, size_t idx) const;
315 
316 private:
317  // on processing the root node, the background values are stored, retrieve them
318  // and check that the root nodes have already been processed
319  const ValueT& background() const;
320  const ValueT& otherBackground() const;
321 
322  // note that this vector is copied in NodeTransformer every time a foreach call is made,
323  // however in typical use cases this cost will be dwarfed by the actual merge algorithm
324  mutable TreeToMerge<TreeT> mTree;
325  mutable const ValueT* mBackground = nullptr;
326  mutable const ValueT* mOtherBackground = nullptr;
327  bool mPruneCancelledTiles = false;
328 }; // struct CsgDifferenceOp
329 
330 
331 /// @brief DynamicNodeManager operator to merge trees using a sum operation.
332 /// @note This class modifies the topology of the tree so is designed to be used
333 /// from DynamicNodeManager::foreachTopDown().
334 template<typename TreeT>
336 {
337  using ValueT = typename TreeT::ValueType;
338  using RootT = typename TreeT::RootNodeType;
339  using LeafT = typename TreeT::LeafNodeType;
340 
341  /// @brief Convenience constructor to sum a single non-const tree with another.
342  /// This constructor takes a Steal or DeepCopy tag dispatch class.
343  template <typename TagT>
344  SumMergeOp(TreeT& tree, TagT tag) { mTreesToMerge.emplace_back(tree, tag); }
345 
346  /// @brief Convenience constructor to sum a single const tree with another.
347  /// This constructor requires a DeepCopy tag dispatch class.
348  SumMergeOp(const TreeT& tree, DeepCopy tag) { mTreesToMerge.emplace_back(tree, tag); }
349 
350  /// @brief Constructor to sum a container of multiple const or non-const tree pointers.
351  /// A Steal tag requires a container of non-const trees, a DeepCopy tag will accept
352  /// either const or non-const trees.
353  template <typename TreesT, typename TagT>
354  SumMergeOp(TreesT& trees, TagT tag)
355  {
356  for (auto* tree : trees) {
357  if (tree) {
358  mTreesToMerge.emplace_back(*tree, tag);
359  }
360  }
361  }
362 
363  /// @brief Constructor to accept a vector of TreeToMerge objects, primarily
364  /// used when mixing const/non-const trees.
365  /// @note Sum order is preserved.
366  explicit SumMergeOp(const std::vector<TreeToMerge<TreeT>>& trees)
367  : mTreesToMerge(trees) { }
368 
369  /// @brief Constructor to accept a deque of TreeToMerge objects, primarily
370  /// used when mixing const/non-const trees.
371  /// @note Sum order is preserved.
372  explicit SumMergeOp(const std::deque<TreeToMerge<TreeT>>& trees)
373  : mTreesToMerge(trees.cbegin(), trees.cend()) { }
374 
375  /// @brief Return true if no trees being merged
376  bool empty() const { return mTreesToMerge.empty(); }
377 
378  /// @brief Return the number of trees being merged
379  size_t size() const { return mTreesToMerge.size(); }
380 
381  // Processes the root node. Required by the NodeManager
382  bool operator()(RootT& root, size_t idx) const;
383 
384  // Processes the internal nodes. Required by the NodeManager
385  template<typename NodeT>
386  bool operator()(NodeT& node, size_t idx) const;
387 
388  // Processes the leaf nodes. Required by the NodeManager
389  bool operator()(LeafT& leaf, size_t idx) const;
390 
391 private:
392  // on processing the root node, the background value is stored, retrieve it
393  // and check that the root node has already been processed
394  const ValueT& background() const;
395 
396  mutable std::vector<TreeToMerge<TreeT>> mTreesToMerge;
397  mutable const ValueT* mBackground = nullptr;
398 }; // struct SumMergeOp
399 
400 
401 ////////////////////////////////////////
402 
403 
404 template<typename TreeT>
406 {
407  if (mSteal) return;
408  mMaskTree.ptr.reset(new MaskTreeType);
409  MaskUnionOp op(*mTree);
410  tree::DynamicNodeManager<MaskTreeType, MaskTreeType::RootNodeType::LEVEL-1> manager(*this->mask());
411  manager.foreachTopDown(op);
412 }
413 
414 template<typename TreeT>
416 {
417  return bool(mMaskTree.ptr);
418 }
419 
420 template<typename TreeT>
421 void TreeToMerge<TreeT>::reset(typename TreeType::Ptr treePtr, Steal)
422 {
423  if (!treePtr) {
424  OPENVDB_THROW(RuntimeError, "Cannot reset with empty Tree shared pointer.");
425  }
426  mSteal = true;
427  mTreePtr = treePtr;
428  mTree = mTreePtr.get();
429 }
430 
431 template<typename TreeT>
432 const typename TreeToMerge<TreeT>::RootNodeType*
434 {
435  return &mTree->root();
436 }
437 
438 template<typename TreeT>
439 template<typename NodeT>
440 const NodeT*
441 TreeToMerge<TreeT>::probeConstNode(const Coord& ijk) const
442 {
443  // test mutable mask first, node may have already been pruned
444  if (!mSteal && !this->mask()->isValueOn(ijk)) return nullptr;
445  return mTree->template probeConstNode<NodeT>(ijk);
446 }
447 
448 template<typename TreeT>
449 void
450 TreeToMerge<TreeT>::pruneMask(Index level, const Coord& ijk)
451 {
452  if (!mSteal) {
453  OPENVDB_ASSERT(this->hasMask());
454  this->mask()->addTile(level, ijk, false, false);
455  }
456 }
457 
458 template<typename TreeT>
459 template<typename NodeT>
460 std::unique_ptr<NodeT>
461 TreeToMerge<TreeT>::stealOrDeepCopyNode(const Coord& ijk, const ValueType& value)
462 {
463  if (mSteal) {
464  TreeType* tree = const_cast<TreeType*>(mTree);
465  return std::unique_ptr<NodeT>(
466  tree->root().template stealNode<NodeT>(ijk, value, false)
467  );
468  } else {
469  auto* child = this->probeConstNode<NodeT>(ijk);
470  if (child) {
471  auto result = std::make_unique<NodeT>(*child);
472  this->pruneMask(NodeT::LEVEL+1, ijk);
473  return result;
474  }
475  }
476  return std::unique_ptr<NodeT>();
477 }
478 
479 template<typename TreeT>
480 template<typename NodeT>
481 std::unique_ptr<NodeT>
483 {
484  return this->stealOrDeepCopyNode<NodeT>(ijk, mTree->root().background());
485 }
486 
487 template<typename TreeT>
488 template<typename NodeT>
489 void
490 TreeToMerge<TreeT>::addTile(const Coord& ijk, const ValueType& value, bool active)
491 {
492  // ignore leaf node tiles (values)
493  if (NodeT::LEVEL == 0) return;
494 
495  if (mSteal) {
496  TreeType* tree = const_cast<TreeType*>(mTree);
497  auto* node = tree->template probeNode<NodeT>(ijk);
498  if (node) {
499  const Index pos = NodeT::coordToOffset(ijk);
500  node->addTile(pos, value, active);
501  }
502  } else {
503  auto* node = mTree->template probeConstNode<NodeT>(ijk);
504  if (node) this->pruneMask(NodeT::LEVEL, ijk);
505  }
506 }
507 
508 
509 ////////////////////////////////////////
510 
511 
512 template <typename TreeT>
513 bool TreeToMerge<TreeT>::MaskUnionOp::operator()(RootT& root, size_t /*idx*/) const
514 {
515  using ChildT = typename RootT::ChildNodeType;
516 
517  const Index count = mTree.root().childCount();
518 
519  std::vector<std::unique_ptr<ChildT>> children(count);
520 
521  // allocate new root children
522 
523  tbb::parallel_for(
524  tbb::blocked_range<Index>(0, count),
525  [&](tbb::blocked_range<Index>& range)
526  {
527  for (Index i = range.begin(); i < range.end(); i++) {
528  children[i] = std::make_unique<ChildT>(Coord::max(), true, true);
529  }
530  }
531  );
532 
533  // apply origins and add root children to new root node
534 
535  size_t i = 0;
536  for (auto iter = mTree.root().cbeginChildOn(); iter; ++iter) {
537  children[i]->setOrigin(iter->origin());
538  root.addChild(children[i].release());
539  i++;
540  }
541 
542  return true;
543 }
544 
545 template <typename TreeT>
546 template <typename NodeT>
547 bool TreeToMerge<TreeT>::MaskUnionOp::operator()(NodeT& node, size_t /*idx*/) const
548 {
549  using ChildT = typename NodeT::ChildNodeType;
550 
551  const auto* otherNode = mTree.template probeConstNode<NodeT>(node.origin());
552  if (!otherNode) return false;
553 
554  // this mask tree stores active tiles in place of leaf nodes for compactness
555 
556  if (NodeT::LEVEL == 1) {
557  for (auto iter = otherNode->cbeginChildOn(); iter; ++iter) {
558  node.addTile(iter.pos(), true, true);
559  }
560  } else {
561  for (auto iter = otherNode->cbeginChildOn(); iter; ++iter) {
562  auto* child = new ChildT(iter->origin(), true, true);
563  node.addChild(child);
564  }
565  }
566 
567  return true;
568 }
569 
570 
571 ////////////////////////////////////////
572 
573 /// @cond OPENVDB_DOCS_INTERNAL
574 
575 namespace merge_internal {
576 
577 
578 template <typename BufferT, typename ValueT>
579 struct UnallocatedBuffer
580 {
581  static void allocateAndFill(BufferT& buffer, const ValueT& background)
582  {
583  if (buffer.empty()) {
584  if (!buffer.isOutOfCore()) {
585  buffer.allocate();
586  buffer.fill(background);
587  }
588  }
589  }
590 
591  static bool isPartiallyConstructed(const BufferT& buffer)
592  {
593  return !buffer.isOutOfCore() && buffer.empty();
594  }
595 }; // struct AllocateAndFillBuffer
596 
597 template <typename BufferT>
598 struct UnallocatedBuffer<BufferT, bool>
599 {
600  // do nothing for bool buffers as they cannot be unallocated
601  static void allocateAndFill(BufferT&, const bool&) { }
602  static bool isPartiallyConstructed(const BufferT&) { return false; }
603 }; // struct AllocateAndFillBuffer
604 
605 
606 // a convenience class that combines nested parallelism with the depth-visit
607 // node visitor which can result in increased performance with large sub-trees
608 template <Index LEVEL>
609 struct Dispatch
610 {
611  template <typename NodeT, typename OpT>
612  static void run(NodeT& node, OpT& op)
613  {
614  using NonConstChildT = typename NodeT::ChildNodeType;
615  using ChildT = typename CopyConstness<NodeT, NonConstChildT>::Type;
616 
617  // use nested parallelism if there is more than one child
618 
619  Index32 childCount = node.childCount();
620  if (childCount > 0) {
621  op(node, size_t(0));
622 
623  // build linear list of child pointers
624  std::vector<ChildT*> children;
625  children.reserve(childCount);
626  for (auto iter = node.beginChildOn(); iter; ++iter) {
627  children.push_back(&(*iter));
628  }
629 
630  // parallelize across children
631  tbb::parallel_for(
632  tbb::blocked_range<Index32>(0, childCount),
633  [&](tbb::blocked_range<Index32>& range) {
634  for (Index32 n = range.begin(); n < range.end(); n++) {
635  DepthFirstNodeVisitor<ChildT>::visit(*children[n], op);
636  }
637  }
638  );
639  } else {
641  }
642  }
643 }; // struct Dispatch
644 
645 // when LEVEL = 0, do not attempt nested parallelism
646 template <>
647 struct Dispatch<0>
648 {
649  template <typename NodeT, typename OpT>
650  static void run(NodeT& node, OpT& op)
651  {
653  }
654 };
655 
656 
657 // an DynamicNodeManager operator to add a value and modify active state
658 // for every tile and voxel in a given subtree
659 template <typename TreeT>
660 struct ApplyTileSumToNodeOp
661 {
662  using LeafT = typename TreeT::LeafNodeType;
663  using ValueT = typename TreeT::ValueType;
664 
665  ApplyTileSumToNodeOp(const ValueT& value, const bool active):
666  mValue(value), mActive(active) { }
667 
668  template <typename NodeT>
669  void operator()(NodeT& node, size_t) const
670  {
671  // TODO: Need to add an InternalNode::setValue(Index offset, ...) to
672  // avoid the cost of using a value iterator or coordToOffset() in the case
673  // where node.isChildMaskOff() is true
674 
675  for (auto iter = node.beginValueAll(); iter; ++iter) {
676  iter.setValue(mValue + *iter);
677  }
678  if (mActive) node.setValuesOn();
679  }
680 
681  void operator()(LeafT& leaf, size_t) const
682  {
683  auto* data = leaf.buffer().data();
684 
685  if (mValue != zeroVal<ValueT>()) {
686  for (Index i = 0; i < LeafT::SIZE; ++i) {
687  data[i] += mValue;
688  }
689  }
690  if (mActive) leaf.setValuesOn();
691  }
692 
693  template <typename NodeT>
694  void run(NodeT& node)
695  {
696  Dispatch<NodeT::LEVEL>::run(node, *this);
697  }
698 
699 private:
700  ValueT mValue;
701  bool mActive;
702 }; // struct ApplyTileSumToNodeOp
703 
704 
705 
706 } // namespace merge_internal
707 
708 
709 /// @endcond
710 
711 ////////////////////////////////////////
712 
713 
714 template <typename TreeT, bool Union>
716 {
717  const bool Intersect = !Union;
718 
719  if (this->empty()) return false;
720 
721  // store the background value
722  if (!mBackground) mBackground = &root.background();
723 
724  // does the key exist in the root node?
725  auto keyExistsInRoot = [&](const Coord& key) -> bool
726  {
727  return root.getValueDepth(key) > -1;
728  };
729 
730  // does the key exist in all merge tree root nodes?
731  auto keyExistsInAllTrees = [&](const Coord& key) -> bool
732  {
733  for (TreeToMerge<TreeT>& mergeTree : mTreesToMerge) {
734  const auto* mergeRoot = mergeTree.rootPtr();
735  if (!mergeRoot) return false;
736  if (mergeRoot->getValueDepth(key) == -1) return false;
737  }
738  return true;
739  };
740 
741  // delete any background tiles
742  root.eraseBackgroundTiles();
743 
744  // for intersection, delete any root node keys that are not present in all trees
745  if (Intersect) {
746  // find all tile coordinates to delete
747  std::vector<Coord> toDelete;
748  for (auto valueIter = root.cbeginValueAll(); valueIter; ++valueIter) {
749  const Coord& key = valueIter.getCoord();
750  if (!keyExistsInAllTrees(key)) toDelete.push_back(key);
751  }
752  // find all child coordinates to delete
753  for (auto childIter = root.cbeginChildOn(); childIter; ++childIter) {
754  const Coord& key = childIter.getCoord();
755  if (!keyExistsInAllTrees(key)) toDelete.push_back(key);
756  }
757  // only mechanism to delete elements in root node is to delete background tiles,
758  // so insert background tiles (which will replace any child nodes) and then delete
759  for (Coord& key : toDelete) root.addTile(key, *mBackground, false);
760  root.eraseBackgroundTiles();
761  }
762 
763  // find all tile values in this root and track inside/outside and active state
764  // note that level sets should never contain active tiles, but we handle them anyway
765 
766  constexpr uint8_t ACTIVE_TILE = 0x1;
767  constexpr uint8_t INSIDE_TILE = 0x2;
768  constexpr uint8_t OUTSIDE_TILE = 0x4;
769 
770  constexpr uint8_t INSIDE_STATE = Union ? INSIDE_TILE : OUTSIDE_TILE;
771  constexpr uint8_t OUTSIDE_STATE = Union ? OUTSIDE_TILE : INSIDE_TILE;
772 
773  const ValueT insideBackground = Union ? -this->background() : this->background();
774  const ValueT outsideBackground = -insideBackground;
775 
776  auto getTileFlag = [&](auto& valueIter) -> uint8_t
777  {
778  uint8_t flag(0);
779  const ValueT& value = valueIter.getValue();
780  if (value < zeroVal<ValueT>()) flag |= INSIDE_TILE;
781  else if (value > zeroVal<ValueT>()) flag |= OUTSIDE_TILE;
782  if (valueIter.isValueOn()) flag |= ACTIVE_TILE;
783  return flag;
784  };
785 
786  std::unordered_map<Coord, /*flags*/uint8_t> tiles;
787 
788  if (root.getTableSize() > 0) {
789  for (auto valueIter = root.cbeginValueAll(); valueIter; ++valueIter) {
790  const Coord& key = valueIter.getCoord();
791  tiles.insert({key, getTileFlag(valueIter)});
792  }
793  }
794 
795  // find all tiles values in other roots and replace outside tiles with inside tiles
796 
797  for (TreeToMerge<TreeT>& mergeTree : mTreesToMerge) {
798  const auto* mergeRoot = mergeTree.rootPtr();
799  if (!mergeRoot) continue;
800  for (auto valueIter = mergeRoot->cbeginValueAll(); valueIter; ++valueIter) {
801  const Coord& key = valueIter.getCoord();
802  auto it = tiles.find(key);
803  if (it == tiles.end()) {
804  // if no tile with this key, insert it
805  tiles.insert({key, getTileFlag(valueIter)});
806  } else {
807  // replace an outside tile with an inside tile
808  const uint8_t flag = it->second;
809  if (flag & OUTSIDE_STATE) {
810  const uint8_t newFlag = getTileFlag(valueIter);
811  if (newFlag & INSIDE_STATE) {
812  it->second = newFlag;
813  }
814  }
815  }
816  }
817  }
818 
819  // insert all inside tiles
820 
821  for (auto it : tiles) {
822  const uint8_t flag = it.second;
823  if (flag & INSIDE_STATE) {
824  const Coord& key = it.first;
825  const bool state = flag & ACTIVE_TILE;
826  // for intersection, only add the tile if the key already exists in the tree
827  if (Union || keyExistsInRoot(key)) {
828  root.addTile(key, insideBackground, state);
829  }
830  }
831  }
832 
833  std::unordered_set<Coord> children;
834 
835  if (root.getTableSize() > 0) {
836  for (auto childIter = root.cbeginChildOn(); childIter; ++childIter) {
837  const Coord& key = childIter.getCoord();
838  children.insert(key);
839  }
840  }
841 
842  bool continueRecurse = false;
843 
844  // find all children in other roots and insert them if a child or tile with this key
845  // does not already exist or if the child will replace an outside tile
846 
847  for (TreeToMerge<TreeT>& mergeTree : mTreesToMerge) {
848  const auto* mergeRoot = mergeTree.rootPtr();
849  if (!mergeRoot) continue;
850  for (auto childIter = mergeRoot->cbeginChildOn(); childIter; ++childIter) {
851  const Coord& key = childIter.getCoord();
852 
853  // for intersection, only add child nodes if the key already exists in the tree
854  if (Intersect && !keyExistsInRoot(key)) continue;
855 
856  // if child already exists, merge recursion will need to continue to resolve conflict
857  if (children.count(key)) {
858  continueRecurse = true;
859  continue;
860  }
861 
862  // if an inside tile exists, do nothing
863  auto it = tiles.find(key);
864  if (it != tiles.end() && it->second == INSIDE_STATE) continue;
865 
866  auto childPtr = mergeTree.template stealOrDeepCopyNode<typename RootT::ChildNodeType>(key);
867  childPtr->resetBackground(mergeRoot->background(), root.background());
868  if (childPtr) root.addChild(childPtr.release());
869 
870  children.insert(key);
871  }
872  }
873 
874  // insert all outside tiles that don't replace an inside tile or a child node
875 
876  for (auto it : tiles) {
877  const uint8_t flag = it.second;
878  if (flag & OUTSIDE_STATE) {
879  const Coord& key = it.first;
880  if (!children.count(key)) {
881  const bool state = flag & ACTIVE_TILE;
882  // for intersection, only add the tile if the key already exists in the tree
883  if (Union || keyExistsInRoot(key)) {
884  root.addTile(key, outsideBackground, state);
885  }
886  }
887  }
888  }
889 
890  // finish by removing any background tiles
891  root.eraseBackgroundTiles();
892 
893  return continueRecurse;
894 }
895 
896 template<typename TreeT, bool Union>
897 template<typename NodeT>
899 {
900  using NonConstNodeT = typename std::remove_const<NodeT>::type;
901 
902  if (this->empty()) return false;
903 
904  const ValueT insideBackground = Union ? -this->background() : this->background();
905  const ValueT outsideBackground = -insideBackground;
906 
907  using NodeMaskT = typename NodeT::NodeMaskType;
908 
909  // store temporary masks to track inside and outside tile states
910  NodeMaskT validTile;
911  NodeMaskT invalidTile;
912 
913  auto isValid = [](const ValueT& value)
914  {
915  return Union ? value < zeroVal<ValueT>() : value > zeroVal<ValueT>();
916  };
917 
918  auto isInvalid = [](const ValueT& value)
919  {
920  return Union ? value > zeroVal<ValueT>() : value < zeroVal<ValueT>();
921  };
922 
923  for (auto iter = node.cbeginValueAll(); iter; ++iter) {
924  if (isValid(iter.getValue())) {
925  validTile.setOn(iter.pos());
926  } else if (isInvalid(iter.getValue())) {
927  invalidTile.setOn(iter.pos());
928  }
929  }
930 
931  bool continueRecurse = false;
932 
933  for (TreeToMerge<TreeT>& mergeTree : mTreesToMerge) {
934 
935  auto* mergeNode = mergeTree.template probeConstNode<NonConstNodeT>(node.origin());
936 
937  if (!mergeNode) continue;
938 
939  // iterate over all tiles
940 
941  for (auto iter = mergeNode->cbeginValueAll(); iter; ++iter) {
942  Index pos = iter.pos();
943  // source node contains an inside tile, so ignore
944  if (validTile.isOn(pos)) continue;
945  // this node contains an inside tile, so turn into an inside tile
946  if (isValid(iter.getValue())) {
947  node.addTile(pos, insideBackground, iter.isValueOn());
948  validTile.setOn(pos);
949  }
950  }
951 
952  // iterate over all child nodes
953 
954  for (auto iter = mergeNode->cbeginChildOn(); iter; ++iter) {
955  Index pos = iter.pos();
956  const Coord& ijk = iter.getCoord();
957  // source node contains an inside tile, so ensure other node has no child
958  if (validTile.isOn(pos)) {
959  mergeTree.template addTile<NonConstNodeT>(ijk, outsideBackground, false);
960  } else if (invalidTile.isOn(pos)) {
961  auto childPtr = mergeTree.template stealOrDeepCopyNode<typename NodeT::ChildNodeType>(ijk);
962  if (childPtr) {
963  childPtr->resetBackground(mergeTree.rootPtr()->background(), this->background());
964  node.addChild(childPtr.release());
965  }
966  invalidTile.setOff(pos);
967  } else {
968  // if both source and target are child nodes, merge recursion needs to continue
969  // along this branch to resolve the conflict
970  continueRecurse = true;
971  }
972  }
973  }
974 
975  return continueRecurse;
976 }
977 
978 template <typename TreeT, bool Union>
980 {
981  using LeafT = typename TreeT::LeafNodeType;
982  using ValueT = typename LeafT::ValueType;
983  using BufferT = typename LeafT::Buffer;
984 
985  if (this->empty()) return false;
986 
987  const ValueT background = Union ? this->background() : -this->background();
988 
989  // if buffer is not out-of-core and empty, leaf node must have only been
990  // partially constructed, so allocate and fill with background value
991 
992  merge_internal::UnallocatedBuffer<BufferT, ValueT>::allocateAndFill(
993  leaf.buffer(), background);
994 
995  for (TreeToMerge<TreeT>& mergeTree : mTreesToMerge) {
996  const LeafT* mergeLeaf = mergeTree.template probeConstNode<LeafT>(leaf.origin());
997  if (!mergeLeaf) continue;
998  // if buffer is not out-of-core yet empty, leaf node must have only been
999  // partially constructed, so skip merge
1000  if (merge_internal::UnallocatedBuffer<BufferT, ValueT>::isPartiallyConstructed(
1001  mergeLeaf->buffer())) {
1002  continue;
1003  }
1004 
1005  if (mPruneCancelledTiles) {
1006  bool allnegequal = true;
1007  for (Index i = 0 ; i < LeafT::SIZE; i++) {
1008  const ValueT& newValue = mergeLeaf->getValue(i);
1009  const ValueT& oldValue = leaf.getValue(i);
1010  allnegequal &= oldValue == math::negative(newValue);
1011  const bool doMerge = Union ? newValue < oldValue : newValue > oldValue;
1012  if (doMerge) {
1013  leaf.setValueOnly(i, newValue);
1014  leaf.setActiveState(i, mergeLeaf->isValueOn(i));
1015  }
1016  }
1017  if (allnegequal) {
1018  // If two diffed tiles have the same values of opposite signs,
1019  // we know they have both the same distances and gradients.
1020  // Thus they will cancel out.
1021  if (Union) { leaf.fill(math::negative(this->background()), false); }
1022  else { leaf.fill(this->background(), false); }
1023  }
1024 
1025  } else {
1026  for (Index i = 0 ; i < LeafT::SIZE; i++) {
1027  const ValueT& newValue = mergeLeaf->getValue(i);
1028  const ValueT& oldValue = leaf.getValue(i);
1029  const bool doMerge = Union ? newValue < oldValue : newValue > oldValue;
1030  if (doMerge) {
1031  leaf.setValueOnly(i, newValue);
1032  leaf.setActiveState(i, mergeLeaf->isValueOn(i));
1033  }
1034  }
1035  }
1036  }
1037 
1038  return false;
1039 }
1040 
1041 template <typename TreeT, bool Union>
1044 {
1045  // this operator is only intended to be used with foreachTopDown()
1046  OPENVDB_ASSERT(mBackground);
1047  return *mBackground;
1048 }
1049 
1050 
1051 ////////////////////////////////////////
1052 
1053 
1054 template <typename TreeT>
1056 {
1057  // store the background values
1058  if (!mBackground) mBackground = &root.background();
1059  if (!mOtherBackground) mOtherBackground = &mTree.rootPtr()->background();
1060 
1061  // find all tile values in this root and track inside/outside and active state
1062  // note that level sets should never contain active tiles, but we handle them anyway
1063 
1064  constexpr uint8_t ACTIVE_TILE = 0x1;
1065  constexpr uint8_t INSIDE_TILE = 0x2;
1066  constexpr uint8_t CHILD = 0x4;
1067 
1068  auto getTileFlag = [&](auto& valueIter) -> uint8_t
1069  {
1070  uint8_t flag(0);
1071  const ValueT& value = valueIter.getValue();
1072  if (value < zeroVal<ValueT>()) flag |= INSIDE_TILE;
1073  if (valueIter.isValueOn()) flag |= ACTIVE_TILE;
1074  return flag;
1075  };
1076 
1077  // delete any background tiles
1078  root.eraseBackgroundTiles();
1079 
1080  std::unordered_map<Coord, /*flags*/uint8_t> flags;
1081 
1082  if (root.getTableSize() > 0) {
1083  for (auto valueIter = root.cbeginValueAll(); valueIter; ++valueIter) {
1084  const Coord& key = valueIter.getCoord();
1085  const uint8_t flag = getTileFlag(valueIter);
1086  if (flag & INSIDE_TILE) {
1087  flags.insert({key, getTileFlag(valueIter)});
1088  }
1089  }
1090 
1091  for (auto childIter = root.cbeginChildOn(); childIter; ++childIter) {
1092  const Coord& key = childIter.getCoord();
1093  flags.insert({key, CHILD});
1094  }
1095  }
1096 
1097  bool continueRecurse = false;
1098 
1099  const auto* mergeRoot = mTree.rootPtr();
1100 
1101  if (mergeRoot) {
1102  for (auto valueIter = mergeRoot->cbeginValueAll(); valueIter; ++valueIter) {
1103  const Coord& key = valueIter.getCoord();
1104  const uint8_t flag = getTileFlag(valueIter);
1105  if (flag & INSIDE_TILE) {
1106  auto it = flags.find(key);
1107  if (it != flags.end()) {
1108  const bool state = flag & ACTIVE_TILE;
1109  root.addTile(key, this->background(), state);
1110  }
1111  }
1112  }
1113 
1114  for (auto childIter = mergeRoot->cbeginChildOn(); childIter; ++childIter) {
1115  const Coord& key = childIter.getCoord();
1116  auto it = flags.find(key);
1117  if (it != flags.end()) {
1118  const uint8_t otherFlag = it->second;
1119  if (otherFlag & CHILD) {
1120  // if child already exists, merge recursion will need to continue to resolve conflict
1121  continueRecurse = true;
1122  } else if (otherFlag & INSIDE_TILE) {
1123  auto childPtr = mTree.template stealOrDeepCopyNode<typename RootT::ChildNodeType>(key);
1124  if (childPtr) {
1125  childPtr->resetBackground(this->otherBackground(), this->background());
1126  childPtr->negate();
1127  root.addChild(childPtr.release());
1128  }
1129  }
1130  }
1131  }
1132  }
1133 
1134  // finish by removing any background tiles
1135  root.eraseBackgroundTiles();
1136 
1137  return continueRecurse;
1138 }
1139 
1140 template<typename TreeT>
1141 template<typename NodeT>
1142 bool CsgDifferenceOp<TreeT>::operator()(NodeT& node, size_t) const
1143 {
1144  using NonConstNodeT = typename std::remove_const<NodeT>::type;
1145 
1146  using NodeMaskT = typename NodeT::NodeMaskType;
1147 
1148  // store temporary mask to track inside tile state
1149 
1150  NodeMaskT insideTile;
1151  for (auto iter = node.cbeginValueAll(); iter; ++iter) {
1152  if (iter.getValue() < zeroVal<ValueT>()) {
1153  insideTile.setOn(iter.pos());
1154  }
1155  }
1156 
1157  bool continueRecurse = false;
1158 
1159  auto* mergeNode = mTree.template probeConstNode<NonConstNodeT>(node.origin());
1160 
1161  if (!mergeNode) return continueRecurse;
1162 
1163  // iterate over all tiles
1164 
1165  for (auto iter = mergeNode->cbeginValueAll(); iter; ++iter) {
1166  Index pos = iter.pos();
1167  if (iter.getValue() < zeroVal<ValueT>()) {
1168  if (insideTile.isOn(pos) || node.isChildMaskOn(pos)) {
1169  node.addTile(pos, this->background(), iter.isValueOn());
1170  }
1171  }
1172  }
1173 
1174  // iterate over all children
1175 
1176  for (auto iter = mergeNode->cbeginChildOn(); iter; ++iter) {
1177  Index pos = iter.pos();
1178  const Coord& ijk = iter.getCoord();
1179  if (insideTile.isOn(pos)) {
1180  auto childPtr = mTree.template stealOrDeepCopyNode<typename NodeT::ChildNodeType>(ijk);
1181  if (childPtr) {
1182  childPtr->resetBackground(this->otherBackground(), this->background());
1183  childPtr->negate();
1184  node.addChild(childPtr.release());
1185  }
1186  } else if (node.isChildMaskOn(pos)) {
1187  // if both source and target are child nodes, merge recursion needs to continue
1188  // along this branch to resolve the conflict
1189  continueRecurse = true;
1190  }
1191  }
1192 
1193  return continueRecurse;
1194 }
1195 
1196 template <typename TreeT>
1198 {
1199  using LeafT = typename TreeT::LeafNodeType;
1200  using ValueT = typename LeafT::ValueType;
1201  using BufferT = typename LeafT::Buffer;
1202 
1203  // if buffer is not out-of-core and empty, leaf node must have only been
1204  // partially constructed, so allocate and fill with background value
1205 
1206  merge_internal::UnallocatedBuffer<BufferT, ValueT>::allocateAndFill(
1207  leaf.buffer(), this->background());
1208 
1209  const LeafT* mergeLeaf = mTree.template probeConstNode<LeafT>(leaf.origin());
1210  if (!mergeLeaf) return false;
1211 
1212  // if buffer is not out-of-core yet empty, leaf node must have only been
1213  // partially constructed, so skip merge
1214 
1215  if (merge_internal::UnallocatedBuffer<BufferT, ValueT>::isPartiallyConstructed(
1216  mergeLeaf->buffer())) {
1217  return false;
1218  }
1219 
1220  if (mPruneCancelledTiles) {
1221  bool allequal = true;
1222  for (Index i = 0 ; i < LeafT::SIZE; i++) {
1223  const ValueT& aValue = leaf.getValue(i);
1224  ValueT bValue = mergeLeaf->getValue(i);
1225  allequal &= aValue == bValue;
1226  bValue = math::negative(bValue);
1227  if (aValue < bValue) { // a = max(a, -b)
1228  leaf.setValueOnly(i, bValue);
1229  leaf.setActiveState(i, mergeLeaf->isValueOn(i));
1230  }
1231  }
1232  if (allequal) {
1233  // If two diffed tiles have the same values, we know they
1234  // have both the same distances and gradients. Thus they will
1235  // cancel out.
1236  leaf.fill(background(), false);
1237  }
1238  } else {
1239  for (Index i = 0 ; i < LeafT::SIZE; i++) {
1240  const ValueT& aValue = leaf.getValue(i);
1241  ValueT bValue = mergeLeaf->getValue(i);
1242  bValue = math::negative(bValue);
1243  if (aValue < bValue) { // a = max(a, -b)
1244  leaf.setValueOnly(i, bValue);
1245  leaf.setActiveState(i, mergeLeaf->isValueOn(i));
1246  }
1247  }
1248  }
1249 
1250  return false;
1251 }
1252 
1253 template <typename TreeT>
1254 const typename CsgDifferenceOp<TreeT>::ValueT&
1256 {
1257  // this operator is only intended to be used with foreachTopDown()
1258  OPENVDB_ASSERT(mBackground);
1259  return *mBackground;
1260 }
1261 
1262 template <typename TreeT>
1263 const typename CsgDifferenceOp<TreeT>::ValueT&
1265 {
1266  // this operator is only intended to be used with foreachTopDown()
1267  OPENVDB_ASSERT(mOtherBackground);
1268  return *mOtherBackground;
1269 }
1270 
1271 
1272 ////////////////////////////////////////
1273 
1274 
1275 template <typename TreeT>
1276 bool SumMergeOp<TreeT>::operator()(RootT& root, size_t) const
1277 {
1278  using ValueT = typename RootT::ValueType;
1279  using ChildT = typename RootT::ChildNodeType;
1280  using NonConstChildT = typename std::remove_const<ChildT>::type;
1281 
1282  if (this->empty()) return false;
1283 
1284  // store the background value
1285  if (!mBackground) mBackground = &root.background();
1286 
1287  // does the key exist in the root node?
1288  auto keyExistsInRoot = [](const auto& rootToTest, const Coord& key) -> bool
1289  {
1290  return rootToTest.getValueDepth(key) > -1;
1291  };
1292 
1293  constexpr uint8_t TILE = 0x1;
1294  constexpr uint8_t CHILD = 0x2;
1295  constexpr uint8_t TARGET_CHILD = 0x4; // child already exists in the target tree
1296 
1297  std::unordered_map<Coord, /*flags*/uint8_t> children;
1298 
1299  // find all tiles and child nodes in our root
1300 
1301  if (root.getTableSize() > 0) {
1302  for (auto valueIter = root.cbeginValueAll(); valueIter; ++valueIter) {
1303  const Coord& key = valueIter.getCoord();
1304  children.insert({key, TILE});
1305  }
1306 
1307  for (auto childIter = root.cbeginChildOn(); childIter; ++childIter) {
1308  const Coord& key = childIter.getCoord();
1309  children.insert({key, CHILD | TARGET_CHILD});
1310  }
1311  }
1312 
1313  // find all tiles and child nodes in other roots
1314 
1315  for (TreeToMerge<TreeT>& mergeTree : mTreesToMerge) {
1316  const auto* mergeRoot = mergeTree.rootPtr();
1317  if (!mergeRoot) continue;
1318 
1319  for (auto valueIter = mergeRoot->cbeginValueAll(); valueIter; ++valueIter) {
1320  const Coord& key = valueIter.getCoord();
1321  auto it = children.find(key);
1322  if (it == children.end()) {
1323  // if no element with this key, insert it
1324  children.insert({key, TILE});
1325  } else {
1326  // mark as tile
1327  it->second |= TILE;
1328  }
1329  }
1330 
1331  for (auto childIter = mergeRoot->cbeginChildOn(); childIter; ++childIter) {
1332  const Coord& key = childIter.getCoord();
1333  auto it = children.find(key);
1334  if (it == children.end()) {
1335  // if no element with this key, insert it
1336  children.insert({key, CHILD});
1337  } else {
1338  // mark as child
1339  it->second |= CHILD;
1340  }
1341  }
1342  }
1343 
1344  // if any coords do not already exist in the root, insert an inactive background tile
1345 
1346  for (const auto& it : children) {
1347  if (!keyExistsInRoot(root, it.first)) {
1348  root.addTile(it.first, root.background(), false);
1349  }
1350  }
1351 
1352  // for each coord, merge each tile into the root until a child is found, then steal it
1353 
1354  for (const auto& it : children) {
1355 
1356  const Coord& key = it.first;
1357 
1358  // do nothing if the target root already contains a child node,
1359  // merge recursion will need to continue to resolve conflict
1360  if (it.second & TARGET_CHILD) continue;
1361 
1362  ValueT value;
1363  const bool active = root.probeValue(key, value);
1364 
1365  for (TreeToMerge<TreeT>& mergeTree : mTreesToMerge) {
1366  const auto* mergeRoot = mergeTree.rootPtr();
1367  if (!mergeRoot) continue;
1368 
1369  // steal or deep-copy the first child node that is encountered,
1370  // then cease processing of this root node coord as merge recursion
1371  // will need to continue to resolve conflict
1372 
1373  const auto* mergeNode = mergeRoot->template probeConstNode<ChildT>(key);
1374  if (mergeNode) {
1375  auto childPtr = mergeTree.template stealOrDeepCopyNode<ChildT>(key);
1376  if (childPtr) {
1377  // apply tile value and active state to the sub-tree
1378  merge_internal::ApplyTileSumToNodeOp<TreeT> applyOp(value, active);
1379  applyOp.run(*childPtr);
1380  root.addChild(childPtr.release());
1381  }
1382  break;
1383  }
1384 
1385  ValueT mergeValue;
1386  const bool mergeActive = mergeRoot->probeValue(key, mergeValue);
1387 
1388  if (active || mergeActive) {
1389  value += mergeValue;
1390  root.addTile(key, value, true);
1391  } else {
1392  value += mergeValue;
1393  root.addTile(key, value, false);
1394  }
1395 
1396  // reset tile value to zero to prevent it being merged twice
1397  mergeTree.template addTile<NonConstChildT>(key, zeroVal<ValueT>(), false);
1398  }
1399  }
1400 
1401  // set root background to be the sum of all other root backgrounds
1402 
1403  ValueT background = root.background();
1404 
1405  for (TreeToMerge<TreeT>& mergeTree : mTreesToMerge) {
1406  const auto* mergeRoot = mergeTree.rootPtr();
1407  if (!mergeRoot) continue;
1408  background += mergeRoot->background();
1409  }
1410 
1411  root.setBackground(background, /*updateChildNodes=*/false);
1412 
1413  return true;
1414 }
1415 
1416 template<typename TreeT>
1417 template<typename NodeT>
1418 bool SumMergeOp<TreeT>::operator()(NodeT& node, size_t) const
1419 {
1420  using ChildT = typename NodeT::ChildNodeType;
1421  using NonConstNodeT = typename std::remove_const<NodeT>::type;
1422 
1423  if (this->empty()) return false;
1424 
1425  for (TreeToMerge<TreeT>& mergeTree : mTreesToMerge) {
1426  const auto* mergeRoot = mergeTree.rootPtr();
1427  if (!mergeRoot) continue;
1428 
1429  const auto* mergeNode = mergeTree.template probeConstNode<NonConstNodeT>(node.origin());
1430  if (mergeNode) {
1431  // merge node
1432 
1433  for (auto iter = node.beginValueAll(); iter; ++iter) {
1434  if (mergeNode->isChildMaskOn(iter.pos())) {
1435  // steal child node
1436  auto childPtr = mergeTree.template stealOrDeepCopyNode<ChildT>(iter.getCoord());
1437  if (childPtr) {
1438  // apply tile value and active state to the sub-tree
1439  merge_internal::ApplyTileSumToNodeOp<TreeT> applyOp(*iter, iter.isValueOn());
1440  applyOp.run(*childPtr);
1441  node.addChild(childPtr.release());
1442  }
1443  } else {
1444  ValueT mergeValue;
1445  const bool mergeActive = mergeNode->probeValue(iter.getCoord(), mergeValue);
1446  iter.setValue(*iter + mergeValue);
1447  if (mergeActive && !iter.isValueOn()) iter.setValueOn();
1448  }
1449  }
1450 
1451  } else {
1452  // merge tile or background value
1453 
1454  if (mergeTree.hasMask()) {
1455  // if not stealing, test the original tree and if the node exists there, it means it
1456  // has been stolen so don't merge the tile value with this node
1457  const ChildT* originalMergeNode = mergeRoot->template probeConstNode<ChildT>(node.origin());
1458  if (originalMergeNode) continue;
1459  }
1460 
1461  ValueT mergeValue;
1462  const bool mergeActive = mergeRoot->probeValue(node.origin(), mergeValue);
1463  for (auto iter = node.beginValueAll(); iter; ++iter) {
1464  iter.setValue(*iter + mergeValue);
1465  if (mergeActive && !iter.isValueOn()) iter.setValueOn();
1466  }
1467  }
1468  }
1469 
1470  return true;
1471 }
1472 
1473 template <typename TreeT>
1474 bool SumMergeOp<TreeT>::operator()(LeafT& leaf, size_t) const
1475 {
1476  using RootT = typename TreeT::RootNodeType;
1477  using RootChildT = typename RootT::ChildNodeType;
1478  using NonConstRootChildT = typename std::remove_const<RootChildT>::type;
1479  using LeafT = typename TreeT::LeafNodeType;
1480  using ValueT = typename LeafT::ValueType;
1481  using BufferT = typename LeafT::Buffer;
1482  using NonConstLeafT = typename std::remove_const<LeafT>::type;
1483 
1484  if (this->empty()) return false;
1485 
1486  const Coord& ijk = leaf.origin();
1487 
1488  // if buffer is not out-of-core and empty, leaf node must have only been
1489  // partially constructed, so allocate and fill with background value
1490 
1491  merge_internal::UnallocatedBuffer<BufferT, ValueT>::allocateAndFill(
1492  leaf.buffer(), this->background());
1493 
1494  auto* data = leaf.buffer().data();
1495 
1496  for (TreeToMerge<TreeT>& mergeTree : mTreesToMerge) {
1497  const RootT* mergeRoot = mergeTree.rootPtr();
1498  if (!mergeRoot) continue;
1499 
1500  const LeafT* mergeLeaf = mergeTree.template probeConstNode<NonConstLeafT>(ijk);
1501  if (mergeLeaf) {
1502  // merge leaf
1503 
1504  // if buffer is not out-of-core yet empty, leaf node must have only been
1505  // partially constructed, so skip merge
1506 
1507  if (merge_internal::UnallocatedBuffer<BufferT, ValueT>::isPartiallyConstructed(
1508  mergeLeaf->buffer())) {
1509  return false;
1510  }
1511 
1512  for (Index i = 0; i < LeafT::SIZE; ++i) {
1513  data[i] += mergeLeaf->getValue(i);
1514  }
1515 
1516  leaf.getValueMask() |= mergeLeaf->getValueMask();
1517  } else {
1518  // merge root tile or background value (as the merge leaf does not exist)
1519 
1520  if (mergeTree.hasMask()) {
1521  // if not stealing, test the original tree and if the leaf exists there, it means it
1522  // has been stolen so don't merge the tile value with this leaf
1523  const LeafT* originalMergeLeaf = mergeRoot->template probeConstNode<NonConstLeafT>(ijk);
1524  if (originalMergeLeaf) continue;
1525  }
1526 
1527  const RootChildT* mergeRootChild = mergeRoot->template probeConstNode<NonConstRootChildT>(ijk);
1528 
1529  ValueT mergeValue;
1530  bool mergeActive = mergeRootChild ?
1531  mergeRootChild->probeValue(ijk, mergeValue) : mergeRoot->probeValue(ijk, mergeValue);
1532 
1533  if (mergeValue != zeroVal<ValueT>()) {
1534  for (Index i = 0; i < LeafT::SIZE; ++i) {
1535  data[i] += mergeValue;
1536  }
1537  }
1538 
1539  if (mergeActive) leaf.setValuesOn();
1540  }
1541  }
1542 
1543  return false;
1544 }
1545 
1546 template <typename TreeT>
1547 const typename SumMergeOp<TreeT>::ValueT&
1549 {
1550  // this operator is only intended to be used with foreachTopDown()
1551  OPENVDB_ASSERT(mBackground);
1552  return *mBackground;
1553 }
1554 
1555 
1556 ////////////////////////////////////////
1557 
1558 // Explicit Template Instantiation
1559 
1560 #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
1561 
1562 #ifdef OPENVDB_INSTANTIATE_MERGE
1564 #endif
1565 
1575 
1585 
1588 
1591 
1594 
1595 #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
1596 
1597 
1598 } // namespace tools
1599 } // namespace OPENVDB_VERSION_NAME
1600 } // namespace openvdb
1601 
1602 #endif // OPENVDB_TOOLS_MERGE_HAS_BEEN_INCLUDED
DynamicNodeManager operator to merge two trees using a CSG difference.
Definition: Merge.h:280
typename TreeT::ValueType ValueT
Definition: Merge.h:337
SumMergeOp(TreeT &tree, TagT tag)
Convenience constructor to sum a single non-const tree with another. This constructor takes a Steal o...
Definition: Merge.h:344
typename TreeT::LeafNodeType LeafT
Definition: Merge.h:197
SumMergeOp(TreesT &trees, TagT tag)
Constructor to sum a container of multiple const or non-const tree pointers. A Steal tag requires a c...
Definition: Merge.h:354
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
typename TreeType::ValueType ValueType
Definition: Merge.h:52
#define OPENVDB_INSTANTIATE_STRUCT
Definition: version.h.in:159
__hostdev__ bool isValid(GridType gridType, GridClass gridClass)
return true if the combination of GridType and GridClass is valid.
Definition: NanoVDB.h:613
CsgUnionOrIntersectionOp(const std::deque< TreeToMerge< TreeT >> &trees)
Constructor to accept a deque of TreeToMerge objects, primarily used when mixing const/non-const tree...
Definition: Merge.h:233
size_t size() const
Return the number of trees being merged.
Definition: Merge.h:379
Definition: NodeManager.h:37
DynamicNodeManager operator used to generate a mask of the input tree, but with dense leaf nodes repl...
Definition: Merge.h:167
TreeToMerge(TreeType &tree, Steal)
Non-const pointer tree constructor for stealing data.
Definition: Merge.h:58
Implementation of a depth-first node visitor.
Tag dispatch class that distinguishes constructors that deep copy.
Definition: Types.h:685
void setPruneCancelledTiles(bool doprune)
Enables immediate pruning of tiles that cancel each other out.
Definition: Merge.h:301
typename std::remove_const< ToType >::type Type
Definition: Types.h:439
Definition: Coord.h:590
DynamicNodeManager operator to merge trees using a sum operation.
Definition: Merge.h:335
MaskPtr(const MaskPtr &other)
Definition: Merge.h:154
MaskTreeType * mask()
Definition: Merge.h:130
size_t size() const
Return the number of trees being merged (only ever 1)
Definition: Merge.h:304
CsgUnionOrIntersectionOp(const std::vector< TreeToMerge< TreeT >> &trees)
Constructor to accept a vector of TreeToMerge objects, primarily used when mixing const/non-const tre...
Definition: Merge.h:227
Index32 Index
Definition: Types.h:54
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:110
TreeToMerge(typename TreeType::Ptr treePtr, Steal)
Non-const shared pointer tree constructor for stealing data.
Definition: Merge.h:61
typename TreeT::LeafNodeType LeafT
Definition: Merge.h:284
OutGridT XformOp & op
Definition: ValueTransformer.h:139
CsgUnionOrIntersectionOp(TreeT &tree, TagT tag)
Convenience constructor to CSG union or intersect a single non-const tree with another. This constructor takes a Steal or DeepCopy tag dispatch class.
Definition: Merge.h:203
bool empty() const
Return true if no trees being merged.
Definition: Merge.h:376
typename MaskT::RootNodeType RootT
Definition: Merge.h:170
MaskTreeType MaskT
Definition: Merge.h:169
typename TreeType::RootNodeType RootNodeType
Definition: Merge.h:51
void reset(typename TreeType::Ptr treePtr, Steal)
Reset the non-const tree shared pointer. This is primarily used to preserve the order of trees to mer...
Definition: Merge.h:421
MaskPtr & operator=(const MaskPtr &other)
Definition: Merge.h:156
CsgUnionOrIntersectionOp(const TreeT &tree, DeepCopy tag)
Convenience constructor to CSG union or intersect a single const tree with another. This constructor requires a DeepCopy tag dispatch class.
Definition: Merge.h:208
void addTile(const Coord &ijk, const ValueType &value, bool active)
Add a tile containing voxel (x, y, z) at the level of NodeT, deleting the existing branch if necessar...
Definition: Merge.h:490
T negative(const T &val)
Return the unary negation of the given value.
Definition: Math.h:128
SumMergeOp(const TreeT &tree, DeepCopy tag)
Convenience constructor to sum a single const tree with another. This constructor requires a DeepCopy...
Definition: Merge.h:348
typename TreeT::RootNodeType RootT
Definition: Merge.h:283
OPENVDB_IMPORT void initialize()
Global registration of native Grid, Transform, Metadata and Point attribute types. Also initializes blosc (if enabled).
#define OPENVDB_ASSERT(X)
Definition: Assert.h:41
SumMergeOp(const std::deque< TreeToMerge< TreeT >> &trees)
Constructor to accept a deque of TreeToMerge objects, primarily used when mixing const/non-const tree...
Definition: Merge.h:372
Wrapper around unique_ptr that deep-copies mask on copy construction.
Definition: Merge.h:146
bool empty() const
Return true if no trees being merged.
Definition: Merge.h:237
Tag dispatch class that distinguishes constructors that steal.
Definition: Types.h:687
typename MaskT::LeafNodeType LeafT
Definition: Merge.h:171
bool empty(const char *str)
tests if a c-string str is empty, that is its first value is &#39;\0&#39;
Definition: Util.h:144
Definition: Exceptions.h:13
TreeToMerge(const TreeType &tree, DeepCopy, bool initialize=true)
Const tree pointer constructor for deep-copying data. As the tree is not mutable and thus cannot be p...
Definition: Merge.h:69
const MaskTreeType * mask() const
Definition: Merge.h:131
typename TreeT::LeafNodeType LeafT
Definition: Merge.h:339
TreeToMerge(TreeType &tree, DeepCopy tag, bool initialize=true)
Non-const tree pointer constructor for deep-copying data. The tree is not intended to be modified so ...
Definition: Merge.h:80
OutGridT const XformOp bool bool
Definition: ValueTransformer.h:609
CsgDifferenceOp(TreeToMerge< TreeT > &tree)
Constructor to CSG difference the tree in a TreeToMerge object from another.
Definition: Merge.h:298
OPENVDB_AX_API void run(const char *ax, openvdb::GridBase &grid, const AttributeBindings &bindings={})
Run a full AX pipeline (parse, compile and execute) on a single OpenVDB Grid.
typename TreeT::ValueType ValueT
Definition: Merge.h:282
typename TreeT::RootNodeType RootT
Definition: Merge.h:338
uint32_t Index32
Definition: Types.h:52
DynamicNodeManager operator to merge trees using a CSG union or intersection.
Definition: Merge.h:193
CsgDifferenceOp(const TreeT &tree, DeepCopy tag)
Convenience constructor to CSG difference a single const tree from another. This constructor requires...
Definition: Merge.h:294
TreeType * treeToSteal()
Return a pointer to the tree to be stolen.
Definition: Merge.h:89
MaskUnionOp(const TreeT &tree)
Definition: Merge.h:173
Convenience class that contains a pointer to a tree to be stolen or deep copied depending on the tag ...
Definition: Merge.h:48
Visit all nodes that are downstream of a specific node in depth-first order and apply a user-supplied...
Definition: NodeVisitor.h:189
void setPruneCancelledTiles(bool doprune)
Enables immediate pruning of tiles that cancel each other out.
Definition: Merge.h:243
const TreeType * treeToDeepCopy()
Return a pointer to the tree to be deep-copied.
Definition: Merge.h:91
typename TreeT::ValueType ValueT
Definition: Merge.h:195
SumMergeOp(const std::vector< TreeToMerge< TreeT >> &trees)
Constructor to accept a vector of TreeToMerge objects, primarily used when mixing const/non-const tre...
Definition: Merge.h:366
typename TreeT::template ValueConverter< ValueMask >::Type MaskTreeType
Definition: Merge.h:53
CsgUnionOrIntersectionOp(TreesT &trees, TagT tag)
Constructor to CSG union or intersect a container of multiple const or non-const tree pointers...
Definition: Merge.h:215
NodeManager produces linear arrays of all tree nodes allowing for efficient threading and bottom-up p...
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
std::unique_ptr< MaskTreeType > ptr
Definition: Merge.h:148
bool operator()(LeafT &, size_t) const
Definition: Merge.h:177
std::remove_const_t< TreeT > TreeType
Definition: Merge.h:50
CsgDifferenceOp(TreeT &tree, TagT tag)
Convenience constructor to CSG difference a single non-const tree from another. This constructor take...
Definition: Merge.h:290
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:218
typename TreeT::RootNodeType RootT
Definition: Merge.h:196
size_t size() const
Return the number of trees being merged.
Definition: Merge.h:240
Definition: Exceptions.h:63