OpenVDB  4.0.1
GridTransformer.h
Go to the documentation of this file.
1 //
3 // Copyright (c) 2012-2017 DreamWorks Animation LLC
4 //
5 // All rights reserved. This software is distributed under the
6 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
7 //
8 // Redistributions of source code must retain the above copyright
9 // and license notice and the following restrictions and disclaimer.
10 //
11 // * Neither the name of DreamWorks Animation nor the names of
12 // its contributors may be used to endorse or promote products derived
13 // from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
20 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 // IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
27 // LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
28 //
30 
33 
34 #ifndef OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
35 #define OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
36 
37 #include <openvdb/Grid.h>
38 #include <openvdb/Types.h>
39 #include <openvdb/math/Math.h> // for isApproxEqual()
41 #include "ChangeBackground.h"
42 #include "Interpolation.h"
43 #include "LevelSetRebuild.h" // for doLevelSetRebuild()
44 #include "SignedFloodFill.h" // for signedFloodFill
45 #include "Prune.h" // for pruneLevelSet
46 #include <tbb/blocked_range.h>
47 #include <tbb/parallel_reduce.h>
48 #include <cmath>
49 #include <functional>
50 
51 namespace openvdb {
53 namespace OPENVDB_VERSION_NAME {
54 namespace tools {
55 
79 template<typename Sampler, typename Interrupter, typename GridType>
80 inline void
81 resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter);
82 
104 template<typename Sampler, typename GridType>
105 inline void
106 resampleToMatch(const GridType& inGrid, GridType& outGrid);
107 
108 
110 
111 
112 namespace internal {
113 
117 template<typename Sampler, typename TreeT>
118 class TileSampler: public Sampler
119 {
120 public:
121  using ValueT = typename TreeT::ValueType;
122 
126  TileSampler(const CoordBBox& b, const ValueT& tileVal, bool on):
127  mBBox(b.min().asVec3d(), b.max().asVec3d()), mVal(tileVal), mActive(on), mEmpty(false)
128  {
129  mBBox.expand(-this->radius()); // shrink the bounding box by the sample radius
130  mEmpty = mBBox.empty();
131  }
132 
133  bool sample(const TreeT& inTree, const Vec3R& inCoord, ValueT& result) const
134  {
135  if (!mEmpty && mBBox.isInside(inCoord)) { result = mVal; return mActive; }
136  return Sampler::sample(inTree, inCoord, result);
137  }
138 
139 protected:
142  bool mActive, mEmpty;
143 };
144 
145 
148 template<typename TreeT>
149 class TileSampler<PointSampler, TreeT>: public PointSampler {
150 public:
151  TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {}
152 };
153 
156 template<typename TreeT>
158 public:
159  TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {}
160 };
161 
162 } // namespace internal
163 
164 
166 
167 
186 {
187 public:
189  using InterruptFunc = std::function<bool (void)>;
190 
191  GridResampler(): mThreaded(true), mTransformTiles(true) {}
192  virtual ~GridResampler() {}
193 
195  void setThreaded(bool b) { mThreaded = b; }
197  bool threaded() const { return mThreaded; }
199  void setTransformTiles(bool b) { mTransformTiles = b; }
201  bool transformTiles() const { return mTransformTiles; }
202 
206  template<typename InterrupterType> void setInterrupter(InterrupterType&);
207 
208  template<typename Sampler, typename GridT, typename Transformer>
209  void transformGrid(const Transformer&,
210  const GridT& inGrid, GridT& outGrid) const;
211 
212 protected:
213  template<typename Sampler, typename GridT, typename Transformer>
214  void applyTransform(const Transformer&, const GridT& inGrid, GridT& outGrid) const;
215 
216  bool interrupt() const { return mInterrupt && mInterrupt(); }
217 
218 private:
219  template<typename Sampler, typename InTreeT, typename OutTreeT, typename Transformer>
220  static void transformBBox(const Transformer&, const CoordBBox& inBBox,
221  const InTreeT& inTree, OutTreeT& outTree, const InterruptFunc&,
222  const Sampler& = Sampler());
223 
224  template<typename Sampler, typename TreeT, typename Transformer>
225  class RangeProcessor;
226 
227  bool mThreaded, mTransformTiles;
228  InterruptFunc mInterrupt;
229 };
230 
231 
233 
234 
254 {
255 public:
257 
258  GridTransformer(const Mat4R& xform);
260  const Vec3R& pivot,
261  const Vec3R& scale,
262  const Vec3R& rotate,
263  const Vec3R& translate,
264  const std::string& xformOrder = "tsr",
265  const std::string& rotationOrder = "zyx");
266  ~GridTransformer() override = default;
267 
268  const Mat4R& getTransform() const { return mTransform; }
269 
270  template<class Sampler, class GridT>
271  void transformGrid(const GridT& inGrid, GridT& outGrid) const;
272 
273 private:
274  struct MatrixTransform;
275 
276  inline void init(const Vec3R& pivot, const Vec3R& scale,
277  const Vec3R& rotate, const Vec3R& translate,
278  const std::string& xformOrder, const std::string& rotOrder);
279 
280  Vec3R mPivot;
281  Vec3i mMipLevels;
282  Mat4R mTransform, mPreScaleTransform, mPostScaleTransform;
283 };
284 
285 
287 
288 
289 namespace local_util {
290 
293 template<typename T>
294 inline bool
296  math::Vec3<T>& rotate, math::Vec3<T>& translate)
297 {
298  if (!math::isAffine(m)) return false;
299 
300  // This is the translation in world space
301  translate = m.getTranslation();
302  // Extract translation.
303  const math::Mat3<T> xform = m.getMat3();
304 
305  const math::Vec3<T> unsignedScale(
306  (math::Vec3<T>(1, 0, 0) * xform).length(),
307  (math::Vec3<T>(0, 1, 0) * xform).length(),
308  (math::Vec3<T>(0, 0, 1) * xform).length());
309 
310  const bool hasUniformScale = unsignedScale.eq(math::Vec3<T>(unsignedScale[0]));
311 
312  bool hasRotation = false;
313  bool validDecomposition = false;
314 
315  T minAngle = std::numeric_limits<T>::max();
316 
317  // If the transformation matrix contains a reflection,
318  // test different negative scales to find a decomposition
319  // that favors the optimal resampling algorithm.
320  for (size_t n = 0; n < 8; ++n) {
321 
322  const math::Vec3<T> signedScale(
323  n & 0x1 ? -unsignedScale.x() : unsignedScale.x(),
324  n & 0x2 ? -unsignedScale.y() : unsignedScale.y(),
325  n & 0x4 ? -unsignedScale.z() : unsignedScale.z());
326 
327  // Extract scale and potentially reflection.
328  const math::Mat3<T> mat = xform * math::scale<math::Mat3<T> >(signedScale).inverse();
329  if (mat.det() < T(0.0)) continue; // Skip if mat contains a reflection.
330 
331  const math::Vec3<T> tmpAngle = math::eulerAngles(mat, math::XYZ_ROTATION);
332 
333  const math::Mat3<T> rebuild =
334  math::rotation<math::Mat3<T> >(math::Vec3<T>(1, 0, 0), tmpAngle.x()) *
335  math::rotation<math::Mat3<T> >(math::Vec3<T>(0, 1, 0), tmpAngle.y()) *
336  math::rotation<math::Mat3<T> >(math::Vec3<T>(0, 0, 1), tmpAngle.z()) *
337  math::scale<math::Mat3<T> >(signedScale);
338 
339  if (xform.eq(rebuild)) {
340 
341  const T maxAngle = std::max(std::abs(tmpAngle[0]),
342  std::max(std::abs(tmpAngle[1]), std::abs(tmpAngle[2])));
343 
344  if (!(minAngle < maxAngle)) { // Update if less or equal.
345 
346  minAngle = maxAngle;
347  rotate = tmpAngle;
348  scale = signedScale;
349 
350  hasRotation = !rotate.eq(math::Vec3<T>::zero());
351  validDecomposition = true;
352 
353  if (hasUniformScale || !hasRotation) {
354  // Current decomposition is optimal.
355  break;
356  }
357  }
358  }
359  }
360 
361  if (!validDecomposition || (hasRotation && !hasUniformScale)) {
362  // The decomposition is invalid if the transformation matrix contains shear.
363  // No unique decomposition if scale is nonuniform and rotation is nonzero.
364  return false;
365  }
366 
367  return true;
368 }
369 
370 } // namespace local_util
371 
372 
374 
375 
380 {
381  MatrixTransform(): mat(Mat4R::identity()), invMat(Mat4R::identity()) {}
382  MatrixTransform(const Mat4R& xform): mat(xform), invMat(xform.inverse()) {}
383 
384  bool isAffine() const { return math::isAffine(mat); }
385 
386  Vec3R transform(const Vec3R& pos) const { return mat.transformH(pos); }
387 
388  Vec3R invTransform(const Vec3R& pos) const { return invMat.transformH(pos); }
389 
390  Mat4R mat, invMat;
391 };
392 
393 
395 
396 
402 {
403 public:
406  ABTransform(const math::Transform& aXform, const math::Transform& bXform):
407  mAXform(aXform),
408  mBXform(bXform),
409  mIsAffine(mAXform.isLinear() && mBXform.isLinear()),
410  mIsIdentity(mIsAffine && mAXform == mBXform)
411  {}
412 
413  bool isAffine() const { return mIsAffine; }
414 
415  bool isIdentity() const { return mIsIdentity; }
416 
418  {
419  return mBXform.worldToIndex(mAXform.indexToWorld(pos));
420  }
421 
423  {
424  return mAXform.worldToIndex(mBXform.indexToWorld(pos));
425  }
426 
427  const math::Transform& getA() const { return mAXform; }
428  const math::Transform& getB() const { return mBXform; }
429 
430 private:
431  const math::Transform &mAXform, &mBXform;
432  const bool mIsAffine;
433  const bool mIsIdentity;
434 };
435 
436 
443 template<typename Sampler, typename Interrupter, typename GridType>
444 inline void
445 doResampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter)
446 {
447  ABTransform xform(inGrid.transform(), outGrid.transform());
448 
449  if (Sampler::consistent() && xform.isIdentity()) {
450  // If the transforms of the input and output are identical, the
451  // output tree is simply a deep copy of the input tree.
452  outGrid.setTree(inGrid.tree().copy());
453  } else if (xform.isAffine()) {
454  // If the input and output transforms are both affine, create an
455  // input to output transform (in:index-to-world * out:world-to-index)
456  // and use the fast GridTransformer API.
457  Mat4R mat = xform.getA().baseMap()->getAffineMap()->getMat4() *
458  ( xform.getB().baseMap()->getAffineMap()->getMat4().inverse() );
459 
460  GridTransformer transformer(mat);
461  transformer.setInterrupter(interrupter);
462 
463  // Transform the input grid and store the result in the output grid.
464  transformer.transformGrid<Sampler>(inGrid, outGrid);
465  } else {
466  // If either the input or the output transform is non-affine,
467  // use the slower GridResampler API.
468  GridResampler resampler;
469  resampler.setInterrupter(interrupter);
470 
471  resampler.transformGrid<Sampler>(xform, inGrid, outGrid);
472  }
473 }
474 
475 
476 template<typename Sampler, typename Interrupter, typename GridType>
477 inline void
478 resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter)
479 {
480  if (inGrid.getGridClass() == GRID_LEVEL_SET) {
481  // If the input grid is a level set, resample it using the level set rebuild tool.
482 
483  if (inGrid.constTransform() == outGrid.constTransform()) {
484  // If the transforms of the input and output grids are identical,
485  // the output tree is simply a deep copy of the input tree.
486  outGrid.setTree(inGrid.tree().copy());
487  return;
488  }
489 
490  // If the output grid is a level set, resample the input grid to have the output grid's
491  // background value. Otherwise, preserve the input grid's background value.
492  using ValueT = typename GridType::ValueType;
493  const ValueT halfWidth = ((outGrid.getGridClass() == openvdb::GRID_LEVEL_SET)
494  ? ValueT(outGrid.background() * (1.0 / outGrid.voxelSize()[0]))
495  : ValueT(inGrid.background() * (1.0 / inGrid.voxelSize()[0])));
496 
497  typename GridType::Ptr tempGrid;
498  try {
499  tempGrid = doLevelSetRebuild(inGrid, /*iso=*/zeroVal<ValueT>(),
500  /*exWidth=*/halfWidth, /*inWidth=*/halfWidth,
501  &outGrid.constTransform(), &interrupter);
502  } catch (TypeError&) {
503  // The input grid is classified as a level set, but it has a value type
504  // that is not supported by the level set rebuild tool. Fall back to
505  // using the generic resampler.
506  tempGrid.reset();
507  }
508  if (tempGrid) {
509  outGrid.setTree(tempGrid->treePtr());
510  return;
511  }
512  }
513 
514  // If the input grid is not a level set, use the generic resampler.
515  doResampleToMatch<Sampler>(inGrid, outGrid, interrupter);
516 }
517 
518 
519 template<typename Sampler, typename GridType>
520 inline void
521 resampleToMatch(const GridType& inGrid, GridType& outGrid)
522 {
523  util::NullInterrupter interrupter;
524  resampleToMatch<Sampler>(inGrid, outGrid, interrupter);
525 }
526 
527 
529 
530 
531 inline
532 GridTransformer::GridTransformer(const Mat4R& xform):
533  mPivot(0, 0, 0),
534  mMipLevels(0, 0, 0),
535  mTransform(xform),
536  mPreScaleTransform(Mat4R::identity()),
537  mPostScaleTransform(Mat4R::identity())
538 {
539  Vec3R scale, rotate, translate;
540  if (local_util::decompose(mTransform, scale, rotate, translate)) {
541  // If the transform can be decomposed into affine components,
542  // use them to set up a mipmapping-like scheme for downsampling.
543  init(mPivot, scale, rotate, translate, "srt", "zyx");
544  }
545 }
546 
547 
548 inline
550  const Vec3R& pivot, const Vec3R& scale,
551  const Vec3R& rotate, const Vec3R& translate,
552  const std::string& xformOrder, const std::string& rotOrder):
553  mPivot(0, 0, 0),
554  mMipLevels(0, 0, 0),
555  mPreScaleTransform(Mat4R::identity()),
556  mPostScaleTransform(Mat4R::identity())
557 {
558  init(pivot, scale, rotate, translate, xformOrder, rotOrder);
559 }
560 
561 
563 
564 
565 inline void
566 GridTransformer::init(
567  const Vec3R& pivot, const Vec3R& scale,
568  const Vec3R& rotate, const Vec3R& translate,
569  const std::string& xformOrder, const std::string& rotOrder)
570 {
571  if (xformOrder.size() != 3) {
572  OPENVDB_THROW(ValueError, "invalid transform order (" + xformOrder + ")");
573  }
574  if (rotOrder.size() != 3) {
575  OPENVDB_THROW(ValueError, "invalid rotation order (" + rotOrder + ")");
576  }
577 
578  mPivot = pivot;
579 
580  // Scaling is handled via a mipmapping-like scheme of successive
581  // halvings of the tree resolution, until the remaining scale
582  // factor is greater than or equal to 1/2.
583  Vec3R scaleRemainder = scale;
584  for (int i = 0; i < 3; ++i) {
585  double s = std::fabs(scale(i));
586  if (s < 0.5) {
587  mMipLevels(i) = int(std::floor(-std::log(s)/std::log(2.0)));
588  scaleRemainder(i) = scale(i) * (1 << mMipLevels(i));
589  }
590  }
591 
592  // Build pre-scale and post-scale transform matrices based on
593  // the user-specified order of operations.
594  // Note that we iterate over the transform order string in reverse order
595  // (e.g., "t", "r", "s", given "srt"). This is because math::Mat matrices
596  // postmultiply row vectors rather than premultiplying column vectors.
597  mTransform = mPreScaleTransform = mPostScaleTransform = Mat4R::identity();
598  Mat4R* remainder = &mPostScaleTransform;
599  int rpos, spos, tpos;
600  rpos = spos = tpos = 3;
601  for (int ix = 2; ix >= 0; --ix) { // reverse iteration
602  switch (xformOrder[ix]) {
603 
604  case 'r':
605  rpos = ix;
606  mTransform.preTranslate(pivot);
607  remainder->preTranslate(pivot);
608 
609  int xpos, ypos, zpos;
610  xpos = ypos = zpos = 3;
611  for (int ir = 2; ir >= 0; --ir) {
612  switch (rotOrder[ir]) {
613  case 'x':
614  xpos = ir;
615  mTransform.preRotate(math::X_AXIS, rotate.x());
616  remainder->preRotate(math::X_AXIS, rotate.x());
617  break;
618  case 'y':
619  ypos = ir;
620  mTransform.preRotate(math::Y_AXIS, rotate.y());
621  remainder->preRotate(math::Y_AXIS, rotate.y());
622  break;
623  case 'z':
624  zpos = ir;
625  mTransform.preRotate(math::Z_AXIS, rotate.z());
626  remainder->preRotate(math::Z_AXIS, rotate.z());
627  break;
628  }
629  }
630  // Reject rotation order strings that don't contain exactly one
631  // instance of "x", "y" and "z".
632  if (xpos > 2 || ypos > 2 || zpos > 2) {
633  OPENVDB_THROW(ValueError, "invalid rotation order (" + rotOrder + ")");
634  }
635 
636  mTransform.preTranslate(-pivot);
637  remainder->preTranslate(-pivot);
638  break;
639 
640  case 's':
641  spos = ix;
642  mTransform.preTranslate(pivot);
643  mTransform.preScale(scale);
644  mTransform.preTranslate(-pivot);
645 
646  remainder->preTranslate(pivot);
647  remainder->preScale(scaleRemainder);
648  remainder->preTranslate(-pivot);
649  remainder = &mPreScaleTransform;
650  break;
651 
652  case 't':
653  tpos = ix;
654  mTransform.preTranslate(translate);
655  remainder->preTranslate(translate);
656  break;
657  }
658  }
659  // Reject transform order strings that don't contain exactly one
660  // instance of "t", "r" and "s".
661  if (tpos > 2 || rpos > 2 || spos > 2) {
662  OPENVDB_THROW(ValueError, "invalid transform order (" + xformOrder + ")");
663  }
664 }
665 
666 
668 
669 
670 template<typename InterrupterType>
671 void
672 GridResampler::setInterrupter(InterrupterType& interrupter)
673 {
674  mInterrupt = std::bind(&InterrupterType::wasInterrupted,
675  /*this=*/&interrupter, /*percent=*/-1);
676 }
677 
678 
679 template<typename Sampler, typename GridT, typename Transformer>
680 void
681 GridResampler::transformGrid(const Transformer& xform,
682  const GridT& inGrid, GridT& outGrid) const
683 {
684  tools::changeBackground(outGrid.tree(), inGrid.background());
685  applyTransform<Sampler>(xform, inGrid, outGrid);
686 }
687 
688 
689 template<class Sampler, class GridT>
690 void
691 GridTransformer::transformGrid(const GridT& inGrid, GridT& outGrid) const
692 {
693  tools::changeBackground(outGrid.tree(), inGrid.background());
694 
695  if (!Sampler::mipmap() || mMipLevels == Vec3i::zero()) {
696  // Skip the mipmapping step.
697  const MatrixTransform xform(mTransform);
698  applyTransform<Sampler>(xform, inGrid, outGrid);
699 
700  } else {
701  bool firstPass = true;
702  const typename GridT::ValueType background = inGrid.background();
703  typename GridT::Ptr tempGrid = GridT::create(background);
704 
705  if (!mPreScaleTransform.eq(Mat4R::identity())) {
706  firstPass = false;
707  // Apply the pre-scale transform to the input grid
708  // and store the result in a temporary grid.
709  const MatrixTransform xform(mPreScaleTransform);
710  applyTransform<Sampler>(xform, inGrid, *tempGrid);
711  }
712 
713  // While the scale factor along one or more axes is less than 1/2,
714  // scale the grid by half along those axes.
715  Vec3i count = mMipLevels; // # of halvings remaining per axis
716  while (count != Vec3i::zero()) {
717  MatrixTransform xform;
718  xform.mat.setTranslation(mPivot);
719  xform.mat.preScale(Vec3R(
720  count.x() ? .5 : 1, count.y() ? .5 : 1, count.z() ? .5 : 1));
721  xform.mat.preTranslate(-mPivot);
722  xform.invMat = xform.mat.inverse();
723 
724  if (firstPass) {
725  firstPass = false;
726  // Scale the input grid and store the result in a temporary grid.
727  applyTransform<Sampler>(xform, inGrid, *tempGrid);
728  } else {
729  // Scale the temporary grid and store the result in a transient grid,
730  // then swap the two and discard the transient grid.
731  typename GridT::Ptr destGrid = GridT::create(background);
732  applyTransform<Sampler>(xform, *tempGrid, *destGrid);
733  tempGrid.swap(destGrid);
734  }
735  // (3, 2, 1) -> (2, 1, 0) -> (1, 0, 0) -> (0, 0, 0), etc.
736  count = math::maxComponent(count - 1, Vec3i::zero());
737  }
738 
739  // Apply the post-scale transform and store the result in the output grid.
740  if (!mPostScaleTransform.eq(Mat4R::identity())) {
741  const MatrixTransform xform(mPostScaleTransform);
742  applyTransform<Sampler>(xform, *tempGrid, outGrid);
743  } else {
744  outGrid.setTree(tempGrid->treePtr());
745  }
746  }
747 }
748 
749 
751 
752 
753 template<class Sampler, class TreeT, typename Transformer>
754 class GridResampler::RangeProcessor
755 {
756 public:
757  using LeafIterT = typename TreeT::LeafCIter;
758  using TileIterT = typename TreeT::ValueAllCIter;
759  using LeafRange = typename tree::IteratorRange<LeafIterT>;
760  using TileRange = typename tree::IteratorRange<TileIterT>;
761  using InTreeAccessor = typename tree::ValueAccessor<const TreeT>;
762  using OutTreeAccessor = typename tree::ValueAccessor<TreeT>;
763 
764  RangeProcessor(const Transformer& xform, const CoordBBox& b, const TreeT& inT, TreeT& outT):
765  mIsRoot(true), mXform(xform), mBBox(b),
766  mInTree(inT), mOutTree(&outT), mInAcc(mInTree), mOutAcc(*mOutTree)
767  {}
768 
769  RangeProcessor(const Transformer& xform, const CoordBBox& b, const TreeT& inTree):
770  mIsRoot(false), mXform(xform), mBBox(b),
771  mInTree(inTree), mOutTree(new TreeT(inTree.background())),
772  mInAcc(mInTree), mOutAcc(*mOutTree)
773  {}
774 
775  ~RangeProcessor() { if (!mIsRoot) delete mOutTree; }
776 
778  RangeProcessor(RangeProcessor& other, tbb::split):
779  mIsRoot(false),
780  mXform(other.mXform),
781  mBBox(other.mBBox),
782  mInTree(other.mInTree),
783  mOutTree(new TreeT(mInTree.background())),
784  mInAcc(mInTree),
785  mOutAcc(*mOutTree),
786  mInterrupt(other.mInterrupt)
787  {}
788 
789  void setInterrupt(const InterruptFunc& f) { mInterrupt = f; }
790 
792  void operator()(LeafRange& r)
793  {
794  for ( ; r; ++r) {
795  if (interrupt()) break;
796  LeafIterT i = r.iterator();
797  CoordBBox bbox(i->origin(), i->origin() + Coord(i->dim()));
798  if (!mBBox.empty()) {
799  // Intersect the leaf node's bounding box with mBBox.
800  bbox = CoordBBox(
801  Coord::maxComponent(bbox.min(), mBBox.min()),
802  Coord::minComponent(bbox.max(), mBBox.max()));
803  }
804  if (!bbox.empty()) {
805  transformBBox<Sampler>(mXform, bbox, mInAcc, mOutAcc, mInterrupt);
806  }
807  }
808  }
809 
811  void operator()(TileRange& r)
812  {
813  for ( ; r; ++r) {
814  if (interrupt()) break;
815 
816  TileIterT i = r.iterator();
817  // Skip voxels and background tiles.
818  if (!i.isTileValue()) continue;
819  if (!i.isValueOn() && math::isApproxEqual(*i, mOutTree->background())) continue;
820 
821  CoordBBox bbox;
822  i.getBoundingBox(bbox);
823  if (!mBBox.empty()) {
824  // Intersect the tile's bounding box with mBBox.
825  bbox = CoordBBox(
826  Coord::maxComponent(bbox.min(), mBBox.min()),
827  Coord::minComponent(bbox.max(), mBBox.max()));
828  }
829  if (!bbox.empty()) {
835  sampler(bbox, i.getValue(), i.isValueOn());
836  transformBBox(mXform, bbox, mInAcc, mOutAcc, mInterrupt, sampler);
837  }
838  }
839  }
840 
842  void join(RangeProcessor& other)
843  {
844  if (!interrupt()) mOutTree->merge(*other.mOutTree);
845  }
846 
847 private:
848  bool interrupt() const { return mInterrupt && mInterrupt(); }
849 
850  const bool mIsRoot; // true if mOutTree is the top-level tree
851  Transformer mXform;
852  CoordBBox mBBox;
853  const TreeT& mInTree;
854  TreeT* mOutTree;
855  InTreeAccessor mInAcc;
856  OutTreeAccessor mOutAcc;
857  InterruptFunc mInterrupt;
858 };
859 
860 
862 
863 
864 template<class Sampler, class GridT, typename Transformer>
865 void
866 GridResampler::applyTransform(const Transformer& xform,
867  const GridT& inGrid, GridT& outGrid) const
868 {
869  using TreeT = typename GridT::TreeType;
870  const TreeT& inTree = inGrid.tree();
871  TreeT& outTree = outGrid.tree();
872 
873  using RangeProc = RangeProcessor<Sampler, TreeT, Transformer>;
874 
875  const GridClass gridClass = inGrid.getGridClass();
876 
877  if (gridClass != GRID_LEVEL_SET && mTransformTiles) {
878  // Independently transform the tiles of the input grid.
879  // Note: Tiles in level sets can only be background tiles, and they
880  // are handled more efficiently with a signed flood fill (see below).
881 
882  RangeProc proc(xform, CoordBBox(), inTree, outTree);
883  proc.setInterrupt(mInterrupt);
884 
885  typename RangeProc::TileIterT tileIter = inTree.cbeginValueAll();
886  tileIter.setMaxDepth(tileIter.getLeafDepth() - 1); // skip leaf nodes
887  typename RangeProc::TileRange tileRange(tileIter);
888 
889  if (mThreaded) {
890  tbb::parallel_reduce(tileRange, proc);
891  } else {
892  proc(tileRange);
893  }
894  }
895 
896  CoordBBox clipBBox;
897  if (gridClass == GRID_LEVEL_SET) {
898  // Inactive voxels in level sets can only be background voxels, and they
899  // are handled more efficiently with a signed flood fill (see below).
900  clipBBox = inGrid.evalActiveVoxelBoundingBox();
901  }
902 
903  // Independently transform the leaf nodes of the input grid.
904 
905  RangeProc proc(xform, clipBBox, inTree, outTree);
906  proc.setInterrupt(mInterrupt);
907 
908  typename RangeProc::LeafRange leafRange(inTree.cbeginLeaf());
909 
910  if (mThreaded) {
911  tbb::parallel_reduce(leafRange, proc);
912  } else {
913  proc(leafRange);
914  }
915 
916  // If the grid is a level set, mark inactive voxels as inside or outside.
917  if (gridClass == GRID_LEVEL_SET) {
918  tools::pruneLevelSet(outTree);
919  tools::signedFloodFill(outTree);
920  }
921 }
922 
923 
925 
926 
927 //static
928 template<class Sampler, class InTreeT, class OutTreeT, class Transformer>
929 void
930 GridResampler::transformBBox(
931  const Transformer& xform,
932  const CoordBBox& bbox,
933  const InTreeT& inTree,
934  OutTreeT& outTree,
935  const InterruptFunc& interrupt,
936  const Sampler& sampler)
937 {
938  using ValueT = typename OutTreeT::ValueType;
939 
940  // Transform the corners of the input tree's bounding box
941  // and compute the enclosing bounding box in the output tree.
942  Vec3R
943  inRMin(bbox.min().x(), bbox.min().y(), bbox.min().z()),
944  inRMax(bbox.max().x(), bbox.max().y(), bbox.max().z()),
945  outRMin = math::minComponent(xform.transform(inRMin), xform.transform(inRMax)),
946  outRMax = math::maxComponent(xform.transform(inRMin), xform.transform(inRMax));
947  for (int i = 0; i < 8; ++i) {
948  Vec3R corner(
949  i & 1 ? inRMax.x() : inRMin.x(),
950  i & 2 ? inRMax.y() : inRMin.y(),
951  i & 4 ? inRMax.z() : inRMin.z());
952  outRMin = math::minComponent(outRMin, xform.transform(corner));
953  outRMax = math::maxComponent(outRMax, xform.transform(corner));
954  }
955  Vec3i
956  outMin = local_util::floorVec3(outRMin) - Sampler::radius(),
957  outMax = local_util::ceilVec3(outRMax) + Sampler::radius();
958 
959  if (!xform.isAffine()) {
960  // If the transform is not affine, back-project each output voxel
961  // into the input tree.
962  Vec3R xyz, inXYZ;
963  Coord outXYZ;
964  int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z();
965  for (x = outMin.x(); x <= outMax.x(); ++x) {
966  if (interrupt && interrupt()) break;
967  xyz.x() = x;
968  for (y = outMin.y(); y <= outMax.y(); ++y) {
969  if (interrupt && interrupt()) break;
970  xyz.y() = y;
971  for (z = outMin.z(); z <= outMax.z(); ++z) {
972  xyz.z() = z;
973  inXYZ = xform.invTransform(xyz);
974  ValueT result;
975  if (sampler.sample(inTree, inXYZ, result)) {
976  outTree.setValueOn(outXYZ, result);
977  } else {
978  // Note: Don't overwrite existing active values with inactive values.
979  if (!outTree.isValueOn(outXYZ)) {
980  outTree.setValueOff(outXYZ, result);
981  }
982  }
983  }
984  }
985  }
986  } else { // affine
987  // Compute step sizes in the input tree that correspond to
988  // unit steps in x, y and z in the output tree.
989  const Vec3R
990  translation = xform.invTransform(Vec3R(0, 0, 0)),
991  deltaX = xform.invTransform(Vec3R(1, 0, 0)) - translation,
992  deltaY = xform.invTransform(Vec3R(0, 1, 0)) - translation,
993  deltaZ = xform.invTransform(Vec3R(0, 0, 1)) - translation;
994 
995 #if defined(__ICC)
996  const Vec3R dummy = deltaX;
1000 #endif
1001 
1002  // Step by whole voxels through the output tree, sampling the
1003  // corresponding fractional voxels of the input tree.
1004  Vec3R inStartX = xform.invTransform(Vec3R(outMin));
1005  Coord outXYZ;
1006  int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z();
1007  for (x = outMin.x(); x <= outMax.x(); ++x, inStartX += deltaX) {
1008  if (interrupt && interrupt()) break;
1009  Vec3R inStartY = inStartX;
1010  for (y = outMin.y(); y <= outMax.y(); ++y, inStartY += deltaY) {
1011  if (interrupt && interrupt()) break;
1012  Vec3R inXYZ = inStartY;
1013  for (z = outMin.z(); z <= outMax.z(); ++z, inXYZ += deltaZ) {
1014  ValueT result;
1015  if (sampler.sample(inTree, inXYZ, result)) {
1016  outTree.setValueOn(outXYZ, result);
1017  } else {
1018  // Note: Don't overwrite existing active values with inactive values.
1019  if (!outTree.isValueOn(outXYZ)) {
1020  outTree.setValueOff(outXYZ, result);
1021  }
1022  }
1023  }
1024  }
1025  }
1026  }
1027 } // GridResampler::transformBBox()
1028 
1029 } // namespace tools
1030 } // namespace OPENVDB_VERSION_NAME
1031 } // namespace openvdb
1032 
1033 #endif // OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
1034 
1035 // Copyright (c) 2012-2017 DreamWorks Animation LLC
1036 // All rights reserved. This software is distributed under the
1037 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
ABTransform(const math::Transform &aXform, const math::Transform &bXform)
Definition: GridTransformer.h:406
bool isAffine(const Mat4< T > &m)
Definition: Mat4.h:1363
bool isApproxEqual(const Type &a, const Type &b)
Return true if a is equal to b to within the default floating-point comparison tolerance.
Definition: Math.h:370
const math::Transform & getA() const
Definition: GridTransformer.h:427
Calculate an axis-aligned bounding box in index space from a bounding sphere in world space...
Definition: Transform.h:66
bool isAffine() const
Definition: GridTransformer.h:384
void setThreaded(bool b)
Enable or disable threading. (Threading is enabled by default.)
Definition: GridTransformer.h:195
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:401
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
A GridTransformer applies a geometric transformation to an input grid using one of several sampling s...
Definition: GridTransformer.h:253
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
openvdb::Vec3R transform(const openvdb::Vec3R &pos) const
Definition: GridTransformer.h:417
virtual ~GridResampler()
Definition: GridTransformer.h:192
Vec3< int32_t > Vec3i
Definition: Vec3.h:705
T & z()
Definition: Vec3.h:111
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:101
void pruneLevelSet(TreeT &tree, bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing nodes whose values are all inactive with inactive ...
Definition: Prune.h:416
MatType rotation(const Quat< typename MatType::value_type > &q, typename MatType::value_type eps=static_cast< typename MatType::value_type >(1.0e-8))
Return the rotation matrix specified by the given quaternion.
Definition: Mat.h:169
Efficient multi-threaded replacement of the background values in tree.
bool sample(const TreeT &inTree, const Vec3R &inCoord, ValueT &result) const
Definition: GridTransformer.h:133
SharedPtr< GridResampler > Ptr
Definition: GridTransformer.h:188
bool threaded() const
Return true if threading is enabled.
Definition: GridTransformer.h:197
Definition: Math.h:859
MatrixTransform(const Mat4R &xform)
Definition: GridTransformer.h:382
void doResampleToMatch(const GridType &inGrid, GridType &outGrid, Interrupter &interrupter)
Definition: GridTransformer.h:445
MatType scale(const Vec3< typename MatType::value_type > &s)
Return a matrix that scales by s.
Definition: Mat.h:610
void setTransformTiles(bool b)
Enable or disable processing of tiles. (Enabled by default, except for level set grids.)
Definition: GridTransformer.h:199
void setInterrupter(InterrupterType &)
Allow processing to be aborted by providing an interrupter object. The interrupter will be queried pe...
Definition: GridTransformer.h:672
GridResampler()
Definition: GridTransformer.h:191
Vec3i floorVec3(const Vec3R &v)
Definition: Interpolation.h:611
Provises a unified interface for sampling, i.e. interpolation.
Definition: Interpolation.h:90
TileSampler(const CoordBBox &, const typename TreeT::ValueType &, bool)
Definition: GridTransformer.h:151
bool mEmpty
Definition: GridTransformer.h:142
Defined various multi-threaded utility functions for trees.
Mat4R mat
Definition: GridTransformer.h:390
Definition: GridTransformer.h:185
GridClass
Definition: Types.h:262
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:157
Definition: Interpolation.h:123
void applyTransform(const Transformer &, const GridT &inGrid, GridT &outGrid) const
Definition: GridTransformer.h:866
void transformGrid(const GridT &inGrid, GridT &outGrid) const
Definition: GridTransformer.h:691
ValueT mVal
Definition: GridTransformer.h:141
void preScale(const Vec3< T0 > &v)
Definition: Mat4.h:790
Vec2< T > maxComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise maximum of the two vectors.
Definition: Vec2.h:562
void transformGrid(const Transformer &, const GridT &inGrid, GridT &outGrid) const
Definition: GridTransformer.h:681
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:387
Vec3R invTransform(const Vec3R &pos) const
Definition: GridTransformer.h:388
const boost::disable_if_c< VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:128
BBoxd mBBox
Definition: GridTransformer.h:140
#define OPENVDB_VERSION_NAME
Definition: version.h:43
const math::Transform & getB() const
Definition: GridTransformer.h:428
const Mat4R & getTransform() const
Definition: GridTransformer.h:268
void preRotate(Axis axis, T angle)
Left multiplies by a rotation clock-wiseabout the given axis into this matrix.
Definition: Mat4.h:852
A TileSampler wraps a grid sampler of another type (BoxSampler, QuadraticSampler, etc...
Definition: GridTransformer.h:118
3x3 matrix class.
Definition: Mat3.h:54
Definition: Exceptions.h:91
Propagates the sign of distance values from the active voxels in the narrow band to the inactive valu...
Mat3< T > getMat3() const
Definition: Mat4.h:351
Vec3R transform(const Vec3R &pos) const
Definition: GridTransformer.h:386
Definition: Interpolation.h:223
MatrixTransform()
Definition: GridTransformer.h:381
Definition: Exceptions.h:39
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:76
bool interrupt() const
Definition: GridTransformer.h:216
const boost::disable_if_c< VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:132
T & x()
Reference to the component, e.g. v.x() = 4.5f;.
Definition: Vec3.h:109
T & y()
Definition: Vec3.h:110
bool isIdentity() const
Definition: GridTransformer.h:415
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:295
void resampleToMatch(const GridType &inGrid, GridType &outGrid)
Resample an input grid into an output grid of the same type such that, after resampling, the input and output grids coincide (apart from sampling artifacts), but the output grid&#39;s transform is unchanged.
Definition: GridTransformer.h:521
Vec3i ceilVec3(const Vec3R &v)
Definition: Interpolation.h:618
Vec3< typename MatType::value_type > eulerAngles(const MatType &mat, RotationOrder rotationOrder, typename MatType::value_type eps=static_cast< typename MatType::value_type >(1.0e-8))
Return the Euler angles composing the given rotation matrix.
Definition: Mat.h:330
Definition: TreeIterator.h:1339
void preTranslate(const Vec3< T0 > &tr)
Left multiples by the specified translation, i.e. Trans * (*this)
Definition: Mat4.h:757
Definition: Exceptions.h:92
bool isAffine() const
Definition: GridTransformer.h:413
void changeBackground(TreeOrLeafManagerT &tree, const typename TreeOrLeafManagerT::ValueType &background, bool threaded=true, size_t grainSize=32)
Replace the background value in all the nodes of a tree.
Definition: ChangeBackground.h:230
math::Vec3< Real > Vec3R
Definition: Types.h:75
GridTransformer(const Mat4R &xform)
Definition: GridTransformer.h:532
Mat4 inverse(T tolerance=0) const
Definition: Mat4.h:539
Definition: Math.h:858
std::shared_ptr< T > SharedPtr
Definition: Types.h:130
typename TreeT::ValueType ValueT
Definition: GridTransformer.h:121
Vec3< T > getTranslation() const
Return the translation component.
Definition: Mat4.h:363
std::function< bool(void)> InterruptFunc
Definition: GridTransformer.h:189
TileSampler(const CoordBBox &, const typename TreeT::ValueType &, bool)
Definition: GridTransformer.h:159
Dummy NOOP interrupter class defining interface.
Definition: NullInterrupter.h:52
static const Mat4< Real > & identity()
Predefined constant for identity matrix.
Definition: Mat4.h:152
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:71
static bool sample(const TreeT &inTree, const Vec3R &inCoord, typename TreeT::ValueType &result)
Sample inTree at the floating-point index coordinate inCoord and store the result in result...
Vec2< T > minComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise minimum of the two vectors.
Definition: Vec2.h:553
TileSampler(const CoordBBox &b, const ValueT &tileVal, bool on)
Definition: GridTransformer.h:126
Mat4R invMat
Definition: GridTransformer.h:390
void setTranslation(const Vec3< T > &t)
Definition: Mat4.h:368
bool eq(const Mat3 &m, T eps=1.0e-8) const
Test if "this" is equivalent to m with tolerance of eps value.
Definition: Mat3.h:355
openvdb::Vec3R invTransform(const openvdb::Vec3R &pos) const
Definition: GridTransformer.h:422
Definition: Types.h:264
bool transformTiles() const
Return true if tile processing is enabled.
Definition: GridTransformer.h:201
void signedFloodFill(TreeOrLeafManagerT &tree, bool threaded=true, size_t grainSize=1, Index minLevel=0)
Set the values of all inactive voxels and tiles of a narrow-band level set from the signs of the acti...
Definition: SignedFloodFill.h:294
Definition: Math.h:857