OpenVDB  8.2.0
GridTransformer.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 GridTransformer.h
5 /// @author Peter Cucka
6 
7 #ifndef OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
8 #define OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
9 
10 #include <openvdb/Grid.h>
11 #include <openvdb/Types.h>
12 #include <openvdb/math/Math.h> // for isApproxEqual()
14 #include "ChangeBackground.h"
15 #include "Interpolation.h"
16 #include "LevelSetRebuild.h" // for doLevelSetRebuild()
17 #include "SignedFloodFill.h" // for signedFloodFill
18 #include "Prune.h" // for pruneLevelSet
19 #include <tbb/blocked_range.h>
20 #include <tbb/parallel_reduce.h>
21 #include <cmath>
22 #include <functional>
23 
24 namespace openvdb {
26 namespace OPENVDB_VERSION_NAME {
27 namespace tools {
28 
29 /// @brief Resample an input grid into an output grid of the same type such that,
30 /// after resampling, the input and output grids coincide (apart from sampling
31 /// artifacts), but the output grid's transform is unchanged.
32 /// @details Specifically, this function resamples the input grid into the output
33 /// grid's index space, using a sampling kernel like PointSampler, BoxSampler,
34 /// or QuadraticSampler.
35 /// @param inGrid the grid to be resampled
36 /// @param outGrid the grid into which to write the resampled voxel data
37 /// @param interrupter an object adhering to the util::NullInterrupter interface
38 /// @par Example:
39 /// @code
40 /// // Create an input grid with the default identity transform
41 /// // and populate it with a level-set sphere.
42 /// FloatGrid::ConstPtr src = tools::makeSphere(...);
43 /// // Create an output grid and give it a uniform-scale transform.
44 /// FloatGrid::Ptr dest = FloatGrid::create();
45 /// const float voxelSize = 0.5;
46 /// dest->setTransform(math::Transform::createLinearTransform(voxelSize));
47 /// // Resample the input grid into the output grid, reproducing
48 /// // the level-set sphere at a smaller voxel size.
49 /// MyInterrupter interrupter = ...;
50 /// tools::resampleToMatch<tools::QuadraticSampler>(*src, *dest, interrupter);
51 /// @endcode
52 template<typename Sampler, typename Interrupter, typename GridType>
53 inline void
54 resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter);
55 
56 /// @brief Resample an input grid into an output grid of the same type such that,
57 /// after resampling, the input and output grids coincide (apart from sampling
58 /// artifacts), but the output grid's transform is unchanged.
59 /// @details Specifically, this function resamples the input grid into the output
60 /// grid's index space, using a sampling kernel like PointSampler, BoxSampler,
61 /// or QuadraticSampler.
62 /// @param inGrid the grid to be resampled
63 /// @param outGrid the grid into which to write the resampled voxel data
64 /// @par Example:
65 /// @code
66 /// // Create an input grid with the default identity transform
67 /// // and populate it with a level-set sphere.
68 /// FloatGrid::ConstPtr src = tools::makeSphere(...);
69 /// // Create an output grid and give it a uniform-scale transform.
70 /// FloatGrid::Ptr dest = FloatGrid::create();
71 /// const float voxelSize = 0.5;
72 /// dest->setTransform(math::Transform::createLinearTransform(voxelSize));
73 /// // Resample the input grid into the output grid, reproducing
74 /// // the level-set sphere at a smaller voxel size.
75 /// tools::resampleToMatch<tools::QuadraticSampler>(*src, *dest);
76 /// @endcode
77 template<typename Sampler, typename GridType>
78 inline void
79 resampleToMatch(const GridType& inGrid, GridType& outGrid);
80 
81 
82 ////////////////////////////////////////
83 
84 /// @cond OPENVDB_DOCS_INTERNAL
85 
86 namespace internal {
87 
88 /// @brief A TileSampler wraps a grid sampler of another type (BoxSampler,
89 /// QuadraticSampler, etc.), and for samples that fall within a given tile
90 /// of the grid, it returns a cached tile value instead of accessing the grid.
91 template<typename Sampler, typename TreeT>
92 class TileSampler: public Sampler
93 {
94 public:
95  using ValueT = typename TreeT::ValueType;
96 
97  /// @param b the index-space bounding box of a particular grid tile
98  /// @param tileVal the tile's value
99  /// @param on the tile's active state
100  TileSampler(const CoordBBox& b, const ValueT& tileVal, bool on):
101  mBBox(b.min().asVec3d(), b.max().asVec3d()), mVal(tileVal), mActive(on), mEmpty(false)
102  {
103  mBBox.expand(-this->radius()); // shrink the bounding box by the sample radius
104  mEmpty = mBBox.empty();
105  }
106 
107  bool sample(const TreeT& inTree, const Vec3R& inCoord, ValueT& result) const
108  {
109  if (!mEmpty && mBBox.isInside(inCoord)) { result = mVal; return mActive; }
110  return Sampler::sample(inTree, inCoord, result);
111  }
112 
113 protected:
114  BBoxd mBBox;
115  ValueT mVal;
116  bool mActive, mEmpty;
117 };
118 
119 
120 /// @brief For point sampling, tree traversal is less expensive than testing
121 /// bounding box membership.
122 template<typename TreeT>
123 class TileSampler<PointSampler, TreeT>: public PointSampler {
124 public:
125  TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {}
126 };
127 
128 /// @brief For point sampling, tree traversal is less expensive than testing
129 /// bounding box membership.
130 template<typename TreeT>
131 class TileSampler<StaggeredPointSampler, TreeT>: public StaggeredPointSampler {
132 public:
133  TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {}
134 };
135 
136 } // namespace internal
137 
138 /// @endcond
139 
140 
141 ////////////////////////////////////////
142 
143 
144 /// A GridResampler applies a geometric transformation to an
145 /// input grid using one of several sampling schemes, and stores
146 /// the result in an output grid.
147 ///
148 /// Usage:
149 /// @code
150 /// GridResampler resampler();
151 /// resampler.transformGrid<BoxSampler>(xform, inGrid, outGrid);
152 /// @endcode
153 /// where @c xform is a functor that implements the following methods:
154 /// @code
155 /// bool isAffine() const
156 /// openvdb::Vec3d transform(const openvdb::Vec3d&) const
157 /// openvdb::Vec3d invTransform(const openvdb::Vec3d&) const
158 /// @endcode
159 /// @note When the transform is affine and can be expressed as a 4 x 4 matrix,
160 /// a GridTransformer is much more efficient than a GridResampler.
162 {
163 public:
165  using InterruptFunc = std::function<bool (void)>;
166 
167  GridResampler(): mThreaded(true), mTransformTiles(true) {}
168  virtual ~GridResampler() {}
169 
170  GridResampler(const GridResampler&) = default;
172 
173  /// Enable or disable threading. (Threading is enabled by default.)
174  void setThreaded(bool b) { mThreaded = b; }
175  /// Return @c true if threading is enabled.
176  bool threaded() const { return mThreaded; }
177  /// Enable or disable processing of tiles. (Enabled by default, except for level set grids.)
178  void setTransformTiles(bool b) { mTransformTiles = b; }
179  /// Return @c true if tile processing is enabled.
180  bool transformTiles() const { return mTransformTiles; }
181 
182  /// @brief Allow processing to be aborted by providing an interrupter object.
183  /// The interrupter will be queried periodically during processing.
184  /// @see util/NullInterrupter.h for interrupter interface requirements.
185  template<typename InterrupterType> void setInterrupter(InterrupterType&);
186 
187  template<typename Sampler, typename GridT, typename Transformer>
188  void transformGrid(const Transformer&,
189  const GridT& inGrid, GridT& outGrid) const;
190 
191 protected:
192  template<typename Sampler, typename GridT, typename Transformer>
193  void applyTransform(const Transformer&, const GridT& inGrid, GridT& outGrid) const;
194 
195  bool interrupt() const { return mInterrupt && mInterrupt(); }
196 
197 private:
198  template<typename Sampler, typename InTreeT, typename OutTreeT, typename Transformer>
199  static void transformBBox(const Transformer&, const CoordBBox& inBBox,
200  const InTreeT& inTree, OutTreeT& outTree, const InterruptFunc&,
201  const Sampler& = Sampler());
202 
203  template<typename Sampler, typename TreeT, typename Transformer>
204  class RangeProcessor;
205 
206  bool mThreaded, mTransformTiles;
207  InterruptFunc mInterrupt;
208 };
209 
210 
211 ////////////////////////////////////////
212 
213 
214 /// @brief A GridTransformer applies a geometric transformation to an
215 /// input grid using one of several sampling schemes, and stores
216 /// the result in an output grid.
217 ///
218 /// @note GridTransformer is optimized for affine transformations.
219 ///
220 /// Usage:
221 /// @code
222 /// Mat4R xform = ...;
223 /// GridTransformer transformer(xform);
224 /// transformer.transformGrid<BoxSampler>(inGrid, outGrid);
225 /// @endcode
226 /// or
227 /// @code
228 /// Vec3R pivot = ..., scale = ..., rotate = ..., translate = ...;
229 /// GridTransformer transformer(pivot, scale, rotate, translate);
230 /// transformer.transformGrid<QuadraticSampler>(inGrid, outGrid);
231 /// @endcode
233 {
234 public:
236 
237  GridTransformer(const Mat4R& xform);
239  const Vec3R& pivot,
240  const Vec3R& scale,
241  const Vec3R& rotate,
242  const Vec3R& translate,
243  const std::string& xformOrder = "tsr",
244  const std::string& rotationOrder = "zyx");
245  ~GridTransformer() override = default;
246 
247  GridTransformer(const GridTransformer&) = default;
249 
250  const Mat4R& getTransform() const { return mTransform; }
251 
252  template<class Sampler, class GridT>
253  void transformGrid(const GridT& inGrid, GridT& outGrid) const;
254 
255 private:
256  struct MatrixTransform;
257 
258  inline void init(const Vec3R& pivot, const Vec3R& scale,
259  const Vec3R& rotate, const Vec3R& translate,
260  const std::string& xformOrder, const std::string& rotOrder);
261 
262  Vec3R mPivot;
263  Vec3i mMipLevels;
264  Mat4R mTransform, mPreScaleTransform, mPostScaleTransform;
265 };
266 
267 
268 ////////////////////////////////////////
269 
270 
271 namespace local_util {
272 
274 
275 /// @brief Decompose an affine transform into scale, rotation (XYZ order),
276 /// and translation components.
277 /// @return DECOMP_INVALID if the given matrix is not affine or cannot
278 /// be decomposed, DECOMP_UNIQUE if the matrix has a unique decomposition,
279 /// DECOMP_VALID otherwise
280 template<typename T>
281 inline int
283  math::Vec3<T>& rotate, math::Vec3<T>& translate)
284 {
285  if (!math::isAffine(m)) return DECOMP_INVALID;
286 
287  // This is the translation in world space
288  translate = m.getTranslation();
289  // Extract translation.
290  const math::Mat3<T> xform = m.getMat3();
291 
292  const math::Vec3<T> unsignedScale(
293  (math::Vec3<T>(1, 0, 0) * xform).length(),
294  (math::Vec3<T>(0, 1, 0) * xform).length(),
295  (math::Vec3<T>(0, 0, 1) * xform).length());
296 
297  const bool hasUniformScale = unsignedScale.eq(math::Vec3<T>(unsignedScale[0]));
298 
299  bool hasRotation = false;
300  bool validDecomposition = false;
301 
302  T minAngle = std::numeric_limits<T>::max();
303 
304  // If the transformation matrix contains a reflection, test different negative scales
305  // to find a decomposition that favors the optimal resampling algorithm.
306  for (size_t n = 0; n < 8; ++n) {
307  const math::Vec3<T> signedScale(
308  n & 0x1 ? -unsignedScale.x() : unsignedScale.x(),
309  n & 0x2 ? -unsignedScale.y() : unsignedScale.y(),
310  n & 0x4 ? -unsignedScale.z() : unsignedScale.z());
311 
312  // Extract scale and potentially reflection.
313  const math::Mat3<T> mat = xform * math::scale<math::Mat3<T> >(signedScale).inverse();
314  if (mat.det() < T(0.0)) continue; // Skip if mat contains a reflection.
315 
316  const math::Vec3<T> tmpAngle = math::eulerAngles(mat, math::XYZ_ROTATION);
317 
318  const math::Mat3<T> rebuild =
319  math::rotation<math::Mat3<T> >(math::Vec3<T>(0, 0, 1), tmpAngle.z()) *
320  math::rotation<math::Mat3<T> >(math::Vec3<T>(0, 1, 0), tmpAngle.y()) *
321  math::rotation<math::Mat3<T> >(math::Vec3<T>(1, 0, 0), tmpAngle.x()) *
322  math::scale<math::Mat3<T> >(signedScale);
323 
324  if (xform.eq(rebuild)) {
325 
326  const T maxAngle = std::max(std::abs(tmpAngle[0]),
327  std::max(std::abs(tmpAngle[1]), std::abs(tmpAngle[2])));
328 
329  if (!(minAngle < maxAngle)) { // Update if less or equal.
330 
331  minAngle = maxAngle;
332  rotate = tmpAngle;
333  scale = signedScale;
334 
335  hasRotation = !rotate.eq(math::Vec3<T>::zero());
336  validDecomposition = true;
337 
338  if (hasUniformScale || !hasRotation) {
339  // Current decomposition is optimal.
340  break;
341  }
342  }
343  }
344  }
345 
346  if (!validDecomposition) {
347  // The decomposition is invalid if the transformation matrix contains shear.
348  return DECOMP_INVALID;
349  }
350  if (hasRotation && !hasUniformScale) {
351  // No unique decomposition if scale is nonuniform and rotation is nonzero.
352  return DECOMP_VALID;
353  }
354  return DECOMP_UNIQUE;
355 }
356 
357 } // namespace local_util
358 
359 
360 ////////////////////////////////////////
361 
362 
363 /// This class implements the Transformer functor interface (specifically,
364 /// the isAffine(), transform() and invTransform() methods) for a transform
365 /// that is expressed as a 4 x 4 matrix.
367 {
368  MatrixTransform(): mat(Mat4R::identity()), invMat(Mat4R::identity()) {}
369  MatrixTransform(const Mat4R& xform): mat(xform), invMat(xform.inverse()) {}
370 
371  bool isAffine() const { return math::isAffine(mat); }
372 
373  Vec3R transform(const Vec3R& pos) const { return mat.transformH(pos); }
374 
375  Vec3R invTransform(const Vec3R& pos) const { return invMat.transformH(pos); }
376 
377  Mat4R mat, invMat;
378 };
379 
380 
381 ////////////////////////////////////////
382 
383 
384 /// @brief This class implements the Transformer functor interface (specifically,
385 /// the isAffine(), transform() and invTransform() methods) for a transform
386 /// that maps an A grid into a B grid's index space such that, after resampling,
387 /// A's index space and transform match B's index space and transform.
389 {
390 public:
391  /// @param aXform the A grid's transform
392  /// @param bXform the B grid's transform
393  ABTransform(const math::Transform& aXform, const math::Transform& bXform):
394  mAXform(aXform),
395  mBXform(bXform),
396  mIsAffine(mAXform.isLinear() && mBXform.isLinear()),
397  mIsIdentity(mIsAffine && mAXform == mBXform)
398  {}
399 
400  bool isAffine() const { return mIsAffine; }
401 
402  bool isIdentity() const { return mIsIdentity; }
403 
405  {
406  return mBXform.worldToIndex(mAXform.indexToWorld(pos));
407  }
408 
410  {
411  return mAXform.worldToIndex(mBXform.indexToWorld(pos));
412  }
413 
414  const math::Transform& getA() const { return mAXform; }
415  const math::Transform& getB() const { return mBXform; }
416 
417 private:
418  const math::Transform &mAXform, &mBXform;
419  const bool mIsAffine;
420  const bool mIsIdentity;
421 };
422 
423 
424 /// The normal entry points for resampling are the resampleToMatch() functions,
425 /// which correctly handle level set grids under scaling and shearing.
426 /// doResampleToMatch() is mainly for internal use but is typically faster
427 /// for level sets, and correct provided that no scaling or shearing is needed.
428 ///
429 /// @warning Do not use this function to scale or shear a level set grid.
430 template<typename Sampler, typename Interrupter, typename GridType>
431 inline void
432 doResampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter)
433 {
434  ABTransform xform(inGrid.transform(), outGrid.transform());
435 
436  if (Sampler::consistent() && xform.isIdentity()) {
437  // If the transforms of the input and output are identical, the
438  // output tree is simply a deep copy of the input tree.
439  outGrid.setTree(inGrid.tree().copy());
440  } else if (xform.isAffine()) {
441  // If the input and output transforms are both affine, create an
442  // input to output transform (in:index-to-world * out:world-to-index)
443  // and use the fast GridTransformer API.
444  Mat4R mat = xform.getA().baseMap()->getAffineMap()->getMat4() *
445  ( xform.getB().baseMap()->getAffineMap()->getMat4().inverse() );
446 
447  GridTransformer transformer(mat);
448  transformer.setInterrupter(interrupter);
449 
450  // Transform the input grid and store the result in the output grid.
451  transformer.transformGrid<Sampler>(inGrid, outGrid);
452  } else {
453  // If either the input or the output transform is non-affine,
454  // use the slower GridResampler API.
455  GridResampler resampler;
456  resampler.setInterrupter(interrupter);
457 
458  resampler.transformGrid<Sampler>(xform, inGrid, outGrid);
459  }
460 }
461 
462 
463 template<typename ValueType>
464 struct HalfWidthOp {
465  static ValueType eval(const ValueType& background, const Vec3d& voxelSize)
466  {
468  ValueType result(background * (1.0 / voxelSize[0]));
470  return result;
471  }
472 }; // struct HalfWidthOp
473 
474 template<>
475 struct HalfWidthOp<bool> {
476  static bool eval(const bool& background, const Vec3d& /*voxelSize*/)
477  {
478  return background;
479  }
480 }; // struct HalfWidthOp<bool>
481 
482 
483 template<typename Sampler, typename Interrupter, typename GridType>
484 inline void
485 resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter)
486 {
487  if (inGrid.getGridClass() == GRID_LEVEL_SET) {
488  // If the input grid is a level set, resample it using the level set rebuild tool.
489 
490  if (inGrid.constTransform() == outGrid.constTransform()) {
491  // If the transforms of the input and output grids are identical,
492  // the output tree is simply a deep copy of the input tree.
493  outGrid.setTree(inGrid.tree().copy());
494  return;
495  }
496 
497  // If the output grid is a level set, resample the input grid to have the output grid's
498  // background value. Otherwise, preserve the input grid's background value.
499  using ValueT = typename GridType::ValueType;
500  const bool outIsLevelSet = outGrid.getGridClass() == openvdb::GRID_LEVEL_SET;
501 
502  const ValueT halfWidth = outIsLevelSet
503  ? HalfWidthOp<ValueT>::eval(outGrid.background(), outGrid.voxelSize())
504  : HalfWidthOp<ValueT>::eval(inGrid.background(), inGrid.voxelSize());
505 
506  typename GridType::Ptr tempGrid;
507  try {
508  tempGrid = doLevelSetRebuild(inGrid, /*iso=*/zeroVal<ValueT>(),
509  /*exWidth=*/halfWidth, /*inWidth=*/halfWidth,
510  &outGrid.constTransform(), &interrupter);
511  } catch (TypeError&) {
512  // The input grid is classified as a level set, but it has a value type
513  // that is not supported by the level set rebuild tool. Fall back to
514  // using the generic resampler.
515  tempGrid.reset();
516  }
517  if (tempGrid) {
518  outGrid.setTree(tempGrid->treePtr());
519  return;
520  }
521  }
522 
523  // If the input grid is not a level set, use the generic resampler.
524  doResampleToMatch<Sampler>(inGrid, outGrid, interrupter);
525 }
526 
527 
528 template<typename Sampler, typename GridType>
529 inline void
530 resampleToMatch(const GridType& inGrid, GridType& outGrid)
531 {
532  util::NullInterrupter interrupter;
533  resampleToMatch<Sampler>(inGrid, outGrid, interrupter);
534 }
535 
536 
537 ////////////////////////////////////////
538 
539 
540 inline
541 GridTransformer::GridTransformer(const Mat4R& xform):
542  mPivot(0, 0, 0),
543  mMipLevels(0, 0, 0),
544  mTransform(xform),
545  mPreScaleTransform(Mat4R::identity()),
546  mPostScaleTransform(Mat4R::identity())
547 {
548  Vec3R scale, rotate, translate;
549  if (local_util::decompose(mTransform, scale, rotate, translate)) {
550  // If the transform can be decomposed into affine components,
551  // use them to set up a mipmapping-like scheme for downsampling.
552  init(mPivot, scale, rotate, translate, "rst", "zyx");
553  }
554 }
555 
556 
557 inline
559  const Vec3R& pivot, const Vec3R& scale,
560  const Vec3R& rotate, const Vec3R& translate,
561  const std::string& xformOrder, const std::string& rotOrder):
562  mPivot(0, 0, 0),
563  mMipLevels(0, 0, 0),
564  mPreScaleTransform(Mat4R::identity()),
565  mPostScaleTransform(Mat4R::identity())
566 {
567  init(pivot, scale, rotate, translate, xformOrder, rotOrder);
568 }
569 
570 
571 ////////////////////////////////////////
572 
573 
574 inline void
575 GridTransformer::init(
576  const Vec3R& pivot, const Vec3R& scale,
577  const Vec3R& rotate, const Vec3R& translate,
578  const std::string& xformOrder, const std::string& rotOrder)
579 {
580  if (xformOrder.size() != 3) {
581  OPENVDB_THROW(ValueError, "invalid transform order (" + xformOrder + ")");
582  }
583  if (rotOrder.size() != 3) {
584  OPENVDB_THROW(ValueError, "invalid rotation order (" + rotOrder + ")");
585  }
586 
587  mPivot = pivot;
588 
589  // Scaling is handled via a mipmapping-like scheme of successive
590  // halvings of the tree resolution, until the remaining scale
591  // factor is greater than or equal to 1/2.
592  Vec3R scaleRemainder = scale;
593  for (int i = 0; i < 3; ++i) {
594  double s = std::fabs(scale(i));
595  if (s < 0.5) {
596  mMipLevels(i) = int(std::floor(-std::log(s)/std::log(2.0)));
597  scaleRemainder(i) = scale(i) * (1 << mMipLevels(i));
598  }
599  }
600 
601  // Build pre-scale and post-scale transform matrices based on
602  // the user-specified order of operations.
603  // Note that we iterate over the transform order string in reverse order
604  // (e.g., "t", "r", "s", given "srt"). This is because math::Mat matrices
605  // postmultiply row vectors rather than premultiplying column vectors.
606  mTransform = mPreScaleTransform = mPostScaleTransform = Mat4R::identity();
607  Mat4R* remainder = &mPostScaleTransform;
608  int rpos, spos, tpos;
609  rpos = spos = tpos = 3;
610  for (int ix = 2; ix >= 0; --ix) { // reverse iteration
611  switch (xformOrder[ix]) {
612 
613  case 'r':
614  rpos = ix;
615  mTransform.preTranslate(pivot);
616  remainder->preTranslate(pivot);
617 
618  int xpos, ypos, zpos;
619  xpos = ypos = zpos = 3;
620  for (int ir = 2; ir >= 0; --ir) {
621  switch (rotOrder[ir]) {
622  case 'x':
623  xpos = ir;
624  mTransform.preRotate(math::X_AXIS, rotate.x());
625  remainder->preRotate(math::X_AXIS, rotate.x());
626  break;
627  case 'y':
628  ypos = ir;
629  mTransform.preRotate(math::Y_AXIS, rotate.y());
630  remainder->preRotate(math::Y_AXIS, rotate.y());
631  break;
632  case 'z':
633  zpos = ir;
634  mTransform.preRotate(math::Z_AXIS, rotate.z());
635  remainder->preRotate(math::Z_AXIS, rotate.z());
636  break;
637  }
638  }
639  // Reject rotation order strings that don't contain exactly one
640  // instance of "x", "y" and "z".
641  if (xpos > 2 || ypos > 2 || zpos > 2) {
642  OPENVDB_THROW(ValueError, "invalid rotation order (" + rotOrder + ")");
643  }
644 
645  mTransform.preTranslate(-pivot);
646  remainder->preTranslate(-pivot);
647  break;
648 
649  case 's':
650  spos = ix;
651  mTransform.preTranslate(pivot);
652  mTransform.preScale(scale);
653  mTransform.preTranslate(-pivot);
654 
655  remainder->preTranslate(pivot);
656  remainder->preScale(scaleRemainder);
657  remainder->preTranslate(-pivot);
658  remainder = &mPreScaleTransform;
659  break;
660 
661  case 't':
662  tpos = ix;
663  mTransform.preTranslate(translate);
664  remainder->preTranslate(translate);
665  break;
666  }
667  }
668  // Reject transform order strings that don't contain exactly one
669  // instance of "t", "r" and "s".
670  if (tpos > 2 || rpos > 2 || spos > 2) {
671  OPENVDB_THROW(ValueError, "invalid transform order (" + xformOrder + ")");
672  }
673 }
674 
675 
676 ////////////////////////////////////////
677 
678 
679 template<typename InterrupterType>
680 void
681 GridResampler::setInterrupter(InterrupterType& interrupter)
682 {
683  mInterrupt = std::bind(&InterrupterType::wasInterrupted,
684  /*this=*/&interrupter, /*percent=*/-1);
685 }
686 
687 
688 template<typename Sampler, typename GridT, typename Transformer>
689 void
690 GridResampler::transformGrid(const Transformer& xform,
691  const GridT& inGrid, GridT& outGrid) const
692 {
693  tools::changeBackground(outGrid.tree(), inGrid.background());
694  applyTransform<Sampler>(xform, inGrid, outGrid);
695 }
696 
697 
698 template<class Sampler, class GridT>
699 void
700 GridTransformer::transformGrid(const GridT& inGrid, GridT& outGrid) const
701 {
702  tools::changeBackground(outGrid.tree(), inGrid.background());
703 
704  if (!Sampler::mipmap() || mMipLevels == Vec3i::zero()) {
705  // Skip the mipmapping step.
706  const MatrixTransform xform(mTransform);
707  applyTransform<Sampler>(xform, inGrid, outGrid);
708 
709  } else {
710  bool firstPass = true;
711  const typename GridT::ValueType background = inGrid.background();
712  typename GridT::Ptr tempGrid = GridT::create(background);
713 
714  if (!mPreScaleTransform.eq(Mat4R::identity())) {
715  firstPass = false;
716  // Apply the pre-scale transform to the input grid
717  // and store the result in a temporary grid.
718  const MatrixTransform xform(mPreScaleTransform);
719  applyTransform<Sampler>(xform, inGrid, *tempGrid);
720  }
721 
722  // While the scale factor along one or more axes is less than 1/2,
723  // scale the grid by half along those axes.
724  Vec3i count = mMipLevels; // # of halvings remaining per axis
725  while (count != Vec3i::zero()) {
726  MatrixTransform xform;
727  xform.mat.setTranslation(mPivot);
728  xform.mat.preScale(Vec3R(
729  count.x() ? .5 : 1, count.y() ? .5 : 1, count.z() ? .5 : 1));
730  xform.mat.preTranslate(-mPivot);
731  xform.invMat = xform.mat.inverse();
732 
733  if (firstPass) {
734  firstPass = false;
735  // Scale the input grid and store the result in a temporary grid.
736  applyTransform<Sampler>(xform, inGrid, *tempGrid);
737  } else {
738  // Scale the temporary grid and store the result in a transient grid,
739  // then swap the two and discard the transient grid.
740  typename GridT::Ptr destGrid = GridT::create(background);
741  applyTransform<Sampler>(xform, *tempGrid, *destGrid);
742  tempGrid.swap(destGrid);
743  }
744  // (3, 2, 1) -> (2, 1, 0) -> (1, 0, 0) -> (0, 0, 0), etc.
745  count = math::maxComponent(count - 1, Vec3i::zero());
746  }
747 
748  // Apply the post-scale transform and store the result in the output grid.
749  if (!mPostScaleTransform.eq(Mat4R::identity())) {
750  const MatrixTransform xform(mPostScaleTransform);
751  applyTransform<Sampler>(xform, *tempGrid, outGrid);
752  } else {
753  outGrid.setTree(tempGrid->treePtr());
754  }
755  }
756 }
757 
758 
759 ////////////////////////////////////////
760 
761 
762 template<class Sampler, class TreeT, typename Transformer>
763 class GridResampler::RangeProcessor
764 {
765 public:
766  using LeafIterT = typename TreeT::LeafCIter;
767  using TileIterT = typename TreeT::ValueAllCIter;
768  using LeafRange = typename tree::IteratorRange<LeafIterT>;
769  using TileRange = typename tree::IteratorRange<TileIterT>;
770  using InTreeAccessor = typename tree::ValueAccessor<const TreeT>;
771  using OutTreeAccessor = typename tree::ValueAccessor<TreeT>;
772 
773  RangeProcessor(const Transformer& xform, const CoordBBox& b, const TreeT& inT, TreeT& outT):
774  mIsRoot(true), mXform(xform), mBBox(b),
775  mInTree(inT), mOutTree(&outT), mInAcc(mInTree), mOutAcc(*mOutTree)
776  {}
777 
778  RangeProcessor(const Transformer& xform, const CoordBBox& b, const TreeT& inTree):
779  mIsRoot(false), mXform(xform), mBBox(b),
780  mInTree(inTree), mOutTree(new TreeT(inTree.background())),
781  mInAcc(mInTree), mOutAcc(*mOutTree)
782  {}
783 
784  ~RangeProcessor() { if (!mIsRoot) delete mOutTree; }
785 
786  /// Splitting constructor: don't copy the original processor's output tree
787  RangeProcessor(RangeProcessor& other, tbb::split):
788  mIsRoot(false),
789  mXform(other.mXform),
790  mBBox(other.mBBox),
791  mInTree(other.mInTree),
792  mOutTree(new TreeT(mInTree.background())),
793  mInAcc(mInTree),
794  mOutAcc(*mOutTree),
795  mInterrupt(other.mInterrupt)
796  {}
797 
798  void setInterrupt(const InterruptFunc& f) { mInterrupt = f; }
799 
800  /// Transform each leaf node in the given range.
801  void operator()(LeafRange& r)
802  {
803  for ( ; r; ++r) {
804  if (interrupt()) break;
805  LeafIterT i = r.iterator();
806  CoordBBox bbox(i->origin(), i->origin() + Coord(i->dim()));
807  if (!mBBox.empty()) {
808  // Intersect the leaf node's bounding box with mBBox.
809  bbox = CoordBBox(
810  Coord::maxComponent(bbox.min(), mBBox.min()),
811  Coord::minComponent(bbox.max(), mBBox.max()));
812  }
813  if (!bbox.empty()) {
814  transformBBox<Sampler>(mXform, bbox, mInAcc, mOutAcc, mInterrupt);
815  }
816  }
817  }
818 
819  /// Transform each non-background tile in the given range.
820  void operator()(TileRange& r)
821  {
822  for ( ; r; ++r) {
823  if (interrupt()) break;
824 
825  TileIterT i = r.iterator();
826  // Skip voxels and background tiles.
827  if (!i.isTileValue()) continue;
828  if (!i.isValueOn() && math::isApproxEqual(*i, mOutTree->background())) continue;
829 
830  CoordBBox bbox;
831  i.getBoundingBox(bbox);
832  if (!mBBox.empty()) {
833  // Intersect the tile's bounding box with mBBox.
834  bbox = CoordBBox(
835  Coord::maxComponent(bbox.min(), mBBox.min()),
836  Coord::minComponent(bbox.max(), mBBox.max()));
837  }
838  if (!bbox.empty()) {
839  /// @todo This samples the tile voxel-by-voxel, which is much too slow.
840  /// Instead, compute the largest axis-aligned bounding box that is
841  /// contained in the transformed tile (adjusted for the sampler radius)
842  /// and fill it with the tile value. Then transform the remaining voxels.
843  internal::TileSampler<Sampler, InTreeAccessor>
844  sampler(bbox, i.getValue(), i.isValueOn());
845  transformBBox(mXform, bbox, mInAcc, mOutAcc, mInterrupt, sampler);
846  }
847  }
848  }
849 
850  /// Merge another processor's output tree into this processor's tree.
851  void join(RangeProcessor& other)
852  {
853  if (!interrupt()) mOutTree->merge(*other.mOutTree);
854  }
855 
856 private:
857  bool interrupt() const { return mInterrupt && mInterrupt(); }
858 
859  const bool mIsRoot; // true if mOutTree is the top-level tree
860  Transformer mXform;
861  CoordBBox mBBox;
862  const TreeT& mInTree;
863  TreeT* mOutTree;
864  InTreeAccessor mInAcc;
865  OutTreeAccessor mOutAcc;
866  InterruptFunc mInterrupt;
867 };
868 
869 
870 ////////////////////////////////////////
871 
872 
873 template<class Sampler, class GridT, typename Transformer>
874 void
875 GridResampler::applyTransform(const Transformer& xform,
876  const GridT& inGrid, GridT& outGrid) const
877 {
878  using TreeT = typename GridT::TreeType;
879  const TreeT& inTree = inGrid.tree();
880  TreeT& outTree = outGrid.tree();
881 
882  using RangeProc = RangeProcessor<Sampler, TreeT, Transformer>;
883 
884  const GridClass gridClass = inGrid.getGridClass();
885 
886  if (gridClass != GRID_LEVEL_SET && mTransformTiles) {
887  // Independently transform the tiles of the input grid.
888  // Note: Tiles in level sets can only be background tiles, and they
889  // are handled more efficiently with a signed flood fill (see below).
890 
891  RangeProc proc(xform, CoordBBox(), inTree, outTree);
892  proc.setInterrupt(mInterrupt);
893 
894  typename RangeProc::TileIterT tileIter = inTree.cbeginValueAll();
895  tileIter.setMaxDepth(tileIter.getLeafDepth() - 1); // skip leaf nodes
896  typename RangeProc::TileRange tileRange(tileIter);
897 
898  if (mThreaded) {
899  tbb::parallel_reduce(tileRange, proc);
900  } else {
901  proc(tileRange);
902  }
903  }
904 
905  CoordBBox clipBBox;
906  if (gridClass == GRID_LEVEL_SET) {
907  // Inactive voxels in level sets can only be background voxels, and they
908  // are handled more efficiently with a signed flood fill (see below).
909  clipBBox = inGrid.evalActiveVoxelBoundingBox();
910  }
911 
912  // Independently transform the leaf nodes of the input grid.
913 
914  RangeProc proc(xform, clipBBox, inTree, outTree);
915  proc.setInterrupt(mInterrupt);
916 
917  typename RangeProc::LeafRange leafRange(inTree.cbeginLeaf());
918 
919  if (mThreaded) {
920  tbb::parallel_reduce(leafRange, proc);
921  } else {
922  proc(leafRange);
923  }
924 
925  // If the grid is a level set, mark inactive voxels as inside or outside.
926  if (gridClass == GRID_LEVEL_SET) {
927  tools::pruneLevelSet(outTree);
928  tools::signedFloodFill(outTree);
929  }
930 }
931 
932 
933 ////////////////////////////////////////
934 
935 
936 //static
937 template<class Sampler, class InTreeT, class OutTreeT, class Transformer>
938 void
939 GridResampler::transformBBox(
940  const Transformer& xform,
941  const CoordBBox& bbox,
942  const InTreeT& inTree,
943  OutTreeT& outTree,
944  const InterruptFunc& interrupt,
945  const Sampler& sampler)
946 {
947  using ValueT = typename OutTreeT::ValueType;
948 
949  // Transform the corners of the input tree's bounding box
950  // and compute the enclosing bounding box in the output tree.
951  Vec3R
952  inRMin(bbox.min().x(), bbox.min().y(), bbox.min().z()),
953  inRMax(bbox.max().x()+1, bbox.max().y()+1, bbox.max().z()+1),
954  outRMin = math::minComponent(xform.transform(inRMin), xform.transform(inRMax)),
955  outRMax = math::maxComponent(xform.transform(inRMin), xform.transform(inRMax));
956  for (int i = 0; i < 8; ++i) {
957  Vec3R corner(
958  i & 1 ? inRMax.x() : inRMin.x(),
959  i & 2 ? inRMax.y() : inRMin.y(),
960  i & 4 ? inRMax.z() : inRMin.z());
961  outRMin = math::minComponent(outRMin, xform.transform(corner));
962  outRMax = math::maxComponent(outRMax, xform.transform(corner));
963  }
964  Vec3i
965  outMin = local_util::floorVec3(outRMin) - Sampler::radius(),
966  outMax = local_util::ceilVec3(outRMax) + Sampler::radius();
967 
968  if (!xform.isAffine()) {
969  // If the transform is not affine, back-project each output voxel
970  // into the input tree.
971  Vec3R xyz, inXYZ;
972  Coord outXYZ;
973  int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z();
974  for (x = outMin.x(); x <= outMax.x(); ++x) {
975  if (interrupt && interrupt()) break;
976  xyz.x() = x;
977  for (y = outMin.y(); y <= outMax.y(); ++y) {
978  if (interrupt && interrupt()) break;
979  xyz.y() = y;
980  for (z = outMin.z(); z <= outMax.z(); ++z) {
981  xyz.z() = z;
982  inXYZ = xform.invTransform(xyz);
983  ValueT result;
984  if (sampler.sample(inTree, inXYZ, result)) {
985  outTree.setValueOn(outXYZ, result);
986  } else {
987  // Note: Don't overwrite existing active values with inactive values.
988  if (!outTree.isValueOn(outXYZ)) {
989  outTree.setValueOff(outXYZ, result);
990  }
991  }
992  }
993  }
994  }
995  } else { // affine
996  // Compute step sizes in the input tree that correspond to
997  // unit steps in x, y and z in the output tree.
998  const Vec3R
999  translation = xform.invTransform(Vec3R(0, 0, 0)),
1000  deltaX = xform.invTransform(Vec3R(1, 0, 0)) - translation,
1001  deltaY = xform.invTransform(Vec3R(0, 1, 0)) - translation,
1002  deltaZ = xform.invTransform(Vec3R(0, 0, 1)) - translation;
1003 
1004 #if defined(__ICC)
1005  /// @todo The following line is a workaround for bad code generation
1006  /// in opt-icc11.1_64 (but not debug or gcc) builds. It should be
1007  /// removed once the problem has been addressed at its source.
1008  const Vec3R dummy = deltaX;
1009 #endif
1010 
1011  // Step by whole voxels through the output tree, sampling the
1012  // corresponding fractional voxels of the input tree.
1013  Vec3R inStartX = xform.invTransform(Vec3R(outMin));
1014  Coord outXYZ;
1015  int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z();
1016  for (x = outMin.x(); x <= outMax.x(); ++x, inStartX += deltaX) {
1017  if (interrupt && interrupt()) break;
1018  Vec3R inStartY = inStartX;
1019  for (y = outMin.y(); y <= outMax.y(); ++y, inStartY += deltaY) {
1020  if (interrupt && interrupt()) break;
1021  Vec3R inXYZ = inStartY;
1022  for (z = outMin.z(); z <= outMax.z(); ++z, inXYZ += deltaZ) {
1023  ValueT result;
1024  if (sampler.sample(inTree, inXYZ, result)) {
1025  outTree.setValueOn(outXYZ, result);
1026  } else {
1027  // Note: Don't overwrite existing active values with inactive values.
1028  if (!outTree.isValueOn(outXYZ)) {
1029  outTree.setValueOff(outXYZ, result);
1030  }
1031  }
1032  }
1033  }
1034  }
1035  }
1036 } // GridResampler::transformBBox()
1037 
1038 } // namespace tools
1039 } // namespace OPENVDB_VERSION_NAME
1040 } // namespace openvdb
1041 
1042 #endif // OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
Efficient multi-threaded replacement of the background values in tree.
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
Bracket code with OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN/_END, to inhibit warnings about type conve...
Definition: Platform.h:206
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_END
Definition: Platform.h:207
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...
Definition: Exceptions.h:64
Definition: Exceptions.h:65
Axis-aligned bounding box of signed integer coordinates.
Definition: Coord.h:248
const Coord & min() const
Definition: Coord.h:320
const Coord & max() const
Definition: Coord.h:321
Signed (x, y, z) 32-bit integer coordinates.
Definition: Coord.h:25
Int32 y() const
Definition: Coord.h:131
void minComponent(const Coord &other)
Perform a component-wise minimum with the other Coord.
Definition: Coord.h:175
void maxComponent(const Coord &other)
Perform a component-wise maximum with the other Coord.
Definition: Coord.h:183
Int32 x() const
Definition: Coord.h:130
Int32 z() const
Definition: Coord.h:132
3x3 matrix class.
Definition: Mat3.h:29
bool eq(const Mat3 &m, T eps=1.0e-8) const
Return true if this matrix is equivalent to m within a tolerance of eps.
Definition: Mat3.h:315
T det() const
Determinant of matrix.
Definition: Mat3.h:493
void preScale(const Vec3< T0 > &v)
Definition: Mat4.h:750
Vec3< T > getTranslation() const
Return the translation component.
Definition: Mat4.h:323
void preTranslate(const Vec3< T0 > &tr)
Left multiples by the specified translation, i.e. Trans * (*this)
Definition: Mat4.h:717
Mat3< T > getMat3() const
Definition: Mat4.h:311
Mat4 inverse(T tolerance=0) const
Definition: Mat4.h:499
void preRotate(Axis axis, T angle)
Left multiplies by a rotation clock-wiseabout the given axis into this matrix.
Definition: Mat4.h:812
bool eq(const Mat4 &m, T eps=1.0e-8) const
Return true if this matrix is equivalent to m within a tolerance of eps.
Definition: Mat4.h:347
static const Mat4< Real > & identity()
Predefined constant for identity matrix.
Definition: Mat4.h:131
void setTranslation(const Vec3< T > &t)
Definition: Mat4.h:328
Definition: Transform.h:40
MapBase::ConstPtr baseMap() const
Return a base pointer to the transformation map.
Definition: Transform.h:131
bool eq(const Vec3< T > &v, T eps=static_cast< T >(1.0e-7)) const
Test if "this" vector is equivalent to vector v with tolerance of eps.
Definition: Vec3.h:137
T & y()
Definition: Vec3.h:90
T & z()
Definition: Vec3.h:91
T & x()
Reference to the component, e.g. v.x() = 4.5f;.
Definition: Vec3.h:89
This class implements the Transformer functor interface (specifically, the isAffine(),...
Definition: GridTransformer.h:389
const math::Transform & getA() const
Definition: GridTransformer.h:414
openvdb::Vec3R transform(const openvdb::Vec3R &pos) const
Definition: GridTransformer.h:404
ABTransform(const math::Transform &aXform, const math::Transform &bXform)
Definition: GridTransformer.h:393
const math::Transform & getB() const
Definition: GridTransformer.h:415
bool isAffine() const
Definition: GridTransformer.h:400
bool isIdentity() const
Definition: GridTransformer.h:402
openvdb::Vec3R invTransform(const openvdb::Vec3R &pos) const
Definition: GridTransformer.h:409
Definition: GridTransformer.h:162
bool transformTiles() const
Return true if tile processing is enabled.
Definition: GridTransformer.h:180
void applyTransform(const Transformer &, const GridT &inGrid, GridT &outGrid) const
Definition: GridTransformer.h:875
GridResampler()
Definition: GridTransformer.h:167
void setInterrupter(InterrupterType &)
Allow processing to be aborted by providing an interrupter object. The interrupter will be queried pe...
Definition: GridTransformer.h:681
std::function< bool(void)> InterruptFunc
Definition: GridTransformer.h:165
GridResampler & operator=(const GridResampler &)=default
void setTransformTiles(bool b)
Enable or disable processing of tiles. (Enabled by default, except for level set grids....
Definition: GridTransformer.h:178
SharedPtr< GridResampler > Ptr
Definition: GridTransformer.h:164
bool interrupt() const
Definition: GridTransformer.h:195
void transformGrid(const Transformer &, const GridT &inGrid, GridT &outGrid) const
Definition: GridTransformer.h:690
void setThreaded(bool b)
Enable or disable threading. (Threading is enabled by default.)
Definition: GridTransformer.h:174
virtual ~GridResampler()
Definition: GridTransformer.h:168
bool threaded() const
Return true if threading is enabled.
Definition: GridTransformer.h:176
GridResampler(const GridResampler &)=default
A GridTransformer applies a geometric transformation to an input grid using one of several sampling s...
Definition: GridTransformer.h:233
GridTransformer(const Mat4R &xform)
Definition: GridTransformer.h:541
GridTransformer(const GridTransformer &)=default
const Mat4R & getTransform() const
Definition: GridTransformer.h:250
GridTransformer & operator=(const GridTransformer &)=default
void transformGrid(const GridT &inGrid, GridT &outGrid) const
Definition: GridTransformer.h:700
Definition: TreeIterator.h:1303
void pivot(int i, int j, Mat3< T > &S, Vec3< T > &D, Mat3< T > &Q)
Definition: Mat3.h:682
MatType scale(const Vec3< typename MatType::value_type > &s)
Return a matrix that scales by s.
Definition: Mat.h:637
Vec2< T > maxComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise maximum of the two vectors.
Definition: Vec2.h:517
MatType rotation(const Quat< typename MatType::value_type > &q, typename MatType::value_type eps=static_cast< typename MatType::value_type >(1.0e-8))
Return the rotation matrix specified by the given quaternion.
Definition: Mat.h:194
Vec2< T > minComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise minimum of the two vectors.
Definition: Vec2.h:508
bool isAffine(const Mat4< T > &m)
Definition: Mat4.h:1318
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
Vec3< typename MatType::value_type > eulerAngles(const MatType &mat, RotationOrder rotationOrder, typename MatType::value_type eps=static_cast< typename MatType::value_type >(1.0e-8))
Return the Euler angles composing the given rotation matrix.
Definition: Mat.h:355
Vec3< int32_t > Vec3i
Definition: Vec3.h:665
@ Z_AXIS
Definition: Math.h:907
@ X_AXIS
Definition: Math.h:905
@ Y_AXIS
Definition: Math.h:906
@ XYZ_ROTATION
Definition: Math.h:912
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:102
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:106
Vec3i ceilVec3(const Vec3R &v)
Definition: Interpolation.h:591
int decompose(const math::Mat4< T > &m, math::Vec3< T > &scale, math::Vec3< T > &rotate, math::Vec3< T > &translate)
Decompose an affine transform into scale, rotation (XYZ order), and translation components.
Definition: GridTransformer.h:282
@ DECOMP_VALID
Definition: GridTransformer.h:273
@ DECOMP_INVALID
Definition: GridTransformer.h:273
@ DECOMP_UNIQUE
Definition: GridTransformer.h:273
Vec3i floorVec3(const Vec3R &v)
Definition: Interpolation.h:584
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 signedFloodFill(TreeOrLeafManagerT &tree, bool threaded=true, size_t grainSize=1, Index minLevel=0)
Set the values of all inactive voxels and tiles of a narrow-band level set from the signs of the acti...
Definition: SignedFloodFill.h:266
void changeBackground(TreeOrLeafManagerT &tree, const typename TreeOrLeafManagerT::ValueType &background, bool threaded=true, size_t grainSize=32)
Replace the background value in all the nodes of a tree.
Definition: ChangeBackground.h:203
void doResampleToMatch(const GridType &inGrid, GridType &outGrid, Interrupter &interrupter)
Definition: GridTransformer.h:432
void resampleToMatch(const GridType &inGrid, GridType &outGrid)
Resample an input grid into an output grid of the same type such that, after resampling,...
Definition: GridTransformer.h:530
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:49
GridClass
Definition: Types.h:335
@ GRID_LEVEL_SET
Definition: Types.h:337
math::Mat4< Real > Mat4R
Definition: Types.h:101
math::BBox< Vec3d > BBoxd
Definition: Types.h:84
math::Vec3< Real > Vec3R
Definition: Types.h:72
std::shared_ptr< T > SharedPtr
Definition: Types.h:114
Definition: Exceptions.h:13
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
Vec3R invTransform(const Vec3R &pos) const
Definition: GridTransformer.h:375
MatrixTransform()
Definition: GridTransformer.h:368
bool isAffine() const
Definition: GridTransformer.h:371
Mat4R invMat
Definition: GridTransformer.h:377
Mat4R mat
Definition: GridTransformer.h:377
Vec3R transform(const Vec3R &pos) const
Definition: GridTransformer.h:373
MatrixTransform(const Mat4R &xform)
Definition: GridTransformer.h:369
static bool eval(const bool &background, const Vec3d &)
Definition: GridTransformer.h:476
Definition: GridTransformer.h:464
static ValueType eval(const ValueType &background, const Vec3d &voxelSize)
Definition: GridTransformer.h:465
Provises a unified interface for sampling, i.e. interpolation.
Definition: Interpolation.h:64
static bool sample(const TreeT &inTree, const Vec3R &inCoord, typename TreeT::ValueType &result)
Sample inTree at the floating-point index coordinate inCoord and store the result in result.
Dummy NOOP interrupter class defining interface.
Definition: NullInterrupter.h:26
#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