OpenVDB  3.1.0
GridTransformer.h
Go to the documentation of this file.
1 //
3 // Copyright (c) 2012-2015 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 <cmath>
38 #include <boost/bind.hpp>
39 #include <boost/function.hpp>
40 #include <boost/shared_ptr.hpp>
41 #include <tbb/blocked_range.h>
42 #include <tbb/parallel_reduce.h>
43 #include <openvdb/Grid.h>
44 #include <openvdb/Types.h>
45 #include <openvdb/math/Math.h> // for isApproxEqual()
46 #include <openvdb/util/NullInterrupter.h>
47 #include "ChangeBackground.h"
48 #include "Interpolation.h"
49 #include "LevelSetRebuild.h" // for doLevelSetRebuild()
50 #include "SignedFloodFill.h" // for signedFloodFill
51 #include "Prune.h" // for pruneLevelSet
52 
53 namespace openvdb {
55 namespace OPENVDB_VERSION_NAME {
56 namespace tools {
57 
81 template<typename Sampler, typename Interrupter, typename GridType>
82 inline void
83 resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter);
84 
106 template<typename Sampler, typename GridType>
107 inline void
108 resampleToMatch(const GridType& inGrid, GridType& outGrid);
109 
110 
112 
113 
114 namespace internal {
115 
119 template<typename Sampler, typename TreeT>
120 class TileSampler: public Sampler
121 {
122 public:
123  typedef typename TreeT::ValueType ValueT;
124 
128  TileSampler(const CoordBBox& b, const ValueT& tileVal, bool on):
129  mBBox(b.min().asVec3d(), b.max().asVec3d()), mVal(tileVal), mActive(on), mEmpty(false)
130  {
131  mBBox.expand(-this->radius()); // shrink the bounding box by the sample radius
132  mEmpty = mBBox.empty();
133  }
134 
135  bool sample(const TreeT& inTree, const Vec3R& inCoord, ValueT& result) const
136  {
137  if (!mEmpty && mBBox.isInside(inCoord)) { result = mVal; return mActive; }
138  return Sampler::sample(inTree, inCoord, result);
139  }
140 
141 protected:
143  ValueT mVal;
144  bool mActive, mEmpty;
145 };
146 
147 
150 template<typename TreeT>
151 class TileSampler<PointSampler, TreeT>: public PointSampler {
152 public:
153  TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {}
154 };
155 
158 template<typename TreeT>
160 public:
161  TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {}
162 };
163 
164 } // namespace internal
165 
166 
168 
169 
188 {
189 public:
190  typedef boost::shared_ptr<GridResampler> Ptr;
191  typedef boost::function<bool (void)> InterruptFunc;
192 
193  GridResampler(): mThreaded(true), mTransformTiles(true) {}
194  virtual ~GridResampler() {}
195 
197  void setThreaded(bool b) { mThreaded = b; }
199  bool threaded() const { return mThreaded; }
201  void setTransformTiles(bool b) { mTransformTiles = b; }
203  bool transformTiles() const { return mTransformTiles; }
204 
208  template<typename InterrupterType> void setInterrupter(InterrupterType&);
209 
210  template<typename Sampler, typename GridT, typename Transformer>
211  void transformGrid(const Transformer&,
212  const GridT& inGrid, GridT& outGrid) const;
213 
214 protected:
215  template<typename Sampler, typename GridT, typename Transformer>
216  void applyTransform(const Transformer&, const GridT& inGrid, GridT& outGrid) const;
217 
218  bool interrupt() const { return mInterrupt && mInterrupt(); }
219 
220 private:
221  template<typename Sampler, typename InTreeT, typename OutTreeT, typename Transformer>
222  static void transformBBox(const Transformer&, const CoordBBox& inBBox,
223  const InTreeT& inTree, OutTreeT& outTree, const InterruptFunc&,
224  const Sampler& = Sampler());
225 
226  template<typename Sampler, typename TreeT, typename Transformer>
227  class RangeProcessor;
228 
229  bool mThreaded, mTransformTiles;
230  InterruptFunc mInterrupt;
231 };
232 
233 
235 
236 
256 {
257 public:
258  typedef boost::shared_ptr<GridTransformer> Ptr;
259 
260  GridTransformer(const Mat4R& xform);
262  const Vec3R& pivot,
263  const Vec3R& scale,
264  const Vec3R& rotate,
265  const Vec3R& translate,
266  const std::string& xformOrder = "tsr",
267  const std::string& rotationOrder = "zyx");
268  virtual ~GridTransformer() {}
269 
270  const Mat4R& getTransform() const { return mTransform; }
271 
272  template<class Sampler, class GridT>
273  void transformGrid(const GridT& inGrid, GridT& outGrid) const;
274 
275 private:
276  struct MatrixTransform;
277 
278  inline void init(const Vec3R& pivot, const Vec3R& scale,
279  const Vec3R& rotate, const Vec3R& translate,
280  const std::string& xformOrder, const std::string& rotOrder);
281 
282  Vec3R mPivot;
283  Vec3i mMipLevels;
284  Mat4R mTransform, mPreScaleTransform, mPostScaleTransform;
285 };
286 
287 
289 
290 
291 namespace local_util {
292 
296 template<typename T>
297 inline bool
299  math::Vec3<T>& rotate, math::Vec3<T>& translate)
300 {
301  if (!math::isAffine(m)) return false;
302 
303  // this is the translation in world space
304  translate = m.getTranslation();
305  // Extract translation.
306  math::Mat3<T> temp = m.getMat3();
307 
308  scale.init(
309  (math::Vec3<T>(1, 0, 0) * temp).length(),
310  (math::Vec3<T>(0, 1, 0) * temp).length(),
311  (math::Vec3<T>(0, 0, 1) * temp).length());
312  // Extract scale.
313  temp *= math::scale<math::Mat3<T> >(scale).inverse();
314 
315  rotate = math::eulerAngles(temp, math::XYZ_ROTATION);
316 
317  if (!rotate.eq(math::Vec3<T>::zero()) && !scale.eq(math::Vec3<T>(scale[0]))) {
318  // No unique decomposition if scale is nonuniform and rotation is nonzero.
319  return false;
320  }
321  return true;
322 }
323 
324 } // namespace local_util
325 
326 
328 
329 
334 {
335  MatrixTransform(): mat(Mat4R::identity()), invMat(Mat4R::identity()) {}
336  MatrixTransform(const Mat4R& xform): mat(xform), invMat(xform.inverse()) {}
337 
338  bool isAffine() const { return math::isAffine(mat); }
339 
340  Vec3R transform(const Vec3R& pos) const { return mat.transformH(pos); }
341 
342  Vec3R invTransform(const Vec3R& pos) const { return invMat.transformH(pos); }
343 
344  Mat4R mat, invMat;
345 };
346 
347 
349 
350 
356 {
357 public:
360  ABTransform(const math::Transform& aXform, const math::Transform& bXform):
361  mAXform(aXform),
362  mBXform(bXform),
363  mIsAffine(mAXform.isLinear() && mBXform.isLinear()),
364  mIsIdentity(mIsAffine && mAXform == mBXform)
365  {}
366 
367  bool isAffine() const { return mIsAffine; }
368 
369  bool isIdentity() const { return mIsIdentity; }
370 
372  {
373  return mBXform.worldToIndex(mAXform.indexToWorld(pos));
374  }
375 
377  {
378  return mAXform.worldToIndex(mBXform.indexToWorld(pos));
379  }
380 
381  const math::Transform& getA() const { return mAXform; }
382  const math::Transform& getB() const { return mBXform; }
383 
384 private:
385  const math::Transform &mAXform, &mBXform;
386  const bool mIsAffine;
387  const bool mIsIdentity;
388 };
389 
390 
397 template<typename Sampler, typename Interrupter, typename GridType>
398 inline void
399 doResampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter)
400 {
401  ABTransform xform(inGrid.transform(), outGrid.transform());
402 
403  if (Sampler::consistent() && xform.isIdentity()) {
404  // If the transforms of the input and output are identical, the
405  // output tree is simply a deep copy of the input tree.
406  outGrid.setTree(inGrid.tree().copy());
407  } else if (xform.isAffine()) {
408  // If the input and output transforms are both affine, create an
409  // input to output transform (in:index-to-world * out:world-to-index)
410  // and use the fast GridTransformer API.
411  Mat4R mat = xform.getA().baseMap()->getAffineMap()->getMat4() *
412  ( xform.getB().baseMap()->getAffineMap()->getMat4().inverse() );
413 
414  GridTransformer transformer(mat);
415  transformer.setInterrupter(interrupter);
416 
417  // Transform the input grid and store the result in the output grid.
418  transformer.transformGrid<Sampler>(inGrid, outGrid);
419  } else {
420  // If either the input or the output transform is non-affine,
421  // use the slower GridResampler API.
422  GridResampler resampler;
423  resampler.setInterrupter(interrupter);
424 
425  resampler.transformGrid<Sampler>(xform, inGrid, outGrid);
426  }
427 }
428 
429 
430 template<typename Sampler, typename Interrupter, typename GridType>
431 inline void
432 resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter)
433 {
434  if (inGrid.getGridClass() == GRID_LEVEL_SET) {
435  // If the input grid is a level set, resample it using the level set rebuild tool.
436 
437  if (inGrid.constTransform() == outGrid.constTransform()) {
438  // If the transforms of the input and output grids are identical,
439  // the output tree is simply a deep copy of the input tree.
440  outGrid.setTree(inGrid.tree().copy());
441  return;
442  }
443 
444  // If the output grid is a level set, resample the input grid to have the output grid's
445  // background value. Otherwise, preserve the input grid's background value.
446  typedef typename GridType::ValueType ValueT;
447  const ValueT halfWidth = ((outGrid.getGridClass() == openvdb::GRID_LEVEL_SET)
448  ? ValueT(outGrid.background() * (1.0 / outGrid.voxelSize()[0]))
449  : ValueT(inGrid.background() * (1.0 / inGrid.voxelSize()[0])));
450 
451  typename GridType::Ptr tempGrid;
452  try {
453  tempGrid = doLevelSetRebuild(inGrid, /*iso=*/zeroVal<ValueT>(),
454  /*exWidth=*/halfWidth, /*inWidth=*/halfWidth,
455  &outGrid.constTransform(), &interrupter);
456  } catch (TypeError&) {
457  // The input grid is classified as a level set, but it has a value type
458  // that is not supported by the level set rebuild tool. Fall back to
459  // using the generic resampler.
460  tempGrid.reset();
461  }
462  if (tempGrid) {
463  outGrid.setTree(tempGrid->treePtr());
464  return;
465  }
466  }
467 
468  // If the input grid is not a level set, use the generic resampler.
469  doResampleToMatch<Sampler>(inGrid, outGrid, interrupter);
470 }
471 
472 
473 template<typename Sampler, typename GridType>
474 inline void
475 resampleToMatch(const GridType& inGrid, GridType& outGrid)
476 {
477  util::NullInterrupter interrupter;
478  resampleToMatch<Sampler>(inGrid, outGrid, interrupter);
479 }
480 
481 
483 
484 
485 inline
486 GridTransformer::GridTransformer(const Mat4R& xform):
487  mPivot(0, 0, 0),
488  mMipLevels(0, 0, 0),
489  mTransform(xform),
490  mPreScaleTransform(Mat4R::identity()),
491  mPostScaleTransform(Mat4R::identity())
492 {
493  Vec3R scale, rotate, translate;
494  if (local_util::decompose(mTransform, scale, rotate, translate)) {
495  // If the transform can be decomposed into affine components,
496  // use them to set up a mipmapping-like scheme for downsampling.
497  init(mPivot, scale, rotate, translate, "srt", "zyx");
498  }
499 }
500 
501 
502 inline
504  const Vec3R& pivot, const Vec3R& scale,
505  const Vec3R& rotate, const Vec3R& translate,
506  const std::string& xformOrder, const std::string& rotOrder):
507  mPivot(0, 0, 0),
508  mMipLevels(0, 0, 0),
509  mPreScaleTransform(Mat4R::identity()),
510  mPostScaleTransform(Mat4R::identity())
511 {
512  init(pivot, scale, rotate, translate, xformOrder, rotOrder);
513 }
514 
515 
517 
518 
519 inline void
520 GridTransformer::init(
521  const Vec3R& pivot, const Vec3R& scale,
522  const Vec3R& rotate, const Vec3R& translate,
523  const std::string& xformOrder, const std::string& rotOrder)
524 {
525  if (xformOrder.size() != 3) {
526  OPENVDB_THROW(ValueError, "invalid transform order (" + xformOrder + ")");
527  }
528  if (rotOrder.size() != 3) {
529  OPENVDB_THROW(ValueError, "invalid rotation order (" + rotOrder + ")");
530  }
531 
532  mPivot = pivot;
533 
534  // Scaling is handled via a mipmapping-like scheme of successive
535  // halvings of the tree resolution, until the remaining scale
536  // factor is greater than or equal to 1/2.
537  Vec3R scaleRemainder = scale;
538  for (int i = 0; i < 3; ++i) {
539  double s = std::fabs(scale(i));
540  if (s < 0.5) {
541  mMipLevels(i) = int(std::floor(-std::log(s)/std::log(2.0)));
542  scaleRemainder(i) = scale(i) * (1 << mMipLevels(i));
543  }
544  }
545 
546  // Build pre-scale and post-scale transform matrices based on
547  // the user-specified order of operations.
548  // Note that we iterate over the transform order string in reverse order
549  // (e.g., "t", "r", "s", given "srt"). This is because math::Mat matrices
550  // postmultiply row vectors rather than premultiplying column vectors.
551  mTransform = mPreScaleTransform = mPostScaleTransform = Mat4R::identity();
552  Mat4R* remainder = &mPostScaleTransform;
553  int rpos, spos, tpos;
554  rpos = spos = tpos = 3;
555  for (int ix = 2; ix >= 0; --ix) { // reverse iteration
556  switch (xformOrder[ix]) {
557 
558  case 'r':
559  rpos = ix;
560  mTransform.preTranslate(pivot);
561  remainder->preTranslate(pivot);
562 
563  int xpos, ypos, zpos;
564  xpos = ypos = zpos = 3;
565  for (int ir = 2; ir >= 0; --ir) {
566  switch (rotOrder[ir]) {
567  case 'x':
568  xpos = ir;
569  mTransform.preRotate(math::X_AXIS, rotate.x());
570  remainder->preRotate(math::X_AXIS, rotate.x());
571  break;
572  case 'y':
573  ypos = ir;
574  mTransform.preRotate(math::Y_AXIS, rotate.y());
575  remainder->preRotate(math::Y_AXIS, rotate.y());
576  break;
577  case 'z':
578  zpos = ir;
579  mTransform.preRotate(math::Z_AXIS, rotate.z());
580  remainder->preRotate(math::Z_AXIS, rotate.z());
581  break;
582  }
583  }
584  // Reject rotation order strings that don't contain exactly one
585  // instance of "x", "y" and "z".
586  if (xpos > 2 || ypos > 2 || zpos > 2) {
587  OPENVDB_THROW(ValueError, "invalid rotation order (" + rotOrder + ")");
588  }
589 
590  mTransform.preTranslate(-pivot);
591  remainder->preTranslate(-pivot);
592  break;
593 
594  case 's':
595  spos = ix;
596  mTransform.preTranslate(pivot);
597  mTransform.preScale(scale);
598  mTransform.preTranslate(-pivot);
599 
600  remainder->preTranslate(pivot);
601  remainder->preScale(scaleRemainder);
602  remainder->preTranslate(-pivot);
603  remainder = &mPreScaleTransform;
604  break;
605 
606  case 't':
607  tpos = ix;
608  mTransform.preTranslate(translate);
609  remainder->preTranslate(translate);
610  break;
611  }
612  }
613  // Reject transform order strings that don't contain exactly one
614  // instance of "t", "r" and "s".
615  if (tpos > 2 || rpos > 2 || spos > 2) {
616  OPENVDB_THROW(ValueError, "invalid transform order (" + xformOrder + ")");
617  }
618 }
619 
620 
622 
623 
624 template<typename InterrupterType>
625 void
626 GridResampler::setInterrupter(InterrupterType& interrupter)
627 {
628  mInterrupt = boost::bind(&InterrupterType::wasInterrupted,
629  /*this=*/&interrupter, /*percent=*/-1);
630 }
631 
632 
633 template<typename Sampler, typename GridT, typename Transformer>
634 void
635 GridResampler::transformGrid(const Transformer& xform,
636  const GridT& inGrid, GridT& outGrid) const
637 {
638  tools::changeBackground(outGrid.tree(), inGrid.background());
639  applyTransform<Sampler>(xform, inGrid, outGrid);
640 }
641 
642 
643 template<class Sampler, class GridT>
644 void
645 GridTransformer::transformGrid(const GridT& inGrid, GridT& outGrid) const
646 {
647  tools::changeBackground(outGrid.tree(), inGrid.background());
648 
649  if (!Sampler::mipmap() || mMipLevels == Vec3i::zero()) {
650  // Skip the mipmapping step.
651  const MatrixTransform xform(mTransform);
652  applyTransform<Sampler>(xform, inGrid, outGrid);
653 
654  } else {
655  bool firstPass = true;
656  const typename GridT::ValueType background = inGrid.background();
657  typename GridT::Ptr tempGrid = GridT::create(background);
658 
659  if (!mPreScaleTransform.eq(Mat4R::identity())) {
660  firstPass = false;
661  // Apply the pre-scale transform to the input grid
662  // and store the result in a temporary grid.
663  const MatrixTransform xform(mPreScaleTransform);
664  applyTransform<Sampler>(xform, inGrid, *tempGrid);
665  }
666 
667  // While the scale factor along one or more axes is less than 1/2,
668  // scale the grid by half along those axes.
669  Vec3i count = mMipLevels; // # of halvings remaining per axis
670  while (count != Vec3i::zero()) {
671  MatrixTransform xform;
672  xform.mat.setTranslation(mPivot);
673  xform.mat.preScale(Vec3R(
674  count.x() ? .5 : 1, count.y() ? .5 : 1, count.z() ? .5 : 1));
675  xform.mat.preTranslate(-mPivot);
676  xform.invMat = xform.mat.inverse();
677 
678  if (firstPass) {
679  firstPass = false;
680  // Scale the input grid and store the result in a temporary grid.
681  applyTransform<Sampler>(xform, inGrid, *tempGrid);
682  } else {
683  // Scale the temporary grid and store the result in a transient grid,
684  // then swap the two and discard the transient grid.
685  typename GridT::Ptr destGrid = GridT::create(background);
686  applyTransform<Sampler>(xform, *tempGrid, *destGrid);
687  tempGrid.swap(destGrid);
688  }
689  // (3, 2, 1) -> (2, 1, 0) -> (1, 0, 0) -> (0, 0, 0), etc.
690  count = math::maxComponent(count - 1, Vec3i::zero());
691  }
692 
693  // Apply the post-scale transform and store the result in the output grid.
694  if (!mPostScaleTransform.eq(Mat4R::identity())) {
695  const MatrixTransform xform(mPostScaleTransform);
696  applyTransform<Sampler>(xform, *tempGrid, outGrid);
697  } else {
698  outGrid.setTree(tempGrid->treePtr());
699  }
700  }
701 }
702 
703 
705 
706 
707 template<class Sampler, class TreeT, typename Transformer>
708 class GridResampler::RangeProcessor
709 {
710 public:
711  typedef typename TreeT::LeafCIter LeafIterT;
712  typedef typename TreeT::ValueAllCIter TileIterT;
713  typedef typename tree::IteratorRange<LeafIterT> LeafRange;
714  typedef typename tree::IteratorRange<TileIterT> TileRange;
715  typedef typename tree::ValueAccessor<const TreeT> InTreeAccessor;
716  typedef typename tree::ValueAccessor<TreeT> OutTreeAccessor;
717 
718  RangeProcessor(const Transformer& xform, const CoordBBox& b, const TreeT& inT, TreeT& outT):
719  mIsRoot(true), mXform(xform), mBBox(b),
720  mInTree(inT), mOutTree(&outT), mInAcc(mInTree), mOutAcc(*mOutTree)
721  {}
722 
723  RangeProcessor(const Transformer& xform, const CoordBBox& b, const TreeT& inTree):
724  mIsRoot(false), mXform(xform), mBBox(b),
725  mInTree(inTree), mOutTree(new TreeT(inTree.background())),
726  mInAcc(mInTree), mOutAcc(*mOutTree)
727  {}
728 
729  ~RangeProcessor() { if (!mIsRoot) delete mOutTree; }
730 
732  RangeProcessor(RangeProcessor& other, tbb::split):
733  mIsRoot(false),
734  mXform(other.mXform),
735  mBBox(other.mBBox),
736  mInTree(other.mInTree),
737  mOutTree(new TreeT(mInTree.background())),
738  mInAcc(mInTree),
739  mOutAcc(*mOutTree),
740  mInterrupt(other.mInterrupt)
741  {}
742 
743  void setInterrupt(const InterruptFunc& f) { mInterrupt = f; }
744 
746  void operator()(LeafRange& r)
747  {
748  for ( ; r; ++r) {
749  if (interrupt()) break;
750  LeafIterT i = r.iterator();
751  CoordBBox bbox(i->origin(), i->origin() + Coord(i->dim()));
752  if (!mBBox.empty()) {
753  // Intersect the leaf node's bounding box with mBBox.
754  bbox = CoordBBox(
755  Coord::maxComponent(bbox.min(), mBBox.min()),
756  Coord::minComponent(bbox.max(), mBBox.max()));
757  }
758  if (!bbox.empty()) {
759  transformBBox<Sampler>(mXform, bbox, mInAcc, mOutAcc, mInterrupt);
760  }
761  }
762  }
763 
765  void operator()(TileRange& r)
766  {
767  for ( ; r; ++r) {
768  if (interrupt()) break;
769 
770  TileIterT i = r.iterator();
771  // Skip voxels and background tiles.
772  if (!i.isTileValue()) continue;
773  if (!i.isValueOn() && math::isApproxEqual(*i, mOutTree->background())) continue;
774 
775  CoordBBox bbox;
776  i.getBoundingBox(bbox);
777  if (!mBBox.empty()) {
778  // Intersect the tile's bounding box with mBBox.
779  bbox = CoordBBox(
780  Coord::maxComponent(bbox.min(), mBBox.min()),
781  Coord::minComponent(bbox.max(), mBBox.max()));
782  }
783  if (!bbox.empty()) {
789  sampler(bbox, i.getValue(), i.isValueOn());
790  transformBBox(mXform, bbox, mInAcc, mOutAcc, mInterrupt, sampler);
791  }
792  }
793  }
794 
796  void join(RangeProcessor& other)
797  {
798  if (!interrupt()) mOutTree->merge(*other.mOutTree);
799  }
800 
801 private:
802  bool interrupt() const { return mInterrupt && mInterrupt(); }
803 
804  const bool mIsRoot; // true if mOutTree is the top-level tree
805  Transformer mXform;
806  CoordBBox mBBox;
807  const TreeT& mInTree;
808  TreeT* mOutTree;
809  InTreeAccessor mInAcc;
810  OutTreeAccessor mOutAcc;
811  InterruptFunc mInterrupt;
812 };
813 
814 
816 
817 
818 template<class Sampler, class GridT, typename Transformer>
819 void
820 GridResampler::applyTransform(const Transformer& xform,
821  const GridT& inGrid, GridT& outGrid) const
822 {
823  typedef typename GridT::TreeType TreeT;
824  const TreeT& inTree = inGrid.tree();
825  TreeT& outTree = outGrid.tree();
826 
827  typedef RangeProcessor<Sampler, TreeT, Transformer> RangeProc;
828 
829  const GridClass gridClass = inGrid.getGridClass();
830 
831  if (gridClass != GRID_LEVEL_SET && mTransformTiles) {
832  // Independently transform the tiles of the input grid.
833  // Note: Tiles in level sets can only be background tiles, and they
834  // are handled more efficiently with a signed flood fill (see below).
835 
836  RangeProc proc(xform, CoordBBox(), inTree, outTree);
837  proc.setInterrupt(mInterrupt);
838 
839  typename RangeProc::TileIterT tileIter = inTree.cbeginValueAll();
840  tileIter.setMaxDepth(tileIter.getLeafDepth() - 1); // skip leaf nodes
841  typename RangeProc::TileRange tileRange(tileIter);
842 
843  if (mThreaded) {
844  tbb::parallel_reduce(tileRange, proc);
845  } else {
846  proc(tileRange);
847  }
848  }
849 
850  CoordBBox clipBBox;
851  if (gridClass == GRID_LEVEL_SET) {
852  // Inactive voxels in level sets can only be background voxels, and they
853  // are handled more efficiently with a signed flood fill (see below).
854  clipBBox = inGrid.evalActiveVoxelBoundingBox();
855  }
856 
857  // Independently transform the leaf nodes of the input grid.
858 
859  RangeProc proc(xform, clipBBox, inTree, outTree);
860  proc.setInterrupt(mInterrupt);
861 
862  typename RangeProc::LeafRange leafRange(inTree.cbeginLeaf());
863 
864  if (mThreaded) {
865  tbb::parallel_reduce(leafRange, proc);
866  } else {
867  proc(leafRange);
868  }
869 
870  // If the grid is a level set, mark inactive voxels as inside or outside.
871  if (gridClass == GRID_LEVEL_SET) {
872  tools::pruneLevelSet(outTree);
873  tools::signedFloodFill(outTree);
874  }
875 }
876 
877 
879 
880 
881 //static
882 template<class Sampler, class InTreeT, class OutTreeT, class Transformer>
883 void
884 GridResampler::transformBBox(
885  const Transformer& xform,
886  const CoordBBox& bbox,
887  const InTreeT& inTree,
888  OutTreeT& outTree,
889  const InterruptFunc& interrupt,
890  const Sampler& sampler)
891 {
892  typedef typename OutTreeT::ValueType ValueT;
893 
894  // Transform the corners of the input tree's bounding box
895  // and compute the enclosing bounding box in the output tree.
896  Vec3R
897  inRMin(bbox.min().x(), bbox.min().y(), bbox.min().z()),
898  inRMax(bbox.max().x(), bbox.max().y(), bbox.max().z()),
899  outRMin = math::minComponent(xform.transform(inRMin), xform.transform(inRMax)),
900  outRMax = math::maxComponent(xform.transform(inRMin), xform.transform(inRMax));
901  for (int i = 0; i < 8; ++i) {
902  Vec3R corner(
903  i & 1 ? inRMax.x() : inRMin.x(),
904  i & 2 ? inRMax.y() : inRMin.y(),
905  i & 4 ? inRMax.z() : inRMin.z());
906  outRMin = math::minComponent(outRMin, xform.transform(corner));
907  outRMax = math::maxComponent(outRMax, xform.transform(corner));
908  }
909  Vec3i
910  outMin = local_util::floorVec3(outRMin) - Sampler::radius(),
911  outMax = local_util::ceilVec3(outRMax) + Sampler::radius();
912 
913  if (!xform.isAffine()) {
914  // If the transform is not affine, back-project each output voxel
915  // into the input tree.
916  Vec3R xyz, inXYZ;
917  Coord outXYZ;
918  int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z();
919  for (x = outMin.x(); x <= outMax.x(); ++x) {
920  if (interrupt && interrupt()) break;
921  xyz.x() = x;
922  for (y = outMin.y(); y <= outMax.y(); ++y) {
923  if (interrupt && interrupt()) break;
924  xyz.y() = y;
925  for (z = outMin.z(); z <= outMax.z(); ++z) {
926  xyz.z() = z;
927  inXYZ = xform.invTransform(xyz);
928  ValueT result;
929  if (sampler.sample(inTree, inXYZ, result)) {
930  outTree.setValueOn(outXYZ, result);
931  } else {
932  // Note: Don't overwrite existing active values with inactive values.
933  if (!outTree.isValueOn(outXYZ)) {
934  outTree.setValueOff(outXYZ, result);
935  }
936  }
937  }
938  }
939  }
940  } else { // affine
941  // Compute step sizes in the input tree that correspond to
942  // unit steps in x, y and z in the output tree.
943  const Vec3R
944  translation = xform.invTransform(Vec3R(0, 0, 0)),
945  deltaX = xform.invTransform(Vec3R(1, 0, 0)) - translation,
946  deltaY = xform.invTransform(Vec3R(0, 1, 0)) - translation,
947  deltaZ = xform.invTransform(Vec3R(0, 0, 1)) - translation;
948 
949 #if defined(__ICC)
950  const Vec3R dummy = deltaX;
954 #endif
955 
956  // Step by whole voxels through the output tree, sampling the
957  // corresponding fractional voxels of the input tree.
958  Vec3R inStartX = xform.invTransform(Vec3R(outMin));
959  Coord outXYZ;
960  int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z();
961  for (x = outMin.x(); x <= outMax.x(); ++x, inStartX += deltaX) {
962  if (interrupt && interrupt()) break;
963  Vec3R inStartY = inStartX;
964  for (y = outMin.y(); y <= outMax.y(); ++y, inStartY += deltaY) {
965  if (interrupt && interrupt()) break;
966  Vec3R inXYZ = inStartY;
967  for (z = outMin.z(); z <= outMax.z(); ++z, inXYZ += deltaZ) {
968  ValueT result;
969  if (sampler.sample(inTree, inXYZ, result)) {
970  outTree.setValueOn(outXYZ, result);
971  } else {
972  // Note: Don't overwrite existing active values with inactive values.
973  if (!outTree.isValueOn(outXYZ)) {
974  outTree.setValueOff(outXYZ, result);
975  }
976  }
977  }
978  }
979  }
980  }
981 } // GridResampler::transformBBox()
982 
983 } // namespace tools
984 } // namespace OPENVDB_VERSION_NAME
985 } // namespace openvdb
986 
987 #endif // OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
988 
989 // Copyright (c) 2012-2015 DreamWorks Animation LLC
990 // All rights reserved. This software is distributed under the
991 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
3x3 matrix class.
Definition: Mat3.h:54
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:76
virtual ~GridTransformer()
Definition: GridTransformer.h:268
ValueT mVal
Definition: GridTransformer.h:143
BBoxd mBBox
Definition: GridTransformer.h:142
MatrixTransform(const Mat4R &xform)
Definition: GridTransformer.h:336
Mat4 inverse(T tolerance=0) const
Definition: Mat4.h:490
Definition: TreeIterator.h:1339
Definition: Interpolation.h:223
void setThreaded(bool b)
Enable or disable threading. (Threading is enabled by default.)
Definition: GridTransformer.h:197
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:355
Definition: Exceptions.h:88
bool isAffine() const
Definition: GridTransformer.h:367
Definition: Interpolation.h:123
Definition: Math.h:841
GridTransformer(const Mat4R &xform)
Definition: GridTransformer.h:486
Mat3< T > getMat3() const
Definition: Mat4.h:302
math::Vec3< Real > Vec3R
Definition: Types.h:76
bool isIdentity() const
Definition: GridTransformer.h:369
virtual ~GridResampler()
Definition: GridTransformer.h:194
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:97
boost::function< bool(void)> InterruptFunc
Definition: GridTransformer.h:191
Efficient multi-threaded replacement of the background values in tree.
Vec3i floorVec3(const Vec3R &v)
Definition: Interpolation.h:610
Vec2< T > maxComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise maximum of the two vectors.
Definition: Vec2.h:520
const Vec3< T > & init(T x=0, T y=0, T z=0)
Definition: Vec3.h:117
bool threaded() const
Return true if threading is enabled.
Definition: GridTransformer.h:199
Vec3R invTransform(const Vec3R &pos) const
Definition: GridTransformer.h:342
Vec3< T > getTranslation() const
Return the translation component.
Definition: Mat4.h:314
Vec3i ceilVec3(const Vec3R &v)
Definition: Interpolation.h:617
T & y()
Definition: Vec3.h:98
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:475
void setTransformTiles(bool b)
Enable or disable processing of tiles. (Enabled by default, except for level set grids.)
Definition: GridTransformer.h:201
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:370
void setInterrupter(InterrupterType &)
Allow processing to be aborted by providing an interrupter object. The interrupter will be queried pe...
Definition: GridTransformer.h:626
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
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:314
Vec3R transform(const Vec3R &pos) const
Definition: GridTransformer.h:340
const boost::disable_if_c< VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:105
Vec3< int32_t > Vec3i
Definition: Vec3.h:640
void preTranslate(const Vec3< T0 > &tr)
Left multiples by the specified translation, i.e. Trans * (*this)
Definition: Mat4.h:715
GridResampler()
Definition: GridTransformer.h:193
MatrixTransform()
Definition: GridTransformer.h:335
Defined various multi-threaded utility functions for trees.
Vec2< T > minComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise minimum of the two vectors.
Definition: Vec2.h:511
static const Mat4< Real > & identity()
Predefined constant for identity matrix.
Definition: Mat4.h:147
GridClass
Definition: Types.h:204
void transformGrid(const Transformer &, const GridT &inGrid, GridT &outGrid) const
Definition: GridTransformer.h:635
void setTranslation(const Vec3< T > &t)
Definition: Mat4.h:319
void applyTransform(const Transformer &, const GridT &inGrid, GridT &outGrid) const
Definition: GridTransformer.h:820
boost::enable_if< boost::is_floating_point< typename GridType::ValueType >, typename GridType::Ptr >::type doLevelSetRebuild(const GridType &grid, typename GridType::ValueType iso, typename GridType::ValueType exWidth, typename GridType::ValueType inWidth, const math::Transform *xform, InterruptT *interrupter)
Definition: LevelSetRebuild.h:229
TileSampler(const CoordBBox &b, const ValueT &tileVal, bool on)
Definition: GridTransformer.h:128
#define OPENVDB_VERSION_NAME
Definition: version.h:43
Dummy NOOP interrupter class defining interface.
Definition: NullInterrupter.h:52
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:298
const math::Transform & getA() const
Definition: GridTransformer.h:381
A GridTransformer applies a geometric transformation to an input grid using one of several sampling s...
Definition: GridTransformer.h:255
Propagates the sign of distance values from the active voxels in the narrow band to the inactive valu...
openvdb::Vec3R invTransform(const openvdb::Vec3R &pos) const
Definition: GridTransformer.h:376
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:402
ABTransform(const math::Transform &aXform, const math::Transform &bXform)
Definition: GridTransformer.h:360
Definition: Exceptions.h:39
bool interrupt() const
Definition: GridTransformer.h:218
Mat4R mat
Definition: GridTransformer.h:344
openvdb::Vec3R transform(const openvdb::Vec3R &pos) const
Definition: GridTransformer.h:371
Definition: Math.h:840
void doResampleToMatch(const GridType &inGrid, GridType &outGrid, Interrupter &interrupter)
Definition: GridTransformer.h:399
T & z()
Definition: Vec3.h:99
Definition: Types.h:206
bool isAffine(const Mat4< T > &m)
Definition: Mat4.h:1336
bool sample(const TreeT &inTree, const Vec3R &inCoord, ValueT &result) const
Definition: GridTransformer.h:135
TreeT::ValueType ValueT
Definition: GridTransformer.h:123
TileSampler(const CoordBBox &, const typename TreeT::ValueType &, bool)
Definition: GridTransformer.h:153
Mat4R invMat
Definition: GridTransformer.h:344
void signedFloodFill(TreeOrLeafManagerT &tree, bool threaded=true, size_t grainSize=1)
Set the values of all inactive voxels and tiles of a narrow-band level set from the signs of the acti...
Definition: SignedFloodFill.h:280
Calculate an axis-aligned bounding box in index space from a bounding sphere in world space...
Definition: Transform.h:66
const boost::disable_if_c< VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:109
bool mEmpty
Definition: GridTransformer.h:144
A TileSampler wraps a grid sampler of another type (BoxSampler, QuadraticSampler, etc...
Definition: GridTransformer.h:120
TileSampler(const CoordBBox &, const typename TreeT::ValueType &, bool)
Definition: GridTransformer.h:161
Definition: GridTransformer.h:187
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:145
void transformGrid(const GridT &inGrid, GridT &outGrid) const
Definition: GridTransformer.h:645
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:71
void preScale(const Vec3< T0 > &v)
Definition: Mat4.h:748
bool isAffine() const
Definition: GridTransformer.h:338
Provises a unified interface for sampling, i.e. interpolation.
Definition: Interpolation.h:90
MatType scale(const Vec3< typename MatType::value_type > &s)
Return a matrix that scales by s.
Definition: Mat.h:594
Definition: Exceptions.h:87
boost::shared_ptr< GridTransformer > Ptr
Definition: GridTransformer.h:258
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:839
const math::Transform & getB() const
Definition: GridTransformer.h:382
const Mat4R & getTransform() const
Definition: GridTransformer.h:270
void preRotate(Axis axis, T angle)
Left multiplies by a rotation clock-wiseabout the given axis into this matrix.
Definition: Mat4.h:810
boost::shared_ptr< GridResampler > Ptr
Definition: GridTransformer.h:190
bool transformTiles() const
Return true if tile processing is enabled.
Definition: GridTransformer.h:203
T & x()
Reference to the component, e.g. v.x() = 4.5f;.
Definition: Vec3.h:97
bool eq(const Mat4 &m, T eps=1.0e-8) const
Test if "this" is equivalent to m with tolerance of eps value.
Definition: Mat4.h:338