OpenVDB  2.0.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/ )
OPENVDB_API Hermite max(const Hermite &, const Hermite &)
min and max operations done directly on the compressed data.
A TileSampler wraps a grid sampler of another type (BoxSampler, QuadraticSampler, etc...
Definition: GridTransformer.h:116
Vec3i floorVec3(const Vec3R &v)
Definition: Interpolation.h:373
boost::shared_ptr< GridResampler > Ptr
Definition: GridTransformer.h:184
bool sample(const TreeT &inTree, const Vec3R &inCoord, ValueT &result) const
Definition: GridTransformer.h:131
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 &b, const ValueT &tileVal, bool on)
Definition: GridTransformer.h:124
Definition: Interpolation.h:89
T & z()
Definition: Vec3.h:96
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:349
Vec3i ceilVec3(const Vec3R &v)
Definition: Interpolation.h:380
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:97
ValueT mVal
Definition: GridTransformer.h:139
openvdb::Vec3R transform(const openvdb::Vec3R &pos) const
Definition: GridTransformer.h:365
virtual ~GridResampler()
Definition: GridTransformer.h:188
Definition: TreeIterator.h:1322
math::Vec4< Real > Vec4R
Definition: Types.h:87
boost::function< bool(void)> InterruptFunc
Definition: GridTransformer.h:185
Vec3< int32_t > Vec3i
Definition: Vec3.h:602
boost::shared_ptr< GridTransformer > Ptr
Definition: GridTransformer.h:252
Mat4R mat
Definition: GridTransformer.h:338
Definition: Math.h:764
bool threaded() const
Return true if threading is enabled.
Definition: GridTransformer.h:193
void preScale(const Vec3< T0 > &v)
Definition: Mat4.h:750
Vec2< T > minComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise minimum of the two vectors.
Definition: Vec2.h:482
bool isIdentity() const
Definition: GridTransformer.h:363
bool transformTiles() const
Return true if tile processing is enabled.
Definition: GridTransformer.h:197
bool eq(const Vec3< T > &v, T eps=static_cast< T >(1.0e-7)) const
Test if &quot;this&quot; vector is equivalent to vector v with tolerance of eps.
Definition: Vec3.h:141
Definition: GridTransformer.h:181
void setTransformTiles(bool b)
Enable or disable processing of tiles. (Enabled by default, except for level set grids.)
Definition: GridTransformer.h:195
Vec3R invTransform(const Vec3R &pos) const
Definition: GridTransformer.h:336
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 setInterrupter(InterrupterType &)
Allow processing to be aborted by providing an interrupter object. The interrupter will be queried pe...
Definition: GridTransformer.h:620
GridResampler()
Definition: GridTransformer.h:187
void preRotate(Axis axis, T angle)
Left multiplies by a rotation clock-wiseabout the given axis into this matrix.
Definition: Mat4.h:812
Definition: Math.h:763
Definition: Mat4.h:51
#define OPENVDB_VERSION_NAME
Definition: version.h:45
ABTransform(const math::Transform &aXform, const math::Transform &bXform)
Definition: GridTransformer.h:354
math::Vec3< Real > Vec3R
Definition: Types.h:74
Mat4R invMat
Definition: GridTransformer.h:338
void doResampleToMatch(const GridType &inGrid, GridType &outGrid, Interrupter &interrupter)
Definition: GridTransformer.h:393
const math::Transform & getA() const
Definition: GridTransformer.h:375
Vec2< T > maxComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise maximum of the two vectors.
Definition: Vec2.h:491
Mat3< T > getMat3() const
Definition: Mat4.h:304
void transformGrid(const Transformer &, const GridT &inGrid, GridT &outGrid) const
Definition: GridTransformer.h:629
bool eq(const Mat4 &m, T eps=1.0e-8) const
Test if &quot;this&quot; is equivalent to m with tolerance of eps value.
Definition: Mat4.h:340
bool interrupt() const
Definition: GridTransformer.h:212
T & x()
Reference to the component, e.g. v.x() = 4.5f;.
Definition: Vec3.h:94
InterruptT * mInterrupt
Definition: GridOperators.h:261
const Mat4R & getTransform() const
Definition: GridTransformer.h:264
void transformGrid(const GridT &inGrid, GridT &outGrid) const
Definition: GridTransformer.h:639
OPENVDB_API Hermite min(const Hermite &, const Hermite &)
min and max operations done directly on the compressed data.
bool mEmpty
Definition: GridTransformer.h:140
GridTransformer(const Mat4R &xform)
Definition: GridTransformer.h:480
3x3 matrix class.
Definition: Mat3.h:54
MatrixTransform()
Definition: GridTransformer.h:329
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
bool isAffine() const
Definition: GridTransformer.h:361
const Vec3< T > & init(T x=0, T y=0, T z=0)
Definition: Vec3.h:114
const math::Transform & getB() const
Definition: GridTransformer.h:376
openvdb::Vec3R invTransform(const openvdb::Vec3R &pos) const
Definition: GridTransformer.h:370
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
TileSampler(const CoordBBox &, const typename TreeT::ValueType &, bool)
Definition: GridTransformer.h:155
Definition: Math.h:765
bool isAffine() const
Definition: GridTransformer.h:332
MatrixTransform(const Mat4R &xform)
Definition: GridTransformer.h:330
virtual ~GridTransformer()
Definition: GridTransformer.h:262
Vec3R transform(const Vec3R &pos) const
Definition: GridTransformer.h:334
A GridTransformer applies a geometric transformation to an input grid using one of several sampling s...
Definition: GridTransformer.h:249
void preTranslate(const Vec3< T0 > &tr)
Left multiples by the specified translation, i.e. Trans * (*this)
Definition: Mat4.h:717
void setThreaded(bool b)
Enable or disable threading. (Threading is enabled by default.)
Definition: GridTransformer.h:191
MatType scale(const Vec3< typename MatType::value_type > &scaling)
Definition: Mat.h:595
Definition: Exceptions.h:88
Vec3< T > getTranslation() const
Return the translation component.
Definition: Mat4.h:316
GridClass
Definition: Types.h:135
Mat4 inverse(T tolerance=0) const
Definition: Mat4.h:492
static const Mat4< Real > & identity()
Predefined constant for identity matrix.
Definition: Mat4.h:149
BBoxd mBBox
Definition: GridTransformer.h:138
void resampleToMatch(const GridType &inGrid, GridType &outGrid, Interrupter &interrupter)
Resample an input grid into an output grid of the same type such that, after resampling, the input and output grids coincide (apart from sampling artifacts), but the output grid&#39;s transform is unchanged.
Definition: GridTransformer.h:426
Definition: Exceptions.h:87
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:56
bool isApproxEqual(const Hermite &lhs, const Hermite &rhs)
Definition: Hermite.h:470
void setTranslation(const Vec3< T > &t)
Definition: Mat4.h:321
TileSampler(const CoordBBox &, const typename TreeT::ValueType &, bool)
Definition: GridTransformer.h:148
TreeT::ValueType ValueT
Definition: GridTransformer.h:119
void applyTransform(const Transformer &, const GridT &inGrid, GridT &outGrid) const
Definition: GridTransformer.h:814
math::Mat4< Real > Mat4R
Definition: Types.h:99
Definition: Types.h:137
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:76
Definition: Interpolation.h:154
bool isAffine(const Mat4< T > &m)
Definition: Mat4.h:1337
Dummy NOOP interrupter class defining interface.
Definition: NullInterrupter.h:52