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