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