OpenVDB 12.0.0
 
Loading...
Searching...
No Matches
Composite.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-2.0
3//
4/// @file 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 using ValueT = typename GridOrTreeT::ValueType;
800 struct Local {
801 static inline void op(CombineArgs<typename TreeT::ValueType>& args) {
802 if constexpr(std::is_same<ValueT, bool>::value) {
803 args.setResult(args.a() && args.b());
804 } else {
805 args.setResult(args.a() * args.b());
806 }
807 }
808 };
809 Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false);
810}
811
812
813template<typename GridOrTreeT>
814void
815compDiv(GridOrTreeT& aTree, GridOrTreeT& bTree)
816{
817 using Adapter = TreeAdapter<GridOrTreeT>;
818 using TreeT = typename Adapter::TreeType;
819 struct Local {
820 static inline void op(CombineArgs<typename TreeT::ValueType>& args) {
821 args.setResult(composite::divide(args.a(), args.b()));
822 }
823 };
824 Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false);
825}
826
827
828////////////////////////////////////////
829
830
831template<typename TreeT>
833{
834 TreeT* const aTree;
835
836 CompReplaceOp(TreeT& _aTree): aTree(&_aTree) {}
837
838 /// @note fill operation is not thread safe
839 void operator()(const typename TreeT::ValueOnCIter& iter) const
840 {
841 CoordBBox bbox;
842 iter.getBoundingBox(bbox);
843 aTree->fill(bbox, *iter);
844 }
845
846 void operator()(const typename TreeT::LeafCIter& leafIter) const
847 {
849 for (typename TreeT::LeafCIter::LeafNodeT::ValueOnCIter iter =
850 leafIter->cbeginValueOn(); iter; ++iter)
851 {
852 acc.setValue(iter.getCoord(), *iter);
853 }
854 }
855};
856
857
858template<typename GridOrTreeT>
859void
860compReplace(GridOrTreeT& aTree, const GridOrTreeT& bTree)
861{
862 using Adapter = TreeAdapter<GridOrTreeT>;
863 using TreeT = typename Adapter::TreeType;
864 using ValueOnCIterT = typename TreeT::ValueOnCIter;
865
866 // Copy active states (but not values) from B to A.
867 Adapter::tree(aTree).topologyUnion(Adapter::tree(bTree));
868
869 CompReplaceOp<TreeT> op(Adapter::tree(aTree));
870
871 // Copy all active tile values from B to A.
872 ValueOnCIterT iter = bTree.cbeginValueOn();
873 iter.setMaxDepth(iter.getLeafDepth() - 1); // don't descend into leaf nodes
874 foreach(iter, op, /*threaded=*/false);
875
876 // Copy all active voxel values from B to A.
877 foreach(Adapter::tree(bTree).cbeginLeaf(), op);
878}
879
880
881////////////////////////////////////////
882
883
884template<typename GridOrTreeT>
885void
886csgUnion(GridOrTreeT& a, GridOrTreeT& b, bool prune, bool pruneCancelledTiles)
887{
888 using Adapter = TreeAdapter<GridOrTreeT>;
889 using TreeT = typename Adapter::TreeType;
890 TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b);
891 composite::validateLevelSet(aTree, "A");
892 composite::validateLevelSet(bTree, "B");
893 CsgUnionOp<TreeT> op(bTree, Steal());
894 op.setPruneCancelledTiles(prune && pruneCancelledTiles);
895 tree::DynamicNodeManager<TreeT> nodeManager(aTree);
896 nodeManager.foreachTopDown(op);
897 if (prune) tools::pruneLevelSet(aTree);
898}
899
900template<typename GridOrTreeT>
901void
902csgIntersection(GridOrTreeT& a, GridOrTreeT& b, bool prune, bool pruneCancelledTiles)
903{
904 using Adapter = TreeAdapter<GridOrTreeT>;
905 using TreeT = typename Adapter::TreeType;
906 TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b);
907 composite::validateLevelSet(aTree, "A");
908 composite::validateLevelSet(bTree, "B");
910 op.setPruneCancelledTiles(prune && pruneCancelledTiles);
911 tree::DynamicNodeManager<TreeT> nodeManager(aTree);
912 nodeManager.foreachTopDown(op);
913 if (prune) tools::pruneLevelSet(aTree);
914}
915
916template<typename GridOrTreeT>
917void
918csgDifference(GridOrTreeT& a, GridOrTreeT& b, bool prune, bool pruneCancelledTiles)
919{
920 using Adapter = TreeAdapter<GridOrTreeT>;
921 using TreeT = typename Adapter::TreeType;
922 TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b);
923 composite::validateLevelSet(aTree, "A");
924 composite::validateLevelSet(bTree, "B");
926 op.setPruneCancelledTiles(prune && pruneCancelledTiles);
927 tree::DynamicNodeManager<TreeT> nodeManager(aTree);
928 nodeManager.foreachTopDown(op);
929 if (prune) tools::pruneLevelSet(aTree);
930}
931
932
933template<typename GridOrTreeT>
934typename GridOrTreeT::Ptr
935csgUnionCopy(const GridOrTreeT& a, const GridOrTreeT& b)
936{
937 using Adapter = TreeAdapter<GridOrTreeT>;
938 using TreePtrT = typename Adapter::TreeType::Ptr;
939
940 TreePtrT output = composite::doCSGCopy<composite::CSG_UNION>(
941 Adapter::tree(a), Adapter::tree(b));
942
943 return composite::GridOrTreeConstructor<GridOrTreeT>::construct(a, output);
944}
945
946
947template<typename GridOrTreeT>
948typename GridOrTreeT::Ptr
949csgIntersectionCopy(const GridOrTreeT& a, const GridOrTreeT& b)
950{
951 using Adapter = TreeAdapter<GridOrTreeT>;
952 using TreePtrT = typename Adapter::TreeType::Ptr;
953
954 TreePtrT output = composite::doCSGCopy<composite::CSG_INTERSECTION>(
955 Adapter::tree(a), Adapter::tree(b));
956
957 return composite::GridOrTreeConstructor<GridOrTreeT>::construct(a, output);
958}
959
960
961template<typename GridOrTreeT>
962typename GridOrTreeT::Ptr
963csgDifferenceCopy(const GridOrTreeT& a, const GridOrTreeT& b)
964{
965 using Adapter = TreeAdapter<GridOrTreeT>;
966 using TreePtrT = typename Adapter::TreeType::Ptr;
967
968 TreePtrT output = composite::doCSGCopy<composite::CSG_DIFFERENCE>(
969 Adapter::tree(a), Adapter::tree(b));
970
971 return composite::GridOrTreeConstructor<GridOrTreeT>::construct(a, output);
972}
973
974////////////////////////////////////////////////////////
975
976/// @brief Composite the active values in leaf nodes, i.e. active
977/// voxels, of a source tree into a destination tree.
978///
979/// @param srcTree source tree from which active voxels are composited.
980///
981/// @param dstTree destination tree into which active voxels are composited.
982///
983/// @param op a functor of the form <tt>void op(T& dst, const T& src)</tt>,
984/// where @c T is the @c ValueType of the tree, that composites
985/// a source value into a destination value. By default
986/// it copies the value from src to dst.
987///
988/// @details All active voxels in the source tree will
989/// be active in the destination tree, and their value is
990/// determined by a use-defined functor (OpT op) that operates on the
991/// source and destination values. The only exception is when
992/// the tree type is MaskTree, in which case no functor is
993/// needed since by defintion a MaskTree has no values (only topology).
994///
995/// @warning This function only operated on leaf node values,
996/// i.e. tile values are ignored.
997template<typename TreeT, typename OpT = composite::CopyOp<TreeT> >
998void
999compActiveLeafVoxels(TreeT &srcTree, TreeT &dstTree, OpT op = composite::CopyOp<TreeT>())
1000{
1001 composite::doCompActiveLeafVoxels<TreeT, OpT>(srcTree, dstTree, op);
1002}
1003
1004
1005////////////////////////////////////////
1006
1007
1008// Explicit Template Instantiation
1009
1010#ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
1011
1012#ifdef OPENVDB_INSTANTIATE_COMPOSITE
1014#endif
1015
1016#define _FUNCTION(TreeT) \
1017 void csgUnion(TreeT&, TreeT&, bool, bool)
1019#undef _FUNCTION
1020
1021#define _FUNCTION(TreeT) \
1022 void csgUnion(Grid<TreeT>&, Grid<TreeT>&, bool, bool)
1024#undef _FUNCTION
1025
1026#define _FUNCTION(TreeT) \
1027 void csgIntersection(TreeT&, TreeT&, bool, bool)
1029#undef _FUNCTION
1030
1031#define _FUNCTION(TreeT) \
1032 void csgIntersection(Grid<TreeT>&, Grid<TreeT>&, bool, bool)
1034#undef _FUNCTION
1035
1036#define _FUNCTION(TreeT) \
1037 void csgDifference(TreeT&, TreeT&, bool, bool)
1039#undef _FUNCTION
1040
1041#define _FUNCTION(TreeT) \
1042 void csgDifference(Grid<TreeT>&, Grid<TreeT>&, bool, bool)
1044#undef _FUNCTION
1045
1046#define _FUNCTION(TreeT) \
1047 TreeT::Ptr csgUnionCopy(const TreeT&, const TreeT&)
1049#undef _FUNCTION
1050
1051#define _FUNCTION(TreeT) \
1052 Grid<TreeT>::Ptr csgUnionCopy(const Grid<TreeT>&, const Grid<TreeT>&)
1054#undef _FUNCTION
1055
1056#define _FUNCTION(TreeT) \
1057 TreeT::Ptr csgIntersectionCopy(const TreeT&, const TreeT&)
1059#undef _FUNCTION
1060
1061#define _FUNCTION(TreeT) \
1062 Grid<TreeT>::Ptr csgIntersectionCopy(const Grid<TreeT>&, const Grid<TreeT>&)
1064#undef _FUNCTION
1065
1066#define _FUNCTION(TreeT) \
1067 TreeT::Ptr csgDifferenceCopy(const TreeT&, const TreeT&)
1069#undef _FUNCTION
1070
1071#define _FUNCTION(TreeT) \
1072 Grid<TreeT>::Ptr csgDifferenceCopy(const Grid<TreeT>&, const Grid<TreeT>&)
1074#undef _FUNCTION
1075
1076#define _FUNCTION(TreeT) \
1077 void compMax(TreeT&, TreeT&)
1079#undef _FUNCTION
1080
1081#define _FUNCTION(TreeT) \
1082 void compMax(Grid<TreeT>&, Grid<TreeT>&)
1084#undef _FUNCTION
1085
1086#define _FUNCTION(TreeT) \
1087 void compMin(TreeT&, TreeT&)
1089#undef _FUNCTION
1090
1091#define _FUNCTION(TreeT) \
1092 void compMin(Grid<TreeT>&, Grid<TreeT>&)
1094#undef _FUNCTION
1095
1096#define _FUNCTION(TreeT) \
1097 void compSum(TreeT&, TreeT&)
1099#undef _FUNCTION
1100
1101#define _FUNCTION(TreeT) \
1102 void compSum(Grid<TreeT>&, Grid<TreeT>&)
1104#undef _FUNCTION
1105
1106#define _FUNCTION(TreeT) \
1107 void compDiv(TreeT&, TreeT&)
1109#undef _FUNCTION
1110
1111#define _FUNCTION(TreeT) \
1112 void compDiv(Grid<TreeT>&, Grid<TreeT>&)
1114#undef _FUNCTION
1115
1116#define _FUNCTION(TreeT) \
1117 void compReplace(TreeT&, const TreeT&)
1119#undef _FUNCTION
1120
1121#define _FUNCTION(TreeT) \
1122 void compReplace(Grid<TreeT>&, const Grid<TreeT>&)
1124#undef _FUNCTION
1125
1126#endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
1127
1128
1129} // namespace tools
1130} // namespace OPENVDB_VERSION_NAME
1131} // namespace openvdb
1132
1133#endif // OPENVDB_TOOLS_COMPOSITE_HAS_BEEN_INCLUDED
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:569
const BValueType & b() const
Get the B input value.
Definition Types.h:610
const AValueType & a() const
Get the A input value.
Definition Types.h:608
CombineArgs & setResult(const AValueType &val)
Set the output value.
Definition Types.h:618
Tag dispatch class that distinguishes constructors that steal.
Definition Types.h:687
Definition Exceptions.h:65
Axis-aligned bounding box of signed integer coordinates.
Definition Coord.h:252
Definition NodeManager.h:891
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:977
void setValue(const Coord &xyz, const ValueType &value)
Set a particular value at the given coordinate and mark the coordinate as active.
Definition ValueAccessor.h:550
bool empty(const char *str)
tests if a c-string str is empty, that is its first value is '\0'
Definition Util.h:144
GridType
List of types that are currently supported by NanoVDB.
Definition NanoVDB.h:219
Definition Composite.h:101
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:815
void compReplace(GridOrTreeT &a, const GridOrTreeT &b)
Copy the active voxels of B into A.
Definition Composite.h:860
CsgUnionOrIntersectionOp< TreeT, false > CsgIntersectionOp
Definition Merge.h:270
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 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:918
OutGridT XformOp & op
Definition ValueTransformer.h:140
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:963
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:935
CsgUnionOrIntersectionOp< TreeT, true > CsgUnionOp
Definition Merge.h:267
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:902
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:999
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:949
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:886
Definition PointDataGrid.h:170
ValueAccessorImpl< TreeType, IsSafe, MutexType, openvdb::make_index_sequence< CacheLevels > > ValueAccessor
Default alias for a ValueAccessor. This is simply a helper alias for the generic definition but takes...
Definition ValueAccessor.h:86
constexpr T zeroVal()
Return the value of type T that corresponds to zero.
Definition Math.h:70
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:833
void operator()(const typename TreeT::LeafCIter &leafIter) const
Definition Composite.h:846
TreeT *const aTree
Definition Composite.h:834
void operator()(const typename TreeT::ValueOnCIter &iter) const
Definition Composite.h:839
CompReplaceOp(TreeT &_aTree)
Definition Composite.h:836
DynamicNodeManager operator to merge two trees using a CSG difference.
Definition Merge.h:281
#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:218
#define OPENVDB_REAL_TREE_INSTANTIATE(Function)
Definition version.h.in:162
#define OPENVDB_VOLUME_TREE_INSTANTIATE(Function)
Definition version.h.in:165