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