HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Morphology.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 Morphology.h
5 ///
6 /// @brief Implementation of morphological dilation and erosion.
7 ///
8 /// @note By design the morphological operations only change the
9 /// state of voxels, not their values. If one desires to
10 /// change the values of voxels that change state an efficient
11 /// technique is to construct a boolean mask by performing a
12 /// topology difference between the original and final grids.
13 ///
14 /// @todo Extend erosion with 18 and 26 neighbors (coming soon!)
15 ///
16 /// @author Ken Museth
17 ///
18 
19 #ifndef OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED
20 #define OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED
21 
22 #include <openvdb/Types.h>
23 #include <openvdb/Grid.h>
24 #include <openvdb/math/Math.h> // for isApproxEqual()
28 #include "Prune.h"// for pruneLevelSet
29 #include "ValueTransformer.h" // for foreach()
30 #include <tbb/tbb_thread.h>
31 #include <tbb/task_scheduler_init.h>
32 #include <tbb/enumerable_thread_specific.h>
33 #include <tbb/parallel_for.h>
34 #include <functional>
35 #include <type_traits>
36 #include <vector>
37 
38 
39 namespace openvdb {
41 namespace OPENVDB_VERSION_NAME {
42 namespace tools {
43 
44 /// @brief Voxel topology of nearest neighbors
45 /// @details
46 /// <dl>
47 /// <dt><b>NN_FACE</b>
48 /// <dd>face adjacency (6 nearest neighbors, defined as all neighbor
49 /// voxels connected along one of the primary axes)
50 ///
51 /// <dt><b>NN_FACE_EDGE</b>
52 /// <dd>face and edge adjacency (18 nearest neighbors, defined as all
53 /// neighbor voxels connected along either one or two of the primary axes)
54 ///
55 /// <dt><b>NN_FACE_EDGE_VERTEX</b>
56 /// <dd>face, edge and vertex adjacency (26 nearest neighbors, defined
57 /// as all neighbor voxels connected along either one, two or all
58 /// three of the primary axes)
59 /// </dl>
61 
62 /// @brief Different policies when dilating trees with active tiles
63 /// @details
64 /// <dl>
65 /// <dt><b>IGNORE_TILES</b>
66 /// <dd>Active tiles are ignores, i.e. only active voxels are dilates.
67 ///
68 /// <dt><b>EXPAND_TILES</b>
69 /// <dd>Active tiles are expanded into active voxels and then dilated.
70 ///
71 /// <dt><b>PRESERVE_TILES</b>
72 /// <dd>Active tiles remain unchanged but they still contribute to the
73 /// dilation as if they were active voxels.
74 /// </dl>
76 
77 /// @brief Topologically dilate all active values (i.e. both voxels
78 /// and tiles) in a tree using one of three nearest neighbor
79 /// connectivity patterns.
80 /// @note This method is fully multi-threaded and support active tiles!
81 ///
82 /// @param tree tree to be dilated
83 /// @param iterations number of iterations to apply the dilation
84 /// @param nn connectivity pattern of the dilation: either
85 /// face-adjacent (6 nearest neighbors), face- and edge-adjacent
86 /// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26
87 /// nearest neighbors).
88 /// @param mode Defined the policy for handling active tiles
89 /// (see above for details)
90 ///
91 /// @note The values of any voxels are unchanged.
92 template<typename TreeType>
93 inline void dilateActiveValues(TreeType& tree,
94  int iterations = 1,
97 
98 /// @brief Topologically dilate all active values (i.e. both voxels
99 /// and tiles) in a tree using one of three nearest neighbor
100 /// connectivity patterns.
101 ///
102 /// @warning Unlike the method above this one takes a LeafManger,
103 /// however (unlike dilateVoxels method below) it offers no performance
104 /// advantage over the one that takes a tree. Its merely included for
105 /// API compatability. The leaf nodes in the manger are updated
106 /// after the dilation, which incurres a (very small) overhead.
107 ///
108 /// @note This method is fully multi-threaded and support active tiles!
109 ///
110 /// @param manager Leaf node manager for the tree to be dilated.
111 /// On exit it is updated to include all the leaf
112 /// nodes of the dilated tree.
113 /// @param iterations number of iterations to apply the dilation
114 /// @param nn connectivity pattern of the dilation: either
115 /// face-adjacent (6 nearest neighbors), face- and edge-adjacent
116 /// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26
117 /// nearest neighbors).
118 /// @param mode Defined the policy for handling active tiles
119 /// (see above for details)
120 ///
121 /// @note The values of any voxels are unchanged.
122 template<typename TreeType>
124  int iterations = 1,
127 
128 
129 /// @brief Topologically dilate all leaf-level active voxels in a tree
130 /// using one of three nearest neighbor connectivity patterns.
131 /// @warning This method is NOT multi-threaded and ignores active tiles!
132 ///
133 /// @param tree tree to be dilated
134 /// @param iterations number of iterations to apply the dilation
135 /// @param nn connectivity pattern of the dilation: either
136 /// face-adjacent (6 nearest neighbors), face- and edge-adjacent
137 /// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26
138 /// nearest neighbors).
139 ///
140 /// @note The values of any voxels are unchanged.
141 template<typename TreeType>
142 inline void dilateVoxels(TreeType& tree,
143  int iterations = 1,
145 
146 /// @brief Topologically dilate all leaf-level active voxels in a tree
147 /// using one of three nearest neighbor connectivity patterns.
148 /// @warning This method is NOT multi-threaded and ignores active tiles!
149 ///
150 /// @param manager LeafManager containing the tree to be dilated.
151 /// On exit it is updated to include all the leaf
152 /// nodes of the dilated tree.
153 /// @param iterations number of iterations to apply the dilation
154 /// @param nn connectivity pattern of the dilation: either
155 /// face-adjacent (6 nearest neighbors), face- and edge-adjacent
156 /// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26
157 /// nearest neighbors).
158 ///
159 /// @note The values of any voxels are unchanged.
160 template<typename TreeType>
161 inline void dilateVoxels(tree::LeafManager<TreeType>& manager,
162  int iterations = 1,
164 
165 
166 //@{
167 /// @brief Topologically erode all leaf-level active voxels in the given tree.
168 /// @details That is, shrink the set of active voxels by @a iterations voxels
169 /// in the +x, -x, +y, -y, +z and -z directions, but don't change the values
170 /// of any voxels, only their active states.
171 /// @todo Currently operates only on leaf voxels; need to extend to tiles.
172 template<typename TreeType>
173 inline void erodeVoxels(TreeType& tree,
174  int iterations=1,
176 
177 template<typename TreeType>
178 inline void erodeVoxels(tree::LeafManager<TreeType>& manager,
179  int iterations = 1,
181 //@}
182 
183 
184 /// @brief Mark as active any inactive tiles or voxels in the given grid or tree
185 /// whose values are equal to @a value (optionally to within the given @a tolerance).
186 template<typename GridOrTree>
187 inline void activate(
188  GridOrTree&,
189  const typename GridOrTree::ValueType& value,
190  const typename GridOrTree::ValueType& tolerance = zeroVal<typename GridOrTree::ValueType>()
191 );
192 
193 
194 /// @brief Mark as inactive any active tiles or voxels in the given grid or tree
195 /// whose values are equal to @a value (optionally to within the given @a tolerance).
196 template<typename GridOrTree>
197 inline void deactivate(
198  GridOrTree&,
199  const typename GridOrTree::ValueType& value,
200  const typename GridOrTree::ValueType& tolerance = zeroVal<typename GridOrTree::ValueType>()
201 );
202 
203 
204 ////////////////////////////////////////
205 
206 
207 /// Mapping from a Log2Dim to a data type of size 2^Log2Dim bits
208 template<Index Log2Dim> struct DimToWord {};
209 template<> struct DimToWord<3> { using Type = uint8_t; };
210 template<> struct DimToWord<4> { using Type = uint16_t; };
211 template<> struct DimToWord<5> { using Type = uint32_t; };
212 template<> struct DimToWord<6> { using Type = uint64_t; };
213 
214 
215 ////////////////////////////////////////
216 
217 
218 template<typename TreeType>
220 {
221 public:
223 
224  Morphology(TreeType& tree):
225  mOwnsManager(true), mManager(new ManagerType(tree)), mAcc(tree), mSteps(1) {}
227  mOwnsManager(false), mManager(mgr), mAcc(mgr->tree()), mSteps(1) {}
228  virtual ~Morphology() { if (mOwnsManager) delete mManager; }
229 
230  /// @brief Face-adjacent dilation pattern
231  void dilateVoxels6();
232  /// @brief Face- and edge-adjacent dilation pattern.
233  void dilateVoxels18();
234  /// @brief Face-, edge- and vertex-adjacent dilation pattern.
235  void dilateVoxels26();
236  void dilateVoxels(int iterations = 1, NearestNeighbors nn = NN_FACE);
237 
238  /// @brief Face-adjacent erosion pattern.
239  void erodeVoxels6() { mSteps = 1; this->doErosion(NN_FACE); }
240  /// @brief Face- and edge-adjacent erosion pattern.
241  void erodeVoxels18() { mSteps = 1; this->doErosion(NN_FACE_EDGE); }
242  /// @brief Face-, edge- and vertex-adjacent erosion pattern.
244  void erodeVoxels(int iterations = 1, NearestNeighbors nn = NN_FACE)
245  {
246  mSteps = iterations;
247  this->doErosion(nn);
248  }
249 
250 protected:
251 
252  void doErosion(NearestNeighbors nn);
253 
254  using LeafType = typename TreeType::LeafNodeType;
255  using MaskType = typename LeafType::NodeMaskType;
257 
258  const bool mOwnsManager;
261  int mSteps;
262 
263  static const int LEAF_DIM = LeafType::DIM;
264  static const int END = LEAF_DIM - 1;
265  static const int LEAF_LOG2DIM = LeafType::LOG2DIM;
267 
268  struct Neighbor {
269  LeafType* leaf;//null if a tile
270  bool init;//true if initialization is required
271  bool isOn;//true if an active tile
272  Neighbor() : leaf(nullptr), init(true) {}
273  inline void clear() { leaf = nullptr; init = true; }
274  template<int DX, int DY, int DZ>
275  void scatter(AccessorType& acc, const Coord &xyz, int indx, Word mask)
276  {
277  if (init) {
278  init = false;
279  Coord orig = xyz.offsetBy(DX*LEAF_DIM, DY*LEAF_DIM, DZ*LEAF_DIM);
280  leaf = acc.probeLeaf(orig);
281  if ((leaf == nullptr) && !acc.isValueOn(orig)) leaf = acc.touchLeaf(orig);
282  }
283  static const int N = (LEAF_DIM - 1)*(DY + DX*LEAF_DIM);
284  if (leaf) leaf->getValueMask().template getWord<Word>(indx-N) |= mask;
285  }
286 
287  template<int DX, int DY, int DZ>
288  Word gather(AccessorType& acc, const Coord &xyz, int indx)
289  {
290  if (init) {
291  init = false;
292  Coord orig = xyz.offsetBy(DX*LEAF_DIM, DY*LEAF_DIM, DZ*LEAF_DIM);
293  leaf = acc.probeLeaf(orig);
294  isOn = leaf ? false : acc.isValueOn(orig);
295  }
296  static const int N = (LEAF_DIM -1 )*(DY + DX*LEAF_DIM);
297  return leaf ? leaf->getValueMask().template getWord<Word>(indx-N)
298  : isOn ? ~Word(0) : Word(0);
299  }
300  };// Neighbor
301 
302  struct LeafCache
303  {
304  LeafCache(size_t n, TreeType& tree) : size(n), leafs(new LeafType*[n]), acc(tree)
305  {
306  onTile.setValuesOn();
307  this->clear();
308  }
309  ~LeafCache() { delete [] leafs; }
310  LeafType*& operator[](int offset) { return leafs[offset]; }
311  inline void clear() { for (size_t i = 0; i < size; ++i) leafs[i] = nullptr; }
312  inline void setOrigin(const Coord& xyz) { origin = &xyz; }
313  inline void scatter(int n, int indx)
314  {
315  assert(leafs[n]);
316  leafs[n]->getValueMask().template getWord<Word>(indx) |= mask;
317  }
318  template<int DX, int DY, int DZ>
319  inline void scatter(int n, int indx)
320  {
321  if (!leafs[n]) {
322  const Coord xyz = origin->offsetBy(DX*LEAF_DIM, DY*LEAF_DIM, DZ*LEAF_DIM);
323  leafs[n] = acc.probeLeaf(xyz);
324  if (!leafs[n]) leafs[n] = acc.isValueOn(xyz) ? &onTile : acc.touchLeaf(xyz);
325  }
326  this->scatter(n, indx - (LEAF_DIM - 1)*(DY + DX*LEAF_DIM));
327  }
328  inline Word gather(int n, int indx)
329  {
330  assert(leafs[n]);
331  return leafs[n]->getValueMask().template getWord<Word>(indx);
332  }
333  template<int DX, int DY, int DZ>
334  inline Word gather(int n, int indx)
335  {
336  if (!leafs[n]) {
337  const Coord xyz = origin->offsetBy(DX*LEAF_DIM, DY*LEAF_DIM, DZ*LEAF_DIM);
338  leafs[n] = acc.probeLeaf(xyz);
339  if (!leafs[n]) leafs[n] = acc.isValueOn(xyz) ? &onTile : &offTile;
340  }
341  return this->gather(n, indx - (LEAF_DIM -1 )*(DY + DX*LEAF_DIM));
342  }
343  // Scatters in the xy face-directions relative to leaf i1
344  void scatterFacesXY(int x, int y, int i1, int n, int i2);
345 
346  // Scatters in the xy edge-directions relative to leaf i1
347  void scatterEdgesXY(int x, int y, int i1, int n, int i2);
348 
349  Word gatherFacesXY(int x, int y, int i1, int n, int i2);
350 
351  Word gatherEdgesXY(int x, int y, int i1, int n, int i2);
352 
353  const Coord* origin;
354  size_t size;
359  };// LeafCache
360 
361  struct ErodeVoxelsOp {
362  using RangeT = tbb::blocked_range<size_t>;
363  ErodeVoxelsOp(std::vector<MaskType>& masks, ManagerType& manager)
364  : mTask(nullptr), mSavedMasks(masks) , mManager(manager) {}
365  void runParallel(NearestNeighbors nn);
366  void operator()(const RangeT& r) const {mTask(const_cast<ErodeVoxelsOp*>(this), r);}
367  void erode6( const RangeT&) const;
368  void erode18(const RangeT&) const;
369  void erode26(const RangeT&) const;
370  private:
371  using FuncT = typename std::function<void (ErodeVoxelsOp*, const RangeT&)>;
372  FuncT mTask;
373  std::vector<MaskType>& mSavedMasks;
374  ManagerType& mManager;
375  };// ErodeVoxelsOp
376 
377  struct MaskManager {
378  MaskManager(std::vector<MaskType>& masks, ManagerType& manager)
379  : mMasks(masks) , mManager(manager), mSaveMasks(true) {}
380 
381  void save() { mSaveMasks = true; tbb::parallel_for(mManager.getRange(), *this); }
382  void update() { mSaveMasks = false; tbb::parallel_for(mManager.getRange(), *this); }
383  void operator()(const tbb::blocked_range<size_t>& range) const
384  {
385  if (mSaveMasks) {
386  for (size_t i = range.begin(); i < range.end(); ++i) {
387  mMasks[i] = mManager.leaf(i).getValueMask();
388  }
389  } else {
390  for (size_t i = range.begin(); i < range.end(); ++i) {
391  mManager.leaf(i).setValueMask(mMasks[i]);
392  }
393  }
394  }
395  private:
396  std::vector<MaskType>& mMasks;
397  ManagerType& mManager;
398  bool mSaveMasks;
399  };// MaskManager
400 
401  struct UpdateMasks {
402  UpdateMasks(const std::vector<MaskType>& masks, ManagerType& manager)
403  : mMasks(masks), mManager(manager) {}
405  void operator()(const tbb::blocked_range<size_t>& r) const {
406  for (size_t i=r.begin(); i<r.end(); ++i) mManager.leaf(i).setValueMask(mMasks[i]);
407  }
408  const std::vector<MaskType>& mMasks;
410  };
411  struct CopyMasks {
412  CopyMasks(std::vector<MaskType>& masks, const ManagerType& manager)
413  : mMasks(masks), mManager(manager) {}
414  void copy() { tbb::parallel_for(mManager.getRange(), *this); }
415  void operator()(const tbb::blocked_range<size_t>& r) const {
416  for (size_t i=r.begin(); i<r.end(); ++i) mMasks[i]=mManager.leaf(i).getValueMask();
417  }
418  std::vector<MaskType>& mMasks;
420  };
421  void copyMasks(std::vector<MaskType>& a, const ManagerType& b) {CopyMasks c(a, b); c.copy();}
422 };// Morphology
423 
424 
425 template<typename TreeType>
426 inline void
428 {
429  for (int i=0; i<iterations; ++i) {
430  switch (nn) {
431  case NN_FACE_EDGE:
432  this->dilateVoxels18();
433  break;
434  case NN_FACE_EDGE_VERTEX:
435  this->dilateVoxels26();
436  break;
437  case NN_FACE:
438  default:
439  this->dilateVoxels6();
440  }
441  }
442 }
443 
444 
445 template<typename TreeType>
446 inline void
448 {
449  /// @todo Currently operates only on leaf voxels; need to extend to tiles.
450  const int leafCount = static_cast<int>(mManager->leafCount());
451 
452  // Save the value masks of all leaf nodes.
453  std::vector<MaskType> savedMasks(leafCount);
454  this->copyMasks(savedMasks, *mManager);
455  LeafCache cache(7, mManager->tree());
456  for (int leafIdx = 0; leafIdx < leafCount; ++leafIdx) {
457  const MaskType& oldMask = savedMasks[leafIdx];//original bit-mask of current leaf node
458  cache[0] = &mManager->leaf(leafIdx);
459  cache.setOrigin(cache[0]->origin());
460  for (int x = 0; x < LEAF_DIM; ++x ) {
461  for (int y = 0, n = (x << LEAF_LOG2DIM); y < LEAF_DIM; ++y, ++n) {
462  // Extract the portion of the original mask that corresponds to a row in z.
463  if (const Word w = oldMask.template getWord<Word>(n)) {
464 
465  // Dilate the current leaf in the +z and -z direction
466  cache.mask = Word(w | (w>>1) | (w<<1)); cache.scatter(0, n);
467 
468  // Dilate into neighbor leaf in the -z direction
469  if ( (cache.mask = Word(w<<(LEAF_DIM-1))) ) {
470  cache.template scatter< 0, 0,-1>(1, n);
471  }
472  // Dilate into neighbor leaf in the +z direction
473  if ( (cache.mask = Word(w>>(LEAF_DIM-1))) ) {
474  cache.template scatter< 0, 0, 1>(2, n);
475  }
476  // Dilate in the xy-face directions relative to the center leaf
477  cache.mask = w; cache.scatterFacesXY(x, y, 0, n, 3);
478  }
479  }// loop over y
480  }//loop over x
481  cache.clear();
482  }//loop over leafs
483 
484  mManager->rebuildLeafArray();
485 }//dilateVoxels6
486 
487 
488 template<typename TreeType>
489 inline void
491 {
492  /// @todo Currently operates only on leaf voxels; need to extend to tiles.
493  const int leafCount = static_cast<int>(mManager->leafCount());
494 
495  // Save the value masks of all leaf nodes.
496  std::vector<MaskType> savedMasks(leafCount);
497  this->copyMasks(savedMasks, *mManager);
498  LeafCache cache(19, mManager->tree());
499  Coord orig_mz, orig_pz;//origins of neighbor leaf nodes in the -z and +z directions
500  for (int leafIdx = 0; leafIdx < leafCount; ++leafIdx) {
501  const MaskType& oldMask = savedMasks[leafIdx];//original bit-mask of current leaf node
502  cache[0] = &mManager->leaf(leafIdx);
503  orig_mz = cache[0]->origin().offsetBy(0, 0, -LEAF_DIM);
504  orig_pz = cache[0]->origin().offsetBy(0, 0, LEAF_DIM);
505  for (int x = 0; x < LEAF_DIM; ++x ) {
506  for (int y = 0, n = (x << LEAF_LOG2DIM); y < LEAF_DIM; ++y, ++n) {
507  if (const Word w = oldMask.template getWord<Word>(n)) {
508  {
509  cache.mask = Word(w | (w>>1) | (w<<1));
510  cache.setOrigin(cache[0]->origin());
511  cache.scatter(0, n);
512  cache.scatterFacesXY(x, y, 0, n, 3);
513  cache.mask = w;
514  cache.scatterEdgesXY(x, y, 0, n, 3);
515  }
516  if ( (cache.mask = Word(w<<(LEAF_DIM-1))) ) {
517  cache.setOrigin(cache[0]->origin());
518  cache.template scatter< 0, 0,-1>(1, n);
519  cache.setOrigin(orig_mz);
520  cache.scatterFacesXY(x, y, 1, n, 11);
521  }
522  if ( (cache.mask = Word(w>>(LEAF_DIM-1))) ) {
523  cache.setOrigin(cache[0]->origin());
524  cache.template scatter< 0, 0, 1>(2, n);
525  cache.setOrigin(orig_pz);
526  cache.scatterFacesXY(x, y, 2, n, 15);
527  }
528  }
529  }// loop over y
530  }//loop over x
531  cache.clear();
532  }//loop over leafs
533 
534  mManager->rebuildLeafArray();
535 }// dilateVoxels18
536 
537 
538 template<typename TreeType>
539 inline void
541 {
542  const int leafCount = static_cast<int>(mManager->leafCount());
543 
544  // Save the value masks of all leaf nodes.
545  std::vector<MaskType> savedMasks(leafCount);
546  this->copyMasks(savedMasks, *mManager);
547  LeafCache cache(27, mManager->tree());
548  Coord orig_mz, orig_pz;//origins of neighbor leaf nodes in the -z and +z directions
549  for (int leafIdx = 0; leafIdx < leafCount; ++leafIdx) {
550  const MaskType& oldMask = savedMasks[leafIdx];//original bit-mask of current leaf node
551  cache[0] = &mManager->leaf(leafIdx);
552  orig_mz = cache[0]->origin().offsetBy(0, 0, -LEAF_DIM);
553  orig_pz = cache[0]->origin().offsetBy(0, 0, LEAF_DIM);
554  for (int x = 0; x < LEAF_DIM; ++x ) {
555  for (int y = 0, n = (x << LEAF_LOG2DIM); y < LEAF_DIM; ++y, ++n) {
556  if (const Word w = oldMask.template getWord<Word>(n)) {
557  {
558  cache.mask = Word(w | (w>>1) | (w<<1));
559  cache.setOrigin(cache[0]->origin());
560  cache.scatter(0, n);
561  cache.scatterFacesXY(x, y, 0, n, 3);
562  cache.scatterEdgesXY(x, y, 0, n, 3);
563  }
564  if ( (cache.mask = Word(w<<(LEAF_DIM-1))) ) {
565  cache.setOrigin(cache[0]->origin());
566  cache.template scatter< 0, 0,-1>(1, n);
567  cache.setOrigin(orig_mz);
568  cache.scatterFacesXY(x, y, 1, n, 11);
569  cache.scatterEdgesXY(x, y, 1, n, 11);
570  }
571  if ( (cache.mask = Word(w>>(LEAF_DIM-1))) ) {
572  cache.setOrigin(cache[0]->origin());
573  cache.template scatter< 0, 0, 1>(2, n);
574  cache.setOrigin(orig_pz);
575  cache.scatterFacesXY(x, y, 2, n, 19);
576  cache.scatterEdgesXY(x, y, 2, n, 19);
577  }
578  }
579  }// loop over y
580  }//loop over x
581  cache.clear();
582  }//loop over leafs
583 
584  mManager->rebuildLeafArray();
585 }// dilateVoxels26
586 
587 
588 template<typename TreeType>
589 inline void
590 Morphology<TreeType>::LeafCache::scatterFacesXY(int x, int y, int i1, int n, int i2)
591 {
592  // dilate current leaf or neighbor in the -x direction
593  if (x > 0) {
594  this->scatter(i1, n-LEAF_DIM);
595  } else {
596  this->template scatter<-1, 0, 0>(i2, n);
597  }
598  // dilate current leaf or neighbor in the +x direction
599  if (x < LEAF_DIM-1) {
600  this->scatter(i1, n+LEAF_DIM);
601  } else {
602  this->template scatter< 1, 0, 0>(i2+1, n);
603  }
604  // dilate current leaf or neighbor in the -y direction
605  if (y > 0) {
606  this->scatter(i1, n-1);
607  } else {
608  this->template scatter< 0,-1, 0>(i2+2, n);
609  }
610  // dilate current leaf or neighbor in the +y direction
611  if (y < LEAF_DIM-1) {
612  this->scatter(i1, n+1);
613  } else {
614  this->template scatter< 0, 1, 0>(i2+3, n);
615  }
616 }
617 
618 
619 template<typename TreeType>
620 inline void
621 Morphology<TreeType>::LeafCache::scatterEdgesXY(int x, int y, int i1, int n, int i2)
622 {
623  if (x > 0) {
624  if (y > 0) {
625  this->scatter(i1, n-LEAF_DIM-1);
626  } else {
627  this->template scatter< 0,-1, 0>(i2+2, n-LEAF_DIM);
628  }
629  if (y < LEAF_DIM-1) {
630  this->scatter(i1, n-LEAF_DIM+1);
631  } else {
632  this->template scatter< 0, 1, 0>(i2+3, n-LEAF_DIM);
633  }
634  } else {
635  if (y < LEAF_DIM-1) {
636  this->template scatter<-1, 0, 0>(i2 , n+1);
637  } else {
638  this->template scatter<-1, 1, 0>(i2+7, n );
639  }
640  if (y > 0) {
641  this->template scatter<-1, 0, 0>(i2 , n-1);
642  } else {
643  this->template scatter<-1,-1, 0>(i2+4, n );
644  }
645  }
646  if (x < LEAF_DIM-1) {
647  if (y > 0) {
648  this->scatter(i1, n+LEAF_DIM-1);
649  } else {
650  this->template scatter< 0,-1, 0>(i2+2, n+LEAF_DIM);
651  }
652  if (y < LEAF_DIM-1) {
653  this->scatter(i1, n+LEAF_DIM+1);
654  } else {
655  this->template scatter< 0, 1, 0>(i2+3, n+LEAF_DIM);
656  }
657  } else {
658  if (y > 0) {
659  this->template scatter< 1, 0, 0>(i2+1, n-1);
660  } else {
661  this->template scatter< 1,-1, 0>(i2+6, n );
662  }
663  if (y < LEAF_DIM-1) {
664  this->template scatter< 1, 0, 0>(i2+1, n+1);
665  } else {
666  this->template scatter< 1, 1, 0>(i2+5, n );
667  }
668  }
669 }
670 
671 
672 template<typename TreeType>
673 inline void
675 {
676  namespace ph = std::placeholders;
677  switch (nn) {
678  case NN_FACE_EDGE:
679  mTask = std::bind(&ErodeVoxelsOp::erode18, ph::_1, ph::_2);
680  break;
681  case NN_FACE_EDGE_VERTEX:
682  mTask = std::bind(&ErodeVoxelsOp::erode26, ph::_1, ph::_2);
683  break;
684  case NN_FACE:
685  default:
686  mTask = std::bind(&ErodeVoxelsOp::erode6, ph::_1, ph::_2);
687  }
689 }
690 
691 
692 template<typename TreeType>
693 inline typename Morphology<TreeType>::Word
694 Morphology<TreeType>::LeafCache::gatherFacesXY(int x, int y, int i1, int n, int i2)
695 {
696  // erode current leaf or neighbor in negative x-direction
697  Word w = x>0 ? this->gather(i1,n-LEAF_DIM) : this->template gather<-1,0,0>(i2, n);
698 
699  // erode current leaf or neighbor in positive x-direction
700  w = Word(w & (x<LEAF_DIM-1?this->gather(i1,n+LEAF_DIM):this->template gather<1,0,0>(i2+1,n)));
701 
702  // erode current leaf or neighbor in negative y-direction
703  w = Word(w & (y>0 ? this->gather(i1, n-1) : this->template gather<0,-1,0>(i2+2, n)));
704 
705  // erode current leaf or neighbor in positive y-direction
706  w = Word(w & (y<LEAF_DIM-1 ? this->gather(i1, n+1) : this->template gather<0,1,0>(i2+3, n)));
707 
708  return w;
709 }
710 
711 
712 template<typename TreeType>
713 inline typename Morphology<TreeType>::Word
714 Morphology<TreeType>::LeafCache::gatherEdgesXY(int x, int y, int i1, int n, int i2)
715 {
716  Word w = ~Word(0);
717 
718  if (x > 0) {
719  w &= y > 0 ? this->gather(i1, n-LEAF_DIM-1) :
720  this->template gather< 0,-1, 0>(i2+2, n-LEAF_DIM);
721  w &= y < LEAF_DIM-1 ? this->gather(i1, n-LEAF_DIM+1) :
722  this->template gather< 0, 1, 0>(i2+3, n-LEAF_DIM);
723  } else {
724  w &= y < LEAF_DIM-1 ? this->template gather<-1, 0, 0>(i2 , n+1):
725  this->template gather<-1, 1, 0>(i2+7, n );
726  w &= y > 0 ? this->template gather<-1, 0, 0>(i2 , n-1):
727  this->template gather<-1,-1, 0>(i2+4, n );
728  }
729  if (x < LEAF_DIM-1) {
730  w &= y > 0 ? this->gather(i1, n+LEAF_DIM-1) :
731  this->template gather< 0,-1, 0>(i2+2, n+LEAF_DIM);
732  w &= y < LEAF_DIM-1 ? this->gather(i1, n+LEAF_DIM+1) :
733  this->template gather< 0, 1, 0>(i2+3, n+LEAF_DIM);
734  } else {
735  w &= y > 0 ? this->template gather< 1, 0, 0>(i2+1, n-1):
736  this->template gather< 1,-1, 0>(i2+6, n );
737  w &= y < LEAF_DIM-1 ? this->template gather< 1, 0, 0>(i2+1, n+1):
738  this->template gather< 1, 1, 0>(i2+5, n );
739  }
740 
741  return w;
742 }
743 
744 
745 template <typename TreeType>
746 inline void
748 {
749  LeafCache cache(7, mManager.tree());
750  for (size_t leafIdx = range.begin(); leafIdx < range.end(); ++leafIdx) {
751  cache[0] = &mManager.leaf(leafIdx);
752  if (cache[0]->isEmpty()) continue;
753  cache.setOrigin(cache[0]->origin());
754  MaskType& newMask = mSavedMasks[leafIdx];//original bit-mask of current leaf node
755  for (int x = 0; x < LEAF_DIM; ++x ) {
756  for (int y = 0, n = (x << LEAF_LOG2DIM); y < LEAF_DIM; ++y, ++n) {
757  // Extract the portion of the original mask that corresponds to a row in z.
758  if (Word& w = newMask.template getWord<Word>(n)) {
759 
760  // erode in two z directions (this is first since it uses the original w)
761  w = Word(w &
762  (Word(w<<1 | (cache.template gather<0,0,-1>(1, n)>>(LEAF_DIM-1))) &
763  Word(w>>1 | (cache.template gather<0,0, 1>(2, n)<<(LEAF_DIM-1)))));
764 
765  w = Word(w & cache.gatherFacesXY(x, y, 0, n, 3));
766  }
767  }// loop over y
768  }//loop over x
769  cache.clear();
770  }//loop over leafs
771 }
772 
773 
774 template <typename TreeType>
775 inline void
777 {
778  OPENVDB_THROW(NotImplementedError, "tools::erode18 is not implemented yet!");
779 }
780 
781 
782 template <typename TreeType>
783 inline void
785 {
786  OPENVDB_THROW(NotImplementedError, "tools::erode26 is not implemented yet!");
787 }
788 
789 
790 template<typename TreeType>
791 inline void
793 {
794  /// @todo Currently operates only on leaf voxels; need to extend to tiles.
795  const size_t leafCount = mManager->leafCount();
796 
797  // Save the value masks of all leaf nodes.
798  std::vector<MaskType> savedMasks(leafCount);
799  this->copyMasks(savedMasks, *mManager);
800  UpdateMasks a(savedMasks, *mManager);
801  ErodeVoxelsOp erode(savedMasks, *mManager);
802 
803  for (int i = 0; i < mSteps; ++i) {
804  erode.runParallel(nn);
805  a.update();
806  }
807 
809 }
810 
811 
812 ////////////////////////////////////////
813 
814 
815 template<typename TreeType>
816 inline void
818 {
819  if (iterations > 0 ) {
820  Morphology<TreeType> m(&manager);
821  m.dilateVoxels(iterations, nn);
822  }
823 }
824 
825 template<typename TreeType>
826 inline void
827 dilateVoxels(TreeType& tree, int iterations, NearestNeighbors nn)
828 {
829  if (iterations > 0 ) {
830  Morphology<TreeType> m(tree);
831  m.dilateVoxels(iterations, nn);
832  }
833 }
834 
835 template<typename TreeType>
836 inline void
838 {
839  if (iterations > 0 ) {
840  Morphology<TreeType> m(&manager);
841  m.erodeVoxels(iterations, nn);
842  }
843 }
844 
845 template<typename TreeType>
846 inline void
847 erodeVoxels(TreeType& tree, int iterations, NearestNeighbors nn)
848 {
849  if (iterations > 0 ) {
850  Morphology<TreeType> m(tree);
851  m.erodeVoxels(iterations, nn);
852  }
853 }
854 
855 
856 ////////////////////////////////////////
857 
858 
859 namespace activation {
860 
861 template<typename TreeType>
863 {
864 public:
865  using ValueT = typename TreeType::ValueType;
866 
867  ActivationOp(bool state, const ValueT& val, const ValueT& tol)
868  : mActivate(state)
869  , mValue(val)
870  , mTolerance(tol)
871  {}
872 
873  void operator()(const typename TreeType::ValueOnIter& it) const
874  {
875  if (math::isApproxEqual(*it, mValue, mTolerance)) {
876  it.setValueOff();
877  }
878  }
879 
880  void operator()(const typename TreeType::ValueOffIter& it) const
881  {
882  if (math::isApproxEqual(*it, mValue, mTolerance)) {
883  it.setActiveState(/*on=*/true);
884  }
885  }
886 
887  void operator()(const typename TreeType::LeafIter& lit) const
888  {
889  using LeafT = typename TreeType::LeafNodeType;
890  LeafT& leaf = *lit;
891  if (mActivate) {
892  for (typename LeafT::ValueOffIter it = leaf.beginValueOff(); it; ++it) {
893  if (math::isApproxEqual(*it, mValue, mTolerance)) {
894  leaf.setValueOn(it.pos());
895  }
896  }
897  } else {
898  for (typename LeafT::ValueOnIter it = leaf.beginValueOn(); it; ++it) {
899  if (math::isApproxEqual(*it, mValue, mTolerance)) {
900  leaf.setValueOff(it.pos());
901  }
902  }
903  }
904  }
905 
906 private:
907  bool mActivate;
908  const ValueT mValue, mTolerance;
909 }; // class ActivationOp
910 
911 } // namespace activation
912 
913 
914 template<typename GridOrTree>
915 inline void
916 activate(GridOrTree& gridOrTree, const typename GridOrTree::ValueType& value,
917  const typename GridOrTree::ValueType& tolerance)
918 {
919  using Adapter = TreeAdapter<GridOrTree>;
920  using TreeType = typename Adapter::TreeType;
921 
922  TreeType& tree = Adapter::tree(gridOrTree);
923 
924  activation::ActivationOp<TreeType> op(/*activate=*/true, value, tolerance);
925 
926  // Process all leaf nodes in parallel.
927  foreach(tree.beginLeaf(), op);
928 
929  // Process all other inactive values serially (because changing active states
930  // is not thread-safe unless no two threads modify the same node).
931  typename TreeType::ValueOffIter it = tree.beginValueOff();
932  it.setMaxDepth(tree.treeDepth() - 2);
933  foreach(it, op, /*threaded=*/false);
934 }
935 
936 
937 template<typename GridOrTree>
938 inline void
939 deactivate(GridOrTree& gridOrTree, const typename GridOrTree::ValueType& value,
940  const typename GridOrTree::ValueType& tolerance)
941 {
942  using Adapter = TreeAdapter<GridOrTree>;
943  using TreeType = typename Adapter::TreeType;
944 
945  TreeType& tree = Adapter::tree(gridOrTree);
946 
947  activation::ActivationOp<TreeType> op(/*activate=*/false, value, tolerance);
948 
949  // Process all leaf nodes in parallel.
950  foreach(tree.beginLeaf(), op);
951 
952  // Process all other active values serially (because changing active states
953  // is not thread-safe unless no two threads modify the same node).
954  typename TreeType::ValueOnIter it = tree.beginValueOn();
955  it.setMaxDepth(tree.treeDepth() - 2);
956  foreach(it, op, /*threaded=*/false);
957 }
958 
959 /// @brief Class that performs multi-threaded dilation with support for active tiles.
960 /// @warning Dont use this class directly, instead call the function dilateActiveValues!
961 template<typename TreeT>
963 {
964  using MaskT = typename TreeT::template ValueConverter<ValueMask>::Type;
965  using PoolT = tbb::enumerable_thread_specific<MaskT>;
966  using LeafT = typename MaskT::LeafNodeType;
967 
968  // Very light-weight member data
969  const int mIter;// number of iterations
970  const tools::NearestNeighbors mNN;//enum to specify the dilation scheme
971  PoolT *mPool;// pointer to the thread-local pool of mask trees
972  LeafT **mLeafs;// raw array of pointers to leaf nodes
973 
974 public:
975 
976  DilationOp(TreeT &tree, int iterations, NearestNeighbors nn, TilePolicy mode)
977  : mIter(iterations), mNN(nn), mPool(nullptr), mLeafs(nullptr)
978  {
979  const size_t numLeafs = this->init( tree, mode );
980  const size_t numThreads = size_t(tbb::task_scheduler_init::default_num_threads());
981  const size_t grainSize = math::Max(size_t(1), numLeafs/(2*numThreads));
982 
983  MaskT mask;
984  PoolT pool(mask);// Scoped thread-local storage of mask trees
985  mPool = &pool;
986 
987  tbb::parallel_for(tbb::blocked_range<LeafT**>(mLeafs, mLeafs+numLeafs, grainSize), *this);
988 
989  delete [] mLeafs;// no more need for the array of leaf node pointers
990 
991  using IterT = typename PoolT::iterator;
992  for (IterT it=pool.begin(); it!=pool.end(); ++it) mask.merge(*it);// fast stealing
993 
994  if (mode == PRESERVE_TILES) tools::prune(mask);//multithreaded
995 
996  tree.topologyUnion(mask);//multithreaded
997  }
998 
999  // This is required by tbb and should never be called directly
1000  void operator()(const tbb::blocked_range<LeafT**> &r) const
1001  {
1002  MaskT mask;// thread-local temporary mask tree
1003  for (LeafT** it=r.begin(); it!=r.end(); ++it) mask.addLeaf( *it );
1004  tree::LeafManager<MaskT> manager(mask, r.begin(), r.end());
1005  tools::dilateVoxels(manager, mIter, mNN);// serial dilation of active voxels
1006  mPool->local().merge(mask, MERGE_ACTIVE_STATES);
1007  }
1008 private:
1009 
1010  // Simple wrapper of a raw double-pointer to mimic a std container
1011  struct MyArray {
1012  using value_type = LeafT*;//required by Tree::stealNodes
1013  value_type* ptr;
1014  MyArray(value_type* array) : ptr(array) {}
1015  void push_back(value_type leaf) { *ptr++ = leaf; }//required by Tree::stealNodes
1016  };
1017 
1018  // Convert active tiles to leafs and de-construct the tree into a linear array of leafs.
1019  size_t linearize(MaskT& mask, TilePolicy mode)
1020  {
1021  if (mode != IGNORE_TILES) mask.voxelizeActiveTiles();//lightweight since this is a mask tree
1022  const size_t numLeafs = mask.leafCount();
1023  mLeafs = new LeafT*[numLeafs];// fast pre-allocation
1024  MyArray tmp(mLeafs);
1025  mask.stealNodes(tmp);// serializes the mask tree and leaves it empty
1026  return numLeafs;
1027  }
1028 
1029  template<typename T>
1031  init(T& tree, TilePolicy mode)
1032  {
1033  return this->linearize(tree, mode);
1034  }
1035 
1036  template<typename T>
1038  init(const T& tree, TilePolicy mode)
1039  {
1040  MaskT mask(tree, false, true, TopologyCopy());
1041  return this->linearize(mask, mode);
1042  }
1043 
1044 };// DilationOp
1045 
1046 template<typename TreeType>
1047 inline void
1048 dilateActiveValues(TreeType& tree, int iterations, NearestNeighbors nn, TilePolicy mode)
1049 {
1050  if (iterations > 0 ) DilationOp<TreeType> tmp(tree, iterations, nn, mode);
1051 }
1052 
1053 template<typename TreeType>
1054 inline void
1056  int iterations,
1057  NearestNeighbors nn,
1058  TilePolicy mode)
1059 {
1060  if (iterations > 0 ) {
1061  DilationOp<TreeType> tmp(manager.tree(), iterations, nn, mode);
1062  manager.rebuildLeafArray();
1063  }
1064 }
1065 
1066 } // namespace tools
1067 } // namespace OPENVDB_VERSION_NAME
1068 } // namespace openvdb
1069 
1070 #endif // OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED
void erodeVoxels6()
Face-adjacent erosion pattern.
Definition: Morphology.h:239
void parallel_for(int64_t start, int64_t end, std::function< void(int64_t index)> &&task, parallel_options opt=parallel_options(0, Split_Y, 1))
Definition: parallel.h:153
GLenum GLint * range
Definition: glew.h:3500
typename LeafType::NodeMaskType MaskType
Definition: Morphology.h:255
void dilateVoxels26()
Face-, edge- and vertex-adjacent dilation pattern.
Definition: Morphology.h:540
GLsizeiptr size
Definition: glew.h:1681
*Note that the tasks the is the thread number *for the pool
Definition: thread.h:655
typename TreeType::LeafNodeType LeafType
Definition: Morphology.h:254
typename DimToWord< LEAF_LOG2DIM >::Type Word
Definition: Morphology.h:266
GLenum mode
Definition: glew.h:2163
Class that performs multi-threaded dilation with support for active tiles.
Definition: Morphology.h:962
GLuint const GLfloat * val
Definition: glew.h:2794
const TreeType & tree() const
Return a const reference to tree associated with this manager.
Definition: LeafManager.h:304
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:9477
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:167
const GLdouble * m
Definition: glew.h:9124
void dilateVoxels18()
Face- and edge-adjacent dilation pattern.
Definition: Morphology.h:490
void operator()(const tbb::blocked_range< LeafT ** > &r) const
Definition: Morphology.h:1000
GLenum GLint GLuint mask
Definition: glew.h:1845
void dilateActiveValues(TreeType &tree, int iterations=1, NearestNeighbors nn=NN_FACE, TilePolicy mode=PRESERVE_TILES)
Topologically dilate all active values (i.e. both voxels and tiles) in a tree using one of three near...
Definition: Morphology.h:1048
uint64 value_type
Definition: GA_PrimCompat.h:29
DilationOp(TreeT &tree, int iterations, NearestNeighbors nn, TilePolicy mode)
Definition: Morphology.h:976
This adapter allows code that is templated on a Tree type to accept either a Tree type or a Grid type...
Definition: Grid.h:1068
void erodeVoxels26()
Face-, edge- and vertex-adjacent erosion pattern.
Definition: Morphology.h:243
size_t leafCount() const
Return the number of leaf nodes.
Definition: LeafManager.h:289
void rebuildLeafArray(bool serial=false)
Remove the auxiliary buffers and rebuild the leaf array.
Definition: LeafManager.h:277
bool isApproxEqual(const Type &a, const Type &b, const Type &tolerance)
Return true if a is equal to b to within the given tolerance.
Definition: Math.h:397
GLint GLint GLint GLint GLint x
Definition: glew.h:1252
NearestNeighbors
Voxel topology of nearest neighbors.
Definition: Morphology.h:60
Word gatherEdgesXY(int x, int y, int i1, int n, int i2)
Definition: Morphology.h:714
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1252
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
Word gatherFacesXY(int x, int y, int i1, int n, int i2)
Definition: Morphology.h:694
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1890
void erodeVoxels(int iterations=1, NearestNeighbors nn=NN_FACE)
Definition: Morphology.h:244
ImageBuf OIIO_API erode(const ImageBuf &src, int width=3, int height=-1, ROI roi={}, int nthreads=0)
GLsizei n
Definition: glew.h:4040
const GLfloat * c
Definition: glew.h:16296
Defined various multi-threaded utility functions for trees.
LeafNodeT * touchLeaf(const Coord &xyz)
Return a pointer to the leaf node that contains voxel (x, y, z). If no such node exists, create one, but preserve the values and active states of all voxels.
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition: LeafManager.h:84
void operator()(const tbb::blocked_range< size_t > &range) const
Definition: Morphology.h:383
Mapping from a Log2Dim to a data type of size 2^Log2Dim bits.
Definition: Morphology.h:208
CopyMasks(std::vector< MaskType > &masks, const ManagerType &manager)
Definition: Morphology.h:412
RangeType getRange(size_t grainsize=1) const
Return a tbb::blocked_range of leaf array indices.
Definition: LeafManager.h:344
LeafNodeT * probeLeaf(const Coord &xyz)
Return a pointer to the leaf node that contains voxel (x, y, z), or nullptr if no such node exists...
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1253
GLdouble GLdouble GLdouble b
Definition: glew.h:9122
bool isValueOn(const Coord &xyz) const
Return the active state of the voxel at the given coordinates.
void dilateVoxels(int iterations=1, NearestNeighbors nn=NN_FACE)
Definition: Morphology.h:427
UpdateMasks(const std::vector< MaskType > &masks, ManagerType &manager)
Definition: Morphology.h:402
void operator()(const typename TreeType::LeafIter &lit) const
Definition: Morphology.h:887
void operator()(const tbb::blocked_range< size_t > &r) const
Definition: Morphology.h:405
const void * ptr(const T *p)
Definition: format.h:3292
TilePolicy
Different policies when dilating trees with active tiles.
Definition: Morphology.h:75
GLdouble GLdouble GLdouble r
Definition: glew.h:1406
void erodeVoxels18()
Face- and edge-adjacent erosion pattern.
Definition: Morphology.h:241
ActivationOp(bool state, const ValueT &val, const ValueT &tol)
Definition: Morphology.h:867
GA_API const UT_StringHolder N
void operator()(const typename TreeType::ValueOnIter &it) const
Definition: Morphology.h:873
void scatterEdgesXY(int x, int y, int i1, int n, int i2)
Definition: Morphology.h:621
GLenum array
Definition: glew.h:9066
void erodeVoxels(TreeType &tree, int iterations=1, NearestNeighbors nn=NN_FACE)
Topologically erode all leaf-level active voxels in the given tree.
Definition: Morphology.h:847
Word gather(AccessorType &acc, const Coord &xyz, int indx)
Definition: Morphology.h:288
void dilateVoxels6()
Face-adjacent dilation pattern.
Definition: Morphology.h:447
A LeafManager manages a linear array of pointers to a given tree's leaf nodes, as well as optional au...
LeafType & leaf(size_t leafIdx) const
Return a pointer to the leaf node at index leafIdx in the array.
Definition: LeafManager.h:320
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:334
void deactivate(GridOrTree &, const typename GridOrTree::ValueType &value, const typename GridOrTree::ValueType &tolerance=zeroVal< typename GridOrTree::ValueType >())
Mark as inactive any active tiles or voxels in the given grid or tree whose values are equal to value...
Definition: Morphology.h:939
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:389
void operator()(const tbb::blocked_range< size_t > &r) const
Definition: Morphology.h:415
void activate(GridOrTree &, const typename GridOrTree::ValueType &value, const typename GridOrTree::ValueType &tolerance=zeroVal< typename GridOrTree::ValueType >())
Mark as active any inactive tiles or voxels in the given grid or tree whose values are equal to value...
Definition: Morphology.h:916
void scatter(AccessorType &acc, const Coord &xyz, int indx, Word mask)
Definition: Morphology.h:275
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:113
GLsizei const GLfloat * value
Definition: glew.h:1849
void dilateVoxels(TreeType &tree, int iterations=1, NearestNeighbors nn=NN_FACE)
Topologically dilate all leaf-level active voxels in a tree using one of three nearest neighbor conne...
Definition: Morphology.h:827
const Type & Max(const Type &a, const Type &b)
Return the maximum of two values.
Definition: Math.h:588
void copyMasks(std::vector< MaskType > &a, const ManagerType &b)
Definition: Morphology.h:421
void operator()(const typename TreeType::ValueOffIter &it) const
Definition: Morphology.h:880
void scatterFacesXY(int x, int y, int i1, int n, int i2)
Definition: Morphology.h:590
ErodeVoxelsOp(std::vector< MaskType > &masks, ManagerType &manager)
Definition: Morphology.h:363
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:82
MaskManager(std::vector< MaskType > &masks, ManagerType &manager)
Definition: Morphology.h:378
GLintptr offset
Definition: glew.h:1682