OpenVDB  6.2.0
GridTransformer.h
Go to the documentation of this file.
1 //
3 // Copyright (c) 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 //
30 
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 
79 template<typename Sampler, typename Interrupter, typename GridType>
80 inline void
81 resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter);
82 
104 template<typename Sampler, typename GridType>
105 inline void
106 resampleToMatch(const GridType& inGrid, GridType& outGrid);
107 
108 
110 
111 
112 namespace internal {
113 
117 template<typename Sampler, typename TreeT>
118 class TileSampler: public Sampler
119 {
120 public:
121  using ValueT = typename TreeT::ValueType;
122 
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:
142  bool mActive, mEmpty;
143 };
144 
145 
148 template<typename TreeT>
149 class TileSampler<PointSampler, TreeT>: public PointSampler {
150 public:
151  TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {}
152 };
153 
156 template<typename TreeT>
158 public:
159  TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {}
160 };
161 
162 } // namespace internal
163 
164 
166 
167 
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 
198  void setThreaded(bool b) { mThreaded = b; }
200  bool threaded() const { return mThreaded; }
202  void setTransformTiles(bool b) { mTransformTiles = b; }
204  bool transformTiles() const { return mTransformTiles; }
205 
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 
236 
237 
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 
293 
294 
295 namespace local_util {
296 
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 
380 
381 
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 
396  Mat4R mat, invMat;
397 };
398 
399 
401 
402 
408 {
409 public:
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 
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 bool outIsLevelSet = outGrid.getGridClass() == openvdb::GRID_LEVEL_SET;
500 
502  const ValueT halfWidth = outIsLevelSet
503  ? ValueT(outGrid.background() * (1.0 / outGrid.voxelSize()[0]))
504  : ValueT(inGrid.background() * (1.0 / inGrid.voxelSize()[0]));
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 inline void
531 resampleToMatch(const GridType& inGrid, GridType& outGrid)
532 {
533  util::NullInterrupter interrupter;
534  resampleToMatch<Sampler>(inGrid, outGrid, interrupter);
535 }
536 
537 
539 
540 
541 inline
542 GridTransformer::GridTransformer(const Mat4R& xform):
543  mPivot(0, 0, 0),
544  mMipLevels(0, 0, 0),
545  mTransform(xform),
546  mPreScaleTransform(Mat4R::identity()),
547  mPostScaleTransform(Mat4R::identity())
548 {
549  Vec3R scale, rotate, translate;
550  if (local_util::decompose(mTransform, scale, rotate, translate)) {
551  // If the transform can be decomposed into affine components,
552  // use them to set up a mipmapping-like scheme for downsampling.
553  init(mPivot, scale, rotate, translate, "srt", "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 
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 
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 
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 
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 
802  void operator()(LeafRange& r)
803  {
804  for ( ; r; ++r) {
805  if (interrupt()) break;
806  LeafIterT i = r.iterator();
807  CoordBBox bbox(i->origin(), i->origin() + Coord(i->dim()));
808  if (!mBBox.empty()) {
809  // Intersect the leaf node's bounding box with mBBox.
810  bbox = CoordBBox(
811  Coord::maxComponent(bbox.min(), mBBox.min()),
812  Coord::minComponent(bbox.max(), mBBox.max()));
813  }
814  if (!bbox.empty()) {
815  transformBBox<Sampler>(mXform, bbox, mInAcc, mOutAcc, mInterrupt);
816  }
817  }
818  }
819 
821  void operator()(TileRange& r)
822  {
823  for ( ; r; ++r) {
824  if (interrupt()) break;
825 
826  TileIterT i = r.iterator();
827  // Skip voxels and background tiles.
828  if (!i.isTileValue()) continue;
829  if (!i.isValueOn() && math::isApproxEqual(*i, mOutTree->background())) continue;
830 
831  CoordBBox bbox;
832  i.getBoundingBox(bbox);
833  if (!mBBox.empty()) {
834  // Intersect the tile's bounding box with mBBox.
835  bbox = CoordBBox(
836  Coord::maxComponent(bbox.min(), mBBox.min()),
837  Coord::minComponent(bbox.max(), mBBox.max()));
838  }
839  if (!bbox.empty()) {
845  sampler(bbox, i.getValue(), i.isValueOn());
846  transformBBox(mXform, bbox, mInAcc, mOutAcc, mInterrupt, sampler);
847  }
848  }
849  }
850 
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 
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 
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  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 } // namespace tools
1040 } // namespace OPENVDB_VERSION_NAME
1041 } // namespace openvdb
1042 
1043 #endif // OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
1044 
1045 // Copyright (c) DreamWorks Animation LLC
1046 // All rights reserved. This software is distributed under the
1047 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
A GridTransformer applies a geometric transformation to an input grid using one of several sampling s...
Definition: GridTransformer.h:256
TileSampler(const CoordBBox &, const typename TreeT::ValueType &, bool)
Definition: GridTransformer.h:151
#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:223
Definition: Math.h:884
Definition: Interpolation.h:123
void pivot(int i, int j, Mat3< T > &S, Vec3< T > &D, Mat3< T > &Q)
Definition: Mat3.h:716
3x3 matrix class.
Definition: Mat3.h:55
typename TreeT::ValueType ValueT
Definition: GridTransformer.h:121
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
void setTransformTiles(bool b)
Enable or disable processing of tiles. (Enabled by default, except for level set grids.)
Definition: GridTransformer.h:202
Provises a unified interface for sampling, i.e. interpolation.
Definition: Interpolation.h:90
Definition: GridTransformer.h:185
Mat4R mat
Definition: GridTransformer.h:396
void applyTransform(const Transformer &, const GridT &inGrid, GridT &outGrid) const
Definition: GridTransformer.h:876
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.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:379
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:109
std::function< bool(void)> InterruptFunc
Definition: GridTransformer.h:189
Efficient multi-threaded replacement of the background values in tree.
GridResampler()
Definition: GridTransformer.h:191
math::Vec3< Real > Vec3R
Definition: Types.h:79
bool isIdentity() const
Definition: GridTransformer.h:421
ValueT mVal
Definition: GridTransformer.h:141
Vec3R transform(const Vec3R &pos) const
Definition: GridTransformer.h:392
std::shared_ptr< T > SharedPtr
Definition: Types.h:139
Vec3< T > getTranslation() const
Return the translation component.
Definition: Mat4.h:355
Vec3< int32_t > Vec3i
Definition: Vec3.h:686
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_END
Definition: Platform.h:224
static const Mat4< Real > & identity()
Predefined constant for identity matrix.
Definition: Mat4.h:152
Defined various multi-threaded utility functions for trees.
void setInterrupter(InterrupterType &)
Allow processing to be aborted by providing an interrupter object. The interrupter will be queried pe...
Definition: GridTransformer.h:682
TileSampler(const CoordBBox &b, const ValueT &tileVal, bool on)
Definition: GridTransformer.h:126
Vec3i ceilVec3(const Vec3R &v)
Definition: Interpolation.h:618
bool isAffine() const
Definition: GridTransformer.h:419
MatrixTransform(const Mat4R &xform)
Definition: GridTransformer.h:388
Vec2< T > maxComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise maximum of the two vectors.
Definition: Vec2.h:539
void resampleToMatch(const GridType &inGrid, GridType &outGrid)
Resample an input grid into an output grid of the same type such that, after resampling, the input and output grids coincide (apart from sampling artifacts), but the output grid&#39;s transform is unchanged.
Definition: GridTransformer.h:531
void preTranslate(const Vec3< T0 > &tr)
Left multiples by the specified translation, i.e. Trans * (*this)
Definition: Mat4.h:749
Definition: Math.h:879
bool isAffine() const
Definition: GridTransformer.h:390
BBoxd mBBox
Definition: GridTransformer.h:140
Definition: Interpolation.h:223
void doResampleToMatch(const GridType &inGrid, GridType &outGrid, Interrupter &interrupter)
Definition: GridTransformer.h:451
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:128
Definition: Exceptions.h:91
const Mat4R & getTransform() const
Definition: GridTransformer.h:274
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
void preScale(const Vec3< T0 > &v)
Definition: Mat4.h:782
Propagate the signs of distance values from the active voxels in the narrow band to the inactive valu...
void changeBackground(TreeOrLeafManagerT &tree, const typename TreeOrLeafManagerT::ValueType &background, bool threaded=true, size_t grainSize=32)
Replace the background value in all the nodes of a tree.
Definition: ChangeBackground.h:230
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 threaded() const
Return true if threading is enabled.
Definition: GridTransformer.h:200
This class implements the Transformer functor interface (specifically, the isAffine(), transform() and invTransform() methods) for a transform that maps an A grid into a B grid&#39;s index space such that, after resampling, A&#39;s index space and transform match B&#39;s index space and transform.
Definition: GridTransformer.h:407
T & z()
Definition: Vec3.h:112
bool transformTiles() const
Return true if tile processing is enabled.
Definition: GridTransformer.h:204
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:378
T & y()
Definition: Vec3.h:111
Definition: Exceptions.h:40
GridClass
Definition: Types.h:502
Definition: TreeIterator.h:1334
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
Mat3< T > getMat3() const
Definition: Mat4.h:343
Dummy NOOP interrupter class defining interface.
Definition: NullInterrupter.h:52
openvdb::Vec3R transform(const openvdb::Vec3R &pos) const
Definition: GridTransformer.h:423
const math::Transform & getA() const
Definition: GridTransformer.h:433
Mat4 inverse(T tolerance=0) const
Definition: Mat4.h:531
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...
Definition: Math.h:878
Definition: Types.h:504
bool isAffine(const Mat4< T > &m)
Definition: Mat4.h:1350
void setThreaded(bool b)
Enable or disable threading. (Threading is enabled by default.)
Definition: GridTransformer.h:198
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.
Definition: GridTransformer.h:301
SharedPtr< GridResampler > Ptr
Definition: GridTransformer.h:188
Mat4R invMat
Definition: GridTransformer.h:396
virtual ~GridResampler()
Definition: GridTransformer.h:192
ABTransform(const math::Transform &aXform, const math::Transform &bXform)
Definition: GridTransformer.h:412
openvdb::Vec3R invTransform(const openvdb::Vec3R &pos) const
Definition: GridTransformer.h:428
MatrixTransform()
Definition: GridTransformer.h:387
void preRotate(Axis axis, T angle)
Left multiplies by a rotation clock-wiseabout the given axis into this matrix.
Definition: Mat4.h:844
void transformGrid(const GridT &inGrid, GridT &outGrid) const
Definition: GridTransformer.h:701
const math::Transform & getB() const
Definition: GridTransformer.h:434
Vec3R invTransform(const Vec3R &pos) const
Definition: GridTransformer.h:394
Definition: Math.h:877
A TileSampler wraps a grid sampler of another type (BoxSampler, QuadraticSampler, etc...
Definition: GridTransformer.h:118
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:129
Vec3i floorVec3(const Vec3R &v)
Definition: Interpolation.h:611
void transformGrid(const Transformer &, const GridT &inGrid, GridT &outGrid) const
Definition: GridTransformer.h:691
Definition: Transform.h:66
void signedFloodFill(TreeOrLeafManagerT &tree, bool threaded=true, size_t grainSize=1, Index minLevel=0)
Set the values of all inactive voxels and tiles of a narrow-band level set from the signs of the acti...
Definition: SignedFloodFill.h:294
TileSampler(const CoordBBox &, const typename TreeT::ValueType &, bool)
Definition: GridTransformer.h:159
GridTransformer(const Mat4R &xform)
Definition: GridTransformer.h:542
Vec2< T > minComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise minimum of the two vectors.
Definition: Vec2.h:530
void setTranslation(const Vec3< T > &t)
Definition: Mat4.h:360
Definition: Exceptions.h:92
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:180
bool interrupt() const
Definition: GridTransformer.h:219
MatType scale(const Vec3< typename MatType::value_type > &s)
Return a matrix that scales by s.
Definition: Mat.h:647
bool sample(const TreeT &inTree, const Vec3R &inCoord, ValueT &result) const
Definition: GridTransformer.h:133
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
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 mEmpty
Definition: GridTransformer.h:142
T & x()
Reference to the component, e.g. v.x() = 4.5f;.
Definition: Vec3.h:110
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:76