16 #ifndef OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED
17 #define OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED
27 #include <tbb/task_arena.h>
28 #include <tbb/enumerable_thread_specific.h>
29 #include <tbb/parallel_for.h>
31 #include <type_traits>
105 template<
typename TreeOrLeafManagerT>
107 const int iterations = 1,
110 const bool threaded =
true);
136 template<
typename TreeOrLeafManagerT>
138 const int iterations = 1,
141 const bool threaded =
true);
147 namespace morphology {
150 template<
typename TreeType>
157 using MaskTreeT =
typename TreeType::template ValueConverter<ValueMask>::Type;
162 : mManagerPtr(new tree::LeafManager<TreeType>(tree))
163 , mManager(*mManagerPtr)
167 : mManagerPtr(nullptr)
175 inline void setThreaded(
const bool threaded) { mThreaded = threaded; }
189 const bool prune =
false);
204 const bool prune =
false,
205 const bool preserveMaskLeafNodes =
false);
213 if (masks.size() < mManager.leafCount()) {
214 masks.resize(mManager.leafCount());
217 if (this->getThreaded()) {
219 tbb::parallel_for(mManager.getRange(),
220 [&](
const tbb::blocked_range<size_t>& r){
221 for (size_t idx = r.begin(); idx < r.end(); ++idx)
222 masks[idx] = mManager.leaf(idx).getValueMask();
226 for (
size_t idx = 0; idx < mManager.leafCount(); ++idx) {
227 masks[idx] = mManager.leaf(idx).getValueMask();
241 static const Int32 LOG2DIM =
static_cast<Int32>(LeafType::LOG2DIM);
244 using Word =
typename std::conditional<LOG2DIM == 3, uint8_t,
245 typename std::conditional<LOG2DIM == 4, uint16_t,
246 typename std::conditional<LOG2DIM == 5, uint32_t,
247 typename std::conditional<LOG2DIM == 6, uint64_t,
248 void>::type>::type>::type>::type;
250 static_assert(!std::is_same<Word, void>::value,
251 "Unsupported Node Dimension for node mask dilation/erosion");
257 , mAccessor(&accessor)
273 const MaskType mask = leaf.getValueMask();
274 this->dilate(leaf, mask);
292 mNeighbours[0] = &(leaf.getValueMask());
293 this->setOrigin(leaf.origin());
297 case NN_FACE : { this->dilate6(mask);
return; }
299 assert(
false &&
"Unknown op during dilation.");
return;
317 MaskType mask = leaf.getValueMask();
318 this->erode(leaf, mask);
338 mNeighbours[0] =
const_cast<MaskType*
>(&leaf.getValueMask());
339 this->setOrigin(leaf.origin());
343 case NN_FACE : { this->erode6(mask);
return; }
345 assert(
false &&
"Unknown op during erosion.");
return;
360 void dilate6(
const MaskType& mask);
361 void dilate18(
const MaskType& mask);
362 void dilate26(
const MaskType& mask);
363 void erode6(MaskType& mask);
372 inline void setOrigin(
const Coord& origin) { mOrigin = &origin; }
373 inline const Coord& getOrigin()
const {
return *mOrigin; }
374 inline void clear() { std::fill(mNeighbours.begin(), mNeighbours.end(),
nullptr); }
376 inline void scatter(
size_t n,
int indx)
378 assert(n < mNeighbours.size());
379 assert(mNeighbours[n]);
380 mNeighbours[n]->template getWord<Word>(indx) |= mWord;
383 template<
int DX,
int DY,
int DZ>
384 inline void scatter(
size_t n,
int indx)
386 assert(n < mNeighbours.size());
387 if (!mNeighbours[n]) {
388 mNeighbours[n] = this->getNeighbour<DX,DY,DZ,true>();
390 assert(mNeighbours[n]);
391 this->scatter(n, indx - (DIM - 1)*(DY + DX*DIM));
393 inline Word gather(
size_t n,
int indx)
395 assert(n < mNeighbours.size());
396 return mNeighbours[n]->template getWord<Word>(indx);
398 template<
int DX,
int DY,
int DZ>
399 inline Word gather(
size_t n,
int indx)
401 assert(n < mNeighbours.size());
402 if (!mNeighbours[n]) {
403 mNeighbours[n] = this->getNeighbour<DX,DY,DZ,false>();
405 return this->gather(n, indx - (DIM -1)*(DY + DX*DIM));
408 void scatterFacesXY(
int x,
int y,
int i1,
int n,
int i2);
409 void scatterEdgesXY(
int x,
int y,
int i1,
int n,
int i2);
410 Word gatherFacesXY(
int x,
int y,
int i1,
int n,
int i2);
412 Word gatherEdgesXY(
int x,
int y,
int i1,
int n,
int i2);
414 template<
int DX,
int DY,
int DZ,
bool Create>
415 inline MaskType* getNeighbour()
417 const Coord xyz = mOrigin->offsetBy(DX*DIM, DY*DIM, DZ*DIM);
418 auto* leaf = mAccessor->probeLeaf(xyz);
419 if (leaf)
return &(leaf->getValueMask());
420 if (mAccessor->isValueOn(xyz))
return &mOnTile;
421 if (!Create)
return &mOffTile;
422 leaf = mAccessor->touchLeaf(xyz);
423 return &(leaf->getValueMask());
427 const Coord* mOrigin;
428 std::vector<MaskType*> mNeighbours;
429 AccessorType*
const mAccessor;
431 MaskType mOnTile, mOffTile;
436 std::unique_ptr<tree::LeafManager<TreeType>> mManagerPtr;
437 tree::LeafManager<TreeType>& mManager;
442 template <
typename TreeT>
443 typename std::enable_if<std::is_same<TreeT, typename TreeT::template ValueConverter<ValueMask>::Type>::value,
444 typename TreeT::template ValueConverter<ValueMask>::Type*>::type
447 template <
typename TreeT>
448 typename std::enable_if<!std::is_same<TreeT, typename TreeT::template ValueConverter<ValueMask>::Type>::value,
449 typename TreeT::template ValueConverter<ValueMask>::Type*>::type
453 template <
typename TreeType>
458 if (iter == 0)
return;
459 const size_t leafCount = mManager.leafCount();
460 if (leafCount == 0)
return;
461 auto& tree = mManager.tree();
491 auto computeWavefront = [&](
const size_t idx) {
492 auto& leaf = manager.
leaf(idx);
493 auto& nodemask = leaf.getValueMask();
494 if (
const auto* original = tree.probeConstLeaf(leaf.origin())) {
495 nodemask ^= original->getValueMask();
502 assert(!nodemask.isOn());
506 if (this->getThreaded()) {
507 tbb::parallel_for(manager.
getRange(),
508 [&](
const tbb::blocked_range<size_t>& r){
509 for (size_t idx = r.begin(); idx < r.end(); ++idx) {
510 computeWavefront(idx);
515 for (
size_t idx = 0; idx < manager.leafCount(); ++idx) {
516 computeWavefront(idx);
521 m.dilateVoxels(iter, nn,
false);
524 auto subtractTopology = [&](
const size_t idx) {
525 auto& leaf = mManager.leaf(idx);
526 const auto* maskleaf = mask.probeConstLeaf(leaf.origin());
528 leaf.getValueMask() -= maskleaf->getValueMask();
531 if (this->getThreaded()) {
532 tbb::parallel_for(mManager.getRange(),
533 [&](
const tbb::blocked_range<size_t>& r){
534 for (size_t idx = r.begin(); idx < r.end(); ++idx) {
535 subtractTopology(idx);
540 for (
size_t idx = 0; idx < leafCount; ++idx) {
541 subtractTopology(idx);
549 std::vector<MaskType> nodeMasks;
550 this->copyMasks(nodeMasks);
552 if (this->getThreaded()) {
553 const auto range = mManager.getRange();
554 for (
size_t i = 0; i < iter; ++i) {
557 tbb::parallel_for(range,
558 [&](
const tbb::blocked_range<size_t>& r) {
559 AccessorType accessor(tree);
560 NodeMaskOp cache(accessor, nn);
561 for (
size_t idx = r.begin(); idx < r.end(); ++idx) {
562 const auto& leaf = mManager.leaf(idx);
563 if (leaf.isEmpty())
continue;
565 MaskType& newMask = nodeMasks[idx];
566 cache.erode(leaf, newMask);
571 tbb::parallel_for(range,
572 [&](
const tbb::blocked_range<size_t>& r){
573 for (
size_t idx = r.begin(); idx < r.end(); ++idx)
574 mManager.leaf(idx).setValueMask(nodeMasks[idx]);
579 AccessorType accessor(tree);
580 NodeMaskOp cache(accessor, nn);
581 for (
size_t i = 0; i < iter; ++i) {
584 for (
size_t idx = 0; idx < leafCount; ++idx) {
585 const auto& leaf = mManager.leaf(idx);
586 if (leaf.isEmpty())
continue;
588 MaskType& newMask = nodeMasks[idx];
589 cache.erode(leaf, newMask);
592 for (
size_t idx = 0; idx < leafCount; ++idx) {
593 mManager.leaf(idx).setValueMask(nodeMasks[idx]);
602 zeroVal<typename TreeType::ValueType>(),
603 this->getThreaded());
604 mManager.rebuild(!this->getThreaded());
608 template <
typename TreeType>
612 const bool preserveMaskLeafNodes)
614 if (iter == 0)
return;
616 const bool threaded = this->getThreaded();
622 auto dilate = [iter, nn, threaded](
auto& manager,
const bool collapse) {
624 using LeafManagerT =
typename std::decay<decltype(manager)>::type;
625 using TreeT =
typename LeafManagerT::TreeType;
626 using ValueT =
typename TreeT::ValueType;
627 using LeafT =
typename TreeT::LeafNodeType;
633 TreeT& tree = manager.tree();
638 std::vector<MaskType> nodeMasks;
639 std::vector<std::unique_ptr<LeafT>> nodes;
640 const ValueT& bg = tree.background();
641 const bool steal = iter > 1;
643 for (
size_t i = 0; i < iter; ++i) {
644 if (i > 0) manager.rebuild(!threaded);
646 const size_t leafCount = manager.leafCount();
647 if (leafCount == 0)
return;
656 manager.foreach([&](LeafT& leaf,
const size_t idx) {
658 const MaskType& oldMask = nodeMasks[idx];
659 const bool dense = oldMask.isOn();
660 cache.
dilate(leaf, oldMask);
665 accessor.
addTile(1, leaf.origin(), bg,
true);
670 tree.template stealNode<LeafT>(leaf.origin(),
671 zeroVal<ValueT>(),
true));
676 if (nodes.empty())
return;
678 for (
auto& node : nodes) {
679 accessor.
addLeaf(node.release());
688 constexpr
bool isMask = std::is_same<TreeType, MaskTreeT>::value;
689 dilate(mManager, isMask &&
prune);
690 if (!isMask &&
prune) {
692 zeroVal<typename TreeType::ValueType>(),
704 std::vector<MaskLeafT*> array;
709 topology.topologyUnion(mManager.tree());
710 array.reserve(mManager.leafCount());
711 topology.stealNodes(array);
713 else if (preserveMaskLeafNodes) {
715 array.resize(mManager.leafCount());
716 tbb::parallel_for(mManager.getRange(),
717 [&](
const tbb::blocked_range<size_t>& r){
718 for (size_t idx = r.begin(); idx < r.end(); ++idx) {
719 array[idx] = new MaskLeafT(mManager.leaf(idx));
724 array.reserve(mManager.leafCount());
725 mask->stealNodes(array);
729 const size_t numThreads = size_t(tbb::this_task_arena::max_concurrency());
730 const size_t subTreeSize =
math::Max(
size_t(1), array.size()/(2*numThreads));
733 tbb::enumerable_thread_specific<std::unique_ptr<MaskTreeT>> pool;
735 tbb::parallel_for(tbb::blocked_range<MaskLeafT**>(start, start + array.size(), subTreeSize),
736 [&](
const tbb::blocked_range<MaskLeafT**>& range) {
737 std::unique_ptr<MaskTreeT> mask(new MaskTreeT);
738 for (MaskLeafT** it = range.begin(); it != range.end(); ++it) mask->addLeaf(*it);
739 tree::LeafManager<MaskTreeT> manager(*mask, range.begin(), range.end());
740 dilate(manager, prune);
741 auto& subtree = pool.local();
742 if (!subtree) subtree = std::move(mask);
743 else subtree->merge(*mask, MERGE_ACTIVE_STATES);
747 auto piter = pool.begin();
748 MaskTreeT& subtree = mask ? *mask : **piter++;
749 for (; piter != pool.end(); ++piter) subtree.merge(**piter);
752 if (
prune)
tools::prune(subtree, zeroVal<typename MaskTreeT::ValueType>(), threaded);
755 if (!mask) mManager.tree().topologyUnion(subtree,
true);
760 mManager.rebuild(!threaded);
764 template <
typename TreeType>
766 Morphology<TreeType>::NodeMaskOp::erode6(MaskType& mask)
768 for (
int x = 0; x < DIM; ++x) {
769 for (
int y = 0, n = (x << LOG2DIM); y < DIM; ++y, ++n) {
771 if (Word& w = mask.template getWord<Word>(n)) {
774 (Word(w<<1 | (this->
template gather<0,0,-1>(1, n)>>(DIM-1))) &
775 Word(w>>1 | (this->
template gather<0,0, 1>(2, n)<<(DIM-1)))));
776 w = Word(w & this->gatherFacesXY(x, y, 0, n, 3));
782 template <
typename TreeType>
784 Morphology<TreeType>::NodeMaskOp::dilate6(
const MaskType& mask)
786 for (
int x = 0; x < DIM; ++x ) {
787 for (
int y = 0, n = (x << LOG2DIM);
790 if (
const Word w = mask.template getWord<Word>(n)) {
792 this->mWord = Word(w | (w>>1) | (w<<1));
795 if ( (this->mWord = Word(w<<(DIM-1))) ) {
796 this->
template scatter< 0, 0,-1>(1, n);
799 if ( (this->mWord = Word(w>>(DIM-1))) ) {
800 this->
template scatter< 0, 0, 1>(2, n);
804 this->scatterFacesXY(x, y, 0, n, 3);
810 template <
typename TreeType>
812 Morphology<TreeType>::NodeMaskOp::dilate18(
const MaskType& mask)
815 const Coord origin = this->getOrigin();
816 const Coord orig_mz = origin.offsetBy(0, 0, -DIM);
817 const Coord orig_pz = origin.offsetBy(0, 0, DIM);
818 for (
int x = 0; x < DIM; ++x ) {
819 for (
int y = 0, n = (x << LOG2DIM); y < DIM; ++y, ++n) {
820 if (
const Word w = mask.template getWord<Word>(n)) {
822 this->mWord = Word(w | (w>>1) | (w<<1));
823 this->setOrigin(origin);
825 this->scatterFacesXY(x, y, 0, n, 3);
827 this->scatterEdgesXY(x, y, 0, n, 3);
829 if ( (this->mWord = Word(w<<(DIM-1))) ) {
830 this->setOrigin(origin);
831 this->
template scatter< 0, 0,-1>(1, n);
832 this->setOrigin(orig_mz);
833 this->scatterFacesXY(x, y, 1, n, 11);
835 if ( (this->mWord = Word(w>>(DIM-1))) ) {
836 this->setOrigin(origin);
837 this->
template scatter< 0, 0, 1>(2, n);
838 this->setOrigin(orig_pz);
839 this->scatterFacesXY(x, y, 2, n, 15);
847 template <
typename TreeType>
849 Morphology<TreeType>::NodeMaskOp::dilate26(
const MaskType& mask)
852 const Coord origin = this->getOrigin();
853 const Coord orig_mz = origin.offsetBy(0, 0, -DIM);
854 const Coord orig_pz = origin.offsetBy(0, 0, DIM);
855 for (
int x = 0; x < DIM; ++x) {
856 for (
int y = 0, n = (x << LOG2DIM); y < DIM; ++y, ++n) {
857 if (
const Word w = mask.template getWord<Word>(n)) {
859 this->mWord = Word(w | (w>>1) | (w<<1));
860 this->setOrigin(origin);
862 this->scatterFacesXY(x, y, 0, n, 3);
863 this->scatterEdgesXY(x, y, 0, n, 3);
865 if ( (this->mWord = Word(w<<(DIM-1))) ) {
866 this->setOrigin(origin);
867 this->
template scatter< 0, 0,-1>(1, n);
868 this->setOrigin(orig_mz);
869 this->scatterFacesXY(x, y, 1, n, 11);
870 this->scatterEdgesXY(x, y, 1, n, 11);
872 if ( (this->mWord = Word(w>>(DIM-1))) ) {
873 this->setOrigin(origin);
874 this->
template scatter< 0, 0, 1>(2, n);
875 this->setOrigin(orig_pz);
876 this->scatterFacesXY(x, y, 2, n, 19);
877 this->scatterEdgesXY(x, y, 2, n, 19);
884 template<
typename TreeType>
886 Morphology<TreeType>::NodeMaskOp::scatterFacesXY(
int x,
int y,
int i1,
int n,
int i2)
890 this->scatter(i1, n-DIM);
892 this->
template scatter<-1, 0, 0>(i2, n);
896 this->scatter(i1, n+DIM);
898 this->
template scatter< 1, 0, 0>(i2+1, n);
902 this->scatter(i1, n-1);
904 this->
template scatter< 0,-1, 0>(i2+2, n);
908 this->scatter(i1, n+1);
910 this->
template scatter< 0, 1, 0>(i2+3, n);
915 template<
typename TreeType>
917 Morphology<TreeType>::NodeMaskOp::scatterEdgesXY(
int x,
int y,
int i1,
int n,
int i2)
921 this->scatter(i1, n-DIM-1);
923 this->
template scatter< 0,-1, 0>(i2+2, n-DIM);
926 this->scatter(i1, n-DIM+1);
928 this->
template scatter< 0, 1, 0>(i2+3, n-DIM);
932 this->
template scatter<-1, 0, 0>(i2 , n+1);
934 this->
template scatter<-1, 1, 0>(i2+7, n );
937 this->
template scatter<-1, 0, 0>(i2 , n-1);
939 this->
template scatter<-1,-1, 0>(i2+4, n );
944 this->scatter(i1, n+DIM-1);
946 this->
template scatter< 0,-1, 0>(i2+2, n+DIM);
949 this->scatter(i1, n+DIM+1);
951 this->
template scatter< 0, 1, 0>(i2+3, n+DIM);
955 this->
template scatter< 1, 0, 0>(i2+1, n-1);
957 this->
template scatter< 1,-1, 0>(i2+6, n );
960 this->
template scatter< 1, 0, 0>(i2+1, n+1);
962 this->
template scatter< 1, 1, 0>(i2+5, n );
968 template<
typename TreeType>
969 inline typename Morphology<TreeType>::NodeMaskOp::Word
970 Morphology<TreeType>::NodeMaskOp::gatherFacesXY(
int x,
int y,
int i1,
int n,
int i2)
974 this->gather(i1, n - DIM) :
975 this->template gather<-1,0,0>(i2, n);
978 w = Word(w & (x < DIM - 1 ?
979 this->gather(i1, n + DIM) :
980 this->
template gather<1,0,0>(i2 + 1, n)));
983 w = Word(w & (y > 0 ?
984 this->gather(i1, n - 1) :
985 this->
template gather<0,-1,0>(i2 + 2, n)));
988 w = Word(w & (y < DIM - 1 ?
989 this->gather(i1, n + 1) :
990 this->
template gather<0,1,0>(i2+3, n)));
996 template<
typename TreeType>
997 inline typename Morphology<TreeType>::NodeMaskOp::Word
998 Morphology<TreeType>::NodeMaskOp::gatherEdgesXY(
int x,
int y,
int i1,
int n,
int i2)
1003 w &= y > 0 ? this->gather(i1, n-DIM-1) :
1004 this->template gather< 0,-1, 0>(i2+2, n-DIM);
1005 w &= y < DIM-1 ? this->gather(i1, n-DIM+1) :
1006 this->template gather< 0, 1, 0>(i2+3, n-DIM);
1008 w &= y < DIM-1 ? this->
template gather<-1, 0, 0>(i2 , n+1):
1009 this->
template gather<-1, 1, 0>(i2+7, n );
1010 w &= y > 0 ? this->
template gather<-1, 0, 0>(i2 , n-1):
1011 this->
template gather<-1,-1, 0>(i2+4, n );
1014 w &= y > 0 ? this->gather(i1, n+DIM-1) :
1015 this->template gather< 0,-1, 0>(i2+2, n+DIM);
1016 w &= y < DIM-1 ? this->gather(i1, n+DIM+1) :
1017 this->template gather< 0, 1, 0>(i2+3, n+DIM);
1019 w &= y > 0 ? this->
template gather< 1, 0, 0>(i2+1, n-1):
1020 this->template gather< 1,-1, 0>(i2+6, n );
1021 w &= y < DIM-1 ? this->
template gather< 1, 0, 0>(i2+1, n+1):
1022 this->template gather< 1, 1, 0>(i2+5, n );
1036 namespace morph_internal {
1037 template <
typename T>
struct Adapter {
1039 static TreeType& get(T& tree) {
return tree; }
1040 static void sync(T&) {}
1042 template <
typename T>
1043 struct Adapter<openvdb::tree::LeafManager<T>> {
1045 static TreeType& get(openvdb::tree::LeafManager<T>& M) {
return M.tree(); }
1046 static void sync(openvdb::tree::LeafManager<T>& M) { M.rebuild(); }
1052 template<
typename TreeOrLeafManagerT>
1054 const int iterations,
1057 const bool threaded)
1059 using AdapterT = morph_internal::Adapter<TreeOrLeafManagerT>;
1060 using TreeT =
typename AdapterT::TreeType;
1061 using MaskT =
typename TreeT::template ValueConverter<ValueMask>::Type;
1063 if (iterations <= 0)
return;
1069 morph.
dilateVoxels(
static_cast<size_t>(iterations), nn,
false);
1076 auto& tree = AdapterT::get(treeOrLeafM);
1081 constexpr
bool isMask = std::is_same<TreeT, MaskT>::value;
1084 tree.voxelizeActiveTiles();
1085 AdapterT::sync(treeOrLeafM);
1090 morph.
dilateVoxels(
static_cast<size_t>(iterations), nn,
true);
1094 morph.
dilateVoxels(
static_cast<size_t>(iterations), nn,
false);
1111 topology.topologyUnion(tree);
1112 topology.voxelizeActiveTiles();
1116 morph.
dilateVoxels(
static_cast<size_t>(iterations), nn,
true);
1118 tree.topologyUnion(topology,
true);
1124 tools::prune(tree, zeroVal<typename TreeT::ValueType>(), threaded);
1125 AdapterT::sync(treeOrLeafM);
1129 template<
typename TreeOrLeafManagerT>
1131 const int iterations,
1134 const bool threaded)
1136 using AdapterT = morph_internal::Adapter<TreeOrLeafManagerT>;
1137 using TreeT =
typename AdapterT::TreeType;
1138 using MaskT =
typename TreeT::template ValueConverter<ValueMask>::Type;
1140 if (iterations <= 0)
return;
1146 auto& tree = AdapterT::get(treeOrLeafM);
1148 topology.topologyUnion(tree);
1149 topology.voxelizeActiveTiles();
1154 morph.
erodeVoxels(
static_cast<size_t>(iterations), nn,
false);
1159 tools::prune(topology, zeroVal<typename MaskT::ValueType>(), threaded);
1160 tree.topologyIntersection(topology);
1161 AdapterT::sync(treeOrLeafM);
1168 auto& tree = AdapterT::get(treeOrLeafM);
1169 if (tree.hasActiveTiles()) {
1170 tree.voxelizeActiveTiles();
1171 AdapterT::sync(treeOrLeafM);
1178 morph.
erodeVoxels(
static_cast<size_t>(iterations), nn,
false);
1198 template<
typename TreeType>
1199 OPENVDB_DEPRECATED_MESSAGE(
"Switch to tools::dilateActiveValues. Use tools::IGNORE_TILES to maintain same (but perhaps unintended) behaviour")
1204 if (iterations <= 0)
return;
1208 morph.
dilateVoxels(
static_cast<size_t>(iterations), nn,
false);
1225 template<
typename TreeType>
1226 OPENVDB_DEPRECATED_MESSAGE(
"Switch to tools::dilateActiveValues. Use tools::IGNORE_TILES to maintain same (but perhaps unintended) behaviour")
1231 if (iterations <= 0)
return;
1234 morph.
dilateVoxels(
static_cast<size_t>(iterations), nn,
false);
1243 template<
typename TreeType>
1244 OPENVDB_DEPRECATED_MESSAGE(
"Switch to tools::erodeActiveValues. Use tools::IGNORE_TILES to maintain same (but perhaps unintended) behaviour")
1249 if (iterations > 0) {
1252 morph.
erodeVoxels(
static_cast<size_t>(iterations), nn,
false);
1258 template<
typename TreeType>
1259 OPENVDB_DEPRECATED_MESSAGE(
"Switch to tools::erodeActiveValues. Use tools::IGNORE_TILES to maintain same (but perhaps unintended) behaviour")
1264 if (iterations <= 0)
return;
1267 morph.
erodeVoxels(
static_cast<size_t>(iterations), nn,
false);
Implementation of topological activation/deactivation.
A LeafManager manages a linear array of pointers to a given tree's leaf nodes, as well as optional au...
Defined various multi-threaded utility functions for trees.
Definition: Exceptions.h:61
Tag dispatch class that distinguishes topology copy constructors from deep copy constructors.
Definition: Types.h:564
Signed (x, y, z) 32-bit integer coordinates.
Definition: Coord.h:25
RangeType getRange(size_t grainsize=1) const
Return a tbb::blocked_range of leaf array indices.
Definition: LeafManager.h:342
LeafType & leaf(size_t leafIdx) const
Return a pointer to the leaf node at index leafIdx in the array.
Definition: LeafManager.h:318
void addLeaf(LeafNodeT *leaf)
Add the specified leaf to this tree, possibly creating a child branch in the process....
Definition: ValueAccessor.h:329
void addTile(Index level, const Coord &xyz, const ValueType &value, bool state)
Add a tile at the specified tree level that contains voxel (x, y, z), possibly deleting existing node...
Definition: ValueAccessor.h:337
const Type & Max(const Type &a, const Type &b)
Return the maximum of two values.
Definition: Math.h:598
int32_t Int32
Definition: Types.h:56
Definition: Exceptions.h:13
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
#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:180