OpenVDB: Merge.h Source File

OpenVDB 8.0.0

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>
17 #include <openvdb/tree/NodeManager.h>
18
19 #include <unordered_map>
20 #include <unordered_set>
21
22 namespace openvdb {
23 OPENVDB_USE_VERSION_NAMESPACE
24 namespace OPENVDB_VERSION_NAME {
25 namespace tools {
26
27
42 template <typename TreeT>
43 struct TreeToMerge
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
53 TreeToMerge(TreeType& tree, Steal)
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 {
154 using MaskT = MaskTreeType;
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>
178 struct CsgUnionOrIntersectionOp
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>
258 struct CsgDifferenceOp
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>
309 void TreeToMerge<TreeT>::initializeMask()
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>
319 bool TreeToMerge<TreeT>::hasMask() const
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*
337 TreeToMerge<TreeT>::rootPtr() const
338 {
339 return &mTree->root();
340 }
341
342 template<typename TreeT>
343 template<typename NodeT>
344 const NodeT*
345 TreeToMerge<TreeT>::probeConstNode(const Coord& ijk) const
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>
355 TreeToMerge<TreeT>::stealOrDeepCopyNode(const Coord& ijk)
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>
470 struct UnallocatedBuffer
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>
502 bool CsgUnionOrIntersectionOp<TreeT, Union>::operator()(RootT& root, size_t) const
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>
631 bool CsgUnionOrIntersectionOp<TreeT, Union>::operator()(NodeT& node, size_t) const
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>
709 bool CsgUnionOrIntersectionOp<TreeT, Union>::operator()(LeafT& leaf, size_t) const
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
722 merge_internal::UnallocatedBuffer<BufferT, ValueT>::allocateAndFill(
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
730 if (merge_internal::UnallocatedBuffer<BufferT, ValueT>::isPartiallyConstructed(
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>
749 const typename CsgUnionOrIntersectionOp<TreeT, Union>::ValueT&
750 CsgUnionOrIntersectionOp<TreeT, Union>::background() const
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
907 merge_internal::UnallocatedBuffer<BufferT, ValueT>::allocateAndFill(
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
916 if (merge_internal::UnallocatedBuffer<BufferT, ValueT>::isPartiallyConstructed(
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&
935 CsgDifferenceOp<TreeT>::background() const
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::v8_2::tools::TreeToMerge::MaskUnionOp::MaskUnionOp
MaskUnionOp(const TreeT &tree)
Definition: Merge.h:158
openvdb::v8_2::tools::TreeToMerge::treeToSteal
TreeType * treeToSteal()
Return a pointer to the tree to be stolen.
Definition: Merge.h:84
openvdb::v8_2::tools::CsgUnionOrIntersectionOp::size
size_t size() const
Return the number of trees being merged.
Definition: Merge.h:225
openvdb::v8_2::tools::TreeToMerge::MaskUnionOp::MaskT
MaskTreeType MaskT
Definition: Merge.h:154
NodeManager.h
NodeManager produces linear arrays of all tree nodes allowing for efficient threading and bottom-up p...
openvdb::v8_2::math::Coord
Signed (x, y, z) 32-bit integer coordinates.
Definition: Coord.h:26
openvdb::v8_2::tools::CsgUnionOrIntersectionOp::RootT
typename TreeT::RootNodeType RootT
Definition: Merge.h:181
openvdb::v8_2::tools::TreeToMerge::treeToDeepCopy
const TreeType * treeToDeepCopy()
Return a pointer to the tree to be deep-copied.
Definition: Merge.h:86
openvdb::v8_2::tools::merge_internal::UnallocatedBuffer
Definition: Merge.h:471
openvdb::v8_2::tools::TreeToMerge::MaskUnionOp::RootT
typename MaskT::RootNodeType RootT
Definition: Merge.h:155
openvdb::v8_2::DeepCopy
Tag dispatch class that distinguishes constructors that deep copy.
Definition: openvdb/Types.h:544
openvdb::v8_2::initialize
OPENVDB_IMPORT void initialize()
Global registration of basic types.
openvdb::v8_2::tools::CsgUnionOrIntersectionOp
DynamicNodeManager operator to merge trees using a CSG union or intersection.
Definition: Merge.h:179
openvdb::v8_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::v8_2::tools::TreeToMerge::ValueType
typename TreeType::ValueType ValueType
Definition: Merge.h:47
openvdb::v8_2::RuntimeError
Definition: openvdb/Exceptions.h:63
openvdb::v8_2::tools::CsgUnionOrIntersectionOp::empty
bool empty() const
Return true if no trees being merged.
Definition: Merge.h:222
openvdb::v8_2::tools::TreeToMerge::MaskPtr::ptr
std::unique_ptr< MaskTreeType > ptr
Definition: Merge.h:134
openvdb::v8_2::Index
Index32 Index
Definition: openvdb/Types.h:32
openvdb::v8_2::tools::CsgDifferenceOp::LeafT
typename TreeT::LeafNodeType LeafT
Definition: Merge.h:262
openvdb::v8_2::tools::TreeToMerge::MaskUnionOp::operator()
bool operator()(LeafT &, size_t) const
Definition: Merge.h:162
openvdb::v8_2::tools::TreeToMerge::MaskPtr::MaskPtr
MaskPtr(const MaskPtr &other)
Definition: Merge.h:140
openvdb::v8_2::tools::TreeToMerge::TreeToMerge
TreeToMerge(TreeType &tree, Steal)
Non-const pointer tree constructor for stealing data.
Definition: Merge.h:53
openvdb::v8_2::tools::TreeToMerge::TreeType
std::remove_const_t< TreeT > TreeType
Definition: Merge.h:45
openvdb::v8_2::tools::CsgDifferenceOp::size
size_t size() const
Return the number of trees being merged (only ever 1)
Definition: Merge.h:279
openvdb::v8_2::math::negative
T negative(const T &val)
Return the unary negation of the given value.
Definition: Math.h:127
openvdb::v8_2::tools::TreeToMerge::TreeToMerge
TreeToMerge()=delete
OPENVDB_THROW
#define OPENVDB_THROW(exception, message)
Definition: openvdb/Exceptions.h:74
openvdb::v8_2::tools::TreeToMerge::MaskPtr::MaskPtr
MaskPtr(MaskPtr &&other)=default
openvdb::v8_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
Platform.h
Grid.h
openvdb::v8_2::tools::TreeToMerge::MaskPtr::MaskPtr
MaskPtr()=default
openvdb::v8_2::tools::TreeToMerge::RootNodeType
typename TreeType::RootNodeType RootNodeType
Definition: Merge.h:46
openvdb::v8_2::tools::TreeToMerge::MaskUnionOp::LeafT
typename MaskT::LeafNodeType LeafT
Definition: Merge.h:156
openvdb::v8_2::tools::CsgDifferenceOp::ValueT
typename TreeT::ValueType ValueT
Definition: Merge.h:260
openvdb::v8_2::tools::merge_internal::UnallocatedBuffer< BufferT, bool >::isPartiallyConstructed
static bool isPartiallyConstructed(const BufferT &)
Definition: Merge.h:491
openvdb::v8_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
openvdb::v8_2::tools::TreeToMerge::MaskPtr::operator=
MaskPtr & operator=(MaskPtr &&other)=default
openvdb::v8_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::v8_2::tools::TreeToMerge::MaskTreeType
typename TreeT::template ValueConverter< ValueMask >::Type MaskTreeType
Definition: Merge.h:48
openvdb::v8_2::tools::CsgDifferenceOp::RootT
typename TreeT::RootNodeType RootT
Definition: Merge.h:261
openvdb::v8_2::tools::TreeToMerge::mask
const MaskTreeType * mask() const
Definition: Merge.h:117
openvdb::v8_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::v8_2::tools::merge_internal::UnallocatedBuffer::isPartiallyConstructed
static bool isPartiallyConstructed(const BufferT &buffer)
Definition: Merge.h:480
openvdb::v8_2::tools::CsgUnionOrIntersectionOp::LeafT
typename TreeT::LeafNodeType LeafT
Definition: Merge.h:182
openvdb::v8_2::tools::TreeToMerge::TreeToMerge
TreeToMerge(typename TreeType::Ptr treePtr, Steal)
Non-const shared pointer tree constructor for stealing data.
Definition: Merge.h:56
openvdb::v8_2::tools::TreeToMerge::MaskPtr::operator=
MaskPtr & operator=(const MaskPtr &other)
Definition: Merge.h:142
openvdb::v8_2::tools::CsgUnionOrIntersectionOp::ValueT
typename TreeT::ValueType ValueT
Definition: Merge.h:180
openvdb::v8_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:153
openvdb::v8_2::tools::CsgDifferenceOp::CsgDifferenceOp
CsgDifferenceOp(TreeToMerge< TreeT > &tree)
Constructor to CSG difference the tree in a TreeToMerge object from another.
Definition: Merge.h:276
std
Definition: Coord.h:587
openvdb::v8_2::tools::merge_internal::UnallocatedBuffer< BufferT, bool >::allocateAndFill
static void allocateAndFill(BufferT &, const bool &)
Definition: Merge.h:490
openvdb::v8_2::tools::merge_internal::UnallocatedBuffer::allocateAndFill
static void allocateAndFill(BufferT &buffer, const ValueT &background)
Definition: Merge.h:472
openvdb::v8_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_VERSION_NAME
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:101
openvdb::v8_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::v8_2::tools::CsgDifferenceOp
DynamicNodeManager operator to merge two trees using a CSG difference.
Definition: Merge.h:259
openvdb
Definition: openvdb/Exceptions.h:13
openvdb::v8_2::tools::TreeToMerge::MaskPtr
Wrapper around unique_ptr that deep-copies mask on copy construction.
Definition: Merge.h:133
openvdb::v8_2::Steal
Tag dispatch class that distinguishes constructors that steal.
Definition: openvdb/Types.h:546
Exceptions.h
openvdb::v8_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::v8_2::tree::DynamicNodeManager
Definition: NodeManager.h:858
openvdb::v8_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::v8_2::tools::TreeToMerge::MaskPtr::~MaskPtr
~MaskPtr()=default
openvdb::v8_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::v8_2::tools::TreeToMerge::mask
MaskTreeType * mask()
Definition: Merge.h:116
openvdb::v8_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