OpenVDB  7.2.1
Merge.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 //
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 
19 #include <unordered_map>
20 #include <unordered_set>
21 
22 namespace openvdb {
24 namespace OPENVDB_VERSION_NAME {
25 namespace tools {
26 
27 
42 template <typename TreeT>
44 {
45  using TreeType = std::remove_const_t<TreeT>;
46  using RootNodeType = typename TreeType::RootNodeType;
47  using ValueType = typename TreeType::ValueType;
48  using MaskTreeType = typename TreeT::template ValueConverter<ValueMask>::Type;
49 
50  TreeToMerge() = delete;
51 
54  : mTree(&tree), mSteal(true) { }
56  TreeToMerge(typename TreeType::Ptr treePtr, Steal)
57  : mTreePtr(treePtr), mTree(mTreePtr.get()), mSteal(true) { }
58 
64  TreeToMerge(const TreeType& tree, DeepCopy, bool initialize = true)
65  : mTree(&tree), mSteal(false)
66  {
67  if (mTree && initialize) this->initializeMask();
68  }
69 
75  TreeToMerge(TreeType& tree, DeepCopy tag, bool initialize = true)
76  : TreeToMerge(static_cast<const TreeType&>(tree), tag, initialize) { }
77 
81  void reset(typename TreeType::Ptr treePtr, Steal);
82 
84  TreeType* treeToSteal() { return mSteal ? const_cast<TreeType*>(mTree) : nullptr; }
86  const TreeType* treeToDeepCopy() { return mSteal ? nullptr : mTree; }
87 
89  const RootNodeType* rootPtr() const;
90 
93  template<typename NodeT>
94  const NodeT* probeConstNode(const Coord& ijk) const;
95 
100  template <typename NodeT>
101  std::unique_ptr<NodeT> stealOrDeepCopyNode(const Coord& ijk);
102 
105  template <typename NodeT>
106  void addTile(const Coord& ijk, const ValueType& value, bool active);
107 
108  // build a lightweight mask using a union of the const tree where leaf nodes
109  // are converted into active tiles
110  void initializeMask();
111 
112  // returns true if mask has been initialized
113  bool hasMask() const;
114 
115  // returns MaskTree pointer or nullptr
116  MaskTreeType* mask() { return mMaskTree.ptr.get(); }
117  const MaskTreeType* mask() const { return mMaskTree.ptr.get(); }
118 
119 private:
120  struct MaskPtr;
121  struct MaskUnionOp;
122 
123  typename TreeType::Ptr mTreePtr;
124  const TreeType* mTree;
125  MaskPtr mMaskTree;
126  bool mSteal;
127 }; // struct TreeToMerge
128 
129 
131 template <typename TreeT>
132 struct TreeToMerge<TreeT>::MaskPtr
133 {
134  std::unique_ptr<MaskTreeType> ptr;
135 
136  MaskPtr() = default;
137  ~MaskPtr() = default;
138  MaskPtr(MaskPtr&& other) = default;
139  MaskPtr& operator=(MaskPtr&& other) = default;
140  MaskPtr(const MaskPtr& other)
141  : ptr(bool(other.ptr) ? std::make_unique<MaskTreeType>(*other.ptr) : nullptr) { }
142  MaskPtr& operator=(const MaskPtr& other)
143  {
144  ptr.reset(bool(other.ptr) ? std::make_unique<MaskTreeType>(*other.ptr) : nullptr);
145  return *this;
146  }
147 };
148 
151 template <typename TreeT>
152 struct TreeToMerge<TreeT>::MaskUnionOp
153 {
155  using RootT = typename MaskT::RootNodeType;
156  using LeafT = typename MaskT::LeafNodeType;
157 
158  explicit MaskUnionOp(const TreeT& tree) : mTree(tree) { }
159  bool operator()(RootT& root, size_t) const;
160  template<typename NodeT>
161  bool operator()(NodeT& node, size_t) const;
162  bool operator()(LeafT&, size_t) const { return false; }
163 private:
164  const TreeT& mTree;
165 }; // struct TreeToMerge<TreeT>::MaskUnionOp
166 
167 
169 
170 
177 template<typename TreeT, bool Union>
179 {
180  using ValueT = typename TreeT::ValueType;
181  using RootT = typename TreeT::RootNodeType;
182  using LeafT = typename TreeT::LeafNodeType;
183 
187  template <typename TagT>
188  CsgUnionOrIntersectionOp(TreeT& tree, TagT tag) { mTreesToMerge.emplace_back(tree, tag); }
189 
193  CsgUnionOrIntersectionOp(const TreeT& tree, DeepCopy tag) { mTreesToMerge.emplace_back(tree, tag); }
194 
199  template <typename TreesT, typename TagT>
200  CsgUnionOrIntersectionOp(TreesT& trees, TagT tag)
201  {
202  for (auto* tree : trees) {
203  if (tree) {
204  mTreesToMerge.emplace_back(*tree, tag);
205  }
206  }
207  }
208 
212  explicit CsgUnionOrIntersectionOp(const std::vector<TreeToMerge<TreeT>>& trees)
213  : mTreesToMerge(trees) { }
214 
218  explicit CsgUnionOrIntersectionOp(const std::deque<TreeToMerge<TreeT>>& trees)
219  : mTreesToMerge(trees.cbegin(), trees.cend()) { }
220 
222  bool empty() const { return mTreesToMerge.empty(); }
223 
225  size_t size() const { return mTreesToMerge.size(); }
226 
227  // Processes the root node. Required by the NodeManager
228  bool operator()(RootT& root, size_t idx) const;
229 
230  // Processes the internal nodes. Required by the NodeManager
231  template<typename NodeT>
232  bool operator()(NodeT& node, size_t idx) const;
233 
234  // Processes the leaf nodes. Required by the NodeManager
235  bool operator()(LeafT& leaf, size_t idx) const;
236 
237 private:
238  // on processing the root node, the background value is stored, retrieve it
239  // and check that the root node has already been processed
240  const ValueT& background() const;
241 
242  mutable std::vector<TreeToMerge<TreeT>> mTreesToMerge;
243  mutable const ValueT* mBackground = nullptr;
244 }; // struct CsgUnionOrIntersectionOp
245 
246 
247 template <typename TreeT>
248 using CsgUnionOp = CsgUnionOrIntersectionOp<TreeT, /*Union=*/true>;
249 
250 template <typename TreeT>
251 using CsgIntersectionOp = CsgUnionOrIntersectionOp<TreeT, /*Union=*/false>;
252 
253 
257 template<typename TreeT>
259 {
260  using ValueT = typename TreeT::ValueType;
261  using RootT = typename TreeT::RootNodeType;
262  using LeafT = typename TreeT::LeafNodeType;
263 
267  template <typename TagT>
268  CsgDifferenceOp(TreeT& tree, TagT tag) : mTree(tree, tag) { }
272  CsgDifferenceOp(const TreeT& tree, DeepCopy tag) : mTree(tree, tag) { }
273 
276  explicit CsgDifferenceOp(TreeToMerge<TreeT>& tree) : mTree(tree) { }
277 
279  size_t size() const { return 1; }
280 
281  // Processes the root node. Required by the NodeManager
282  bool operator()(RootT& root, size_t idx) const;
283 
284  // Processes the internal nodes. Required by the NodeManager
285  template<typename NodeT>
286  bool operator()(NodeT& node, size_t idx) const;
287 
288  // Processes the leaf nodes. Required by the NodeManager
289  bool operator()(LeafT& leaf, size_t idx) const;
290 
291 private:
292  // on processing the root node, the background values are stored, retrieve them
293  // and check that the root nodes have already been processed
294  const ValueT& background() const;
295  const ValueT& otherBackground() const;
296 
297  // note that this vector is copied in NodeTransformer every time a foreach call is made,
298  // however in typical use cases this cost will be dwarfed by the actual merge algorithm
299  mutable TreeToMerge<TreeT> mTree;
300  mutable const ValueT* mBackground = nullptr;
301  mutable const ValueT* mOtherBackground = nullptr;
302 }; // struct CsgDifferenceOp
303 
304 
306 
307 
308 template<typename TreeT>
310 {
311  if (mSteal) return;
312  mMaskTree.ptr.reset(new MaskTreeType);
313  MaskUnionOp op(*mTree);
314  tree::DynamicNodeManager<MaskTreeType, MaskTreeType::RootNodeType::LEVEL-1> manager(*this->mask());
315  manager.foreachTopDown(op);
316 }
317 
318 template<typename TreeT>
320 {
321  return bool(mMaskTree.ptr);
322 }
323 
324 template<typename TreeT>
325 void TreeToMerge<TreeT>::reset(typename TreeType::Ptr treePtr, Steal)
326 {
327  if (!treePtr) {
328  OPENVDB_THROW(RuntimeError, "Cannot reset with empty Tree shared pointer.");
329  }
330  mSteal = true;
331  mTreePtr = treePtr;
332  mTree = mTreePtr.get();
333 }
334 
335 template<typename TreeT>
336 const typename TreeToMerge<TreeT>::RootNodeType*
338 {
339  return &mTree->root();
340 }
341 
342 template<typename TreeT>
343 template<typename NodeT>
344 const NodeT*
346 {
347  // test mutable mask first, node may have already been pruned
348  if (!mSteal && !this->mask()->isValueOn(ijk)) return nullptr;
349  return mTree->template probeConstNode<NodeT>(ijk);
350 }
351 
352 template<typename TreeT>
353 template<typename NodeT>
354 std::unique_ptr<NodeT>
356 {
357  if (mSteal) {
358  TreeType* tree = const_cast<TreeType*>(mTree);
359  return std::unique_ptr<NodeT>(
360  tree->root().template stealNode<NodeT>(ijk, mTree->root().background(), false)
361  );
362  } else {
363  auto* child = this->probeConstNode<NodeT>(ijk);
364  if (child) {
365  assert(this->hasMask());
366  auto result = std::make_unique<NodeT>(*child);
367  // prune mask tree
368  this->mask()->addTile(NodeT::LEVEL, ijk, false, false);
369  return result;
370  }
371  }
372  return std::unique_ptr<NodeT>();
373 }
374 
375 template<typename TreeT>
376 template<typename NodeT>
377 void
378 TreeToMerge<TreeT>::addTile(const Coord& ijk, const ValueType& value, bool active)
379 {
380  // ignore leaf node tiles (values)
381  if (NodeT::LEVEL == 0) return;
382 
383  if (mSteal) {
384  TreeType* tree = const_cast<TreeType*>(mTree);
385  auto* node = tree->template probeNode<NodeT>(ijk);
386  if (node) {
387  const Index pos = NodeT::coordToOffset(ijk);
388  node->addTile(pos, value, active);
389  }
390  } else {
391  auto* node = mTree->template probeConstNode<NodeT>(ijk);
392  // prune mask tree
393  if (node) {
394  assert(this->hasMask());
395  this->mask()->addTile(NodeT::LEVEL, ijk, false, false);
396  }
397  }
398 }
399 
400 
402 
403 
404 template <typename TreeT>
405 bool TreeToMerge<TreeT>::MaskUnionOp::operator()(RootT& root, size_t /*idx*/) const
406 {
407  using ChildT = typename RootT::ChildNodeType;
408 
409  const Index count = mTree.root().childCount();
410 
411  std::vector<std::unique_ptr<ChildT>> children(count);
412 
413  // allocate new root children
414 
415  tbb::parallel_for(
416  tbb::blocked_range<Index>(0, count),
417  [&](tbb::blocked_range<Index>& range)
418  {
419  for (Index i = range.begin(); i < range.end(); i++) {
420  children[i] = std::make_unique<ChildT>(Coord::max(), true, true);
421  }
422  }
423  );
424 
425  // apply origins and add root children to new root node
426 
427  size_t i = 0;
428  for (auto iter = mTree.root().cbeginChildOn(); iter; ++iter) {
429  children[i]->setOrigin(iter->origin());
430  root.addChild(children[i].release());
431  i++;
432  }
433 
434  return true;
435 }
436 
437 template <typename TreeT>
438 template <typename NodeT>
439 bool TreeToMerge<TreeT>::MaskUnionOp::operator()(NodeT& node, size_t /*idx*/) const
440 {
441  using ChildT = typename NodeT::ChildNodeType;
442 
443  const auto* otherNode = mTree.template probeConstNode<NodeT>(node.origin());
444  if (!otherNode) return false;
445 
446  // this mask tree stores active tiles in place of leaf nodes for compactness
447 
448  if (NodeT::LEVEL == 1) {
449  for (auto iter = otherNode->cbeginChildOn(); iter; ++iter) {
450  node.addTile(iter.pos(), true, true);
451  }
452  } else {
453  for (auto iter = otherNode->cbeginChildOn(); iter; ++iter) {
454  auto* child = new ChildT(iter->origin(), true, true);
455  node.addChild(child);
456  }
457  }
458 
459  return true;
460 }
461 
462 
464 
465 
466 namespace merge_internal {
467 
468 
469 template <typename BufferT, typename ValueT>
471 {
472  static void allocateAndFill(BufferT& buffer, const ValueT& background)
473  {
474  if (!buffer.isOutOfCore() && buffer.empty()) {
475  buffer.allocate();
476  buffer.fill(background);
477  }
478  }
479 
480  static bool isPartiallyConstructed(const BufferT& buffer)
481  {
482  return !buffer.isOutOfCore() && buffer.empty();
483  }
484 }; // struct AllocateAndFillBuffer
485 
486 template <typename BufferT>
487 struct UnallocatedBuffer<BufferT, bool>
488 {
489  // do nothing for bool buffers as they cannot be unallocated
490  static void allocateAndFill(BufferT&, const bool&) { }
491  static bool isPartiallyConstructed(const BufferT&) { return false; }
492 }; // struct AllocateAndFillBuffer
493 
494 
495 } // namespace merge_internal
496 
497 
499 
500 
501 template <typename TreeT, bool Union>
503 {
504  if (this->empty()) return false;
505 
506  // store the background value
507  if (!mBackground) mBackground = &root.background();
508 
509  // find all tile values in this root and track inside/outside and active state
510  // note that level sets should never contain active tiles, but we handle them anyway
511 
512  constexpr uint8_t ACTIVE_TILE = 0x1;
513  constexpr uint8_t INSIDE_TILE = 0x2;
514  constexpr uint8_t OUTSIDE_TILE = 0x4;
515 
516  constexpr uint8_t INSIDE_STATE = Union ? INSIDE_TILE : OUTSIDE_TILE;
517  constexpr uint8_t OUTSIDE_STATE = Union ? OUTSIDE_TILE : INSIDE_TILE;
518 
519  const ValueT insideBackground = Union ? -this->background() : this->background();
520  const ValueT outsideBackground = -insideBackground;
521 
522  auto getTileFlag = [&](auto& valueIter) -> uint8_t
523  {
524  uint8_t flag(0);
525  const ValueT& value = valueIter.getValue();
526  if (value < zeroVal<ValueT>()) flag |= INSIDE_TILE;
527  else if (value > zeroVal<ValueT>()) flag |= OUTSIDE_TILE;
528  if (valueIter.isValueOn()) flag |= ACTIVE_TILE;
529  return flag;
530  };
531 
532  std::unordered_map<Coord, /*flags*/uint8_t> tiles;
533 
534  if (root.getTableSize() > 0) {
535  for (auto valueIter = root.cbeginValueAll(); valueIter; ++valueIter) {
536  const Coord& key = valueIter.getCoord();
537  tiles.insert({key, getTileFlag(valueIter)});
538  }
539  }
540 
541  // find all tiles values in other roots and replace outside tiles with inside tiles
542 
543  for (TreeToMerge<TreeT>& mergeTree : mTreesToMerge) {
544  const auto* mergeRoot = mergeTree.rootPtr();
545  if (!mergeRoot) continue;
546  for (auto valueIter = mergeRoot->cbeginValueAll(); valueIter; ++valueIter) {
547  const Coord& key = valueIter.getCoord();
548  auto it = tiles.find(key);
549  if (it == tiles.end()) {
550  // if no tile with this key, insert it
551  tiles.insert({key, getTileFlag(valueIter)});
552  } else {
553  // replace an outside tile with an inside tile
554  const uint8_t flag = it->second;
555  if (flag & OUTSIDE_STATE) {
556  const uint8_t newFlag = getTileFlag(valueIter);
557  if (newFlag & INSIDE_STATE) {
558  it->second = newFlag;
559  }
560  }
561  }
562  }
563  }
564 
565  // insert all inside tiles
566 
567  for (auto it : tiles) {
568  const uint8_t flag = it.second;
569  if (flag & INSIDE_STATE) {
570  const Coord& key = it.first;
571  const bool state = flag & ACTIVE_TILE;
572  root.addTile(key, insideBackground, state);
573  }
574  }
575 
576  std::unordered_set<Coord> children;
577 
578  if (root.getTableSize() > 0) {
579  for (auto childIter = root.cbeginChildOn(); childIter; ++childIter) {
580  const Coord& key = childIter.getCoord();
581  children.insert(key);
582  }
583  }
584 
585  bool continueRecurse = false;
586 
587  // find all children in other roots and insert them if a child or tile with this key
588  // does not already exist or if the child will replace an outside tile
589 
590  for (TreeToMerge<TreeT>& mergeTree : mTreesToMerge) {
591  const auto* mergeRoot = mergeTree.rootPtr();
592  if (!mergeRoot) continue;
593  for (auto childIter = mergeRoot->cbeginChildOn(); childIter; ++childIter) {
594  const Coord& key = childIter.getCoord();
595 
596  // if child already exists, merge recursion will need to continue to resolve conflict
597  if (children.count(key)) {
598  continueRecurse = true;
599  continue;
600  }
601 
602  // if an inside tile exists, do nothing
603  auto it = tiles.find(key);
604  if (it != tiles.end() && it->second == INSIDE_STATE) continue;
605 
606  auto childPtr = mergeTree.template stealOrDeepCopyNode<typename RootT::ChildNodeType>(key);
607  if (childPtr) root.addChild(childPtr.release());
608 
609  children.insert(key);
610  }
611  }
612 
613  // insert all outside tiles that don't replace an inside tile or a child node
614 
615  for (auto it : tiles) {
616  const uint8_t flag = it.second;
617  if (flag & OUTSIDE_STATE) {
618  const Coord& key = it.first;
619  if (!children.count(key)) {
620  const bool state = flag & ACTIVE_TILE;
621  root.addTile(key, outsideBackground, state);
622  }
623  }
624  }
625 
626  return continueRecurse;
627 }
628 
629 template<typename TreeT, bool Union>
630 template<typename NodeT>
632 {
633  using NonConstNodeT = typename std::remove_const<NodeT>::type;
634 
635  if (this->empty()) return false;
636 
637  const ValueT insideBackground = Union ? -this->background() : this->background();
638  const ValueT outsideBackground = -insideBackground;
639 
640  using NodeMaskT = typename NodeT::NodeMaskType;
641 
642  // store temporary masks to track inside and outside tile states
643  NodeMaskT validTile;
644  NodeMaskT invalidTile;
645 
646  auto isValid = [](const ValueT& value)
647  {
648  return Union ? value < zeroVal<ValueT>() : value > zeroVal<ValueT>();
649  };
650 
651  auto isInvalid = [](const ValueT& value)
652  {
653  return Union ? value > zeroVal<ValueT>() : value < zeroVal<ValueT>();
654  };
655 
656  for (auto iter = node.cbeginValueAll(); iter; ++iter) {
657  if (isValid(iter.getValue())) {
658  validTile.setOn(iter.pos());
659  } else if (isInvalid(iter.getValue())) {
660  invalidTile.setOn(iter.pos());
661  }
662  }
663 
664  bool continueRecurse = false;
665 
666  for (TreeToMerge<TreeT>& mergeTree : mTreesToMerge) {
667 
668  auto* mergeNode = mergeTree.template probeConstNode<NonConstNodeT>(node.origin());
669 
670  if (!mergeNode) continue;
671 
672  // iterate over all tiles
673 
674  for (auto iter = mergeNode->cbeginValueAll(); iter; ++iter) {
675  Index pos = iter.pos();
676  // source node contains an inside tile, so ignore
677  if (validTile.isOn(pos)) continue;
678  // this node contains an inside tile, so turn into an inside tile
679  if (isValid(iter.getValue())) {
680  node.addTile(pos, insideBackground, iter.isValueOn());
681  validTile.setOn(pos);
682  }
683  }
684 
685  // iterate over all child nodes
686 
687  for (auto iter = mergeNode->cbeginChildOn(); iter; ++iter) {
688  Index pos = iter.pos();
689  const Coord& ijk = iter.getCoord();
690  // source node contains an inside tile, so ensure other node has no child
691  if (validTile.isOn(pos)) {
692  mergeTree.template addTile<NonConstNodeT>(ijk, outsideBackground, false);
693  } else if (invalidTile.isOn(pos)) {
694  auto childPtr = mergeTree.template stealOrDeepCopyNode<typename NodeT::ChildNodeType>(ijk);
695  if (childPtr) node.addChild(childPtr.release());
696  invalidTile.setOff(pos);
697  } else {
698  // if both source and target are child nodes, merge recursion needs to continue
699  // along this branch to resolve the conflict
700  continueRecurse = true;
701  }
702  }
703  }
704 
705  return continueRecurse;
706 }
707 
708 template <typename TreeT, bool Union>
710 {
711  using LeafT = typename TreeT::LeafNodeType;
712  using ValueT = typename LeafT::ValueType;
713  using BufferT = typename LeafT::Buffer;
714 
715  if (this->empty()) return false;
716 
717  const ValueT background = Union ? this->background() : -this->background();
718 
719  // if buffer is not out-of-core and empty, leaf node must have only been
720  // partially constructed, so allocate and fill with background value
721 
723  leaf.buffer(), background);
724 
725  for (TreeToMerge<TreeT>& mergeTree : mTreesToMerge) {
726  const LeafT* mergeLeaf = mergeTree.template probeConstNode<LeafT>(leaf.origin());
727  if (!mergeLeaf) continue;
728  // if buffer is not out-of-core yet empty, leaf node must have only been
729  // partially constructed, so skip merge
731  mergeLeaf->buffer())) {
732  continue;
733  }
734 
735  for (Index i = 0 ; i < LeafT::SIZE; i++) {
736  const ValueT& newValue = mergeLeaf->getValue(i);
737  const bool doMerge = Union ? newValue < leaf.getValue(i) : newValue > leaf.getValue(i);
738  if (doMerge) {
739  leaf.setValueOnly(i, newValue);
740  leaf.setActiveState(i, mergeLeaf->isValueOn(i));
741  }
742  }
743  }
744 
745  return false;
746 }
747 
748 template <typename TreeT, bool Union>
751 {
752  // this operator is only intended to be used with foreachTopDown()
753  assert(mBackground);
754  return *mBackground;
755 }
756 
757 
759 
760 
761 template <typename TreeT>
762 bool CsgDifferenceOp<TreeT>::operator()(RootT& root, size_t) const
763 {
764  // store the background values
765  if (!mBackground) mBackground = &root.background();
766  if (!mOtherBackground) mOtherBackground = &mTree.rootPtr()->background();
767 
768  // find all tile values in this root and track inside/outside and active state
769  // note that level sets should never contain active tiles, but we handle them anyway
770 
771  constexpr uint8_t ACTIVE_TILE = 0x1;
772  constexpr uint8_t INSIDE_TILE = 0x2;
773  constexpr uint8_t CHILD = 0x4;
774 
775  auto getTileFlag = [&](auto& valueIter) -> uint8_t
776  {
777  uint8_t flag(0);
778  const ValueT& value = valueIter.getValue();
779  if (value < zeroVal<ValueT>()) flag |= INSIDE_TILE;
780  if (valueIter.isValueOn()) flag |= ACTIVE_TILE;
781  return flag;
782  };
783 
784  std::unordered_map<Coord, /*flags*/uint8_t> flags;
785 
786  if (root.getTableSize() > 0) {
787  for (auto valueIter = root.cbeginValueAll(); valueIter; ++valueIter) {
788  const Coord& key = valueIter.getCoord();
789  const uint8_t flag = getTileFlag(valueIter);
790  if (flag & INSIDE_TILE) {
791  flags.insert({key, getTileFlag(valueIter)});
792  }
793  }
794 
795  for (auto childIter = root.cbeginChildOn(); childIter; ++childIter) {
796  const Coord& key = childIter.getCoord();
797  flags.insert({key, CHILD});
798  }
799  }
800 
801  bool continueRecurse = false;
802 
803  const auto* mergeRoot = mTree.rootPtr();
804 
805  if (mergeRoot) {
806  for (auto valueIter = mergeRoot->cbeginValueAll(); valueIter; ++valueIter) {
807  const Coord& key = valueIter.getCoord();
808  const uint8_t flag = getTileFlag(valueIter);
809  if (flag & INSIDE_TILE) {
810  auto it = flags.find(key);
811  if (it != flags.end()) {
812  const bool state = flag & ACTIVE_TILE;
813  root.addTile(key, this->background(), state);
814  }
815  }
816  }
817 
818  for (auto childIter = mergeRoot->cbeginChildOn(); childIter; ++childIter) {
819  const Coord& key = childIter.getCoord();
820  auto it = flags.find(key);
821  if (it != flags.end()) {
822  const uint8_t otherFlag = it->second;
823  if (otherFlag & CHILD) {
824  // if child already exists, merge recursion will need to continue to resolve conflict
825  continueRecurse = true;
826  } else if (otherFlag & INSIDE_TILE) {
827  auto childPtr = mTree.template stealOrDeepCopyNode<typename RootT::ChildNodeType>(key);
828  if (childPtr) {
829  childPtr->resetBackground(this->otherBackground(), this->background());
830  childPtr->negate();
831  root.addChild(childPtr.release());
832  }
833  }
834  }
835  }
836  }
837 
838  return continueRecurse;
839 }
840 
841 template<typename TreeT>
842 template<typename NodeT>
843 bool CsgDifferenceOp<TreeT>::operator()(NodeT& node, size_t) const
844 {
845  using NonConstNodeT = typename std::remove_const<NodeT>::type;
846 
847  using NodeMaskT = typename NodeT::NodeMaskType;
848 
849  // store temporary mask to track inside tile state
850 
851  NodeMaskT insideTile;
852  for (auto iter = node.cbeginValueAll(); iter; ++iter) {
853  if (iter.getValue() < zeroVal<ValueT>()) {
854  insideTile.setOn(iter.pos());
855  }
856  }
857 
858  bool continueRecurse = false;
859 
860  auto* mergeNode = mTree.template probeConstNode<NonConstNodeT>(node.origin());
861 
862  if (!mergeNode) return continueRecurse;
863 
864  // iterate over all tiles
865 
866  for (auto iter = mergeNode->cbeginValueAll(); iter; ++iter) {
867  Index pos = iter.pos();
868  if (iter.getValue() < zeroVal<ValueT>()) {
869  if (insideTile.isOn(pos) || node.isChildMaskOn(pos)) {
870  node.addTile(pos, this->background(), iter.isValueOn());
871  }
872  }
873  }
874 
875  // iterate over all children
876 
877  for (auto iter = mergeNode->cbeginChildOn(); iter; ++iter) {
878  Index pos = iter.pos();
879  const Coord& ijk = iter.getCoord();
880  if (insideTile.isOn(pos)) {
881  auto childPtr = mTree.template stealOrDeepCopyNode<typename NodeT::ChildNodeType>(ijk);
882  if (childPtr) {
883  childPtr->resetBackground(this->otherBackground(), this->background());
884  childPtr->negate();
885  node.addChild(childPtr.release());
886  }
887  } else if (node.isChildMaskOn(pos)) {
888  // if both source and target are child nodes, merge recursion needs to continue
889  // along this branch to resolve the conflict
890  continueRecurse = true;
891  }
892  }
893 
894  return continueRecurse;
895 }
896 
897 template <typename TreeT>
898 bool CsgDifferenceOp<TreeT>::operator()(LeafT& leaf, size_t) const
899 {
900  using LeafT = typename TreeT::LeafNodeType;
901  using ValueT = typename LeafT::ValueType;
902  using BufferT = typename LeafT::Buffer;
903 
904  // if buffer is not out-of-core and empty, leaf node must have only been
905  // partially constructed, so allocate and fill with background value
906 
908  leaf.buffer(), this->background());
909 
910  const LeafT* mergeLeaf = mTree.template probeConstNode<LeafT>(leaf.origin());
911  if (!mergeLeaf) return false;
912 
913  // if buffer is not out-of-core yet empty, leaf node must have only been
914  // partially constructed, so skip merge
915 
917  mergeLeaf->buffer())) {
918  return false;
919  }
920 
921  for (Index i = 0 ; i < LeafT::SIZE; i++) {
922  const ValueT& aValue = leaf.getValue(i);
923  ValueT bValue = math::negative(mergeLeaf->getValue(i));
924  if (aValue < bValue) { // a = max(a, -b)
925  leaf.setValueOnly(i, bValue);
926  leaf.setActiveState(i, mergeLeaf->isValueOn(i));
927  }
928  }
929 
930  return false;
931 }
932 
933 template <typename TreeT>
934 const typename CsgDifferenceOp<TreeT>::ValueT&
936 {
937  // this operator is only intended to be used with foreachTopDown()
938  assert(mBackground);
939  return *mBackground;
940 }
941 
942 template <typename TreeT>
943 const typename CsgDifferenceOp<TreeT>::ValueT&
944 CsgDifferenceOp<TreeT>::otherBackground() const
945 {
946  // this operator is only intended to be used with foreachTopDown()
947  assert(mOtherBackground);
948  return *mOtherBackground;
949 }
950 
951 
952 } // namespace tools
953 } // namespace OPENVDB_VERSION_NAME
954 } // namespace openvdb
955 
956 #endif // OPENVDB_TOOLS_MERGE_HAS_BEEN_INCLUDED
Types.h
openvdb::v7_2::tools::CsgDifferenceOp::RootT
typename TreeT::RootNodeType RootT
Definition: Merge.h:261
openvdb::v7_2::tools::TreeToMerge::MaskPtr::MaskPtr
MaskPtr(const MaskPtr &other)
Definition: Merge.h:140
NodeManager.h
NodeManager produces linear arrays of all tree nodes allowing for efficient threading and bottom-up p...
openvdb::v7_2::tools::CsgUnionOrIntersectionOp::size
size_t size() const
Return the number of trees being merged.
Definition: Merge.h:225
openvdb::v7_2::math::Coord
Signed (x, y, z) 32-bit integer coordinates.
Definition: Coord.h:26
openvdb::v7_2::tools::TreeToMerge::reset
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:325
openvdb::v7_2::tools::CsgUnionOrIntersectionOp::RootT
typename TreeT::RootNodeType RootT
Definition: Merge.h:181
openvdb::v7_2::tools::CsgDifferenceOp::CsgDifferenceOp
CsgDifferenceOp(TreeToMerge< TreeT > &tree)
Constructor to CSG difference the tree in a TreeToMerge object from another.
Definition: Merge.h:276
openvdb::v7_2::tools::TreeToMerge::MaskPtr::MaskPtr
MaskPtr()=default
openvdb::v7_2::tools::merge_internal::UnallocatedBuffer
Definition: Merge.h:471
openvdb::v7_2::tools::TreeToMerge::ValueType
typename TreeType::ValueType ValueType
Definition: Merge.h:47
openvdb::v7_2::tools::TreeToMerge::RootNodeType
typename TreeType::RootNodeType RootNodeType
Definition: Merge.h:46
openvdb::v7_2::tools::CsgUnionOrIntersectionOp::CsgUnionOrIntersectionOp
CsgUnionOrIntersectionOp(const TreeT &tree, DeepCopy tag)
Convenience constructor to CSG union or intersect a single const tree with another....
Definition: Merge.h:193
openvdb::v7_2::math::negative
T negative(const T &val)
Return the unary negation of the given value.
Definition: Math.h:117
openvdb::v7_2::tools::CsgUnionOrIntersectionOp::empty
bool empty() const
Return true if no trees being merged.
Definition: Merge.h:222
openvdb::v7_2::tools::CsgDifferenceOp::CsgDifferenceOp
CsgDifferenceOp(const TreeT &tree, DeepCopy tag)
Convenience constructor to CSG difference a single const tree from another. This constructor requires...
Definition: Merge.h:272
openvdb::v7_2::tools::TreeToMerge::treeToSteal
TreeType * treeToSteal()
Return a pointer to the tree to be stolen.
Definition: Merge.h:84
openvdb::v7_2::tools::TreeToMerge::TreeType
std::remove_const_t< TreeT > TreeType
Definition: Merge.h:45
openvdb::v7_2::tools::TreeToMerge::TreeToMerge
TreeToMerge(TreeType &tree, Steal)
Non-const pointer tree constructor for stealing data.
Definition: Merge.h:53
openvdb::v7_2::tools::TreeToMerge::mask
MaskTreeType * mask()
Definition: Merge.h:116
openvdb::v7_2::tools::TreeToMerge::treeToDeepCopy
const TreeType * treeToDeepCopy()
Return a pointer to the tree to be deep-copied.
Definition: Merge.h:86
openvdb::v7_2::tools::TreeToMerge::TreeToMerge
TreeToMerge()=delete
openvdb::v7_2::tools::TreeToMerge::MaskTreeType
typename TreeT::template ValueConverter< ValueMask >::Type MaskTreeType
Definition: Merge.h:48
openvdb::v7_2::tools::TreeToMerge::MaskPtr::~MaskPtr
~MaskPtr()=default
OPENVDB_THROW
#define OPENVDB_THROW(exception, message)
Definition: openvdb/Exceptions.h:82
Platform.h
Grid.h
openvdb::v7_2::tools::CsgUnionOrIntersectionOp::CsgUnionOrIntersectionOp
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:212
openvdb::v7_2::tools::TreeToMerge::MaskUnionOp::operator()
bool operator()(LeafT &, size_t) const
Definition: Merge.h:162
openvdb::v7_2::Steal
Tag dispatch class that distinguishes constructors that steal.
Definition: openvdb/Types.h:546
openvdb::v7_2::tools::TreeToMerge::mask
const MaskTreeType * mask() const
Definition: Merge.h:117
openvdb::v7_2::tools::TreeToMerge::MaskPtr::ptr
std::unique_ptr< MaskTreeType > ptr
Definition: Merge.h:134
openvdb::v7_2::tools::merge_internal::UnallocatedBuffer::isPartiallyConstructed
static bool isPartiallyConstructed(const BufferT &buffer)
Definition: Merge.h:480
openvdb::v7_2::tools::CsgDifferenceOp
DynamicNodeManager operator to merge two trees using a CSG difference.
Definition: Merge.h:259
openvdb::v7_2::tools::TreeToMerge::MaskUnionOp::LeafT
typename MaskT::LeafNodeType LeafT
Definition: Merge.h:156
openvdb::v7_2::tools::CsgUnionOrIntersectionOp::LeafT
typename TreeT::LeafNodeType LeafT
Definition: Merge.h:182
openvdb::v7_2::tools::merge_internal::UnallocatedBuffer< BufferT, bool >::isPartiallyConstructed
static bool isPartiallyConstructed(const BufferT &)
Definition: Merge.h:491
openvdb::v7_2::tools::CsgUnionOrIntersectionOp::ValueT
typename TreeT::ValueType ValueT
Definition: Merge.h:180
openvdb::v7_2::tools::CsgUnionOrIntersectionOp
DynamicNodeManager operator to merge trees using a CSG union or intersection.
Definition: Merge.h:179
openvdb::v7_2::tools::CsgUnionOrIntersectionOp::CsgUnionOrIntersectionOp
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:218
OPENVDB_USE_VERSION_NAMESPACE
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:147
openvdb::v7_2::tools::CsgDifferenceOp::LeafT
typename TreeT::LeafNodeType LeafT
Definition: Merge.h:262
openvdb::v7_2::tools::merge_internal::UnallocatedBuffer< BufferT, bool >::allocateAndFill
static void allocateAndFill(BufferT &, const bool &)
Definition: Merge.h:490
openvdb::v7_2::tools::TreeToMerge::MaskUnionOp::MaskT
MaskTreeType MaskT
Definition: Merge.h:154
openvdb::v7_2::tools::TreeToMerge::TreeToMerge
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:64
openvdb::v7_2::tools::TreeToMerge::MaskPtr::operator=
MaskPtr & operator=(MaskPtr &&other)=default
std
Definition: Coord.h:587
openvdb::v7_2::tree::DynamicNodeManager
Definition: NodeManager.h:858
openvdb::v7_2::initialize
OPENVDB_IMPORT void initialize()
Global registration of basic types.
openvdb::v7_2::tools::merge_internal::UnallocatedBuffer::allocateAndFill
static void allocateAndFill(BufferT &buffer, const ValueT &background)
Definition: Merge.h:472
openvdb::v7_2::tools::TreeToMerge::TreeToMerge
TreeToMerge(typename TreeType::Ptr treePtr, Steal)
Non-const shared pointer tree constructor for stealing data.
Definition: Merge.h:56
openvdb::v7_2::tools::TreeToMerge::MaskUnionOp::RootT
typename MaskT::RootNodeType RootT
Definition: Merge.h:155
OPENVDB_VERSION_NAME
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:95
openvdb::v7_2::tools::CsgDifferenceOp::ValueT
typename TreeT::ValueType ValueT
Definition: Merge.h:260
openvdb::v7_2::tools::CsgDifferenceOp::size
size_t size() const
Return the number of trees being merged (only ever 1)
Definition: Merge.h:279
openvdb::v7_2::RuntimeError
Definition: openvdb/Exceptions.h:63
openvdb::v7_2::tools::TreeToMerge
Convenience class that contains a pointer to a tree to be stolen or deep copied depending on the tag ...
Definition: Merge.h:44
openvdb::v7_2::tools::TreeToMerge::MaskUnionOp::MaskUnionOp
MaskUnionOp(const TreeT &tree)
Definition: Merge.h:158
openvdb::v7_2::tools::TreeToMerge::TreeToMerge
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:75
openvdb
Definition: openvdb/Exceptions.h:13
openvdb::v7_2::DeepCopy
Tag dispatch class that distinguishes constructors that deep copy.
Definition: openvdb/Types.h:544
openvdb::v7_2::tools::TreeToMerge::MaskPtr
Wrapper around unique_ptr that deep-copies mask on copy construction.
Definition: Merge.h:133
Exceptions.h
openvdb::v7_2::tools::CsgUnionOrIntersectionOp::CsgUnionOrIntersectionOp
CsgUnionOrIntersectionOp(TreesT &trees, TagT tag)
Constructor to CSG union or intersect a container of multiple const or non-const tree pointers....
Definition: Merge.h:200
openvdb::v7_2::tools::CsgDifferenceOp::CsgDifferenceOp
CsgDifferenceOp(TreeT &tree, TagT tag)
Convenience constructor to CSG difference a single non-const tree from another. This constructor take...
Definition: Merge.h:268
openvdb::v7_2::Index
Index32 Index
Definition: openvdb/Types.h:32
openvdb::v7_2::tools::CsgUnionOrIntersectionOp::CsgUnionOrIntersectionOp
CsgUnionOrIntersectionOp(TreeT &tree, TagT tag)
Convenience constructor to CSG union or intersect a single non-const tree with another....
Definition: Merge.h:188
openvdb::v7_2::tools::TreeToMerge::MaskPtr::operator=
MaskPtr & operator=(const MaskPtr &other)
Definition: Merge.h:142
openvdb::v7_2::tools::TreeToMerge::MaskPtr::MaskPtr
MaskPtr(MaskPtr &&other)=default
openvdb::v7_2::tools::TreeToMerge::MaskUnionOp
DynamicNodeManager operator used to generate a mask of the input tree, but with dense leaf nodes repl...
Definition: Merge.h:153