OpenVDB 13.0.0
Loading...
Searching...
No Matches
GridTransformer.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-2.0
3
4/// @file GridTransformer.h
5/// @author Peter Cucka
6
7#ifndef OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
8#define OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
9
10#include <openvdb/Grid.h>
11#include <openvdb/Types.h>
12#include <openvdb/math/Math.h> // for isApproxEqual()
14#include "ChangeBackground.h"
15#include "Interpolation.h"
16#include "LevelSetRebuild.h" // for doLevelSetRebuild()
17#include "SignedFloodFill.h" // for signedFloodFill
18#include "Prune.h" // for pruneLevelSet
19#include <openvdb/openvdb.h>
20#include <tbb/blocked_range.h>
21#include <tbb/parallel_reduce.h>
22#include <cmath>
23#include <functional>
24
25namespace openvdb {
27namespace OPENVDB_VERSION_NAME {
28namespace tools {
29
30/// @brief Resample an input grid into an output grid of the same type such that,
31/// after resampling, the input and output grids coincide (apart from sampling
32/// artifacts), but the output grid's transform is unchanged.
33/// @details Specifically, this function resamples the input grid into the output
34/// grid's index space, using a sampling kernel like PointSampler, BoxSampler,
35/// or QuadraticSampler.
36/// @param inGrid the grid to be resampled
37/// @param outGrid the grid into which to write the resampled voxel data
38/// @param interrupter an object adhering to the util::NullInterrupter interface
39/// @par Example:
40/// @code
41/// // Create an input grid with the default identity transform
42/// // and populate it with a level-set sphere.
43/// FloatGrid::ConstPtr src = tools::makeSphere(...);
44/// // Create an output grid and give it a uniform-scale transform.
45/// FloatGrid::Ptr dest = FloatGrid::create();
46/// const float voxelSize = 0.5;
47/// dest->setTransform(math::Transform::createLinearTransform(voxelSize));
48/// // Resample the input grid into the output grid, reproducing
49/// // the level-set sphere at a smaller voxel size.
50/// MyInterrupter interrupter = ...;
51/// tools::resampleToMatch<tools::QuadraticSampler>(*src, *dest, interrupter);
52/// @endcode
53template<typename Sampler, typename Interrupter, typename GridType>
54void
55resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter);
56
57/// @brief Resample an input grid into an output grid of the same type such that,
58/// after resampling, the input and output grids coincide (apart from sampling
59/// artifacts), but the output grid's transform is unchanged.
60/// @details Specifically, this function resamples the input grid into the output
61/// grid's index space, using a sampling kernel like PointSampler, BoxSampler,
62/// or QuadraticSampler.
63/// @param inGrid the grid to be resampled
64/// @param outGrid the grid into which to write the resampled voxel data
65/// @par Example:
66/// @code
67/// // Create an input grid with the default identity transform
68/// // and populate it with a level-set sphere.
69/// FloatGrid::ConstPtr src = tools::makeSphere(...);
70/// // Create an output grid and give it a uniform-scale transform.
71/// FloatGrid::Ptr dest = FloatGrid::create();
72/// const float voxelSize = 0.5;
73/// dest->setTransform(math::Transform::createLinearTransform(voxelSize));
74/// // Resample the input grid into the output grid, reproducing
75/// // the level-set sphere at a smaller voxel size.
76/// tools::resampleToMatch<tools::QuadraticSampler>(*src, *dest);
77/// @endcode
78template<typename Sampler, typename GridType>
79void
80resampleToMatch(const GridType& inGrid, GridType& outGrid);
81
82
83////////////////////////////////////////
84
85/// @cond OPENVDB_DOCS_INTERNAL
86
87namespace internal {
88
89/// @brief A TileSampler wraps a grid sampler of another type (BoxSampler,
90/// QuadraticSampler, etc.), and for samples that fall within a given tile
91/// of the grid, it returns a cached tile value instead of accessing the grid.
92template<typename Sampler, typename TreeT>
93class TileSampler: public Sampler
94{
95public:
96 using ValueT = typename TreeT::ValueType;
97
98 /// @param b the index-space bounding box of a particular grid tile
99 /// @param tileVal the tile's value
100 /// @param on the tile's active state
101 TileSampler(const CoordBBox& b, const ValueT& tileVal, bool on):
102 mBBox(b.min().asVec3d(), b.max().asVec3d()), mVal(tileVal), mActive(on), mEmpty(false)
103 {
104 mBBox.expand(-this->radius()); // shrink the bounding box by the sample radius
105 mEmpty = mBBox.empty();
106 }
107
108 bool sample(const TreeT& inTree, const Vec3R& inCoord, ValueT& result) const
109 {
110 if (!mEmpty && mBBox.isInside(inCoord)) { result = mVal; return mActive; }
111 return Sampler::sample(inTree, inCoord, result);
112 }
113
114protected:
115 BBoxd mBBox;
116 ValueT mVal;
117 bool mActive, mEmpty;
118};
119
120
121/// @brief For point sampling, tree traversal is less expensive than testing
122/// bounding box membership.
123template<typename TreeT>
124class TileSampler<PointSampler, TreeT>: public PointSampler {
125public:
126 TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {}
127};
128
129/// @brief For point sampling, tree traversal is less expensive than testing
130/// bounding box membership.
131template<typename TreeT>
132class TileSampler<StaggeredPointSampler, TreeT>: public StaggeredPointSampler {
133public:
134 TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {}
135};
136
137} // namespace internal
138
139/// @endcond
140
141
142////////////////////////////////////////
143
144
145/// A GridResampler applies a geometric transformation to an
146/// input grid using one of several sampling schemes, and stores
147/// the result in an output grid.
148///
149/// Usage:
150/// @code
151/// GridResampler resampler();
152/// resampler.transformGrid<BoxSampler>(xform, inGrid, outGrid);
153/// @endcode
154/// where @c xform is a functor that implements the following methods:
155/// @code
156/// bool isAffine() const
157/// openvdb::Vec3d transform(const openvdb::Vec3d&) const
158/// openvdb::Vec3d invTransform(const openvdb::Vec3d&) const
159/// @endcode
160/// @note When the transform is affine and can be expressed as a 4 x 4 matrix,
161/// a GridTransformer is much more efficient than a GridResampler.
163{
164public:
166 using InterruptFunc = std::function<bool (void)>;
167
168 GridResampler(): mThreaded(true), mTransformTiles(true) {}
169 virtual ~GridResampler() {}
170
171 GridResampler(const GridResampler&) = default;
173
174 /// Enable or disable threading. (Threading is enabled by default.)
175 void setThreaded(bool b) { mThreaded = b; }
176 /// Return @c true if threading is enabled.
177 bool threaded() const { return mThreaded; }
178 /// Enable or disable processing of tiles. (Enabled by default, except for level set grids.)
179 void setTransformTiles(bool b) { mTransformTiles = b; }
180 /// Return @c true if tile processing is enabled.
181 bool transformTiles() const { return mTransformTiles; }
182
183 /// @brief Allow processing to be aborted by providing an interrupter object.
184 /// The interrupter will be queried periodically during processing.
185 /// @see util/NullInterrupter.h for interrupter interface requirements.
186 template<typename InterrupterType> void setInterrupter(InterrupterType&);
187
188 template<typename Sampler, typename GridT, typename Transformer>
189 void transformGrid(const Transformer&,
190 const GridT& inGrid, GridT& outGrid) const;
191
192protected:
193 template<typename Sampler, typename GridT, typename Transformer>
194 void applyTransform(const Transformer&, const GridT& inGrid, GridT& outGrid) const;
195
196 bool interrupt() const { return mInterrupt && mInterrupt(); }
197
198private:
199 template<typename Sampler, typename InTreeT, typename OutTreeT, typename Transformer>
200 static void transformBBox(const Transformer&, const CoordBBox& inBBox,
201 const InTreeT& inTree, OutTreeT& outTree, const InterruptFunc&,
202 const Sampler& = Sampler());
203
204 template<typename Sampler, typename TreeT, typename Transformer>
205 class RangeProcessor;
206
207 bool mThreaded, mTransformTiles;
208 InterruptFunc mInterrupt;
209};
210
211
212////////////////////////////////////////
213
214
215/// @brief A GridTransformer applies a geometric transformation to an
216/// input grid using one of several sampling schemes, and stores
217/// the result in an output grid.
218///
219/// @note GridTransformer is optimized for affine transformations.
220///
221/// Usage:
222/// @code
223/// Mat4R xform = ...;
224/// GridTransformer transformer(xform);
225/// transformer.transformGrid<BoxSampler>(inGrid, outGrid);
226/// @endcode
227/// or
228/// @code
229/// Vec3R pivot = ..., scale = ..., rotate = ..., translate = ...;
230/// GridTransformer transformer(pivot, scale, rotate, translate);
231/// transformer.transformGrid<QuadraticSampler>(inGrid, outGrid);
232/// @endcode
234{
235public:
237
238 GridTransformer(const Mat4R& xform);
240 const Vec3R& pivot,
241 const Vec3R& scale,
242 const Vec3R& rotate,
243 const Vec3R& translate,
244 const std::string& xformOrder = "tsr",
245 const std::string& rotationOrder = "zyx");
246 ~GridTransformer() override = default;
247
250
251 const Mat4R& getTransform() const { return mTransform; }
252
253 template<class Sampler, class GridT>
254 void transformGrid(const GridT& inGrid, GridT& outGrid) const;
255
256private:
257 struct MatrixTransform;
258
259 inline void init(const Vec3R& pivot, const Vec3R& scale,
260 const Vec3R& rotate, const Vec3R& translate,
261 const std::string& xformOrder, const std::string& rotOrder);
262
263 Vec3R mPivot;
264 Vec3i mMipLevels;
265 Mat4R mTransform, mPreScaleTransform, mPostScaleTransform;
266};
267
268
269////////////////////////////////////////
270
271
272namespace local_util {
273
275
276/// @brief Decompose an affine transform into scale, rotation (XYZ order),
277/// and translation components.
278/// @return DECOMP_INVALID if the given matrix is not affine or cannot
279/// be decomposed, DECOMP_UNIQUE if the matrix has a unique decomposition,
280/// DECOMP_VALID otherwise
281template<typename T>
282int
284 math::Vec3<T>& rotate, math::Vec3<T>& translate)
285{
286 if (!math::isAffine(m)) return DECOMP_INVALID;
287
288 // This is the translation in world space
289 translate = m.getTranslation();
290 // Extract translation.
291 const math::Mat3<T> xform = m.getMat3();
292
293 const math::Vec3<T> unsignedScale(
294 (math::Vec3<T>(1, 0, 0) * xform).length(),
295 (math::Vec3<T>(0, 1, 0) * xform).length(),
296 (math::Vec3<T>(0, 0, 1) * xform).length());
297
298 const bool hasUniformScale = unsignedScale.eq(math::Vec3<T>(unsignedScale[0]));
299
300 bool hasRotation = false;
301 bool validDecomposition = false;
302
303 T minAngle = std::numeric_limits<T>::max();
304
305 // If the transformation matrix contains a reflection, test different negative scales
306 // to find a decomposition that favors the optimal resampling algorithm.
307 for (size_t n = 0; n < 8; ++n) {
308 const math::Vec3<T> signedScale(
309 n & 0x1 ? -unsignedScale.x() : unsignedScale.x(),
310 n & 0x2 ? -unsignedScale.y() : unsignedScale.y(),
311 n & 0x4 ? -unsignedScale.z() : unsignedScale.z());
312
313 // Extract scale and potentially reflection.
314 const math::Mat3<T> mat = xform * math::scale<math::Mat3<T> >(signedScale).inverse();
315 if (mat.det() < T(0.0)) continue; // Skip if mat contains a reflection.
316
318
319 const math::Mat3<T> rebuild =
320 math::rotation<math::Mat3<T> >(math::Vec3<T>(0, 0, 1), tmpAngle.z()) *
321 math::rotation<math::Mat3<T> >(math::Vec3<T>(0, 1, 0), tmpAngle.y()) *
322 math::rotation<math::Mat3<T> >(math::Vec3<T>(1, 0, 0), tmpAngle.x()) *
323 math::scale<math::Mat3<T> >(signedScale);
324
325 if (xform.eq(rebuild)) {
326
327 const T maxAngle = std::max(std::abs(tmpAngle[0]),
328 std::max(std::abs(tmpAngle[1]), std::abs(tmpAngle[2])));
329
330 if (!(minAngle < maxAngle)) { // Update if less or equal.
331
332 minAngle = maxAngle;
333 rotate = tmpAngle;
334 scale = signedScale;
335
336 hasRotation = !rotate.eq(math::Vec3<T>::zero());
337 validDecomposition = true;
338
339 if (hasUniformScale || !hasRotation) {
340 // Current decomposition is optimal.
341 break;
342 }
343 }
344 }
345 }
346
347 if (!validDecomposition) {
348 // The decomposition is invalid if the transformation matrix contains shear.
349 return DECOMP_INVALID;
350 }
351 if (hasRotation && !hasUniformScale) {
352 // No unique decomposition if scale is nonuniform and rotation is nonzero.
353 return DECOMP_VALID;
354 }
355 return DECOMP_UNIQUE;
356}
357
358} // namespace local_util
359
360
361////////////////////////////////////////
362
363
364/// This class implements the Transformer functor interface (specifically,
365/// the isAffine(), transform() and invTransform() methods) for a transform
366/// that is expressed as a 4 x 4 matrix.
368{
369 MatrixTransform(): mat(Mat4R::identity()), invMat(Mat4R::identity()) {}
370 MatrixTransform(const Mat4R& xform): mat(xform), invMat(xform.inverse()) {}
371
372 bool isAffine() const { return math::isAffine(mat); }
373
374 Vec3R transform(const Vec3R& pos) const { return mat.transformH(pos); }
375
376 Vec3R invTransform(const Vec3R& pos) const { return invMat.transformH(pos); }
377
379};
380
381
382////////////////////////////////////////
383
384
385/// @brief This class implements the Transformer functor interface (specifically,
386/// the isAffine(), transform() and invTransform() methods) for a transform
387/// that maps an A grid into a B grid's index space such that, after resampling,
388/// A's index space and transform match B's index space and transform.
390{
391public:
392 /// @param aXform the A grid's transform
393 /// @param bXform the B grid's transform
394 ABTransform(const math::Transform& aXform, const math::Transform& bXform):
395 mAXform(aXform),
396 mBXform(bXform),
397 mIsAffine(mAXform.isLinear() && mBXform.isLinear()),
398 mIsIdentity(mIsAffine && mAXform == mBXform)
399 {}
400
401 bool isAffine() const { return mIsAffine; }
402
403 bool isIdentity() const { return mIsIdentity; }
404
406 {
407 return mBXform.worldToIndex(mAXform.indexToWorld(pos));
408 }
409
411 {
412 return mAXform.worldToIndex(mBXform.indexToWorld(pos));
413 }
414
415 const math::Transform& getA() const { return mAXform; }
416 const math::Transform& getB() const { return mBXform; }
417
418private:
419 const math::Transform &mAXform, &mBXform;
420 const bool mIsAffine;
421 const bool mIsIdentity;
422};
423
424
425/// The normal entry points for resampling are the resampleToMatch() functions,
426/// which correctly handle level set grids under scaling and shearing.
427/// doResampleToMatch() is mainly for internal use but is typically faster
428/// for level sets, and correct provided that no scaling or shearing is needed.
429///
430/// @warning Do not use this function to scale or shear a level set grid.
431template<typename Sampler, typename Interrupter, typename GridType>
432void
433doResampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter)
434{
435 ABTransform xform(inGrid.transform(), outGrid.transform());
436
437 if (Sampler::consistent() && xform.isIdentity()) {
438 // If the transforms of the input and output are identical, the
439 // output tree is simply a deep copy of the input tree.
440 outGrid.setTree(inGrid.tree().copy());
441 } else if (xform.isAffine()) {
442 // If the input and output transforms are both affine, create an
443 // input to output transform (in:index-to-world * out:world-to-index)
444 // and use the fast GridTransformer API.
445 Mat4R mat = xform.getA().baseMap()->getAffineMap()->getMat4() *
446 ( xform.getB().baseMap()->getAffineMap()->getMat4().inverse() );
447
448 GridTransformer transformer(mat);
449 transformer.setInterrupter(interrupter);
450
451 // Transform the input grid and store the result in the output grid.
452 transformer.transformGrid<Sampler>(inGrid, outGrid);
453 } else {
454 // If either the input or the output transform is non-affine,
455 // use the slower GridResampler API.
456 GridResampler resampler;
457 resampler.setInterrupter(interrupter);
458
459 resampler.transformGrid<Sampler>(xform, inGrid, outGrid);
460 }
461}
462
463
464template<typename ValueType>
466 static ValueType eval(const ValueType& background, const Vec3d& voxelSize)
467 {
469 ValueType result(background * (1.0 / voxelSize[0]));
471 return result;
472 }
473}; // struct HalfWidthOp
474
475template<>
477 static bool eval(const bool& background, const Vec3d& /*voxelSize*/)
478 {
479 return background;
480 }
481}; // struct HalfWidthOp<bool>
482
483
484template<typename Sampler, typename Interrupter, typename GridType>
485void
486resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter)
487{
488 if (inGrid.getGridClass() == GRID_LEVEL_SET) {
489 // If the input grid is a level set, resample it using the level set rebuild tool.
490
491 if (inGrid.constTransform() == outGrid.constTransform()) {
492 // If the transforms of the input and output grids are identical,
493 // the output tree is simply a deep copy of the input tree.
494 outGrid.setTree(inGrid.tree().copy());
495 return;
496 }
497
498 // If the output grid is a level set, resample the input grid to have the output grid's
499 // background value. Otherwise, preserve the input grid's background value.
500 using ValueT = typename GridType::ValueType;
501 const bool outIsLevelSet = outGrid.getGridClass() == openvdb::GRID_LEVEL_SET;
502
503 const ValueT halfWidth = outIsLevelSet
504 ? HalfWidthOp<ValueT>::eval(outGrid.background(), outGrid.voxelSize())
505 : HalfWidthOp<ValueT>::eval(inGrid.background(), inGrid.voxelSize());
506
507 typename GridType::Ptr tempGrid;
508 try {
509 tempGrid = doLevelSetRebuild(inGrid, /*iso=*/zeroVal<ValueT>(),
510 /*exWidth=*/halfWidth, /*inWidth=*/halfWidth,
511 &outGrid.constTransform(), &interrupter);
512 } catch (TypeError&) {
513 // The input grid is classified as a level set, but it has a value type
514 // that is not supported by the level set rebuild tool. Fall back to
515 // using the generic resampler.
516 tempGrid.reset();
517 }
518 if (tempGrid) {
519 outGrid.setTree(tempGrid->treePtr());
520 return;
521 }
522 }
523
524 // If the input grid is not a level set, use the generic resampler.
525 doResampleToMatch<Sampler>(inGrid, outGrid, interrupter);
526}
527
528
529template<typename Sampler, typename GridType>
530void
531resampleToMatch(const GridType& inGrid, GridType& outGrid)
532{
533 util::NullInterrupter interrupter;
534 resampleToMatch<Sampler>(inGrid, outGrid, interrupter);
535}
536
537
538////////////////////////////////////////
539
540
541inline
543 mPivot(0, 0, 0),
544 mMipLevels(0, 0, 0),
545 mTransform(xform),
546 mPreScaleTransform(Mat4R::identity()),
547 mPostScaleTransform(Mat4R::identity())
548{
549 Vec3R scale, rotate, translate;
550 if (local_util::decompose(mTransform, scale, rotate, translate)) {
551 // If the transform can be decomposed into affine components,
552 // use them to set up a mipmapping-like scheme for downsampling.
553 init(mPivot, scale, rotate, translate, "rst", "zyx");
554 }
555}
556
557
558inline
560 const Vec3R& pivot, const Vec3R& scale,
561 const Vec3R& rotate, const Vec3R& translate,
562 const std::string& xformOrder, const std::string& rotOrder):
563 mPivot(0, 0, 0),
564 mMipLevels(0, 0, 0),
565 mPreScaleTransform(Mat4R::identity()),
566 mPostScaleTransform(Mat4R::identity())
567{
568 init(pivot, scale, rotate, translate, xformOrder, rotOrder);
569}
570
571
572////////////////////////////////////////
573
574
575inline void
576GridTransformer::init(
577 const Vec3R& pivot, const Vec3R& scale,
578 const Vec3R& rotate, const Vec3R& translate,
579 const std::string& xformOrder, const std::string& rotOrder)
580{
581 if (xformOrder.size() != 3) {
582 OPENVDB_THROW(ValueError, "invalid transform order (" + xformOrder + ")");
583 }
584 if (rotOrder.size() != 3) {
585 OPENVDB_THROW(ValueError, "invalid rotation order (" + rotOrder + ")");
586 }
587
588 mPivot = pivot;
589
590 // Scaling is handled via a mipmapping-like scheme of successive
591 // halvings of the tree resolution, until the remaining scale
592 // factor is greater than or equal to 1/2.
593 Vec3R scaleRemainder = scale;
594 for (int i = 0; i < 3; ++i) {
595 double s = std::fabs(scale(i));
596 if (s < 0.5) {
597 mMipLevels(i) = int(std::floor(-std::log(s)/std::log(2.0)));
598 scaleRemainder(i) = scale(i) * (1 << mMipLevels(i));
599 }
600 }
601
602 // Build pre-scale and post-scale transform matrices based on
603 // the user-specified order of operations.
604 // Note that we iterate over the transform order string in reverse order
605 // (e.g., "t", "r", "s", given "srt"). This is because math::Mat matrices
606 // postmultiply row vectors rather than premultiplying column vectors.
607 mTransform = mPreScaleTransform = mPostScaleTransform = Mat4R::identity();
608 Mat4R* remainder = &mPostScaleTransform;
609 int rpos, spos, tpos;
610 rpos = spos = tpos = 3;
611 for (int ix = 2; ix >= 0; --ix) { // reverse iteration
612 switch (xformOrder[ix]) {
613
614 case 'r':
615 rpos = ix;
616 mTransform.preTranslate(pivot);
617 remainder->preTranslate(pivot);
618
619 int xpos, ypos, zpos;
620 xpos = ypos = zpos = 3;
621 for (int ir = 2; ir >= 0; --ir) {
622 switch (rotOrder[ir]) {
623 case 'x':
624 xpos = ir;
625 mTransform.preRotate(math::X_AXIS, rotate.x());
626 remainder->preRotate(math::X_AXIS, rotate.x());
627 break;
628 case 'y':
629 ypos = ir;
630 mTransform.preRotate(math::Y_AXIS, rotate.y());
631 remainder->preRotate(math::Y_AXIS, rotate.y());
632 break;
633 case 'z':
634 zpos = ir;
635 mTransform.preRotate(math::Z_AXIS, rotate.z());
636 remainder->preRotate(math::Z_AXIS, rotate.z());
637 break;
638 }
639 }
640 // Reject rotation order strings that don't contain exactly one
641 // instance of "x", "y" and "z".
642 if (xpos > 2 || ypos > 2 || zpos > 2) {
643 OPENVDB_THROW(ValueError, "invalid rotation order (" + rotOrder + ")");
644 }
645
646 mTransform.preTranslate(-pivot);
647 remainder->preTranslate(-pivot);
648 break;
649
650 case 's':
651 spos = ix;
652 mTransform.preTranslate(pivot);
653 mTransform.preScale(scale);
654 mTransform.preTranslate(-pivot);
655
656 remainder->preTranslate(pivot);
657 remainder->preScale(scaleRemainder);
658 remainder->preTranslate(-pivot);
659 remainder = &mPreScaleTransform;
660 break;
661
662 case 't':
663 tpos = ix;
664 mTransform.preTranslate(translate);
665 remainder->preTranslate(translate);
666 break;
667 }
668 }
669 // Reject transform order strings that don't contain exactly one
670 // instance of "t", "r" and "s".
671 if (tpos > 2 || rpos > 2 || spos > 2) {
672 OPENVDB_THROW(ValueError, "invalid transform order (" + xformOrder + ")");
673 }
674}
675
676
677////////////////////////////////////////
678
679
680template<typename InterrupterType>
681void
682GridResampler::setInterrupter(InterrupterType& interrupter)
683{
684 mInterrupt = std::bind(&InterrupterType::wasInterrupted,
685 /*this=*/&interrupter, /*percent=*/-1);
686}
687
688
689template<typename Sampler, typename GridT, typename Transformer>
690void
691GridResampler::transformGrid(const Transformer& xform,
692 const GridT& inGrid, GridT& outGrid) const
693{
694 tools::changeBackground(outGrid.tree(), inGrid.background());
695 applyTransform<Sampler>(xform, inGrid, outGrid);
696}
697
698
699template<class Sampler, class GridT>
700void
701GridTransformer::transformGrid(const GridT& inGrid, GridT& outGrid) const
702{
703 tools::changeBackground(outGrid.tree(), inGrid.background());
704
705 if (!Sampler::mipmap() || mMipLevels == Vec3i::zero()) {
706 // Skip the mipmapping step.
707 const MatrixTransform xform(mTransform);
708 applyTransform<Sampler>(xform, inGrid, outGrid);
709
710 } else {
711 bool firstPass = true;
712 const typename GridT::ValueType background = inGrid.background();
713 typename GridT::Ptr tempGrid = GridT::create(background);
714
715 if (!mPreScaleTransform.eq(Mat4R::identity())) {
716 firstPass = false;
717 // Apply the pre-scale transform to the input grid
718 // and store the result in a temporary grid.
719 const MatrixTransform xform(mPreScaleTransform);
720 applyTransform<Sampler>(xform, inGrid, *tempGrid);
721 }
722
723 // While the scale factor along one or more axes is less than 1/2,
724 // scale the grid by half along those axes.
725 Vec3i count = mMipLevels; // # of halvings remaining per axis
726 while (count != Vec3i::zero()) {
727 MatrixTransform xform;
728 xform.mat.setTranslation(mPivot);
729 xform.mat.preScale(Vec3R(
730 count.x() ? .5 : 1, count.y() ? .5 : 1, count.z() ? .5 : 1));
731 xform.mat.preTranslate(-mPivot);
732 xform.invMat = xform.mat.inverse();
733
734 if (firstPass) {
735 firstPass = false;
736 // Scale the input grid and store the result in a temporary grid.
737 applyTransform<Sampler>(xform, inGrid, *tempGrid);
738 } else {
739 // Scale the temporary grid and store the result in a transient grid,
740 // then swap the two and discard the transient grid.
741 typename GridT::Ptr destGrid = GridT::create(background);
742 applyTransform<Sampler>(xform, *tempGrid, *destGrid);
743 tempGrid.swap(destGrid);
744 }
745 // (3, 2, 1) -> (2, 1, 0) -> (1, 0, 0) -> (0, 0, 0), etc.
746 count = math::maxComponent(count - 1, Vec3i::zero());
747 }
748
749 // Apply the post-scale transform and store the result in the output grid.
750 if (!mPostScaleTransform.eq(Mat4R::identity())) {
751 const MatrixTransform xform(mPostScaleTransform);
752 applyTransform<Sampler>(xform, *tempGrid, outGrid);
753 } else {
754 outGrid.setTree(tempGrid->treePtr());
755 }
756 }
757}
758
759
760////////////////////////////////////////
761
762
763template<class Sampler, class TreeT, typename Transformer>
764class GridResampler::RangeProcessor
765{
766public:
767 using LeafIterT = typename TreeT::LeafCIter;
768 using TileIterT = typename TreeT::ValueAllCIter;
769 using LeafRange = typename tree::IteratorRange<LeafIterT>;
770 using TileRange = typename tree::IteratorRange<TileIterT>;
771 using InTreeAccessor = typename tree::ValueAccessor<const TreeT>;
772 using OutTreeAccessor = typename tree::ValueAccessor<TreeT>;
773
774 RangeProcessor(const Transformer& xform, const CoordBBox& b, const TreeT& inT, TreeT& outT):
775 mIsRoot(true), mXform(xform), mBBox(b),
776 mInTree(inT), mOutTree(&outT), mInAcc(mInTree), mOutAcc(*mOutTree)
777 {}
778
779 RangeProcessor(const Transformer& xform, const CoordBBox& b, const TreeT& inTree):
780 mIsRoot(false), mXform(xform), mBBox(b),
781 mInTree(inTree), mOutTree(new TreeT(inTree.background())),
782 mInAcc(mInTree), mOutAcc(*mOutTree)
783 {}
784
785 ~RangeProcessor() { if (!mIsRoot) delete mOutTree; }
786
787 /// Splitting constructor: don't copy the original processor's output tree
788 RangeProcessor(RangeProcessor& other, tbb::split):
789 mIsRoot(false),
790 mXform(other.mXform),
791 mBBox(other.mBBox),
792 mInTree(other.mInTree),
793 mOutTree(new TreeT(mInTree.background())),
794 mInAcc(mInTree),
795 mOutAcc(*mOutTree),
796 mInterrupt(other.mInterrupt)
797 {}
798
799 void setInterrupt(const InterruptFunc& f) { mInterrupt = f; }
800
801 /// Transform each leaf node in the given range.
802 void operator()(const LeafRange& r)
803 {
804 for (LeafRange it(r); it.test(); ++it) {
805 if (interrupt()) break;
806 LeafIterT i = it.iterator();
807 CoordBBox bbox(i->origin(), i->origin() + Coord(i->dim()));
808 if (!mBBox.empty()) {
809 // Intersect the leaf node's bounding box with mBBox.
810 bbox = CoordBBox(
811 Coord::maxComponent(bbox.min(), mBBox.min()),
812 Coord::minComponent(bbox.max(), mBBox.max()));
813 }
814 if (!bbox.empty()) {
815 transformBBox<Sampler>(mXform, bbox, mInAcc, mOutAcc, mInterrupt);
816 }
817 }
818 }
819
820 /// Transform each non-background tile in the given range.
821 void operator()(const TileRange& r)
822 {
823 for (TileRange it(r); it.test(); ++it) {
824 if (interrupt()) break;
825
826 TileIterT i = it.iterator();
827 // Skip voxels and background tiles.
828 if (!i.isTileValue()) continue;
829 if (!i.isValueOn() && math::isApproxEqual(*i, mOutTree->background())) continue;
830
831 CoordBBox bbox;
832 i.getBoundingBox(bbox);
833 if (!mBBox.empty()) {
834 // Intersect the tile's bounding box with mBBox.
835 bbox = CoordBBox(
836 Coord::maxComponent(bbox.min(), mBBox.min()),
837 Coord::minComponent(bbox.max(), mBBox.max()));
838 }
839 if (!bbox.empty()) {
840 /// @todo This samples the tile voxel-by-voxel, which is much too slow.
841 /// Instead, compute the largest axis-aligned bounding box that is
842 /// contained in the transformed tile (adjusted for the sampler radius)
843 /// and fill it with the tile value. Then transform the remaining voxels.
844 internal::TileSampler<Sampler, InTreeAccessor>
845 sampler(bbox, i.getValue(), i.isValueOn());
846 transformBBox(mXform, bbox, mInAcc, mOutAcc, mInterrupt, sampler);
847 }
848 }
849 }
850
851 /// Merge another processor's output tree into this processor's tree.
852 void join(RangeProcessor& other)
853 {
854 if (!interrupt()) mOutTree->merge(*other.mOutTree);
855 }
856
857private:
858 bool interrupt() const { return mInterrupt && mInterrupt(); }
859
860 const bool mIsRoot; // true if mOutTree is the top-level tree
861 Transformer mXform;
862 CoordBBox mBBox;
863 const TreeT& mInTree;
864 TreeT* mOutTree;
865 InTreeAccessor mInAcc;
866 OutTreeAccessor mOutAcc;
867 InterruptFunc mInterrupt;
868};
869
870
871////////////////////////////////////////
872
873
874template<class Sampler, class GridT, typename Transformer>
875void
876GridResampler::applyTransform(const Transformer& xform,
877 const GridT& inGrid, GridT& outGrid) const
878{
879 using TreeT = typename GridT::TreeType;
880 const TreeT& inTree = inGrid.tree();
881 TreeT& outTree = outGrid.tree();
882
883 using RangeProc = RangeProcessor<Sampler, TreeT, Transformer>;
884
885 const GridClass gridClass = inGrid.getGridClass();
886
887 if (gridClass != GRID_LEVEL_SET && mTransformTiles) {
888 // Independently transform the tiles of the input grid.
889 // Note: Tiles in level sets can only be background tiles, and they
890 // are handled more efficiently with a signed flood fill (see below).
891
892 RangeProc proc(xform, CoordBBox(), inTree, outTree);
893 proc.setInterrupt(mInterrupt);
894
895 typename RangeProc::TileIterT tileIter = inTree.cbeginValueAll();
896 tileIter.setMaxDepth(tileIter.getLeafDepth() - 1); // skip leaf nodes
897 typename RangeProc::TileRange tileRange(tileIter);
898
899 if (mThreaded) {
900 tbb::parallel_reduce(tileRange, proc);
901 } else {
902 proc(tileRange);
903 }
904 }
905
906 CoordBBox clipBBox;
907 if (gridClass == GRID_LEVEL_SET) {
908 // Inactive voxels in level sets can only be background voxels, and they
909 // are handled more efficiently with a signed flood fill (see below).
910 clipBBox = inGrid.evalActiveVoxelBoundingBox();
911 }
912
913 // Independently transform the leaf nodes of the input grid.
914
915 RangeProc proc(xform, clipBBox, inTree, outTree);
916 proc.setInterrupt(mInterrupt);
917
918 typename RangeProc::LeafRange leafRange(inTree.cbeginLeaf());
919
920 if (mThreaded) {
921 tbb::parallel_reduce(leafRange, proc);
922 } else {
923 proc(leafRange);
924 }
925
926 // If the grid is a level set, mark inactive voxels as inside or outside.
927 if (gridClass == GRID_LEVEL_SET) {
928 tools::pruneLevelSet(outTree);
929 tools::signedFloodFill(outTree);
930 }
931}
932
933
934////////////////////////////////////////
935
936
937//static
938template<class Sampler, class InTreeT, class OutTreeT, class Transformer>
939void
940GridResampler::transformBBox(
941 const Transformer& xform,
942 const CoordBBox& bbox,
943 const InTreeT& inTree,
944 OutTreeT& outTree,
945 const InterruptFunc& interrupt,
946 const Sampler& sampler)
947{
948 using ValueT = typename OutTreeT::ValueType;
949
950 // Transform the corners of the input tree's bounding box
951 // and compute the enclosing bounding box in the output tree.
952 Vec3R
953 inRMin(bbox.min().x(), bbox.min().y(), bbox.min().z()),
954 inRMax(bbox.max().x()+1, bbox.max().y()+1, bbox.max().z()+1),
955 outRMin = math::minComponent(xform.transform(inRMin), xform.transform(inRMax)),
956 outRMax = math::maxComponent(xform.transform(inRMin), xform.transform(inRMax));
957 for (int i = 0; i < 8; ++i) {
958 Vec3R corner(
959 i & 1 ? inRMax.x() : inRMin.x(),
960 i & 2 ? inRMax.y() : inRMin.y(),
961 i & 4 ? inRMax.z() : inRMin.z());
962 outRMin = math::minComponent(outRMin, xform.transform(corner));
963 outRMax = math::maxComponent(outRMax, xform.transform(corner));
964 }
965 Vec3i
966 outMin = local_util::floorVec3(outRMin) - Sampler::radius(),
967 outMax = local_util::ceilVec3(outRMax) + Sampler::radius();
968
969 if (!xform.isAffine()) {
970 // If the transform is not affine, back-project each output voxel
971 // into the input tree.
972 Vec3R xyz, inXYZ;
973 Coord outXYZ;
974 int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z();
975 for (x = outMin.x(); x <= outMax.x(); ++x) {
976 if (interrupt && interrupt()) break;
977 xyz.x() = x;
978 for (y = outMin.y(); y <= outMax.y(); ++y) {
979 if (interrupt && interrupt()) break;
980 xyz.y() = y;
981 for (z = outMin.z(); z <= outMax.z(); ++z) {
982 xyz.z() = z;
983 inXYZ = xform.invTransform(xyz);
984 ValueT result;
985 if (sampler.sample(inTree, inXYZ, result)) {
986 outTree.setValueOn(outXYZ, result);
987 } else {
988 // Note: Don't overwrite existing active values with inactive values.
989 if (!outTree.isValueOn(outXYZ)) {
990 outTree.setValueOff(outXYZ, result);
991 }
992 }
993 }
994 }
995 }
996 } else { // affine
997 // Compute step sizes in the input tree that correspond to
998 // unit steps in x, y and z in the output tree.
999 const Vec3R
1000 translation = xform.invTransform(Vec3R(0, 0, 0)),
1001 deltaX = xform.invTransform(Vec3R(1, 0, 0)) - translation,
1002 deltaY = xform.invTransform(Vec3R(0, 1, 0)) - translation,
1003 deltaZ = xform.invTransform(Vec3R(0, 0, 1)) - translation;
1004
1005#if defined(__ICC)
1006 /// @todo The following line is a workaround for bad code generation
1007 /// in opt-icc11.1_64 (but not debug or gcc) builds. It should be
1008 /// removed once the problem has been addressed at its source.
1009 const Vec3R dummy = deltaX;
1010#endif
1011
1012 // Step by whole voxels through the output tree, sampling the
1013 // corresponding fractional voxels of the input tree.
1014 Vec3R inStartX = xform.invTransform(Vec3R(outMin));
1015 Coord outXYZ;
1016 int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z();
1017 for (x = outMin.x(); x <= outMax.x(); ++x, inStartX += deltaX) {
1018 if (interrupt && interrupt()) break;
1019 Vec3R inStartY = inStartX;
1020 for (y = outMin.y(); y <= outMax.y(); ++y, inStartY += deltaY) {
1021 if (interrupt && interrupt()) break;
1022 Vec3R inXYZ = inStartY;
1023 for (z = outMin.z(); z <= outMax.z(); ++z, inXYZ += deltaZ) {
1024 ValueT result;
1025 if (sampler.sample(inTree, inXYZ, result)) {
1026 outTree.setValueOn(outXYZ, result);
1027 } else {
1028 // Note: Don't overwrite existing active values with inactive values.
1029 if (!outTree.isValueOn(outXYZ)) {
1030 outTree.setValueOff(outXYZ, result);
1031 }
1032 }
1033 }
1034 }
1035 }
1036 }
1037} // GridResampler::transformBBox()
1038
1039
1040////////////////////////////////////////
1041
1042
1043// Explicit Template Instantiation
1044
1045#ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
1046
1047#ifdef OPENVDB_INSTANTIATE_GRIDTRANSFORMER
1049#endif
1050
1051#define _FUNCTION(TreeT) \
1052 void resampleToMatch<PointSampler>(const Grid<TreeT>&, Grid<TreeT>&, util::NullInterrupter&)
1054#undef _FUNCTION
1055
1056#define _FUNCTION(TreeT) \
1057 void resampleToMatch<BoxSampler>(const Grid<TreeT>&, Grid<TreeT>&, util::NullInterrupter&)
1059#undef _FUNCTION
1060
1061#define _FUNCTION(TreeT) \
1062 void resampleToMatch<QuadraticSampler>(const Grid<TreeT>&, Grid<TreeT>&, util::NullInterrupter&)
1064#undef _FUNCTION
1065
1066#define _FUNCTION(TreeT) \
1067 void resampleToMatch<QuadraticSampler>(const Grid<TreeT>&, Grid<TreeT>&, util::NullInterrupter&)
1069#undef _FUNCTION
1070
1071#endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
1072
1073
1074} // namespace tools
1075} // namespace OPENVDB_VERSION_NAME
1076} // namespace openvdb
1077
1078#endif // OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
Efficient multi-threaded replacement of the background values in tree.
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
Bracket code with OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN/_END, to inhibit warnings about type conve...
Definition Platform.h:244
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_END
Definition Platform.h:245
Defined various multi-threaded utility functions for trees.
Propagate the signs of distance values from the active voxels in the narrow band to the inactive valu...
CoordBBox()
The default constructor produces an empty bounding box.
Definition Coord.h:297
void minComponent(const Coord &other)
Perform a component-wise minimum with the other Coord.
Definition Coord.h:176
void maxComponent(const Coord &other)
Perform a component-wise maximum with the other Coord.
Definition Coord.h:184
Definition Exceptions.h:64
Definition Exceptions.h:65
Axis-aligned bounding box of signed integer coordinates.
Definition Coord.h:252
const Coord & min() const
Definition Coord.h:324
const Coord & max() const
Definition Coord.h:325
Signed (x, y, z) 32-bit integer coordinates.
Definition Coord.h:26
Int32 y() const
Definition Coord.h:132
Int32 x() const
Definition Coord.h:131
Int32 z() const
Definition Coord.h:133
3x3 matrix class.
Definition Mat3.h:29
bool eq(const Mat3 &m, T eps=T(1.0e-8)) const
Return true if this matrix is equivalent to m within a tolerance of eps.
Definition Mat3.h:301
T det() const
Determinant of matrix.
Definition Mat3.h:479
4x4 -matrix class.
Definition Mat4.h:31
void preScale(const Vec3< T0 > &v)
Definition Mat4.h:736
Vec3< T > getTranslation() const
Return the translation component.
Definition Mat4.h:309
void preTranslate(const Vec3< T0 > &tr)
Left multiples by the specified translation, i.e. Trans * (*this).
Definition Mat4.h:703
Mat4 inverse(T tolerance=0) const
Definition Mat4.h:485
Mat3< T > getMat3() const
Definition Mat4.h:297
static const Mat4< Real > & identity()
Definition Mat4.h:117
void setTranslation(const Vec3< T > &t)
Definition Mat4.h:314
Definition Transform.h:40
MapBase::ConstPtr baseMap() const
Return a base pointer to the transformation map.
Definition Transform.h:131
Definition Vec3.h:25
T & x()
Reference to the component, e.g. v.x() = 4.5f;.
Definition Vec3.h:86
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:134
T & y()
Definition Vec3.h:87
T & z()
Definition Vec3.h:88
static Vec3< T > zero()
Predefined constants, e.g. Vec3d v = Vec3d::xNegAxis();.
Definition Vec3.h:467
This class implements the Transformer functor interface (specifically, the isAffine(),...
Definition GridTransformer.h:390
openvdb::Vec3R transform(const openvdb::Vec3R &pos) const
Definition GridTransformer.h:405
ABTransform(const math::Transform &aXform, const math::Transform &bXform)
Definition GridTransformer.h:394
bool isAffine() const
Definition GridTransformer.h:401
bool isIdentity() const
Definition GridTransformer.h:403
openvdb::Vec3R invTransform(const openvdb::Vec3R &pos) const
Definition GridTransformer.h:410
const math::Transform & getA() const
Definition GridTransformer.h:415
const math::Transform & getB() const
Definition GridTransformer.h:416
Definition GridTransformer.h:163
bool transformTiles() const
Return true if tile processing is enabled.
Definition GridTransformer.h:181
void applyTransform(const Transformer &, const GridT &inGrid, GridT &outGrid) const
Definition GridTransformer.h:876
GridResampler()
Definition GridTransformer.h:168
void setInterrupter(InterrupterType &)
Allow processing to be aborted by providing an interrupter object. The interrupter will be queried pe...
Definition GridTransformer.h:682
std::function< bool(void)> InterruptFunc
Definition GridTransformer.h:166
void setTransformTiles(bool b)
Enable or disable processing of tiles. (Enabled by default, except for level set grids....
Definition GridTransformer.h:179
SharedPtr< GridResampler > Ptr
Definition GridTransformer.h:165
bool interrupt() const
Definition GridTransformer.h:196
GridResampler & operator=(const GridResampler &)=default
void transformGrid(const Transformer &, const GridT &inGrid, GridT &outGrid) const
Definition GridTransformer.h:691
void setThreaded(bool b)
Enable or disable threading. (Threading is enabled by default.).
Definition GridTransformer.h:175
virtual ~GridResampler()
Definition GridTransformer.h:169
bool threaded() const
Return true if threading is enabled.
Definition GridTransformer.h:177
GridResampler(const GridResampler &)=default
A GridTransformer applies a geometric transformation to an input grid using one of several sampling s...
Definition GridTransformer.h:234
GridTransformer & operator=(const GridTransformer &)=default
SharedPtr< GridTransformer > Ptr
Definition GridTransformer.h:236
GridTransformer(const Mat4R &xform)
Definition GridTransformer.h:542
GridTransformer(const GridTransformer &)=default
void transformGrid(const GridT &inGrid, GridT &outGrid) const
Definition GridTransformer.h:701
const Mat4R & getTransform() const
Definition GridTransformer.h:251
Definition TreeIterator.h:1304
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:333
bool isApproxEqual(const Type &a, const Type &b, const Type &tolerance)
Return true if a is equal to b to within the given tolerance.
Definition Math.h:431
MatType scale(const Vec3< typename MatType::value_type > &s)
Return a matrix that scales by s.
Definition Mat.h:615
Vec3< double > Vec3d
Definition Vec3.h:665
bool isAffine(const Mat4< T > &m)
Definition Mat4.h:1305
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:172
Vec2< T > minComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise minimum of the two vectors.
Definition Vec2.h:504
Vec3< int32_t > Vec3i
Definition Vec3.h:662
Vec2< T > maxComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise maximum of the two vectors.
Definition Vec2.h:513
@ Z_AXIS
Definition Math.h:975
@ X_AXIS
Definition Math.h:973
@ Y_AXIS
Definition Math.h:974
@ XYZ_ROTATION
Definition Math.h:980
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition Composite.h:110
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition Composite.h:106
Definition GridTransformer.h:272
int 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 (XYZ order), and translation components.
Definition GridTransformer.h:283
Vec3i ceilVec3(const Vec3R &v)
Definition Interpolation.h:592
@ DECOMP_VALID
Definition GridTransformer.h:274
@ DECOMP_INVALID
Definition GridTransformer.h:274
@ DECOMP_UNIQUE
Definition GridTransformer.h:274
Vec3i floorVec3(const Vec3R &v)
Definition Interpolation.h:585
Processor proc(inIter, Adapter::tree(outGrid), op, merge)
OutGridT const XformOp bool bool
Definition ValueTransformer.h:610
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:390
OutGridT & outGrid
Definition ValueTransformer.h:139
typename Adapter::TreeType OutTreeT
Definition ValueTransformer.h:595
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:267
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:204
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,...
Definition GridTransformer.h:486
void doResampleToMatch(const GridType &inGrid, GridType &outGrid, Interrupter &interrupter)
Definition GridTransformer.h:433
ValueAccessorImpl< TreeType, IsSafe, MutexType, openvdb::make_index_sequence< CacheLevels > > ValueAccessor
Default alias for a ValueAccessor. This is simply a helper alias for the generic definition but takes...
Definition ValueAccessor.h:86
GridClass
Definition Types.h:520
@ GRID_LEVEL_SET
Definition Types.h:522
constexpr T zeroVal()
Return the value of type T that corresponds to zero.
Definition Math.h:71
math::Mat4< Real > Mat4R
Definition Types.h:82
math::BBox< Vec3d > BBoxd
Definition Types.h:65
math::Vec3< Real > Vec3R
Definition Types.h:53
std::shared_ptr< T > SharedPtr
Definition Types.h:95
Definition Exceptions.h:13
#define OPENVDB_THROW(exception, message)
Definition Exceptions.h:74
Vec3R invTransform(const Vec3R &pos) const
Definition GridTransformer.h:376
MatrixTransform()
Definition GridTransformer.h:369
bool isAffine() const
Definition GridTransformer.h:372
Mat4R invMat
Definition GridTransformer.h:378
Mat4R mat
Definition GridTransformer.h:378
Vec3R transform(const Vec3R &pos) const
Definition GridTransformer.h:374
MatrixTransform(const Mat4R &xform)
Definition GridTransformer.h:370
static bool eval(const bool &background, const Vec3d &)
Definition GridTransformer.h:477
Definition GridTransformer.h:465
static ValueType eval(const ValueType &background, const Vec3d &voxelSize)
Definition GridTransformer.h:466
Provises a unified interface for sampling, i.e. interpolation.
Definition Interpolation.h:65
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.
Base class for interrupters.
Definition NullInterrupter.h:26
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:218
#define OPENVDB_NUMERIC_TREE_INSTANTIATE(Function)
Definition version.h.in:163
#define OPENVDB_VEC3_TREE_INSTANTIATE(Function)
Definition version.h.in:164
#define OPENVDB_VOLUME_TREE_INSTANTIATE(Function)
Definition version.h.in:165