OpenVDB 12.0.0
 
Loading...
Searching...
No Matches
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
27namespace openvdb {
29namespace OPENVDB_VERSION_NAME {
30namespace 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.
47template <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.
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.
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
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
133private:
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
145template <typename TreeT>
146struct 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
166template <typename TreeT>
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; }
178private:
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.
192template<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
255private:
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
266template <typename TreeT>
267using CsgUnionOp = CsgUnionOrIntersectionOp<TreeT, /*Union=*/true>;
268
269template <typename TreeT>
270using 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.
279template<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.
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
316private:
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().
334template<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
391private:
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
404template<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
414template<typename TreeT>
416{
417 return bool(mMaskTree.ptr);
418}
419
420template<typename TreeT>
421void 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
431template<typename TreeT>
434{
435 return &mTree->root();
436}
437
438template<typename TreeT>
439template<typename NodeT>
440const NodeT*
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
448template<typename TreeT>
449void
451{
452 if (!mSteal) {
453 OPENVDB_ASSERT(this->hasMask());
454 this->mask()->addTile(level, ijk, false, false);
455 }
456}
457
458template<typename TreeT>
459template<typename NodeT>
460std::unique_ptr<NodeT>
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
479template<typename TreeT>
480template<typename NodeT>
481std::unique_ptr<NodeT>
483{
484 return this->stealOrDeepCopyNode<NodeT>(ijk, mTree->root().background());
485}
486
487template<typename TreeT>
488template<typename NodeT>
489void
490TreeToMerge<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
512template <typename TreeT>
513bool 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
545template <typename TreeT>
546template <typename NodeT>
547bool 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
575namespace merge_internal {
576
577
578template <typename BufferT, typename ValueT>
579struct 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
597template <typename BufferT>
598struct 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
608template <Index LEVEL>
609struct 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 {
640 DepthFirstNodeVisitor<NodeT>::visit(node, op);
641 }
642 }
643}; // struct Dispatch
644
645// when LEVEL = 0, do not attempt nested parallelism
646template <>
647struct Dispatch<0>
648{
649 template <typename NodeT, typename OpT>
650 static void run(NodeT& node, OpT& op)
651 {
652 DepthFirstNodeVisitor<NodeT>::visit(node, op);
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
659template <typename TreeT>
660struct 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
699private:
700 ValueT mValue;
701 bool mActive;
702}; // struct ApplyTileSumToNodeOp
703
704
705
706} // namespace merge_internal
707
708
709/// @endcond
710
711////////////////////////////////////////
712
713
714template <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
896template<typename TreeT, bool Union>
897template<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
978template <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
1041template <typename TreeT, bool Union>
1043CsgUnionOrIntersectionOp<TreeT, Union>::background() const
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
1054template <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
1140template<typename TreeT>
1141template<typename NodeT>
1142bool 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
1196template <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
1253template <typename TreeT>
1254const typename CsgDifferenceOp<TreeT>::ValueT&
1255CsgDifferenceOp<TreeT>::background() const
1256{
1257 // this operator is only intended to be used with foreachTopDown()
1258 OPENVDB_ASSERT(mBackground);
1259 return *mBackground;
1260}
1261
1262template <typename TreeT>
1263const typename CsgDifferenceOp<TreeT>::ValueT&
1264CsgDifferenceOp<TreeT>::otherBackground() const
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
1275template <typename TreeT>
1276bool 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
1416template<typename TreeT>
1417template<typename NodeT>
1418bool 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
1473template <typename TreeT>
1474bool 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
1546template <typename TreeT>
1547const typename SumMergeOp<TreeT>::ValueT&
1548SumMergeOp<TreeT>::background() const
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
1566OPENVDB_INSTANTIATE_STRUCT TreeToMerge<MaskTree>;
1567OPENVDB_INSTANTIATE_STRUCT TreeToMerge<BoolTree>;
1568OPENVDB_INSTANTIATE_STRUCT TreeToMerge<FloatTree>;
1569OPENVDB_INSTANTIATE_STRUCT TreeToMerge<DoubleTree>;
1570OPENVDB_INSTANTIATE_STRUCT TreeToMerge<Int32Tree>;
1571OPENVDB_INSTANTIATE_STRUCT TreeToMerge<Int64Tree>;
1572OPENVDB_INSTANTIATE_STRUCT TreeToMerge<Vec3STree>;
1573OPENVDB_INSTANTIATE_STRUCT TreeToMerge<Vec3DTree>;
1574OPENVDB_INSTANTIATE_STRUCT TreeToMerge<Vec3ITree>;
1575
1576OPENVDB_INSTANTIATE_STRUCT SumMergeOp<MaskTree>;
1577OPENVDB_INSTANTIATE_STRUCT SumMergeOp<BoolTree>;
1578OPENVDB_INSTANTIATE_STRUCT SumMergeOp<FloatTree>;
1579OPENVDB_INSTANTIATE_STRUCT SumMergeOp<DoubleTree>;
1580OPENVDB_INSTANTIATE_STRUCT SumMergeOp<Int32Tree>;
1581OPENVDB_INSTANTIATE_STRUCT SumMergeOp<Int64Tree>;
1582OPENVDB_INSTANTIATE_STRUCT SumMergeOp<Vec3STree>;
1583OPENVDB_INSTANTIATE_STRUCT SumMergeOp<Vec3DTree>;
1584OPENVDB_INSTANTIATE_STRUCT SumMergeOp<Vec3ITree>;
1585
1586OPENVDB_INSTANTIATE_STRUCT CsgUnionOrIntersectionOp<FloatTree, true>;
1587OPENVDB_INSTANTIATE_STRUCT CsgUnionOrIntersectionOp<DoubleTree, true>;
1588
1589OPENVDB_INSTANTIATE_STRUCT CsgUnionOrIntersectionOp<FloatTree, false>;
1590OPENVDB_INSTANTIATE_STRUCT CsgUnionOrIntersectionOp<DoubleTree, false>;
1591
1592OPENVDB_INSTANTIATE_STRUCT CsgDifferenceOp<FloatTree>;
1593OPENVDB_INSTANTIATE_STRUCT CsgDifferenceOp<DoubleTree>;
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
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
Implementation of a depth-first node visitor.
static Coord max()
Return the largest possible coordinate.
Definition Coord.h:47
Tag dispatch class that distinguishes constructors that deep copy.
Definition Types.h:685
Definition Exceptions.h:63
Tag dispatch class that distinguishes constructors that steal.
Definition Types.h:687
Signed (x, y, z) 32-bit integer coordinates.
Definition Coord.h:26
Definition NodeManager.h:891
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.
T negative(const T &val)
Return the unary negation of the given value.
Definition Math.h:128
OutGridT const XformOp bool bool
Definition ValueTransformer.h:610
CsgUnionOrIntersectionOp< TreeT, false > CsgIntersectionOp
Definition Merge.h:270
OutGridT XformOp & op
Definition ValueTransformer.h:140
CsgUnionOrIntersectionOp< TreeT, true > CsgUnionOp
Definition Merge.h:267
Definition PointDataGrid.h:170
Index32 Index
Definition Types.h:54
OPENVDB_IMPORT void initialize()
Global registration of native Grid, Transform, Metadata and Point attribute types....
constexpr T zeroVal()
Return the value of type T that corresponds to zero.
Definition Math.h:70
uint32_t Index32
Definition Types.h:52
Definition Exceptions.h:13
Definition Coord.h:590
#define OPENVDB_THROW(exception, message)
Definition Exceptions.h:74
NodeManager produces linear arrays of all tree nodes allowing for efficient threading and bottom-up p...
CsgDifferenceOp(TreeToMerge< TreeT > &tree)
Constructor to CSG difference the tree in a TreeToMerge object from another.
Definition Merge.h:298
size_t size() const
Return the number of trees being merged (only ever 1)
Definition Merge.h:304
bool operator()(RootT &root, size_t idx) const
Definition Merge.h:1055
typename TreeT::RootNodeType RootT
Definition Merge.h:283
typename TreeT::LeafNodeType LeafT
Definition Merge.h:284
typename TreeT::ValueType ValueT
Definition Merge.h:282
CsgDifferenceOp(const TreeT &tree, DeepCopy tag)
Convenience constructor to CSG difference a single const tree from another. This constructor requires...
Definition Merge.h:294
CsgDifferenceOp(TreeT &tree, TagT tag)
Convenience constructor to CSG difference a single non-const tree from another. This constructor take...
Definition Merge.h:290
void setPruneCancelledTiles(bool doprune)
Enables immediate pruning of tiles that cancel each other out.
Definition Merge.h:301
DynamicNodeManager operator to merge trees using a CSG union or intersection.
Definition Merge.h:194
bool operator()(LeafT &leaf, size_t idx) const
Definition Merge.h:979
CsgUnionOrIntersectionOp(const TreeT &tree, DeepCopy tag)
Convenience constructor to CSG union or intersect a single const tree with another....
Definition Merge.h:208
bool operator()(NodeT &node, size_t idx) const
Definition Merge.h:898
size_t size() const
Return the number of trees being merged.
Definition Merge.h:240
bool operator()(RootT &root, size_t idx) const
Definition Merge.h:715
typename TreeT::RootNodeType RootT
Definition Merge.h:196
typename TreeT::LeafNodeType LeafT
Definition Merge.h:197
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
bool empty() const
Return true if no trees being merged.
Definition Merge.h:237
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
CsgUnionOrIntersectionOp(TreeT &tree, TagT tag)
Convenience constructor to CSG union or intersect a single non-const tree with another....
Definition Merge.h:203
typename TreeT::ValueType ValueT
Definition Merge.h:195
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
void setPruneCancelledTiles(bool doprune)
Enables immediate pruning of tiles that cancel each other out.
Definition Merge.h:243
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
size_t size() const
Return the number of trees being merged.
Definition Merge.h:379
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
bool operator()(RootT &root, size_t idx) const
Definition Merge.h:1276
typename TreeT::RootNodeType RootT
Definition Merge.h:338
typename TreeT::LeafNodeType LeafT
Definition Merge.h:339
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
bool empty() const
Return true if no trees being merged.
Definition Merge.h:376
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::ValueType ValueT
Definition Merge.h:337
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
std::unique_ptr< MaskTreeType > ptr
Definition Merge.h:148
MaskPtr & operator=(MaskPtr &&other)=default
MaskPtr(const MaskPtr &other)
Definition Merge.h:154
MaskPtr & operator=(const MaskPtr &other)
Definition Merge.h:156
DynamicNodeManager operator used to generate a mask of the input tree, but with dense leaf nodes repl...
Definition Merge.h:168
typename MaskT::LeafNodeType LeafT
Definition Merge.h:171
MaskUnionOp(const TreeT &tree)
Definition Merge.h:173
bool operator()(LeafT &, size_t) const
Definition Merge.h:177
MaskTreeType MaskT
Definition Merge.h:169
typename MaskT::RootNodeType RootT
Definition Merge.h:170
bool operator()(RootT &root, size_t) const
Definition Merge.h:513
Convenience class that contains a pointer to a tree to be stolen or deep copied depending on the tag ...
Definition Merge.h:49
TreeToMerge(typename TreeType::Ptr treePtr, Steal)
Non-const shared pointer tree constructor for stealing data.
Definition Merge.h:61
std::remove_const_t< TreeT > TreeType
Definition Merge.h:50
typename TreeType::ValueType ValueType
Definition Merge.h:52
bool hasMask() const
Definition Merge.h:415
MaskTreeType * mask()
Definition Merge.h:130
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
typename TreeType::RootNodeType RootNodeType
Definition Merge.h:51
typename TreeT::template ValueConverter< ValueMask >::Type MaskTreeType
Definition Merge.h:53
void initializeMask()
Definition Merge.h:405
std::unique_ptr< NodeT > stealOrDeepCopyNode(const Coord &ijk, const ValueType &value)
Return a pointer to the node of type NodeT that contains voxel (x, y, z). If the tree is non-const,...
Definition Merge.h:461
const MaskTreeType * mask() const
Definition Merge.h:131
std::unique_ptr< NodeT > stealOrDeepCopyNode(const Coord &ijk)
Return a pointer to the node of type NodeT that contains voxel (x, y, z). If the tree is non-const,...
Definition Merge.h:482
void pruneMask(Index level, const Coord &ijk)
Prune the mask and remove the node associated with this coord.
Definition Merge.h:450
const TreeType * treeToDeepCopy()
Return a pointer to the tree to be deep-copied.
Definition Merge.h:91
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 RootNodeType * rootPtr() const
Retrieve a const pointer to the root node.
Definition Merge.h:433
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
const NodeT * probeConstNode(const Coord &ijk) const
Return a pointer to the node of type NodeT that contains voxel (x, y, z). If no such node exists,...
Definition Merge.h:441
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
TreeToMerge(TreeType &tree, Steal)
Non-const pointer tree constructor for stealing data.
Definition Merge.h:58
TreeType * treeToSteal()
Return a pointer to the tree to be stolen.
Definition Merge.h:89
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
#define OPENVDB_INSTANTIATE_STRUCT
Definition version.h.in:159
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:218