OpenVDB  8.2.0
Clip.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 Clip.h
5 ///
6 /// @brief Functions to clip a grid against a bounding box, a camera frustum,
7 /// or another grid's active voxel topology
8 
9 #ifndef OPENVDB_TOOLS_CLIP_HAS_BEEN_INCLUDED
10 #define OPENVDB_TOOLS_CLIP_HAS_BEEN_INCLUDED
11 
12 #include <openvdb/Grid.h>
13 #include <openvdb/math/Math.h> // for math::isNegative()
14 #include <openvdb/math/Maps.h> // for math::NonlinearFrustumMap
16 #include "GridTransformer.h" // for tools::resampleToMatch()
17 #include "Prune.h"
18 #include <tbb/blocked_range.h>
19 #include <tbb/parallel_reduce.h>
20 #include <type_traits> // for std::enable_if, std::is_same
21 #include <vector>
22 
23 
24 namespace openvdb {
26 namespace OPENVDB_VERSION_NAME {
27 namespace tools {
28 
29 /// @brief Clip the given grid against a world-space bounding box
30 /// and return a new grid containing the result.
31 /// @param grid the grid to be clipped
32 /// @param bbox a world-space bounding box
33 /// @param keepInterior if true, discard voxels that lie outside the bounding box;
34 /// if false, discard voxels that lie inside the bounding box
35 /// @warning Clipping a level set will likely produce a grid that is
36 /// no longer a valid level set.
37 template<typename GridType>
38 inline typename GridType::Ptr
39 clip(const GridType& grid, const BBoxd& bbox, bool keepInterior = true);
40 
41 /// @brief Clip the given grid against a frustum and return a new grid containing the result.
42 /// @param grid the grid to be clipped
43 /// @param frustum a frustum map
44 /// @param keepInterior if true, discard voxels that lie outside the frustum;
45 /// if false, discard voxels that lie inside the frustum
46 /// @warning Clipping a level set will likely produce a grid that is
47 /// no longer a valid level set.
48 template<typename GridType>
49 inline typename GridType::Ptr
50 clip(const GridType& grid, const math::NonlinearFrustumMap& frustum, bool keepInterior = true);
51 
52 /// @brief Clip a grid against the active voxels of another grid
53 /// and return a new grid containing the result.
54 /// @param grid the grid to be clipped
55 /// @param mask a grid whose active voxels form a boolean clipping mask
56 /// @param keepInterior if true, discard voxels that do not intersect the mask;
57 /// if false, discard voxels that intersect the mask
58 /// @details The mask grid need not have the same transform as the source grid.
59 /// Also, if the mask grid is a level set, consider using tools::sdfInteriorMask
60 /// to construct a new mask comprising the interior (rather than the narrow band)
61 /// of the level set.
62 /// @warning Clipping a level set will likely produce a grid that is
63 /// no longer a valid level set.
64 template<typename GridType, typename MaskTreeType>
65 inline typename GridType::Ptr
66 clip(const GridType& grid, const Grid<MaskTreeType>& mask, bool keepInterior = true);
67 
68 
69 ////////////////////////////////////////
70 
71 /// @cond OPENVDB_DOCS_INTERNAL
72 
73 namespace clip_internal {
74 
75 // Use either MaskGrids or BoolGrids internally.
76 // (MaskGrids have a somewhat lower memory footprint.)
77 using MaskValueType = ValueMask;
78 //using MaskValueType = bool;
79 
80 
81 template<typename TreeT>
82 class MaskInteriorVoxels
83 {
84 public:
85  using ValueT = typename TreeT::ValueType;
86  using LeafNodeT = typename TreeT::LeafNodeType;
87 
88  MaskInteriorVoxels(const TreeT& tree): mAcc(tree) {}
89 
90  template<typename LeafNodeType>
91  void operator()(LeafNodeType& leaf, size_t /*leafIndex*/) const
92  {
93  const auto* refLeaf = mAcc.probeConstLeaf(leaf.origin());
94  if (refLeaf) {
95  for (auto iter = leaf.beginValueOff(); iter; ++iter) {
96  const auto pos = iter.pos();
97  leaf.setActiveState(pos, math::isNegative(refLeaf->getValue(pos)));
98  }
99  }
100  }
101 
102 private:
103  tree::ValueAccessor<const TreeT> mAcc;
104 };
105 
106 
107 ////////////////////////////////////////
108 
109 
110 template<typename TreeT>
111 class CopyLeafNodes
112 {
113 public:
114  using MaskTreeT = typename TreeT::template ValueConverter<MaskValueType>::Type;
115  using MaskLeafManagerT = tree::LeafManager<const MaskTreeT>;
116 
117  CopyLeafNodes(const TreeT&, const MaskLeafManagerT&);
118 
119  void run(bool threaded = true);
120 
121  typename TreeT::Ptr tree() const { return mNewTree; }
122 
123  CopyLeafNodes(CopyLeafNodes&, tbb::split);
124  void operator()(const tbb::blocked_range<size_t>&);
125  void join(const CopyLeafNodes& rhs) { mNewTree->merge(*rhs.mNewTree); }
126 
127 private:
128  const MaskTreeT* mClipMask;
129  const TreeT* mTree;
130  const MaskLeafManagerT* mLeafNodes;
131  typename TreeT::Ptr mNewTree;
132 };
133 
134 
135 template<typename TreeT>
136 CopyLeafNodes<TreeT>::CopyLeafNodes(const TreeT& tree, const MaskLeafManagerT& leafNodes)
137  : mTree(&tree)
138  , mLeafNodes(&leafNodes)
139  , mNewTree(new TreeT(mTree->background()))
140 {
141 }
142 
143 
144 template<typename TreeT>
145 CopyLeafNodes<TreeT>::CopyLeafNodes(CopyLeafNodes& rhs, tbb::split)
146  : mTree(rhs.mTree)
147  , mLeafNodes(rhs.mLeafNodes)
148  , mNewTree(new TreeT(mTree->background()))
149 {
150 }
151 
152 
153 template<typename TreeT>
154 void
155 CopyLeafNodes<TreeT>::run(bool threaded)
156 {
157  if (threaded) tbb::parallel_reduce(mLeafNodes->getRange(), *this);
158  else (*this)(mLeafNodes->getRange());
159 }
160 
161 
162 template<typename TreeT>
163 void
164 CopyLeafNodes<TreeT>::operator()(const tbb::blocked_range<size_t>& range)
165 {
166  tree::ValueAccessor<TreeT> acc(*mNewTree);
167  tree::ValueAccessor<const TreeT> refAcc(*mTree);
168 
169  for (auto n = range.begin(); n != range.end(); ++n) {
170  const auto& maskLeaf = mLeafNodes->leaf(n);
171  const auto& ijk = maskLeaf.origin();
172  const auto* refLeaf = refAcc.probeConstLeaf(ijk);
173 
174  auto* newLeaf = acc.touchLeaf(ijk);
175 
176  if (refLeaf) {
177  for (auto it = maskLeaf.cbeginValueOn(); it; ++it) {
178  const auto pos = it.pos();
179  newLeaf->setValueOnly(pos, refLeaf->getValue(pos));
180  newLeaf->setActiveState(pos, refLeaf->isValueOn(pos));
181  }
182  } else {
183  typename TreeT::ValueType value;
184  bool isActive = refAcc.probeValue(ijk, value);
185 
186  for (auto it = maskLeaf.cbeginValueOn(); it; ++it) {
187  const auto pos = it.pos();
188  newLeaf->setValueOnly(pos, value);
189  newLeaf->setActiveState(pos, isActive);
190  }
191  }
192  }
193 }
194 
195 
196 ////////////////////////////////////////
197 
198 
199 struct BoolSampler
200 {
201  static const char* name() { return "bin"; }
202  static int radius() { return 2; }
203  static bool mipmap() { return false; }
204  static bool consistent() { return true; }
205 
206  template<class TreeT>
207  static bool sample(const TreeT& inTree,
208  const Vec3R& inCoord, typename TreeT::ValueType& result)
209  {
210  return inTree.probeValue(Coord::floor(inCoord), result);
211  }
212 };
213 
214 
215 ////////////////////////////////////////
216 
217 
218 // Convert a grid of one type to a grid of another type
219 template<typename FromGridT, typename ToGridT>
220 struct ConvertGrid
221 {
222  using FromGridCPtrT = typename FromGridT::ConstPtr;
223  using ToGridPtrT = typename ToGridT::Ptr;
224  ToGridPtrT operator()(const FromGridCPtrT& grid) { return ToGridPtrT(new ToGridT(*grid)); }
225 };
226 
227 // Partial specialization that avoids copying when
228 // the input and output grid types are the same
229 template<typename GridT>
230 struct ConvertGrid<GridT, GridT>
231 {
232  using GridCPtrT = typename GridT::ConstPtr;
233  GridCPtrT operator()(const GridCPtrT& grid) { return grid; }
234 };
235 
236 
237 ////////////////////////////////////////
238 
239 
240 // Convert a grid of arbitrary type to a mask grid with the same tree configuration
241 // and return a pointer to the new grid.
242 /// @private
243 template<typename GridT>
244 inline typename std::enable_if<!std::is_same<MaskValueType, typename GridT::BuildType>::value,
245  typename GridT::template ValueConverter<MaskValueType>::Type::Ptr>::type
246 convertToMaskGrid(const GridT& grid)
247 {
248  using MaskGridT = typename GridT::template ValueConverter<MaskValueType>::Type;
249  auto mask = MaskGridT::create(/*background=*/false);
250  mask->topologyUnion(grid);
251  mask->setTransform(grid.constTransform().copy());
252  return mask;
253 }
254 
255 // Overload that avoids any processing if the input grid is already a mask grid
256 /// @private
257 template<typename GridT>
258 inline typename std::enable_if<std::is_same<MaskValueType, typename GridT::BuildType>::value,
259  typename GridT::ConstPtr>::type
260 convertToMaskGrid(const GridT& grid)
261 {
262  return grid.copy(); // shallow copy
263 }
264 
265 
266 ////////////////////////////////////////
267 
268 
269 /// @private
270 template<typename GridType>
271 inline typename GridType::Ptr
272 doClip(
273  const GridType& grid,
274  const typename GridType::template ValueConverter<MaskValueType>::Type& clipMask,
275  bool keepInterior)
276 {
277  using TreeT = typename GridType::TreeType;
278  using MaskTreeT = typename GridType::TreeType::template ValueConverter<MaskValueType>::Type;
279 
280  const auto gridClass = grid.getGridClass();
281  const auto& tree = grid.tree();
282 
283  MaskTreeT gridMask(false);
284  gridMask.topologyUnion(tree);
285 
286  if (gridClass == GRID_LEVEL_SET) {
287  tree::LeafManager<MaskTreeT> leafNodes(gridMask);
288  leafNodes.foreach(MaskInteriorVoxels<TreeT>(tree));
289 
290  tree::ValueAccessor<const TreeT> acc(tree);
291 
292  typename MaskTreeT::ValueAllIter iter(gridMask);
293  iter.setMaxDepth(MaskTreeT::ValueAllIter::LEAF_DEPTH - 1);
294 
295  for ( ; iter; ++iter) {
296  iter.setActiveState(math::isNegative(acc.getValue(iter.getCoord())));
297  }
298  }
299 
300  if (keepInterior) {
301  gridMask.topologyIntersection(clipMask.constTree());
302  } else {
303  gridMask.topologyDifference(clipMask.constTree());
304  }
305 
306  auto outGrid = grid.copyWithNewTree();
307  {
308  // Copy voxel values and states.
309  tree::LeafManager<const MaskTreeT> leafNodes(gridMask);
310  CopyLeafNodes<TreeT> maskOp(tree, leafNodes);
311  maskOp.run();
312  outGrid->setTree(maskOp.tree());
313  }
314  {
315  // Copy tile values and states.
316  tree::ValueAccessor<const TreeT> refAcc(tree);
317  tree::ValueAccessor<const MaskTreeT> maskAcc(gridMask);
318 
319  typename TreeT::ValueAllIter it(outGrid->tree());
320  it.setMaxDepth(TreeT::ValueAllIter::LEAF_DEPTH - 1);
321  for ( ; it; ++it) {
322  Coord ijk = it.getCoord();
323 
324  if (maskAcc.isValueOn(ijk)) {
325  typename TreeT::ValueType value;
326  bool isActive = refAcc.probeValue(ijk, value);
327 
328  it.setValue(value);
329  if (!isActive) it.setValueOff();
330  }
331  }
332  }
333 
334  outGrid->setTransform(grid.transform().copy());
335  if (gridClass != GRID_LEVEL_SET) outGrid->setGridClass(gridClass);
336 
337  return outGrid;
338 }
339 
340 } // namespace clip_internal
341 
342 /// @endcond
343 
344 
345 ////////////////////////////////////////
346 
347 
348 /// @private
349 template<typename GridType>
350 inline typename GridType::Ptr
351 clip(const GridType& grid, const BBoxd& bbox, bool keepInterior)
352 {
353  using MaskValueT = clip_internal::MaskValueType;
354  using MaskGridT = typename GridType::template ValueConverter<MaskValueT>::Type;
355 
356  // Transform the world-space bounding box into the source grid's index space.
357  Vec3d idxMin, idxMax;
358  math::calculateBounds(grid.constTransform(), bbox.min(), bbox.max(), idxMin, idxMax);
359  CoordBBox region(Coord::floor(idxMin), Coord::floor(idxMax));
360  // Construct a boolean mask grid that is true inside the index-space bounding box
361  // and false everywhere else.
362  MaskGridT clipMask(/*background=*/false);
363  clipMask.fill(region, /*value=*/true, /*active=*/true);
364 
365  return clip_internal::doClip(grid, clipMask, keepInterior);
366 }
367 
368 
369 /// @private
370 template<typename SrcGridType, typename ClipTreeType>
371 inline typename SrcGridType::Ptr
372 clip(const SrcGridType& srcGrid, const Grid<ClipTreeType>& clipGrid, bool keepInterior)
373 {
374  using MaskValueT = clip_internal::MaskValueType;
375  using ClipGridType = Grid<ClipTreeType>;
376  using SrcMaskGridType = typename SrcGridType::template ValueConverter<MaskValueT>::Type;
377  using ClipMaskGridType = typename ClipGridType::template ValueConverter<MaskValueT>::Type;
378 
379  // Convert the clipping grid to a boolean-valued mask grid with the same tree configuration.
380  auto maskGrid = clip_internal::convertToMaskGrid(clipGrid);
381 
382  // Resample the mask grid into the source grid's index space.
383  if (srcGrid.constTransform() != maskGrid->constTransform()) {
384  auto resampledMask = ClipMaskGridType::create(/*background=*/false);
385  resampledMask->setTransform(srcGrid.constTransform().copy());
386  tools::resampleToMatch<clip_internal::BoolSampler>(*maskGrid, *resampledMask);
387  tools::prune(resampledMask->tree());
388  maskGrid = resampledMask;
389  }
390 
391  // Convert the mask grid to a mask grid with the same tree configuration as the source grid.
392  auto clipMask = clip_internal::ConvertGrid<
393  /*from=*/ClipMaskGridType, /*to=*/SrcMaskGridType>()(maskGrid);
394 
395  // Clip the source grid against the mask grid.
396  return clip_internal::doClip(srcGrid, *clipMask, keepInterior);
397 }
398 
399 
400 /// @private
401 template<typename GridType>
402 inline typename GridType::Ptr
403 clip(const GridType& inGrid, const math::NonlinearFrustumMap& frustumMap, bool keepInterior)
404 {
405  using ValueT = typename GridType::ValueType;
406  using TreeT = typename GridType::TreeType;
407  using LeafT = typename TreeT::LeafNodeType;
408 
409  const auto& gridXform = inGrid.transform();
410  const auto frustumIndexBBox = frustumMap.getBBox();
411 
412  // Return true if index-space point (i,j,k) lies inside the frustum.
413  auto frustumContainsCoord = [&](const Coord& ijk) -> bool {
414  auto xyz = gridXform.indexToWorld(ijk);
415  xyz = frustumMap.applyInverseMap(xyz);
416  return frustumIndexBBox.isInside(xyz);
417  };
418 
419  // Return the frustum index-space bounding box of the corners of
420  // the given grid index-space bounding box.
421  auto toFrustumIndexSpace = [&](const CoordBBox& inBBox) -> BBoxd {
422  const Coord bounds[2] = { inBBox.min(), inBBox.max() };
423  Coord ijk;
424  BBoxd outBBox;
425  for (int i = 0; i < 8; ++i) {
426  ijk[0] = bounds[(i & 1) >> 0][0];
427  ijk[1] = bounds[(i & 2) >> 1][1];
428  ijk[2] = bounds[(i & 4) >> 2][2];
429  auto xyz = gridXform.indexToWorld(ijk);
430  xyz = frustumMap.applyInverseMap(xyz);
431  outBBox.expand(xyz);
432  }
433  return outBBox;
434  };
435 
436  // Construct an output grid with the same transform and metadata as the input grid.
437  auto outGrid = inGrid.copyWithNewTree();
438  if (outGrid->getGridClass() == GRID_LEVEL_SET) {
439  // After clipping, a level set grid might no longer be a valid SDF.
440  outGrid->setGridClass(GRID_UNKNOWN);
441  }
442 
443  const auto& bg = outGrid->background();
444 
445  auto outAcc = outGrid->getAccessor();
446 
447  // Copy active and inactive tiles that intersect the clipping region
448  // from the input grid to the output grid.
449  // ("Clipping region" refers to either the interior or the exterior
450  // of the frustum, depending on the value of keepInterior.)
451  auto tileIter = inGrid.beginValueAll();
452  tileIter.setMaxDepth(GridType::ValueAllIter::LEAF_DEPTH - 1);
453  CoordBBox tileBBox;
454  for ( ; tileIter; ++tileIter) {
455  const bool tileActive = tileIter.isValueOn();
456  const auto& tileValue = tileIter.getValue();
457 
458  // Skip background tiles.
459  if (!tileActive && math::isApproxEqual(tileValue, bg)) continue;
460 
461  // Transform the tile's bounding box into frustum index space.
462  tileIter.getBoundingBox(tileBBox);
463  const auto tileFrustumBBox = toFrustumIndexSpace(tileBBox);
464 
465  // Determine whether any or all of the tile intersects the clipping region.
466  enum class CopyTile { kNone, kPartial, kFull };
467  auto copyTile = CopyTile::kNone;
468  if (keepInterior) {
469  if (frustumIndexBBox.isInside(tileFrustumBBox)) {
470  copyTile = CopyTile::kFull;
471  } else if (frustumIndexBBox.hasOverlap(tileFrustumBBox)) {
472  copyTile = CopyTile::kPartial;
473  }
474  } else {
475  if (!frustumIndexBBox.hasOverlap(tileFrustumBBox)) {
476  copyTile = CopyTile::kFull;
477  } else if (!frustumIndexBBox.isInside(tileFrustumBBox)) {
478  copyTile = CopyTile::kPartial;
479  }
480  }
481  switch (copyTile) {
482  case CopyTile::kNone:
483  break;
484  case CopyTile::kFull:
485  // Copy the entire tile.
486  outAcc.addTile(tileIter.getLevel(), tileBBox.min(), tileValue, tileActive);
487  break;
488  case CopyTile::kPartial:
489  // Copy only voxels inside the clipping region.
490  for (std::vector<CoordBBox> bboxVec = { tileBBox }; !bboxVec.empty(); ) {
491  // For efficiency, subdivide sufficiently large tiles and discard
492  // subregions based on additional bounding box intersection tests.
493  // The mimimum subregion size is chosen so that cost of the
494  // bounding box test is comparable to testing every voxel.
495  if (bboxVec.back().volume() > 64 && bboxVec.back().is_divisible()) {
496  // Subdivide this region in-place and append the other half to the list.
497  bboxVec.emplace_back(bboxVec.back(), tbb::split{});
498  continue;
499  }
500  auto subBBox = bboxVec.back();
501  bboxVec.pop_back();
502 
503  // Discard the subregion if it lies completely outside the clipping region.
504  if (keepInterior) {
505  if (!frustumIndexBBox.hasOverlap(toFrustumIndexSpace(subBBox))) continue;
506  } else {
507  if (frustumIndexBBox.isInside(toFrustumIndexSpace(subBBox))) continue;
508  }
509 
510  // Test every voxel within the subregion.
511  for (const auto& ijk: subBBox) {
512  if (frustumContainsCoord(ijk) == keepInterior) {
513  if (tileActive) {
514  outAcc.setValueOn(ijk, tileValue);
515  } else {
516  outAcc.setValueOff(ijk, tileValue);
517  }
518  }
519  }
520  }
521  break;
522  }
523  }
524  tools::prune(outGrid->tree());
525 
526  // Ensure that the output grid has the same leaf node topology as the input grid,
527  // with the exception of leaf nodes that lie completely outside the clipping region.
528  // (This operation is serial.)
529  for (auto leafIter = inGrid.constTree().beginLeaf(); leafIter; ++leafIter) {
530  const auto leafBBox = leafIter->getNodeBoundingBox();
531  const auto leafFrustumBBox = toFrustumIndexSpace(leafBBox);
532  if (keepInterior) {
533  if (frustumIndexBBox.hasOverlap(leafFrustumBBox)) {
534  outAcc.touchLeaf(leafBBox.min());
535  }
536  } else {
537  if (!frustumIndexBBox.hasOverlap(leafFrustumBBox)
538  || !frustumIndexBBox.isInside(leafFrustumBBox))
539  {
540  outAcc.touchLeaf(leafBBox.min());
541  }
542  }
543  }
544 
545  // In parallel across output leaf nodes, copy leaf voxels
546  // from the input grid to the output grid.
547  tree::LeafManager<TreeT> outLeafNodes{outGrid->tree()};
548  outLeafNodes.foreach(
549  [&](LeafT& leaf, size_t /*idx*/) {
550  auto inAcc = inGrid.getConstAccessor();
551  ValueT val;
552  for (auto voxelIter = leaf.beginValueAll(); voxelIter; ++voxelIter) {
553  const auto ijk = voxelIter.getCoord();
554  if (frustumContainsCoord(ijk) == keepInterior) {
555  const bool active = inAcc.probeValue(ijk, val);
556  voxelIter.setValue(val);
557  voxelIter.setValueOn(active);
558  }
559  }
560  }
561  );
562 
563  return outGrid;
564 }
565 
566 } // namespace tools
567 } // namespace OPENVDB_VERSION_NAME
568 } // namespace openvdb
569 
570 #endif // OPENVDB_TOOLS_CLIP_HAS_BEEN_INCLUDED
A LeafManager manages a linear array of pointers to a given tree's leaf nodes, as well as optional au...
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
Defined various multi-threaded utility functions for trees.
Container class that associates a tree with a transform and metadata.
Definition: Grid.h:577
Definition: Types.h:111
const Vec3T & max() const
Return a const reference to the maximum point of this bounding box.
Definition: BBox.h:64
void expand(ElementType padding)
Pad this bounding box.
Definition: BBox.h:321
const Vec3T & min() const
Return a const reference to the minimum point of this bounding box.
Definition: BBox.h:62
Axis-aligned bounding box of signed integer coordinates.
Definition: Coord.h:248
const Coord & min() const
Definition: Coord.h:320
bool empty() const
Return true if this bounding box is empty (i.e., encloses no coordinates).
Definition: Coord.h:355
Signed (x, y, z) 32-bit integer coordinates.
Definition: Coord.h:25
static Coord min()
Return the smallest possible coordinate.
Definition: Coord.h:43
static Coord floor(const Vec3< T > &xyz)
Return the largest integer coordinates that are not greater than xyz (node centered conversion).
Definition: Coord.h:56
static Coord max()
Return the largest possible coordinate.
Definition: Coord.h:46
This map is composed of three steps. First it will take a box of size (Lx X Ly X Lz) defined by a mem...
Definition: Maps.h:1911
Vec3d applyInverseMap(const Vec3d &in) const override
Return the pre-image of in under the map.
Definition: Maps.h:2122
const BBoxd & getBBox() const
Return the bounding box that defines the frustum in pre-image space.
Definition: Maps.h:2382
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition: LeafManager.h:85
const TreeType & tree() const
Return a const reference to tree associated with this manager.
Definition: LeafManager.h:302
void run(const char *ax, openvdb::GridBase &grid)
Run a full AX pipeline (parse, compile and execute) on a single OpenVDB Grid.
bool isNegative(const Type &x)
Return true if x is less than zero.
Definition: Math.h:368
Vec3< double > Vec3d
Definition: Vec3.h:668
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:407
OPENVDB_API void calculateBounds(const Transform &t, const Vec3d &minWS, const Vec3d &maxWS, Vec3d &minIS, Vec3d &maxIS)
Calculate an axis-aligned bounding box in index space from an axis-aligned bounding box in world spac...
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
GridType::Ptr clip(const GridType &grid, const BBoxd &bbox, bool keepInterior=true)
Clip the given grid against a world-space bounding box and return a new grid containing the result.
Definition: Clip.h:351
@ GRID_LEVEL_SET
Definition: Types.h:337
@ GRID_UNKNOWN
Definition: Types.h:336
math::BBox< Vec3d > BBoxd
Definition: Types.h:84
math::Vec3< Real > Vec3R
Definition: Types.h:72
Definition: Exceptions.h:13
#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