OpenVDB 9.0.0
Composite.h
Go to the documentation of this file.
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
32namespace openvdb {
34namespace OPENVDB_VERSION_NAME {
35namespace 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.
40template<typename GridOrTreeT>
41void 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.
45template<typename GridOrTreeT>
46void 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.
50template<typename GridOrTreeT>
51void 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.
56template<typename GridOrTreeT>
57typename 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.
61template<typename GridOrTreeT>
62typename 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.
66template<typename GridOrTreeT>
67typename 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.
71template<typename GridOrTreeT>
72void 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.
75template<typename GridOrTreeT>
76void 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.
79template<typename GridOrTreeT>
80void 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.
83template<typename GridOrTreeT>
84void 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.
87template<typename GridOrTreeT>
88void compDiv(GridOrTreeT& a, GridOrTreeT& b);
89
90/// Copy the active voxels of B into A.
91template<typename GridOrTreeT>
92void compReplace(GridOrTreeT& a, const GridOrTreeT& b);
93
94
95////////////////////////////////////////
96
97
98namespace composite {
99
100// composite::min() and composite::max() for non-vector types compare with operator<().
101template<typename T> inline
102const typename std::enable_if<!VecTraits<T>::IsVec, T>::type& // = T if T is not a vector type
103min(const T& a, const T& b) { return std::min(a, b); }
104
105template<typename T> inline
106const typename std::enable_if<!VecTraits<T>::IsVec, T>::type&
107max(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.
111template<typename T> inline
112const typename std::enable_if<VecTraits<T>::IsVec, T>::type& // = T if T is a vector type
113min(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
119template<typename T> inline
120const typename std::enable_if<VecTraits<T>::IsVec, T>::type&
121max(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
128template<typename T> inline
129typename std::enable_if<!std::is_integral<T>::value, T>::type // = T if T is not an integer type
130divide(const T& a, const T& b) { return a / b; }
131
132template<typename T> inline
133typename std::enable_if<std::is_integral<T>::value, T>::type // = T if T is an integer type
134divide(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;
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.
145inline bool divide(bool a, bool /*b*/) { return a; }
146
147
148/// @cond OPENVDB_DOCS_INTERNAL
149
150enum CSGOperation { CSG_UNION, CSG_INTERSECTION, CSG_DIFFERENCE };
151
152template<typename TreeType, CSGOperation Operation>
153struct 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
163 BuildPrimarySegment(const TreeType& lhs, const TreeType& rhs)
164 : mSegment(new TreeType(lhs.background()))
165 , mLhsTree(&lhs)
166 , mRhsTree(&rhs)
167 {
168 }
169
170 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
186 TreePtrType& segment() { return mSegment; }
187
188private:
189
190 struct ProcessInternalNodes {
191
192 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
204 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
214 void join(ProcessInternalNodes& other)
215 {
216 mOutputTree->merge(*other.mOutputTree);
217 mOutputLeafNodes->insert(mOutputLeafNodes->end(),
218 other.mOutputLeafNodes->begin(), other.mOutputLeafNodes->end());
219 }
220
221 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
270 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
279 ProcessLeafNodes(ProcessLeafNodes& other, tbb::split)
280 : mLhsNodes(other.mLhsNodes)
281 , mRhsTree(other.mRhsTree)
282 , mLocalTree(mRhsTree->background())
283 , mOutputTree(&mLocalTree)
284 {
285 }
286
287 void join(ProcessLeafNodes& rhs) { mOutputTree->merge(*rhs.mOutputTree); }
288
289 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
360template<typename TreeType, CSGOperation Operation>
361struct 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
371 BuildSecondarySegment(const TreeType& lhs, const TreeType& rhs)
372 : mSegment(new TreeType(lhs.background()))
373 , mLhsTree(&lhs)
374 , mRhsTree(&rhs)
375 {
376 }
377
378 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
394 TreePtrType& segment() { return mSegment; }
395
396private:
397
398 struct ProcessInternalNodes {
399
400 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
412 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
422 void join(ProcessInternalNodes& other)
423 {
424 mOutputTree->merge(*other.mOutputTree);
425 mOutputLeafNodes->insert(mOutputLeafNodes->end(),
426 other.mOutputLeafNodes->begin(), other.mOutputLeafNodes->end());
427 }
428
429 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
488 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
497 ProcessLeafNodes(ProcessLeafNodes& rhs, tbb::split)
498 : mRhsNodes(rhs.mRhsNodes)
499 , mLhsTree(rhs.mLhsTree)
500 , mLocalTree(mLhsTree->background())
501 , mOutputTree(&mLocalTree)
502 {
503 }
504
505 void join(ProcessLeafNodes& rhs) { mOutputTree->merge(*rhs.mOutputTree); }
506
507 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
551template<CSGOperation Operation, typename TreeType>
552typename TreeType::Ptr
553doCSGCopy(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
576template<typename TreeType>
577struct GridOrTreeConstructor
578{
579 using TreeTypePtr = typename TreeType::Ptr;
580 static TreeTypePtr construct(const TreeType&, TreeTypePtr& tree) { return tree; }
581};
582
583
584template<typename TreeType>
585struct 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
603template <typename LeafT>
604using 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.
610template <typename TreeT>
611void 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
631template <typename TreeT, typename OpT>
632inline
633typename std::enable_if<
636 std::is_same<typename TreeT::LeafNodeType::Buffer::ValueType,
637 typename TreeT::LeafNodeType::Buffer::StorageType>::value>::type
638doCompActiveLeafVoxels(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
657template <typename TreeT, typename OpT>
658inline
659typename std::enable_if<
662doCompActiveLeafVoxels(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
678template <typename TreeT, typename OpT>
679inline
680typename std::enable_if<
683doCompActiveLeafVoxels(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
709template <typename TreeT>
710struct CopyOp
711{
712 using ValueT = typename TreeT::ValueType;
713 CopyOp() = default;
714 void operator()(ValueT& dst, const ValueT& src) const { dst = src; }
715};
716
717template <typename TreeT>
718void 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
743template<typename GridOrTreeT>
744void
745compMax(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
759template<typename GridOrTreeT>
760void
761compMin(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
775template<typename GridOrTreeT>
776void
777compSum(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
790template<typename GridOrTreeT>
791void
792compMul(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
805template<typename GridOrTreeT>
806void
807compDiv(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
823template<typename TreeT>
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
850template<typename GridOrTreeT>
851void
852compReplace(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
876template<typename GridOrTreeT>
877void
878csgUnion(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
891template<typename GridOrTreeT>
892void
893csgIntersection(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
906template<typename GridOrTreeT>
907void
908csgDifference(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
922template<typename GridOrTreeT>
923typename GridOrTreeT::Ptr
924csgUnionCopy(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
936template<typename GridOrTreeT>
937typename GridOrTreeT::Ptr
938csgIntersectionCopy(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
950template<typename GridOrTreeT>
951typename GridOrTreeT::Ptr
952csgDifferenceCopy(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.
986template<typename TreeT, typename OpT = composite::CopyOp<TreeT> >
987void
988compActiveLeafVoxels(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
1003#endif
1004
1005#define _FUNCTION(TreeT) \
1006 void csgUnion(TreeT&, TreeT&, bool)
1008#undef _FUNCTION
1009
1010#define _FUNCTION(TreeT) \
1011 void csgUnion(Grid<TreeT>&, Grid<TreeT>&, bool)
1013#undef _FUNCTION
1014
1015#define _FUNCTION(TreeT) \
1016 void csgIntersection(TreeT&, TreeT&, bool)
1018#undef _FUNCTION
1019
1020#define _FUNCTION(TreeT) \
1021 void csgIntersection(Grid<TreeT>&, Grid<TreeT>&, bool)
1023#undef _FUNCTION
1024
1025#define _FUNCTION(TreeT) \
1026 void csgDifference(TreeT&, TreeT&, bool)
1028#undef _FUNCTION
1029
1030#define _FUNCTION(TreeT) \
1031 void csgDifference(Grid<TreeT>&, Grid<TreeT>&, bool)
1033#undef _FUNCTION
1034
1035#define _FUNCTION(TreeT) \
1036 TreeT::Ptr csgUnionCopy(const TreeT&, const TreeT&)
1038#undef _FUNCTION
1039
1040#define _FUNCTION(TreeT) \
1041 Grid<TreeT>::Ptr csgUnionCopy(const Grid<TreeT>&, const Grid<TreeT>&)
1043#undef _FUNCTION
1044
1045#define _FUNCTION(TreeT) \
1046 TreeT::Ptr csgIntersectionCopy(const TreeT&, const TreeT&)
1048#undef _FUNCTION
1049
1050#define _FUNCTION(TreeT) \
1051 Grid<TreeT>::Ptr csgIntersectionCopy(const Grid<TreeT>&, const Grid<TreeT>&)
1053#undef _FUNCTION
1054
1055#define _FUNCTION(TreeT) \
1056 TreeT::Ptr csgDifferenceCopy(const TreeT&, const TreeT&)
1058#undef _FUNCTION
1059
1060#define _FUNCTION(TreeT) \
1061 Grid<TreeT>::Ptr csgDifferenceCopy(const Grid<TreeT>&, const Grid<TreeT>&)
1063#undef _FUNCTION
1064
1065#define _FUNCTION(TreeT) \
1066 void compMax(TreeT&, TreeT&)
1068#undef _FUNCTION
1069
1070#define _FUNCTION(TreeT) \
1071 void compMax(Grid<TreeT>&, Grid<TreeT>&)
1073#undef _FUNCTION
1074
1075#define _FUNCTION(TreeT) \
1076 void compMin(TreeT&, TreeT&)
1078#undef _FUNCTION
1079
1080#define _FUNCTION(TreeT) \
1081 void compMin(Grid<TreeT>&, Grid<TreeT>&)
1083#undef _FUNCTION
1084
1085#define _FUNCTION(TreeT) \
1086 void compSum(TreeT&, TreeT&)
1088#undef _FUNCTION
1089
1090#define _FUNCTION(TreeT) \
1091 void compSum(Grid<TreeT>&, Grid<TreeT>&)
1093#undef _FUNCTION
1094
1095#define _FUNCTION(TreeT) \
1096 void compDiv(TreeT&, TreeT&)
1098#undef _FUNCTION
1099
1100#define _FUNCTION(TreeT) \
1101 void compDiv(Grid<TreeT>&, Grid<TreeT>&)
1103#undef _FUNCTION
1104
1105#define _FUNCTION(TreeT) \
1106 void compReplace(TreeT&, const TreeT&)
1108#undef _FUNCTION
1109
1110#define _FUNCTION(TreeT) \
1111 void compReplace(Grid<TreeT>&, const Grid<TreeT>&)
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
ValueT value
Definition: GridBuilder.h:1287
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
Functions to efficiently merge grids.
Defined various multi-threaded utility functions for trees.
Propagate the signs of distance values from the active voxels in the narrow band to the inactive valu...
This struct collects both input and output arguments to "grid combiner" functors used with the tree::...
Definition: Types.h:451
const BValueType & b() const
Get the B input value.
Definition: Types.h:492
const AValueType & a() const
Get the A input value.
Definition: Types.h:490
CombineArgs & setResult(const AValueType &val)
Set the output value.
Definition: Types.h:500
SharedPtr< Grid > Ptr
Definition: Grid.h:579
Tag dispatch class that distinguishes constructors that steal.
Definition: Types.h:568
Axis-aligned bounding box of signed integer coordinates.
Definition: Coord.h:248
Definition: NodeManager.h:890
void foreachTopDown(const NodeOp &op, bool threaded=true, size_t leafGrainSize=1, size_t nonLeafGrainSize=1)
Threaded method that applies a user-supplied functor to all the nodes in the tree.
Definition: NodeManager.h:976
void setValue(const Coord &xyz, const ValueType &value)
Set the value of the voxel at the given coordinates and mark the voxel as active.
Definition: ValueAccessor.h:250
GridType
List of types that are currently supported by NanoVDB.
Definition: NanoVDB.h:216
T negative(const T &val)
Return the unary negation of the given value.
Definition: Math.h:127
const std::enable_if< VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:121
const std::enable_if< VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:113
bool divide(bool a, bool)
Definition: Composite.h:145
void compDiv(GridOrTreeT &a, GridOrTreeT &b)
Given grids A and B, compute a / b per voxel (using sparse traversal). Store the result in the A grid...
Definition: Composite.h:807
void csgUnion(GridOrTreeT &a, GridOrTreeT &b, bool prune=true)
Given two level set grids, replace the A grid with the union of A and B.
Definition: Composite.h:878
void compReplace(GridOrTreeT &a, const GridOrTreeT &b)
Copy the active voxels of B into A.
Definition: Composite.h:852
void csgDifference(GridOrTreeT &a, GridOrTreeT &b, bool prune=true)
Given two level set grids, replace the A grid with the difference A / B.
Definition: Composite.h:908
void pruneLevelSet(TreeT &tree, bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing nodes whose values are all inactive with inactive ...
Definition: Prune.h:390
void compActiveLeafVoxels(TreeT &srcTree, TreeT &dstTree, OpT op=composite::CopyOp< TreeT >())
Composite the active values in leaf nodes, i.e. active voxels, of a source tree into a destination tr...
Definition: Composite.h:988
GridOrTreeT::Ptr csgDifferenceCopy(const GridOrTreeT &a, const GridOrTreeT &b)
Threaded CSG difference operation that produces a new grid or tree from immutable inputs.
Definition: Composite.h:952
void csgIntersection(GridOrTreeT &a, GridOrTreeT &b, bool prune=true)
Given two level set grids, replace the A grid with the intersection of A and B.
Definition: Composite.h:893
void prune(TreeT &tree, typename TreeT::ValueType tolerance=zeroVal< typename TreeT::ValueType >(), bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing with tiles any nodes whose values are all the same...
Definition: Prune.h:335
GridOrTreeT::Ptr csgUnionCopy(const GridOrTreeT &a, const GridOrTreeT &b)
Threaded CSG union operation that produces a new grid or tree from immutable inputs.
Definition: Composite.h:924
void compSum(GridOrTreeT &a, GridOrTreeT &b)
Given grids A and B, compute a + b per voxel (using sparse traversal). Store the result in the A grid...
Definition: Composite.h:777
void compMax(GridOrTreeT &a, GridOrTreeT &b)
Given grids A and B, compute max(a, b) per voxel (using sparse traversal). Store the result in the A ...
Definition: Composite.h:745
void signedFloodFill(TreeOrLeafManagerT &tree, bool threaded=true, size_t grainSize=1, Index minLevel=0)
Set the values of all inactive voxels and tiles of a narrow-band level set from the signs of the acti...
Definition: SignedFloodFill.h:267
void compMul(GridOrTreeT &a, GridOrTreeT &b)
Given grids A and B, compute a * b per voxel (using sparse traversal). Store the result in the A grid...
Definition: Composite.h:792
void compMin(GridOrTreeT &a, GridOrTreeT &b)
Given grids A and B, compute min(a, b) per voxel (using sparse traversal). Store the result in the A ...
Definition: Composite.h:761
GridOrTreeT::Ptr csgIntersectionCopy(const GridOrTreeT &a, const GridOrTreeT &b)
Threaded CSG intersection operation that produces a new grid or tree from immutable inputs.
Definition: Composite.h:938
Index32 Index
Definition: Types.h:54
openvdb::GridBase Grid
Definition: Utils.h:34
Definition: Exceptions.h:13
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
This adapter allows code that is templated on a Tree type to accept either a Tree type or a Grid type...
Definition: Grid.h:1071
Definition: Composite.h:825
void operator()(const typename TreeT::LeafCIter &leafIter) const
Definition: Composite.h:838
TreeT *const aTree
Definition: Composite.h:826
void operator()(const typename TreeT::ValueOnCIter &iter) const
Definition: Composite.h:831
CompReplaceOp(TreeT &_aTree)
Definition: Composite.h:828
DynamicNodeManager operator to merge two trees using a CSG difference.
Definition: Merge.h:263
DynamicNodeManager operator to merge trees using a CSG union or intersection.
Definition: Merge.h:183
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:116
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:202
#define OPENVDB_REAL_TREE_INSTANTIATE(Function)
Definition: version.h.in:147
#define OPENVDB_VOLUME_TREE_INSTANTIATE(Function)
Definition: version.h.in:150