HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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
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 
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
543  mPivot(0, 0, 0),
544  mMipLevels(0, 0, 0),
545  mTransform(xform),
546  mPreScaleTransform(Mat4R::identity()),
547  mPostScaleTransform(Mat4R::identity())
548 {
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()(const LeafRange& r)
803  {
804  for (LeafRange it(r); it.test(); ++it) {
805  if (interrupt()) break;
806  LeafIterT i = it.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()(const TileRange& r)
822  {
823  for (TileRange it(r); it.test(); ++it) {
824  if (interrupt()) break;
825 
826  TileIterT i = it.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 transformTiles() const
Return true if tile processing is enabled.
void setThreaded(bool b)
Enable or disable threading. (Threading is enabled by default.)
Vec2< T > minComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise minimum of the two vectors.
Definition: Vec2.h:504
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_END
Definition: Platform.h:234
typedef int(APIENTRYP RE_PFNGLXSWAPINTERVALSGIPROC)(int)
void transformGrid(const GridT &inGrid, GridT &outGrid) const
Vec3d indexToWorld(const Vec3d &xyz) const
Apply this transformation to the given coordinates.
Definition: Transform.h:108
math::Vec3< Real > Vec3R
Definition: Types.h:72
Vec3< T0 > transformH(const Vec3< T0 > &p) const
Transform a Vec3 by post-multiplication, doing homogenous divison.
Definition: Mat4.h:1026
IMATH_HOSTDEVICE constexpr int floor(T x) IMATH_NOEXCEPT
Definition: ImathFun.h:112
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...
GridResampler & operator=(const GridResampler &)=default
GLsizei const GLchar *const * string
Definition: glcorearb.h:814
GLdouble GLdouble GLdouble z
Definition: glcorearb.h:848
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.
openvdb::Vec3R transform(const openvdb::Vec3R &pos) const
GLdouble s
Definition: glad.h:3009
static bool eval(const bool &background, const Vec3d &)
GLuint GLsizei GLsizei * length
Definition: glcorearb.h:795
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:239
ABTransform(const math::Transform &aXform, const math::Transform &bXform)
ImageBuf OIIO_API min(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
GLint y
Definition: glcorearb.h:103
The Value Accessor Implementation and API methods. The majoirty of the API matches the API of a compa...
Definition: ValueAccessor.h:68
**But if you need a result
Definition: thread.h:613
GLuint sampler
Definition: glcorearb.h:1656
void setTranslation(const Vec3< T > &t)
Definition: Mat4.h:314
void preRotate(Axis axis, T angle)
Left multiplies by a rotation clock-wiseabout the given axis into this matrix.
Definition: Mat4.h:798
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:333
GridTransformer & operator=(const GridTransformer &)=default
size_t remainder
Definition: wrapArray.h:342
static ValueType eval(const ValueType &background, const Vec3d &voxelSize)
std::shared_ptr< T > SharedPtr
Definition: Types.h:114
GA_API const UT_StringHolder scale
GLdouble n
Definition: glcorearb.h:2008
GLfloat f
Definition: glcorearb.h:1926
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:406
void preScale(const Vec3< T0 > &v)
Definition: Mat4.h:736
Vec3< T > getTranslation() const
Return the translation component.
Definition: Mat4.h:309
void applyTransform(const Transformer &, const GridT &inGrid, GridT &outGrid) const
void transformGrid(const Transformer &, const GridT &inGrid, GridT &outGrid) const
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
Efficient multi-threaded replacement of the background values in tree.
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:301
openvdb::Vec3R invTransform(const openvdb::Vec3R &pos) const
Defined various multi-threaded utility functions for trees.
constexpr enabler dummy
An instance to use in EnableIf.
Definition: CLI11.h:985
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's index space such that, after resampling, A's index space and transform match B's index space and transform.
void preTranslate(const Vec3< T0 > &tr)
Left multiples by the specified translation, i.e. Trans * (*this)
Definition: Mat4.h:703
bool threaded() const
Return true if threading is enabled.
class OCIOEXPORT MatrixTransform
#define OPENVDB_VOLUME_TREE_INSTANTIATE(Function)
Definition: version.h:169
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1222
GLint GLenum GLint x
Definition: glcorearb.h:409
ImageBuf OIIO_API rotate(const ImageBuf &src, float angle, string_view filtername=string_view(), float filterwidth=0.0f, bool recompute_roi=false, ROI roi={}, int nthreads=0)
#define OPENVDB_VEC3_TREE_INSTANTIATE(Function)
Definition: version.h:165
Propagate the signs of distance values from the active voxels in the narrow band to the inactive valu...
#define OPENVDB_NUMERIC_TREE_INSTANTIATE(Function)
Definition: version.h:160
Provises a unified interface for sampling, i.e. interpolation.
Definition: Interpolation.h:63
void resampleToMatch(const GridType &inGrid, GridType &outGrid, Interrupter &interrupter)
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's transform is unchanged.
MatType scale(const Vec3< typename MatType::value_type > &s)
Return a matrix that scales by s.
Definition: Mat.h:615
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:133
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:333
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...
T & x()
Reference to the component, e.g. v.x() = 4.5f;.
Definition: Vec3.h:85
A GridTransformer applies a geometric transformation to an input grid using one of several sampling s...
#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:233
math::BBox< Vec3d > BBoxd
Definition: Types.h:84
ImageBuf OIIO_API max(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
Vec2< T > maxComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise maximum of the two vectors.
Definition: Vec2.h:513
static const Mat4< Real > & identity()
Predefined constant for identity matrix.
Definition: Mat4.h:117
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.
GA_API const UT_StringHolder pivot
void setTransformTiles(bool b)
Enable or disable processing of tiles. (Enabled by default, except for level set grids.)
void setInterrupter(InterrupterType &)
Allow processing to be aborted by providing an interrupter object. The interrupter will be queried pe...
IMATH_INTERNAL_NAMESPACE_HEADER_ENTER IMATH_HOSTDEVICE constexpr T abs(T a) IMATH_NOEXCEPT
Definition: ImathFun.h:26
void doResampleToMatch(const GridType &inGrid, GridType &outGrid, Interrupter &interrupter)
GLboolean r
Definition: glcorearb.h:1222
PUGI__FN char_t * translate(char_t *buffer, const char_t *from, const char_t *to, size_t to_length)
Definition: pugixml.cpp:8352
void OIIO_UTIL_API split(string_view str, std::vector< string_view > &result, string_view sep=string_view(), int maxsplit=-1)
OIIO_FORCEINLINE T log(const T &v)
Definition: simd.h:7688
Vec3d worldToIndex(const Vec3d &xyz) const
Apply this transformation to the given coordinates.
Definition: Transform.h:110
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
bool wasInterrupted(T *i, int percent=-1)
ImageBuf OIIO_API zero(ROI roi, int nthreads=0)
math::Mat4< Real > Mat4R
Definition: Types.h:101
bool isAffine(const Mat4< T > &m)
Definition: Mat4.h:1304
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:119
Mat4 inverse(T tolerance=0) const
Definition: Mat4.h:485
GLint GLsizei count
Definition: glcorearb.h:405
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
auto join(It begin, Sentinel end, string_view sep) -> join_view< It, Sentinel >
Definition: format.h:2559
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:172