1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 //
4 /// @file Composite.h
5 ///
6 /// @brief Functions to efficiently perform various compositing operations on grids
7 ///
8 /// @authors Peter Cucka, Mihai Alden, Ken Museth
9 
10 #ifndef OPENVDB_TOOLS_COMPOSITE_HAS_BEEN_INCLUDED
11 #define OPENVDB_TOOLS_COMPOSITE_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/math/Math.h> // for isExactlyEqual()
18 #include <openvdb/openvdb.h>
19 #include "Merge.h"
20 #include "ValueTransformer.h" // for transformValues()
21 #include "Prune.h"// for prune
22 #include "SignedFloodFill.h" // for signedFloodFill()
23 
24 #include <tbb/blocked_range.h>
25 #include <tbb/parallel_for.h>
26 #include <tbb/parallel_reduce.h>
27 #include <tbb/task_group.h>
28 
29 #include <type_traits>
30 #include <functional>
31 
32 namespace openvdb {
33 OPENVDB_USE_VERSION_NAMESPACE
34 namespace OPENVDB_VERSION_NAME {
35 namespace tools {
36 
37 /// @brief Given two level set grids, replace the A grid with the union of A and B.
38 /// @throw ValueError if the background value of either grid is not greater than zero.
39 /// @note This operation always leaves the B grid empty.
40 template<typename GridOrTreeT>
41 void csgUnion(GridOrTreeT& a, GridOrTreeT& b, bool prune = true);
42 /// @brief Given two level set grids, replace the A grid with the intersection of A and B.
43 /// @throw ValueError if the background value of either grid is not greater than zero.
44 /// @note This operation always leaves the B grid empty.
45 template<typename GridOrTreeT>
46 void csgIntersection(GridOrTreeT& a, GridOrTreeT& b, bool prune = true);
47 /// @brief Given two level set grids, replace the A grid with the difference A / B.
48 /// @throw ValueError if the background value of either grid is not greater than zero.
49 /// @note This operation always leaves the B grid empty.
50 template<typename GridOrTreeT>
51 void csgDifference(GridOrTreeT& a, GridOrTreeT& b, bool prune = true);
52 
53 /// @brief  Threaded CSG union operation that produces a new grid or tree from
54 ///         immutable inputs.
55 /// @return The CSG union of the @a and @b level set inputs.
56 template<typename GridOrTreeT>
57 typename GridOrTreeT::Ptr csgUnionCopy(const GridOrTreeT& a, const GridOrTreeT& b);
58 /// @brief  Threaded CSG intersection operation that produces a new grid or tree from
59 ///         immutable inputs.
60 /// @return The CSG intersection of the @a and @b level set inputs.
61 template<typename GridOrTreeT>
62 typename GridOrTreeT::Ptr csgIntersectionCopy(const GridOrTreeT& a, const GridOrTreeT& b);
63 /// @brief  Threaded CSG difference operation that produces a new grid or tree from
64 ///         immutable inputs.
65 /// @return The CSG difference of the @a and @b level set inputs.
66 template<typename GridOrTreeT>
67 typename GridOrTreeT::Ptr csgDifferenceCopy(const GridOrTreeT& a, const GridOrTreeT& b);
68 
69 /// @brief Given grids A and B, compute max(a, b) per voxel (using sparse traversal).
70 /// Store the result in the A grid and leave the B grid empty.
71 template<typename GridOrTreeT>
72 void compMax(GridOrTreeT& a, GridOrTreeT& b);
73 /// @brief Given grids A and B, compute min(a, b) per voxel (using sparse traversal).
74 /// Store the result in the A grid and leave the B grid empty.
75 template<typename GridOrTreeT>
76 void compMin(GridOrTreeT& a, GridOrTreeT& b);
77 /// @brief Given grids A and B, compute a + b per voxel (using sparse traversal).
78 /// Store the result in the A grid and leave the B grid empty.
79 template<typename GridOrTreeT>
80 void compSum(GridOrTreeT& a, GridOrTreeT& b);
81 /// @brief Given grids A and B, compute a * b per voxel (using sparse traversal).
82 /// Store the result in the A grid and leave the B grid empty.
83 template<typename GridOrTreeT>
84 void compMul(GridOrTreeT& a, GridOrTreeT& b);
85 /// @brief Given grids A and B, compute a / b per voxel (using sparse traversal).
86 /// Store the result in the A grid and leave the B grid empty.
87 template<typename GridOrTreeT>
88 void compDiv(GridOrTreeT& a, GridOrTreeT& b);
89 
90 /// Copy the active voxels of B into A.
91 template<typename GridOrTreeT>
92 void compReplace(GridOrTreeT& a, const GridOrTreeT& b);
93 
94 
95 ////////////////////////////////////////
96 
97 
98 namespace composite {
99 
100 // composite::min() and composite::max() for non-vector types compare with operator<().
101 template<typename T> inline
102 const typename std::enable_if<!VecTraits<T>::IsVec, T>::type& // = T if T is not a vector type
min(const T & a,const T & b)103 min(const T& a, const T& b) { return std::min(a, b); }
104 
105 template<typename T> inline
106 const typename std::enable_if<!VecTraits<T>::IsVec, T>::type&
max(const T & a,const T & b)107 max(const T& a, const T& b) { return std::max(a, b); }
108 
109 
110 // composite::min() and composite::max() for OpenVDB vector types compare by magnitude.
111 template<typename T> inline
112 const typename std::enable_if<VecTraits<T>::IsVec, T>::type& // = T if T is a vector type
min(const T & a,const T & b)113 min(const T& a, const T& b)
114 {
115     const typename T::ValueType aMag = a.lengthSqr(), bMag = b.lengthSqr();
116     return (aMag < bMag ? a : (bMag < aMag ? b : std::min(a, b)));
117 }
118 
119 template<typename T> inline
120 const typename std::enable_if<VecTraits<T>::IsVec, T>::type&
max(const T & a,const T & b)121 max(const T& a, const T& b)
122 {
123     const typename T::ValueType aMag = a.lengthSqr(), bMag = b.lengthSqr();
124     return (aMag < bMag ? b : (bMag < aMag ? a : std::max(a, b)));
125 }
126 
127 
128 template<typename T> inline
129 typename std::enable_if<!std::is_integral<T>::value, T>::type // = T if T is not an integer type
divide(const T & a,const T & b)130 divide(const T& a, const T& b) { return a / b; }
131 
132 template<typename T> inline
133 typename std::enable_if<std::is_integral<T>::value, T>::type // = T if T is an integer type
divide(const T & a,const T & b)134 divide(const T& a, const T& b)
135 {
136     const T zero(0);
137     if (b != zero) return a / b;
138     if (a == zero) return 0;
139     return (a > 0 ? std::numeric_limits<T>::max() : -std::numeric_limits<T>::max());
140 }
141 
142 // If b is true, return a / 1 = a.
143 // If b is false and a is true, return 1 / 0 = inf = MAX_BOOL = 1 = a.
144 // If b is false and a is false, return 0 / 0 = NaN = 0 = a.
divide(bool a,bool)145 inline bool divide(bool a, bool /*b*/) { return a; }
146 
147 
148 /// @cond OPENVDB_DOCS_INTERNAL
149 
150 enum CSGOperation { CSG_UNION, CSG_INTERSECTION, CSG_DIFFERENCE };
151 
152 template<typename TreeType, CSGOperation Operation>
153 struct BuildPrimarySegment
154 {
155     using ValueType = typename TreeType::ValueType;
156     using TreePtrType = typename TreeType::Ptr;
157     using LeafNodeType = typename TreeType::LeafNodeType;
158     using NodeMaskType = typename LeafNodeType::NodeMaskType;
159     using RootNodeType = typename TreeType::RootNodeType;
160     using NodeChainType = typename RootNodeType::NodeChainType;
161     using InternalNodeType = typename NodeChainType::template Get<1>;
162 
BuildPrimarySegmentBuildPrimarySegment163     BuildPrimarySegment(const TreeType& lhs, const TreeType& rhs)
164         : mSegment(new TreeType(lhs.background()))
165         , mLhsTree(&lhs)
166         , mRhsTree(&rhs)
167     {
168     }
169 
operatorBuildPrimarySegment170     void operator()() const
171     {
172         std::vector<const LeafNodeType*> leafNodes;
173 
174         {
175             std::vector<const InternalNodeType*> internalNodes;
176             mLhsTree->getNodes(internalNodes);
177 
178             ProcessInternalNodes op(internalNodes, *mRhsTree, *mSegment, leafNodes);
179             tbb::parallel_reduce(tbb::blocked_range<size_t>(0, internalNodes.size()), op);
180         }
181 
182         ProcessLeafNodes op(leafNodes, *mRhsTree, *mSegment);
183         tbb::parallel_reduce(tbb::blocked_range<size_t>(0, leafNodes.size()), op);
184     }
185 
segmentBuildPrimarySegment186     TreePtrType& segment() { return mSegment; }
187 
188 private:
189 
190     struct ProcessInternalNodes {
191 
ProcessInternalNodesBuildPrimarySegment::ProcessInternalNodes192         ProcessInternalNodes(std::vector<const InternalNodeType*>& lhsNodes,
193             const TreeType& rhsTree, TreeType& outputTree,
194             std::vector<const LeafNodeType*>& outputLeafNodes)
195             : mLhsNodes(lhsNodes.empty() ? nullptr : &lhsNodes.front())
196             , mRhsTree(&rhsTree)
197             , mLocalTree(mRhsTree->background())
198             , mOutputTree(&outputTree)
199             , mLocalLeafNodes()
200             , mOutputLeafNodes(&outputLeafNodes)
201         {
202         }
203 
ProcessInternalNodesBuildPrimarySegment::ProcessInternalNodes204         ProcessInternalNodes(ProcessInternalNodes& other, tbb::split)
205             : mLhsNodes(other.mLhsNodes)
206             , mRhsTree(other.mRhsTree)
207             , mLocalTree(mRhsTree->background())
208             , mOutputTree(&mLocalTree)
209             , mLocalLeafNodes()
210             , mOutputLeafNodes(&mLocalLeafNodes)
211         {
212         }
213 
joinBuildPrimarySegment::ProcessInternalNodes214         void join(ProcessInternalNodes& other)
215         {
216             mOutputTree->merge(*other.mOutputTree);
217             mOutputLeafNodes->insert(mOutputLeafNodes->end(),
218                 other.mOutputLeafNodes->begin(), other.mOutputLeafNodes->end());
219         }
220 
operatorBuildPrimarySegment::ProcessInternalNodes221         void operator()(const tbb::blocked_range<size_t>& range)
222         {
223             tree::ValueAccessor<const TreeType> rhsAcc(*mRhsTree);
224             tree::ValueAccessor<TreeType>       outputAcc(*mOutputTree);
225 
226             std::vector<const LeafNodeType*> tmpLeafNodes;
227 
228             for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
229 
230                 const InternalNodeType& lhsNode = *mLhsNodes[n];
231                 const Coord& ijk = lhsNode.origin();
232                 const InternalNodeType * rhsNode =
233                     rhsAcc.template probeConstNode<InternalNodeType>(ijk);
234 
235                 if (rhsNode) {
236                     lhsNode.getNodes(*mOutputLeafNodes);
237                 } else {
238                     if (Operation == CSG_INTERSECTION) {
239                         if (rhsAcc.getValue(ijk) < ValueType(0.0)) {
240                             tmpLeafNodes.clear();
241                             lhsNode.getNodes(tmpLeafNodes);
242                             for (size_t i = 0, I = tmpLeafNodes.size(); i < I; ++i) {
243                                 outputAcc.addLeaf(new LeafNodeType(*tmpLeafNodes[i]));
244                             }
245                         }
246                     } else { // Union & Difference
247                         if (!(rhsAcc.getValue(ijk) < ValueType(0.0))) {
248                             tmpLeafNodes.clear();
249                             lhsNode.getNodes(tmpLeafNodes);
250                             for (size_t i = 0, I = tmpLeafNodes.size(); i < I; ++i) {
251                                 outputAcc.addLeaf(new LeafNodeType(*tmpLeafNodes[i]));
252                             }
253                         }
254                     }
255                 }
256             } //  end range loop
257         }
258 
259         InternalNodeType const * const * const mLhsNodes;
260         TreeType                 const * const mRhsTree;
261         TreeType                               mLocalTree;
262         TreeType                       * const mOutputTree;
263 
264         std::vector<const LeafNodeType*>         mLocalLeafNodes;
265         std::vector<const LeafNodeType*> * const mOutputLeafNodes;
266     }; // struct ProcessInternalNodes
267 
268     struct ProcessLeafNodes {
269 
ProcessLeafNodesBuildPrimarySegment::ProcessLeafNodes270         ProcessLeafNodes(std::vector<const LeafNodeType*>& lhsNodes,
271             const TreeType& rhsTree, TreeType& output)
272             : mLhsNodes(lhsNodes.empty() ? nullptr : &lhsNodes.front())
273             , mRhsTree(&rhsTree)
274             , mLocalTree(mRhsTree->background())
275             , mOutputTree(&output)
276         {
277         }
278 
ProcessLeafNodesBuildPrimarySegment::ProcessLeafNodes279         ProcessLeafNodes(ProcessLeafNodes& other, tbb::split)
280             : mLhsNodes(other.mLhsNodes)
281             , mRhsTree(other.mRhsTree)
282             , mLocalTree(mRhsTree->background())
283             , mOutputTree(&mLocalTree)
284         {
285         }
286 
joinBuildPrimarySegment::ProcessLeafNodes287         void join(ProcessLeafNodes& rhs) { mOutputTree->merge(*rhs.mOutputTree); }
288 
operatorBuildPrimarySegment::ProcessLeafNodes289         void operator()(const tbb::blocked_range<size_t>& range)
290         {
291             tree::ValueAccessor<const TreeType> rhsAcc(*mRhsTree);
292             tree::ValueAccessor<TreeType>       outputAcc(*mOutputTree);
293 
294             for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
295 
296                 const LeafNodeType& lhsNode = *mLhsNodes[n];
297                 const Coord& ijk = lhsNode.origin();
298 
299                 const LeafNodeType* rhsNodePt = rhsAcc.probeConstLeaf(ijk);
300 
301                 if (rhsNodePt) { // combine overlapping nodes
302 
303                     LeafNodeType* outputNode = outputAcc.touchLeaf(ijk);
304                     ValueType * outputData = outputNode->buffer().data();
305                     NodeMaskType& outputMask = outputNode->getValueMask();
306 
307                     const ValueType * lhsData = lhsNode.buffer().data();
308                     const NodeMaskType& lhsMask = lhsNode.getValueMask();
309 
310                     const ValueType * rhsData = rhsNodePt->buffer().data();
311                     const NodeMaskType& rhsMask = rhsNodePt->getValueMask();
312 
313                     if (Operation == CSG_INTERSECTION) {
314                         for (Index pos = 0; pos < LeafNodeType::SIZE; ++pos) {
315                             const bool fromRhs = lhsData[pos] < rhsData[pos];
316                             outputData[pos] = fromRhs ? rhsData[pos] : lhsData[pos];
317                             outputMask.set(pos, fromRhs ? rhsMask.isOn(pos) : lhsMask.isOn(pos));
318                         }
319                     } else if (Operation == CSG_DIFFERENCE){
320                         for (Index pos = 0; pos < LeafNodeType::SIZE; ++pos) {
321                             const ValueType rhsVal = math::negative(rhsData[pos]);
322                             const bool fromRhs = lhsData[pos] < rhsVal;
323                             outputData[pos] = fromRhs ? rhsVal : lhsData[pos];
324                             outputMask.set(pos, fromRhs ? rhsMask.isOn(pos) : lhsMask.isOn(pos));
325                         }
326                     } else { // Union
327                         for (Index pos = 0; pos < LeafNodeType::SIZE; ++pos) {
328                             const bool fromRhs = lhsData[pos] > rhsData[pos];
329                             outputData[pos] = fromRhs ? rhsData[pos] : lhsData[pos];
330                             outputMask.set(pos, fromRhs ? rhsMask.isOn(pos) : lhsMask.isOn(pos));
331                         }
332                     }
333 
334                 } else {
335                     if (Operation == CSG_INTERSECTION) {
336                         if (rhsAcc.getValue(ijk) < ValueType(0.0)) {
337                             outputAcc.addLeaf(new LeafNodeType(lhsNode));
338                         }
339                     } else { // Union & Difference
340                         if (!(rhsAcc.getValue(ijk) < ValueType(0.0))) {
341                             outputAcc.addLeaf(new LeafNodeType(lhsNode));
342                         }
343                     }
344                 }
345             } //  end range loop
346         }
347 
348         LeafNodeType const * const * const mLhsNodes;
349         TreeType             const * const mRhsTree;
350         TreeType                           mLocalTree;
351         TreeType                   * const mOutputTree;
352     }; // struct ProcessLeafNodes
353 
354     TreePtrType               mSegment;
355     TreeType    const * const mLhsTree;
356     TreeType    const * const mRhsTree;
357 }; // struct BuildPrimarySegment
358 
359 
360 template<typename TreeType, CSGOperation Operation>
361 struct BuildSecondarySegment
362 {
363     using ValueType = typename TreeType::ValueType;
364     using TreePtrType = typename TreeType::Ptr;
365     using LeafNodeType = typename TreeType::LeafNodeType;
366     using NodeMaskType = typename LeafNodeType::NodeMaskType;
367     using RootNodeType = typename TreeType::RootNodeType;
368     using NodeChainType = typename RootNodeType::NodeChainType;
369     using InternalNodeType = typename NodeChainType::template Get<1>;
370 
BuildSecondarySegmentBuildSecondarySegment371     BuildSecondarySegment(const TreeType& lhs, const TreeType& rhs)
372         : mSegment(new TreeType(lhs.background()))
373         , mLhsTree(&lhs)
374         , mRhsTree(&rhs)
375     {
376     }
377 
operatorBuildSecondarySegment378     void operator()() const
379     {
380         std::vector<const LeafNodeType*> leafNodes;
381 
382         {
383             std::vector<const InternalNodeType*> internalNodes;
384             mRhsTree->getNodes(internalNodes);
385 
386             ProcessInternalNodes op(internalNodes, *mLhsTree, *mSegment, leafNodes);
387             tbb::parallel_reduce(tbb::blocked_range<size_t>(0, internalNodes.size()), op);
388         }
389 
390         ProcessLeafNodes op(leafNodes, *mLhsTree, *mSegment);
391         tbb::parallel_reduce(tbb::blocked_range<size_t>(0, leafNodes.size()), op);
392     }
393 
segmentBuildSecondarySegment394     TreePtrType& segment() { return mSegment; }
395 
396 private:
397 
398     struct ProcessInternalNodes {
399 
ProcessInternalNodesBuildSecondarySegment::ProcessInternalNodes400         ProcessInternalNodes(std::vector<const InternalNodeType*>& rhsNodes,
401             const TreeType& lhsTree, TreeType& outputTree,
402             std::vector<const LeafNodeType*>& outputLeafNodes)
403             : mRhsNodes(rhsNodes.empty() ? nullptr : &rhsNodes.front())
404             , mLhsTree(&lhsTree)
405             , mLocalTree(mLhsTree->background())
406             , mOutputTree(&outputTree)
407             , mLocalLeafNodes()
408             , mOutputLeafNodes(&outputLeafNodes)
409         {
410         }
411 
ProcessInternalNodesBuildSecondarySegment::ProcessInternalNodes412         ProcessInternalNodes(ProcessInternalNodes& other, tbb::split)
413             : mRhsNodes(other.mRhsNodes)
414             , mLhsTree(other.mLhsTree)
415             , mLocalTree(mLhsTree->background())
416             , mOutputTree(&mLocalTree)
417             , mLocalLeafNodes()
418             , mOutputLeafNodes(&mLocalLeafNodes)
419         {
420         }
421 
joinBuildSecondarySegment::ProcessInternalNodes422         void join(ProcessInternalNodes& other)
423         {
424             mOutputTree->merge(*other.mOutputTree);
425             mOutputLeafNodes->insert(mOutputLeafNodes->end(),
426                 other.mOutputLeafNodes->begin(), other.mOutputLeafNodes->end());
427         }
428 
operatorBuildSecondarySegment::ProcessInternalNodes429         void operator()(const tbb::blocked_range<size_t>& range)
430         {
431             tree::ValueAccessor<const TreeType> lhsAcc(*mLhsTree);
432             tree::ValueAccessor<TreeType>       outputAcc(*mOutputTree);
433 
434             std::vector<const LeafNodeType*> tmpLeafNodes;
435 
436             for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
437 
438                 const InternalNodeType& rhsNode = *mRhsNodes[n];
439                 const Coord& ijk = rhsNode.origin();
440                 const InternalNodeType * lhsNode =
441                     lhsAcc.template probeConstNode<InternalNodeType>(ijk);
442 
443                 if (lhsNode) {
444                    rhsNode.getNodes(*mOutputLeafNodes);
445                 } else {
446                     if (Operation == CSG_INTERSECTION) {
447                         if (lhsAcc.getValue(ijk) < ValueType(0.0)) {
448                             tmpLeafNodes.clear();
449                             rhsNode.getNodes(tmpLeafNodes);
450                             for (size_t i = 0, I = tmpLeafNodes.size(); i < I; ++i) {
451                                 outputAcc.addLeaf(new LeafNodeType(*tmpLeafNodes[i]));
452                             }
453                         }
454                     } else if (Operation == CSG_DIFFERENCE) {
455                         if (lhsAcc.getValue(ijk) < ValueType(0.0)) {
456                             tmpLeafNodes.clear();
457                             rhsNode.getNodes(tmpLeafNodes);
458                             for (size_t i = 0, I = tmpLeafNodes.size(); i < I; ++i) {
459                                 LeafNodeType* outputNode = new LeafNodeType(*tmpLeafNodes[i]);
460                                 outputNode->negate();
461                                 outputAcc.addLeaf(outputNode);
462                             }
463                         }
464                     } else { // Union
465                         if (!(lhsAcc.getValue(ijk) < ValueType(0.0))) {
466                             tmpLeafNodes.clear();
467                             rhsNode.getNodes(tmpLeafNodes);
468                             for (size_t i = 0, I = tmpLeafNodes.size(); i < I; ++i) {
469                                 outputAcc.addLeaf(new LeafNodeType(*tmpLeafNodes[i]));
470                             }
471                         }
472                     }
473                 }
474             } //  end range loop
475         }
476 
477         InternalNodeType const * const * const mRhsNodes;
478         TreeType                 const * const mLhsTree;
479         TreeType                               mLocalTree;
480         TreeType                       * const mOutputTree;
481 
482         std::vector<const LeafNodeType*>         mLocalLeafNodes;
483         std::vector<const LeafNodeType*> * const mOutputLeafNodes;
484     }; // struct ProcessInternalNodes
485 
486     struct ProcessLeafNodes {
487 
ProcessLeafNodesBuildSecondarySegment::ProcessLeafNodes488         ProcessLeafNodes(std::vector<const LeafNodeType*>& rhsNodes,
489             const TreeType& lhsTree, TreeType& output)
490             : mRhsNodes(rhsNodes.empty() ? nullptr : &rhsNodes.front())
491             , mLhsTree(&lhsTree)
492             , mLocalTree(mLhsTree->background())
493             , mOutputTree(&output)
494         {
495         }
496 
ProcessLeafNodesBuildSecondarySegment::ProcessLeafNodes497         ProcessLeafNodes(ProcessLeafNodes& rhs, tbb::split)
498             : mRhsNodes(rhs.mRhsNodes)
499             , mLhsTree(rhs.mLhsTree)
500             , mLocalTree(mLhsTree->background())
501             , mOutputTree(&mLocalTree)
502         {
503         }
504 
joinBuildSecondarySegment::ProcessLeafNodes505         void join(ProcessLeafNodes& rhs) { mOutputTree->merge(*rhs.mOutputTree); }
506 
operatorBuildSecondarySegment::ProcessLeafNodes507         void operator()(const tbb::blocked_range<size_t>& range)
508         {
509             tree::ValueAccessor<const TreeType> lhsAcc(*mLhsTree);
510             tree::ValueAccessor<TreeType>       outputAcc(*mOutputTree);
511 
512             for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
513 
514                 const LeafNodeType& rhsNode = *mRhsNodes[n];
515                 const Coord& ijk = rhsNode.origin();
516 
517                 const LeafNodeType* lhsNode = lhsAcc.probeConstLeaf(ijk);
518 
519                 if (!lhsNode) {
520                     if (Operation == CSG_INTERSECTION) {
521                         if (lhsAcc.getValue(ijk) < ValueType(0.0)) {
522                             outputAcc.addLeaf(new LeafNodeType(rhsNode));
523                         }
524                     } else if (Operation == CSG_DIFFERENCE) {
525                         if (lhsAcc.getValue(ijk) < ValueType(0.0)) {
526                             LeafNodeType* outputNode = new LeafNodeType(rhsNode);
527                             outputNode->negate();
528                             outputAcc.addLeaf(outputNode);
529                         }
530                     } else { // Union
531                         if (!(lhsAcc.getValue(ijk) < ValueType(0.0))) {
532                             outputAcc.addLeaf(new LeafNodeType(rhsNode));
533                         }
534                     }
535                 }
536             } //  end range loop
537         }
538 
539         LeafNodeType const * const * const mRhsNodes;
540         TreeType             const * const mLhsTree;
541         TreeType                           mLocalTree;
542         TreeType                   * const mOutputTree;
543     }; // struct ProcessLeafNodes
544 
545     TreePtrType               mSegment;
546     TreeType    const * const mLhsTree;
547     TreeType    const * const mRhsTree;
548 }; // struct BuildSecondarySegment
549 
550 
551 template<CSGOperation Operation, typename TreeType>
552 typename TreeType::Ptr
doCSGCopy(const TreeType & lhs,const TreeType & rhs)553 doCSGCopy(const TreeType& lhs, const TreeType& rhs)
554 {
555     BuildPrimarySegment<TreeType, Operation> primary(lhs, rhs);
556     BuildSecondarySegment<TreeType, Operation> secondary(lhs, rhs);
557 
558     // Exploiting nested parallelism
559     tbb::task_group tasks;
560     tasks.run(primary);
561     tasks.run(secondary);
562     tasks.wait();
563 
564     primary.segment()->merge(*secondary.segment());
565 
566     // The leafnode (level = 0) sign is set in the segment construction.
567     tools::signedFloodFill(*primary.segment(), /*threaded=*/true, /*grainSize=*/1, /*minLevel=*/1);
568 
569     return primary.segment();
570 }
571 
572 
573 ////////////////////////////////////////
574 
575 
576 template<typename TreeType>
577 struct GridOrTreeConstructor
578 {
579     using TreeTypePtr = typename TreeType::Ptr;
constructGridOrTreeConstructor580     static TreeTypePtr construct(const TreeType&, TreeTypePtr& tree) { return tree; }
581 };
582 
583 
584 template<typename TreeType>
585 struct GridOrTreeConstructor<Grid<TreeType> >
586 {
587     using GridType = Grid<TreeType>;
588     using GridTypePtr = typename Grid<TreeType>::Ptr;
589     using TreeTypePtr = typename TreeType::Ptr;
590 
591     static GridTypePtr construct(const GridType& grid, TreeTypePtr& tree) {
592         GridTypePtr maskGrid(GridType::create(tree));
593         maskGrid->setTransform(grid.transform().copy());
594         maskGrid->insertMeta(grid);
595         return maskGrid;
596     }
597 };
598 
599 
600 ////////////////////////////////////////
601 
602 /// List of pairs of leaf node pointers
603 template <typename LeafT>
604 using LeafPairList = std::vector<std::pair<LeafT*, LeafT*>>;
605 
606 /// Transfers leaf nodes from a source tree into a
607 /// destination tree, unless it already exists in the destination tree
608 /// in which case pointers to both leaf nodes are added to a list for
609 /// subsequent compositing operations.
610 template <typename TreeT>
611 void transferLeafNodes(TreeT &srcTree, TreeT &dstTree,
612                               LeafPairList<typename TreeT::LeafNodeType> &overlapping)
613 {
614     using LeafT = typename TreeT::LeafNodeType;
615     tree::ValueAccessor<TreeT> acc(dstTree);//destination
616     std::vector<LeafT*> srcLeafNodes;
617     srcLeafNodes.reserve(srcTree.leafCount());
618     srcTree.stealNodes(srcLeafNodes);
619     srcTree.clear();
620     for (LeafT *srcLeaf : srcLeafNodes) {
621         LeafT *dstLeaf = acc.probeLeaf(srcLeaf->origin());
622         if (dstLeaf) {
623             overlapping.emplace_back(dstLeaf, srcLeaf);//dst, src
624         } else {
625             acc.addLeaf(srcLeaf);
626         }
627     }
628 }
629 
630 /// Template specialization of compActiveLeafVoxels
631 template <typename TreeT, typename OpT>
632 inline
633 typename std::enable_if<
634     !std::is_same<typename TreeT::ValueType, bool>::value &&
635     !std::is_same<typename TreeT::BuildType, ValueMask>::value &&
636     std::is_same<typename TreeT::LeafNodeType::Buffer::ValueType,
637     typename TreeT::LeafNodeType::Buffer::StorageType>::value>::type
638 doCompActiveLeafVoxels(TreeT &srcTree, TreeT &dstTree, OpT op)
639 {
640     using LeafT  = typename TreeT::LeafNodeType;
641     LeafPairList<LeafT> overlapping;//dst, src
642     transferLeafNodes(srcTree, dstTree, overlapping);
643 
644     using RangeT = tbb::blocked_range<size_t>;
645     tbb::parallel_for(RangeT(0, overlapping.size()), [op, &overlapping](const RangeT& r) {
646         for (auto i = r.begin(); i != r.end(); ++i) {
647             LeafT *dstLeaf = overlapping[i].first, *srcLeaf = overlapping[i].second;
648             dstLeaf->getValueMask() |= srcLeaf->getValueMask();
649             auto *ptr = dstLeaf->buffer().data();
650             for (auto v = srcLeaf->cbeginValueOn(); v; ++v) op(ptr[v.pos()], *v);
651             delete srcLeaf;
652         }
653    });
654 }
655 
656 /// Template specialization of compActiveLeafVoxels
657 template <typename TreeT, typename OpT>
658 inline
659 typename std::enable_if<
660     std::is_same<typename TreeT::BuildType, ValueMask>::value &&
661     std::is_same<typename TreeT::ValueType, bool>::value>::type
662 doCompActiveLeafVoxels(TreeT &srcTree, TreeT &dstTree, OpT)
663 {
664     using LeafT  = typename TreeT::LeafNodeType;
665     LeafPairList<LeafT> overlapping;//dst, src
666     transferLeafNodes(srcTree, dstTree, overlapping);
667 
668     using RangeT = tbb::blocked_range<size_t>;
669     tbb::parallel_for(RangeT(0, overlapping.size()), [&overlapping](const RangeT& r) {
670         for (auto i = r.begin(); i != r.end(); ++i) {
671             overlapping[i].first->getValueMask() |= overlapping[i].second->getValueMask();
672             delete overlapping[i].second;
673         }
674     });
675 }
676 
677 /// Template specialization of compActiveLeafVoxels
678 template <typename TreeT, typename OpT>
679 inline
680 typename std::enable_if<
681     std::is_same<typename TreeT::ValueType, bool>::value &&
682     !std::is_same<typename TreeT::BuildType, ValueMask>::value>::type
683 doCompActiveLeafVoxels(TreeT &srcTree, TreeT &dstTree, OpT op)
684 {
685     using LeafT = typename TreeT::LeafNodeType;
686     LeafPairList<LeafT> overlapping;//dst, src
687     transferLeafNodes(srcTree, dstTree, overlapping);
688 
689     using RangeT = tbb::blocked_range<size_t>;
690     using WordT = typename LeafT::Buffer::WordType;
691     tbb::parallel_for(RangeT(0, overlapping.size()), [op, &overlapping](const RangeT& r) {
692         for (auto i = r.begin(); i != r.end(); ++i) {
693             LeafT *dstLeaf = overlapping[i].first, *srcLeaf = overlapping[i].second;
694             WordT *w1 = dstLeaf->buffer().data();
695             const WordT *w2 = srcLeaf->buffer().data();
696             const WordT *w3 = &(srcLeaf->getValueMask().template getWord<WordT>(0));
697             for (Index32 n = LeafT::Buffer::WORD_COUNT; n--; ++w1) {
698                 WordT tmp = *w1, state = *w3++;
699                 op (tmp, *w2++);
700                 *w1 = (state & tmp) | (~state & *w1);//inactive values are unchanged
701             }
702             dstLeaf->getValueMask() |= srcLeaf->getValueMask();
703             delete srcLeaf;
704         }
705     });
706 }
707 
708 /// Default functor for compActiveLeafVoxels
709 template <typename TreeT>
710 struct CopyOp
711 {
712     using ValueT = typename TreeT::ValueType;
713     CopyOp() = default;
714     void operator()(ValueT& dst, const ValueT& src) const { dst = src; }
715 };
716 
717 template <typename TreeT>
718 void validateLevelSet(const TreeT& tree, const std::string& gridName = std::string(""))
719 {
720     using ValueT = typename TreeT::ValueType;
721     const ValueT zero = zeroVal<ValueT>();
722     if (!(tree.background() > zero)) {
723         std::stringstream ss;
724         ss << "expected grid ";
725         if (!gridName.empty()) ss << gridName << " ";
726         ss << "outside value > 0, got " << tree.background();
727         OPENVDB_THROW(ValueError, ss.str());
728     }
729     if (!(-tree.background() < zero)) {
730         std::stringstream ss;
731         ss << "expected grid ";
732         if (!gridName.empty()) ss << gridName << " ";
733         ss << "inside value < 0, got " << -tree.background();
734         OPENVDB_THROW(ValueError, ss.str());
735     }
736 }
737 
738 /// @endcond
739 
740 } // namespace composite
741 
742 
743 template<typename GridOrTreeT>
744 void
745 compMax(GridOrTreeT& aTree, GridOrTreeT& bTree)
746 {
747     using Adapter = TreeAdapter<GridOrTreeT>;
748     using TreeT = typename Adapter::TreeType;
749     using ValueT = typename TreeT::ValueType;
750     struct Local {
751         static inline void op(CombineArgs<ValueT>& args) {
752             args.setResult(composite::max(args.a(), args.b()));
753         }
754     };
755     Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false);
756 }
757 
758 
759 template<typename GridOrTreeT>
760 void
761 compMin(GridOrTreeT& aTree, GridOrTreeT& bTree)
762 {
763     using Adapter = TreeAdapter<GridOrTreeT>;
764     using TreeT = typename Adapter::TreeType;
765     using ValueT = typename TreeT::ValueType;
766     struct Local {
767         static inline void op(CombineArgs<ValueT>& args) {
768             args.setResult(composite::min(args.a(), args.b()));
769         }
770     };
771     Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false);
772 }
773 
774 
775 template<typename GridOrTreeT>
776 void
777 compSum(GridOrTreeT& aTree, GridOrTreeT& bTree)
778 {
779     using Adapter = TreeAdapter<GridOrTreeT>;
780     using TreeT = typename Adapter::TreeType;
781     struct Local {
782         static inline void op(CombineArgs<typename TreeT::ValueType>& args) {
783             args.setResult(args.a() + args.b());
784         }
785     };
786     Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false);
787 }
788 
789 
790 template<typename GridOrTreeT>
791 void
792 compMul(GridOrTreeT& aTree, GridOrTreeT& bTree)
793 {
794     using Adapter = TreeAdapter<GridOrTreeT>;
795     using TreeT = typename Adapter::TreeType;
796     struct Local {
797         static inline void op(CombineArgs<typename TreeT::ValueType>& args) {
798             args.setResult(args.a() * args.b());
799         }
800     };
801     Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false);
802 }
803 
804 
805 template<typename GridOrTreeT>
806 void
807 compDiv(GridOrTreeT& aTree, GridOrTreeT& bTree)
808 {
809     using Adapter = TreeAdapter<GridOrTreeT>;
810     using TreeT = typename Adapter::TreeType;
811     struct Local {
812         static inline void op(CombineArgs<typename TreeT::ValueType>& args) {
813             args.setResult(composite::divide(args.a(), args.b()));
814         }
815     };
816     Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false);
817 }
818 
819 
820 ////////////////////////////////////////
821 
822 
823 template<typename TreeT>
824 struct CompReplaceOp
825 {
826     TreeT* const aTree;
827 
828     CompReplaceOp(TreeT& _aTree): aTree(&_aTree) {}
829 
830     /// @note fill operation is not thread safe
831     void operator()(const typename TreeT::ValueOnCIter& iter) const
832     {
833         CoordBBox bbox;
834         iter.getBoundingBox(bbox);
835         aTree->fill(bbox, *iter);
836     }
837 
838     void operator()(const typename TreeT::LeafCIter& leafIter) const
839     {
840         tree::ValueAccessor<TreeT> acc(*aTree);
841         for (typename TreeT::LeafCIter::LeafNodeT::ValueOnCIter iter =
842             leafIter->cbeginValueOn(); iter; ++iter)
843         {
844             acc.setValue(iter.getCoord(), *iter);
845         }
846     }
847 };
848 
849 
850 template<typename GridOrTreeT>
851 void
852 compReplace(GridOrTreeT& aTree, const GridOrTreeT& bTree)
853 {
854     using Adapter = TreeAdapter<GridOrTreeT>;
855     using TreeT = typename Adapter::TreeType;
856     using ValueOnCIterT = typename TreeT::ValueOnCIter;
857 
858     // Copy active states (but not values) from B to A.
859     Adapter::tree(aTree).topologyUnion(Adapter::tree(bTree));
860 
861     CompReplaceOp<TreeT> op(Adapter::tree(aTree));
862 
863     // Copy all active tile values from B to A.
864     ValueOnCIterT iter = bTree.cbeginValueOn();
865     iter.setMaxDepth(iter.getLeafDepth() - 1); // don't descend into leaf nodes
866     foreach(iter, op, /*threaded=*/false);
867 
868     // Copy all active voxel values from B to A.
869     foreach(Adapter::tree(bTree).cbeginLeaf(), op);
870 }
871 
872 
873 ////////////////////////////////////////
874 
875 
876 template<typename GridOrTreeT>
877 void
878 csgUnion(GridOrTreeT& a, GridOrTreeT& b, bool prune)
879 {
880     using Adapter = TreeAdapter<GridOrTreeT>;
881     using TreeT = typename Adapter::TreeType;
882     TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b);
883     composite::validateLevelSet(aTree, "A");
884     composite::validateLevelSet(bTree, "B");
885     CsgUnionOp<TreeT> op(bTree, Steal());
886     tree::DynamicNodeManager<TreeT> nodeManager(aTree);
887     nodeManager.foreachTopDown(op);
888     if (prune) tools::pruneLevelSet(aTree);
889 }
890 
891 template<typename GridOrTreeT>
892 void
893 csgIntersection(GridOrTreeT& a, GridOrTreeT& b, bool prune)
894 {
895     using Adapter = TreeAdapter<GridOrTreeT>;
896     using TreeT = typename Adapter::TreeType;
897     TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b);
898     composite::validateLevelSet(aTree, "A");
899     composite::validateLevelSet(bTree, "B");
900     CsgIntersectionOp<TreeT> op(bTree, Steal());
901     tree::DynamicNodeManager<TreeT> nodeManager(aTree);
902     nodeManager.foreachTopDown(op);
903     if (prune) tools::pruneLevelSet(aTree);
904 }
905 
906 template<typename GridOrTreeT>
907 void
908 csgDifference(GridOrTreeT& a, GridOrTreeT& b, bool prune)
909 {
910     using Adapter = TreeAdapter<GridOrTreeT>;
911     using TreeT = typename Adapter::TreeType;
912     TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b);
913     composite::validateLevelSet(aTree, "A");
914     composite::validateLevelSet(bTree, "B");
915     CsgDifferenceOp<TreeT> op(bTree, Steal());
916     tree::DynamicNodeManager<TreeT> nodeManager(aTree);
917     nodeManager.foreachTopDown(op);
918     if (prune) tools::pruneLevelSet(aTree);
919 }
920 
921 
922 template<typename GridOrTreeT>
923 typename GridOrTreeT::Ptr
924 csgUnionCopy(const GridOrTreeT& a, const GridOrTreeT& b)
925 {
926     using Adapter = TreeAdapter<GridOrTreeT>;
927     using TreePtrT = typename Adapter::TreeType::Ptr;
928 
929     TreePtrT output = composite::doCSGCopy<composite::CSG_UNION>(
930                         Adapter::tree(a), Adapter::tree(b));
931 
932     return composite::GridOrTreeConstructor<GridOrTreeT>::construct(a, output);
933 }
934 
935 
936 template<typename GridOrTreeT>
937 typename GridOrTreeT::Ptr
938 csgIntersectionCopy(const GridOrTreeT& a, const GridOrTreeT& b)
939 {
940     using Adapter = TreeAdapter<GridOrTreeT>;
941     using TreePtrT = typename Adapter::TreeType::Ptr;
942 
943     TreePtrT output = composite::doCSGCopy<composite::CSG_INTERSECTION>(
944                         Adapter::tree(a), Adapter::tree(b));
945 
946     return composite::GridOrTreeConstructor<GridOrTreeT>::construct(a, output);
947 }
948 
949 
950 template<typename GridOrTreeT>
951 typename GridOrTreeT::Ptr
952 csgDifferenceCopy(const GridOrTreeT& a, const GridOrTreeT& b)
953 {
954     using Adapter = TreeAdapter<GridOrTreeT>;
955     using TreePtrT = typename Adapter::TreeType::Ptr;
956 
957     TreePtrT output = composite::doCSGCopy<composite::CSG_DIFFERENCE>(
958                         Adapter::tree(a), Adapter::tree(b));
959 
960     return composite::GridOrTreeConstructor<GridOrTreeT>::construct(a, output);
961 }
962 
963 ////////////////////////////////////////////////////////
964 
965 /// @brief Composite the active values in leaf nodes, i.e. active
966 ///        voxels, of a source tree into a destination tree.
967 ///
968 /// @param srcTree source tree from which active voxels are composited.
969 ///
970 /// @param dstTree destination tree into which active voxels are composited.
971 ///
972 /// @param op      a functor of the form <tt>void op(T& dst, const T& src)</tt>,
973 ///                where @c T is the @c ValueType of the tree, that composites
974 ///                a source value into a destination value. By default
975 ///                it copies the value from src to dst.
976 ///
977 /// @details All active voxels in the source tree will
978 ///          be active in the destination tree, and their value is
979 ///          determined by a use-defined functor (OpT op) that operates on the
980 ///          source and destination values. The only exception is when
981 ///          the tree type is MaskTree, in which case no functor is
982 ///          needed since by defintion a MaskTree has no values (only topology).
983 ///
984 /// @warning This function only operated on leaf node values,
985 ///          i.e. tile values are ignored.
986 template<typename TreeT, typename OpT = composite::CopyOp<TreeT> >
987 void
988 compActiveLeafVoxels(TreeT &srcTree, TreeT &dstTree, OpT op = composite::CopyOp<TreeT>())
989 {
990     composite::doCompActiveLeafVoxels<TreeT, OpT>(srcTree, dstTree, op);
991 }
992 
993 
994 ////////////////////////////////////////
995 
996 
997 // Explicit Template Instantiation
998 
999 #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
1000 
1001 #ifdef OPENVDB_INSTANTIATE_COMPOSITE
1002 #include <openvdb/util/ExplicitInstantiation.h>
1003 #endif
1004 
1005 #define _FUNCTION(TreeT) \
1006     void csgUnion(TreeT&, TreeT&, bool)
1007 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
1008 #undef _FUNCTION
1009 
1010 #define _FUNCTION(TreeT) \
1011     void csgUnion(Grid<TreeT>&, Grid<TreeT>&, bool)
1012 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
1013 #undef _FUNCTION
1014 
1015 #define _FUNCTION(TreeT) \
1016     void csgIntersection(TreeT&, TreeT&, bool)
1017 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
1018 #undef _FUNCTION
1019 
1020 #define _FUNCTION(TreeT) \
1021     void csgIntersection(Grid<TreeT>&, Grid<TreeT>&, bool)
1022 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
1023 #undef _FUNCTION
1024 
1025 #define _FUNCTION(TreeT) \
1026     void csgDifference(TreeT&, TreeT&, bool)
1027 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
1028 #undef _FUNCTION
1029 
1030 #define _FUNCTION(TreeT) \
1031     void csgDifference(Grid<TreeT>&, Grid<TreeT>&, bool)
1032 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
1033 #undef _FUNCTION
1034 
1035 #define _FUNCTION(TreeT) \
1036     TreeT::Ptr csgUnionCopy(const TreeT&, const TreeT&)
1037 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
1038 #undef _FUNCTION
1039 
1040 #define _FUNCTION(TreeT) \
1041     Grid<TreeT>::Ptr csgUnionCopy(const Grid<TreeT>&, const Grid<TreeT>&)
1042 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
1043 #undef _FUNCTION
1044 
1045 #define _FUNCTION(TreeT) \
1046     TreeT::Ptr csgIntersectionCopy(const TreeT&, const TreeT&)
1047 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
1048 #undef _FUNCTION
1049 
1050 #define _FUNCTION(TreeT) \
1051     Grid<TreeT>::Ptr csgIntersectionCopy(const Grid<TreeT>&, const Grid<TreeT>&)
1052 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
1053 #undef _FUNCTION
1054 
1055 #define _FUNCTION(TreeT) \
1056     TreeT::Ptr csgDifferenceCopy(const TreeT&, const TreeT&)
1057 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
1058 #undef _FUNCTION
1059 
1060 #define _FUNCTION(TreeT) \
1061     Grid<TreeT>::Ptr csgDifferenceCopy(const Grid<TreeT>&, const Grid<TreeT>&)
1062 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
1063 #undef _FUNCTION
1064 
1065 #define _FUNCTION(TreeT) \
1066     void compMax(TreeT&, TreeT&)
1067 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
1068 #undef _FUNCTION
1069 
1070 #define _FUNCTION(TreeT) \
1071     void compMax(Grid<TreeT>&, Grid<TreeT>&)
1072 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
1073 #undef _FUNCTION
1074 
1075 #define _FUNCTION(TreeT) \
1076     void compMin(TreeT&, TreeT&)
1077 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
1078 #undef _FUNCTION
1079 
1080 #define _FUNCTION(TreeT) \
1081     void compMin(Grid<TreeT>&, Grid<TreeT>&)
1082 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
1083 #undef _FUNCTION
1084 
1085 #define _FUNCTION(TreeT) \
1086     void compSum(TreeT&, TreeT&)
1087 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
1088 #undef _FUNCTION
1089 
1090 #define _FUNCTION(TreeT) \
1091     void compSum(Grid<TreeT>&, Grid<TreeT>&)
1092 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
1093 #undef _FUNCTION
1094 
1095 #define _FUNCTION(TreeT) \
1096     void compDiv(TreeT&, TreeT&)
1097 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
1098 #undef _FUNCTION
1099 
1100 #define _FUNCTION(TreeT) \
1101     void compDiv(Grid<TreeT>&, Grid<TreeT>&)
1102 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
1103 #undef _FUNCTION
1104 
1105 #define _FUNCTION(TreeT) \
1106     void compReplace(TreeT&, const TreeT&)
1107 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
1108 #undef _FUNCTION
1109 
1110 #define _FUNCTION(TreeT) \
1111     void compReplace(Grid<TreeT>&, const Grid<TreeT>&)
1112 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
1113 #undef _FUNCTION
1114 
1115 #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
1116 
1117 
1118 } // namespace tools
1119 } // namespace OPENVDB_VERSION_NAME
1120 } // namespace openvdb
1121 
1122 #endif // OPENVDB_TOOLS_COMPOSITE_HAS_BEEN_INCLUDED
1123