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