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