diff options
author | Rafael Campos <rafaelcdn@gmail.com> | 2014-04-30 23:47:09 +0400 |
---|---|---|
committer | Rafael Campos <rafaelcdn@gmail.com> | 2014-04-30 23:47:09 +0400 |
commit | 724750f47d836df1e71a538283e1c99f4e69f8fc (patch) | |
tree | da5eb3fa7e0672cfee75e0b779848e0f7a85ef82 | |
parent | 155805b20959a7a41eb7e4fbbc4c5fad4c546869 (diff) |
Updated OpenVDB library to 2.3.0;soc-2013-cycles_volume
83 files changed, 20576 insertions, 7092 deletions
diff --git a/extern/openvdb/CMakeLists.txt b/extern/openvdb/CMakeLists.txt index 7eb33cf2dc8..82d0d02d79b 100644 --- a/extern/openvdb/CMakeLists.txt +++ b/extern/openvdb/CMakeLists.txt @@ -40,6 +40,7 @@ set(SRC internal/openvdb/io/Compression.cc internal/openvdb/io/File.cc internal/openvdb/io/GridDescriptor.cc + internal/openvdb/io/Queue.cc internal/openvdb/io/Stream.cc internal/openvdb/math/Hermite.cc internal/openvdb/math/Maps.cc @@ -59,9 +60,11 @@ set(SRC internal/openvdb/io/Compression.h internal/openvdb/io/File.h internal/openvdb/io/GridDescriptor.h + internal/openvdb/io/Queue.h internal/openvdb/io/Stream.h internal/openvdb/math/BBox.h internal/openvdb/math/Coord.h + internal/openvdb/math/DDA.h internal/openvdb/math/FiniteDifference.h internal/openvdb/math/Hermite.h internal/openvdb/math/LegacyFrustum.h @@ -74,6 +77,7 @@ set(SRC internal/openvdb/math/Proximity.h internal/openvdb/math/QuantizedUnitVec.h internal/openvdb/math/Quat.h + internal/openvdb/math/Ray.h internal/openvdb/math/Stats.h internal/openvdb/math/Stencils.h internal/openvdb/math/Transform.h @@ -90,6 +94,7 @@ set(SRC internal/openvdb/PlatformConfig.h internal/openvdb/tools/Composite.h internal/openvdb/tools/Dense.h + internal/openvdb/tools/DenseSparseTools.h internal/openvdb/tools/Filter.h internal/openvdb/tools/GridOperators.h internal/openvdb/tools/GridTransformer.h @@ -97,6 +102,8 @@ set(SRC internal/openvdb/tools/LevelSetAdvect.h internal/openvdb/tools/LevelSetFilter.h internal/openvdb/tools/LevelSetFracture.h + internal/openvdb/tools/LevelSetMeasure.h + internal/openvdb/tools/LevelSetMorph.h internal/openvdb/tools/LevelSetRebuild.h internal/openvdb/tools/LevelSetSphere.h internal/openvdb/tools/LevelSetTracker.h @@ -106,9 +113,13 @@ set(SRC internal/openvdb/tools/ParticlesToLevelSet.h internal/openvdb/tools/PointAdvect.h internal/openvdb/tools/PointScatter.h + internal/openvdb/tools/RayIntersector.h + internal/openvdb/tools/RayTracer.h internal/openvdb/tools/Statistics.h internal/openvdb/tools/ValueTransformer.h + internal/openvdb/tools/VectorTransformer.h internal/openvdb/tools/VolumeToMesh.h + internal/openvdb/tools/VolumeToSpheres.h internal/openvdb/tree/InternalNode.h internal/openvdb/tree/Iterator.h internal/openvdb/tree/LeafManager.h diff --git a/extern/openvdb/internal/openvdb/Grid.h b/extern/openvdb/internal/openvdb/Grid.h index e6f6914f531..42018f27097 100644 --- a/extern/openvdb/internal/openvdb/Grid.h +++ b/extern/openvdb/internal/openvdb/Grid.h @@ -93,7 +93,7 @@ inline typename Grid<typename TreePtrType::element_type>::Ptr createGrid(TreePtr /// that is larger than one voxel unit, otherwise zero crossings are not guaranteed. template<typename GridType> typename GridType::Ptr createLevelSet( - double voxelSize = 1.0, double halfWidth = LEVEL_SET_HALF_WIDTH); + Real voxelSize = 1.0, Real halfWidth = LEVEL_SET_HALF_WIDTH); //////////////////////////////////////// @@ -417,7 +417,7 @@ typedef boost::shared_ptr<GridCPtrSet> GridCPtrSetPtr; /// @brief Predicate functor that returns @c true for grids that have a specified name struct OPENVDB_API GridNamePred { - GridNamePred(const Name& name): name(name) {} + GridNamePred(const Name& _name): name(_name) {} bool operator()(const GridBase::ConstPtr& g) const { return g && g->getName() == name; } Name name; }; @@ -509,6 +509,13 @@ public: explicit Grid(TreePtrType); /// Deep copy another grid's metadata, transform and tree. Grid(const Grid&); + /// @brief Deep copy the metadata, transform and tree of another grid whose tree + /// configuration is the same as this grid's but whose value type is different. + /// Cast the other grid's values to this grid's value type. + /// @throw TypeError if the other grid's tree configuration doesn't match this grid's + /// or if this grid's ValueType is not constructible from the other grid's ValueType. + template<typename OtherTreeType> + explicit Grid(const Grid<OtherTreeType>&); /// Deep copy another grid's metadata, but share its tree and transform. Grid(const Grid&, ShallowCopy); /// @brief Deep copy another grid's metadata and transform, but construct a new tree @@ -618,10 +625,12 @@ public: /// Reduce the memory footprint of this grid by increasing its sparseness. virtual void pruneGrid(float tolerance = 0.0); - /// @brief Transfer active voxels from another grid to this grid - /// wherever those voxels coincide with inactive voxels in this grid. - /// @note This operation always empties the other tree. - void merge(Grid& other) { tree().merge(other.tree()); } + /// @brief Efficiently merge another grid into this grid using one of several schemes. + /// @details This operation is primarily intended to combine grids that are mostly + /// non-overlapping (for example, intermediate grids from computations that are + /// parallelized across disjoint regions of space). + /// @warning This operation always empties the other grid. + void merge(Grid& other, MergePolicy policy = MERGE_ACTIVE_STATES); /// @brief Union this grid's set of active values with the active values /// of the other grid, whose value type may be different. @@ -630,16 +639,42 @@ public: /// value maps to a voxel if the corresponding value already mapped to a voxel /// OR if it is a voxel in the other grid. Thus, a resulting value can only /// map to a tile if the corresponding value already mapped to a tile - /// AND if it is a tile value in other grid. + /// AND if it is a tile value in the other grid. /// /// @note This operation modifies only active states, not values. /// Specifically, active tiles and voxels in this grid are not changed, and /// tiles or voxels that were inactive in this grid but active in the other grid /// are marked as active in this grid but left with their original values. template<typename OtherTreeType> - void topologyUnion(const Grid<OtherTreeType>& other) { tree().topologyUnion(other.tree()); } - /// @todo topologyIntersection - /// @todo topologyDifference + void topologyUnion(const Grid<OtherTreeType>& other); + + /// @brief Intersect this grid's set of active values with the active values + /// of the other grid, whose value type may be different. + /// @details The resulting state of a value is active only if the corresponding + /// value was already active AND if it is active in the other tree. Also, a + /// resulting value maps to a voxel if the corresponding value + /// already mapped to an active voxel in either of the two grids + /// and it maps to an active tile or voxel in the other grid. + /// + /// @note This operation can delete branches of this grid that overlap with + /// inactive tiles in the other grid. Also, because it can deactivate voxels, + /// it can create leaf nodes with no active values. Thus, it is recommended + /// to prune this grid after calling this method. + template<typename OtherTreeType> + void topologyIntersection(const Grid<OtherTreeType>& other); + + /// @brief Difference this grid's set of active values with the active values + /// of the other grid, whose value type may be different. + /// @details After this method is called, voxels in this grid will be active + /// only if they were active to begin with and if the corresponding voxels + /// in the other grid were inactive. + /// + /// @note This operation can delete branches of this grid that overlap with + /// active tiles in the other grid. Also, because it can deactivate voxels, + /// it can create leaf nodes with no active values. Thus, it is recommended + /// to prune this grid after calling this method. + template<typename OtherTreeType> + void topologyDifference(const Grid<OtherTreeType>& other); // // Statistics @@ -812,6 +847,9 @@ struct TreeAdapter typedef typename NonConstGridType::Ptr NonConstGridPtrType; typedef typename GridType::ConstPtr ConstGridPtrType; typedef typename TreeType::ValueType ValueType; + typedef typename tree::ValueAccessor<TreeType> AccessorType; + typedef typename tree::ValueAccessor<const TreeType> ConstAccessorType; + typedef typename tree::ValueAccessor<NonConstTreeType> NonConstAccessorType; static TreeType& tree(TreeType& t) { return t; } static TreeType& tree(GridType& g) { return g.tree(); } @@ -839,6 +877,9 @@ struct TreeAdapter<Grid<_TreeType> > typedef typename NonConstGridType::Ptr NonConstGridPtrType; typedef typename GridType::ConstPtr ConstGridPtrType; typedef typename TreeType::ValueType ValueType; + typedef typename tree::ValueAccessor<TreeType> AccessorType; + typedef typename tree::ValueAccessor<const TreeType> ConstAccessorType; + typedef typename tree::ValueAccessor<NonConstTreeType> NonConstAccessorType; static TreeType& tree(TreeType& t) { return t; } static TreeType& tree(GridType& g) { return g.tree(); } @@ -849,6 +890,38 @@ struct TreeAdapter<Grid<_TreeType> > static const TreeType& constTree(const TreeType& t) { return t; } static const TreeType& constTree(const GridType& g) { return g.constTree(); } }; + +/// Partial specialization for ValueAccessor types +template<typename _TreeType> +struct TreeAdapter<tree::ValueAccessor<_TreeType> > +{ + typedef _TreeType TreeType; + typedef typename boost::remove_const<TreeType>::type NonConstTreeType; + typedef typename TreeType::Ptr TreePtrType; + typedef typename TreeType::ConstPtr ConstTreePtrType; + typedef typename NonConstTreeType::Ptr NonConstTreePtrType; + typedef Grid<TreeType> GridType; + typedef Grid<NonConstTreeType> NonConstGridType; + typedef typename GridType::Ptr GridPtrType; + typedef typename NonConstGridType::Ptr NonConstGridPtrType; + typedef typename GridType::ConstPtr ConstGridPtrType; + typedef typename TreeType::ValueType ValueType; + typedef typename tree::ValueAccessor<TreeType> AccessorType; + typedef typename tree::ValueAccessor<const TreeType> ConstAccessorType; + typedef typename tree::ValueAccessor<NonConstTreeType> NonConstAccessorType; + + static TreeType& tree(TreeType& t) { return t; } + static TreeType& tree(GridType& g) { return g.tree(); } + static TreeType& tree(AccessorType& a) { return a.tree(); } + static const TreeType& tree(const TreeType& t) { return t; } + static const TreeType& tree(const GridType& g) { return g.tree(); } + static const TreeType& tree(const AccessorType& a) { return a.tree(); } + static const TreeType& constTree(TreeType& t) { return t; } + static const TreeType& constTree(GridType& g) { return g.constTree(); } + static const TreeType& constTree(const TreeType& t) { return t; } + static const TreeType& constTree(const GridType& g) { return g.constTree(); } +}; + //@} @@ -940,6 +1013,15 @@ inline Grid<TreeT>::Grid(const Grid& other): template<typename TreeT> +template<typename OtherTreeType> +inline Grid<TreeT>::Grid(const Grid<OtherTreeType>& other): + GridBase(other), + mTree(new TreeType(other.constTree())) +{ +} + + +template<typename TreeT> inline Grid<TreeT>::Grid(const Grid& other, ShallowCopy): GridBase(other, ShallowCopy()), mTree(other.mTree) @@ -1047,6 +1129,9 @@ Grid<TreeT>::newTree() } +//////////////////////////////////////// + + template<typename TreeT> inline void Grid<TreeT>::fill(const CoordBBox& bbox, const ValueType& value, bool active) @@ -1073,6 +1158,44 @@ Grid<TreeT>::pruneGrid(float tolerance) template<typename TreeT> inline void +Grid<TreeT>::merge(Grid& other, MergePolicy policy) +{ + tree().merge(other.tree(), policy); +} + + +template<typename TreeT> +template<typename OtherTreeType> +inline void +Grid<TreeT>::topologyUnion(const Grid<OtherTreeType>& other) +{ + tree().topologyUnion(other.tree()); +} + + +template<typename TreeT> +template<typename OtherTreeType> +inline void +Grid<TreeT>::topologyIntersection(const Grid<OtherTreeType>& other) +{ + tree().topologyIntersection(other.tree()); +} + + +template<typename TreeT> +template<typename OtherTreeType> +inline void +Grid<TreeT>::topologyDifference(const Grid<OtherTreeType>& other) +{ + tree().topologyDifference(other.tree()); +} + + +//////////////////////////////////////// + + +template<typename TreeT> +inline void Grid<TreeT>::evalMinMax(ValueType& minVal, ValueType& maxVal) const { tree().evalMinMax(minVal, maxVal); @@ -1191,7 +1314,7 @@ createGrid(TreePtrType tree) template<typename GridType> typename GridType::Ptr -createLevelSet(double voxelSize, double halfWidth) +createLevelSet(Real voxelSize, Real halfWidth) { typedef typename GridType::ValueType ValueType; diff --git a/extern/openvdb/internal/openvdb/Platform.h b/extern/openvdb/internal/openvdb/Platform.h index 052dfddb4f7..d0b72868f1b 100644 --- a/extern/openvdb/internal/openvdb/Platform.h +++ b/extern/openvdb/internal/openvdb/Platform.h @@ -55,6 +55,31 @@ #define OPENVDB_CHECK_GCC(MAJOR, MINOR) 0 #endif +/// Macro for determining if there are sufficient C++0x/C++11 features +#ifdef __INTEL_COMPILER + #ifdef __INTEL_CXX11_MODE__ + #define OPENVDB_HAS_CXX11 1 + #endif +#elif defined(__clang__) + #ifndef _LIBCPP_VERSION + #include <ciso646> + #endif + #ifdef _LIBCPP_VERSION + #define OPENVDB_HAS_CXX11 1 + #endif +#elif defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus > 199711L) + #define OPENVDB_HAS_CXX11 1 +#elif defined(_MSC_VER) + #if (_MSC_VER >= 1700) + #define OPENVDB_HAS_CXX11 1 + #endif +#endif +#if defined(__GNUC__) && !OPENVDB_CHECK_GCC(4, 4) + // ICC uses GCC's standard library headers, so even if the ICC version + // is recent enough for C++11, the GCC version might not be. + #undef OPENVDB_HAS_CXX11 +#endif + /// For compilers that need templated function specializations to have /// storage qualifiers, we need to declare the specializations as static inline. /// Otherwise, we'll get linker errors about multiply defined symbols. @@ -81,12 +106,15 @@ /// In the above, <tt>NodeType::LEVEL == 0</tt> is a compile-time constant expression, /// so for some template instantiations, the line below it is unreachable. #if defined(__INTEL_COMPILER) - // Disable ICC remark #111 ("statement is unreachable") and - // remark #185 ("dynamic initialization in unreachable code"). + // Disable ICC remarks 111 ("statement is unreachable"), 128 ("loop is not reachable"), + // 185 ("dynamic initialization in unreachable code"), and 280 ("selector expression + // is constant"). #define OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN \ _Pragma("warning (push)") \ _Pragma("warning (disable:111)") \ - _Pragma("warning (disable:185)") + _Pragma("warning (disable:128)") \ + _Pragma("warning (disable:185)") \ + _Pragma("warning (disable:280)") #define OPENVDB_NO_UNREACHABLE_CODE_WARNING_END \ _Pragma("warning (pop)") #else diff --git a/extern/openvdb/internal/openvdb/Types.h b/extern/openvdb/internal/openvdb/Types.h index 94e106b163b..117c3e50fe1 100644 --- a/extern/openvdb/internal/openvdb/Types.h +++ b/extern/openvdb/internal/openvdb/Types.h @@ -44,6 +44,7 @@ #include <openvdb/math/Mat4.h> #include <openvdb/math/Coord.h> #include <openvdb/math/Hermite.h> +#include <boost/type_traits/is_convertible.hpp> namespace openvdb { @@ -51,7 +52,6 @@ OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { // One-dimensional scalar types -typedef uint32_t Uint; typedef uint32_t Index32; typedef uint64_t Index64; typedef Index32 Index; @@ -63,34 +63,35 @@ typedef unsigned char Byte; typedef double Real; // Two-dimensional vector types -typedef math::Vec2i Vec2i; -typedef math::Vec2s Vec2s; -typedef math::Vec2d Vec2d; typedef math::Vec2<Real> Vec2R; -typedef math::Vec2<Index> Vec2I; +typedef math::Vec2<Index32> Vec2I; +typedef math::Vec2<float> Vec2f; typedef math::Vec2<half> Vec2H; +using math::Vec2i; +using math::Vec2s; +using math::Vec2d; // Three-dimensional vector types typedef math::Vec3<Real> Vec3R; typedef math::Vec3<Index32> Vec3I; -typedef math::Vec3<Int32> Vec3i; typedef math::Vec3<float> Vec3f; -typedef math::Vec3s Vec3s; -typedef math::Vec3<double> Vec3d; typedef math::Vec3<half> Vec3H; +using math::Vec3i; +using math::Vec3s; +using math::Vec3d; -typedef math::Coord Coord; -typedef math::CoordBBox CoordBBox; +using math::Coord; +using math::CoordBBox; typedef math::BBox<Vec3d> BBoxd; // Four-dimensional vector types typedef math::Vec4<Real> Vec4R; typedef math::Vec4<Index32> Vec4I; -typedef math::Vec4<Int32> Vec4i; typedef math::Vec4<float> Vec4f; -typedef math::Vec4s Vec4s; -typedef math::Vec4<double> Vec4d; typedef math::Vec4<half> Vec4H; +using math::Vec4i; +using math::Vec4s; +using math::Vec4d; // Three-dimensional matrix types typedef math::Mat3<Real> Mat3R; @@ -110,10 +111,54 @@ typedef math::Quat<Real> QuatR; //////////////////////////////////////// -template<typename T> struct VecTraits { static const bool IsVec = false; }; -template<typename T> struct VecTraits<math::Vec2<T> > { static const bool IsVec = true; }; -template<typename T> struct VecTraits<math::Vec3<T> > { static const bool IsVec = true; }; -template<typename T> struct VecTraits<math::Vec4<T> > { static const bool IsVec = true; }; +template<typename T> struct VecTraits { + static const bool IsVec = false; + static const int Size = 1; +}; +template<typename T> struct VecTraits<math::Vec2<T> > { + static const bool IsVec = true; + static const int Size = 2; +}; +template<typename T> struct VecTraits<math::Vec3<T> > { + static const bool IsVec = true; + static const int Size = 3; +}; +template<typename T> struct VecTraits<math::Vec4<T> > { + static const bool IsVec = true; + static const int Size = 4; +}; + + +//////////////////////////////////////// + + +/// @brief CanConvertType<FromType, ToType>::value is @c true if a value +/// of type @a ToType can be constructed from a value of type @a FromType. +/// +/// @note @c boost::is_convertible tests for implicit convertibility only. +/// What we want is the equivalent of C++11's @c std::is_constructible, +/// which allows for explicit conversions as well. Unfortunately, not all +/// compilers support @c std::is_constructible yet, so for now, types that +/// can only be converted explicitly have to be indicated with specializations +/// of this template. +template<typename FromType, typename ToType> +struct CanConvertType { enum { value = boost::is_convertible<FromType, ToType>::value }; }; + +// Specializations for vector types, which can be constructed from values +// of their own ValueTypes (or values that can be converted to their ValueTypes), +// but only explicitly +template<typename T> struct CanConvertType<T, math::Vec2<T> > { enum { value = true }; }; +template<typename T> struct CanConvertType<T, math::Vec3<T> > { enum { value = true }; }; +template<typename T> struct CanConvertType<T, math::Vec4<T> > { enum { value = true }; }; +template<typename T> struct CanConvertType<math::Vec2<T>, math::Vec2<T> > { enum {value = true}; }; +template<typename T> struct CanConvertType<math::Vec3<T>, math::Vec3<T> > { enum {value = true}; }; +template<typename T> struct CanConvertType<math::Vec4<T>, math::Vec4<T> > { enum {value = true}; }; +template<typename T0, typename T1> +struct CanConvertType<T0, math::Vec2<T1> > { enum { value = CanConvertType<T0, T1>::value }; }; +template<typename T0, typename T1> +struct CanConvertType<T0, math::Vec3<T1> > { enum { value = CanConvertType<T0, T1>::value }; }; +template<typename T0, typename T1> +struct CanConvertType<T0, math::Vec4<T1> > { enum { value = CanConvertType<T0, T1>::value }; }; //////////////////////////////////////// @@ -160,6 +205,26 @@ enum VecType { enum { NUM_VEC_TYPES = VEC_CONTRAVARIANT_ABSOLUTE + 1 }; +/// Specify how grids should be merged during certain (typically multithreaded) operations. +/// <dl> +/// <dt><b>MERGE_ACTIVE_STATES</b> +/// <dd>The output grid is active wherever any of the input grids is active. +/// +/// <dt><b>MERGE_NODES</b> +/// <dd>The output grid's tree has a node wherever any of the input grids' trees +/// has a node, regardless of any active states. +/// +/// <dt><b>MERGE_ACTIVE_STATES_AND_NODES</b> +/// <dd>The output grid is active wherever any of the input grids is active, +/// and its tree has a node wherever any of the input grids' trees has a node. +/// </dl> +enum MergePolicy { + MERGE_ACTIVE_STATES = 0, + MERGE_NODES, + MERGE_ACTIVE_STATES_AND_NODES +}; + + //////////////////////////////////////// @@ -187,7 +252,7 @@ template<> inline const char* typeNameAsString<Mat4d>() { return "mat4d"; /// @brief This struct collects both input and output arguments to "grid combiner" functors /// used with the tree::TypedGrid::combineExtended() and combine2Extended() methods. -/// ValueType is the value type of the two grids being combined. +/// AValueType and BValueType are the value types of the two grids being combined. /// /// @see openvdb/tree/Tree.h for usage information. /// @@ -196,11 +261,12 @@ template<> inline const char* typeNameAsString<Mat4d>() { return "mat4d"; /// CombineArgs<float> args; /// myCombineOp(args.setARef(aVal).setBRef(bVal).setAIsActive(true).setBIsActive(false)); /// @endcode -template<typename ValueType> +template<typename AValueType, typename BValueType = AValueType> class CombineArgs { public: - typedef ValueType ValueT; + typedef AValueType AValueT; + typedef BValueType BValueT; CombineArgs(): mAValPtr(NULL), mBValPtr(NULL), mResultValPtr(&mResultVal), @@ -208,37 +274,37 @@ public: {} /// Use this constructor when the result value is stored externally. - CombineArgs(const ValueType& a, const ValueType& b, ValueType& result, + CombineArgs(const AValueType& a, const BValueType& b, AValueType& result, bool aOn = false, bool bOn = false): mAValPtr(&a), mBValPtr(&b), mResultValPtr(&result), mAIsActive(aOn), mBIsActive(bOn) { updateResultActive(); } /// Use this constructor when the result value should be stored in this struct. - CombineArgs(const ValueType& a, const ValueType& b, bool aOn = false, bool bOn = false): + CombineArgs(const AValueType& a, const BValueType& b, bool aOn = false, bool bOn = false): mAValPtr(&a), mBValPtr(&b), mResultValPtr(&mResultVal), mAIsActive(aOn), mBIsActive(bOn) { updateResultActive(); } /// Get the A input value. - const ValueType& a() const { return *mAValPtr; } + const AValueType& a() const { return *mAValPtr; } /// Get the B input value. - const ValueType& b() const { return *mBValPtr; } + const BValueType& b() const { return *mBValPtr; } //@{ /// Get the output value. - const ValueType& result() const { return *mResultValPtr; } - ValueType& result() { return *mResultValPtr; } + const AValueType& result() const { return *mResultValPtr; } + AValueType& result() { return *mResultValPtr; } //@} /// Set the output value. - CombineArgs& setResult(const ValueType& val) { *mResultValPtr = val; return *this; } + CombineArgs& setResult(const AValueType& val) { *mResultValPtr = val; return *this; } /// Redirect the A value to a new external source. - CombineArgs& setARef(const ValueType& a) { mAValPtr = &a; return *this; } + CombineArgs& setARef(const AValueType& a) { mAValPtr = &a; return *this; } /// Redirect the B value to a new external source. - CombineArgs& setBRef(const ValueType& b) { mBValPtr = &b; return *this; } + CombineArgs& setBRef(const BValueType& b) { mBValPtr = &b; return *this; } /// Redirect the result value to a new external destination. - CombineArgs& setResultRef(ValueType& val) { mResultValPtr = &val; return *this; } + CombineArgs& setResultRef(AValueType& val) { mResultValPtr = &val; return *this; } /// @return true if the A value is active bool aIsActive() const { return mAIsActive; } @@ -259,12 +325,12 @@ protected: /// but this behavior can be overridden by calling setResultIsActive(). void updateResultActive() { mResultIsActive = mAIsActive || mBIsActive; } - const ValueType* mAValPtr; // pointer to input value from A grid - const ValueType* mBValPtr; // pointer to input value from B grid - ValueType mResultVal; // computed output value (unused if stored externally) - ValueType* mResultValPtr; // pointer to either mResultVal or an external value - bool mAIsActive, mBIsActive; // active states of A and B values - bool mResultIsActive; // computed active state (default: A active || B active) + const AValueType* mAValPtr; // pointer to input value from A grid + const BValueType* mBValPtr; // pointer to input value from B grid + AValueType mResultVal; // computed output value (unused if stored externally) + AValueType* mResultValPtr; // pointer to either mResultVal or an external value + bool mAIsActive, mBIsActive; // active states of A and B values + bool mResultIsActive; // computed active state (default: A active || B active) }; @@ -274,7 +340,7 @@ protected: template<typename ValueType, typename CombineOp> struct SwappedCombineOp { - SwappedCombineOp(CombineOp& op): op(op) {} + SwappedCombineOp(CombineOp& _op): op(_op) {} void operator()(CombineArgs<ValueType>& args) { diff --git a/extern/openvdb/internal/openvdb/io/Archive.cc b/extern/openvdb/internal/openvdb/io/Archive.cc index 9b178ef99f1..c05ed0f8889 100644 --- a/extern/openvdb/internal/openvdb/io/Archive.cc +++ b/extern/openvdb/internal/openvdb/io/Archive.cc @@ -54,6 +54,7 @@ struct StreamState static const long MAGIC_NUMBER; StreamState(); + ~StreamState(); int magicNumber; int fileVersion; @@ -125,6 +126,14 @@ StreamState::StreamState(): magicNumber(std::ios_base::xalloc()) } +StreamState::~StreamState() +{ + // Ensure that this StreamState struct can no longer be accessed. + std::cout.iword(magicNumber) = 0; + std::cout.pword(magicNumber) = NULL; +} + + //////////////////////////////////////// @@ -146,6 +155,13 @@ Archive::~Archive() } +boost::shared_ptr<Archive> +Archive::copy() const +{ + return boost::shared_ptr<Archive>(new Archive(*this)); +} + + //////////////////////////////////////// @@ -596,8 +612,12 @@ Archive::write(std::ostream& os, const GridCPtrVec& grids, bool seekable, for (GridCPtrVecCIter i = grids.begin(), e = grids.end(); i != e; ++i) { if (const GridBase::ConstPtr& grid = *i) { - // Ensure that the grid's descriptor has a unique grid name. + // Ensure that the grid's descriptor has a unique grid name, by appending + // a number to it if a grid with the same name was already written. + // Always add a number if the grid name is empty, so that the grid can be + // properly identified as an instance parent, if necessary. std::string name = grid->getName(); + if (name.empty()) name = GridDescriptor::addSuffix(name, 0); for (int n = 1; uniqueNames.find(name) != uniqueNames.end(); ++n) { name = GridDescriptor::addSuffix(grid->getName(), n); } diff --git a/extern/openvdb/internal/openvdb/io/Archive.h b/extern/openvdb/internal/openvdb/io/Archive.h index ecfc65b6df0..03270a79415 100644 --- a/extern/openvdb/internal/openvdb/io/Archive.h +++ b/extern/openvdb/internal/openvdb/io/Archive.h @@ -37,6 +37,7 @@ #include <string> #include <boost/uuid/uuid.hpp> #include <boost/cstdint.hpp> +#include <boost/shared_ptr.hpp> #include <openvdb/Grid.h> #include <openvdb/metadata/MetaMap.h> #include <openvdb/version.h> // for VersionId @@ -117,6 +118,9 @@ public: Archive(); virtual ~Archive(); + /// @brief Return a copy of this archive. + virtual boost::shared_ptr<Archive> copy() const; + /// @brief Return the UUID that was most recently written (or read, /// if no UUID has been written yet). std::string getUniqueTag() const; @@ -163,6 +167,9 @@ public: /// bounding box, etc.) should be computed and written as grid metadata. void setGridStatsMetadataEnabled(bool b) { mEnableGridStats = b; } + /// @brief Write the grids in the given container to this archive's output stream. + virtual void write(const GridCPtrVec&, const MetaMap& = MetaMap()) const {} + protected: /// @brief Return @c true if the input stream contains grid offsets /// that allow for random access or partial reading. @@ -198,7 +205,7 @@ protected: void setWriteGridStatsMetadata(std::ostream&); /// Read in and return the number of grids on the input stream. - static int readGridCount(std::istream&); + static int32_t readGridCount(std::istream&); /// Populate the given grid from the input stream. static void readGrid(GridBase::Ptr, const GridDescriptor&, std::istream&); @@ -242,7 +249,7 @@ private: /// The version of the library that was used to create the file that was read VersionId mLibraryVersion; /// 16-byte (128-bit) UUID - mutable boost::uuids::uuid mUuid;// needs to mutable since writeHeader is const! + mutable boost::uuids::uuid mUuid;// needs to be mutable since writeHeader is const! /// Flag indicating whether the input stream contains grid offsets /// and therefore supports partial reading bool mInputHasGridOffsets; diff --git a/extern/openvdb/internal/openvdb/io/Compression.h b/extern/openvdb/internal/openvdb/io/Compression.h index acb5b72673f..ab2d9582afe 100644 --- a/extern/openvdb/internal/openvdb/io/Compression.h +++ b/extern/openvdb/internal/openvdb/io/Compression.h @@ -39,6 +39,7 @@ #include <string> #include <vector> + namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { @@ -85,7 +86,8 @@ enum { /*2*/ NO_MASK_AND_ONE_INACTIVE_VAL, // all inactive vals have the same non-background val /*3*/ MASK_AND_NO_INACTIVE_VALS, // mask selects between -background and +background /*4*/ MASK_AND_ONE_INACTIVE_VAL, // mask selects between backgd and one other inactive val - /*5*/ MASK_AND_TWO_INACTIVE_VALS // mask selects between two non-background inactive vals + /*5*/ MASK_AND_TWO_INACTIVE_VALS, // mask selects between two non-background inactive vals + /*6*/ NO_MASK_AND_ALL_VALS // > 2 inactive vals, so no mask compression at all }; @@ -281,7 +283,7 @@ readCompressedValues(std::istream& is, ValueT* destBuf, Index destCount, zipped = compression & COMPRESS_ZIP, maskCompressed = compression & COMPRESS_ACTIVE_MASK; - int8_t metadata = NO_MASK_OR_INACTIVE_VALS; + int8_t metadata = NO_MASK_AND_ALL_VALS; if (getFormatVersion(is) >= OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION) { // Read the flag that specifies what, if any, additional metadata // (selection mask and/or inactive value(s)) is saved. @@ -294,11 +296,11 @@ readCompressedValues(std::istream& is, ValueT* destBuf, Index destCount, } ValueT inactiveVal1 = background; ValueT inactiveVal0 = - ((metadata == NO_MASK_OR_INACTIVE_VALS) ? background : negative(background)); + ((metadata == NO_MASK_OR_INACTIVE_VALS) ? background : math::negative(background)); - if (metadata != NO_MASK_OR_INACTIVE_VALS && - metadata != NO_MASK_AND_MINUS_BG && - metadata != MASK_AND_NO_INACTIVE_VALS) + if (metadata == NO_MASK_AND_ONE_INACTIVE_VAL || + metadata == MASK_AND_ONE_INACTIVE_VAL || + metadata == MASK_AND_TWO_INACTIVE_VALS) { // Read one of at most two distinct inactive values. is.read(reinterpret_cast<char*>(&inactiveVal0), sizeof(ValueT)); @@ -309,9 +311,9 @@ readCompressedValues(std::istream& is, ValueT* destBuf, Index destCount, } MaskT selectionMask; - if (metadata != NO_MASK_OR_INACTIVE_VALS && - metadata != NO_MASK_AND_MINUS_BG && - metadata != NO_MASK_AND_ONE_INACTIVE_VAL) + if (metadata == MASK_AND_NO_INACTIVE_VALS || + metadata == MASK_AND_ONE_INACTIVE_VAL || + metadata == MASK_AND_TWO_INACTIVE_VALS) { // For use in mask compression (only), read the bitmask that selects // between two distinct inactive values. @@ -322,7 +324,9 @@ readCompressedValues(std::istream& is, ValueT* destBuf, Index destCount, boost::scoped_array<ValueT> scopedTempBuf; Index tempCount = destCount; - if (maskCompressed && getFormatVersion(is) >= OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION) { + if (maskCompressed && metadata != NO_MASK_AND_ALL_VALS + && getFormatVersion(is) >= OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION) + { tempCount = valueMask.countOn(); if (tempCount != destCount) { // If this node has inactive voxels, allocate a temporary buffer @@ -394,7 +398,7 @@ writeCompressedValues(std::ostream& os, ValueT* srcBuf, Index srcCount, ValueT* tempBuf = srcBuf; boost::scoped_array<ValueT> scopedTempBuf; - int8_t metadata = NO_MASK_OR_INACTIVE_VALS; + int8_t metadata = NO_MASK_AND_ALL_VALS; if (!maskCompress) { os.write(reinterpret_cast<const char*>(&metadata), /*bytes=*/1); @@ -437,7 +441,7 @@ writeCompressedValues(std::ostream& os, ValueT* srcBuf, Index srcCount, if (numUniqueInactiveVals == 1) { if (!Local::eq(inactiveVal[0], background)) { - if (Local::eq(inactiveVal[0], negative(background))) { + if (Local::eq(inactiveVal[0], math::negative(background))) { metadata = NO_MASK_AND_MINUS_BG; } else { metadata = NO_MASK_AND_ONE_INACTIVE_VAL; @@ -451,7 +455,7 @@ writeCompressedValues(std::ostream& os, ValueT* srcBuf, Index srcCount, metadata = MASK_AND_TWO_INACTIVE_VALS; } else if (Local::eq(inactiveVal[1], background)) { - if (Local::eq(inactiveVal[0], negative(background))) { + if (Local::eq(inactiveVal[0], math::negative(background))) { // If the second inactive value is equal to the background and // the first is equal to -background, neither value needs to be saved, // but save a mask that selects between -background and +background. @@ -463,7 +467,7 @@ writeCompressedValues(std::ostream& os, ValueT* srcBuf, Index srcCount, metadata = MASK_AND_ONE_INACTIVE_VAL; } } else if (Local::eq(inactiveVal[0], background)) { - if (Local::eq(inactiveVal[1], negative(background))) { + if (Local::eq(inactiveVal[1], math::negative(background))) { // If the first inactive value is equal to the background and // the second is equal to -background, neither value needs to be saved, // but save a mask that selects between -background and +background. @@ -477,13 +481,15 @@ writeCompressedValues(std::ostream& os, ValueT* srcBuf, Index srcCount, metadata = MASK_AND_ONE_INACTIVE_VAL; } } + } else if (numUniqueInactiveVals > 2) { + metadata = NO_MASK_AND_ALL_VALS; } os.write(reinterpret_cast<const char*>(&metadata), /*bytes=*/1); - if (metadata != NO_MASK_OR_INACTIVE_VALS && - metadata != NO_MASK_AND_MINUS_BG && - metadata != MASK_AND_NO_INACTIVE_VALS) + if (metadata == NO_MASK_AND_ONE_INACTIVE_VAL || + metadata == MASK_AND_ONE_INACTIVE_VAL || + metadata == MASK_AND_TWO_INACTIVE_VALS) { if (!toHalf) { // Write one of at most two distinct inactive values. @@ -504,7 +510,7 @@ writeCompressedValues(std::ostream& os, ValueT* srcBuf, Index srcCount, } } - if (metadata == NO_MASK_OR_INACTIVE_VALS && numUniqueInactiveVals > 2) { + if (metadata == NO_MASK_AND_ALL_VALS) { // If there are more than two unique inactive values, the entire input buffer // needs to be saved (both active and inactive values). /// @todo Save the selection mask as long as most of the inactive values diff --git a/extern/openvdb/internal/openvdb/io/File.cc b/extern/openvdb/internal/openvdb/io/File.cc index 5ca4c76ad3b..cb071560528 100644 --- a/extern/openvdb/internal/openvdb/io/File.cc +++ b/extern/openvdb/internal/openvdb/io/File.cc @@ -57,6 +57,41 @@ File::~File() } +File::File(const File& other) + : Archive(other) + , mFilename(other.mFilename) + , mMeta(other.mMeta) + , mIsOpen(false) // don't want two file objects reading from the same stream + , mGridDescriptors(other.mGridDescriptors) + , mNamedGrids(other.mNamedGrids) + , mGrids(other.mGrids) +{ +} + + +File& +File::operator=(const File& other) +{ + if (&other != this) { + Archive::operator=(other); + mFilename = other.mFilename; + mMeta = other.mMeta; + mIsOpen = false; // don't want two file objects reading from the same stream + mGridDescriptors = other.mGridDescriptors; + mNamedGrids = other.mNamedGrids; + mGrids = other.mGrids; + } + return *this; +} + + +boost::shared_ptr<Archive> +File::copy() const +{ + return boost::shared_ptr<Archive>(new File(*this)); +} + + //////////////////////////////////////// diff --git a/extern/openvdb/internal/openvdb/io/File.h b/extern/openvdb/internal/openvdb/io/File.h index a00fd0d7858..e1bb60b6704 100644 --- a/extern/openvdb/internal/openvdb/io/File.h +++ b/extern/openvdb/internal/openvdb/io/File.h @@ -57,8 +57,24 @@ public: typedef NameMap::const_iterator NameMapCIter; explicit File(const std::string& filename); - ~File(); + virtual ~File(); + /// @brief Copy constructor + /// @details The copy will be closed and will not reference the same + /// file descriptor as the original. + File(const File& other); + /// @brief Assignment + /// @details After assignment, this File will be closed and will not + /// reference the same file descriptor as the source File. + File& operator=(const File& other); + + /// @brief Return a copy of this archive. + /// @details The copy will be closed and will not reference the same + /// file descriptor as the original. + virtual boost::shared_ptr<Archive> copy() const; + + /// @brief Return the name of the file with which this archive is associated. + /// @details The file does not necessarily exist on disk yet. const std::string& filename() const { return mFilename; } /// Open the file, read the file header and the file-level metadata, and @@ -67,7 +83,7 @@ public: /// @return @c true if the file's UUID has changed since it was last read. bool open(); - /// Return @c true if the file has been opened for reading, false otherwise. + /// Return @c true if the file has been opened for reading. bool isOpen() const { return mIsOpen; } /// Close the file once we are done reading from it. @@ -109,6 +125,10 @@ public: /// @brief Write the grids in the given container to the file whose name /// was given in the constructor. + virtual void write(const GridCPtrVec&, const MetaMap& = MetaMap()) const; + + /// @brief Write the grids in the given container to the file whose name + /// was given in the constructor. template<typename GridPtrContainerT> void write(const GridPtrContainerT&, const MetaMap& = MetaMap()) const; @@ -165,10 +185,6 @@ private: void writeGrids(const GridCPtrVec&, const MetaMap&) const; - // Disallow copying of instances of this class. - File(const File& other); - File& operator=(const File& other); - friend class ::TestFile; friend class ::TestStream; @@ -193,19 +209,19 @@ private: //////////////////////////////////////// -template<typename GridPtrContainerT> inline void -File::write(const GridPtrContainerT& container, const MetaMap& metadata) const +File::write(const GridCPtrVec& grids, const MetaMap& metadata) const { - GridCPtrVec grids; - std::copy(container.begin(), container.end(), std::back_inserter(grids)); this->writeGrids(grids, metadata); } -template<> + +template<typename GridPtrContainerT> inline void -File::write<GridCPtrVec>(const GridCPtrVec& grids, const MetaMap& metadata) const +File::write(const GridPtrContainerT& container, const MetaMap& metadata) const { + GridCPtrVec grids; + std::copy(container.begin(), container.end(), std::back_inserter(grids)); this->writeGrids(grids, metadata); } diff --git a/extern/openvdb/internal/openvdb/io/GridDescriptor.cc b/extern/openvdb/internal/openvdb/io/GridDescriptor.cc index e0dcbcabbe5..93d87a1a756 100644 --- a/extern/openvdb/internal/openvdb/io/GridDescriptor.cc +++ b/extern/openvdb/internal/openvdb/io/GridDescriptor.cc @@ -211,8 +211,9 @@ GridDescriptor::stringAsUniqueName(const std::string& s) std::string::size_type pos = ret.find("["); // Replace "[N]" with SEP "N". if (pos != std::string::npos) { - if (ret.substr(pos) == "[0]") { - // "name[0]" is equivalent to "name". + if (pos != 0 && ret.substr(pos) == "[0]") { + // "name[0]" is equivalent to "name", except in the case of + // the empty name "[0]". ret.erase(pos); } else { ret.resize(ret.size() - 1); // drop trailing ']' diff --git a/extern/openvdb/internal/openvdb/io/Queue.cc b/extern/openvdb/internal/openvdb/io/Queue.cc new file mode 100644 index 00000000000..6d76cd66d6d --- /dev/null +++ b/extern/openvdb/internal/openvdb/io/Queue.cc @@ -0,0 +1,337 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +// +// Redistributions of source code must retain the above copyright +// and license notice and the following restrictions and disclaimer. +// +// * Neither the name of DreamWorks Animation nor the names of +// its contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE +// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. +// +/////////////////////////////////////////////////////////////////////////// +// +/// @file Queue.cc +/// @author Peter Cucka + +#include "Queue.h" + +#include "File.h" +#include "Stream.h" +#include <openvdb/Exceptions.h> +#include <openvdb/util/logging.h> +#include <boost/bind.hpp> +#include <tbb/atomic.h> +#include <tbb/concurrent_hash_map.h> +#include <tbb/mutex.h> +#include <tbb/task.h> +#include <tbb/tbb_thread.h> // for tbb::this_tbb_thread::sleep() +#include <tbb/tick_count.h> +#include <algorithm> // for std::max() +#include <iostream> +#include <map> + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +namespace { + +typedef tbb::mutex Mutex; +typedef Mutex::scoped_lock Lock; + + +// Abstract base class for queuable TBB tasks that adds a task completion callback +class Task: public tbb::task +{ +public: + Task(Queue::Id id): mId(id) {} + virtual ~Task() {} + + Queue::Id id() const { return mId; } + + void setNotifier(Queue::Notifier& notifier) { mNotify = notifier; } + +protected: + void notify(Queue::Status status) { if (mNotify) mNotify(this->id(), status); } + +private: + Queue::Id mId; + Queue::Notifier mNotify; +}; + + +// Queuable TBB task that writes one or more grids to a .vdb file or an output stream +class OutputTask: public Task +{ +public: + OutputTask(Queue::Id id, const GridCPtrVec& grids, const Archive& archive, + const MetaMap& metadata) + : Task(id) + , mGrids(grids) + , mArchive(archive.copy()) + , mMetadata(metadata) + {} + + virtual tbb::task* execute() + { + Queue::Status status = Queue::FAILED; + try { + mArchive->write(mGrids, mMetadata); + status = Queue::SUCCEEDED; + } catch (std::exception& e) { + if (const char* msg = e.what()) { + OPENVDB_LOG_ERROR(msg); + } + } catch (...) { + } + this->notify(status); + return NULL; // no successor to this task + } + +private: + GridCPtrVec mGrids; + boost::shared_ptr<Archive> mArchive; + MetaMap mMetadata; +}; + +} // unnamed namespace + + +//////////////////////////////////////// + + +// Private implementation details of a Queue +struct Queue::Impl +{ + typedef std::map<Queue::Id, Queue::Notifier> NotifierMap; + /// @todo Provide more information than just "succeeded" or "failed"? + typedef tbb::concurrent_hash_map<Queue::Id, Queue::Status> StatusMap; + + + Impl() + : mTimeout(Queue::DEFAULT_TIMEOUT) + , mCapacity(Queue::DEFAULT_CAPACITY) + , mNextId(1) + , mNextNotifierId(1) + { + mNumTasks = 0; // note: must explicitly zero-initialize atomics + } + ~Impl() {} + + // Disallow copying of instances of this class. + Impl(const Impl&); + Impl& operator=(const Impl&); + + // This method might be called from multiple threads. + void setStatus(Queue::Id id, Queue::Status status) + { + StatusMap::accessor acc; + mStatus.insert(acc, id); + acc->second = status; + } + + // This method might be called from multiple threads. + void setStatusWithNotification(Queue::Id id, Queue::Status status) + { + const bool completed = (status == SUCCEEDED || status == FAILED); + + // Update the task's entry in the status map with the new status. + this->setStatus(id, status); + + // If the client registered any callbacks, call them now. + bool didNotify = false; + { + // tbb::concurrent_hash_map does not support concurrent iteration + // (i.e., iteration concurrent with insertion or deletion), + // so we use a mutex-protected STL map instead. But if a callback + // invokes a notifier method such as removeNotifier() on this queue, + // the result will be a deadlock. + /// @todo Is it worth trying to avoid such deadlocks? + Lock lock(mNotifierMutex); + if (!mNotifiers.empty()) { + didNotify = true; + for (NotifierMap::const_iterator it = mNotifiers.begin(); + it != mNotifiers.end(); ++it) + { + it->second(id, status); + } + } + } + // If the task completed and callbacks were called, remove + // the task's entry from the status map. + if (completed) { + if (didNotify) { + StatusMap::accessor acc; + if (mStatus.find(acc, id)) { + mStatus.erase(acc); + } + } + --mNumTasks; + } + } + + bool canEnqueue() const { return mNumTasks < Int64(mCapacity); } + + void enqueue(Task& task) + { + tbb::tick_count start = tbb::tick_count::now(); + while (!canEnqueue()) { + tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(0.5/*sec*/)); + if ((tbb::tick_count::now() - start).seconds() > double(mTimeout)) { + OPENVDB_THROW(RuntimeError, + "unable to queue I/O task; " << mTimeout << "-second time limit expired"); + } + } + Queue::Notifier notify = boost::bind(&Impl::setStatusWithNotification, this, _1, _2); + task.setNotifier(notify); + this->setStatus(task.id(), Queue::PENDING); + tbb::task::enqueue(task); + ++mNumTasks; + } + + Index32 mTimeout; + Index32 mCapacity; + tbb::atomic<Int32> mNumTasks; + Index32 mNextId; + StatusMap mStatus; + NotifierMap mNotifiers; + Index32 mNextNotifierId; + Mutex mNotifierMutex; +}; + + +//////////////////////////////////////// + + +Queue::Queue(Index32 capacity): mImpl(new Impl) +{ + mImpl->mCapacity = capacity; +} + + +Queue::~Queue() +{ + // Wait for all queued tasks to complete (successfully or unsuccessfully). + /// @todo Allow the queue to be destroyed while there are uncompleted tasks + /// (e.g., by keeping a static registry of queues that also dispatches + /// or blocks notifications)? + while (mImpl->mNumTasks > 0) { + tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(0.5/*sec*/)); + } +} + + +//////////////////////////////////////// + + +bool Queue::empty() const { return (mImpl->mNumTasks == 0); } +Index32 Queue::size() const { return Index32(std::max<Int32>(0, mImpl->mNumTasks)); } +Index32 Queue::capacity() const { return mImpl->mCapacity; } +void Queue::setCapacity(Index32 n) { mImpl->mCapacity = std::max<Index32>(1, n); } + +/// @todo void Queue::setCapacity(Index64 bytes); + +/// @todo Provide a way to limit the number of tasks in flight +/// (e.g., by enqueueing tbb::tasks that pop Tasks off a concurrent_queue)? + +/// @todo Remove any tasks from the queue that are not currently executing. +//void clear() const; + +Index32 Queue::timeout() const { return mImpl->mTimeout; } +void Queue::setTimeout(Index32 sec) { mImpl->mTimeout = sec; } + + +//////////////////////////////////////// + + +Queue::Status +Queue::status(Id id) const +{ + Impl::StatusMap::const_accessor acc; + if (mImpl->mStatus.find(acc, id)) { + const Status status = acc->second; + if (status == SUCCEEDED || status == FAILED) { + mImpl->mStatus.erase(acc); + } + return status; + } + return UNKNOWN; +} + + +Queue::Id +Queue::addNotifier(Notifier notify) +{ + Lock lock(mImpl->mNotifierMutex); + Queue::Id id = mImpl->mNextNotifierId++; + mImpl->mNotifiers[id] = notify; + return id; +} + + +void +Queue::removeNotifier(Id id) +{ + Lock lock(mImpl->mNotifierMutex); + Impl::NotifierMap::iterator it = mImpl->mNotifiers.find(id); + if (it != mImpl->mNotifiers.end()) { + mImpl->mNotifiers.erase(it); + } +} + + +void +Queue::clearNotifiers() +{ + Lock lock(mImpl->mNotifierMutex); + mImpl->mNotifiers.clear(); +} + + +//////////////////////////////////////// + + +Queue::Id +Queue::writeGrid(GridBase::ConstPtr grid, const Archive& archive, const MetaMap& metadata) +{ + return writeGridVec(GridCPtrVec(1, grid), archive, metadata); +} + + +Queue::Id +Queue::writeGridVec(const GridCPtrVec& grids, const Archive& archive, const MetaMap& metadata) +{ + // From the "GUI Thread" chapter in the TBB Design Patterns guide + OutputTask* task = + new(tbb::task::allocate_root()) OutputTask(mImpl->mNextId++, grids, archive, metadata); + mImpl->enqueue(*task); + return task->id(); +} + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/extern/openvdb/internal/openvdb/io/Queue.h b/extern/openvdb/internal/openvdb/io/Queue.h new file mode 100644 index 00000000000..ac91feaa3e6 --- /dev/null +++ b/extern/openvdb/internal/openvdb/io/Queue.h @@ -0,0 +1,277 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +// +// Redistributions of source code must retain the above copyright +// and license notice and the following restrictions and disclaimer. +// +// * Neither the name of DreamWorks Animation nor the names of +// its contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE +// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. +// +/////////////////////////////////////////////////////////////////////////// +// +/// @file Queue.h +/// @author Peter Cucka + +#ifndef OPENVDB_IO_QUEUE_HAS_BEEN_INCLUDED +#define OPENVDB_IO_QUEUE_HAS_BEEN_INCLUDED + +#include <openvdb/Types.h> +#include <openvdb/Grid.h> +#include <boost/function.hpp> +#include <boost/shared_ptr.hpp> +#include <algorithm> // for std::copy +#include <iterator> // for std::back_inserter + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +class Archive; + +/// @brief Queue for asynchronous output of grids to files or streams +/// +/// @warning The queue holds shared pointers to grids. It is not safe +/// to modify a grid that has been placed in the queue. Instead, +/// make a deep copy of the grid (Grid::deepCopy()). +/// +/// @par Example: +/// @code +/// #include <boost/bind.hpp> +/// #include <tbb/concurrent_hash_map.h> +/// #include <openvdb/openvdb.h> +/// #include <openvdb/io/Queue.h> +/// +/// using openvdb::io::Queue; +/// +/// struct MyNotifier +/// { +/// // Use a concurrent container, because queue callback functions +/// // must be thread-safe. +/// typedef tbb::concurrent_hash_map<Queue::Id, std::string> FilenameMap; +/// FilenameMap filenames; +/// +/// // Callback function that prints the status of a completed task. +/// void callback(Queue::Id id, Queue::Status status) +/// { +/// const bool ok = (status == Queue::SUCCEEDED); +/// FilenameMap::accessor acc; +/// if (filenames.find(acc, id)) { +/// std::cout << (ok ? "wrote " : "failed to write ") +/// << acc->second << std::endl; +/// filenames.erase(acc); +/// } +/// } +/// }; +/// +/// int main() +/// { +/// // Construct an object to receive notifications from the queue. +/// // The object's lifetime must exceed the queue's. +/// MyNotifier notifier; +/// +/// Queue queue; +/// +/// // Register the callback() method of the MyNotifier object +/// // to receive notifications of completed tasks. +/// queue.addNotifier(boost::bind(&MyNotifier::callback, ¬ifier, _1, _2)); +/// +/// // Queue grids for output (e.g., for each step of a simulation). +/// for (int step = 1; step <= 10; ++step) { +/// openvdb::FloatGrid::Ptr grid = ...; +/// +/// std::ostringstream os; +/// os << "mygrid." << step << ".vdb"; +/// const std::string filename = os.str(); +/// +/// Queue::Id id = queue.writeGrid(grid, openvdb::io::File(filename)); +/// +/// // Associate the filename with the ID of the queued task. +/// MyNotifier::FilenameMap::accessor acc; +/// notifier.filenames.insert(acc, id); +/// acc->second = filename; +/// } +/// } +/// @endcode +/// Output: +/// @code +/// wrote mygrid.1.vdb +/// wrote mygrid.2.vdb +/// wrote mygrid.4.vdb +/// wrote mygrid.3.vdb +/// ... +/// wrote mygrid.10.vdb +/// @endcode +/// Note that tasks do not necessarily complete in the order in which they were queued. +class OPENVDB_API Queue +{ +public: + /// Default maximum queue length (see setCapacity()) + static const Index32 DEFAULT_CAPACITY = 100; + /// @brief Default maximum time in seconds to wait to queue a task + /// when the queue is full (see setTimeout()) + static const Index32 DEFAULT_TIMEOUT = 120; // seconds + + /// ID number of a queued task or of a registered notification callback + typedef Index32 Id; + + /// Status of a queued task + enum Status { UNKNOWN, PENDING, SUCCEEDED, FAILED }; + + + /// Construct a queue with the given capacity. + explicit Queue(Index32 capacity = DEFAULT_CAPACITY); + /// Block until all queued tasks complete (successfully or unsuccessfully). + ~Queue(); + + /// @brief Return @c true if the queue is empty. + bool empty() const; + /// @brief Return the number of tasks currently in the queue. + Index32 size() const; + + /// @brief Return the maximum number of tasks allowed in the queue. + /// @details Once the queue has reached its maximum size, adding + /// a new task will block until an existing task has executed. + Index32 capacity() const; + /// Set the maximum number of tasks allowed in the queue. + void setCapacity(Index32); + + /// Return the maximum number of seconds to wait to queue a task when the queue is full. + Index32 timeout() const; + /// Set the maximum number of seconds to wait to queue a task when the queue is full. + void setTimeout(Index32 seconds = DEFAULT_TIMEOUT); + + /// @brief Return the status of the task with the given ID. + /// @note Querying the status of a task that has already completed + /// (whether successfully or not) removes the task from the status registry. + /// Subsequent queries of its status will return UNKNOWN. + Status status(Id) const; + + typedef boost::function<void (Id, Status)> Notifier; + /// @brief Register a function that will be called with a task's ID + /// and status when that task completes, whether successfully or not. + /// @return an ID that can be passed to removeNotifier() to deregister the function + /// @details When multiple notifiers are registered, they are called + /// in the order in which they were registered. + /// @warning Notifiers are called from worker threads, so they must be thread-safe + /// and their lifetimes must exceed that of the queue. They must also not call, + /// directly or indirectly, addNotifier(), removeNotifier() or clearNotifiers(), + /// as that can result in a deadlock. + Id addNotifier(Notifier); + /// Deregister the notifier with the given ID. + void removeNotifier(Id); + /// Deregister all notifiers. + void clearNotifiers(); + + /// @brief Queue a single grid for output to a file or stream. + /// @param grid the grid to be serialized + /// @param archive the io::File or io::Stream to which to output the grid + /// @param fileMetadata optional file-level metadata + /// @return an ID with which the status of the queued task can be queried + /// @throw RuntimeError if the task cannot be queued within the time limit + /// (see setTimeout()) because the queue is full + /// @par Example: + /// @code + /// openvdb::FloatGrid::Ptr grid = ...; + /// + /// openvdb::io::Queue queue; + /// + /// // Write the grid to the file mygrid.vdb. + /// queue.writeGrid(grid, openvdb::io::File("mygrid.vdb")); + /// + /// // Stream the grid to a binary string. + /// std::ostringstream ostr(std::ios_base::binary); + /// queue.writeGrid(grid, openvdb::io::Stream(ostr)); + /// @endcode + Id writeGrid(GridBase::ConstPtr grid, const Archive& archive, + const MetaMap& fileMetadata = MetaMap()); + + /// @brief Queue a container of grids for output to a file. + /// @param grids any iterable container of grid pointers + /// (e.g., a GridPtrVec or GridPtrSet) + /// @param archive the io::File or io::Stream to which to output the grids + /// @param fileMetadata optional file-level metadata + /// @return an ID with which the status of the queued task can be queried + /// @throw RuntimeError if the task cannot be queued within the time limit + /// (see setTimeout()) because the queue is full + /// @par Example: + /// @code + /// openvdb::FloatGrid::Ptr floatGrid = ...; + /// openvdb::BoolGrid::Ptr boolGrid = ...; + /// openvdb::GridPtrVec grids; + /// grids.push_back(floatGrid); + /// grids.push_back(boolGrid); + /// + /// openvdb::io::Queue queue; + /// + /// // Write the grids to the file mygrid.vdb. + /// queue.write(grids, openvdb::io::File("mygrid.vdb")); + /// + /// // Stream the grids to a (binary) string. + /// std::ostringstream ostr(std::ios_base::binary); + /// queue.write(grids, openvdb::io::Stream(ostr)); + /// @endcode + template<typename GridPtrContainer> + Id write(const GridPtrContainer& grids, const Archive& archive, + const MetaMap& fileMetadata = MetaMap()); + +private: + // Disallow copying of instances of this class. + Queue(const Queue&); + Queue& operator=(const Queue&); + + Id writeGridVec(const GridCPtrVec&, const Archive&, const MetaMap&); + + class Impl; + boost::shared_ptr<Impl> mImpl; +}; // class Queue + + +template<typename GridPtrContainer> +inline Queue::Id +Queue::write(const GridPtrContainer& container, + const Archive& archive, const MetaMap& metadata) +{ + GridCPtrVec grids; + std::copy(container.begin(), container.end(), std::back_inserter(grids)); + return this->writeGridVec(grids, archive, metadata); +} + +// Specialization for vectors of const Grid pointers; no copying necessary +template<> +inline Queue::Id +Queue::write<GridCPtrVec>(const GridCPtrVec& grids, + const Archive& archive, const MetaMap& metadata) +{ + return this->writeGridVec(grids, archive, metadata); +} + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_IO_QUEUE_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/extern/openvdb/internal/openvdb/io/Stream.cc b/extern/openvdb/internal/openvdb/io/Stream.cc index 70bf486cee7..eb28cda631c 100644 --- a/extern/openvdb/internal/openvdb/io/Stream.cc +++ b/extern/openvdb/internal/openvdb/io/Stream.cc @@ -42,7 +42,7 @@ OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { namespace io { -Stream::Stream(std::istream& is) +Stream::Stream(std::istream& is): mOutputStream(NULL) { if (!is) return; @@ -81,7 +81,12 @@ Stream::Stream(std::istream& is) } -Stream::Stream() +Stream::Stream(): mOutputStream(NULL) +{ +} + + +Stream::Stream(std::ostream& os): mOutputStream(&os) { } @@ -91,6 +96,13 @@ Stream::~Stream() } +boost::shared_ptr<Archive> +Stream::copy() const +{ + return boost::shared_ptr<Archive>(new Stream(*this)); +} + + //////////////////////////////////////// diff --git a/extern/openvdb/internal/openvdb/io/Stream.h b/extern/openvdb/internal/openvdb/io/Stream.h index cd57779b245..c786e463d5d 100644 --- a/extern/openvdb/internal/openvdb/io/Stream.h +++ b/extern/openvdb/internal/openvdb/io/Stream.h @@ -49,8 +49,16 @@ class OPENVDB_API Stream: public Archive public: /// Read grids from an input stream. explicit Stream(std::istream&); + + /// Construct an archive for stream output. Stream(); - ~Stream(); + /// Construct an archive for output to the given stream. + explicit Stream(std::ostream&); + + virtual ~Stream(); + + /// @brief Return a copy of this archive. + virtual boost::shared_ptr<Archive> copy() const; /// Return the file-level metadata in a newly created MetaMap. MetaMap::Ptr getMetadata() const; @@ -58,9 +66,20 @@ public: /// Return pointers to the grids that were read from the input stream. GridPtrVecPtr getGrids() { return mGrids; } - /// Write the grids in the given container to an output stream. + /// @brief Write the grids in the given container to this archive's output stream. + /// @throw ValueError if this archive was constructed without specifying an output stream. + virtual void write(const GridCPtrVec&, const MetaMap& = MetaMap()) const; + + /// @brief Write the grids in the given container to this archive's output stream. + /// @throw ValueError if this archive was constructed without specifying an output stream. + template<typename GridPtrContainerT> + void write(const GridPtrContainerT&, const MetaMap& = MetaMap()) const; + + /// @brief Write the grids in the given container to an output stream. + /// @deprecated Use Stream(os).write(grids) instead. template<typename GridPtrContainerT> - void write(std::ostream&, const GridPtrContainerT&, const MetaMap& = MetaMap()) const; + OPENVDB_DEPRECATED void write(std::ostream&, + const GridPtrContainerT&, const MetaMap& = MetaMap()) const; private: /// Create a new grid of the type specified by the given descriptor, @@ -70,19 +89,39 @@ private: void writeGrids(std::ostream&, const GridCPtrVec&, const MetaMap&) const; - // Disallow copying of instances of this class. - Stream(const Stream&); - Stream& operator=(const Stream&); - MetaMap::Ptr mMeta; GridPtrVecPtr mGrids; + std::ostream* mOutputStream; }; //////////////////////////////////////// +inline void +Stream::write(const GridCPtrVec& grids, const MetaMap& metadata) const +{ + if (mOutputStream == NULL) { + OPENVDB_THROW(ValueError, "no output stream was specified"); + } + this->writeGrids(*mOutputStream, grids, metadata); +} + + +template<typename GridPtrContainerT> +inline void +Stream::write(const GridPtrContainerT& container, const MetaMap& metadata) const +{ + if (mOutputStream == NULL) { + OPENVDB_THROW(ValueError, "no output stream was specified"); + } + GridCPtrVec grids; + std::copy(container.begin(), container.end(), std::back_inserter(grids)); + this->writeGrids(*mOutputStream, grids, metadata); +} + + template<typename GridPtrContainerT> inline void Stream::write(std::ostream& os, const GridPtrContainerT& container, diff --git a/extern/openvdb/internal/openvdb/math/BBox.h b/extern/openvdb/internal/openvdb/math/BBox.h index 0af43d5d732..b3a32a3a48f 100644 --- a/extern/openvdb/internal/openvdb/math/BBox.h +++ b/extern/openvdb/internal/openvdb/math/BBox.h @@ -31,7 +31,7 @@ #ifndef OPENVDB_MATH_BBOX_HAS_BEEN_INCLUDED #define OPENVDB_MATH_BBOX_HAS_BEEN_INCLUDED -#include "Math.h" // for isApproxEqual() and tolerance() +#include "Math.h" // for math::isApproxEqual() and math::Tolerance() #include "Vec3.h" #include <ostream> #include <algorithm> // for min/max @@ -43,53 +43,93 @@ namespace OPENVDB_VERSION_NAME { namespace math { /// @brief Axis-aligned bounding box -template<class _VectorType> +template<typename Vec3T> class BBox { public: - typedef _VectorType VectorType; - typedef _VectorType ValueType; - typedef typename _VectorType::ValueType ElementType; + typedef Vec3T Vec3Type; + typedef Vec3T ValueType; + typedef Vec3T VectorType; + typedef typename Vec3Type::ValueType ElementType; + /// @brief Default constructor creates an invalid BBox BBox(); - BBox(const VectorType& xyzMin, const VectorType& xyzMax); - BBox(const VectorType& xyzMin, const VectorType& xyzMax, bool sorted); + + /// @brief Constructor based on a minimum and maximum point. + BBox(const Vec3T& xyzMin, const Vec3T& xyzMax); + + /// @brief Constructor based on a minimum and maximum point. + /// If sorted is false the points will be sorted by x,y,z component. + BBox(const Vec3T& xyzMin, const Vec3T& xyzMax, bool sorted); + /// @brief Contruct a cubical BBox from a minimum coordinate and a /// single edge length. /// @note inclusive for integral <tt>ElementType</tt>s - BBox(const VectorType& xyzMin, const ElementType& length); + BBox(const Vec3T& xyzMin, const ElementType& length); + + /// @brief Constructor based on a raw array of six points. If + /// sorted is false the points will be sorted by x,y,z component. explicit BBox(const ElementType* xyz, bool sorted = true); + + /// @brief Copy constructor BBox(const BBox& other); + /// @brief Sort the min/max by x,y,z component. void sort(); - const VectorType& min() const { return mMin; } - const VectorType& max() const { return mMax; } + /// @brief Return a const reference to the minimum point of the BBox + const Vec3T& min() const { return mMin; } + + /// @brief Return a const reference to the maximum point of the BBox + const Vec3T& max() const { return mMax; } + + /// @brief Return a non-const reference to the minimum point of the BBox + Vec3T& min() { return mMin; } - VectorType& min() { return mMin; } - VectorType& max() { return mMax; } + /// @brief Return a non-const reference to the maximum point of the BBox + Vec3T& max() { return mMax; } + /// @brief Return true if the two BBox'es are identical bool operator==(const BBox& rhs) const; + + /// @brief Return true if the two BBox'es are not identical bool operator!=(const BBox& rhs) const { return !(*this == rhs); } + /// @brief Return true if the BBox is empty, i.e. has no + /// (positive) volume. bool empty() const; - bool hasVolume() const { return !empty(); } - operator bool() const { return !empty(); } + /// @brief Return true if the BBox has a (positive) volume. + bool hasVolume() const { return !this->empty(); } + + /// @brief Return true if the BBox is valid, i.e. as a (positive) volume. + operator bool() const { return !this->empty(); } + + /// @brief Return true if the all components of mMin <= mMax, + /// i.e. the volume is not negative. + /// @note For floating point values a tolerance is used for this test. bool isSorted() const; + /// @brief Return the center point of the BBox Vec3d getCenter() const; + /// @brief Returns the extents of the BBox, i.e. the length per axis + /// for floating points values or number of grids per axis points + /// integral values. /// @note inclusive for integral <tt>ElementType</tt>s - VectorType extents() const; + Vec3T extents() const; - ElementType volume() const { VectorType e = extents(); return e[0] * e[1] * e[2]; } + /// @brief Return the volume spanned by this BBox. + ElementType volume() const { Vec3T e = this->extents(); return e[0] * e[1] * e[2]; } /// Return the index (0, 1 or 2) of the longest axis. - size_t maxExtent() const; + size_t maxExtent() const { return MaxIndex(mMax - mMin); } + + /// Return the index (0, 1 or 2) of the shortest axis. + size_t minExtent() const { return MinIndex(mMax - mMin); } /// Return @c true if point (x, y, z) is inside this bounding box. - bool isInside(const VectorType& xyz) const; + bool isInside(const Vec3T& xyz) const; /// Return @c true if the given bounding box is inside this bounding box. bool isInside(const BBox&) const; @@ -99,56 +139,68 @@ public: /// Pad this bounding box. void expand(ElementType padding); + /// Expand this bounding box to enclose point (x, y, z). - void expand(const VectorType& xyz); + void expand(const Vec3T& xyz); + /// Union this bounding box with the given bounding box. void expand(const BBox&); // @brief Union this bbox with the cubical bbox defined from xyzMin and // length /// @note inclusive for integral <tt>ElementType</tt>s - void expand(const VectorType& xyzMin, const ElementType& length); + void expand(const Vec3T& xyzMin, const ElementType& length); + /// Translate this bounding box by \f$(t_x, t_y, t_z)\f$. - void translate(const VectorType& t); + void translate(const Vec3T& t); + + /// Apply a map to this bounding box + template<typename MapType> + BBox applyMap(const MapType& map) const; + + /// Apply the inverse of a map to this bounding box + template<typename MapType> + BBox applyInverseMap(const MapType& map) const; /// Unserialize this bounding box from the given stream. void read(std::istream& is) { mMin.read(is); mMax.read(is); } + /// Serialize this bounding box to the given stream. void write(std::ostream& os) const { mMin.write(os); mMax.write(os); } private: - VectorType mMin, mMax; + Vec3T mMin, mMax; }; // class BBox //////////////////////////////////////// -template<class VectorType> +template<typename Vec3T> inline -BBox<VectorType>::BBox(): - mMin(ElementType(0), ElementType(0), ElementType(0)), - mMax(ElementType(0), ElementType(0), ElementType(0)) +BBox<Vec3T>::BBox(): + mMin( std::numeric_limits<ElementType>::max()), + mMax(-std::numeric_limits<ElementType>::max()) { } -template<class VectorType> +template<typename Vec3T> inline -BBox<VectorType>::BBox(const VectorType& xyzMin, const VectorType& xyzMax): +BBox<Vec3T>::BBox(const Vec3T& xyzMin, const Vec3T& xyzMax): mMin(xyzMin), mMax(xyzMax) { } -template<class VectorType> +template<typename Vec3T> inline -BBox<VectorType>::BBox(const VectorType& xyzMin, const VectorType& xyzMax, bool sorted): +BBox<Vec3T>::BBox(const Vec3T& xyzMin, const Vec3T& xyzMax, bool sorted): mMin(xyzMin), mMax(xyzMax) { if (!sorted) this->sort(); } -template<class VectorType> +template<typename Vec3T> inline -BBox<VectorType>::BBox(const VectorType& xyzMin, const ElementType& length): +BBox<Vec3T>::BBox(const Vec3T& xyzMin, const ElementType& length): mMin(xyzMin), mMax(xyzMin) { // min and max are inclusive for integral ElementType @@ -158,9 +210,9 @@ BBox<VectorType>::BBox(const VectorType& xyzMin, const ElementType& length): mMax[2] += size; } -template<class VectorType> +template<typename Vec3T> inline -BBox<VectorType>::BBox(const ElementType* xyz, bool sorted): +BBox<Vec3T>::BBox(const ElementType* xyz, bool sorted): mMin(xyz[0], xyz[1], xyz[2]), mMax(xyz[3], xyz[4], xyz[5]) { @@ -168,9 +220,9 @@ BBox<VectorType>::BBox(const ElementType* xyz, bool sorted): } -template<class VectorType> +template<typename Vec3T> inline -BBox<VectorType>::BBox(const BBox& other): +BBox<Vec3T>::BBox(const BBox& other): mMin(other.mMin), mMax(other.mMax) { } @@ -179,9 +231,9 @@ BBox<VectorType>::BBox(const BBox& other): //////////////////////////////////////// -template<class VectorType> +template<typename Vec3T> inline bool -BBox<VectorType>::empty() const +BBox<Vec3T>::empty() const { if (boost::is_integral<ElementType>::value) { // min and max are inclusive for integral ElementType @@ -191,9 +243,9 @@ BBox<VectorType>::empty() const } -template<class VectorType> +template<typename Vec3T> inline bool -BBox<VectorType>::operator==(const BBox& rhs) const +BBox<Vec3T>::operator==(const BBox& rhs) const { if (boost::is_integral<ElementType>::value) { return mMin == rhs.min() && mMax == rhs.max(); @@ -203,11 +255,11 @@ BBox<VectorType>::operator==(const BBox& rhs) const } -template<class VectorType> +template<typename Vec3T> inline void -BBox<VectorType>::sort() +BBox<Vec3T>::sort() { - VectorType tMin(mMin), tMax(mMax); + Vec3T tMin(mMin), tMax(mMax); for (size_t i = 0; i < 3; ++i) { mMin[i] = std::min(tMin[i], tMax[i]); mMax[i] = std::max(tMin[i], tMax[i]); @@ -215,63 +267,51 @@ BBox<VectorType>::sort() } -template<class VectorType> +template<typename Vec3T> inline bool -BBox<VectorType>::isSorted() const +BBox<Vec3T>::isSorted() const { if (boost::is_integral<ElementType>::value) { return (mMin[0] <= mMax[0] && mMin[1] <= mMax[1] && mMin[2] <= mMax[2]); } else { - ElementType t = tolerance<ElementType>::value(); + ElementType t = math::Tolerance<ElementType>::value(); return (mMin[0] < (mMax[0] + t) && mMin[1] < (mMax[1] + t) && mMin[2] < (mMax[2] + t)); } } -template<class VectorType> +template<typename Vec3T> inline Vec3d -BBox<VectorType>::getCenter() const +BBox<Vec3T>::getCenter() const { return (Vec3d(mMin.asPointer()) + Vec3d(mMax.asPointer())) * 0.5; } -template<class VectorType> -inline VectorType -BBox<VectorType>::extents() const +template<typename Vec3T> +inline Vec3T +BBox<Vec3T>::extents() const { if (boost::is_integral<ElementType>::value) { - return (mMax - mMin) + VectorType(1, 1, 1); + return (mMax - mMin) + Vec3T(1, 1, 1); } else { return (mMax - mMin); } } - -template<class VectorType> -inline size_t -BBox<VectorType>::maxExtent() const -{ - VectorType e = extents(); - if (e[0] > e[1] && e[0] > e[2]) return 0; - else if (e[1] > e[2]) return 1; - return 2; -} - - //////////////////////////////////////// -template<class VectorType> +template<typename Vec3T> inline bool -BBox<VectorType>::isInside(const VectorType& xyz) const +BBox<Vec3T>::isInside(const Vec3T& xyz) const { if (boost::is_integral<ElementType>::value) { return xyz[0] >= mMin[0] && xyz[0] <= mMax[0] && xyz[1] >= mMin[1] && xyz[1] <= mMax[1] && xyz[2] >= mMin[2] && xyz[2] <= mMax[2]; } else { - ElementType t = tolerance<ElementType>::value(); + ElementType t = math::Tolerance<ElementType>::value(); return xyz[0] > (mMin[0]-t) && xyz[0] < (mMax[0]+t) && xyz[1] > (mMin[1]-t) && xyz[1] < (mMax[1]+t) && xyz[2] > (mMin[2]-t) && xyz[2] < (mMax[2]+t); @@ -279,16 +319,16 @@ BBox<VectorType>::isInside(const VectorType& xyz) const } -template<class VectorType> +template<typename Vec3T> inline bool -BBox<VectorType>::isInside(const BBox& b) const +BBox<Vec3T>::isInside(const BBox& b) const { if (boost::is_integral<ElementType>::value) { return b.min()[0] >= mMin[0] && b.max()[0] <= mMax[0] && b.min()[1] >= mMin[1] && b.max()[1] <= mMax[1] && b.min()[2] >= mMin[2] && b.max()[2] <= mMax[2]; } else { - ElementType t = tolerance<ElementType>::value(); + ElementType t = math::Tolerance<ElementType>::value(); return (b.min()[0]-t) > mMin[0] && (b.max()[0]+t) < mMax[0] && (b.min()[1]-t) > mMin[1] && (b.max()[1]+t) < mMax[1] && (b.min()[2]-t) > mMin[2] && (b.max()[2]+t) < mMax[2]; @@ -296,16 +336,16 @@ BBox<VectorType>::isInside(const BBox& b) const } -template<class VectorType> +template<typename Vec3T> inline bool -BBox<VectorType>::hasOverlap(const BBox& b) const +BBox<Vec3T>::hasOverlap(const BBox& b) const { if (boost::is_integral<ElementType>::value) { return mMax[0] >= b.min()[0] && mMin[0] <= b.max()[0] && mMax[1] >= b.min()[1] && mMin[1] <= b.max()[1] && mMax[2] >= b.min()[2] && mMin[2] <= b.max()[2]; } else { - ElementType t = tolerance<ElementType>::value(); + ElementType t = math::Tolerance<ElementType>::value(); return mMax[0] > (b.min()[0]-t) && mMin[0] < (b.max()[0]+t) && mMax[1] > (b.min()[1]-t) && mMin[1] < (b.max()[1]+t) && mMax[2] > (b.min()[2]-t) && mMin[2] < (b.max()[2]+t); @@ -316,9 +356,9 @@ BBox<VectorType>::hasOverlap(const BBox& b) const //////////////////////////////////////// -template<class VectorType> +template<typename Vec3T> inline void -BBox<VectorType>::expand(ElementType dx) +BBox<Vec3T>::expand(ElementType dx) { dx = std::abs(dx); for (size_t i = 0; i < 3; ++i) { @@ -328,9 +368,9 @@ BBox<VectorType>::expand(ElementType dx) } -template<class VectorType> +template<typename Vec3T> inline void -BBox<VectorType>::expand(const VectorType& xyz) +BBox<Vec3T>::expand(const Vec3T& xyz) { for (size_t i = 0; i < 3; ++i) { mMin[i] = std::min(mMin[i], xyz[i]); @@ -339,9 +379,9 @@ BBox<VectorType>::expand(const VectorType& xyz) } -template<class VectorType> +template<typename Vec3T> inline void -BBox<VectorType>::expand(const BBox& b) +BBox<Vec3T>::expand(const BBox& b) { for (size_t i = 0; i < 3; ++i) { mMin[i] = std::min(mMin[i], b.min()[i]); @@ -349,9 +389,9 @@ BBox<VectorType>::expand(const BBox& b) } } -template<class VectorType> +template<typename Vec3T> inline void -BBox<VectorType>::expand(const VectorType& xyzMin, const ElementType& length) +BBox<Vec3T>::expand(const Vec3T& xyzMin, const ElementType& length) { const ElementType size = boost::is_integral<ElementType>::value ? length-1 : length; for (size_t i = 0; i < 3; ++i) { @@ -361,21 +401,56 @@ BBox<VectorType>::expand(const VectorType& xyzMin, const ElementType& length) } -template<class VectorType> +template<typename Vec3T> inline void -BBox<VectorType>::translate(const VectorType& dx) +BBox<Vec3T>::translate(const Vec3T& dx) { mMin += dx; mMax += dx; } +template<typename Vec3T> +template<typename MapType> +inline BBox<Vec3T> +BBox<Vec3T>::applyMap(const MapType& map) const +{ + typedef Vec3<double> Vec3R; + BBox<Vec3T> bbox; + bbox.expand(map.applyMap(Vec3R(mMin[0], mMin[1], mMin[2]))); + bbox.expand(map.applyMap(Vec3R(mMin[0], mMin[1], mMax[2]))); + bbox.expand(map.applyMap(Vec3R(mMin[0], mMax[1], mMin[2]))); + bbox.expand(map.applyMap(Vec3R(mMax[0], mMin[1], mMin[2]))); + bbox.expand(map.applyMap(Vec3R(mMax[0], mMax[1], mMin[2]))); + bbox.expand(map.applyMap(Vec3R(mMax[0], mMin[1], mMax[2]))); + bbox.expand(map.applyMap(Vec3R(mMin[0], mMax[1], mMax[2]))); + bbox.expand(map.applyMap(Vec3R(mMax[0], mMax[1], mMax[2]))); + return bbox; +} + +template<typename Vec3T> +template<typename MapType> +inline BBox<Vec3T> +BBox<Vec3T>::applyInverseMap(const MapType& map) const +{ + typedef Vec3<double> Vec3R; + BBox<Vec3T> bbox; + bbox.expand(map.applyInverseMap(Vec3R(mMin[0], mMin[1], mMin[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMin[0], mMin[1], mMax[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMin[0], mMax[1], mMin[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMax[0], mMin[1], mMin[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMax[0], mMax[1], mMin[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMax[0], mMin[1], mMax[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMin[0], mMax[1], mMax[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMax[0], mMax[1], mMax[2]))); + return bbox; +} //////////////////////////////////////// -template<class VectorType> +template<typename Vec3T> inline std::ostream& -operator<<(std::ostream& os, const BBox<VectorType>& b) +operator<<(std::ostream& os, const BBox<Vec3T>& b) { os << b.min() << " -> " << b.max(); return os; diff --git a/extern/openvdb/internal/openvdb/math/Coord.h b/extern/openvdb/internal/openvdb/math/Coord.h index 3c5a8211023..99e5872a8f5 100644 --- a/extern/openvdb/internal/openvdb/math/Coord.h +++ b/extern/openvdb/internal/openvdb/math/Coord.h @@ -31,10 +31,6 @@ #ifndef OPENVDB_MATH_COORD_HAS_BEEN_INCLUDED #define OPENVDB_MATH_COORD_HAS_BEEN_INCLUDED -#ifdef _WIN32 -#define NOMINMAX -#endif - #include <openvdb/Platform.h> #include "Math.h" #include "Vec3.h" @@ -47,7 +43,7 @@ OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { namespace math { -/// @brief Signed (x, y, z) integer coordinates +/// @brief Signed (x, y, z) 32-bit integer coordinates class Coord { public: @@ -65,8 +61,6 @@ public: { mVec[0] = mVec[1] = mVec[2] = xyz; } Coord(Int32 x, Int32 y, Int32 z) { mVec[0] = x; mVec[1] = y; mVec[2] = z; } - Coord(Index32 x, Index32 y, Index32 z) - { mVec[0] = Int32(x); mVec[1] = Int32(y); mVec[2] = Int32(z); } explicit Coord(const Vec3i& v) { mVec[0] = v[0]; mVec[1] = v[1]; mVec[2] = v[2]; } explicit Coord(const Vec3I& v) @@ -74,78 +68,49 @@ public: explicit Coord(const Int32* v) { mVec[0] = v[0]; mVec[1] = v[1]; mVec[2] = v[2]; } + /// @brief Return the smallest possible coordinate static const Coord& min() { static const Coord sMin(Limits::min()); return sMin; } + + /// @brief Return the largest possible coordinate static const Coord& max() { static const Coord sMax(Limits::max()); return sMax; } /// @brief Return @a xyz rounded to the closest integer coordinates /// (cell centered conversion). template<typename T> static Coord round(const Vec3<T>& xyz) - { - return Coord(static_cast<int>(Round(xyz[0])), - static_cast<int>(Round(xyz[1])), - static_cast<int>(Round(xyz[2]))); - } + { return Coord(Int32(Round(xyz[0])), Int32(Round(xyz[1])), Int32(Round(xyz[2]))); } /// @brief Return the largest integer coordinates that are not greater /// than @a xyz (node centered conversion). template<typename T> static Coord floor(const Vec3<T>& xyz) - { - return Coord(static_cast<int>(Floor(xyz[0])), - static_cast<int>(Floor(xyz[1])), - static_cast<int>(Floor(xyz[2]))); - } + { return Coord(Int32(Floor(xyz[0])), Int32(Floor(xyz[1])), Int32(Floor(xyz[2]))); } /// @brief Return the largest integer coordinates that are not greater /// than @a xyz+1 (node centered conversion). template<typename T> static Coord ceil(const Vec3<T>& xyz) - { - return Coord(static_cast<int>(Ceil(xyz[0])), - static_cast<int>(Ceil(xyz[1])), - static_cast<int>(Ceil(xyz[2]))); - } + { return Coord(Int32(Ceil(xyz[0])), Int32(Ceil(xyz[1])), Int32(Ceil(xyz[2]))); } Coord& reset(Int32 x, Int32 y, Int32 z) - { - mVec[0] = x; mVec[1] = y; mVec[2] = z; - this->dirty(); - return *this; - } - Coord& reset(Index32 x, Index32 y, Index32 z) - { - return this->reset(Int32(x), Int32(y), Int32(z)); - } + { mVec[0] = x; mVec[1] = y; mVec[2] = z; this->dirty(); return *this; } Coord& reset(Int32 xyz) { return this->reset(xyz, xyz, xyz); } + Coord& setX(Int32 x) { mVec[0] = x; dirty(); return *this; } Coord& setY(Int32 y) { mVec[1] = y; dirty(); return *this; } Coord& setZ(Int32 z) { mVec[2] = z; dirty(); return *this; } + Coord& offset(Int32 dx, Int32 dy, Int32 dz) - { - mVec[0]+=dx; mVec[1]+=dy; mVec[2]+=dz; - this->dirty(); - return *this; - } + { mVec[0]+=dx; mVec[1]+=dy; mVec[2]+=dz; this->dirty(); return *this; } Coord& offset(Int32 n) { return this->offset(n, n, n); } Coord offsetBy(Int32 dx, Int32 dy, Int32 dz) const - { - return Coord(mVec[0] + dx, mVec[1] + dy, mVec[2] + dz); - } + { return Coord(mVec[0] + dx, mVec[1] + dy, mVec[2] + dz); } Coord offsetBy(Int32 n) const { return offsetBy(n, n, n); } Coord& operator+=(const Coord& rhs) - { - mVec[0] += rhs[0]; mVec[1] += rhs[1]; mVec[2] += rhs[2]; return *this; - } + { mVec[0] += rhs[0]; mVec[1] += rhs[1]; mVec[2] += rhs[2]; return *this; } Coord& operator-=(const Coord& rhs) - { - mVec[0] -= rhs[0]; mVec[1] -= rhs[1]; mVec[2] -= rhs[2]; return *this; - } + { mVec[0] -= rhs[0]; mVec[1] -= rhs[1]; mVec[2] -= rhs[2]; return *this; } Coord operator+(const Coord& rhs) const - { - return Coord(mVec[0] + rhs[0], mVec[1] + rhs[1], mVec[2] + rhs[2]); - } + { return Coord(mVec[0] + rhs[0], mVec[1] + rhs[1], mVec[2] + rhs[2]); } Coord operator-(const Coord& rhs) const - { - return Coord(mVec[0] - rhs[0], mVec[1] - rhs[1], mVec[2] - rhs[2]); - } + { return Coord(mVec[0] - rhs[0], mVec[1] - rhs[1], mVec[2] - rhs[2]); } Coord operator-() const { return Coord(-mVec[0], -mVec[1], -mVec[2]); } Coord operator>> (size_t n) const { return Coord(mVec[0]>>n, mVec[1]>>n, mVec[2]>>n); } @@ -171,16 +136,11 @@ public: Vec3d asVec3d() const { return Vec3d(double(mVec[0]), double(mVec[1]), double(mVec[2])); } Vec3s asVec3s() const { return Vec3s(float(mVec[0]), float(mVec[1]), float(mVec[2])); } Vec3i asVec3i() const { return Vec3i(mVec); } - Vec3I asVec3I() const - { - return Vec3I(Index32(mVec[0]), Index32(mVec[1]), Index32(mVec[2])); - } + Vec3I asVec3I() const { return Vec3I(Index32(mVec[0]), Index32(mVec[1]), Index32(mVec[2])); } void asXYZ(Int32& x, Int32& y, Int32& z) const { x = mVec[0]; y = mVec[1]; z = mVec[2]; } bool operator==(const Coord& rhs) const - { - return (mVec[0] == rhs.mVec[0] && mVec[1] == rhs.mVec[1] && mVec[2] == rhs.mVec[2]); - } + { return (mVec[0] == rhs.mVec[0] && mVec[1] == rhs.mVec[1] && mVec[2] == rhs.mVec[2]); } bool operator!=(const Coord& rhs) const { return !(*this == rhs); } /// Lexicographic less than @@ -239,11 +199,16 @@ public: { return (a[0] < b[0] || a[1] < b[1] || a[2] < b[2]); } + + /// @brief Return the index (0, 1 or 2) with the smallest value. + size_t minIndex() const { return MinIndex(mVec); } + + /// @brief Return the index (0, 1 or 2) with the largest value. + size_t maxIndex() const { return MaxIndex(mVec); } + void read(std::istream& is) { is.read(reinterpret_cast<char*>(mVec), sizeof(mVec)); } void write(std::ostream& os) const - { - os.write(reinterpret_cast<const char*>(mVec), sizeof(mVec)); - } + { os.write(reinterpret_cast<const char*>(mVec), sizeof(mVec)); } private: //no-op for now @@ -266,7 +231,7 @@ class CoordBBox public: typedef uint64_t Index64; typedef Coord::ValueType ValueType; - + /// @brief The default constructor produces an empty bounding box. CoordBBox(): mMin(Coord::max()), mMax(Coord::min()) {} /// @brief Construct a bounding box with the given @a min and @a max bounds. @@ -295,6 +260,7 @@ public: Coord& min() { return mMin; } Coord& max() { return mMax; } + void reset() { mMin = Coord::max(); mMax = Coord::min(); } void reset(const Coord& min, const Coord& max) { mMin = min; mMax = max; } void resetToCube(const Coord& min, ValueType dim) { mMin = min; mMax = min.offsetBy(dim - 1); } @@ -332,12 +298,11 @@ public: /// Return @c true if this bounding box can be subdivided [mainly for use by TBB]. bool is_divisible() const { return mMin[0]<mMax[0] && mMin[1]<mMax[1] && mMin[2]<mMax[2]; } - /// Return the index (0, 1 or 2) of the longest axis. - size_t maxExtent() const - { - const Coord d = this->dim(); - return (d[0] > d[1] && d[0] > d[2]) ? 0 : (d[1] > d[2]) ? 1 : 2; - } + /// @brief Return the index (0, 1 or 2) of the shortest axis. + size_t minExtent() const { return this->dim().minIndex(); } + + /// @brief Return the index (0, 1 or 2) of the longest axis. + size_t maxExtent() const { return this->dim().maxIndex(); } /// Return @c true if point (x, y, z) is inside this bounding box. bool isInside(const Coord& xyz) const diff --git a/extern/openvdb/internal/openvdb/math/DDA.h b/extern/openvdb/internal/openvdb/math/DDA.h new file mode 100644 index 00000000000..562e32e088c --- /dev/null +++ b/extern/openvdb/internal/openvdb/math/DDA.h @@ -0,0 +1,351 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +// +// Redistributions of source code must retain the above copyright +// and license notice and the following restrictions and disclaimer. +// +// * Neither the name of DreamWorks Animation nor the names of +// its contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE +// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. +// +/////////////////////////////////////////////////////////////////////////// +// +/// @file DDA.h +/// +/// @author Ken Museth +/// +/// @brief Digital Differential Analyzers specialized for VDB. + +#ifndef OPENVDB_MATH_DDA_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_DDA_HAS_BEEN_INCLUDED + +#include "Coord.h" +#include "Math.h" +#include "Vec3.h" +#include <iostream>// for std::ostream +#include <limits>// for std::numeric_limits<Type>::max() + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +/// @brief A Digital Differential Analyzer specialized for OpenVDB grids +/// @note Conceptually similar to Bresenham's line algorithm applied +/// to a 3D Ray intersecting OpenVDB nodes or voxels. Log2Dim = 0 +/// corresponds to a voxel and Log2Dim a tree node of size 2^Log2Dim. +/// +/// @note The Ray template class is expected to have the following +/// methods: test(time), t0(), t1(), invDir(), and operator()(time). +/// See the example Ray class above for their definition. +template<typename RayT, Index Log2Dim = 0> +class DDA +{ +public: + typedef typename RayT::RealType RealType; + typedef RealType RealT; + typedef typename RayT::Vec3Type Vec3Type; + typedef Vec3Type Vec3T; + + /// @brief uninitialized constructor + DDA() {} + + DDA(const RayT& ray) { this->init(ray); } + + DDA(const RayT& ray, RealT startTime) { this->init(ray, startTime); } + + DDA(const RayT& ray, RealT startTime, RealT maxTime) { this->init(ray, startTime, maxTime); } + + inline void init(const RayT& ray, RealT startTime, RealT maxTime) + { + assert(startTime <= maxTime); + static const int DIM = 1 << Log2Dim; + mT0 = startTime; + mT1 = maxTime; + const Vec3T &pos = ray(mT0), &dir = ray.dir(), &inv = ray.invDir(); + mVoxel = Coord::floor(pos) & (~(DIM-1)); + for (size_t axis = 0; axis < 3; ++axis) { + if (math::isZero(dir[axis])) {//handles dir = +/- 0 + mStep[axis] = 0;//dummy value + mNext[axis] = std::numeric_limits<RealT>::max();//i.e. disabled! + mDelta[axis] = std::numeric_limits<RealT>::max();//dummy value + } else if (inv[axis] > 0) { + mStep[axis] = DIM; + mNext[axis] = mT0 + (mVoxel[axis] + DIM - pos[axis]) * inv[axis]; + mDelta[axis] = mStep[axis] * inv[axis]; + } else { + mStep[axis] = -DIM; + mNext[axis] = mT0 + (mVoxel[axis] - pos[axis]) * inv[axis]; + mDelta[axis] = mStep[axis] * inv[axis]; + } + } + } + + inline void init(const RayT& ray) { this->init(ray, ray.t0(), ray.t1()); } + + inline void init(const RayT& ray, RealT startTime) { this->init(ray, startTime, ray.t1()); } + + /// @brief Increment the voxel index to next intersected voxel or node + /// and returns true if the step in time does not exceed maxTime. + inline bool step() + { + const size_t stepAxis = math::MinIndex(mNext); + mT0 = mNext[stepAxis]; + mNext[stepAxis] += mDelta[stepAxis]; + mVoxel[stepAxis] += mStep[stepAxis]; + return mT0 <= mT1; + } + + /// @brief Return the index coordinates of the next node or voxel + /// intersected by the ray. If Log2Dim = 0 the return value is the + /// actual signed coordinate of the voxel, else it is the origin + /// of the corresponding VDB tree node or tile. + /// @note Incurs no computational overhead. + inline const Coord& voxel() const { return mVoxel; } + + /// @brief Return the time (parameterized along the Ray) of the + /// first hit of a tree node of size 2^Log2Dim. + /// @details This value is initialized to startTime or ray.t0() + /// depending on the constructor used. + /// @note Incurs no computational overhead. + inline RealType time() const { return mT0; } + + /// @brief Return the maximum time (parameterized along the Ray). + inline RealType maxTime() const { return mT1; } + + /// @brief Return the time (parameterized along the Ray) of the + /// second (i.e. next) hit of a tree node of size 2^Log2Dim. + /// @note Incurs a (small) computational overhead. + inline RealType next() const { return math::Min(mT1, mNext[0], mNext[1], mNext[2]); } + + /// @brief Print information about this DDA for debugging. + /// @param os a stream to which to write textual information. + void print(std::ostream& os = std::cout) const + { + os << "Dim=" << (1<<Log2Dim) << " time=" << mT0 << " next()=" + << this->next() << " voxel=" << mVoxel << " next=" << mNext + << " delta=" << mDelta << " step=" << mStep << std::endl; + } + +private: + RealT mT0, mT1; + Coord mVoxel, mStep; + Vec3T mDelta, mNext; +}; // class DDA + +/// @brief Output streaming of the Ray class. +/// @note Primarily intended for debugging. +template<typename RayT, Index Log2Dim> +inline std::ostream& operator<<(std::ostream& os, const DDA<RayT, Log2Dim>& dda) +{ + os << "Dim=" << (1<<Log2Dim) << " time=" << dda.time() + << " next()=" << dda.next() << " voxel=" << dda.voxel(); + return os; +} + +/////////////////////////////////////////// LevelSetHDDA //////////////////////////////////////////// + + +/// @brief Helper class that implements Hierarchical Digital Differential Analyzers +/// and is specialized for ray intersections with level sets +template<typename TreeT, int NodeLevel> +struct LevelSetHDDA +{ + typedef typename TreeT::RootNodeType::NodeChainType ChainT; + typedef typename boost::mpl::at<ChainT, boost::mpl::int_<NodeLevel> >::type NodeT; + + template <typename TesterT> + static bool test(TesterT& tester) + { + math::DDA<typename TesterT::RayT, NodeT::TOTAL> dda(tester.ray()); + do { + if (tester.template hasNode<NodeT>(dda.voxel())) { + tester.setRange(dda.time(), dda.next()); + if (LevelSetHDDA<TreeT, NodeLevel-1>::test(tester)) return true; + } + } while(dda.step()); + return false; + } +}; + +/// @brief Specialization of Hierarchical Digital Differential Analyzer +/// class that intersects a ray against the voxels of a level set +template<typename TreeT> +struct LevelSetHDDA<TreeT, -1> +{ + template <typename TesterT> + static bool test(TesterT& tester) + { + math::DDA<typename TesterT::RayT, 0> dda(tester.ray()); + tester.init(dda.time()); + do { if (tester(dda.voxel(), dda.next())) return true; } while(dda.step()); + return false; + } +}; + +//////////////////////////////////////////// VolumeHDDA ///////////////////////////////////////////// + +/// @brief Helper class that implements Hierarchical Digital Differential Analyzers +/// for ray intersections against a generic volume. +template <typename TreeT, typename RayT, int ChildNodeLevel> +class VolumeHDDA +{ +public: + + typedef typename TreeT::RootNodeType::NodeChainType ChainT; + typedef typename boost::mpl::at<ChainT, boost::mpl::int_<ChildNodeLevel> >::type NodeT; + typedef typename tree::ValueAccessor<const TreeT> AccessorT; + typedef typename RayT::TimeSpan TimeSpanT; + + VolumeHDDA() {} + + TimeSpanT march(RayT& ray, AccessorT &acc) + { + TimeSpanT t(-1, -1); + if (ray.valid()) this->march(ray, acc, t); + return t; + } + + void hits(RayT& ray, AccessorT &acc, std::vector<TimeSpanT>& times) + { + TimeSpanT t(-1,-1); + times.clear(); + this->hits(ray, acc, times, t); + if (t.valid()) times.push_back(t); + } + +private: + + friend class VolumeHDDA<TreeT, RayT, ChildNodeLevel+1>; + + bool march(RayT& ray, AccessorT &acc, TimeSpanT& t) + { + mDDA.init(ray); + do { + if (acc.template probeConstNode<NodeT>(mDDA.voxel()) != NULL) {//child node + ray.setTimes(mDDA.time(), mDDA.next()); + if (mHDDA.march(ray, acc, t)) return true;//terminate + } else if (acc.isValueOn(mDDA.voxel())) {//hit an active tile + if (t.t0<0) t.t0 = mDDA.time();//this is the first hit so set t0 + } else if (t.t0>=0) {//hit an inactive tile after hitting active values + t.t1 = mDDA.time();//set end of active ray segment + if (t.valid()) return true;//terminate + t.set(-1, -1);//reset to an empty and invalid time-span + } + } while (mDDA.step()); + if (t.t0>=0) t.t1 = mDDA.maxTime(); + return false; + } + + void hits(RayT& ray, AccessorT &acc, std::vector<TimeSpanT>& times, TimeSpanT& t) + { + mDDA.init(ray); + do { + if (acc.template probeConstNode<NodeT>(mDDA.voxel()) != NULL) {//child node + ray.setTimes(mDDA.time(), mDDA.next()); + mHDDA.hits(ray, acc, times, t); + } else if (acc.isValueOn(mDDA.voxel())) {//hit an active tile + if (t.t0<0) t.t0 = mDDA.time();//this is the first hit so set t0 + } else if (t.t0>=0) {//hit an inactive tile after hitting active values + t.t1 = mDDA.time();//set end of active ray segment + if (t.valid()) times.push_back(t); + t.set(-1,-1);//reset to an empty and invalid time-span + } + } while (mDDA.step()); + if (t.t0>=0) t.t1 = mDDA.maxTime(); + } + + math::DDA<RayT, NodeT::TOTAL> mDDA; + VolumeHDDA<TreeT, RayT, ChildNodeLevel-1> mHDDA; +}; + +/// @brief Specialization of Hierarchical Digital Differential Analyzer +/// class that intersects against the leafs or tiles of a generic volume. +template <typename TreeT, typename RayT> +class VolumeHDDA<TreeT, RayT, 0> +{ +public: + + typedef typename TreeT::LeafNodeType LeafT; + typedef typename tree::ValueAccessor<const TreeT> AccessorT; + typedef typename RayT::TimeSpan TimeSpanT; + + VolumeHDDA() {} + + TimeSpanT march(RayT& ray, AccessorT &acc) + { + TimeSpanT t(-1, -1); + if (ray.valid()) this->march(ray, acc, t); + return t; + } + + void hits(RayT& ray, AccessorT &acc, std::vector<TimeSpanT>& times) + { + TimeSpanT t(-1,-1); + times.clear(); + this->hits(ray, acc, times, t); + if (t.valid()) times.push_back(t); + } + +private: + + friend class VolumeHDDA<TreeT, RayT, 1>; + + bool march(RayT& ray, AccessorT &acc, TimeSpanT& t) + { + mDDA.init(ray); + do { + if (acc.template probeConstNode<LeafT>(mDDA.voxel()) || + acc.isValueOn(mDDA.voxel())) {//hit a leaf or an active tile + if (t.t0<0) t.t0 = mDDA.time();//this is the first hit + } else if (t.t0>=0) {//hit an inactive tile after hitting active values + t.t1 = mDDA.time();//set end of active ray segment + if (t.valid()) return true;//terminate + t.set(-1, -1);//reset to an empty and invalid time-span + } + } while (mDDA.step()); + if (t.t0>=0) t.t1 = mDDA.maxTime(); + return false; + } + + void hits(RayT& ray, AccessorT &acc, std::vector<TimeSpanT>& times, TimeSpanT& t) + { + mDDA.init(ray); + do { + if (acc.template probeConstNode<LeafT>(mDDA.voxel()) || + acc.isValueOn(mDDA.voxel())) {//hit a leaf or an active tile + if (t.t0<0) t.t0 = mDDA.time();//this is the first hit + } else if (t.t0>=0) {//hit an inactive tile after hitting active values + t.t1 = mDDA.time();//set end of active ray segment + if (t.valid()) times.push_back(t); + t.set(-1, -1);//reset to an empty and invalid time-span + } + } while (mDDA.step()); + if (t.t0>=0) t.t1 = mDDA.maxTime(); + } + math::DDA<RayT, LeafT::TOTAL> mDDA; +}; + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_DDA_HAS_BEEN_INCLUDED diff --git a/extern/openvdb/internal/openvdb/math/FiniteDifference.h b/extern/openvdb/internal/openvdb/math/FiniteDifference.h index 6e02a7432fc..6a68ee4b60e 100644 --- a/extern/openvdb/internal/openvdb/math/FiniteDifference.h +++ b/extern/openvdb/internal/openvdb/math/FiniteDifference.h @@ -42,7 +42,7 @@ #include <boost/algorithm/string/trim.hpp> #ifdef DWA_OPENVDB -#include <simd/Simd.h> +#include <simd/Simd.h> #endif namespace openvdb { @@ -255,8 +255,8 @@ biasedGradientSchemeToMenuName(BiasedGradientScheme bgs) //////////////////////////////////////// - -/// @brief Temporal integrations schemes + +/// @brief Temporal integration schemes // Add new items to the *end* of this list, and update NUM_TEMPORAL_SCHEMES. enum TemporalIntegrationScheme { UNKNOWN_TIS = -1, @@ -296,7 +296,7 @@ stringToTemporalIntegrationScheme(const std::string& s) } else if (str == temporalIntegrationSchemeToString(TVD_RK3)) { ret = TVD_RK3; } - + return ret; } @@ -317,18 +317,19 @@ temporalIntegrationSchemeToMenuName(TemporalIntegrationScheme tis) //@} -/// @brief implimentation of nonimally fith-order finite-difference WENO. -/// This function returns the numerical flux. See "High Order Finite Difference and +/// @brief Implementation of nominally fifth-order finite-difference WENO +/// @details This function returns the numerical flux. See "High Order Finite Difference and /// Finite Volume WENO Schemes and Discontinuous Galerkin Methods for CFD" - Chi-Wang Shu /// ICASE Report No 2001-11 (page 6). Also see ICASE No 97-65 for a more complete reference -/// (Shu, 1997) -/// Given v1 = f(x-2dx), v2 = f(x-dx), v3 = f(x), v4 = f(x+dx), v5 = f(x+2dx), -/// the returns and interpolated value f(x+dx/2) with the special property that +/// (Shu, 1997). +/// Given v1 = f(x-2dx), v2 = f(x-dx), v3 = f(x), v4 = f(x+dx) and v5 = f(x+2dx), +/// return an interpolated value f(x+dx/2) with the special property that /// ( f(x+dx/2) - f(x-dx/2) ) / dx = df/dx (x) + error, -/// where the error is 5-order in smooth regions: O(dx) <= error <=O(dx^5) +/// where the error is fifth-order in smooth regions: O(dx) <= error <=O(dx^5) template<typename ValueType> inline ValueType -WENO5(const ValueType& v1, const ValueType& v2, const ValueType& v3, const ValueType& v4, const ValueType& v5, float scale2 = 0.01) +WENO5(const ValueType& v1, const ValueType& v2, const ValueType& v3, + const ValueType& v4, const ValueType& v5, float scale2 = 0.01) { static const double C=13.0/12.0; // Weno is formulated for non-dimensional equations, here the optional scale2 @@ -369,13 +370,14 @@ inline Real GudonovsNormSqrd(bool isOutside, dPLen2 += Max(Pow2(Min(dP_zm, zero)), Pow2(Max(dP_zp,zero))); // (dP/dz)2 } return dPLen2; // |\nabla\phi|^2 -}; +} -template <typename Real> -inline Real GudonovsNormSqrd(bool isOutside, const Vec3<Real>& gradient_m, const Vec3<Real>& gradient_p) +template<typename Real> +inline Real +GudonovsNormSqrd(bool isOutside, const Vec3<Real>& gradient_m, const Vec3<Real>& gradient_p) { - return GudonovsNormSqrd<Real>(isOutside, + return GudonovsNormSqrd<Real>(isOutside, gradient_m[0], gradient_p[0], gradient_m[1], gradient_p[1], gradient_m[2], gradient_p[2]); @@ -447,7 +449,7 @@ struct D1 template<typename Accessor> static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk); - + // stencil access version template<typename Stencil> static typename Stencil::ValueType inX(const Stencil& S); @@ -472,28 +474,34 @@ struct D1<CD_2NDT> template<typename Accessor> static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) { - return difference(grid.getValue(ijk.offsetBy(1, 0, 0)), grid.getValue(ijk.offsetBy(-1, 0, 0))); + return difference( + grid.getValue(ijk.offsetBy(1, 0, 0)), + grid.getValue(ijk.offsetBy(-1, 0, 0))); } template<typename Accessor> static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) { - return difference(grid.getValue(ijk.offsetBy(0, 1, 0)), grid.getValue(ijk.offsetBy( 0, -1, 0))); + return difference( + grid.getValue(ijk.offsetBy(0, 1, 0)), + grid.getValue(ijk.offsetBy( 0, -1, 0))); } template<typename Accessor> static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) { - return difference(grid.getValue(ijk.offsetBy(0, 0, 1)), grid.getValue(ijk.offsetBy( 0, 0, -1))); + return difference( + grid.getValue(ijk.offsetBy(0, 0, 1)), + grid.getValue(ijk.offsetBy( 0, 0, -1))); } - + // stencil access version template<typename Stencil> static typename Stencil::ValueType inX(const Stencil& S) { return difference( S.template getValue< 1, 0, 0>(), S.template getValue<-1, 0, 0>()); } - + template<typename Stencil> static typename Stencil::ValueType inY(const Stencil& S) { @@ -517,45 +525,48 @@ struct D1<CD_2ND> return (xp1 - xm1)*ValueType(0.5); } - + // random access template<typename Accessor> static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) { - return difference(grid.getValue(ijk.offsetBy(1, 0, 0)), grid.getValue(ijk.offsetBy(-1, 0, 0))); + return difference( + grid.getValue(ijk.offsetBy(1, 0, 0)), + grid.getValue(ijk.offsetBy(-1, 0, 0))); } template<typename Accessor> static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) { - return difference(grid.getValue(ijk.offsetBy(0, 1, 0)), grid.getValue(ijk.offsetBy( 0, -1, 0))); + return difference( + grid.getValue(ijk.offsetBy(0, 1, 0)), + grid.getValue(ijk.offsetBy( 0, -1, 0))); } template<typename Accessor> static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) { - return difference(grid.getValue(ijk.offsetBy(0, 0, 1)), grid.getValue(ijk.offsetBy( 0, 0, -1))); + return difference( + grid.getValue(ijk.offsetBy(0, 0, 1)), + grid.getValue(ijk.offsetBy( 0, 0, -1))); } - + // stencil access version template<typename Stencil> static typename Stencil::ValueType inX(const Stencil& S) { - typedef typename Stencil::ValueType ValueType; return difference(S.template getValue< 1, 0, 0>(), S.template getValue<-1, 0, 0>()); } - template<typename Stencil> + template<typename Stencil> static typename Stencil::ValueType inY(const Stencil& S) { - typedef typename Stencil::ValueType ValueType; return difference(S.template getValue< 0, 1, 0>(), S.template getValue< 0,-1, 0>()); } template<typename Stencil> static typename Stencil::ValueType inZ(const Stencil& S) { - typedef typename Stencil::ValueType ValueType; return difference(S.template getValue< 0, 0, 1>(), S.template getValue< 0, 0,-1>()); } @@ -564,67 +575,68 @@ struct D1<CD_2ND> template<> struct D1<CD_4TH> { - + // the difference opperator template <typename ValueType> - static ValueType difference( const ValueType& xp2, const ValueType& xp1, + static ValueType difference( const ValueType& xp2, const ValueType& xp1, const ValueType& xm1, const ValueType& xm2 ) { return ValueType(2./3.)*(xp1 - xm1) + ValueType(1./12.)*(xm2 - xp2) ; } - + // random access version template<typename Accessor> static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) { - return difference( grid.getValue(ijk.offsetBy( 2,0,0)), grid.getValue(ijk.offsetBy( 1,0,0)), - grid.getValue(ijk.offsetBy(-1,0,0)), grid.getValue(ijk.offsetBy(-2,0,0)) ); - + return difference( + grid.getValue(ijk.offsetBy( 2,0,0)), grid.getValue(ijk.offsetBy( 1,0,0)), + grid.getValue(ijk.offsetBy(-1,0,0)), grid.getValue(ijk.offsetBy(-2,0,0)) ); } template<typename Accessor> static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) { - - return difference( grid.getValue(ijk.offsetBy( 0, 2, 0)), grid.getValue(ijk.offsetBy( 0, 1, 0)), - grid.getValue(ijk.offsetBy( 0,-1, 0)), grid.getValue(ijk.offsetBy( 0,-2, 0)) ); - + + return difference( + grid.getValue(ijk.offsetBy( 0, 2, 0)), grid.getValue(ijk.offsetBy( 0, 1, 0)), + grid.getValue(ijk.offsetBy( 0,-1, 0)), grid.getValue(ijk.offsetBy( 0,-2, 0)) ); } template<typename Accessor> static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) { - - return difference( grid.getValue(ijk.offsetBy( 0, 0, 2)), grid.getValue(ijk.offsetBy( 0, 0, 1)), - grid.getValue(ijk.offsetBy( 0, 0,-1)), grid.getValue(ijk.offsetBy( 0, 0,-2)) ); + + return difference( + grid.getValue(ijk.offsetBy( 0, 0, 2)), grid.getValue(ijk.offsetBy( 0, 0, 1)), + grid.getValue(ijk.offsetBy( 0, 0,-1)), grid.getValue(ijk.offsetBy( 0, 0,-2)) ); } - + // stencil access version template<typename Stencil> static typename Stencil::ValueType inX(const Stencil& S) { - return difference( S.template getValue< 2, 0, 0>(), + return difference( S.template getValue< 2, 0, 0>(), S.template getValue< 1, 0, 0>(), - S.template getValue<-1, 0, 0>(), + S.template getValue<-1, 0, 0>(), S.template getValue<-2, 0, 0>() ); } template<typename Stencil> static typename Stencil::ValueType inY(const Stencil& S) { - return difference( S.template getValue< 0, 2, 0>(), + return difference( S.template getValue< 0, 2, 0>(), S.template getValue< 0, 1, 0>(), - S.template getValue< 0,-1, 0>(), + S.template getValue< 0,-1, 0>(), S.template getValue< 0,-2, 0>() ); } template<typename Stencil> static typename Stencil::ValueType inZ(const Stencil& S) { - return difference( S.template getValue< 0, 0, 2>(), + return difference( S.template getValue< 0, 0, 2>(), S.template getValue< 0, 0, 1>(), - S.template getValue< 0, 0,-1>(), + S.template getValue< 0, 0,-1>(), S.template getValue< 0, 0,-2>() ); } }; @@ -632,73 +644,78 @@ struct D1<CD_4TH> template<> struct D1<CD_6TH> { - + // the difference opperator template <typename ValueType> - static ValueType difference( const ValueType& xp3, const ValueType& xp2, const ValueType& xp1, - const ValueType& xm1, const ValueType& xm2, const ValueType& xm3 ) { - return ValueType(3./4.)*(xp1 - xm1) - ValueType(0.15)*(xp2 - xm2) + ValueType(1./60.)*(xp3-xm3); + static ValueType difference( const ValueType& xp3, const ValueType& xp2, const ValueType& xp1, + const ValueType& xm1, const ValueType& xm2, const ValueType& xm3 ) + { + return ValueType(3./4.)*(xp1 - xm1) - ValueType(0.15)*(xp2 - xm2) + + ValueType(1./60.)*(xp3-xm3); } - - // random access version + + // random access version template<typename Accessor> static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) { - return difference( grid.getValue(ijk.offsetBy( 3,0,0)), grid.getValue(ijk.offsetBy( 2,0,0)), - grid.getValue(ijk.offsetBy( 1,0,0)), grid.getValue(ijk.offsetBy(-1,0,0)), - grid.getValue(ijk.offsetBy(-2,0,0)), grid.getValue(ijk.offsetBy(-3,0,0))); + return difference( + grid.getValue(ijk.offsetBy( 3,0,0)), grid.getValue(ijk.offsetBy( 2,0,0)), + grid.getValue(ijk.offsetBy( 1,0,0)), grid.getValue(ijk.offsetBy(-1,0,0)), + grid.getValue(ijk.offsetBy(-2,0,0)), grid.getValue(ijk.offsetBy(-3,0,0))); } template<typename Accessor> static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) { - return difference( grid.getValue(ijk.offsetBy( 0, 3,0)), grid.getValue(ijk.offsetBy( 0, 2,0)), - grid.getValue(ijk.offsetBy( 0, 1,0)), grid.getValue(ijk.offsetBy( 0,-1,0)), - grid.getValue(ijk.offsetBy( 0,-2,0)), grid.getValue(ijk.offsetBy( 0,-3,0))); + return difference( + grid.getValue(ijk.offsetBy( 0, 3, 0)), grid.getValue(ijk.offsetBy( 0, 2, 0)), + grid.getValue(ijk.offsetBy( 0, 1, 0)), grid.getValue(ijk.offsetBy( 0,-1, 0)), + grid.getValue(ijk.offsetBy( 0,-2, 0)), grid.getValue(ijk.offsetBy( 0,-3, 0))); } - + template<typename Accessor> static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) { - return difference( grid.getValue(ijk.offsetBy( 0, 0, 3)), grid.getValue(ijk.offsetBy( 0, 0, 2)), - grid.getValue(ijk.offsetBy( 0, 0, 1)), grid.getValue(ijk.offsetBy( 0, 0,-1)), - grid.getValue(ijk.offsetBy( 0, 0,-2)), grid.getValue(ijk.offsetBy( 0, 0,-3))); + return difference( + grid.getValue(ijk.offsetBy( 0, 0, 3)), grid.getValue(ijk.offsetBy( 0, 0, 2)), + grid.getValue(ijk.offsetBy( 0, 0, 1)), grid.getValue(ijk.offsetBy( 0, 0,-1)), + grid.getValue(ijk.offsetBy( 0, 0,-2)), grid.getValue(ijk.offsetBy( 0, 0,-3))); } - + // stencil access version template<typename Stencil> static typename Stencil::ValueType inX(const Stencil& S) - { - return difference(S.template getValue< 3, 0, 0>(), + { + return difference(S.template getValue< 3, 0, 0>(), S.template getValue< 2, 0, 0>(), - S.template getValue< 1, 0, 0>(), + S.template getValue< 1, 0, 0>(), S.template getValue<-1, 0, 0>(), - S.template getValue<-2, 0, 0>(), + S.template getValue<-2, 0, 0>(), S.template getValue<-3, 0, 0>()); } template<typename Stencil> static typename Stencil::ValueType inY(const Stencil& S) { - - return difference( S.template getValue< 0, 3, 0>(), + + return difference( S.template getValue< 0, 3, 0>(), S.template getValue< 0, 2, 0>(), - S.template getValue< 0, 1, 0>(), + S.template getValue< 0, 1, 0>(), S.template getValue< 0,-1, 0>(), - S.template getValue< 0,-2, 0>(), + S.template getValue< 0,-2, 0>(), S.template getValue< 0,-3, 0>()); } template<typename Stencil> static typename Stencil::ValueType inZ(const Stencil& S) { - - return difference( S.template getValue< 0, 0, 3>(), + + return difference( S.template getValue< 0, 0, 3>(), S.template getValue< 0, 0, 2>(), - S.template getValue< 0, 0, 1>(), + S.template getValue< 0, 0, 1>(), S.template getValue< 0, 0,-1>(), - S.template getValue< 0, 0,-2>(), + S.template getValue< 0, 0,-2>(), S.template getValue< 0, 0,-3>()); } }; @@ -707,15 +724,15 @@ struct D1<CD_6TH> template<> struct D1<FD_1ST> { - + // the difference opperator template <typename ValueType> static ValueType difference(const ValueType& xp1, const ValueType& xp0) { return xp1 - xp0; } - - // random access version + + // random access version template<typename Accessor> static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) { @@ -733,7 +750,7 @@ struct D1<FD_1ST> { return difference(grid.getValue(ijk.offsetBy(0, 0, 1)), grid.getValue(ijk)); } - + // stencil access version template<typename Stencil> static typename Stencil::ValueType inX(const Stencil& S) @@ -758,47 +775,56 @@ struct D1<FD_1ST> template<> struct D1<FD_2ND> { - // the difference opperator template <typename ValueType> - static ValueType difference(const ValueType& xp2, const ValueType& xp1, const ValueType& xp0) { + static ValueType difference(const ValueType& xp2, const ValueType& xp1, const ValueType& xp0) + { return ValueType(2)*xp1 -(ValueType(0.5)*xp2 + ValueType(3./2.)*xp0); } - + // random access version template<typename Accessor> static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) { - return difference(grid.getValue(ijk.offsetBy(2,0,0)), grid.getValue(ijk.offsetBy(1,0,0)), grid.getValue(ijk)); + return difference( + grid.getValue(ijk.offsetBy(2,0,0)), + grid.getValue(ijk.offsetBy(1,0,0)), + grid.getValue(ijk)); } template<typename Accessor> static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) { - return difference(grid.getValue(ijk.offsetBy(0,2,0)), grid.getValue(ijk.offsetBy(0,1,0)), grid.getValue(ijk)); + return difference( + grid.getValue(ijk.offsetBy(0,2,0)), + grid.getValue(ijk.offsetBy(0,1,0)), + grid.getValue(ijk)); } template<typename Accessor> static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) { - return difference(grid.getValue(ijk.offsetBy(0,0,2)), grid.getValue(ijk.offsetBy(0,0,1)), grid.getValue(ijk)); + return difference( + grid.getValue(ijk.offsetBy(0,0,2)), + grid.getValue(ijk.offsetBy(0,0,1)), + grid.getValue(ijk)); } - + // stencil access version template<typename Stencil> static typename Stencil::ValueType inX(const Stencil& S) { - return difference( S.template getValue< 2, 0, 0>(), - S.template getValue< 1, 0, 0>(), + return difference( S.template getValue< 2, 0, 0>(), + S.template getValue< 1, 0, 0>(), S.template getValue< 0, 0, 0>() ); } template<typename Stencil> static typename Stencil::ValueType inY(const Stencil& S) { - return difference( S.template getValue< 0, 2, 0>(), + return difference( S.template getValue< 0, 2, 0>(), S.template getValue< 0, 1, 0>(), S.template getValue< 0, 0, 0>() ); } @@ -806,8 +832,8 @@ struct D1<FD_2ND> template<typename Stencil> static typename Stencil::ValueType inZ(const Stencil& S) { - return difference( S.template getValue< 0, 0, 2>(), - S.template getValue< 0, 0, 1>(), + return difference( S.template getValue< 0, 0, 2>(), + S.template getValue< 0, 0, 1>(), S.template getValue< 0, 0, 0>() ); } @@ -817,68 +843,71 @@ struct D1<FD_2ND> template<> struct D1<FD_3RD> { - + // the difference opperator template <typename ValueType> - static ValueType difference(const ValueType& xp3, const ValueType& xp2, const ValueType& xp1, const ValueType& xp0) { - return ValueType(1./3.)*xp3 - ValueType(1.5)*xp2 + ValueType(3.)*xp1 - ValueType(11./6.)*xp0; + static ValueType difference(const ValueType& xp3, const ValueType& xp2, + const ValueType& xp1, const ValueType& xp0) + { + return ValueType(1./3.)*xp3 - ValueType(1.5)*xp2 + + ValueType(3.)*xp1 - ValueType(11./6.)*xp0; } - + // random access version template<typename Accessor> static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) { - return difference( grid.getValue(ijk.offsetBy(3,0,0)), + return difference( grid.getValue(ijk.offsetBy(3,0,0)), grid.getValue(ijk.offsetBy(2,0,0)), - grid.getValue(ijk.offsetBy(1,0,0)), + grid.getValue(ijk.offsetBy(1,0,0)), grid.getValue(ijk) ); } template<typename Accessor> static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) { - return difference( grid.getValue(ijk.offsetBy(0,3,0)), + return difference( grid.getValue(ijk.offsetBy(0,3,0)), grid.getValue(ijk.offsetBy(0,2,0)), - grid.getValue(ijk.offsetBy(0,1,0)), + grid.getValue(ijk.offsetBy(0,1,0)), grid.getValue(ijk) ); } template<typename Accessor> static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) { - return difference( grid.getValue(ijk.offsetBy(0,0,3)), + return difference( grid.getValue(ijk.offsetBy(0,0,3)), grid.getValue(ijk.offsetBy(0,0,2)), - grid.getValue(ijk.offsetBy(0,0,1)), + grid.getValue(ijk.offsetBy(0,0,1)), grid.getValue(ijk) ); } - + // stencil access version template<typename Stencil> static typename Stencil::ValueType inX(const Stencil& S) { - return difference(S.template getValue< 3, 0, 0>(), + return difference(S.template getValue< 3, 0, 0>(), S.template getValue< 2, 0, 0>(), - S.template getValue< 1, 0, 0>(), + S.template getValue< 1, 0, 0>(), S.template getValue< 0, 0, 0>() ); } template<typename Stencil> static typename Stencil::ValueType inY(const Stencil& S) { - return difference(S.template getValue< 0, 3, 0>(), + return difference(S.template getValue< 0, 3, 0>(), S.template getValue< 0, 2, 0>(), - S.template getValue< 0, 1, 0>(), + S.template getValue< 0, 1, 0>(), S.template getValue< 0, 0, 0>() ); } template<typename Stencil> static typename Stencil::ValueType inZ(const Stencil& S) { - return difference( S.template getValue< 0, 0, 3>(), + return difference( S.template getValue< 0, 0, 3>(), S.template getValue< 0, 0, 2>(), - S.template getValue< 0, 0, 1>(), + S.template getValue< 0, 0, 1>(), S.template getValue< 0, 0, 0>() ); } }; @@ -887,13 +916,13 @@ struct D1<FD_3RD> template<> struct D1<BD_1ST> { - + // the difference opperator template <typename ValueType> static ValueType difference(const ValueType& xm1, const ValueType& xm0) { return -D1<FD_1ST>::difference(xm1, xm0); } - + // random access version template<typename Accessor> @@ -914,7 +943,7 @@ struct D1<BD_1ST> return difference(grid.getValue(ijk.offsetBy(0, 0,-1)), grid.getValue(ijk)); } - + // stencil access version template<typename Stencil> static typename Stencil::ValueType inX(const Stencil& S) @@ -939,45 +968,46 @@ struct D1<BD_1ST> template<> struct D1<BD_2ND> { - + // the difference opperator template <typename ValueType> - static ValueType difference(const ValueType& xm2, const ValueType& xm1, const ValueType& xm0) { + static ValueType difference(const ValueType& xm2, const ValueType& xm1, const ValueType& xm0) + { return -D1<FD_2ND>::difference(xm2, xm1, xm0); } - + // random access version template<typename Accessor> static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) { - return difference( grid.getValue(ijk.offsetBy(-2,0,0)), - grid.getValue(ijk.offsetBy(-1,0,0)), + return difference( grid.getValue(ijk.offsetBy(-2,0,0)), + grid.getValue(ijk.offsetBy(-1,0,0)), grid.getValue(ijk) ); } template<typename Accessor> static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) { - return difference( grid.getValue(ijk.offsetBy(0,-2,0)), - grid.getValue(ijk.offsetBy(0,-1,0)), + return difference( grid.getValue(ijk.offsetBy(0,-2,0)), + grid.getValue(ijk.offsetBy(0,-1,0)), grid.getValue(ijk) ); } template<typename Accessor> static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) { - return difference( grid.getValue(ijk.offsetBy(0,0,-2)), - grid.getValue(ijk.offsetBy(0,0,-1)), + return difference( grid.getValue(ijk.offsetBy(0,0,-2)), + grid.getValue(ijk.offsetBy(0,0,-1)), grid.getValue(ijk) ); } - + // stencil access version template<typename Stencil> static typename Stencil::ValueType inX(const Stencil& S) { return difference( S.template getValue<-2, 0, 0>(), - S.template getValue<-1, 0, 0>(), + S.template getValue<-1, 0, 0>(), S.template getValue< 0, 0, 0>() ); } @@ -985,7 +1015,7 @@ struct D1<BD_2ND> static typename Stencil::ValueType inY(const Stencil& S) { return difference( S.template getValue< 0,-2, 0>(), - S.template getValue< 0,-1, 0>(), + S.template getValue< 0,-1, 0>(), S.template getValue< 0, 0, 0>() ); } @@ -993,7 +1023,7 @@ struct D1<BD_2ND> static typename Stencil::ValueType inZ(const Stencil& S) { return difference( S.template getValue< 0, 0,-2>(), - S.template getValue< 0, 0,-1>(), + S.template getValue< 0, 0,-1>(), S.template getValue< 0, 0, 0>() ); } }; @@ -1002,13 +1032,15 @@ struct D1<BD_2ND> template<> struct D1<BD_3RD> { - + // the difference opperator template <typename ValueType> - static ValueType difference(const ValueType& xm3, const ValueType& xm2, const ValueType& xm1, const ValueType& xm0){ + static ValueType difference(const ValueType& xm3, const ValueType& xm2, + const ValueType& xm1, const ValueType& xm0) + { return -D1<FD_3RD>::difference(xm3, xm2, xm1, xm0); } - + // random access version template<typename Accessor> static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) @@ -1036,7 +1068,7 @@ struct D1<BD_3RD> grid.getValue(ijk.offsetBy( 0, 0,-1)), grid.getValue(ijk) ); } - + // stencil access version template<typename Stencil> static typename Stencil::ValueType inX(const Stencil& S) @@ -1072,7 +1104,7 @@ struct D1<FD_WENO5> { // the difference opperator template <typename ValueType> - static ValueType difference(const ValueType& xp3, const ValueType& xp2, + static ValueType difference(const ValueType& xp3, const ValueType& xp2, const ValueType& xp1, const ValueType& xp0, const ValueType& xm1, const ValueType& xm2) { return WENO5<ValueType>(xp3, xp2, xp1, xp0, xm1) @@ -1130,23 +1162,23 @@ struct D1<FD_WENO5> template<typename Stencil> static typename Stencil::ValueType inX(const Stencil& S) { - - return difference( S.template getValue< 3, 0, 0>(), - S.template getValue< 2, 0, 0>(), - S.template getValue< 1, 0, 0>(), - S.template getValue< 0, 0, 0>(), + + return difference( S.template getValue< 3, 0, 0>(), + S.template getValue< 2, 0, 0>(), + S.template getValue< 1, 0, 0>(), + S.template getValue< 0, 0, 0>(), S.template getValue<-1, 0, 0>(), S.template getValue<-2, 0, 0>() ); - + } template<typename Stencil> static typename Stencil::ValueType inY(const Stencil& S) { - return difference( S.template getValue< 0, 3, 0>(), - S.template getValue< 0, 2, 0>(), - S.template getValue< 0, 1, 0>(), - S.template getValue< 0, 0, 0>(), + return difference( S.template getValue< 0, 3, 0>(), + S.template getValue< 0, 2, 0>(), + S.template getValue< 0, 1, 0>(), + S.template getValue< 0, 0, 0>(), S.template getValue< 0,-1, 0>(), S.template getValue< 0,-2, 0>() ); } @@ -1154,11 +1186,11 @@ struct D1<FD_WENO5> template<typename Stencil> static typename Stencil::ValueType inZ(const Stencil& S) { - - return difference( S.template getValue< 0, 0, 3>(), - S.template getValue< 0, 0, 2>(), - S.template getValue< 0, 0, 1>(), - S.template getValue< 0, 0, 0>(), + + return difference( S.template getValue< 0, 0, 3>(), + S.template getValue< 0, 0, 2>(), + S.template getValue< 0, 0, 1>(), + S.template getValue< 0, 0, 0>(), S.template getValue< 0, 0,-1>(), S.template getValue< 0, 0,-2>() ); } @@ -1167,10 +1199,10 @@ struct D1<FD_WENO5> template<> struct D1<FD_HJWENO5> { - + // the difference opperator template <typename ValueType> - static ValueType difference(const ValueType& xp3, const ValueType& xp2, + static ValueType difference(const ValueType& xp3, const ValueType& xp2, const ValueType& xp1, const ValueType& xp0, const ValueType& xm1, const ValueType& xm2) { return WENO5<ValueType>(xp3 - xp2, xp2 - xp1, xp1 - xp0, xp0-xm1, xm1-xm2); @@ -1227,23 +1259,23 @@ struct D1<FD_HJWENO5> template<typename Stencil> static typename Stencil::ValueType inX(const Stencil& S) { - - return difference( S.template getValue< 3, 0, 0>(), - S.template getValue< 2, 0, 0>(), - S.template getValue< 1, 0, 0>(), - S.template getValue< 0, 0, 0>(), + + return difference( S.template getValue< 3, 0, 0>(), + S.template getValue< 2, 0, 0>(), + S.template getValue< 1, 0, 0>(), + S.template getValue< 0, 0, 0>(), S.template getValue<-1, 0, 0>(), S.template getValue<-2, 0, 0>() ); - + } template<typename Stencil> static typename Stencil::ValueType inY(const Stencil& S) { - return difference( S.template getValue< 0, 3, 0>(), - S.template getValue< 0, 2, 0>(), - S.template getValue< 0, 1, 0>(), - S.template getValue< 0, 0, 0>(), + return difference( S.template getValue< 0, 3, 0>(), + S.template getValue< 0, 2, 0>(), + S.template getValue< 0, 1, 0>(), + S.template getValue< 0, 0, 0>(), S.template getValue< 0,-1, 0>(), S.template getValue< 0,-2, 0>() ); } @@ -1251,28 +1283,29 @@ struct D1<FD_HJWENO5> template<typename Stencil> static typename Stencil::ValueType inZ(const Stencil& S) { - - return difference( S.template getValue< 0, 0, 3>(), - S.template getValue< 0, 0, 2>(), - S.template getValue< 0, 0, 1>(), - S.template getValue< 0, 0, 0>(), + + return difference( S.template getValue< 0, 0, 3>(), + S.template getValue< 0, 0, 2>(), + S.template getValue< 0, 0, 1>(), + S.template getValue< 0, 0, 0>(), S.template getValue< 0, 0,-1>(), S.template getValue< 0, 0,-2>() ); } - + }; template<> struct D1<BD_WENO5> { - + template<typename ValueType> static ValueType difference(const ValueType& xm3, const ValueType& xm2, const ValueType& xm1, - const ValueType& xm0, const ValueType& xp1, const ValueType& xp2) { + const ValueType& xm0, const ValueType& xp1, const ValueType& xp2) + { return -D1<FD_WENO5>::difference(xm3, xm2, xm1, xm0, xp1, xp2); } - - + + // random access version template<typename Accessor> static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) @@ -1372,7 +1405,8 @@ struct D1<BD_HJWENO5> { template<typename ValueType> static ValueType difference(const ValueType& xm3, const ValueType& xm2, const ValueType& xm1, - const ValueType& xm0, const ValueType& xp1, const ValueType& xp2) { + const ValueType& xm0, const ValueType& xp1, const ValueType& xp2) + { return -D1<FD_HJWENO5>::difference(xm3, xm2, xm1, xm0, xp1, xp2); } @@ -1475,23 +1509,26 @@ struct D1Vec { // random access version template<typename Accessor> - static typename Accessor::ValueType::value_type inX(const Accessor& grid, const Coord& ijk, int n) + static typename Accessor::ValueType::value_type + inX(const Accessor& grid, const Coord& ijk, int n) { return D1<DiffScheme>::inX(grid, ijk)[n]; } template<typename Accessor> - static typename Accessor::ValueType::value_type inY(const Accessor& grid, const Coord& ijk, int n) + static typename Accessor::ValueType::value_type + inY(const Accessor& grid, const Coord& ijk, int n) { return D1<DiffScheme>::inY(grid, ijk)[n]; } template<typename Accessor> - static typename Accessor::ValueType::value_type inZ(const Accessor& grid, const Coord& ijk, int n) + static typename Accessor::ValueType::value_type + inZ(const Accessor& grid, const Coord& ijk, int n) { return D1<DiffScheme>::inZ(grid, ijk)[n]; } - + // stencil access version template<typename Stencil> static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) @@ -1519,45 +1556,48 @@ struct D1Vec<CD_2NDT> // random access version template<typename Accessor> - static typename Accessor::ValueType::value_type inX(const Accessor& grid, const Coord& ijk, int n) + static typename Accessor::ValueType::value_type + inX(const Accessor& grid, const Coord& ijk, int n) { - return D1<CD_2NDT>::difference( grid.getValue(ijk.offsetBy( 1, 0, 0))[n], + return D1<CD_2NDT>::difference( grid.getValue(ijk.offsetBy( 1, 0, 0))[n], grid.getValue(ijk.offsetBy(-1, 0, 0))[n] ); } template<typename Accessor> - static typename Accessor::ValueType::value_type inY(const Accessor& grid, const Coord& ijk, int n) + static typename Accessor::ValueType::value_type + inY(const Accessor& grid, const Coord& ijk, int n) { - return D1<CD_2NDT>::difference( grid.getValue(ijk.offsetBy(0, 1, 0))[n], + return D1<CD_2NDT>::difference( grid.getValue(ijk.offsetBy(0, 1, 0))[n], grid.getValue(ijk.offsetBy(0,-1, 0))[n] ); } template<typename Accessor> - static typename Accessor::ValueType::value_type inZ(const Accessor& grid, const Coord& ijk, int n) + static typename Accessor::ValueType::value_type + inZ(const Accessor& grid, const Coord& ijk, int n) { - return D1<CD_2NDT>::difference( grid.getValue(ijk.offsetBy(0, 0, 1))[n], + return D1<CD_2NDT>::difference( grid.getValue(ijk.offsetBy(0, 0, 1))[n], grid.getValue(ijk.offsetBy(0, 0,-1))[n] ); } - + // stencil access version template<typename Stencil> static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) { - return D1<CD_2NDT>::difference( S.template getValue< 1, 0, 0>()[n], + return D1<CD_2NDT>::difference( S.template getValue< 1, 0, 0>()[n], S.template getValue<-1, 0, 0>()[n] ); } template<typename Stencil> static typename Stencil::ValueType::value_type inY(const Stencil& S, int n) { - return D1<CD_2NDT>::difference( S.template getValue< 0, 1, 0>()[n], + return D1<CD_2NDT>::difference( S.template getValue< 0, 1, 0>()[n], S.template getValue< 0,-1, 0>()[n] ); } template<typename Stencil> static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n) { - return D1<CD_2NDT>::difference( S.template getValue< 0, 0, 1>()[n], + return D1<CD_2NDT>::difference( S.template getValue< 0, 0, 1>()[n], S.template getValue< 0, 0,-1>()[n] ); } }; @@ -1568,27 +1608,30 @@ struct D1Vec<CD_2ND> // random access version template<typename Accessor> - static typename Accessor::ValueType::value_type inX(const Accessor& grid, const Coord& ijk, int n) + static typename Accessor::ValueType::value_type + inX(const Accessor& grid, const Coord& ijk, int n) { return D1<CD_2ND>::difference( grid.getValue(ijk.offsetBy( 1, 0, 0))[n] , grid.getValue(ijk.offsetBy(-1, 0, 0))[n] ); - } + } template<typename Accessor> - static typename Accessor::ValueType::value_type inY(const Accessor& grid, const Coord& ijk, int n) + static typename Accessor::ValueType::value_type + inY(const Accessor& grid, const Coord& ijk, int n) { return D1<CD_2ND>::difference( grid.getValue(ijk.offsetBy(0, 1, 0))[n] , grid.getValue(ijk.offsetBy(0,-1, 0))[n] ); } template<typename Accessor> - static typename Accessor::ValueType::value_type inZ(const Accessor& grid, const Coord& ijk, int n) + static typename Accessor::ValueType::value_type + inZ(const Accessor& grid, const Coord& ijk, int n) { return D1<CD_2ND>::difference( grid.getValue(ijk.offsetBy(0, 0, 1))[n] , grid.getValue(ijk.offsetBy(0, 0,-1))[n] ); } - + // stencil access version template<typename Stencil> static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) @@ -1616,47 +1659,63 @@ struct D1Vec<CD_2ND> template<> struct D1Vec<CD_4TH> { // typedef typename Accessor::ValueType::value_type value_type; - - + + // random access version template<typename Accessor> - static typename Accessor::ValueType::value_type inX(const Accessor& grid, const Coord& ijk, int n) { - return D1<CD_4TH>::difference(grid.getValue(ijk.offsetBy(2, 0, 0))[n], grid.getValue(ijk.offsetBy( 1, 0, 0))[n], - grid.getValue(ijk.offsetBy(-1,0, 0))[n], grid.getValue(ijk.offsetBy(-2, 0, 0))[n]); + static typename Accessor::ValueType::value_type + inX(const Accessor& grid, const Coord& ijk, int n) + { + return D1<CD_4TH>::difference( + grid.getValue(ijk.offsetBy(2, 0, 0))[n], grid.getValue(ijk.offsetBy( 1, 0, 0))[n], + grid.getValue(ijk.offsetBy(-1,0, 0))[n], grid.getValue(ijk.offsetBy(-2, 0, 0))[n]); } - + template<typename Accessor> - static typename Accessor::ValueType::value_type inY(const Accessor& grid, const Coord& ijk, int n) { - return D1<CD_4TH>::difference(grid.getValue(ijk.offsetBy( 0, 2, 0))[n], grid.getValue(ijk.offsetBy( 0, 1, 0))[n], - grid.getValue(ijk.offsetBy( 0,-1, 0))[n], grid.getValue(ijk.offsetBy( 0,-2, 0))[n]); + static typename Accessor::ValueType::value_type + inY(const Accessor& grid, const Coord& ijk, int n) + { + return D1<CD_4TH>::difference( + grid.getValue(ijk.offsetBy( 0, 2, 0))[n], grid.getValue(ijk.offsetBy( 0, 1, 0))[n], + grid.getValue(ijk.offsetBy( 0,-1, 0))[n], grid.getValue(ijk.offsetBy( 0,-2, 0))[n]); } template<typename Accessor> - static typename Accessor::ValueType::value_type inZ(const Accessor& grid, const Coord& ijk, int n) { - return D1<CD_4TH>::difference(grid.getValue(ijk.offsetBy(0,0, 2))[n], grid.getValue(ijk.offsetBy( 0, 0, 1))[n], - grid.getValue(ijk.offsetBy(0,0,-1))[n], grid.getValue(ijk.offsetBy( 0, 0,-2))[n]); + static typename Accessor::ValueType::value_type + inZ(const Accessor& grid, const Coord& ijk, int n) + { + return D1<CD_4TH>::difference( + grid.getValue(ijk.offsetBy(0,0, 2))[n], grid.getValue(ijk.offsetBy( 0, 0, 1))[n], + grid.getValue(ijk.offsetBy(0,0,-1))[n], grid.getValue(ijk.offsetBy( 0, 0,-2))[n]); } - + // stencil access version template<typename Stencil> - static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) { - return D1<CD_4TH>::difference(S.template getValue< 2, 0, 0>()[n], S.template getValue< 1, 0, 0>()[n], - S.template getValue<-1, 0, 0>()[n], S.template getValue<-2, 0, 0>()[n] ); + static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) + { + return D1<CD_4TH>::difference( + S.template getValue< 2, 0, 0>()[n], S.template getValue< 1, 0, 0>()[n], + S.template getValue<-1, 0, 0>()[n], S.template getValue<-2, 0, 0>()[n] ); } - + template<typename Stencil> - static typename Stencil::ValueType::value_type inY(const Stencil& S, int n) { - return D1<CD_4TH>::difference(S.template getValue< 0, 2, 0>()[n], S.template getValue< 0, 1, 0>()[n], - S.template getValue< 0,-1, 0>()[n], S.template getValue< 0,-2, 0>()[n]); + static typename Stencil::ValueType::value_type inY(const Stencil& S, int n) + { + return D1<CD_4TH>::difference( + S.template getValue< 0, 2, 0>()[n], S.template getValue< 0, 1, 0>()[n], + S.template getValue< 0,-1, 0>()[n], S.template getValue< 0,-2, 0>()[n]); } template<typename Stencil> - static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n) { - return D1<CD_4TH>::difference(S.template getValue< 0, 0, 2>()[n], S.template getValue< 0, 0, 1>()[n], - S.template getValue< 0, 0,-1>()[n], S.template getValue< 0, 0,-2>()[n]); + static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n) + { + return D1<CD_4TH>::difference( + S.template getValue< 0, 0, 2>()[n], S.template getValue< 0, 0, 1>()[n], + S.template getValue< 0, 0,-1>()[n], S.template getValue< 0, 0,-2>()[n]); } }; + template<> struct D1Vec<CD_6TH> { @@ -1664,55 +1723,62 @@ struct D1Vec<CD_6TH> // random access version template<typename Accessor> - static typename Accessor::ValueType::value_type inX(const Accessor& grid, const Coord& ijk, int n) + static typename Accessor::ValueType::value_type + inX(const Accessor& grid, const Coord& ijk, int n) { - return D1<CD_6TH>::difference( grid.getValue(ijk.offsetBy( 3, 0, 0))[n], grid.getValue(ijk.offsetBy( 2, 0, 0))[n], - grid.getValue(ijk.offsetBy( 1, 0, 0))[n], grid.getValue(ijk.offsetBy(-1, 0, 0))[n], - grid.getValue(ijk.offsetBy(-2, 0, 0))[n], grid.getValue(ijk.offsetBy(-3, 0, 0))[n] ); + return D1<CD_6TH>::difference( + grid.getValue(ijk.offsetBy( 3, 0, 0))[n], grid.getValue(ijk.offsetBy( 2, 0, 0))[n], + grid.getValue(ijk.offsetBy( 1, 0, 0))[n], grid.getValue(ijk.offsetBy(-1, 0, 0))[n], + grid.getValue(ijk.offsetBy(-2, 0, 0))[n], grid.getValue(ijk.offsetBy(-3, 0, 0))[n] ); } template<typename Accessor> - static typename Accessor::ValueType::value_type inY(const Accessor& grid, const Coord& ijk, int n) + static typename Accessor::ValueType::value_type + inY(const Accessor& grid, const Coord& ijk, int n) { - - return D1<CD_6TH>::difference( grid.getValue(ijk.offsetBy( 0, 3, 0))[n], grid.getValue(ijk.offsetBy( 0, 2, 0))[n], - grid.getValue(ijk.offsetBy( 0, 1, 0))[n], grid.getValue(ijk.offsetBy( 0,-1, 0))[n], - grid.getValue(ijk.offsetBy( 0,-2, 0))[n], grid.getValue(ijk.offsetBy( 0,-3, 0))[n] ); + return D1<CD_6TH>::difference( + grid.getValue(ijk.offsetBy( 0, 3, 0))[n], grid.getValue(ijk.offsetBy( 0, 2, 0))[n], + grid.getValue(ijk.offsetBy( 0, 1, 0))[n], grid.getValue(ijk.offsetBy( 0,-1, 0))[n], + grid.getValue(ijk.offsetBy( 0,-2, 0))[n], grid.getValue(ijk.offsetBy( 0,-3, 0))[n] ); } template<typename Accessor> - static typename Accessor::ValueType::value_type inZ(const Accessor& grid, const Coord& ijk, int n) + static typename Accessor::ValueType::value_type + inZ(const Accessor& grid, const Coord& ijk, int n) { - - return D1<CD_6TH>::difference( grid.getValue(ijk.offsetBy( 0, 0, 3))[n], grid.getValue(ijk.offsetBy( 0, 0, 2))[n], - grid.getValue(ijk.offsetBy( 0, 0, 1))[n], grid.getValue(ijk.offsetBy( 0, 0,-1))[n], - grid.getValue(ijk.offsetBy( 0, 0,-2))[n], grid.getValue(ijk.offsetBy( 0, 0,-3))[n] ); + return D1<CD_6TH>::difference( + grid.getValue(ijk.offsetBy( 0, 0, 3))[n], grid.getValue(ijk.offsetBy( 0, 0, 2))[n], + grid.getValue(ijk.offsetBy( 0, 0, 1))[n], grid.getValue(ijk.offsetBy( 0, 0,-1))[n], + grid.getValue(ijk.offsetBy( 0, 0,-2))[n], grid.getValue(ijk.offsetBy( 0, 0,-3))[n] ); } - + // stencil access version template<typename Stencil> static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) { - return D1<CD_6TH>::difference( S.template getValue< 3, 0, 0>()[n], S.template getValue< 2, 0, 0>()[n], - S.template getValue< 1, 0, 0>()[n], S.template getValue<-1, 0, 0>()[n], - S.template getValue<-2, 0, 0>()[n], S.template getValue<-3, 0, 0>()[n] ); + return D1<CD_6TH>::difference( + S.template getValue< 3, 0, 0>()[n], S.template getValue< 2, 0, 0>()[n], + S.template getValue< 1, 0, 0>()[n], S.template getValue<-1, 0, 0>()[n], + S.template getValue<-2, 0, 0>()[n], S.template getValue<-3, 0, 0>()[n] ); } template<typename Stencil> static typename Stencil::ValueType::value_type inY(const Stencil& S, int n) - { - return D1<CD_6TH>::difference( S.template getValue< 0, 3, 0>()[n], S.template getValue< 0, 2, 0>()[n], - S.template getValue< 0, 1, 0>()[n], S.template getValue< 0,-1, 0>()[n], - S.template getValue< 0,-2, 0>()[n], S.template getValue< 0,-3, 0>()[n] ); + { + return D1<CD_6TH>::difference( + S.template getValue< 0, 3, 0>()[n], S.template getValue< 0, 2, 0>()[n], + S.template getValue< 0, 1, 0>()[n], S.template getValue< 0,-1, 0>()[n], + S.template getValue< 0,-2, 0>()[n], S.template getValue< 0,-3, 0>()[n] ); } - + template<typename Stencil> static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n) - { - return D1<CD_6TH>::difference( S.template getValue< 0, 0, 3>()[n], S.template getValue< 0, 0, 2>()[n], - S.template getValue< 0, 0, 1>()[n], S.template getValue< 0, 0,-1>()[n], - S.template getValue< 0, 0,-2>()[n], S.template getValue< 0, 0,-3>()[n] ); + { + return D1<CD_6TH>::difference( + S.template getValue< 0, 0, 3>()[n], S.template getValue< 0, 0, 2>()[n], + S.template getValue< 0, 0, 1>()[n], S.template getValue< 0, 0,-1>()[n], + S.template getValue< 0, 0,-2>()[n], S.template getValue< 0, 0,-3>()[n] ); } }; @@ -1737,7 +1803,7 @@ struct D2 template<typename Accessor> static typename Accessor::ValueType inYandZ(const Accessor& grid, const Coord& ijk); - + // stencil access version template<typename Stencil> static typename Stencil::ValueType inX(const Stencil& S); @@ -1760,16 +1826,18 @@ struct D2 template<> struct D2<CD_SECOND> { - + // the difference opperator template <typename ValueType> - static ValueType difference(const ValueType& xp1, const ValueType& xp0, const ValueType& xm1) { + static ValueType difference(const ValueType& xp1, const ValueType& xp0, const ValueType& xm1) + { return xp1 + xm1 - ValueType(2)*xp0; } - + template <typename ValueType> - static ValueType crossdifference(const ValueType& xpyp, const ValueType& xpym, - const ValueType& xmyp, const ValueType& xmym) { + static ValueType crossdifference(const ValueType& xpyp, const ValueType& xpym, + const ValueType& xmyp, const ValueType& xmym) + { return ValueType(0.25)*(xpyp + xmym - xpym - xmyp); } @@ -1777,22 +1845,22 @@ struct D2<CD_SECOND> template<typename Accessor> static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) { - return difference( grid.getValue(ijk.offsetBy( 1,0,0)), grid.getValue(ijk), + return difference( grid.getValue(ijk.offsetBy( 1,0,0)), grid.getValue(ijk), grid.getValue(ijk.offsetBy(-1,0,0)) ); } template<typename Accessor> static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) { - - return difference( grid.getValue(ijk.offsetBy(0, 1,0)), grid.getValue(ijk), + + return difference( grid.getValue(ijk.offsetBy(0, 1,0)), grid.getValue(ijk), grid.getValue(ijk.offsetBy(0,-1,0)) ); } template<typename Accessor> static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) { - return difference( grid.getValue(ijk.offsetBy( 0,0, 1)), grid.getValue(ijk), + return difference( grid.getValue(ijk.offsetBy( 0,0, 1)), grid.getValue(ijk), grid.getValue(ijk.offsetBy( 0,0,-1)) ); } @@ -1800,31 +1868,34 @@ struct D2<CD_SECOND> template<typename Accessor> static typename Accessor::ValueType inXandY(const Accessor& grid, const Coord& ijk) { - return crossdifference(grid.getValue(ijk.offsetBy(1, 1,0)), grid.getValue(ijk.offsetBy( 1,-1,0)), - grid.getValue(ijk.offsetBy(-1,1,0)), grid.getValue(ijk.offsetBy(-1,-1,0))); - + return crossdifference( + grid.getValue(ijk.offsetBy(1, 1,0)), grid.getValue(ijk.offsetBy( 1,-1,0)), + grid.getValue(ijk.offsetBy(-1,1,0)), grid.getValue(ijk.offsetBy(-1,-1,0))); + } template<typename Accessor> static typename Accessor::ValueType inXandZ(const Accessor& grid, const Coord& ijk) { - return crossdifference(grid.getValue(ijk.offsetBy(1,0, 1)), grid.getValue(ijk.offsetBy(1, 0,-1)), - grid.getValue(ijk.offsetBy(-1,0,1)), grid.getValue(ijk.offsetBy(-1,0,-1)) ); + return crossdifference( + grid.getValue(ijk.offsetBy(1,0, 1)), grid.getValue(ijk.offsetBy(1, 0,-1)), + grid.getValue(ijk.offsetBy(-1,0,1)), grid.getValue(ijk.offsetBy(-1,0,-1)) ); } template<typename Accessor> static typename Accessor::ValueType inYandZ(const Accessor& grid, const Coord& ijk) { - return crossdifference(grid.getValue(ijk.offsetBy(0, 1,1)), grid.getValue(ijk.offsetBy(0, 1,-1)), - grid.getValue(ijk.offsetBy(0,-1,1)), grid.getValue(ijk.offsetBy(0,-1,-1)) ); + return crossdifference( + grid.getValue(ijk.offsetBy(0, 1,1)), grid.getValue(ijk.offsetBy(0, 1,-1)), + grid.getValue(ijk.offsetBy(0,-1,1)), grid.getValue(ijk.offsetBy(0,-1,-1)) ); } - + // stencil access version template<typename Stencil> static typename Stencil::ValueType inX(const Stencil& S) { - return difference( S.template getValue< 1, 0, 0>(), S.template getValue< 0, 0, 0>(), + return difference( S.template getValue< 1, 0, 0>(), S.template getValue< 0, 0, 0>(), S.template getValue<-1, 0, 0>() ); } @@ -1869,14 +1940,14 @@ struct D2<CD_SECOND> template<> struct D2<CD_FOURTH> { - + // the difference opperator template <typename ValueType> static ValueType difference(const ValueType& xp2, const ValueType& xp1, const ValueType& xp0, const ValueType& xm1, const ValueType& xm2) { return ValueType(-1./12.)*(xp2 + xm2) + ValueType(4./3.)*(xp1 + xm1) -ValueType(2.5)*xp0; } - + template <typename ValueType> static ValueType crossdifference(const ValueType& xp2yp2, const ValueType& xp2yp1, const ValueType& xp2ym1, const ValueType& xp2ym2, @@ -1886,41 +1957,44 @@ struct D2<CD_FOURTH> const ValueType& xm2ym1, const ValueType& xm2ym2, const ValueType& xm1yp2, const ValueType& xm1yp1, const ValueType& xm1ym1, const ValueType& xm1ym2 ) { - ValueType tmp1 = + ValueType tmp1 = ValueType(2./3.0)*(xp1yp1 - xm1yp1 - xp1ym1 + xm1ym1)- ValueType(1./12.)*(xp2yp1 - xm2yp1 - xp2ym1 + xm2ym1); ValueType tmp2 = ValueType(2./3.0)*(xp1yp2 - xm1yp2 - xp1ym2 + xm1ym2)- - ValueType(1./12.)*(xp2yp2 - xm2yp2 - xp2ym2 + xm2ym2); - + ValueType(1./12.)*(xp2yp2 - xm2yp2 - xp2ym2 + xm2ym2); + return ValueType(2./3.)*tmp1 - ValueType(1./12.)*tmp2; } - - + + // random access version template<typename Accessor> static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) { - return difference(grid.getValue(ijk.offsetBy(2,0,0)), grid.getValue(ijk.offsetBy( 1,0,0)), - grid.getValue(ijk), - grid.getValue(ijk.offsetBy(-1,0,0)), grid.getValue(ijk.offsetBy(-2, 0, 0))); + return difference( + grid.getValue(ijk.offsetBy(2,0,0)), grid.getValue(ijk.offsetBy( 1,0,0)), + grid.getValue(ijk), + grid.getValue(ijk.offsetBy(-1,0,0)), grid.getValue(ijk.offsetBy(-2, 0, 0))); } template<typename Accessor> static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) { - return difference(grid.getValue(ijk.offsetBy(0, 2,0)), grid.getValue(ijk.offsetBy(0, 1,0)), - grid.getValue(ijk), - grid.getValue(ijk.offsetBy(0,-1,0)), grid.getValue(ijk.offsetBy(0,-2, 0))); + return difference( + grid.getValue(ijk.offsetBy(0, 2,0)), grid.getValue(ijk.offsetBy(0, 1,0)), + grid.getValue(ijk), + grid.getValue(ijk.offsetBy(0,-1,0)), grid.getValue(ijk.offsetBy(0,-2, 0))); } template<typename Accessor> static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) { - return difference(grid.getValue(ijk.offsetBy(0,0, 2)), grid.getValue(ijk.offsetBy(0, 0,1)), - grid.getValue(ijk), - grid.getValue(ijk.offsetBy(0,0,-1)), grid.getValue(ijk.offsetBy(0,0,-2))); + return difference( + grid.getValue(ijk.offsetBy(0,0, 2)), grid.getValue(ijk.offsetBy(0, 0,1)), + grid.getValue(ijk), + grid.getValue(ijk.offsetBy(0,0,-1)), grid.getValue(ijk.offsetBy(0,0,-2))); } // cross derivatives @@ -1928,10 +2002,10 @@ struct D2<CD_FOURTH> static typename Accessor::ValueType inXandY(const Accessor& grid, const Coord& ijk) { typedef typename Accessor::ValueType ValueType; - typename Accessor::ValueType tmp1 = + typename Accessor::ValueType tmp1 = D1<CD_4TH>::inX(grid, ijk.offsetBy(0, 1, 0)) - D1<CD_4TH>::inX(grid, ijk.offsetBy(0,-1, 0)); - typename Accessor::ValueType tmp2 = + typename Accessor::ValueType tmp2 = D1<CD_4TH>::inX(grid, ijk.offsetBy(0, 2, 0)) - D1<CD_4TH>::inX(grid, ijk.offsetBy(0,-2, 0)); return ValueType(2./3.)*tmp1 - ValueType(1./12.)*tmp2; @@ -1941,10 +2015,10 @@ struct D2<CD_FOURTH> static typename Accessor::ValueType inXandZ(const Accessor& grid, const Coord& ijk) { typedef typename Accessor::ValueType ValueType; - typename Accessor::ValueType tmp1 = + typename Accessor::ValueType tmp1 = D1<CD_4TH>::inX(grid, ijk.offsetBy(0, 0, 1)) - D1<CD_4TH>::inX(grid, ijk.offsetBy(0, 0,-1)); - typename Accessor::ValueType tmp2 = + typename Accessor::ValueType tmp2 = D1<CD_4TH>::inX(grid, ijk.offsetBy(0, 0, 2)) - D1<CD_4TH>::inX(grid, ijk.offsetBy(0, 0,-2)); return ValueType(2./3.)*tmp1 - ValueType(1./12.)*tmp2; @@ -1954,38 +2028,38 @@ struct D2<CD_FOURTH> static typename Accessor::ValueType inYandZ(const Accessor& grid, const Coord& ijk) { typedef typename Accessor::ValueType ValueType; - typename Accessor::ValueType tmp1 = + typename Accessor::ValueType tmp1 = D1<CD_4TH>::inY(grid, ijk.offsetBy(0, 0, 1)) - D1<CD_4TH>::inY(grid, ijk.offsetBy(0, 0,-1)); - typename Accessor::ValueType tmp2 = + typename Accessor::ValueType tmp2 = D1<CD_4TH>::inY(grid, ijk.offsetBy(0, 0, 2)) - D1<CD_4TH>::inY(grid, ijk.offsetBy(0, 0,-2)); return ValueType(2./3.)*tmp1 - ValueType(1./12.)*tmp2; } - - // stencil access version + + // stencil access version template<typename Stencil> static typename Stencil::ValueType inX(const Stencil& S) { - return difference(S.template getValue< 2, 0, 0>(), S.template getValue< 1, 0, 0>(), - S.template getValue< 0, 0, 0>(), + return difference(S.template getValue< 2, 0, 0>(), S.template getValue< 1, 0, 0>(), + S.template getValue< 0, 0, 0>(), S.template getValue<-1, 0, 0>(), S.template getValue<-2, 0, 0>() ); } template<typename Stencil> static typename Stencil::ValueType inY(const Stencil& S) { - return difference(S.template getValue< 0, 2, 0>(), S.template getValue< 0, 1, 0>(), - S.template getValue< 0, 0, 0>(), + return difference(S.template getValue< 0, 2, 0>(), S.template getValue< 0, 1, 0>(), + S.template getValue< 0, 0, 0>(), S.template getValue< 0,-1, 0>(), S.template getValue< 0,-2, 0>() ); } template<typename Stencil> static typename Stencil::ValueType inZ(const Stencil& S) { - return difference(S.template getValue< 0, 0, 2>(), S.template getValue< 0, 0, 1>(), - S.template getValue< 0, 0, 0>(), + return difference(S.template getValue< 0, 0, 2>(), S.template getValue< 0, 0, 1>(), + S.template getValue< 0, 0, 0>(), S.template getValue< 0, 0,-1>(), S.template getValue< 0, 0,-2>() ); } @@ -1993,62 +2067,63 @@ struct D2<CD_FOURTH> template<typename Stencil> static typename Stencil::ValueType inXandY(const Stencil& S) { - return crossdifference( S.template getValue< 2, 2, 0>(), S.template getValue< 2, 1, 0>(), - S.template getValue< 2,-1, 0>(), S.template getValue< 2,-2, 0>(), - S.template getValue< 1, 2, 0>(), S.template getValue< 1, 1, 0>(), - S.template getValue< 1,-1, 0>(), S.template getValue< 1,-2, 0>(), - S.template getValue<-2, 2, 0>(), S.template getValue<-2, 1, 0>(), - S.template getValue<-2,-1, 0>(), S.template getValue<-2,-2, 0>(), - S.template getValue<-1, 2, 0>(), S.template getValue<-1, 1, 0>(), - S.template getValue<-1,-1, 0>(), S.template getValue<-1,-2, 0>() ); - } + return crossdifference( + S.template getValue< 2, 2, 0>(), S.template getValue< 2, 1, 0>(), + S.template getValue< 2,-1, 0>(), S.template getValue< 2,-2, 0>(), + S.template getValue< 1, 2, 0>(), S.template getValue< 1, 1, 0>(), + S.template getValue< 1,-1, 0>(), S.template getValue< 1,-2, 0>(), + S.template getValue<-2, 2, 0>(), S.template getValue<-2, 1, 0>(), + S.template getValue<-2,-1, 0>(), S.template getValue<-2,-2, 0>(), + S.template getValue<-1, 2, 0>(), S.template getValue<-1, 1, 0>(), + S.template getValue<-1,-1, 0>(), S.template getValue<-1,-2, 0>() ); + } template<typename Stencil> static typename Stencil::ValueType inXandZ(const Stencil& S) { - return crossdifference( S.template getValue< 2, 0, 2>(), S.template getValue< 2, 0, 1>(), - S.template getValue< 2, 0,-1>(), S.template getValue< 2, 0,-2>(), - S.template getValue< 1, 0, 2>(), S.template getValue< 1, 0, 1>(), - S.template getValue< 1, 0,-1>(), S.template getValue< 1, 0,-2>(), - S.template getValue<-2, 0, 2>(), S.template getValue<-2, 0, 1>(), - S.template getValue<-2, 0,-1>(), S.template getValue<-2, 0,-2>(), - S.template getValue<-1, 0, 2>(), S.template getValue<-1, 0, 1>(), - S.template getValue<-1, 0,-1>(), S.template getValue<-1, 0,-2>() ); + return crossdifference( + S.template getValue< 2, 0, 2>(), S.template getValue< 2, 0, 1>(), + S.template getValue< 2, 0,-1>(), S.template getValue< 2, 0,-2>(), + S.template getValue< 1, 0, 2>(), S.template getValue< 1, 0, 1>(), + S.template getValue< 1, 0,-1>(), S.template getValue< 1, 0,-2>(), + S.template getValue<-2, 0, 2>(), S.template getValue<-2, 0, 1>(), + S.template getValue<-2, 0,-1>(), S.template getValue<-2, 0,-2>(), + S.template getValue<-1, 0, 2>(), S.template getValue<-1, 0, 1>(), + S.template getValue<-1, 0,-1>(), S.template getValue<-1, 0,-2>() ); } - + template<typename Stencil> static typename Stencil::ValueType inYandZ(const Stencil& S) { - return crossdifference( S.template getValue< 0, 2, 2>(), S.template getValue< 0, 2, 1>(), - S.template getValue< 0, 2,-1>(), S.template getValue< 0, 2,-2>(), - S.template getValue< 0, 1, 2>(), S.template getValue< 0, 1, 1>(), - S.template getValue< 0, 1,-1>(), S.template getValue< 0, 1,-2>(), - S.template getValue< 0,-2, 2>(), S.template getValue< 0,-2, 1>(), - S.template getValue< 0,-2,-1>(), S.template getValue< 0,-2,-2>(), - S.template getValue< 0,-1, 2>(), S.template getValue< 0,-1, 1>(), - S.template getValue< 0,-1,-1>(), S.template getValue< 0,-1,-2>() ); + return crossdifference( + S.template getValue< 0, 2, 2>(), S.template getValue< 0, 2, 1>(), + S.template getValue< 0, 2,-1>(), S.template getValue< 0, 2,-2>(), + S.template getValue< 0, 1, 2>(), S.template getValue< 0, 1, 1>(), + S.template getValue< 0, 1,-1>(), S.template getValue< 0, 1,-2>(), + S.template getValue< 0,-2, 2>(), S.template getValue< 0,-2, 1>(), + S.template getValue< 0,-2,-1>(), S.template getValue< 0,-2,-2>(), + S.template getValue< 0,-1, 2>(), S.template getValue< 0,-1, 1>(), + S.template getValue< 0,-1,-1>(), S.template getValue< 0,-1,-2>() ); } - - - }; + template<> struct D2<CD_SIXTH> { - // the difference opperator template <typename ValueType> - static ValueType difference(const ValueType& xp3, const ValueType& xp2, const ValueType& xp1, + static ValueType difference(const ValueType& xp3, const ValueType& xp2, const ValueType& xp1, const ValueType& xp0, - const ValueType& xm1, const ValueType& xm2, const ValueType& xm3) { - return ValueType(1./90.)*(xp3 + xm3) - ValueType(3./20.)*(xp2 + xm2) + const ValueType& xm1, const ValueType& xm2, const ValueType& xm3) + { + return ValueType(1./90.)*(xp3 + xm3) - ValueType(3./20.)*(xp2 + xm2) + ValueType(1.5)*(xp1 + xm1) - ValueType(49./18.)*xp0; } - + template <typename ValueType> static ValueType crossdifference( const ValueType& xp1yp1,const ValueType& xm1yp1, - const ValueType& xp1ym1,const ValueType& xm1ym1, + const ValueType& xp1ym1,const ValueType& xm1ym1, const ValueType& xp2yp1,const ValueType& xm2yp1, const ValueType& xp2ym1,const ValueType& xm2ym1, const ValueType& xp3yp1,const ValueType& xm3yp1, @@ -2064,58 +2139,57 @@ struct D2<CD_SIXTH> const ValueType& xp2yp3,const ValueType& xm2yp3, const ValueType& xp2ym3,const ValueType& xm2ym3, const ValueType& xp3yp3,const ValueType& xm3yp3, - const ValueType& xp3ym3,const ValueType& xm3ym3 ) + const ValueType& xp3ym3,const ValueType& xm3ym3 ) { ValueType tmp1 = ValueType(0.7500)*(xp1yp1 - xm1yp1 - xp1ym1 + xm1ym1) - ValueType(0.1500)*(xp2yp1 - xm2yp1 - xp2ym1 + xm2ym1) + ValueType(1./60.)*(xp3yp1 - xm3yp1 - xp3ym1 + xm3ym1); - + ValueType tmp2 = ValueType(0.7500)*(xp1yp2 - xm1yp2 - xp1ym2 + xm1ym2) - ValueType(0.1500)*(xp2yp2 - xm2yp2 - xp2ym2 + xm2ym2) + ValueType(1./60.)*(xp3yp2 - xm3yp2 - xp3ym2 + xm3ym2); - + ValueType tmp3 = ValueType(0.7500)*(xp1yp3 - xm1yp3 - xp1ym3 + xm1ym3) - ValueType(0.1500)*(xp2yp3 - xm2yp3 - xp2ym3 + xm2ym3) + ValueType(1./60.)*(xp3yp3 - xm3yp3 - xp3ym3 + xm3ym3); - + return ValueType(0.75)*tmp1 - ValueType(0.15)*tmp2 + ValueType(1./60)*tmp3; } - + // random access version template<typename Accessor> static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) { - return difference(grid.getValue(ijk.offsetBy( 3, 0, 0)), grid.getValue(ijk.offsetBy( 2, 0, 0)), - grid.getValue(ijk.offsetBy( 1, 0, 0)), grid.getValue(ijk), - grid.getValue(ijk.offsetBy(-1, 0, 0)), grid.getValue(ijk.offsetBy(-2, 0, 0)), - grid.getValue(ijk.offsetBy(-3, 0, 0)) ); - + return difference( + grid.getValue(ijk.offsetBy( 3, 0, 0)), grid.getValue(ijk.offsetBy( 2, 0, 0)), + grid.getValue(ijk.offsetBy( 1, 0, 0)), grid.getValue(ijk), + grid.getValue(ijk.offsetBy(-1, 0, 0)), grid.getValue(ijk.offsetBy(-2, 0, 0)), + grid.getValue(ijk.offsetBy(-3, 0, 0)) ); } template<typename Accessor> static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) { - - return difference(grid.getValue(ijk.offsetBy( 0, 3, 0)), grid.getValue(ijk.offsetBy( 0, 2, 0)), - grid.getValue(ijk.offsetBy( 0, 1, 0)), grid.getValue(ijk), - grid.getValue(ijk.offsetBy( 0,-1, 0)), grid.getValue(ijk.offsetBy( 0,-2, 0)), - grid.getValue(ijk.offsetBy( 0,-3, 0)) ); - + return difference( + grid.getValue(ijk.offsetBy( 0, 3, 0)), grid.getValue(ijk.offsetBy( 0, 2, 0)), + grid.getValue(ijk.offsetBy( 0, 1, 0)), grid.getValue(ijk), + grid.getValue(ijk.offsetBy( 0,-1, 0)), grid.getValue(ijk.offsetBy( 0,-2, 0)), + grid.getValue(ijk.offsetBy( 0,-3, 0)) ); } template<typename Accessor> static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) { - - return difference(grid.getValue(ijk.offsetBy( 0, 0, 3)), grid.getValue(ijk.offsetBy( 0, 0, 2)), - grid.getValue(ijk.offsetBy( 0, 0, 1)), grid.getValue(ijk), - grid.getValue(ijk.offsetBy( 0, 0,-1)), grid.getValue(ijk.offsetBy( 0, 0,-2)), - grid.getValue(ijk.offsetBy( 0, 0,-3)) ); - + + return difference( + grid.getValue(ijk.offsetBy( 0, 0, 3)), grid.getValue(ijk.offsetBy( 0, 0, 2)), + grid.getValue(ijk.offsetBy( 0, 0, 1)), grid.getValue(ijk), + grid.getValue(ijk.offsetBy( 0, 0,-1)), grid.getValue(ijk.offsetBy( 0, 0,-2)), + grid.getValue(ijk.offsetBy( 0, 0,-3)) ); } template<typename Accessor> @@ -2124,25 +2198,25 @@ struct D2<CD_SIXTH> typename Accessor::ValueType tmp1 = D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 1, 0)) - D1<CD_6TH>::inX(grid, ijk.offsetBy(0,-1, 0)); - typename Accessor::ValueType tmp2 = + typename Accessor::ValueType tmp2 = D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 2, 0)) - D1<CD_6TH>::inX(grid, ijk.offsetBy(0,-2, 0)); - typename Accessor::ValueType tmp3 = + typename Accessor::ValueType tmp3 = D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 3, 0)) - D1<CD_6TH>::inX(grid, ijk.offsetBy(0,-3, 0)); return 0.75*tmp1 - 0.15*tmp2 + 1./60*tmp3; } - + template<typename Accessor> static typename Accessor::ValueType inXandZ(const Accessor& grid, const Coord& ijk) { - typename Accessor::ValueType tmp1 = + typename Accessor::ValueType tmp1 = D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 0, 1)) - D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 0,-1)); - typename Accessor::ValueType tmp2 = + typename Accessor::ValueType tmp2 = D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 0, 2)) - D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 0,-2)); - typename Accessor::ValueType tmp3 = + typename Accessor::ValueType tmp3 = D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 0, 3)) - D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 0,-3)); return 0.75*tmp1 - 0.15*tmp2 + 1./60*tmp3; @@ -2151,13 +2225,13 @@ struct D2<CD_SIXTH> template<typename Accessor> static typename Accessor::ValueType inYandZ(const Accessor& grid, const Coord& ijk) { - typename Accessor::ValueType tmp1 = + typename Accessor::ValueType tmp1 = D1<CD_6TH>::inY(grid, ijk.offsetBy(0, 0, 1)) - D1<CD_6TH>::inY(grid, ijk.offsetBy(0, 0,-1)); - typename Accessor::ValueType tmp2 = + typename Accessor::ValueType tmp2 = D1<CD_6TH>::inY(grid, ijk.offsetBy(0, 0, 2)) - D1<CD_6TH>::inY(grid, ijk.offsetBy(0, 0,-2)); - typename Accessor::ValueType tmp3 = + typename Accessor::ValueType tmp3 = D1<CD_6TH>::inY(grid, ijk.offsetBy(0, 0, 3)) - D1<CD_6TH>::inY(grid, ijk.offsetBy(0, 0,-3)); return 0.75*tmp1 - 0.15*tmp2 + 1./60*tmp3; @@ -2181,7 +2255,7 @@ struct D2<CD_SIXTH> S.template getValue< 0, 1, 0>(), S.template getValue< 0, 0, 0>(), S.template getValue< 0,-1, 0>(), S.template getValue< 0,-2, 0>(), S.template getValue< 0,-3, 0>() ); - + } template<typename Stencil> @@ -2197,7 +2271,7 @@ struct D2<CD_SIXTH> static typename Stencil::ValueType inXandY(const Stencil& S) { return crossdifference( S.template getValue< 1, 1, 0>(), S.template getValue<-1, 1, 0>(), - S.template getValue< 1,-1, 0>(), S.template getValue<-1,-1, 0>(), + S.template getValue< 1,-1, 0>(), S.template getValue<-1,-1, 0>(), S.template getValue< 2, 1, 0>(), S.template getValue<-2, 1, 0>(), S.template getValue< 2,-1, 0>(), S.template getValue<-2,-1, 0>(), S.template getValue< 3, 1, 0>(), S.template getValue<-3, 1, 0>(), @@ -2214,14 +2288,13 @@ struct D2<CD_SIXTH> S.template getValue< 2,-3, 0>(), S.template getValue<-2,-3, 0>(), S.template getValue< 3, 3, 0>(), S.template getValue<-3, 3, 0>(), S.template getValue< 3,-3, 0>(), S.template getValue<-3,-3, 0>() ); - } template<typename Stencil> static typename Stencil::ValueType inXandZ(const Stencil& S) { return crossdifference( S.template getValue< 1, 0, 1>(), S.template getValue<-1, 0, 1>(), - S.template getValue< 1, 0,-1>(), S.template getValue<-1, 0,-1>(), + S.template getValue< 1, 0,-1>(), S.template getValue<-1, 0,-1>(), S.template getValue< 2, 0, 1>(), S.template getValue<-2, 0, 1>(), S.template getValue< 2, 0,-1>(), S.template getValue<-2, 0,-1>(), S.template getValue< 3, 0, 1>(), S.template getValue<-3, 0, 1>(), @@ -2238,14 +2311,13 @@ struct D2<CD_SIXTH> S.template getValue< 2, 0,-3>(), S.template getValue<-2, 0,-3>(), S.template getValue< 3, 0, 3>(), S.template getValue<-3, 0, 3>(), S.template getValue< 3, 0,-3>(), S.template getValue<-3, 0,-3>() ); - } - + template<typename Stencil> static typename Stencil::ValueType inYandZ(const Stencil& S) { return crossdifference( S.template getValue< 0, 1, 1>(), S.template getValue< 0,-1, 1>(), - S.template getValue< 0, 1,-1>(), S.template getValue< 0,-1,-1>(), + S.template getValue< 0, 1,-1>(), S.template getValue< 0,-1,-1>(), S.template getValue< 0, 2, 1>(), S.template getValue< 0,-2, 1>(), S.template getValue< 0, 2,-1>(), S.template getValue< 0,-2,-1>(), S.template getValue< 0, 3, 1>(), S.template getValue< 0,-3, 1>(), @@ -2266,8 +2338,6 @@ struct D2<CD_SIXTH> }; - - } // end math namespace } // namespace OPENVDB_VERSION_NAME } // end openvdb namespace @@ -2277,4 +2347,3 @@ struct D2<CD_SIXTH> // Copyright (c) 2012-2013 DreamWorks Animation LLC // All rights reserved. This software is distributed under the // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) - diff --git a/extern/openvdb/internal/openvdb/math/Hermite.h b/extern/openvdb/internal/openvdb/math/Hermite.h index 175478ce23d..edb99e6ac70 100644 --- a/extern/openvdb/internal/openvdb/math/Hermite.h +++ b/extern/openvdb/internal/openvdb/math/Hermite.h @@ -164,8 +164,8 @@ public: /// @note These methods don't perform meaningful operations on Hermite data. bool operator< (const Hermite&) const { return false; }; bool operator> (const Hermite&) const { return false; }; - template<class T> Hermite operator+(const T&) const { return *this; }; - template<class T> Hermite operator-(const T&) const { return *this; }; + template<class T> Hermite operator+(const T&) const { return *this; } + template<class T> Hermite operator-(const T&) const { return *this; } //@} private: diff --git a/extern/openvdb/internal/openvdb/math/LegacyFrustum.h b/extern/openvdb/internal/openvdb/math/LegacyFrustum.h index 056158801ef..f0e5f7bd6ec 100644 --- a/extern/openvdb/internal/openvdb/math/LegacyFrustum.h +++ b/extern/openvdb/internal/openvdb/math/LegacyFrustum.h @@ -34,6 +34,7 @@ #define OPENVDB_MATH_LEGACYFRUSTUM_HAS_BEEN_INCLUDED #include <iostream> +#include <openvdb/Types.h> // for Real typedef #include "Coord.h" #include "Mat4.h" #include "Vec3.h" @@ -53,12 +54,15 @@ public: { // First read in the old transform's base class. // the "extents" - Coord tmpMin, tmpMax; - is.read(reinterpret_cast<char*>(&tmpMin), sizeof(Coord::ValueType) * 3); - is.read(reinterpret_cast<char*>(&tmpMax), sizeof(Coord::ValueType) * 3); + Vec3i tmpMin, tmpMax; + is.read(reinterpret_cast<char*>(&tmpMin), sizeof(Vec3i::ValueType) * 3); + is.read(reinterpret_cast<char*>(&tmpMax), sizeof(Vec3i::ValueType) * 3); + + Coord tmpMinCoord(tmpMin); + Coord tmpMaxCoord(tmpMax); // set the extents - mExtents = CoordBBox(tmpMin, tmpMax); + mExtents = CoordBBox(tmpMinCoord, tmpMaxCoord); // read the old-frustum class member data //Mat4d tmpW2C; diff --git a/extern/openvdb/internal/openvdb/math/Maps.cc b/extern/openvdb/internal/openvdb/math/Maps.cc index e62a40266bf..5818cab7da7 100644 --- a/extern/openvdb/internal/openvdb/math/Maps.cc +++ b/extern/openvdb/internal/openvdb/math/Maps.cc @@ -29,80 +29,102 @@ /////////////////////////////////////////////////////////////////////////// #include "Maps.h" +#include <tbb/mutex.h> namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { namespace math { +namespace { + +typedef tbb::mutex Mutex; +typedef Mutex::scoped_lock Lock; + +// Declare this at file scope to ensure thread-safe initialization. +// NOTE: Do *NOT* move this into Maps.h or else we will need to pull in +// Windows.h with things like 'rad2' defined! +Mutex sInitMapRegistryMutex; + +} // unnamed namespace + //////////////////////////////////////// MapRegistry* MapRegistry::mInstance = NULL; -// Declare this at file scope to ensure thread-safe initialization. -tbb::mutex sInitMapRegistryMutex; +// Caller is responsible for calling this function serially. MapRegistry* -MapRegistry::instance() +MapRegistry::staticInstance() { - Lock lock(sInitMapRegistryMutex); - - if(mInstance == NULL) { + if (mInstance == NULL) { OPENVDB_START_THREADSAFE_STATIC_WRITE mInstance = new MapRegistry(); OPENVDB_FINISH_THREADSAFE_STATIC_WRITE return mInstance; } - return mInstance; } + +MapRegistry* +MapRegistry::instance() +{ + Lock lock(sInitMapRegistryMutex); + return staticInstance(); +} + + MapBase::Ptr MapRegistry::createMap(const Name& name) { - Lock lock(instance()->mMutex); - MapDictionary::const_iterator iter = instance()->mMap.find(name); + Lock lock(sInitMapRegistryMutex); + MapDictionary::const_iterator iter = staticInstance()->mMap.find(name); - if (iter == instance()->mMap.end()) { + if (iter == staticInstance()->mMap.end()) { OPENVDB_THROW(LookupError, "Cannot create map of unregistered type " << name); } return (iter->second)(); } + bool MapRegistry::isRegistered(const Name& name) { - Lock lock(instance()->mMutex); - return (instance()->mMap.find(name) != instance()->mMap.end()); + Lock lock(sInitMapRegistryMutex); + return (staticInstance()->mMap.find(name) != staticInstance()->mMap.end()); } + void MapRegistry::registerMap(const Name& name, MapBase::MapFactory factory) { - Lock lock(instance()->mMutex); + Lock lock(sInitMapRegistryMutex); - if (instance()->mMap.find(name) != instance()->mMap.end()) { + if (staticInstance()->mMap.find(name) != staticInstance()->mMap.end()) { OPENVDB_THROW(KeyError, "Map type " << name << " is already registered"); } - instance()->mMap[name] = factory; + staticInstance()->mMap[name] = factory; } + void MapRegistry::unregisterMap(const Name& name) { - Lock lock(instance()->mMutex); - instance()->mMap.erase(name); + Lock lock(sInitMapRegistryMutex); + staticInstance()->mMap.erase(name); } + void MapRegistry::clear() { - Lock lock(instance()->mMutex); - instance()->mMap.clear(); + Lock lock(sInitMapRegistryMutex); + staticInstance()->mMap.clear(); } @@ -130,7 +152,7 @@ createSymmetricMap(const Mat3d& m) UnitaryMap rotation(Umatrix); ScaleMap diagonal(eigenValues); CompoundMap<UnitaryMap, ScaleMap> first(rotation, diagonal); - + UnitaryMap rotationInv(Umatrix.transpose()); return SymmetricMap::Ptr( new SymmetricMap(first, rotationInv)); } @@ -143,7 +165,7 @@ createPolarDecomposedMap(const Mat3d& m) // we are constructing M = Symmetric * Unitary instead of the more // standard M = Unitary * Symmetric Mat3d unitary, symmetric, mat3 = m.transpose(); - + // factor mat3 = U * S where U is unitary and S is symmetric bool gotPolar = math::polarDecomposition(mat3, unitary, symmetric); if (!gotPolar) { @@ -152,7 +174,7 @@ createPolarDecomposedMap(const Mat3d& m) // put the result in a polar map and then copy it into the output polar UnitaryMap unitary_map(unitary.transpose()); SymmetricMap::Ptr symmetric_map = createSymmetricMap(symmetric); - + return PolarDecomposedMap::Ptr(new PolarDecomposedMap(*symmetric_map, unitary_map)); } @@ -167,7 +189,7 @@ createFullyDecomposedMap(const Mat4d& m) TranslationMap translate(m.getTranslation()); PolarDecomposedMap::Ptr polar = createPolarDecomposedMap(m.getMat3()); - + UnitaryAndTranslationMap rotationAndTranslate(polar->secondMap(), translate); return FullyDecomposedMap::Ptr(new FullyDecomposedMap(polar->firstMap(), rotationAndTranslate)); @@ -185,7 +207,7 @@ simplify(AffineMap::Ptr affine) } else { return MapBase::Ptr(new ScaleMap(scale)); } - + } else if (affine->isScaleTranslate()) { // can be simplified into a ScaleTranslateMap Vec3d translate = affine->applyMap(Vec3d(0,0,0)); @@ -202,26 +224,27 @@ simplify(AffineMap::Ptr affine) return boost::static_pointer_cast<MapBase, AffineMap>(affine); } -Mat4d + +Mat4d approxInverse(const Mat4d& mat4d) { - if (std::abs(mat4d.det()) >= 3 * tolerance<double>::value()) { + if (std::abs(mat4d.det()) >= 3 * math::Tolerance<double>::value()) { try { Mat4d result = mat4d.inverse(); return result; } catch (ArithmeticError& ) { - // Mat4 code couldn't invert. + // Mat4 code couldn't invert. } } const Mat3d mat3 = mat4d.getMat3(); const Mat3d mat3T = mat3.transpose(); const Vec3d trans = mat4d.getTranslation(); - + // absolute tolerance used for the symmetric test. const double tol = 1.e-6; - // only create the pseudoInverse for symmetric + // only create the pseudoInverse for symmetric bool symmetric = true; for (int i = 0; i < 3; ++i ) { for (int j = 0; j < 3; ++j ) { @@ -230,8 +253,8 @@ approxInverse(const Mat4d& mat4d) } } } - - if (!symmetric) { + + if (!symmetric) { // not symmetric, so just zero out the mat3 inverse and reverse the translation @@ -241,43 +264,39 @@ approxInverse(const Mat4d& mat4d) return result; } else { - + // compute the pseudo inverse Mat3d eigenVectors; Vec3d eigenValues; - + diagonalizeSymmetricMatrix(mat3, eigenVectors, eigenValues); - + Mat3d d = Mat3d::identity(); for (int i = 0; i < 3; ++i ) { - if (std::abs(eigenValues[i]) < 10.*tolerance<double>::value() ) { + if (std::abs(eigenValues[i]) < 10.0 * math::Tolerance<double>::value()) { d[i][i] = 0.f; } else { d[i][i] = 1.f/eigenValues[i]; } } // assemble the pseudo inverse - - Mat3d pseudoInv = eigenVectors * d * eigenVectors.transpose(); + + Mat3d pseudoInv = eigenVectors * d * eigenVectors.transpose(); Vec3d invTrans = -trans * pseudoInv; - + Mat4d result = Mat4d::identity(); result.setMat3(pseudoInv); result.setTranslation(invTrans); - + return result; } } -//////////////////////////////////////// - - } // namespace math } // namespace OPENVDB_VERSION_NAME } // namespace openvdb - // Copyright (c) 2012-2013 DreamWorks Animation LLC // All rights reserved. This software is distributed under the // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/extern/openvdb/internal/openvdb/math/Maps.h b/extern/openvdb/internal/openvdb/math/Maps.h index f1400e0c240..109d0217790 100644 --- a/extern/openvdb/internal/openvdb/math/Maps.h +++ b/extern/openvdb/internal/openvdb/math/Maps.h @@ -33,6 +33,7 @@ #ifndef OPENVDB_MATH_MAPS_HAS_BEEN_INCLUDED #define OPENVDB_MATH_MAPS_HAS_BEEN_INCLUDED +#include "Math.h" #include "Mat4.h" #include "Vec3.h" #include "BBox.h" @@ -40,7 +41,6 @@ #include <openvdb/util/Name.h> #include <openvdb/Types.h> #include <boost/shared_ptr.hpp> -#include <tbb/mutex.h> #include <map> namespace openvdb { @@ -182,11 +182,17 @@ public: virtual Vec3d applyMap(const Vec3d& in) const = 0; virtual Vec3d applyInverseMap(const Vec3d& in) const = 0; - + + //@{ + /// @brief Apply the Inverse Jacobian Transpose of this map to a vector. + /// For a linear map this is equivalent to applying the transpose of + /// inverse map excluding translation. virtual Vec3d applyIJT(const Vec3d& in) const = 0; - virtual Vec3d applyIJT(const Vec3d& in, const Vec3d& pos) const = 0; + virtual Vec3d applyIJT(const Vec3d& in, const Vec3d& domainPos) const = 0; + //@} + virtual Mat3d applyIJC(const Mat3d& m) const = 0; - virtual Mat3d applyIJC(const Mat3d& m, const Vec3d& v, const Vec3d& pos) const = 0; + virtual Mat3d applyIJC(const Mat3d& m, const Vec3d& v, const Vec3d& domainPos) const = 0; virtual double determinant() const = 0; @@ -195,6 +201,8 @@ public: //@{ /// @brief Method to return the local size of a voxel. + /// When a location is specified as an argument, it is understood to be + /// be in the domain of the map (i.e. index space) virtual Vec3d voxelSize() const = 0; virtual Vec3d voxelSize(const Vec3d&) const = 0; //@} @@ -219,6 +227,45 @@ public: virtual MapBase::Ptr postShear(double shear, Axis axis0, Axis axis1) const = 0; //@} + //@{ + /// @brief Apply the Jacobian of this map to a vector. + /// For a linear map this is equivalent to applying the map excluding translation. + /// @warning Houdini 12.5 uses an earlier version of OpenVDB, and maps created + /// with that version lack a virtual table entry for this method. Do not call + /// this method from Houdini 12.5. + virtual Vec3d applyJacobian(const Vec3d& in) const = 0; + virtual Vec3d applyJacobian(const Vec3d& in, const Vec3d& domainPos) const = 0; + //@} + + //@{ + /// @brief Apply the InverseJacobian of this map to a vector. + /// For a linear map this is equivalent to applying the map inverse excluding translation. + /// @warning Houdini 12.5 uses an earlier version of OpenVDB, and maps created + /// with that version lack a virtual table entry for this method. Do not call + /// this method from Houdini 12.5. + virtual Vec3d applyInverseJacobian(const Vec3d& in) const = 0; + virtual Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d& domainPos) const = 0; + //@} + + + //@{ + /// @brief Apply the Jacobian transpose of this map to a vector. + /// For a linear map this is equivalent to applying the transpose of the map + /// excluding translation. + /// @warning Houdini 12.5 uses an earlier version of OpenVDB, and maps created + /// with that version lack a virtual table entry for this method. Do not call + /// this method from Houdini 12.5. + virtual Vec3d applyJT(const Vec3d& in) const = 0; + virtual Vec3d applyJT(const Vec3d& in, const Vec3d& domainPos) const = 0; + //@} + + /// @brief Return a new map representing the inverse of this map. + /// @throw NotImplementedError if the map is a NonlinearFrustumMap. + /// @warning Houdini 12.5 uses an earlier version of OpenVDB, and maps created + /// with that version lack a virtual table entry for this method. Do not call + /// this method from Houdini 12.5. + virtual MapBase::Ptr inverseMap() const = 0; + protected: MapBase() {} @@ -239,8 +286,6 @@ class OPENVDB_API MapRegistry { public: typedef std::map<Name, MapBase::MapFactory> MapDictionary; - typedef tbb::mutex Mutex; - typedef Mutex::scoped_lock Lock; static MapRegistry* instance(); @@ -261,9 +306,11 @@ public: private: MapRegistry() {} + + static MapRegistry* staticInstance(); + static MapRegistry* mInstance; - mutable Mutex mMutex; MapDictionary mMap; }; @@ -334,6 +381,8 @@ public: /// Return a MapBase::Ptr to a deep copy of this map MapBase::Ptr copy() const { return MapBase::Ptr(new AffineMap(*this)); } + MapBase::Ptr inverseMap() const { return MapBase::Ptr(new AffineMap(mMatrixInv)); } + static bool isRegistered() { return MapRegistry::isRegistered(AffineMap::mapType()); } static void registerMap() @@ -350,7 +399,17 @@ public: bool isLinear() const { return true; } /// Return @c false ( test if this is unitary with translation ) - bool hasUniformScale() const { return isUnitary(mMatrix.getMat3());} + bool hasUniformScale() const + { + Mat3d mat = mMatrix.getMat3(); + const double det = mat.det(); + if (isApproxEqual(det, double(0))) { + return false; + } else { + mat *= (1.f / pow(std::abs(det),1./3.)); + return isUnitary(mat); + } + } virtual bool isEqual(const MapBase& other) const { return isEqualBase(*this, other); } @@ -381,14 +440,25 @@ public: /// Return the pre-image of @c in under the map Vec3d applyInverseMap(const Vec3d& in) const {return in * mMatrixInv; } - /// Return the Jacobian of the map applied to @a in. + /// Return the Jacobian of the map applied to @a in. Vec3d applyJacobian(const Vec3d& in, const Vec3d&) const { return applyJacobian(in); } /// Return the Jacobian of the map applied to @a in. - Vec3d applyJacobian(const Vec3d& in) const { + Vec3d applyJacobian(const Vec3d& in) const { return mMatrix.transform3x3(in); } + + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d&) const { return applyInverseJacobian(in); } + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const { return mMatrixInv.transform3x3(in); } + + /// Return the Jacobian Transpose of the map applied to @a in. + /// This tranforms range-space gradients to domain-space gradients + Vec3d applyJT(const Vec3d& in, const Vec3d&) const { return applyJT(in); } + /// Return the Jacobian Transpose of the map applied to @a in. + Vec3d applyJT(const Vec3d& in) const { const double* m = mMatrix.asPointer(); - return Vec3d( m[ 0] * in[0] + m[ 4] * in[1] + m[ 8] * in[2], - m[ 1] * in[0] + m[ 5] * in[1] + m[ 9] * in[2], - m[ 2] * in[0] + m[ 6] * in[1] + m[10] * in[2] ); + return Vec3d( m[ 0] * in[0] + m[ 1] * in[1] + m[ 2] * in[2], + m[ 4] * in[0] + m[ 5] * in[1] + m[ 6] * in[2], + m[ 8] * in[0] + m[ 9] * in[1] + m[10] * in[2] ); } /// Return the transpose of the inverse Jacobian of the map applied to @a in. @@ -427,8 +497,7 @@ public: // Methods that modify the existing affine map //@{ - /// @brief Methods that modify the existing - /// affine map by pre-applying the given operation. + /// @brief Modify the existing affine map by pre-applying the given operation. void accumPreRotation(Axis axis, double radians) { mMatrix.preRotate(axis, radians); @@ -453,8 +522,7 @@ public: //@{ - /// @brief Methods that modify the existing - /// affine map by post-applying the given operation. + /// @brief Modify the existing affine map by post-applying the given operation. void accumPostRotation(Axis axis, double radians) { mMatrix.postRotate(axis, radians); @@ -579,14 +647,15 @@ public: private: void updateAcceleration() { - mDeterminant = mMatrix.getMat3().det(); + Mat3d mat3 = mMatrix.getMat3(); + mDeterminant = mat3.det(); - if (std::abs(mDeterminant) < (3.0 * tolerance<double>::value())) { + if (std::abs(mDeterminant) < (3.0 * math::Tolerance<double>::value())) { OPENVDB_THROW(ArithmeticError, "Tried to initialize an affine transform from a nearly singular matrix"); } mMatrixInv = mMatrix.inverse(); - mJacobianInv = mMatrixInv.getMat3().transpose(); + mJacobianInv = mat3.inverse().transpose(); mIsDiagonal = math::isDiagonal(mMatrix); mIsIdentity = math::isIdentity(mMatrix); Vec3d pos = applyMap(Vec3d(0,0,0)); @@ -618,8 +687,7 @@ public: typedef boost::shared_ptr<ScaleMap> Ptr; typedef boost::shared_ptr<const ScaleMap> ConstPtr; - ScaleMap(): MapBase(), mScaleValues(Vec3d(1,1,1)), - mVoxelSize(Vec3d(1,1,1)), + ScaleMap(): MapBase(), mScaleValues(Vec3d(1,1,1)), mVoxelSize(Vec3d(1,1,1)), mScaleValuesInverse(Vec3d(1,1,1)), mInvScaleSqr(1,1,1), mInvTwiceScale(0.5,0.5,0.5){} @@ -629,7 +697,7 @@ public: mVoxelSize(Vec3d(std::abs(scale(0)),std::abs(scale(1)), std::abs(scale(2)))) { double determinant = scale[0]* scale[1] * scale[2]; - if (std::abs(determinant) < 3.0 * tolerance<double>::value()) { + if (std::abs(determinant) < 3.0 * math::Tolerance<double>::value()) { OPENVDB_THROW(ArithmeticError, "Non-zero scale values required"); } mScaleValuesInverse = 1.0 / mScaleValues; @@ -654,6 +722,8 @@ public: /// Return a MapBase::Ptr to a deep copy of this map MapBase::Ptr copy() const { return MapBase::Ptr(new ScaleMap(*this)); } + MapBase::Ptr inverseMap() const { return MapBase::Ptr(new ScaleMap(mScaleValuesInverse)); } + static bool isRegistered() { return MapRegistry::isRegistered(ScaleMap::mapType()); } static void registerMap() @@ -670,11 +740,13 @@ public: bool isLinear() const { return true; } /// Return @c true if the values have the same magitude (eg. -1, 1, -1 would be a rotation). - bool hasUniformScale() const { - bool value; - value = isApproxEqual(std::abs(mScaleValues.x()), std::abs(mScaleValues.y()), double(5e-7)); - value = value && isApproxEqual(std::abs(mScaleValues.x()), std::abs(mScaleValues.z()), double(5e-7)); - return value; + bool hasUniformScale() const + { + bool value = isApproxEqual( + std::abs(mScaleValues.x()), std::abs(mScaleValues.y()), double(5e-7)); + value = value && isApproxEqual( + std::abs(mScaleValues.x()), std::abs(mScaleValues.z()), double(5e-7)); + return value; } /// Return the image of @c in under the map @@ -696,44 +768,53 @@ public: /// Return the Jacobian of the map applied to @a in. Vec3d applyJacobian(const Vec3d& in, const Vec3d&) const { return applyJacobian(in); } /// Return the Jacobian of the map applied to @a in. - Vec3d applyJacobian(const Vec3d& in) const { - return applyMap(in); - } - + Vec3d applyJacobian(const Vec3d& in) const { return applyMap(in); } + + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d&) const { return applyInverseJacobian(in); } + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const { return applyInverseMap(in); } + + /// Return the Jacobian Transpose of the map applied to @a in. + /// This tranforms range-space gradients to domain-space gradients + Vec3d applyJT(const Vec3d& in, const Vec3d&) const { return applyJT(in); } + /// Return the Jacobian Transpose of the map applied to @a in. + Vec3d applyJT(const Vec3d& in) const { return applyMap(in); } + + /// @brief Return the transpose of the inverse Jacobian of the map applied to @a in. /// @details Ignores second argument - Vec3d applyIJT(const Vec3d& in, const Vec3d& ) const { return applyIJT(in);} + Vec3d applyIJT(const Vec3d& in, const Vec3d&) const { return applyIJT(in);} /// Return the transpose of the inverse Jacobian of the map applied to @c in Vec3d applyIJT(const Vec3d& in) const { return applyInverseMap(in); } /// Return the Jacobian Curvature: zero for a linear map - Mat3d applyIJC(const Mat3d& in) const { + Mat3d applyIJC(const Mat3d& in) const + { Mat3d tmp; - for (int i=0; i<3; i++){ - tmp.setRow(i, in.row(i)*mScaleValuesInverse(i)); + for (int i = 0; i < 3; i++) { + tmp.setRow(i, in.row(i) * mScaleValuesInverse(i)); } - for (int i=0; i<3; i++){ - tmp.setCol(i, tmp.col(i)*mScaleValuesInverse(i)); + for (int i = 0; i < 3; i++) { + tmp.setCol(i, tmp.col(i) * mScaleValuesInverse(i)); } return tmp; } - Mat3d applyIJC(const Mat3d& in, const Vec3d&, const Vec3d& ) const { - return applyIJC(in); - } + Mat3d applyIJC(const Mat3d& in, const Vec3d&, const Vec3d&) const { return applyIJC(in); } /// Return the product of the scale values, ignores argument - double determinant(const Vec3d& ) const { return determinant(); } + double determinant(const Vec3d&) const { return determinant(); } /// Return the product of the scale values - double determinant() const { return mScaleValues.x()*mScaleValues.y()*mScaleValues.z(); } + double determinant() const { return mScaleValues.x() * mScaleValues.y() * mScaleValues.z(); } /// Return the scale values that define the map const Vec3d& getScale() const {return mScaleValues;} /// Return the square of the scale. Used to optimize some finite difference calculations - const Vec3d& getInvScaleSqr() const {return mInvScaleSqr;} + const Vec3d& getInvScaleSqr() const { return mInvScaleSqr; } /// Return 1/(2 scale). Used to optimize some finite difference calculations - const Vec3d& getInvTwiceScale() const {return mInvTwiceScale;} + const Vec3d& getInvTwiceScale() const { return mInvTwiceScale; } /// Return 1/(scale) - const Vec3d& getInvScale() const {return mScaleValuesInverse; } + const Vec3d& getInvScale() const { return mScaleValuesInverse; } //@{ /// @brief Returns the lengths of the images @@ -741,7 +822,7 @@ public: /// \f$(0,0,0)-(1,0,0)\f$, \f$(0,0,0)-(0,1,0)\f$, \f$(0,0,0)-(0,0,1)\f$ /// this is equivalent to the absolute values of the scale values Vec3d voxelSize() const { return mVoxelSize; } - Vec3d voxelSize(const Vec3d&) const { return voxelSize();} + Vec3d voxelSize(const Vec3d&) const { return voxelSize(); } //@} /// read serialization @@ -858,6 +939,12 @@ public: /// Return a MapBase::Ptr to a deep copy of this map MapBase::Ptr copy() const { return MapBase::Ptr(new UniformScaleMap(*this)); } + MapBase::Ptr inverseMap() const + { + const Vec3d& invScale = getInvScale(); + return MapBase::Ptr(new UniformScaleMap( invScale[0])); + } + static bool isRegistered() { return MapRegistry::isRegistered(UniformScaleMap::mapType()); } static void registerMap() { @@ -926,6 +1013,8 @@ public: /// Return a MapBase::Ptr to a deep copy of this map MapBase::Ptr copy() const { return MapBase::Ptr(new TranslationMap(*this)); } + MapBase::Ptr inverseMap() const { return MapBase::Ptr(new TranslationMap(-mTranslation)); } + static bool isRegistered() { return MapRegistry::isRegistered(TranslationMap::mapType()); } static void registerMap() @@ -951,10 +1040,20 @@ public: /// Return the Jacobian of the map applied to @a in. Vec3d applyJacobian(const Vec3d& in, const Vec3d&) const { return applyJacobian(in); } /// Return the Jacobian of the map applied to @a in. - Vec3d applyJacobian(const Vec3d& in) const { - return in; - } - + Vec3d applyJacobian(const Vec3d& in) const { return in; } + + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d&) const { return applyInverseJacobian(in); } + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const { return in; } + + + /// Return the Jacobian Transpose of the map applied to @a in. + /// This tranforms range-space gradients to domain-space gradients + Vec3d applyJT(const Vec3d& in, const Vec3d&) const { return applyJT(in); } + /// Return the Jacobian Transpose of the map applied to @a in. + Vec3d applyJT(const Vec3d& in) const { return in; } + /// @brief Return the transpose of the inverse Jacobian (Identity for TranslationMap) /// of the map applied to @c in, ignores second argument Vec3d applyIJT(const Vec3d& in, const Vec3d& ) const { return applyIJT(in);} @@ -963,9 +1062,7 @@ public: Vec3d applyIJT(const Vec3d& in) const {return in;} /// Return the Jacobian Curvature: zero for a linear map Mat3d applyIJC(const Mat3d& mat) const {return mat;} - Mat3d applyIJC(const Mat3d& mat, const Vec3d&, const Vec3d&) const { - return applyIJC(mat); - } + Mat3d applyIJC(const Mat3d& mat, const Vec3d&, const Vec3d&) const { return applyIJC(mat); } /// Return @c 1 double determinant(const Vec3d& ) const { return determinant(); } @@ -997,8 +1094,7 @@ public: bool operator==(const TranslationMap& other) const { // ::eq() uses a tolerance - if (!mTranslation.eq(other.mTranslation)) { return false; } - return true; + return mTranslation.eq(other.mTranslation); } bool operator!=(const TranslationMap& other) const { return !(*this == other); } @@ -1098,7 +1194,7 @@ public: mVoxelSize(std::abs(scale(0)), std::abs(scale(1)), std::abs(scale(2))) { const double determinant = scale[0]* scale[1] * scale[2]; - if (std::abs(determinant) < 3.0 * tolerance<double>::value()) { + if (std::abs(determinant) < 3.0 * math::Tolerance<double>::value()) { OPENVDB_THROW(ArithmeticError, "Non-zero scale values required"); } mScaleValuesInverse = 1.0 / mScaleValues; @@ -1136,6 +1232,12 @@ public: /// Return a MapBase::Ptr to a deep copy of this map MapBase::Ptr copy() const { return MapBase::Ptr(new ScaleTranslateMap(*this)); } + MapBase::Ptr inverseMap() const + { + return MapBase::Ptr(new ScaleTranslateMap( + mScaleValuesInverse, -mScaleValuesInverse * mTranslation)); + } + static bool isRegistered() { return MapRegistry::isRegistered(ScaleTranslateMap::mapType()); } static void registerMap() @@ -1153,11 +1255,12 @@ public: /// @brief Return @c true if the scale values have the same magnitude /// (eg. -1, 1, -1 would be a rotation). - bool hasUniformScale() const { - bool value; - value = isApproxEqual(std::abs(mScaleValues.x()), std::abs(mScaleValues.y()), double(5e-7)); - value = value && isApproxEqual(std::abs(mScaleValues.x()), std::abs(mScaleValues.z()), double(5e-7)); - + bool hasUniformScale() const + { + bool value = isApproxEqual( + std::abs(mScaleValues.x()), std::abs(mScaleValues.y()), double(5e-7)); + value = value && isApproxEqual( + std::abs(mScaleValues.x()), std::abs(mScaleValues.z()), double(5e-7)); return value; } @@ -1173,18 +1276,27 @@ public: Vec3d applyInverseMap(const Vec3d& in) const { return Vec3d( - (in.x() - mTranslation.x() ) / mScaleValues.x(), - (in.y() - mTranslation.y() ) / mScaleValues.y(), - (in.z() - mTranslation.z() ) / mScaleValues.z()); + (in.x() - mTranslation.x() ) * mScaleValuesInverse.x(), + (in.y() - mTranslation.y() ) * mScaleValuesInverse.y(), + (in.z() - mTranslation.z() ) * mScaleValuesInverse.z()); } /// Return the Jacobian of the map applied to @a in. Vec3d applyJacobian(const Vec3d& in, const Vec3d&) const { return applyJacobian(in); } /// Return the Jacobian of the map applied to @a in. - Vec3d applyJacobian(const Vec3d& in) const { - return in * mScaleValues; - } - + Vec3d applyJacobian(const Vec3d& in) const { return in * mScaleValues; } + + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d&) const { return applyInverseJacobian(in); } + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const { return in * mScaleValuesInverse; } + + /// Return the Jacobian Transpose of the map applied to @a in. + /// This tranforms range-space gradients to domain-space gradients + Vec3d applyJT(const Vec3d& in, const Vec3d&) const { return applyJT(in); } + /// Return the Jacobian Transpose of the map applied to @a in. + Vec3d applyJT(const Vec3d& in) const { return applyJacobian(in); } + /// @brief Return the transpose of the inverse Jacobian of the map applied to @a in /// @details Ignores second argument Vec3d applyIJT(const Vec3d& in, const Vec3d& ) const { return applyIJT(in);} @@ -1192,12 +1304,13 @@ public: Vec3d applyIJT(const Vec3d& in) const { return Vec3d( - in.x() / mScaleValues.x(), - in.y() / mScaleValues.y(), - in.z() / mScaleValues.z()); + in.x() * mScaleValuesInverse.x(), + in.y() * mScaleValuesInverse.y(), + in.z() * mScaleValuesInverse.z()); } /// Return the Jacobian Curvature: zero for a linear map - Mat3d applyIJC(const Mat3d& in) const { + Mat3d applyIJC(const Mat3d& in) const + { Mat3d tmp; for (int i=0; i<3; i++){ tmp.setRow(i, in.row(i)*mScaleValuesInverse(i)); @@ -1207,9 +1320,7 @@ public: } return tmp; } - Mat3d applyIJC(const Mat3d& in, const Vec3d&, const Vec3d& ) const { - return applyIJC(in); - } + Mat3d applyIJC(const Mat3d& in, const Vec3d&, const Vec3d& ) const { return applyIJC(in); } /// Return the product of the scale values, ignores argument double determinant(const Vec3d& ) const { return determinant(); } @@ -1382,6 +1493,13 @@ public: /// Return a MapBase::Ptr to a deep copy of this map MapBase::Ptr copy() const { return MapBase::Ptr(new UniformScaleTranslateMap(*this)); } + MapBase::Ptr inverseMap() const + { + const Vec3d& scaleInv = getInvScale(); + const Vec3d& trans = getTranslation(); + return MapBase::Ptr(new UniformScaleTranslateMap(scaleInv[0], -scaleInv[0] * trans)); + } + static bool isRegistered() { return MapRegistry::isRegistered(UniformScaleTranslateMap::mapType()); @@ -1390,8 +1508,8 @@ public: static void registerMap() { MapRegistry::registerMap( - UniformScaleTranslateMap::mapType(), - UniformScaleTranslateMap::create); + UniformScaleTranslateMap::mapType(), + UniformScaleTranslateMap::create); } Name type() const { return mapType(); } @@ -1439,6 +1557,7 @@ UniformScaleMap::preTranslate(const Vec3d& t) const return MapBase::Ptr(new UniformScaleTranslateMap(scale, scale*t)); } + inline MapBase::Ptr TranslationMap::preScale(const Vec3d& v) const { @@ -1449,6 +1568,7 @@ TranslationMap::preScale(const Vec3d& v) const } } + inline MapBase::Ptr TranslationMap::postScale(const Vec3d& v) const { @@ -1462,6 +1582,7 @@ TranslationMap::postScale(const Vec3d& v) const } } + inline MapBase::Ptr ScaleTranslateMap::preScale(const Vec3d& v) const { @@ -1473,6 +1594,7 @@ ScaleTranslateMap::preScale(const Vec3d& v) const } } + inline MapBase::Ptr ScaleTranslateMap::postScale(const Vec3d& v) const { @@ -1573,6 +1695,11 @@ public: /// Returns a MapBase::Ptr to a deep copy of *this MapBase::Ptr copy() const { return MapBase::Ptr(new UnitaryMap(*this)); } + MapBase::Ptr inverseMap() const + { + return MapBase::Ptr(new UnitaryMap(mAffineMap.getMat4().inverse())); + } + static bool isRegistered() { return MapRegistry::isRegistered(UnitaryMap::mapType()); } static void registerMap() @@ -1607,25 +1734,35 @@ public: Vec3d applyMap(const Vec3d& in) const { return mAffineMap.applyMap(in); } /// Return the pre-image of @c in under the map Vec3d applyInverseMap(const Vec3d& in) const { return mAffineMap.applyInverseMap(in); } - + Vec3d applyJacobian(const Vec3d& in, const Vec3d&) const { return applyJacobian(in); } /// Return the Jacobian of the map applied to @a in. - Vec3d applyJacobian(const Vec3d& in) const { - return applyMap(in); + Vec3d applyJacobian(const Vec3d& in) const { return mAffineMap.applyJacobian(in); } + + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d&) const { return applyInverseJacobian(in); } + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const { return mAffineMap.applyInverseJacobian(in); } + + + /// Return the Jacobian Transpose of the map applied to @a in. + /// This tranforms range-space gradients to domain-space gradients + Vec3d applyJT(const Vec3d& in, const Vec3d&) const { return applyJT(in); } + /// Return the Jacobian Transpose of the map applied to @a in. + Vec3d applyJT(const Vec3d& in) const { + // The transpose of the unitary map is its inverse + return applyInverseMap(in); } - + + /// @brief Return the transpose of the inverse Jacobian of the map applied to @a in /// @details Ignores second argument Vec3d applyIJT(const Vec3d& in, const Vec3d& ) const { return applyIJT(in);} /// Return the transpose of the inverse Jacobian of the map applied to @c in Vec3d applyIJT(const Vec3d& in) const { return mAffineMap.applyIJT(in); } - /// Return the Jacobian Curvature: zero for a linear map - Mat3d applyIJC(const Mat3d& in) const { - return mAffineMap.applyIJC(in); - } - Mat3d applyIJC(const Mat3d& in, const Vec3d&, const Vec3d& ) const { - return applyIJC(in); - } + /// Return the Jacobian Curvature: zero for a linear map + Mat3d applyIJC(const Mat3d& in) const { return mAffineMap.applyIJC(in); } + Mat3d applyIJC(const Mat3d& in, const Vec3d&, const Vec3d& ) const { return applyIJC(in); } /// Return the determinant of the Jacobian, ignores argument double determinant(const Vec3d& ) const { return determinant(); } /// Return the determinant of the Jacobian @@ -1864,6 +2001,14 @@ public: /// Return a MapBase::Ptr to a deep copy of this map MapBase::Ptr copy() const { return MapBase::Ptr(new NonlinearFrustumMap(*this)); } + /// @brief Not implemented, since there is currently no map type that can + /// represent the inverse of a frustum + /// @throw NotImplementedError + MapBase::Ptr inverseMap() const + { + OPENVDB_THROW(NotImplementedError, + "inverseMap() is not implemented for NonlinearFrustumMap"); + } static bool isRegistered() { return MapRegistry::isRegistered(NonlinearFrustumMap::mapType()); } static void registerMap() @@ -1882,24 +2027,24 @@ public: /// Return @c false (by convention false) bool hasUniformScale() const { return false; } - - /// Return @c true if the map is equivalent to an identity - bool isIdentity() const { - + + /// Return @c true if the map is equivalent to an identity + bool isIdentity() const + { // The frustum can only be consistent with a linear map if the taper value is 1 if (!isApproxEqual(mTaper, double(1)) ) return false; - + // There are various ways an identity can decomposed between the two parts of the // map. Best to just check that the principle vectors are stationary. const Vec3d e1(1,0,0); if (!applyMap(e1).eq(e1)) return false; - + const Vec3d e2(0,1,0); if (!applyMap(e2).eq(e2)) return false; - + const Vec3d e3(0,0,1); if (!applyMap(e3).eq(e3)) return false; - + return true; } @@ -1942,12 +2087,12 @@ public: } /// Return the Jacobian of the linear second map applied to @c in Vec3d applyJacobian(const Vec3d& in) const { return mSecondMap.applyJacobian(in); } - /// Return the Jacobian of the linear second map applied to @c in - Vec3d applyJacobian(const Vec3d& in, const Vec3d& loc) const { - + /// Return the Jacobian defined at @c isloc applied to @c in + Vec3d applyJacobian(const Vec3d& in, const Vec3d& isloc) const + { // Move the center of the x-face of the bbox // to the origin in index space. - Vec3d centered(loc); + Vec3d centered(isloc); centered = centered - mBBox.min(); centered.x() -= mXo; centered.y() -= mYo; @@ -1958,14 +2103,73 @@ public: const double scale = (mGamma * zprime + 1.) / mLx; const double scale2 = mGamma * mDepthOnLz / mLx; - const Vec3d tmp(scale * in.x() + scale2 * centered.x()* in.z(), scale * in.y() + scale2 * centered.y()* in.z(), mDepthOnLz * in.z()); - return mSecondMap.applyJacobian(tmp); + return mSecondMap.applyJacobian(tmp); } + + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const { return mSecondMap.applyInverseJacobian(in); } + /// Return the Inverse Jacobian defined at @c isloc of the map applied to @a in. + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d& isloc) const { + + // Move the center of the x-face of the bbox + // to the origin in index space. + Vec3d centered(isloc); + centered = centered - mBBox.min(); + centered.x() -= mXo; + centered.y() -= mYo; + + // scale the z-direction on depth / K count + const double zprime = centered.z()*mDepthOnLz; + + const double scale = (mGamma * zprime + 1.) / mLx; + const double scale2 = mGamma * mDepthOnLz / mLx; + + + Vec3d out = mSecondMap.applyInverseJacobian(in); + + out.x() = (out.x() - scale2 * centered.x() * out.z() / mDepthOnLz) / scale; + out.y() = (out.y() - scale2 * centered.y() * out.z() / mDepthOnLz) / scale; + out.z() = out.z() / mDepthOnLz; + + return out; + } + + + + /// Return the Jacobian Transpose of the map applied to vector @c in at @c indexloc. + /// This tranforms range-space gradients to domain-space gradients. + /// + Vec3d applyJT(const Vec3d& in, const Vec3d& isloc) const { + const Vec3d tmp = mSecondMap.applyJT(in); + // Move the center of the x-face of the bbox + // to the origin in index space. + Vec3d centered(isloc); + centered = centered - mBBox.min(); + centered.x() -= mXo; + centered.y() -= mYo; + + // scale the z-direction on depth / K count + const double zprime = centered.z()*mDepthOnLz; + + const double scale = (mGamma * zprime + 1.) / mLx; + const double scale2 = mGamma * mDepthOnLz / mLx; + + return Vec3d(scale * tmp.x(), + scale * tmp.y(), + scale2 * centered.x()* tmp.x() + + scale2 * centered.y()* tmp.y() + + mDepthOnLz * tmp.z()); + } + /// Return the Jacobian Transpose of the second map applied to @c in. + Vec3d applyJT(const Vec3d& in) const { + return mSecondMap.applyJT(in); + } + /// Return the transpose of the inverse Jacobian of the linear second map applied to @c in Vec3d applyIJT(const Vec3d& in) const { return mSecondMap.applyIJT(in); } @@ -2005,7 +2209,7 @@ public: Vec3d result; for (int i = 0; i < 3; ++i) { - result(0) = d1_is(0) * gradE(0,i) + d1_is(1) * gradE(1,i) + d1_is(2) * gradE(2,i); + result(i) = d1_is(0) * gradE(0,i) + d1_is(1) * gradE(1,i) + d1_is(2) * gradE(2,i); } return result; @@ -2037,7 +2241,6 @@ public: const double pt2 = pt1 * sinv; // gamma * Lx / ( z*gamma +1)**2 const double pt3 = pt2 * sinv; // gamma * Lx / ( z*gamma +1)**3 - const Mat3d& jacinv = mSecondMap.getConstJacobianInv(); // compute \frac{\partial^2 E_i}{\partial x_j \partial x_k} @@ -2101,7 +2304,8 @@ public: } /// Return the size of a voxel at the center of the near plane - Vec3d voxelSize() const { + Vec3d voxelSize() const + { const Vec3d loc( 0.5*(mBBox.min().x() + mBBox.max().x()), 0.5*(mBBox.min().y() + mBBox.max().y()), mBBox.min().z()); @@ -2114,7 +2318,8 @@ public: /// from @a loc to @a loc + (1,0,0), from @a loc to @a loc + (0,1,0) /// and from @a loc to @a loc + (0,0,1) /// @param loc a location in the pre-image space (e.g., index space) - Vec3d voxelSize(const Vec3d& loc) const { + Vec3d voxelSize(const Vec3d& loc) const + { Vec3d out, pos = applyMap(loc); out(0) = (applyMap(loc + Vec3d(1,0,0)) - pos).length(); out(1) = (applyMap(loc + Vec3d(0,1,0)) - pos).length(); @@ -2122,10 +2327,7 @@ public: return out; } - AffineMap::Ptr getAffineMap() const - { - return mSecondMap.getAffineMap(); - } + AffineMap::Ptr getAffineMap() const { return mSecondMap.getAffineMap(); } /// set the taper value, the ratio of nearplane width / far plane width void setTaper(double t) { mTaper = t; init();} @@ -2252,9 +2454,9 @@ public: } //@} - private: - void init() { + void init() + { // set up as a frustum mLx = mBBox.extents().x(); mLy = mBBox.extents().y(); @@ -2406,7 +2608,8 @@ public: return *this; } - bool isIdentity() const { + bool isIdentity() const + { if (is_linear<MyType>::value) { return mAffineMap.isIdentity(); } else { @@ -2421,6 +2624,7 @@ public: return mFirstMap.isDiagonal()&&mSecondMap.isDiagonal(); } } + AffineMap::Ptr getAffineMap() const { if (is_linear<MyType>::value) { diff --git a/extern/openvdb/internal/openvdb/math/Mat.h b/extern/openvdb/internal/openvdb/math/Mat.h index d1eb61b6fdd..29de4a8f218 100644 --- a/extern/openvdb/internal/openvdb/math/Mat.h +++ b/extern/openvdb/internal/openvdb/math/Mat.h @@ -35,11 +35,7 @@ #define OPENVDB_MATH_MAT_HAS_BEEN_INCLUDED #include <math.h> -#include <cstdlib> -#include <cstdio> -#include <assert.h> #include <iostream> -#include <sstream> #include <boost/format.hpp> #include <openvdb/Exceptions.h> #include "Math.h" @@ -149,13 +145,12 @@ protected: template<typename T> class Quat; template<typename T> class Vec3; -/// Returns rotation matrix specified by the quaternion -/// The quaternion is normalized and used to construct the matrix -/// Note that the matrix is transposed to match post-multiplication -/// symantics. +/// @brief Return the rotation matrix specified by the given quaternion. +/// @details The quaternion is normalized and used to construct the matrix. +/// Note that the matrix is transposed to match post-multiplication semantics. template<class MatType> -MatType rotation(const Quat<typename MatType::value_type> &q, - typename MatType::value_type eps = 1.0e-8) +MatType +rotation(const Quat<typename MatType::value_type> &q, typename MatType::value_type eps = 1.0e-8) { typedef typename MatType::value_type T; @@ -190,11 +185,12 @@ MatType rotation(const Quat<typename MatType::value_type> &q, -/// @brief Set the matrix to a rotation about the given axis. +/// @brief Return a matrix for rotation by @a angle radians about the given @a axis. /// @param axis The axis (one of X, Y, Z) to rotate about. /// @param angle The rotation angle, in radians. template<class MatType> -MatType rotation(Axis axis, typename MatType::value_type angle) +MatType +rotation(Axis axis, typename MatType::value_type angle) { typedef typename MatType::value_type T; T c = static_cast<T>(cos(angle)); @@ -228,12 +224,11 @@ MatType rotation(Axis axis, typename MatType::value_type angle) } -/// @return matrix to the rotation specified by axis and angle -/// @note The axis must be unit vector +/// @brief Return a matrix for rotation by @a angle radians about the given @a axis. +/// @note The axis must be a unit vector. template<class MatType> -MatType rotation( - const Vec3<typename MatType::value_type> &_axis, - typename MatType::value_type angle) +MatType +rotation(const Vec3<typename MatType::value_type> &_axis, typename MatType::value_type angle) { typedef typename MatType::value_type T; T txy, txz, tyz, sx, sy, sz; @@ -276,9 +271,9 @@ MatType rotation( } -/// Return the euler angles composing this rotation matrix. Optional -/// axes arguments describe in what order elementary rotations are -/// applied. Note that in our convention, XYZ means Rz * Ry * Rx. +/// @brief Return the Euler angles composing the given rotation matrix. +/// @details Optional axes arguments describe in what order elementary rotations +/// are applied. Note that in our convention, XYZ means Rz * Ry * Rx. /// Because we are using rows rather than columns to represent the /// local axes of a coordinate frame, the interpretation from a local /// reference point of view is to first rotate about the x axis, then @@ -286,14 +281,14 @@ MatType rotation( /// From a fixed reference point of view, the interpretation is to /// rotate about the stationary world z, y, and x axes respectively. /// -/// Irrespective of the euler angle convention, in the case of distinct +/// Irrespective of the Euler angle convention, in the case of distinct /// axes, eulerAngles() returns the x, y, and z angles in the corresponding /// x, y, z components of the returned Vec3. For the XZX convention, the /// left X value is returned in Vec3.x, and the right X value in Vec3.y. /// For the ZXZ convention the left Z value is returned in Vec3.z and /// the right Z value in Vec3.y /// -/// Examples of reconstructing r from its euler angle decomposition +/// Examples of reconstructing r from its Euler angle decomposition /// /// v = eulerAngles(r, ZYX_ROTATION); /// rx.setToRotation(Vec3d(1,0,0), v[0]); @@ -313,8 +308,9 @@ MatType rotation( /// rz.setToRotation (Vec3d(0,0,1), v[2]); /// r = rx2 * rz * rx1; /// -template <class MatType> -Vec3<typename MatType::value_type> eulerAngles( +template<class MatType> +Vec3<typename MatType::value_type> +eulerAngles( const MatType& mat, RotationOrder rotationOrder, typename MatType::value_type eps=1.0e-8) @@ -478,10 +474,11 @@ Vec3<typename MatType::value_type> eulerAngles( } -/// @brief Set the matrix to a rotation that maps v1 onto v2 about the cross -/// product of v1 and v2. +/// @brief Return a rotation matrix that maps @a v1 onto @a v2 +/// about the cross product of @a v1 and @a v2. template<class MatType> -MatType rotation( +MatType +rotation( const Vec3<typename MatType::value_type>& _v1, const Vec3<typename MatType::value_type>& _v2, typename MatType::value_type eps=1.0e-8) @@ -590,25 +587,25 @@ MatType rotation( } -/// @return the matrix to a matrix that scales by v +/// Return a matrix that scales by @a s. template<class MatType> -MatType scale(const Vec3<typename MatType::value_type> &scaling) +MatType +scale(const Vec3<typename MatType::value_type>& s) { // Gets identity, then sets top 3 diagonal // Inefficient by 3 sets. MatType result; result.setIdentity(); - result[0][0] = scaling[0]; - result[1][1] = scaling[1]; - result[2][2] = scaling[2]; + result[0][0] = s[0]; + result[1][1] = s[1]; + result[2][2] = s[2]; return result; } -/// @return a Vec3 representing the lengths of the passed matrix's upper -/// 3x3's rows. +/// Return a Vec3 representing the lengths of the passed matrix's upper 3x3's rows. template<class MatType> Vec3<typename MatType::value_type> getScale(const MatType &mat) @@ -621,8 +618,8 @@ getScale(const MatType &mat) } -/// @return a copy of included matrix with its upper 3x3 rows normalized. -/// This can be geometrically interpretted as a matrix with no scaling +/// @brief Return a copy of the given matrix with its upper 3x3 rows normalized. +/// @details This can be geometrically interpreted as a matrix with no scaling /// along its major axes. template<class MatType> MatType @@ -633,9 +630,9 @@ unit(const MatType &mat, typename MatType::value_type eps = 1.0e-8) } -/// @return a copy of included matrix with its upper 3x3 rows normalized, -/// and writes the length of each of these rows. -/// This can be geometrically interpretted as a matrix with no scaling +/// @brief Return a copy of the given matrix with its upper 3x3 rows normalized, +/// and return the length of each of these rows in @a scaling. +/// @details This can be geometrically interpretted as a matrix with no scaling /// along its major axes, and the scaling in the input vector template<class MatType> MatType @@ -660,7 +657,7 @@ unit( } -/// @brief Set the matrix to a shear along axis0 by a fraction of axis1. +/// @brief Set the matrix to a shear along @a axis0 by a fraction of @a axis1. /// @param axis0 The fixed axis of the shear. /// @param axis1 The shear axis. /// @param shear The shear factor. @@ -683,7 +680,7 @@ shear(Axis axis0, Axis axis1, typename MatType::value_type shear) } -/// @return a matrix as the cross product of the given vector +/// Return a matrix as the cross product of the given vector. template<class MatType> MatType skew(const Vec3<typename MatType::value_type> &skew) @@ -700,8 +697,8 @@ skew(const Vec3<typename MatType::value_type> &skew) } -/// Build an orientation matrix such that z points along direction, -/// and y is along direction/vertical plane. +/// @brief Return an orientation matrix such that z points along @a direction, +/// and y is along the @a direction / @a vertical plane. template<class MatType> MatType aim(const Vec3<typename MatType::value_type>& direction, @@ -723,8 +720,8 @@ aim(const Vec3<typename MatType::value_type>& direction, } -/// Write 0's along Mat4's last row and column, and a 1 on its diagonal -/// Useful initialization when we're initializing juse the 3x3 block +/// @brief Write 0s along Mat4's last row and column, and a 1 on its diagonal. +/// @details Useful initialization when we're initializing just the 3x3 block. template<class MatType> static MatType& padMat4(MatType& dest) @@ -737,9 +734,8 @@ padMat4(MatType& dest) } -/// Solve for A=B*B, given A -/// -/// Denman-Beavers square root iteration +/// @brief Solve for A=B*B, given A. +/// @details Denman-Beavers square root iteration template <typename MatType> inline void sqrtSolve(const MatType &aA, MatType &aB, double aTol=0.01) @@ -830,38 +826,54 @@ powSolve(const MatType &aA, MatType &aB, double aPower, double aTol=0.01) } } -template <typename MatType> -inline bool isIdentity(const MatType& m) { - typedef typename MatType::ValueType value_type; + +/// @brief Determine if a matrix is an identity matrix. +template<typename MatType> +inline bool +isIdentity(const MatType& m) +{ return m.eq(MatType::identity()); } -template <typename MatType> -inline bool isInvertible(const MatType& m) { +/// @brief Determine if a matrix is invertible. +template<typename MatType> +inline bool +isInvertible(const MatType& m) +{ typedef typename MatType::ValueType value_type; return !isApproxEqual(m.det(), (value_type)0); } -/// Determine if a matrix is symmetric. -/// This implicitly uses "isApproxEqual" to determine the equality -template <typename MatType> -inline bool isSymmetric(const MatType& m) { + + +/// @brief Determine if a matrix is symmetric. +/// @details This implicitly uses math::isApproxEqual() to determine equality. +template<typename MatType> +inline bool +isSymmetric(const MatType& m) +{ return m.eq(m.transpose()); } -/// Determine is a matrix is Unitary (i.e. rotation or reflection) -template <typename MatType> -inline bool isUnitary(const MatType& m) { - typedef typename MatType::ValueType value_type; + +/// Determine if a matrix is unitary (i.e., rotation or reflection). +template<typename MatType> +inline bool +isUnitary(const MatType& m) +{ + typedef typename MatType::ValueType value_type; if (!isApproxEqual(std::abs(m.det()), value_type(1.0))) return false; // check that the matrix transpose is the inverse MatType temp = m * m.transpose(); - return temp.eq(MatType::identity(), toleranceValue<value_type>()); + return temp.eq(MatType::identity()); } -/// Determine if a matrix is diagonal -template <typename MatType> -inline bool isDiagonal(const MatType& mat) { + +/// Determine if a matrix is diagonal. +template<typename MatType> +inline bool +isDiagonal(const MatType& mat) +{ int n = MatType::size; typename MatType::ValueType temp(0); for (int i = 0; i < n; ++i) { @@ -874,9 +886,11 @@ inline bool isDiagonal(const MatType& mat) { return isApproxEqual(temp, typename MatType::ValueType(0.0)); } -/// takes a n by n matrix and returns the L_Infinty norm + +/// Return the @f$L_\infty@f$ norm of an N x N matrix. template<typename MatType> -typename MatType::ValueType lInfinityNorm(const MatType& matrix) +typename MatType::ValueType +lInfinityNorm(const MatType& matrix) { int n = MatType::size; typename MatType::ValueType norm = 0; @@ -893,9 +907,11 @@ typename MatType::ValueType lInfinityNorm(const MatType& matrix) return norm; } -/// takes an n by n matrix and returns the L_1 norm + +/// Return the @f$L_1@f$ norm of an N x N matrix. template<typename MatType> -typename MatType::ValueType lOneNorm(const MatType& matrix) +typename MatType::ValueType +lOneNorm(const MatType& matrix) { int n = MatType::size; typename MatType::ValueType norm = 0; @@ -913,23 +929,23 @@ typename MatType::ValueType lOneNorm(const MatType& matrix) } -///@brief Decompose an invertible 3x3 matrix into Unitary following -/// a symmetric matrix (postitive semi-defininte Hermitian): -/// i.e. M = U * S -/// if the Unitary.det() = 1 it is a rotation, otherwise -/// Unitary.det() = -1, meaning there is some part reflection. +/// @brief Decompose an invertible 3x3 matrix into a unitary matrix +/// followed by a symmetric matrix (positive semi-definite Hermitian), +/// i.e., M = U * S. +/// @details If det(U) = 1 it is a rotation, otherwise det(U) = -1, +/// meaning there is some part reflection. /// See "Computing the polar decomposition with applications" /// Higham, N.J. - SIAM J. Sc. Stat Comput 7(4):1160-1174 template<typename MatType> -bool polarDecomposition(const MatType& input, MatType& unitary, MatType& positive_hermitian, unsigned int MAX_ITERATIONS=100) +bool +polarDecomposition(const MatType& input, MatType& unitary, + MatType& positive_hermitian, unsigned int MAX_ITERATIONS=100) { - /// tolerance - /// this should really be a trait of MatType::ValueType unitary = input; MatType new_unitary(input); MatType unitary_inv; - if (fabs(unitary.det()) < tolerance<typename MatType::ValueType>::value()) return false; + if (fabs(unitary.det()) < math::Tolerance<typename MatType::ValueType>::value()) return false; unsigned int iteration(0); @@ -958,7 +974,7 @@ bool polarDecomposition(const MatType& input, MatType& unitary, MatType& positiv /// this generally converges in less than ten iterations if (iteration > MAX_ITERATIONS) return false; iteration++; - } while (l1_error > tolerance<typename MatType::ValueType>::value()); + } while (l1_error > math::Tolerance<typename MatType::ValueType>::value()); positive_hermitian = unitary.transpose() * input; return true; diff --git a/extern/openvdb/internal/openvdb/math/Mat3.h b/extern/openvdb/internal/openvdb/math/Mat3.h index 0575944ca87..94729eb883e 100644 --- a/extern/openvdb/internal/openvdb/math/Mat3.h +++ b/extern/openvdb/internal/openvdb/math/Mat3.h @@ -702,7 +702,7 @@ namespace { double Sjj_minus_Sii = D[j] - D[i]; - if (fabs(Sjj_minus_Sii) * (10*tolerance<T>::value()) > fabs(Sij)) { + if (fabs(Sjj_minus_Sii) * (10*math::Tolerance<T>::value()) > fabs(Sij)) { tan_of_theta = Sij / Sjj_minus_Sii; } else { /// pivot on Sij @@ -781,7 +781,7 @@ bool diagonalizeSymmetricMatrix(const Mat3<T>& input, Mat3<T>& Q, Vec3<T>& D, er += fabs(S(i,j)); } } - if (std::abs(er) < tolerance<T>::value()) { + if (std::abs(er) < math::Tolerance<T>::value()) { return true; } iterations++; @@ -793,7 +793,7 @@ bool diagonalizeSymmetricMatrix(const Mat3<T>& input, Mat3<T>& Q, Vec3<T>& D, for (int i = 0; i < n; ++i) { for (int j = i+1; j < n; ++j){ - if ( fabs(D[i]) * (10*tolerance<T>::value()) > fabs(S(i,j))) { + if ( fabs(D[i]) * (10*math::Tolerance<T>::value()) > fabs(S(i,j))) { /// value too small to pivot on S(i,j) = 0; } diff --git a/extern/openvdb/internal/openvdb/math/Mat4.h b/extern/openvdb/internal/openvdb/math/Mat4.h index 460e34bbafb..cf1452c37ec 100644 --- a/extern/openvdb/internal/openvdb/math/Mat4.h +++ b/extern/openvdb/internal/openvdb/math/Mat4.h @@ -75,9 +75,7 @@ public: template<typename Source> Mat4(Source *a) { - register int i; - - for (i = 0; i < 16; i++) { + for (int i = 0; i < 16; i++) { MyBase::mm[i] = a[i]; } } diff --git a/extern/openvdb/internal/openvdb/math/Math.h b/extern/openvdb/internal/openvdb/math/Math.h index 9dbe7dc5483..dd5b5d2ac8d 100644 --- a/extern/openvdb/internal/openvdb/math/Math.h +++ b/extern/openvdb/internal/openvdb/math/Math.h @@ -28,24 +28,30 @@ // /////////////////////////////////////////////////////////////////////////// // -/// @author Ken Museth -/// /// @file Math.h +/// @brief General-purpose arithmetic and comparison routines, most of which +/// accept arbitrary value types (or at least arbitrary numeric value types) #ifndef OPENVDB_MATH_HAS_BEEN_INCLUDED #define OPENVDB_MATH_HAS_BEEN_INCLUDED #include <assert.h> -#include <algorithm> //for std::max -#include <cmath> //for floor, ceil and sqrt -#include <math.h> //for pow, fabs(float,double,long double) etc -#include <cstdlib> //for srand, abs(int) -#include <limits> //for std::numeric_limits<Type>::max() +#include <algorithm> // for std::max() +#include <cmath> // for floor(), ceil() and sqrt() +#include <math.h> // for pow(), fabs() etc +#include <cstdlib> // for srand(), abs(int) +#include <limits> // for std::numeric_limits<Type>::max() #include <string> #include <boost/numeric/conversion/conversion_traits.hpp> +#include <boost/math/special_functions/cbrt.hpp> +#include <boost/random/mersenne_twister.hpp> // for boost::random::mt19937 +#include <boost/random/uniform_01.hpp> +#include <boost/random/uniform_int.hpp> +#include <boost/version.hpp> // for BOOST_VERSION #include <openvdb/Platform.h> #include <openvdb/version.h> + // Compile pragmas // Intel(r) compiler fires remark #1572: floating-point equality and inequality @@ -72,20 +78,16 @@ namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { -/// Return the value of type T that corresponds to zero. -/// @note A zeroVal<T>() specialization must be defined for each ValueType T -/// that cannot be constructed using the form T(0). For example, std::string(0) -/// treats 0 as NULL and throws a std::logic_error. +/// @brief Return the value of type T that corresponds to zero. +/// @note A zeroVal<T>() specialization must be defined for each @c ValueType T +/// that cannot be constructed using the form @c T(0). For example, @c std::string(0) +/// treats 0 as @c NULL and throws a @c std::logic_error. template<typename T> inline T zeroVal() { return T(0); } -/// Return the std::string value that corresponds to zero. +/// Return the @c std::string value that corresponds to zero. template<> inline std::string zeroVal<std::string>() { return ""; } -/// Return the bool value that corresponds to zero. +/// Return the @c bool value that corresponds to zero. template<> inline bool zeroVal<bool>() { return false; } -template<typename T> inline T toleranceValue() { return T(1e-8); } -template<> inline float toleranceValue<float>() { return float(1e-6); } - - /// @todo These won't be needed if we eliminate StringGrids. //@{ /// @brief Needed to support the <tt>(zeroVal<ValueType>() + val)</tt> idiom @@ -97,125 +99,176 @@ inline std::string operator+(const std::string& s, double) { return s; } //@} -/// Return the unary negation of the given value. +namespace math { + +/// @brief Return the unary negation of the given value. /// @note A negative<T>() specialization must be defined for each ValueType T /// for which unary negation is not defined. template<typename T> inline T negative(const T& val) { return T(-val); } +/// Return the negation of the given boolean. template<> inline bool negative(const bool& val) { return !val; } /// Return the "negation" of the given string. template<> inline std::string negative(const std::string& val) { return val; } -namespace math { +//@{ +/// Tolerance for floating-point comparison +template<typename T> struct Tolerance { static T value() { return zeroVal<T>(); } }; +template<> struct Tolerance<float> { static float value() { return 1e-8f; } }; +template<> struct Tolerance<double> { static double value() { return 1e-15; } }; +//@} -/// ==========> Random Values <================== +//@{ +/// Delta for small floating-point offsets +template<typename T> struct Delta { static T value() { return zeroVal<T>(); } }; +template<> struct Delta<float> { static float value() { return 1e-5f; } }; +template<> struct Delta<double> { static double value() { return 1e-9; } }; +//@} -/// Initialize random number generator -inline void randSeed(unsigned int seed) -{ - srand(seed); -} -/// Return random value [0,1] -inline double randUniform() +// ==========> Random Values <================== + +/// @brief Simple generator of random numbers over the range [0, 1) +/// @details Thread-safe as long as each thread has its own Rand01 instance +template<typename FloatType = double, typename EngineType = boost::mt19937> +class Rand01 { - return (double)(rand() / (RAND_MAX + 1.0)); -} +private: + EngineType mEngine; + boost::uniform_01<FloatType> mRand; + +public: + typedef FloatType ValueType; + + /// @brief Initialize the generator. + /// @param seed seed value for the random number generator + Rand01(unsigned int seed): mEngine(static_cast<typename EngineType::result_type>(seed)) {} + + /// Return a uniformly distributed random number in the range [0, 1). + FloatType operator()() { return mRand(mEngine); } +}; -/// Simple class to generate random intergers -class RandomInt +typedef Rand01<double, boost::mt19937> Random01; + + +/// @brief Simple random integer generator +/// @details Thread-safe as long as each thread has its own RandInt instance +template<typename EngineType = boost::mt19937> +class RandInt { - protected: - int my_min, my_range; - public: - RandomInt(unsigned int seed, int min, int max) : my_min(min), my_range(max-min+1) { - assert(min<max && "RandomInt: invalid arguments"); - randSeed(seed); +private: +#if BOOST_VERSION >= 104700 + typedef boost::random::uniform_int_distribution<int> Distr; +#else + typedef boost::uniform_int<int> Distr; +#endif + EngineType mEngine; + Distr mRand; + +public: + /// @brief Initialize the generator. + /// @param seed seed value for the random number generator + /// @param imin,imax generate integers that are uniformly distributed over [imin, imax] + RandInt(unsigned int seed, int imin, int imax): + mEngine(static_cast<typename EngineType::result_type>(seed)), + mRand(std::min(imin, imax), std::max(imin, imax)) + {} + + /// Change the range over which integers are distributed to [imin, imax]. + void setRange(int imin, int imax) { mRand = Distr(std::min(imin, imax), std::max(imin, imax)); } + + /// Return a randomly-generated integer in the current range. + int operator()() { return mRand(mEngine); } + /// @brief Return a randomly-generated integer in the new range [imin, imax], + /// without changing the current range. + int operator()(int imin, int imax) + { + const int lo = std::min(imin, imax), hi = std::max(imin, imax); +#if BOOST_VERSION >= 104700 + return mRand(mEngine, Distr::param_type(lo, hi)); +#else + return Distr(lo, hi)(mEngine); +#endif } - void setRange(int min, int max) {my_min=min; my_range=max-min+1;} - int operator() (void) const {return rand() % my_range + my_min;} - int operator() (int min, int max) const {return rand() % (max-min+1) + min;} }; +typedef RandInt<boost::mt19937> RandomInt; + -// ==========> Clamp/Abs <================== +// ==========> Clamp <================== /// Return @a x clamped to [@a min, @a max] -template <typename Type> -inline Type Clamp(Type x, Type min, Type max) { +template<typename Type> +inline Type +Clamp(Type x, Type min, Type max) +{ assert(min<max); return x > min ? x < max ? x : max : min; } + /// Return @a x clamped to [0, 1] -template <class Type> -inline Type Clamp01(Type x) { - return x > Type(0) ? x < Type(1) ? x : Type(1) : Type(0); -} +template<typename Type> +inline Type +Clamp01(Type x) { return x > Type(0) ? x < Type(1) ? x : Type(1) : Type(0); } + + /// Return @c true if @a x is outside [0,1] -template <class Type> -inline bool ClampTest01(Type &x) { - if (x>=Type(0) && x<=Type(1)) return false; - x = x< Type(0) ? Type(0) : Type(1); +template<typename Type> +inline bool +ClampTest01(Type &x) +{ + if (x >= Type(0) && x <= Type(1)) return false; + x = x < Type(0) ? Type(0) : Type(1); return true; } -/// Return 0 if x<min, 1 if x>max and else (3-2*t)*t*t, (x-min)/(max-min) -template <class Type> -inline Type SmoothUnitStep(Type x, Type min, Type max) { - assert(min<max); + +/// @brief Return 0 if @a x < @a min, 1 if @a x > @a max or else @f$(3-2t)t^2@f$, +/// where @f$t = (x-min)/(max-min)@f$. +template<typename Type> +inline Type +SmoothUnitStep(Type x, Type min, Type max) +{ + assert(min < max); const Type t = (x-min)/(max-min); return t > 0 ? t < 1 ? (3-2*t)*t*t : Type(1) : Type(0); } -/// Return the absolute value of a signed integer -inline int32_t Abs(int32_t i) -{ - return abs(i); -} -/// Return the absolute value of a signed long integer +// ==========> Absolute Value <================== + + +//@{ +/// Return the absolute value of the given quantity. +inline int32_t Abs(int32_t i) { return abs(i); } inline int64_t Abs(int64_t i) { #ifdef _MSC_VER return (i < int64_t(0) ? -i : i); #else - return abs(i); + return labs(i); #endif } +inline float Abs(float x) { return fabs(x); } +inline double Abs(double x) { return fabs(x); } +inline long double Abs(long double x) { return fabsl(x); } +inline uint32_t Abs(uint32_t i) { return i; } +inline uint64_t Abs(uint64_t i) { return i; } +// On OSX size_t and uint64_t are different types +#if defined(__APPLE__) || defined(MACOSX) +inline size_t Abs(size_t i) { return i; } +#endif +//@} -/// Return the absolute value of a float -inline float Abs(float x) -{ - return fabs(x); -} -/// Return the absolute value of a double -inline double Abs(double x) -{ - return fabs(x); -} +//////////////////////////////////////// -/// Return the absolute value of a long double -inline long double Abs(long double x) -{ - return fabs(x); -} -/// Return the absolute value of a unsigned integer -inline uint32_t Abs(uint32_t i) -{ - return i; -} - -/// Return the absolute value of a unsigned integer -inline uint64_t Abs(uint64_t i) -{ - return i; -} -//////////////////////////////////////// +// ==========> Value Comparison <================== +/// Return @c true if @a x is exactly equal to zero. template<typename Type> inline bool isZero(const Type& x) @@ -224,22 +277,48 @@ isZero(const Type& x) return x == zeroVal<Type>(); OPENVDB_NO_FP_EQUALITY_WARNING_END } - + + +/// @brief Return @c true if @a x is equal to zero to within +/// the default floating-point comparison tolerance. template<typename Type> inline bool -isNegative(const Type& x) +isApproxZero(const Type& x) { - return x < zeroVal<Type>(); -} + const Type tolerance = Type(zeroVal<Type>() + Tolerance<Type>::value()); + return x < tolerance && x > -tolerance; +} +/// Return @c true if @a x is equal to zero to within the given tolerance. +template<typename Type> +inline bool +isApproxZero(const Type& x, const Type& tolerance) +{ + return x < tolerance && x > -tolerance; +} + + +/// Return @c true if @a x is less than zero. +template<typename Type> +inline bool +isNegative(const Type& x) { return x < zeroVal<Type>(); } + +/// Return @c false, since @c bool values are never less than zero. +template<> inline bool isNegative<bool>(const bool&) { return false; } + + +/// @brief Return @c true if @a a is equal to @a b to within +/// the default floating-point comparison tolerance. template<typename Type> inline bool isApproxEqual(const Type& a, const Type& b) { - const Type tolerance = Type(zeroVal<Type>() + toleranceValue<Type>()); + const Type tolerance = Type(zeroVal<Type>() + Tolerance<Type>::value()); return !(Abs(a - b) > tolerance); } + +/// Return @c true if @a a is equal to @a b to within the given tolerance. template<typename Type> inline bool isApproxEqual(const Type& a, const Type& b, const Type& tolerance) @@ -256,6 +335,17 @@ OPENVDB_EXACT_IS_APPROX_EQUAL(bool) OPENVDB_EXACT_IS_APPROX_EQUAL(std::string) +/// @brief Return @c true if @a a is larger than @a b to within +/// the given tolerance, i.e., if @a b - @a a < @a tolerance. +template<typename Type> +inline bool +isApproxLarger(const Type& a, const Type& b, const Type& tolerance) +{ + return (b - a < tolerance); +} + + +/// @brief Return @c true if @a a is exactly equal to @a b. template<typename T0, typename T1> inline bool isExactlyEqual(const T0& a, const T1& b) @@ -294,9 +384,6 @@ isRelOrApproxEqual(const bool& a, const bool& b, const bool&, const bool&) } -//////////////////////////////////////// - - // Avoid strict aliasing issues by using type punning // http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html // Using "casting through a union(2)" @@ -308,6 +395,7 @@ floatToInt32(const float aFloatValue) return foi->int32Value; } + inline int64_t doubleToInt64(const double aDoubleValue) { @@ -359,253 +447,317 @@ isUlpsEqual(const float aLeft, const float aRight, const int32_t aUnitsInLastPla return (difference <= aUnitsInLastPlace); } + +//////////////////////////////////////// + + // ==========> Pow <================== -/// Return x to the power of two, i.e. x*x +/// Return @f$ x^2 @f$. template<typename Type> -inline Type Pow2(Type x) -{ - return x*x; -} +inline Type Pow2(Type x) { return x*x; } -/// Return x to the power of three, i.e. x*x*x +/// Return @f$ x^3 @f$. template<typename Type> -inline Type Pow3(Type x) -{ - return x*x*x; -} +inline Type Pow3(Type x) { return x*x*x; } -/// Return x to the power of four, i.e. x*x*x*x +/// Return @f$ x^4 @f$. template<typename Type> -inline Type Pow4(Type x) -{ - return Pow2(Pow2(x)); -} +inline Type Pow4(Type x) { return Pow2(Pow2(x)); } -/// Return x to the power of n, i.e. x^n -template <typename Type> -Type Pow(Type x, int n) +/// Return @f$ x^n @f$. +template<typename Type> +Type +Pow(Type x, int n) { Type ans = 1; if (n < 0) { n = -n; x = Type(1)/x; } - while(n--) ans *= x; + while (n--) ans *= x; return ans; } -/// Return b to the power of e, i.e. b^e -inline float Pow(float b, float e) +//@{ +/// Return @f$ b^e @f$. +inline float +Pow(float b, float e) { assert( b >= 0.0f && "Pow(float,float): base is negative" ); return powf(b,e); } -/// Return b to the power of e, i.e. b^e -inline double Pow(double b, double e) +inline double +Pow(double b, double e) { assert( b >= 0.0 && "Pow(double,double): base is negative" ); return pow(b,e); } +//@} + // ==========> Max <================== /// Return the maximum of two values -template< typename Type > -inline const Type& Max( const Type& a, const Type& b ) +template<typename Type> +inline const Type& +Max(const Type& a, const Type& b) { return std::max(a,b) ; } /// Return the maximum of three values -template< typename Type > -inline const Type& Max( const Type& a, const Type& b, const Type& c ) +template<typename Type> +inline const Type& +Max(const Type& a, const Type& b, const Type& c) { return std::max( std::max(a,b), c ) ; } /// Return the maximum of four values -template< typename Type > -inline const Type& Max( const Type& a, const Type& b, const Type& c, const Type& d ) +template<typename Type> +inline const Type& +Max(const Type& a, const Type& b, const Type& c, const Type& d) { - return std::max( std::max(a,b), std::max(c,d) ) ; + return std::max(std::max(a,b), std::max(c,d)); } /// Return the maximum of five values -template< typename Type > -inline const Type& Max( const Type& a, const Type& b, const Type& c, - const Type& d, const Type& e ) +template<typename Type> +inline const Type& +Max(const Type& a, const Type& b, const Type& c, const Type& d, const Type& e) { - return std::max( std::max(a,b), Max(c,d,e) ) ; + return std::max(std::max(a,b), Max(c,d,e)); } /// Return the maximum of six values -template< typename Type > -inline const Type& Max( const Type& a, const Type& b, const Type& c, - const Type& d, const Type& e, const Type& f ) +template<typename Type> +inline const Type& +Max(const Type& a, const Type& b, const Type& c, const Type& d, const Type& e, const Type& f) { - return std::max( Max(a,b,c), Max(d,e,f) ) ; + return std::max(Max(a,b,c), Max(d,e,f)); } /// Return the maximum of seven values -template< typename Type > -inline const Type& Max( const Type& a, const Type& b, const Type& c, const Type& d, - const Type& e, const Type& f, const Type& g ) +template<typename Type> +inline const Type& +Max(const Type& a, const Type& b, const Type& c, const Type& d, + const Type& e, const Type& f, const Type& g) { - return std::max( Max(a,b,c,d), Max(e,f,g) ) ; + return std::max(Max(a,b,c,d), Max(e,f,g)); } /// Return the maximum of eight values -template< typename Type > -inline const Type& Max( const Type& a, const Type& b, const Type& c, const Type& d, - const Type& e, const Type& f, const Type& g, const Type& h ) +template<typename Type> +inline const Type& +Max(const Type& a, const Type& b, const Type& c, const Type& d, + const Type& e, const Type& f, const Type& g, const Type& h) { - return std::max( Max(a,b,c,d), Max(e,f,g,h) ) ; + return std::max(Max(a,b,c,d), Max(e,f,g,h)); } + // ==========> Min <================== /// Return the minimum of two values -template< typename Type > -inline const Type& Min( const Type& a, const Type& b ) -{ - return std::min(a,b) ; -} +template<typename Type> +inline const Type& +Min(const Type& a, const Type& b) { return std::min(a, b); } /// Return the minimum of three values -template< typename Type > -inline const Type& Min( const Type& a, const Type& b, const Type& c ) -{ - return std::min( std::min(a,b), c ) ; -} +template<typename Type> +inline const Type& +Min(const Type& a, const Type& b, const Type& c) { return std::min(std::min(a, b), c); } /// Return the minimum of four values -template< typename Type > -inline const Type& Min( const Type& a, const Type& b, const Type& c, const Type& d ) +template<typename Type> +inline const Type& +Min(const Type& a, const Type& b, const Type& c, const Type& d) { - return std::min( std::min(a,b), std::min(c,d) ) ; + return std::min(std::min(a, b), std::min(c, d)); } /// Return the minimum of five values -template< typename Type > -inline const Type& Min( const Type& a, const Type& b, const Type& c, - const Type& d, const Type& e ) +template<typename Type> +inline const Type& +Min(const Type& a, const Type& b, const Type& c, const Type& d, const Type& e) { - return std::min( std::min(a,b), Min(c,d,e) ) ; + return std::min(std::min(a,b), Min(c,d,e)); } /// Return the minimum of six values -template< typename Type > -inline const Type& Min( const Type& a, const Type& b, const Type& c, - const Type& d, const Type& e, const Type& f ) +template<typename Type> +inline const Type& +Min(const Type& a, const Type& b, const Type& c, const Type& d, const Type& e, const Type& f) { - return std::min( Min(a,b,c), Min(d,e,f) ) ; + return std::min(Min(a,b,c), Min(d,e,f)); } /// Return the minimum of seven values -template< typename Type > -inline const Type& Min( const Type& a, const Type& b, const Type& c, const Type& d, - const Type& e, const Type& f, const Type& g ) +template<typename Type> +inline const Type& +Min(const Type& a, const Type& b, const Type& c, const Type& d, + const Type& e, const Type& f, const Type& g) { - return std::min( Min(a,b,c,d), Min(e,f,g) ) ; + return std::min(Min(a,b,c,d), Min(e,f,g)); } /// Return the minimum of eight values -template< typename Type > -inline const Type& Min( const Type& a, const Type& b, const Type& c, const Type& d, - const Type& e, const Type& f, const Type& g, const Type& h ) +template<typename Type> +inline const Type& +Min(const Type& a, const Type& b, const Type& c, const Type& d, + const Type& e, const Type& f, const Type& g, const Type& h) { - return std::min( Min(a,b,c,d), Min(e,f,g,h) ) ; + return std::min(Min(a,b,c,d), Min(e,f,g,h)); } -/// Return the sign of a variable as an integer. The three cases are -1, 0 or 1 -template <typename Type> -inline int Sign(const Type &x) {return ( (x)<0 ? -1 : (x)==0 ? 0 : 1);} +// ============> Exp <================== + +/// Return @f$ e^x @f$. +template<typename Type> +inline Type Exp(const Type& x) { return std::exp(x); } + + +//////////////////////////////////////// -/// Return square-root of a floating point -inline float Sqrt(float x) {return sqrtf(x);} -inline double Sqrt(double x){return sqrt(x);} -inline long double Sqrt(long double x) {return sqrtl(x);} +/// Return the sign of the given value as an integer (either -1, 0 or 1). +template <typename Type> +inline int Sign(const Type &x) { return (zeroVal<Type>() < x) - (x < zeroVal<Type>()); } -/// Return remainder of x/y = Mod -inline int Mod(int i, int j) {return (i%j);}; -inline float Mod(float x, float y) {return fmodf(x,y);} -inline double Mod(double x, double y){return fmod(x,y);} -inline long double Mod(long double x, long double y) {return fmodl(x,y);} -/// Return reminder of x/y +/// @brief Return @c true if @a a and @a b have different signs. +/// @note Zero is considered a positive number. template <typename Type> -inline Type Reminder(Type x, Type y) {return Mod(x,y);} +inline bool +SignChange(const Type& a, const Type& b) +{ + return ( (a<zeroVal<Type>()) ^ (b<zeroVal<Type>()) ); +} + + +/// @brief Return @c true if the interval [@a a, @a b] includes zero, +/// i.e., if either @a a or @a b is zero or if they have different signs. +template <typename Type> +inline bool +ZeroCrossing(const Type& a, const Type& b) +{ + return a * b <= zeroVal<Type>(); +} -/// Return round up to nearest integer or base + +//@{ +/// Return the square root of a floating-point value. +inline float Sqrt(float x) { return sqrtf(x); } +inline double Sqrt(double x) { return sqrt(x); } +inline long double Sqrt(long double x) { return sqrtl(x); } +//@} + + +//@{ +/// Return the cube root of a floating-point value. +inline float Cbrt(float x) { return boost::math::cbrt(x); } +inline double Cbrt(double x) { return boost::math::cbrt(x); } +inline long double Cbrt(long double x) { return boost::math::cbrt(x); } +//@} + + +//@{ +/// Return the remainder of @a x / @a y. +inline int Mod(int x, int y) { return (x % y); } +inline float Mod(float x, float y) { return fmodf(x,y); } +inline double Mod(double x, double y) { return fmod(x,y); } +inline long double Mod(long double x, long double y) { return fmodl(x,y); } +template<typename Type> inline Type Remainder(Type x, Type y) { return Mod(x,y); } +//@} + + +//@{ +/// Return @a x rounded up to the nearest integer. inline float RoundUp(float x) { return ceilf(x); } inline double RoundUp(double x) { return ceil(x); } inline long double RoundUp(long double x) { return ceill(x); } -template <typename Type> -inline Type RoundUp(Type x, Type base) +//@} +/// Return @a x rounded up to the nearest multiple of @a base. +template<typename Type> +inline Type +RoundUp(Type x, Type base) { - Type reminder=Reminder(x,base); - return reminder ? x-reminder+base : x; + Type remainder = Remainder(x, base); + return remainder ? x-remainder+base : x; } -/// Return rounds down to nearest integer or base +//@{ +/// Return @a x rounded down to the nearest integer. inline float RoundDown(float x) { return floorf(x); } -inline double RoundDown(double x){ return floor(x); } +inline double RoundDown(double x) { return floor(x); } inline long double RoundDown(long double x) { return floorl(x); } -template <typename Type> -inline Type RoundDown(Type x, Type base) +template <typename Type> inline Type Round(Type x) { return RoundDown(x+0.5); } +//@} +/// Return @a x rounded down to the nearest multiple of @a base. +template<typename Type> +inline Type +RoundDown(Type x, Type base) { - Type reminder=Reminder(x,base); - if (!reminder) - return x; - else - return x-reminder; + Type remainder = Remainder(x, base); + return remainder ? x-remainder : x; } -/// Return integer part -template <typename Type> -inline Type IntegerPart(Type x) + +/// Return the integer part of @a x. +template<typename Type> +inline Type +IntegerPart(Type x) { return (x > 0 ? RoundDown(x) : RoundUp(x)); } -/// Return fractional part -template <typename Type> +/// Return the fractional part of @a x. +template<typename Type> inline Type FractionalPart(Type x) { return Mod(x,Type(1)); } -/// Return floor + +//@{ +/// Return the floor of @a x. inline int Floor(float x) { return (int)RoundDown(x); } inline int Floor(double x) { return (int)RoundDown(x); } inline int Floor(long double x) { return (int)RoundDown(x); } +//@} -/// Return ceil + +//@{ +/// Return the ceiling of @a x. inline int Ceil(float x) { return (int)RoundUp(x); } inline int Ceil(double x) { return (int)RoundUp(x); } inline int Ceil(long double x) { return (int)RoundUp(x); } +//@} -/// Return rounds off x to nearest integer value -template <typename Type> -inline Type Round(Type x) { return RoundDown(x+0.5); } -/// Return chop of x -template <typename Type> -inline Type Chop(Type x, Type delta) { return (Abs(x) < delta ? 0 : x); } +/// Return @a x if it is greater in magnitude than @a delta. Otherwise, return zero. +template<typename Type> +inline Type Chop(Type x, Type delta) { return (Abs(x) < delta ? zeroVal<Type>() : x); } -/// Return truncation of x to smoe digits -template <typename Type> -inline Type Truncate(Type x,unsigned int digits) + +/// Return @a x truncated to the given number of decimal digits. +template<typename Type> +inline Type +Truncate(Type x, unsigned int digits) { - Type tenth=Pow(10,digits); + Type tenth = Pow(10,digits); return RoundDown(x*tenth+0.5)/tenth; } -/// Return inverse of x -template <typename Type> -inline Type Inv(Type x) + +//////////////////////////////////////// + + +/// Return the inverse of @a x. +template<typename Type> +inline Type +Inv(Type x) { assert(x); return Type(1)/x; @@ -637,9 +789,40 @@ struct promote { }; -template<typename T> struct tolerance { static T value() { return 0; } }; -template<> struct tolerance<float> { static float value() { return 1e-8f; } }; -template<> struct tolerance<double> { static double value() { return 1e-15; } }; +/// @brief Return the index [0,1,2] of the smallest value in a 3D vector. +/// @note This methods assumes operator[] exists and avoids branching. +/// @details If two components of the input vector are equal and smaller then the +/// third component, the largest index of the two is always returned. +/// If all three vector components are equal the largest index, i.e. 2, is +/// returned. In other words the return value corresponds to the largest index +/// of the of the smallest vector components. +template<typename Vec3T> +size_t +MinIndex(const Vec3T& v) +{ + static const size_t hashTable[8] = { 2, 1, 9, 1, 2, 9, 0, 0 };//9 is a dummy value + const size_t hashKey = + ((v[0] < v[1]) << 2) + ((v[0] < v[2]) << 1) + (v[1] < v[2]);// ?*4+?*2+?*1 + return hashTable[hashKey]; +} + + +/// @brief Return the index [0,1,2] of the largest value in a 3D vector. +/// @note This methods assumes operator[] exists and avoids branching. +/// @details If two components of the input vector are equal and larger then the +/// third component, the largest index of the two is always returned. +/// If all three vector components are equal the largest index, i.e. 2, is +/// returned. In other words the return value corresponds to the largest index +/// of the of the largest vector components. +template<typename Vec3T> +size_t +MaxIndex(const Vec3T& v) +{ + static const size_t hashTable[8] = { 2, 1, 9, 1, 2, 9, 0, 0 };//9 is a dummy value + const size_t hashKey = + ((v[0] > v[1]) << 2) + ((v[0] > v[2]) << 1) + (v[1] > v[2]);// ?*4+?*2+?*1 + return hashTable[hashKey]; +} } // namespace math } // namespace OPENVDB_VERSION_NAME diff --git a/extern/openvdb/internal/openvdb/math/Operators.h b/extern/openvdb/internal/openvdb/math/Operators.h index ce99ffb864f..1203a8dd395 100644 --- a/extern/openvdb/internal/openvdb/math/Operators.h +++ b/extern/openvdb/internal/openvdb/math/Operators.h @@ -290,9 +290,6 @@ struct ISGradientNormSqrd<HJWENO5_BIAS> static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk) { - typedef typename Accessor::ValueType ValueType; - typedef math::Vec3<ValueType> Vec3Type; - // SSE optimized const simd::Float4 v1(grid.getValue(ijk.offsetBy(-2, 0, 0)) - grid.getValue(ijk.offsetBy(-3, 0, 0)), @@ -324,9 +321,6 @@ struct ISGradientNormSqrd<HJWENO5_BIAS> static typename StencilT::ValueType result(const StencilT& s) { - typedef typename StencilT::ValueType ValueType; - typedef math::Vec3<ValueType> Vec3Type; - // SSE optimized const simd::Float4 v1(s.template getValue<-2, 0, 0>() - s.template getValue<-3, 0, 0>(), @@ -539,68 +533,86 @@ struct ISCurl template<DDScheme DiffScheme2, DScheme DiffScheme1> struct ISMeanCurvature { - // random access version + /// @brief random access version + /// @return true if the gradient is none-zero, in which case the + /// mean curvature is computed as two parts: @c alpha is the numerator in + /// @f$\nabla \cdot (\nabla \phi / |\nabla \phi|)@f$, and @c beta is @f$|\nabla \phi|@f$. template<typename Accessor> - static void result(const Accessor& grid, const Coord& ijk, + static bool result(const Accessor& grid, const Coord& ijk, typename Accessor::ValueType& alpha, typename Accessor::ValueType& beta) { typedef typename Accessor::ValueType ValueType; - ValueType Dx = D1<DiffScheme1>::inX(grid, ijk); - ValueType Dy = D1<DiffScheme1>::inY(grid, ijk); - ValueType Dz = D1<DiffScheme1>::inZ(grid, ijk); - - ValueType Dx2 = Dx*Dx; - ValueType Dy2 = Dy*Dy; - ValueType Dz2 = Dz*Dz; + const ValueType Dx = D1<DiffScheme1>::inX(grid, ijk); + const ValueType Dy = D1<DiffScheme1>::inY(grid, ijk); + const ValueType Dz = D1<DiffScheme1>::inZ(grid, ijk); + + const ValueType Dx2 = Dx*Dx; + const ValueType Dy2 = Dy*Dy; + const ValueType Dz2 = Dz*Dz; + const ValueType normGrad = Dx2 + Dy2 + Dz2; + if (normGrad <= math::Tolerance<ValueType>::value()) { + alpha = beta = 0; + return false; + } - ValueType Dxx = D2<DiffScheme2>::inX(grid, ijk); - ValueType Dyy = D2<DiffScheme2>::inY(grid, ijk); - ValueType Dzz = D2<DiffScheme2>::inZ(grid, ijk); + const ValueType Dxx = D2<DiffScheme2>::inX(grid, ijk); + const ValueType Dyy = D2<DiffScheme2>::inY(grid, ijk); + const ValueType Dzz = D2<DiffScheme2>::inZ(grid, ijk); - ValueType Dxy = D2<DiffScheme2>::inXandY(grid, ijk); - ValueType Dyz = D2<DiffScheme2>::inYandZ(grid, ijk); - ValueType Dxz = D2<DiffScheme2>::inXandZ(grid, ijk); + const ValueType Dxy = D2<DiffScheme2>::inXandY(grid, ijk); + const ValueType Dyz = D2<DiffScheme2>::inYandZ(grid, ijk); + const ValueType Dxz = D2<DiffScheme2>::inXandZ(grid, ijk); // for return alpha = (Dx2*(Dyy+Dzz)+Dy2*(Dxx+Dzz)+Dz2*(Dxx+Dyy)-2*(Dx*(Dy*Dxy+Dz*Dxz)+Dy*Dz*Dyz)); - beta = ValueType(std::sqrt(double(Dx2 + Dy2 + Dz2))); // * 1/dx + beta = ValueType(std::sqrt(double(normGrad))); // * 1/dx + return true; } - // stencil access version + /// @brief stencil access version + /// @return true if the gradient is none-zero, in which case the + /// mean curvature is computed as two parts: @c alpha is the numerator in + /// @f$\nabla \cdot (\nabla \phi / |\nabla \phi|)@f$, and @c beta is @f$|\nabla \phi|@f$. template<typename StencilT> - static void result(const StencilT& stencil, + static bool result(const StencilT& stencil, typename StencilT::ValueType& alpha, typename StencilT::ValueType& beta) { typedef typename StencilT::ValueType ValueType; - ValueType Dx = D1<DiffScheme1>::inX(stencil); - ValueType Dy = D1<DiffScheme1>::inY(stencil); - ValueType Dz = D1<DiffScheme1>::inZ(stencil); - - ValueType Dx2 = Dx*Dx; - ValueType Dy2 = Dy*Dy; - ValueType Dz2 = Dz*Dz; + const ValueType Dx = D1<DiffScheme1>::inX(stencil); + const ValueType Dy = D1<DiffScheme1>::inY(stencil); + const ValueType Dz = D1<DiffScheme1>::inZ(stencil); + + const ValueType Dx2 = Dx*Dx; + const ValueType Dy2 = Dy*Dy; + const ValueType Dz2 = Dz*Dz; + const ValueType normGrad = Dx2 + Dy2 + Dz2; + if (normGrad <= math::Tolerance<ValueType>::value()) { + alpha = beta = 0; + return false; + } - ValueType Dxx = D2<DiffScheme2>::inX(stencil); - ValueType Dyy = D2<DiffScheme2>::inY(stencil); - ValueType Dzz = D2<DiffScheme2>::inZ(stencil); + const ValueType Dxx = D2<DiffScheme2>::inX(stencil); + const ValueType Dyy = D2<DiffScheme2>::inY(stencil); + const ValueType Dzz = D2<DiffScheme2>::inZ(stencil); - ValueType Dxy = D2<DiffScheme2>::inXandY(stencil); - ValueType Dyz = D2<DiffScheme2>::inYandZ(stencil); - ValueType Dxz = D2<DiffScheme2>::inXandZ(stencil); + const ValueType Dxy = D2<DiffScheme2>::inXandY(stencil); + const ValueType Dyz = D2<DiffScheme2>::inYandZ(stencil); + const ValueType Dxz = D2<DiffScheme2>::inXandZ(stencil); // for return alpha = (Dx2*(Dyy+Dzz)+Dy2*(Dxx+Dzz)+Dz2*(Dxx+Dyy)-2*(Dx*(Dy*Dxy+Dz*Dxz)+Dy*Dz*Dyz)); - beta = ValueType(std::sqrt(double(Dx2 + Dy2 + Dz2))); // * 1/dx + beta = ValueType(std::sqrt(double(normGrad))); // * 1/dx + return true; } }; +//////////////////////////////////////////////////////// // --- Operators defined in the Range of a given map - //@{ /// @brief Center difference gradient operators, defined with respect to /// the range-space of the @c map @@ -631,7 +643,7 @@ struct Gradient } }; - +// Partial template specialization of Gradient // translation, any order template<DScheme DiffScheme> struct Gradient<TranslationMap, DiffScheme> @@ -653,8 +665,8 @@ struct Gradient<TranslationMap, DiffScheme> } }; - -// uniform scale, 2nd order +/// Full template specialization of Gradient +/// uniform scale, 2nd order template<> struct Gradient<UniformScaleMap, CD_2ND> { @@ -685,8 +697,8 @@ struct Gradient<UniformScaleMap, CD_2ND> } }; - -// uniform scale translate, 2nd order +/// Full template specialization of Gradient +/// uniform scale translate, 2nd order template<> struct Gradient<UniformScaleTranslateMap, CD_2ND> { @@ -717,8 +729,8 @@ struct Gradient<UniformScaleTranslateMap, CD_2ND> } }; - -// scale, 2nd order +/// Full template specialization of Gradient +/// scale, 2nd order template<> struct Gradient<ScaleMap, CD_2ND> { @@ -751,8 +763,8 @@ struct Gradient<ScaleMap, CD_2ND> } }; - -// scale translate, 2nd order +/// Full template specialization of Gradient +/// scale translate, 2nd order template<> struct Gradient<ScaleTranslateMap, CD_2ND> { @@ -820,6 +832,9 @@ struct GradientBiased //@} +//////////////////////////////////////////////////////// + +// Computes |Grad[Phi]| using upwinding template<typename MapType, BiasedGradientScheme GradScheme> struct GradientNormSqrd { @@ -845,7 +860,7 @@ struct GradientNormSqrd static typename StencilT::ValueType result(const MapType& map, const StencilT& stencil) { - typedef typename StencilT::ValueType ValueType; + typedef typename StencilT::ValueType ValueType; typedef math::Vec3<ValueType> Vec3Type; Vec3Type up = Gradient<MapType, FD>::result(map, stencil); @@ -854,7 +869,7 @@ struct GradientNormSqrd } }; - +/// Partial template specialization of GradientNormSqrd template<BiasedGradientScheme GradScheme> struct GradientNormSqrd<UniformScaleMap, GradScheme> { @@ -881,6 +896,7 @@ struct GradientNormSqrd<UniformScaleMap, GradScheme> } }; +/// Partial template specialization of GradientNormSqrd template<BiasedGradientScheme GradScheme> struct GradientNormSqrd<UniformScaleTranslateMap, GradScheme> { @@ -918,7 +934,6 @@ struct Divergence template<typename Accessor> static typename Accessor::ValueType::value_type result(const MapType& map, const Accessor& grid, const Coord& ijk) { - typedef typename Accessor::ValueType Vec3Type; typedef typename Accessor::ValueType::value_type ValueType; ValueType div(0); @@ -935,7 +950,6 @@ struct Divergence template<typename StencilT> static typename StencilT::ValueType::value_type result(const MapType& map, const StencilT& stencil) { - typedef typename StencilT::ValueType Vec3Type; typedef typename StencilT::ValueType::value_type ValueType; ValueType div(0); @@ -949,8 +963,8 @@ struct Divergence } }; - -// translation, any scheme +/// Partial template specialization of Divergence +/// translation, any scheme template<DScheme DiffScheme> struct Divergence<TranslationMap, DiffScheme> { @@ -958,7 +972,6 @@ struct Divergence<TranslationMap, DiffScheme> template<typename Accessor> static typename Accessor::ValueType::value_type result(const TranslationMap&, const Accessor& grid, const Coord& ijk) { - typedef typename Accessor::ValueType Vec3Type; typedef typename Accessor::ValueType::value_type ValueType; ValueType div(0); @@ -970,7 +983,6 @@ struct Divergence<TranslationMap, DiffScheme> template<typename StencilT> static typename StencilT::ValueType::value_type result(const TranslationMap&, const StencilT& stencil) { - typedef typename StencilT::ValueType Vec3Type; typedef typename StencilT::ValueType::value_type ValueType; ValueType div(0); @@ -979,8 +991,8 @@ struct Divergence<TranslationMap, DiffScheme> } }; - -// uniform scale, any scheme +/// Partial template specialization of Divergence +/// uniform scale, any scheme template<DScheme DiffScheme> struct Divergence<UniformScaleMap, DiffScheme> { @@ -988,7 +1000,6 @@ struct Divergence<UniformScaleMap, DiffScheme> template<typename Accessor> static typename Accessor::ValueType::value_type result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk) { - typedef typename Accessor::ValueType Vec3Type; typedef typename Accessor::ValueType::value_type ValueType; ValueType div(0); @@ -1002,7 +1013,6 @@ struct Divergence<UniformScaleMap, DiffScheme> template<typename StencilT> static typename StencilT::ValueType::value_type result(const UniformScaleMap& map, const StencilT& stencil) { - typedef typename StencilT::ValueType Vec3Type; typedef typename StencilT::ValueType::value_type ValueType; ValueType div(0); @@ -1013,8 +1023,8 @@ struct Divergence<UniformScaleMap, DiffScheme> } }; - -// uniform scale and translation, any scheme +/// Partial template specialization of Divergence +/// uniform scale and translation, any scheme template<DScheme DiffScheme> struct Divergence<UniformScaleTranslateMap, DiffScheme> { @@ -1022,7 +1032,6 @@ struct Divergence<UniformScaleTranslateMap, DiffScheme> template<typename Accessor> static typename Accessor::ValueType::value_type result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) { - typedef typename Accessor::ValueType Vec3Type; typedef typename Accessor::ValueType::value_type ValueType; ValueType div(0); @@ -1036,7 +1045,6 @@ struct Divergence<UniformScaleTranslateMap, DiffScheme> template<typename StencilT> static typename StencilT::ValueType::value_type result(const UniformScaleTranslateMap& map, const StencilT& stencil) { - typedef typename StencilT::ValueType Vec3Type; typedef typename StencilT::ValueType::value_type ValueType; ValueType div(0); @@ -1047,8 +1055,8 @@ struct Divergence<UniformScaleTranslateMap, DiffScheme> } }; - -// uniform scale 2nd order +/// Full template specialization of Divergence +/// uniform scale 2nd order template<> struct Divergence<UniformScaleMap, CD_2ND> { @@ -1056,7 +1064,6 @@ struct Divergence<UniformScaleMap, CD_2ND> template<typename Accessor> static typename Accessor::ValueType::value_type result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk) { - typedef typename Accessor::ValueType Vec3Type; typedef typename Accessor::ValueType::value_type ValueType; ValueType div(0); @@ -1069,7 +1076,6 @@ struct Divergence<UniformScaleMap, CD_2ND> template<typename StencilT> static typename StencilT::ValueType::value_type result(const UniformScaleMap& map, const StencilT& stencil) { - typedef typename StencilT::ValueType Vec3Type; typedef typename StencilT::ValueType::value_type ValueType; ValueType div(0); @@ -1079,8 +1085,8 @@ struct Divergence<UniformScaleMap, CD_2ND> } }; - -// uniform scale translate 2nd order +/// Full template specialization of Divergence +/// uniform scale translate 2nd order template<> struct Divergence<UniformScaleTranslateMap, CD_2ND> { @@ -1088,7 +1094,6 @@ struct Divergence<UniformScaleTranslateMap, CD_2ND> template<typename Accessor> static typename Accessor::ValueType::value_type result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) { - typedef typename Accessor::ValueType Vec3Type; typedef typename Accessor::ValueType::value_type ValueType; ValueType div(0); @@ -1102,7 +1107,6 @@ struct Divergence<UniformScaleTranslateMap, CD_2ND> template<typename StencilT> static typename StencilT::ValueType::value_type result(const UniformScaleTranslateMap& map, const StencilT& stencil) { - typedef typename StencilT::ValueType Vec3Type; typedef typename StencilT::ValueType::value_type ValueType; ValueType div(0); @@ -1113,8 +1117,8 @@ struct Divergence<UniformScaleTranslateMap, CD_2ND> } }; - -// scale, any scheme +/// Partial template specialization of Divergence +/// scale, any scheme template<DScheme DiffScheme> struct Divergence<ScaleMap, DiffScheme> { @@ -1122,7 +1126,6 @@ struct Divergence<ScaleMap, DiffScheme> template<typename Accessor> static typename Accessor::ValueType::value_type result(const ScaleMap& map, const Accessor& grid, const Coord& ijk) { - typedef typename Accessor::ValueType Vec3Type; typedef typename Accessor::ValueType::value_type ValueType; ValueType div = ValueType( @@ -1136,7 +1139,6 @@ struct Divergence<ScaleMap, DiffScheme> template<typename StencilT> static typename StencilT::ValueType::value_type result(const ScaleMap& map, const StencilT& stencil) { - typedef typename StencilT::ValueType Vec3Type; typedef typename StencilT::ValueType::value_type ValueType; ValueType div(0); @@ -1148,8 +1150,8 @@ struct Divergence<ScaleMap, DiffScheme> } }; - -// scale translate, any scheme +/// Partial template specialization of Divergence +/// scale translate, any scheme template<DScheme DiffScheme> struct Divergence<ScaleTranslateMap, DiffScheme> { @@ -1157,7 +1159,6 @@ struct Divergence<ScaleTranslateMap, DiffScheme> template<typename Accessor> static typename Accessor::ValueType::value_type result(const ScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) { - typedef typename Accessor::ValueType Vec3Type; typedef typename Accessor::ValueType::value_type ValueType; ValueType div = ValueType( @@ -1171,7 +1172,6 @@ struct Divergence<ScaleTranslateMap, DiffScheme> template<typename StencilT> static typename StencilT::ValueType::value_type result(const ScaleTranslateMap& map, const StencilT& stencil) { - typedef typename StencilT::ValueType Vec3Type; typedef typename StencilT::ValueType::value_type ValueType; ValueType div(0); @@ -1183,8 +1183,8 @@ struct Divergence<ScaleTranslateMap, DiffScheme> } }; - -// scale 2nd order +/// Full template specialization Divergence +/// scale 2nd order template<> struct Divergence<ScaleMap, CD_2ND> { @@ -1192,7 +1192,6 @@ struct Divergence<ScaleMap, CD_2ND> template<typename Accessor> static typename Accessor::ValueType::value_type result(const ScaleMap& map, const Accessor& grid, const Coord& ijk) { - typedef typename Accessor::ValueType Vec3Type; typedef typename Accessor::ValueType::value_type ValueType; ValueType div = ValueType( @@ -1206,7 +1205,6 @@ struct Divergence<ScaleMap, CD_2ND> template<typename StencilT> static typename StencilT::ValueType::value_type result(const ScaleMap& map, const StencilT& stencil) { - typedef typename StencilT::ValueType Vec3Type; typedef typename StencilT::ValueType::value_type ValueType; ValueType div = ValueType( @@ -1217,8 +1215,8 @@ struct Divergence<ScaleMap, CD_2ND> } }; - -// scale and translate, 2nd order +/// Full template specialization of Divergence +/// scale and translate, 2nd order template<> struct Divergence<ScaleTranslateMap, CD_2ND> { @@ -1226,7 +1224,6 @@ struct Divergence<ScaleTranslateMap, CD_2ND> template<typename Accessor> static typename Accessor::ValueType::value_type result(const ScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) { - typedef typename Accessor::ValueType Vec3Type; typedef typename Accessor::ValueType::value_type ValueType; ValueType div = ValueType( @@ -1240,7 +1237,6 @@ struct Divergence<ScaleTranslateMap, CD_2ND> template<typename StencilT> static typename StencilT::ValueType::value_type result(const ScaleTranslateMap& map, const StencilT& stencil) { - typedef typename StencilT::ValueType Vec3Type; typedef typename StencilT::ValueType::value_type ValueType; ValueType div = ValueType( @@ -1298,7 +1294,7 @@ struct Curl } }; - +/// Partial template specialization of Curl template<DScheme DiffScheme> struct Curl<UniformScaleMap, DiffScheme> { @@ -1321,7 +1317,7 @@ struct Curl<UniformScaleMap, DiffScheme> } }; - +/// Partial template specialization of Curl template<DScheme DiffScheme> struct Curl<UniformScaleTranslateMap, DiffScheme> { @@ -1346,6 +1342,7 @@ struct Curl<UniformScaleTranslateMap, DiffScheme> } }; +/// Full template specialization of Curl template<> struct Curl<UniformScaleMap, CD_2ND> { @@ -1370,6 +1367,7 @@ struct Curl<UniformScaleMap, CD_2ND> } }; +/// Full template specialization of Curl template<> struct Curl<UniformScaleTranslateMap, CD_2ND> { @@ -1731,18 +1729,33 @@ struct CPT_RANGE template<typename MapType, DDScheme DiffScheme2, DScheme DiffScheme1> struct MeanCurvature { - // random access version + /// @brief random access version + /// @return true if the gradient is none-zero, in which case the + /// mean curvature is computed as two parts: @c alpha is the numerator in + /// @f$\nabla \cdot (\nabla \phi / |\nabla \phi|)@f$, and @c beta is @f$|\nabla \phi|@f$. template<typename Accessor> - static void compute(const MapType& map, const Accessor& grid, const Coord& ijk, - double& alpha, double& beta) + static bool compute(const MapType& map, const Accessor& grid, const Coord& ijk, + double& alpha, double& beta) { typedef typename Accessor::ValueType ValueType; - typedef Vec3<ValueType> Vec3Type; - // compute the gradient in index space + // compute the gradient in index and world space Vec3d d1_is(D1<DiffScheme1>::inX(grid, ijk), D1<DiffScheme1>::inY(grid, ijk), - D1<DiffScheme1>::inZ(grid, ijk) ); + D1<DiffScheme1>::inZ(grid, ijk)), d1_ws; + if (is_linear<MapType>::value) {//resolved at compiletime + d1_ws = map.applyIJT(d1_is); + } else { + d1_ws = map.applyIJT(d1_is, ijk.asVec3d()); + } + const double Dx2 = d1_ws(0)*d1_ws(0); + const double Dy2 = d1_ws(1)*d1_ws(1); + const double Dz2 = d1_ws(2)*d1_ws(2); + const double normGrad = Dx2 + Dy2 + Dz2; + if (normGrad <= math::Tolerance<double>::value()) { + alpha = beta = 0; + return false; + } // all the second derivatives in index space ValueType iddx = D2<DiffScheme2>::inX(grid, ijk); @@ -1758,28 +1771,21 @@ struct MeanCurvature iddxy, iddy, iddyz, iddxz, iddyz, iddz); - // convert to range space - Mat3d d2_rs; - Vec3d d1_rs; - if (is_linear<MapType>::value) { - d2_rs = map.applyIJC(d2_is); - d1_rs = map.applyIJT(d1_is); + // convert second derivatives to world space + Mat3d d2_ws; + if (is_linear<MapType>::value) {//resolved at compiletime + d2_ws = map.applyIJC(d2_is); } else { - d2_rs = map.applyIJC(d2_is, d1_is, ijk.asVec3d()); - d1_rs = map.applyIJT(d1_is, ijk.asVec3d()); + d2_ws = map.applyIJC(d2_is, d1_is, ijk.asVec3d()); } - // assemble the mean curvature - double Dx2 = d1_rs(0)*d1_rs(0); - double Dy2 = d1_rs(1)*d1_rs(1); - double Dz2 = d1_rs(2)*d1_rs(2); - - // for return - alpha = (Dx2*(d2_rs(1,1)+d2_rs(2,2))+Dy2*(d2_rs(0,0)+d2_rs(2,2)) - +Dz2*(d2_rs(0,0)+d2_rs(1,1)) - -2*(d1_rs(0)*(d1_rs(1)*d2_rs(0,1)+d1_rs(2)*d2_rs(0,2)) - +d1_rs(1)*d1_rs(2)*d2_rs(1,2))); - beta = std::sqrt(Dx2 + Dy2 + Dz2); // * 1/dx + // assemble the nominator and denominator for mean curvature + alpha = (Dx2*(d2_ws(1,1)+d2_ws(2,2))+Dy2*(d2_ws(0,0)+d2_ws(2,2)) + +Dz2*(d2_ws(0,0)+d2_ws(1,1)) + -2*(d1_ws(0)*(d1_ws(1)*d2_ws(0,1)+d1_ws(2)*d2_ws(0,2)) + +d1_ws(1)*d1_ws(2)*d2_ws(1,2))); + beta = std::sqrt(normGrad); // * 1/dx + return true; } template<typename Accessor> @@ -1788,9 +1794,8 @@ struct MeanCurvature { typedef typename Accessor::ValueType ValueType; double alpha, beta; - compute(map, grid, ijk, alpha, beta); - - return ValueType(alpha/(2. *math::Pow3(beta))); + return compute(map, grid, ijk, alpha, beta) ? + ValueType(alpha/(2. *math::Pow3(beta))) : 0; } template<typename Accessor> @@ -1799,22 +1804,37 @@ struct MeanCurvature { typedef typename Accessor::ValueType ValueType; double alpha, beta; - compute(map, grid, ijk, alpha, beta); - - return ValueType(alpha/(2. *math::Pow2(beta))); + return compute(map, grid, ijk, alpha, beta) ? + ValueType(alpha/(2. *math::Pow2(beta))) : 0; } - // stencil access version + /// @brief stencil access version + /// @return true if the gradient is none-zero, in which case the + /// mean curvature is computed as two parts: @c alpha is the numerator in + /// @f$\nabla \cdot (\nabla \phi / |\nabla \phi|)@f$, and @c beta is @f$|\nabla \phi|@f$. template<typename StencilT> - static void compute(const MapType& map, const StencilT& stencil, double& alpha, double& beta) + static bool compute(const MapType& map, const StencilT& stencil, + double& alpha, double& beta) { typedef typename StencilT::ValueType ValueType; - typedef Vec3<ValueType> Vec3Type; - // compute the gradient in index space + // compute the gradient in index and world space Vec3d d1_is(D1<DiffScheme1>::inX(stencil), D1<DiffScheme1>::inY(stencil), - D1<DiffScheme1>::inZ(stencil) ); + D1<DiffScheme1>::inZ(stencil) ), d1_ws; + if (is_linear<MapType>::value) {//resolved at compiletime + d1_ws = map.applyIJT(d1_is); + } else { + d1_ws = map.applyIJT(d1_is, stencil.getCenterCoord().asVec3d()); + } + const double Dx2 = d1_ws(0)*d1_ws(0); + const double Dy2 = d1_ws(1)*d1_ws(1); + const double Dz2 = d1_ws(2)*d1_ws(2); + const double normGrad = Dx2 + Dy2 + Dz2; + if (normGrad <= math::Tolerance<double>::value()) { + alpha = beta = 0; + return false; + } // all the second derivatives in index space ValueType iddx = D2<DiffScheme2>::inX(stencil); @@ -1830,28 +1850,21 @@ struct MeanCurvature iddxy, iddy, iddyz, iddxz, iddyz, iddz); - // convert to range space - Mat3d d2_rs; - Vec3d d1_rs; - if (is_linear<MapType>::value) { - d2_rs = map.applyIJC(d2_is); - d1_rs = map.applyIJT(d1_is); + // convert second derivatives to world space + Mat3d d2_ws; + if (is_linear<MapType>::value) {//resolved at compiletime + d2_ws = map.applyIJC(d2_is); } else { - d2_rs = map.applyIJC(d2_is, d1_is, stencil.getCenterCoord().asVec3d()); - d1_rs = map.applyIJT(d1_is, stencil.getCenterCoord().asVec3d()); + d2_ws = map.applyIJC(d2_is, d1_is, stencil.getCenterCoord().asVec3d()); } - // assemble the mean curvature - double Dx2 = d1_rs(0)*d1_rs(0); - double Dy2 = d1_rs(1)*d1_rs(1); - double Dz2 = d1_rs(2)*d1_rs(2); - // for return - alpha = (Dx2*(d2_rs(1,1)+d2_rs(2,2))+Dy2*(d2_rs(0,0)+d2_rs(2,2)) - +Dz2*(d2_rs(0,0)+d2_rs(1,1)) - -2*(d1_rs(0)*(d1_rs(1)*d2_rs(0,1)+d1_rs(2)*d2_rs(0,2)) - +d1_rs(1)*d1_rs(2)*d2_rs(1,2))); - beta = std::sqrt(Dx2 + Dy2 + Dz2); // * 1/dx + alpha = (Dx2*(d2_ws(1,1)+d2_ws(2,2))+Dy2*(d2_ws(0,0)+d2_ws(2,2)) + +Dz2*(d2_ws(0,0)+d2_ws(1,1)) + -2*(d1_ws(0)*(d1_ws(1)*d2_ws(0,1)+d1_ws(2)*d2_ws(0,2)) + +d1_ws(1)*d1_ws(2)*d2_ws(1,2))); + beta = std::sqrt(normGrad); // * 1/dx + return true; } template<typename StencilT> @@ -1860,8 +1873,8 @@ struct MeanCurvature { typedef typename StencilT::ValueType ValueType; double alpha, beta; - compute(map, stencil, alpha, beta); - return ValueType(alpha/(2*math::Pow3(beta))); + return compute(map, stencil, alpha, beta) ? + ValueType(alpha/(2*math::Pow3(beta))) : 0; } template<typename StencilT> @@ -1869,9 +1882,8 @@ struct MeanCurvature { typedef typename StencilT::ValueType ValueType; double alpha, beta; - compute(map, stencil, alpha, beta); - - return ValueType(alpha/(2*math::Pow2(beta))); + return compute(map, stencil, alpha, beta) ? + ValueType(alpha/(2*math::Pow2(beta))) : 0; } }; @@ -1887,9 +1899,8 @@ struct MeanCurvature<TranslationMap, DiffScheme2, DiffScheme1> typedef typename Accessor::ValueType ValueType; ValueType alpha, beta; - ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta); - - return ValueType(alpha /(2*math::Pow3(beta))); + return ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta) ? + ValueType(alpha /(2*math::Pow3(beta))) : 0; } template<typename Accessor> @@ -1899,9 +1910,8 @@ struct MeanCurvature<TranslationMap, DiffScheme2, DiffScheme1> typedef typename Accessor::ValueType ValueType; ValueType alpha, beta; - ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta); - - return ValueType(alpha/(2*math::Pow2(beta))); + return ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta) ? + ValueType(alpha/(2*math::Pow2(beta))) : 0; } // stencil access version @@ -1911,9 +1921,8 @@ struct MeanCurvature<TranslationMap, DiffScheme2, DiffScheme1> typedef typename StencilT::ValueType ValueType; ValueType alpha, beta; - ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta); - - return ValueType(alpha /(2*math::Pow3(beta))); + return ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta) ? + ValueType(alpha /(2*math::Pow3(beta))) : 0; } template<typename StencilT> @@ -1922,9 +1931,8 @@ struct MeanCurvature<TranslationMap, DiffScheme2, DiffScheme1> typedef typename StencilT::ValueType ValueType; ValueType alpha, beta; - ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta); - - return ValueType(alpha/(2*math::Pow2(beta))); + return ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta) ? + ValueType(alpha/(2*math::Pow2(beta))) : 0; } }; @@ -1940,10 +1948,11 @@ struct MeanCurvature<UniformScaleMap, DiffScheme2, DiffScheme1> typedef typename Accessor::ValueType ValueType; ValueType alpha, beta; - ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta); - ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); - - return ValueType(alpha*inv2dx/math::Pow3(beta)); + if (ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta)) { + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return ValueType(alpha*inv2dx/math::Pow3(beta)); + } + return 0; } template<typename Accessor> @@ -1953,10 +1962,11 @@ struct MeanCurvature<UniformScaleMap, DiffScheme2, DiffScheme1> typedef typename Accessor::ValueType ValueType; ValueType alpha, beta; - ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta); - ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); - - return ValueType(alpha*invdxdx/(2*math::Pow2(beta))); + if (ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta)) { + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ValueType(alpha*invdxdx/(2*math::Pow2(beta))); + } + return 0; } // stencil access version @@ -1966,10 +1976,11 @@ struct MeanCurvature<UniformScaleMap, DiffScheme2, DiffScheme1> typedef typename StencilT::ValueType ValueType; ValueType alpha, beta; - ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta); - ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); - - return ValueType(alpha*inv2dx/math::Pow3(beta)); + if (ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta)) { + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return ValueType(alpha*inv2dx/math::Pow3(beta)); + } + return 0; } template<typename StencilT> @@ -1978,10 +1989,11 @@ struct MeanCurvature<UniformScaleMap, DiffScheme2, DiffScheme1> typedef typename StencilT::ValueType ValueType; ValueType alpha, beta; - ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta); - ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); - - return ValueType(alpha*invdxdx/(2*math::Pow2(beta))); + if (ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta)) { + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ValueType(alpha*invdxdx/(2*math::Pow2(beta))); + } + return 0; } }; @@ -1996,10 +2008,11 @@ struct MeanCurvature<UniformScaleTranslateMap, DiffScheme2, DiffScheme1> typedef typename Accessor::ValueType ValueType; ValueType alpha, beta; - ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta); - ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); - - return ValueType(alpha*inv2dx/math::Pow3(beta)); + if (ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta)) { + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return ValueType(alpha*inv2dx/math::Pow3(beta)); + } + return 0; } template<typename Accessor> static typename Accessor::ValueType @@ -2008,10 +2021,11 @@ struct MeanCurvature<UniformScaleTranslateMap, DiffScheme2, DiffScheme1> typedef typename Accessor::ValueType ValueType; ValueType alpha, beta; - ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta); - ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); - - return ValueType(alpha*invdxdx/(2*math::Pow2(beta))); + if (ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta)) { + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ValueType(alpha*invdxdx/(2*math::Pow2(beta))); + } + return 0; } // stencil access version @@ -2021,10 +2035,11 @@ struct MeanCurvature<UniformScaleTranslateMap, DiffScheme2, DiffScheme1> typedef typename StencilT::ValueType ValueType; ValueType alpha, beta; - ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta); - ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); - - return ValueType(alpha*inv2dx/math::Pow3(beta)); + if (ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta)) { + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return ValueType(alpha*inv2dx/math::Pow3(beta)); + } + return 0; } template<typename StencilT> static typename StencilT::ValueType @@ -2033,10 +2048,11 @@ struct MeanCurvature<UniformScaleTranslateMap, DiffScheme2, DiffScheme1> typedef typename StencilT::ValueType ValueType; ValueType alpha, beta; - ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta); - ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); - - return ValueType(alpha*invdxdx/(2*math::Pow2(beta))); + if (ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta)) { + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ValueType(alpha*invdxdx/(2*math::Pow2(beta))); + } + return 0; } }; diff --git a/extern/openvdb/internal/openvdb/math/Proximity.cc b/extern/openvdb/internal/openvdb/math/Proximity.cc index e5cfc278172..9f327ff9718 100644 --- a/extern/openvdb/internal/openvdb/math/Proximity.cc +++ b/extern/openvdb/internal/openvdb/math/Proximity.cc @@ -37,12 +37,39 @@ namespace math { OPENVDB_API Vec3d -closestPointOnTriangleToPoint(const Vec3d& a, const Vec3d& b, const Vec3d& c, - const Vec3d& p, Vec3d& uvw) +closestPointOnTriangleToPoint( + const Vec3d& a, const Vec3d& b, const Vec3d& c, const Vec3d& p, Vec3d& uvw) { + uvw.setZero(); + + // degenerate triangle, singular + if ((isApproxEqual(a, b) && isApproxEqual(a, c))) { + uvw[0] = 1.0; + return a; + } + Vec3d ab = b - a, ac = c - a, ap = p - a; double d1 = ab.dot(ap), d2 = ac.dot(ap); - uvw.setZero(); + + // degenerate triangle edges + if (isApproxEqual(a, b)) { + + double t = 0.0; + Vec3d cp = closestPointOnSegmentToPoint(a, c, p, t); + + uvw[0] = 1.0 - t; + uvw[2] = t; + + return cp; + + } else if (isApproxEqual(a, c) || isApproxEqual(b, c)) { + + double t = 0.0; + Vec3d cp = closestPointOnSegmentToPoint(a, b, p, t); + uvw[0] = 1.0 - t; + uvw[1] = t; + return cp; + } if (d1 <= 0.0 && d2 <= 0.0) { uvw[0] = 1.0; @@ -62,7 +89,7 @@ closestPointOnTriangleToPoint(const Vec3d& a, const Vec3d& b, const Vec3d& c, if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0) { uvw[1] = d1 / (d1 - d3); uvw[0] = 1.0 - uvw[1]; - return a + uvw[1] * ab; // barycentric coordinates (1-v,v,0) + return a + uvw[1] * ab; // barycentric coordinates (1-v,v,0) } // Check if P in vertex region outside C @@ -86,7 +113,7 @@ closestPointOnTriangleToPoint(const Vec3d& a, const Vec3d& b, const Vec3d& c, if (va <= 0.0 && (d4 - d3) >= 0.0 && (d5 - d6) >= 0.0) { uvw[2] = (d4 - d3) / ((d4 - d3) + (d5 - d6)); uvw[1] = 1.0 - uvw[2]; - return b + uvw[2] * (c - b); // barycentric coordinates (0,1-w,w) + return b + uvw[2] * (c - b); // barycentric coordinates (0,1-w,w) } // P inside face region. Compute Q through its barycentric coordinates (u,v,w) @@ -95,277 +122,35 @@ closestPointOnTriangleToPoint(const Vec3d& a, const Vec3d& b, const Vec3d& c, uvw[1] = vb * denom; uvw[0] = 1.0 - uvw[1] - uvw[2]; - return a + ab*uvw[1] + ac*uvw[2]; // = u*a + v*b + w*c , u= va*denom = 1.0-v-w + return a + ab*uvw[1] + ac*uvw[2]; // = u*a + v*b + w*c , u= va*denom = 1.0-v-w } -//////////////////////////////////////// - - -// DEPRECATED METHODS - -double -sLineSeg3ToPointDistSqr(const Vec3d &p0, - const Vec3d &p1, - const Vec3d &point, - double &t, - double epsilon) -{ - Vec3d pDelta; - Vec3d tDelta; - double pDeltaDot; - - pDelta.sub(p1, p0); - tDelta.sub(point, p0); - - // - // Line is nearly a point check end points - // - pDeltaDot = pDelta.dot(pDelta); - if (pDeltaDot < epsilon) { - pDelta.sub(p1, point); - if (pDelta.dot(pDelta) < tDelta.dot(tDelta)) { - t = 1; - return pDelta.dot(pDelta); - } else { - t = 0; - return tDelta.dot(tDelta); - } - } - t = tDelta.dot(pDelta) / pDeltaDot; - if (t < 0) { - t = 0; - } else if (t > 1) { - t = 1; - tDelta.sub(point, p1); - } else { - tDelta -= t * pDelta; - } - return tDelta.dot(tDelta); -} - - -//////////////////////////////////////// - - -double -sTri3ToPointDistSqr(const Vec3d &v0, - const Vec3d &v1, - const Vec3d &v2, - const Vec3d &point, - Vec2d &uv, - double) +OPENVDB_API Vec3d +closestPointOnSegmentToPoint(const Vec3d& a, const Vec3d& b, const Vec3d& p, double& t) { - Vec3d e0, e1; - double distSqr; - - e0.sub(v1, v0); - e1.sub(v2, v0); - - Vec3d delta = v0 - point; - double a00 = e0.dot(e0); - double a01 = e0.dot(e1); - double a11 = e1.dot(e1); - double b0 = delta.dot(e0); - double b1 = delta.dot(e1); - double c = delta.dot(delta); - double det = fabs(a00*a11-a01*a01); - /* DEPRECATED - double aMax = (a00 > a11) ? a00 : a11; - double epsilon2 = epsilon * epsilon; + Vec3d ab = b - a; + t = (p - a).dot(ab); - // - // Triangle is degenerate. Use an absolute test for the length - // of the edges and a relative test for area squared - // - if ((a00 <= epsilon2 && a11 <= epsilon2) || det <= epsilon * aMax * aMax) { - - double t; - double minDistSqr; - - minDistSqr = sLineSeg3ToPointDistSqr(v0, v1, point, t, epsilon); - uv[0] = 1.0 - t; - uv[1] = t; - - distSqr = sLineSeg3ToPointDistSqr(v0, v2, point, t, epsilon); - if (distSqr < minDistSqr) { - minDistSqr = distSqr; - uv[0] = 1.0 - t; - uv[1] = 0; - } - - distSqr = sLineSeg3ToPointDistSqr(v1, v2, point, t, epsilon); - if (distSqr < minDistSqr) { - minDistSqr = distSqr; - uv[0] = 0; - uv[1] = 1.0 - t; - } - - return minDistSqr; - }*/ - - double s = a01*b1-a11*b0; - double t = a01*b0-a00*b1; - - if (s + t <= det ) { - if (s < 0.0) { - if (t < 0.0) { - // region 4 - if (b0 < 0.0) { - t = 0.0; - if (-b0 >= a00) { - s = 1.0; - distSqr = a00+2.0*b0+c; - } else { - s = -b0/a00; - distSqr = b0*s+c; - } - } else { - s = 0.0; - if (b1 >= 0.0) { - t = 0.0; - distSqr = c; - } else if (-b1 >= a11) { - t = 1.0; - distSqr = a11+2.0*b1+c; - } else { - t = -b1/a11; - distSqr = b1*t+c; - } - } - } else { - // region 3 - s = 0.0; - if (b1 >= 0.0) { - t = 0.0; - distSqr = c; - } - else if (-b1 >= a11) { - t = 1.0; - distSqr = a11+2.0*b1+c; - } - else { - t = -b1/a11; - distSqr = b1*t+c; - } - } - } else if (t < 0.0) { - // region 5 - - t = 0.0; - if (b0 >= 0.0) { - s = 0.0; - distSqr = c; - } else if (-b0 >= a00) { - s = 1.0; - distSqr = a00+2.0*b0+c; - } else { - s = -b0/a00; - distSqr = b0*s+c; - } - } else { - // region 0 - - // minimum at interior point - double fInvDet = 1.0/det; - s *= fInvDet; - t *= fInvDet; - distSqr = s*(a00*s+a01*t+2.0*b0) + - t*(a01*s+a11*t+2.0*b1)+c; - } + if (t <= 0.0) { + // c projects outside the [a,b] interval, on the a side. + t = 0.0; + return a; } else { - double tmp0, tmp1, numer, denom; - - if (s < 0.0) { - // region 2 - tmp0 = a01 + b0; - tmp1 = a11 + b1; - if (tmp1 > tmp0) { - numer = tmp1 - tmp0; - denom = a00-2.0*a01+a11; - if (numer >= denom) { - s = 1.0; - t = 0.0; - distSqr = a00+2.0*b0+c; - } else { - s = numer/denom; - t = 1.0 - s; - distSqr = s*(a00*s+a01*t+2.0*b0) + - t*(a01*s+a11*t+2.0*b1)+c; - } - } else { - s = 0.0; - if (tmp1 <= 0.0) { - t = 1.0; - distSqr = a11+2.0*b1+c; - } else if (b1 >= 0.0) { - t = 0.0; - distSqr = c; - } else { - t = -b1/a11; - distSqr = b1*t+c; - } - } - } else if (t < 0.0) { - // region 6 + // always nonnegative since denom = ||ab||^2 + double denom = ab.dot(ab); - tmp0 = a01 + b1; - tmp1 = a00 + b0; - if (tmp1 > tmp0 ) { - numer = tmp1 - tmp0; - denom = a00-2.0*a01+a11; - if (numer >= denom ) { - t = 1.0; - s = 0.0; - distSqr = a11+2.0*b1+c; - } else { - t = numer/denom; - s = 1.0 - t; - distSqr = s*(a00*s+a01*t+2.0*b0) + - t*(a01*s+a11*t+2.0*b1)+c; - } - } else { - t = 0.0; - if (tmp1 <= 0.0) { - s = 1.0; - distSqr = a00+2.0*b0+c; - } else if (b0 >= 0.0) { - s = 0.0; - distSqr = c; - } else { - s = -b0/a00; - distSqr = b0*s+c; - } - } - } else { - // region 1 - numer = a11 + b1 - a01 - b0; - if (numer <= 0.0) { - s = 0.0; - t = 1.0; - distSqr = a11+2.0*b1+c; - } else { - denom = a00-2.0*a01+a11; - if (numer >= denom ) { - s = 1.0; - t = 0.0; - distSqr = a00+2.0*b0+c; - } else { - s = numer/denom; - t = 1.0 - s; - distSqr = s*(a00*s+a01*t+2.0*b0) + - t*(a01*s+a11*t+2.0*b1)+c; - } - } + if (t >= denom) { + // c projects outside the [a,b] interval, on the b side. + t = 1.0; + return b; + } else { + // c projects inside the [a,b] interval. + t = t / denom; + return a + (ab * t); } } - - // Convert s,t into barycentric coordinates - uv[0] = 1.0 - s - t; - uv[1] = s; - - return (distSqr < 0) ? 0.0 : distSqr; } } // namespace math diff --git a/extern/openvdb/internal/openvdb/math/Proximity.h b/extern/openvdb/internal/openvdb/math/Proximity.h index dfa2353c0ee..cc0106cd032 100644 --- a/extern/openvdb/internal/openvdb/math/Proximity.h +++ b/extern/openvdb/internal/openvdb/math/Proximity.h @@ -32,7 +32,6 @@ #define OPENVDB_MATH_PROXIMITY_HAS_BEEN_INCLUDED #include <openvdb/Types.h> -//#include <openvdb/openvdb.h> namespace openvdb { @@ -40,88 +39,34 @@ OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { namespace math { - -/// @brief Closest Point on Triangle to Point. Given a triangle @c abc and a -/// point @c p, returns the point on @c abc closest to @c p and the -/// corresponding barycentric coordinates. +/// @brief Closest Point on Triangle to Point. Given a triangle @c abc and a point @c p, +/// return the point on @c abc closest to @c p and the corresponding barycentric coordinates. /// -/// @note Algorithm from "Real-Time Collision Detection" pg 136 to 142 by Christer Ericson. -/// The closest point is obtained by first determining which of the triangles -/// Voronoi feature regions @c p is in and then computing the orthogonal projection -/// of @c p onto the corresponding feature. +/// @details Algorithms from "Real-Time Collision Detection" pg 136 to 142 by Christer Ericson. +/// The closest point is obtained by first determining which of the triangles' +/// Voronoi feature regions @c p is in and then computing the orthogonal projection +/// of @c p onto the corresponding feature. /// /// @param a The triangle's first vertex point. /// @param b The triangle's second vertex point. /// @param c The triangle's third vertex point. -/// @param p Point to compute the closest point on @c abc for. +/// @param p Point to compute the closest point on @c abc for. /// @param uvw Barycentric coordinates, computed and returned. OPENVDB_API Vec3d -closestPointOnTriangleToPoint(const Vec3d& a, const Vec3d& b, const Vec3d& c, - const Vec3d& p, Vec3d& uvw); - - -//////////////////////////////////////// - - -// DEPRECATED METHODS +closestPointOnTriangleToPoint( + const Vec3d& a, const Vec3d& b, const Vec3d& c, const Vec3d& p, Vec3d& uvw); -/// @brief Squared distance of a line segment p(t) = (1-t)*p0 + t*p1 to point. -/// @return the closest point on the line segment as a function of t -OPENVDB_API OPENVDB_DEPRECATED double -sLineSeg3ToPointDistSqr(const Vec3d &p0, - const Vec3d &p1, - const Vec3d &point, - double &t, - double epsilon = 1e-10); - - -/// @brief Slightly modified version of the algorithm described in "Geometric Tools for -/// Computer Graphics" pg 376 to 382 by Schneider and Eberly. Extended to handle -/// the case of a degenerate triangle. Also returns barycentric rather than -/// (s,t) coordinates. -/// -/// Basic Idea (See book for details): -/// -/// Write the equation of the line as -/// -/// T(s,t) = v0 + s*(v1-v0) + t*(v2-v0) +/// @brief Closest Point on Line Segment to Point. Given segment @c ab and point @c p, +/// return the point on @c ab closest to @c p and @c t the parametric distance to @c b. /// -/// Minimize the quadratic function -/// -/// || T(s,t) - point || ^2 -/// -/// by solving for when the gradient is 0. This can be done without any -/// square roots. -/// -/// If the resulting solution satisfies 0 <= s + t <= 1, then the solution lies -/// on the interior of the triangle, and we are done (region 0). If it does -/// not then the closest solution lies on a boundary and we have to solve for -/// it by solving a 1D problem where we use one variable as free say "s" and -/// set the other variable t = (1-s) -/// -/// @return the closest point on the triangle and barycentric coordinates. -OPENVDB_API OPENVDB_DEPRECATED double -sTri3ToPointDistSqr(const Vec3d &v0, - const Vec3d &v1, - const Vec3d &v2, - const Vec3d &point, - Vec2d &uv, - double epsilon); - - -/// @return the closest point on the triangle. -static inline OPENVDB_DEPRECATED double -triToPtnDistSqr(const Vec3d &v0, - const Vec3d &v1, - const Vec3d &v2, - const Vec3d &point) -{ - Vec3d cpt, uvw; - cpt = closestPointOnTriangleToPoint(v0, v1, v2, point, uvw); - return (cpt - point).lengthSqr(); -} - +/// @param a The segment's first vertex point. +/// @param b The segment's second vertex point. +/// @param p Point to compute the closest point on @c ab for. +/// @param t Parametric distance to @c b. +OPENVDB_API Vec3d +closestPointOnSegmentToPoint( + const Vec3d& a, const Vec3d& b, const Vec3d& p, double& t); } // namespace math } // namespace OPENVDB_VERSION_NAME diff --git a/extern/openvdb/internal/openvdb/math/QuantizedUnitVec.cc b/extern/openvdb/internal/openvdb/math/QuantizedUnitVec.cc index 3a538bec209..b51d1cbc7cc 100644 --- a/extern/openvdb/internal/openvdb/math/QuantizedUnitVec.cc +++ b/extern/openvdb/internal/openvdb/math/QuantizedUnitVec.cc @@ -41,7 +41,7 @@ namespace math { //////////////////////////////////////// - + bool QuantizedUnitVec::sInitialized = false; float QuantizedUnitVec::sNormalizationWeights[MASK_SLOTS + 1]; @@ -55,7 +55,7 @@ tbb::mutex sInitMutex; void QuantizedUnitVec::init() { - tbb::mutex::scoped_lock(sInitMutex); + tbb::mutex::scoped_lock lock(sInitMutex); if (!sInitialized) { @@ -92,7 +92,6 @@ QuantizedUnitVec::init() } // namespace OPENVDB_VERSION_NAME } // namespace openvdb - // Copyright (c) 2012-2013 DreamWorks Animation LLC // All rights reserved. This software is distributed under the // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/extern/openvdb/internal/openvdb/math/Ray.h b/extern/openvdb/internal/openvdb/math/Ray.h new file mode 100644 index 00000000000..b9ed32a552f --- /dev/null +++ b/extern/openvdb/internal/openvdb/math/Ray.h @@ -0,0 +1,342 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +// +// Redistributions of source code must retain the above copyright +// and license notice and the following restrictions and disclaimer. +// +// * Neither the name of DreamWorks Animation nor the names of +// its contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE +// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. +// +/////////////////////////////////////////////////////////////////////////// +// +/// @file Ray.h +/// +/// @author Ken Museth +/// +/// @brief A Ray class. + +#ifndef OPENVDB_MATH_RAY_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_RAY_HAS_BEEN_INCLUDED + +#include "Math.h" +#include "Vec3.h" +#include "Transform.h" +#include <iostream> // for std::ostream +#include <boost/type_traits/is_floating_point.hpp> +#include <limits>// for std::numeric_limits<Type>::max() + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +template<typename RealT = double> +class Ray +{ +public: + BOOST_STATIC_ASSERT(boost::is_floating_point<RealT>::value); + typedef RealT RealType; + typedef Vec3<Real> Vec3Type; + typedef Vec3Type Vec3T; + struct TimeSpan { + RealT t0, t1; + /// @brief Default constructor + TimeSpan() {} + /// @brief Constructor + TimeSpan(RealT _t0, RealT _t1) : t0(_t0), t1(_t1) {} + /// @brief Set both times + inline void set(RealT _t0, RealT _t1) { t0=_t0; t1=_t1; } + /// @brief Get both times + inline void get(RealT& _t0, RealT& _t1) const { _t0=t0; _t1=t1; } + /// @brief Return @c true if t1 is larger then t0 by at least eps. + inline bool valid(RealT eps=math::Delta<RealT>::value()) const { return (t1-t0)>eps; } + /// @brief Return the midpoint of the ray. + inline RealT mid() const { return 0.5*(t0 + t1); } + /// @brief Multiplies both times + inline void scale(RealT s) {assert(s>0); t0*=s; t1*=s; } + /// @brief Return @c true if time is inclusive + inline bool test(RealT t) const { return (t>=t0 && t<=t1); } + }; + + Ray(const Vec3Type& eye = Vec3Type(0,0,0), + const Vec3Type& direction = Vec3Type(1,0,0), + RealT t0 = math::Delta<RealT>::value(), + RealT t1 = std::numeric_limits<RealT>::max()) + : mEye(eye), mDir(direction), mInvDir(1/mDir), mTimeSpan(t0, t1) + { + } + + inline void setEye(const Vec3Type& eye) { mEye = eye; } + + inline void setDir(const Vec3Type& dir) + { + mDir = dir; + mInvDir = 1/mDir; + } + + inline void setMinTime(RealT t0) { assert(t0>0); mTimeSpan.t0 = t0; } + + inline void setMaxTime(RealT t1) { assert(t1>0); mTimeSpan.t1 = t1; } + + inline void setTimes(RealT t0 = math::Delta<RealT>::value(), + RealT t1 = std::numeric_limits<RealT>::max()) + { + assert(t0>0 && t1>0); + mTimeSpan.set(t0, t1); + } + + inline void scaleTimes(RealT scale) { mTimeSpan.scale(scale); } + + inline void reset(const Vec3Type& eye, + const Vec3Type& direction, + RealT t0 = math::Delta<RealT>::value(), + RealT t1 = std::numeric_limits<RealT>::max()) + { + this->setEye(eye); + this->setDir(direction); + this->setTimes(t0, t1); + } + + inline const Vec3T& eye() const {return mEye;} + + inline const Vec3T& dir() const {return mDir;} + + inline const Vec3T& invDir() const {return mInvDir;} + + inline RealT t0() const {return mTimeSpan.t0;} + + inline RealT t1() const {return mTimeSpan.t1;} + + /// @brief Return the position along the ray at the specified time. + inline Vec3R operator()(RealT time) const { return mEye + mDir * time; } + + /// @brief Return the starting point of the ray. + inline Vec3R start() const { return (*this)(mTimeSpan.t0); } + + /// @brief Return the endpoint of the ray. + inline Vec3R end() const { return (*this)(mTimeSpan.t1); } + + /// @brief Return the midpoint of the ray. + inline Vec3R mid() const { return (*this)(mTimeSpan.mid()); } + + /// @brief Return @c true if t0 is strictly less then t1. + OPENVDB_DEPRECATED inline bool test() const { return mTimeSpan.valid(RealT(0)); } + + /// @brief Return @c true if t1 is larger then t0 by at least eps. + inline bool valid(RealT eps=math::Delta<float>::value()) const + { + return mTimeSpan.valid(eps); + } + + /// @brief Return @c true if @a time is within t0 and t1, both inclusive. + inline bool test(RealT time) const { return mTimeSpan.test(time); } + + /// @brief Return a new Ray that is transformed with the specified map. + /// @param map the map from which to construct the new Ray. + /// @warning Assumes a linear map and a normalize direction. + /// @details The requirement that the direction is normalized + /// follows from the transformation of t0 and t1 - and that fact that + /// we want applyMap and applyInverseMap to be inverse operations. + template<typename MapType> + inline Ray applyMap(const MapType& map) const + { + assert(map.isLinear()); + assert(math::isApproxEqual(mDir.length(), RealT(1))); + const Vec3T eye = map.applyMap(mEye); + const Vec3T dir = map.applyJacobian(mDir); + const RealT length = dir.length(); + return Ray(eye, dir/length, length*mTimeSpan.t0, length*mTimeSpan.t1); + } + + /// @brief Return a new Ray that is transformed with the inverse of the specified map. + /// @param map the map from which to construct the new Ray by inverse mapping. + /// @warning Assumes a linear map and a normalize direction. + /// @details The requirement that the direction is normalized + /// follows from the transformation of t0 and t1 - and that fact that + /// we want applyMap and applyInverseMap to be inverse operations. + template<typename MapType> + inline Ray applyInverseMap(const MapType& map) const + { + assert(map.isLinear()); + assert(math::isApproxEqual(mDir.length(), RealT(1))); + const Vec3T eye = map.applyInverseMap(mEye); + const Vec3T dir = map.applyInverseJacobian(mDir); + const RealT length = dir.length(); + return Ray(eye, dir/length, length*mTimeSpan.t0, length*mTimeSpan.t1); + } + + /// @brief Return a new ray in world space, assuming the existing + /// ray is represented in the index space of the specified grid. + template<typename GridType> + inline Ray indexToWorld(const GridType& grid) const + { + return this->applyMap(*(grid.transform().baseMap())); + } + + /// @brief Return a new ray in the index space of the specified + /// grid, assuming the existing ray is represented in world space. + template<typename GridType> + inline Ray worldToIndex(const GridType& grid) const + { + return this->applyInverseMap(*(grid.transform().baseMap())); + } + + /// @brief Return true if this ray intersects the specified sphere. + /// @param center The center of the sphere in the same space as this ray. + /// @param radius The radius of the sphere in the same units as this ray. + /// @param t0 The first intersection point if an intersection exists. + /// @param t1 The second intersection point if an intersection exists. + /// @note If the return value is true, i.e. a hit, and t0 = + /// this->t0() or t1 == this->t1() only one true intersection exist. + inline bool intersects(const Vec3T& center, RealT radius, RealT& t0, RealT& t1) const + { + const Vec3T origin = mEye - center; + const RealT A = mDir.lengthSqr(); + const RealT B = 2 * mDir.dot(origin); + const RealT C = origin.lengthSqr() - radius * radius; + const RealT D = B * B - 4 * A * C; + + if (D < 0) return false; + + const RealT Q = RealT(-0.5)*(B<0 ? (B + Sqrt(D)) : (B - Sqrt(D))); + + t0 = Q / A; + t1 = C / Q; + + if (t0 > t1) std::swap(t0, t1); + if (t0 < mTimeSpan.t0) t0 = mTimeSpan.t0; + if (t1 > mTimeSpan.t1) t1 = mTimeSpan.t1; + return t0 <= t1; + } + + /// @brief Return true if this ray intersects the specified sphere. + /// @param center The center of the sphere in the same space as this ray. + /// @param radius The radius of the sphere in the same units as this ray. + inline bool intersects(const Vec3T& center, RealT radius) const + { + RealT t0, t1; + return this->intersects(center, radius, t0, t1)>0; + } + + /// @brief Return true if this ray intersects the specified sphere. + /// @note For intersection this ray is clipped to the two intersection points. + /// @param center The center of the sphere in the same space as this ray. + /// @param radius The radius of the sphere in the same units as this ray. + inline bool clip(const Vec3T& center, RealT radius) + { + RealT t0, t1; + const bool hit = this->intersects(center, radius, t0, t1); + if (hit) mTimeSpan.set(t0, t1); + return hit; + } + + /// @brief Return true if the Ray intersects the specified + /// axisaligned bounding box. + /// @param bbox Axis-aligned bounding box in the same space as the Ray. + /// @param t0 If an intersection is detected this is assigned + /// the time for the first intersection point. + /// @param t1 If an intersection is detected this is assigned + /// the time for the second intersection point. + template<typename BBoxT> + inline bool intersects(const BBoxT& bbox, RealT& t0, RealT& t1) const + { + mTimeSpan.get(t0, t1); + for (size_t i = 0; i < 3; ++i) { + RealT a = (bbox.min()[i] - mEye[i]) * mInvDir[i]; + RealT b = (bbox.max()[i] - mEye[i]) * mInvDir[i]; + if (a > b) std::swap(a, b); + if (a > t0) t0 = a; + if (b < t1) t1 = b; + if (t0 > t1) return false; + } + return true; + } + + /// @brief Return true if this ray intersects the specified bounding box. + /// @param bbox Axis-aligned bounding box in the same space as this ray. + template<typename BBoxT> + inline bool intersects(const BBoxT& bbox) const + { + RealT t0, t1; + return this->intersects(bbox, t0, t1); + } + + /// @brief Return true if this ray intersects the specified bounding box. + /// @note For intersection this ray is clipped to the two intersection points. + /// @param bbox Axis-aligned bounding box in the same space as this ray. + template<typename BBoxT> + inline bool clip(const BBoxT& bbox) + { + RealT t0, t1; + const bool hit = this->intersects(bbox, t0, t1); + if (hit) mTimeSpan.set(t0, t1); + return hit; + } + + /// @brief Return true if the Ray intersects the plane specified + /// by a normal and distance from the origin. + /// @param normal Normal of the plane. + /// @param distance Distance of the plane to the origin. + /// @param t Time of intersection, if one exists. + inline bool intersects(const Vec3T& normal, RealT distance, RealT& t) const + { + const RealT cosAngle = mDir.dot(normal); + if (math::isApproxZero(cosAngle)) return false;//parallel + t = (distance - mEye.dot(normal))/cosAngle; + return this->test(t); + } + + /// @brief Return true if the Ray intersects the plane specified + /// by a normal and point. + /// @param normal Normal of the plane. + /// @param point Point in the plane. + /// @param t Time of intersection, if one exists. + inline bool intersects(const Vec3T& normal, const Vec3T& point, RealT& t) const + { + return this->intersects(normal, point.dot(normal), t); + } + +private: + Vec3T mEye, mDir, mInvDir; + TimeSpan mTimeSpan; +}; // end of Ray class + +/// @brief Output streaming of the Ray class. +/// @note Primarily intended for debugging. +template<typename RealT> +inline std::ostream& operator<<(std::ostream& os, const Ray<RealT>& r) +{ + os << "eye=" << r.eye() << " dir=" << r.dir() << " 1/dir="<<r.invDir() + << " t0=" << r.t0() << " t1=" << r.t1(); + return os; +} + + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_RAY_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/extern/openvdb/internal/openvdb/math/Stats.h b/extern/openvdb/internal/openvdb/math/Stats.h index 7985a22d76a..88414994e6f 100644 --- a/extern/openvdb/internal/openvdb/math/Stats.h +++ b/extern/openvdb/internal/openvdb/math/Stats.h @@ -36,7 +36,12 @@ #define OPENVDB_MATH_STATS_HAS_BEEN_INCLUDED #include <iosfwd> // for ostringstream - +#include <openvdb/version.h> +#include <iostream> +#include <iomanip> +#include <sstream> +#include <vector> +#include "Math.h" namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE @@ -83,29 +88,46 @@ public: /// Add the samples from the other Stats instance. void add(const Stats& other) { - mMin = std::min<double>(mMin, other.mMin); - mMax = std::max<double>(mMax, other.mMax); - const double denom = 1.0/double(mSize + other.mSize); - const double delta = other.mAvg - mAvg; - mAvg += denom*delta*other.mSize; - mAux += other.mAux + denom*delta*delta*mSize*other.mSize; - mSize += other.mSize; + if (other.mSize > 0) { + mMin = std::min<double>(mMin, other.mMin); + mMax = std::max<double>(mMax, other.mMax); + const double denom = 1.0/double(mSize + other.mSize); + const double delta = other.mAvg - mAvg; + mAvg += denom*delta*other.mSize; + mAux += other.mAux + denom*delta*delta*mSize*other.mSize; + mSize += other.mSize; + } } /// Return the size of the population, i.e., the total number of samples. - uint64_t size() const { return mSize; } + inline uint64_t size() const { return mSize; } + /// Return the minimum value. - double min() const { return mMin; } + inline double min() const { return mMin; } + /// Return the maximum value. - double max() const { return mMax; } - /// Return the mean value. - double mean() const { return mAvg; } + inline double max() const { return mMax; } + + //@{ + /// Return the arithmetic mean, i.e. average, value. + inline double avg() const { return mAvg; } + inline double mean() const { return mAvg; } + //@} + + //@{ /// @brief Return the population variance. - /// @note The unbiased sample variance = population variance * num/(num-1) - double variance() const { return mSize<2 ? 0.0 : mAux/double(mSize); } + /// @note The unbiased sample variance = population variance * + //num/(num-1) + inline double var() const { return mSize<2 ? 0.0 : mAux/double(mSize); } + inline double variance() const { return this->var(); } + //@} + + //@{ /// @brief Return the standard deviation (=Sqrt(variance)) as /// defined from the (biased) population variance. - double stdDev() const { return sqrt(this->variance()); } + inline double std() const { return sqrt(this->var()); } + inline double stdDev() const { return this->std(); } + //@} /// @brief Print statistics to the specified output stream. void print(const std::string &name= "", std::ostream &strm=std::cout, int precision=3) const @@ -116,12 +138,16 @@ public: os << std::setprecision(precision) << std::setiosflags(std::ios::fixed); os << "Statistics "; if (!name.empty()) os << "for \"" << name << "\" "; - os << "with " << mSize << " samples:\n" - << " Min=" << mMin - << ", Max=" << mMax - << ", Ave=" << mAvg - << ", Std=" << this->stdDev() - << ", Var=" << this->variance() << std::endl; + if (mSize>0) { + os << "with " << mSize << " samples:\n" + << " Min=" << mMin + << ", Max=" << mMax + << ", Ave=" << mAvg + << ", Std=" << this->stdDev() + << ", Var=" << this->variance() << std::endl; + } else { + os << ": no samples were added." << std::endl; + } strm << os.str(); } @@ -163,7 +189,7 @@ public: /// @brief Add @a n samples with constant value @a val, provided that the /// @a val falls within this histogram's value range. /// @return @c true if the sample value falls within this histogram's value range. - bool add(double val, uint64_t n = 1) + inline bool add(double val, uint64_t n = 1) { if (val<mMin || val>mMax) return false; mBins[size_t(mDelta*(val-mMin))] += n; @@ -183,19 +209,19 @@ public: } /// Return the number of bins in this histogram. - size_t numBins() const { return mBins.size(); } + inline size_t numBins() const { return mBins.size(); } /// Return the lower bound of this histogram's value range. - double min() const { return mMin; } + inline double min() const { return mMin; } /// Return the upper bound of this histogram's value range. - double max() const { return mMax; } + inline double max() const { return mMax; } /// Return the minimum value in the <i>n</i>th bin. - double min(int n) const { return mMin+n/mDelta; } + inline double min(int n) const { return mMin+n/mDelta; } /// Return the maximum value in the <i>n</i>th bin. - double max(int n) const { return mMin+(n+1)/mDelta; } + inline double max(int n) const { return mMin+(n+1)/mDelta; } /// Return the number of samples in the <i>n</i>th bin. - uint64_t count(int n) const { return mBins[n]; } + inline uint64_t count(int n) const { return mBins[n]; } /// Return the population size, i.e., the total number of samples. - uint64_t size() const { return mSize; } + inline uint64_t size() const { return mSize; } /// Print the histogram to the specified output stream. void print(const std::string& name = "", std::ostream& strm = std::cout) const @@ -206,16 +232,20 @@ public: os << std::setprecision(6) << std::setiosflags(std::ios::fixed) << std::endl; os << "Histogram "; if (!name.empty()) os << "for \"" << name << "\" "; - os << "with " << mSize << " samples:\n"; - os << "==============================================================\n"; - os << "|| # | Min | Max | Frequency | % ||\n"; - os << "==============================================================\n"; - for (size_t i=0, e=mBins.size(); i!=e; ++i) { - os << "|| " << std::setw(4) << i << " | " << std::setw(14) << this->min(i) << " | " - << std::setw(14) << this->max(i) << " | " << std::setw(9) << mBins[i] << " | " - << std::setw(3) << (100*mBins[i]/mSize) << " ||\n"; + if (mSize > 0) { + os << "with " << mSize << " samples:\n"; + os << "==============================================================\n"; + os << "|| # | Min | Max | Frequency | % ||\n"; + os << "==============================================================\n"; + for (size_t i=0, e=mBins.size(); i!=e; ++i) { + os << "|| " << std::setw(4) << i << " | " << std::setw(14) << this->min(i) << " | " + << std::setw(14) << this->max(i) << " | " << std::setw(9) << mBins[i] << " | " + << std::setw(3) << (100*mBins[i]/mSize) << " ||\n"; + } + os << "==============================================================\n"; + } else { + os << ": no samples were added." << std::endl; } - os << "==============================================================\n"; strm << os.str(); } diff --git a/extern/openvdb/internal/openvdb/math/Stencils.h b/extern/openvdb/internal/openvdb/math/Stencils.h index bfdf18fe646..6aa678ea551 100644 --- a/extern/openvdb/internal/openvdb/math/Stencils.h +++ b/extern/openvdb/internal/openvdb/math/Stencils.h @@ -54,20 +54,35 @@ template<typename _GridType, typename StencilType> class BaseStencil { public: - typedef _GridType GridType; - typedef typename GridType::TreeType TreeType; - typedef typename GridType::ValueType ValueType; - typedef std::vector<ValueType> BufferType; - typedef typename BufferType::iterator IterType; - - /// Initialize the stencil buffer with the values of voxel (x, y, z) + typedef _GridType GridType; + typedef typename GridType::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + typedef std::vector<ValueType> BufferType; + typedef typename BufferType::iterator IterType; + typedef typename GridType::ConstAccessor AccessorType; + + /// @brief Initialize the stencil buffer with the values of voxel (i, j, k) /// and its neighbors. + /// @param ijk Index coordinates of stencil center inline void moveTo(const Coord& ijk) { mCenter = ijk; mStencil[0] = mCache.getValue(ijk); static_cast<StencilType&>(*this).init(mCenter); } + + /// @brief Initialize the stencil buffer with the values of voxel (i, j, k) + /// and its neighbors. The method also takes a value of the center + /// element of the stencil, assuming it is already known. + /// @param ijk Index coordinates of stnecil center + /// @param centerValue Value of the center element of the stencil + inline void moveTo(const Coord& ijk, const ValueType& centerValue) + { + mCenter = ijk; + mStencil[0] = centerValue; + static_cast<StencilType&>(*this).init(mCenter); + } + /// @brief Initialize the stencil buffer with the values of voxel /// (x, y, z) and its neighbors. /// @@ -81,34 +96,47 @@ public: static_cast<StencilType&>(*this).init(mCenter); } + /// @brief Initialize the stencil buffer with the values of voxel (x, y, z) + /// and its neighbors. + /// @param xyz Floating point voxel coordinates of stencil center + /// @details This method will check to see if it is necessary to + /// update the stencil based on the cached index coordinates of + /// the center point. + inline void moveTo(const Vec3R& xyz) + { + Coord ijk = openvdb::Coord::floor(xyz); + if (ijk != mCenter) this->moveTo(ijk); + } + /// @brief Return the value from the stencil buffer with linear - /// offset pos. + /// offset pos. /// - /// The default (@a pos = 0) corresponds to the center point of the stencil. - inline ValueType getValue(unsigned int pos = 0) const + /// @note The default (@a pos = 0) corresponds to the first element + /// which is typically the center point of the stencil. + inline const ValueType& getValue(unsigned int pos = 0) const { assert(pos < mStencil.size()); return mStencil[pos]; } - /// Return the value at the specified location relative to the center of the stencil + /// @brief Return the value at the specified location relative to the center of the stencil template<int i, int j, int k> - const ValueType& getValue() const + inline const ValueType& getValue() const { return mStencil[static_cast<const StencilType&>(*this).template pos<i,j,k>()]; } - /// Set the value at the specified location relative to the center of the stencil + /// @brief Set the value at the specified location relative to the center of the stencil template<int i, int j, int k> - void setValue(const ValueType& value) + inline void setValue(const ValueType& value) { mStencil[static_cast<const StencilType&>(*this).template pos<i,j,k>()] = value; } - /// Return the size of the stencil buffer. + /// @brief Return the size of the stencil buffer. inline int size() { return mStencil.size(); } - /// Return the median value of the current stencil. + /// @brief Return the median value of the current stencil. inline ValueType median() const { std::vector<ValueType> tmp(mStencil);//local copy @@ -119,38 +147,35 @@ public: return tmp[midpoint]; } - /// Return the mean value of the current stencil. + /// @brief Return the mean value of the current stencil. inline ValueType mean() const { - double sum = 0.0; + ValueType sum = 0.0; for (int n=0, s=mStencil.size(); n<s; ++n) sum += mStencil[n]; - return ValueType(sum / mStencil.size()); + return sum / mStencil.size(); } - /// Return the smallest value in the stencil buffer. + /// @brief Return the smallest value in the stencil buffer. inline ValueType min() const { IterType iter = std::min_element(mStencil.begin(), mStencil.end()); return *iter; } - /// Return the largest value in the stencil buffer. + /// @brief Return the largest value in the stencil buffer. inline ValueType max() const { IterType iter = std::max_element(mStencil.begin(), mStencil.end()); return *iter; } - /// Return the coordinates of the center point of the stencil. + /// @brief Return the coordinates of the center point of the stencil. inline const Coord& getCenterCoord() const { return mCenter; } - /// Return the value at the center of the stencil - inline const ValueType& getCenterValue() const - { - return this->getValue<0,0,0>(); - } + /// @brief Return the value at the center of the stencil + inline const ValueType& getCenterValue() const { return mStencil[0]; } - /// Return true if the center of the stencil intersects the + /// @brief Return true if the center of the stencil intersects the /// iso-contour specified by the isoValue inline bool intersects(const ValueType &isoValue = zeroVal<ValueType>()) const { @@ -163,17 +188,26 @@ public: (less ^ (this->getValue< 0, 0, 1>() < isoValue)) ; } + /// @brief Return a const reference to the grid from which this + /// stencil was constructed. + inline const GridType& grid() const { return *mGrid; } + + /// @brief Return a const reference to the ValueAccessor + /// associated with this Stencil. + inline const AccessorType& accessor() const { return mCache; } + protected: // Constructor is protected to prevent direct instantiation. BaseStencil(const GridType& grid, int size): - mCache(grid.getConstAccessor()), - mStencil(size) + mGrid(&grid), mCache(grid.getConstAccessor()), + mStencil(size), mCenter(Coord::max()) { } - typename GridType::ConstAccessor mCache; - BufferType mStencil; - Coord mCenter; + const GridType* mGrid; + AccessorType mCache; + BufferType mStencil; + Coord mCenter; }; // class BaseStencil @@ -215,8 +249,6 @@ public: private: inline void init(const Coord& ijk) { - BaseType::template setValue< 0, 0, 0>(mCache.getValue(ijk)); - BaseType::template setValue<-1, 0, 0>(mCache.getValue(ijk.offsetBy(-1, 0, 0))); BaseType::template setValue< 1, 0, 0>(mCache.getValue(ijk.offsetBy( 1, 0, 0))); @@ -238,6 +270,142 @@ private: namespace { // anonymous namespace for stencil-layout map + // the eight point box stencil + template<int i, int j, int k> struct BoxPt {}; + template<> struct BoxPt< 0, 0, 0> { enum { idx = 0 }; }; + template<> struct BoxPt< 0, 0, 1> { enum { idx = 1 }; }; + template<> struct BoxPt< 0, 1, 1> { enum { idx = 2 }; }; + template<> struct BoxPt< 0, 1, 0> { enum { idx = 3 }; }; + template<> struct BoxPt< 1, 0, 0> { enum { idx = 4 }; }; + template<> struct BoxPt< 1, 0, 1> { enum { idx = 5 }; }; + template<> struct BoxPt< 1, 1, 1> { enum { idx = 6 }; }; + template<> struct BoxPt< 1, 1, 0> { enum { idx = 7 }; }; +} + +template<typename GridType> +class BoxStencil: public BaseStencil<GridType, BoxStencil<GridType> > +{ +public: + typedef BaseStencil<GridType, BoxStencil<GridType> > BaseType; + typedef typename BaseType::BufferType BufferType; + typedef typename GridType::ValueType ValueType; + typedef math::Vec3<ValueType> Vec3Type; + static const int SIZE = 8; + + BoxStencil(const GridType& grid): BaseType(grid, SIZE) {} + + /// Return linear offset for the specified stencil point relative to its center + template<int i, int j, int k> + unsigned int pos() const { return BoxPt<i,j,k>::idx; } + + /// @brief Return true if the center of the stencil intersects the + /// iso-contour specified by the isoValue + inline bool intersects(const ValueType &isoValue = zeroVal<ValueType>()) const + { + const bool less = mStencil[0] < isoValue; + return (less ^ (mStencil[1] < isoValue)) || + (less ^ (mStencil[2] < isoValue)) || + (less ^ (mStencil[3] < isoValue)) || + (less ^ (mStencil[4] < isoValue)) || + (less ^ (mStencil[5] < isoValue)) || + (less ^ (mStencil[6] < isoValue)) || + (less ^ (mStencil[7] < isoValue)) ; + } + + /// @brief Return the trilinear interpolation at the normalized position. + /// @param xyz Floating point coordinate position. + /// @warning It is assumed that the stencil has already been moved + /// to the relevant voxel position, e.g. using moveTo(xyz). + /// @note Trilinear interpolation kernal reads as: + /// v000 (1-u)(1-v)(1-w) + v001 (1-u)(1-v)w + v010 (1-u)v(1-w) + v011 (1-u)vw + /// + v100 u(1-v)(1-w) + v101 u(1-v)w + v110 uv(1-w) + v111 uvw + inline ValueType interpolation(const Vec3Type& xyz) const + { + const Real u = xyz[0] - BaseType::mCenter[0]; assert(u>=0 && u<=1); + const Real v = xyz[1] - BaseType::mCenter[1]; assert(v>=0 && v<=1); + const Real w = xyz[2] - BaseType::mCenter[2]; assert(w>=0 && w<=1); + + ValueType V = BaseType::template getValue<0,0,0>(); + ValueType A = V + (BaseType::template getValue<0,0,1>() - V) * w; + V = BaseType::template getValue< 0, 1, 0>(); + ValueType B = V + (BaseType::template getValue<0,1,1>() - V) * w; + ValueType C = A + (B - A) * v; + + V = BaseType::template getValue<1,0,0>(); + A = V + (BaseType::template getValue<1,0,1>() - V) * w; + V = BaseType::template getValue<1,1,0>(); + B = V + (BaseType::template getValue<1,1,1>() - V) * w; + ValueType D = A + (B - A) * v; + + return C + (D - C) * u; + } + + /// @brief Return the gradient in world space of the trilinear interpolation kernel. + /// @param xyz Floating point coordinate position. + /// @warning It is assumed that the stencil has already been moved + /// to the relevant voxel position, e.g. using moveTo(xyz). + /// @note Computed as partial derivatives of the trilinear interpolation kernel: + /// v000 (1-u)(1-v)(1-w) + v001 (1-u)(1-v)w + v010 (1-u)v(1-w) + v011 (1-u)vw + /// + v100 u(1-v)(1-w) + v101 u(1-v)w + v110 uv(1-w) + v111 uvw + inline Vec3Type gradient(const Vec3Type& xyz) const + { + const Real u = xyz[0] - BaseType::mCenter[0]; assert(u>=0 && u<=1); + const Real v = xyz[1] - BaseType::mCenter[1]; assert(v>=0 && v<=1); + const Real w = xyz[2] - BaseType::mCenter[2]; assert(w>=0 && w<=1); + + ValueType D[4]={BaseType::template getValue<0,0,1>()-BaseType::template getValue<0,0,0>(), + BaseType::template getValue<0,1,1>()-BaseType::template getValue<0,1,0>(), + BaseType::template getValue<1,0,1>()-BaseType::template getValue<1,0,0>(), + BaseType::template getValue<1,1,1>()-BaseType::template getValue<1,1,0>()}; + + // Z component + ValueType A = D[0] + (D[1]- D[0]) * v; + ValueType B = D[2] + (D[3]- D[2]) * v; + Vec3Type grad(zeroVal<ValueType>(), zeroVal<ValueType>(), A + (B - A) * u); + + D[0] = BaseType::template getValue<0,0,0>() + D[0] * w; + D[1] = BaseType::template getValue<0,1,0>() + D[1] * w; + D[2] = BaseType::template getValue<1,0,0>() + D[2] * w; + D[3] = BaseType::template getValue<1,1,0>() + D[3] * w; + + // X component + A = D[0] + (D[1] - D[0]) * v; + B = D[2] + (D[3] - D[2]) * v; + + grad[0] = B - A; + + // Y component + A = D[1] - D[0]; + B = D[3] - D[2]; + + grad[1] = A + (B - A) * u; + + return BaseType::mGrid->transform().baseMap()->applyIJT(grad, xyz); + } + +private: + inline void init(const Coord& ijk) + { + BaseType::template setValue< 0, 0, 1>(mCache.getValue(ijk.offsetBy( 0, 0, 1))); + BaseType::template setValue< 0, 1, 1>(mCache.getValue(ijk.offsetBy( 0, 1, 1))); + BaseType::template setValue< 0, 1, 0>(mCache.getValue(ijk.offsetBy( 0, 1, 0))); + BaseType::template setValue< 1, 0, 0>(mCache.getValue(ijk.offsetBy( 1, 0, 0))); + BaseType::template setValue< 1, 0, 1>(mCache.getValue(ijk.offsetBy( 1, 0, 1))); + BaseType::template setValue< 1, 1, 1>(mCache.getValue(ijk.offsetBy( 1, 1, 1))); + BaseType::template setValue< 1, 1, 0>(mCache.getValue(ijk.offsetBy( 1, 1, 0))); + } + + template<typename, typename> friend class BaseStencil; // allow base class to call init() + using BaseType::mCache; + using BaseType::mStencil; +}; + + +//////////////////////////////////////// + + +namespace { // anonymous namespace for stencil-layout map + // the dense point stencil template<int i, int j, int k> struct DensePt {}; template<> struct DensePt< 0, 0, 0> { enum { idx = 0 }; }; @@ -289,8 +457,6 @@ public: private: inline void init(const Coord& ijk) { - mStencil[DensePt< 0, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 0)); - mStencil[DensePt< 1, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 1, 0, 0)); mStencil[DensePt< 0, 1, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 1, 0)); mStencil[DensePt< 0, 0, 1>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 1)); @@ -369,8 +535,6 @@ public: private: inline void init(const Coord& ijk) { - mStencil[ThirteenPt< 0, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 0)); - mStencil[ThirteenPt< 2, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 2, 0, 0)); mStencil[ThirteenPt< 1, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 1, 0, 0)); mStencil[ThirteenPt<-1, 0, 0>::idx] = mCache.getValue(ijk.offsetBy(-1, 0, 0)); @@ -500,8 +664,6 @@ public: private: inline void init(const Coord& ijk) { - mStencil[FourthDensePt< 0, 0, 0>::idx] = mCache.getValue(ijk); - mStencil[FourthDensePt<-2, 2, 0>::idx] = mCache.getValue(ijk.offsetBy(-2, 2, 0)); mStencil[FourthDensePt<-1, 2, 0>::idx] = mCache.getValue(ijk.offsetBy(-1, 2, 0)); mStencil[FourthDensePt< 0, 2, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 2, 0)); @@ -653,7 +815,6 @@ private: mStencil[NineteenPt< 0,-2, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, -2, 0)); mStencil[NineteenPt< 0,-3, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, -3, 0)); - mStencil[NineteenPt< 0, 0, 3>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 3)); mStencil[NineteenPt< 0, 0, 2>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 2)); mStencil[NineteenPt< 0, 0, 1>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 1)); @@ -854,8 +1015,6 @@ public: private: inline void init(const Coord& ijk) { - mStencil[SixthDensePt< 0, 0, 0>::idx] = mCache.getValue(ijk); - mStencil[SixthDensePt<-3, 3, 0>::idx] = mCache.getValue(ijk.offsetBy(-3, 3, 0)); mStencil[SixthDensePt<-2, 3, 0>::idx] = mCache.getValue(ijk.offsetBy(-2, 3, 0)); mStencil[SixthDensePt<-1, 3, 0>::idx] = mCache.getValue(ijk.offsetBy(-1, 3, 0)); @@ -1115,6 +1274,7 @@ public: } private: + inline void init(const Coord& ijk) { mStencil[1] = mCache.getValue(ijk.offsetBy(-1, 0, 0)); @@ -1126,7 +1286,7 @@ private: mStencil[5] = mCache.getValue(ijk.offsetBy( 0, 0, -1)); mStencil[6] = mCache.getValue(ijk.offsetBy( 0, 0, 1)); } - + template<typename, typename> friend class BaseStencil; // allow base class to call init() using BaseType::mCache; using BaseType::mStencil; @@ -1318,8 +1478,7 @@ public: inline ValueType meanCurvature() { Real alpha, beta; - this->meanCurvature(alpha, beta); - return ValueType(alpha*mInv2Dx/math::Pow3(beta)); + return this->meanCurvature(alpha, beta) ? ValueType(alpha*mInv2Dx/math::Pow3(beta)) : 0; } /// Return the mean curvature multiplied by the norm of the @@ -1331,8 +1490,7 @@ public: inline ValueType meanCurvatureNormGrad() { Real alpha, beta; - this->meanCurvature(alpha, beta); - return ValueType(alpha*mInvDx2/(2*math::Pow2(beta))); + return this->meanCurvature(alpha, beta) ? ValueType(alpha*mInvDx2/(2*math::Pow2(beta))) : 0; } /// Return the Laplacian computed at the previously buffered @@ -1389,7 +1547,7 @@ private: mStencil[18] = mCache.getValue(ijk.offsetBy( 0, 1, 1)); } - inline void meanCurvature(Real& alpha, Real& beta) const + inline bool meanCurvature(Real& alpha, Real& beta) const { // For performance all finite differences are unscaled wrt dx const Real @@ -1397,6 +1555,12 @@ private: Dx = Half * (mStencil[2] - mStencil[1]), Dx2 = Dx * Dx, // * 1/dx Dy = Half * (mStencil[4] - mStencil[3]), Dy2 = Dy * Dy, // * 1/dx Dz = Half * (mStencil[6] - mStencil[5]), Dz2 = Dz * Dz, // * 1/dx + normGrad = Dx2 + Dy2 + Dz2; + if (normGrad <= math::Tolerance<Real>::value()) { + alpha = beta = 0; + return false; + } + const Real Dxx = mStencil[2] - 2 * mStencil[0] + mStencil[1], // * 1/dx2 Dyy = mStencil[4] - 2 * mStencil[0] + mStencil[3], // * 1/dx2 Dzz = mStencil[6] - 2 * mStencil[0] + mStencil[5], // * 1/dx2 @@ -1404,7 +1568,8 @@ private: Dxz = Quarter * (mStencil[14] - mStencil[12] + mStencil[11] - mStencil[13]), // * 1/dx2 Dyz = Quarter * (mStencil[18] - mStencil[16] + mStencil[15] - mStencil[17]); // * 1/dx2 alpha = (Dx2*(Dyy+Dzz)+Dy2*(Dxx+Dzz)+Dz2*(Dxx+Dyy)-2*(Dx*(Dy*Dxy+Dz*Dxz)+Dy*Dz*Dyz)); - beta = std::sqrt(Dx2 + Dy2 + Dz2); // * 1/dx + beta = std::sqrt(normGrad); // * 1/dx + return true; } template<typename, typename> friend class BaseStencil; // allow base class to call init() @@ -1429,20 +1594,37 @@ public: BaseType(grid, /*size=*/math::Pow3(2 * halfWidth + 1)), mHalfWidth(halfWidth) { - //assert(halfWidth>0);//should this be allowed? + assert(halfWidth>0); + } + + inline const ValueType& getCenterValue() const { return mStencil[(mStencil.size()-1)>>1]; } + + /// @brief Initialize the stencil buffer with the values of voxel (x, y, z) + /// and its neighbors. + inline void moveTo(const Coord& ijk) + { + BaseType::mCenter = ijk; + this->init(ijk); + } + /// @brief Initialize the stencil buffer with the values of voxel + /// (x, y, z) and its neighbors. + template<typename IterType> + inline void moveTo(const IterType& iter) + { + BaseType::mCenter = iter.getCoord(); + this->init(BaseType::mCenter); } private: - /// Initialize the stencil buffer centered at (x, y, z). + /// Initialize the stencil buffer centered at (i, j, k). + /// @warning The center point is NOT at mStencil[0] for this DenseStencil! inline void init(const Coord& ijk) { - for (int n=0, i=ijk[0]-mHalfWidth, ie = ijk[0]+mHalfWidth; i <= ie; ++i) { - Coord sample_ijk(i,0,0); - for (int j = ijk[1]-mHalfWidth, je = ijk[1]+mHalfWidth; j <= je; ++j) { - sample_ijk.setY(j); - for (int k = ijk[2]-mHalfWidth, ke = ijk[2] + mHalfWidth; k <= ke; ++k) { - sample_ijk.setZ(k); - mStencil[n++] = mCache.getValue(sample_ijk); + int n = 0; + for (Coord p=ijk.offsetBy(-mHalfWidth), q=ijk.offsetBy(mHalfWidth); p[0] <= q[0]; ++p[0]) { + for (p[1] = ijk[1]-mHalfWidth; p[1] <= q[1]; ++p[1]) { + for (p[2] = ijk[2]-mHalfWidth; p[2] <= q[2]; ++p[2]) { + mStencil[n++] = mCache.getValue(p); } } } diff --git a/extern/openvdb/internal/openvdb/math/Transform.cc b/extern/openvdb/internal/openvdb/math/Transform.cc index c0b45710476..cda3602675b 100644 --- a/extern/openvdb/internal/openvdb/math/Transform.cc +++ b/extern/openvdb/internal/openvdb/math/Transform.cc @@ -100,7 +100,7 @@ Transform::read(std::istream& is) if (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_NEW_TRANSFORM) { // Handle old-style transforms. - if(type == "LinearTransform") { + if (type == "LinearTransform") { // First read in the old transform's base class. Coord tmpMin, tmpMax; is.read(reinterpret_cast<char*>(&tmpMin), sizeof(Coord::ValueType) * 3); @@ -123,20 +123,34 @@ Transform::read(std::istream& is) internal::LegacyFrustum legacyFrustum(is); CoordBBox bb = legacyFrustum.getBBox(); - BBoxd bbox(bb.min().asVec3d(), bb.max().asVec3d()); + BBoxd bbox(bb.min().asVec3d(), bb.max().asVec3d() + /* -Vec3d(1,1,1) */ + ); double taper = legacyFrustum.getTaper(); double depth = legacyFrustum.getDepth(); double nearPlaneWidth = legacyFrustum.getNearPlaneWidth(); double nearPlaneDist = legacyFrustum.getNearPlaneDist(); - Mat4d camxform = legacyFrustum.getCamXForm(); + const Mat4d& camxform = legacyFrustum.getCamXForm(); // create the new frustum with these parameters Mat4d xform(Mat4d::identity()); xform.setToTranslation(Vec3d(0,0, -nearPlaneDist)); xform.preScale(Vec3d(nearPlaneWidth, nearPlaneWidth, -nearPlaneWidth)); - MapBase::Ptr linearMap(simplify(AffineMap(xform * camxform).getAffineMap())); + // create the linear part of the frustum (the second map) + Mat4d second = xform * camxform; + + // we might have precision problems, the constructor for the + // affine map is not forgiving (so we fix here). + const Vec4d col3 = second.col(3); + const Vec4d ref(0, 0, 0, 1); + + if (ref.eq(col3) ) { + second.setCol(3, ref); + } + + MapBase::Ptr linearMap(simplify(AffineMap(second).getAffineMap())); // note that the depth is scaled on the nearPlaneSize. // the linearMap will uniformly scale the frustum to the correct size @@ -144,18 +158,17 @@ Transform::read(std::istream& is) mMap = MapBase::Ptr(new NonlinearFrustumMap( bbox, taper, depth/nearPlaneWidth, linearMap)); - } else { OPENVDB_THROW(IoError, "Transforms of type " + type + " are no longer supported"); } } else { // Check if the map has been registered. - if (!MapRegistry::instance()->isRegistered(type)) { + if (!MapRegistry::isRegistered(type)) { OPENVDB_THROW(KeyError, "Map " << type << " is not registered"); } // Create the map of the type and then read it in. - mMap = math::MapRegistry::instance()->createMap(type); + mMap = math::MapRegistry::createMap(type); mMap->read(is); } } @@ -175,13 +188,14 @@ Transform::write(std::ostream& os) const //////////////////////////////////////// + bool -Transform::isIdentity() const +Transform::isIdentity() const { if (mMap->isLinear()) { return mMap->getAffineMap()->isIdentity(); } else if ( mMap->isType<NonlinearFrustumMap>() ) { - NonlinearFrustumMap::Ptr frustum + NonlinearFrustumMap::Ptr frustum = boost::static_pointer_cast<NonlinearFrustumMap, MapBase>(mMap); return frustum->isIdentity(); } @@ -189,6 +203,10 @@ Transform::isIdentity() const return false; } + +//////////////////////////////////////// + + void Transform::preRotate(double radians, const Axis axis) { @@ -222,33 +240,33 @@ Transform::preShear(double shear, Axis axis0, Axis axis1) void Transform::preMult(const Mat4d& m) -{ +{ if (mMap->isLinear()) { const Mat4d currentMat4 = mMap->getAffineMap()->getMat4(); const Mat4d newMat4 = m * currentMat4; - + AffineMap::Ptr affineMap( new AffineMap( newMat4) ); mMap = simplify(affineMap); } else if (mMap->isType<NonlinearFrustumMap>() ) { - - NonlinearFrustumMap::Ptr currentFrustum = + + NonlinearFrustumMap::Ptr currentFrustum = boost::static_pointer_cast<NonlinearFrustumMap, MapBase>(mMap); - + const Mat4d currentMat4 = currentFrustum->secondMap().getMat4(); const Mat4d newMat4 = m * currentMat4; AffineMap affine(newMat4); - + NonlinearFrustumMap::Ptr frustum( new NonlinearFrustumMap( currentFrustum->getBBox(), currentFrustum->getTaper(), currentFrustum->getDepth(), affine.copy() ) ); mMap = boost::static_pointer_cast<MapBase, NonlinearFrustumMap>( frustum ); } - -} + +} void Transform::preMult(const Mat3d& m) @@ -292,33 +310,33 @@ Transform::postShear(double shear, Axis axis0, Axis axis1) void Transform::postMult(const Mat4d& m) -{ +{ if (mMap->isLinear()) { const Mat4d currentMat4 = mMap->getAffineMap()->getMat4(); const Mat4d newMat4 = currentMat4 * m; - + AffineMap::Ptr affineMap( new AffineMap( newMat4) ); mMap = simplify(affineMap); } else if (mMap->isType<NonlinearFrustumMap>() ) { - - NonlinearFrustumMap::Ptr currentFrustum = + + NonlinearFrustumMap::Ptr currentFrustum = boost::static_pointer_cast<NonlinearFrustumMap, MapBase>(mMap); - + const Mat4d currentMat4 = currentFrustum->secondMap().getMat4(); const Mat4d newMat4 = currentMat4 * m; AffineMap affine(newMat4); - + NonlinearFrustumMap::Ptr frustum( new NonlinearFrustumMap( currentFrustum->getBBox(), currentFrustum->getTaper(), currentFrustum->getDepth(), affine.copy() ) ); mMap = boost::static_pointer_cast<MapBase, NonlinearFrustumMap>( frustum ); } - -} + +} void Transform::postMult(const Mat3d& m) diff --git a/extern/openvdb/internal/openvdb/math/Transform.h b/extern/openvdb/internal/openvdb/math/Transform.h index 39794f8613b..7159bfb6545 100644 --- a/extern/openvdb/internal/openvdb/math/Transform.h +++ b/extern/openvdb/internal/openvdb/math/Transform.h @@ -27,7 +27,7 @@ // LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. // /////////////////////////////////////////////////////////////////////////// -#pragma once + #ifndef OPENVDB_MATH_TRANSFORM_HAS_BEEN_INCLUDED #define OPENVDB_MATH_TRANSFORM_HAS_BEEN_INCLUDED diff --git a/extern/openvdb/internal/openvdb/math/Tuple.h b/extern/openvdb/internal/openvdb/math/Tuple.h index 579ccad8608..a4aec55a1d1 100644 --- a/extern/openvdb/internal/openvdb/math/Tuple.h +++ b/extern/openvdb/internal/openvdb/math/Tuple.h @@ -51,7 +51,8 @@ class Tuple { public: typedef T value_type; typedef T ValueType; - enum SIZE_ { size = SIZE }; + + static const int size = SIZE; /// Default ctor. Does nothing. Required because declaring a copy (or /// other) constructor means the default constructor gets left out. diff --git a/extern/openvdb/internal/openvdb/math/Vec2.h b/extern/openvdb/internal/openvdb/math/Vec2.h index b1c31ee668f..3a63e74b92e 100644 --- a/extern/openvdb/internal/openvdb/math/Vec2.h +++ b/extern/openvdb/internal/openvdb/math/Vec2.h @@ -197,6 +197,21 @@ public: /// does not involve square root T lengthSqr() const { return (this->mm[0]*this->mm[0] + this->mm[1]*this->mm[1]); } + /// Return a reference to itsef after the exponent has been + /// applied to all the vector components. + inline const Vec2<T>& exp() + { + this->mm[0] = std::exp(this->mm[0]); + this->mm[1] = std::exp(this->mm[1]); + return *this; + } + + /// Return the sum of all the vector components. + inline T sum() const + { + return this->mm[0] + this->mm[1]; + } + /// this = normalized this bool normalize(T eps=1.0e-8) { @@ -495,17 +510,15 @@ inline Vec2<T> maxComponent(const Vec2<T> &v1, const Vec2<T> &v2) std::max(v1.y(), v2.y())); } +/// @brief Return a vector with the exponent applied to each of +/// the components of the input vector. +template <typename T> +inline Vec2<T> Exp(Vec2<T> v) { return v.exp(); } -typedef Vec2<float> Vec2s; -typedef Vec2<int> Vec2i; -typedef Vec2<unsigned int> Vec2ui; -typedef Vec2<double> Vec2d; - -#if DWREAL_IS_DOUBLE == 1 -typedef Vec2d Vec2f; -#else -typedef Vec2s Vec2f; -#endif // DWREAL_IS_DOUBLE +typedef Vec2<int32_t> Vec2i; +typedef Vec2<uint32_t> Vec2ui; +typedef Vec2<float> Vec2s; +typedef Vec2<double> Vec2d; } // namespace math } // namespace OPENVDB_VERSION_NAME diff --git a/extern/openvdb/internal/openvdb/math/Vec3.h b/extern/openvdb/internal/openvdb/math/Vec3.h index dd0a130b7cd..fac73195736 100644 --- a/extern/openvdb/internal/openvdb/math/Vec3.h +++ b/extern/openvdb/internal/openvdb/math/Vec3.h @@ -27,7 +27,7 @@ // LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. // /////////////////////////////////////////////////////////////////////////// -#pragma once + #ifndef OPENVDB_MATH_VEC3_HAS_BEEN_INCLUDED #define OPENVDB_MATH_VEC3_HAS_BEEN_INCLUDED @@ -324,6 +324,22 @@ public: return *this; } + /// Return a reference to itsef after the exponent has been + /// applied to all the vector components. + inline const Vec3<T>& exp() + { + this->mm[0] = std::exp(this->mm[0]); + this->mm[1] = std::exp(this->mm[1]); + this->mm[2] = std::exp(this->mm[2]); + return *this; + } + + /// Return the sum of all the vector components. + inline T sum() const + { + return this->mm[0] + this->mm[1] + this->mm[2]; + } + /// this = normalized this bool normalize(T eps = T(1.0e-7)) { @@ -598,16 +614,15 @@ inline Vec3<T> maxComponent(const Vec3<T> &v1, const Vec3<T> &v2) std::max(v1.z(), v2.z())); } -typedef Vec3<int> Vec3i; -typedef Vec3<unsigned int> Vec3ui; -typedef Vec3<float> Vec3s; -typedef Vec3<double> Vec3d; +/// @brief Return a vector with the exponent applied to each of +/// the components of the input vector. +template <typename T> +inline Vec3<T> Exp(Vec3<T> v) { return v.exp(); } -#if DWREAL_IS_DOUBLE == 1 -typedef Vec3d Vec3f; -#else -typedef Vec3s Vec3f; -#endif // DWREAL_IS_DOUBLE +typedef Vec3<int32_t> Vec3i; +typedef Vec3<uint32_t> Vec3ui; +typedef Vec3<float> Vec3s; +typedef Vec3<double> Vec3d; } // namespace math } // namespace OPENVDB_VERSION_NAME diff --git a/extern/openvdb/internal/openvdb/math/Vec4.h b/extern/openvdb/internal/openvdb/math/Vec4.h index 04fc78d0f3c..a8ddf501362 100644 --- a/extern/openvdb/internal/openvdb/math/Vec4.h +++ b/extern/openvdb/internal/openvdb/math/Vec4.h @@ -236,6 +236,24 @@ public: + this->mm[2]*this->mm[2] + this->mm[3]*this->mm[3]); } + /// Return a reference to itsef after the exponent has been + /// applied to all the vector components. + inline const Vec4<T>& exp() + { + this->mm[0] = std::exp(this->mm[0]); + this->mm[1] = std::exp(this->mm[1]); + this->mm[2] = std::exp(this->mm[2]); + this->mm[3] = std::exp(this->mm[3]); + return *this; + } + + /// Return the sum of all the vector components. + inline T sum() const + { + return this->mm[0] + this->mm[1] + this->mm[2] + this->mm[3]; + } + + /// this = normalized this bool normalize(T eps=1.0e-8) { @@ -517,17 +535,15 @@ inline Vec4<T> maxComponent(const Vec4<T> &v1, const Vec4<T> &v2) std::max(v1.w(), v2.w())); } +/// @brief Return a vector with the exponent applied to each of +/// the components of the input vector. +template <typename T> +inline Vec4<T> Exp(Vec4<T> v) { return v.exp(); } -typedef Vec4<int> Vec4i; -typedef Vec4<unsigned int> Vec4ui; -typedef Vec4<float> Vec4s; -typedef Vec4<double> Vec4d; - -#if DWREAL_IS_DOUBLE == 1 -typedef Vec4d Vec4f; -#else -typedef Vec4s Vec4f; -#endif // DWREAL_IS_DOUBLE +typedef Vec4<int32_t> Vec4i; +typedef Vec4<uint32_t> Vec4ui; +typedef Vec4<float> Vec4s; +typedef Vec4<double> Vec4d; } // namespace math } // namespace OPENVDB_VERSION_NAME diff --git a/extern/openvdb/internal/openvdb/tools/Composite.h b/extern/openvdb/internal/openvdb/tools/Composite.h index 58a3d91bc7d..b6c5e8e8bc9 100644 --- a/extern/openvdb/internal/openvdb/tools/Composite.h +++ b/extern/openvdb/internal/openvdb/tools/Composite.h @@ -83,6 +83,10 @@ inline void compSum(GridOrTreeT& a, GridOrTreeT& b); /// Store the result in the A grid and leave the B grid empty. template<typename GridOrTreeT> OPENVDB_STATIC_SPECIALIZATION inline void compMul(GridOrTreeT& a, GridOrTreeT& b); +/// @brief Given grids A and B, compute a / b per voxel (using sparse traversal). +/// Store the result in the A grid and leave the B grid empty. +template<typename GridOrTreeT> OPENVDB_STATIC_SPECIALIZATION +inline void compDiv(GridOrTreeT& a, GridOrTreeT& b); /// Copy the active voxels of B into A. template<typename GridOrTreeT> OPENVDB_STATIC_SPECIALIZATION @@ -186,6 +190,21 @@ compMul(GridOrTreeT& aTree, GridOrTreeT& bTree) } +template<typename GridOrTreeT> +OPENVDB_STATIC_SPECIALIZATION inline void +compDiv(GridOrTreeT& aTree, GridOrTreeT& bTree) +{ + typedef TreeAdapter<GridOrTreeT> Adapter; + typedef typename Adapter::TreeType TreeT; + struct Local { + static inline void op(CombineArgs<typename TreeT::ValueType>& args) { + args.setResult(args.a() / args.b()); + } + }; + Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false); +} + + //////////////////////////////////////// @@ -255,9 +274,9 @@ public: CsgVisitorBase(const TreeT& aTree, const TreeT& bTree): mAOutside(aTree.background()), - mAInside(negative(mAOutside)), + mAInside(math::negative(mAOutside)), mBOutside(bTree.background()), - mBInside(negative(mBOutside)) + mBInside(math::negative(mBOutside)) { const ValueT zero = zeroVal<ValueT>(); if (!(mAOutside > zero)) { @@ -492,7 +511,7 @@ struct CsgDiffVisitor: public CsgVisitorBase<TreeType> ValueT aValue, bValue; aIter.probeValue(aValue); bIter.probeValue(bValue); - bValue = negative(bValue); + bValue = math::negative(bValue); if (aValue < bValue) { // a = max(a, -b) aIter.setValue(bValue); aIter.setValueOn(bIter.isValueOn()); diff --git a/extern/openvdb/internal/openvdb/tools/Dense.h b/extern/openvdb/internal/openvdb/tools/Dense.h index 3fbb8ace92a..45ab9a97c56 100644 --- a/extern/openvdb/internal/openvdb/tools/Dense.h +++ b/extern/openvdb/internal/openvdb/tools/Dense.h @@ -38,28 +38,27 @@ #include <openvdb/Types.h> #include <openvdb/Grid.h> +#include <openvdb/tree/ValueAccessor.h> #include <openvdb/Exceptions.h> #include <tbb/parallel_for.h> +#include <boost/scoped_array.hpp> +#include <boost/scoped_ptr.hpp> namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { namespace tools { -// Forward declaration (see definition below) -template<typename ValueT> class Dense; - - /// @brief Populate a dense grid with the values of voxels from a sparse grid, /// where the sparse grid intersects the dense grid. /// @param sparse an OpenVDB grid or tree from which to copy values /// @param dense the dense grid into which to copy values /// @param serial if false, process voxels in parallel -template<typename GridOrTreeT> +template<typename DenseT, typename GridOrTreeT> void copyToDense( const GridOrTreeT& sparse, - Dense<typename GridOrTreeT::ValueType>& dense, + DenseT& dense, bool serial = false); @@ -69,10 +68,10 @@ copyToDense( /// @param tolerance values in the dense grid that are within this tolerance of the sparse /// grid's background value become inactive background voxels or tiles in the sparse grid /// @param serial if false, process voxels in parallel -template<typename GridOrTreeT> +template<typename DenseT, typename GridOrTreeT> void copyFromDense( - const Dense<typename GridOrTreeT::ValueType>& dense, + const DenseT& dense, GridOrTreeT& sparse, const typename GridOrTreeT::ValueType& tolerance, bool serial = false); @@ -80,33 +79,121 @@ copyFromDense( //////////////////////////////////////// +/// We currently support the following two 3D memory layouts for dense +/// volumes: XYZ, i.e. x is the fastest moving index, and ZYX, i.e. z +/// is the fastest moving index. The ZYX memory layout leads to nested +/// for-loops of the order x, y, z, which we find to be the most +/// intuitive. Hence, ZYX is the layout used throughout VDB. However, +/// other data structures, e.g. Houdini and Maya, employ the XYZ +/// layout. Clearly a dense volume with the ZYX layout converts more +/// efficiently to a VDB, but we support both for convenience. +enum MemoryLayout { LayoutXYZ, LayoutZYX }; + +/// @brief Base class for Dense which is defined below. +/// @note The constructor of this class is protected to prevent direct +/// instantiation. +template<typename ValueT, MemoryLayout Layout> class DenseBase; + +/// @brief Partial template specialization of DenseBase. +/// @note ZYX is the memory-layout in VDB. It leads to nested +/// for-loops of the order x, y, z which we find to be the most intuitive. +template<typename ValueT> +class DenseBase<ValueT, LayoutZYX> +{ +public: + /// @brief Return the linear offset into this grid's value array given by + /// unsigned coordinates (i, j, k), i.e., coordinates relative to + /// the origin of this grid's bounding box. + inline size_t coordToOffset(size_t i, size_t j, size_t k) const { return i*mX + j*mY + k; } + + /// @brief Return the stride of the array in the x direction ( = dimY*dimZ). + /// @note This method is required by both CopyToDense and CopyFromDense. + inline size_t xStride() const { return mX; } + /// @brief Return the stride of the array in the y direction ( = dimZ). + /// @note This method is required by both CopyToDense and CopyFromDense. + inline size_t yStride() const { return mY; } + + /// @brief Return the stride of the array in the z direction ( = 1). + /// @note This method is required by both CopyToDense and CopyFromDense. + static size_t zStride() { return 1; } + +protected: + /// Protected constructor so as to prevent direct instantiation + DenseBase(const CoordBBox& bbox) : mBBox(bbox), mY(bbox.dim()[2]), mX(mY*bbox.dim()[1]) {} + + const CoordBBox mBBox;//signed coordinates of the domain represented by the grid + const size_t mY, mX;//strides in the y and x direction +};// end of DenseBase<ValueT, LayoutZYX> + +/// @brief Partial template specialization of DenseBase. +/// @note This is the memory-layout emplayed in Houdini and Maya. It leads +/// to nested for-loops of the order z, y, x. +template<typename ValueT> +class DenseBase<ValueT, LayoutXYZ> +{ +public: + /// @brief Return the linear offset into this grid's value array given by + /// unsigned coordinates (i, j, k), i.e., coordinates relative to + /// the origin of this grid's bounding box. + inline size_t coordToOffset(size_t i, size_t j, size_t k) const { return i + j*mY + k*mZ; } + + /// @brief Return the stride of the array in the x direction ( = 1). + /// @note This method is required by both CopyToDense and CopyFromDense. + static size_t xStride() { return 1; } + + /// @brief Return the stride of the array in the y direction ( = dimX). + /// @note This method is required by both CopyToDense and CopyFromDense. + inline size_t yStride() const { return mY; } + + /// @brief Return the stride of the array in the y direction ( = dimX*dimY). + /// @note This method is required by both CopyToDense and CopyFromDense. + inline size_t zStride() const { return mZ; } + +protected: + /// Protected constructor so as to prevent direct instantiation + DenseBase(const CoordBBox& bbox) : mBBox(bbox), mY(bbox.dim()[0]), mZ(mY*bbox.dim()[1]) {} + + const CoordBBox mBBox;//signed coordinates of the domain represented by the grid + const size_t mY, mZ;//strides in the y and z direction +};// end of DenseBase<ValueT, LayoutXYZ> + /// @brief Dense is a simple dense grid API used by the CopyToDense and /// CopyFromDense classes defined below. /// @details Use the Dense class to efficiently produce a dense in-memory /// representation of an OpenVDB grid. However, be aware that a dense grid /// could have a memory footprint that is orders of magnitude larger than -/// the corresponding sparse grid from which it originates. +/// the sparse grid from which it originates. /// /// @note This class can be used as a simple wrapper for existing dense grid /// classes if they provide access to the raw data array. -/// @note This implementation assumes a data layout where @e z is the -/// fastest-changing index (because that is the layout used by OpenVDB grids). -template<typename ValueT> -class Dense +/// @note This implementation allows for the 3D memory layout to be +/// defined by the MemoryLayout template parameter (see above for definition). +/// The default memory layout is ZYX since that's the layout used by OpenVDB grids. +template<typename ValueT, MemoryLayout Layout = LayoutZYX> +class Dense : public DenseBase<ValueT, Layout> { public: + typedef ValueT ValueType; + typedef DenseBase<ValueT, Layout> BaseT; + /// @brief Construct a dense grid with a given range of coordinates. /// /// @param bbox the bounding box of the (signed) coordinate range of this grid /// @throw ValueError if the bounding box is empty. - Dense(const CoordBBox& bbox) - : mBBox(bbox), mArray(new ValueT[bbox.volume()]), mData(mArray.get()), - mY(bbox.dim()[2]), mX(mY*bbox.dim()[1]) + /// @note The min and max coordinates of the bounding box are inclusive. + Dense(const CoordBBox& bbox) : BaseT(bbox) { this->init(); } + + /// @brief Construct a dense grid with a given range of coordinates and initial value + /// + /// @param bbox the bounding box of the (signed) coordinate range of this grid + /// @param value the initial value of the grid. + /// @throw ValueError if the bounding box is empty. + /// @note The min and max coordinates of the bounding box are inclusive. + Dense(const CoordBBox& bbox, const ValueT& value) : BaseT(bbox) { - if (bbox.empty()) { - OPENVDB_THROW(ValueError, "can't construct a dense grid with an empty bounding box"); - } + this->init(); + this->fill(value); } /// @brief Construct a dense grid that wraps an external array. @@ -117,109 +204,87 @@ public: /// /// @note The data array is assumed to have a stride of one in the @e z direction. /// @throw ValueError if the bounding box is empty. - Dense(const CoordBBox& bbox, ValueT* data) - : mBBox(bbox), mData(data), mY(mBBox.dim()[2]), mX(mY*mBBox.dim()[1]) + /// @note The min and max coordinates of the bounding box are inclusive. + Dense(const CoordBBox& bbox, ValueT* data) : BaseT(bbox), mData(data) { - if (mBBox.empty()) { + if (BaseT::mBBox.empty()) { OPENVDB_THROW(ValueError, "can't construct a dense grid with an empty bounding box"); } - } + } /// @brief Construct a dense grid with a given origin and dimensions. /// /// @param dim the desired dimensions of the grid /// @param min the signed coordinates of the first voxel in the dense grid /// @throw ValueError if any of the dimensions are zero. + /// @note The @a min coordinate is inclusive, and the max coordinate will be + /// @a min + @a dim - 1. Dense(const Coord& dim, const Coord& min = Coord(0)) - : mBBox(min, min+dim.offsetBy(-1)), mArray(new ValueT[mBBox.volume()]), - mData(mArray.get()), mY(mBBox.dim()[2]), mX(mY*mBBox.dim()[1]) + : BaseT(CoordBBox(min, min+dim.offsetBy(-1))) { - if (mBBox.empty()) { - OPENVDB_THROW(ValueError, "can't construct a dense grid of size zero"); - } + this->init(); } + /// @brief Return the memory layout for this grid (see above for definitions). + static MemoryLayout memoryLayout() { return Layout; } + /// @brief Return a raw pointer to this grid's value array. - /// /// @note This method is required by CopyToDense. - ValueT* data() { return mData; } + inline ValueT* data() { return mData; } /// @brief Return a raw pointer to this grid's value array. - /// /// @note This method is required by CopyFromDense. - const ValueT* data() const { return mData; } + inline const ValueT* data() const { return mData; } /// @brief Return the bounding box of the signed index domain of this grid. - /// - /// @note This method is required by both CopyToDense and CopyFromDense. - const CoordBBox& bbox() const { return mBBox; } - - /// @brief Return the stride of the array in the x direction ( = dimY*dimZ). - /// - /// @note This method is required by both CopyToDense and CopyFromDense. - size_t xStride() const { return mX; } - - /// @brief Return the stride of the array in the y direction ( = dimZ). - /// /// @note This method is required by both CopyToDense and CopyFromDense. - size_t yStride() const { return mY; } + inline const CoordBBox& bbox() const { return BaseT::mBBox; } /// @brief Return the number of voxels contained in this grid. - size_t valueCount() const { return mBBox.volume(); } + inline Index64 valueCount() const { return BaseT::mBBox.volume(); } /// @brief Set the value of the voxel at the given array offset. - void setValue(size_t offset, const ValueT& value) { mData[offset] = value; } + inline void setValue(size_t offset, const ValueT& value) { mData[offset] = value; } /// @brief Return the value of the voxel at the given array offset. const ValueT& getValue(size_t offset) const { return mData[offset]; } /// @brief Set the value of the voxel at unsigned index coordinates (i, j, k). /// @note This is somewhat slower than using an array offset. - void setValue(size_t i, size_t j, size_t k, const ValueT& value) + inline void setValue(size_t i, size_t j, size_t k, const ValueT& value) { - mData[this->coordToOffset(i,j,k)] = value; + mData[BaseT::coordToOffset(i,j,k)] = value; } - + /// @brief Return the value of the voxel at unsigned index coordinates (i, j, k). /// @note This is somewhat slower than using an array offset. - const ValueT& getValue(size_t i, size_t j, size_t k) const + inline const ValueT& getValue(size_t i, size_t j, size_t k) const { - return mData[this->coordToOffset(i,j,k)]; + return mData[BaseT::coordToOffset(i,j,k)]; } - + /// @brief Set the value of the voxel at the given signed coordinates. /// @note This is slower than using either an array offset or unsigned index coordinates. - void setValue(const Coord& xyz, const ValueT& value) + inline void setValue(const Coord& xyz, const ValueT& value) { mData[this->coordToOffset(xyz)] = value; } - + /// @brief Return the value of the voxel at the given signed coordinates. /// @note This is slower than using either an array offset or unsigned index coordinates. - const ValueT& getValue(const Coord& xyz) const + inline const ValueT& getValue(const Coord& xyz) const { return mData[this->coordToOffset(xyz)]; } - + /// @brief Fill this grid with a constant value. - void fill(const ValueT& value) + inline void fill(const ValueT& value) { size_t size = this->valueCount(); ValueT* a = mData; while(size--) *a++ = value; } - - /// @brief Return the linear offset into this grid's value array given by - /// unsigned coordinates (i, j, k), i.e., coordinates relative to - /// the origin of this grid's bounding box. - /// - /// @note This method reflects the fact that we assume the same layout - /// of values as an OpenVDB grid, i.e., the fastest coordinate is @e k. - inline size_t coordToOffset(size_t i, size_t j, size_t k) const - { - return k + j*mY + i*mX; - } - + /// @brief Return the linear offset into this grid's value array given by /// the specified signed coordinates, i.e., coordinates in the space of /// this grid's bounding box. @@ -228,66 +293,84 @@ public: /// layout of values as an OpenVDB grid, i.e., the fastest coordinate is @e z. inline size_t coordToOffset(Coord xyz) const { - assert(mBBox.isInside(xyz)); - return this->coordToOffset(size_t(xyz[0]-mBBox.min()[0]), - size_t(xyz[1]-mBBox.min()[1]), - size_t(xyz[2]-mBBox.min()[2])); + assert(BaseT::mBBox.isInside(xyz)); + return BaseT::coordToOffset(size_t(xyz[0]-BaseT::mBBox.min()[0]), + size_t(xyz[1]-BaseT::mBBox.min()[1]), + size_t(xyz[2]-BaseT::mBBox.min()[2])); } - + + /// @brief Return the memory footprint of this Dense grid in bytes. + inline Index64 memUsage() const + { + return sizeof(*this) + BaseT::mBBox.volume() * sizeof(ValueType); + } + private: - const CoordBBox mBBox;//signed coordinates of the domain represented by the grid - boost::shared_array<ValueT> mArray; - ValueT* mData;//raw c-style pointer to values - const size_t mY, mX;//strides in x and y (by design it's 1 in z) -};// end of Dense + /// @brief Private method to initialize the dense value array. + void init() + { + if (BaseT::mBBox.empty()) { + OPENVDB_THROW(ValueError, "can't construct a dense grid with an empty bounding box"); + } + mArray.reset(new ValueT[BaseT::mBBox.volume()]); + mData = mArray.get(); + } + + boost::scoped_array<ValueT> mArray; + ValueT* mData;//raw c-style pointer to values +};// end of Dense //////////////////////////////////////// /// @brief Copy an OpenVDB tree into an existing dense grid. /// -/// @note Only the voxels enclosed by the existing dense grid are copied -/// from the OpenVDB tree. -template<typename TreeT> +/// @note Only voxels that intersect the dense grid's bounding box are copied +/// from the OpenVDB tree. But both active and inactive voxels are copied, +/// so all existing values in the dense grid are overwritten, regardless of +/// the OpenVDB tree's tolopogy. +template<typename _TreeT, typename _DenseT = Dense<typename _TreeT::ValueType> > class CopyToDense { public: + typedef _DenseT DenseT; + typedef _TreeT TreeT; typedef typename TreeT::ValueType ValueT; - CopyToDense(const TreeT& tree, Dense<ValueT> &dense) - : mRoot(tree.root()), mDense(dense) {} + CopyToDense(const TreeT& tree, DenseT& dense) + : mRoot(&(tree.root())), mDense(&dense) {} void copy(bool serial = false) const { if (serial) { - mRoot.copyToDense(mDense.bbox(), mDense); + mRoot->copyToDense(mDense->bbox(), *mDense); } else { - tbb::parallel_for(mDense.bbox(), *this); + tbb::parallel_for(mDense->bbox(), *this); } } /// @brief Public method called by tbb::parallel_for void operator()(const CoordBBox& bbox) const { - mRoot.copyToDense(bbox, mDense); + mRoot->copyToDense(bbox, *mDense); } private: - const typename TreeT::RootNodeType &mRoot; - Dense<ValueT> &mDense; + const typename TreeT::RootNodeType* mRoot; + DenseT* mDense; };// CopyToDense // Convenient wrapper function for the CopyToDense class -template<typename GridOrTreeT> +template<typename DenseT, typename GridOrTreeT> void -copyToDense(const GridOrTreeT& sparse, Dense<typename GridOrTreeT::ValueType>& dense, bool serial) +copyToDense(const GridOrTreeT& sparse, DenseT& dense, bool serial) { typedef TreeAdapter<GridOrTreeT> Adapter; typedef typename Adapter::TreeType TreeT; - CopyToDense<TreeT> op(Adapter::constTree(sparse), dense); + CopyToDense<TreeT, DenseT> op(Adapter::constTree(sparse), dense); op.copy(serial); } @@ -304,75 +387,106 @@ copyToDense(const GridOrTreeT& sparse, Dense<typename GridOrTreeT::ValueType>& d /// @note Since this class allocates leaf nodes concurrently it is recommended /// to use a scalable implementation of @c new like the one provided by TBB, /// rather than the mutex-protected standard library @c new. -template<typename TreeT> +template<typename _TreeT, typename _DenseT = Dense<typename _TreeT::ValueType> > class CopyFromDense { public: + typedef _DenseT DenseT; + typedef _TreeT TreeT; typedef typename TreeT::ValueType ValueT; typedef typename TreeT::LeafNodeType LeafT; - - CopyFromDense(const Dense<ValueT>& dense, TreeT& tree, const ValueT& tolerance) - : mDense(dense), mTree(tree), mBlocks(NULL), mTolerance(tolerance) + typedef tree::ValueAccessor<TreeT> AccessorT; + + CopyFromDense(const DenseT& dense, TreeT& tree, const ValueT& tolerance) + : mDense(&dense), + mTree(&tree), + mBlocks(NULL), + mTolerance(tolerance), + mAccessor(tree.empty() ? NULL : new AccessorT(tree)) + { + } + CopyFromDense(const CopyFromDense& other) + : mDense(other.mDense), + mTree(other.mTree), + mBlocks(other.mBlocks), + mTolerance(other.mTolerance), + mAccessor(other.mAccessor.get() == NULL ? NULL : new AccessorT(*mTree)) { } /// @brief Copy values from the dense grid to the sparse tree. void copy(bool serial = false) { - std::vector<Block> blocks; - mBlocks = &blocks; - const CoordBBox& bbox = mDense.bbox(); + mBlocks = new std::vector<Block>(); + const CoordBBox& bbox = mDense->bbox(); // Pre-process: Construct a list of blocks alligned with (potential) leaf nodes for (CoordBBox sub=bbox; sub.min()[0] <= bbox.max()[0]; sub.min()[0] = sub.max()[0] + 1) { for (sub.min()[1] = bbox.min()[1]; sub.min()[1] <= bbox.max()[1]; - sub.min()[1] = sub.max()[1] + 1) + sub.min()[1] = sub.max()[1] + 1) { for (sub.min()[2] = bbox.min()[2]; sub.min()[2] <= bbox.max()[2]; - sub.min()[2] = sub.max()[2] + 1) + sub.min()[2] = sub.max()[2] + 1) { sub.max() = Coord::minComponent(bbox.max(), (sub.min()&(~(LeafT::DIM-1u))).offsetBy(LeafT::DIM-1u)); - blocks.push_back(Block(sub)); + mBlocks->push_back(Block(sub)); } } } + // Multi-threaded process: Convert dense grid into leaf nodes and tiles if (serial) { - (*this)(tbb::blocked_range<size_t>(0, blocks.size())); + (*this)(tbb::blocked_range<size_t>(0, mBlocks->size())); } else { - tbb::parallel_for(tbb::blocked_range<size_t>(0, blocks.size()), *this); + tbb::parallel_for(tbb::blocked_range<size_t>(0, mBlocks->size()), *this); } + // Post-process: Insert leaf nodes and tiles into the tree, and prune the tiles only! - tree::ValueAccessor<TreeT> acc(mTree); - for (size_t m=0, size = blocks.size(); m<size; ++m) { - Block& block = blocks[m]; + tree::ValueAccessor<TreeT> acc(*mTree); + for (size_t m=0, size = mBlocks->size(); m<size; ++m) { + Block& block = (*mBlocks)[m]; if (block.leaf) { acc.addLeaf(block.leaf); } else if (block.tile.second) {//only background tiles are inactive acc.addTile(1, block.bbox.min(), block.tile.first, true);//leaf tile } } - mTree.root().pruneTiles(mTolerance); + delete mBlocks; + mBlocks = NULL; + + mTree->root().pruneTiles(mTolerance); } /// @brief Public method called by tbb::parallel_for + /// @warning Never call this method directly! void operator()(const tbb::blocked_range<size_t> &r) const { - LeafT* leaf = NULL; + assert(mBlocks); + LeafT* leaf = new LeafT(); for (size_t m=r.begin(), n=0, end = r.end(); m != end; ++m, ++n) { - if (leaf == NULL) leaf = new LeafT(); - Block& block = (*mBlocks)[m]; const CoordBBox &bbox = block.bbox; - leaf->copyFromDense(bbox, mDense, mTree.background(), mTolerance); + if (mAccessor.get() == NULL) {//i.e. empty target tree + leaf->fill(mTree->background(), false); + } else {//account for existing leaf nodes in the target tree + if (const LeafT* target = mAccessor->probeConstLeaf(bbox.min())) { + (*leaf) = (*target); + } else { + ValueT value = zeroVal<ValueT>(); + bool state = mAccessor->probeValue(bbox.min(), value); + leaf->fill(value, state); + } + } + + leaf->copyFromDense(bbox, *mDense, mTree->background(), mTolerance); if (!leaf->isConstant(block.tile.first, block.tile.second, mTolerance)) { - leaf->setOrigin(bbox.min()); + leaf->setOrigin(bbox.min() & (~(LeafT::DIM - 1))); block.leaf = leaf; - leaf = NULL; + leaf = new LeafT(); } }// loop over blocks @@ -387,23 +501,24 @@ private: Block(const CoordBBox& b) : bbox(b), leaf(NULL) {} }; - const Dense<ValueT>& mDense; - TreeT& mTree; - std::vector<Block>* mBlocks; - ValueT mTolerance; + const DenseT* mDense; + TreeT* mTree; + std::vector<Block>* mBlocks; + ValueT mTolerance; + boost::scoped_ptr<AccessorT> mAccessor; };// CopyFromDense // Convenient wrapper function for the CopyFromDense class -template<typename GridOrTreeT> +template<typename DenseT, typename GridOrTreeT> void -copyFromDense(const Dense<typename GridOrTreeT::ValueType>& dense, GridOrTreeT& sparse, +copyFromDense(const DenseT& dense, GridOrTreeT& sparse, const typename GridOrTreeT::ValueType& tolerance, bool serial) { typedef TreeAdapter<GridOrTreeT> Adapter; typedef typename Adapter::TreeType TreeT; - CopyFromDense<TreeT> op(dense, Adapter::tree(sparse), tolerance); + CopyFromDense<TreeT, DenseT> op(dense, Adapter::tree(sparse), tolerance); op.copy(serial); } diff --git a/extern/openvdb/internal/openvdb/tools/DenseSparseTools.h b/extern/openvdb/internal/openvdb/tools/DenseSparseTools.h new file mode 100644 index 00000000000..3c0f9c8b4aa --- /dev/null +++ b/extern/openvdb/internal/openvdb/tools/DenseSparseTools.h @@ -0,0 +1,1259 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +// +// Redistributions of source code must retain the above copyright +// and license notice and the following restrictions and disclaimer. +// +// * Neither the name of DreamWorks Animation nor the names of +// its contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE +// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef OPENVDB_TOOLS_DENSESPARSETOOLS_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_DENSESPARSETOOLS_HAS_BEEN_INCLUDED + +#include <tbb/parallel_reduce.h> +#include <tbb/blocked_range3d.h> +#include <tbb/blocked_range2d.h> +#include <tbb/blocked_range.h> +#include <openvdb/Types.h> +#include <openvdb/tree/LeafManager.h> +#include "Dense.h" + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Selectively extract and transform data from a dense grid, producing a +/// sparse tree with leaf nodes only (e.g. create a tree from the square +/// of values greater than a cutoff.) +/// @param dense A dense grid that acts as a data source +/// @param functor A functor that selects and transforms data for output +/// @param background The background value of the resulting sparse grid +/// @param threaded Option to use threaded or serial code path +/// @return @c Ptr to tree with the valuetype and configuration defined +/// by typedefs in the @c functor. +/// @note To achieve optimal sparsity consider calling the prune() +/// method on the result. +/// @note To simply copy the all the data from a Dense grid to a +/// OpenVDB Grid, use tools::copyFromDense() for better performance. +/// +/// The type of the sparse tree is determined by the specified OtpType +/// functor by means of the typedef OptType::ResultTreeType +/// +/// The OptType function is responsible for the the transformation of +/// dense grid data to sparse grid data on a per-voxel basis. +/// +/// Only leaf nodes with active values will be added to the sparse grid. +/// +/// The OpType must struct that defines a the minimal form +/// @code +/// struct ExampleOp +/// { +/// typedef DesiredTreeType ResultTreeType; +/// +/// template<typename IndexOrCoord> +/// void OpType::operator() (const DenseValueType a, const IndexOrCoord& ijk, +/// ResultTreeType::LeafNodeType* leaf); +/// }; +/// @endcode +/// +/// For example, to generate a <ValueType, 5, 4, 3> tree with valuesOn +/// at locations greater than a given maskvalue +/// @code +/// template <typename ValueType> +/// class Rule +/// { +/// public: +/// // Standard tree type (e.g. BoolTree or FloatTree in openvdb.h) +/// typedef typename openvdb::tree::Tree4<ValueType, 5, 4, 3>::Type ResultTreeType; +/// +/// typedef typename ResultTreeType::LeafNodeType ResultLeafNodeType; +/// typedef typename ResultTreeType::ValueType ResultValueType; +/// +/// typedef float DenseValueType; +/// +/// typedef vdbmath::Coord::ValueType Index; +/// +/// Rule(const DenseValueType& value): mMaskValue(value){}; +/// +/// template <typename IndexOrCoord> +/// void operator()(const DenseValueType& a, const IndexOrCoord& offset, +/// ResultLeafNodeType* leaf) const +/// { +/// if (a > mMaskValue) { +/// leaf->setValueOn(offset, a); +/// } +/// } +/// +/// private: +/// const DenseValueType mMaskValue; +/// }; +/// @endcode +template<typename OpType, typename DenseType> +typename OpType::ResultTreeType::Ptr +extractSparseTree(const DenseType& dense, const OpType& functor, + const typename OpType::ResultValueType& background, + bool threaded = true); + +/// This struct that aids template resoluion of a new tree type +/// has the same configuration at TreeType, but the ValueType from +/// DenseType. +template <typename DenseType, typename TreeType> struct DSConverter { + typedef typename DenseType::ValueType ValueType; + + typedef typename TreeType::template ValueConverter<ValueType>::Type Type; +}; + + +/// @brief Copy data from the intersection of a sparse tree and a dense input grid. +/// The resulting tree has the same configuration as the sparse tree, but holds +/// the data type specified by the dense input. +/// @param dense A dense grid that acts as a data source +/// @param mask The active voxels and tiles intersected with dense define iteration mask +/// @param background The background value of the resulting sparse grid +/// @param threaded Option to use threaded or serial code path +/// @return @c Ptr to tree with the same configuration as @c mask but of value type +/// defined by @c dense. +template<typename DenseType, typename MaskTreeType> +typename DSConverter<DenseType, MaskTreeType>::Type::Ptr +extractSparseTreeWithMask(const DenseType& dense, + const MaskTreeType& mask, + const typename DenseType::ValueType& background, + bool threaded = true); + + +/// Apply a point-wise functor to the intersection of a dense grid and a given bounding box +/// @param dense A dense grid to be transformed +/// @param bbox Index space bounding box, define region where the transformation is applied +/// @param op A functor that acts on the dense grid value type +/// @param parallel Used to select multithreaded or single threaded +/// Minimally, the @c op class has to support a @c operator() method, +/// @code +/// // Square values in a grid +/// struct Op +/// { +/// ValueT operator()(const ValueT& in) const +/// { +/// // do work +/// ValueT result = in * in; +/// +/// return result; +/// } +/// }; +/// @endcode +/// NB: only Dense grids with memory layout zxy are supported +template<typename ValueT, typename OpType> +void transformDense(Dense<ValueT, openvdb::tools::LayoutZYX>& dense, + const openvdb::CoordBBox& bbox, const OpType& op, bool parallel=true); + +/// We currrently support the following operations when compositing sparse +/// data into a dense grid. +enum DSCompositeOp { + DS_OVER, DS_ADD, DS_SUB, DS_MIN, DS_MAX, DS_MULT, DS_SET +}; + +/// @brief Composite data from a sparse tree into a dense array of the same value type. +/// @param dense Dense grid to be altered by the operation +/// @param source Sparse data to composite into @c dense +/// @param alpha Sparse Alpha mask used in compositing operations. +/// @param beta Constant multiplier on src +/// @param strength Constant multiplier on alpha +/// @param threaded Enable threading for this operation. +template<DSCompositeOp, typename TreeT> +void compositeToDense(Dense<typename TreeT::ValueType, LayoutZYX>& dense, + const TreeT& source, + const TreeT& alpha, + const typename TreeT::ValueType beta, + const typename TreeT::ValueType strength, + bool threaded = true); + + +/// @brief Functor-based class used to extract data that satisfies some +/// criteria defined by the embedded @c OpType functor. The @c extractSparseTree +/// function wraps this class. +template<typename OpType, typename DenseType> +class SparseExtractor +{ + +public: + + typedef openvdb::math::Coord::ValueType Index; + + typedef typename DenseType::ValueType DenseValueType; + typedef typename OpType::ResultTreeType ResultTreeType; + typedef typename ResultTreeType::ValueType ResultValueType; + typedef typename ResultTreeType::LeafNodeType ResultLeafNodeType; + typedef typename ResultTreeType::template ValueConverter<bool>::Type BoolTree; + + typedef tbb::blocked_range3d<Index, Index, Index> Range3d; + + +private: + + const DenseType& mDense; + const OpType& mFunctor; + const ResultValueType mBackground; + const openvdb::math::CoordBBox mBBox; + const Index mWidth; + typename ResultTreeType::Ptr mMask; + openvdb::math::Coord mMin; + + +public: + + SparseExtractor(const DenseType& dense, const OpType& functor, + const ResultValueType background) : + mDense(dense), mFunctor(functor), + mBackground(background), + mBBox(dense.bbox()), + mWidth(ResultLeafNodeType::DIM), + mMask( new ResultTreeType(mBackground)) + {} + + + SparseExtractor(const DenseType& dense, + const openvdb::math::CoordBBox& bbox, + const OpType& functor, + const ResultValueType background) : + mDense(dense), mFunctor(functor), + mBackground(background), + mBBox(bbox), + mWidth(ResultLeafNodeType::DIM), + mMask( new ResultTreeType(mBackground)) + { + // mBBox must be inside the coordinate rage of the dense grid + if (!dense.bbox().isInside(mBBox)) { + OPENVDB_THROW(ValueError, "Data extraction window out of bound"); + } + } + + + SparseExtractor(SparseExtractor& other, tbb::split): + mDense(other.mDense), mFunctor(other.mFunctor), + mBackground(other.mBackground), mBBox(other.mBBox), + mWidth(other.mWidth), + mMask(new ResultTreeType(mBackground)), + mMin(other.mMin) + {} + + typename ResultTreeType::Ptr extract(bool threaded = true) { + + + // Construct 3D range of leaf nodes that + // intersect mBBox. + + // Snap the bbox to nearest leaf nodes min and max + + openvdb::math::Coord padded_min = mBBox.min(); + openvdb::math::Coord padded_max = mBBox.max(); + + + padded_min &= ~(mWidth - 1); + padded_max &= ~(mWidth - 1); + + padded_max[0] += mWidth - 1; + padded_max[1] += mWidth - 1; + padded_max[2] += mWidth - 1; + + + // number of leaf nodes in each direction + // division by leaf width, e.g. 8 in most cases + + const Index xleafCount = ( padded_max.x() - padded_min.x() + 1 ) / mWidth; + const Index yleafCount = ( padded_max.y() - padded_min.y() + 1 ) / mWidth; + const Index zleafCount = ( padded_max.z() - padded_min.z() + 1 ) / mWidth; + + mMin = padded_min; + + + Range3d leafRange(0, xleafCount, 1, + 0, yleafCount, 1, + 0, zleafCount, 1); + + + // Iterate over the leafnodes applying *this as a functor. + if (threaded) { + tbb::parallel_reduce(leafRange, *this); + } else { + (*this)(leafRange); + } + + return mMask; + } + + + void operator()(const Range3d& range) { + + ResultLeafNodeType* leaf = NULL; + + // Unpack the range3d item. + const Index imin = range.pages().begin(); + const Index imax = range.pages().end(); + + const Index jmin = range.rows().begin(); + const Index jmax = range.rows().end(); + + const Index kmin = range.cols().begin(); + const Index kmax = range.cols().end(); + + + // loop over all the canidate leafs. Adding only those with 'true' values + // to the tree + + for (Index i = imin; i < imax; ++i) { + for (Index j = jmin; j < jmax; ++j) { + for (Index k = kmin; k < kmax; ++k) { + + // Calculate the origin of canidate leaf + const openvdb::math::Coord origin = + mMin + openvdb::math::Coord(mWidth * i, + mWidth * j, + mWidth * k ); + + if (leaf == NULL) { + leaf = new ResultLeafNodeType(origin, mBackground); + } else { + leaf->setOrigin(origin); + leaf->fill(mBackground); + leaf->setValuesOff(); + } + + // The bouding box for this leaf + + openvdb::math::CoordBBox localBBox = leaf->getNodeBoundingBox(); + + // Shrink to the intersection with mBBox (i.e. the dense + // volume) + + localBBox.intersect(mBBox); + + // Early out for non-intersecting leafs + + if (localBBox.empty()) continue; + + + const openvdb::math::Coord start = localBBox.getStart(); + const openvdb::math::Coord end = localBBox.getEnd(); + + // Order the looping to respect the memory layout in + // the Dense source + + if (mDense.memoryLayout() == openvdb::tools::LayoutZYX) { + + openvdb::math::Coord ijk; + Index offset; + const DenseValueType* dp; + for (ijk[0] = start.x(); ijk[0] < end.x(); ++ijk[0] ) { + for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1] ) { + for (ijk[2] = start.z(), + offset = ResultLeafNodeType::coordToOffset(ijk), + dp = &mDense.getValue(ijk); + ijk[2] < end.z(); ++ijk[2], ++offset, ++dp) { + + mFunctor(*dp, offset, leaf); + } + } + } + + } else { + + openvdb::math::Coord ijk; + const DenseValueType* dp; + for (ijk[2] = start.z(); ijk[2] < end.z(); ++ijk[2]) { + for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1]) { + for (ijk[0] = start.x(), + dp = &mDense.getValue(ijk); + ijk[0] < end.x(); ++ijk[0], ++dp) { + + mFunctor(*dp, ijk, leaf); + + } + } + } + } + + // Only add non-empty leafs (empty is defined as all inactive) + + if (!leaf->isEmpty()) { + mMask->addLeaf(*leaf); + leaf = NULL; + } + + } + } + } + + // Clean up an unused leaf. + + if (leaf != NULL) delete leaf; + }; + + void join(SparseExtractor& rhs) { + mMask->merge(*rhs.mMask); + } +}; // class SparseExtractor + + +template<typename OpType, typename DenseType> +typename OpType::ResultTreeType::Ptr +extractSparseTree(const DenseType& dense, const OpType& functor, + const typename OpType::ResultValueType& background, + bool threaded) +{ + + // Construct the mask using a parallel reduce patern. + // Each thread computes disjoint mask-trees. The join merges + // into a single tree. + + SparseExtractor<OpType, DenseType> extractor(dense, functor, background); + + return extractor.extract(threaded); +} + + +/// @brief Functor-based class used to extract data from a dense grid, at +/// the index-space intersection with a suppiled maks in the form of a sparse tree. +/// The @c extractSparseTreeWithMask function wraps this class. +template <typename DenseType, typename MaskTreeType> +class SparseMaskedExtractor +{ +public: + + typedef typename DSConverter<DenseType, MaskTreeType>::Type _ResultTreeType; + typedef _ResultTreeType ResultTreeType; + typedef typename ResultTreeType::LeafNodeType ResultLeafNodeType; + typedef typename ResultTreeType::ValueType ResultValueType; + typedef ResultValueType DenseValueType; + + typedef typename ResultTreeType::template ValueConverter<bool>::Type BoolTree; + typedef typename BoolTree::LeafCIter BoolLeafCIter; + typedef std::vector<const typename BoolTree::LeafNodeType*> BoolLeafVec; + + + SparseMaskedExtractor(const DenseType& dense, + const ResultValueType& background, + const BoolLeafVec& leafVec + ): + mDense(dense), mBackground(background), mBBox(dense.bbox()), + mLeafVec(leafVec), + mResult(new ResultTreeType(mBackground)) + {} + + + + SparseMaskedExtractor(const SparseMaskedExtractor& other, tbb::split): + mDense(other.mDense), mBackground(other.mBackground), mBBox(other.mBBox), + mLeafVec(other.mLeafVec), mResult( new ResultTreeType(mBackground)) + {} + + typename ResultTreeType::Ptr extract(bool threaded = true) { + + tbb::blocked_range<size_t> range(0, mLeafVec.size()); + + if (threaded) { + tbb::parallel_reduce(range, *this); + } else { + (*this)(range); + } + + return mResult; + } + + + // Used in looping over leaf nodes in the masked grid + // and using the active mask to select data to + void operator()(const tbb::blocked_range<size_t>& range) { + + ResultLeafNodeType* leaf = NULL; + + + // loop over all the canidate leafs. Adding only those with 'true' values + // to the tree + + for (size_t idx = range.begin(); idx < range.end(); ++ idx) { + + const typename BoolTree::LeafNodeType* boolLeaf = mLeafVec[idx]; + + // The bouding box for this leaf + + openvdb::math::CoordBBox localBBox = boolLeaf->getNodeBoundingBox(); + + // Shrink to the intersection with the dense volume + + localBBox.intersect(mBBox); + + // Early out if there was no intersection + + if (localBBox.empty()) continue; + + // Reset or allocate the target leaf + + if (leaf == NULL) { + leaf = new ResultLeafNodeType(boolLeaf->origin(), mBackground); + } else { + leaf->setOrigin(boolLeaf->origin()); + leaf->fill(mBackground); + leaf->setValuesOff(); + } + + + // Iterate over the intersecting bounding box + // copying active values to the result tree + + const openvdb::math::Coord start = localBBox.getStart(); + const openvdb::math::Coord end = localBBox.getEnd(); + + + openvdb::math::Coord ijk; + + if (mDense.memoryLayout() == openvdb::tools::LayoutZYX + && boolLeaf->isDense()) { + + Index offset; + const DenseValueType* src; + for (ijk[0] = start.x(); ijk[0] < end.x(); ++ijk[0] ) { + for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1] ) { + for (ijk[2] = start.z(), + offset = ResultLeafNodeType::coordToOffset(ijk), + src = &mDense.getValue(ijk); + ijk[2] < end.z(); ++ijk[2], ++offset, ++src) { + + // copy into leaf + leaf->setValueOn(offset, *src); + } + + } + } + + } else { + + Index offset; + for (ijk[0] = start.x(); ijk[0] < end.x(); ++ijk[0] ) { + for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1] ) { + for (ijk[2] = start.z(), + offset = ResultLeafNodeType::coordToOffset(ijk); + ijk[2] < end.z(); ++ijk[2], ++offset) { + + if (boolLeaf->isValueOn(offset)) { + const ResultValueType denseValue = mDense.getValue(ijk); + leaf->setValueOn(offset, denseValue); + } + } + } + } + } + // Only add non-empty leafs (empty is defined as all inactive) + + if (!leaf->isEmpty()) { + mResult->addLeaf(*leaf); + leaf = NULL; + } + } + + // Clean up an unused leaf. + + if (leaf != NULL) delete leaf; + }; + + void join(SparseMaskedExtractor& rhs) { + mResult->merge(*rhs.mResult); + } + + +private: + const DenseType& mDense; + const ResultValueType mBackground; + const openvdb::math::CoordBBox& mBBox; + const BoolLeafVec& mLeafVec; + + typename ResultTreeType::Ptr mResult; + +}; // class SparseMaskedExtractor + + +/// @brief a simple utility class used by @c extractSparseTreeWithMask +template<typename _ResultTreeType, typename DenseValueType> +struct ExtractAll +{ + typedef _ResultTreeType ResultTreeType; + typedef typename ResultTreeType::LeafNodeType ResultLeafNodeType; + + template<typename CoordOrIndex> inline void + operator()(const DenseValueType& a, const CoordOrIndex& offset, ResultLeafNodeType* leaf) const + { + leaf->setValueOn(offset, a); + } +}; + + +template <typename DenseType, typename MaskTreeType> +typename DSConverter<DenseType, MaskTreeType>::Type::Ptr +extractSparseTreeWithMask(const DenseType& dense, + const MaskTreeType& mask, + const typename DenseType::ValueType& background, + bool threaded) +{ + typedef SparseMaskedExtractor<DenseType, MaskTreeType> LeafExtractor; + typedef typename LeafExtractor::DenseValueType DenseValueType; + typedef typename LeafExtractor::ResultTreeType ResultTreeType; + typedef typename LeafExtractor::BoolLeafVec BoolLeafVec; + typedef typename LeafExtractor::BoolTree BoolTree; + typedef typename LeafExtractor::BoolLeafCIter BoolLeafCIter; + typedef ExtractAll<ResultTreeType, DenseValueType> ExtractionRule; + + // Use Bool tree to hold the topology + + BoolTree boolTree(mask, false, TopologyCopy()); + + // Construct an array of pointers to the mask leafs. + + const size_t leafCount = boolTree.leafCount(); + BoolLeafVec leafarray(leafCount); + BoolLeafCIter leafiter = boolTree.cbeginLeaf(); + for (size_t n = 0; n != leafCount; ++n, ++leafiter) { + leafarray[n] = leafiter.getLeaf(); + } + + + // Extract the data that is masked leaf nodes in the mask. + + LeafExtractor leafextractor(dense, background, leafarray); + typename ResultTreeType::Ptr resultTree = leafextractor.extract(threaded); + + + // Extract data that is masked by tiles in the mask. + + + // Loop over the mask tiles, extracting the data into new trees. + // These trees will be leaf-orthogonal to the leafTree (i.e. no leaf + // nodes will overlap). Merge these trees into the result. + + typename MaskTreeType::ValueOnCIter tileIter(mask); + tileIter.setMaxDepth(MaskTreeType::ValueOnCIter::LEAF_DEPTH - 1); + + // Return the leaf tree if the mask had no tiles + + if (!tileIter) return resultTree; + + ExtractionRule allrule; + + // Loop over the tiles in series, but the actual data extraction + // is in parallel. + + CoordBBox bbox; + for ( ; tileIter; ++tileIter) { + + // Find the intersection of the tile with the dense grid. + + tileIter.getBoundingBox(bbox); + bbox.intersect(dense.bbox()); + + if (bbox.empty()) continue; + + SparseExtractor<ExtractionRule, DenseType> copyData(dense, bbox, allrule, background); + typename ResultTreeType::Ptr fromTileTree = copyData.extract(threaded); + resultTree->merge(*fromTileTree); + } + + return resultTree; +} + + +/// @brief Class that applies a functor to the index space intersection +/// of a prescribed bounding box and the dense grid. +/// NB: This class only supports DenseGrids with ZYX memory layout. +template <typename _ValueT, typename OpType> +class DenseTransformer +{ +public: + + typedef _ValueT ValueT; + typedef Dense<ValueT, openvdb::tools::LayoutZYX> DenseT; + typedef openvdb::math::Coord::ValueType IntType; + typedef tbb::blocked_range2d<IntType, IntType> RangeType; + + +private: + + DenseT& mDense; + const OpType& mOp; + openvdb::math::CoordBBox mBBox; + +public: + DenseTransformer(DenseT& dense, + const openvdb::math::CoordBBox& bbox, + const OpType& functor): + mDense(dense), mOp(functor), mBBox(dense.bbox()) + { + // The interation space is the intersection of the + // input bbox and the index-space covered by the dense grid + mBBox.intersect(bbox); + } + + DenseTransformer(const DenseTransformer& other) : + mDense(other.mDense), mOp(other.mOp), mBBox(other.mBBox) {} + + void apply(bool threaded = true) { + + // Early out if the interation space is empty + + if (mBBox.empty()) return; + + + const openvdb::math::Coord start = mBBox.getStart(); + const openvdb::math::Coord end = mBBox.getEnd(); + + // The interation range only the slower two directions. + const RangeType range(start.x(), end.x(), 1, + start.y(), end.y(), 1); + + if (threaded) { + tbb::parallel_for(range, *this); + } else { + (*this)(range); + } + } + + void operator()(const RangeType& range) const { + + // The stride in the z-direction. + // Note: the bbox is [inclusive, inclusive] + + const size_t zlength = size_t(mBBox.max().z() - mBBox.min().z() + 1); + + const IntType imin = range.rows().begin(); + const IntType imax = range.rows().end(); + const IntType jmin = range.cols().begin(); + const IntType jmax = range.cols().end(); + + + openvdb::math::Coord xyz(imin, jmin, mBBox.min().z()); + for (xyz[0] = imin; xyz[0] != imax; ++xyz[0]) { + for (xyz[1] = jmin; xyz[1] != jmax; ++xyz[1]) { + + mOp.transform(mDense, xyz, zlength); + } + } + } +}; // class DenseTransformer + + +/// @brief a wrapper struct used to avoid unnecessary computation of +/// memory access from @c Coord when all offsets are guaranteed to be +/// within the dense grid. +template <typename ValueT, typename PointWiseOp> +struct ContiguousOp +{ + ContiguousOp(const PointWiseOp& op) : mOp(op){}; + + typedef Dense<ValueT, openvdb::tools::LayoutZYX> DenseT; + inline void transform(DenseT& dense, openvdb::math::Coord& ijk, size_t size) const + { + ValueT* dp = const_cast<ValueT*>(&dense.getValue(ijk)); + + for (size_t offset = 0; offset < size; ++offset) { + dp[offset] = mOp(dp[offset]); + } + } + + const PointWiseOp mOp; +}; + + +/// Apply a point-wise functor to the intersection of a dense grid and a given bounding box +template <typename ValueT, typename PointwiseOpT> +void +transformDense(Dense<ValueT, openvdb::tools::LayoutZYX>& dense, + const openvdb::CoordBBox& bbox, + const PointwiseOpT& functor, bool parallel) +{ + typedef ContiguousOp<ValueT, PointwiseOpT> OpT; + + // Convert the Op so it operates on an contiguous line in memory + + OpT op(functor); + + // Apply to the index space intersection in the dense grid + DenseTransformer<ValueT, OpT> transformer(dense, bbox, op); + transformer.apply(parallel); +} + + +template <typename CompositeMethod, typename _TreeT> +class SparseToDenseCompositor +{ + +public: + typedef _TreeT TreeT; + typedef typename TreeT::ValueType ValueT; + typedef typename TreeT::LeafNodeType LeafT; + typedef typename TreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef typename BoolTreeT::LeafNodeType BoolLeafT; + typedef Dense<ValueT, openvdb::tools::LayoutZYX> DenseT; + typedef openvdb::math::Coord::ValueType Index; + typedef tbb::blocked_range3d<Index, Index, Index> Range3d; + + SparseToDenseCompositor(DenseT& dense, const TreeT& source, const TreeT& alpha, + const ValueT beta, const ValueT strength) : + mDense(dense), mSource(source), mAlpha(alpha), mBeta(beta), mStrength(strength) + {} + + SparseToDenseCompositor(const SparseToDenseCompositor& other): + mDense(other.mDense), mSource(other.mSource), mAlpha(other.mAlpha), + mBeta(other.mBeta), mStrength(other.mStrength) {}; + + + + void sparseComposite(bool threaded) { + + const ValueT beta = mBeta; + const ValueT strenght = mStrength; + + // construct a tree that defines the iteration space + + BoolTreeT boolTree(mSource, false /*background*/, openvdb::TopologyCopy()); + boolTree.topologyUnion(mAlpha); + + // Coposite regions that are represented by leafnodes in either mAlpha or mSource + // Parallelize over bool-leafs + + openvdb::tree::LeafManager<const BoolTreeT> boolLeafs(boolTree); + boolLeafs.foreach(*this, threaded); + + // Composite tregions that are represnted by tiles + // Parallelize within each tile. + + typename BoolTreeT::ValueOnCIter citer = boolTree.cbeginValueOn(); + citer.setMaxDepth(BoolTree::ValueOnCIter::LEAF_DEPTH - 1); + + if (!citer) return; + + typename tree::ValueAccessor<const TreeT> alphaAccessor(mAlpha); + typename tree::ValueAccessor<const TreeT> sourceAccessor(mSource); + + for (; citer; ++citer) { + + const openvdb::math::Coord org = citer.getCoord(); + + // Early out if both alpha and source are zero in this tile. + + const ValueT alphaValue = alphaAccessor.getValue(org); + const ValueT sourceValue = sourceAccessor.getValue(org); + + if (openvdb::math::isZero(alphaValue) && + openvdb::math::isZero(sourceValue) ) continue; + + // Compute overlap of tile with the dense grid + + openvdb::math::CoordBBox localBBox = citer.getBoundingBox(); + localBBox.intersect(mDense.bbox()); + + // Early out if there is no intersection + + if (localBBox.empty()) continue; + + // Composite the tile-uniform values into the dense grid. + compositeFromTile(mDense, localBBox, sourceValue, + alphaValue, beta, strenght, threaded); + } + } + + // Composites leaf values where the alpha values are active. + // Used in sparseComposite + void inline operator()(const BoolLeafT& boolLeaf, size_t /*i*/) const + { + + typedef UniformLeaf ULeaf; + openvdb::math::CoordBBox localBBox = boolLeaf.getNodeBoundingBox(); + localBBox.intersect(mDense.bbox()); + + // Early out for non-overlapping leafs + + if (localBBox.empty()) return; + + const openvdb::math::Coord org = boolLeaf.origin(); + const LeafT* alphaLeaf = mAlpha.probeLeaf(org); + const LeafT* sourceLeaf = mSource.probeLeaf(org); + + if (!sourceLeaf) { + + // Create a source leaf proxy with the correct value + ULeaf uniformSource(mSource.getValue(org)); + + if (!alphaLeaf) { + + // Create an alpha leaf proxy with the correct value + ULeaf uniformAlpha(mAlpha.getValue(org)); + + compositeFromLeaf(mDense, localBBox, uniformSource, uniformAlpha, + mBeta, mStrength); + } else { + + compositeFromLeaf(mDense, localBBox, uniformSource, *alphaLeaf, + mBeta, mStrength); + } + } else { + if (!alphaLeaf) { + + // Create an alpha leaf proxy with the correct value + ULeaf uniformAlpha(mAlpha.getValue(org)); + + compositeFromLeaf(mDense, localBBox, *sourceLeaf, uniformAlpha, + mBeta, mStrength); + } else { + + compositeFromLeaf(mDense, localBBox, *sourceLeaf, *alphaLeaf, + mBeta, mStrength); + } + } + } + // i.e. it assumes that all valueOff Alpha voxels have value 0. + + template <typename LeafT1, typename LeafT2> + inline static void compositeFromLeaf(DenseT& dense, const openvdb::math::CoordBBox& bbox, + const LeafT1& source, const LeafT2& alpha, + const ValueT beta, const ValueT strength) + { + typedef openvdb::math::Coord::ValueType IntType; + + const ValueT sbeta = strength * beta; + openvdb::math::Coord ijk = bbox.min(); + + + if (alpha.isDense() /*all active values*/) { + + // Optial path for dense alphaLeaf + const IntType size = bbox.max().z() + 1 - bbox.min().z(); + + for (ijk[0] = bbox.min().x(); ijk[0] < bbox.max().x() + 1; ++ijk[0]) { + for (ijk[1] = bbox.min().y(); ijk[1] < bbox.max().y() + 1; ++ijk[1]) { + + ValueT* d = const_cast<ValueT*>(&dense.getValue(ijk)); + const ValueT* a = &alpha.getValue(ijk); + const ValueT* s = &source.getValue(ijk); + + for (IntType idx = 0; idx < size; ++idx) { + d[idx] = CompositeMethod::apply(d[idx], a[idx], s[idx], + strength, beta, sbeta); + } + } + } + } else { + + // AlphaLeaf has non-active cells. + + for (ijk[0] = bbox.min().x(); ijk[0] < bbox.max().x() + 1; ++ijk[0]) { + for (ijk[1] = bbox.min().y(); ijk[1] < bbox.max().y() + 1; ++ijk[1]) { + for (ijk[2] = bbox.min().z(); ijk[2] < bbox.max().z() + 1; ++ijk[2]) { + + if (alpha.isValueOn(ijk)) { + + dense.setValue(ijk, + CompositeMethod::apply(dense.getValue(ijk), + alpha.getValue(ijk), source.getValue(ijk), + strength, beta, sbeta) + ); + } + } + } + } + } + } + + inline static void compositeFromTile(DenseT& dense, openvdb::math::CoordBBox& bbox, + const ValueT& sourceValue, const ValueT& alphaValue, + const ValueT& beta, const ValueT& strength, + bool threaded) + { + + typedef UniformTransformer TileTransformer; + TileTransformer functor(sourceValue, alphaValue, beta, strength); + + // Transform the data inside the bbox according to the TileTranformer. + + transformDense(dense, bbox, functor, threaded); + + } + + + void denseComposite(bool threaded) + { + /// Construct a range that corresponds to the + /// bounding box of the dense volume + const openvdb::math::CoordBBox& bbox = mDense.bbox(); + + Range3d range(bbox.min().x(), bbox.max().x(), LeafT::DIM, + bbox.min().y(), bbox.max().y(), LeafT::DIM, + bbox.min().z(), bbox.max().z(), LeafT::DIM); + + // Interate over the range, compositing into + // the dense grid using value accessors for + // sparse the grids. + if (threaded) { + tbb::parallel_for(range, *this); + } else { + (*this)(range); + } + + } + + // Composites a dense region using value accessors + // into a dense grid + void inline operator()(const Range3d& range) const + { + // Use value accessors to alpha and source + + typename tree::ValueAccessor<const TreeT> alphaAccessor(mAlpha); + typename tree::ValueAccessor<const TreeT> sourceAccessor(mSource); + + const ValueT strength = mStrength; + const ValueT beta = mBeta; + const ValueT sbeta = strength * beta; + + // Unpack the range3d item. + const Index imin = range.pages().begin(); + const Index imax = range.pages().end(); + + const Index jmin = range.rows().begin(); + const Index jmax = range.rows().end(); + + const Index kmin = range.cols().begin(); + const Index kmax = range.cols().end(); + + openvdb::Coord ijk; + for (ijk[0] = imin; ijk[0] < imax; ++ijk[0]) { + for (ijk[1] = jmin; ijk[1] < jmax; ++ijk[1]) { + for (ijk[2] = kmin; ijk[2] < kmax; ++ijk[2]) { + const ValueT d_old = mDense.getValue(ijk); + const ValueT& alpha = alphaAccessor.getValue(ijk); + const ValueT& src = sourceAccessor.getValue(ijk); + + mDense.setValue(ijk, CompositeMethod::apply(d_old, alpha, src, + strength, beta, sbeta)); + } + } + } + + } + + +private: + + // Internal class that wraps the templated composite method + // for use when both alpha and source are uniform over + // a prescribed bbox (e.g. a tile). + class UniformTransformer + { + public: + UniformTransformer(const ValueT& source, const ValueT& alpha, const ValueT& _beta, + const ValueT& _strength) : + mSource(source), mAlpha(alpha), mBeta(_beta), + mStrength(_strength), mSBeta(_strength * _beta) + {} + + ValueT operator()(const ValueT& input) const + { + return CompositeMethod::apply(input, mAlpha, mSource, + mStrength, mBeta, mSBeta); + } + + private: + const ValueT mSource; const ValueT mAlpha; const ValueT mBeta; + const ValueT mStrength; const ValueT mSBeta; + }; + + + // Simple Class structure that mimics a leaf + // with uniform values. Holds LeafT::DIM copies + // of a value in an array. + struct Line { ValueT mValues[LeafT::DIM]; }; + class UniformLeaf : private Line + { + public: + typedef typename LeafT::ValueType ValueT; + + typedef Line BaseT; + UniformLeaf(const ValueT& value) : BaseT(init(value)){}; + + static const BaseT init(const ValueT& value) { + BaseT tmp; + for (openvdb::Index i = 0; i < LeafT::DIM; ++i) { + tmp.mValues[i] = value; + } + return tmp; + } + + bool isDense() const { return true; } + bool isValueOn(openvdb::math::Coord&) const { return true; } + + inline const ValueT& getValue(const openvdb::math::Coord& ) const + {return BaseT::mValues[0];} + }; + +private: + DenseT& mDense; + const TreeT& mSource; + const TreeT& mAlpha; + ValueT mBeta; + ValueT mStrength; +}; // class SparseToDenseCompositor + + +namespace ds +{ + //@{ + /// @brief Point wise methods used to apply various compositing operations. + template <typename ValueT> + struct OpOver + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT strength, + const ValueT beta, + const ValueT /*sbeta*/) + { return (u + strength * alpha * (beta * v - u)); } + }; + + + template <typename ValueT> + struct OpAdd + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT /*strength*/, + const ValueT /*beta*/, + const ValueT sbeta) + { return (u + sbeta * alpha * v); } + }; + + template <typename ValueT> + struct OpSub + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT /*strength*/, + const ValueT /*beta*/, + const ValueT sbeta) + { return (u - sbeta * alpha * v); } + }; + + template <typename ValueT> + struct OpMin + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT s /*trength*/, + const ValueT beta, + const ValueT /*sbeta*/) + { return ( ( 1 - s * alpha) * u + s * alpha * std::min(u, beta * v) ); } + }; + + + template <typename ValueT> + struct OpMax + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT s/*trength*/, + const ValueT beta, + const ValueT /*sbeta*/) + { return ( ( 1 - s * alpha ) * u + s * alpha * std::min(u, beta * v) ); } + }; + + template <typename ValueT> + struct OpMult + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT s/*trength*/, + const ValueT /*beta*/, + const ValueT sbeta) + { return ( ( 1 + alpha * (sbeta * v - s)) * u ); } + }; + //@} + + //@{ + /// Translator that converts an enum to compositing functor types + template <DSCompositeOp OP, typename ValueT> + struct CompositeFunctorTranslator{}; + + template <typename ValueT> + struct CompositeFunctorTranslator<DS_OVER, ValueT>{ typedef OpOver<ValueT> OpT; }; + + template <typename ValueT> + struct CompositeFunctorTranslator<DS_ADD, ValueT>{ typedef OpAdd<ValueT> OpT; }; + + template <typename ValueT> + struct CompositeFunctorTranslator<DS_SUB, ValueT>{ typedef OpSub<ValueT> OpT; }; + + template <typename ValueT> + struct CompositeFunctorTranslator<DS_MIN, ValueT>{ typedef OpMin<ValueT> OpT; }; + + template <typename ValueT> + struct CompositeFunctorTranslator<DS_MAX, ValueT>{ typedef OpMax<ValueT> OpT; }; + + template <typename ValueT> + struct CompositeFunctorTranslator<DS_MULT, ValueT>{ typedef OpMult<ValueT> OpT; }; + //@} + +} // namespace ds + + +template <DSCompositeOp OpT, typename TreeT> +void compositeToDense( + Dense<typename TreeT::ValueType, LayoutZYX>& dense, + const TreeT& source, const TreeT& alpha, + const typename TreeT::ValueType beta, + const typename TreeT::ValueType strength, + bool threaded) +{ + typedef typename TreeT::ValueType ValueT; + typedef ds::CompositeFunctorTranslator<OpT, ValueT> Translator; + typedef typename Translator::OpT Method; + + if (openvdb::math::isZero(strength)) return; + + SparseToDenseCompositor<Method, TreeT> tool(dense, source, alpha, beta, strength); + + if (openvdb::math::isZero(alpha.background()) && + openvdb::math::isZero(source.background())) + { + // Use the sparsity of (alpha U source) as the iteration space. + tool.sparseComposite(threaded); + } else { + // Use the bounding box of dense as the iteration space. + tool.denseComposite(threaded); + } +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif //OPENVDB_TOOLS_DENSESPARSETOOLS_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/extern/openvdb/internal/openvdb/tools/Filter.h b/extern/openvdb/internal/openvdb/tools/Filter.h index f36145946a1..268148f6655 100644 --- a/extern/openvdb/internal/openvdb/tools/Filter.h +++ b/extern/openvdb/internal/openvdb/tools/Filter.h @@ -31,6 +31,10 @@ /// @author Ken Museth /// /// @file Filter.h +/// +/// @brief Filtering of VDB volumes. Note that only the values in the +/// grid are changed, not its topology! All operations can optionally +/// be masked with another grid that acts as an alpha-mask. #ifndef OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED #define OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED @@ -39,7 +43,7 @@ #include <tbb/parallel_for.h> #include <boost/bind.hpp> #include <boost/function.hpp> -#include <boost/scoped_ptr.hpp> +#include <boost/type_traits/is_floating_point.hpp> #include <openvdb/Types.h> #include <openvdb/math/Math.h> #include <openvdb/math/Stencils.h> @@ -47,122 +51,253 @@ #include <openvdb/tree/LeafManager.h> #include <openvdb/util/NullInterrupter.h> #include <openvdb/Grid.h> +#include "Interpolation.h" namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { namespace tools { -/// @brief Filtering of VDB volumes -template<typename GridT, typename InterruptT = util::NullInterrupter> +/// @brief Volume filtering (e.g., diffusion) with optional alpha masking +/// +/// @note Only the values in the grid are changed, not its topology! +template<typename GridT, + typename MaskT = typename GridT::template ValueConverter<float>::Type, + typename InterruptT = util::NullInterrupter> class Filter { public: - typedef GridT GridType; - typedef typename GridType::TreeType TreeType; - typedef typename TreeType::LeafNodeType LeafType; - typedef typename LeafType::ValueType ValueType; - typedef typename tree::LeafManager<TreeType> LeafManagerType; - typedef typename LeafManagerType::LeafRange RangeType; - typedef typename LeafManagerType::BufferType BufferType; + typedef GridT GridType; + typedef MaskT MaskType; + typedef typename GridType::TreeType TreeType; + typedef typename TreeType::LeafNodeType LeafType; + typedef typename GridType::ValueType ValueType; + typedef typename MaskType::ValueType AlphaType; + typedef typename tree::LeafManager<TreeType> LeafManagerType; + typedef typename LeafManagerType::LeafRange RangeType; + typedef typename LeafManagerType::BufferType BufferType; + BOOST_STATIC_ASSERT(boost::is_floating_point<AlphaType>::value); /// Constructor - Filter(GridT& grid, InterruptT* interrupt = NULL) : - mGrid(grid), mTask(0), mInterrupter(interrupt) + /// @param grid Grid to be filtered. + /// @param interrupt Optional interrupter. + Filter(GridT& grid, InterruptT* interrupt = NULL) + : mGrid(&grid) + , mTask(0) + , mInterrupter(interrupt) + , mMask(NULL) + , mGrainSize(1) + , mMinMask(0) + , mMaxMask(1) + , mInvertMask(false) + { + } + + /// @brief Shallow copy constructor called by tbb::parallel_for() + /// threads during filtering. + /// @param other The other Filter from which to copy. + Filter(const Filter& other) + : mGrid(other.mGrid) + , mTask(other.mTask) + , mInterrupter(other.mInterrupter) + , mMask(other.mMask) + , mGrainSize(other.mGrainSize) + , mMinMask(other.mMinMask) + , mMaxMask(other.mMaxMask) + , mInvertMask(other.mInvertMask) + { + } + + /// @return the grain-size used for multi-threading + int getGrainSize() const { return mGrainSize; } + /// @brief Set the grain-size used for multi-threading. + /// @note A grainsize of 0 or less disables multi-threading! + void setGrainSize(int grainsize) { mGrainSize = grainsize; } + + /// @brief Return the minimum value of the mask to be used for the + /// derivation of a smooth alpha value. + AlphaType minMask() const { return mMinMask; } + /// @brief Return the maximum value of the mask to be used for the + /// derivation of a smooth alpha value. + AlphaType maxMask() const { return mMaxMask; } + /// @brief Define the range for the (optional) scalar mask. + /// @param min Minimum value of the range. + /// @param max Maximum value of the range. + /// @details Mask values outside the range are clamped to zero or one, and + /// values inside the range map smoothly to 0->1 (unless the mask is inverted). + /// @throw ValueError if @a min is not smaller then @a max. + void setMaskRange(AlphaType min, AlphaType max) { + if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)"); + mMinMask = min; + mMaxMask = max; } + /// @brief Return true if the mask is inverted, i.e. min->max in the + /// original mask maps to 1->0 in the inverted alpha mask. + bool isMaskInverted() const { return mInvertMask; } + /// @brief Invert the optional mask, i.e. min->max in the original + /// mask maps to 1->0 in the inverted alpha mask. + void invertMask(bool invert=true) { mInvertMask = invert; } + /// @brief One iteration of a fast separable mean-value (i.e. box) filter. - void mean(int width = 1, int iterations = 1, bool serial = false); + /// @param width The width of the mean-value filter is 2*width+1 voxels. + /// @param iterations Number of times the mean-value filter is applied. + /// @param mask Optional alpha mask. + void mean(int width = 1, int iterations = 1, const MaskType* mask = NULL); /// @brief One iteration of a fast separable gaussian filter. /// /// @note This is approximated as 4 iterations of a separable mean filter /// which typically leads an approximation that's better than 95%! - void gaussian(int width = 1, int iterations = 1, bool serial = false); + /// @param width The width of the mean-value filter is 2*width+1 voxels. + /// @param iterations Numer of times the mean-value filter is applied. + /// @param mask Optional alpha mask. + void gaussian(int width = 1, int iterations = 1, const MaskType* mask = NULL); /// @brief One iteration of a median-value filter /// /// @note This filter is not separable and is hence relatively slow! - void median(int width = 1, int iterations = 1, bool serial = false); + /// @param width The width of the mean-value filter is 2*width+1 voxels. + /// @param iterations Numer of times the mean-value filter is applied. + /// @param mask Optional alpha mask. + void median(int width = 1, int iterations = 1, const MaskType* mask = NULL); /// Offsets (i.e. adds) a constant value to all active voxels. - void offset(float offset, bool serial = false); + /// @param offset Offset in the same units as the grid. + /// @param mask Optional alpha mask. + void offset(ValueType offset, const MaskType* mask = NULL); /// @brief Used internally by tbb::parallel_for() + /// @param range Range of LeafNodes over which to multi-thread. /// - /// @note Never call this method directly! - void operator()(const RangeType& r) const + /// @warning Never call this method directly! + void operator()(const RangeType& range) const { - if (mTask) mTask(const_cast<Filter*>(this), r); + if (mTask) mTask(const_cast<Filter*>(this), range); else OPENVDB_THROW(ValueError, "task is undefined - call median(), mean(), etc."); } private: - typedef typename boost::function<void (Filter*, const RangeType&)> FuncType; + typedef typename TreeType::LeafNodeType LeafT; + typedef typename LeafT::ValueOnIter VoxelIterT; + typedef typename LeafT::ValueOnCIter VoxelCIterT; + typedef typename tree::LeafManager<TreeType>::BufferType BufferT; + typedef typename RangeType::Iterator LeafIterT; - void cook(bool serial, LeafManagerType& leafs); + void cook(LeafManagerType& leafs); + + // Private class to derive the normalized alpha mask + struct AlphaMask + { + AlphaMask(const GridType& grid, const MaskType& mask, + AlphaType min, AlphaType max, bool invert) + : mAcc(mask.tree()), mSampler(mAcc, mask.transform(), grid.transform()), + mMin(min), mInvNorm(1/(max-min)), mInvert(invert) + { + assert(min < max); + } + inline bool operator()(const Coord& xyz, AlphaType& a, AlphaType& b) const + { + a = mSampler(xyz); + const AlphaType t = (a-mMin)*mInvNorm; + a = t > 0 ? t < 1 ? (3-2*t)*t*t : 1 : 0;//smooth mapping to 0->1 + b = 1 - a; + if (mInvert) std::swap(a,b); + return a>0; + } + typedef typename MaskType::ConstAccessor AccType; + AccType mAcc; + tools::DualGridSampler<AccType, tools::BoxSampler> mSampler; + const AlphaType mMin, mInvNorm; + const bool mInvert; + }; + + template <size_t Axis> + struct Avg { + Avg(const GridT* grid, Int32 w) : + acc(grid->tree()), width(w), frac(1/ValueType(2*w+1)) {} + ValueType operator()(Coord xyz) { + ValueType sum = zeroVal<ValueType>(); + Int32& i = xyz[Axis], j = i + width; + for (i -= width; i <= j; ++i) sum += acc.getValue(xyz); + return sum*frac; + } + typename GridT::ConstAccessor acc; + const Int32 width; + const ValueType frac; + }; // Private filter methods called by tbb::parallel_for threads - void doBoxX(const RangeType&, Int32); - void doBoxY(const RangeType&, Int32); - void doBoxZ(const RangeType&, Int32); + template <typename AvgT> + void doBox( const RangeType& r, Int32 w); + void doBoxX(const RangeType& r, Int32 w) { this->doBox<Avg<0> >(r,w); } + void doBoxZ(const RangeType& r, Int32 w) { this->doBox<Avg<1> >(r,w); } + void doBoxY(const RangeType& r, Int32 w) { this->doBox<Avg<2> >(r,w); } void doMedian(const RangeType&, int); - void doOffset(const RangeType&, float); + void doOffset(const RangeType&, ValueType); /// @return true if the process was interrupted bool wasInterrupted(); - GridType& mGrid; - FuncType mTask; - InterruptT* mInterrupter; + GridType* mGrid; + typename boost::function<void (Filter*, const RangeType&)> mTask; + InterruptT* mInterrupter; + const MaskType* mMask; + int mGrainSize; + AlphaType mMinMask, mMaxMask; + bool mInvertMask; }; // end of Filter class //////////////////////////////////////// -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline void -Filter<GridT, InterruptT>::mean(int width, int iterations, bool serial) +Filter<GridT, MaskT, InterruptT>::mean(int width, int iterations, const MaskType* mask) { + mMask = mask; + if (mInterrupter) mInterrupter->start("Applying mean filter"); const int w = std::max(1, width); - LeafManagerType leafs(mGrid.tree(), 1, serial); + LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0); for (int i=0; i<iterations && !this->wasInterrupted(); ++i) { mTask = boost::bind(&Filter::doBoxX, _1, _2, w); - this->cook(serial, leafs); + this->cook(leafs); mTask = boost::bind(&Filter::doBoxY, _1, _2, w); - this->cook(serial, leafs); + this->cook(leafs); mTask = boost::bind(&Filter::doBoxZ, _1, _2, w); - this->cook(serial, leafs); + this->cook(leafs); } if (mInterrupter) mInterrupter->end(); } -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline void -Filter<GridT, InterruptT>::gaussian(int width, int iterations, bool serial) +Filter<GridT, MaskT, InterruptT>::gaussian(int width, int iterations, const MaskType* mask) { + mMask = mask; + if (mInterrupter) mInterrupter->start("Applying gaussian filter"); const int w = std::max(1, width); - LeafManagerType leafs(mGrid.tree(), 1, serial); + LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0); for (int i=0; i<iterations; ++i) { for (int n=0; n<4 && !this->wasInterrupted(); ++n) { mTask = boost::bind(&Filter::doBoxX, _1, _2, w); - this->cook(serial, leafs); + this->cook(leafs); mTask = boost::bind(&Filter::doBoxY, _1, _2, w); - this->cook(serial, leafs); + this->cook(leafs); mTask = boost::bind(&Filter::doBoxZ, _1, _2, w); - this->cook(serial, leafs); + this->cook(leafs); } } @@ -170,30 +305,34 @@ Filter<GridT, InterruptT>::gaussian(int width, int iterations, bool serial) } -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline void -Filter<GridT, InterruptT>::median(int width, int iterations, bool serial) +Filter<GridT, MaskT, InterruptT>::median(int width, int iterations, const MaskType* mask) { + mMask = mask; + if (mInterrupter) mInterrupter->start("Applying median filter"); - LeafManagerType leafs(mGrid.tree(), 1, serial); + LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0); mTask = boost::bind(&Filter::doMedian, _1, _2, std::max(1, width)); - for (int i=0; i<iterations && !this->wasInterrupted(); ++i) this->cook(serial, leafs); + for (int i=0; i<iterations && !this->wasInterrupted(); ++i) this->cook(leafs); if (mInterrupter) mInterrupter->end(); } -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline void -Filter<GridT, InterruptT>::offset(float value, bool serial) +Filter<GridT, MaskT, InterruptT>::offset(ValueType value, const MaskType* mask) { + mMask = mask; + if (mInterrupter) mInterrupter->start("Applying offset"); - LeafManagerType leafs(mGrid.tree(), 0, serial); + LeafManagerType leafs(mGrid->tree(), 0, mGrainSize==0); mTask = boost::bind(&Filter::doOffset, _1, _2, value); - this->cook(serial, leafs); + this->cook(leafs); if (mInterrupter) mInterrupter->end(); } @@ -203,109 +342,104 @@ Filter<GridT, InterruptT>::offset(float value, bool serial) /// Private method to perform the task (serial or threaded) and /// subsequently swap the leaf buffers. -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline void -Filter<GridT, InterruptT>::cook(bool serial, LeafManagerType& leafs) +Filter<GridT, MaskT, InterruptT>::cook(LeafManagerType& leafs) { - if (serial) { - (*this)(leafs.leafRange()); + if (mGrainSize>0) { + tbb::parallel_for(leafs.leafRange(mGrainSize), *this); } else { - tbb::parallel_for(leafs.leafRange(), *this); + (*this)(leafs.leafRange()); } - leafs.swapLeafBuffer(1, serial); + leafs.swapLeafBuffer(1, mGrainSize==0); } -/// X convolution of a separable box filter -template<typename GridT, typename InterruptT> +/// One dimensional convolution of a separable box filter +template<typename GridT, typename MaskT, typename InterruptT> +template <typename AvgT> inline void -Filter<GridT, InterruptT>::doBoxX(const RangeType& range, Int32 w) +Filter<GridT, MaskT, InterruptT>::doBox(const RangeType& range, Int32 w) { this->wasInterrupted(); - const ValueType frac = ValueType(1)/ValueType(2*w+1); - typename GridT::ConstAccessor acc = mGrid.getConstAccessor(); - for (typename RangeType::Iterator lIter=range.begin(); lIter; ++lIter) { - BufferType& buffer = lIter.buffer(1); - for (typename LeafType::ValueOnCIter vIter = lIter->cbeginValueOn(); vIter; ++vIter) { - ValueType sum = zeroVal<ValueType>(); - math::Coord xyz = vIter.getCoord(); - for (Int32 x = xyz.x()-w, xLast = xyz.x()+w; x <= xLast; ++x) { - sum += acc.getValue(xyz.setX(x)); + AvgT avg(mGrid, w); + if (mMask) { + AlphaType a, b; + AlphaMask alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + const Coord xyz = iter.getCoord(); + if (alpha(xyz, a, b)) { + buffer.setValue(iter.pos(), ValueType(b*(*iter) + a*avg(xyz))); + } } - buffer.setValue(vIter.pos(), sum*frac); } - } -} - -/// Y convolution of a separable box filter -template<typename GridT, typename InterruptT> -inline void -Filter<GridT, InterruptT>::doBoxY(const RangeType& range, Int32 w) -{ - this->wasInterrupted(); - const ValueType frac = ValueType(1)/ValueType(2*w+1); - typename GridT::ConstAccessor acc = mGrid.getConstAccessor(); - for (typename RangeType::Iterator lIter=range.begin(); lIter; ++lIter) { - BufferType& buffer = lIter.buffer(1); - for (typename LeafType::ValueOnCIter vIter = lIter->cbeginValueOn(); vIter; ++vIter) { - ValueType sum = zeroVal<ValueType>(); - math::Coord xyz = vIter.getCoord(); - for (Int32 y = xyz.y()-w, yLast = xyz.y()+w; y <= yLast; ++y) { - sum += acc.getValue(xyz.setY(y)); + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + buffer.setValue(iter.pos(), avg(iter.getCoord())); } - buffer.setValue(vIter.pos(), sum*frac); } } } -/// Z convolution of a separable box filter -template<typename GridT, typename InterruptT> +/// Performs simple but slow median-value diffusion +template<typename GridT, typename MaskT, typename InterruptT> inline void -Filter<GridT, InterruptT>::doBoxZ(const RangeType& range, Int32 w) +Filter<GridT, MaskT, InterruptT>::doMedian(const RangeType& range, int width) { this->wasInterrupted(); - const ValueType frac = ValueType(1)/ValueType(2*w+1); - typename GridT::ConstAccessor acc = mGrid.getConstAccessor(); - for (typename RangeType::Iterator lIter=range.begin(); lIter; ++lIter) { - BufferType& buffer = lIter.buffer(1); - for (typename LeafType::ValueOnCIter vIter = lIter->cbeginValueOn(); vIter; ++vIter) { - ValueType sum = zeroVal<ValueType>(); - math::Coord xyz = vIter.getCoord(); - for (Int32 z = xyz.z()-w, zLast = xyz.z()+w; z <= zLast; ++z) { - sum += acc.getValue(xyz.setZ(z)); + typename math::DenseStencil<GridType> stencil(*mGrid, width);//creates local cache! + if (mMask) { + AlphaType a, b; + AlphaMask alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) { + stencil.moveTo(iter); + buffer.setValue(iter.pos(), ValueType(b*(*iter) + a*stencil.median())); + } + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + stencil.moveTo(iter); + buffer.setValue(iter.pos(), stencil.median()); } - buffer.setValue(vIter.pos(), sum*frac); } } } -/// Performs simple but slow median-value diffusion -template<typename GridT, typename InterruptT> +/// Offsets the values by a constant +template<typename GridT, typename MaskT, typename InterruptT> inline void -Filter<GridT, InterruptT>::doMedian(const RangeType& range, int width) +Filter<GridT, MaskT, InterruptT>::doOffset(const RangeType& range, ValueType offset) { this->wasInterrupted(); - typename math::DenseStencil<GridType> stencil(mGrid, width);//creates local cache! - for (typename RangeType::Iterator lIter=range.begin(); lIter; ++lIter) { - BufferType& buffer = lIter.buffer(1); - for (typename LeafType::ValueOnCIter vIter = lIter->cbeginValueOn(); vIter; ++vIter) { - stencil.moveTo(vIter); - buffer.setValue(vIter.pos(), stencil.median()); + if (mMask) { + AlphaType a, b; + AlphaMask alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) iter.setValue(ValueType(*iter + a*offset)); + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) { + iter.setValue(*iter + offset); + } } } } -/// Offsets the values by a constant -template<typename GridT, typename InterruptT> -inline void -Filter<GridT, InterruptT>::doOffset(const RangeType& range, float floatVal) -{ - const ValueType value = static_cast<ValueType>(floatVal); - for (typename RangeType::Iterator lIter=range.begin(); lIter; ++lIter) lIter->addValue(value); -} - -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline bool -Filter<GridT, InterruptT>::wasInterrupted() +Filter<GridT, MaskT, InterruptT>::wasInterrupted() { if (util::wasInterrupted(mInterrupter)) { tbb::task::self().cancel_group_execution(); diff --git a/extern/openvdb/internal/openvdb/tools/GridOperators.h b/extern/openvdb/internal/openvdb/tools/GridOperators.h index ae092974ca0..8fa838c3176 100644 --- a/extern/openvdb/internal/openvdb/tools/GridOperators.h +++ b/extern/openvdb/internal/openvdb/tools/GridOperators.h @@ -71,7 +71,9 @@ template<typename ScalarGridType> struct ScalarToVectorConverter { /// @brief Compute the Closest-Point Transform (CPT) from a distance field. /// @return a new vector-valued grid with the same numerical precision as the input grid /// (for example, if the input grid is a DoubleGrid, the output grid will be a Vec3DGrid) -/// +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. /// @note The current implementation assumes all the input distance values /// are represented by leaf voxels and not tiles. This is true for all /// narrow-band level sets, which this class was originally developed for. @@ -79,7 +81,11 @@ template<typename ScalarGridType> struct ScalarToVectorConverter { template<typename GridType, typename InterruptT> inline typename ScalarToVectorConverter<GridType>::Type::Ptr cpt(const GridType& grid, bool threaded, InterruptT* interrupt); - + +template<typename GridType, typename MaskT, typename InterruptT> inline +typename ScalarToVectorConverter<GridType>::Type::Ptr +cpt(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + template<typename GridType> inline typename ScalarToVectorConverter<GridType>::Type::Ptr cpt(const GridType& grid, bool threaded = true) @@ -87,13 +93,27 @@ cpt(const GridType& grid, bool threaded = true) return cpt<GridType, util::NullInterrupter>(grid, threaded, NULL); } +template<typename GridType, typename MaskT> inline +typename ScalarToVectorConverter<GridType>::Type::Ptr +cpt(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return cpt<GridType, MaskT, util::NullInterrupter>(grid, mask, threaded, NULL); +} + /// @brief Compute the curl of the given vector-valued grid. /// @return a new vector-valued grid +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. template<typename GridType, typename InterruptT> inline typename GridType::Ptr curl(const GridType& grid, bool threaded, InterruptT* interrupt); - + +template<typename GridType, typename MaskT, typename InterruptT> inline +typename GridType::Ptr +curl(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + template<typename GridType> inline typename GridType::Ptr curl(const GridType& grid, bool threaded = true) @@ -101,13 +121,28 @@ curl(const GridType& grid, bool threaded = true) return curl<GridType, util::NullInterrupter>(grid, threaded, NULL); } +template<typename GridType, typename MaskT> inline +typename GridType::Ptr +curl(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return curl<GridType, MaskT, util::NullInterrupter>(grid, mask, threaded, NULL); +} + + /// @brief Compute the divergence of the given vector-valued grid. /// @return a new scalar-valued grid with the same numerical precision as the input grid /// (for example, if the input grid is a Vec3DGrid, the output grid will be a DoubleGrid) +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. template<typename GridType, typename InterruptT> inline typename VectorToScalarConverter<GridType>::Type::Ptr divergence(const GridType& grid, bool threaded, InterruptT* interrupt); - + +template<typename GridType, typename MaskT, typename InterruptT> inline +typename VectorToScalarConverter<GridType>::Type::Ptr +divergence(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + template<typename GridType> inline typename VectorToScalarConverter<GridType>::Type::Ptr divergence(const GridType& grid, bool threaded = true) @@ -115,13 +150,28 @@ divergence(const GridType& grid, bool threaded = true) return divergence<GridType, util::NullInterrupter>(grid, threaded, NULL); } +template<typename GridType, typename MaskT> inline +typename VectorToScalarConverter<GridType>::Type::Ptr +divergence(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return divergence<GridType, MaskT, util::NullInterrupter>(grid, mask, threaded, NULL); +} + + /// @brief Compute the gradient of the given scalar grid. /// @return a new vector-valued grid with the same numerical precision as the input grid /// (for example, if the input grid is a DoubleGrid, the output grid will be a Vec3DGrid) +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. template<typename GridType, typename InterruptT> inline typename ScalarToVectorConverter<GridType>::Type::Ptr gradient(const GridType& grid, bool threaded, InterruptT* interrupt); +template<typename GridType, typename MaskT, typename InterruptT> inline +typename ScalarToVectorConverter<GridType>::Type::Ptr +gradient(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + template<typename GridType> inline typename ScalarToVectorConverter<GridType>::Type::Ptr gradient(const GridType& grid, bool threaded = true) @@ -129,64 +179,140 @@ gradient(const GridType& grid, bool threaded = true) return gradient<GridType, util::NullInterrupter>(grid, threaded, NULL); } +template<typename GridType, typename MaskT> inline +typename ScalarToVectorConverter<GridType>::Type::Ptr +gradient(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return gradient<GridType, MaskT, util::NullInterrupter>(grid, mask, threaded, NULL); +} + /// @brief Compute the Laplacian of the given scalar grid. /// @return a new scalar grid +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. template<typename GridType, typename InterruptT> inline typename GridType::Ptr laplacian(const GridType& grid, bool threaded, InterruptT* interrupt); +template<typename GridType, typename MaskT, typename InterruptT> inline +typename GridType::Ptr +laplacian(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + template<typename GridType> inline typename GridType::Ptr laplacian(const GridType& grid, bool threaded = true) { return laplacian<GridType, util::NullInterrupter>(grid, threaded, NULL); } - + +template<typename GridType, typename MaskT> inline +typename GridType::Ptr +laplacian(const GridType& grid, const MaskT mask, bool threaded = true) +{ + return laplacian<GridType, MaskT, util::NullInterrupter>(grid, mask, threaded, NULL); +} + + /// @brief Compute the mean curvature of the given grid. /// @return a new grid +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. template<typename GridType, typename InterruptT> inline typename GridType::Ptr meanCurvature(const GridType& grid, bool threaded, InterruptT* interrupt); +template<typename GridType, typename MaskT, typename InterruptT> inline +typename GridType::Ptr +meanCurvature(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + template<typename GridType> inline typename GridType::Ptr -meanCurvature(const GridType& grid, bool threaded = true) +meanCurvature(const GridType& grid, bool threaded = true) { return meanCurvature<GridType, util::NullInterrupter>(grid, threaded, NULL); } +template<typename GridType, typename MaskT> inline +typename GridType::Ptr +meanCurvature(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return meanCurvature<GridType, MaskT, util::NullInterrupter>(grid, mask, threaded, NULL); +} + + /// @brief Compute the magnitudes of the vectors of the given vector-valued grid. /// @return a new scalar-valued grid with the same numerical precision as the input grid /// (for example, if the input grid is a Vec3DGrid, the output grid will be a DoubleGrid) +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. template<typename GridType, typename InterruptT> inline typename VectorToScalarConverter<GridType>::Type::Ptr magnitude(const GridType& grid, bool threaded, InterruptT* interrupt); - + +template<typename GridType, typename MaskT, typename InterruptT> inline +typename VectorToScalarConverter<GridType>::Type::Ptr +magnitude(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + template<typename GridType> inline typename VectorToScalarConverter<GridType>::Type::Ptr -magnitude(const GridType& grid, bool threaded = true) +magnitude(const GridType& grid, bool threaded = true) { return magnitude<GridType, util::NullInterrupter>(grid, threaded, NULL); } - + +template<typename GridType, typename MaskT> inline +typename VectorToScalarConverter<GridType>::Type::Ptr +magnitude(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return magnitude<GridType, MaskT, util::NullInterrupter>(grid, mask, threaded, NULL); +} + + /// @brief Normalize the vectors of the given vector-valued grid. /// @return a new vector-valued grid +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. template<typename GridType, typename InterruptT> inline typename GridType::Ptr normalize(const GridType& grid, bool threaded, InterruptT* interrupt); +template<typename GridType, typename MaskT, typename InterruptT> inline +typename GridType::Ptr +normalize(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + template<typename GridType> inline typename GridType::Ptr normalize(const GridType& grid, bool threaded = true) { return normalize<GridType, util::NullInterrupter>(grid, threaded, NULL); } - + +template<typename GridType, typename MaskT> inline +typename GridType::Ptr +normalize(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return normalize<GridType, MaskT, util::NullInterrupter>(grid, mask, threaded, NULL); +} + + //////////////////////////////////////// -namespace { +namespace gridop { + +/// @brief ToBoolGrid<T>::Type is the type of a grid having the same +/// tree hierarchy as grid type T but a value type of bool. +/// @details For example, ToBoolGrid<FloatGrid>::Type is equivalent to BoolGrid. +template<typename GridType> +struct ToBoolGrid { + typedef Grid<typename GridType::TreeType::template ValueConverter<bool>::Type> Type; +}; + /// @brief Apply an operator on an input grid to produce an output grid /// with the same topology but a possibly different value type. @@ -197,8 +323,13 @@ namespace { /// @note The current implementation assumes all the input /// values are represented by leaf voxels and not tiles. In the /// future we will expand this class to also handle tile values. -template<typename InGridT, typename OutGridT, typename MapT, - typename OperatorT, typename InterruptT = util::NullInterrupter> +template< + typename InGridT, + typename MaskGridType, + typename OutGridT, + typename MapT, + typename OperatorT, + typename InterruptT = util::NullInterrupter> class GridOperator { public: @@ -206,32 +337,44 @@ public: typedef typename OutTreeT::LeafNodeType OutLeafT; typedef typename tree::LeafManager<OutTreeT> LeafManagerT; - GridOperator(const InGridT& grid, const MapT& map, InterruptT* interrupt = NULL): - mAcc(grid.getConstAccessor()), mMap(map), mInterrupt(interrupt) + GridOperator(const InGridT& grid, const MaskGridType* mask, const MapT& map, + InterruptT* interrupt = NULL): + mAcc(grid.getConstAccessor()), mMap(map), mInterrupt(interrupt), mMask(mask) { } + virtual ~GridOperator() {} typename OutGridT::Ptr process(bool threaded = true) { if (mInterrupt) mInterrupt->start("Processing grid"); + // Derive background value of the output grid - typename InGridT::TreeType tmp(mAcc.getTree()->background()); + typename InGridT::TreeType tmp(mAcc.tree().background()); typename OutGridT::ValueType backg = OperatorT::result(mMap, tmp, math::Coord(0)); + // output tree = topology copy of input tree! - typename OutTreeT::Ptr tree(new OutTreeT(*mAcc.getTree(), backg, TopologyCopy())); + typename OutTreeT::Ptr tree(new OutTreeT(mAcc.tree(), backg, TopologyCopy())); + + // create grid with output tree and unit transform typename OutGridT::Ptr result(new OutGridT(tree)); + + // Modify the solution area if a mask was supplied. + if (mMask) { + result->topologyIntersection(*mMask); + } + // transform of output grid = transform of input grid result->setTransform(math::Transform::Ptr(new math::Transform( mMap.copy() ))); - + LeafManagerT leafManager(*tree); - + if (threaded) { tbb::parallel_for(leafManager.leafRange(), *this); } else { (*this)(leafManager.leafRange()); } - + if (mInterrupt) mInterrupt->end(); return result; } @@ -245,7 +388,7 @@ public: void operator()(const typename LeafManagerT::LeafRange& range) const { if (util::wasInterrupted(mInterrupt)) tbb::task::self().cancel_group_execution(); - + for (typename LeafManagerT::LeafRange::Iterator leaf=range.begin(); leaf; ++leaf) { for (typename OutLeafT::ValueOnIter value=leaf->beginValueOn(); value; ++value) { value.setValue(OperatorT::result(mMap, mAcc, value.getCoord())); @@ -254,21 +397,24 @@ public: } protected: - typedef typename InGridT::ConstAccessor AccessorT; - mutable AccessorT mAcc; - const MapT& mMap; - InterruptT* mInterrupt; + mutable AccessorT mAcc; + const MapT& mMap; + InterruptT* mInterrupt; + const MaskGridType* mMask; }; // end of GridOperator class -} //end of anonymous namespace +} // namespace gridop //////////////////////////////////////// /// @brief Compute the closest-point transform of a scalar grid. -template<typename InGridT, typename InterruptT = util::NullInterrupter> +template< + typename InGridT, + typename MaskGridType = typename gridop::ToBoolGrid<InGridT>::Type, + typename InterruptT = util::NullInterrupter> class Cpt { public: @@ -276,15 +422,23 @@ public: typedef typename ScalarToVectorConverter<InGridT>::Type OutGridType; Cpt(const InGridType& grid, InterruptT* interrupt = NULL): - mInputGrid(grid), mInterrupt(interrupt) + mInputGrid(grid), mInterrupt(interrupt), mMask(NULL) { } + + Cpt(const InGridType& grid, const MaskGridType& mask, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + typename OutGridType::Ptr process(bool threaded = true, bool useWorldTransform = true) { - Functor functor(mInputGrid, threaded, useWorldTransform, mInterrupt); + Functor functor(mInputGrid, mMask, threaded, useWorldTransform, mInterrupt); processTypedMap(mInputGrid.transform(), functor); + if (functor.mOutputGrid) functor.mOutputGrid->setVectorType(VEC_CONTRAVARIANT_ABSOLUTE); return functor.mOutputGrid; } + private: struct IsOpT { @@ -306,16 +460,25 @@ private: }; struct Functor { - Functor(const InGridType& grid, bool threaded, bool worldspace, InterruptT* interrupt): - mThreaded(threaded), mWorldSpace(worldspace), mInputGrid(grid), mInterrupt(interrupt){} + Functor(const InGridType& grid, const MaskGridType* mask, + bool threaded, bool worldspace, InterruptT* interrupt) + : mThreaded(threaded) + , mWorldSpace(worldspace) + , mInputGrid(grid) + , mInterrupt(interrupt) + , mMask(mask) + {} + template<typename MapT> void operator()(const MapT& map) { if (mWorldSpace) { - GridOperator<InGridType, OutGridType, MapT, WsOpT, InterruptT> op(mInputGrid, map, mInterrupt); + gridop::GridOperator<InGridType, MaskGridType, OutGridType, MapT, WsOpT, InterruptT> + op(mInputGrid, mMask, map, mInterrupt); mOutputGrid = op.process(mThreaded); // cache the result } else { - GridOperator<InGridType, OutGridType, MapT, IsOpT, InterruptT> op(mInputGrid, map, mInterrupt); + gridop::GridOperator<InGridType, MaskGridType, OutGridType, MapT, IsOpT, InterruptT> + op(mInputGrid, mMask, map, mInterrupt); mOutputGrid = op.process(mThreaded); // cache the result } } @@ -324,61 +487,83 @@ private: const InGridType& mInputGrid; typename OutGridType::Ptr mOutputGrid; InterruptT* mInterrupt; + const MaskGridType* mMask; }; - const InGridType& mInputGrid; - InterruptT* mInterrupt; + const InGridType& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; }; // end of Cpt class //////////////////////////////////////// -/// @brief Compute the curl of a scalar grid. -template<typename GridT, typename InterruptT = util::NullInterrupter> +/// @brief Compute the curl of a vector grid. +template< + typename GridT, + typename MaskGridType = typename gridop::ToBoolGrid<GridT>::Type, + typename InterruptT = util::NullInterrupter> class Curl { public: typedef GridT InGridType; typedef GridT OutGridType; + Curl(const GridT& grid, InterruptT* interrupt = NULL): - mInputGrid(grid), mInterrupt(interrupt) + mInputGrid(grid), mInterrupt(interrupt), mMask(NULL) { } + + Curl(const GridT& grid, const MaskGridType& mask, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + typename GridT::Ptr process(bool threaded = true) { - Functor functor(mInputGrid, threaded, mInterrupt); + Functor functor(mInputGrid, mMask, threaded, mInterrupt); processTypedMap(mInputGrid.transform(), functor); + if (functor.mOutputGrid) functor.mOutputGrid->setVectorType(VEC_COVARIANT); return functor.mOutputGrid; } private: struct Functor { - Functor(const GridT& grid, bool threaded, InterruptT* interrupt): - mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt){} + Functor(const GridT& grid, const MaskGridType* mask, + bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + template<typename MapT> void operator()(const MapT& map) { typedef math::Curl<MapT, math::CD_2ND> OpT; - GridOperator<GridT, GridT, MapT, OpT, InterruptT> op(mInputGrid, map, mInterrupt); + gridop::GridOperator<GridT, MaskGridType, GridT, MapT, OpT, InterruptT> + op(mInputGrid, mMask, map, mInterrupt); mOutputGrid = op.process(mThreaded); // cache the result } + const bool mThreaded; const GridT& mInputGrid; typename GridT::Ptr mOutputGrid; InterruptT* mInterrupt; + const MaskGridType* mMask; }; // Private Functor - const GridT& mInputGrid; - InterruptT* mInterrupt; + const GridT& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; }; // end of Curl class //////////////////////////////////////// -/// @brief Computes the Divergence of a scalar grid -template<typename InGridT, typename InterruptT = util::NullInterrupter> +/// @brief Compute the divergence of a vector grid. +template< + typename InGridT, + typename MaskGridType = typename gridop::ToBoolGrid<InGridT>::Type, + typename InterruptT = util::NullInterrupter> class Divergence { public: @@ -386,18 +571,23 @@ public: typedef typename VectorToScalarConverter<InGridT>::Type OutGridType; Divergence(const InGridT& grid, InterruptT* interrupt = NULL): - mInputGrid(grid), mInterrupt(interrupt) + mInputGrid(grid), mInterrupt(interrupt), mMask(NULL) + { + } + + Divergence(const InGridT& grid, const MaskGridType& mask, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) { } + typename OutGridType::Ptr process(bool threaded = true) { - if( mInputGrid.getGridClass() == GRID_STAGGERED ) { - Functor<math::FD_1ST> functor(mInputGrid, threaded, mInterrupt); + if (mInputGrid.getGridClass() == GRID_STAGGERED) { + Functor<math::FD_1ST> functor(mInputGrid, mMask, threaded, mInterrupt); processTypedMap(mInputGrid.transform(), functor); return functor.mOutputGrid; - } - else { - Functor<math::CD_2ND> functor(mInputGrid, threaded, mInterrupt); + } else { + Functor<math::CD_2ND> functor(mInputGrid, mMask, threaded, mInterrupt); processTypedMap(mInputGrid.transform(), functor); return functor.mOutputGrid; } @@ -407,31 +597,40 @@ protected: template<math::DScheme DiffScheme> struct Functor { - Functor(const InGridT& grid, bool threaded, InterruptT* interrupt): - mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt) {} + Functor(const InGridT& grid, const MaskGridType* mask, + bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + template<typename MapT> void operator()(const MapT& map) { typedef math::Divergence<MapT, DiffScheme> OpT; - GridOperator<InGridType, OutGridType, MapT, OpT, InterruptT> op(mInputGrid, map, mInterrupt); + gridop::GridOperator<InGridType, MaskGridType, OutGridType, MapT, OpT, InterruptT> + op(mInputGrid, mMask, map, mInterrupt); mOutputGrid = op.process(mThreaded); // cache the result } + const bool mThreaded; const InGridType& mInputGrid; typename OutGridType::Ptr mOutputGrid; InterruptT* mInterrupt; + const MaskGridType* mMask; }; // Private Functor - const InGridType& mInputGrid; - InterruptT* mInterrupt; + const InGridType& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; }; // end of Divergence class //////////////////////////////////////// -/// @brief Computes the Gradient of a scalar grid -template<typename InGridT, typename InterruptT = util::NullInterrupter> +/// @brief Compute the gradient of a scalar grid. +template< + typename InGridT, + typename MaskGridType = typename gridop::ToBoolGrid<InGridT>::Type, + typename InterruptT = util::NullInterrupter> class Gradient { public: @@ -439,142 +638,196 @@ public: typedef typename ScalarToVectorConverter<InGridT>::Type OutGridType; Gradient(const InGridT& grid, InterruptT* interrupt = NULL): - mInputGrid(grid), mInterrupt(interrupt) + mInputGrid(grid), mInterrupt(interrupt), mMask(NULL) + { + } + + Gradient(const InGridT& grid, const MaskGridType& mask, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) { } + typename OutGridType::Ptr process(bool threaded = true) { - Functor functor(mInputGrid, threaded, mInterrupt); + Functor functor(mInputGrid, mMask, threaded, mInterrupt); processTypedMap(mInputGrid.transform(), functor); + if (functor.mOutputGrid) functor.mOutputGrid->setVectorType(VEC_COVARIANT); return functor.mOutputGrid; } protected: struct Functor { - Functor(const InGridT& grid, bool threaded, InterruptT* interrupt): - mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt) {} + Functor(const InGridT& grid, const MaskGridType* mask, + bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + template<typename MapT> void operator()(const MapT& map) { typedef math::Gradient<MapT, math::CD_2ND> OpT; - GridOperator<InGridType, OutGridType, MapT, OpT, InterruptT> op(mInputGrid, map, mInterrupt); + gridop::GridOperator<InGridType, MaskGridType, OutGridType, MapT, OpT, InterruptT> + op(mInputGrid, mMask, map, mInterrupt); mOutputGrid = op.process(mThreaded); // cache the result } + const bool mThreaded; const InGridT& mInputGrid; typename OutGridType::Ptr mOutputGrid; InterruptT* mInterrupt; + const MaskGridType* mMask; }; // Private Functor - const InGridT& mInputGrid; - InterruptT* mInterrupt; + const InGridT& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; }; // end of Gradient class //////////////////////////////////////// -/// @brief Computes the Laplacian of a scalar grid -template<typename GridT, typename InterruptT = util::NullInterrupter> +template< + typename GridT, + typename MaskGridType = typename gridop::ToBoolGrid<GridT>::Type, + typename InterruptT = util::NullInterrupter> class Laplacian { public: typedef GridT InGridType; typedef GridT OutGridType; + Laplacian(const GridT& grid, InterruptT* interrupt = NULL): - mInputGrid(grid), mInterrupt(interrupt) + mInputGrid(grid), mInterrupt(interrupt), mMask(NULL) { } + + Laplacian(const GridT& grid, const MaskGridType& mask, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + typename GridT::Ptr process(bool threaded = true) { - Functor functor(mInputGrid, threaded, mInterrupt); + Functor functor(mInputGrid, mMask, threaded, mInterrupt); processTypedMap(mInputGrid.transform(), functor); + if (functor.mOutputGrid) functor.mOutputGrid->setVectorType(VEC_COVARIANT); return functor.mOutputGrid; } protected: struct Functor { - Functor(const GridT& grid, bool threaded, InterruptT* interrupt): - mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt) {} + Functor(const GridT& grid, const MaskGridType* mask, bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + template<typename MapT> void operator()(const MapT& map) { typedef math::Laplacian<MapT, math::CD_SECOND> OpT; - GridOperator<GridT, GridT, MapT, OpT, InterruptT> op(mInputGrid, map); + gridop::GridOperator<GridT, MaskGridType, GridT, MapT, OpT, InterruptT> + op(mInputGrid, mMask, map); mOutputGrid = op.process(mThreaded); // cache the result } + const bool mThreaded; const GridT& mInputGrid; typename GridT::Ptr mOutputGrid; InterruptT* mInterrupt; + const MaskGridType* mMask; }; // Private Functor - const GridT& mInputGrid; - InterruptT* mInterrupt; + const GridT& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; }; // end of Laplacian class //////////////////////////////////////// -template<typename GridT, typename InterruptT = util::NullInterrupter> +template< + typename GridT, + typename MaskGridType = typename gridop::ToBoolGrid<GridT>::Type, + typename InterruptT = util::NullInterrupter> class MeanCurvature { public: typedef GridT InGridType; typedef GridT OutGridType; + MeanCurvature(const GridT& grid, InterruptT* interrupt = NULL): - mInputGrid(grid), mInterrupt(interrupt) + mInputGrid(grid), mInterrupt(interrupt), mMask(NULL) + { + } + + MeanCurvature(const GridT& grid, const MaskGridType& mask, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) { } + typename GridT::Ptr process(bool threaded = true) { - Functor functor(mInputGrid, threaded, mInterrupt); + Functor functor(mInputGrid, mMask, threaded, mInterrupt); processTypedMap(mInputGrid.transform(), functor); + if (functor.mOutputGrid) functor.mOutputGrid->setVectorType(VEC_COVARIANT); return functor.mOutputGrid; } protected: struct Functor { - Functor(const GridT& grid, bool threaded, InterruptT* interrupt): - mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt) {} + Functor(const GridT& grid, const MaskGridType* mask, bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + template<typename MapT> void operator()(const MapT& map) { typedef math::MeanCurvature<MapT, math::CD_SECOND, math::CD_2ND> OpT; - GridOperator<GridT, GridT, MapT, OpT, InterruptT> op(mInputGrid, map); + gridop::GridOperator<GridT, MaskGridType, GridT, MapT, OpT, InterruptT> + op(mInputGrid, mMask, map); mOutputGrid = op.process(mThreaded); // cache the result } + const bool mThreaded; const GridT& mInputGrid; typename GridT::Ptr mOutputGrid; InterruptT* mInterrupt; + const MaskGridType* mMask; }; // Private Functor - const GridT& mInputGrid; - InterruptT* mInterrupt; + const GridT& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; }; // end of MeanCurvature class //////////////////////////////////////// -template<typename InGridT, typename InterruptT = util::NullInterrupter> +template< + typename InGridT, + typename MaskGridType = typename gridop::ToBoolGrid<InGridT>::Type, + typename InterruptT = util::NullInterrupter> class Magnitude { public: typedef InGridT InGridType; typedef typename VectorToScalarConverter<InGridT>::Type OutGridType; + Magnitude(const InGridType& grid, InterruptT* interrupt = NULL): - mInputGrid(grid), mInterrupt(interrupt) + mInputGrid(grid), mInterrupt(interrupt), mMask(NULL) + { + } + + Magnitude(const InGridType& grid, const MaskGridType& mask, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) { } + typename OutGridType::Ptr process(bool threaded = true) { - Functor functor(mInputGrid, threaded, mInterrupt); + Functor functor(mInputGrid, mMask, threaded, mInterrupt); processTypedMap(mInputGrid.transform(), functor); return functor.mOutputGrid; } @@ -588,42 +841,66 @@ protected: }; struct Functor { - Functor(const InGridT& grid, bool threaded, InterruptT* interrupt): - mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt) {} + Functor(const InGridT& grid, const MaskGridType* mask, + bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + template<typename MapT> void operator()(const MapT& map) { - GridOperator<InGridType, OutGridType, MapT, OpT, InterruptT> op(mInputGrid, map); + gridop::GridOperator<InGridType, MaskGridType, OutGridType, MapT, OpT, InterruptT> + op(mInputGrid, mMask, map); mOutputGrid = op.process(mThreaded); // cache the result } + const bool mThreaded; const InGridType& mInputGrid; typename OutGridType::Ptr mOutputGrid; InterruptT* mInterrupt; + const MaskGridType* mMask; }; // Private Functor - const InGridType& mInputGrid; - InterruptT* mInterrupt; + const InGridType& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; }; // end of Magnitude class //////////////////////////////////////// -template<typename GridT, typename InterruptT = util::NullInterrupter> +template< + typename GridT, + typename MaskGridType = typename gridop::ToBoolGrid<GridT>::Type, + typename InterruptT = util::NullInterrupter> class Normalize { public: typedef GridT InGridType; typedef GridT OutGridType; + Normalize(const GridT& grid, InterruptT* interrupt = NULL): - mInputGrid(grid), mInterrupt(interrupt) + mInputGrid(grid), mInterrupt(interrupt), mMask(NULL) { } + + Normalize(const GridT& grid, const MaskGridType& mask, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + typename GridT::Ptr process(bool threaded = true) { - Functor functor(mInputGrid, threaded, mInterrupt); + Functor functor(mInputGrid, mMask, threaded, mInterrupt); processTypedMap(mInputGrid.transform(), functor); + if (typename GridT::Ptr outGrid = functor.mOutputGrid) { + const VecType vecType = mInputGrid.getVectorType(); + if (vecType == VEC_COVARIANT) { + outGrid->setVectorType(VEC_COVARIANT_NORMALIZE); + } else { + outGrid->setVectorType(vecType); + } + } return functor.mOutputGrid; } @@ -641,22 +918,27 @@ protected: }; struct Functor { - Functor(const GridT& grid, bool threaded, InterruptT* interrupt): - mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt) {} + Functor(const GridT& grid, const MaskGridType* mask, bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + template<typename MapT> void operator()(const MapT& map) { - GridOperator<GridT, GridT, MapT, OpT, InterruptT> op(mInputGrid, map); + gridop::GridOperator<GridT, MaskGridType, GridT, MapT, OpT, InterruptT> + op(mInputGrid, mMask,map); mOutputGrid = op.process(mThreaded); // cache the result } + const bool mThreaded; const GridT& mInputGrid; typename GridT::Ptr mOutputGrid; InterruptT* mInterrupt; + const MaskGridType* mMask; }; // Private Functor - const GridT& mInputGrid; - InterruptT* mInterrupt; + const GridT& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; }; // end of Normalize class @@ -667,7 +949,15 @@ template<typename GridType, typename InterruptT> inline typename ScalarToVectorConverter<GridType>::Type::Ptr cpt(const GridType& grid, bool threaded, InterruptT* interrupt) { - Cpt<GridType, InterruptT> op(grid, interrupt); + Cpt<GridType, typename gridop::ToBoolGrid<GridType>::Type, InterruptT> op(grid, interrupt); + return op.process(threaded); +} + +template<typename GridType, typename MaskT, typename InterruptT> inline +typename ScalarToVectorConverter<GridType>::Type::Ptr +cpt(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Cpt<GridType, MaskT, InterruptT> op(grid, mask, interrupt); return op.process(threaded); } @@ -675,7 +965,15 @@ template<typename GridType, typename InterruptT> inline typename GridType::Ptr curl(const GridType& grid, bool threaded, InterruptT* interrupt) { - Curl<GridType, InterruptT> op(grid, interrupt); + Curl<GridType, typename gridop::ToBoolGrid<GridType>::Type, InterruptT> op(grid, interrupt); + return op.process(threaded); +} + +template<typename GridType, typename MaskT, typename InterruptT> inline +typename GridType::Ptr +curl(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Curl<GridType, MaskT, InterruptT> op(grid, mask, interrupt); return op.process(threaded); } @@ -683,7 +981,16 @@ template<typename GridType, typename InterruptT> inline typename VectorToScalarConverter<GridType>::Type::Ptr divergence(const GridType& grid, bool threaded, InterruptT* interrupt) { - Divergence<GridType, InterruptT> op(grid, interrupt); + Divergence<GridType, typename gridop::ToBoolGrid<GridType>::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template<typename GridType, typename MaskT, typename InterruptT> inline +typename VectorToScalarConverter<GridType>::Type::Ptr +divergence(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Divergence<GridType, MaskT, InterruptT> op(grid, mask, interrupt); return op.process(threaded); } @@ -691,7 +998,16 @@ template<typename GridType, typename InterruptT> inline typename ScalarToVectorConverter<GridType>::Type::Ptr gradient(const GridType& grid, bool threaded, InterruptT* interrupt) { - Gradient<GridType, InterruptT> op(grid, interrupt); + Gradient<GridType, typename gridop::ToBoolGrid<GridType>::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template<typename GridType, typename MaskT, typename InterruptT> inline +typename ScalarToVectorConverter<GridType>::Type::Ptr +gradient(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Gradient<GridType, MaskT, InterruptT> op(grid, mask, interrupt); return op.process(threaded); } @@ -699,7 +1015,16 @@ template<typename GridType, typename InterruptT> inline typename GridType::Ptr laplacian(const GridType& grid, bool threaded, InterruptT* interrupt) { - Laplacian<GridType, InterruptT> op(grid, interrupt); + Laplacian<GridType, typename gridop::ToBoolGrid<GridType>::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template<typename GridType, typename MaskT, typename InterruptT> inline +typename GridType::Ptr +laplacian(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Laplacian<GridType, MaskT, InterruptT> op(grid, mask, interrupt); return op.process(threaded); } @@ -707,7 +1032,16 @@ template<typename GridType, typename InterruptT> inline typename GridType::Ptr meanCurvature(const GridType& grid, bool threaded, InterruptT* interrupt) { - MeanCurvature<GridType, InterruptT> op(grid, interrupt); + MeanCurvature<GridType, typename gridop::ToBoolGrid<GridType>::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template<typename GridType, typename MaskT, typename InterruptT> inline +typename GridType::Ptr +meanCurvature(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + MeanCurvature<GridType, MaskT, InterruptT> op(grid, mask, interrupt); return op.process(threaded); } @@ -715,7 +1049,16 @@ template<typename GridType, typename InterruptT> inline typename VectorToScalarConverter<GridType>::Type::Ptr magnitude(const GridType& grid, bool threaded, InterruptT* interrupt) { - Magnitude<GridType, InterruptT> op(grid, interrupt); + Magnitude<GridType, typename gridop::ToBoolGrid<GridType>::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template<typename GridType, typename MaskT, typename InterruptT> inline +typename VectorToScalarConverter<GridType>::Type::Ptr +magnitude(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Magnitude<GridType, MaskT, InterruptT> op(grid, mask, interrupt); return op.process(threaded); } @@ -723,7 +1066,16 @@ template<typename GridType, typename InterruptT> inline typename GridType::Ptr normalize(const GridType& grid, bool threaded, InterruptT* interrupt) { - Normalize<GridType, InterruptT> op(grid, interrupt); + Normalize<GridType, typename gridop::ToBoolGrid<GridType>::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template<typename GridType, typename MaskT, typename InterruptT> inline +typename GridType::Ptr +normalize(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Normalize<GridType, MaskT, InterruptT> op(grid, mask, interrupt); return op.process(threaded); } diff --git a/extern/openvdb/internal/openvdb/tools/GridTransformer.h b/extern/openvdb/internal/openvdb/tools/GridTransformer.h index 12586492988..9756ed842bf 100644 --- a/extern/openvdb/internal/openvdb/tools/GridTransformer.h +++ b/extern/openvdb/internal/openvdb/tools/GridTransformer.h @@ -29,6 +29,7 @@ /////////////////////////////////////////////////////////////////////////// // /// @file GridTransformer.h +/// @author Peter Cucka #ifndef OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED #define OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED @@ -742,7 +743,7 @@ public: for ( ; r; ++r) { if (interrupt()) break; LeafIterT i = r.iterator(); - CoordBBox bbox(i->getOrigin(), i->getOrigin() + Coord(i->dim())); + CoordBBox bbox(i->origin(), i->origin() + Coord(i->dim())); if (!mBBox.empty()) { // Intersect the leaf node's bounding box with mBBox. bbox = CoordBBox( @@ -884,7 +885,6 @@ GridResampler::transformBBox( const Sampler& sampler) { typedef typename OutTreeT::ValueType ValueT; - typedef math::Vec4<Real> Vec4R; // Transform the corners of the input tree's bounding box // and compute the enclosing bounding box in the output tree. diff --git a/extern/openvdb/internal/openvdb/tools/Interpolation.h b/extern/openvdb/internal/openvdb/tools/Interpolation.h index 3ddbc2dff1d..1eed214f4a3 100644 --- a/extern/openvdb/internal/openvdb/tools/Interpolation.h +++ b/extern/openvdb/internal/openvdb/tools/Interpolation.h @@ -72,7 +72,8 @@ #include <openvdb/version.h> // for OPENVDB_VERSION_NAME #include <openvdb/Platform.h> // for round() #include <openvdb/math/Transform.h> // for Transform - +#include <openvdb/Grid.h> +#include <openvdb/tree/ValueAccessor.h> namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE @@ -83,7 +84,7 @@ namespace tools { // When the samplers are applied to grids holding vector or other non-scalar data, // the data is assumed to be collocated. For example, using the BoxSampler on a grid // with ValueType Vec3f assumes that all three elements in a vector can be assigned -// the same physical location. +// the same physical location. Consider using the GridSampler below instead. struct PointSampler { @@ -201,37 +202,67 @@ struct StaggeredQuadraticSampler //////////////////////////////////////// -/// @brief Base class that provides the interface for continuous sampling -/// of values in a grid. -/// @details Since grids support only discrete voxel sampling, GridSampler -/// must be used to sample arbitrary continuous points in (world or index) space. -template<typename TreeOrAccessorType, typename SamplerType> +/// @brief Class that provides the interface for continuous sampling +/// of values in a tree. +/// +/// @details Since trees support only discrete voxel sampling, TreeSampler +/// must be used to sample arbitrary continuous points in (world or +/// index) space. +/// +/// @warning This implementation of the GridSampler stores a pointer +/// to a Tree for value access. While this is thread-safe it is +/// uncached and hence slow compared to using a +/// ValueAccessor. Consequently it is normally advisable to use the +/// template specialization below that employs a +/// ValueAccessor. However, care must be taken when dealing with +/// multi-threading (see warning below). +template<typename GridOrTreeType, typename SamplerType> class GridSampler { public: - typedef boost::shared_ptr<GridSampler> Ptr; - typedef typename TreeOrAccessorType::ValueType ValueType; + typedef boost::shared_ptr<GridSampler> Ptr; + typedef typename GridOrTreeType::ValueType ValueType; + typedef typename TreeAdapter<GridOrTreeType>::GridType GridType; + typedef typename TreeAdapter<GridOrTreeType>::TreeType TreeType; + typedef typename TreeAdapter<GridOrTreeType>::AccessorType AccessorType; + + /// @param grid a grid to be sampled + explicit GridSampler(const GridType& grid) + : mTree(&(grid.tree())), mTransform(&(grid.transform())) {} /// @param tree a tree to be sampled, or a ValueAccessor for the tree /// @param transform is used when sampling world space locations. - /// (by default an identity transform is used) - explicit GridSampler(const TreeOrAccessorType& tree, - const math::Transform& transform = math::Transform()): - mTree(&tree), mTransform(transform) {} + GridSampler(const TreeType& tree, const math::Transform& transform) + : mTree(&tree), mTransform(&transform) {} - ~GridSampler() {}; + const math::Transform& transform() const { return *mTransform; } /// @brief Sample a point in index space in the grid. - /// @param x x-coordinate of point in index-coordinates of grid - /// @param y y-coordinate of point in index-coordinates of grid - /// @param z z-coordinate of point in index-coordinates of grid + /// @param x Fractional x-coordinate of point in index-coordinates of grid + /// @param y Fractional y-coordinate of point in index-coordinates of grid + /// @param z Fractional z-coordinate of point in index-coordinates of grid template<typename RealType> ValueType sampleVoxel(const RealType& x, const RealType& y, const RealType& z) const { - return isSample(Vec3d(x,y,z)); + return this->isSample(Vec3d(x,y,z)); } - /// @brief Sample in index space + /// @brief Sample value in integer index space + /// @param i Integer x-coordinate in index space + /// @param j Integer y-coordinate in index space + /// @param k Integer x-coordinate in index space + ValueType sampleVoxel(typename Coord::ValueType i, + typename Coord::ValueType j, + typename Coord::ValueType k) const + { + return this->isSample(Coord(i,j,k)); + } + + /// @brief Sample value in integer index space + /// @param ijk the location in index space + ValueType isSample(const Coord& ijk) const { return mTree->getValue(ijk); } + + /// @brief Sample in fractional index space /// @param ispoint the location in index space ValueType isSample(const Vec3d& ispoint) const { @@ -245,15 +276,198 @@ public: ValueType wsSample(const Vec3d& wspoint) const { ValueType result = zeroVal<ValueType>(); - SamplerType::sample(*mTree, mTransform.worldToIndex(wspoint), result); + SamplerType::sample(*mTree, mTransform->worldToIndex(wspoint), result); return result; } private: - const TreeOrAccessorType* mTree; - const math::Transform mTransform; -}; + const TreeType* mTree; + const math::Transform* mTransform; +}; // class GridSampler + + +/// @brief Specialization of GridSampler for construction from a ValueAccessor type +/// +/// @note This version should normally be favoured over the one above +/// that takes a Grid or Tree. The reason is this version uses a +/// ValueAccessor that performs fast (cached) access where the +/// tree-based flavour performs slower (uncached) access. +/// +/// @warning Since this version stores a pointer to an (externally +/// allocated) value accessor it is not threadsafe. Hence each thread +/// should have it own instance of a GridSampler constructed from a +/// local ValueAccessor. Alternatively the Grid/Tree-based GridSampler +/// is threadsafe, but also slower. +template<typename TreeT, typename SamplerType> +class GridSampler<tree::ValueAccessor<TreeT>, SamplerType> +{ +public: + typedef boost::shared_ptr<GridSampler> Ptr; + typedef typename TreeT::ValueType ValueType; + typedef TreeT TreeType; + typedef Grid<TreeType> GridType; + typedef typename tree::ValueAccessor<TreeT> AccessorType; + + /// @param acc a ValueAccessor to be sampled + /// @param transform is used when sampling world space locations. + GridSampler(const AccessorType& acc, + const math::Transform& transform) + : mAccessor(&acc), mTransform(&transform) {} + + const math::Transform& transform() const { return *mTransform; } + + /// @brief Sample a point in index space in the grid. + /// @param x Fractional x-coordinate of point in index-coordinates of grid + /// @param y Fractional y-coordinate of point in index-coordinates of grid + /// @param z Fractional z-coordinate of point in index-coordinates of grid + template<typename RealType> + ValueType sampleVoxel(const RealType& x, const RealType& y, const RealType& z) const + { + return this->isSample(Vec3d(x,y,z)); + } + + /// @brief Sample value in integer index space + /// @param i Integer x-coordinate in index space + /// @param j Integer y-coordinate in index space + /// @param k Integer x-coordinate in index space + ValueType sampleVoxel(typename Coord::ValueType i, + typename Coord::ValueType j, + typename Coord::ValueType k) const + { + return this->isSample(Coord(i,j,k)); + } + + /// @brief Sample value in integer index space + /// @param ijk the location in index space + ValueType isSample(const Coord& ijk) const { return mAccessor->getValue(ijk); } + + /// @brief Sample in fractional index space + /// @param ispoint the location in index space + ValueType isSample(const Vec3d& ispoint) const + { + ValueType result = zeroVal<ValueType>(); + SamplerType::sample(*mAccessor, ispoint, result); + return result; + } + /// @brief Sample in world space + /// @param wspoint the location in world space + ValueType wsSample(const Vec3d& wspoint) const + { + ValueType result = zeroVal<ValueType>(); + SamplerType::sample(*mAccessor, mTransform->worldToIndex(wspoint), result); + return result; + } + +private: + const AccessorType* mAccessor;//not thread-safe! + const math::Transform* mTransform; +};//Specialization of GridSampler + + +//////////////////////////////////////// + + +/// @brief This is a simple convenience class that allows for sampling +/// from a source grid into the index space of a target grid. At +/// construction the source and target grids are checked for alignment +/// which potentially renders interpolation unnecessary. Else +/// interpolation is performed according to the templated Sampler +/// type. +/// +/// @warning For performance reasons the check for alignment of the +/// two grids is only performed at construction time! +template<typename GridOrTreeT, + typename SamplerT> +class DualGridSampler +{ +public: + typedef typename GridOrTreeT::ValueType ValueType; + typedef typename TreeAdapter<GridOrTreeT>::GridType GridType; + typedef typename TreeAdapter<GridOrTreeT>::TreeType TreeType; + typedef typename TreeAdapter<GridType>::AccessorType AccessorType; + + /// @brief Grid and transform constructor. + /// @param sourceGrid Source grid. + /// @param targetXform Transform of the target grid. + DualGridSampler(const GridType& sourceGrid, + const math::Transform& targetXform) + : mSourceTree(&(sourceGrid.tree())) + , mSourceXform(&(sourceGrid.transform())) + , mTargetXform(&targetXform) + , mAligned(targetXform == *mSourceXform) + { + } + /// @brief Tree and transform constructor. + /// @param sourceTree Source tree. + /// @param sourceXform Transform of the source grid. + /// @param targetXform Transform of the target grid. + DualGridSampler(const TreeType& sourceTree, + const math::Transform& sourceXform, + const math::Transform& targetXform) + : mSourceTree(&sourceTree) + , mSourceXform(&sourceXform) + , mTargetXform(&targetXform) + , mAligned(targetXform == sourceXform) + { + } + /// @brief Return the value of the source grid at the index + /// coordinates, ijk, relative to the target grid (or its tranform). + inline ValueType operator()(const Coord& ijk) const + { + if (mAligned) return mSourceTree->getValue(ijk); + const Vec3R world = mTargetXform->indexToWorld(ijk); + return SamplerT::sample(*mSourceTree, mSourceXform->worldToIndex(world)); + } + /// @brief Return true if the two grids are aligned. + inline bool isAligned() const { return mAligned; } +private: + const TreeType* mSourceTree; + const math::Transform* mSourceXform; + const math::Transform* mTargetXform; + const bool mAligned; +};// DualGridSampler + +/// @brief Specialization of DualGridSampler for construction from a ValueAccessor type. +template<typename TreeT, + typename SamplerT> +class DualGridSampler<tree::ValueAccessor<TreeT>, SamplerT> +{ + public: + typedef typename TreeT::ValueType ValueType; + typedef TreeT TreeType; + typedef Grid<TreeType> GridType; + typedef typename tree::ValueAccessor<TreeT> AccessorType; + + /// @brief ValueAccessor and transform constructor. + /// @param sourceAccessor ValueAccessor into the source grid. + /// @param sourceXform Transform for the source grid. + /// @param targetXform Transform for the target grid. + DualGridSampler(const AccessorType& sourceAccessor, + const math::Transform& sourceXform, + const math::Transform& targetXform) + : mSourceAcc(&sourceAccessor) + , mSourceXform(&sourceXform) + , mTargetXform(&targetXform) + , mAligned(targetXform == sourceXform) + { + } + /// @brief Return the value of the source grid at the index + /// coordinates, ijk, relative to the target grid. + inline ValueType operator()(const Coord& ijk) const + { + if (mAligned) return mSourceAcc->getValue(ijk); + const Vec3R world = mTargetXform->indexToWorld(ijk); + return SamplerT::sample(*mSourceAcc, mSourceXform->worldToIndex(world)); + } + /// @brief Return true if the two grids are aligned. + inline bool isAligned() const { return mAligned; } +private: + const AccessorType* mSourceAcc; + const math::Transform* mSourceXform; + const math::Transform* mTargetXform; + const bool mAligned; +};//Specialization of DualGridSampler //////////////////////////////////////// diff --git a/extern/openvdb/internal/openvdb/tools/LevelSetAdvect.h b/extern/openvdb/internal/openvdb/tools/LevelSetAdvect.h index 0782c20ef19..2119720d389 100644 --- a/extern/openvdb/internal/openvdb/tools/LevelSetAdvect.h +++ b/extern/openvdb/internal/openvdb/tools/LevelSetAdvect.h @@ -66,21 +66,23 @@ template <typename VelGridT, typename Interpolator = BoxSampler> class DiscreteField { public: - typedef typename VelGridT::ConstAccessor AccessorType; typedef typename VelGridT::ValueType VectorType; typedef typename VectorType::ValueType ScalarType; - DiscreteField(const VelGridT &vel): - mAccessor(vel.getConstAccessor()), - mTransform(vel.transform()) {} + DiscreteField(const VelGridT &vel): mAccessor(vel.tree()), mTransform(&vel.transform()) {} /// @return const reference to the transfrom between world and index space /// @note Use this method to determine if a client grid is /// aligned with the coordinate space of the velocity grid. - const math::Transform& transform() const { return mTransform; } + const math::Transform& transform() const { return *mTransform; } /// @return the interpolated velocity at the world space position xyz - inline VectorType operator() (const Vec3d& xyz, ScalarType) const; + inline VectorType operator() (const Vec3d& xyz, ScalarType) const + { + VectorType result = zeroVal<VectorType>(); + Interpolator::sample(mAccessor, mTransform->worldToIndex(xyz), result); + return result; + } /// @return the velocity at the coordinate space position ijk inline VectorType operator() (const Coord& ijk, ScalarType) const @@ -89,13 +91,17 @@ public: } private: - const AccessorType mAccessor; - const math::Transform& mTransform; + const typename VelGridT::ConstAccessor mAccessor;//Not thread-safe + const math::Transform* mTransform; }; // end of DiscreteField /// @brief Analytical, divergence-free and periodic vecloity field /// @note Primarily intended for debugging! +/// @warning This analytical velocity only produce meaningfull values +/// in the unitbox in world space. In other words make sure any level +/// set surface in fully enclodes in the axis aligned bounding box +/// spanning 0->1 in world units. template <typename ScalarT = float> class EnrightField { @@ -108,7 +114,7 @@ public: /// @return const reference to the identity transfrom between world and index space /// @note Use this method to determine if a client grid is /// aligned with the coordinate space of this velocity field - const math::Transform& transform() const { return mTransform; } + math::Transform transform() const { return math::Transform(); } /// @return the velocity in world units, evaluated at the world /// position xyz and at the specified time @@ -119,10 +125,6 @@ public: { return (*this)(ijk.asVec3d(), time); } - -private: - const math::Transform mTransform;//identity transform between world and index space - }; // end of EnrightField /// @brief Hyperbolic advection of narrow-band level sets in an @@ -265,7 +267,7 @@ private: VectorType** mVec; const ScalarType mMinAbsV; ScalarType mMaxAbsV; - const MapT& mMap; + const MapT* mMap; FuncType mTask; const bool mIsMaster; /// Enum to defeing the type of multi-threading @@ -378,15 +380,7 @@ LevelSetAdvection<GridT, FieldT, InterruptT>::advect3(ScalarType time0, ScalarTy return tmp.advect(time0, time1); } -template <typename VelGridT, typename Interpolator> -inline typename VelGridT::ValueType -DiscreteField<VelGridT,Interpolator>::operator() (const Vec3d& xyz, ScalarType time) const -{ - const VectorType coord = mTransform.worldToIndex(xyz); - VectorType result; - Interpolator::template sample<AccessorType>(mAccessor, coord, result); - return result; -} +/////////////////////////////////////////////////////////////////////// template <typename ScalarT> inline math::Vec3<ScalarT> @@ -418,7 +412,7 @@ LevelSetAdvect(LevelSetAdvection& parent): mParent(parent), mVec(NULL), mMinAbsV(1e-6), - mMap(*(parent.mTracker.grid().transform().template constMap<MapT>())), + mMap(parent.mTracker.grid().transform().template constMap<MapT>().get()), mTask(0), mIsMaster(true) { @@ -471,12 +465,13 @@ advect(ScalarType time0, ScalarType time1) const bool isForward = time0 < time1; while ((isForward ? time0<time1 : time0>time1) && mParent.mTracker.checkInterrupter()) { /// Make sure we have enough temporal auxiliary buffers - mParent.mTracker.mLeafs->rebuildAuxBuffers(TemporalScheme == math::TVD_RK3 ? 2 : 1); + mParent.mTracker.leafs().rebuildAuxBuffers(TemporalScheme == math::TVD_RK3 ? 2 : 1); const ScalarType dt = this->sampleField(time0, time1); if ( math::isZero(dt) ) break;//V is essentially zero so terminate - switch(TemporalScheme) {//switch is resolved at compile-time + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN //switch is resolved at compile-time + switch(TemporalScheme) { case math::TVD_RK1: // Perform one explicit Euler step: t1 = t0 + dt // Phi_t1(1) = Phi_t0(0) - dt * VdotG_t0(0) @@ -518,10 +513,12 @@ advect(ScalarType time0, ScalarType time1) break; default: OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); - } + }//end of compile-time resolved switch + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + time0 += isForward ? dt : -dt; ++countCFL; - mParent.mTracker.mLeafs->removeAuxBuffers(); + mParent.mTracker.leafs().removeAuxBuffers(); this->clearField(); /// Track the narrow band mParent.mTracker.track(); @@ -538,10 +535,10 @@ LevelSetAdvect<MapT, SpatialScheme, TemporalScheme>:: sampleField(ScalarType time0, ScalarType time1) { mMaxAbsV = mMinAbsV; - const size_t leafCount = mParent.mTracker.mLeafs->leafCount(); + const size_t leafCount = mParent.mTracker.leafs().leafCount(); if (leafCount==0) return ScalarType(0.0); mVec = new VectorType*[leafCount]; - if (mParent.mField.transform() == mParent.mTracker.mGrid.transform()) { + if (mParent.mField.transform() == mParent.mTracker.grid().transform()) { mTask = boost::bind(&LevelSetAdvect::sampleAlignedField, _1, _2, time0, time1); } else { mTask = boost::bind(&LevelSetAdvect::sampleXformedField, _1, _2, time0, time1); @@ -565,10 +562,10 @@ sampleXformedField(const RangeType& range, ScalarType time0, ScalarType time1) { const bool isForward = time0 < time1; typedef typename LeafType::ValueOnCIter VoxelIterT; - const MapT& map = mMap; + const MapT& map = *mMap; mParent.mTracker.checkInterrupter(); for (size_t n=range.begin(), e=range.end(); n != e; ++n) { - const LeafType& leaf = mParent.mTracker.mLeafs->leaf(n); + const LeafType& leaf = mParent.mTracker.leafs().leaf(n); VectorType* vec = new VectorType[leaf.onVoxelCount()]; int m = 0; for (VoxelIterT iter = leaf.cbeginValueOn(); iter; ++iter, ++m) { @@ -592,7 +589,7 @@ sampleAlignedField(const RangeType& range, ScalarType time0, ScalarType time1) typedef typename LeafType::ValueOnCIter VoxelIterT; mParent.mTracker.checkInterrupter(); for (size_t n=range.begin(), e=range.end(); n != e; ++n) { - const LeafType& leaf = mParent.mTracker.mLeafs->leaf(n); + const LeafType& leaf = mParent.mTracker.leafs().leaf(n); VectorType* vec = new VectorType[leaf.onVoxelCount()]; int m = 0; for (VoxelIterT iter = leaf.cbeginValueOn(); iter; ++iter, ++m) { @@ -613,7 +610,7 @@ LevelSetAdvect<MapT, SpatialScheme, TemporalScheme>:: clearField() { if (mVec == NULL) return; - for (size_t n=0, e=mParent.mTracker.mLeafs->leafCount(); n<e; ++n) delete [] mVec[n]; + for (size_t n=0, e=mParent.mTracker.leafs().leafCount(); n<e; ++n) delete [] mVec[n]; delete [] mVec; mVec = NULL; } @@ -629,16 +626,16 @@ cook(ThreadingMode mode, size_t swapBuffer) mParent.mTracker.startInterrupter("Advecting level set"); if (mParent.mTracker.getGrainSize()==0) { - (*this)(mParent.mTracker.mLeafs->getRange()); + (*this)(mParent.mTracker.leafs().getRange()); } else if (mode == PARALLEL_FOR) { - tbb::parallel_for(mParent.mTracker.mLeafs->getRange(mParent.mTracker.getGrainSize()), *this); + tbb::parallel_for(mParent.mTracker.leafs().getRange(mParent.mTracker.getGrainSize()), *this); } else if (mode == PARALLEL_REDUCE) { - tbb::parallel_reduce(mParent.mTracker.mLeafs->getRange(mParent.mTracker.getGrainSize()), *this); + tbb::parallel_reduce(mParent.mTracker.leafs().getRange(mParent.mTracker.getGrainSize()), *this); } else { throw std::runtime_error("Undefined threading mode"); } - mParent.mTracker.mLeafs->swapLeafBuffer(swapBuffer, mParent.mTracker.getGrainSize()==0); + mParent.mTracker.leafs().swapLeafBuffer(swapBuffer, mParent.mTracker.getGrainSize()==0); mParent.mTracker.endInterrupter(); } @@ -657,13 +654,14 @@ euler1(const RangeType& range, ScalarType dt, Index resultBuffer) typedef typename Scheme::template ISStencil<GridType>::StencilType Stencil; typedef typename LeafType::ValueOnCIter VoxelIterT; mParent.mTracker.checkInterrupter(); - const MapT& map = mMap; - Stencil stencil(mParent.mTracker.mGrid); + const MapT& map = *mMap; + typename TrackerT::LeafManagerType& leafs = mParent.mTracker.leafs(); + Stencil stencil(mParent.mTracker.grid()); for (size_t n=range.begin(), e=range.end(); n != e; ++n) { - BufferType& result = mParent.mTracker.mLeafs->getBuffer(n, resultBuffer); + BufferType& result = leafs.getBuffer(n, resultBuffer); const VectorType* vec = mVec[n]; int m=0; - for (VoxelIterT iter = mParent.mTracker.mLeafs->leaf(n).cbeginValueOn(); iter; ++iter, ++m) { + for (VoxelIterT iter = leafs.leaf(n).cbeginValueOn(); iter; ++iter, ++m) { stencil.moveTo(iter); const VectorType V = vec[m], G = math::GradientBiased<MapT, SpatialScheme>::result(map, stencil, V); result.setValue(iter.pos(), *iter - dt * V.dot(G)); @@ -685,15 +683,16 @@ euler2(const RangeType& range, ScalarType dt, ScalarType alpha, Index phiBuffer, typedef typename Scheme::template ISStencil<GridType>::StencilType Stencil; typedef typename LeafType::ValueOnCIter VoxelIterT; mParent.mTracker.checkInterrupter(); - const MapT& map = mMap; + const MapT& map = *mMap; + typename TrackerT::LeafManagerType& leafs = mParent.mTracker.leafs(); const ScalarType beta = ScalarType(1.0) - alpha; - Stencil stencil(mParent.mTracker.mGrid); + Stencil stencil(mParent.mTracker.grid()); for (size_t n=range.begin(), e=range.end(); n != e; ++n) { - const BufferType& phi = mParent.mTracker.mLeafs->getBuffer(n, phiBuffer); - BufferType& result = mParent.mTracker.mLeafs->getBuffer(n, resultBuffer); + const BufferType& phi = leafs.getBuffer(n, phiBuffer); + BufferType& result = leafs.getBuffer(n, resultBuffer); const VectorType* vec = mVec[n]; int m=0; - for (VoxelIterT iter = mParent.mTracker.mLeafs->leaf(n).cbeginValueOn(); iter; ++iter, ++m) { + for (VoxelIterT iter = leafs.leaf(n).cbeginValueOn(); iter; ++iter, ++m) { stencil.moveTo(iter); const VectorType V = vec[m], G = math::GradientBiased<MapT, SpatialScheme>::result(map, stencil, V); result.setValue(iter.pos(), alpha*phi[iter.pos()] + beta*(*iter - dt * V.dot(G))); diff --git a/extern/openvdb/internal/openvdb/tools/LevelSetFilter.h b/extern/openvdb/internal/openvdb/tools/LevelSetFilter.h index a0e1dcb842f..ea3c478d88c 100644 --- a/extern/openvdb/internal/openvdb/tools/LevelSetFilter.h +++ b/extern/openvdb/internal/openvdb/tools/LevelSetFilter.h @@ -36,104 +36,217 @@ /// interface tracking. These unrestricted deformations include /// surface smoothing (e.g., Laplacian flow), filtering (e.g., mean /// value) and morphological operations (e.g., morphological opening). +/// All these operations can optionally be masked with another grid that +/// acts as an alpha-mask. #ifndef OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED #define OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED +#include <assert.h> +#include <boost/type_traits/is_floating_point.hpp> #include "LevelSetTracker.h" +#include "Interpolation.h" namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { namespace tools { -/// @brief Filtering (i.e. diffusion) of narrow-band level sets +/// @brief Filtering (e.g. diffusion) of narrow-band level sets. An +/// optional scalar field can be used to produce a (smooth) alpha mask +/// for the filtering. /// /// @note This class performs propper interface tracking which allows /// for unrestricted surface deformations template<typename GridT, + typename MaskT = typename GridT::template ValueConverter<float>::Type, typename InterruptT = util::NullInterrupter> class LevelSetFilter : public LevelSetTracker<GridT, InterruptT> { public: - typedef boost::shared_ptr<LevelSetFilter> Ptr; - typedef LevelSetTracker<GridT, InterruptT> BaseType; - typedef GridT GridType; - typedef typename GridType::TreeType TreeType; - typedef typename TreeType::LeafNodeType LeafType; - typedef typename LeafType::ValueType ValueType; - typedef typename tree::LeafManager<TreeType> LeafManagerType; - typedef typename LeafManagerType::RangeType RangeType; - typedef typename LeafManagerType::BufferType BufferType; - - /// Main constructor + typedef LevelSetTracker<GridT, InterruptT> BaseType; + typedef GridT GridType; + typedef MaskT MaskType; + typedef typename GridType::TreeType TreeType; + typedef typename TreeType::ValueType ValueType; + typedef typename MaskType::ValueType AlphaType; + typedef typename tree::LeafManager<TreeType>::LeafRange RangeType; + BOOST_STATIC_ASSERT(boost::is_floating_point<AlphaType>::value); + + /// @brief Main constructor from a grid + /// @param grid The level set to be filtered. + /// @param interrupt Optional interrupter. LevelSetFilter(GridType& grid, InterruptT* interrupt = NULL) - : BaseType(grid, interrupt), mTask(0) + : BaseType(grid, interrupt) + , mTask(0) + , mMask(NULL) + , mMinMask(0) + , mMaxMask(1) + , mInvertMask(false) { } - /// Shallow copy constructor called by tbb::parallel_for() threads during filtering + /// @brief Shallow copy constructor called by tbb::parallel_for() + /// threads during filtering. + /// @param other The other LevelSetFilter from which to copy. LevelSetFilter(const LevelSetFilter& other) - : BaseType(other), mTask(other.mTask) + : BaseType(other) + , mTask(other.mTask) + , mMask(other.mMask) + , mMinMask(other.mMinMask) + , mMaxMask(other.mMaxMask) + , mInvertMask(other.mInvertMask) { } + /// @brief Destructor virtual ~LevelSetFilter() {}; - /// Used internally by tbb::parallel_for() - void operator()(const RangeType& r) const + /// @brief Used internally by tbb::parallel_for(). + /// @param range The range over which to perform multi-threading. + /// @warning Never call this method directly! + void operator()(const RangeType& range) const { - if (mTask) mTask(const_cast<LevelSetFilter*>(this), r); + if (mTask) mTask(const_cast<LevelSetFilter*>(this), range); else OPENVDB_THROW(ValueError, "task is undefined - call offset(), etc"); } - /// @brief One iteration of mean-curvature flow of the level set - void meanCurvature(); + /// @brief Return the minimum value of the mask to be used for the + /// derivation of a smooth alpha value. + AlphaType minMask() const { return mMinMask; } + /// @brief Return the maximum value of the mask to be used for the + /// derivation of a smooth alpha value. + AlphaType maxMask() const { return mMaxMask; } + /// @brief Define the range for the (optional) scalar mask. + /// @param min Minimum value of the range. + /// @param max Maximum value of the range. + /// @details Mask values outside the range maps to alpha values of + /// respectfully zero and one, and values inside the range maps + /// smoothly to 0->1 (unless of course the mask is inverted). + /// @throw ValueError if @a min is not smaller then @a max. + void setMaskRange(AlphaType min, AlphaType max) + { + if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)"); + mMinMask = min; + mMaxMask = max; + } + + /// @brief Return true if the mask is inverted, i.e. min->max in the + /// original mask maps to 1->0 in the inverted alpha mask. + bool isMaskInverted() const { return mInvertMask; } + /// @brief Invert the optional mask, i.e. min->max in the original + /// mask maps to 1->0 in the inverted alpha mask. + void invertMask(bool invert=true) { mInvertMask = invert; } - /// @brief One iteration of laplacian flow of the level set - void laplacian(); + /// @brief One iteration of mean-curvature flow of the level set. + /// @param mask Optional alpha mask. + void meanCurvature(const MaskType* mask = NULL); + + /// @brief One iteration of laplacian flow of the level set. + /// @param mask Optional alpha mask. + void laplacian(const MaskType* mask = NULL); /// @brief One iteration of a fast separable gaussian filter. + /// @param width Width of the gaussian kernel in voxel units. + /// @param mask Optional alpha mask. /// /// @note This is approximated as 4 iterations of a separable mean filter /// which typically leads an approximation that's better than 95%! - void gaussian(int width = 1); + void gaussian(int width = 1, const MaskType* mask = NULL); - /// @brief Offset the level set by the specified (world) distance - void offset(ValueType offset); + /// @brief Offset the level set by the specified (world) distance. + /// @param offset Value of the offset. + /// @param mask Optional alpha mask. + void offset(ValueType offset, const MaskType* mask = NULL); - /// @brief One iteration of median-value flow of the level set + /// @brief One iteration of median-value flow of the level set. + /// @param width Width of the median-value kernel in voxel units. + /// @param mask Optional alpha mask. /// - /// @note This filter is not separable and is hence relatively slow! - void median(int width = 1); + /// @warning This filter is not separable and is hence relatively + /// slow! + void median(int width = 1, const MaskType* mask = NULL); - /// @brief One iteration of mean-value flow of the level set + /// @brief One iteration of mean-value flow of the level set. + /// @param width Width of the mean-value kernel in voxel units. + /// @param mask Optional alpha mask. /// /// @note This filter is separable so it's fast! - void mean(int width = 1); + void mean(int width = 1, const MaskType* mask = NULL); private: - typedef typename boost::function<void (LevelSetFilter*, const RangeType&)> FuncType; - - FuncType mTask; + typedef typename TreeType::LeafNodeType LeafT; + typedef typename LeafT::ValueOnIter VoxelIterT; + typedef typename LeafT::ValueOnCIter VoxelCIterT; + typedef typename tree::LeafManager<TreeType>::BufferType BufferT; + typedef typename RangeType::Iterator LeafIterT; + + // Only two private member data + typename boost::function<void (LevelSetFilter*, const RangeType&)> mTask; + const MaskType* mMask; + AlphaType mMinMask, mMaxMask; + bool mInvertMask; // Private cook method calling tbb::parallel_for void cook(bool swap) { const int n = BaseType::getGrainSize(); if (n>0) { - tbb::parallel_for(BaseType::mLeafs->getRange(n), *this); + tbb::parallel_for(BaseType::leafs().leafRange(n), *this); } else { - (*this)(BaseType::mLeafs->getRange()); + (*this)(BaseType::leafs().leafRange()); } - if (swap) BaseType::mLeafs->swapLeafBuffer(1, n==0); + if (swap) BaseType::leafs().swapLeafBuffer(1, n==0); } - // Prive driver method for mean and gaussian filtering + // Private class to derive the normalized alpha mask + struct AlphaMask + { + AlphaMask(const GridType& grid, const MaskType& mask, + AlphaType min, AlphaType max, bool invert) + : mAcc(mask.tree()), mSampler(mAcc, mask.transform(), grid.transform()), + mMin(min), mInvNorm(1/(max-min)), mInvert(invert) + { + assert(min < max); + } + inline bool operator()(const Coord& xyz, AlphaType& a, AlphaType& b) const + { + a = mSampler(xyz); + const AlphaType t = (a-mMin)*mInvNorm; + a = t > 0 ? t < 1 ? (3-2*t)*t*t : 1 : 0;//smooth mapping to 0->1 + b = 1 - a; + if (mInvert) std::swap(a,b); + return a>0; + } + typedef typename MaskType::ConstAccessor AccType; + AccType mAcc; + tools::DualGridSampler<AccType, tools::BoxSampler> mSampler; + const AlphaType mMin, mInvNorm; + const bool mInvert; + }; + + // Private driver method for mean and gaussian filtering void box(int width); + template <size_t Axis> + struct Avg { + Avg(const GridT& grid, Int32 w) : + acc(grid.tree()), width(w), frac(1/ValueType(2*w+1)) {} + ValueType operator()(Coord xyz) { + ValueType sum = zeroVal<ValueType>(); + Int32& i = xyz[Axis], j = i + width; + for (i -= width; i <= j; ++i) sum += acc.getValue(xyz); + return sum*frac; + } + typename GridT::ConstAccessor acc; + const Int32 width; + const ValueType frac; + }; + // Private methods called by tbb::parallel_for threads - void doBoxX(const RangeType&, Int32); - void doBoxY(const RangeType&, Int32); - void doBoxZ(const RangeType&, Int32); + template <typename AvgT> + void doBox( const RangeType& r, Int32 w); + void doBoxX(const RangeType& r, Int32 w) { this->doBox<Avg<0> >(r,w); } + void doBoxZ(const RangeType& r, Int32 w) { this->doBox<Avg<1> >(r,w); } + void doBoxY(const RangeType& r, Int32 w) { this->doBox<Avg<2> >(r,w); } void doMedian(const RangeType&, int); void doMeanCurvature(const RangeType&); void doLaplacian(const RangeType&); @@ -144,13 +257,15 @@ private: //////////////////////////////////////// -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline void -LevelSetFilter<GridT, InterruptT>::median(int width) +LevelSetFilter<GridT, MaskT, InterruptT>::median(int width, const MaskType* mask) { + mMask = mask; + BaseType::startInterrupter("Median-value flow of level set"); - BaseType::mLeafs->rebuildAuxBuffers(1, BaseType::getGrainSize()==0); + BaseType::leafs().rebuildAuxBuffers(1, BaseType::getGrainSize()==0); mTask = boost::bind(&LevelSetFilter::doMedian, _1, _2, std::max(1, width)); this->cook(true); @@ -160,11 +275,12 @@ LevelSetFilter<GridT, InterruptT>::median(int width) BaseType::endInterrupter(); } - -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline void -LevelSetFilter<GridT, InterruptT>::mean(int width) +LevelSetFilter<GridT, MaskT, InterruptT>::mean(int width, const MaskType* mask) { + mMask = mask; + BaseType::startInterrupter("Mean-value flow of level set"); this->box(width); @@ -172,10 +288,12 @@ LevelSetFilter<GridT, InterruptT>::mean(int width) BaseType::endInterrupter(); } -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline void -LevelSetFilter<GridT, InterruptT>::gaussian(int width) +LevelSetFilter<GridT, MaskT, InterruptT>::gaussian(int width, const MaskType* mask) { + mMask = mask; + BaseType::startInterrupter("Gaussian flow of level set"); for (int n=0; n<4; ++n) this->box(width); @@ -183,11 +301,11 @@ LevelSetFilter<GridT, InterruptT>::gaussian(int width) BaseType::endInterrupter(); } -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline void -LevelSetFilter<GridT, InterruptT>::box(int width) +LevelSetFilter<GridT, MaskT, InterruptT>::box(int width) { - BaseType::mLeafs->rebuildAuxBuffers(1, BaseType::getGrainSize()==0); + BaseType::leafs().rebuildAuxBuffers(1, BaseType::getGrainSize()==0); width = std::max(1, width); @@ -203,14 +321,15 @@ LevelSetFilter<GridT, InterruptT>::box(int width) BaseType::track(); } - -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline void -LevelSetFilter<GridT, InterruptT>::meanCurvature() +LevelSetFilter<GridT, MaskT, InterruptT>::meanCurvature(const MaskType* mask) { + mMask = mask; + BaseType::startInterrupter("Mean-curvature flow of level set"); - BaseType::mLeafs->rebuildAuxBuffers(1, BaseType::getGrainSize()==0); + BaseType::leafs().rebuildAuxBuffers(1, BaseType::getGrainSize()==0); mTask = boost::bind(&LevelSetFilter::doMeanCurvature, _1, _2); this->cook(true); @@ -220,13 +339,15 @@ LevelSetFilter<GridT, InterruptT>::meanCurvature() BaseType::endInterrupter(); } -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline void -LevelSetFilter<GridT, InterruptT>::laplacian() +LevelSetFilter<GridT, MaskT, InterruptT>::laplacian(const MaskType* mask) { + mMask = mask; + BaseType::startInterrupter("Laplacian flow of level set"); - BaseType::mLeafs->rebuildAuxBuffers(1, BaseType::getGrainSize()==0); + BaseType::leafs().rebuildAuxBuffers(1, BaseType::getGrainSize()==0); mTask = boost::bind(&LevelSetFilter::doLaplacian, _1, _2); this->cook(true); @@ -236,14 +357,15 @@ LevelSetFilter<GridT, InterruptT>::laplacian() BaseType::endInterrupter(); } - -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline void -LevelSetFilter<GridT, InterruptT>::offset(ValueType value) +LevelSetFilter<GridT, MaskT, InterruptT>::offset(ValueType value, const MaskType* mask) { + mMask = mask; + BaseType::startInterrupter("Offsetting level set"); - BaseType::mLeafs->removeAuxBuffers();// no auxiliary buffers required + BaseType::leafs().removeAuxBuffers();// no auxiliary buffers required const ValueType CFL = ValueType(0.5) * BaseType::voxelSize(), offset = openvdb::math::Abs(value); ValueType dist = 0.0; @@ -251,7 +373,7 @@ LevelSetFilter<GridT, InterruptT>::offset(ValueType value) const ValueType delta = openvdb::math::Min(offset-dist, CFL); dist += delta; - mTask = boost::bind(&LevelSetFilter::doOffset, _1, _2, copysign(delta,value)); + mTask = boost::bind(&LevelSetFilter::doOffset, _1, _2, copysign(delta, value)); this->cook(false); BaseType::track(); @@ -264,20 +386,34 @@ LevelSetFilter<GridT, InterruptT>::offset(ValueType value) ///////////////////////// PRIVATE METHODS ////////////////////// /// Performs parabolic mean-curvature diffusion -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline void -LevelSetFilter<GridT, InterruptT>::doMeanCurvature(const RangeType& range) +LevelSetFilter<GridT, MaskT, InterruptT>::doMeanCurvature(const RangeType& range) { - typedef typename LeafType::ValueOnCIter VoxelIterT; BaseType::checkInterrupter(); //const float CFL = 0.9f, dt = CFL * mDx * mDx / 6.0f; - const ValueType dx = BaseType::voxelSize(),dt = math::Pow2(dx) / ValueType(3.0); - math::CurvatureStencil<GridType> stencil(BaseType::mGrid, dx); - for (size_t n=range.begin(), e=range.end(); n != e; ++n) { - BufferType& buffer = BaseType::mLeafs->getBuffer(n,1); - for (VoxelIterT iter = BaseType::mLeafs->leaf(n).cbeginValueOn(); iter; ++iter) { - stencil.moveTo(iter); - buffer.setValue(iter.pos(), stencil.getValue() + dt * stencil.meanCurvatureNormGrad()); + const ValueType dx = BaseType::voxelSize(), dt = math::Pow2(dx) / ValueType(3.0); + math::CurvatureStencil<GridType> stencil(BaseType::grid(), dx); + if (mMask) { + AlphaType a, b; + AlphaMask alpha(BaseType::grid(), *mMask, mMinMask, mMaxMask, mInvertMask); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) { + stencil.moveTo(iter); + const ValueType phi0 = *iter, phi1 = phi0 + dt*stencil.meanCurvatureNormGrad(); + buffer.setValue(iter.pos(), b*phi0 + a*phi1); + } + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + stencil.moveTo(iter); + buffer.setValue(iter.pos(), *iter + dt*stencil.meanCurvatureNormGrad()); + } } } } @@ -289,117 +425,119 @@ LevelSetFilter<GridT, InterruptT>::doMeanCurvature(const RangeType& range) /// expensive! In other words if you're performing renormalization /// anyway (e.g. rebuilding the narrow-band) you should consider /// performing laplacian diffusion over mean curvature flow! -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline void -LevelSetFilter<GridT, InterruptT>::doLaplacian(const RangeType& range) +LevelSetFilter<GridT, MaskT, InterruptT>::doLaplacian(const RangeType& range) { - typedef typename LeafType::ValueOnCIter VoxelIterT; BaseType::checkInterrupter(); //const float CFL = 0.9f, half_dt = CFL * mDx * mDx / 12.0f; const ValueType dx = BaseType::voxelSize(), dt = math::Pow2(dx) / ValueType(6.0); - math::GradStencil<GridType> stencil(BaseType::mGrid, dx); - for (size_t n=range.begin(), e=range.end(); n != e; ++n) { - BufferType& buffer = BaseType::mLeafs->getBuffer(n,1); - for (VoxelIterT iter = BaseType::mLeafs->leaf(n).cbeginValueOn(); iter; ++iter) { - stencil.moveTo(iter); - buffer.setValue(iter.pos(), stencil.getValue() + dt * stencil.laplacian()); + math::GradStencil<GridType> stencil(BaseType::grid(), dx); + if (mMask) { + AlphaType a, b; + AlphaMask alpha(BaseType::grid(), *mMask, mMinMask, mMaxMask, mInvertMask); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) { + stencil.moveTo(iter); + const ValueType phi0 = *iter, phi1 = phi0 + dt*stencil.laplacian(); + buffer.setValue(iter.pos(), b*phi0 + a*phi1); + } + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + stencil.moveTo(iter); + buffer.setValue(iter.pos(), *iter + dt*stencil.laplacian()); + } } } } /// Offsets the values by a constant -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline void -LevelSetFilter<GridT, InterruptT>::doOffset(const RangeType& range, ValueType value) +LevelSetFilter<GridT, MaskT, InterruptT>::doOffset(const RangeType& range, ValueType offset) { BaseType::checkInterrupter(); - for (size_t n=range.begin(), e=range.end(); n != e; ++n) - BaseType::mLeafs->leaf(n).addValue(value); + if (mMask) { + AlphaType a, b; + AlphaMask alpha(BaseType::grid(), *mMask, mMinMask, mMaxMask, mInvertMask); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) iter.setValue(*iter + a*offset); + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) { + iter.setValue(*iter + offset); + } + } + } } /// Performs simple but slow median-value diffusion -template<typename GridT, typename InterruptT> +template<typename GridT, typename MaskT, typename InterruptT> inline void -LevelSetFilter<GridT, InterruptT>::doMedian(const RangeType& range, int width) +LevelSetFilter<GridT, MaskT, InterruptT>::doMedian(const RangeType& range, int width) { - typedef typename LeafType::ValueOnCIter VoxelIterT; BaseType::checkInterrupter(); - typename math::DenseStencil<GridType> stencil(BaseType::mGrid, width);//creates local cache! - for (size_t n=range.begin(), e=range.end(); n != e; ++n) { - BufferType& buffer = BaseType::mLeafs->getBuffer(n,1); - for (VoxelIterT iter=BaseType::mLeafs->leaf(n).cbeginValueOn(); iter; ++iter) { - stencil.moveTo(iter); - buffer.setValue(iter.pos(), stencil.median()); + typename math::DenseStencil<GridType> stencil(BaseType::grid(), width);//creates local cache! + if (mMask) { + AlphaType a, b; + AlphaMask alpha(BaseType::grid(), *mMask, mMinMask, mMaxMask, mInvertMask); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) { + stencil.moveTo(iter); + buffer.setValue(iter.pos(), b*(*iter) + a*stencil.median()); + } + } } - } -} - -/// X convolution of a separable box filter -template<typename GridT, typename InterruptT> -inline void -LevelSetFilter<GridT, InterruptT>::doBoxX(const RangeType& range, Int32 w) -{ - this->checkInterrupter(); - const ValueType frac = ValueType(1)/ValueType(2*w+1); - typename GridT::ConstAccessor acc = BaseType::mGrid.getConstAccessor(); - for (size_t n=range.begin(), nLast = range.end(); n != nLast; ++n) { - const LeafType& leaf = BaseType::mLeafs->leaf(n); - BufferType& buffer = BaseType::mLeafs->getBuffer(n, 1); - for (typename LeafType::ValueOnCIter iter = leaf.cbeginValueOn(); iter; ++iter) { - ValueType sum = zeroVal<ValueType>(); - math::Coord xyz = iter.getCoord(); - for (Int32 x = xyz.x()-w, xLast = xyz.x()+w; x <= xLast; ++x) { - sum += acc.getValue(xyz.setX(x)); + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + stencil.moveTo(iter); + buffer.setValue(iter.pos(), stencil.median()); } - buffer.setValue(iter.pos(), sum*frac); } } } -/// Y convolution of a separable box filter -template<typename GridT, typename InterruptT> +/// One dimensional convolution of a separable box filter +template<typename GridT, typename MaskT, typename InterruptT> +template <typename AvgT> inline void -LevelSetFilter<GridT, InterruptT>::doBoxY(const RangeType& range, Int32 w) +LevelSetFilter<GridT, MaskT, InterruptT>::doBox(const RangeType& range, Int32 w) { - this->checkInterrupter(); - const ValueType frac = ValueType(1)/ValueType(2*w+1); - typename GridT::ConstAccessor acc = BaseType::mGrid.getConstAccessor(); - for (size_t n=range.begin(), nLast = range.end(); n != nLast; ++n) { - const LeafType& leaf = BaseType::mLeafs->leaf(n); - BufferType& buffer = BaseType::mLeafs->getBuffer(n, 1); - for (typename LeafType::ValueOnCIter iter = leaf.cbeginValueOn(); iter; ++iter) { - ValueType sum = zeroVal<ValueType>(); - math::Coord xyz = iter.getCoord(); - for (Int32 y = xyz.y()-w, yLast = xyz.y()+w; y <= yLast; ++y) { - sum += acc.getValue(xyz.setY(y)); + BaseType::checkInterrupter(); + AvgT avg(BaseType::grid(), w); + if (mMask) { + AlphaType a, b; + AlphaMask alpha(BaseType::grid(), *mMask, mMinMask, mMaxMask, mInvertMask); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + const Coord xyz = iter.getCoord(); + if (alpha(xyz, a, b)) buffer.setValue(iter.pos(), b*(*iter)+ a*avg(xyz)); } - buffer.setValue(iter.pos(), sum*frac); } - } -} - -/// Z convolution of a separable box filter -template<typename GridT, typename InterruptT> -inline void -LevelSetFilter<GridT, InterruptT>::doBoxZ(const RangeType& range, Int32 w) -{ - this->checkInterrupter(); - const ValueType frac = ValueType(1)/ValueType(2*w+1); - typename GridT::ConstAccessor acc = BaseType::mGrid.getConstAccessor(); - for (size_t n=range.begin(), nLast = range.end(); n != nLast; ++n) { - const LeafType& leaf = BaseType::mLeafs->leaf(n); - BufferType& buffer = BaseType::mLeafs->getBuffer(n, 1); - for (typename LeafType::ValueOnCIter iter = leaf.cbeginValueOn(); iter; ++iter) { - ValueType sum = zeroVal<ValueType>(); - math::Coord xyz = iter.getCoord(); - for (Int32 z = xyz.z()-w, zLast = xyz.z()+w; z <= zLast; ++z) { - sum += acc.getValue(xyz.setZ(z)); + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + buffer.setValue(iter.pos(), avg(iter.getCoord())); } - buffer.setValue(iter.pos(), sum*frac); } } } - + } // namespace tools } // namespace OPENVDB_VERSION_NAME } // namespace openvdb diff --git a/extern/openvdb/internal/openvdb/tools/LevelSetMeasure.h b/extern/openvdb/internal/openvdb/tools/LevelSetMeasure.h new file mode 100644 index 00000000000..600900e0166 --- /dev/null +++ b/extern/openvdb/internal/openvdb/tools/LevelSetMeasure.h @@ -0,0 +1,560 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +// +// Redistributions of source code must retain the above copyright +// and license notice and the following restrictions and disclaimer. +// +// * Neither the name of DreamWorks Animation nor the names of +// its contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE +// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. +// +/////////////////////////////////////////////////////////////////////////// +// +/// @author Ken Museth +/// +/// @file LevelSetMeasure.h + +#ifndef OPENVDB_TOOLS_LEVELSETMEASURE_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVELSETMEASURE_HAS_BEEN_INCLUDED + +#include <tbb/parallel_for.h> +#include <tbb/parallel_sort.h> +#include <boost/bind.hpp> +#include <boost/function.hpp> +#include <boost/type_traits/is_floating_point.hpp> +#include <boost/utility/enable_if.hpp> +#include <boost/math/constants/constants.hpp>//for Pi +#include <openvdb/math/Math.h> +#include <openvdb/Types.h> +#include <openvdb/Grid.h> +#include <openvdb/tree/LeafManager.h> +#include <openvdb/tree/ValueAccessor.h> +#include <openvdb/math/FiniteDifference.h> +#include <openvdb/math/Operators.h> +#include <openvdb/util/NullInterrupter.h> + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Return the surface area of a narrow-band level set. +/// +/// @param grid a scalar, floating-point grid with one or more disjoint, +/// closed isosurfaces at the given @a isovalue +/// @param useWorldSpace if true the area is computed in +/// world space units, else in voxel units. +/// +/// @throw TypeError if @a grid is not scalar or not floating-point or not a level set. +template<class GridType> +inline Real +levelSetArea(const GridType& grid, bool useWorldSpace = true); + +/// @brief Return the volume of a narrow-band level set surface. +/// +/// @param grid a scalar, floating-point grid with one or more disjoint, +/// closed isosurfaces at the given @a isovalue +/// @param useWorldSpace if true the volume is computed in +/// world space units, else in voxel units. +/// +/// @throw TypeError if @a grid is not scalar or not floating-point or not a level set. +template<class GridType> +inline Real +levelSetVolume(const GridType& grid, bool useWorldSpace = true); + +/// @brief Compute the surface area and volume of a narrow-band level set. +/// +/// @param grid a scalar, floating-point grid with one or more disjoint, +/// closed isosurfaces at the given @a isovalue +/// @param area surface area of the level set +/// @param volume volume of the level set surface +/// @param useWorldSpace if true the area and volume are computed in +/// world space units, else in voxel units. +/// +/// @throw TypeError if @a grid is not scalar or not floating-point or not a level set. +template<class GridType> +inline void +levelSetMeasure(const GridType& grid, Real& area, Real& volume, bool useWorldSpace = true); + +/// @brief Compute the surface area and volume of a narrow-band level set. +/// +/// @param grid a scalar, floating-point grid with one or more disjoint, +/// closed isosurfaces at the given @a isovalue +/// @param area surface area of the level set +/// @param volume volume of the level set surface +/// @param avgCurvature average mean curvature of the level set surface +/// @param useWorldSpace if true the area, volume and curvature are computed in +/// world space units, else in voxel units. +/// +/// @throw TypeError if @a grid is not scalar or not floating-point or not a level set. +template<class GridType> +inline void +levelSetMeasure(const GridType& grid, Real& area, Real& volume, Real& avgCurvature, + bool useWorldSpace = true); + +/// @brief Smeared-out and continuous Dirac Delta function. +template<typename RealT> +class DiracDelta +{ +public: + DiracDelta(RealT eps) : mC(0.5/eps), mD(2*boost::math::constants::pi<RealT>()*mC), mE(eps) {} + inline RealT operator()(RealT phi) const { return math::Abs(phi) > mE ? 0 : mC*(1+cos(mD*phi)); } +private: + const RealT mC, mD, mE; +}; + + +/// @brief Multi-threaded computation of surface area, volume and +/// average mean-curvature for narrow band level sets. +/// +/// @details To reduce the risk of round-off errors (primarily due to +/// catastrophic cancellation) and guarantee determinism during +/// multi-threading this class is implemented using parallel_for, and +/// delayed reduction of a sorted list. +template<typename GridT, + typename InterruptT = util::NullInterrupter> +class LevelSetMeasure +{ +public: + typedef GridT GridType; + typedef typename GridType::TreeType TreeType; + typedef typename TreeType::ValueType ValueType; + typedef typename tree::LeafManager<const TreeType> ManagerType; + typedef typename ManagerType::LeafRange RangeType; + BOOST_STATIC_ASSERT(boost::is_floating_point<ValueType>::value); + + /// @brief Main constructor from a grid + /// @param grid The level set to be measured. + /// @param interrupt Optional interrupter. + /// @throw RuntimeError if the grid is not a level set. + LevelSetMeasure(const GridType& grid, InterruptT* interrupt = NULL); + + LevelSetMeasure(ManagerType& leafs, Real Dx, InterruptT* interrupt); + + /// @brief Re-initialize using the specified grid. + void reinit(const GridType& grid); + + /// @brief Re-initialize using the specified LeafManager and voxelSize. + void reinit(ManagerType& leafs, Real dx); + + /// @brief Destructor + ~LevelSetMeasure() {} + + /// @return the grain-size used for multi-threading + int getGrainSize() const { return mGrainSize; } + + /// @brief Set the grain-size used for multi-threading. + /// @note A grainsize of 0 or less disables multi-threading! + void setGrainSize(int grainsize) { mGrainSize = grainsize; } + + /// @brief Compute the surface area and volume of the level + /// set. Use the last argument to specify the result in world or + /// voxel units. + /// @note This method is faster (about 3x) then the measure method + /// below that also computes the average mean-curvature. + void measure(Real& area, Real& volume, bool useWorldUnits = true); + + /// @brief Compute the surface area, volume, and average + /// mean-curvatue of the level set. Use the last argument to + /// specify the result in world or voxel units. + /// @note This method is slower (about 3x) then the measure method + /// above that only computes the area and volume. + void measure(Real& area, Real& volume, Real& avgMeanCurvature, bool useWorldUnits = true); + + /// @brief Used internally by tbb::parallel_reduce(). + /// @param range The range over which to perform multi-threading. + /// @warning Never call this method directly! + void operator()(const RangeType& range) const + { + if (mTask) mTask(const_cast<LevelSetMeasure*>(this), range); + else OPENVDB_THROW(ValueError, "task is undefined"); + } + +private: + typedef typename GridT::ConstAccessor AccT; + typedef typename TreeType::LeafNodeType LeafT; + typedef typename LeafT::ValueOnCIter VoxelCIterT; + typedef typename ManagerType::BufferType BufferT; + typedef typename RangeType::Iterator LeafIterT; + + AccT mAcc; + ManagerType* mLeafs; + InterruptT* mInterrupter; + double mDx; + double* mArray; + typename boost::function<void (LevelSetMeasure*, const RangeType&)> mTask; + int mGrainSize; + + // @brief Return false if the process was interrupted + bool checkInterrupter(); + + // Private methods called by tbb::parallel_reduce threads + void measure2( const RangeType& ); + + // Private methods called by tbb::parallel_reduce threads + void measure3( const RangeType& ); + + inline double reduce(double* first, double scale) + { + double* last = first + mLeafs->leafCount(); + tbb::parallel_sort(first, last);//reduces catastrophic cancellation + Real sum = 0.0; + while(first != last) sum += *first++; + return scale * sum; + } + +}; // end of LevelSetMeasure class + + +template<typename GridT, typename InterruptT> +inline +LevelSetMeasure<GridT, InterruptT>::LevelSetMeasure(const GridType& grid, InterruptT* interrupt) + : mAcc(grid.tree()) + , mLeafs(NULL) + , mInterrupter(interrupt) + , mDx(grid.voxelSize()[0]) + , mArray(NULL) + , mTask(0) + , mGrainSize(1) +{ + if (!grid.hasUniformVoxels()) { + OPENVDB_THROW(RuntimeError, + "The transform must have uniform scale for the LevelSetMeasure to function"); + } + if (grid.getGridClass() != GRID_LEVEL_SET) { + OPENVDB_THROW(RuntimeError, + "LevelSetMeasure only supports level sets;" + " try setting the grid class to \"level set\""); + } +} + + +template<typename GridT, typename InterruptT> +inline +LevelSetMeasure<GridT, InterruptT>::LevelSetMeasure( + ManagerType& leafs, Real dx, InterruptT* interrupt) + : mAcc(leafs.tree()) + , mLeafs(&leafs) + , mInterrupter(interrupt) + , mDx(dx) + , mArray(NULL) + , mTask(0) + , mGrainSize(1) +{ +} + +template<typename GridT, typename InterruptT> +inline void +LevelSetMeasure<GridT, InterruptT>::reinit(const GridType& grid) +{ + if (!grid.hasUniformVoxels()) { + OPENVDB_THROW(RuntimeError, + "The transform must have uniform scale for the LevelSetMeasure to function"); + } + if (grid.getGridClass() != GRID_LEVEL_SET) { + OPENVDB_THROW(RuntimeError, + "LevelSetMeasure only supports level sets;" + " try setting the grid class to \"level set\""); + } + mLeafs = NULL; + mAcc = grid.getConstAccessor(); + mDx = grid.voxelSize()[0]; +} + + +template<typename GridT, typename InterruptT> +inline void +LevelSetMeasure<GridT, InterruptT>::reinit(ManagerType& leafs, Real dx) +{ + mLeafs = &leafs; + mAcc = AccT(leafs.tree()); + mDx = dx; +} + +//////////////////////////////////////// + + +template<typename GridT, typename InterruptT> +inline void +LevelSetMeasure<GridT, InterruptT>::measure(Real& area, Real& volume, bool useWorldUnits) +{ + if (mInterrupter) mInterrupter->start("Measuring level set"); + mTask = boost::bind(&LevelSetMeasure::measure2, _1, _2); + + const bool newLeafs = mLeafs == NULL; + if (newLeafs) mLeafs = new ManagerType(mAcc.tree()); + const size_t leafCount = mLeafs->leafCount(); + if (leafCount == 0) { + area = volume = 0; + return; + } + mArray = new double[2*leafCount]; + + if (mGrainSize>0) { + tbb::parallel_for(mLeafs->leafRange(mGrainSize), *this); + } else { + (*this)(mLeafs->leafRange()); + } + + const double dx = useWorldUnits ? mDx : 1.0; + area = this->reduce(mArray, math::Pow2(dx)); + volume = this->reduce(mArray + leafCount, math::Pow3(dx) / 3.0); + + if (newLeafs) { + delete mLeafs; + mLeafs = NULL; + } + delete [] mArray; + + if (mInterrupter) mInterrupter->end(); +} + + +template<typename GridT, typename InterruptT> +inline void +LevelSetMeasure<GridT, InterruptT>::measure(Real& area, Real& volume, Real& avgMeanCurvature, + bool useWorldUnits) +{ + if (mInterrupter) mInterrupter->start("Measuring level set"); + mTask = boost::bind(&LevelSetMeasure::measure3, _1, _2); + + const bool newLeafs = mLeafs == NULL; + if (newLeafs) mLeafs = new ManagerType(mAcc.tree()); + const size_t leafCount = mLeafs->leafCount(); + if (leafCount == 0) { + area = volume = avgMeanCurvature = 0; + return; + } + mArray = new double[3*leafCount]; + + if (mGrainSize>0) { + tbb::parallel_for(mLeafs->leafRange(mGrainSize), *this); + } else { + (*this)(mLeafs->leafRange()); + } + + const double dx = useWorldUnits ? mDx : 1.0; + area = this->reduce(mArray, math::Pow2(dx)); + volume = this->reduce(mArray + leafCount, math::Pow3(dx) / 3.0); + avgMeanCurvature = this->reduce(mArray + 2*leafCount, dx/area); + + if (newLeafs) { + delete mLeafs; + mLeafs = NULL; + } + delete [] mArray; + + if (mInterrupter) mInterrupter->end(); +} + + +///////////////////////// PRIVATE METHODS ////////////////////// + + +template<typename GridT, typename InterruptT> +inline bool +LevelSetMeasure<GridT, InterruptT>::checkInterrupter() +{ + if (util::wasInterrupted(mInterrupter)) { + tbb::task::self().cancel_group_execution(); + return false; + } + return true; +} + +template<typename GridT, typename InterruptT> +inline void +LevelSetMeasure<GridT, InterruptT>::measure2(const RangeType& range) +{ + typedef math::Vec3<ValueType> Vec3T; + typedef math::ISGradient<math::CD_2ND> Grad; + this->checkInterrupter(); + const Real invDx = 1.0/mDx; + const DiracDelta<Real> DD(1.5); + const size_t leafCount = mLeafs->leafCount(); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + Real sumA = 0, sumV = 0;//reduce risk of catastrophic cancellation + for (VoxelCIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + const Real dd = DD(invDx * (*voxelIter)); + if (dd > 0.0) { + const Coord p = voxelIter.getCoord(); + const Vec3T g = invDx*Grad::result(mAcc, p);//voxel units + sumA += dd * g.dot(g); + sumV += dd * (g[0]*p[0]+g[1]*p[1]+g[2]*p[2]); + } + } + double* v = mArray + leafIter.pos(); + *v = sumA; + v += leafCount; + *v = sumV; + } +} + + +template<typename GridT, typename InterruptT> +inline void +LevelSetMeasure<GridT, InterruptT>::measure3(const RangeType& range) +{ + typedef math::Vec3<ValueType> Vec3T; + typedef math::ISGradient<math::CD_2ND> Grad; + typedef math::ISMeanCurvature<math::CD_SECOND, math::CD_2ND> Curv; + this->checkInterrupter(); + const Real invDx = 1.0/mDx; + const DiracDelta<Real> DD(1.5); + ValueType alpha, beta; + const size_t leafCount = mLeafs->leafCount(); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + Real sumA = 0, sumV = 0, sumC = 0;//reduce risk of catastrophic cancellation + for (VoxelCIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + const Real dd = DD(invDx * (*voxelIter)); + if (dd > 0.0) { + const Coord p = voxelIter.getCoord(); + const Vec3T g = invDx*Grad::result(mAcc, p);//voxel units + const Real dA = dd * g.dot(g); + sumA += dA; + sumV += dd * (g[0]*p[0]+g[1]*p[1]+g[2]*p[2]); + Curv::result(mAcc, p, alpha, beta); + sumC += dA * alpha/(2*math::Pow2(beta))*invDx; + } + } + double* v = mArray + leafIter.pos(); + *v = sumA; + v += leafCount; + *v = sumV; + v += leafCount; + *v = sumC; + } +} + +//////////////////////////////////////// + +template<class GridT> +inline typename boost::enable_if<boost::is_floating_point<typename GridT::ValueType>, Real>::type +doLevelSetArea(const GridT& grid, bool useWorldSpace) +{ + Real area, volume; + LevelSetMeasure<GridT> m(grid); + m.measure(area, volume, useWorldSpace); + return area; +} + +template<class GridT> +inline typename boost::disable_if<boost::is_floating_point<typename GridT::ValueType>, Real>::type +doLevelSetArea(const GridT&, bool) +{ + OPENVDB_THROW(TypeError, + "level set area is supported only for scalar, floating-point grids"); +} + +template<class GridT> +inline Real +levelSetArea(const GridT& grid, bool useWorldSpace) +{ + return doLevelSetArea<GridT>(grid, useWorldSpace); +} + +//////////////////////////////////////// + +template<class GridT> +inline typename boost::enable_if<boost::is_floating_point<typename GridT::ValueType>, Real>::type +doLevelSetVolume(const GridT& grid, bool useWorldSpace) +{ + Real area, volume; + LevelSetMeasure<GridT> m(grid); + m.measure(area, volume, useWorldSpace); + return volume; +} + +template<class GridT> +inline typename boost::disable_if<boost::is_floating_point<typename GridT::ValueType>, Real>::type +doLevelSetVolume(const GridT&, bool) +{ + OPENVDB_THROW(TypeError, + "level set volume is supported only for scalar, floating-point grids"); +} + +template<class GridT> +inline Real +levelSetVolume(const GridT& grid, bool useWorldSpace) +{ + return doLevelSetVolume<GridT>(grid, useWorldSpace); +} + +//////////////////////////////////////// + +template<class GridT> +inline typename boost::enable_if<boost::is_floating_point<typename GridT::ValueType> >::type +doLevelSetMeasure(const GridT& grid, Real& area, Real& volume, bool useWorldSpace) +{ + LevelSetMeasure<GridT> m(grid); + m.measure(area, volume, useWorldSpace); +} + +template<class GridT> +inline typename boost::disable_if<boost::is_floating_point<typename GridT::ValueType> >::type +doLevelSetMeasure(const GridT&, Real&, Real&, bool) +{ + OPENVDB_THROW(TypeError, + "level set measure is supported only for scalar, floating-point grids"); +} + +template<class GridT> +inline void +levelSetMeasure(const GridT& grid, Real& area, Real& volume, bool useWorldSpace) +{ + doLevelSetMeasure<GridT>(grid, area, volume, useWorldSpace); +} + +//////////////////////////////////////// + +template<class GridT> +inline typename boost::enable_if<boost::is_floating_point<typename GridT::ValueType> >::type +doLevelSetMeasure(const GridT& grid, Real& area, Real& volume, Real& avgCurvature, + bool useWorldSpace) +{ + LevelSetMeasure<GridT> m(grid); + m.measure(area, volume, avgCurvature, useWorldSpace); +} + +template<class GridT> +inline typename boost::disable_if<boost::is_floating_point<typename GridT::ValueType> >::type +doLevelSetMeasure(const GridT&, Real&, Real&, Real&, bool) +{ + OPENVDB_THROW(TypeError, + "level set measure is supported only for scalar, floating-point grids"); +} + +template<class GridT> +inline void +levelSetMeasure(const GridT& grid, Real& area, Real& volume, Real& avgCurvature, bool useWorldSpace) +{ + doLevelSetMeasure<GridT>(grid, area, volume, avgCurvature, useWorldSpace); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVELSETMEASURE_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/extern/openvdb/internal/openvdb/tools/LevelSetMorph.h b/extern/openvdb/internal/openvdb/tools/LevelSetMorph.h new file mode 100644 index 00000000000..ac8a3526e71 --- /dev/null +++ b/extern/openvdb/internal/openvdb/tools/LevelSetMorph.h @@ -0,0 +1,601 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +// +// Redistributions of source code must retain the above copyright +// and license notice and the following restrictions and disclaimer. +// +// * Neither the name of DreamWorks Animation nor the names of +// its contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE +// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. +// +/////////////////////////////////////////////////////////////////////////// +// +/////////////////////////////////////////////////////////////////////////// +// +/// @author Ken Museth +/// +/// @file LevelSetMorph.h +/// +/// @brief Shape morphology of level sets. Morphing from a source +/// narrow-band level sets to a target narrow-band level set. + +#ifndef OPENVDB_TOOLS_LEVEL_SET_MORPH_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVEL_SET_MORPH_HAS_BEEN_INCLUDED + +#include "LevelSetTracker.h" +#include "Interpolation.h" // for BoxSampler, etc. +#include <openvdb/math/FiniteDifference.h> + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +/// @brief Shape morphology of level sets. Morphing from a source +/// narrow-band level sets to a target narrow-band level set. +/// +/// @details +/// The @c InterruptType template argument below refers to any class +/// with the following interface: +/// @code +/// class Interrupter { +/// ... +/// public: +/// void start(const char* name = NULL)// called when computations begin +/// void end() // called when computations end +/// bool wasInterrupted(int percent=-1)// return true to break computation +/// }; +/// @endcode +/// +/// @note If no template argument is provided for this InterruptType, +/// the util::NullInterrupter is used, which implies that all interrupter +/// calls are no-ops (i.e., they incur no computational overhead). +template<typename GridT, + typename InterruptT = util::NullInterrupter> +class LevelSetMorphing +{ +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef LevelSetTracker<GridT, InterruptT> TrackerT; + typedef typename TrackerT::LeafRange LeafRange; + typedef typename TrackerT::LeafType LeafType; + typedef typename TrackerT::BufferType BufferType; + typedef typename TrackerT::ValueType ScalarType; + + /// Main constructor + LevelSetMorphing(GridT& sourceGrid, const GridT& targetGrid, InterruptT* interrupt = NULL): + mTracker(sourceGrid, interrupt), mTarget(&targetGrid), + mSpatialScheme(math::HJWENO5_BIAS), + mTemporalScheme(math::TVD_RK2) {} + + virtual ~LevelSetMorphing() {}; + + /// Redefine the target level set + void setTarget(const GridT& targetGrid) { mTarget = &targetGrid; } + + /// Return the spatial finite-difference scheme + math::BiasedGradientScheme getSpatialScheme() const { return mSpatialScheme; } + /// Set the spatial finite-difference scheme + void setSpatialScheme(math::BiasedGradientScheme scheme) { mSpatialScheme = scheme; } + + /// Return the temporal integration scheme + math::TemporalIntegrationScheme getTemporalScheme() const { return mTemporalScheme; } + /// Set the temporal integration scheme + void setTemporalScheme(math::TemporalIntegrationScheme scheme) { mTemporalScheme = scheme; } + + /// Return the spatial finite-difference scheme + math::BiasedGradientScheme getTrackerSpatialScheme() const + { + return mTracker.getSpatialScheme(); + } + /// Set the spatial finite-difference scheme + void setTrackerSpatialScheme(math::BiasedGradientScheme scheme) + { + mTracker.setSpatialScheme(scheme); + } + /// Return the temporal integration scheme + math::TemporalIntegrationScheme getTrackerTemporalScheme() const + { + return mTracker.getTemporalScheme(); + } + /// Set the temporal integration scheme + void setTrackerTemporalScheme(math::TemporalIntegrationScheme scheme) + { + mTracker.setTemporalScheme(scheme); + } + /// Return the number of normalizations performed per track or normalize call. + int getNormCount() const { return mTracker.getNormCount(); } + /// Set the number of normalizations performed per track or normalize call. + void setNormCount(int n) { mTracker.setNormCount(n); } + + /// Return the grain size used for multithreading + int getGrainSize() const { return mTracker.getGrainSize(); } + /// @brief Set the grain size used for multithreading. + /// @note A grain size of 0 or less disables multithreading! + void setGrainSize(int grainsize) { mTracker.setGrainSize(grainsize); } + + /// @brief Advect the level set from its current time, @a time0, to its + /// final time, @a time1. If @a time0 > @a time1, perform backward advection. + /// + /// @return the number of CFL iterations used to advect from @a time0 to @a time1 + size_t advect(ScalarType time0, ScalarType time1); + +private: + + // This templated private class implements all the level set magic. + template<typename MapT, math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> + class LevelSetMorph + { + public: + /// Main constructor + LevelSetMorph(TrackerT& tracker, const GridT* target); + /// Shallow copy constructor called by tbb::parallel_for() threads + LevelSetMorph(const LevelSetMorph& other); + /// Shallow copy constructor called by tbb::parallel_reduce() threads + LevelSetMorph(LevelSetMorph& other, tbb::split); + /// destructor + virtual ~LevelSetMorph() {} + /// Advect the level set from it's current time, time0, to it's final time, time1. + /// @return number of CFL iterations + size_t advect(ScalarType time0, ScalarType time1); + /// Used internally by tbb::parallel_for() + void operator()(const LeafRange& r) const + { + if (mTask) mTask(const_cast<LevelSetMorph*>(this), r); + else OPENVDB_THROW(ValueError, "task is undefined - don\'t call this method directly"); + } + /// Used internally by tbb::parallel_reduce() + void operator()(const LeafRange& r) + { + if (mTask) mTask(this, r); + else OPENVDB_THROW(ValueError, "task is undefined - don\'t call this method directly"); + } + /// This is only called by tbb::parallel_reduce() threads + void join(const LevelSetMorph& other) { mMaxAbsS = math::Max(mMaxAbsS, other.mMaxAbsS); } + private: + typedef typename boost::function<void (LevelSetMorph*, const LeafRange&)> FuncType; + TrackerT* mTracker; + const GridT* mTarget; + ScalarType mMinAbsS, mMaxAbsS; + const MapT* mMap; + FuncType mTask; + + /// Enum to define the type of multithreading + enum ThreadingMode { PARALLEL_FOR, PARALLEL_REDUCE }; // for internal use + // method calling tbb + void cook(ThreadingMode mode, size_t swapBuffer = 0); + + /// Sample field and return the CFT time step + typename GridT::ValueType sampleSpeed( + ScalarType time0, ScalarType time1, Index speedBuffer); + void sampleXformedSpeed(const LeafRange& r, Index speedBuffer); + void sampleAlignedSpeed(const LeafRange& r, Index speedBuffer); + + // Forward Euler advection steps: Phi(result) = Phi(0) - dt * Speed(speed)*|Grad[Phi(0)]|; + void euler1(const LeafRange& r, ScalarType dt, Index resultBuffer, Index speedBuffer); + + // Convex combination of Phi and a forward Euler advection steps: + // Phi(result) = alpha * Phi(phi) + (1-alpha) * (Phi(0) - dt * Speed(speed)*|Grad[Phi(0)]|); + void euler2(const LeafRange& r, ScalarType dt, ScalarType alpha, + Index phiBuffer, Index resultBuffer, Index speedBuffer); + + }; // end of private LevelSetMorph class + + template<math::BiasedGradientScheme SpatialScheme> + size_t advect1(ScalarType time0, ScalarType time1); + + template<math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> + size_t advect2(ScalarType time0, ScalarType time1); + + template<math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme, + typename MapType> + size_t advect3(ScalarType time0, ScalarType time1); + + TrackerT mTracker; + const GridT* mTarget; + math::BiasedGradientScheme mSpatialScheme; + math::TemporalIntegrationScheme mTemporalScheme; + + // disallow copy by assignment + void operator=(const LevelSetMorphing& other) {} + +};//end of LevelSetMorphing + +template<typename GridT, typename InterruptT> +inline size_t +LevelSetMorphing<GridT, InterruptT>::advect(ScalarType time0, ScalarType time1) +{ + switch (mSpatialScheme) { + case math::FIRST_BIAS: + return this->advect1<math::FIRST_BIAS >(time0, time1); + //case math::SECOND_BIAS: + //return this->advect1<math::SECOND_BIAS >(time0, time1); + //case math::THIRD_BIAS: + //return this->advect1<math::THIRD_BIAS >(time0, time1); + //case math::WENO5_BIAS: + //return this->advect1<math::WENO5_BIAS >(time0, time1); + case math::HJWENO5_BIAS: + return this->advect1<math::HJWENO5_BIAS>(time0, time1); + default: + OPENVDB_THROW(ValueError, "Spatial difference scheme not supported!"); + } + return 0; +} + +template<typename GridT, typename InterruptT> +template<math::BiasedGradientScheme SpatialScheme> +inline size_t +LevelSetMorphing<GridT, InterruptT>::advect1(ScalarType time0, ScalarType time1) +{ + switch (mTemporalScheme) { + case math::TVD_RK1: + return this->advect2<SpatialScheme, math::TVD_RK1>(time0, time1); + case math::TVD_RK2: + return this->advect2<SpatialScheme, math::TVD_RK2>(time0, time1); + case math::TVD_RK3: + return this->advect2<SpatialScheme, math::TVD_RK3>(time0, time1); + default: + OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); + } + return 0; +} + +template<typename GridT, typename InterruptT> +template<math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline size_t +LevelSetMorphing<GridT, InterruptT>::advect2(ScalarType time0, ScalarType time1) +{ + const math::Transform& trans = mTracker.grid().transform(); + if (trans.mapType() == math::UniformScaleMap::mapType()) { + return this->advect3<SpatialScheme, TemporalScheme, math::UniformScaleMap>(time0, time1); + } else if (trans.mapType() == math::UniformScaleTranslateMap::mapType()) { + return this->advect3<SpatialScheme, TemporalScheme, math::UniformScaleTranslateMap>( + time0, time1); + } else if (trans.mapType() == math::UnitaryMap::mapType()) { + return this->advect3<SpatialScheme, TemporalScheme, math::UnitaryMap >(time0, time1); + } else if (trans.mapType() == math::TranslationMap::mapType()) { + return this->advect3<SpatialScheme, TemporalScheme, math::TranslationMap>(time0, time1); + } else { + OPENVDB_THROW(ValueError, "MapType not supported!"); + } + return 0; +} + +template<typename GridT, typename InterruptT> +template<math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme, + typename MapT> +inline size_t +LevelSetMorphing<GridT, InterruptT>::advect3(ScalarType time0, ScalarType time1) +{ + LevelSetMorph<MapT, SpatialScheme, TemporalScheme> tmp(mTracker, mTarget); + return tmp.advect(time0, time1); +} + + +/////////////////////////////////////////////////////////////////////// + + +template<typename GridT, typename InterruptT> +template <typename MapT, math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline +LevelSetMorphing<GridT, InterruptT>:: +LevelSetMorph<MapT, SpatialScheme, TemporalScheme>:: +LevelSetMorph(TrackerT& tracker, const GridT* target): + mTracker(&tracker), + mTarget(target), + mMinAbsS(1e-6), + mMap(tracker.grid().transform().template constMap<MapT>().get()), + mTask(0) +{ +} + +template<typename GridT, typename InterruptT> +template <typename MapT, math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline +LevelSetMorphing<GridT, InterruptT>:: +LevelSetMorph<MapT, SpatialScheme, TemporalScheme>:: +LevelSetMorph(const LevelSetMorph& other): + mTracker(other.mTracker), + mTarget(other.mTarget), + mMinAbsS(other.mMinAbsS), + mMaxAbsS(other.mMaxAbsS), + mMap(other.mMap), + mTask(other.mTask) +{ +} + +template<typename GridT, typename InterruptT> +template <typename MapT, math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline +LevelSetMorphing<GridT, InterruptT>:: +LevelSetMorph<MapT, SpatialScheme, TemporalScheme>:: +LevelSetMorph(LevelSetMorph& other, tbb::split): + mTracker(other.mTracker), + mTarget(other.mTarget), + mMinAbsS(other.mMinAbsS), + mMaxAbsS(other.mMaxAbsS), + mMap(other.mMap), + mTask(other.mTask) +{ +} + +template<typename GridT, typename InterruptT> +template <typename MapT, math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline size_t +LevelSetMorphing<GridT, InterruptT>:: +LevelSetMorph<MapT, SpatialScheme, TemporalScheme>:: +advect(ScalarType time0, ScalarType time1) +{ + // Make sure we have enough temporal auxiliary buffers for the time + // integration AS WELL AS an extra buffer with the speed function! + static const Index auxBuffers = 1 + (TemporalScheme == math::TVD_RK3 ? 2 : 1); + size_t countCFL = 0; + while (time0 < time1 && mTracker->checkInterrupter()) { + mTracker->leafs().rebuildAuxBuffers(auxBuffers); + + const ScalarType dt = this->sampleSpeed(time0, time1, auxBuffers); + if ( math::isZero(dt) ) break;//V is essentially zero so terminate + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN //switch is resolved at compile-time + switch(TemporalScheme) { + case math::TVD_RK1: + // Perform one explicit Euler step: t1 = t0 + dt + // Phi_t1(1) = Phi_t0(0) - dt * Speed(2) * |Grad[Phi(0)]| + mTask = boost::bind(&LevelSetMorph::euler1, _1, _2, dt, /*result=*/1, /*speed*/2); + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook(PARALLEL_FOR, 1); + break; + case math::TVD_RK2: + // Perform one explicit Euler step: t1 = t0 + dt + // Phi_t1(1) = Phi_t0(0) - dt * Speed(2) * |Grad[Phi(0)]| + mTask = boost::bind(&LevelSetMorph::euler1, _1, _2, dt, /*result=*/1, /*speed*/2); + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook(PARALLEL_FOR, 1); + + // Convex combine explict Euler step: t2 = t0 + dt + // Phi_t2(1) = 1/2 * Phi_t0(1) + 1/2 * (Phi_t1(0) - dt * Speed(2) * |Grad[Phi(0)]|) + mTask = boost::bind(&LevelSetMorph::euler2, _1, _2, dt, ScalarType(0.5), + /*phi=*/1, /*result=*/1, /*speed*/2); + // Cook and swap buffer 0 and 1 such that Phi_t2(0) and Phi_t1(1) + this->cook(PARALLEL_FOR, 1); + break; + case math::TVD_RK3: + // Perform one explicit Euler step: t1 = t0 + dt + // Phi_t1(1) = Phi_t0(0) - dt * Speed(3) * |Grad[Phi(0)]| + mTask = boost::bind(&LevelSetMorph::euler1, _1, _2, dt, /*result=*/1, /*speed*/3); + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook(PARALLEL_FOR, 1); + + // Convex combine explict Euler step: t2 = t0 + dt/2 + // Phi_t2(2) = 3/4 * Phi_t0(1) + 1/4 * (Phi_t1(0) - dt * Speed(3) * |Grad[Phi(0)]|) + mTask = boost::bind(&LevelSetMorph::euler2, _1, _2, dt, ScalarType(0.75), + /*phi=*/1, /*result=*/2, /*speed*/3); + // Cook and swap buffer 0 and 2 such that Phi_t2(0) and Phi_t1(2) + this->cook(PARALLEL_FOR, 2); + + // Convex combine explict Euler step: t3 = t0 + dt + // Phi_t3(2) = 1/3 * Phi_t0(1) + 2/3 * (Phi_t2(0) - dt * Speed(3) * |Grad[Phi(0)]|) + mTask = boost::bind(&LevelSetMorph::euler2, _1, _2, dt, ScalarType(1.0/3.0), + /*phi=*/1, /*result=*/2, /*speed*/3); + // Cook and swap buffer 0 and 2 such that Phi_t3(0) and Phi_t2(2) + this->cook(PARALLEL_FOR, 2); + break; + default: + OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); + }//end of compile-time resolved switch + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + + time0 += dt; + ++countCFL; + mTracker->leafs().removeAuxBuffers(); + + // Track the narrow band + mTracker->track(); + }//end wile-loop over time + + return countCFL;//number of CLF propagation steps +} + +template<typename GridT, typename InterruptT> +template<typename MapT, math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline typename GridT::ValueType +LevelSetMorphing<GridT, InterruptT>:: +LevelSetMorph<MapT, SpatialScheme, TemporalScheme>:: +sampleSpeed(ScalarType time0, ScalarType time1, Index speedBuffer) +{ + mMaxAbsS = mMinAbsS; + const size_t leafCount = mTracker->leafs().leafCount(); + if (leafCount==0 || time0 >= time1) return ScalarType(0); + + if (mTarget->transform() == mTracker->grid().transform()) { + mTask = boost::bind(&LevelSetMorph::sampleAlignedSpeed, _1, _2, speedBuffer); + } else { + mTask = boost::bind(&LevelSetMorph::sampleXformedSpeed, _1, _2, speedBuffer); + } + this->cook(PARALLEL_REDUCE); + if (math::isApproxEqual(mMinAbsS, mMaxAbsS)) return ScalarType(0);//speed is essentially zero + static const ScalarType CFL = (TemporalScheme == math::TVD_RK1 ? ScalarType(0.3) : + TemporalScheme == math::TVD_RK2 ? ScalarType(0.9) : + ScalarType(1.0))/math::Sqrt(ScalarType(3.0)); + const ScalarType dt = math::Abs(time1 - time0), dx = mTracker->voxelSize(); + return math::Min(dt, ScalarType(CFL*dx/mMaxAbsS)); +} + +template<typename GridT, typename InterruptT> +template <typename MapT, math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline void +LevelSetMorphing<GridT, InterruptT>:: +LevelSetMorph<MapT, SpatialScheme, TemporalScheme>:: +sampleXformedSpeed(const LeafRange& range, Index speedBuffer) +{ + typedef typename LeafType::ValueOnCIter VoxelIterT; + typedef tools::GridSampler<typename GridT::ConstAccessor, tools::BoxSampler> SamplerT; + const MapT& map = *mMap; + mTracker->checkInterrupter(); + + SamplerT sampler(mTarget->getAccessor(), mTarget->transform()); + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + BufferType& speed = leafIter.buffer(speedBuffer); + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + ScalarType& s = const_cast<ScalarType&>(speed.getValue(voxelIter.pos())); + s -= sampler.wsSample(map.applyMap(voxelIter.getCoord().asVec3d())); + mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s)); + } + } +} + +template<typename GridT, typename InterruptT> +template <typename MapT, math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline void +LevelSetMorphing<GridT, InterruptT>:: +LevelSetMorph<MapT, SpatialScheme, TemporalScheme>:: +sampleAlignedSpeed(const LeafRange& range, Index speedBuffer) +{ + typedef typename LeafType::ValueOnCIter VoxelIterT; + mTracker->checkInterrupter(); + + typename GridT::ConstAccessor target = mTarget->getAccessor(); + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + BufferType& speed = leafIter.buffer(speedBuffer); + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + ScalarType& s = const_cast<ScalarType&>(speed.getValue(voxelIter.pos())); + s -= target.getValue(voxelIter.getCoord()); + mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s)); + } + } +} + +template<typename GridT, typename InterruptT> +template <typename MapT, math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline void +LevelSetMorphing<GridT, InterruptT>:: +LevelSetMorph<MapT, SpatialScheme, TemporalScheme>:: +cook(ThreadingMode mode, size_t swapBuffer) +{ + mTracker->startInterrupter("Morphing level set"); + + const int grainSize = mTracker->getGrainSize(); + const LeafRange range = mTracker->leafs().leafRange(grainSize); + + if (mTracker->getGrainSize()==0) { + (*this)(range); + } else if (mode == PARALLEL_FOR) { + tbb::parallel_for(range, *this); + } else if (mode == PARALLEL_REDUCE) { + tbb::parallel_reduce(range, *this); + } else { + throw std::runtime_error("Undefined threading mode"); + } + + mTracker->leafs().swapLeafBuffer(swapBuffer, grainSize == 0); + + mTracker->endInterrupter(); +} + +// Forward Euler advection steps: +// Phi(result) = Phi(0) - dt * Phi(speed) * |Grad[Phi(0)]| +template<typename GridT, typename InterruptT> +template <typename MapT, + math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline void +LevelSetMorphing<GridT, InterruptT>:: +LevelSetMorph<MapT, SpatialScheme, TemporalScheme>:: +euler1(const LeafRange& range, ScalarType dt, Index resultBuffer, Index speedBuffer) +{ + typedef math::BIAS_SCHEME<SpatialScheme> Scheme; + typedef typename Scheme::template ISStencil<GridType>::StencilType Stencil; + typedef typename LeafType::ValueOnCIter VoxelIterT; + + mTracker->checkInterrupter(); + const MapT& map = *mMap; + Stencil stencil(mTracker->grid()); + + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + BufferType& speed = leafIter.buffer(speedBuffer); + BufferType& result = leafIter.buffer(resultBuffer); + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + const Index n = voxelIter.pos(); + stencil.moveTo(voxelIter); + const ScalarType G = math::GradientNormSqrd<MapT,SpatialScheme>::result(map, stencil); + result.setValue(n, *voxelIter - dt * speed.getValue(n) * G); + } + } +} + +// Convex combination of Phi and a forward Euler advection steps: +// Phi(result) = alpha * Phi(phi) + (1-alpha) * (Phi(0) - dt * Phi(speed) * |Grad[Phi(0)]|) +template<typename GridT, typename InterruptT> +template <typename MapT, math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline void +LevelSetMorphing<GridT, InterruptT>:: +LevelSetMorph<MapT, SpatialScheme, TemporalScheme>:: +euler2(const LeafRange& range, ScalarType dt, ScalarType alpha, + Index phiBuffer, Index resultBuffer, Index speedBuffer) +{ + typedef math::BIAS_SCHEME<SpatialScheme> Scheme; + typedef typename Scheme::template ISStencil<GridType>::StencilType Stencil; + typedef typename LeafType::ValueOnCIter VoxelIterT; + + mTracker->checkInterrupter(); + const MapT& map = *mMap; + const ScalarType beta = ScalarType(1.0) - alpha; + Stencil stencil(mTracker->grid()); + + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + BufferType& speed = leafIter.buffer(speedBuffer); + BufferType& result = leafIter.buffer(resultBuffer); + BufferType& phi = leafIter.buffer(phiBuffer); + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + const Index n = voxelIter.pos(); + stencil.moveTo(voxelIter); + const ScalarType G = math::GradientNormSqrd<MapT,SpatialScheme>::result(map, stencil); + result.setValue(n, + alpha * phi.getValue(n) + beta * (*voxelIter - dt * speed.getValue(n) * G)); + } + } +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVEL_SET_MORPH_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/extern/openvdb/internal/openvdb/tools/LevelSetRebuild.h b/extern/openvdb/internal/openvdb/tools/LevelSetRebuild.h index f49245e9ee8..1b708435bf6 100644 --- a/extern/openvdb/internal/openvdb/tools/LevelSetRebuild.h +++ b/extern/openvdb/internal/openvdb/tools/LevelSetRebuild.h @@ -235,7 +235,7 @@ doLevelSetRebuild(const GridType& grid, typename GridType::ValueType iso, exBandWidth = float(exWidth), inBandWidth = float(inWidth); - tools::VolumeToMesh mesher(isovalue, 0.0005); + tools::VolumeToMesh mesher(isovalue); mesher(grid); math::Transform::Ptr transform = (xform != NULL) ? xform->copy() : grid.transform().copy(); @@ -268,8 +268,9 @@ doLevelSetRebuild(const GridType& grid, typename GridType::ValueType iso, primCpy.runParallel(); } - MeshToVolume<GridType, InterruptT> vol(transform, 0, interrupter); + MeshToVolume<GridType, InterruptT> vol(transform, OUTPUT_RAW_DATA, interrupter); vol.convertToLevelSet(points, primitives, exBandWidth, inBandWidth); + return vol.distGridPtr(); } diff --git a/extern/openvdb/internal/openvdb/tools/LevelSetTracker.h b/extern/openvdb/internal/openvdb/tools/LevelSetTracker.h index e6ef3ce8070..daf28dc14cb 100644 --- a/extern/openvdb/internal/openvdb/tools/LevelSetTracker.h +++ b/extern/openvdb/internal/openvdb/tools/LevelSetTracker.h @@ -52,6 +52,8 @@ #include <openvdb/math/Transform.h> #include <openvdb/Grid.h> #include <openvdb/util/NullInterrupter.h> +#include <openvdb/tree/ValueAccessor.h> +#include <openvdb/tree/LeafManager.h> #include "Morphology.h"//for tools::dilateVoxels namespace openvdb { @@ -70,10 +72,12 @@ public: typedef typename TreeType::ValueType ValueType; typedef typename tree::LeafManager<TreeType> LeafManagerType; // leafs + buffers typedef typename LeafManagerType::RangeType RangeType; + typedef typename LeafManagerType::LeafRange LeafRange; typedef typename LeafManagerType::BufferType BufferType; BOOST_STATIC_ASSERT(boost::is_floating_point<ValueType>::value); /// Main constructor + /// @throw RuntimeError if the grid is not a level set LevelSetTracker(GridT& grid, InterruptT* interrupt = NULL); /// Shallow copy constructor called by tbb::parallel_for() threads during filtering @@ -121,7 +125,10 @@ public: /// @return false if the process was interrupted bool checkInterrupter(); - const GridType& grid() const { return mGrid; } + const GridType& grid() const { return *mGrid; } + + LeafManagerType& leafs() { return *mLeafs; } + const LeafManagerType& leafs() const { return *mLeafs; } /// @brief Public functor called by tbb::parallel_for() /// @note Never call this method directly @@ -130,8 +137,8 @@ public: if (mTask) mTask(const_cast<LevelSetTracker*>(this), r); else OPENVDB_THROW(ValueError, "task is undefined - call track(), etc"); } - -protected: + +private: template<math::BiasedGradientScheme SpatialScheme, math::TemporalIntegrationScheme TemporalScheme> @@ -148,21 +155,7 @@ protected: void euler2(const RangeType& range, ValueType dt, ValueType alpha, Index phiBuffer, Index resultBuffer); }; // end of protected Normalizer class - - // required to access mLeafs - template<typename, typename, typename> - friend class LevelSetAdvection; - - GridType& mGrid; - // Throughout the methods below mLeafs is always assumed to contain - // a list of the current LeafNodes! The auxiliary buffers on the - // other hand always have to be allocated locally, since some - // methods need them and others don't! - LeafManagerType* mLeafs; - InterruptT* mInterrupter; - const ValueType mDx; - -private: + typedef typename boost::function<void (LevelSetTracker*, const RangeType&)> FuncType; void trim(const RangeType& r); @@ -173,7 +166,15 @@ private: template<math::BiasedGradientScheme SpatialScheme, math::TemporalIntegrationScheme TemporalScheme> void normalize2(); - + + // Throughout the methods below mLeafs is always assumed to contain + // a list of the current LeafNodes! The auxiliary buffers on the + // other hand always have to be allocated locally, since some + // methods need them and others don't! + GridType* mGrid; + LeafManagerType* mLeafs; + InterruptT* mInterrupter; + const ValueType mDx; math::BiasedGradientScheme mSpatialScheme; math::TemporalIntegrationScheme mTemporalScheme; int mNormCount;// Number of iteratations of normalization @@ -188,7 +189,7 @@ private: template<typename GridT, typename InterruptT> LevelSetTracker<GridT, InterruptT>::LevelSetTracker(GridT& grid, InterruptT* interrupt): - mGrid(grid), + mGrid(&grid), mLeafs(new LeafManagerType(grid.tree())), mInterrupter(interrupt), mDx(grid.voxelSize()[0]), @@ -240,7 +241,7 @@ LevelSetTracker<GridT, InterruptT>::prune() } // Remove inactive nodes from tree - mGrid.tree().pruneLevelSet(); + mGrid->tree().pruneLevelSet(); // The tree topology has changes so rebuild the list of leafs mLeafs->rebuildLeafArray(); @@ -293,7 +294,7 @@ LevelSetTracker<GridT, InterruptT>::trim(const RangeType& range) { typedef typename LeafType::ValueOnIter VoxelIterT; const_cast<LevelSetTracker*>(this)->checkInterrupter(); - const ValueType gamma = mGrid.background(); + const ValueType gamma = mGrid->background(); for (size_t n=range.begin(), e=range.end(); n != e; ++n) { LeafType &leaf = mLeafs->leaf(n); for (VoxelIterT iter = leaf.beginValueOn(); iter; ++iter) { @@ -369,6 +370,7 @@ normalize() for (int n=0, e=mTracker.getNormCount(); n < e; ++n) { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN switch(TemporalScheme) {//switch is resolved at compile-time case math::TVD_RK1: //std::cerr << "1"; @@ -418,6 +420,7 @@ normalize() default: OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END } mTracker.mLeafs->removeAuxBuffers(); } diff --git a/extern/openvdb/internal/openvdb/tools/LevelSetUtil.h b/extern/openvdb/internal/openvdb/tools/LevelSetUtil.h index b77d8fff43b..a7f5e0694b2 100644 --- a/extern/openvdb/internal/openvdb/tools/LevelSetUtil.h +++ b/extern/openvdb/internal/openvdb/tools/LevelSetUtil.h @@ -39,7 +39,6 @@ #include <openvdb/Grid.h> #include <openvdb/tree/LeafManager.h> #include <tbb/blocked_range.h> -#include <tbb/parallel_for.h> #include <tbb/parallel_reduce.h> #include <limits> @@ -147,34 +146,6 @@ private: //////////////////////////////////////// -/// @brief Threaded operator that applies a user-supplied functor -/// to each leaf node in a LeafManager -template<class TreeType, class LeafOp> -class LeafTransformer -{ -public: - typedef tree::LeafManager<TreeType> LeafArray; - typedef typename TreeType::ValueType ValueType; - - // LeafArray = openvdb::tree::LeafManager<TreeType> leafs(myTree) - LeafTransformer(LeafArray&, LeafOp&); - - void runParallel(); - void runSerial(); - - - inline void operator()(const tbb::blocked_range<size_t>&) const; - inline LeafTransformer(const LeafTransformer<TreeType, LeafOp>&); - -private: - LeafArray& mLeafArray; - LeafOp& mLeafOp; -}; - - -//////////////////////////////////////// - - // Internal utility objects and implementation details namespace internal { @@ -223,7 +194,7 @@ public: template <typename LeafNodeType> void operator()(LeafNodeType &leaf, size_t/*leafIndex*/) const { - const Coord origin = leaf.getOrigin(); + const Coord origin = leaf.origin(); const typename TreeType::LeafNodeType* refLeafPt = mTree.probeConstLeaf(origin); if (refLeafPt != NULL) { @@ -313,52 +284,6 @@ MinMaxVoxel<TreeType>::join(const MinMaxVoxel<TreeType>& rhs) } -//////////////////////////////////////// - - -template <class TreeType, class LeafOp> -LeafTransformer<TreeType, LeafOp>::LeafTransformer(LeafArray& leafs, LeafOp& leafOp) - : mLeafArray(leafs) - , mLeafOp(leafOp) -{ -} - - -template <class TreeType, class LeafOp> -inline -LeafTransformer<TreeType, LeafOp>::LeafTransformer( - const LeafTransformer<TreeType, LeafOp>& rhs) - : mLeafArray(rhs.mLeafArray) - , mLeafOp(rhs.mLeafOp) -{ -} - - -template <class TreeType, class LeafOp> -void -LeafTransformer<TreeType, LeafOp>::runParallel() -{ - tbb::parallel_for(mLeafArray.getRange(), *this); -} - - -template <class TreeType, class LeafOp> -void -LeafTransformer<TreeType, LeafOp>::runSerial() -{ - (*this)(mLeafArray.getRange()); -} - - -template <class TreeType, class LeafOp> -inline void -LeafTransformer<TreeType, LeafOp>::operator()(const tbb::blocked_range<size_t>& range) const -{ - for (size_t n = range.begin(); n < range.end(); ++n) { - mLeafOp(mLeafArray.leaf(n), n); - } -} - //////////////////////////////////////// @@ -385,9 +310,7 @@ sdfToFogVolume(GridType& grid, typename GridType::ValueType cutoffDistance) cutoffDistance = minmax.minVoxel(); } - internal::FogVolumeOp<ValueType> op(cutoffDistance); - LeafTransformer<TreeType, internal::FogVolumeOp<ValueType> > transform(leafs, op); - transform.runParallel(); + leafs.foreach(internal::FogVolumeOp<ValueType>(cutoffDistance)); } // Transform all tile values (serial, but the iteration @@ -446,11 +369,7 @@ sdfInteriorMask(const GridType& grid, typename GridType::ValueType iso) tree::LeafManager<BoolTreeType> leafs(maskTree); - typedef internal::InteriorMaskOp<typename GridType::TreeType> InteriorMaskOp; - InteriorMaskOp op(grid.tree(), iso); - - LeafTransformer<BoolTreeType, InteriorMaskOp> transform(leafs, op); - transform.runParallel(); + leafs.foreach(internal::InteriorMaskOp<typename GridType::TreeType>(grid.tree(), iso)); } // Evaluate tile values (serial, but the iteration diff --git a/extern/openvdb/internal/openvdb/tools/MeshToVolume.h b/extern/openvdb/internal/openvdb/tools/MeshToVolume.h index cef6e5d4648..457c0b6e580 100644 --- a/extern/openvdb/internal/openvdb/tools/MeshToVolume.h +++ b/extern/openvdb/internal/openvdb/tools/MeshToVolume.h @@ -35,7 +35,6 @@ #include <openvdb/math/FiniteDifference.h> #include <openvdb/math/Operators.h> // for ISGradientNormSqrd #include <openvdb/math/Proximity.h> // for closestPointOnTriangleToPoint() -#include <openvdb/tools/LevelSetUtil.h> // for LeafTransformer #include <openvdb/tools/Morphology.h> // for dilateVoxels() #include <openvdb/util/NullInterrupter.h> #include <openvdb/util/Util.h> // for nearestCoord() @@ -46,7 +45,6 @@ #include <deque> #include <limits> - namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { @@ -190,20 +188,20 @@ meshToUnsignedDistanceField( /// Conversion flags, used to control the MeshToVolume output -enum { GENERATE_PRIM_INDEX_GRID = 0x1 }; +enum { GENERATE_PRIM_INDEX_GRID = 0x1, OUTPUT_RAW_DATA = 0x2}; // MeshToVolume -template<typename DistGridT, typename InterruptT = util::NullInterrupter> +template<typename FloatGridT, typename InterruptT = util::NullInterrupter> class MeshToVolume { public: - typedef typename DistGridT::TreeType DistTreeT; - typedef typename DistTreeT::ValueType DistValueT; - typedef typename DistTreeT::template ValueConverter<Int32>::Type IndexTreeT; - typedef Grid<IndexTreeT> IndexGridT; - typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT; - typedef Grid<StencilTreeT> StencilGridT; + typedef typename FloatGridT::TreeType FloatTreeT; + typedef typename FloatTreeT::ValueType FloatValueT; + typedef typename FloatTreeT::template ValueConverter<Int32>::Type IntTreeT; + typedef Grid<IntTreeT> IntGridT; + typedef typename FloatTreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef Grid<BoolTreeT> BoolGridT; MeshToVolume(openvdb::math::Transform::Ptr&, int conversionFlags = 0, InterruptT *interrupter = NULL, int signSweeps = 1); @@ -222,8 +220,8 @@ public: void convertToLevelSet( const std::vector<Vec3s>& pointList, const std::vector<Vec4I>& polygonList, - DistValueT exBandWidth = DistValueT(LEVEL_SET_HALF_WIDTH), - DistValueT inBandWidth = DistValueT(LEVEL_SET_HALF_WIDTH)); + FloatValueT exBandWidth = FloatValueT(LEVEL_SET_HALF_WIDTH), + FloatValueT inBandWidth = FloatValueT(LEVEL_SET_HALF_WIDTH)); /// @brief Mesh to Unsigned Distance Field conversion /// @@ -234,35 +232,114 @@ public: /// @param polygonList List of triangles and/or quads. /// @param exBandWidth The narrow-band width in voxel units. void convertToUnsignedDistanceField(const std::vector<Vec3s>& pointList, - const std::vector<Vec4I>& polygonList, DistValueT exBandWidth); + const std::vector<Vec4I>& polygonList, FloatValueT exBandWidth); void clear(); /// Returns a narrow-band (signed) distance field / level set grid. - typename DistGridT::Ptr distGridPtr() const { return mDistGrid; } + typename FloatGridT::Ptr distGridPtr() const { return mDistGrid; } /// Returns a grid containing the closest-primitive index for each /// voxel in the narrow-band. - typename IndexGridT::Ptr indexGridPtr() const { return mIndexGrid; } + typename IntGridT::Ptr indexGridPtr() const { return mIndexGrid; } private: // disallow copy by assignment - void operator=(const MeshToVolume<DistGridT, InterruptT>&) {} + void operator=(const MeshToVolume<FloatGridT, InterruptT>&) {} void doConvert(const std::vector<Vec3s>&, const std::vector<Vec4I>&, - DistValueT exBandWidth, DistValueT inBandWidth, bool unsignedDistField = false); + FloatValueT exBandWidth, FloatValueT inBandWidth, bool unsignedDistField = false); + + bool wasInterrupted(int percent = -1) const { + return mInterrupter && mInterrupter->wasInterrupted(percent); + } openvdb::math::Transform::Ptr mTransform; int mConversionFlags, mSignSweeps; - typename DistGridT::Ptr mDistGrid; - typename IndexGridT::Ptr mIndexGrid; - typename StencilGridT::Ptr mIntersectingVoxelsGrid; + typename FloatGridT::Ptr mDistGrid; + typename IntGridT::Ptr mIndexGrid; + typename BoolGridT::Ptr mIntersectingVoxelsGrid; InterruptT *mInterrupter; }; +//////////////////////////////////////// + + +/// @brief Extracts and stores voxel edge intersection data from a mesh. +class MeshToVoxelEdgeData +{ +public: + + ////////// + + ///@brief Internal edge data type. + struct EdgeData { + EdgeData(float dist = 1.0) + : mXDist(dist), mYDist(dist), mZDist(dist) + , mXPrim(util::INVALID_IDX) + , mYPrim(util::INVALID_IDX) + , mZPrim(util::INVALID_IDX) + { + } + + //@{ + /// Required by several of the tree nodes + /// @note These methods don't perform meaningful operations. + bool operator< (const EdgeData&) const { return false; }; + bool operator> (const EdgeData&) const { return false; }; + template<class T> EdgeData operator+(const T&) const { return *this; } + template<class T> EdgeData operator-(const T&) const { return *this; } + EdgeData operator-() const { return *this; } + //@} + + bool operator==(const EdgeData& rhs) const + { + return mXPrim == rhs.mXPrim && mYPrim == rhs.mYPrim && mZPrim == rhs.mZPrim; + } + + float mXDist, mYDist, mZDist; + Index32 mXPrim, mYPrim, mZPrim; + }; + + typedef tree::Tree4<EdgeData, 5, 4, 3>::Type TreeType; + typedef tree::ValueAccessor<TreeType> Accessor; + + + ////////// + + + MeshToVoxelEdgeData(); + + + /// @brief Threaded method to extract voxel edge data, the closest + /// intersection point and corresponding primitive index, + /// from the given mesh. + /// + /// @param pointList List of points in grid index space, preferably unique + /// and shared by different polygons. + /// @param polygonList List of triangles and/or quads. + void convert(const std::vector<Vec3s>& pointList, const std::vector<Vec4I>& polygonList); + + + /// @brief Returns intersection points with corresponding primitive + /// indices for the given @c ijk voxel. + void getEdgeData(Accessor& acc, const Coord& ijk, + std::vector<Vec3d>& points, std::vector<Index32>& primitives); + + /// @return An accessor of @c MeshToVoxelEdgeData::Accessor type that + /// provides random read access to the internal tree. + Accessor getAccessor() { return Accessor(mTree); } + +private: + void operator=(const MeshToVoxelEdgeData&) {} + TreeType mTree; + class GenEdgeData; +}; + + //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// @@ -283,14 +360,13 @@ public: { } - void runParallel() - { - tbb::parallel_for(tbb::blocked_range<size_t>(0, mPointsOut->size()), *this); - } - - void runSerial() + void run(bool threaded = true) { - (*this)(tbb::blocked_range<size_t>(0, mPointsOut->size())); + if (threaded) { + tbb::parallel_for(tbb::blocked_range<size_t>(0, mPointsOut->size()), *this); + } else { + (*this)(tbb::blocked_range<size_t>(0, mPointsOut->size())); + } } inline void operator()(const tbb::blocked_range<size_t>& range) const @@ -315,27 +391,27 @@ struct Tolerance }; -template<typename DistTreeT, typename IndexTreeT> +template<typename FloatTreeT, typename IntTreeT> inline void -combine(DistTreeT& lhsDist, IndexTreeT& lhsIndex, DistTreeT& rhsDist, IndexTreeT& rhsIndex) +combine(FloatTreeT& lhsDist, IntTreeT& lhsIndex, FloatTreeT& rhsDist, IntTreeT& rhsIndex) { - typedef typename DistTreeT::ValueType DistValueT; - typename tree::ValueAccessor<DistTreeT> lhsDistAccessor(lhsDist); - typename tree::ValueAccessor<IndexTreeT> lhsIndexAccessor(lhsIndex); - typename tree::ValueAccessor<IndexTreeT> rhsIndexAccessor(rhsIndex); - typename DistTreeT::LeafCIter iter = rhsDist.cbeginLeaf(); + typedef typename FloatTreeT::ValueType FloatValueT; + typename tree::ValueAccessor<FloatTreeT> lhsDistAccessor(lhsDist); + typename tree::ValueAccessor<IntTreeT> lhsIndexAccessor(lhsIndex); + typename tree::ValueAccessor<IntTreeT> rhsIndexAccessor(rhsIndex); + typename FloatTreeT::LeafCIter iter = rhsDist.cbeginLeaf(); - DistValueT rhsValue; + FloatValueT rhsValue; Coord ijk; for ( ; iter; ++iter) { - typename DistTreeT::LeafNodeType::ValueOnCIter it = iter->cbeginValueOn(); + typename FloatTreeT::LeafNodeType::ValueOnCIter it = iter->cbeginValueOn(); for ( ; it; ++it) { ijk = it.getCoord(); rhsValue = it.getValue(); - DistValueT& lhsValue = const_cast<DistValueT&>(lhsDistAccessor.getValue(ijk)); + FloatValueT& lhsValue = const_cast<FloatValueT&>(lhsDistAccessor.getValue(ijk)); if (-rhsValue < std::abs(lhsValue)) { lhsValue = rhsValue; @@ -352,85 +428,94 @@ combine(DistTreeT& lhsDist, IndexTreeT& lhsIndex, DistTreeT& rhsDist, IndexTreeT /// MeshVoxelizer /// @brief TBB body object to voxelize a mesh of triangles and/or quads into a collection /// of VDB grids, namely a squared distance grid, a closest primitive grid and an -/// intersecting voxels grid (the voxels intersect the mesh) +/// intersecting voxels grid (masks the mesh intersecting voxels) /// @note Only the leaf nodes that intersect the mesh are allocated, and only voxels in /// a narrow band (of two to three voxels in proximity to the mesh's surface) are activated. /// They are populated with distance values and primitive indices. -template<typename DistTreeT, typename InterruptT = util::NullInterrupter> +template<typename FloatTreeT, typename InterruptT = util::NullInterrupter> class MeshVoxelizer { public: - typedef typename DistTreeT::ValueType DistValueT; - typedef typename tree::ValueAccessor<DistTreeT> DistAccessorT; - typedef typename DistTreeT::template ValueConverter<Int32>::Type IndexTreeT; - typedef typename tree::ValueAccessor<IndexTreeT> IndexAccessorT; - typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT; - typedef typename tree::ValueAccessor<StencilTreeT> StencilAccessorT; + typedef typename FloatTreeT::ValueType FloatValueT; + typedef typename FloatTreeT::LeafNodeType FloatLeafT; + typedef typename tree::ValueAccessor<FloatTreeT> FloatAccessorT; + typedef typename FloatTreeT::template ValueConverter<Int32>::Type IntTreeT; + typedef typename IntTreeT::LeafNodeType IntLeafT; + typedef typename tree::ValueAccessor<IntTreeT> IntAccessorT; + typedef typename FloatTreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef typename BoolTreeT::LeafNodeType BoolLeafT; + typedef typename tree::ValueAccessor<BoolTreeT> BoolAccessorT; + MeshVoxelizer(const std::vector<Vec3s>& pointList, const std::vector<Vec4I>& polygonList, InterruptT *interrupter = NULL); ~MeshVoxelizer() {} - void runParallel(); - void runSerial(); + void run(bool threaded = true); - MeshVoxelizer(MeshVoxelizer<DistTreeT, InterruptT>& rhs, tbb::split); + MeshVoxelizer(MeshVoxelizer<FloatTreeT, InterruptT>& rhs, tbb::split); void operator() (const tbb::blocked_range<size_t> &range); - void join(MeshVoxelizer<DistTreeT, InterruptT>& rhs); + void join(MeshVoxelizer<FloatTreeT, InterruptT>& rhs); - DistTreeT& sqrDistTree() { return mSqrDistTree; } - IndexTreeT& primIndexTree() { return mPrimIndexTree; } - StencilTreeT& intersectionTree() { return mIntersectionTree; } + FloatTreeT& sqrDistTree() { return mSqrDistTree; } + IntTreeT& primIndexTree() { return mPrimIndexTree; } + BoolTreeT& intersectionTree() { return mIntersectionTree; } private: // disallow copy by assignment - void operator=(const MeshVoxelizer<DistTreeT, InterruptT>&) {} + void operator=(const MeshVoxelizer<FloatTreeT, InterruptT>&) {} + bool wasInterrupted() const { return mInterrupter && mInterrupter->wasInterrupted(); } - inline bool shortEdge(const Vec3d&, const Vec3d&, const Vec3d&) const; bool evalVoxel(const Coord& ijk, const Int32 polyIdx); - std::vector<Vec3s> const * const mPointList; - std::vector<Vec4I> const * const mPolygonList; + const std::vector<Vec3s>& mPointList; + const std::vector<Vec4I>& mPolygonList; - DistTreeT mSqrDistTree; - DistAccessorT mSqrDistAccessor; + FloatTreeT mSqrDistTree; + FloatAccessorT mSqrDistAccessor; - IndexTreeT mPrimIndexTree; - IndexAccessorT mPrimIndexAccessor; + IntTreeT mPrimIndexTree; + IntAccessorT mPrimIndexAccessor; - StencilTreeT mIntersectionTree; - StencilAccessorT mIntersectionAccessor; + BoolTreeT mIntersectionTree; + BoolAccessorT mIntersectionAccessor; // Used internally for acceleration - IndexTreeT mLastPrimTree; - IndexAccessorT mLastPrimAccessor; + IntTreeT mLastPrimTree; + IntAccessorT mLastPrimAccessor; InterruptT *mInterrupter; -}; -template<typename DistTreeT, typename InterruptT> -void -MeshVoxelizer<DistTreeT, InterruptT>::runParallel() -{ - tbb::parallel_reduce(tbb::blocked_range<size_t>(0, mPolygonList->size()), *this); -} + struct Primitive { Vec3d a, b, c, d; Int32 index; }; + + template<bool IsQuad> + bool evalPrimitive(const Coord&, const Primitive&); + + template<bool IsQuad> + void voxelize(const Primitive&); +}; -template<typename DistTreeT, typename InterruptT> + +template<typename FloatTreeT, typename InterruptT> void -MeshVoxelizer<DistTreeT, InterruptT>::runSerial() +MeshVoxelizer<FloatTreeT, InterruptT>::run(bool threaded) { - (*this)(tbb::blocked_range<size_t>(0, mPolygonList->size())); + if (threaded) { + tbb::parallel_reduce(tbb::blocked_range<size_t>(0, mPolygonList.size()), *this); + } else { + (*this)(tbb::blocked_range<size_t>(0, mPolygonList.size())); + } } -template<typename DistTreeT, typename InterruptT> -MeshVoxelizer<DistTreeT, InterruptT>::MeshVoxelizer( +template<typename FloatTreeT, typename InterruptT> +MeshVoxelizer<FloatTreeT, InterruptT>::MeshVoxelizer( const std::vector<Vec3s>& pointList, const std::vector<Vec4I>& polygonList, InterruptT *interrupter) - : mPointList(&pointList) - , mPolygonList(&polygonList) - , mSqrDistTree(std::numeric_limits<DistValueT>::max()) + : mPointList(pointList) + , mPolygonList(polygonList) + , mSqrDistTree(std::numeric_limits<FloatValueT>::max()) , mSqrDistAccessor(mSqrDistTree) , mPrimIndexTree(Int32(util::INVALID_IDX)) , mPrimIndexAccessor(mPrimIndexTree) @@ -442,12 +527,12 @@ MeshVoxelizer<DistTreeT, InterruptT>::MeshVoxelizer( { } -template<typename DistTreeT, typename InterruptT> -MeshVoxelizer<DistTreeT, InterruptT>::MeshVoxelizer( - MeshVoxelizer<DistTreeT, InterruptT>& rhs, tbb::split) +template<typename FloatTreeT, typename InterruptT> +MeshVoxelizer<FloatTreeT, InterruptT>::MeshVoxelizer( + MeshVoxelizer<FloatTreeT, InterruptT>& rhs, tbb::split) : mPointList(rhs.mPointList) , mPolygonList(rhs.mPolygonList) - , mSqrDistTree(std::numeric_limits<DistValueT>::max()) + , mSqrDistTree(std::numeric_limits<FloatValueT>::max()) , mSqrDistAccessor(mSqrDistTree) , mPrimIndexTree(Int32(util::INVALID_IDX)) , mPrimIndexAccessor(mPrimIndexTree) @@ -459,28 +544,12 @@ MeshVoxelizer<DistTreeT, InterruptT>::MeshVoxelizer( { } -template<typename DistTreeT, typename InterruptT> -inline bool -MeshVoxelizer<DistTreeT, InterruptT>::shortEdge( - const Vec3d& v0, const Vec3d& v1, const Vec3d& v2) const -{ - double edge_max = std::abs(v1[0] - v0[0]); - edge_max = std::max(edge_max, std::abs(v1[1] - v0[1])); - edge_max = std::max(edge_max, std::abs(v1[2] - v0[2])); - edge_max = std::max(edge_max, std::abs(v0[0] - v2[0])); - edge_max = std::max(edge_max, std::abs(v0[1] - v2[1])); - edge_max = std::max(edge_max, std::abs(v0[2] - v2[2])); - return edge_max < 200.0; -} -template<typename DistTreeT, typename InterruptT> +template<typename FloatTreeT, typename InterruptT> void -MeshVoxelizer<DistTreeT, InterruptT>::operator()(const tbb::blocked_range<size_t> &range) +MeshVoxelizer<FloatTreeT, InterruptT>::operator()(const tbb::blocked_range<size_t> &range) { - std::deque<Coord> coordList; - StencilTreeT auxTree(false); - StencilAccessorT auxAcc(auxTree); - Coord ijk, n_ijk; + Primitive prim; for (size_t n = range.begin(); n < range.end(); ++n) { @@ -489,165 +558,163 @@ MeshVoxelizer<DistTreeT, InterruptT>::operator()(const tbb::blocked_range<size_t break; } - const Int32 primIdx = n; - const Vec4I verts = (*mPolygonList)[n]; - - Vec3d p0((*mPointList)[verts[0]]); - Vec3d p1((*mPointList)[verts[1]]); - Vec3d p2((*mPointList)[verts[2]]); - - if (shortEdge(p0, p1, p2)) { - coordList.clear(); - - - ijk = util::nearestCoord(p0); - evalVoxel(ijk, primIdx); - coordList.push_back(ijk); + const Vec4I& verts = mPolygonList[n]; + prim.index = Int32(n); + prim.a = Vec3d(mPointList[verts[0]]); + prim.b = Vec3d(mPointList[verts[1]]); + prim.c = Vec3d(mPointList[verts[2]]); - ijk = util::nearestCoord(p1); - evalVoxel(ijk, primIdx); - coordList.push_back(ijk); - - - ijk = util::nearestCoord(p2); - evalVoxel(ijk, primIdx); - coordList.push_back(ijk); - - if (util::INVALID_IDX != verts[3]) { - Vec3d p3((*mPointList)[verts[3]]); - ijk = util::nearestCoord(p3); - evalVoxel(ijk, primIdx); - coordList.push_back(ijk); - } - - while (!coordList.empty()) { - if (mInterrupter && mInterrupter->wasInterrupted()) { - break; - } - - ijk = coordList.back(); - coordList.pop_back(); - - mIntersectionAccessor.setActiveState(ijk, true); - - for (Int32 i = 0; i < 26; ++i) { - n_ijk = ijk + util::COORD_OFFSETS[i]; - - if (primIdx != mLastPrimAccessor.getValue(n_ijk)) { - mLastPrimAccessor.setValue(n_ijk, n); - if(evalVoxel(n_ijk, n)) coordList.push_back(n_ijk); - } - } - } - + if (util::INVALID_IDX != verts[3]) { + prim.d = Vec3d(mPointList[verts[3]]); + voxelize<true>(prim); } else { + voxelize<false>(prim); + } + } +} - ijk = util::nearestCoord(p0); - evalVoxel(ijk, primIdx); - - mLastPrimAccessor.setValue(ijk, primIdx); - auxAcc.setActiveState(ijk, true); - - while (!auxTree.empty()) { - - if (mInterrupter && mInterrupter->wasInterrupted()) { - break; - } - typename StencilTreeT::LeafIter leafIter = auxTree.beginLeaf(); - for (; leafIter; leafIter.next()) { +template<typename FloatTreeT, typename InterruptT> +template<bool IsQuad> +void +MeshVoxelizer<FloatTreeT, InterruptT>::voxelize(const Primitive& prim) +{ + std::deque<Coord> coordList; + Coord ijk, nijk; - if (mInterrupter && mInterrupter->wasInterrupted()) { - break; - } + ijk = util::nearestCoord(prim.a); + coordList.push_back(ijk); - typename StencilTreeT::LeafNodeType::ValueOnIter iter = - leafIter->beginValueOn(); - for (; iter; iter.next()) { - ijk = iter.getCoord(); - iter.setValueOff(); + evalPrimitive<IsQuad>(ijk, prim); - mIntersectionAccessor.setActiveState(ijk, true); + while (!coordList.empty()) { + if(wasInterrupted()) break; - for (Int32 i = 0; i < 26; ++i) { - n_ijk = ijk + util::COORD_OFFSETS[i]; + ijk = coordList.back(); + coordList.pop_back(); - if (primIdx != mLastPrimAccessor.getValue(n_ijk)) { - mLastPrimAccessor.setValue(n_ijk, n); - if (evalVoxel(n_ijk, n)) auxAcc.setActiveState(n_ijk, true); - } - } - } - } + mIntersectionAccessor.setActiveState(ijk, true); - auxTree.pruneInactive(); + for (Int32 i = 0; i < 26; ++i) { + nijk = ijk + util::COORD_OFFSETS[i]; + if (prim.index != mLastPrimAccessor.getValue(nijk)) { + mLastPrimAccessor.setValue(nijk, prim.index); + if(evalPrimitive<IsQuad>(nijk, prim)) coordList.push_back(nijk); } } } } -template<typename DistTreeT, typename InterruptT> + +template<typename FloatTreeT, typename InterruptT> +template<bool IsQuad> bool -MeshVoxelizer<DistTreeT, InterruptT>::evalVoxel(const Coord& ijk, const Int32 polyIdx) +MeshVoxelizer<FloatTreeT, InterruptT>::evalPrimitive(const Coord& ijk, const Primitive& prim) { - Vec3d voxelCenter(ijk[0], ijk[1], ijk[2]); - const Vec4I& verts = (*mPolygonList)[polyIdx]; - const std::vector<Vec3s>& points = *mPointList; + Vec3d uvw, voxelCenter(ijk[0], ijk[1], ijk[2]); // Evaluate first triangle - const Vec3d a(points[verts[0]]); - const Vec3d b(points[verts[1]]); - const Vec3d c(points[verts[2]]); - - Vec3d uvw; - double dist = (voxelCenter - - closestPointOnTriangleToPoint(a, c, b, voxelCenter, uvw)).lengthSqr(); - - // Split-up quad into a second triangle and calac distance. - if (util::INVALID_IDX != verts[3]) { - const Vec3d d(points[verts[3]]); + FloatValueT dist = FloatValueT((voxelCenter - + closestPointOnTriangleToPoint(prim.a, prim.c, prim.b, voxelCenter, uvw)).lengthSqr()); - double secondDist = (voxelCenter - - closestPointOnTriangleToPoint(a, d, c, voxelCenter, uvw)).lengthSqr(); + if (IsQuad) { + // Split quad into a second triangle and calculate distance. + FloatValueT secondDist = FloatValueT((voxelCenter - + closestPointOnTriangleToPoint(prim.a, prim.d, prim.c, voxelCenter, uvw)).lengthSqr()); if (secondDist < dist) dist = secondDist; } - const DistValueT tmp(dist); - if (tmp < std::abs(mSqrDistAccessor.getValue(ijk))) { - mSqrDistAccessor.setValue(ijk, -tmp); - mPrimIndexAccessor.setValue(ijk, polyIdx); + FloatValueT oldDist = std::abs(mSqrDistAccessor.getValue(ijk)); + + if (dist < oldDist) { + mSqrDistAccessor.setValue(ijk, -dist); + mPrimIndexAccessor.setValue(ijk, prim.index); + } else if (math::isExactlyEqual(dist, oldDist)) { + // makes reduction deterministic when different polygons + // produce the same distance value. + mPrimIndexAccessor.setValue(ijk, std::min(prim.index, mPrimIndexAccessor.getValue(ijk))); } return (dist < 0.86602540378443861); } -template<typename DistTreeT, typename InterruptT> + +template<typename FloatTreeT, typename InterruptT> void -MeshVoxelizer<DistTreeT, InterruptT>::join(MeshVoxelizer<DistTreeT, InterruptT>& rhs) +MeshVoxelizer<FloatTreeT, InterruptT>::join(MeshVoxelizer<FloatTreeT, InterruptT>& rhs) { - typename DistTreeT::LeafCIter iter = rhs.mSqrDistTree.cbeginLeaf(); - DistValueT rhsDist; + typedef typename FloatTreeT::RootNodeType FloatRootNodeT; + typedef typename FloatRootNodeT::NodeChainType FloatNodeChainT; + BOOST_STATIC_ASSERT(boost::mpl::size<FloatNodeChainT>::value > 1); + typedef typename boost::mpl::at<FloatNodeChainT, boost::mpl::int_<1> >::type FloatInternalNodeT; + + typedef typename IntTreeT::RootNodeType IntRootNodeT; + typedef typename IntRootNodeT::NodeChainType IntNodeChainT; + BOOST_STATIC_ASSERT(boost::mpl::size<IntNodeChainT>::value > 1); + typedef typename boost::mpl::at<IntNodeChainT, boost::mpl::int_<1> >::type IntInternalNodeT; + + const FloatValueT background = std::numeric_limits<FloatValueT>::max(); + Coord ijk; + Index offset; - for ( ; iter; ++iter) { - typename DistTreeT::LeafNodeType::ValueOnCIter it = iter->cbeginValueOn(); + rhs.mSqrDistTree.clearAllAccessors(); + rhs.mPrimIndexTree.clearAllAccessors(); - for ( ; it; ++it) { + typename FloatTreeT::LeafIter leafIt = rhs.mSqrDistTree.beginLeaf(); + for ( ; leafIt; ++leafIt) { - ijk = it.getCoord(); - rhsDist = it.getValue(); - DistValueT lhsDist = mSqrDistAccessor.getValue(ijk); + ijk = leafIt->origin(); + FloatLeafT* lhsDistLeafPt = mSqrDistAccessor.probeLeaf(ijk); + + if (!lhsDistLeafPt) { + + // Steals leaf nodes through their parent, always the last internal-node + // stored in the ValueAccessor's node chain, avoiding the overhead of + // the root node. This is significantly faster than going through the + // tree or root node. + mSqrDistAccessor.addLeaf(rhs.mSqrDistAccessor.probeLeaf(ijk)); + FloatInternalNodeT* floatNode = + rhs.mSqrDistAccessor.template getNode<FloatInternalNodeT>(); + floatNode->template stealNode<FloatLeafT>(ijk, background, false); + + mPrimIndexAccessor.addLeaf(rhs.mPrimIndexAccessor.probeLeaf(ijk)); + IntInternalNodeT* intNode = + rhs.mPrimIndexAccessor.template getNode<IntInternalNodeT>(); + intNode->template stealNode<IntLeafT>(ijk, util::INVALID_IDX, false); + + } else { + + IntLeafT* lhsIdxLeafPt = mPrimIndexAccessor.probeLeaf(ijk); + IntLeafT* rhsIdxLeafPt = rhs.mPrimIndexAccessor.probeLeaf(ijk); + FloatValueT lhsValue, rhsValue; + + typename FloatLeafT::ValueOnCIter it = leafIt->cbeginValueOn(); + for ( ; it; ++it) { + + offset = it.pos(); + + lhsValue = std::abs(lhsDistLeafPt->getValue(offset)); + rhsValue = std::abs(it.getValue()); - if (-rhsDist < std::abs(lhsDist)) { - mSqrDistAccessor.setValue(ijk, rhsDist); - mPrimIndexAccessor.setValue(ijk, rhs.mPrimIndexAccessor.getValue(ijk)); + if (rhsValue < lhsValue) { + lhsDistLeafPt->setValueOn(offset, it.getValue()); + lhsIdxLeafPt->setValueOn(offset, rhsIdxLeafPt->getValue(offset)); + } else if (math::isExactlyEqual(rhsValue, lhsValue)) { + lhsIdxLeafPt->setValueOn(offset, + std::min(lhsIdxLeafPt->getValue(offset), rhsIdxLeafPt->getValue(offset))); + } } } } mIntersectionTree.merge(rhs.mIntersectionTree); + + rhs.mSqrDistTree.clear(); + rhs.mPrimIndexTree.clear(); + rhs.mIntersectionTree.clear(); } @@ -657,34 +724,33 @@ MeshVoxelizer<DistTreeT, InterruptT>::join(MeshVoxelizer<DistTreeT, InterruptT>& // ContourTracer /// @brief TBB body object that partitions a volume into 2D slices that can be processed /// in parallel and marks the exterior contour of disjoint voxel sets in each slice -template<typename DistTreeT, typename InterruptT = util::NullInterrupter> +template<typename FloatTreeT, typename InterruptT = util::NullInterrupter> class ContourTracer { public: - typedef typename DistTreeT::ValueType DistValueT; - typedef typename tree::ValueAccessor<DistTreeT> DistAccessorT; - typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT; - typedef typename tree::ValueAccessor<const StencilTreeT> StencilAccessorT; + typedef typename FloatTreeT::ValueType FloatValueT; + typedef typename tree::ValueAccessor<FloatTreeT> DistAccessorT; + typedef typename FloatTreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef typename tree::ValueAccessor<const BoolTreeT> BoolAccessorT; - ContourTracer(DistTreeT&, const StencilTreeT&, InterruptT *interrupter = NULL); + ContourTracer(FloatTreeT&, const BoolTreeT&, InterruptT *interrupter = NULL); ~ContourTracer() {} - void runParallel(); - void runSerial(); + void run(bool threaded = true); - ContourTracer(const ContourTracer<DistTreeT, InterruptT>& rhs); + ContourTracer(const ContourTracer<FloatTreeT, InterruptT>& rhs); void operator()(const tbb::blocked_range<int> &range) const; private: - void operator=(const ContourTracer<DistTreeT, InterruptT>&) {} + void operator=(const ContourTracer<FloatTreeT, InterruptT>&) {} int sparseScan(int slice) const; - DistTreeT& mDistTree; + FloatTreeT& mDistTree; DistAccessorT mDistAccessor; - const StencilTreeT& mIntersectionTree; - StencilAccessorT mIntersectionAccessor; + const BoolTreeT& mIntersectionTree; + BoolAccessorT mIntersectionAccessor; CoordBBox mBBox; @@ -694,23 +760,22 @@ private: InterruptT *mInterrupter; }; -template<typename DistTreeT, typename InterruptT> -void -ContourTracer<DistTreeT, InterruptT>::runParallel() -{ - tbb::parallel_for(tbb::blocked_range<int>(mBBox.min()[0], mBBox.max()[0]+1), *this); -} -template<typename DistTreeT, typename InterruptT> +template<typename FloatTreeT, typename InterruptT> void -ContourTracer<DistTreeT, InterruptT>::runSerial() +ContourTracer<FloatTreeT, InterruptT>::run(bool threaded) { - (*this)(tbb::blocked_range<int>(mBBox.min()[0], mBBox.max()[0]+1)); + if (threaded) { + tbb::parallel_for(tbb::blocked_range<int>(mBBox.min()[0], mBBox.max()[0]+1), *this); + } else { + (*this)(tbb::blocked_range<int>(mBBox.min()[0], mBBox.max()[0]+1)); + } } -template<typename DistTreeT, typename InterruptT> -ContourTracer<DistTreeT, InterruptT>::ContourTracer( - DistTreeT& distTree, const StencilTreeT& intersectionTree, InterruptT *interrupter) + +template<typename FloatTreeT, typename InterruptT> +ContourTracer<FloatTreeT, InterruptT>::ContourTracer( + FloatTreeT& distTree, const BoolTreeT& intersectionTree, InterruptT *interrupter) : mDistTree(distTree) , mDistAccessor(mDistTree) , mIntersectionTree(intersectionTree) @@ -753,9 +818,10 @@ ContourTracer<DistTreeT, InterruptT>::ContourTracer( } } -template<typename DistTreeT, typename InterruptT> -ContourTracer<DistTreeT, InterruptT>::ContourTracer( - const ContourTracer<DistTreeT, InterruptT> &rhs) + +template<typename FloatTreeT, typename InterruptT> +ContourTracer<FloatTreeT, InterruptT>::ContourTracer( + const ContourTracer<FloatTreeT, InterruptT> &rhs) : mDistTree(rhs.mDistTree) , mDistAccessor(mDistTree) , mIntersectionTree(rhs.mIntersectionTree) @@ -766,9 +832,10 @@ ContourTracer<DistTreeT, InterruptT>::ContourTracer( { } -template<typename DistTreeT, typename InterruptT> + +template<typename FloatTreeT, typename InterruptT> void -ContourTracer<DistTreeT, InterruptT>::operator()(const tbb::blocked_range<int> &range) const +ContourTracer<FloatTreeT, InterruptT>::operator()(const tbb::blocked_range<int> &range) const { // Slice up the volume and trace contours. int iStep = 1; @@ -783,12 +850,13 @@ ContourTracer<DistTreeT, InterruptT>::operator()(const tbb::blocked_range<int> & } } -template<typename DistTreeT, typename InterruptT> + +template<typename FloatTreeT, typename InterruptT> int -ContourTracer<DistTreeT, InterruptT>::sparseScan(int slice) const +ContourTracer<FloatTreeT, InterruptT>::sparseScan(int slice) const { bool lastVoxelWasOut = true; - int last_k; + int last_k = mBBox.min()[2]; Coord ijk(slice, mBBox.min()[1], mBBox.min()[2]); Coord step(mStepSize[mDistAccessor.getValueDepth(ijk) + 1]); @@ -820,12 +888,12 @@ ContourTracer<DistTreeT, InterruptT>::sparseScan(int slice) const } else if (lastVoxelWasOut) { - DistValueT& val = const_cast<DistValueT&>(mDistAccessor.getValue(ijk)); + FloatValueT& val = const_cast<FloatValueT&>(mDistAccessor.getValue(ijk)); val = -val; // flip sign } else { - DistValueT val; + FloatValueT val; for (Int32 n = 3; n < 6; n += 2) { n_ijk = ijk + util::COORD_OFFSETS[n]; @@ -837,7 +905,7 @@ ContourTracer<DistTreeT, InterruptT>::sparseScan(int slice) const if (lastVoxelWasOut) { - DistValueT& v = const_cast<DistValueT&>(mDistAccessor.getValue(ijk)); + FloatValueT& v = const_cast<FloatValueT&>(mDistAccessor.getValue(ijk)); v = -v; // flip sign const int tmp_k = ijk[2]; @@ -845,8 +913,9 @@ ContourTracer<DistTreeT, InterruptT>::sparseScan(int slice) const // backtrace for (--ijk[2]; ijk[2] >= last_k; --ijk[2]) { if (mIntersectionAccessor.isValueOn(ijk)) break; - DistValueT& vb = const_cast<DistValueT&>(mDistAccessor.getValue(ijk)); - if(vb < DistValueT(0.0)) vb = -vb; // flip sign + FloatValueT& vb = + const_cast<FloatValueT&>(mDistAccessor.getValue(ijk)); + if (vb < FloatValueT(0.0)) vb = -vb; // flip sign } last_k = tmp_k; @@ -868,183 +937,476 @@ ContourTracer<DistTreeT, InterruptT>::sparseScan(int slice) const //////////////////////////////////////// +/// @brief TBB body object that that finds seed points for the parallel flood fill. +template<typename FloatTreeT, typename InterruptT = util::NullInterrupter> +class SignMask +{ +public: + typedef typename FloatTreeT::ValueType FloatValueT; + typedef typename FloatTreeT::LeafNodeType FloatLeafT; + typedef tree::LeafManager<FloatTreeT> FloatLeafManager; + typedef typename tree::ValueAccessor<const FloatTreeT> FloatAccessorT; + typedef typename FloatTreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef typename BoolTreeT::LeafNodeType BoolLeafT; + typedef typename tree::ValueAccessor<BoolTreeT> BoolAccessorT; + typedef typename tree::ValueAccessor<const BoolTreeT> BoolConstAccessorT; + + + SignMask(const FloatLeafManager&, const FloatTreeT&, const BoolTreeT&, + InterruptT *interrupter = NULL); + + ~SignMask() {} + + void run(bool threaded = true); + + SignMask(SignMask<FloatTreeT, InterruptT>& rhs, tbb::split); + void operator() (const tbb::blocked_range<size_t> &range); + void join(SignMask<FloatTreeT, InterruptT>& rhs); + + BoolTreeT& signMaskTree() { return mSignMaskTree; } + +private: + // disallow copy by assignment + void operator=(const SignMask<FloatTreeT, InterruptT>&) {} + bool wasInterrupted() const { return mInterrupter && mInterrupter->wasInterrupted(); } + + const FloatLeafManager& mDistLeafs; + const FloatTreeT& mDistTree; + const BoolTreeT& mIntersectionTree; + + BoolTreeT mSignMaskTree; + + InterruptT *mInterrupter; +}; // class SignMask + + +template<typename FloatTreeT, typename InterruptT> +SignMask<FloatTreeT, InterruptT>::SignMask( + const FloatLeafManager& distLeafs, const FloatTreeT& distTree, + const BoolTreeT& intersectionTree, InterruptT *interrupter) + : mDistLeafs(distLeafs) + , mDistTree(distTree) + , mIntersectionTree(intersectionTree) + , mSignMaskTree(false) + , mInterrupter(interrupter) +{ +} + + +template<typename FloatTreeT, typename InterruptT> +SignMask<FloatTreeT, InterruptT>::SignMask( + SignMask<FloatTreeT, InterruptT>& rhs, tbb::split) + : mDistLeafs(rhs.mDistLeafs) + , mDistTree(rhs.mDistTree) + , mIntersectionTree(rhs.mIntersectionTree) + , mSignMaskTree(false) + , mInterrupter(rhs.mInterrupter) +{ +} + + +template<typename FloatTreeT, typename InterruptT> +void +SignMask<FloatTreeT, InterruptT>::run(bool threaded) +{ + if (threaded) tbb::parallel_reduce(mDistLeafs.getRange(), *this); + else (*this)(mDistLeafs.getRange()); +} + + +template<typename FloatTreeT, typename InterruptT> +void +SignMask<FloatTreeT, InterruptT>::operator()(const tbb::blocked_range<size_t> &range) +{ + FloatAccessorT distAcc(mDistTree); + BoolConstAccessorT intersectionAcc(mIntersectionTree); + BoolAccessorT maskAcc(mSignMaskTree); + + FloatValueT value; + CoordBBox bbox; + Coord& maxCoord = bbox.max(); + Coord& minCoord = bbox.min(); + Coord ijk; + const int extent = BoolLeafT::DIM - 1; + + for (size_t n = range.begin(); n < range.end(); ++n) { + + const FloatLeafT& distLeaf = mDistLeafs.leaf(n); + + minCoord = distLeaf.origin(); + maxCoord[0] = minCoord[0] + extent; + maxCoord[1] = minCoord[1] + extent; + maxCoord[2] = minCoord[2] + extent; + + const BoolLeafT* intersectionLeaf = intersectionAcc.probeConstLeaf(minCoord); + + BoolLeafT* maskLeafPt = new BoolLeafT(minCoord, false); + BoolLeafT& maskLeaf = *maskLeafPt; + bool addLeaf = false; + + bbox.expand(-1); + + typename FloatLeafT::ValueOnCIter it = distLeaf.cbeginValueOn(); + for (; it; ++it) { + if (intersectionLeaf && intersectionLeaf->isValueOn(it.pos())) continue; + if (it.getValue() < FloatValueT(0.0)) { + ijk = it.getCoord(); + if (bbox.isInside(ijk)) { + for (size_t i = 0; i < 6; ++i) { + if (distLeaf.probeValue(ijk+util::COORD_OFFSETS[i], value) && value>0.0) { + maskLeaf.setValueOn(ijk); + addLeaf = true; + break; + } + } + } else { + for (size_t i = 0; i < 6; ++i) { + if (distAcc.probeValue(ijk+util::COORD_OFFSETS[i], value) && value>0.0) { + maskLeaf.setValueOn(ijk); + addLeaf = true; + break; + } + } + } + } + } + + if (addLeaf) maskAcc.addLeaf(maskLeafPt); + else delete maskLeafPt; + } +} + + +template<typename FloatTreeT, typename InterruptT> +void +SignMask<FloatTreeT, InterruptT>::join(SignMask<FloatTreeT, InterruptT>& rhs) +{ + mSignMaskTree.merge(rhs.mSignMaskTree); +} + + +//////////////////////////////////////// + + +/// @brief TBB body object that performs a parallel flood fill +template<typename FloatTreeT, typename InterruptT = util::NullInterrupter> +class PropagateSign +{ +public: + typedef typename FloatTreeT::ValueType FloatValueT; + typedef typename FloatTreeT::LeafNodeType FloatLeafT; + typedef typename tree::ValueAccessor<FloatTreeT> FloatAccessorT; + typedef typename FloatTreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef typename BoolTreeT::LeafNodeType BoolLeafT; + typedef tree::LeafManager<BoolTreeT> BoolLeafManager; + typedef typename tree::ValueAccessor<BoolTreeT> BoolAccessorT; + typedef typename tree::ValueAccessor<const BoolTreeT> BoolConstAccessorT; + + PropagateSign(BoolLeafManager&, FloatTreeT&, const BoolTreeT&, InterruptT *interrupter = NULL); + + ~PropagateSign() {} + + void run(bool threaded = true); + + PropagateSign(PropagateSign<FloatTreeT, InterruptT>& rhs, tbb::split); + void operator() (const tbb::blocked_range<size_t> &range); + void join(PropagateSign<FloatTreeT, InterruptT>& rhs); + + BoolTreeT& signMaskTree() { return mSignMaskTree; } + +private: + // disallow copy by assignment + void operator=(const PropagateSign<FloatTreeT, InterruptT>&); + bool wasInterrupted() const { return mInterrupter && mInterrupter->wasInterrupted(); } + + BoolLeafManager& mOldSignMaskLeafs; + FloatTreeT& mDistTree; + const BoolTreeT& mIntersectionTree; + + BoolTreeT mSignMaskTree; + InterruptT *mInterrupter; +}; + + +template<typename FloatTreeT, typename InterruptT> +PropagateSign<FloatTreeT, InterruptT>::PropagateSign(BoolLeafManager& signMaskLeafs, + FloatTreeT& distTree, const BoolTreeT& intersectionTree, InterruptT *interrupter) + : mOldSignMaskLeafs(signMaskLeafs) + , mDistTree(distTree) + , mIntersectionTree(intersectionTree) + , mSignMaskTree(false) + , mInterrupter(interrupter) +{ +} + + +template<typename FloatTreeT, typename InterruptT> +PropagateSign<FloatTreeT, InterruptT>::PropagateSign( + PropagateSign<FloatTreeT, InterruptT>& rhs, tbb::split) + : mOldSignMaskLeafs(rhs.mOldSignMaskLeafs) + , mDistTree(rhs.mDistTree) + , mIntersectionTree(rhs.mIntersectionTree) + , mSignMaskTree(false) + , mInterrupter(rhs.mInterrupter) +{ +} + + +template<typename FloatTreeT, typename InterruptT> +void +PropagateSign<FloatTreeT, InterruptT>::run(bool threaded) +{ + if (threaded) tbb::parallel_reduce(mOldSignMaskLeafs.getRange(), *this); + else (*this)(mOldSignMaskLeafs.getRange()); +} + + +template<typename FloatTreeT, typename InterruptT> +void +PropagateSign<FloatTreeT, InterruptT>::operator()(const tbb::blocked_range<size_t> &range) +{ + FloatAccessorT distAcc(mDistTree); + BoolConstAccessorT intersectionAcc(mIntersectionTree); + BoolAccessorT maskAcc(mSignMaskTree); + + std::deque<Coord> coordList; + + FloatValueT value; + CoordBBox bbox; + Coord& maxCoord = bbox.max(); + Coord& minCoord = bbox.min(); + Coord ijk, nijk; + const int extent = BoolLeafT::DIM - 1; + + for (size_t n = range.begin(); n < range.end(); ++n) { + BoolLeafT& oldMaskLeaf = mOldSignMaskLeafs.leaf(n); + + minCoord = oldMaskLeaf.origin(); + maxCoord[0] = minCoord[0] + extent; + maxCoord[1] = minCoord[1] + extent; + maxCoord[2] = minCoord[2] + extent; + + FloatLeafT& distLeaf = *distAcc.probeLeaf(minCoord); + const BoolLeafT* intersectionLeaf = intersectionAcc.probeConstLeaf(minCoord); + + typename BoolLeafT::ValueOnCIter it = oldMaskLeaf.cbeginValueOn(); + for (; it; ++it) { + coordList.push_back(it.getCoord()); + + while (!coordList.empty()) { + + ijk = coordList.back(); + coordList.pop_back(); + + FloatValueT& dist = const_cast<FloatValueT&>(distLeaf.getValue(ijk)); + if (dist < FloatValueT(0.0)) { + dist = -dist; // flip sign + + for (size_t i = 0; i < 6; ++i) { + nijk = ijk + util::COORD_OFFSETS[i]; + if (bbox.isInside(nijk)) { + if (intersectionLeaf && intersectionLeaf->isValueOn(nijk)) continue; + + if (distLeaf.probeValue(nijk, value) && value < 0.0) { + coordList.push_back(nijk); + } + + } else { + if(!intersectionAcc.isValueOn(nijk) && + distAcc.probeValue(nijk, value) && value < 0.0) { + maskAcc.setValueOn(nijk); + } + } + } + } + } + } + } +} + + +template<typename FloatTreeT, typename InterruptT> +void +PropagateSign<FloatTreeT, InterruptT>::join(PropagateSign<FloatTreeT, InterruptT>& rhs) +{ + mSignMaskTree.merge(rhs.mSignMaskTree); +} + + +//////////////////////////////////////// + + // IntersectingVoxelSign /// @brief TBB body object that traversers all intersecting voxels (defined by the /// intersectingVoxelsGrid) and potentially flips their sign, by comparing the "closest point" /// directions of outside-marked and non-intersecting neighboring voxels -template<typename DistTreeT> +template<typename FloatTreeT> class IntersectingVoxelSign { public: - typedef typename DistTreeT::ValueType DistValueT; - typedef typename tree::ValueAccessor<DistTreeT> DistAccessorT; - typedef typename DistTreeT::template ValueConverter<Int32>::Type IndexTreeT; - typedef typename tree::ValueAccessor<IndexTreeT> IndexAccessorT; - typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT; - typedef typename tree::ValueAccessor<StencilTreeT> StencilAccessorT; - typedef tree::LeafManager<StencilTreeT> StencilArrayT; + typedef typename FloatTreeT::ValueType FloatValueT; + typedef typename tree::ValueAccessor<FloatTreeT> FloatAccessorT; + typedef typename FloatTreeT::template ValueConverter<Int32>::Type IntTreeT; + typedef typename tree::ValueAccessor<IntTreeT> IntAccessorT; + typedef typename FloatTreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef typename tree::ValueAccessor<BoolTreeT> BoolAccessorT; + typedef tree::LeafManager<BoolTreeT> BoolLeafManager; IntersectingVoxelSign( const std::vector<Vec3s>& pointList, const std::vector<Vec4I>& polygonList, - DistTreeT& distTree, - IndexTreeT& indexTree, - StencilTreeT& intersectionTree, - StencilArrayT& leafs); + FloatTreeT& distTree, + IntTreeT& indexTree, + BoolTreeT& intersectionTree, + BoolLeafManager& leafs); ~IntersectingVoxelSign() {} - void runParallel(); - void runSerial(); + void run(bool threaded = true); - IntersectingVoxelSign(const IntersectingVoxelSign<DistTreeT> &rhs); + IntersectingVoxelSign(const IntersectingVoxelSign<FloatTreeT> &rhs); void operator()(const tbb::blocked_range<size_t>&) const; private: - void operator=(const IntersectingVoxelSign<DistTreeT>&) {} + void operator=(const IntersectingVoxelSign<FloatTreeT>&) {} - void evalVoxel(const Coord& ijk) const; - Vec3d getClosestPointDir(const Coord& ijk) const; + Vec3d getClosestPoint(const Coord& ijk, const Vec4I& prim) const; std::vector<Vec3s> const * const mPointList; std::vector<Vec4I> const * const mPolygonList; - DistTreeT& mDistTree; - DistAccessorT mDistAccessor; - - IndexTreeT& mIndexTree; - IndexAccessorT mIndexAccessor; + FloatTreeT& mDistTree; + IntTreeT& mIndexTree; + BoolTreeT& mIntersectionTree; - StencilTreeT& mIntersectionTree; - StencilAccessorT mIntersectionAccessor; - StencilArrayT& mLeafs; + BoolLeafManager& mLeafs; }; -template<typename DistTreeT> -void -IntersectingVoxelSign<DistTreeT>::runParallel() -{ - tbb::parallel_for(mLeafs.getRange(), *this); -} -template<typename DistTreeT> +template<typename FloatTreeT> void -IntersectingVoxelSign<DistTreeT>::runSerial() +IntersectingVoxelSign<FloatTreeT>::run(bool threaded) { - (*this)(mLeafs.getRange()); + if (threaded) tbb::parallel_for(mLeafs.getRange(), *this); + else (*this)(mLeafs.getRange()); } -template<typename DistTreeT> -IntersectingVoxelSign<DistTreeT>::IntersectingVoxelSign( + +template<typename FloatTreeT> +IntersectingVoxelSign<FloatTreeT>::IntersectingVoxelSign( const std::vector<Vec3s>& pointList, const std::vector<Vec4I>& polygonList, - DistTreeT& distTree, - IndexTreeT& indexTree, - StencilTreeT& intersectionTree, - StencilArrayT& leafs) + FloatTreeT& distTree, + IntTreeT& indexTree, + BoolTreeT& intersectionTree, + BoolLeafManager& leafs) : mPointList(&pointList) , mPolygonList(&polygonList) , mDistTree(distTree) - , mDistAccessor(mDistTree) , mIndexTree(indexTree) - , mIndexAccessor(mIndexTree) , mIntersectionTree(intersectionTree) - , mIntersectionAccessor(mIntersectionTree) , mLeafs(leafs) { } -template<typename DistTreeT> -IntersectingVoxelSign<DistTreeT>::IntersectingVoxelSign( - const IntersectingVoxelSign<DistTreeT> &rhs) + +template<typename FloatTreeT> +IntersectingVoxelSign<FloatTreeT>::IntersectingVoxelSign( + const IntersectingVoxelSign<FloatTreeT> &rhs) : mPointList(rhs.mPointList) , mPolygonList(rhs.mPolygonList) , mDistTree(rhs.mDistTree) - , mDistAccessor(mDistTree) , mIndexTree(rhs.mIndexTree) - , mIndexAccessor(mIndexTree) , mIntersectionTree(rhs.mIntersectionTree) - , mIntersectionAccessor(mIntersectionTree) , mLeafs(rhs.mLeafs) { } -template<typename DistTreeT> + +template<typename FloatTreeT> void -IntersectingVoxelSign<DistTreeT>::operator()( +IntersectingVoxelSign<FloatTreeT>::operator()( const tbb::blocked_range<size_t>& range) const { - typename StencilTreeT::LeafNodeType::ValueOnCIter iter; + Coord ijk, nijk; + + FloatAccessorT distAcc(mDistTree); + BoolAccessorT maskAcc(mIntersectionTree); + IntAccessorT idxAcc(mIndexTree); + FloatValueT tmpValue; + Vec3d cpt, center, dir1, dir2; + + typename BoolTreeT::LeafNodeType::ValueOnCIter iter; for (size_t n = range.begin(); n < range.end(); ++n) { iter = mLeafs.leaf(n).cbeginValueOn(); for (; iter; ++iter) { - evalVoxel(iter.getCoord()); - } - } -} -template<typename DistTreeT> -void -IntersectingVoxelSign<DistTreeT>::evalVoxel(const Coord& ijk) const -{ - const DistValueT val = mDistAccessor.getValue(ijk), zeroVal(0.0); + ijk = iter.getCoord(); - if(!(val < zeroVal)) return; + FloatValueT value = distAcc.getValue(ijk); - Vec3d dir = getClosestPointDir(ijk), n_dir; - DistValueT n_val; - Coord n_ijk; + if (!(value < FloatValueT(0.0))) continue; - // Check voxel-face adjacent neighbors. - for (Int32 n = 0; n < 26; ++n) { - n_ijk = ijk + util::COORD_OFFSETS[n]; + center = Vec3d(ijk[0], ijk[1], ijk[2]); - if (mIntersectionAccessor.isValueOn(n_ijk)) continue; - if (!mDistAccessor.probeValue(n_ijk, n_val)) continue; - if (n_val < zeroVal) continue; + for (Int32 i = 0; i < 26; ++i) { + nijk = ijk + util::COORD_OFFSETS[i]; - n_dir = getClosestPointDir(n_ijk); + if (!maskAcc.isValueOn(nijk) && distAcc.probeValue(nijk, tmpValue)) { + if (tmpValue < FloatValueT(0.0)) continue; - if (n_dir.dot(dir) > 0.0 ) { - const_cast<IntersectingVoxelSign<DistTreeT> *>(this)-> - mDistAccessor.setValue(ijk, -val); - break; + const Vec4I& prim = (*mPolygonList)[idxAcc.getValue(nijk)]; + + cpt = getClosestPoint(nijk, prim); + + dir1 = center - cpt; + dir1.normalize(); + + dir2 = Vec3d(nijk[0], nijk[1], nijk[2]) - cpt; + dir2.normalize(); + + if (dir2.dot(dir1) > 0.0) { + distAcc.setValue(ijk, -value); + break; + } + } + } } } } -template<typename DistTreeT> + +template<typename FloatTreeT> Vec3d -IntersectingVoxelSign<DistTreeT>::getClosestPointDir(const Coord& ijk) const +IntersectingVoxelSign<FloatTreeT>::getClosestPoint(const Coord& ijk, const Vec4I& prim) const { Vec3d voxelCenter(ijk[0], ijk[1], ijk[2]); - const Vec4I& prim = (*mPolygonList)[mIndexAccessor.getValue(ijk)]; - const std::vector<Vec3s>& points = *mPointList; // Evaluate first triangle - const Vec3d a(points[prim[0]]); - const Vec3d b(points[prim[1]]); - const Vec3d c(points[prim[2]]); + const Vec3d a((*mPointList)[prim[0]]); + const Vec3d b((*mPointList)[prim[1]]); + const Vec3d c((*mPointList)[prim[2]]); Vec3d uvw; - Vec3d cpt = closestPointOnTriangleToPoint(a, c, b, voxelCenter, uvw); - Vec3d diff = voxelCenter - cpt; + Vec3d cpt1 = closestPointOnTriangleToPoint(a, c, b, voxelCenter, uvw); // Evaluate second triangle if quad. if (prim[3] != util::INVALID_IDX) { - const Vec3d d(points[prim[3]]); - cpt = closestPointOnTriangleToPoint(a, d, c, voxelCenter, uvw); - Vec3d tmpDiff = voxelCenter - cpt; + Vec3d diff1 = voxelCenter - cpt1; - if (tmpDiff.lengthSqr() < diff.lengthSqr()) { - diff = tmpDiff; + const Vec3d d((*mPointList)[prim[3]]); + + Vec3d cpt2 = closestPointOnTriangleToPoint(a, d, c, voxelCenter, uvw); + Vec3d diff2 = voxelCenter - cpt2; + + if (diff2.lengthSqr() < diff1.lengthSqr()) { + return cpt2; } } - diff.normalize(); - return diff; + return cpt1; } @@ -1054,63 +1416,58 @@ IntersectingVoxelSign<DistTreeT>::getClosestPointDir(const Coord& ijk) const // IntersectingVoxelCleaner /// @brief TBB body object that removes intersecting voxels that were set via /// voxelization of self-intersecting parts of a mesh -template<typename DistTreeT> +template<typename FloatTreeT> class IntersectingVoxelCleaner { public: - typedef typename DistTreeT::ValueType DistValueT; - typedef typename tree::ValueAccessor<DistTreeT> DistAccessorT; - typedef typename DistTreeT::LeafNodeType DistLeafT; - typedef typename DistTreeT::template ValueConverter<Int32>::Type IndexTreeT; - typedef typename tree::ValueAccessor<IndexTreeT> IndexAccessorT; - typedef typename IndexTreeT::LeafNodeType IndexLeafT; - typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT; - typedef typename tree::ValueAccessor<StencilTreeT> StencilAccessorT; - typedef typename StencilTreeT::LeafNodeType StencilLeafT; - typedef tree::LeafManager<StencilTreeT> StencilArrayT; - - IntersectingVoxelCleaner(DistTreeT& distTree, IndexTreeT& indexTree, - StencilTreeT& intersectionTree, StencilArrayT& leafs); + typedef typename FloatTreeT::ValueType FloatValueT; + typedef typename tree::ValueAccessor<FloatTreeT> DistAccessorT; + typedef typename FloatTreeT::LeafNodeType DistLeafT; + typedef typename FloatTreeT::template ValueConverter<Int32>::Type IntTreeT; + typedef typename tree::ValueAccessor<IntTreeT> IntAccessorT; + typedef typename IntTreeT::LeafNodeType IntLeafT; + typedef typename FloatTreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef typename tree::ValueAccessor<BoolTreeT> BoolAccessorT; + typedef typename BoolTreeT::LeafNodeType BoolLeafT; + typedef tree::LeafManager<BoolTreeT> BoolLeafManager; + + IntersectingVoxelCleaner(FloatTreeT& distTree, IntTreeT& indexTree, + BoolTreeT& intersectionTree, BoolLeafManager& leafs); ~IntersectingVoxelCleaner() {} - void runParallel(); - void runSerial(); + void run(bool threaded = true); - IntersectingVoxelCleaner(const IntersectingVoxelCleaner<DistTreeT> &rhs); + IntersectingVoxelCleaner(const IntersectingVoxelCleaner<FloatTreeT> &rhs); void operator()(const tbb::blocked_range<size_t>&) const; private: - void operator=(const IntersectingVoxelCleaner<DistTreeT>&) {} + void operator=(const IntersectingVoxelCleaner<FloatTreeT>&) {} - DistTreeT& mDistTree; - IndexTreeT& mIndexTree; - StencilTreeT& mIntersectionTree; - StencilArrayT& mLeafs; + FloatTreeT& mDistTree; + IntTreeT& mIndexTree; + BoolTreeT& mIntersectionTree; + BoolLeafManager& mLeafs; }; -template<typename DistTreeT> -void -IntersectingVoxelCleaner<DistTreeT>::runParallel() -{ - tbb::parallel_for(mLeafs.getRange(), *this); - mIntersectionTree.pruneInactive(); -} -template<typename DistTreeT> +template<typename FloatTreeT> void -IntersectingVoxelCleaner<DistTreeT>::runSerial() +IntersectingVoxelCleaner<FloatTreeT>::run(bool threaded) { - (*this)(mLeafs.getRange()); + if (threaded) tbb::parallel_for(mLeafs.getRange(), *this); + else (*this)(mLeafs.getRange()); + mIntersectionTree.pruneInactive(); } -template<typename DistTreeT> -IntersectingVoxelCleaner<DistTreeT>::IntersectingVoxelCleaner( - DistTreeT& distTree, - IndexTreeT& indexTree, - StencilTreeT& intersectionTree, - StencilArrayT& leafs) + +template<typename FloatTreeT> +IntersectingVoxelCleaner<FloatTreeT>::IntersectingVoxelCleaner( + FloatTreeT& distTree, + IntTreeT& indexTree, + BoolTreeT& intersectionTree, + BoolLeafManager& leafs) : mDistTree(distTree) , mIndexTree(indexTree) , mIntersectionTree(intersectionTree) @@ -1118,9 +1475,10 @@ IntersectingVoxelCleaner<DistTreeT>::IntersectingVoxelCleaner( { } -template<typename DistTreeT> -IntersectingVoxelCleaner<DistTreeT>::IntersectingVoxelCleaner( - const IntersectingVoxelCleaner<DistTreeT>& rhs) + +template<typename FloatTreeT> +IntersectingVoxelCleaner<FloatTreeT>::IntersectingVoxelCleaner( + const IntersectingVoxelCleaner<FloatTreeT>& rhs) : mDistTree(rhs.mDistTree) , mIndexTree(rhs.mIndexTree) , mIntersectionTree(rhs.mIntersectionTree) @@ -1128,52 +1486,54 @@ IntersectingVoxelCleaner<DistTreeT>::IntersectingVoxelCleaner( { } -template<typename DistTreeT> + +template<typename FloatTreeT> void -IntersectingVoxelCleaner<DistTreeT>::operator()( +IntersectingVoxelCleaner<FloatTreeT>::operator()( const tbb::blocked_range<size_t>& range) const { Coord ijk, m_ijk; bool turnOff; - DistValueT value; + FloatValueT value; Index offset; - typename StencilLeafT::ValueOnCIter iter; + typename BoolLeafT::ValueOnCIter iter; - IndexAccessorT indexAcc(mIndexTree); + IntAccessorT indexAcc(mIndexTree); DistAccessorT distAcc(mDistTree); - StencilAccessorT maskAcc(mIntersectionTree); + BoolAccessorT maskAcc(mIntersectionTree); for (size_t n = range.begin(); n < range.end(); ++n) { - StencilLeafT& maskLeaf = mLeafs.leaf(n); - - ijk = maskLeaf.getOrigin(); + BoolLeafT& maskLeaf = mLeafs.leaf(n); - DistLeafT& distLeaf = *distAcc.probeLeaf(ijk); + ijk = maskLeaf.origin(); - iter = maskLeaf.cbeginValueOn(); - for (; iter; ++iter) { + DistLeafT * distLeaf = distAcc.probeLeaf(ijk); + if (distLeaf) { + iter = maskLeaf.cbeginValueOn(); + for (; iter; ++iter) { - offset = iter.pos(); + offset = iter.pos(); - if(distLeaf.getValue(offset) > 0.1) continue; + if(distLeaf->getValue(offset) > 0.0) continue; - ijk = iter.getCoord(); - turnOff = true; - for (Int32 m = 0; m < 26; ++m) { - m_ijk = ijk + util::COORD_OFFSETS[m]; - if (distAcc.probeValue(m_ijk, value)) { - if (value > 0.1) { - turnOff = false; - break; + ijk = iter.getCoord(); + turnOff = true; + for (Int32 m = 0; m < 26; ++m) { + m_ijk = ijk + util::COORD_OFFSETS[m]; + if (distAcc.probeValue(m_ijk, value)) { + if (value > 0.0) { + turnOff = false; + break; + } } } - } - if (turnOff) { - maskLeaf.setValueOff(offset); - distLeaf.setValueOn(offset, -0.86602540378443861); + if (turnOff) { + maskLeaf.setValueOff(offset); + distLeaf->setValueOn(offset, -0.86602540378443861); + } } } } @@ -1186,65 +1546,59 @@ IntersectingVoxelCleaner<DistTreeT>::operator()( // ShellVoxelCleaner /// @brief TBB body object that removes non-intersecting voxels that where set by rasterizing /// self-intersecting parts of the mesh. -template<typename DistTreeT> +template<typename FloatTreeT> class ShellVoxelCleaner { public: - typedef typename DistTreeT::ValueType DistValueT; - typedef typename tree::ValueAccessor<DistTreeT> DistAccessorT; - typedef typename DistTreeT::LeafNodeType DistLeafT; - typedef tree::LeafManager<DistTreeT> DistArrayT; - typedef typename DistTreeT::template ValueConverter<Int32>::Type IndexTreeT; - typedef typename tree::ValueAccessor<IndexTreeT> IndexAccessorT; - typedef typename IndexTreeT::LeafNodeType IndexLeafT; - typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT; - typedef typename tree::ValueAccessor<StencilTreeT> StencilAccessorT; - typedef typename StencilTreeT::LeafNodeType StencilLeafT; - - ShellVoxelCleaner(DistTreeT& distTree, DistArrayT& leafs, IndexTreeT& indexTree, - StencilTreeT& intersectionTree); + typedef typename FloatTreeT::ValueType FloatValueT; + typedef typename tree::ValueAccessor<FloatTreeT> DistAccessorT; + typedef typename FloatTreeT::LeafNodeType DistLeafT; + typedef tree::LeafManager<FloatTreeT> DistArrayT; + typedef typename FloatTreeT::template ValueConverter<Int32>::Type IntTreeT; + typedef typename tree::ValueAccessor<IntTreeT> IntAccessorT; + typedef typename IntTreeT::LeafNodeType IntLeafT; + typedef typename FloatTreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef typename tree::ValueAccessor<BoolTreeT> BoolAccessorT; + typedef typename BoolTreeT::LeafNodeType BoolLeafT; + + ShellVoxelCleaner(FloatTreeT& distTree, DistArrayT& leafs, IntTreeT& indexTree, + BoolTreeT& intersectionTree); ~ShellVoxelCleaner() {} - void runParallel(); - void runSerial(); + void run(bool threaded = true); - ShellVoxelCleaner(const ShellVoxelCleaner<DistTreeT> &rhs); + ShellVoxelCleaner(const ShellVoxelCleaner<FloatTreeT> &rhs); void operator()(const tbb::blocked_range<size_t>&) const; private: - void operator=(const ShellVoxelCleaner<DistTreeT>&) {} + void operator=(const ShellVoxelCleaner<FloatTreeT>&) {} - DistTreeT& mDistTree; + FloatTreeT& mDistTree; DistArrayT& mLeafs; - IndexTreeT& mIndexTree; - StencilTreeT& mIntersectionTree; + IntTreeT& mIndexTree; + BoolTreeT& mIntersectionTree; }; -template<typename DistTreeT> -void -ShellVoxelCleaner<DistTreeT>::runParallel() -{ - tbb::parallel_for(mLeafs.getRange(), *this); - mDistTree.pruneInactive(); - mIndexTree.pruneInactive(); -} -template<typename DistTreeT> +template<typename FloatTreeT> void -ShellVoxelCleaner<DistTreeT>::runSerial() +ShellVoxelCleaner<FloatTreeT>::run(bool threaded) { - (*this)(mLeafs.getRange()); + if (threaded) tbb::parallel_for(mLeafs.getRange(), *this); + else (*this)(mLeafs.getRange()); + mDistTree.pruneInactive(); mIndexTree.pruneInactive(); } -template<typename DistTreeT> -ShellVoxelCleaner<DistTreeT>::ShellVoxelCleaner( - DistTreeT& distTree, + +template<typename FloatTreeT> +ShellVoxelCleaner<FloatTreeT>::ShellVoxelCleaner( + FloatTreeT& distTree, DistArrayT& leafs, - IndexTreeT& indexTree, - StencilTreeT& intersectionTree) + IntTreeT& indexTree, + BoolTreeT& intersectionTree) : mDistTree(distTree) , mLeafs(leafs) , mIndexTree(indexTree) @@ -1252,9 +1606,10 @@ ShellVoxelCleaner<DistTreeT>::ShellVoxelCleaner( { } -template<typename DistTreeT> -ShellVoxelCleaner<DistTreeT>::ShellVoxelCleaner( - const ShellVoxelCleaner<DistTreeT> &rhs) + +template<typename FloatTreeT> +ShellVoxelCleaner<FloatTreeT>::ShellVoxelCleaner( + const ShellVoxelCleaner<FloatTreeT> &rhs) : mDistTree(rhs.mDistTree) , mLeafs(rhs.mLeafs) , mIndexTree(rhs.mIndexTree) @@ -1262,33 +1617,34 @@ ShellVoxelCleaner<DistTreeT>::ShellVoxelCleaner( { } -template<typename DistTreeT> + +template<typename FloatTreeT> void -ShellVoxelCleaner<DistTreeT>::operator()( +ShellVoxelCleaner<FloatTreeT>::operator()( const tbb::blocked_range<size_t>& range) const { Coord ijk, m_ijk; bool turnOff; - DistValueT value; + FloatValueT value; Index offset; typename DistLeafT::ValueOnCIter iter; - const DistValueT distBG = mDistTree.background(); + const FloatValueT distBG = mDistTree.background(); const Int32 indexBG = mIntersectionTree.background(); - IndexAccessorT indexAcc(mIndexTree); + IntAccessorT indexAcc(mIndexTree); DistAccessorT distAcc(mDistTree); - StencilAccessorT maskAcc(mIntersectionTree); + BoolAccessorT maskAcc(mIntersectionTree); for (size_t n = range.begin(); n < range.end(); ++n) { DistLeafT& distLeaf = mLeafs.leaf(n); - ijk = distLeaf.getOrigin(); + ijk = distLeaf.origin(); - const StencilLeafT* maskLeaf = maskAcc.probeConstLeaf(ijk); - IndexLeafT& indexLeaf = *indexAcc.probeLeaf(ijk); + const BoolLeafT* maskLeaf = maskAcc.probeConstLeaf(ijk); + IntLeafT& indexLeaf = *indexAcc.probeLeaf(ijk); iter = distLeaf.cbeginValueOn(); for (; iter; ++iter) { @@ -1301,7 +1657,7 @@ ShellVoxelCleaner<DistTreeT>::operator()( ijk = iter.getCoord(); turnOff = true; - for (Int32 m = 0; m < 18; ++m) { + for (Int32 m = 0; m < 26; ++m) { m_ijk = ijk + util::COORD_OFFSETS[m]; if (maskAcc.isValueOn(m_ijk)) { turnOff = false; @@ -1321,370 +1677,347 @@ ShellVoxelCleaner<DistTreeT>::operator()( //////////////////////////////////////// +template<typename TreeType> +struct CopyActiveVoxelsOp +{ + typedef typename tree::ValueAccessor<TreeType> AccessorT; + + CopyActiveVoxelsOp(TreeType& tree) : mAcc(tree) { } + + template <typename LeafNodeType> + void operator()(LeafNodeType &leaf, size_t) const + { + LeafNodeType* rhsLeaf = const_cast<LeafNodeType*>(mAcc.probeLeaf(leaf.origin())); + typename LeafNodeType::ValueOnIter iter = leaf.beginValueOn(); + for (; iter; ++iter) { + rhsLeaf->setValueOnly(iter.pos(), iter.getValue()); + } + } + +private: + AccessorT mAcc; +}; + + // ExpandNB /// @brief TBB body object to expand the level set narrow band /// @note The interior and exterior widths should be in world space units and squared. -template<typename DistTreeT> +template<typename FloatTreeT> class ExpandNB { public: - typedef typename DistTreeT::ValueType DistValueT; - typedef typename tree::ValueAccessor<DistTreeT> DistAccessorT; - typedef typename DistTreeT::template ValueConverter<Int32>::Type IndexTreeT; - typedef typename tree::ValueAccessor<IndexTreeT> IndexAccessorT; - typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT; - typedef typename tree::ValueAccessor<StencilTreeT> StencilAccessorT; - typedef tree::LeafManager<StencilTreeT> StencilArrayT; - - typedef typename DistTreeT::LeafNodeType DistLeafT; - typedef typename IndexTreeT::LeafNodeType IndexLeafT; - typedef typename StencilTreeT::LeafNodeType StencilLeafT; - - ExpandNB(const std::vector<Vec3s>& pointList, const std::vector<Vec4I>& polygonList, - DistTreeT& distTree, IndexTreeT& indexTree, StencilTreeT& maskTree, StencilArrayT& leafs, - DistValueT exteriorBandWidth, DistValueT interiorBandWidth, DistValueT voxelSize); - - ExpandNB(const ExpandNB<DistTreeT>& rhs, tbb::split); - + typedef typename FloatTreeT::ValueType FloatValueT; + typedef typename FloatTreeT::LeafNodeType FloatLeafT; + typedef typename tree::ValueAccessor<FloatTreeT> FloatAccessorT; + typedef typename FloatTreeT::template ValueConverter<Int32>::Type IntTreeT; + typedef typename IntTreeT::LeafNodeType IntLeafT; + typedef typename tree::ValueAccessor<IntTreeT> IntAccessorT; + typedef typename FloatTreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef typename BoolTreeT::LeafNodeType BoolLeafT; + typedef tree::LeafManager<BoolTreeT> BoolLeafManager; + typedef typename tree::ValueAccessor<BoolTreeT> BoolAccessorT; + + ExpandNB(BoolLeafManager& leafs, + FloatTreeT& distTree, IntTreeT& indexTree, BoolTreeT& maskTree, + FloatValueT exteriorBandWidth, FloatValueT interiorBandWidth, FloatValueT voxelSize, + const std::vector<Vec3s>& pointList, const std::vector<Vec4I>& polygonList); + + void run(bool threaded = true); + + void operator()(const tbb::blocked_range<size_t>&); + void join(ExpandNB<FloatTreeT>&); + ExpandNB(const ExpandNB<FloatTreeT>&, tbb::split); ~ExpandNB() {} - void runParallel(); - void runSerial(); - - void operator()(const tbb::blocked_range<size_t>&) const; - private: - void operator=(const ExpandNB<DistTreeT>&) {} + void operator=(const ExpandNB<FloatTreeT>&) {} - double getDist(const Coord&, DistAccessorT&, IndexAccessorT&, StencilAccessorT&, - Int32& primIndex) const; + double evalVoxelDist(const Coord&, FloatAccessorT&, IntAccessorT&, + BoolAccessorT&, std::vector<Int32>&, Int32&) const; - double getDist(const Coord&, DistLeafT&, IndexLeafT&, StencilLeafT&, - Int32& primIndex) const; + double evalVoxelDist(const Coord&, FloatLeafT&, IntLeafT&, + BoolLeafT&, std::vector<Int32>&, Int32&) const; - double getDistToPrim(const Coord& ijk, const Int32 polyIdx) const; + double closestPrimDist(const Coord&, std::vector<Int32>&, Int32&) const; - std::vector<Vec3s> const * const mPointList; - std::vector<Vec4I> const * const mPolygonList; + BoolLeafManager& mMaskLeafs; - DistTreeT& mDistTree; - IndexTreeT& mIndexTree; - StencilTreeT& mMaskTree; - StencilArrayT& mLeafs; + FloatTreeT& mDistTree; + IntTreeT& mIndexTree; + BoolTreeT& mMaskTree; - const DistValueT mExteriorBandWidth, mInteriorBandWidth, mVoxelSize; -}; + const FloatValueT mExteriorBandWidth, mInteriorBandWidth, mVoxelSize; + const std::vector<Vec3s>& mPointList; + const std::vector<Vec4I>& mPolygonList; -template<typename DistTreeT> -void -ExpandNB<DistTreeT>::runParallel() -{ - tbb::parallel_for(mLeafs.getRange(), *this); - mMaskTree.pruneInactive(); -} + FloatTreeT mNewDistTree; + IntTreeT mNewIndexTree; + BoolTreeT mNewMaskTree; +}; -template<typename DistTreeT> -void -ExpandNB<DistTreeT>::runSerial() -{ - (*this)(mLeafs.getRange()); - mMaskTree.pruneInactive(); -} -template<typename DistTreeT> -ExpandNB<DistTreeT>::ExpandNB( +template<typename FloatTreeT> +ExpandNB<FloatTreeT>::ExpandNB( + BoolLeafManager& leafs, + FloatTreeT& distTree, + IntTreeT& indexTree, + BoolTreeT& maskTree, + FloatValueT exteriorBandWidth, + FloatValueT interiorBandWidth, + FloatValueT voxelSize, const std::vector<Vec3s>& pointList, - const std::vector<Vec4I>& polygonList, - DistTreeT& distTree, - IndexTreeT& indexTree, - StencilTreeT& maskTree, - StencilArrayT& leafs, - DistValueT exteriorBandWidth, DistValueT interiorBandWidth, - DistValueT voxelSize) - : mPointList(&pointList) - , mPolygonList(&polygonList) + const std::vector<Vec4I>& polygonList) + : mMaskLeafs(leafs) , mDistTree(distTree) , mIndexTree(indexTree) , mMaskTree(maskTree) - , mLeafs(leafs) , mExteriorBandWidth(exteriorBandWidth) , mInteriorBandWidth(interiorBandWidth) , mVoxelSize(voxelSize) + , mPointList(pointList) + , mPolygonList(polygonList) + , mNewDistTree(std::numeric_limits<FloatValueT>::max()) + , mNewIndexTree(Int32(util::INVALID_IDX)) + , mNewMaskTree(false) { } -template<typename DistTreeT> -ExpandNB<DistTreeT>::ExpandNB(const ExpandNB<DistTreeT>& rhs, tbb::split) - : mPointList(rhs.mPointList) - , mPolygonList(rhs.mPolygonList) + +template<typename FloatTreeT> +ExpandNB<FloatTreeT>::ExpandNB(const ExpandNB<FloatTreeT>& rhs, tbb::split) + : mMaskLeafs(rhs.mMaskLeafs) , mDistTree(rhs.mDistTree) , mIndexTree(rhs.mIndexTree) , mMaskTree(rhs.mMaskTree) - , mLeafs(rhs.mLeafs) , mExteriorBandWidth(rhs.mExteriorBandWidth) , mInteriorBandWidth(rhs.mInteriorBandWidth) , mVoxelSize(rhs.mVoxelSize) + , mPointList(rhs.mPointList) + , mPolygonList(rhs.mPolygonList) + , mNewDistTree(std::numeric_limits<FloatValueT>::max()) + , mNewIndexTree(Int32(util::INVALID_IDX)) + , mNewMaskTree(false) { } -template<typename DistTreeT> + +template<typename FloatTreeT> void -ExpandNB<DistTreeT>::operator()(const tbb::blocked_range<size_t>& range) const +ExpandNB<FloatTreeT>::run(bool threaded) { + if (threaded) tbb::parallel_reduce(mMaskLeafs.getRange(), *this); + else (*this)(mMaskLeafs.getRange()); + + // Copy only the active voxels (tree::merge does branch stealing + // which also moves indicative values). + mDistTree.topologyUnion(mNewDistTree); + tree::LeafManager<FloatTreeT> leafs(mNewDistTree); + leafs.foreach(CopyActiveVoxelsOp<FloatTreeT>(mDistTree)); + + mIndexTree.merge(mNewIndexTree); + mMaskTree.clear(); + mMaskTree.merge(mNewMaskTree); +} + + +template<typename FloatTreeT> +void +ExpandNB<FloatTreeT>::operator()(const tbb::blocked_range<size_t>& range) +{ Coord ijk; - Int32 closestPrimIndex = 0; - Index pos; - DistValueT distance; + Int32 closestPrim = 0; + Index pos = 0; + FloatValueT distance; bool inside; - DistAccessorT distAcc(mDistTree); - IndexAccessorT indexAcc(mIndexTree); - StencilAccessorT maskAcc(mMaskTree); + FloatAccessorT newDistAcc(mNewDistTree); + IntAccessorT newIndexAcc(mNewIndexTree); + BoolAccessorT newMaskAcc(mNewMaskTree); + + FloatAccessorT distAcc(mDistTree); + IntAccessorT indexAcc(mIndexTree); + BoolAccessorT maskAcc(mMaskTree); CoordBBox bbox; + std::vector<Int32> primitives(18); for (size_t n = range.begin(); n < range.end(); ++n) { - StencilLeafT& maskLeaf = mLeafs.leaf(n); + BoolLeafT& maskLeaf = mMaskLeafs.leaf(n); if (maskLeaf.isEmpty()) continue; - ijk = maskLeaf.getOrigin(); + ijk = maskLeaf.origin(); - DistLeafT& distLeaf = *distAcc.probeLeaf(ijk); - IndexLeafT& indexLeaf = *indexAcc.probeLeaf(ijk); + FloatLeafT* distLeafPt = distAcc.probeLeaf(ijk); + + if (!distLeafPt) { + distLeafPt = new FloatLeafT(ijk, distAcc.getValue(ijk)); + newDistAcc.addLeaf(distLeafPt); + } + + IntLeafT* indexLeafPt = indexAcc.probeLeaf(ijk); + if (!indexLeafPt) indexLeafPt = newIndexAcc.touchLeaf(ijk); bbox = maskLeaf.getNodeBoundingBox(); bbox.expand(-1); - typename StencilLeafT::ValueOnIter iter = maskLeaf.beginValueOn(); + typename BoolLeafT::ValueOnIter iter = maskLeaf.beginValueOn(); for (; iter; ++iter) { ijk = iter.getCoord(); - distance = Tolerance<DistValueT>::epsilon(); - if (bbox.isInside(ijk)) { - distance += getDist(ijk, distLeaf, indexLeaf, maskLeaf, closestPrimIndex); + distance = evalVoxelDist(ijk, *distLeafPt, *indexLeafPt, maskLeaf, + primitives, closestPrim); } else { - distance += getDist(ijk, distAcc, indexAcc, maskAcc, closestPrimIndex); + distance = evalVoxelDist(ijk, distAcc, indexAcc, maskAcc, + primitives, closestPrim); } - + pos = iter.pos(); - inside = distLeaf.getValue(pos) < DistValueT(0.0); + inside = distLeafPt->getValue(pos) < FloatValueT(0.0); if (!inside && distance < mExteriorBandWidth) { - distLeaf.setValueOn(pos, distance); - indexLeaf.setValueOn(pos, closestPrimIndex); + distLeafPt->setValueOn(pos, distance); + indexLeafPt->setValueOn(pos, closestPrim); } else if (inside && distance < mInteriorBandWidth) { - distLeaf.setValueOn(pos, -distance); - indexLeaf.setValueOn(pos, closestPrimIndex); + distLeafPt->setValueOn(pos, -distance); + indexLeafPt->setValueOn(pos, closestPrim); } else { - iter.setValueOff(); + continue; + } + + for (Int32 i = 0; i < 6; ++i) { + newMaskAcc.setValueOn(ijk + util::COORD_OFFSETS[i]); } } } } -template<typename DistTreeT> + +template<typename FloatTreeT> double -ExpandNB<DistTreeT>::getDist( +ExpandNB<FloatTreeT>::evalVoxelDist( const Coord& ijk, - DistAccessorT& distAcc, - IndexAccessorT& indexAcc, - StencilAccessorT& maskAcc, - Int32& primIndex) const + FloatAccessorT& distAcc, + IntAccessorT& indexAcc, + BoolAccessorT& maskAcc, + std::vector<Int32>& prims, + Int32& closestPrim) const { - Vec3d voxelCenter(ijk[0], ijk[1], ijk[2]); - DistValueT nDist, dist = std::numeric_limits<double>::max(); + FloatValueT tmpDist, minDist = std::numeric_limits<FloatValueT>::max(); + prims.clear(); - // Find neighbor with closest face point + // Collect primitive indices from active neighbors and min distance. Coord n_ijk; for (Int32 n = 0; n < 18; ++n) { n_ijk = ijk + util::COORD_OFFSETS[n]; - if (!maskAcc.isValueOn(n_ijk) && distAcc.probeValue(n_ijk, nDist)) { - nDist = std::abs(nDist); - if (nDist < dist) { - dist = nDist; - primIndex = indexAcc.getValue(n_ijk); - } + if (!maskAcc.isValueOn(n_ijk) && distAcc.probeValue(n_ijk, tmpDist)) { + prims.push_back(indexAcc.getValue(n_ijk)); + tmpDist = std::abs(tmpDist); + if (tmpDist < minDist) minDist = tmpDist; } } // Calc. this voxels distance to the closest primitive. - DistValueT newDist = getDistToPrim(ijk, primIndex); + tmpDist = FloatValueT(closestPrimDist(ijk, prims, closestPrim)); // Forces the gradient to be monotonic for non-manifold // polygonal models with self-intersections. - return newDist > dist ? newDist : dist + mVoxelSize; + return tmpDist > minDist ? tmpDist : minDist + mVoxelSize; } -template<typename DistTreeT> + +// Leaf specialized version. +template<typename FloatTreeT> double -ExpandNB<DistTreeT>::getDist( +ExpandNB<FloatTreeT>::evalVoxelDist( const Coord& ijk, - DistLeafT& distLeaf, - IndexLeafT& indexLeaf, - StencilLeafT& maskLeaf, - Int32& primIndex) const + FloatLeafT& distLeaf, + IntLeafT& indexLeaf, + BoolLeafT& maskLeaf, + std::vector<Int32>& prims, + Int32& closestPrim) const { - Vec3d voxelCenter(ijk[0], ijk[1], ijk[2]); - DistValueT nDist, dist = std::numeric_limits<double>::max(); + FloatValueT tmpDist, minDist = std::numeric_limits<FloatValueT>::max(); + prims.clear(); Index pos; for (Int32 n = 0; n < 18; ++n) { - pos = DistLeafT::coord2offset(ijk + util::COORD_OFFSETS[n]); - if (!maskLeaf.isValueOn(pos) && distLeaf.probeValue(pos, nDist)) { - nDist = std::abs(nDist); - if (nDist < dist) { - dist = nDist; - primIndex = indexLeaf.getValue(pos); - } + pos = FloatLeafT::coordToOffset(ijk + util::COORD_OFFSETS[n]); + if (!maskLeaf.isValueOn(pos) && distLeaf.probeValue(pos, tmpDist)) { + prims.push_back(indexLeaf.getValue(pos)); + tmpDist = std::abs(tmpDist); + if (tmpDist < minDist) minDist = tmpDist; } } - DistValueT newDist = getDistToPrim(ijk, primIndex); - - return newDist > dist ? newDist : dist + mVoxelSize; + tmpDist = FloatValueT(closestPrimDist(ijk, prims, closestPrim)); + return tmpDist > minDist ? tmpDist : minDist + mVoxelSize; } -template<typename DistTreeT> +template<typename FloatTreeT> double -ExpandNB<DistTreeT>::getDistToPrim(const Coord& ijk, const Int32 polyIdx) const +ExpandNB<FloatTreeT>::closestPrimDist(const Coord& ijk, + std::vector<Int32>& prims, Int32& closestPrim) const { - Vec3d voxelCenter(ijk[0], ijk[1], ijk[2]); - const Vec4I& verts = (*mPolygonList)[polyIdx]; - const std::vector<Vec3s>& points = *mPointList; - - // Evaluate first triangle - const Vec3d a(points[verts[0]]); - const Vec3d b(points[verts[1]]); - const Vec3d c(points[verts[2]]); + std::sort(prims.begin(), prims.end()); - Vec3d uvw; - double dist = (voxelCenter - - closestPointOnTriangleToPoint(a, c, b, voxelCenter, uvw)).lengthSqr(); + Int32 lastPrim = -1; + Vec3d uvw, voxelCenter(ijk[0], ijk[1], ijk[2]); + double primDist, tmpDist, dist = std::numeric_limits<double>::max(); - // Split-up quad into a second triangle and calac distance. - if (util::INVALID_IDX != verts[3]) { - const Vec3d d(points[verts[3]]); + for (size_t n = 0, N = prims.size(); n < N; ++n) { + if (prims[n] == lastPrim) continue; - double secondDist = (voxelCenter - - closestPointOnTriangleToPoint(a, d, c, voxelCenter, uvw)).lengthSqr(); + lastPrim = prims[n]; - if (secondDist < dist) dist = secondDist; - } - - return std::sqrt(dist) * double(mVoxelSize); -} + const Vec4I& verts = mPolygonList[lastPrim]; + // Evaluate first triangle + const Vec3d a(mPointList[verts[0]]); + const Vec3d b(mPointList[verts[1]]); + const Vec3d c(mPointList[verts[2]]); -//////////////////////////////////////// + primDist = (voxelCenter - + closestPointOnTriangleToPoint(a, c, b, voxelCenter, uvw)).lengthSqr(); + // Split-up quad into a second triangle and calac distance. + if (util::INVALID_IDX != verts[3]) { + const Vec3d d(mPointList[verts[3]]); -// Helper methods + tmpDist = (voxelCenter - + closestPointOnTriangleToPoint(a, d, c, voxelCenter, uvw)).lengthSqr(); -/// @brief Surface tracing method that flips the sign of interior marked voxels -/// and will not cross the boundary defined by the intersecting voxels -/// -/// @param seed the coordinates of a marked interior seed point -/// @param distTree the distance field to operate on -/// @param intersectionTree tree that defines the surface boundary -template<typename DistTreeT> -inline void -surfaceTracer(const Coord &seed, DistTreeT& distTree, - typename DistTreeT::template ValueConverter<bool>::Type& intersectionTree) -{ - typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT; - typedef typename tree::ValueAccessor<StencilTreeT> StencilAccessorT; - typedef typename tree::ValueAccessor<DistTreeT> DistAccessorT; - typedef typename DistTreeT::ValueType DistValueT; - - StencilAccessorT intrAccessor(intersectionTree); - DistAccessorT distAccessor(distTree); - - std::deque<Coord> coordList; - coordList.push_back(seed); - Coord ijk, n_ijk; - - while (!coordList.empty()) { - ijk = coordList.back(); - coordList.pop_back(); - - if (!distAccessor.isValueOn(ijk)) continue; - - DistValueT& dist = const_cast<DistValueT&>(distAccessor.getValue(ijk)); - if (!(dist < 0.0)) continue; - dist = -dist; // flip sign - - for (int n = 0; n < 6; ++n) { - n_ijk = ijk + util::COORD_OFFSETS[n]; + if (tmpDist < primDist) primDist = tmpDist; + } - if (!intrAccessor.isValueOn(n_ijk)) { // Don't cross the interface - if (distAccessor.isValueOn(n_ijk)) { // Is part of the narrow band - if (distAccessor.getValue(n_ijk) < 0.0) { // Marked as outside. - coordList.push_back(n_ijk); - } - } - } + if (primDist < dist) { + dist = primDist; + closestPrim = lastPrim; + } + } - } // END neighbor voxel loop. - } // END coordList loop. + return std::sqrt(dist) * double(mVoxelSize); } -/// @brief Iterate sparsely over a distance grid to find regions with inconsistent sign -/// information and use internal::surfaceTracer to resolve those inconsistencies. -/// -/// @param distTree signed distance field to operate on -/// @param intersectionTree tree that defines the surface boundary for the surface tracer -/// @param interrupter an object that implements the util::NullInterrupter interface -template<typename DistTreeT, typename InterruptT> -inline void -propagateSign(DistTreeT& distTree, - typename DistTreeT::template ValueConverter<bool>::Type& intersectionTree, - InterruptT *interrupter = NULL) +template<typename FloatTreeT> +void +ExpandNB<FloatTreeT>::join(ExpandNB<FloatTreeT>& rhs) { - typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT; - typedef typename tree::ValueAccessor<StencilTreeT> StencilAccessorT; - typedef typename tree::ValueAccessor<DistTreeT> DistAccessorT; - typedef typename DistTreeT::ValueType DistValueT; - - StencilAccessorT intrAccessor(intersectionTree); - DistAccessorT distAccessor(distTree); - Coord ijk, n_ijk; - - typename DistTreeT::LeafIter leafIter = distTree.beginLeaf(); - for (; leafIter; leafIter.next()) { - - if (interrupter && interrupter->wasInterrupted()) break; - - typename DistTreeT::LeafNodeType::ValueOnIter iter = leafIter->beginValueOn(); - for (; iter; iter.next()) { - - ijk = iter.getCoord(); - - // Ignore intersecting voxels. - if (intrAccessor.isValueOn(ijk)) continue; - - if (iter.getValue() < 0.0) { - for (Int32 n = 0; n < 6; ++n) { - n_ijk = ijk + util::COORD_OFFSETS[n]; + mNewDistTree.merge(rhs.mNewDistTree); + mNewIndexTree.merge(rhs.mNewIndexTree); + mNewMaskTree.merge(rhs.mNewMaskTree); +} - if (distAccessor.isValueOn(n_ijk) && distAccessor.getValue(n_ijk) > 0.0) { - surfaceTracer(ijk, distTree, intersectionTree); - break; - } - } - } - } // END voxel iteration - } // END leaf iteration -} +//////////////////////////////////////// template<typename ValueType> @@ -1760,14 +2093,14 @@ struct TrimOp typename LeafNodeType::ValueOnIter iter = leaf.beginValueOn(); for (; iter; ++iter) { - const ValueType& val = iter.getValue(); + ValueType& val = const_cast<ValueType&>(iter.getValue()); const bool inside = val < ValueType(0.0); if (inside && !(val > -mInBandWidth)) { - iter.setValue(-mInBandWidth); + val = -mInBandWidth; iter.setValueOff(); } else if (!inside && !(val < mExBandWidth)) { - iter.setValue(mExBandWidth); + val = mExBandWidth; iter.setValueOff(); } } @@ -1823,7 +2156,6 @@ struct RenormOp { const ValueType dt = mCFL * mVoxelSize, one(1.0), invDx = one / mVoxelSize; Stencil stencil(mGrid); - BufferType& buffer = mLeafs.getBuffer(leafIndex, 1); typename LeafNodeType::ValueOnIter iter = leaf.beginValueOn(); @@ -1833,7 +2165,7 @@ struct RenormOp const ValueType normSqGradPhi = math::ISGradientNormSqrd<math::FIRST_BIAS>::result(stencil); - const ValueType phi0 = stencil.getValue(); + const ValueType phi0 = iter.getValue(); const ValueType diff = math::Sqrt(normSqGradPhi) * invDx - one; const ValueType S = phi0 / (math::Sqrt(math::Pow2(phi0) + normSqGradPhi)); @@ -1860,8 +2192,8 @@ struct MinOp void operator()(LeafNodeType &leaf, size_t leafIndex) const { BufferType& buffer = mLeafs.getBuffer(leafIndex, 1); - typename LeafNodeType::ValueOnIter iter = leaf.beginValueOn(); + for (; iter; ++iter) { ValueType& val = const_cast<ValueType&>(iter.getValue()); val = std::min(val, buffer.getValue(iter.pos())); @@ -1889,10 +2221,12 @@ struct MergeBufferOp void operator()(LeafNodeType &leaf, size_t leafIndex) const { BufferType& buffer = mLeafs.getBuffer(leafIndex, mBufferIndex); - typename LeafNodeType::ValueOnIter iter = leaf.beginValueOn(); + Index offset; + for (; iter; ++iter) { - leaf.setValueOnly(iter.pos(), buffer.getValue(iter.pos())); + offset = iter.pos(); + leaf.setValueOnly(offset, buffer.getValue(offset)); } } @@ -1908,23 +2242,41 @@ struct LeafTopologyDiffOp typedef typename tree::ValueAccessor<TreeType> AccessorT; typedef typename TreeType::LeafNodeType LeafNodeT; - LeafTopologyDiffOp(TreeType& tree) : mTree(tree) { } + LeafTopologyDiffOp(TreeType& tree) : mAcc(tree) { } template <typename LeafNodeType> void operator()(LeafNodeType &leaf, size_t) const { - const LeafNodeT* rhsLeaf = mTree.probeConstLeaf(leaf.getOrigin()); - - if (rhsLeaf) { - leaf.topologyDifference(*rhsLeaf, false); - } else { - // special case required for the ExpandNB scheme. - leaf.setValuesOff(); - } + const LeafNodeT* rhsLeaf = mAcc.probeConstLeaf(leaf.origin()); + if (rhsLeaf) leaf.topologyDifference(*rhsLeaf, false); } private: - TreeType& mTree; + AccessorT mAcc; +}; + + +template<typename ValueType> +struct SDFPrune +{ + SDFPrune(const ValueType& out, const ValueType& in) + : outside(out) + , inside(in) + { + } + + template <typename ChildType> + bool operator()(ChildType& child) + { + child.pruneOp(*this); + if (!child.isInactive()) return false; + value = math::isNegative(child.getFirstValue()) ? inside : outside; + return true; + } + + static const bool state = false; + const ValueType outside, inside; + ValueType value; }; @@ -1936,8 +2288,8 @@ private: // MeshToVolume -template<typename DistGridT, typename InterruptT> -MeshToVolume<DistGridT, InterruptT>::MeshToVolume( +template<typename FloatGridT, typename InterruptT> +MeshToVolume<FloatGridT, InterruptT>::MeshToVolume( openvdb::math::Transform::Ptr& transform, int conversionFlags, InterruptT *interrupter, int signSweeps) : mTransform(transform) @@ -1950,64 +2302,69 @@ MeshToVolume<DistGridT, InterruptT>::MeshToVolume( } -template<typename DistGridT, typename InterruptT> +template<typename FloatGridT, typename InterruptT> void -MeshToVolume<DistGridT, InterruptT>::clear() +MeshToVolume<FloatGridT, InterruptT>::clear() { - mDistGrid = DistGridT::create(std::numeric_limits<DistValueT>::max()); - mIndexGrid = IndexGridT::create(Int32(util::INVALID_IDX)); - mIntersectingVoxelsGrid = StencilGridT::create(false); + mDistGrid = FloatGridT::create(std::numeric_limits<FloatValueT>::max()); + mIndexGrid = IntGridT::create(Int32(util::INVALID_IDX)); + mIntersectingVoxelsGrid = BoolGridT::create(false); } -template<typename DistGridT, typename InterruptT> +template<typename FloatGridT, typename InterruptT> inline void -MeshToVolume<DistGridT, InterruptT>::convertToLevelSet( +MeshToVolume<FloatGridT, InterruptT>::convertToLevelSet( const std::vector<Vec3s>& pointList, const std::vector<Vec4I>& polygonList, - DistValueT exBandWidth, DistValueT inBandWidth) + FloatValueT exBandWidth, FloatValueT inBandWidth) { // The narrow band width is exclusive, the shortest valid distance has to be > 1 voxel - exBandWidth = std::max(internal::Tolerance<DistValueT>::minNarrowBandWidth(), exBandWidth); - inBandWidth = std::max(internal::Tolerance<DistValueT>::minNarrowBandWidth(), inBandWidth); - const DistValueT vs = mTransform->voxelSize()[0]; + exBandWidth = std::max(internal::Tolerance<FloatValueT>::minNarrowBandWidth(), exBandWidth); + inBandWidth = std::max(internal::Tolerance<FloatValueT>::minNarrowBandWidth(), inBandWidth); + const FloatValueT vs = mTransform->voxelSize()[0]; doConvert(pointList, polygonList, vs * exBandWidth, vs * inBandWidth); mDistGrid->setGridClass(GRID_LEVEL_SET); } -template<typename DistGridT, typename InterruptT> +template<typename FloatGridT, typename InterruptT> inline void -MeshToVolume<DistGridT, InterruptT>::convertToUnsignedDistanceField( +MeshToVolume<FloatGridT, InterruptT>::convertToUnsignedDistanceField( const std::vector<Vec3s>& pointList, const std::vector<Vec4I>& polygonList, - DistValueT exBandWidth) + FloatValueT exBandWidth) { // The narrow band width is exclusive, the shortest valid distance has to be > 1 voxel - exBandWidth = std::max(internal::Tolerance<DistValueT>::minNarrowBandWidth(), exBandWidth); - const DistValueT vs = mTransform->voxelSize()[0]; + exBandWidth = std::max(internal::Tolerance<FloatValueT>::minNarrowBandWidth(), exBandWidth); + const FloatValueT vs = mTransform->voxelSize()[0]; doConvert(pointList, polygonList, vs * exBandWidth, 0.0, true); mDistGrid->setGridClass(GRID_UNKNOWN); } -template<typename DistGridT, typename InterruptT> +template<typename FloatGridT, typename InterruptT> void -MeshToVolume<DistGridT, InterruptT>::doConvert( +MeshToVolume<FloatGridT, InterruptT>::doConvert( const std::vector<Vec3s>& pointList, const std::vector<Vec4I>& polygonList, - DistValueT exBandWidth, DistValueT inBandWidth, bool unsignedDistField) + FloatValueT exBandWidth, FloatValueT inBandWidth, bool unsignedDistField) { mDistGrid->setTransform(mTransform); mIndexGrid->setTransform(mTransform); + const bool rawData = OUTPUT_RAW_DATA & mConversionFlags; + + // The progress estimates given to the interrupter are based on the + // observed average time for each stage and therefore not alway + // accurate. The goal is to give some progression feedback to the user. - if (mInterrupter && mInterrupter->wasInterrupted()) return; + if (wasInterrupted(1)) return; // Voxelize mesh { - internal::MeshVoxelizer<DistTreeT, InterruptT> + internal::MeshVoxelizer<FloatTreeT, InterruptT> voxelizer(pointList, polygonList, mInterrupter); - voxelizer.runParallel(); + voxelizer.run(); - if (mInterrupter && mInterrupter->wasInterrupted()) return; + if (wasInterrupted(18)) return; mDistGrid->tree().merge(voxelizer.sqrDistTree()); mIndexGrid->tree().merge(voxelizer.primIndexTree()); @@ -2015,293 +2372,193 @@ MeshToVolume<DistGridT, InterruptT>::doConvert( } if (!unsignedDistField) { - // Determine the inside/outside state for the narrow band of voxels. { // Slices up the volume and label the exterior contour of each slice in parallel. - internal::ContourTracer<DistTreeT, InterruptT> trace( + internal::ContourTracer<FloatTreeT, InterruptT> trace( mDistGrid->tree(), mIntersectingVoxelsGrid->tree(), mInterrupter); - for (int i = 0; i < mSignSweeps; ++i) { - if (mInterrupter && mInterrupter->wasInterrupted()) break; - trace.runParallel(); + if (wasInterrupted(19)) return; + + trace.run(); - if (mInterrupter && mInterrupter->wasInterrupted()) break; + if (wasInterrupted(24)) return; // Propagate sign information between the slices. - internal::propagateSign<DistTreeT, InterruptT> - (mDistGrid->tree(), mIntersectingVoxelsGrid->tree(), mInterrupter); + BoolTreeT signMaskTree(false); + { + tree::LeafManager<FloatTreeT> leafs(mDistGrid->tree()); + internal::SignMask<FloatTreeT, InterruptT> signMaskOp(leafs, + mDistGrid->tree(), mIntersectingVoxelsGrid->tree(), mInterrupter); + signMaskOp.run(); + signMaskTree.merge(signMaskOp.signMaskTree()); + } + + if (wasInterrupted(25)) return; + + while (true) { + tree::LeafManager<BoolTreeT> leafs(signMaskTree); + if(leafs.leafCount() == 0) break; + + internal::PropagateSign<FloatTreeT, InterruptT> sign(leafs, + mDistGrid->tree(), mIntersectingVoxelsGrid->tree(), mInterrupter); + + sign.run(); + + signMaskTree.clear(); + signMaskTree.merge(sign.signMaskTree()); + } } } - if (mInterrupter && mInterrupter->wasInterrupted()) return; + if (wasInterrupted(28)) return; { - tree::LeafManager<StencilTreeT> leafs(mIntersectingVoxelsGrid->tree()); + tree::LeafManager<BoolTreeT> leafs(mIntersectingVoxelsGrid->tree()); // Determine the sign of the mesh intersecting voxels. - internal::IntersectingVoxelSign<DistTreeT> sign(pointList, polygonList, + internal::IntersectingVoxelSign<FloatTreeT> sign(pointList, polygonList, mDistGrid->tree(), mIndexGrid->tree(), mIntersectingVoxelsGrid->tree(), leafs); - sign.runParallel(); + sign.run(); - if (mInterrupter && mInterrupter->wasInterrupted()) return; + if (wasInterrupted(34)) return; // Remove mesh intersecting voxels that where set by rasterizing // self-intersecting portions of the mesh. - internal::IntersectingVoxelCleaner<DistTreeT> cleaner(mDistGrid->tree(), + internal::IntersectingVoxelCleaner<FloatTreeT> cleaner(mDistGrid->tree(), mIndexGrid->tree(), mIntersectingVoxelsGrid->tree(), leafs); - - - cleaner.runParallel(); + cleaner.run(); } - if (mInterrupter && mInterrupter->wasInterrupted()) return; - + // Remove shell voxels that where set by rasterizing + // self-intersecting portions of the mesh. { - // Remove shell voxels that where set by rasterizing - // self-intersecting portions of the mesh. - - tree::LeafManager<DistTreeT> leafs(mDistGrid->tree()); + tree::LeafManager<FloatTreeT> leafs(mDistGrid->tree()); - internal::ShellVoxelCleaner<DistTreeT> cleaner(mDistGrid->tree(), + internal::ShellVoxelCleaner<FloatTreeT> cleaner(mDistGrid->tree(), leafs, mIndexGrid->tree(), mIntersectingVoxelsGrid->tree()); - cleaner.runParallel(); + cleaner.run(); } - if (mInterrupter && mInterrupter->wasInterrupted()) return; + if (wasInterrupted(38)) return; } else { // if unsigned dist. field - inBandWidth = DistValueT(0.0); + inBandWidth = FloatValueT(0.0); } if (mDistGrid->activeVoxelCount() == 0) return; - - mIntersectingVoxelsGrid->clear(); - const DistValueT voxelSize(mTransform->voxelSize()[0]); - - // Transform values (world space scaling etc.) - { - typedef internal::SqrtAndScaleOp<DistValueT> XForm; - tree::LeafManager<DistTreeT> leafs(mDistGrid->tree()); - XForm op(voxelSize, unsignedDistField); - LeafTransformer<DistTreeT, XForm> transform(leafs, op); - if (mInterrupter && mInterrupter->wasInterrupted()) return; + mIntersectingVoxelsGrid->clear(); + const FloatValueT voxelSize(mTransform->voxelSize()[0]); - transform.runParallel(); + { // Transform values (world space scaling etc.) + tree::LeafManager<FloatTreeT> leafs(mDistGrid->tree()); + leafs.foreach(internal::SqrtAndScaleOp<FloatValueT>(voxelSize, unsignedDistField)); } - if (mInterrupter && mInterrupter->wasInterrupted()) return; - - if (!unsignedDistField) { - // Propagate sign information to inactive values. - mDistGrid->tree().signedFloodFill(); + if (wasInterrupted(40)) return; - if (mInterrupter && mInterrupter->wasInterrupted()) return; - - // Update the background value (inactive values) - tree::LeafManager<DistTreeT> leafs(mDistGrid->tree()); - - typedef internal::VoxelSignOp<DistValueT> SignXForm; - SignXForm op(exBandWidth, inBandWidth); - - LeafTransformer<DistTreeT, SignXForm> transform(leafs, op); - transform.runParallel(); - - if (mInterrupter && mInterrupter->wasInterrupted()) return; - - DistValueT bgValues[2]; - bgValues[0] = exBandWidth; - bgValues[1] = -inBandWidth; - - typename DistTreeT::ValueAllIter tileIt(mDistGrid->tree()); - tileIt.setMaxDepth(DistTreeT::ValueAllIter::LEAF_DEPTH - 1); - - for ( ; tileIt; ++tileIt) { - DistValueT& val = const_cast<DistValueT&>(tileIt.getValue()); - val = bgValues[int(val < DistValueT(0.0))]; - } - - if (mInterrupter && mInterrupter->wasInterrupted()) return; - - // fast bg value swap - typename DistTreeT::Ptr newTree(new DistTreeT(/*background=*/exBandWidth)); - newTree->merge(mDistGrid->tree()); - mDistGrid->setTree(newTree); + if (!unsignedDistField) { // Propagate sign information to inactive values. + mDistGrid->tree().root().setBackground(exBandWidth, /*updateChildNodes=*/false); + mDistGrid->tree().signedFloodFill(exBandWidth, -inBandWidth); } - if (mInterrupter && mInterrupter->wasInterrupted()) return; + if (wasInterrupted(46)) return; // Narrow-band dilation - const DistValueT minWidth = voxelSize * 2.0; + const FloatValueT minWidth = voxelSize * 2.0; if (inBandWidth > minWidth || exBandWidth > minWidth) { // Create the initial voxel mask. - StencilTreeT maskTree(false); - tree::ValueAccessor<StencilTreeT> acc(maskTree); + BoolTreeT maskTree(false); maskTree.topologyUnion(mDistGrid->tree()); - // Preallocate leafs. - { - typedef typename DistTreeT::LeafNodeType DistLeafType; - - std::vector<DistLeafType*> distLeafs; - distLeafs.reserve(mDistGrid->tree().leafCount()); + if (wasInterrupted(48)) return; - typename DistTreeT::LeafIter iter = mDistGrid->tree().beginLeaf(); - for ( ; iter; ++iter) distLeafs.push_back(iter.getLeaf()); - - tree::ValueAccessor<DistTreeT> distAcc(mDistGrid->tree()); - - DistValueT leafSize = DistValueT(DistLeafType::DIM - 1) * voxelSize; - - const double inLeafsRatio = double(inBandWidth) / double(leafSize); - size_t inLeafs = std::numeric_limits<size_t>::max(); - if (double(inLeafs) > (inLeafsRatio + 1.0)) { - inLeafs = size_t(std::ceil(inLeafsRatio)) + 1; - } - size_t exLeafs = size_t(std::ceil(exBandWidth / leafSize)) + 1; - size_t numLeafs = std::max(inLeafs, exLeafs); - - for (size_t i = 0; i < numLeafs; ++i) { - - if (mInterrupter && mInterrupter->wasInterrupted()) return; - - std::vector<DistLeafType*> newDistLeafs; - newDistLeafs.reserve(2 * distLeafs.size()); - - for (size_t n = 0, N = distLeafs.size(); n < N; ++n) { - - Coord ijk = distLeafs[n]->getOrigin(); - - const bool inside = distLeafs[n]->getValue(ijk) < DistValueT(0.0); - - if (inside && !(i < inLeafs)) continue; - else if (!inside && !(i < exLeafs)) continue; - - ijk[0] -= 1; - if (distAcc.probeLeaf(ijk) == NULL) { - newDistLeafs.push_back(distAcc.touchLeaf(ijk)); - } - - ijk[0] += 1; - ijk[1] -= 1; - if (distAcc.probeLeaf(ijk) == NULL) { - newDistLeafs.push_back(distAcc.touchLeaf(ijk)); - } - - ijk[1] += 1; - ijk[2] -= 1; - if (distAcc.probeLeaf(ijk) == NULL) { - newDistLeafs.push_back(distAcc.touchLeaf(ijk)); - } - - ijk[2] += 1; - ijk[0] += DistLeafType::DIM; - if (distAcc.probeLeaf(ijk) == NULL) { - newDistLeafs.push_back(distAcc.touchLeaf(ijk)); - } - - ijk[0] -= DistLeafType::DIM; - ijk[1] += DistLeafType::DIM; - if (distAcc.probeLeaf(ijk) == NULL) { - newDistLeafs.push_back(distAcc.touchLeaf(ijk)); - } - - ijk[1] -= DistLeafType::DIM; - ijk[2] += DistLeafType::DIM; - if (distAcc.probeLeaf(ijk) == NULL) { - newDistLeafs.push_back(distAcc.touchLeaf(ijk)); - } - } + internal::LeafTopologyDiffOp<FloatTreeT> diffOp(mDistGrid->tree()); + openvdb::tools::dilateVoxels(maskTree); - if (newDistLeafs.empty()) break; - distLeafs.swap(newDistLeafs); - } + unsigned maxIterations = std::numeric_limits<unsigned>::max(); + float progress = 48, step = 0.0; + // progress estimation.. + double estimated = + 2.0 * std::ceil((std::max(inBandWidth, exBandWidth) - minWidth) / voxelSize); + if (estimated < double(maxIterations)) { + maxIterations = unsigned(estimated); + step = 42.0 / float(maxIterations); } - if (mInterrupter && mInterrupter->wasInterrupted()) return; - - mIndexGrid->tree().topologyUnion(mDistGrid->tree()); + unsigned count = 0; + while (true) { - typedef internal::LeafTopologyDiffOp<DistTreeT> TopologyDiffOp; - TopologyDiffOp diffOp(mDistGrid->tree()); + if (wasInterrupted(int(progress))) return; - while (maskTree.activeVoxelCount() > 0) { + tree::LeafManager<BoolTreeT> leafs(maskTree); - if (mInterrupter && mInterrupter->wasInterrupted()) break; + if (leafs.leafCount() == 0) break; - openvdb::tools::dilateVoxels(maskTree); - tree::LeafManager<StencilTreeT> leafs(maskTree); + leafs.foreach(diffOp); - LeafTransformer<StencilTreeT, TopologyDiffOp> diff(leafs, diffOp); - diff.runParallel(); + internal::ExpandNB<FloatTreeT> expand( + leafs, mDistGrid->tree(), mIndexGrid->tree(), maskTree, + exBandWidth, inBandWidth, voxelSize, pointList, polygonList); - internal::ExpandNB<DistTreeT> expand(pointList, polygonList, mDistGrid->tree(), - mIndexGrid->tree(), maskTree, leafs, exBandWidth, inBandWidth, voxelSize); + expand.run(); - expand.runParallel(); + if ((++count) >= maxIterations) break; + progress += step; } } if (!bool(GENERATE_PRIM_INDEX_GRID & mConversionFlags)) mIndexGrid->clear(); - // Smooth out bumps caused by self-intersecting and overlapping portions - // of the mesh and renormalize the level set. - if (!unsignedDistField) { - typedef internal::OffsetOp<DistValueT> OffsetOp; - typedef internal::RenormOp<DistGridT, DistValueT> RenormOp; - typedef internal::MinOp<DistTreeT, DistValueT> MinOp; - typedef internal::MergeBufferOp<DistTreeT, DistValueT> MergeBufferOp; + if (wasInterrupted(80)) return; + + // Renormalize distances to smooth out bumps caused by self-intersecting + // and overlapping portions of the mesh and renormalize the level set. + if (!unsignedDistField && !rawData) { - tree::LeafManager<DistTreeT> leafs(mDistGrid->tree(), 1); + mDistGrid->tree().pruneLevelSet(); + tree::LeafManager<FloatTreeT> leafs(mDistGrid->tree(), 1); - const DistValueT offset = 0.8 * voxelSize; + const FloatValueT offset = 0.8 * voxelSize; + if (wasInterrupted(82)) return; - if (mInterrupter && mInterrupter->wasInterrupted()) return; + internal::OffsetOp<FloatValueT> offsetOp(-offset); - OffsetOp offsetOp(-offset); - LeafTransformer<DistTreeT, OffsetOp> offsetXform(leafs, offsetOp); - offsetXform.runParallel(); + leafs.foreach(offsetOp); - if (mInterrupter && mInterrupter->wasInterrupted()) return; + if (wasInterrupted(84)) return; - RenormOp renormOp(*mDistGrid, leafs, voxelSize); - LeafTransformer<DistTreeT, RenormOp> renormXform(leafs, renormOp); - renormXform.runParallel(); + leafs.foreach(internal::RenormOp<FloatGridT, FloatValueT>(*mDistGrid, leafs, voxelSize)); - MinOp minOp(leafs); - LeafTransformer<DistTreeT, MinOp> minXform(leafs, minOp); - minXform.runParallel(); + leafs.foreach(internal::MinOp<FloatTreeT, FloatValueT>(leafs)); - if (mInterrupter && mInterrupter->wasInterrupted()) return; + if (wasInterrupted(95)) return; - offsetOp.resetOffset(offset - internal::Tolerance<DistValueT>::epsilon()); - offsetXform.runParallel(); + offsetOp.resetOffset(offset - internal::Tolerance<FloatValueT>::epsilon()); + leafs.foreach(offsetOp); } - const DistValueT minTrimWidth = voxelSize * 4.0; + if (wasInterrupted(98)) return; + + const FloatValueT minTrimWidth = voxelSize * 4.0; if (inBandWidth < minTrimWidth || exBandWidth < minTrimWidth) { // If the narrow band was not expanded, we might need to trim off // some of the active voxels in order to respect the narrow band limits. // (The mesh voxelization step generates some extra 'shell' voxels) - tree::LeafManager<DistTreeT> leafs(mDistGrid->tree()); - - typedef internal::TrimOp<DistValueT> TrimOp; + tree::LeafManager<FloatTreeT> leafs(mDistGrid->tree()); + leafs.foreach(internal::TrimOp<FloatValueT>( + exBandWidth, unsignedDistField ? exBandWidth : inBandWidth)); - TrimOp op(exBandWidth, unsignedDistField ? exBandWidth : inBandWidth); - LeafTransformer<DistTreeT, TrimOp> transform(leafs, op); - transform.runParallel(); + internal::SDFPrune<FloatValueT> sdfPrune(exBandWidth, -inBandWidth); + mDistGrid->tree().pruneOp(sdfPrune); } - - if (mInterrupter && mInterrupter->wasInterrupted()) return; - - mDistGrid->tree().pruneLevelSet(); } @@ -2325,7 +2582,7 @@ doMeshConversion( { // Copy and transform (required for MeshToVolume) points to grid space. internal::PointTransform ptnXForm(points, indexSpacePoints, xform); - ptnXForm.runParallel(); + ptnXForm.run(); } // Copy primitives @@ -2454,6 +2711,517 @@ meshToUnsignedDistanceField( } +//////////////////////////////////////////////////////////////////////////////// + + +// Required by several of the tree nodes +inline std::ostream& +operator<<(std::ostream& ostr, const MeshToVoxelEdgeData::EdgeData& rhs) +{ + ostr << "{[ " << rhs.mXPrim << ", " << rhs.mXDist << "]"; + ostr << " [ " << rhs.mYPrim << ", " << rhs.mYDist << "]"; + ostr << " [ " << rhs.mZPrim << ", " << rhs.mZDist << "]}"; + return ostr; +} + +// Required by math::Abs +inline MeshToVoxelEdgeData::EdgeData +Abs(const MeshToVoxelEdgeData::EdgeData& x) +{ + return x; +} + + +//////////////////////////////////////// + + +class MeshToVoxelEdgeData::GenEdgeData +{ +public: + + GenEdgeData( + const std::vector<Vec3s>& pointList, + const std::vector<Vec4I>& polygonList); + + void run(bool threaded = true); + + GenEdgeData(GenEdgeData& rhs, tbb::split); + inline void operator() (const tbb::blocked_range<size_t> &range); + inline void join(GenEdgeData& rhs); + + inline TreeType& tree() { return mTree; } + +private: + void operator=(const GenEdgeData&) {} + + struct Primitive { Vec3d a, b, c, d; Int32 index; }; + + template<bool IsQuad> + inline void voxelize(const Primitive&); + + template<bool IsQuad> + inline bool evalPrimitive(const Coord&, const Primitive&); + + inline bool rayTriangleIntersection( const Vec3d& origin, const Vec3d& dir, + const Vec3d& a, const Vec3d& b, const Vec3d& c, double& t); + + + TreeType mTree; + Accessor mAccessor; + + const std::vector<Vec3s>& mPointList; + const std::vector<Vec4I>& mPolygonList; + + // Used internally for acceleration + typedef TreeType::ValueConverter<Int32>::Type IntTreeT; + IntTreeT mLastPrimTree; + tree::ValueAccessor<IntTreeT> mLastPrimAccessor; +}; // class MeshToVoxelEdgeData::GenEdgeData + + +inline +MeshToVoxelEdgeData::GenEdgeData::GenEdgeData( + const std::vector<Vec3s>& pointList, + const std::vector<Vec4I>& polygonList) + : mTree(EdgeData()) + , mAccessor(mTree) + , mPointList(pointList) + , mPolygonList(polygonList) + , mLastPrimTree(Int32(util::INVALID_IDX)) + , mLastPrimAccessor(mLastPrimTree) +{ +} + + +inline +MeshToVoxelEdgeData::GenEdgeData::GenEdgeData(GenEdgeData& rhs, tbb::split) + : mTree(EdgeData()) + , mAccessor(mTree) + , mPointList(rhs.mPointList) + , mPolygonList(rhs.mPolygonList) + , mLastPrimTree(Int32(util::INVALID_IDX)) + , mLastPrimAccessor(mLastPrimTree) +{ +} + + +inline void +MeshToVoxelEdgeData::GenEdgeData::run(bool threaded) +{ + if (threaded) { + tbb::parallel_reduce(tbb::blocked_range<size_t>(0, mPolygonList.size()), *this); + } else { + (*this)(tbb::blocked_range<size_t>(0, mPolygonList.size())); + } +} + + +inline void +MeshToVoxelEdgeData::GenEdgeData::join(GenEdgeData& rhs) +{ + typedef TreeType::RootNodeType RootNodeType; + typedef RootNodeType::NodeChainType NodeChainType; + BOOST_STATIC_ASSERT(boost::mpl::size<NodeChainType>::value > 1); + typedef boost::mpl::at<NodeChainType, boost::mpl::int_<1> >::type InternalNodeType; + + Coord ijk; + Index offset; + + rhs.mTree.clearAllAccessors(); + + TreeType::LeafIter leafIt = rhs.mTree.beginLeaf(); + for ( ; leafIt; ++leafIt) { + ijk = leafIt->origin(); + + TreeType::LeafNodeType* lhsLeafPt = mTree.probeLeaf(ijk); + + if (!lhsLeafPt) { + + mAccessor.addLeaf(rhs.mAccessor.probeLeaf(ijk)); + InternalNodeType* node = rhs.mAccessor.getNode<InternalNodeType>(); + node->stealNode<TreeType::LeafNodeType>(ijk, EdgeData(), false); + rhs.mAccessor.clear(); + + } else { + + TreeType::LeafNodeType::ValueOnCIter it = leafIt->cbeginValueOn(); + for ( ; it; ++it) { + + offset = it.pos(); + const EdgeData& rhsValue = it.getValue(); + + if (!lhsLeafPt->isValueOn(offset)) { + lhsLeafPt->setValueOn(offset, rhsValue); + } else { + + EdgeData& lhsValue = const_cast<EdgeData&>(lhsLeafPt->getValue(offset)); + + if (rhsValue.mXDist < lhsValue.mXDist) { + lhsValue.mXDist = rhsValue.mXDist; + lhsValue.mXPrim = rhsValue.mXPrim; + } + + if (rhsValue.mYDist < lhsValue.mYDist) { + lhsValue.mYDist = rhsValue.mYDist; + lhsValue.mYPrim = rhsValue.mYPrim; + } + + if (rhsValue.mZDist < lhsValue.mZDist) { + lhsValue.mZDist = rhsValue.mZDist; + lhsValue.mZPrim = rhsValue.mZPrim; + } + + } + } // end value iteration + } + } // end leaf iteration +} + + +inline void +MeshToVoxelEdgeData::GenEdgeData::operator()(const tbb::blocked_range<size_t> &range) +{ + Primitive prim; + + for (size_t n = range.begin(); n < range.end(); ++n) { + + const Vec4I& verts = mPolygonList[n]; + + prim.index = Int32(n); + prim.a = Vec3d(mPointList[verts[0]]); + prim.b = Vec3d(mPointList[verts[1]]); + prim.c = Vec3d(mPointList[verts[2]]); + + if (util::INVALID_IDX != verts[3]) { + prim.d = Vec3d(mPointList[verts[3]]); + voxelize<true>(prim); + } else { + voxelize<false>(prim); + } + } +} + + +template<bool IsQuad> +inline void +MeshToVoxelEdgeData::GenEdgeData::voxelize(const Primitive& prim) +{ + std::deque<Coord> coordList; + Coord ijk, nijk; + + ijk = util::nearestCoord(prim.a); + coordList.push_back(ijk); + + evalPrimitive<IsQuad>(ijk, prim); + + while (!coordList.empty()) { + + ijk = coordList.back(); + coordList.pop_back(); + + for (Int32 i = 0; i < 26; ++i) { + nijk = ijk + util::COORD_OFFSETS[i]; + + if (prim.index != mLastPrimAccessor.getValue(nijk)) { + mLastPrimAccessor.setValue(nijk, prim.index); + if(evalPrimitive<IsQuad>(nijk, prim)) coordList.push_back(nijk); + } + } + } +} + + +template<bool IsQuad> +inline bool +MeshToVoxelEdgeData::GenEdgeData::evalPrimitive(const Coord& ijk, const Primitive& prim) +{ + Vec3d uvw, org(ijk[0], ijk[1], ijk[2]); + bool intersecting = false; + double t; + + EdgeData edgeData; + mAccessor.probeValue(ijk, edgeData); + + // Evaluate first triangle + double dist = (org - + closestPointOnTriangleToPoint(prim.a, prim.c, prim.b, org, uvw)).lengthSqr(); + + if (rayTriangleIntersection(org, Vec3d(1.0, 0.0, 0.0), prim.a, prim.c, prim.b, t)) { + if (t < edgeData.mXDist) { + edgeData.mXDist = t; + edgeData.mXPrim = prim.index; + intersecting = true; + } + } + + if (rayTriangleIntersection(org, Vec3d(0.0, 1.0, 0.0), prim.a, prim.c, prim.b, t)) { + if (t < edgeData.mYDist) { + edgeData.mYDist = t; + edgeData.mYPrim = prim.index; + intersecting = true; + } + } + + if (rayTriangleIntersection(org, Vec3d(0.0, 0.0, 1.0), prim.a, prim.c, prim.b, t)) { + if (t < edgeData.mZDist) { + edgeData.mZDist = t; + edgeData.mZPrim = prim.index; + intersecting = true; + } + } + + if (IsQuad) { + // Split quad into a second triangle and calculate distance. + double secondDist = (org - + closestPointOnTriangleToPoint(prim.a, prim.d, prim.c, org, uvw)).lengthSqr(); + + if (secondDist < dist) dist = secondDist; + + if (rayTriangleIntersection(org, Vec3d(1.0, 0.0, 0.0), prim.a, prim.d, prim.c, t)) { + if (t < edgeData.mXDist) { + edgeData.mXDist = t; + edgeData.mXPrim = prim.index; + intersecting = true; + } + } + + if (rayTriangleIntersection(org, Vec3d(0.0, 1.0, 0.0), prim.a, prim.d, prim.c, t)) { + if (t < edgeData.mYDist) { + edgeData.mYDist = t; + edgeData.mYPrim = prim.index; + intersecting = true; + } + } + + if (rayTriangleIntersection(org, Vec3d(0.0, 0.0, 1.0), prim.a, prim.d, prim.c, t)) { + if (t < edgeData.mZDist) { + edgeData.mZDist = t; + edgeData.mZPrim = prim.index; + intersecting = true; + } + } + } + + if (intersecting) mAccessor.setValue(ijk, edgeData); + + return (dist < 0.86602540378443861); +} + + +inline bool +MeshToVoxelEdgeData::GenEdgeData::rayTriangleIntersection( + const Vec3d& origin, const Vec3d& dir, + const Vec3d& a, const Vec3d& b, const Vec3d& c, + double& t) +{ + // Check if ray is parallel with triangle + + Vec3d e1 = b - a; + Vec3d e2 = c - a; + Vec3d s1 = dir.cross(e2); + + double divisor = s1.dot(e1); + if (!(std::abs(divisor) > 0.0)) return false; + + // Compute barycentric coordinates + + double inv_divisor = 1.0 / divisor; + Vec3d d = origin - a; + double b1 = d.dot(s1) * inv_divisor; + + if (b1 < 0.0 || b1 > 1.0) return false; + + Vec3d s2 = d.cross(e1); + double b2 = dir.dot(s2) * inv_divisor; + + if (b2 < 0.0 || (b1 + b2) > 1.0) return false; + + // Compute distance to intersection point + + t = e2.dot(s2) * inv_divisor; + return (t < 0.0) ? false : true; +} + + +//////////////////////////////////////// + + +inline +MeshToVoxelEdgeData::MeshToVoxelEdgeData() + : mTree(EdgeData()) +{ +} + + +inline void +MeshToVoxelEdgeData::convert( + const std::vector<Vec3s>& pointList, + const std::vector<Vec4I>& polygonList) +{ + GenEdgeData converter(pointList, polygonList); + converter.run(); + + mTree.clear(); + mTree.merge(converter.tree()); +} + + +inline void +MeshToVoxelEdgeData::getEdgeData( + Accessor& acc, + const Coord& ijk, + std::vector<Vec3d>& points, + std::vector<Index32>& primitives) +{ + EdgeData data; + Vec3d point; + + Coord coord = ijk; + + if (acc.probeValue(coord, data)) { + + if (data.mXPrim != util::INVALID_IDX) { + point[0] = double(coord[0]) + data.mXDist; + point[1] = double(coord[1]); + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mXPrim); + } + + if (data.mYPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]) + data.mYDist; + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mYPrim); + } + + if (data.mZPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]); + point[2] = double(coord[2]) + data.mZDist; + + points.push_back(point); + primitives.push_back(data.mZPrim); + } + + } + + coord[0] += 1; + + if (acc.probeValue(coord, data)) { + + if (data.mYPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]) + data.mYDist; + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mYPrim); + } + + if (data.mZPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]); + point[2] = double(coord[2]) + data.mZDist; + + points.push_back(point); + primitives.push_back(data.mZPrim); + } + } + + coord[2] += 1; + + if (acc.probeValue(coord, data)) { + if (data.mYPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]) + data.mYDist; + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mYPrim); + } + } + + coord[0] -= 1; + + if (acc.probeValue(coord, data)) { + + if (data.mXPrim != util::INVALID_IDX) { + point[0] = double(coord[0]) + data.mXDist; + point[1] = double(coord[1]); + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mXPrim); + } + + if (data.mYPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]) + data.mYDist; + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mYPrim); + } + } + + + coord[1] += 1; + + if (acc.probeValue(coord, data)) { + + if (data.mXPrim != util::INVALID_IDX) { + point[0] = double(coord[0]) + data.mXDist; + point[1] = double(coord[1]); + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mXPrim); + } + } + + coord[2] -= 1; + + if (acc.probeValue(coord, data)) { + + if (data.mXPrim != util::INVALID_IDX) { + point[0] = double(coord[0]) + data.mXDist; + point[1] = double(coord[1]); + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mXPrim); + } + + if (data.mZPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]); + point[2] = double(coord[2]) + data.mZDist; + + points.push_back(point); + primitives.push_back(data.mZPrim); + } + } + + coord[0] += 1; + + if (acc.probeValue(coord, data)) { + + if (data.mZPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]); + point[2] = double(coord[2]) + data.mZDist; + + points.push_back(point); + primitives.push_back(data.mZPrim); + } + } +} + + } // namespace tools } // namespace OPENVDB_VERSION_NAME } // namespace openvdb diff --git a/extern/openvdb/internal/openvdb/tools/Morphology.h b/extern/openvdb/internal/openvdb/tools/Morphology.h index f99d9934606..90696a570f4 100644 --- a/extern/openvdb/internal/openvdb/tools/Morphology.h +++ b/extern/openvdb/internal/openvdb/tools/Morphology.h @@ -34,15 +34,18 @@ #define OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED #include <openvdb/Types.h> +#include <openvdb/Grid.h> +#include <openvdb/math/Math.h> // for isApproxEqual() #include <openvdb/tree/TreeIterator.h> #include <openvdb/tree/ValueAccessor.h> #include <openvdb/tree/LeafManager.h> +#include "ValueTransformer.h" // for foreach() namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { -namespace tools { - +namespace tools { + //@{ /// Topologically dilate all leaf-level active voxels in the given tree, /// i.e., expand the set of active voxels by @a count voxels in the +x, -x, @@ -51,12 +54,12 @@ namespace tools { /// @todo Currently operates only on leaf voxels; need to extend to tiles. template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION inline void dilateVoxels(TreeType& tree, int count=1); - + template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION inline void dilateVoxels(tree::LeafManager<TreeType>& manager, int count = 1); -//@} - -//@{ +//@} + +//@{ /// Topologically erode all leaf-level active voxels in the given tree, /// i.e., shrink the set of active voxels by @a count voxels in the +x, -x, /// +y, -y, +z and -z directions, but don't change the values of any voxels, @@ -64,37 +67,62 @@ inline void dilateVoxels(tree::LeafManager<TreeType>& manager, int count = 1); /// @todo Currently operates only on leaf voxels; need to extend to tiles. template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION inline void erodeVoxels(TreeType& tree, int count=1); - + template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION inline void erodeVoxels(tree::LeafManager<TreeType>& manager, int count = 1); -//@} +//@} + + +/// @brief Mark as active any inactive tiles or voxels in the given grid or tree +/// whose values are equal to @a value (optionally to within the given @a tolerance). +template<typename GridOrTree> +inline void activate( + GridOrTree&, + const typename GridOrTree::ValueType& value, + const typename GridOrTree::ValueType& tolerance = zeroVal<typename GridOrTree::ValueType>() +); + + +/// @brief Mark as inactive any active tiles or voxels in the given grid or tree +/// whose values are equal to @a value (optionally to within the given @a tolerance). +template<typename GridOrTree> +inline void deactivate( + GridOrTree&, + const typename GridOrTree::ValueType& value, + const typename GridOrTree::ValueType& tolerance = zeroVal<typename GridOrTree::ValueType>() +); + //////////////////////////////////////// - + + /// Mapping from a Log2Dim to a data type of size 2^Log2Dim bits -template<Index Log2Dim> struct DimToWord { typedef uint8_t Type[0]; }; +template<Index Log2Dim> struct DimToWord {}; template<> struct DimToWord<3> { typedef uint8_t Type; }; template<> struct DimToWord<4> { typedef uint16_t Type; }; template<> struct DimToWord<5> { typedef uint32_t Type; }; template<> struct DimToWord<6> { typedef uint64_t Type; }; + + //////////////////////////////////////// - + + template<typename TreeType> class Morphology { - public: - +public: typedef tree::LeafManager<TreeType> ManagerType; - - Morphology(TreeType& tree) : mOwnsManager(true), mManager(new ManagerType(tree)), mAcc(tree), mSteps(1) {} - Morphology(ManagerType* mgr) : mOwnsManager(false), mManager(mgr), mAcc(mgr->tree()), mSteps(1) {} + + Morphology(TreeType& tree): + mOwnsManager(true), mManager(new ManagerType(tree)), mAcc(tree), mSteps(1) {} + Morphology(ManagerType* mgr): + mOwnsManager(false), mManager(mgr), mAcc(mgr->tree()), mSteps(1) {} virtual ~Morphology() { if (mOwnsManager) delete mManager; } void dilateVoxels(); void dilateVoxels(int count) { for (int i=0; i<count; ++i) this->dilateVoxels(); } void erodeVoxels(int count = 1) { mSteps = count; this->doErosion(); } - private: - +private: void doErosion(); typedef typename TreeType::LeafNodeType LeafType; @@ -138,11 +166,12 @@ class Morphology isOn = leaf ? false : acc.isValueOn(orig); } static const int N = (LEAF_DIM -1 )*(DY + DX*LEAF_DIM); - return leaf ? leaf->getValueMask().template getWord<Word>(indx-N) : isOn ? ~Word(0) : Word(0); + return leaf ? leaf->getValueMask().template getWord<Word>(indx-N) + : isOn ? ~Word(0) : Word(0); } };// Neighbor - + struct ErodeVoxelsOp { ErodeVoxelsOp(std::vector<MaskType>& masks, ManagerType& manager) : mSavedMasks(masks) , mManager(manager) {} @@ -182,19 +211,21 @@ class Morphology };// MaskManager }; -template <typename TreeType> -void Morphology<TreeType>::dilateVoxels() + +template<typename TreeType> +void +Morphology<TreeType>::dilateVoxels() { /// @todo Currently operates only on leaf voxels; need to extend to tiles. const int leafCount = mManager->leafCount(); - + // Save the value masks of all leaf nodes. std::vector<MaskType> savedMasks(leafCount); MaskManager masks(savedMasks, *mManager); masks.save(); Neighbor NN[6]; - Coord origin; + Coord origin; for (int leafIdx = 0; leafIdx < leafCount; ++leafIdx) { const MaskType& oldMask = savedMasks[leafIdx];//original bit-mask of current leaf node LeafType& leaf = mManager->leaf(leafIdx);//current leaf node @@ -204,7 +235,7 @@ void Morphology<TreeType>::dilateVoxels() // Extract the portion of the original mask that corresponds to a row in z. const Word oldWord = oldMask.template getWord<Word>(n); if (oldWord == 0) continue; // no active voxels - + // dilate current leaf or neighbor in negative x-direction if (x > 0) { leaf.getValueMask().template getWord<Word>(n-LEAF_DIM) |= oldWord; @@ -240,16 +271,16 @@ void Morphology<TreeType>::dilateVoxels() if (Word w = oldWord>>(LEAF_DIM-1)) { NN[5].template scatter< 0, 0, 1>(mAcc, origin, n, w); } - }// loop over y + }// loop over y }//loop over x for (int i=0; i<6; ++i) NN[i].clear(); }//loop over leafs - + mManager->rebuildLeafArray(); } -template <typename TreeType> +template <typename TreeType> void Morphology<TreeType>::ErodeVoxelsOp::operator()(const tbb::blocked_range<size_t>& range) const { @@ -257,7 +288,7 @@ Morphology<TreeType>::ErodeVoxelsOp::operator()(const tbb::blocked_range<size_t> Neighbor NN[6]; Coord origin; for (size_t leafIdx = range.begin(); leafIdx < range.end(); ++leafIdx) { - LeafType& leaf = mManager.leaf(leafIdx);//current leaf node + LeafType& leaf = mManager.leaf(leafIdx);//current leaf node if (leaf.isEmpty()) continue; MaskType& newMask = mSavedMasks[leafIdx];//original bit-mask of current leaf node leaf.getOrigin(origin);// origin of the current leaf node. @@ -267,34 +298,35 @@ Morphology<TreeType>::ErodeVoxelsOp::operator()(const tbb::blocked_range<size_t> Word& w = newMask.template getWord<Word>(n); if (w == 0) continue; // no active voxels - // Erode in two z directions (this is first since it uses the original w) + // Erode in two z directions (this is first since it uses the original w) w &= (w<<1 | (NN[4].template gather<0,0,-1>(acc, origin, n)>>(LEAF_DIM-1))) & (w>>1 | (NN[5].template gather<0,0, 1>(acc, origin, n)<<(LEAF_DIM-1))); // dilate current leaf or neighbor in negative x-direction w &= (x == 0) ? NN[0].template gather<-1, 0, 0>(acc, origin, n) : leaf.getValueMask().template getWord<Word>(n-LEAF_DIM); - + // dilate current leaf or neighbor in positive x-direction w &= (x == LEAF_DIM-1) ? NN[1].template gather< 1, 0, 0>(acc, origin, n) : leaf.getValueMask().template getWord<Word>(n+LEAF_DIM); - + // dilate current leaf or neighbor in negative y-direction w &= (y == 0) ? NN[2].template gather< 0,-1, 0>(acc, origin, n) : leaf.getValueMask().template getWord<Word>(n-1); - + // dilate current leaf or neighbor in positive y-direction w &= (y == LEAF_DIM-1) ? NN[3].template gather< 0, 1, 0>(acc, origin, n) : leaf.getValueMask().template getWord<Word>(n+1); - }// loop over y + }// loop over y }//loop over x for (int i=0; i<6; ++i) NN[i].clear(); }//loop over leafs } -template <typename TreeType> -void Morphology<TreeType>::doErosion() +template<typename TreeType> +void +Morphology<TreeType>::doErosion() { /// @todo Currently operates only on leaf voxels; need to extend to tiles. const int leafCount = mManager->leafCount(); @@ -304,7 +336,7 @@ void Morphology<TreeType>::doErosion() MaskManager masks(savedMasks, *mManager); masks.save(); - ErodeVoxelsOp erode(savedMasks, *mManager); + ErodeVoxelsOp erode(savedMasks, *mManager); for (int i = 0; i < mSteps; ++i) { erode.runParallel(); masks.update(); @@ -316,36 +348,149 @@ void Morphology<TreeType>::doErosion() //////////////////////////////////////// + template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION inline void dilateVoxels(tree::LeafManager<TreeType>& manager, int count) { - Morphology<TreeType> m(&manager); - m.dilateVoxels(count); + if (count > 0 ) { + Morphology<TreeType> m(&manager); + m.dilateVoxels(count); + } } template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION inline void dilateVoxels(TreeType& tree, int count) { - Morphology<TreeType> m(tree); - m.dilateVoxels(count); + if (count > 0 ) { + Morphology<TreeType> m(tree); + m.dilateVoxels(count); + } } template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION inline void erodeVoxels(tree::LeafManager<TreeType>& manager, int count) { - Morphology<TreeType> m(&manager); - m.erodeVoxels(count); + if (count > 0 ) { + Morphology<TreeType> m(&manager); + m.erodeVoxels(count); + } } template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION inline void erodeVoxels(TreeType& tree, int count) { - Morphology<TreeType> m(tree); - m.erodeVoxels(count); + if (count > 0 ) { + Morphology<TreeType> m(tree); + m.erodeVoxels(count); + } +} + + +//////////////////////////////////////// + + +namespace activation { + +template<typename TreeType> +class ActivationOp +{ +public: + typedef typename TreeType::ValueType ValueT; + + ActivationOp(bool state, const ValueT& val, const ValueT& tol) + : mActivate(state) + , mValue(val) + , mTolerance(tol) + {} + + void operator()(const typename TreeType::ValueOnIter& it) const + { + if (math::isApproxEqual(*it, mValue, mTolerance)) { + it.setValueOff(); + } + } + + void operator()(const typename TreeType::ValueOffIter& it) const + { + if (math::isApproxEqual(*it, mValue, mTolerance)) { + it.setActiveState(/*on=*/true); + } + } + + void operator()(const typename TreeType::LeafIter& lit) const + { + typedef typename TreeType::LeafNodeType LeafT; + LeafT& leaf = *lit; + if (mActivate) { + for (typename LeafT::ValueOffIter it = leaf.beginValueOff(); it; ++it) { + if (math::isApproxEqual(*it, mValue, mTolerance)) { + leaf.setValueOn(it.pos()); + } + } + } else { + for (typename LeafT::ValueOnIter it = leaf.beginValueOn(); it; ++it) { + if (math::isApproxEqual(*it, mValue, mTolerance)) { + leaf.setValueOff(it.pos()); + } + } + } + } + +private: + bool mActivate; + const ValueT mValue, mTolerance; +}; // class ActivationOp + +} // namespace activation + + +template<typename GridOrTree> +inline void +activate(GridOrTree& gridOrTree, const typename GridOrTree::ValueType& value, + const typename GridOrTree::ValueType& tolerance) +{ + typedef TreeAdapter<GridOrTree> Adapter; + typedef typename Adapter::TreeType TreeType; + + TreeType& tree = Adapter::tree(gridOrTree); + + activation::ActivationOp<TreeType> op(/*activate=*/true, value, tolerance); + + // Process all leaf nodes in parallel. + foreach(tree.beginLeaf(), op); + + // Process all other inactive values serially (because changing active states + // is not thread-safe unless no two threads modify the same node). + typename TreeType::ValueOffIter it = tree.beginValueOff(); + it.setMaxDepth(tree.treeDepth() - 2); + foreach(it, op, /*threaded=*/false); +} + + +template<typename GridOrTree> +inline void +deactivate(GridOrTree& gridOrTree, const typename GridOrTree::ValueType& value, + const typename GridOrTree::ValueType& tolerance) +{ + typedef TreeAdapter<GridOrTree> Adapter; + typedef typename Adapter::TreeType TreeType; + + TreeType& tree = Adapter::tree(gridOrTree); + + activation::ActivationOp<TreeType> op(/*activate=*/false, value, tolerance); + + // Process all leaf nodes in parallel. + foreach(tree.beginLeaf(), op); + + // Process all other active values serially (because changing active states + // is not thread-safe unless no two threads modify the same node). + typename TreeType::ValueOnIter it = tree.beginValueOn(); + it.setMaxDepth(tree.treeDepth() - 2); + foreach(it, op, /*threaded=*/false); } } // namespace tools diff --git a/extern/openvdb/internal/openvdb/tools/ParticlesToLevelSet.h b/extern/openvdb/internal/openvdb/tools/ParticlesToLevelSet.h index a0f03262aef..83c1246ab02 100644 --- a/extern/openvdb/internal/openvdb/tools/ParticlesToLevelSet.h +++ b/extern/openvdb/internal/openvdb/tools/ParticlesToLevelSet.h @@ -32,36 +32,51 @@ /// /// @file ParticlesToLevelSet.h /// -/// @brief This tool rasterizes particles (with position, radius and velocity) -/// into a narrow-band level set. +/// @brief This tool converts particles (with position, radius and +/// velocity) into a singed distance field encoded as a narrow band +/// level set. Optionally arbitrary attributes on the particles can +/// be transferred resulting in an additional attribute grid with the +/// same topology as the level set grid. /// /// @note This fast particle to level set converter is always intended /// to be combined with some kind of surface post processing, /// i.e. tools::Filter. Without such post processing the generated /// surface is typically too noisy and blooby. However it serves as a /// great and fast starting point for subsequent level set surface -/// processing and convolution. In the near future we will add support -/// for anisotropic particle kernels. +/// processing and convolution. /// /// The @c ParticleListT template argument below refers to any class /// with the following interface (see unittest/TestParticlesToLevelSet.cc /// and SOP_DW_OpenVDBParticleVoxelizer for practical examples): /// @code +/// /// class ParticleList { /// ... /// public: -/// openvdb::Index size() const;// number of particles in list -/// openvdb::Vec3R pos(int n) const;// world space position of n'th particle -/// openvdb::Vec3R vel(int n) const;// world space velocity of n'th particle -/// openvdb::Real radius(int n) const;// world space radius of n'th particle +/// +/// // Return the total number of particles in list. +/// // Always required! +/// size_t size() const; +/// +/// // Get the world space position of n'th particle. +/// // Required by ParticledToLevelSet::rasterizeSphere(*this,radius). +/// void getPos(size_t n, Vec3R& xyz) const; +/// +/// // Get the world space position and radius of n'th particle. +/// // Required by ParticledToLevelSet::rasterizeSphere(*this). +/// void getPosRad(size_t n, Vec3R& xyz, Real& rad) const; +/// +/// // Get the world space position, radius and velocity of n'th particle. +/// // Required by ParticledToLevelSet::rasterizeSphere(*this,radius). +/// void getPosRadVel(size_t n, Vec3R& xyz, Real& rad, Vec3R& vel) const; +/// +/// // Get the attribute of the n'th particle. AttributeType is user-defined! +/// // Only required if attribute transfer is enabled in ParticledToLevelSet. +/// void getAtt(AttributeType& att) const; /// }; /// @endcode /// -/// @note All methods are assumed to be thread-safe. -/// Also note all access methods return by value -/// since this allows for especailly the radius and velocities to be -/// scaled (i.e. modified) relative to the internal representations -/// (see unittest/TestParticlesToLevelSet.cc for an example). +/// @note See unittest/TestParticlesToLevelSet.cc for an example. /// /// The @c InterruptT template argument below refers to any class /// with the following interface: @@ -86,6 +101,9 @@ #include <tbb/blocked_range.h> #include <boost/bind.hpp> #include <boost/function.hpp> +#include <boost/type_traits/is_floating_point.hpp> +#include <boost/utility/enable_if.hpp> +#include <boost/mpl/if.hpp> #include <openvdb/util/Util.h> #include <openvdb/Types.h> #include <openvdb/Grid.h> @@ -94,113 +112,101 @@ #include <openvdb/util/NullInterrupter.h> #include "Composite.h" // for csgUnion() - namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { namespace tools { -namespace local { -/// Trait class needed to merge and split a distance and a particle id required -/// during attribute transfer. The default implementation of merge simply -/// ignores the particle id. A specialized implementation is given -/// below for a Dual type that holds both a distance and a particle id. -template <typename T> -struct DualTrait -{ - static T merge(T dist, Index32) { return dist; } - static T split(T dist) { return dist; } -}; -}// namespace local -template<typename GridT, - typename ParticleListT, - typename InterruptT=util::NullInterrupter, - typename RealT = typename GridT::ValueType> + +// This is a simple type that combines a distance value and a particle +// attribute. It's required for attribute transfer which is performed +// in the ParticlesToLevelSet::Raster memberclass defined below. +namespace local {template <typename VisibleT, typename BlindT> class BlindData;} + +template<typename SdfGridT, + typename AttributeT = void, + typename InterrupterT = util::NullInterrupter> class ParticlesToLevelSet { public: - /// @brief Main constructor using a default interrupter + + typedef typename boost::is_void<AttributeT>::type DisableT; + typedef InterrupterT InterrupterType; + + typedef SdfGridT SdfGridType; + typedef typename SdfGridT::ValueType SdfType; + + typedef typename boost::mpl::if_<DisableT, size_t, AttributeT>::type AttType; + typedef typename SdfGridT::template ValueConverter<AttType>::Type AttGridType; + + BOOST_STATIC_ASSERT(boost::is_floating_point<SdfType>::value); + + /// @brief Constructor using an exiting signed distance, + /// i.e. narrow band level set, grid. + /// + /// @param grid Level set grid in which particles are rasterized + /// @param interrupt Callback to interrupt a long-running process /// - /// @param grid contains the grid in which particles are rasterized - /// @param interrupt callback to interrupt a long-running process + /// @note The input grid is assumed to be a valid level set and if + /// it already contains voxels (with SDF values) partices are unioned + /// onto the exisinting level set surface. However, if attribute tranfer + /// is enabled, i.e. AttributeT != void, attributes are only + /// generated for voxels that overlap with particles, not the existing + /// voxels in the input grid (for which no attributes exist!). /// - /// @note The width in voxel units of the generated narrow band level set is + /// @details The width in voxel units of the generated narrow band level set is /// given by 2*background/dx, where background is the background value /// stored in the grid, and dx is the voxel size derived from the - /// transform stored in the grid. Also note that -background + /// transform also stored in the grid. Also note that -background /// corresponds to the constant value inside the generated narrow /// band level sets. Finally the default NullInterrupter should /// compile out interruption checks during optimization, thus /// incurring no run-time overhead. + explicit ParticlesToLevelSet(SdfGridT& grid, InterrupterT* interrupt = NULL); + + /// Destructor + ~ParticlesToLevelSet() { delete mBlindGrid; } + + /// @brief This methods syncs up the level set and attribute grids + /// and therefore needs to be called before any of these grids are + /// used and after the last call to any of the rasterizer methods. /// - ParticlesToLevelSet(GridT& grid, InterruptT* interrupt = NULL) : - mGrid(&grid), - mPa(NULL), - mDx(grid.transform().voxelSize()[0]), - mHalfWidth(local::DualTrait<ValueT>::split(grid.background()) / mDx), - mRmin(1.5),// corresponds to the Nyquist grid sampling frequency - mRmax(100.0),// corresponds to a huge particle (probably too large!) - mGrainSize(1), - mInterrupter(interrupt), - mMinCount(0), - mMaxCount(0), - mIsSlave(false) - { - if ( !mGrid->hasUniformVoxels() ) { - OPENVDB_THROW(RuntimeError, - "The transform must have uniform scale for ParticlesToLevelSet to function!"); - } - if (mGrid->getGridClass() != GRID_LEVEL_SET) { - OPENVDB_THROW(RuntimeError, - "ParticlesToLevelSet only supports level sets!" - "\nUse Grid::setGridClass(openvdb::GRID_LEVEL_SET)"); - } - /// @todo create a new tree rather than CSG into an existing tree - //mGrid->newTree(); - } - /// @brief Copy constructor called by tbb - ParticlesToLevelSet(ParticlesToLevelSet& other, tbb::split) : - mGrid(new GridT(*other.mGrid, openvdb::ShallowCopy())), - mPa(other.mPa), - mDx(other.mDx), - mHalfWidth(other.mHalfWidth), - mRmin(other.mRmin), - mRmax(other.mRmax), - mGrainSize(other.mGrainSize), - mTask(other.mTask), - mInterrupter(other.mInterrupter), - mMinCount(0), - mMaxCount(0), - mIsSlave(true) - { - mGrid->newTree(); - } - virtual ~ParticlesToLevelSet() { if (mIsSlave) delete mGrid; } + /// @note Avoid calling this method more then once and only after + /// all the particles have been rasterized. It has no effect if + /// attribute transfer is disabled, i.e. AttributeT = void. + void finalize(); + + /// @brief Return a shared pointer to the grid containing the + /// (optional) attribute. + /// + /// @warning If attribute transfer was disabled, i.e. AttributeT = + /// void, or finalize() was not called the pointer is NULL! + typename AttGridType::Ptr attributeGrid() { return mAttGrid; } - /// @return Half-width (in voxle units) of the narrow band level set - RealT getHalfWidth() const { return mHalfWidth; } + /// @brief Return the size of a voxel in world units + Real getVoxelSize() const { return mDx; } - /// @return Voxel size in world units - RealT getVoxelSize() const { return mDx; } + /// @brief Return the half-width of the narrow band in voxel units + Real getHalfWidth() const { return mHalfWidth; } - /// @return the smallest radius allowed in voxel units - RealT getRmin() const { return mRmin; } - /// @return the largest radius allowed in voxel units - RealT getRmax() const { return mRmax; } + /// @brief Return the smallest radius allowed in voxel units + Real getRmin() const { return mRmin; } + /// @brief Return the largest radius allowed in voxel units + Real getRmax() const { return mRmax; } - /// @return true if any particles were ignored due to their size + /// @brief Return true if any particles were ignored due to their size bool ignoredParticles() const { return mMinCount>0 || mMaxCount>0; } - /// @return number of small particles that were ignore due to Rmin + /// @brief Return number of small particles that were ignore due to Rmin size_t getMinCount() const { return mMinCount; } - /// @return number of large particles that were ignore due to Rmax + /// @brief Return number of large particles that were ignore due to Rmax size_t getMaxCount() const { return mMaxCount; } - /// set the smallest radius allowed in voxel units - void setRmin(RealT Rmin) { mRmin = math::Max(RealT(0),Rmin); } - /// set the largest radius allowed in voxel units - void setRmax(RealT Rmax) { mRmax = math::Max(mRmin,Rmax); } + /// @brief set the smallest radius allowed in voxel units + void setRmin(Real Rmin) { mRmin = math::Max(Real(0),Rmin); } + /// @brief set the largest radius allowed in voxel units + void setRmax(Real Rmax) { mRmax = math::Max(mRmin,Rmax); } - /// @return the grain-size used for multi-threading + /// @brief Rreturn the grain-size used for multi-threading int getGrainSize() const { return mGrainSize; } /// @brief Set the grain-size used for multi-threading. /// @note A grainsize of 0 or less disables multi-threading! @@ -208,21 +214,258 @@ public: /// @brief Rasterize a sphere per particle derived from their /// position and radius. All spheres are CSG unioned. + /// + /// @param pa Particles with position and radius. + template <typename ParticleListT> + void rasterizeSpheres(const ParticleListT& pa); + + /// @brief Rasterize a sphere per particle derived from their + /// position and constant radius. All spheres are CSG unioned. + /// + /// @param pa Particles with position. + /// @param radius Constant particle radius in world units. + template <typename ParticleListT> + void rasterizeSpheres(const ParticleListT& pa, Real radius); + + /// @brief Rasterize a trail per particle derived from their + /// position, radius and velocity. Each trail is generated + /// as CSG unions of sphere instances with decreasing radius. + /// /// @param pa particles with position, radius and velocity. - void rasterizeSpheres(const ParticleListT& pa) + /// @param delta controls distance between sphere instances + /// (default=1). Be careful not to use too small values since this + /// can lead to excessive computation per trail (which the + /// interrupter can't stop). + /// + /// @note The direction of a trail is inverse to the direction of + /// the velocity vector, and the length is given by |V|. The radius + /// at the head of the trail is given by the radius of the particle + /// and the radius at the tail of the trail is Rmin voxel units which + /// has a default value of 1.5 corresponding to the Nyquist + /// frequency! + template <typename ParticleListT> + void rasterizeTrails(const ParticleListT& pa, Real delta=1.0); + +private: + + typedef local::BlindData<SdfType, AttType> BlindType; + typedef typename SdfGridT::template ValueConverter<BlindType>::Type BlindGridType; + + /// Class with multi-threaded implementation of particle rasterization + template<typename ParticleListT, typename GridT> struct Raster; + + SdfGridType* mSdfGrid; + typename AttGridType::Ptr mAttGrid; + BlindGridType* mBlindGrid; + InterrupterT* mInterrupter; + Real mDx, mHalfWidth; + Real mRmin, mRmax;//ignore particles outside this range of radii in voxel + size_t mMinCount, mMaxCount;//counters for ignored particles! + int mGrainSize; + +};//end of ParticlesToLevelSet class + +template<typename SdfGridT, typename AttributeT, typename InterrupterT> +inline ParticlesToLevelSet<SdfGridT, AttributeT, InterrupterT>:: +ParticlesToLevelSet(SdfGridT& grid, InterrupterT* interrupter) : + mSdfGrid(&grid), + mBlindGrid(NULL), + mInterrupter(interrupter), + mDx(grid.voxelSize()[0]), + mHalfWidth(grid.background()/mDx), + mRmin(1.5),// corresponds to the Nyquist grid sampling frequency + mRmax(100.0),// corresponds to a huge particle (probably too large!) + mMinCount(0), + mMaxCount(0), + mGrainSize(1) +{ + if (!mSdfGrid->hasUniformVoxels() ) { + OPENVDB_THROW(RuntimeError, + "ParticlesToLevelSet only supports uniform voxels!"); + } + if (mSdfGrid->getGridClass() != GRID_LEVEL_SET) { + OPENVDB_THROW(RuntimeError, + "ParticlesToLevelSet only supports level sets!" + "\nUse Grid::setGridClass(openvdb::GRID_LEVEL_SET)"); + } + + if (!DisableT::value) { + mBlindGrid = new BlindGridType(BlindType(grid.background())); + mBlindGrid->setTransform(mSdfGrid->transform().copy()); + } +} + +template<typename SdfGridT, typename AttributeT, typename InterrupterT> +template <typename ParticleListT> +inline void ParticlesToLevelSet<SdfGridT, AttributeT, InterrupterT>:: +rasterizeSpheres(const ParticleListT& pa) +{ + if (DisableT::value) { + Raster<ParticleListT, SdfGridT> r(*this, mSdfGrid, pa); + r.rasterizeSpheres(); + } else { + Raster<ParticleListT, BlindGridType> r(*this, mBlindGrid, pa); + r.rasterizeSpheres(); + } +} + +template<typename SdfGridT, typename AttributeT, typename InterrupterT> +template <typename ParticleListT> +inline void ParticlesToLevelSet<SdfGridT, AttributeT, InterrupterT>:: +rasterizeSpheres(const ParticleListT& pa, Real radius) +{ + if (DisableT::value) { + Raster<ParticleListT, SdfGridT> r(*this, mSdfGrid, pa); + r.rasterizeSpheres(radius/mDx); + } else { + Raster<ParticleListT, BlindGridType> r(*this, mBlindGrid, pa); + r.rasterizeSpheres(radius/mDx); + } +} + +template<typename SdfGridT, typename AttributeT, typename InterrupterT> +template <typename ParticleListT> +inline void ParticlesToLevelSet<SdfGridT, AttributeT, InterrupterT>:: +rasterizeTrails(const ParticleListT& pa, Real delta) +{ + if (DisableT::value) { + Raster<ParticleListT, SdfGridT> r(*this, mSdfGrid, pa); + r.rasterizeTrails(delta); + } else { + Raster<ParticleListT, BlindGridType> r(*this, mBlindGrid, pa); + r.rasterizeTrails(delta); + } +} + +template<typename SdfGridT, typename AttributeT, typename InterrupterT> +inline void +ParticlesToLevelSet<SdfGridT, AttributeT, InterrupterT>::finalize() +{ + if (mBlindGrid==NULL) return; + + typedef typename SdfGridType::TreeType SdfTreeT; + typedef typename AttGridType::TreeType AttTreeT; + typedef typename BlindGridType::TreeType BlindTreeT; + // Use topology copy constructors since output grids have the same topology as mBlindDataGrid + const BlindTreeT& tree = mBlindGrid->tree(); + + // New level set tree + typename SdfTreeT::Ptr sdfTree(new SdfTreeT( + tree, tree.background().visible(), openvdb::TopologyCopy())); + + // Note this overwrites any existing attribute grids! + typename AttTreeT::Ptr attTree(new AttTreeT( + tree, tree.background().blind(), openvdb::TopologyCopy())); + mAttGrid = typename AttGridType::Ptr(new AttGridType(attTree)); + mAttGrid->setTransform(mBlindGrid->transform().copy()); + + // Extract the level set and IDs from mBlindDataGrid. We will + // explore the fact that by design active values always live + // at the leaf node level, i.e. no active tiles exist in level sets + typedef typename BlindTreeT::LeafCIter LeafIterT; + typedef typename BlindTreeT::LeafNodeType LeafT; + typedef typename SdfTreeT::LeafNodeType SdfLeafT; + typedef typename AttTreeT::LeafNodeType AttLeafT; + for (LeafIterT n = tree.cbeginLeaf(); n; ++n) { + const LeafT& leaf = *n; + const openvdb::Coord xyz = leaf.origin(); + // Get leafnodes that were allocated during topology contruction! + SdfLeafT* sdfLeaf = sdfTree->probeLeaf(xyz); + AttLeafT* attLeaf = attTree->probeLeaf(xyz); + for (typename LeafT::ValueOnCIter m=leaf.cbeginValueOn(); m; ++m) { + // Use linear offset (vs coordinate) access for better performance! + const openvdb::Index k = m.pos(); + const BlindType& v = *m; + sdfLeaf->setValueOnly(k, v.visible()); + attLeaf->setValueOnly(k, v.blind()); + } + } + sdfTree->signedFloodFill();//required since we only transferred active voxels! + + if (mSdfGrid->empty()) { + mSdfGrid->setTree(sdfTree); + } else { + tools::csgUnion(mSdfGrid->tree(), *sdfTree, /*prune=*/true); + } +} + +/////////////////////////////////////////////////////////// + +template<typename SdfGridT, typename AttributeT, typename InterrupterT> +template<typename ParticleListT, typename GridT> +struct ParticlesToLevelSet<SdfGridT, AttributeT, InterrupterT>::Raster +{ + typedef typename boost::is_void<AttributeT>::type DisableT; + typedef ParticlesToLevelSet<SdfGridT, AttributeT, InterrupterT> ParticlesToLevelSetT; + typedef typename ParticlesToLevelSetT::SdfType SdfT;//type of signed distance values + typedef typename ParticlesToLevelSetT::AttType AttT;//type of particle attribute + typedef typename GridT::ValueType ValueT; + typedef typename GridT::Accessor AccessorT; + + /// @brief Main constructor + Raster(ParticlesToLevelSetT& parent, GridT* grid, const ParticleListT& particles) + : mParent(parent), + mParticles(particles), + mGrid(grid), + mMap(*(mGrid->transform().baseMap())), + mMinCount(0), + mMaxCount(0), + mOwnsGrid(false) { - mPa = &pa; - if (mInterrupter) mInterrupter->start("Rasterizing particles to level set using spheres"); - mTask = boost::bind(&ParticlesToLevelSet::rasterSpheres, _1, _2); - this->cook(); - if (mInterrupter) mInterrupter->end(); } + /// @brief Copy constructor called by tbb threads + Raster(Raster& other, tbb::split) + : mParent(other.mParent), + mParticles(other.mParticles), + mGrid(new GridT(*other.mGrid, openvdb::ShallowCopy())), + mMap(other.mMap), + mMinCount(0), + mMaxCount(0), + mTask(other.mTask), + mOwnsGrid(true) + { + mGrid->newTree(); + } + + virtual ~Raster() { if (mOwnsGrid) delete mGrid; } + + /// @brief Rasterize a sphere per particle derived from their + /// position and radius. All spheres are CSG unioned. + void rasterizeSpheres() + { + mMinCount = mMaxCount = 0; + if (mParent.mInterrupter) { + mParent.mInterrupter->start("Rasterizing particles to level set using spheres"); + } + mTask = boost::bind(&Raster::rasterSpheres, _1, _2); + this->cook(); + if (mParent.mInterrupter) mParent.mInterrupter->end(); + } + /// @brief Rasterize a sphere per particle derived from their + /// position and constant radius. All spheres are CSG unioned. + /// @param radius constant radius of all particles in voxel units. + void rasterizeSpheres(Real radius) + { + mMinCount = radius < mParent.mRmin ? mParticles.size() : 0; + mMaxCount = radius > mParent.mRmax ? mParticles.size() : 0; + if (mMinCount>0 || mMaxCount>0) {//skipping all particles! + mParent.mMinCount = mMinCount; + mParent.mMaxCount = mMaxCount; + } else { + if (mParent.mInterrupter) { + mParent.mInterrupter->start( + "Rasterizing particles to level set using const spheres"); + } + mTask = boost::bind(&Raster::rasterFixedSpheres, _1, _2, SdfT(radius)); + this->cook(); + if (mParent.mInterrupter) mParent.mInterrupter->end(); + } + } /// @brief Rasterize a trail per particle derived from their /// position, radius and velocity. Each trail is generated /// as CSG unions of sphere instances with decreasing radius. /// - /// @param pa particles with position, radius and velocity. /// @param delta controls distance between sphere instances /// (default=1). Be careful not to use too small values since this /// can lead to excessive computation per trail (which the @@ -233,76 +476,161 @@ public: /// at the head of the trail is given by the radius of the particle /// and the radius at the tail of the trail is Rmin voxel units which /// has a default value of 1.5 corresponding to the Nyquist frequency! - void rasterizeTrails(const ParticleListT& pa, Real delta=1.0) + void rasterizeTrails(Real delta=1.0) { - mPa = &pa; - if (mInterrupter) mInterrupter->start("Rasterizing particles to level set using trails"); - mTask = boost::bind(&ParticlesToLevelSet::rasterTrails, _1, _2, RealT(delta)); + mMinCount = mMaxCount = 0; + if (mParent.mInterrupter) { + mParent.mInterrupter->start("Rasterizing particles to level set using trails"); + } + mTask = boost::bind(&Raster::rasterTrails, _1, _2, SdfT(delta)); this->cook(); - if (mInterrupter) mInterrupter->end(); + if (mParent.mInterrupter) mParent.mInterrupter->end(); } - // ========> DO NOT CALL ANY OF THE PUBLIC METHODS BELOW! <============= - - /// @brief Non-const functor called by tbb::parallel_reduce threads - /// - /// @note Do not call this method directly! + /// @brief Kicks off the optionally multithreaded computation void operator()(const tbb::blocked_range<size_t>& r) { - if (!mTask) { - OPENVDB_THROW(ValueError, "mTask is undefined - don't call operator() directly!"); - } + assert(mTask); mTask(this, r); + mParent.mMinCount = mMinCount; + mParent.mMaxCount = mMaxCount; } - /// @brief Method called by tbb::parallel_reduce threads - /// - /// @note Do not call this method directly! - void join(ParticlesToLevelSet& other) + /// @brief Reguired by tbb::parallel_reduce + void join(Raster& other) { tools::csgUnion(*mGrid, *other.mGrid, /*prune=*/true); mMinCount += other.mMinCount; mMaxCount += other.mMaxCount; } - private: - typedef typename GridT::ValueType ValueT; - typedef typename GridT::Accessor Accessor; - typedef typename boost::function<void (ParticlesToLevelSet*, - const tbb::blocked_range<size_t>&)> FuncType; - GridT* mGrid; - const ParticleListT* mPa;//list of particles - const RealT mDx;//size of voxel in world units - const RealT mHalfWidth;//half width of narrow band LS in voxels - RealT mRmin;//ignore particles smaller than this radius in voxels - RealT mRmax;//ignore particles larger than this radius in voxels - int mGrainSize; - FuncType mTask; - InterruptT* mInterrupter; - size_t mMinCount, mMaxCount;//counters for ignored particles! - const bool mIsSlave; + /// Disallow assignment since some of the members are references + Raster& operator=(const Raster& other) { return *this; } - void cook() - { - if (mGrainSize>0) { - tbb::parallel_reduce(tbb::blocked_range<size_t>(0,mPa->size(),mGrainSize), *this); - } else { - (*this)(tbb::blocked_range<size_t>(0, mPa->size())); - } - } /// @return true if the particle is too small or too large - inline bool ignoreParticle(RealT R) + bool ignoreParticle(SdfT R) { - if (R<mRmin) {// below the cutoff radius + if (R < mParent.mRmin) {// below the cutoff radius ++mMinCount; return true; } - if (R>mRmax) {// above the cutoff radius + if (R > mParent.mRmax) {// above the cutoff radius ++mMaxCount; return true; } return false; } + /// @brief Reguired by tbb::parallel_reduce to multithreaded + /// rasterization of particles as spheres with variable radius + /// + /// @param r tbb's default range referring to the list of particles + void rasterSpheres(const tbb::blocked_range<size_t>& r) + { + AccessorT acc = mGrid->getAccessor(); // local accessor + bool run = true; + const SdfT invDx = 1/mParent.mDx; + AttT att; + Vec3R pos; + Real rad; + for (Index32 id = r.begin(), e=r.end(); run && id != e; ++id) { + mParticles.getPosRad(id, pos, rad); + const SdfT R = invDx * rad;// in voxel units + if (this->ignoreParticle(R)) continue; + const Vec3R P = mMap.applyInverseMap(pos); + this->getAtt<DisableT>(id, att); + run = this->makeSphere(P, R, att, acc); + }//end loop over particles + } + /// @brief Reguired by tbb::parallel_reduce to multithreaded + /// rasterization of particles as spheres with a fixed radius + /// + /// @param r tbb's default range referring to the list of particles + void rasterFixedSpheres(const tbb::blocked_range<size_t>& r, SdfT R) + { + const SdfT dx = mParent.mDx, w = mParent.mHalfWidth;// in voxel units + AccessorT acc = mGrid->getAccessor(); // local accessor + const ValueT inside = -mGrid->background(); + const SdfT max = R + w;// maximum distance in voxel units + const SdfT max2 = math::Pow2(max);//square of maximum distance in voxel units + const SdfT min2 = math::Pow2(math::Max(SdfT(0), R - w));//square of minimum distance + ValueT v; + size_t count = 0; + AttT att; + Vec3R pos; + for (size_t id = r.begin(), e=r.end(); id != e; ++id) { + this->getAtt<DisableT>(id, att); + mParticles.getPos(id, pos); + const Vec3R P = mMap.applyInverseMap(pos); + const Coord a(math::Floor(P[0]-max),math::Floor(P[1]-max),math::Floor(P[2]-max)); + const Coord b(math::Ceil( P[0]+max),math::Ceil( P[1]+max),math::Ceil( P[2]+max)); + for ( Coord c = a; c.x() <= b.x(); ++c.x() ) { + //only check interrupter every 32'th scan in x + if (!(count++ & (1<<5)-1) && util::wasInterrupted(mParent.mInterrupter)) { + tbb::task::self().cancel_group_execution(); + return; + } + SdfT x2 = math::Pow2( c.x() - P[0] ); + for ( c.y() = a.y(); c.y() <= b.y(); ++c.y() ) { + SdfT x2y2 = x2 + math::Pow2( c.y() - P[1] ); + for ( c.z() = a.z(); c.z() <= b.z(); ++c.z() ) { + SdfT x2y2z2 = x2y2 + math::Pow2(c.z()- P[2]);//square distance from c to P + if ( x2y2z2 >= max2 || (!acc.probeValue(c,v) && v<ValueT(0)) ) + continue;//outside narrow band of particle or inside existing level set + if ( x2y2z2 <= min2 ) {//inside narrow band of the particle. + acc.setValueOff(c, inside); + continue; + } + // convert signed distance from voxel units to world units + const ValueT d=Merge(dx*(math::Sqrt(x2y2z2) - R), att); + if (d < v) acc.setValue(c, d);//CSG union + }//end loop over z + }//end loop over y + }//end loop over x + }//end loop over particles + } + /// @brief Reguired by tbb::parallel_reduce to multithreaded + /// rasterization of particles as spheres with velocity blurring + /// + /// @param r tbb's default range referring to the list of particles + void rasterTrails(const tbb::blocked_range<size_t>& r, SdfT delta) + { + AccessorT acc = mGrid->getAccessor(); // local accessor + bool run = true; + AttT att; + Vec3R pos, vel; + Real rad; + const Vec3R origin = mMap.applyInverseMap(Vec3R(0,0,0)); + const SdfT Rmin = mParent.mRmin, invDx = 1/mParent.mDx; + for (size_t id = r.begin(), e=r.end(); run && id != e; ++id) { + mParticles.getPosRadVel(id, pos, rad, vel); + const SdfT R0 = invDx*rad; + if (this->ignoreParticle(R0)) continue; + this->getAtt<DisableT>(id, att); + const Vec3R P0 = mMap.applyInverseMap(pos); + const Vec3R V = mMap.applyInverseMap(vel) - origin;//exclude translation + const SdfT speed = V.length(), inv_speed=1.0/speed; + const Vec3R N = -V*inv_speed;// inverse normalized direction + Vec3R P = P0;// local position of instance + SdfT R = R0, d=0;// local radius and length of trail + for (size_t m=0; run && d <= speed ; ++m) { + run = this->makeSphere(P, R, att, acc); + P += 0.5*delta*R*N;// adaptive offset along inverse velocity direction + d = (P-P0).length();// current length of trail + R = R0-(R0-Rmin)*d*inv_speed;// R = R0 -> mRmin(e.g. 1.5) + }//end loop over sphere instances + }//end loop over particles + } + + void cook() + { + if (mParent.mGrainSize>0) { + tbb::parallel_reduce( + tbb::blocked_range<size_t>(0,mParticles.size(),mParent.mGrainSize), *this); + } else { + (*this)(tbb::blocked_range<size_t>(0, mParticles.size())); + } + } + /// @brief Rasterize sphere at position P and radius R into a /// narrow-band level set with half-width, mHalfWidth. /// @return false if it was interrupted @@ -318,289 +646,122 @@ private: /// the closest Euclidian signed distances measured in world /// units). Also note we use the convention of positive distances /// outside the surface an negative distances inside the surface. - inline bool rasterSphere(const Vec3R &P, RealT R, Index32 id, Accessor& accessor) + bool makeSphere(const Vec3R &P, SdfT R, const AttT& att, AccessorT& acc) { const ValueT inside = -mGrid->background(); - const RealT dx = mDx; - const RealT max = R + mHalfWidth;// maximum distance in voxel units + const SdfT dx = mParent.mDx, w = mParent.mHalfWidth; + const SdfT max = R + w;// maximum distance in voxel units const Coord a(math::Floor(P[0]-max),math::Floor(P[1]-max),math::Floor(P[2]-max)); const Coord b(math::Ceil( P[0]+max),math::Ceil( P[1]+max),math::Ceil( P[2]+max)); - const RealT max2 = math::Pow2(max);//square of maximum distance in voxel units - const RealT min2 = math::Pow2(math::Max(RealT(0), R - mHalfWidth));//square of minimum distance + const SdfT max2 = math::Pow2(max);//square of maximum distance in voxel units + const SdfT min2 = math::Pow2(math::Max(SdfT(0), R - w));//square of minimum distance ValueT v; size_t count = 0; for ( Coord c = a; c.x() <= b.x(); ++c.x() ) { //only check interrupter every 32'th scan in x - if (!(count++ & (1<<5)-1) && util::wasInterrupted(mInterrupter)) { + if (!(count++ & (1<<5)-1) && util::wasInterrupted(mParent.mInterrupter)) { tbb::task::self().cancel_group_execution(); return false; } - RealT x2 = math::Pow2( c.x() - P[0] ); + SdfT x2 = math::Pow2( c.x() - P[0] ); for ( c.y() = a.y(); c.y() <= b.y(); ++c.y() ) { - RealT x2y2 = x2 + math::Pow2( c.y() - P[1] ); + SdfT x2y2 = x2 + math::Pow2( c.y() - P[1] ); for ( c.z() = a.z(); c.z() <= b.z(); ++c.z() ) { - RealT x2y2z2 = x2y2 + math::Pow2( c.z() - P[2] );//square distance from c to P - if ( x2y2z2 >= max2 || (!accessor.probeValue(c,v) && v<ValueT(0)) ) - continue;//outside narrow band of the particle or inside existing ls - if ( x2y2z2 <= min2 ) {//inside narrowband of the particle. - accessor.setValueOff(c, inside); + SdfT x2y2z2 = x2y2 + math::Pow2( c.z() - P[2] );//square distance from c to P + if ( x2y2z2 >= max2 || (!acc.probeValue(c,v) && v<ValueT(0)) ) + continue;//outside narrow band of the particle or inside existing level set + if ( x2y2z2 <= min2 ) {//inside narrow band of the particle. + acc.setValueOff(c, inside); continue; } - // distance in world units - const ValueT d = local::DualTrait<ValueT>::merge(dx*(math::Sqrt(x2y2z2) - R), id); - if (d < v) accessor.setValue(c, d);//CSG union + // convert signed distance from voxel units to world units + //const ValueT d=dx*(math::Sqrt(x2y2z2) - R); + const ValueT d=Merge(dx*(math::Sqrt(x2y2z2) - R), att); + if (d < v) acc.setValue(c, d);//CSG union }//end loop over z }//end loop over y }//end loop over x return true; } + typedef typename boost::function<void (Raster*, const tbb::blocked_range<size_t>&)> FuncType; - /// @brief Rasterize particles as spheres with variable radius - /// - /// @param r tbb's default range referring to the list of particles - void rasterSpheres(const tbb::blocked_range<size_t> &r) - { - Accessor accessor = mGrid->getAccessor(); // local accessor - const RealT inv_dx = RealT(1)/mDx; - bool run = true; - for (Index32 id = r.begin(), e=r.end(); run && id != e; ++id) { - const RealT R = inv_dx*mPa->radius(id);// in voxel units - if (this->ignoreParticle(R)) continue; - const Vec3R P = mGrid->transform().worldToIndex(mPa->pos(id)); - run = this->rasterSphere(P, R, id, accessor); - }//end loop over particles - } + template <typename DisableType> + typename boost::enable_if<DisableType>::type + getAtt(size_t, AttT&) const {;} + + template <typename DisableType> + typename boost::disable_if<DisableType>::type + getAtt(size_t n, AttT& a) const {mParticles.getAtt(n, a);} + + template <typename T> + typename boost::enable_if<boost::is_same<T,ValueT>, ValueT>::type + Merge(T s, const AttT&) const { return s; } + + template <typename T> + typename boost::disable_if<boost::is_same<T,ValueT>, ValueT>::type + Merge(T s, const AttT& a) const { return ValueT(s,a); } + + ParticlesToLevelSetT& mParent; + const ParticleListT& mParticles;//list of particles + GridT* mGrid; + const math::MapBase& mMap; + size_t mMinCount, mMaxCount;//counters for ignored particles! + FuncType mTask; + const bool mOwnsGrid; +};//end of Raster struct - /// @brief Rasterize particles as trails with length = |V| - /// - /// @param r tbb's default range referring to the list of particles - /// @param delta scale distance between the velocity blurring of - /// particles. Increasing it (above 1) typically results in aliasing! - /// - /// @note All computations are performed in voxle units. Also - /// for very small values of delta the number of instances will - /// increase resulting in very slow rasterization that cannot be - /// interrupted! - void rasterTrails(const tbb::blocked_range<size_t> &r, - RealT delta)//scale distance between instances (eg 1.0) - { - Accessor accessor = mGrid->getAccessor(); // local accessor - const RealT inv_dx = RealT(1)/mDx, Rmin = mRmin; - bool run = true; - for (Index32 id = r.begin(), e=r.end(); run && id != e; ++id) { - const RealT R0 = inv_dx*mPa->radius(id); - if (this->ignoreParticle(R0)) continue; - const Vec3R P0 = mGrid->transform().worldToIndex(mPa->pos(id)), - V = inv_dx*mPa->vel(id); - const RealT speed = V.length(), inv_speed=1.0/speed; - const Vec3R N = -V*inv_speed;// inverse normalized direction - Vec3R P = P0;// local position of instance - RealT R = R0, d=0;// local radius and length of trail - for (size_t m=0; run && d <= speed ; ++m) { - run = this->rasterSphere(P, R, id, accessor); - P += 0.5*delta*R*N;// adaptive offset along inverse velocity direction - d = (P-P0).length();// current length of trail - R = R0-(R0-Rmin)*d*inv_speed;// R = R0 -> mRmin(e.g. 1.5) - }//loop over sphere instances - }//end loop over particles - } -};//end of ParticlesToLevelSet class ///////////////////// YOU CAN SAFELY IGNORE THIS SECTION ///////////////////// + namespace local { // This is a simple type that combines a distance value and a particle -// id. It's required for attribute transfer which is defined in the -// ParticlesToLevelSetAndId class below. -template <typename RealT> -class Dual +// attribute. It's required for attribute transfer which is defined in the +// Raster class above. +template <typename VisibleT, typename BlindT> +class BlindData { -public: - explicit Dual() : mId(util::INVALID_IDX) {} - explicit Dual(RealT d) : mDist(d), mId(util::INVALID_IDX) {} - explicit Dual(RealT d, Index32 id) : mDist(d), mId(id) {} - Dual& operator=(const Dual& rhs) { mDist = rhs.mDist; mId = rhs.mId; return *this;} - bool isIdValid() const { return mId != util::INVALID_IDX; } - Index32 id() const { return mId; } - RealT dist() const { return mDist; } - bool operator!=(const Dual& rhs) const { return mDist != rhs.mDist; } - bool operator==(const Dual& rhs) const { return mDist == rhs.mDist; } - bool operator< (const Dual& rhs) const { return mDist < rhs.mDist; }; - bool operator<=(const Dual& rhs) const { return mDist <= rhs.mDist; }; - bool operator> (const Dual& rhs) const { return mDist > rhs.mDist; }; - Dual operator+ (const Dual& rhs) const { return Dual(mDist+rhs.mDist); }; - Dual operator+ (const RealT& rhs) const { return Dual(mDist+rhs); }; - Dual operator- (const Dual& rhs) const { return Dual(mDist-rhs.mDist); }; - Dual operator-() const { return Dual(-mDist); } + public: + typedef VisibleT type; + typedef VisibleT VisibleType; + typedef BlindT BlindType; + explicit BlindData() {} + explicit BlindData(VisibleT v) : mVisible(v) {} + BlindData(VisibleT v, BlindT b) : mVisible(v), mBlind(b) {} + BlindData& operator=(const BlindData& rhs) + { + mVisible = rhs.mVisible; + mBlind = rhs.mBlind; + return *this; + } + const VisibleT& visible() const { return mVisible; } + const BlindT& blind() const { return mBlind; } + bool operator==(const BlindData& rhs) const { return mVisible == rhs.mVisible; } + bool operator< (const BlindData& rhs) const { return mVisible < rhs.mVisible; }; + bool operator> (const BlindData& rhs) const { return mVisible > rhs.mVisible; }; + BlindData operator+(const BlindData& rhs) const { return BlindData(mVisible + rhs.mVisible); }; + BlindData operator+(const VisibleT& rhs) const { return BlindData(mVisible + rhs); }; + BlindData operator-(const BlindData& rhs) const { return BlindData(mVisible - rhs.mVisible); }; + BlindData operator-() const { return BlindData(-mVisible, mBlind); } protected: - RealT mDist; - Index32 mId; + VisibleT mVisible; + BlindT mBlind; }; // Required by several of the tree nodes -template <typename RealT> -inline std::ostream& operator<<(std::ostream& ostr, const Dual<RealT>& rhs) +template <typename VisibleT, typename BlindT> +inline std::ostream& operator<<(std::ostream& ostr, const BlindData<VisibleT, BlindT>& rhs) { - ostr << rhs.dist(); + ostr << rhs.visible(); return ostr; } // Required by math::Abs -template <typename RealT> -inline Dual<RealT> Abs(const Dual<RealT>& x) +template <typename VisibleT, typename BlindT> +inline BlindData<VisibleT, BlindT> Abs(const BlindData<VisibleT, BlindT>& x) { - return Dual<RealT>(math::Abs(x.dist()),x.id()); + return BlindData<VisibleT, BlindT>(math::Abs(x.visible()), x.blind()); } -// Specialization of trait class used to merge and split a distance and a particle id -template <typename T> -struct DualTrait<Dual<T> > -{ - static Dual<T> merge(T dist, Index32 id) { return Dual<T>(dist, id); } - static T split(Dual<T> dual) { return dual.dist(); } -}; }// local namespace -////////////////////////////////////////////////////////////////////////////// - -/// @brief Use this wrapper class to convert particles into a level set and a -/// separate index grid of closest-point particle id. The latter can -/// be used to subsequently transfer particles attributes into -/// separate grids. -/// @note This class has the same API as ParticlesToLevelSet - the -/// only exception being the raster methods that return the index grid! -template<typename LevelSetGridT, - typename ParticleListT, - typename InterruptT = util::NullInterrupter> -class ParticlesToLevelSetAndId -{ -public: - typedef typename LevelSetGridT::ValueType RealT; - typedef typename local::Dual<RealT> DualT; - typedef typename LevelSetGridT::TreeType RealTreeT; - typedef typename RealTreeT::template ValueConverter<DualT>::Type DualTreeT; - typedef Int32Tree IndxTreeT; - typedef Grid<DualTreeT> DualGridT; - typedef Int32Grid IndxGridT; - - ParticlesToLevelSetAndId(LevelSetGridT& ls, InterruptT* interrupter = NULL) : - mRealGrid(ls), - mDualGrid(DualT(ls.background())) - { - mDualGrid.setGridClass(ls.getGridClass()); - mDualGrid.setTransform(ls.transformPtr()); - mRaster = new RasterT(mDualGrid, interrupter); - } - - virtual ~ParticlesToLevelSetAndId() { delete mRaster; } - - /// @return Half-width (in voxle units) of the narrow band level set - RealT getHalfWidth() const { return mRaster->getHalfWidth(); } - - /// @return Voxel size in world units - RealT getVoxelSize() const { return mRaster->getVoxelSize(); } - /// @return true if any particles were ignored due to their size - bool ignoredParticles() const { return mRaster->ignoredParticles(); } - /// @return number of small particles that were ignore due to Rmin - size_t getMinCount() const { return mRaster->getMinCount(); } - /// @return number of large particles that were ignore due to Rmax - size_t getMaxCount() const { return mRaster->getMaxCount(); } - - /// @return the smallest radius allowed in voxel units - RealT getRmin() const { return mRaster->getRmin(); } - /// @return the largest radius allowed in voxel units - RealT getRmax() const { return mRaster->getRmax(); } - - /// set the smallest radius allowed in voxel units - void setRmin(RealT Rmin) { mRaster->setRmin(Rmin); } - /// set the largest radius allowed in voxel units - void setRmax(RealT Rmax) { mRaster->setRmax(Rmax); } - - /// @return the grain-size used for multi-threading - int getGrainSize() const { return mRaster->getGrainSize(); } - /// @brief Set the grain-size used for multi-threading. - /// @note A grainsize of 0 or less disables multi-threading! - void setGrainSize(int grainSize) { mRaster->setGrainSize(grainSize); } - - /// @brief Rasterize a sphere per particle derived from their - /// position and radius. All spheres are CSG unioned. - /// @return An index grid storing the id of the closest particle. - /// @param pa particles with position, radius and velocity. - typename IndxGridT::Ptr rasterizeSpheres(const ParticleListT& pa) - { - mRaster->rasterizeSpheres(pa); - return this->extract(); - } - /// @brief Rasterize a trail per particle derived from their - /// position, radius and velocity. Each trail is generated - /// as CSG unions of sphere instances with decreasing radius. - /// - /// @param pa particles with position, radius and velocity. - /// @param delta controls distance between sphere instances - /// (default=1). Be careful not to use too small values since this - /// can lead to excessive computation per trail (which the - /// interrupter can't stop). - /// - /// @note The direction of a trail is inverse to the direction of - /// the velocity vector, and the length is given by |V|. The radius - /// at the head of the trail is given by the radius of the particle - /// and the radius at the tail of the trail is Rmin voxel units which - /// has a default value of 1.5 corresponding to the Nyquist frequency! - typename IndxGridT::Ptr rasterizeTrails(const ParticleListT& pa, Real delta=1.0) - { - mRaster->rasterizeTrails(pa, delta); - return this->extract(); - } - -private: - - /// disallow copy construction - ParticlesToLevelSetAndId(const ParticlesToLevelSetAndId& other) {} - /// disallow copy assignment - ParticlesToLevelSetAndId& operator=(const ParticlesToLevelSetAndId& rhs) - { - return *this; - } - /// @brief Private method to extract the level set and index grid. - typename IndxGridT::Ptr extract() - { - // Use topology copy constructors since output grids have the - // same topology as mDualGrid - const DualTreeT& dualTree = mDualGrid.tree(); - typename IndxTreeT::Ptr indxTree(new IndxTreeT(dualTree,util::INVALID_IDX,TopologyCopy())); - typename IndxGridT::Ptr indxGrid = typename IndxGridT::Ptr(new IndxGridT(indxTree)); - indxGrid->setTransform(mDualGrid.transformPtr()); - typename RealTreeT::Ptr realTree(new RealTreeT(dualTree,mRealGrid.background(),TopologyCopy())); - mRealGrid.setTree(realTree); - - // Extract the level set and IDs from mDualGrid. We will - // explore the fact that by design active values always live - // at the leaf node level, i.e. no active tiles exist in level sets - typedef typename DualGridT::TreeType::LeafCIter LeafIterT; - typedef typename DualGridT::TreeType::LeafNodeType LeafT; - typedef typename LevelSetGridT::TreeType::LeafNodeType RealLeafT; - typedef typename IndxGridT::TreeType::LeafNodeType IndxLeafT; - RealTreeT& realTreeRef = *realTree; - IndxTreeT& indxTreeRef = *indxTree; - for (LeafIterT n = mDualGrid.tree().cbeginLeaf(); n; ++n) { - const LeafT& leaf = *n; - const Coord xyz = leaf.getOrigin(); - // Get leafnodes that were allocated during topology contruction! - RealLeafT& i = *realTreeRef.probeLeaf(xyz); - IndxLeafT& j = *indxTreeRef.probeLeaf(xyz); - for (typename LeafT::ValueOnCIter m=leaf.cbeginValueOn(); m; ++m) { - // Use linear offset (vs coordinate) access for better performance! - const Index k = m.pos(); - const DualT& v = *m; - i.setValueOnly(k, v.dist()); - j.setValueOnly(k, v.id()); - } - } - mRealGrid.signedFloodFill();//required since we only transferred active voxels! - return indxGrid; - } - - typedef ParticlesToLevelSet<DualGridT, ParticleListT, InterruptT, RealT> RasterT; - LevelSetGridT& mRealGrid;// input level set grid - DualGridT mDualGrid;// grid encoding both the level set and the point id - RasterT* mRaster; -};//end of ParticlesToLevelSetAndId +////////////////////////////////////////////////////////////////////////////// } // namespace tools } // namespace OPENVDB_VERSION_NAME diff --git a/extern/openvdb/internal/openvdb/tools/PointScatter.h b/extern/openvdb/internal/openvdb/tools/PointScatter.h index fe3dffd66c3..76d54a8ee20 100644 --- a/extern/openvdb/internal/openvdb/tools/PointScatter.h +++ b/extern/openvdb/internal/openvdb/tools/PointScatter.h @@ -117,7 +117,7 @@ public: void operator()(const GridT& grid) { mVoxelCount = grid.activeVoxelCount(); - if (mVoxelCount == 0) return;//throw std::runtime_error("No voxels in which to scatter points!"); + if (mVoxelCount == 0) return; const openvdb::Index64 voxelId = mVoxelCount - 1; const openvdb::Vec3d dim = grid.voxelSize(); if (mPointsPerVolume>0) { @@ -128,31 +128,29 @@ public: mPointsPerVolume = mPointCount/float(dim[0]*dim[1]*dim[2] * mVoxelCount); } else { return; - //throw std::runtime_error("Invalid point count and point density"); } openvdb::CoordBBox bbox; /// build sorted multi-map of random voxel-ids to contain a point std::multiset<openvdb::Index64> mVoxelSet; + const double maxId = static_cast<double>(voxelId); for (int i=0, chunks=100000; i<mPointCount; i += chunks) { if (util::wasInterrupted(mInterrupter)) return; - // throw std::runtime_error("processing was interrupted"); - //} /// @todo Multi-thread the generation of mVoxelSet for (int j=i, end=std::min(i+chunks, mPointCount); j<end; ++j) { - mVoxelSet.insert(openvdb::Index64(voxelId*getRand())); + mVoxelSet.insert(openvdb::Index64(math::Round(maxId*getRand()))); } } std::multiset<openvdb::Index64>::iterator voxelIter = mVoxelSet.begin(), voxelEnd = mVoxelSet.end(); typename GridT::ValueOnCIter valueIter = grid.cbeginValueOn(); - mPointCount = 0; + mPointCount = 0;//addPoint increments this counter size_t interruptCount = 0; - for (openvdb::Index64 i=valueIter.getVoxelCount(); voxelIter != voxelEnd; ++voxelIter) { + for (openvdb::Index64 n=valueIter.getVoxelCount(); voxelIter != voxelEnd; ++voxelIter) { //only check interrupter for every 32'th particle if (!(interruptCount++ & (1<<5)-1) && util::wasInterrupted(mInterrupter)) return; - while ( i <= *voxelIter ) { + while ( n <= *voxelIter ) { ++valueIter; - i += valueIter.getVoxelCount(); + n += valueIter.getVoxelCount(); } if (valueIter.isVoxelValue()) {// a majorty is expected to be voxels const openvdb::Coord min = valueIter.getCoord(); diff --git a/extern/openvdb/internal/openvdb/tools/RayIntersector.h b/extern/openvdb/internal/openvdb/tools/RayIntersector.h new file mode 100644 index 00000000000..f714289f2ed --- /dev/null +++ b/extern/openvdb/internal/openvdb/tools/RayIntersector.h @@ -0,0 +1,690 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +// +// Redistributions of source code must retain the above copyright +// and license notice and the following restrictions and disclaimer. +// +// * Neither the name of DreamWorks Animation nor the names of +// its contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE +// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. +// +/////////////////////////////////////////////////////////////////////////// +/// +/// @file RayIntersector.h +/// +/// @author Ken Museth +/// +/// @brief Accelerated intersection of a ray with a narrow-band level +/// set or a generic (e.g. density) volume. This will of course be +/// useful for respectively surface and volume rendering. +/// +/// @details This file defines two main classes, +/// LevelSetRayIntersector and VolumeRayIntersector, as well as the +/// three support classes LevelSetHDDA, VolumeHDDA and LinearSearchImpl. +/// The LevelSetRayIntersector is templated on the LinearSearchImpl class +/// and calls instances of the LevelSetHDDA class. The reason to split +/// level set ray intersection into three classes is twofold. First +/// LevelSetRayIntersector defines the public API for client code and +/// LinearSearchImpl defines the actual algorithm used for the +/// ray level-set intersection. In other words this design will allow +/// for the public API to be fixed while the intersection algorithm +/// can change without resolving to (slow) virtual methods. Second, +/// LevelSetHDDA, which implements a hierarchical Differential Digital +/// Analyzer, relies on partial template specialization, so it has to +/// be a standalone class (as opposed to a member class of +/// LevelSetRayIntersector). The VolumeRayIntersector is conceptually +/// much simpler then the LevelSetRayIntersector, and hence it only +/// depends on VolumeHDDA that implements the hierarchical +/// Differential Digital Analyzer. + + +#ifndef OPENVDB_TOOLS_RAYINTERSECTOR_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_RAYINTERSECTOR_HAS_BEEN_INCLUDED + +#include <openvdb/math/DDA.h> +#include <openvdb/math/Math.h> +#include <openvdb/math/Ray.h> +#include <openvdb/math/Stencils.h> +#include <openvdb/Grid.h> +#include <openvdb/Types.h> +#include "Morphology.h" +#include <boost/utility.hpp> +#include <boost/type_traits/is_floating_point.hpp> + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +// Helper class that implements the actual search of the zero-crossing +// of the level set along the direction of a ray. This particular +// implementation uses iterative linear search. +template<typename GridT, int Iterations = 0, typename RealT = double> +class LinearSearchImpl; + + +///////////////////////////////////// LevelSetRayIntersector ///////////////////////////////////// + + +/// @brief This class provides the public API for intersecting a ray +/// with a narrow-band level set. +/// +/// @details It wraps an SearchImplT with a simple public API and +/// performs the actual hierarchical tree node and voxel traversal. +/// +/// @warning Use the (default) copy-constructor to make sure each +/// computational thread has their own instance of this class. This is +/// important since the SearchImplT contains a ValueAccessor that is +/// not thread-safe. However copying is very efficient. +/// +/// @see tools/RayTracer.h for examples of intended usage. +/// +/// @todo Add TrilinearSearchImpl, as an alternative to LinearSearchImpl, +/// that performs analytical 3D trilinear intersection tests, i.e., solves +/// cubic equations. This is slower but also more accurate than the 1D +/// linear interpolation in LinearSearchImpl. +template<typename GridT, + typename SearchImplT = LinearSearchImpl<GridT>, + int NodeLevel = GridT::TreeType::RootNodeType::ChildNodeType::LEVEL, + typename RayT = math::Ray<Real> > +class LevelSetRayIntersector +{ +public: + typedef GridT GridType; + typedef RayT RayType; + typedef typename RayT::RealType RealType; + typedef typename RayT::Vec3T Vec3Type; + typedef typename GridT::ValueType ValueT; + typedef typename GridT::TreeType TreeT; + + BOOST_STATIC_ASSERT( NodeLevel >= -1 && NodeLevel < int(TreeT::DEPTH)-1); + BOOST_STATIC_ASSERT(boost::is_floating_point<ValueT>::value); + + /// @brief Constructor + /// @param grid level set grid to intersect rays against. + /// @param isoValue optional iso-value for the ray-intersection. + LevelSetRayIntersector(const GridT& grid, const ValueT& isoValue = zeroVal<ValueT>()) + : mTester(grid, isoValue) + { + if (!grid.hasUniformVoxels() ) { + OPENVDB_THROW(RuntimeError, + "LevelSetRayIntersector only supports uniform voxels!"); + } + if (grid.getGridClass() != GRID_LEVEL_SET) { + OPENVDB_THROW(RuntimeError, + "LevelSetRayIntersector only supports level sets!" + "\nUse Grid::setGridClass(openvdb::GRID_LEVEL_SET)"); + } + } + + /// @brief Return the iso-value used for ray-intersections + const ValueT& getIsoValue() const { return mTester.getIsoValue(); } + + /// @brief Return @c true if the index-space ray intersects the level set. + /// @param iRay ray represented in index space. + bool intersectsIS(const RayType& iRay) const + { + if (!mTester.setIndexRay(iRay)) return false;//missed bbox + return math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester); + } + + /// @brief Return @c true if the index-space ray intersects the level set + /// @param iRay ray represented in index space. + /// @param iTime if an intersection was found it is assigned the time of the + /// intersection along the index ray. + bool intersectsIS(const RayType& iRay, Real &iTime) const + { + if (!mTester.setIndexRay(iRay)) return false;//missed bbox + iTime = mTester.getIndexTime(); + return math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester); + } + + /// @brief Return @c true if the index-space ray intersects the level set. + /// @param iRay ray represented in index space. + /// @param xyz if an intersection was found it is assigned the + /// intersection point in index space, otherwise it is unchanged. + bool intersectsIS(const RayType& iRay, Vec3Type& xyz) const + { + if (!mTester.setIndexRay(iRay)) return false;//missed bbox + if (!math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester)) return false;//missed level set + mTester.getIndexPos(xyz); + return true; + } + + /// @brief Return @c true if the index-space ray intersects the level set. + /// @param iRay ray represented in index space. + /// @param xyz if an intersection was found it is assigned the + /// intersection point in index space, otherwise it is unchanged. + /// @param iTime if an intersection was found it is assigned the time of the + /// intersection along the index ray. + bool intersectsIS(const RayType& iRay, Vec3Type& xyz, Real &iTime) const + { + if (!mTester.setIndexRay(iRay)) return false;//missed bbox + if (!math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester)) return false;//missed level set + mTester.getIndexPos(xyz); + iTime = mTester.getIndexTime(); + return true; + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + bool intersectsWS(const RayType& wRay) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + return math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester); + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + /// @param wTime if an intersection was found it is assigned the time of the + /// intersection along the world ray. + bool intersectsWS(const RayType& wRay, Real &wTime) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + wTime = mTester.getWorldTime(); + return math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester); + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + /// @param world if an intersection was found it is assigned the + /// intersection point in world space, otherwise it is unchanged + bool intersectsWS(const RayType& wRay, Vec3Type& world) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + if (!math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester)) return false;//missed level set + mTester.getWorldPos(world); + return true; + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + /// @param world if an intersection was found it is assigned the + /// intersection point in world space, otherwise it is unchanged. + /// @param wTime if an intersection was found it is assigned the time of the + /// intersection along the world ray. + bool intersectsWS(const RayType& wRay, Vec3Type& world, Real &wTime) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + if (!math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester)) return false;//missed level set + mTester.getWorldPos(world); + wTime = mTester.getWorldTime(); + return true; + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + /// @param world if an intersection was found it is assigned the + /// intersection point in world space, otherwise it is unchanged. + /// @param normal if an intersection was found it is assigned the normal + /// of the level set surface in world space, otherwise it is unchanged. + bool intersectsWS(const RayType& wRay, Vec3Type& world, Vec3Type& normal) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + if (!math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester)) return false;//missed level set + mTester.getWorldPosAndNml(world, normal); + return true; + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + /// @param world if an intersection was found it is assigned the + /// intersection point in world space, otherwise it is unchanged. + /// @param normal if an intersection was found it is assigned the normal + /// of the level set surface in world space, otherwise it is unchanged. + /// @param wTime if an intersection was found it is assigned the time of the + /// intersection along the world ray. + bool intersectsWS(const RayType& wRay, Vec3Type& world, Vec3Type& normal, Real &wTime) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + if (!math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester)) return false;//missed level set + mTester.getWorldPosAndNml(world, normal); + wTime = mTester.getWorldTime(); + return true; + } + +private: + + mutable SearchImplT mTester; + +};// LevelSetRayIntersector + + +////////////////////////////////////// VolumeRayIntersector ////////////////////////////////////// + + +/// @brief This class provides the public API for intersecting a ray +/// with a generic (e.g. density) volume. +/// @details Internally it performs the actual hierarchical tree node traversal. +/// @warning Use the (default) copy-constructor to make sure each +/// computational thread has their own instance of this class. This is +/// important since it contains a ValueAccessor that is +/// not thread-safe and a CoordBBox of the active voxels that should +/// not be re-computed for each thread. However copying is very efficient. +/// @par Example: +/// @code +/// // Create an instance for the master thread +/// VolumeRayIntersector inter(grid); +/// // For each additional thread use the copy contructor. This +/// // amortizes the overhead of computing the bbox of the active voxels! +/// VolumeRayIntersector inter2(inter); +/// // Before each ray-traversal set the index ray. +/// iter.setIndexRay(ray); +/// // or world ray +/// iter.setWorldRay(ray); +/// // Now you can begin the ray-marching using consecutive calls to VolumeRayIntersector::march +/// double t0=0, t1=0;// note the entry and exit times are with respect to the INDEX ray +/// while ( inter.march(t0, t1) ) { +/// // perform line-integration between t0 and t1 +/// }} +/// @endcode +template<typename GridT, + int NodeLevel = GridT::TreeType::RootNodeType::ChildNodeType::LEVEL, + typename RayT = math::Ray<Real> > +class VolumeRayIntersector +{ +public: + typedef GridT GridType; + typedef RayT RayType; + typedef typename RayT::RealType RealType; + typedef typename GridT::TreeType::RootNodeType RootType; + typedef tree::Tree<typename RootType::template ValueConverter<bool>::Type> TreeT; + + BOOST_STATIC_ASSERT( NodeLevel >= 0 && NodeLevel < int(TreeT::DEPTH)-1); + + /// @brief Grid constructor + /// @param grid Generic grid to intersect rays against. + /// @param dilationCount The number of voxel dilations performed + /// on (a boolean copy of) the input grid. This allows the + /// intersector to account for the size of interpolation kernels + /// in client code. + /// @throw RuntimeError if the voxels of the grid are not uniform + /// or the grid is empty. + VolumeRayIntersector(const GridT& grid, int dilationCount = 0) + : mIsMaster(true) + , mTree(new TreeT(grid.tree(), false, TopologyCopy())) + , mGrid(&grid) + , mAccessor(*mTree) + { + if (!grid.hasUniformVoxels() ) { + OPENVDB_THROW(RuntimeError, + "VolumeRayIntersector only supports uniform voxels!"); + } + if ( grid.empty() ) { + OPENVDB_THROW(RuntimeError, "LinearSearchImpl does not supports empty grids"); + } + + // Dilate active voxels to better account for the size of interpolation kernels + tools::dilateVoxels(*mTree, dilationCount); + + mTree->root().evalActiveBoundingBox(mBBox, /*visit individual voxels*/false); + + mBBox.max().offset(1);//padding so the bbox of a node becomes (origin,origin + node_dim) + } + + /// @brief Grid and BBox constructor + /// @param grid Generic grid to intersect rays against. + /// @param bbox The axis-aligned bounding-box in the index space of the grid. + /// @warning It is assumed that bbox = (min, min + dim) where min denotes + /// to the smallest grid coordinates and dim are the integer length of the bbox. + /// @throw RuntimeError if the voxels of the grid are not uniform + /// or the grid is empty. + VolumeRayIntersector(const GridT& grid, const math::CoordBBox& bbox) + : mIsMaster(true) + , mTree(new TreeT(grid.tree(), false, TopologyCopy())) + , mGrid(&grid) + , mAccessor(*mTree) + , mBBox(bbox) + { + if (!grid.hasUniformVoxels() ) { + OPENVDB_THROW(RuntimeError, + "VolumeRayIntersector only supports uniform voxels!"); + } + if ( grid.empty() ) { + OPENVDB_THROW(RuntimeError, "LinearSearchImpl does not supports empty grids"); + } + } + + /// @brief Shallow copy constructor + /// @warning This copy constructor creates shallow copies of data + /// members of the instance passed as the argument. For + /// performance reasons we are not using shared pointers (their + /// mutex-lock impairs multi-threading). + VolumeRayIntersector(const VolumeRayIntersector& other) + : mIsMaster(false) + , mTree(other.mTree)//shallow copy + , mGrid(other.mGrid)//shallow copy + , mAccessor(*mTree)//deep copy + , mRay(other.mRay)//deep copy + , mTmax(other.mTmax)//deep copy + , mBBox(other.mBBox)//deep copy + { + } + + /// @brief Destructor + ~VolumeRayIntersector() { if (mIsMaster) delete mTree; } + + /// @brief Return @c false if the index ray misses the bbox of the grid. + /// @param iRay Ray represented in index space. + /// @warning Call this method (or setWorldRay) before the ray + /// traversal starts and use the return value to decide if further + /// marching is required. + inline bool setIndexRay(const RayT& iRay) + { + mRay = iRay; + const bool hit = mRay.clip(mBBox); + if (hit) mTmax = mRay.t1(); + return hit; + } + + /// @brief Return @c false if the world ray misses the bbox of the grid. + /// @param wRay Ray represented in world space. + /// @warning Call this method (or setIndexRay) before the ray + /// traversal starts and use the return value to decide if further + /// marching is required. + /// @details Since hit times are computed with repect to the ray + /// represented in index space of the current grid, it is + /// recommended that either the client code uses getIndexPos to + /// compute index position from hit times or alternatively keeps + /// an instance of the index ray and instead uses setIndexRay to + /// initialize the ray. + inline bool setWorldRay(const RayT& wRay) + { + return this->setIndexRay(wRay.worldToIndex(*mGrid)); + } + + inline typename RayT::TimeSpan march() + { + const typename RayT::TimeSpan t = mHDDA.march(mRay, mAccessor); + if (t.t1>0) mRay.setTimes(t.t1 + math::Delta<RealType>::value(), mTmax); + return t; + } + + /// @brief Return @c true if the ray intersects active values, + /// i.e. either active voxels or tiles. Only when a hit is + /// detected are t0 and t1 updated with the corresponding entry + /// and exit times along the INDEX ray! + /// @param t0 If the return value > 0 this is the time of the + /// first hit of an active tile or leaf. + /// @param t1 If the return value > t0 this is the time of the + /// first hit (> t0) of an inactive tile or exit point of the + /// BBOX for the leaf nodes. + /// @warning t0 and t1 are computed with repect to the ray represented in + /// index space of the current grid, not world space! + inline bool march(Real& t0, Real& t1) + { + const typename RayT::TimeSpan t = this->march(); + t.get(t0, t1); + return t.valid(); + } + + inline void hits(std::vector<typename RayT::TimeSpan>& list) + { + mHDDA.hits(mRay, mAccessor, list); + } + + /// @brief Return the floating-point index position along the + /// current index ray at the specified time. + inline Vec3R getIndexPos(Real time) const { return mRay(time); } + + /// @brief Return the floating-point world position along the + /// current index ray at the specified time. + inline Vec3R getWorldPos(Real time) const { return mGrid->indexToWorld(mRay(time)); } + + inline Real getWorldTime(Real time) const + { + return time*mGrid->transform().baseMap()->applyJacobian(mRay.dir()).length(); + } + + /// @brief Return a const reference to the input grid. + const GridT& grid() const { return *mGrid; } + + /// @brief Return a const reference to the (potentially dilated) + /// bool tree used to accelerate the ray marching. + const TreeT& tree() const { return *mTree; } + + /// @brief Return a const reference to the BBOX of the grid + const math::CoordBBox& bbox() const { return mBBox; } + + /// @brief Print bbox, statistics, memory usage and other information. + /// @param os a stream to which to write textual information + /// @param verboseLevel 1: print bbox only; 2: include boolean tree + /// statistics; 3: include memory usage + void print(std::ostream& os = std::cout, int verboseLevel = 1) + { + if (verboseLevel>0) { + os << "BBox: " << mBBox << std::endl; + if (verboseLevel==2) { + mTree->print(os, 1); + } else if (verboseLevel>2) { + mTree->print(os, 2); + } + } + } + +private: + + typedef typename tree::ValueAccessor<const TreeT> AccessorT; + + const bool mIsMaster; + TreeT* mTree; + const GridT* mGrid; + AccessorT mAccessor; + RayT mRay; + Real mTmax; + math::CoordBBox mBBox; + math::VolumeHDDA<TreeT, RayType, NodeLevel> mHDDA; + +};// VolumeRayIntersector + + +//////////////////////////////////////// LinearSearchImpl //////////////////////////////////////// + + +/// @brief Implements linear iterative search for an iso-value of +/// the level set along along the direction of the ray. +/// +/// @note Since this class is used internally in +/// LevelSetRayIntersector (define above) and LevelSetHDDA (defined below) +/// client code should never interact directly with its API. This also +/// explains why we are not concerned with the fact that several of +/// its methods are unsafe to call unless roots were already detected. +/// +/// @details It is approximate due to the limited number of iterations +/// which can can be defined with a template parameter. However the default value +/// has proven surprisingly accurate and fast. In fact more iterations +/// are not guaranteed to give significantly better results. +/// +/// @warning Since the root-searching algorithm is approximate +/// (first-order) it is possible to miss intersections if the +/// iso-value is too close to the inside or outside of the narrow +/// band (typically a distance less then a voxel unit). +/// +/// @warning Since this class internally stores a ValueAccessor it is NOT thread-safe, +/// so make sure to give each thread its own instance. This of course also means that +/// the cost of allocating an instance should (if possible) be amortized over +/// as many ray intersections as possible. +template<typename GridT, int Iterations, typename RealT> +class LinearSearchImpl +{ +public: + typedef math::Ray<RealT> RayT; + typedef typename GridT::ValueType ValueT; + typedef typename GridT::ConstAccessor AccessorT; + typedef math::BoxStencil<GridT> StencilT; + typedef typename StencilT::Vec3Type Vec3T; + + /// @brief Constructor from a grid. + /// @throw RunTimeError if the grid is empty. + /// @throw ValueError if the isoValue is not inside the narrow-band. + LinearSearchImpl(const GridT& grid, const ValueT& isoValue = zeroVal<ValueT>()) + : mStencil(grid), + mIsoValue(isoValue), + mMinValue(isoValue-2*grid.voxelSize()[0]), + mMaxValue(isoValue+2*grid.voxelSize()[0]) + { + if ( grid.empty() ) { + OPENVDB_THROW(RuntimeError, "LinearSearchImpl does not supports empty grids"); + } + if (mIsoValue<= -grid.background() || + mIsoValue>= grid.background() ){ + OPENVDB_THROW(ValueError, "The iso-value must be inside the narrow-band!"); + } + grid.tree().root().evalActiveBoundingBox(mBBox, /*visit individual voxels*/false); + } + + /// @brief Return the iso-value used for ray-intersections + const ValueT& getIsoValue() const { return mIsoValue; } + + /// @brief Return @c false the ray misses the bbox of the grid. + /// @param iRay Ray represented in index space. + /// @warning Call this method before the ray traversal starts. + inline bool setIndexRay(const RayT& iRay) + { + mRay = iRay; + return mRay.clip(mBBox);//did it hit the bbox + } + + /// @brief Return @c false the ray misses the bbox of the grid. + /// @param wRay Ray represented in world space. + /// @warning Call this method before the ray traversal starts. + inline bool setWorldRay(const RayT& wRay) + { + mRay = wRay.worldToIndex(mStencil.grid()); + return mRay.clip(mBBox);//did it hit the bbox + } + + /// @brief Get the intersection point in index space. + /// @param xyz The position in index space of the intersection. + inline void getIndexPos(Vec3d& xyz) const { xyz = mRay(mTime); } + + /// @brief Get the intersection point in world space. + /// @param xyz The position in world space of the intersection. + inline void getWorldPos(Vec3d& xyz) const { xyz = mStencil.grid().indexToWorld(mRay(mTime)); } + + /// @brief Get the intersection point and normal in world space + /// @param xyz The position in world space of the intersection. + /// @param nml The surface normal in world space of the intersection. + inline void getWorldPosAndNml(Vec3d& xyz, Vec3d& nml) + { + this->getIndexPos(xyz); + mStencil.moveTo(xyz); + nml = mStencil.gradient(xyz); + nml.normalize(); + xyz = mStencil.grid().indexToWorld(xyz); + } + + /// @brief Return the time of intersection along the index ray. + inline RealT getIndexTime() const { return mTime; } + + /// @brief Return the time of intersection along the world ray. + inline RealT getWorldTime() const + { + return mTime*mStencil.grid().transform().baseMap()->applyJacobian(mRay.dir()).length(); + } + +private: + + /// @brief Initiate the local voxel intersection test. + /// @warning Make sure to call this method before the local voxel intersection test. + inline void init(RealT t0) + { + mT[0] = t0; + mV[0] = this->interpValue(t0); + } + + inline void setRange(RealT t0, RealT t1) { mRay.setTimes(t0, t1); } + + /// @brief Return a const reference to the ray. + inline const RayT& ray() const { return mRay; } + + /// @brief Return true if a node of the the specified type exists at ijk. + template <typename NodeT> + inline bool hasNode(const Coord& ijk) + { + return mStencil.accessor().template probeConstNode<NodeT>(ijk) != NULL; + } + + /// @brief Return @c true if an intersection is detected. + /// @param ijk Grid coordinate of the node origin or voxel being tested. + /// @param time Time along the index ray being tested. + /// @warning Only if and intersection is detected is it safe to + /// call getIndexPos, getWorldPos and getWorldPosAndNml! + inline bool operator()(const Coord& ijk, RealT time) + { + ValueT V; + if (mStencil.accessor().probeValue(ijk, V) &&//within narrow band + V>mMinValue && V<mMaxValue) {// and close to iso-value? + mT[1] = time; + mV[1] = this->interpValue(time); + if (math::ZeroCrossing(mV[0], mV[1])) { + mTime = this->interpTime(); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + for (int n=0; Iterations>0 && n<Iterations; ++n) {//resolved at compile-time + V = this->interpValue(mTime); + const int m = math::ZeroCrossing(mV[0], V) ? 1 : 0; + mV[m] = V; + mT[m] = mTime; + mTime = this->interpTime(); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + return true; + } + mT[0] = mT[1]; + mV[0] = mV[1]; + } + return false; + } + + inline RealT interpTime() + { + assert(math::isApproxLarger(mT[1], mT[0], 1e-6)); + return mT[0]+(mT[1]-mT[0])*mV[0]/(mV[0]-mV[1]); + } + + inline RealT interpValue(RealT time) + { + const Vec3R pos = mRay(time); + mStencil.moveTo(pos); + return mStencil.interpolation(pos) - mIsoValue; + } + + template<typename, int> friend struct math::LevelSetHDDA; + + RayT mRay; + StencilT mStencil; + RealT mTime;//time of intersection + ValueT mV[2]; + RealT mT[2]; + const ValueT mIsoValue, mMinValue, mMaxValue; + math::CoordBBox mBBox; +};// LinearSearchImpl + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_RAYINTERSECTOR_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/extern/openvdb/internal/openvdb/tools/RayTracer.h b/extern/openvdb/internal/openvdb/tools/RayTracer.h new file mode 100644 index 00000000000..6d06d3323ac --- /dev/null +++ b/extern/openvdb/internal/openvdb/tools/RayTracer.h @@ -0,0 +1,977 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +// +// Redistributions of source code must retain the above copyright +// and license notice and the following restrictions and disclaimer. +// +// * Neither the name of DreamWorks Animation nor the names of +// its contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE +// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. +// +/////////////////////////////////////////////////////////////////////////// +/// +/// @file RayTracer.h +/// +/// @author Ken Museth +/// +/// @brief Defines two simple but multithreaded renders, a level-set +/// ray tracer and a volume render. To support these renders we also define +/// perspective and orthographic cameras (both designed to mimic a Houdini camera), +/// a Film class and some rather naive shaders. +/// +/// @note These classes are included mainly as reference implementations for +/// ray-tracing of OpenVDB volumes. In other words they are not intended for +/// production-quality rendering, but could be used for fast pre-visualiztion +/// or as a startingpoint for a more serious render. + +#ifndef OPENVDB_TOOLS_RAYTRACER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_RAYTRACER_HAS_BEEN_INCLUDED + +#include <openvdb/Types.h> +#include <openvdb/math/BBox.h> +#include <openvdb/math/Ray.h> +#include <openvdb/math/Math.h> +#include <openvdb/tools/RayIntersector.h> +#include <openvdb/tools/Interpolation.h> +#include <boost/scoped_ptr.hpp> +#include <boost/scoped_array.hpp> +#include <vector> + +#ifdef OPENVDB_TOOLS_RAYTRACER_USE_EXR +#include <OpenEXR/ImfPixelType.h> +#include <OpenEXR/ImfChannelList.h> +#include <OpenEXR/ImfOutputFile.h> +#include <OpenEXR/ImfHeader.h> +#include <OpenEXR/ImfFrameBuffer.h> +#endif + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +// Forward declarations +class BaseCamera; +class BaseShader; + +/// @brief Ray-trace a volume. +template<typename GridT> +inline void rayTrace(const GridT&, + const BaseShader&, + BaseCamera&, + size_t pixelSamples = 1, + unsigned int seed = 0, + bool threaded = true); + +/// @brief Ray-trace a volume using a given ray intersector. +template<typename GridT, typename IntersectorT> +inline void rayTrace(const GridT&, + const IntersectorT&, + const BaseShader&, + BaseCamera&, + size_t pixelSamples = 1, + unsigned int seed = 0, + bool threaded = true); + + +///////////////////////////////LEVEL SET RAY TRACER /////////////////////////////////////// + +/// @brief A (very) simple multithreaded ray tracer specifically for narrow-band level sets. +/// @details Included primarily as a reference implementation. +template<typename GridT, typename IntersectorT = tools::LevelSetRayIntersector<GridT> > +class LevelSetRayTracer +{ +public: + typedef GridT GridType; + typedef typename IntersectorT::Vec3Type Vec3Type; + typedef typename IntersectorT::RayType RayType; + + /// @brief Constructor based on an instance of the grid to be rendered. + LevelSetRayTracer(const GridT& grid, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples = 1, + unsigned int seed = 0); + + /// @brief Constructor based on an instance of the intersector + /// performing the ray-intersections. + LevelSetRayTracer(const IntersectorT& inter, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples = 1, + unsigned int seed = 0); + + /// @brief Copy constructor + LevelSetRayTracer(const LevelSetRayTracer& other); + + /// @brief Destructor + ~LevelSetRayTracer(); + + /// @brief Set the level set grid to be ray-traced + void setGrid(const GridT& grid); + + /// @brief Set the intersector that performs the actual + /// intersection of the rays against the narrow-band level set. + void setIntersector(const IntersectorT& inter); + + /// @brief Set the shader derived from the abstract BaseShader class. + /// + /// @note The shader is not assumed to be thread-safe so each + /// thread will get it's only deep copy. For instance it could + /// contains a ValueAccessor into another grid with auxiliary + /// shading information. Thus, make sure it is relatively + /// light-weight and efficient to copy (which is the case for ValueAccesors). + void setShader(const BaseShader& shader); + + /// @brief Set the camera derived from the abstract BaseCamera class. + void setCamera(BaseCamera& camera); + + /// @brief Set the number of pixel samples and the seed for + /// jittered sub-rays. A value larger then one implies + /// anti-aliasing by jittered super-sampling. + /// @throw ValueError if pixelSamples is equal to zero. + void setPixelSamples(size_t pixelSamples, unsigned int seed = 0); + + /// @brief Perform the actual (potentially multithreaded) ray-tracing. + void render(bool threaded = true) const; + + /// @brief Public method required by tbb::parallel_for. + /// @warning Never call it directly. + void operator()(const tbb::blocked_range<size_t>& range) const; + +private: + const bool mIsMaster; + double* mRand; + IntersectorT mInter; + boost::scoped_ptr<const BaseShader> mShader; + BaseCamera* mCamera; + size_t mSubPixels; +};// LevelSetRayTracer + + +///////////////////////////////VOLUME RENDER /////////////////////////////////////// + +/// @brief A (very) simple multithreaded volume render specifically for scalar density. +/// @details Included primarily as a reference implementation. +/// @note It will only compile if the IntersectorT is templated on a Grid with a +/// floating-point voxel type. +template <typename IntersectorT, typename SamplerT = tools::BoxSampler> +class VolumeRender +{ +public: + + typedef typename IntersectorT::GridType GridType; + typedef typename IntersectorT::RayType RayType; + typedef typename GridType::ValueType ValueType; + typedef typename GridType::ConstAccessor AccessorType; + typedef tools::GridSampler<AccessorType, SamplerT> SamplerType; + BOOST_STATIC_ASSERT(boost::is_floating_point<ValueType>::value); + + /// @brief Constructor taking an intersector and a base camera. + VolumeRender(const IntersectorT& inter, BaseCamera& camera); + + /// @brief Copy constructor which creates a thread-safe clone + VolumeRender(const VolumeRender& other); + + /// @brief Perform the actual (potentially multithreaded) volume rendering. + void render(bool threaded=true) const; + + /// @brief Set the camera derived from the abstract BaseCamera class. + void setCamera(BaseCamera& camera) { mCamera = &camera; } + + /// @brief Set the intersector that performs the actual + /// intersection of the rays against the volume. + void setIntersector(const IntersectorT& inter); + + /// @brief Set the vector components of a directional light source + /// @throw ArithmeticError if input is a null vector. + void setLightDir(Real x, Real y, Real z) { mLightDir = Vec3R(x,y,z).unit(); } + + /// @brief Set the color of the direcitonal light source. + void setLightColor(Real r, Real g, Real b) { mLightColor = Vec3R(r,g,b); } + + /// @brief Set the integration step-size in voxel units for the primay ray. + void setPrimaryStep(Real primaryStep) { mPrimaryStep = primaryStep; } + + /// @brief Set the integration step-size in voxel units for the primay ray. + void setShadowStep(Real shadowStep) { mShadowStep = shadowStep; } + + /// @brief Set Scattering coefficients. + void setScattering(Real x, Real y, Real z) { mScattering = Vec3R(x,y,z); } + + /// @brief Set absorption coefficients. + void setAbsorption(Real x, Real y, Real z) { mAbsorption = Vec3R(x,y,z); } + + /// @brief Set parameter that imitates multi-scattering. A value + /// of zero implies no multi-scattering. + void setLightGain(Real gain) { mLightGain = gain; } + + /// @brief Set the cut-off value for density and transmittance. + void setCutOff(Real cutOff) { mCutOff = cutOff; } + + /// @brief Print parameters, statistics, memory usage and other information. + /// @param os a stream to which to write textual information + /// @param verboseLevel 1: print parameters only; 2: include grid + /// statistics; 3: include memory usage + void print(std::ostream& os = std::cout, int verboseLevel = 1); + + /// @brief Public method required by tbb::parallel_for. + /// @warning Never call it directly. + void operator()(const tbb::blocked_range<size_t>& range) const; + +private: + + AccessorType mAccessor; + BaseCamera* mCamera; + boost::scoped_ptr<IntersectorT> mPrimary, mShadow; + Real mPrimaryStep, mShadowStep, mCutOff, mLightGain; + Vec3R mLightDir, mLightColor, mAbsorption, mScattering; +};//VolumeRender + +//////////////////////////////////////// FILM //////////////////////////////////////// + +/// @brief A simple class that allows for concurrent writes to pixels in an image, +/// background initialization of the image, and PPM or EXR file output. +class Film +{ +public: + /// @brief Floating-point RGBA components in the range [0, 1]. + /// @details This is our preferred representation for color processing. + struct RGBA + { + typedef float ValueT; + + RGBA() : r(0), g(0), b(0), a(1) {} + explicit RGBA(ValueT intensity) : r(intensity), g(intensity), b(intensity), a(1) {} + RGBA(ValueT _r, ValueT _g, ValueT _b, ValueT _a=1.0f) : r(_r), g(_g), b(_b), a(_a) {} + + RGBA operator* (ValueT scale) const { return RGBA(r*scale, g*scale, b*scale);} + RGBA operator+ (const RGBA& rhs) const { return RGBA(r+rhs.r, g+rhs.g, b+rhs.b);} + RGBA operator* (const RGBA& rhs) const { return RGBA(r*rhs.r, g*rhs.g, b*rhs.b);} + RGBA& operator+=(const RGBA& rhs) { r+=rhs.r; g+=rhs.g; b+=rhs.b, a+=rhs.a; return *this;} + + void over(const RGBA& rhs) + { + const float s = rhs.a*(1.0f-a); + r = a*r+s*rhs.r; + g = a*g+s*rhs.g; + b = a*b+s*rhs.b; + a = a + s; + } + + ValueT r, g, b, a; + }; + + + Film(size_t width, size_t height) + : mWidth(width), mHeight(height), mSize(width*height), mPixels(new RGBA[mSize]) + { + } + Film(size_t width, size_t height, const RGBA& bg) + : mWidth(width), mHeight(height), mSize(width*height), mPixels(new RGBA[mSize]) + { + this->fill(bg); + } + + const RGBA& pixel(size_t w, size_t h) const + { + assert(w < mWidth); + assert(h < mHeight); + return mPixels[w + h*mWidth]; + } + + RGBA& pixel(size_t w, size_t h) + { + assert(w < mWidth); + assert(h < mHeight); + return mPixels[w + h*mWidth]; + } + + void fill(const RGBA& rgb=RGBA(0)) { for (size_t i=0; i<mSize; ++i) mPixels[i] = rgb; } + void checkerboard(const RGBA& c1=RGBA(0.3f), const RGBA& c2=RGBA(0.6f), size_t size=32) + { + RGBA *p = mPixels.get(); + for (size_t j = 0; j < mHeight; ++j) { + for (size_t i = 0; i < mWidth; ++i, ++p) { + *p = ((i & size) ^ (j & size)) ? c1 : c2; + } + } + } + + void savePPM(const std::string& fileName) + { + std::string name(fileName + ".ppm"); + boost::scoped_array<unsigned char> buffer(new unsigned char[3*mSize]); + unsigned char *tmp = buffer.get(), *q = tmp; + RGBA* p = mPixels.get(); + size_t n = mSize; + while (n--) { + *q++ = static_cast<unsigned char>(255.0f*(*p ).r); + *q++ = static_cast<unsigned char>(255.0f*(*p ).g); + *q++ = static_cast<unsigned char>(255.0f*(*p++).b); + } + + std::ofstream os(name.c_str(), std::ios_base::binary); + if (!os.is_open()) { + std::cerr << "Error opening PPM file \"" << name << "\"" << std::endl; + return; + } + + os << "P6\n" << mWidth << " " << mHeight << "\n255\n"; + os.write((const char *)&(*tmp), 3*mSize*sizeof(unsigned char)); + } + +#ifdef OPENVDB_TOOLS_RAYTRACER_USE_EXR + void saveEXR(const std::string& fileName, size_t compression = 2, size_t threads = 8) + { + std::string name(fileName + ".exr"); + + if (threads>0) Imf::setGlobalThreadCount(threads); + Imf::Header header(mWidth, mHeight); + if (compression==0) header.compression() = Imf::NO_COMPRESSION; + if (compression==1) header.compression() = Imf::RLE_COMPRESSION; + if (compression>=2) header.compression() = Imf::ZIP_COMPRESSION; + header.channels().insert("R", Imf::Channel(Imf::FLOAT)); + header.channels().insert("G", Imf::Channel(Imf::FLOAT)); + header.channels().insert("B", Imf::Channel(Imf::FLOAT)); + header.channels().insert("A", Imf::Channel(Imf::FLOAT)); + + Imf::FrameBuffer framebuffer; + framebuffer.insert("R", Imf::Slice( Imf::FLOAT, (char *) &(mPixels[0].r), + sizeof (RGBA), sizeof (RGBA) * mWidth)); + framebuffer.insert("G", Imf::Slice( Imf::FLOAT, (char *) &(mPixels[0].g), + sizeof (RGBA), sizeof (RGBA) * mWidth)); + framebuffer.insert("B", Imf::Slice( Imf::FLOAT, (char *) &(mPixels[0].b), + sizeof (RGBA), sizeof (RGBA) * mWidth)); + framebuffer.insert("A", Imf::Slice( Imf::FLOAT, (char *) &(mPixels[0].a), + sizeof (RGBA), sizeof (RGBA) * mWidth)); + + Imf::OutputFile file(name.c_str(), header); + file.setFrameBuffer(framebuffer); + file.writePixels(mHeight); + } +#endif + + size_t width() const { return mWidth; } + size_t height() const { return mHeight; } + size_t numPixels() const { return mSize; } + const RGBA* pixels() const { return mPixels.get(); } + +private: + size_t mWidth, mHeight, mSize; + boost::scoped_array<RGBA> mPixels; +};// Film + + +//////////////////////////////////////// CAMERAS //////////////////////////////////////// + +/// Abstract base class for the perspective and orthographic cameras +class BaseCamera +{ +public: + BaseCamera(Film& film, const Vec3R& rotation, const Vec3R& translation, + double frameWidth, double nearPlane, double farPlane) + : mFilm(&film) + , mScaleWidth(frameWidth) + , mScaleHeight(frameWidth*film.height()/double(film.width())) + { + assert(nearPlane > 0 && farPlane > nearPlane); + mScreenToWorld.accumPostRotation(math::X_AXIS, rotation[0] * M_PI / 180.0); + mScreenToWorld.accumPostRotation(math::Y_AXIS, rotation[1] * M_PI / 180.0); + mScreenToWorld.accumPostRotation(math::Z_AXIS, rotation[2] * M_PI / 180.0); + mScreenToWorld.accumPostTranslation(translation); + this->initRay(nearPlane, farPlane); + } + + virtual ~BaseCamera() {} + + Film::RGBA& pixel(size_t i, size_t j) { return mFilm->pixel(i, j); } + + size_t width() const { return mFilm->width(); } + size_t height() const { return mFilm->height(); } + + /// Rotate the camera so its negative z-axis points at xyz and its + /// y axis is in the plane of the xyz and up vectors. In other + /// words the camera will look at xyz and use up as the + /// horizontal direction. + void lookAt(const Vec3R& xyz, const Vec3R& up = Vec3R(0.0, 1.0, 0.0)) + { + const Vec3R orig = mScreenToWorld.applyMap(Vec3R(0.0)); + const Vec3R dir = orig - xyz; + try { + Mat4d xform = math::aim<Mat4d>(dir, up); + xform.postTranslate(orig); + mScreenToWorld = math::AffineMap(xform); + this->initRay(mRay.t0(), mRay.t1()); + } catch (...) {} + } + + Vec3R rasterToScreen(double i, double j, double z) const + { + return Vec3R( (2 * i / mFilm->width() - 1) * mScaleWidth, + (1 - 2 * j / mFilm->height()) * mScaleHeight, z ); + } + + /// @brief Return a Ray in world space given the pixel indices and + /// optional offsets in the range [0, 1]. An offset of 0.5 corresponds + /// to the center of the pixel. + virtual math::Ray<double> getRay( + size_t i, size_t j, double iOffset = 0.5, double jOffset = 0.5) const = 0; + +protected: + void initRay(double t0, double t1) + { + mRay.setTimes(t0, t1); + mRay.setEye(mScreenToWorld.applyMap(Vec3R(0.0))); + mRay.setDir(mScreenToWorld.applyJacobian(Vec3R(0.0, 0.0, -1.0))); + } + + Film* mFilm; + double mScaleWidth, mScaleHeight; + math::Ray<double> mRay; + math::AffineMap mScreenToWorld; +};// BaseCamera + + +class PerspectiveCamera: public BaseCamera +{ + public: + /// @brief Constructor + /// @param film film (i.e. image) defining the pixel resolution + /// @param rotation rotation in degrees of the camera in world space + /// (applied in x, y, z order) + /// @param translation translation of the camera in world-space units, + /// applied after rotation + /// @param focalLength focal length of the camera in mm + /// (the default of 50mm corresponds to Houdini's default camera) + /// @param aperture width in mm of the frame, i.e., the visible field + /// (the default 41.2136 mm corresponds to Houdini's default camera) + /// @param nearPlane depth of the near clipping plane in world-space units + /// @param farPlane depth of the far clipping plane in world-space units + /// + /// @details If no rotation or translation is provided, the camera is placed + /// at (0,0,0) in world space and points in the direction of the negative z axis. + PerspectiveCamera(Film& film, + const Vec3R& rotation = Vec3R(0.0), + const Vec3R& translation = Vec3R(0.0), + double focalLength = 50.0, + double aperture = 41.2136, + double nearPlane = 1e-3, + double farPlane = std::numeric_limits<double>::max()) + : BaseCamera(film, rotation, translation, 0.5*aperture/focalLength, nearPlane, farPlane) + { + } + + virtual ~PerspectiveCamera() {} + + /// @brief Return a Ray in world space given the pixel indices and + /// optional offsets in the range [0,1]. An offset of 0.5 corresponds + /// to the center of the pixel. + virtual math::Ray<double> getRay( + size_t i, size_t j, double iOffset = 0.5, double jOffset = 0.5) const + { + math::Ray<double> ray(mRay); + Vec3R dir = BaseCamera::rasterToScreen(i + iOffset, j + jOffset, -1.0); + dir = BaseCamera::mScreenToWorld.applyJacobian(dir); + dir.normalize(); + ray.scaleTimes(1.0/dir.dot(ray.dir())); + ray.setDir(dir); + return ray; + } + + /// @brief Return the horizontal field of view in degrees given a + /// focal lenth in mm and the specified aperture in mm. + static double focalLengthToFieldOfView(double length, double aperture) + { + return 360.0 / M_PI * atan(aperture/(2.0*length)); + } + /// @brief Return the focal length in mm given a horizontal field of + /// view in degrees and the specified aperture in mm. + static double fieldOfViewToFocalLength(double fov, double aperture) + { + return aperture/(2.0*(tan(fov * M_PI / 360.0))); + } +};// PerspectiveCamera + + +class OrthographicCamera: public BaseCamera +{ +public: + /// @brief Constructor + /// @param film film (i.e. image) defining the pixel resolution + /// @param rotation rotation in degrees of the camera in world space + /// (applied in x, y, z order) + /// @param translation translation of the camera in world-space units, + /// applied after rotation + /// @param frameWidth width in of the frame in world-space units + /// @param nearPlane depth of the near clipping plane in world-space units + /// @param farPlane depth of the far clipping plane in world-space units + /// + /// @details If no rotation or translation is provided, the camera is placed + /// at (0,0,0) in world space and points in the direction of the negative z axis. + OrthographicCamera(Film& film, + const Vec3R& rotation = Vec3R(0.0), + const Vec3R& translation = Vec3R(0.0), + double frameWidth = 1.0, + double nearPlane = 1e-3, + double farPlane = std::numeric_limits<double>::max()) + : BaseCamera(film, rotation, translation, 0.5*frameWidth, nearPlane, farPlane) + { + } + virtual ~OrthographicCamera() {} + + virtual math::Ray<double> getRay( + size_t i, size_t j, double iOffset = 0.5, double jOffset = 0.5) const + { + math::Ray<double> ray(mRay); + Vec3R eye = BaseCamera::rasterToScreen(i + iOffset, j + jOffset, 0.0); + ray.setEye(BaseCamera::mScreenToWorld.applyMap(eye)); + return ray; + } +};// OrthographicCamera + + +//////////////////////////////////////// SHADERS //////////////////////////////////////// + + +/// Abstract base class for the shaders +class BaseShader +{ +public: + typedef math::Ray<Real> RayT; + BaseShader() {} + virtual ~BaseShader() {} + /// @brief Defines the interface of the virtual function that returns a RGB color. + /// @param xyz World position of the intersection point. + /// @param nml Normal in world space at the intersection point. + /// @param dir Direction of the ray in world space. + virtual Film::RGBA operator()(const Vec3R& xyz, const Vec3R& nml, const Vec3R& dir) const = 0; + virtual BaseShader* copy() const = 0; +}; + + +/// Shader that produces a simple matte +class MatteShader: public BaseShader +{ +public: + MatteShader(const Film::RGBA& c = Film::RGBA(1.0f)): mRGBA(c) {} + virtual ~MatteShader() {} + virtual Film::RGBA operator()(const Vec3R&, const Vec3R&, const Vec3R&) const + { + return mRGBA; + } + virtual BaseShader* copy() const { return new MatteShader(*this); } + +private: + const Film::RGBA mRGBA; +}; + + +/// Color shader that treats the surface normal (x, y, z) as an RGB color +class NormalShader: public BaseShader +{ +public: + NormalShader(const Film::RGBA& c = Film::RGBA(1.0f)) : mRGBA(c*0.5f) {} + virtual ~NormalShader() {} + virtual Film::RGBA operator()(const Vec3R&, const Vec3R& normal, const Vec3R&) const + { + return mRGBA*Film::RGBA(normal[0]+1.0f, normal[1]+1.0f, normal[2]+1.0f); + } + virtual BaseShader* copy() const { return new NormalShader(*this); } + +private: + const Film::RGBA mRGBA; +}; + + +/// Color shader that treats position (x, y, z) as an RGB color in a +/// cube defined from an axis-aligned bounding box in world space. +class PositionShader: public BaseShader +{ +public: + PositionShader(const math::BBox<Vec3R>& bbox, const Film::RGBA& c = Film::RGBA(1.0f)) + : mMin(bbox.min()), mInvDim(1.0/bbox.extents()), mRGBA(c) {} + virtual ~PositionShader() {} + virtual Film::RGBA operator()(const Vec3R& xyz, const Vec3R&, const Vec3R&) const + { + const Vec3R rgb = (xyz - mMin)*mInvDim; + return mRGBA*Film::RGBA(rgb[0], rgb[1], rgb[2]); + } + virtual BaseShader* copy() const { return new PositionShader(*this); } + + private: + const Vec3R mMin, mInvDim; + const Film::RGBA mRGBA; +}; + +/// @brief Simple diffuse Lambertian surface shader +/// @details Diffuse simply means the color is constant (e.g., white), and +/// Lambertian implies that the (radiant) intensity is directly proportional +/// to the cosine of the angle between the surface normal and the direction +/// of the light source. +class DiffuseShader: public BaseShader +{ +public: + DiffuseShader(const Film::RGBA& d = Film::RGBA(1.0f)): mRGBA(d) {} + virtual Film::RGBA operator()(const Vec3R&, const Vec3R& normal, const Vec3R& rayDir) const + { + // We assume a single directional light source at the camera, + // so the cosine of the angle between the surface normal and the + // direction of the light source becomes the dot product of the + // surface normal and inverse direction of the ray. We also ignore + // negative dot products, corresponding to strict one-sided shading. + //return mRGBA * math::Max(0.0, normal.dot(-rayDir)); + + // We take the abs of the dot product corresponding to having + // light sources at +/- rayDir, i.e., two-sided shading. + return mRGBA * math::Abs(normal.dot(rayDir)); + } + virtual BaseShader* copy() const { return new DiffuseShader(*this); } + +private: + const Film::RGBA mRGBA; +}; + + +//////////////////////////////////////// RAYTRACER //////////////////////////////////////// + +template<typename GridT> +inline void rayTrace(const GridT& grid, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples, + unsigned int seed, + bool threaded) +{ + LevelSetRayTracer<GridT, tools::LevelSetRayIntersector<GridT> > + tracer(grid, shader, camera, pixelSamples, seed); + tracer.render(threaded); +} + + +template<typename GridT, typename IntersectorT> +inline void rayTrace(const GridT&, + const IntersectorT& inter, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples, + unsigned int seed, + bool threaded) +{ + LevelSetRayTracer<GridT, IntersectorT> tracer(inter, shader, camera, pixelSamples, seed); + tracer.render(threaded); +} + + +//////////////////////////////////////// LevelSetRayTracer //////////////////////////////////////// + + +template<typename GridT, typename IntersectorT> +inline LevelSetRayTracer<GridT, IntersectorT>:: +LevelSetRayTracer(const GridT& grid, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples, + unsigned int seed) + : mIsMaster(true), + mRand(NULL), + mInter(grid), + mShader(shader.copy()), + mCamera(&camera) +{ + this->setPixelSamples(pixelSamples, seed); +} + +template<typename GridT, typename IntersectorT> +inline LevelSetRayTracer<GridT, IntersectorT>:: +LevelSetRayTracer(const IntersectorT& inter, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples, + unsigned int seed) + : mIsMaster(true), + mRand(NULL), + mInter(inter), + mShader(shader.copy()), + mCamera(&camera) +{ + this->setPixelSamples(pixelSamples, seed); +} + +template<typename GridT, typename IntersectorT> +inline LevelSetRayTracer<GridT, IntersectorT>:: +LevelSetRayTracer(const LevelSetRayTracer& other) : + mIsMaster(false), + mRand(other.mRand), + mInter(other.mInter), + mShader(other.mShader->copy()), + mCamera(other.mCamera), + mSubPixels(other.mSubPixels) +{ +} + +template<typename GridT, typename IntersectorT> +inline LevelSetRayTracer<GridT, IntersectorT>:: +~LevelSetRayTracer() +{ + if (mIsMaster) delete [] mRand; +} + +template<typename GridT, typename IntersectorT> +inline void LevelSetRayTracer<GridT, IntersectorT>:: +setGrid(const GridT& grid) +{ + assert(mIsMaster); + mInter = IntersectorT(grid); +} + +template<typename GridT, typename IntersectorT> +inline void LevelSetRayTracer<GridT, IntersectorT>:: +setIntersector(const IntersectorT& inter) +{ + assert(mIsMaster); + mInter = inter; +} + +template<typename GridT, typename IntersectorT> +inline void LevelSetRayTracer<GridT, IntersectorT>:: +setShader(const BaseShader& shader) +{ + assert(mIsMaster); + mShader.reset(shader.copy()); +} + +template<typename GridT, typename IntersectorT> +inline void LevelSetRayTracer<GridT, IntersectorT>:: +setCamera(BaseCamera& camera) +{ + assert(mIsMaster); + mCamera = &camera; +} + +template<typename GridT, typename IntersectorT> +inline void LevelSetRayTracer<GridT, IntersectorT>:: +setPixelSamples(size_t pixelSamples, unsigned int seed) +{ + assert(mIsMaster); + if (pixelSamples == 0) { + OPENVDB_THROW(ValueError, "pixelSamples must be larger then zero!"); + } + mSubPixels = pixelSamples - 1; + delete [] mRand; + if (mSubPixels > 0) { + mRand = new double[16]; + math::Rand01<double> rand(seed);//offsets for anti-aliaing by jittered super-sampling + for (size_t i=0; i<16; ++i) mRand[i] = rand(); + } else { + mRand = NULL; + } +} + +template<typename GridT, typename IntersectorT> +inline void LevelSetRayTracer<GridT, IntersectorT>:: +render(bool threaded) const +{ + tbb::blocked_range<size_t> range(0, mCamera->height()); + threaded ? tbb::parallel_for(range, *this) : (*this)(range); +} + +template<typename GridT, typename IntersectorT> +inline void LevelSetRayTracer<GridT, IntersectorT>:: +operator()(const tbb::blocked_range<size_t>& range) const +{ + const BaseShader& shader = *mShader; + Vec3Type xyz, nml; + const float frac = 1.0f / (1.0f + mSubPixels); + for (size_t j=range.begin(), n=0, je = range.end(); j<je; ++j) { + for (size_t i=0, ie = mCamera->width(); i<ie; ++i) { + Film::RGBA& bg = mCamera->pixel(i,j); + RayType ray = mCamera->getRay(i, j);//primary ray + Film::RGBA c = mInter.intersectsWS(ray, xyz, nml) ? shader(xyz, nml, ray.dir()) : bg; + for (size_t k=0; k<mSubPixels; ++k, n +=2 ) { + ray = mCamera->getRay(i, j, mRand[n & 15], mRand[(n+1) & 15]); + c += mInter.intersectsWS(ray, xyz, nml) ? shader(xyz, nml, ray.dir()) : bg; + }//loop over sub-pixels + bg = c*frac; + }//loop over image height + }//loop over image width +} + +//////////////////////////////////////// VolumeRender //////////////////////////////////////// + +template<typename IntersectorT, typename SampleT> +inline VolumeRender<IntersectorT, SampleT>:: +VolumeRender(const IntersectorT& inter, BaseCamera& camera) + : mAccessor(inter.grid().getConstAccessor()) + , mCamera(&camera) + , mPrimary(new IntersectorT(inter)) + , mShadow(new IntersectorT(inter)) + , mPrimaryStep(1.0) + , mShadowStep(3.0) + , mCutOff(0.005) + , mLightGain(0.2) + , mLightDir(Vec3R(0.3, 0.3, 0).unit()) + , mLightColor(0.7, 0.7, 0.7) + , mAbsorption(0.1) + , mScattering(1.5) +{ +} + +template<typename IntersectorT, typename SampleT> +inline VolumeRender<IntersectorT, SampleT>:: +VolumeRender(const VolumeRender& other) + : mAccessor(other.mAccessor) + , mCamera(other.mCamera) + , mPrimary(new IntersectorT(*(other.mPrimary))) + , mShadow(new IntersectorT(*(other.mShadow))) + , mPrimaryStep(other.mPrimaryStep) + , mShadowStep(other.mShadowStep) + , mCutOff(other.mCutOff) + , mLightGain(other.mLightGain) + , mLightDir(other.mLightDir) + , mLightColor(other.mLightColor) + , mAbsorption(other.mAbsorption) + , mScattering(other.mScattering) +{ +} + +template<typename IntersectorT, typename SampleT> +inline void VolumeRender<IntersectorT, SampleT>:: +print(std::ostream& os, int verboseLevel) +{ + if (verboseLevel>0) { + os << "\nPrimary step: " << mPrimaryStep + << "\nShadow step: " << mShadowStep + << "\nCutoff: " << mCutOff + << "\nLightGain: " << mLightGain + << "\nLightDir: " << mLightDir + << "\nLightColor: " << mLightColor + << "\nAbsorption: " << mAbsorption + << "\nScattering: " << mScattering << std::endl; + } + mPrimary->print(os, verboseLevel); +} + +template<typename IntersectorT, typename SampleT> +inline void VolumeRender<IntersectorT, SampleT>:: +setIntersector(const IntersectorT& inter) +{ + mPrimary.reset(new IntersectorT(inter)); + mShadow.reset( new IntersectorT(inter)); +} + +template<typename IntersectorT, typename SampleT> +inline void VolumeRender<IntersectorT, SampleT>:: +render(bool threaded) const +{ + tbb::blocked_range<size_t> range(0, mCamera->height()); + threaded ? tbb::parallel_for(range, *this) : (*this)(range); +} + +template<typename IntersectorT, typename SampleT> +inline void VolumeRender<IntersectorT, SampleT>:: +operator()(const tbb::blocked_range<size_t>& range) const +{ + SamplerType sampler(mAccessor, mShadow->grid().transform());//light-weight wrapper + + // Any variable prefixed with p (or s) means it's associate with a primay (or shadow) ray + const Vec3R extinction = -mScattering-mAbsorption, One(1.0); + const Vec3R albedo = mLightColor*mScattering/(mScattering+mAbsorption);//single scattering + const Real sGain = mLightGain;//in-scattering along shadow ray + const Real pStep = mPrimaryStep;//Integration step along primary ray in voxel units + const Real sStep = mShadowStep;//Integration step along shadow ray in voxel units + const Real cutoff = mCutOff;//Cutoff for density and transmittance + + // For the sake of completeness we show how to use two different + // methods (hits/march) in VolumeRayIntersector that produce + // segments along the ray that intersects active values. Comment out + // the line below to use VolumeRayIntersector::march instead of + // VolumeRayIntersector::hits. +#define USE_HITS +#ifdef USE_HITS + std::vector<typename RayType::TimeSpan> pTS, sTS; +#endif + + RayType sRay(Vec3R(0), mLightDir);//Shadow ray + for (size_t j=range.begin(), je = range.end(); j<je; ++j) { + for (size_t i=0, ie = mCamera->width(); i<ie; ++i) { + Film::RGBA& bg = mCamera->pixel(i, j); + bg.a = bg.r = bg.g = bg.b = 0; + RayType pRay = mCamera->getRay(i, j);// Primary ray + if( !mPrimary->setWorldRay(pRay)) continue; + Vec3R pTrans(1.0), pLumi(0.0); +#ifndef USE_HITS + Real pT0, pT1; + while (mPrimary->march(pT0, pT1)) { + for (Real pT = pStep*ceil(pT0/pStep); pT <= pT1; pT += pStep) { +#else + mPrimary->hits(pTS); + for (size_t k=0; k<pTS.size(); ++k) { + Real pT = pStep*ceil(pTS[k].t0/pStep), pT1=pTS[k].t1; + for (; pT <= pT1; pT += pStep) { +#endif + Vec3R pPos = mPrimary->getWorldPos(pT); + const Real density = sampler.wsSample(pPos); + if (density < cutoff) continue; + const Vec3R dT = math::Exp(extinction * density * pStep); + Vec3R sTrans(1.0); + sRay.setEye(pPos); + if( !mShadow->setWorldRay(sRay)) continue; +#ifndef USE_HITS + Real sT0, sT1; + while (mShadow->march(sT0, sT1)) { + for (Real sT = sStep*ceil(sT0/sStep); sT <= sT1; sT+= sStep) { +#else + mShadow->hits(sTS); + for (size_t l=0; l<sTS.size(); ++l) { + Real sT = sStep*ceil(sTS[l].t0/sStep), sT1=sTS[l].t1; + for (; sT <= sT1; sT+= sStep) { +#endif + const Real d = sampler.wsSample(mShadow->getWorldPos(sT)); + if (d < cutoff) continue; + sTrans *= math::Exp(extinction * d * sStep/(1.0+sT*sGain)); + if (sTrans.lengthSqr()<cutoff) goto Luminance;//Terminate sRay + }//Integration over shadow segment + }// Shadow ray march + Luminance: + pLumi += albedo * sTrans * pTrans * (One-dT); + pTrans *= dT; + if (pTrans.lengthSqr()<cutoff) goto Pixel; // Terminate Ray + }//Integration over primary segment + }// Primary ray march + Pixel: + bg.r = pLumi[0]; + bg.g = pLumi[1]; + bg.b = pLumi[2]; + bg.a = 1.0f - pTrans.sum()/3.0f; + }//Horizontal pixel scan + }//Vertical pixel scan +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_RAYTRACER_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/extern/openvdb/internal/openvdb/tools/ValueTransformer.h b/extern/openvdb/internal/openvdb/tools/ValueTransformer.h index 92f0dd085ea..d65b35bf9b8 100644 --- a/extern/openvdb/internal/openvdb/tools/ValueTransformer.h +++ b/extern/openvdb/internal/openvdb/tools/ValueTransformer.h @@ -30,6 +30,8 @@ // /// @file ValueTransformer.h /// +/// @author Peter Cucka +/// /// tools::foreach() and tools::transformValues() transform the values in a grid /// by iterating over the grid with a user-supplied iterator and applying a /// user-supplied functor at each step of the iteration. With tools::foreach(), @@ -45,10 +47,16 @@ /// an additional step is performed: when any two threads finish processing, /// @c op.join(otherOp) is called on one thread's functor to allow it to coalesce /// its intermediate result with the other thread's. +/// +/// Finally, tools::setValueOnMin(), tools::setValueOnMax(), tools::setValueOnSum() +/// and tools::setValueOnMult() are wrappers around Tree::modifyValue() (or +/// ValueAccessor::modifyValue()) for some commmon in-place operations. +/// These are typically significantly faster than calling getValue() followed by setValue(). #ifndef OPENVDB_TOOLS_VALUETRANSFORMER_HAS_BEEN_INCLUDED #define OPENVDB_TOOLS_VALUETRANSFORMER_HAS_BEEN_INCLUDED +#include <algorithm> // for std::min(), std::max() #include <tbb/parallel_for.h> #include <tbb/parallel_reduce.h> #include <openvdb/Types.h> @@ -124,6 +132,7 @@ inline void foreach(const IterT& iter, const XformOp& op, /// @param threaded if true, transform multiple values of the input grid in parallel /// @param shareOp if true and @a threaded is true, all threads use the same functor; /// otherwise, each thread gets its own copy of the @e original functor +/// @param merge how to merge intermediate results from multiple threads (see Types.h) /// /// @par Example: /// Populate a scalar floating-point grid with the lengths of the vectors from all @@ -153,12 +162,14 @@ inline void foreach(const IterT& iter, const XformOp& op, /// with a tree::IteratorRange that wraps a grid or tree iterator. template<typename InIterT, typename OutGridT, typename XformOp> inline void transformValues(const InIterT& inIter, OutGridT& outGrid, - XformOp& op, bool threaded = true, bool shareOp = true); + XformOp& op, bool threaded = true, bool shareOp = true, + MergePolicy merge = MERGE_ACTIVE_STATES); #ifndef _MSC_VER template<typename InIterT, typename OutGridT, typename XformOp> inline void transformValues(const InIterT& inIter, OutGridT& outGrid, - const XformOp& op, bool threaded = true, bool shareOp = true); + const XformOp& op, bool threaded = true, bool shareOp = true, + MergePolicy merge = MERGE_ACTIVE_STATES); #endif @@ -210,6 +221,107 @@ template<typename IterT, typename XformOp> inline void accumulate(const IterT& iter, XformOp& op, bool threaded = true); +/// @brief Set the value of the voxel at the given coordinates in @a tree to +/// the minimum of its current value and @a value, and mark the voxel as active. +/// @details This is typically significantly faster than calling getValue() +/// followed by setValueOn(). +/// @note @a TreeT can be either a Tree or a ValueAccessor. +template<typename TreeT> +inline void setValueOnMin(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value); + +/// @brief Set the value of the voxel at the given coordinates in @a tree to +/// the maximum of its current value and @a value, and mark the voxel as active. +/// @details This is typically significantly faster than calling getValue() +/// followed by setValueOn(). +/// @note @a TreeT can be either a Tree or a ValueAccessor. +template<typename TreeT> +inline void setValueOnMax(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value); + +/// @brief Set the value of the voxel at the given coordinates in @a tree to +/// the sum of its current value and @a value, and mark the voxel as active. +/// @details This is typically significantly faster than calling getValue() +/// followed by setValueOn(). +/// @note @a TreeT can be either a Tree or a ValueAccessor. +template<typename TreeT> +inline void setValueOnSum(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value); + +/// @brief Set the value of the voxel at the given coordinates in @a tree to +/// the product of its current value and @a value, and mark the voxel as active. +/// @details This is typically significantly faster than calling getValue() +/// followed by setValueOn(). +/// @note @a TreeT can be either a Tree or a ValueAccessor. +template<typename TreeT> +inline void setValueOnMult(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value); + + +//////////////////////////////////////// + + +namespace valxform { + +template<typename ValueType> +struct MinOp { + const ValueType val; + MinOp(const ValueType& v): val(v) {} + inline void operator()(ValueType& v) const { v = std::min<ValueType>(v, val); } +}; + +template<typename ValueType> +struct MaxOp { + const ValueType val; + MaxOp(const ValueType& v): val(v) {} + inline void operator()(ValueType& v) const { v = std::max<ValueType>(v, val); } +}; + +template<typename ValueType> +struct SumOp { + const ValueType val; + SumOp(const ValueType& v): val(v) {} + inline void operator()(ValueType& v) const { v += val; } +}; + +template<typename ValueType> +struct MultOp { + const ValueType val; + MultOp(const ValueType& v): val(v) {} + inline void operator()(ValueType& v) const { v *= val; } +}; + +} + + +template<typename TreeT> +inline void +setValueOnMin(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value) +{ + tree.modifyValue(xyz, valxform::MinOp<typename TreeT::ValueType>(value)); +} + + +template<typename TreeT> +inline void +setValueOnMax(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value) +{ + tree.modifyValue(xyz, valxform::MaxOp<typename TreeT::ValueType>(value)); +} + + +template<typename TreeT> +inline void +setValueOnSum(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value) +{ + tree.modifyValue(xyz, valxform::SumOp<typename TreeT::ValueType>(value)); +} + + +template<typename TreeT> +inline void +setValueOnMult(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value) +{ + tree.modifyValue(xyz, valxform::MultOp<typename TreeT::ValueType>(value)); +} + + //////////////////////////////////////// @@ -312,12 +424,13 @@ public: typedef typename tree::IteratorRange<InIterT> IterRange; typedef typename OutTreeT::ValueType OutValueT; - SharedOpTransformer(const InIterT& inIter, OutTreeT& outTree, OpT& op): + SharedOpTransformer(const InIterT& inIter, OutTreeT& outTree, OpT& op, MergePolicy merge): mIsRoot(true), mInputIter(inIter), mInputTree(inIter.getTree()), mOutputTree(&outTree), - mOp(op) + mOp(op), + mMergePolicy(merge) { if (static_cast<const void*>(mInputTree) == static_cast<void*>(mOutputTree)) { OPENVDB_LOG_INFO("use tools::foreach(), not transformValues()," @@ -331,7 +444,8 @@ public: mInputIter(other.mInputIter), mInputTree(other.mInputTree), mOutputTree(new OutTreeT(zeroVal<OutValueT>())), - mOp(other.mOp) + mOp(other.mOp), + mMergePolicy(other.mMergePolicy) {} ~SharedOpTransformer() @@ -372,7 +486,7 @@ public: void join(const SharedOpTransformer& other) { if (mOutputTree && other.mOutputTree) { - mOutputTree->merge(*other.mOutputTree); + mOutputTree->merge(*other.mOutputTree, mMergePolicy); } } @@ -382,6 +496,7 @@ private: const InTreeT* mInputTree; OutTreeT* mOutputTree; OpT& mOp; + MergePolicy mMergePolicy; }; // class SharedOpTransformer @@ -393,13 +508,15 @@ public: typedef typename tree::IteratorRange<InIterT> IterRange; typedef typename OutTreeT::ValueType OutValueT; - CopyableOpTransformer(const InIterT& inIter, OutTreeT& outTree, const OpT& op): + CopyableOpTransformer(const InIterT& inIter, OutTreeT& outTree, + const OpT& op, MergePolicy merge): mIsRoot(true), mInputIter(inIter), mInputTree(inIter.getTree()), mOutputTree(&outTree), mOp(op), - mOrigOp(&op) + mOrigOp(&op), + mMergePolicy(merge) { if (static_cast<const void*>(mInputTree) == static_cast<void*>(mOutputTree)) { OPENVDB_LOG_INFO("use tools::foreach(), not transformValues()," @@ -415,7 +532,8 @@ public: mInputTree(other.mInputTree), mOutputTree(new OutTreeT(zeroVal<OutValueT>())), mOp(*other.mOrigOp), - mOrigOp(other.mOrigOp) + mOrigOp(other.mOrigOp), + mMergePolicy(other.mMergePolicy) {} ~CopyableOpTransformer() @@ -456,7 +574,7 @@ public: void join(const CopyableOpTransformer& other) { if (mOutputTree && other.mOutputTree) { - mOutputTree->merge(*other.mOutputTree); + mOutputTree->merge(*other.mOutputTree, mMergePolicy); } } @@ -467,6 +585,7 @@ private: OutTreeT* mOutputTree; OpT mOp; // copy of original functor OpT const * const mOrigOp; // pointer to original functor + MergePolicy mMergePolicy; }; // class CopyableOpTransformer } // namespace valxform @@ -478,17 +597,17 @@ private: template<typename InIterT, typename OutGridT, typename XformOp> inline void transformValues(const InIterT& inIter, OutGridT& outGrid, XformOp& op, - bool threaded, bool shared) + bool threaded, bool shared, MergePolicy merge) { typedef TreeAdapter<OutGridT> Adapter; typedef typename Adapter::TreeType OutTreeT; if (shared) { typedef typename valxform::SharedOpTransformer<InIterT, OutTreeT, XformOp> Processor; - Processor proc(inIter, Adapter::tree(outGrid), op); + Processor proc(inIter, Adapter::tree(outGrid), op, merge); proc.process(threaded); } else { typedef typename valxform::CopyableOpTransformer<InIterT, OutTreeT, XformOp> Processor; - Processor proc(inIter, Adapter::tree(outGrid), op); + Processor proc(inIter, Adapter::tree(outGrid), op, merge); proc.process(threaded); } } @@ -497,13 +616,13 @@ transformValues(const InIterT& inIter, OutGridT& outGrid, XformOp& op, template<typename InIterT, typename OutGridT, typename XformOp> inline void transformValues(const InIterT& inIter, OutGridT& outGrid, const XformOp& op, - bool threaded, bool /*share*/) + bool threaded, bool /*share*/, MergePolicy merge) { typedef TreeAdapter<OutGridT> Adapter; typedef typename Adapter::TreeType OutTreeT; // Const ops are shared across threads, not copied. typedef typename valxform::SharedOpTransformer<InIterT, OutTreeT, const XformOp> Processor; - Processor proc(inIter, Adapter::tree(outGrid), op); + Processor proc(inIter, Adapter::tree(outGrid), op, merge); proc.process(threaded); } #endif diff --git a/extern/openvdb/internal/openvdb/tools/VectorTransformer.h b/extern/openvdb/internal/openvdb/tools/VectorTransformer.h new file mode 100644 index 00000000000..21fb79ab7f2 --- /dev/null +++ b/extern/openvdb/internal/openvdb/tools/VectorTransformer.h @@ -0,0 +1,158 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +// +// Redistributions of source code must retain the above copyright +// and license notice and the following restrictions and disclaimer. +// +// * Neither the name of DreamWorks Animation nor the names of +// its contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE +// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. +// +/////////////////////////////////////////////////////////////////////////// +// +/// @file VectorTransformer.h + +#ifndef OPENVDB_TOOLS_VECTORTRANSFORMER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_VECTORTRANSFORMER_HAS_BEEN_INCLUDED + +#include <openvdb/Types.h> +#include <openvdb/math/Mat4.h> +#include <openvdb/math/Vec3.h> +#include "ValueTransformer.h" // for tools::foreach() +#include <boost/utility/enable_if.hpp> + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Apply an affine transform to the voxel values of a vector-valued grid +/// in accordance with the grid's vector type (covariant, contravariant, etc.). +/// @throw TypeError if the grid is not vector-valued +template<typename GridType> +inline void +transformVectors(GridType&, const Mat4d&); + + +//////////////////////////////////////// + + +// Functors for use with tools::foreach() to transform vector voxel values + +struct HomogeneousMatMul +{ + const Mat4d mat; + HomogeneousMatMul(const Mat4d& _mat): mat(_mat) {} + template<typename TreeIterT> void operator()(const TreeIterT& it) const + { + Vec3d v(*it); + it.setValue(mat.transformH(v)); + } +}; + +struct MatMul +{ + const Mat4d mat; + MatMul(const Mat4d& _mat): mat(_mat) {} + template<typename TreeIterT> + void operator()(const TreeIterT& it) const + { + Vec3d v(*it); + it.setValue(mat.transform3x3(v)); + } +}; + +struct MatMulNormalize +{ + const Mat4d mat; + MatMulNormalize(const Mat4d& _mat): mat(_mat) {} + template<typename TreeIterT> + void operator()(const TreeIterT& it) const + { + Vec3d v(*it); + v = mat.transform3x3(v); + v.normalize(); + it.setValue(v); + } +}; + + +/// @internal This overload is enabled only for scalar-valued grids. +template<typename GridType> inline +typename boost::disable_if_c<VecTraits<typename GridType::ValueType>::IsVec, void>::type +doTransformVectors(GridType&, const Mat4d&) +{ + OPENVDB_THROW(TypeError, "tools::transformVectors() requires a vector-valued grid"); +} + +/// @internal This overload is enabled only for vector-valued grids. +template<typename GridType> inline +typename boost::enable_if_c<VecTraits<typename GridType::ValueType>::IsVec, void>::type +doTransformVectors(GridType& grid, const Mat4d& mat) +{ + if (!grid.isInWorldSpace()) return; + + const VecType vecType = grid.getVectorType(); + switch (vecType) { + case VEC_COVARIANT: + case VEC_COVARIANT_NORMALIZE: + { + Mat4d invmat = mat.inverse(); + invmat = invmat.transpose(); + + if (vecType == VEC_COVARIANT_NORMALIZE) { + foreach(grid.beginValueAll(), MatMulNormalize(invmat)); + } else { + foreach(grid.beginValueAll(), MatMul(invmat)); + } + break; + } + + case VEC_CONTRAVARIANT_RELATIVE: + foreach(grid.beginValueAll(), MatMul(mat)); + break; + + case VEC_CONTRAVARIANT_ABSOLUTE: + foreach(grid.beginValueAll(), HomogeneousMatMul(mat)); + break; + + case VEC_INVARIANT: + break; + } +} + + +template<typename GridType> +inline void +transformVectors(GridType& grid, const Mat4d& mat) +{ + doTransformVectors<GridType>(grid, mat); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_VECTORTRANSFORMER_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/extern/openvdb/internal/openvdb/tools/VolumeToMesh.h b/extern/openvdb/internal/openvdb/tools/VolumeToMesh.h index 78b4d6c8066..ef0f39c7f17 100644 --- a/extern/openvdb/internal/openvdb/tools/VolumeToMesh.h +++ b/extern/openvdb/internal/openvdb/tools/VolumeToMesh.h @@ -31,25 +31,32 @@ #ifndef OPENVDB_TOOLS_VOLUME_TO_MESH_HAS_BEEN_INCLUDED #define OPENVDB_TOOLS_VOLUME_TO_MESH_HAS_BEEN_INCLUDED +#include <openvdb/Platform.h> // for OPENVDB_HAS_CXX11 #include <openvdb/tree/ValueAccessor.h> #include <openvdb/util/Util.h> // for COORD_OFFSETS #include <openvdb/math/Operators.h> // for ISGradient #include <openvdb/tools/Morphology.h> // for dilateVoxels() +#include <openvdb/tree/LeafManager.h> #include <boost/scoped_array.hpp> #include <boost/scoped_ptr.hpp> - #include <tbb/blocked_range.h> #include <tbb/parallel_for.h> #include <tbb/parallel_reduce.h> #include <vector> +#include <memory> // for auto_ptr/unique_ptr + + +////////// + namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { namespace tools { + //////////////////////////////////////// @@ -94,71 +101,60 @@ volumeToMesh( /// @brief Polygon flags, used for reference based meshing. -enum { - POLYFLAG_EXTERIOR = 0x1, POLYFLAG_FRACTURE_SEAM = 0x2 -}; +enum { POLYFLAG_EXTERIOR = 0x1, POLYFLAG_FRACTURE_SEAM = 0x2, POLYFLAG_SUBDIVIDED = 0x4}; /// @brief Collection of quads and triangles class PolygonPool { public: - PolygonPool() - : mNumQuads(0) - , mNumTriangles(0) - , mQuads(NULL) - , mTriangles(NULL) - , mQuadFlags(NULL) - , mTriangleFlags(NULL) - { - } - void resetQuads(size_t size) - { - mNumQuads = size; - mQuads.reset(new openvdb::Vec4I[mNumQuads]); - mQuadFlags.reset(new char[mNumQuads]); - } + inline PolygonPool(); + inline PolygonPool(const size_t numQuads, const size_t numTriangles); - void clearQuads() - { - mNumQuads = 0; - mQuads.reset(NULL); - mQuadFlags.reset(NULL); - } - void resetTriangles(size_t size) - { - mNumTriangles = size; - mTriangles.reset(new openvdb::Vec3I[mNumTriangles]); - mTriangleFlags.reset(new char[mNumTriangles]); - } + inline void copy(const PolygonPool& rhs); - void clearTriangles() - { - mNumTriangles = 0; - mTriangles.reset(NULL); - mTriangleFlags.reset(NULL); - } + inline void resetQuads(size_t size); + inline void clearQuads(); + + inline void resetTriangles(size_t size); + inline void clearTriangles(); - const size_t& numQuads() const { return mNumQuads; } - const size_t& numTriangles() const { return mNumTriangles; } // polygon accessor methods - openvdb::Vec4I& quad(size_t n) { return mQuads[n]; } - const openvdb::Vec4I& quad(size_t n) const { return mQuads[n]; } - openvdb::Vec3I& triangle(size_t n) { return mTriangles[n]; } - const openvdb::Vec3I& triangle(size_t n) const { return mTriangles[n]; } + const size_t& numQuads() const { return mNumQuads; } + + openvdb::Vec4I& quad(size_t n) { return mQuads[n]; } + const openvdb::Vec4I& quad(size_t n) const { return mQuads[n]; } + + + const size_t& numTriangles() const { return mNumTriangles; } + + openvdb::Vec3I& triangle(size_t n) { return mTriangles[n]; } + const openvdb::Vec3I& triangle(size_t n) const { return mTriangles[n]; } + // polygon flags accessor methods - char& quadFlags(size_t n) { return mQuadFlags[n]; } - const char& quadFlags(size_t n) const { return mQuadFlags[n]; } - char& triangleFlags(size_t n) { return mTriangleFlags[n]; } - const char& triangleFlags(size_t n) const { return mTriangleFlags[n]; } + char& quadFlags(size_t n) { return mQuadFlags[n]; } + const char& quadFlags(size_t n) const { return mQuadFlags[n]; } + + char& triangleFlags(size_t n) { return mTriangleFlags[n]; } + const char& triangleFlags(size_t n) const { return mTriangleFlags[n]; } + + + // reduce the polygon containers, n has to + // be smaller than the current container size. + + inline bool trimQuads(const size_t n, bool reallocate = false); + inline bool trimTrinagles(const size_t n, bool reallocate = false); private: + // disallow copy by assignment + void operator=(const PolygonPool&) {} + size_t mNumQuads, mNumTriangles; boost::scoped_array<openvdb::Vec4I> mQuads; boost::scoped_array<openvdb::Vec3I> mTriangles; @@ -180,23 +176,39 @@ typedef boost::scoped_array<PolygonPool> PolygonPoolList; class VolumeToMesh { public: + /// @param isovalue Determines which isosurface to mesh. /// @param adaptivity Adaptivity threshold [0 to 1] VolumeToMesh(double isovalue = 0, double adaptivity = 0); - PointList& pointList(); + + ////////// + + // Mesh data accessors + const size_t& pointListSize() const; + PointList& pointList(); + const size_t& polygonPoolListSize() const; PolygonPoolList& polygonPoolList(); const PolygonPoolList& polygonPoolList() const; - const size_t& polygonPoolListSize() const; - /// @brief main call + std::vector<unsigned char>& pointFlags(); + const std::vector<unsigned char>& pointFlags() const; + + + ////////// + + + /// @brief Main call /// @note Call with scalar typed grid. template<typename GridT> void operator()(const GridT&); + ////////// + + /// @brief When surfacing fractured SDF fragments, the original unfractured /// SDF grid can be used to eliminate seam lines and tag polygons that are /// coincident with the reference surface with the @c POLYFLAG_EXTERIOR @@ -218,93 +230,452 @@ public: /// that do not exist in the reference grid. (Parts of the /// fragment surface that are not coincident with the /// reference surface.) - /// @param smoothSeams toggle to smooth seam line edges during mesh extraction, - /// removes staircase artifacts. - void setRefGrid(const GridBase::ConstPtr& grid, double secAdaptivity = 0, bool smoothSeams = true); + void setRefGrid(const GridBase::ConstPtr& grid, double secAdaptivity = 0); + + + /// @param mask A boolean grid whose active topology defines the region to mesh. + /// @param invertMask Toggle to mesh the complement of the mask. + /// @note The mask's tree configuration has to match @c GridT's tree configuration. + void setSurfaceMask(const GridBase::ConstPtr& mask, bool invertMask = false); + + /// @param grid A scalar grid used as an spatial multiplier for the adaptivity threshold. + /// @note The grid's tree configuration has to match @c GridT's tree configuration. + void setSpatialAdaptivity(const GridBase::ConstPtr& grid); + + + /// @param tree A boolean tree whose active topology defines the adaptivity mask. + /// @note The tree configuration has to match @c GridT's tree configuration. + void setAdaptivityMask(const TreeBase::ConstPtr& tree); + + + /// @brief Subdivide volume and mesh into disjoint parts + /// @param partitions Number of partitions. + /// @param activePart Specific partition to mesh, 0 to @c partitions - 1. + void partition(unsigned partitions = 1, unsigned activePart = 0); private: PointList mPoints; PolygonPoolList mPolygons; - size_t mPointListSize, mPolygonPoolListSize; + size_t mPointListSize, mSeamPointListSize, mPolygonPoolListSize; double mIsovalue, mPrimAdaptivity, mSecAdaptivity; - GridBase::ConstPtr mRefGrid; - TreeBase::Ptr mRefEdgeTree, mRefTopologyMaskTree, mSeamPointTree; - bool mSmoothSeams; + GridBase::ConstPtr mRefGrid, mSurfaceMaskGrid, mAdaptivityGrid; + TreeBase::ConstPtr mAdaptivityMaskTree; + + TreeBase::Ptr mRefSignTree, mRefIdxTree; + + bool mInvertSurfaceMask; + unsigned mPartitions, mActivePart; + + boost::scoped_array<uint32_t> mQuantizedSeamPoints; + + std::vector<unsigned char> mPointFlags; }; //////////////////////////////////////// -// Internal utility methods +/// @brief Given a set of tangent elements, @c points with corresponding @c normals, +/// this method returns the intersection point of all tangent elements. +/// +/// @note Used to extract surfaces with sharp edges and corners from volume data, +/// see the following paper for details: "Feature Sensitive Surface +/// Extraction from Volume Data, Kobbelt et al. 2001". +inline Vec3d findFeaturePoint( + const std::vector<Vec3d>& points, + const std::vector<Vec3d>& normals) +{ + typedef math::Mat3d Mat3d; + + Vec3d avgPos(0.0); + + if (points.empty()) return avgPos; + + for (size_t n = 0, N = points.size(); n < N; ++n) { + avgPos += points[n]; + } + + avgPos /= double(points.size()); + + // Unique components of the 3x3 A^TA matrix, where A is + // the matrix of normals. + double m00=0,m01=0,m02=0, + m11=0,m12=0, + m22=0; + + // The rhs vector, A^Tb, where b = n dot p + Vec3d rhs(0.0); + + for (size_t n = 0, N = points.size(); n < N; ++n) { + + const Vec3d& n_ref = normals[n]; + + // A^TA + m00 += n_ref[0] * n_ref[0]; // diagonal + m11 += n_ref[1] * n_ref[1]; + m22 += n_ref[2] * n_ref[2]; + + m01 += n_ref[0] * n_ref[1]; // Upper-tri + m02 += n_ref[0] * n_ref[2]; + m12 += n_ref[1] * n_ref[2]; + + // A^Tb (centered around the origin) + rhs += n_ref * n_ref.dot(points[n] - avgPos); + } + + Mat3d A(m00,m01,m02, + m01,m11,m12, + m02,m12,m22); + /* + // Inverse + const double det = A.det(); + if (det > 0.01) { + Mat3d A_inv = A.adjoint(); + A_inv *= (1.0 / det); + return avgPos + A_inv * rhs; + } + */ + + // Compute the pseudo inverse + + math::Mat3d eigenVectors; + Vec3d eigenValues; + + diagonalizeSymmetricMatrix(A, eigenVectors, eigenValues, 300); + + Mat3d D = Mat3d::identity(); + + + double tolerance = std::max(std::abs(eigenValues[0]), std::abs(eigenValues[1])); + tolerance = std::max(tolerance, std::abs(eigenValues[2])); + tolerance *= 0.01; + + int clamped = 0; + for (int i = 0; i < 3; ++i ) { + if (std::abs(eigenValues[i]) < tolerance) { + D[i][i] = 0.0; + ++clamped; + } else { + D[i][i] = 1.0 / eigenValues[i]; + } + } + + // Assemble the pseudo inverse and calc. the intersection point + if (clamped < 3) { + Mat3d pseudoInv = eigenVectors * D * eigenVectors.transpose(); + return avgPos + pseudoInv * rhs; + } + + return avgPos; +} + + +//////////////////////////////////////// + + +// Internal utility methods namespace internal { +template<typename T> +struct UniquePtr +{ +#ifdef OPENVDB_HAS_CXX11 + typedef std::unique_ptr<T> type; +#else + typedef std::auto_ptr<T> type; +#endif +}; + -// Bit-flags -enum { INSIDE = 0x1, XEDGE = 0x2, YEDGE = 0x4, ZEDGE = 0x8 }; - -const bool sAmbiguous[256] = - {0,0,0,0,0,1,0,0, - 0,0,1,0,0,0,0,0, - 0,0,1,0,1,1,1,0, - 1,0,1,0,1,0,1,0, - 0,1,0,0,1,1,0,0, - 1,1,1,0,1,1,0,0, - 0,0,0,0,1,1,0,0, - 1,0,1,0,1,1,1,0, - 0,1,1,1,0,1,0,0, - 1,1,1,1,0,0,0,0, - 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, - 0,1,0,0,0,1,0,0, - 1,1,1,1,0,1,0,0, - 0,0,0,0,0,1,0,0, - 1,1,1,1,1,1,1,0, - 0,1,1,1,1,1,1,1, - 0,0,1,0,0,0,0,0, - 0,0,1,0,1,1,1,1, - 0,0,1,0,0,0,1,0, - 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, - 0,0,0,0,1,1,1,1, - 0,0,1,0,1,1,1,0, - 0,1,1,1,0,1,0,1, - 0,0,1,1,0,0,0,0, - 0,0,1,1,0,1,1,1, - 0,0,1,1,0,0,1,0, - 0,1,0,1,0,1,0,1, - 0,1,1,1,0,1,0,0, - 0,0,0,0,0,1,0,0, - 0,0,1,0,0,0,0,0}; +/// @brief Bit-flags used to classify cells. +enum { SIGNS = 0xFF, EDGES = 0xE00, INSIDE = 0x100, + XEDGE = 0x200, YEDGE = 0x400, ZEDGE = 0x800, SEAM = 0x1000}; + + +/// @brief Used to quickly determine if a given cell is adaptable. +const bool sAdaptable[256] = { + 1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,1,1,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1, + 1,0,1,1,0,0,1,1,0,0,0,1,0,0,1,1,1,1,1,1,0,0,1,1,0,1,0,1,0,0,0,1, + 1,0,0,0,1,0,1,1,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,0,1,1,1,0,1,1,0,0,0,0,1,0,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,0,1,0,0,0,0,1,1,0,1,1,1,0,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,1,0,0,0,1, + 1,0,0,0,1,0,1,0,1,1,0,0,1,1,1,1,1,1,0,0,1,0,0,0,1,1,0,0,1,1,0,1, + 1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,1}; + + +/// @brief Contains the ambiguous face index for certain cell configuration. +const unsigned char sAmbiguousFace[256] = { + 0,0,0,0,0,5,0,0,0,0,5,0,0,0,0,0,0,0,1,0,0,5,1,0,4,0,0,0,4,0,0,0, + 0,1,0,0,2,0,0,0,0,1,5,0,2,0,0,0,0,0,0,0,2,0,0,0,4,0,0,0,0,0,0,0, + 0,0,2,2,0,5,0,0,3,3,0,0,0,0,0,0,6,6,0,0,6,0,0,0,0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,4,0,4,3,0,3,0,0,0,5,0,0,0,0,0,0,0,1,0,3,0,0,0,0,0,0,0,0,0,0,0, + 6,0,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + +/// @brief Lookup table for different cell sign configurations. The first entry specifies +/// the total number of points that need to be generated inside a cell and the +/// remaining 12 entries indicate different edge groups. +const unsigned char sEdgeGroupTable[256][13] = { + {0,0,0,0,0,0,0,0,0,0,0,0,0},{1,1,0,0,1,0,0,0,0,1,0,0,0},{1,1,1,0,0,0,0,0,0,0,1,0,0}, + {1,0,1,0,1,0,0,0,0,1,1,0,0},{1,0,1,1,0,0,0,0,0,0,0,1,0},{1,1,1,1,1,0,0,0,0,1,0,1,0}, + {1,1,0,1,0,0,0,0,0,0,1,1,0},{1,0,0,1,1,0,0,0,0,1,1,1,0},{1,0,0,1,1,0,0,0,0,0,0,0,1}, + {1,1,0,1,0,0,0,0,0,1,0,0,1},{1,1,1,1,1,0,0,0,0,0,1,0,1},{1,0,1,1,0,0,0,0,0,1,1,0,1}, + {1,0,1,0,1,0,0,0,0,0,0,1,1},{1,1,1,0,0,0,0,0,0,1,0,1,1},{1,1,0,0,1,0,0,0,0,0,1,1,1}, + {1,0,0,0,0,0,0,0,0,1,1,1,1},{1,0,0,0,0,1,0,0,1,1,0,0,0},{1,1,0,0,1,1,0,0,1,0,0,0,0}, + {1,1,1,0,0,1,0,0,1,1,1,0,0},{1,0,1,0,1,1,0,0,1,0,1,0,0},{2,0,1,1,0,2,0,0,2,2,0,1,0}, + {1,1,1,1,1,1,0,0,1,0,0,1,0},{1,1,0,1,0,1,0,0,1,1,1,1,0},{1,0,0,1,1,1,0,0,1,0,1,1,0}, + {1,0,0,1,1,1,0,0,1,1,0,0,1},{1,1,0,1,0,1,0,0,1,0,0,0,1},{2,2,1,1,2,1,0,0,1,2,1,0,1}, + {1,0,1,1,0,1,0,0,1,0,1,0,1},{1,0,1,0,1,1,0,0,1,1,0,1,1},{1,1,1,0,0,1,0,0,1,0,0,1,1}, + {2,1,0,0,1,2,0,0,2,1,2,2,2},{1,0,0,0,0,1,0,0,1,0,1,1,1},{1,0,0,0,0,1,1,0,0,0,1,0,0}, + {1,1,0,0,1,1,1,0,0,1,1,0,0},{1,1,1,0,0,1,1,0,0,0,0,0,0},{1,0,1,0,1,1,1,0,0,1,0,0,0}, + {1,0,1,1,0,1,1,0,0,0,1,1,0},{2,2,2,1,1,1,1,0,0,1,2,1,0},{1,1,0,1,0,1,1,0,0,0,0,1,0}, + {1,0,0,1,1,1,1,0,0,1,0,1,0},{2,0,0,2,2,1,1,0,0,0,1,0,2},{1,1,0,1,0,1,1,0,0,1,1,0,1}, + {1,1,1,1,1,1,1,0,0,0,0,0,1},{1,0,1,1,0,1,1,0,0,1,0,0,1},{1,0,1,0,1,1,1,0,0,0,1,1,1}, + {2,1,1,0,0,2,2,0,0,2,1,2,2},{1,1,0,0,1,1,1,0,0,0,0,1,1},{1,0,0,0,0,1,1,0,0,1,0,1,1}, + {1,0,0,0,0,0,1,0,1,1,1,0,0},{1,1,0,0,1,0,1,0,1,0,1,0,0},{1,1,1,0,0,0,1,0,1,1,0,0,0}, + {1,0,1,0,1,0,1,0,1,0,0,0,0},{1,0,1,1,0,0,1,0,1,1,1,1,0},{2,1,1,2,2,0,2,0,2,0,1,2,0}, + {1,1,0,1,0,0,1,0,1,1,0,1,0},{1,0,0,1,1,0,1,0,1,0,0,1,0},{1,0,0,1,1,0,1,0,1,1,1,0,1}, + {1,1,0,1,0,0,1,0,1,0,1,0,1},{2,1,2,2,1,0,2,0,2,1,0,0,2},{1,0,1,1,0,0,1,0,1,0,0,0,1}, + {2,0,2,0,2,0,1,0,1,2,2,1,1},{2,2,2,0,0,0,1,0,1,0,2,1,1},{2,2,0,0,2,0,1,0,1,2,0,1,1}, + {1,0,0,0,0,0,1,0,1,0,0,1,1},{1,0,0,0,0,0,1,1,0,0,0,1,0},{2,1,0,0,1,0,2,2,0,1,0,2,0}, + {1,1,1,0,0,0,1,1,0,0,1,1,0},{1,0,1,0,1,0,1,1,0,1,1,1,0},{1,0,1,1,0,0,1,1,0,0,0,0,0}, + {1,1,1,1,1,0,1,1,0,1,0,0,0},{1,1,0,1,0,0,1,1,0,0,1,0,0},{1,0,0,1,1,0,1,1,0,1,1,0,0}, + {1,0,0,1,1,0,1,1,0,0,0,1,1},{1,1,0,1,0,0,1,1,0,1,0,1,1},{2,1,2,2,1,0,1,1,0,0,1,2,1}, + {2,0,1,1,0,0,2,2,0,2,2,1,2},{1,0,1,0,1,0,1,1,0,0,0,0,1},{1,1,1,0,0,0,1,1,0,1,0,0,1}, + {1,1,0,0,1,0,1,1,0,0,1,0,1},{1,0,0,0,0,0,1,1,0,1,1,0,1},{1,0,0,0,0,1,1,1,1,1,0,1,0}, + {1,1,0,0,1,1,1,1,1,0,0,1,0},{2,1,1,0,0,2,2,1,1,1,2,1,0},{2,0,2,0,2,1,1,2,2,0,1,2,0}, + {1,0,1,1,0,1,1,1,1,1,0,0,0},{2,2,2,1,1,2,2,1,1,0,0,0,0},{2,2,0,2,0,1,1,2,2,2,1,0,0}, + {2,0,0,1,1,2,2,1,1,0,2,0,0},{2,0,0,1,1,1,1,2,2,1,0,1,2},{2,2,0,2,0,2,2,1,1,0,0,2,1}, + {4,3,2,2,3,4,4,1,1,3,4,2,1},{3,0,2,2,0,1,1,3,3,0,1,2,3},{2,0,2,0,2,2,2,1,1,2,0,0,1}, + {2,1,1,0,0,1,1,2,2,0,0,0,2},{3,1,0,0,1,2,2,3,3,1,2,0,3},{2,0,0,0,0,1,1,2,2,0,1,0,2}, + {1,0,0,0,0,1,0,1,0,0,1,1,0},{1,1,0,0,1,1,0,1,0,1,1,1,0},{1,1,1,0,0,1,0,1,0,0,0,1,0}, + {1,0,1,0,1,1,0,1,0,1,0,1,0},{1,0,1,1,0,1,0,1,0,0,1,0,0},{2,1,1,2,2,2,0,2,0,2,1,0,0}, + {1,1,0,1,0,1,0,1,0,0,0,0,0},{1,0,0,1,1,1,0,1,0,1,0,0,0},{1,0,0,1,1,1,0,1,0,0,1,1,1}, + {2,2,0,2,0,1,0,1,0,1,2,2,1},{2,2,1,1,2,2,0,2,0,0,0,1,2},{2,0,2,2,0,1,0,1,0,1,0,2,1}, + {1,0,1,0,1,1,0,1,0,0,1,0,1},{2,2,2,0,0,1,0,1,0,1,2,0,1},{1,1,0,0,1,1,0,1,0,0,0,0,1}, + {1,0,0,0,0,1,0,1,0,1,0,0,1},{1,0,0,0,0,0,0,1,1,1,1,1,0},{1,1,0,0,1,0,0,1,1,0,1,1,0}, + {1,1,1,0,0,0,0,1,1,1,0,1,0},{1,0,1,0,1,0,0,1,1,0,0,1,0},{1,0,1,1,0,0,0,1,1,1,1,0,0}, + {2,2,2,1,1,0,0,1,1,0,2,0,0},{1,1,0,1,0,0,0,1,1,1,0,0,0},{1,0,0,1,1,0,0,1,1,0,0,0,0}, + {2,0,0,2,2,0,0,1,1,2,2,2,1},{2,1,0,1,0,0,0,2,2,0,1,1,2},{3,2,1,1,2,0,0,3,3,2,0,1,3}, + {2,0,1,1,0,0,0,2,2,0,0,1,2},{2,0,1,0,1,0,0,2,2,1,1,0,2},{2,1,1,0,0,0,0,2,2,0,1,0,2}, + {2,1,0,0,1,0,0,2,2,1,0,0,2},{1,0,0,0,0,0,0,1,1,0,0,0,1},{1,0,0,0,0,0,0,1,1,0,0,0,1}, + {1,1,0,0,1,0,0,1,1,1,0,0,1},{2,1,1,0,0,0,0,2,2,0,1,0,2},{1,0,1,0,1,0,0,1,1,1,1,0,1}, + {1,0,1,1,0,0,0,1,1,0,0,1,1},{2,1,1,2,2,0,0,1,1,1,0,1,2},{1,1,0,1,0,0,0,1,1,0,1,1,1}, + {2,0,0,1,1,0,0,2,2,2,2,2,1},{1,0,0,1,1,0,0,1,1,0,0,0,0},{1,1,0,1,0,0,0,1,1,1,0,0,0}, + {1,1,1,1,1,0,0,1,1,0,1,0,0},{1,0,1,1,0,0,0,1,1,1,1,0,0},{1,0,1,0,1,0,0,1,1,0,0,1,0}, + {1,1,1,0,0,0,0,1,1,1,0,1,0},{1,1,0,0,1,0,0,1,1,0,1,1,0},{1,0,0,0,0,0,0,1,1,1,1,1,0}, + {1,0,0,0,0,1,0,1,0,1,0,0,1},{1,1,0,0,1,1,0,1,0,0,0,0,1},{1,1,1,0,0,1,0,1,0,1,1,0,1}, + {1,0,1,0,1,1,0,1,0,0,1,0,1},{1,0,1,1,0,1,0,1,0,1,0,1,1},{2,2,2,1,1,2,0,2,0,0,0,2,1}, + {2,1,0,1,0,2,0,2,0,1,2,2,1},{2,0,0,2,2,1,0,1,0,0,1,1,2},{1,0,0,1,1,1,0,1,0,1,0,0,0}, + {1,1,0,1,0,1,0,1,0,0,0,0,0},{2,1,2,2,1,2,0,2,0,1,2,0,0},{1,0,1,1,0,1,0,1,0,0,1,0,0}, + {1,0,1,0,1,1,0,1,0,1,0,1,0},{1,1,1,0,0,1,0,1,0,0,0,1,0},{2,2,0,0,2,1,0,1,0,2,1,1,0}, + {1,0,0,0,0,1,0,1,0,0,1,1,0},{1,0,0,0,0,1,1,1,1,0,1,0,1},{2,1,0,0,1,2,1,1,2,2,1,0,1}, + {1,1,1,0,0,1,1,1,1,0,0,0,1},{2,0,2,0,2,1,2,2,1,1,0,0,2},{2,0,1,1,0,1,2,2,1,0,1,2,1}, + {4,1,1,3,3,2,4,4,2,2,1,4,3},{2,2,0,2,0,2,1,1,2,0,0,1,2},{3,0,0,1,1,2,3,3,2,2,0,3,1}, + {1,0,0,1,1,1,1,1,1,0,1,0,0},{2,2,0,2,0,1,2,2,1,1,2,0,0},{2,2,1,1,2,2,1,1,2,0,0,0,0}, + {2,0,1,1,0,2,1,1,2,2,0,0,0},{2,0,2,0,2,2,1,1,2,0,2,1,0},{3,1,1,0,0,3,2,2,3,3,1,2,0}, + {2,1,0,0,1,1,2,2,1,0,0,2,0},{2,0,0,0,0,2,1,1,2,2,0,1,0},{1,0,0,0,0,0,1,1,0,1,1,0,1}, + {1,1,0,0,1,0,1,1,0,0,1,0,1},{1,1,1,0,0,0,1,1,0,1,0,0,1},{1,0,1,0,1,0,1,1,0,0,0,0,1}, + {2,0,2,2,0,0,1,1,0,2,2,1,2},{3,1,1,2,2,0,3,3,0,0,1,3,2},{2,1,0,1,0,0,2,2,0,1,0,2,1}, + {2,0,0,1,1,0,2,2,0,0,0,2,1},{1,0,0,1,1,0,1,1,0,1,1,0,0},{1,1,0,1,0,0,1,1,0,0,1,0,0}, + {2,2,1,1,2,0,1,1,0,2,0,0,0},{1,0,1,1,0,0,1,1,0,0,0,0,0},{2,0,1,0,1,0,2,2,0,1,1,2,0}, + {2,1,1,0,0,0,2,2,0,0,1,2,0},{2,1,0,0,1,0,2,2,0,1,0,2,0},{1,0,0,0,0,0,1,1,0,0,0,1,0}, + {1,0,0,0,0,0,1,0,1,0,0,1,1},{1,1,0,0,1,0,1,0,1,1,0,1,1},{1,1,1,0,0,0,1,0,1,0,1,1,1}, + {2,0,2,0,2,0,1,0,1,1,1,2,2},{1,0,1,1,0,0,1,0,1,0,0,0,1},{2,2,2,1,1,0,2,0,2,2,0,0,1}, + {1,1,0,1,0,0,1,0,1,0,1,0,1},{2,0,0,2,2,0,1,0,1,1,1,0,2},{1,0,0,1,1,0,1,0,1,0,0,1,0}, + {1,1,0,1,0,0,1,0,1,1,0,1,0},{2,2,1,1,2,0,2,0,2,0,2,1,0},{2,0,2,2,0,0,1,0,1,1,1,2,0}, + {1,0,1,0,1,0,1,0,1,0,0,0,0},{1,1,1,0,0,0,1,0,1,1,0,0,0},{1,1,0,0,1,0,1,0,1,0,1,0,0}, + {1,0,0,0,0,0,1,0,1,1,1,0,0},{1,0,0,0,0,1,1,0,0,1,0,1,1},{1,1,0,0,1,1,1,0,0,0,0,1,1}, + {2,2,2,0,0,1,1,0,0,2,1,2,2},{2,0,1,0,1,2,2,0,0,0,2,1,1},{1,0,1,1,0,1,1,0,0,1,0,0,1}, + {2,1,1,2,2,1,1,0,0,0,0,0,2},{2,1,0,1,0,2,2,0,0,1,2,0,1},{2,0,0,2,2,1,1,0,0,0,1,0,2}, + {1,0,0,1,1,1,1,0,0,1,0,1,0},{1,1,0,1,0,1,1,0,0,0,0,1,0},{3,1,2,2,1,3,3,0,0,1,3,2,0}, + {2,0,1,1,0,2,2,0,0,0,2,1,0},{1,0,1,0,1,1,1,0,0,1,0,0,0},{1,1,1,0,0,1,1,0,0,0,0,0,0}, + {2,2,0,0,2,1,1,0,0,2,1,0,0},{1,0,0,0,0,1,1,0,0,0,1,0,0},{1,0,0,0,0,1,0,0,1,0,1,1,1}, + {2,2,0,0,2,1,0,0,1,1,2,2,2},{1,1,1,0,0,1,0,0,1,0,0,1,1},{2,0,1,0,1,2,0,0,2,2,0,1,1}, + {1,0,1,1,0,1,0,0,1,0,1,0,1},{3,1,1,3,3,2,0,0,2,2,1,0,3},{1,1,0,1,0,1,0,0,1,0,0,0,1}, + {2,0,0,2,2,1,0,0,1,1,0,0,2},{1,0,0,1,1,1,0,0,1,0,1,1,0},{2,1,0,1,0,2,0,0,2,2,1,1,0}, + {2,1,2,2,1,1,0,0,1,0,0,2,0},{2,0,1,1,0,2,0,0,2,2,0,1,0},{1,0,1,0,1,1,0,0,1,0,1,0,0}, + {2,1,1,0,0,2,0,0,2,2,1,0,0},{1,1,0,0,1,1,0,0,1,0,0,0,0},{1,0,0,0,0,1,0,0,1,1,0,0,0}, + {1,0,0,0,0,0,0,0,0,1,1,1,1},{1,1,0,0,1,0,0,0,0,0,1,1,1},{1,1,1,0,0,0,0,0,0,1,0,1,1}, + {1,0,1,0,1,0,0,0,0,0,0,1,1},{1,0,1,1,0,0,0,0,0,1,1,0,1},{2,1,1,2,2,0,0,0,0,0,1,0,2}, + {1,1,0,1,0,0,0,0,0,1,0,0,1},{1,0,0,1,1,0,0,0,0,0,0,0,1},{1,0,0,1,1,0,0,0,0,1,1,1,0}, + {1,1,0,1,0,0,0,0,0,0,1,1,0},{2,1,2,2,1,0,0,0,0,1,0,2,0},{1,0,1,1,0,0,0,0,0,0,0,1,0}, + {1,0,1,0,1,0,0,0,0,1,1,0,0},{1,1,1,0,0,0,0,0,0,0,1,0,0},{1,1,0,0,1,0,0,0,0,1,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0}}; -template<class AccessorT> -inline bool isAmbiguous(const AccessorT& accessor, const Coord& ijk, - typename AccessorT::ValueType isovalue, int dim) +//////////////////////////////////////// + +inline bool +isPlanarQuad( + const Vec3d& p0, const Vec3d& p1, + const Vec3d& p2, const Vec3d& p3, + double epsilon = 0.001) +{ + // compute representative plane + Vec3d normal = (p2-p0).cross(p1-p3); + normal.normalize(); + const Vec3d centroid = (p0 + p1 + p2 + p3); + const double d = centroid.dot(normal) * 0.25; + + + // test vertice distance to plane + double absDist = std::abs(p0.dot(normal) - d); + if (absDist > epsilon) return false; + + absDist = std::abs(p1.dot(normal) - d); + if (absDist > epsilon) return false; + + absDist = std::abs(p2.dot(normal) - d); + if (absDist > epsilon) return false; + + absDist = std::abs(p3.dot(normal) - d); + if (absDist > epsilon) return false; + + return true; +} + + +//////////////////////////////////////// + + +/// @{ +/// @brief Utility methods for point quantization. + +enum { MASK_FIRST_10_BITS = 0x000003FF, MASK_DIRTY_BIT = 0x80000000, MASK_INVALID_BIT = 0x40000000 }; + +inline uint32_t +packPoint(const Vec3d& v) +{ + uint32_t data = 0; + + // values are expected to be in the [0.0 to 1.0] range. + assert(!(v.x() > 1.0) && !(v.y() > 1.0) && !(v.z() > 1.0)); + assert(!(v.x() < 0.0) && !(v.y() < 0.0) && !(v.z() < 0.0)); + + data |= (uint32_t(v.x() * 1023.0) & MASK_FIRST_10_BITS) << 20; + data |= (uint32_t(v.y() * 1023.0) & MASK_FIRST_10_BITS) << 10; + data |= (uint32_t(v.z() * 1023.0) & MASK_FIRST_10_BITS); + + return data; +} + +inline Vec3d +unpackPoint(uint32_t data) +{ + Vec3d v; + v.z() = double(data & MASK_FIRST_10_BITS) * 0.0009775171; + data = data >> 10; + v.y() = double(data & MASK_FIRST_10_BITS) * 0.0009775171; + data = data >> 10; + v.x() = double(data & MASK_FIRST_10_BITS) * 0.0009775171; + + return v; +} + +/// @} + +//////////////////////////////////////// + + +/// @brief General method that computes the cell-sign configuration at the given +/// @c ijk coordinate. +template<typename AccessorT> +inline unsigned char +evalCellSigns(const AccessorT& accessor, const Coord& ijk, typename AccessorT::ValueType iso) { unsigned signs = 0; Coord coord = ijk; // i, j, k - if (accessor.getValue(coord) < isovalue) signs |= 1u; - coord[0] += dim; // i+dim, j, k - if (accessor.getValue(coord) < isovalue) signs |= 2u; - coord[2] += dim; // i+dim, j, k+dim - if (accessor.getValue(coord) < isovalue) signs |= 4u; - coord[0] = ijk[0]; // i, j, k+dim - if (accessor.getValue(coord) < isovalue) signs |= 8u; - coord[1] += dim; coord[2] = ijk[2]; // i, j+dim, k - if (accessor.getValue(coord) < isovalue) signs |= 16u; - coord[0] += dim; // i+dim, j+dim, k - if (accessor.getValue(coord) < isovalue) signs |= 32u; - coord[2] += dim; // i+dim, j+dim, k+dim - if (accessor.getValue(coord) < isovalue) signs |= 64u; - coord[0] = ijk[0]; // i, j+dim, k+dim - if (accessor.getValue(coord) < isovalue) signs |= 128u; - return sAmbiguous[signs]; + if (accessor.getValue(coord) < iso) signs |= 1u; + coord[0] += 1; // i+1, j, k + if (accessor.getValue(coord) < iso) signs |= 2u; + coord[2] += 1; // i+1, j, k+1 + if (accessor.getValue(coord) < iso) signs |= 4u; + coord[0] = ijk[0]; // i, j, k+1 + if (accessor.getValue(coord) < iso) signs |= 8u; + coord[1] += 1; coord[2] = ijk[2]; // i, j+1, k + if (accessor.getValue(coord) < iso) signs |= 16u; + coord[0] += 1; // i+1, j+1, k + if (accessor.getValue(coord) < iso) signs |= 32u; + coord[2] += 1; // i+1, j+1, k+1 + if (accessor.getValue(coord) < iso) signs |= 64u; + coord[0] = ijk[0]; // i, j+1, k+1 + if (accessor.getValue(coord) < iso) signs |= 128u; + return signs; +} + + +/// @brief Leaf node optimized method that computes the cell-sign configuration +/// at the given local @c offset +template<typename LeafT> +inline unsigned char +evalCellSigns(const LeafT& leaf, const Index offset, typename LeafT::ValueType iso) +{ + unsigned char signs = 0; + + // i, j, k + if (leaf.getValue(offset) < iso) signs |= 1u; + + // i, j, k+1 + if (leaf.getValue(offset + 1) < iso) signs |= 8u; + + // i, j+1, k + if (leaf.getValue(offset + LeafT::DIM) < iso) signs |= 16u; + + // i, j+1, k+1 + if (leaf.getValue(offset + LeafT::DIM + 1) < iso) signs |= 128u; + + // i+1, j, k + if (leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) ) < iso) signs |= 2u; + + // i+1, j, k+1 + if (leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + 1) < iso) signs |= 4u; + + // i+1, j+1, k + if (leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM) < iso) signs |= 32u; + + // i+1, j+1, k+1 + if (leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM + 1) < iso) signs |= 64u; + + return signs; +} + + +/// @brief Used to correct topological ambiguities related to two adjacent cells +/// that share an ambiguous face. +template<class AccessorT> +inline void +correctCellSigns(unsigned char& signs, unsigned char face, + const AccessorT& acc, Coord ijk, typename AccessorT::ValueType iso) +{ + if (face == 1) { + ijk[2] -= 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 3) signs = ~signs; + } else if (face == 3) { + ijk[2] += 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 1) signs = ~signs; + } else if (face == 2) { + ijk[0] += 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 4) signs = ~signs; + } else if (face == 4) { + ijk[0] -= 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 2) signs = ~signs; + } else if (face == 5) { + ijk[1] -= 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 6) signs = ~signs; + } else if (face == 6) { + ijk[1] += 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 5) signs = ~signs; + } } @@ -343,7 +714,7 @@ isNonManifold(const AccessorT& accessor, const Coord& ijk, if (p[5]) signs |= 32u; if (p[6]) signs |= 64u; if (p[7]) signs |= 128u; - if (sAmbiguous[signs]) return true; + if (!sAdaptable[signs]) return true; // Manifold check @@ -519,420 +890,1009 @@ isMergable(LeafType& leaf, const Coord& start, int dim, //////////////////////////////////////// -template <class TreeT> -class LeafCPtrList +template<typename TreeT, typename LeafManagerT> +class SignData { public: - typedef std::vector<const typename TreeT::LeafNodeType *> ListT; + typedef typename TreeT::ValueType ValueT; + typedef tree::ValueAccessor<const TreeT> AccessorT; + + typedef typename TreeT::template ValueConverter<int>::Type IntTreeT; + typedef tree::ValueAccessor<IntTreeT> IntAccessorT; + + typedef typename TreeT::template ValueConverter<Int16>::Type Int16TreeT; + typedef tree::ValueAccessor<Int16TreeT> Int16AccessorT; - LeafCPtrList(const TreeT& tree) + ////////// + + + SignData(const TreeT& distTree, const LeafManagerT& leafs, ValueT iso); + + void run(bool threaded = true); + + typename Int16TreeT::Ptr signTree() const { return mSignTree; } + typename IntTreeT::Ptr idxTree() const { return mIdxTree; } + + ////////// + + SignData(SignData&, tbb::split); + void operator()(const tbb::blocked_range<size_t>&); + void join(const SignData& rhs) { - mLeafNodes.reserve(tree.leafCount()); - typename TreeT::LeafCIter iter = tree.cbeginLeaf(); - for ( ; iter; ++iter) mLeafNodes.push_back(iter.getLeaf()); + mSignTree->merge(*rhs.mSignTree); + mIdxTree->merge(*rhs.mIdxTree); } - size_t size() const { return mLeafNodes.size(); } +private: - const typename TreeT::LeafNodeType* operator[](size_t n) const - { return mLeafNodes[n]; } + const TreeT& mDistTree; + AccessorT mDistAcc; - tbb::blocked_range<size_t> getRange() const - { return tbb::blocked_range<size_t>(0, mLeafNodes.size()); } + const LeafManagerT& mLeafs; + ValueT mIsovalue; - const ListT& getList() const { return mLeafNodes; } + typename Int16TreeT::Ptr mSignTree; + Int16AccessorT mSignAcc; + + typename IntTreeT::Ptr mIdxTree; + IntAccessorT mIdxAcc; -private: - ListT mLeafNodes; }; -template <class TreeT> -class LeafPtrList +template<typename TreeT, typename LeafManagerT> +SignData<TreeT, LeafManagerT>::SignData(const TreeT& distTree, + const LeafManagerT& leafs, ValueT iso) + : mDistTree(distTree) + , mDistAcc(mDistTree) + , mLeafs(leafs) + , mIsovalue(iso) + , mSignTree(new Int16TreeT(0)) + , mSignAcc(*mSignTree) + , mIdxTree(new IntTreeT(int(util::INVALID_IDX))) + , mIdxAcc(*mIdxTree) { -public: - typedef std::vector<typename TreeT::LeafNodeType *> ListT; +} - LeafPtrList(TreeT& tree) - { - mLeafNodes.reserve(tree.leafCount()); - typename TreeT::LeafIter iter = tree.beginLeaf(); - for ( ; iter; ++iter) mLeafNodes.push_back(iter.getLeaf()); - } - size_t size() const { return mLeafNodes.size(); } +template<typename TreeT, typename LeafManagerT> +SignData<TreeT, LeafManagerT>::SignData(SignData& rhs, tbb::split) + : mDistTree(rhs.mDistTree) + , mDistAcc(mDistTree) + , mLeafs(rhs.mLeafs) + , mIsovalue(rhs.mIsovalue) + , mSignTree(new Int16TreeT(0)) + , mSignAcc(*mSignTree) + , mIdxTree(new IntTreeT(int(util::INVALID_IDX))) + , mIdxAcc(*mIdxTree) +{ +} + + +template<typename TreeT, typename LeafManagerT> +void +SignData<TreeT, LeafManagerT>::run(bool threaded) +{ + if (threaded) tbb::parallel_reduce(mLeafs.getRange(), *this); + else (*this)(mLeafs.getRange()); +} + +template<typename TreeT, typename LeafManagerT> +void +SignData<TreeT, LeafManagerT>::operator()(const tbb::blocked_range<size_t>& range) +{ + typedef typename Int16TreeT::LeafNodeType Int16LeafT; + typedef typename IntTreeT::LeafNodeType IntLeafT; + typename LeafManagerT::TreeType::LeafNodeType::ValueOnCIter iter; + unsigned char signs, face; + Coord ijk, coord; + + typename internal::UniquePtr<Int16LeafT>::type signLeafPt(new Int16LeafT(ijk, 0)); - typename TreeT::LeafNodeType* operator[](size_t n) const - { return mLeafNodes[n]; } + for (size_t n = range.begin(); n != range.end(); ++n) { - tbb::blocked_range<size_t> getRange() const - { return tbb::blocked_range<size_t>(0, mLeafNodes.size()); } + bool collectedData = false; - const ListT& getList() const { return mLeafNodes; } + coord = mLeafs.leaf(n).origin(); -private: - ListT mLeafNodes; -}; + if (!signLeafPt.get()) signLeafPt.reset(new Int16LeafT(coord, 0)); + else signLeafPt->setOrigin(coord); + + const typename TreeT::LeafNodeType *leafPt = mDistAcc.probeConstLeaf(coord); + + coord.offset(TreeT::LeafNodeType::DIM - 1); + + for (iter = mLeafs.leaf(n).cbeginValueOn(); iter; ++iter) { + + ijk = iter.getCoord(); + + if (leafPt && ijk[0] < coord[0] && ijk[1] < coord[1] && ijk[2] < coord[2]) { + signs = evalCellSigns(*leafPt, iter.pos(), mIsovalue); + } else { + signs = evalCellSigns(mDistAcc, ijk, mIsovalue); + } + + if (signs != 0 && signs != 0xFF) { + Int16 flags = (signs & 0x1) ? INSIDE : 0; + + if (bool(signs & 0x1) != bool(signs & 0x2)) flags |= XEDGE; + if (bool(signs & 0x1) != bool(signs & 0x10)) flags |= YEDGE; + if (bool(signs & 0x1) != bool(signs & 0x8)) flags |= ZEDGE; + + face = internal::sAmbiguousFace[signs]; + if (face != 0) correctCellSigns(signs, face, mDistAcc, ijk, mIsovalue); + + flags |= Int16(signs); + + signLeafPt->setValue(ijk, flags); + collectedData = true; + } + } + + if (collectedData) { + + IntLeafT* idxLeaf = mIdxAcc.touchLeaf(coord); + idxLeaf->topologyUnion(*signLeafPt); + typename IntLeafT::ValueOnIter it = idxLeaf->beginValueOn(); + for (; it; ++it) { + it.setValue(0); + } + + mSignAcc.addLeaf(signLeafPt.release()); + } + } +} //////////////////////////////////////// -template<typename DistTreeT> -struct ReferenceData +/// @brief Counts the total number of points per leaf, accounts for cells with multiple points. +class CountPoints { - typedef typename DistTreeT::ValueType DistValueT; - typedef typename DistTreeT::template ValueConverter<char>::Type CharTreeT; - typedef typename DistTreeT::template ValueConverter<bool>::Type BoolTreeT; - typedef typename DistTreeT::template ValueConverter<Vec3s>::Type Vec3sTreeT; - - ReferenceData() - : mDistTree(NULL) - , mEdgeTree(NULL) - , mTopologyMaskTree(NULL) - , mSeamPointTree(NULL) - , mSeamMaskTree(typename BoolTreeT::Ptr()) - , mSmoothingMaskTree(typename BoolTreeT::Ptr()) - , mInternalAdaptivity(DistValueT(0.0)) +public: + CountPoints(std::vector<size_t>& pointList) : mPointList(pointList) {} + + template <typename LeafNodeType> + void operator()(LeafNodeType &leaf, size_t leafIndex) const { + size_t points = 0; + + typename LeafNodeType::ValueOnCIter iter = leaf.cbeginValueOn(); + for (; iter; ++iter) { + points += size_t(sEdgeGroupTable[(SIGNS & iter.getValue())][0]); + } + + mPointList[leafIndex] = points; } - bool isValid() const +private: + std::vector<size_t>& mPointList; +}; + + +/// @brief Computes the point list indices for the index tree. +template<typename Int16TreeT> +class MapPoints +{ +public: + typedef tree::ValueAccessor<const Int16TreeT> Int16AccessorT; + + MapPoints(std::vector<size_t>& pointList, const Int16TreeT& signTree) + : mPointList(pointList) + , mSignAcc(signTree) { - return mDistTree && mEdgeTree && mTopologyMaskTree && mSeamMaskTree; } - const DistTreeT* mDistTree; - const CharTreeT* mEdgeTree; - BoolTreeT* mTopologyMaskTree; - Vec3sTreeT* mSeamPointTree; - typename BoolTreeT::Ptr mSeamMaskTree, mSmoothingMaskTree; - DistValueT mInternalAdaptivity; -}; + template <typename LeafNodeType> + void operator()(LeafNodeType &leaf, size_t leafIndex) const + { + size_t ptnIdx = mPointList[leafIndex]; + typename LeafNodeType::ValueOnIter iter = leaf.beginValueOn(); + const typename Int16TreeT::LeafNodeType *signLeafPt = + mSignAcc.probeConstLeaf(leaf.origin()); -//////////////////////////////////////// + for (; iter; ++iter) { + iter.setValue(ptnIdx); + unsigned signs = SIGNS & signLeafPt->getValue(iter.pos()); + ptnIdx += size_t(sEdgeGroupTable[signs][0]); + } + } + +private: + std::vector<size_t>& mPointList; + Int16AccessorT mSignAcc; +}; -template <class DistTreeT> -class Count +/// @brief Counts the total number of points per collapsed region +template<typename IntTreeT> +class CountRegions { public: - Count(const LeafPtrList<DistTreeT>&, std::vector<size_t>&); - inline Count(const Count<DistTreeT>&); + typedef tree::ValueAccessor<IntTreeT> IntAccessorT; + typedef typename IntTreeT::LeafNodeType IntLeafT; + + CountRegions(IntTreeT& idxTree, std::vector<size_t>& regions) + : mIdxAcc(idxTree) + , mRegions(regions) + { + } + + template <typename LeafNodeType> + void operator()(LeafNodeType &leaf, size_t leafIndex) const + { - void runParallel(); - void runSerial(); + size_t regions = 0; + + IntLeafT tmpLeaf(*mIdxAcc.probeConstLeaf(leaf.origin())); + + typename IntLeafT::ValueOnIter iter = tmpLeaf.beginValueOn(); + for (; iter; ++iter) { + if(iter.getValue() == 0) { + iter.setValueOff(); + regions += size_t(sEdgeGroupTable[(SIGNS & leaf.getValue(iter.pos()))][0]); + } + } + + int onVoxelCount = int(tmpLeaf.onVoxelCount()); + while (onVoxelCount > 0) { + ++regions; + iter = tmpLeaf.beginValueOn(); + int regionId = iter.getValue(); + for (; iter; ++iter) { + if (iter.getValue() == regionId) { + iter.setValueOff(); + --onVoxelCount; + } + } + } + + mRegions[leafIndex] = regions; + } - inline void operator()(const tbb::blocked_range<size_t>&) const; private: - const LeafPtrList<DistTreeT>& mLeafNodes; - std::vector<size_t>& mLeafRegionCount; + IntAccessorT mIdxAcc; + std::vector<size_t>& mRegions; }; -template <class DistTreeT> -Count<DistTreeT>::Count( - const LeafPtrList<DistTreeT>& leafs, - std::vector<size_t>& leafRegionCount) - : mLeafNodes(leafs) - , mLeafRegionCount(leafRegionCount) +//////////////////////////////////////// + + +// @brief linear interpolation. +inline double evalRoot(double v0, double v1, double iso) { return (iso - v0) / (v1 - v0); } + + +/// @brief Extracts the eight corner values for leaf inclusive cells. +template<typename LeafT> +inline void +collectCornerValues(const LeafT& leaf, const Index offset, std::vector<double>& values) { + values[0] = double(leaf.getValue(offset)); // i, j, k + values[3] = double(leaf.getValue(offset + 1)); // i, j, k+1 + values[4] = double(leaf.getValue(offset + LeafT::DIM)); // i, j+1, k + values[7] = double(leaf.getValue(offset + LeafT::DIM + 1)); // i, j+1, k+1 + values[1] = double(leaf.getValue(offset + (LeafT::DIM * LeafT::DIM))); // i+1, j, k + values[2] = double(leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + 1)); // i+1, j, k+1 + values[5] = double(leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM)); // i+1, j+1, k + values[6] = double(leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM + 1)); // i+1, j+1, k+1 } -template <class DistTreeT> -inline -Count<DistTreeT>::Count(const Count<DistTreeT>& rhs) - : mLeafNodes(rhs.mLeafNodes) - , mLeafRegionCount(rhs.mLeafRegionCount) +/// @brief Extracts the eight corner values for a cell starting at the given @ijk coordinate. +template<typename AccessorT> +inline void +collectCornerValues(const AccessorT& acc, const Coord& ijk, std::vector<double>& values) { + Coord coord = ijk; + values[0] = double(acc.getValue(coord)); // i, j, k + + coord[0] += 1; + values[1] = double(acc.getValue(coord)); // i+1, j, k + + coord[2] += 1; + values[2] = double(acc.getValue(coord)); // i+i, j, k+1 + + coord[0] = ijk[0]; + values[3] = double(acc.getValue(coord)); // i, j, k+1 + + coord[1] += 1; coord[2] = ijk[2]; + values[4] = double(acc.getValue(coord)); // i, j+1, k + + coord[0] += 1; + values[5] = double(acc.getValue(coord)); // i+1, j+1, k + + coord[2] += 1; + values[6] = double(acc.getValue(coord)); // i+1, j+1, k+1 + + coord[0] = ijk[0]; + values[7] = double(acc.getValue(coord)); // i, j+1, k+1 } -template <class DistTreeT> -void -Count<DistTreeT>::runParallel() +/// @brief Computes the average cell point for a given edge group. +inline Vec3d +computePoint(const std::vector<double>& values, unsigned char signs, + unsigned char edgeGroup, double iso) { - tbb::parallel_for(mLeafNodes.getRange(), *this); + Vec3d avg(0.0, 0.0, 0.0); + int samples = 0; + + if (sEdgeGroupTable[signs][1] == edgeGroup) { // Edged: 0 - 1 + avg[0] += evalRoot(values[0], values[1], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][2] == edgeGroup) { // Edged: 1 - 2 + avg[0] += 1.0; + avg[2] += evalRoot(values[1], values[2], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][3] == edgeGroup) { // Edged: 3 - 2 + avg[0] += evalRoot(values[3], values[2], iso); + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][4] == edgeGroup) { // Edged: 0 - 3 + avg[2] += evalRoot(values[0], values[3], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][5] == edgeGroup) { // Edged: 4 - 5 + avg[0] += evalRoot(values[4], values[5], iso); + avg[1] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][6] == edgeGroup) { // Edged: 5 - 6 + avg[0] += 1.0; + avg[1] += 1.0; + avg[2] += evalRoot(values[5], values[6], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][7] == edgeGroup) { // Edged: 7 - 6 + avg[0] += evalRoot(values[7], values[6], iso); + avg[1] += 1.0; + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][8] == edgeGroup) { // Edged: 4 - 7 + avg[1] += 1.0; + avg[2] += evalRoot(values[4], values[7], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][9] == edgeGroup) { // Edged: 0 - 4 + avg[1] += evalRoot(values[0], values[4], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][10] == edgeGroup) { // Edged: 1 - 5 + avg[0] += 1.0; + avg[1] += evalRoot(values[1], values[5], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][11] == edgeGroup) { // Edged: 2 - 6 + avg[0] += 1.0; + avg[1] += evalRoot(values[2], values[6], iso); + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][12] == edgeGroup) { // Edged: 3 - 7 + avg[1] += evalRoot(values[3], values[7], iso); + avg[2] += 1.0; + ++samples; + } + + if (samples > 1) { + double w = 1.0 / double(samples); + avg[0] *= w; + avg[1] *= w; + avg[2] *= w; + } + + return avg; } -template <class DistTreeT> -void -Count<DistTreeT>::runSerial() +/// @brief Computes the average cell point for a given edge group, ignoring edge +/// samples present in the @c signsMask configuration. +inline int +computeMaskedPoint(Vec3d& avg, const std::vector<double>& values, unsigned char signs, + unsigned char signsMask, unsigned char edgeGroup, double iso) +{ + avg = Vec3d(0.0, 0.0, 0.0); + int samples = 0; + + if (sEdgeGroupTable[signs][1] == edgeGroup + && sEdgeGroupTable[signsMask][1] == 0) { // Edged: 0 - 1 + avg[0] += evalRoot(values[0], values[1], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][2] == edgeGroup + && sEdgeGroupTable[signsMask][2] == 0) { // Edged: 1 - 2 + avg[0] += 1.0; + avg[2] += evalRoot(values[1], values[2], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][3] == edgeGroup + && sEdgeGroupTable[signsMask][3] == 0) { // Edged: 3 - 2 + avg[0] += evalRoot(values[3], values[2], iso); + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][4] == edgeGroup + && sEdgeGroupTable[signsMask][4] == 0) { // Edged: 0 - 3 + avg[2] += evalRoot(values[0], values[3], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][5] == edgeGroup + && sEdgeGroupTable[signsMask][5] == 0) { // Edged: 4 - 5 + avg[0] += evalRoot(values[4], values[5], iso); + avg[1] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][6] == edgeGroup + && sEdgeGroupTable[signsMask][6] == 0) { // Edged: 5 - 6 + avg[0] += 1.0; + avg[1] += 1.0; + avg[2] += evalRoot(values[5], values[6], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][7] == edgeGroup + && sEdgeGroupTable[signsMask][7] == 0) { // Edged: 7 - 6 + avg[0] += evalRoot(values[7], values[6], iso); + avg[1] += 1.0; + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][8] == edgeGroup + && sEdgeGroupTable[signsMask][8] == 0) { // Edged: 4 - 7 + avg[1] += 1.0; + avg[2] += evalRoot(values[4], values[7], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][9] == edgeGroup + && sEdgeGroupTable[signsMask][9] == 0) { // Edged: 0 - 4 + avg[1] += evalRoot(values[0], values[4], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][10] == edgeGroup + && sEdgeGroupTable[signsMask][10] == 0) { // Edged: 1 - 5 + avg[0] += 1.0; + avg[1] += evalRoot(values[1], values[5], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][11] == edgeGroup + && sEdgeGroupTable[signsMask][11] == 0) { // Edged: 2 - 6 + avg[0] += 1.0; + avg[1] += evalRoot(values[2], values[6], iso); + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][12] == edgeGroup + && sEdgeGroupTable[signsMask][12] == 0) { // Edged: 3 - 7 + avg[1] += evalRoot(values[3], values[7], iso); + avg[2] += 1.0; + ++samples; + } + + if (samples > 1) { + double w = 1.0 / double(samples); + avg[0] *= w; + avg[1] *= w; + avg[2] *= w; + } + + return samples; +} + + +/// @brief Computes the average cell point for a given edge group, by computing +/// convex weights based on the distance from the sample point @c p. +inline Vec3d +computeWeightedPoint(const Vec3d& p, const std::vector<double>& values, + unsigned char signs, unsigned char edgeGroup, double iso) { - (*this)(mLeafNodes.getRange()); + std::vector<Vec3d> samples; + samples.reserve(8); + + std::vector<double> weights; + weights.reserve(8); + + Vec3d avg(0.0, 0.0, 0.0); + + if (sEdgeGroupTable[signs][1] == edgeGroup) { // Edged: 0 - 1 + avg[0] = evalRoot(values[0], values[1], iso); + avg[1] = 0.0; + avg[2] = 0.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][2] == edgeGroup) { // Edged: 1 - 2 + avg[0] = 1.0; + avg[1] = 0.0; + avg[2] = evalRoot(values[1], values[2], iso); + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][3] == edgeGroup) { // Edged: 3 - 2 + avg[0] = evalRoot(values[3], values[2], iso); + avg[1] = 0.0; + avg[2] = 1.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][4] == edgeGroup) { // Edged: 0 - 3 + avg[0] = 0.0; + avg[1] = 0.0; + avg[2] = evalRoot(values[0], values[3], iso); + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][5] == edgeGroup) { // Edged: 4 - 5 + avg[0] = evalRoot(values[4], values[5], iso); + avg[1] = 1.0; + avg[2] = 0.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][6] == edgeGroup) { // Edged: 5 - 6 + avg[0] = 1.0; + avg[1] = 1.0; + avg[2] = evalRoot(values[5], values[6], iso); + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][7] == edgeGroup) { // Edged: 7 - 6 + avg[0] = evalRoot(values[7], values[6], iso); + avg[1] = 1.0; + avg[2] = 1.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][8] == edgeGroup) { // Edged: 4 - 7 + avg[0] = 0.0; + avg[1] = 1.0; + avg[2] = evalRoot(values[4], values[7], iso); + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][9] == edgeGroup) { // Edged: 0 - 4 + avg[0] = 0.0; + avg[1] = evalRoot(values[0], values[4], iso); + avg[2] = 0.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][10] == edgeGroup) { // Edged: 1 - 5 + avg[0] = 1.0; + avg[1] = evalRoot(values[1], values[5], iso); + avg[2] = 0.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][11] == edgeGroup) { // Edged: 2 - 6 + avg[0] = 1.0; + avg[1] = evalRoot(values[2], values[6], iso); + avg[2] = 1.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][12] == edgeGroup) { // Edged: 3 - 7 + avg[0] = 0.0; + avg[1] = evalRoot(values[3], values[7], iso); + avg[2] = 1.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + + double minWeight = std::numeric_limits<double>::max(); + double maxWeight = -std::numeric_limits<double>::max(); + + for (size_t i = 0, I = weights.size(); i < I; ++i) { + minWeight = std::min(minWeight, weights[i]); + maxWeight = std::max(maxWeight, weights[i]); + } + + const double offset = maxWeight + minWeight * 0.1; + for (size_t i = 0, I = weights.size(); i < I; ++i) { + weights[i] = offset - weights[i]; + } + + + double weightSum = 0.0; + for (size_t i = 0, I = weights.size(); i < I; ++i) { + weightSum += weights[i]; + } + + avg[0] = 0.0; + avg[1] = 0.0; + avg[2] = 0.0; + + if (samples.size() > 1) { + for (size_t i = 0, I = samples.size(); i < I; ++i) { + avg += samples[i] * (weights[i] / weightSum); + } + } else { + avg = samples.front(); + } + + return avg; } -template <class DistTreeT> +/// @brief Computes the average cell points defined by the sign configuration +/// @c signs and the given corner values @c values. inline void -Count<DistTreeT>::operator()(const tbb::blocked_range<size_t>& range) const +computeCellPoints(std::vector<Vec3d>& points, + const std::vector<double>& values, unsigned char signs, double iso) { - for (size_t n = range.begin(); n != range.end(); ++n) { - mLeafRegionCount[n] = size_t(mLeafNodes[n]->onVoxelCount()); + for (size_t n = 1, N = sEdgeGroupTable[signs][0] + 1; n < N; ++n) { + points.push_back(computePoint(values, signs, n, iso)); } } -//////////////////////////////////////// +/// @brief Given a sign configuration @c lhsSigns and an edge group @c groupId, +/// finds the corresponding edge group in a different sign configuration +/// @c rhsSigns. Returns -1 if no match is found. +inline int +matchEdgeGroup(unsigned char groupId, unsigned char lhsSigns, unsigned char rhsSigns) +{ + int id = -1; + for (size_t i = 1; i <= 12; ++i) { + if (sEdgeGroupTable[lhsSigns][i] == groupId && sEdgeGroupTable[rhsSigns][i] != 0) { + id = sEdgeGroupTable[rhsSigns][i]; + break; + } + } + return id; +} -template <class DistTreeT> -class Merge +/// @brief Computes the average cell points defined by the sign configuration +/// @c signs and the given corner values @c values. Combines data from +/// two different level sets to eliminate seam lines when meshing +/// fractured segments. +inline void +computeCellPoints(std::vector<Vec3d>& points, std::vector<bool>& weightedPointMask, + const std::vector<double>& lhsValues, const std::vector<double>& rhsValues, + unsigned char lhsSigns, unsigned char rhsSigns, + double iso, size_t pointIdx, const boost::scoped_array<uint32_t>& seamPoints) +{ + for (size_t n = 1, N = sEdgeGroupTable[lhsSigns][0] + 1; n < N; ++n) { + + int id = matchEdgeGroup(n, lhsSigns, rhsSigns); + + if (id != -1) { + + const unsigned char e(id); + uint32_t& quantizedPoint = seamPoints[pointIdx + (id - 1)]; + + if ((quantizedPoint & MASK_DIRTY_BIT) && !(quantizedPoint & MASK_INVALID_BIT)) { + Vec3d p = unpackPoint(quantizedPoint); + points.push_back(computeWeightedPoint(p, rhsValues, rhsSigns, e, iso)); + weightedPointMask.push_back(true); + } else { + points.push_back(computePoint(rhsValues, rhsSigns, e, iso)); + weightedPointMask.push_back(false); + } + + } else { + points.push_back(computePoint(lhsValues, lhsSigns, n, iso)); + weightedPointMask.push_back(false); + } + } +} + + +template <typename TreeT, typename LeafManagerT> +class GenPoints { public: - typedef typename DistTreeT::ValueType DistValueT; - typedef typename DistTreeT::template ValueConverter<bool>::Type BoolTreeT; - typedef typename DistTreeT::template ValueConverter<int>::Type IntTreeT; + typedef tree::ValueAccessor<const TreeT> AccessorT; + + typedef typename TreeT::template ValueConverter<int>::Type IntTreeT; + typedef tree::ValueAccessor<IntTreeT> IntAccessorT; + typedef tree::ValueAccessor<const IntTreeT> IntCAccessorT; - Merge( - const DistTreeT& distTree, - LeafPtrList<IntTreeT>& auxLeafs, - std::vector<size_t>& leafRegionCount, - const DistValueT iso, - const DistValueT adaptivity); + typedef typename TreeT::template ValueConverter<Int16>::Type Int16TreeT; + typedef tree::ValueAccessor<const Int16TreeT> Int16CAccessorT; - inline Merge(const Merge<DistTreeT>&); + typedef boost::scoped_array<uint32_t> QuantizedPointList; - void setRefData(const ReferenceData<DistTreeT>&); + ////////// + + + GenPoints(const LeafManagerT& signLeafs, const TreeT& distTree, + IntTreeT& idxTree, PointList& points, std::vector<size_t>& indices, + const math::Transform& xform, double iso); + + void run(bool threaded = true); + + void setRefData(const Int16TreeT* refSignTree = NULL, const TreeT* refDistTree = NULL, + IntTreeT* refIdxTree = NULL, const QuantizedPointList* seamPoints = NULL, + std::vector<unsigned char>* mSeamPointMaskPt = NULL); + + ////////// - void runParallel(); - void runSerial(); void operator()(const tbb::blocked_range<size_t>&) const; private: - const DistTreeT& mDistTree; - LeafPtrList<IntTreeT>& mAuxLeafs; - std::vector<size_t>& mLeafRegionCount; - const DistValueT mIsovalue, mAdaptivity; - const ReferenceData<DistTreeT>* mRefData; -}; + const LeafManagerT& mSignLeafs; + AccessorT mDistAcc; + IntTreeT& mIdxTree; -template <class DistTreeT> -Merge<DistTreeT>::Merge( - const DistTreeT& distTree, - LeafPtrList<IntTreeT>& auxLeafs, - std::vector<size_t>& leafRegionCount, - const DistValueT iso, - const DistValueT adaptivity) - : mDistTree(distTree) - , mAuxLeafs(auxLeafs) - , mLeafRegionCount(leafRegionCount) - , mIsovalue(iso) - , mAdaptivity(adaptivity) - , mRefData(NULL) -{ -} + PointList& mPoints; + std::vector<size_t>& mIndices; + const math::Transform& mTransform; + const double mIsovalue; + // reference data + const Int16TreeT *mRefSignTreePt; + const TreeT* mRefDistTreePt; + const IntTreeT* mRefIdxTreePt; + const QuantizedPointList* mSeamPointsPt; + std::vector<unsigned char>* mSeamPointMaskPt; +}; -template <class DistTreeT> -inline -Merge<DistTreeT>::Merge(const Merge<DistTreeT>& rhs) - : mDistTree(rhs.mDistTree) - , mAuxLeafs(rhs.mAuxLeafs) - , mLeafRegionCount(rhs.mLeafRegionCount) - , mIsovalue(rhs.mIsovalue) - , mAdaptivity(rhs.mAdaptivity) - , mRefData(rhs.mRefData) + +template <typename TreeT, typename LeafManagerT> +GenPoints<TreeT, LeafManagerT>::GenPoints(const LeafManagerT& signLeafs, + const TreeT& distTree, IntTreeT& idxTree, PointList& points, + std::vector<size_t>& indices, const math::Transform& xform, double iso) + : mSignLeafs(signLeafs) + , mDistAcc(distTree) + , mIdxTree(idxTree) + , mPoints(points) + , mIndices(indices) + , mTransform(xform) + , mIsovalue(iso) + , mRefSignTreePt(NULL) + , mRefDistTreePt(NULL) + , mRefIdxTreePt(NULL) + , mSeamPointsPt(NULL) + , mSeamPointMaskPt(NULL) { } -template <class DistTreeT> +template <typename TreeT, typename LeafManagerT> void -Merge<DistTreeT>::runParallel() +GenPoints<TreeT, LeafManagerT>::run(bool threaded) { - tbb::parallel_for(mAuxLeafs.getRange(), *this); + if (threaded) tbb::parallel_for(mSignLeafs.getRange(), *this); + else (*this)(mSignLeafs.getRange()); } -template <class DistTreeT> +template <typename TreeT, typename LeafManagerT> void -Merge<DistTreeT>::runSerial() +GenPoints<TreeT, LeafManagerT>::setRefData(const Int16TreeT *refSignTree, const TreeT *refDistTree, + IntTreeT* refIdxTree, const QuantizedPointList* seamPoints, std::vector<unsigned char>* seamPointMask) { - (*this)(mAuxLeafs.getRange()); + mRefSignTreePt = refSignTree; + mRefDistTreePt = refDistTree; + mRefIdxTreePt = refIdxTree; + mSeamPointsPt = seamPoints; + mSeamPointMaskPt = seamPointMask; } -template <class DistTreeT> -void -Merge<DistTreeT>::setRefData(const ReferenceData<DistTreeT>& refData) -{ - mRefData = &refData; -} -template <class DistTreeT> +template <typename TreeT, typename LeafManagerT> void -Merge<DistTreeT>::operator()(const tbb::blocked_range<size_t>& range) const +GenPoints<TreeT, LeafManagerT>::operator()( + const tbb::blocked_range<size_t>& range) const { - typedef math::Vec3<DistValueT> Vec3T; - typedef typename BoolTreeT::LeafNodeType BoolLeafT; - typedef typename IntTreeT::LeafNodeType IntLeafT; - typedef typename BoolLeafT::template ValueConverter<Vec3T>::Type Vec3LeafT; + typename IntTreeT::LeafNodeType::ValueOnIter iter; + unsigned char signs, refSigns; + Index offset; + Coord ijk, coord; + std::vector<Vec3d> points(4); + std::vector<bool> weightedPointMask(4); + std::vector<double> values(8), refValues(8); - typedef typename IntLeafT::ValueOnIter IntIterT; - typedef typename BoolLeafT::ValueOnCIter BoolCIterT; - typedef typename tree::ValueAccessor<BoolTreeT> BoolTreeAccessorT; - typedef typename tree::ValueAccessor<const BoolTreeT> BoolTreeCAccessorT; + IntAccessorT idxAcc(mIdxTree); - boost::scoped_ptr<BoolTreeAccessorT> seamMaskAcc; - boost::scoped_ptr<BoolTreeCAccessorT> topologyMaskAcc; - if (mRefData && mRefData->isValid()) { - seamMaskAcc.reset(new BoolTreeAccessorT(*mRefData->mSeamMaskTree.get())); - topologyMaskAcc.reset(new BoolTreeCAccessorT(*mRefData->mTopologyMaskTree)); - } - const bool hasRefData = seamMaskAcc && topologyMaskAcc; + // reference data accessors + boost::scoped_ptr<Int16CAccessorT> refSignAcc; + if (mRefSignTreePt) refSignAcc.reset(new Int16CAccessorT(*mRefSignTreePt)); - const int LeafDim = BoolLeafT::DIM; - tree::ValueAccessor<const DistTreeT> distAcc(mDistTree); + boost::scoped_ptr<IntCAccessorT> refIdxAcc; + if (mRefIdxTreePt) refIdxAcc.reset(new IntCAccessorT(*mRefIdxTreePt)); + + boost::scoped_ptr<AccessorT> refDistAcc; + if (mRefDistTreePt) refDistAcc.reset(new AccessorT(*mRefDistTreePt)); - // Allocate reusable leaf buffers - BoolLeafT mask; - Vec3LeafT gradientBuffer; - Coord ijk, coord, end; for (size_t n = range.begin(); n != range.end(); ++n) { - DistValueT adaptivity = mAdaptivity; - IntLeafT& auxLeaf = *mAuxLeafs[n]; + coord = mSignLeafs.leaf(n).origin(); - const Coord& origin = auxLeaf.getOrigin(); - end[0] = origin[0] + LeafDim; - end[1] = origin[1] + LeafDim; - end[2] = origin[2] + LeafDim; + const typename TreeT::LeafNodeType *leafPt = mDistAcc.probeConstLeaf(coord); + typename IntTreeT::LeafNodeType *idxLeafPt = idxAcc.probeLeaf(coord); - mask.setValuesOff(); - // Mask off seam line adjacent voxels - if (hasRefData) { - const BoolLeafT* seamMask = seamMaskAcc->probeConstLeaf(origin); - if (seamMask != NULL) { - for (BoolCIterT it = seamMask->cbeginValueOn(); it; ++it) { - ijk = it.getCoord(); - coord[0] = ijk[0] - (ijk[0] % 2); - coord[1] = ijk[1] - (ijk[1] % 2); - coord[2] = ijk[2] - (ijk[2] % 2); - mask.setActiveState(coord, true); + // reference data leafs + const typename Int16TreeT::LeafNodeType *refSignLeafPt = NULL; + if (refSignAcc) refSignLeafPt = refSignAcc->probeConstLeaf(coord); + + const typename IntTreeT::LeafNodeType *refIdxLeafPt = NULL; + if (refIdxAcc) refIdxLeafPt = refIdxAcc->probeConstLeaf(coord); + + const typename TreeT::LeafNodeType *refDistLeafPt = NULL; + if (refDistAcc) refDistLeafPt = refDistAcc->probeConstLeaf(coord); + + + // generate cell points + size_t ptnIdx = mIndices[n]; + coord.offset(TreeT::LeafNodeType::DIM - 1); + + + + for (iter = idxLeafPt->beginValueOn(); iter; ++iter) { + + if(iter.getValue() != 0) continue; + + iter.setValue(ptnIdx); + iter.setValueOff(); + offset = iter.pos(); + ijk = iter.getCoord(); + + const bool inclusiveCell = ijk[0] < coord[0] && ijk[1] < coord[1] && ijk[2] < coord[2]; + + const Int16& flags = mSignLeafs.leaf(n).getValue(offset); + signs = SIGNS & flags; + refSigns = 0; + + if ((flags & SEAM) && refSignLeafPt && refIdxLeafPt) { + if (refSignLeafPt->isValueOn(offset)) { + refSigns = (SIGNS & refSignLeafPt->getValue(offset)); } } - if (topologyMaskAcc->probeConstLeaf(origin) == NULL) { - adaptivity = mRefData->mInternalAdaptivity; + + + if (inclusiveCell) collectCornerValues(*leafPt, offset, values); + else collectCornerValues(mDistAcc, ijk, values); + + + points.clear(); + weightedPointMask.clear(); + + if (refSigns == 0) { + computeCellPoints(points, values, signs, mIsovalue); + } else { + + if (inclusiveCell) collectCornerValues(*refDistLeafPt, offset, refValues); + else collectCornerValues(*refDistAcc, ijk, refValues); + + computeCellPoints(points, weightedPointMask, values, refValues, signs, refSigns, + mIsovalue, refIdxLeafPt->getValue(offset), *mSeamPointsPt); + } + + + for (size_t i = 0, I = points.size(); i < I; ++i) { + + // offset by cell-origin + points[i][0] += double(ijk[0]); + points[i][1] += double(ijk[1]); + points[i][2] += double(ijk[2]); + + + points[i] = mTransform.indexToWorld(points[i]); + + mPoints[ptnIdx][0] = float(points[i][0]); + mPoints[ptnIdx][1] = float(points[i][1]); + mPoints[ptnIdx][2] = float(points[i][2]); + + if (mSeamPointMaskPt && !weightedPointMask.empty() && weightedPointMask[i]) { + (*mSeamPointMaskPt)[ptnIdx] = 1; + } + + ++ptnIdx; } } - // Mask off ambiguous voxels - for (IntIterT it = auxLeaf.beginValueOn(); it; ++it) { - ijk = it.getCoord(); - coord[0] = ijk[0] - (ijk[0] % 2); - coord[1] = ijk[1] - (ijk[1] % 2); - coord[2] = ijk[2] - (ijk[2] % 2); - if(mask.isValueOn(coord)) continue; - mask.setActiveState(coord, isAmbiguous(distAcc, ijk, mIsovalue, 1)); - } - - int dim = 2; - // Mask off topologically ambiguous 2x2x2 voxel sub-blocks - for (ijk[0] = origin[0]; ijk[0] < end[0]; ijk[0] += dim) { - for (ijk[1] = origin[1]; ijk[1] < end[1]; ijk[1] += dim) { - for (ijk[2] = origin[2]; ijk[2] < end[2]; ijk[2] += dim) { - if (isNonManifold(distAcc, ijk, mIsovalue, dim)) { - mask.setActiveState(ijk, true); - } - } - } - } - - // Compute the gradient for the remaining voxels - gradientBuffer.setValuesOff(); - - for (IntIterT it = auxLeaf.beginValueOn(); it; ++it) { - - ijk = it.getCoord(); - coord[0] = ijk[0] - (ijk[0] % dim); - coord[1] = ijk[1] - (ijk[1] % dim); - coord[2] = ijk[2] - (ijk[2] % dim); - if(mask.isValueOn(coord)) continue; - - Vec3T norm(math::ISGradient<math::CD_2ND>::result(distAcc, ijk)); - // Normalize (Vec3's normalize uses isApproxEqual, which uses abs and does more work) - DistValueT length = norm.length(); - if (length > DistValueT(1.0e-7)) { - norm *= DistValueT(1.0) / length; - } - gradientBuffer.setValue(ijk, norm); - } - - int regionId = 1, next_dim = dim << 1; - - // Process the first adaptivity level. - for (ijk[0] = 0; ijk[0] < LeafDim; ijk[0] += dim) { - coord[0] = ijk[0] - (ijk[0] % next_dim); - for (ijk[1] = 0; ijk[1] < LeafDim; ijk[1] += dim) { - coord[1] = ijk[1] - (ijk[1] % next_dim); - for (ijk[2] = 0; ijk[2] < LeafDim; ijk[2] += dim) { - coord[2] = ijk[2] - (ijk[2] % next_dim); - if(mask.isValueOn(ijk) || !isMergable(gradientBuffer, ijk, dim, adaptivity)) { - mask.setActiveState(coord, true); - continue; - } - mergeVoxels(auxLeaf, ijk, dim, regionId++); - } - } - } - - - // Process remaining adaptivity levels - for (dim = 4; dim < LeafDim; dim = dim << 1) { - next_dim = dim << 1; - coord[0] = ijk[0] - (ijk[0] % next_dim); - for (ijk[0] = origin[0]; ijk[0] < end[0]; ijk[0] += dim) { - coord[1] = ijk[1] - (ijk[1] % next_dim); - for (ijk[1] = origin[1]; ijk[1] < end[1]; ijk[1] += dim) { - coord[2] = ijk[2] - (ijk[2] % next_dim); - for (ijk[2] = origin[2]; ijk[2] < end[2]; ijk[2] += dim) { - - if (mask.isValueOn(ijk) || isNonManifold(distAcc, ijk, mIsovalue, dim) || - !isMergable(gradientBuffer, ijk, dim, adaptivity)) { - mask.setActiveState(coord, true); - continue; - } - mergeVoxels(auxLeaf, ijk, dim, regionId++); - } - } - } - } - - - if (!(mask.isValueOn(origin) || isNonManifold(distAcc, origin, mIsovalue, LeafDim)) - && isMergable(gradientBuffer, origin, LeafDim, adaptivity)) { - mergeVoxels(auxLeaf, origin, LeafDim, regionId++); - } - - - // Count unique regions - size_t numVoxels = 0; - IntLeafT tmpLeaf(auxLeaf); - for (IntIterT it = tmpLeaf.beginValueOn(); it; ++it) { - if(it.getValue() == 0) { - it.setValueOff(); - ++numVoxels; - } - } - - while (tmpLeaf.onVoxelCount() > 0) { - ++numVoxels; - IntIterT it = tmpLeaf.beginValueOn(); - regionId = it.getValue(); - for (; it; ++it) { - if (it.getValue() == regionId) it.setValueOff(); - } - } - - mLeafRegionCount[n] = numVoxels; + // generate collapsed region points + int onVoxelCount = int(idxLeafPt->onVoxelCount()); + while (onVoxelCount > 0) { + + iter = idxLeafPt->beginValueOn(); + int regionId = iter.getValue(), count = 0; + + Vec3d avg(0.0), point; + + for (; iter; ++iter) { + if (iter.getValue() != regionId) continue; + + iter.setValue(ptnIdx); + iter.setValueOff(); + --onVoxelCount; + + ijk = iter.getCoord(); + offset = iter.pos(); + + signs = (SIGNS & mSignLeafs.leaf(n).getValue(offset)); + + if (ijk[0] < coord[0] && ijk[1] < coord[1] && ijk[2] < coord[2]) { + collectCornerValues(*leafPt, offset, values); + } else { + collectCornerValues(mDistAcc, ijk, values); + } + + points.clear(); + computeCellPoints(points, values, signs, mIsovalue); + + avg[0] += double(ijk[0]) + points[0][0]; + avg[1] += double(ijk[1]) + points[0][1]; + avg[2] += double(ijk[2]) + points[0][2]; + + ++count; + } + + + if (count > 1) { + double w = 1.0 / double(count); + avg[0] *= w; + avg[1] *= w; + avg[2] *= w; + } + + avg = mTransform.indexToWorld(avg); + + mPoints[ptnIdx][0] = float(avg[0]); + mPoints[ptnIdx][1] = float(avg[1]); + mPoints[ptnIdx][2] = float(avg[2]); + + ++ptnIdx; + } } } @@ -940,374 +1900,436 @@ Merge<DistTreeT>::operator()(const tbb::blocked_range<size_t>& range) const //////////////////////////////////////// -template <class DistTreeT> -class PointGen +template<typename TreeT> +class SeamWeights { public: - typedef typename DistTreeT::ValueType DistValueT; - typedef tree::ValueAccessor<const DistTreeT> DistTreeAccessorT; - typedef typename DistTreeT::template ValueConverter<int>::Type IntTreeT; + typedef tree::ValueAccessor<const TreeT> AccessorT; - PointGen( - const DistTreeT& distTree, - const LeafPtrList<IntTreeT>& auxLeafs, - std::vector<size_t>& leafIndices, - const openvdb::math::Transform& xform, - PointList& points, - double iso = 0.0); + typedef typename TreeT::template ValueConverter<int>::Type IntTreeT; + typedef tree::ValueAccessor<const IntTreeT> IntAccessorT; - PointGen(const PointGen<DistTreeT>&); + typedef typename TreeT::template ValueConverter<Int16>::Type Int16TreeT; + typedef tree::ValueAccessor<const Int16TreeT> Int16AccessorT; - void setRefData(const ReferenceData<DistTreeT>&); + typedef boost::scoped_array<uint32_t> QuantizedPointList; - void runParallel(); - void runSerial(); + ////////// - void operator()(const tbb::blocked_range<size_t>&) const; + SeamWeights(const TreeT& distTree, const Int16TreeT& refSignTree, + IntTreeT& refIdxTree, QuantizedPointList& points, double iso); + + template <typename LeafNodeType> + void operator()(LeafNodeType &signLeaf, size_t leafIndex) const; private: - const DistTreeT& mDistTree; - const LeafPtrList<IntTreeT>& mAuxLeafs; - const std::vector<size_t>& mLeafIndices; - const openvdb::math::Transform& mTransform; - const PointList& mPoints; - const double mIsovalue; - const ReferenceData<DistTreeT>* mRefData; + AccessorT mDistAcc; + Int16AccessorT mRefSignAcc; + IntAccessorT mRefIdxAcc; - double root(double v0, double v1) const { return (mIsovalue - v0) / (v1 - v0); } - int calcAvgPoint(DistTreeAccessorT&, const Coord&, openvdb::Vec3d&) const; + QuantizedPointList& mPoints; + const double mIsovalue; }; -template <class DistTreeT> -PointGen<DistTreeT>::PointGen( - const DistTreeT& distTree, - const LeafPtrList<IntTreeT>& auxLeafs, - std::vector<size_t>& leafIndices, - const openvdb::math::Transform& xform, - PointList& points, - double iso) - : mDistTree(distTree) - , mAuxLeafs(auxLeafs) - , mLeafIndices(leafIndices) - , mTransform(xform) +template<typename TreeT> +SeamWeights<TreeT>::SeamWeights(const TreeT& distTree, const Int16TreeT& refSignTree, + IntTreeT& refIdxTree, QuantizedPointList& points, double iso) + : mDistAcc(distTree) + , mRefSignAcc(refSignTree) + , mRefIdxAcc(refIdxTree) , mPoints(points) , mIsovalue(iso) - , mRefData(NULL) { } -template <class DistTreeT> -PointGen<DistTreeT>::PointGen(const PointGen<DistTreeT>& rhs) - : mDistTree(rhs.mDistTree) - , mAuxLeafs(rhs.mAuxLeafs) - , mLeafIndices(rhs.mLeafIndices) - , mTransform(rhs.mTransform) - , mPoints(rhs.mPoints) - , mIsovalue(rhs.mIsovalue) - , mRefData(rhs.mRefData) +template<typename TreeT> +template <typename LeafNodeType> +void +SeamWeights<TreeT>::operator()(LeafNodeType &signLeaf, size_t /*leafIndex*/) const { -} + Coord coord = signLeaf.origin(); + const typename Int16TreeT::LeafNodeType *refSignLeafPt = mRefSignAcc.probeConstLeaf(coord); + if (!refSignLeafPt) return; -template <class DistTreeT> -void -PointGen<DistTreeT>::setRefData( - const ReferenceData<DistTreeT>& refData) -{ - mRefData = &refData; -} + const typename TreeT::LeafNodeType *distLeafPt = mDistAcc.probeConstLeaf(coord); + const typename IntTreeT::LeafNodeType *refIdxLeafPt = mRefIdxAcc.probeConstLeaf(coord); -template <class DistTreeT> -void -PointGen<DistTreeT>::runParallel() -{ - tbb::parallel_for(mAuxLeafs.getRange(), *this); -} + std::vector<double> values(8); + unsigned char lhsSigns, rhsSigns; + Vec3d point; + Index offset; + Coord ijk; + coord.offset(TreeT::LeafNodeType::DIM - 1); -template <class DistTreeT> -void -PointGen<DistTreeT>::runSerial() -{ - (*this)(mAuxLeafs.getRange()); -} + typename LeafNodeType::ValueOnCIter iter = signLeaf.cbeginValueOn(); + for (; iter; ++iter) { + offset = iter.pos(); + ijk = iter.getCoord(); -template <class DistTreeT> -void -PointGen<DistTreeT>::operator()(const tbb::blocked_range<size_t>& range) const -{ - typedef typename DistTreeT::template ValueConverter<bool>::Type BoolTreeT; - typedef typename DistTreeT::template ValueConverter<Vec3s>::Type Vec3sTreeT; + const bool inclusiveCell = ijk[0] < coord[0] && ijk[1] < coord[1] && ijk[2] < coord[2]; - typedef tree::ValueAccessor<BoolTreeT> BoolTreeAccessorT; - typedef tree::ValueAccessor<Vec3sTreeT> Vec3sTreeAccessorT; + if ((iter.getValue() & SEAM) && refSignLeafPt->isValueOn(offset)) { - typedef typename BoolTreeT::LeafNodeType BoolLeafT; - typedef typename IntTreeT::LeafNodeType IntLeafT; - typedef typename Vec3sTreeT::LeafNodeType Vec3sLeafT; + lhsSigns = SIGNS & iter.getValue(); + rhsSigns = SIGNS & refSignLeafPt->getValue(offset); - boost::scoped_ptr<DistTreeAccessorT> refDistAcc; - boost::scoped_ptr<BoolTreeAccessorT> refMaskAcc, refSmoothMaskAcc; - boost::scoped_ptr<Vec3sTreeAccessorT> refPtnAcc; - if (mRefData && mRefData->isValid()) { - refDistAcc.reset(new DistTreeAccessorT(*mRefData->mDistTree)); - refMaskAcc.reset(new BoolTreeAccessorT(*mRefData->mTopologyMaskTree)); - refSmoothMaskAcc.reset(new BoolTreeAccessorT(*mRefData->mSmoothingMaskTree)); - refPtnAcc.reset(new Vec3sTreeAccessorT(*mRefData->mSeamPointTree)); - } + if (inclusiveCell) { + collectCornerValues(*distLeafPt, offset, values); + } else { + collectCornerValues(mDistAcc, ijk, values); + } - const bool hasRefData = refDistAcc && refMaskAcc; - typename IntTreeT::LeafNodeType::ValueOnIter auxIter; - DistTreeAccessorT distAcc(mDistTree); + for (size_t n = 1, N = sEdgeGroupTable[lhsSigns][0] + 1; n < N; ++n) { - Coord ijk; - openvdb::Vec3d avg, tmp; + int id = matchEdgeGroup(n, lhsSigns, rhsSigns); - for (size_t n = range.begin(); n != range.end(); ++n) { + if (id != -1) { + + uint32_t& data = mPoints[refIdxLeafPt->getValue(offset) + (id - 1)]; + + if (!(data & MASK_DIRTY_BIT)) { - size_t idx = mLeafIndices[n]; - IntLeafT& auxLeaf = *mAuxLeafs[n]; + int smaples = computeMaskedPoint(point, values, lhsSigns, rhsSigns, n, mIsovalue); - BoolLeafT* maskLeaf = NULL; - BoolLeafT* smoothMaskLeaf = NULL; - Vec3sLeafT* ptnLeaf = NULL; - if (hasRefData) { - maskLeaf = refMaskAcc->probeLeaf(auxLeaf.getOrigin()); - smoothMaskLeaf = refSmoothMaskAcc->probeLeaf(auxLeaf.getOrigin()); - ptnLeaf = refPtnAcc->probeLeaf(auxLeaf.getOrigin()); + if (smaples > 0) data = packPoint(point); + else data = MASK_INVALID_BIT; + + data |= MASK_DIRTY_BIT; + } + } + } } + } +} - for (auxIter = auxLeaf.beginValueOn(); auxIter; ++auxIter) { - if(auxIter.getValue() == 0) { +//////////////////////////////////////// - auxIter.setValue(idx); - auxIter.setValueOff(); - ijk = auxIter.getCoord(); - if (hasRefData && maskLeaf && maskLeaf->isValueOn(ijk)) { +template <typename TreeT, typename LeafManagerT> +class MergeVoxelRegions +{ +public: + typedef typename TreeT::ValueType ValueT; + typedef tree::ValueAccessor<const TreeT> AccessorT; - if (ptnLeaf && ptnLeaf->isValueOn(ijk)) { - avg = ptnLeaf->getValue(ijk); - } else { - int e1 = calcAvgPoint(*refDistAcc.get(), ijk, avg); + typedef typename TreeT::template ValueConverter<int>::Type IntTreeT; + typedef tree::ValueAccessor<IntTreeT> IntAccessorT; - if (e1 != (XEDGE|YEDGE|ZEDGE)) { - int e2 = calcAvgPoint(distAcc, ijk, tmp); - if((e2 & (~e1)) != 0) smoothMaskLeaf->setValueOn(ijk); - } - } - } else { - calcAvgPoint(distAcc, ijk, avg); - } + typedef typename TreeT::template ValueConverter<bool>::Type BoolTreeT; - openvdb::Vec3s& ptn = mPoints[idx]; - ptn[0] = float(avg[0]); - ptn[1] = float(avg[1]); - ptn[2] = float(avg[2]); + typedef typename LeafManagerT::TreeType::template ValueConverter<Int16>::Type Int16TreeT; + typedef tree::ValueAccessor<const Int16TreeT> Int16AccessorT; - ++idx; - } - } + typedef typename TreeT::template ValueConverter<float>::Type FloatTreeT; + typedef Grid<FloatTreeT> FloatGridT; - while(auxLeaf.onVoxelCount() > 0) { - avg[0] = 0; - avg[1] = 0; - avg[2] = 0; + ////////// - auxIter = auxLeaf.beginValueOn(); - int regionId = auxIter.getValue(), points = 0; + MergeVoxelRegions(const LeafManagerT& signLeafs, const Int16TreeT& signTree, + const TreeT& distTree, IntTreeT& idxTree, ValueT iso, ValueT adaptivity); - for (; auxIter; ++auxIter) { - if(auxIter.getValue() == regionId) { + void run(bool threaded = true); - auxIter.setValue(idx); - auxIter.setValueOff(); - ijk = auxIter.getCoord(); + void setSpatialAdaptivity( + const math::Transform& distGridXForm, const FloatGridT& adaptivityField); - if (hasRefData && maskLeaf && maskLeaf->isValueOn(ijk)) { - calcAvgPoint(*refDistAcc.get(), ijk, tmp); - } else { - calcAvgPoint(distAcc, ijk, tmp); - } + void setAdaptivityMask(const BoolTreeT* mask); - avg += tmp; - ++points; - } - } + void setRefData(const Int16TreeT* signTree, ValueT adaptivity); - if (points > 1) { - double w = 1.0 / double(points); - avg[0] *= w; - avg[1] *= w; - avg[2] *= w; - } + ////////// - openvdb::Vec3s& ptn = mPoints[idx]; - ptn[0] = float(avg[0]); - ptn[1] = float(avg[1]); - ptn[2] = float(avg[2]); - ++idx; - } - } + + void operator()(const tbb::blocked_range<size_t>&) const; + +private: + + const LeafManagerT& mSignLeafs; + + const Int16TreeT& mSignTree; + Int16AccessorT mSignAcc; + + const TreeT& mDistTree; + AccessorT mDistAcc; + + IntTreeT& mIdxTree; + ValueT mIsovalue, mSurfaceAdaptivity, mInternalAdaptivity; + + const math::Transform* mTransform; + const FloatGridT* mAdaptivityGrid; + const BoolTreeT* mMask; + + const Int16TreeT* mRefSignTree; +}; + +template <typename TreeT, typename LeafManagerT> +MergeVoxelRegions<TreeT, LeafManagerT>::MergeVoxelRegions( + const LeafManagerT& signLeafs, const Int16TreeT& signTree, + const TreeT& distTree, IntTreeT& idxTree, ValueT iso, ValueT adaptivity) + : mSignLeafs(signLeafs) + , mSignTree(signTree) + , mSignAcc(mSignTree) + , mDistTree(distTree) + , mDistAcc(mDistTree) + , mIdxTree(idxTree) + , mIsovalue(iso) + , mSurfaceAdaptivity(adaptivity) + , mInternalAdaptivity(adaptivity) + , mTransform(NULL) + , mAdaptivityGrid(NULL) + , mMask(NULL) + , mRefSignTree(NULL) +{ } -template <class DistTreeT> -int -PointGen<DistTreeT>::calcAvgPoint(DistTreeAccessorT& acc, - const Coord& ijk, openvdb::Vec3d& avg) const + +template <typename TreeT, typename LeafManagerT> +void +MergeVoxelRegions<TreeT, LeafManagerT>::run(bool threaded) { - double values[8]; - bool signMask[8]; - Coord coord; + if (threaded) tbb::parallel_for(mSignLeafs.getRange(), *this); + else (*this)(mSignLeafs.getRange()); +} - // Sample corner values - coord = ijk; - values[0] = double(acc.getValue(coord)); // i, j, k - coord[0] += 1; - values[1] = double(acc.getValue(coord)); // i+1, j, k +template <typename TreeT, typename LeafManagerT> +void +MergeVoxelRegions<TreeT, LeafManagerT>::setSpatialAdaptivity( + const math::Transform& distGridXForm, const FloatGridT& adaptivityField) +{ + mTransform = &distGridXForm; + mAdaptivityGrid = &adaptivityField; +} - coord[2] += 1; - values[2] = double(acc.getValue(coord)); // i+i, j, k+1 - coord[0] = ijk[0]; - values[3] = double(acc.getValue(coord)); // i, j, k+1 +template <typename TreeT, typename LeafManagerT> +void +MergeVoxelRegions<TreeT, LeafManagerT>::setAdaptivityMask(const BoolTreeT* mask) +{ + mMask = mask; +} - coord[1] += 1; coord[2] = ijk[2]; - values[4] = double(acc.getValue(coord)); // i, j+1, k +template <typename TreeT, typename LeafManagerT> +void +MergeVoxelRegions<TreeT, LeafManagerT>::setRefData(const Int16TreeT* signTree, ValueT adaptivity) +{ + mRefSignTree = signTree; + mInternalAdaptivity = adaptivity; +} - coord[0] += 1; - values[5] = double(acc.getValue(coord)); // i+1, j+1, k - coord[2] += 1; - values[6] = double(acc.getValue(coord)); // i+1, j+1, k+1 +template <typename TreeT, typename LeafManagerT> +void +MergeVoxelRegions<TreeT, LeafManagerT>::operator()(const tbb::blocked_range<size_t>& range) const +{ + typedef math::Vec3<ValueT> Vec3T; - coord[0] = ijk[0]; - values[7] = double(acc.getValue(coord)); // i, j+1, k+1 + typedef typename TreeT::LeafNodeType LeafT; + typedef typename IntTreeT::LeafNodeType IntLeafT; + typedef typename BoolTreeT::LeafNodeType BoolLeafT; + typedef typename LeafT::template ValueConverter<Vec3T>::Type Vec3LeafT; - // init sign mask - for (int n = 0; n < 8; ++n) signMask[n] = (values[n] < mIsovalue); + const int LeafDim = LeafT::DIM; - int samples = 0, edgeFlags = 0; - avg[0] = 0.0; - avg[1] = 0.0; - avg[2] = 0.0; + IntAccessorT idxAcc(mIdxTree); - if (signMask[0] != signMask[1]) { // Edged: 0 - 1 - avg[0] += root(values[0], values[1]); - ++samples; - edgeFlags |= XEDGE; - } + typename LeafManagerT::TreeType::LeafNodeType::ValueOnCIter iter; - if (signMask[1] != signMask[2]) { // Edged: 1 - 2 - avg[0] += 1.0; - avg[2] += root(values[1], values[2]); - ++samples; - edgeFlags |= ZEDGE; + typedef typename tree::ValueAccessor<const FloatTreeT> FloatTreeCAccessorT; + boost::scoped_ptr<FloatTreeCAccessorT> adaptivityAcc; + if (mAdaptivityGrid) { + adaptivityAcc.reset(new FloatTreeCAccessorT(mAdaptivityGrid->tree())); } - if (signMask[3] != signMask[2]) { // Edged: 3 - 2 - avg[0] += root(values[3], values[2]); - avg[2] += 1.0; - ++samples; - edgeFlags |= XEDGE; + typedef typename tree::ValueAccessor<const Int16TreeT> Int16TreeCAccessorT; + boost::scoped_ptr<Int16TreeCAccessorT> refAcc; + if (mRefSignTree) { + refAcc.reset(new Int16TreeCAccessorT(*mRefSignTree)); } - if (signMask[0] != signMask[3]) { // Edged: 0 - 3 - avg[2] += root(values[0], values[3]); - ++samples; - edgeFlags |= ZEDGE; + typedef typename tree::ValueAccessor<const BoolTreeT> BoolTreeCAccessorT; + boost::scoped_ptr<BoolTreeCAccessorT> maskAcc; + if (mMask) { + maskAcc.reset(new BoolTreeCAccessorT(*mMask)); } - if (signMask[4] != signMask[5]) { // Edged: 4 - 5 - avg[0] += root(values[4], values[5]); - avg[1] += 1.0; - ++samples; - edgeFlags |= XEDGE; - } + // Allocate reusable leaf buffers + BoolLeafT mask; + Vec3LeafT gradientBuffer; + Coord ijk, nijk, coord, end; - if (signMask[5] != signMask[6]) { // Edged: 5 - 6 - avg[0] += 1.0; - avg[1] += 1.0; - avg[2] += root(values[5], values[6]); - ++samples; - edgeFlags |= ZEDGE; - } + for (size_t n = range.begin(); n != range.end(); ++n) { - if (signMask[7] != signMask[6]) { // Edged: 7 - 6 - avg[0] += root(values[7], values[6]); - avg[1] += 1.0; - avg[2] += 1.0; - ++samples; - edgeFlags |= XEDGE; - } + const Coord& origin = mSignLeafs.leaf(n).origin(); - if (signMask[4] != signMask[7]) { // Edged: 4 - 7 - avg[1] += 1.0; - avg[2] += root(values[4], values[7]); - ++samples; - edgeFlags |= ZEDGE; - } + ValueT adaptivity = mSurfaceAdaptivity; - if (signMask[0] != signMask[4]) { // Edged: 0 - 4 - avg[1] += root(values[0], values[4]); - ++samples; - edgeFlags |= YEDGE; - } + if (refAcc && refAcc->probeConstLeaf(origin) == NULL) { + adaptivity = mInternalAdaptivity; + } - if (signMask[1] != signMask[5]) { // Edged: 1 - 5 - avg[0] += 1.0; - avg[1] += root(values[1], values[5]); - ++samples; - edgeFlags |= YEDGE; - } + IntLeafT& idxLeaf = *idxAcc.probeLeaf(origin); - if (signMask[2] != signMask[6]) { // Edged: 2 - 6 - avg[0] += 1.0; - avg[1] += root(values[2], values[6]); - avg[2] += 1.0; - ++samples; - edgeFlags |= YEDGE; - } + end[0] = origin[0] + LeafDim; + end[1] = origin[1] + LeafDim; + end[2] = origin[2] + LeafDim; - if (signMask[3] != signMask[7]) { // Edged: 3 - 7 - avg[1] += root(values[3], values[7]); - avg[2] += 1.0; - ++samples; - edgeFlags |= YEDGE; - } + mask.setValuesOff(); + + // Mask off seam line adjacent voxels + if (maskAcc) { + const BoolLeafT* maskLeaf = maskAcc->probeConstLeaf(origin); + if (maskLeaf != NULL) { + typename BoolLeafT::ValueOnCIter it; + for (it = maskLeaf->cbeginValueOn(); it; ++it) { + ijk = it.getCoord(); + coord[0] = ijk[0] - (ijk[0] % 2); + coord[1] = ijk[1] - (ijk[1] % 2); + coord[2] = ijk[2] - (ijk[2] % 2); + mask.setActiveState(coord, true); + } + } + } - if (samples > 1) { - double w = 1.0 / double(samples); - avg[0] *= w; - avg[1] *= w; - avg[2] *= w; - } - // offset by cell-origin - avg[0] += double(ijk[0]); - avg[1] += double(ijk[1]); - avg[2] += double(ijk[2]); + LeafT adaptivityLeaf(origin, adaptivity); - avg = mTransform.indexToWorld(avg); + if (mAdaptivityGrid) { + for (Index offset = 0; offset < LeafT::NUM_VALUES; ++offset) { + ijk = adaptivityLeaf.offsetToGlobalCoord(offset); + Vec3d xyz = mAdaptivityGrid->transform().worldToIndex( + mTransform->indexToWorld(ijk)); + ValueT tmpA = ValueT(adaptivityAcc->getValue(util::nearestCoord(xyz))); + adaptivityLeaf.setValueOnly(offset, tmpA * adaptivity); + } + } - return edgeFlags; + // Mask off ambiguous voxels + for (iter = mSignLeafs.leaf(n).cbeginValueOn(); iter; ++iter) { + ijk = iter.getCoord(); + coord[0] = ijk[0] - (ijk[0] % 2); + coord[1] = ijk[1] - (ijk[1] % 2); + coord[2] = ijk[2] - (ijk[2] % 2); + if(mask.isValueOn(coord)) continue; + + + + int flags = int(iter.getValue()); + unsigned char signs = SIGNS & flags; + if ((flags & SEAM) || !sAdaptable[signs] || sEdgeGroupTable[signs][0] > 1) { + mask.setActiveState(coord, true); + continue; + } + + for (int i = 0; i < 26; ++i) { + nijk = ijk + util::COORD_OFFSETS[i]; + signs = SIGNS & mSignAcc.getValue(nijk); + if (!sAdaptable[signs] || sEdgeGroupTable[signs][0] > 1) { + mask.setActiveState(coord, true); + break; + } + } + } + + int dim = 2; + // Mask off topologically ambiguous 2x2x2 voxel sub-blocks + for (ijk[0] = origin[0]; ijk[0] < end[0]; ijk[0] += dim) { + for (ijk[1] = origin[1]; ijk[1] < end[1]; ijk[1] += dim) { + for (ijk[2] = origin[2]; ijk[2] < end[2]; ijk[2] += dim) { + if (isNonManifold(mDistAcc, ijk, mIsovalue, dim)) { + mask.setActiveState(ijk, true); + } + } + } + } + + // Compute the gradient for the remaining voxels + gradientBuffer.setValuesOff(); + for (iter = mSignLeafs.leaf(n).cbeginValueOn(); iter; ++iter) { + + ijk = iter.getCoord(); + coord[0] = ijk[0] - (ijk[0] % dim); + coord[1] = ijk[1] - (ijk[1] % dim); + coord[2] = ijk[2] - (ijk[2] % dim); + if(mask.isValueOn(coord)) continue; + + Vec3T norm(math::ISGradient<math::CD_2ND>::result(mDistAcc, ijk)); + // Normalize (Vec3's normalize uses isApproxEqual, which uses abs and does more work) + ValueT length = norm.length(); + if (length > ValueT(1.0e-7)) { + norm *= ValueT(1.0) / length; + } + gradientBuffer.setValue(ijk, norm); + } + + int regionId = 1, next_dim = dim << 1; + + // Process the first adaptivity level. + for (ijk[0] = 0; ijk[0] < LeafDim; ijk[0] += dim) { + coord[0] = ijk[0] - (ijk[0] % next_dim); + for (ijk[1] = 0; ijk[1] < LeafDim; ijk[1] += dim) { + coord[1] = ijk[1] - (ijk[1] % next_dim); + for (ijk[2] = 0; ijk[2] < LeafDim; ijk[2] += dim) { + coord[2] = ijk[2] - (ijk[2] % next_dim); + adaptivity = adaptivityLeaf.getValue(ijk); + if (mask.isValueOn(ijk) || !isMergable(gradientBuffer, ijk, dim, adaptivity)) { + mask.setActiveState(coord, true); + continue; + } + mergeVoxels(idxLeaf, ijk, dim, regionId++); + } + } + } + + + // Process remaining adaptivity levels + for (dim = 4; dim < LeafDim; dim = dim << 1) { + next_dim = dim << 1; + coord[0] = ijk[0] - (ijk[0] % next_dim); + for (ijk[0] = origin[0]; ijk[0] < end[0]; ijk[0] += dim) { + coord[1] = ijk[1] - (ijk[1] % next_dim); + for (ijk[1] = origin[1]; ijk[1] < end[1]; ijk[1] += dim) { + coord[2] = ijk[2] - (ijk[2] % next_dim); + for (ijk[2] = origin[2]; ijk[2] < end[2]; ijk[2] += dim) { + adaptivity = adaptivityLeaf.getValue(ijk); + if (mask.isValueOn(ijk) || isNonManifold(mDistAcc, ijk, mIsovalue, dim) || + !isMergable(gradientBuffer, ijk, dim, adaptivity)) + { + mask.setActiveState(coord, true); + continue; + } + mergeVoxels(idxLeaf, ijk, dim, regionId++); + } + } + } + } + + adaptivity = adaptivityLeaf.getValue(origin); + if (!(mask.isValueOn(origin) || isNonManifold(mDistAcc, origin, mIsovalue, LeafDim)) + && isMergable(gradientBuffer, origin, LeafDim, adaptivity)) + { + mergeVoxels(idxLeaf, origin, LeafDim, regionId++); + } + } } //////////////////////////////////////// -struct QuadMeshOp +// Constructs qudas +struct UniformPrimBuilder { - QuadMeshOp(): mIdx(0), mPolygonPool(NULL) {} + UniformPrimBuilder(): mIdx(0), mPolygonPool(NULL) {} void init(const size_t upperBound, PolygonPool& quadPool) { @@ -1331,7 +2353,10 @@ struct QuadMeshOp ++mIdx; } - void done() {} + void done() + { + mPolygonPool->trimQuads(mIdx); + } private: size_t mIdx; @@ -1339,16 +2364,16 @@ private: }; -struct AdaptiveMeshOp +// Constructs qudas and triangles +struct AdaptivePrimBuilder { - AdaptiveMeshOp(): mQuadIdx(0), mTriangleIdx(0), mPolygonPool(NULL), mTmpPolygonPool() {} + AdaptivePrimBuilder() : mQuadIdx(0), mTriangleIdx(0), mPolygonPool(NULL) {} void init(const size_t upperBound, PolygonPool& polygonPool) { mPolygonPool = &polygonPool; - - mTmpPolygonPool.resetQuads(upperBound); - mTmpPolygonPool.resetTriangles(upperBound); + mPolygonPool->resetQuads(upperBound); + mPolygonPool->resetTriangles(upperBound); mQuadIdx = 0; mTriangleIdx = 0; @@ -1358,35 +2383,35 @@ struct AdaptiveMeshOp { if (verts[0] != verts[1] && verts[0] != verts[2] && verts[0] != verts[3] && verts[1] != verts[2] && verts[1] != verts[3] && verts[2] != verts[3]) { - mTmpPolygonPool.quadFlags(mQuadIdx) = flags; + mPolygonPool->quadFlags(mQuadIdx) = flags; addQuad(verts, reverse); } else if ( verts[0] == verts[3] && verts[1] != verts[2] && verts[1] != verts[0] && verts[2] != verts[0]) { - mTmpPolygonPool.triangleFlags(mTriangleIdx) = flags; + mPolygonPool->triangleFlags(mTriangleIdx) = flags; addTriangle(verts[0], verts[1], verts[2], reverse); } else if ( verts[1] == verts[2] && verts[0] != verts[3] && verts[0] != verts[1] && verts[3] != verts[1]) { - mTmpPolygonPool.triangleFlags(mTriangleIdx) = flags; + mPolygonPool->triangleFlags(mTriangleIdx) = flags; addTriangle(verts[0], verts[1], verts[3], reverse); } else if ( verts[0] == verts[1] && verts[2] != verts[3] && verts[2] != verts[0] && verts[3] != verts[0]) { - mTmpPolygonPool.triangleFlags(mTriangleIdx) = flags; + mPolygonPool->triangleFlags(mTriangleIdx) = flags; addTriangle(verts[0], verts[2], verts[3], reverse); } else if ( verts[2] == verts[3] && verts[0] != verts[1] && verts[0] != verts[2] && verts[1] != verts[2]) { - mTmpPolygonPool.triangleFlags(mTriangleIdx) = flags; + mPolygonPool->triangleFlags(mTriangleIdx) = flags; addTriangle(verts[0], verts[1], verts[2], reverse); } } @@ -1394,19 +2419,8 @@ struct AdaptiveMeshOp void done() { - mPolygonPool->resetQuads(mQuadIdx); - for (size_t i = 0; i < mQuadIdx; ++i) { - mPolygonPool->quad(i) = mTmpPolygonPool.quad(i); - mPolygonPool->quadFlags(i) = mTmpPolygonPool.quadFlags(i); - } - mTmpPolygonPool.clearQuads(); - - mPolygonPool->resetTriangles(mTriangleIdx); - for (size_t i = 0; i < mTriangleIdx; ++i) { - mPolygonPool->triangle(i) = mTmpPolygonPool.triangle(i); - mPolygonPool->triangleFlags(i) = mTmpPolygonPool.triangleFlags(i); - } - mTmpPolygonPool.clearTriangles(); + mPolygonPool->trimQuads(mQuadIdx, /*reallocate=*/true); + mPolygonPool->trimTrinagles(mTriangleIdx, /*reallocate=*/true); } private: @@ -1414,9 +2428,9 @@ private: void addQuad(const Vec4I& verts, bool reverse) { if (!reverse) { - mTmpPolygonPool.quad(mQuadIdx) = verts; + mPolygonPool->quad(mQuadIdx) = verts; } else { - Vec4I& quad = mTmpPolygonPool.quad(mQuadIdx); + Vec4I& quad = mPolygonPool->quad(mQuadIdx); quad[0] = verts[3]; quad[1] = verts[2]; quad[2] = verts[1]; @@ -1427,7 +2441,7 @@ private: void addTriangle(unsigned v0, unsigned v1, unsigned v2, bool reverse) { - Vec3I& prim = mTmpPolygonPool.triangle(mTriangleIdx); + Vec3I& prim = mPolygonPool->triangle(mTriangleIdx); prim[1] = v1; @@ -1443,206 +2457,295 @@ private: size_t mQuadIdx, mTriangleIdx; PolygonPool *mPolygonPool; - PolygonPool mTmpPolygonPool; }; -//////////////////////////////////////// +template<typename SignAccT, typename IdxAccT, typename PrimBuilder> +inline void +constructPolygons(Int16 flags, Int16 refFlags, const Vec4i& offsets, const Coord& ijk, + const SignAccT& signAcc, const IdxAccT& idxAcc, PrimBuilder& mesher, Index32 pointListSize) +{ + const Index32 v0 = idxAcc.getValue(ijk); + if (v0 == util::INVALID_IDX) return; + char tag[2]; + tag[0] = (flags & SEAM) ? POLYFLAG_FRACTURE_SEAM : 0; + tag[1] = tag[0] | char(POLYFLAG_EXTERIOR); -template<class DistTreeT, class MeshingOp> -class MeshGen -{ -public: - typedef typename DistTreeT::template ValueConverter<char>::Type CharTreeT; - typedef typename DistTreeT::template ValueConverter<int>::Type IntTreeT; + const bool isInside = flags & INSIDE; - MeshGen(const LeafCPtrList<CharTreeT>& edgeLeafs, const IntTreeT& auxTree, PolygonPoolList&); - MeshGen(const MeshGen<DistTreeT, MeshingOp>&); + Coord coord; + openvdb::Vec4I quad; + unsigned char cell; + Index32 tmpIdx = 0; - void setRefData(const ReferenceData<DistTreeT>&); + if (flags & XEDGE) { - void runParallel(); - void runSerial(); + quad[0] = v0 + offsets[0]; - void operator()(const tbb::blocked_range<size_t>&) const; + // i, j-1, k + coord[0] = ijk[0]; + coord[1] = ijk[1] - 1; + coord[2] = ijk[2]; -private: - const LeafCPtrList<CharTreeT>& mEdgeLeafs; - const IntTreeT& mAuxTree; - const PolygonPoolList& mPolygonPoolList; - size_t mID; - const ReferenceData<DistTreeT>* mRefData; -}; + quad[1] = idxAcc.getValue(coord); + cell = SIGNS & signAcc.getValue(coord); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[1] + Index32(sEdgeGroupTable[cell][5] - 1); + if (tmpIdx < pointListSize) quad[1] = tmpIdx; + } + // i, j-1, k-1 + coord[2] -= 1; -template<class DistTreeT, class MeshingOp> -MeshGen<DistTreeT, MeshingOp>::MeshGen(const LeafCPtrList<CharTreeT>& edgeLeafs, - const IntTreeT& auxTree, PolygonPoolList& polygonPoolList) - : mEdgeLeafs(edgeLeafs) - , mAuxTree(auxTree) - , mPolygonPoolList(polygonPoolList) - , mRefData(NULL) -{ -} + quad[2] = idxAcc.getValue(coord); + cell = SIGNS & signAcc.getValue(coord); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[2] + Index32(sEdgeGroupTable[cell][7] - 1); + if (tmpIdx < pointListSize) quad[2] = tmpIdx; + } + // i, j, k-1 + coord[1] = ijk[1]; -template<class DistTreeT, class MeshingOp> -MeshGen<DistTreeT, MeshingOp>::MeshGen(const MeshGen<DistTreeT, MeshingOp>& rhs) - : mEdgeLeafs(rhs.mEdgeLeafs) - , mAuxTree(rhs.mAuxTree) - , mPolygonPoolList(rhs.mPolygonPoolList) - , mRefData(rhs.mRefData) -{ + quad[3] = idxAcc.getValue(coord); + cell = SIGNS & signAcc.getValue(coord); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[3] + Index32(sEdgeGroupTable[cell][3] - 1); + if (tmpIdx < pointListSize) quad[3] = tmpIdx; + } + + if (quad[1] != util::INVALID_IDX && + quad[2] != util::INVALID_IDX && quad[3] != util::INVALID_IDX) { + mesher.addPrim(quad, isInside, tag[bool(refFlags & XEDGE)]); + } + } + + + if (flags & YEDGE) { + + quad[0] = v0 + offsets[1]; + + // i, j, k-1 + coord[0] = ijk[0]; + coord[1] = ijk[1]; + coord[2] = ijk[2] - 1; + + quad[1] = idxAcc.getValue(coord); + cell = SIGNS & signAcc.getValue(coord); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[1] + Index32(sEdgeGroupTable[cell][12] - 1); + if (tmpIdx < pointListSize) quad[1] = tmpIdx; + } + + // i-1, j, k-1 + coord[0] -= 1; + + quad[2] = idxAcc.getValue(coord); + cell = SIGNS & signAcc.getValue(coord); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[2] + Index32(sEdgeGroupTable[cell][11] - 1); + if (tmpIdx < pointListSize) quad[2] = tmpIdx; + } + + // i-1, j, k + coord[2] = ijk[2]; + + quad[3] = idxAcc.getValue(coord); + cell = SIGNS & signAcc.getValue(coord); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[3] + Index32(sEdgeGroupTable[cell][10] - 1); + if (tmpIdx < pointListSize) quad[3] = tmpIdx; + } + + if (quad[1] != util::INVALID_IDX && + quad[2] != util::INVALID_IDX && quad[3] != util::INVALID_IDX) { + mesher.addPrim(quad, isInside, tag[bool(refFlags & YEDGE)]); + } + } + + if (flags & ZEDGE) { + + quad[0] = v0 + offsets[2]; + + // i, j-1, k + coord[0] = ijk[0]; + coord[1] = ijk[1] - 1; + coord[2] = ijk[2]; + + quad[1] = idxAcc.getValue(coord); + cell = SIGNS & signAcc.getValue(coord); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[1] + Index32(sEdgeGroupTable[cell][8] - 1); + if (tmpIdx < pointListSize) quad[1] = tmpIdx; + } + + // i-1, j-1, k + coord[0] -= 1; + + quad[2] = idxAcc.getValue(coord); + cell = SIGNS & signAcc.getValue(coord); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[2] + Index32(sEdgeGroupTable[cell][6] - 1); + if (tmpIdx < pointListSize) quad[2] = tmpIdx; + } + + // i-1, j, k + coord[1] = ijk[1]; + + quad[3] = idxAcc.getValue(coord); + cell = SIGNS & signAcc.getValue(coord); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[3] + Index32(sEdgeGroupTable[cell][2] - 1); + if (tmpIdx < pointListSize) quad[3] = tmpIdx; + } + + if (quad[1] != util::INVALID_IDX && + quad[2] != util::INVALID_IDX && quad[3] != util::INVALID_IDX) { + mesher.addPrim(quad, !isInside, tag[bool(refFlags & ZEDGE)]); + } + } } -template<class DistTreeT, class MeshingOp> -void -MeshGen<DistTreeT, MeshingOp>::setRefData( - const ReferenceData<DistTreeT>& refData) +//////////////////////////////////////// + + +template<typename LeafManagerT, typename PrimBuilder> +class GenPolygons { - mRefData = &refData; -} +public: + typedef typename LeafManagerT::TreeType::template ValueConverter<int>::Type IntTreeT; + typedef typename LeafManagerT::TreeType::template ValueConverter<Int16>::Type Int16TreeT; + typedef tree::ValueAccessor<const IntTreeT> IntAccessorT; + typedef tree::ValueAccessor<const Int16TreeT> Int16AccessorT; -template<class DistTreeT, class MeshingOp> -void -MeshGen<DistTreeT, MeshingOp>::runParallel() + ////////// + + + GenPolygons(const LeafManagerT& signLeafs, const Int16TreeT& signTree, + const IntTreeT& idxTree, PolygonPoolList& polygons, Index32 pointListSize); + + void run(bool threaded = true); + + + void setRefSignTree(const Int16TreeT *r) { mRefSignTree = r; } + + ////////// + + + void operator()(const tbb::blocked_range<size_t>&) const; + +private: + const LeafManagerT& mSignLeafs; + const Int16TreeT& mSignTree; + const IntTreeT& mIdxTree; + const PolygonPoolList& mPolygonPoolList; + const Index32 mPointListSize; + + const Int16TreeT *mRefSignTree; + }; + + +template<typename LeafManagerT, typename PrimBuilder> +GenPolygons<LeafManagerT, PrimBuilder>::GenPolygons(const LeafManagerT& signLeafs, + const Int16TreeT& signTree, const IntTreeT& idxTree, PolygonPoolList& polygons, + Index32 pointListSize) + : mSignLeafs(signLeafs) + , mSignTree(signTree) + , mIdxTree(idxTree) + , mPolygonPoolList(polygons) + , mPointListSize(pointListSize) + , mRefSignTree(NULL) { - tbb::parallel_for(mEdgeLeafs.getRange(), *this); } - -template<class DistTreeT, class MeshingOp> +template<typename LeafManagerT, typename PrimBuilder> void -MeshGen<DistTreeT, MeshingOp>::runSerial() +GenPolygons<LeafManagerT, PrimBuilder>::run(bool threaded) { - (*this)(mEdgeLeafs.getRange()); + if (threaded) tbb::parallel_for(mSignLeafs.getRange(), *this); + else (*this)(mSignLeafs.getRange()); } - -template<class DistTreeT, class MeshingOp> +template<typename LeafManagerT, typename PrimBuilder> void -MeshGen<DistTreeT, MeshingOp>::operator()( +GenPolygons<LeafManagerT, PrimBuilder>::operator()( const tbb::blocked_range<size_t>& range) const { - typedef typename DistTreeT::template ValueConverter<bool>::Type BoolTreeT; - typedef typename BoolTreeT::LeafNodeType BoolLeafT; - typedef typename CharTreeT::LeafNodeType CharLeafT; + typename LeafManagerT::TreeType::LeafNodeType::ValueOnCIter iter; + IntAccessorT idxAcc(mIdxTree); + Int16AccessorT signAcc(mSignTree); - typedef openvdb::tree::ValueAccessor<const CharTreeT> CharTreeAccessorT; - typedef openvdb::tree::ValueAccessor<const IntTreeT> IntTreeAccessorT; - typedef openvdb::tree::ValueAccessor<const BoolTreeT> BoolTreeAccessorT; - boost::scoped_ptr<CharTreeAccessorT> refEdgeAcc; - boost::scoped_ptr<BoolTreeAccessorT> refMaskAcc; - if (mRefData && mRefData->isValid()) { - refEdgeAcc.reset(new CharTreeAccessorT(*mRefData->mEdgeTree)); - refMaskAcc.reset(new BoolTreeAccessorT(*mRefData->mSeamMaskTree.get())); - } - const bool hasRefData = refEdgeAcc && refMaskAcc; + PrimBuilder mesher; + size_t edgeCount; + Coord ijk, origin; - typename CharTreeT::LeafNodeType::ValueOnCIter iter; - IntTreeAccessorT auxAcc(mAuxTree); + // reference data + boost::scoped_ptr<Int16AccessorT> refSignAcc; + if (mRefSignTree) refSignAcc.reset(new Int16AccessorT(*mRefSignTree)); - Coord ijk, coord; - char refEdgeFlags, isSemLinePoly; - const char isExteriorPoly[2] = {0, char(POLYFLAG_EXTERIOR)}; - openvdb::Vec4I quad; - size_t edgeCount; - - MeshingOp mesher; for (size_t n = range.begin(); n != range.end(); ++n) { - const Coord origin = mEdgeLeafs[n]->getOrigin(); + origin = mSignLeafs.leaf(n).origin(); // Get an upper bound on the number of primitives. edgeCount = 0; - iter = mEdgeLeafs[n]->cbeginValueOn(); + iter = mSignLeafs.leaf(n).cbeginValueOn(); for (; iter; ++iter) { - char edgeFlags = iter.getValue() >> 1; - edgeCount += edgeFlags & 0x1; - - edgeFlags = edgeFlags >> 1; - edgeCount += edgeFlags & 0x1; - - edgeFlags = edgeFlags >> 1; - edgeCount += edgeFlags & 0x1; + if (iter.getValue() & XEDGE) ++edgeCount; + if (iter.getValue() & YEDGE) ++edgeCount; + if (iter.getValue() & ZEDGE) ++edgeCount; } + if(edgeCount == 0) continue; + mesher.init(edgeCount, mPolygonPoolList[n]); - const CharLeafT* refEdgeLeaf = NULL; - const BoolLeafT* refMaskLeaf = NULL; + const typename Int16TreeT::LeafNodeType *signleafPt = signAcc.probeConstLeaf(origin); + const typename IntTreeT::LeafNodeType *idxLeafPt = idxAcc.probeConstLeaf(origin); - if (hasRefData) { - refEdgeLeaf = refEdgeAcc->probeConstLeaf(origin); - refMaskLeaf = refMaskAcc->probeConstLeaf(origin); - } + if (!signleafPt || !idxLeafPt) continue; - iter = mEdgeLeafs[n]->cbeginValueOn(); - for (; iter; ++iter) { - ijk = iter.getCoord(); - const char& edgeFlags = iter.getValue(); - const bool isInside = edgeFlags & INSIDE; - - refEdgeFlags = 0; - isSemLinePoly = 0; - if (hasRefData) { - if(refEdgeLeaf) refEdgeFlags = refEdgeLeaf->getValue(ijk); - if (refMaskLeaf && refMaskLeaf->isValueOn(ijk)) { - isSemLinePoly = char(POLYFLAG_FRACTURE_SEAM); - } - } + const typename Int16TreeT::LeafNodeType *refSignLeafPt = NULL; + if (refSignAcc) refSignLeafPt = refSignAcc->probeConstLeaf(origin); + Vec4i offsets; - int v0 = auxAcc.getValue(ijk); + iter = mSignLeafs.leaf(n).cbeginValueOn(); + for (; iter; ++iter) { + ijk = iter.getCoord(); - if (edgeFlags & XEDGE) { + Int16 flags = iter.getValue(); - quad[0] = v0; - coord[0] = ijk[0]; coord[1] = ijk[1]-1; coord[2] = ijk[2]; // i, j-1, k - quad[1] = auxAcc.getValue(coord); - coord[2] -= 1; // i, j-1, k-1 - quad[2] = auxAcc.getValue(coord); - coord[1] = ijk[1]; // i, j, k-1 - quad[3] = auxAcc.getValue(coord); + if (!(flags & 0xE00)) continue; - mesher.addPrim(quad, isInside, - (isSemLinePoly | isExteriorPoly[bool(refEdgeFlags & XEDGE)])); + Int16 refFlags = 0; + if (refSignLeafPt) { + refFlags = refSignLeafPt->getValue(iter.pos()); } + offsets[0] = 0; + offsets[1] = 0; + offsets[2] = 0; - if (edgeFlags & YEDGE) { - - quad[0] = v0; - coord[0] = ijk[0]; coord[1] = ijk[1]; coord[2] = ijk[2]-1; // i, j, k-1 - quad[1] = auxAcc.getValue(coord); - coord[0] -= 1; // i-1, j, k-1 - quad[2] = auxAcc.getValue(coord); - coord[2] = ijk[2]; // i-1, j, k - quad[3] = auxAcc.getValue(coord); + const unsigned char cell = (SIGNS & flags); - mesher.addPrim(quad, isInside, - (isSemLinePoly | isExteriorPoly[bool(refEdgeFlags & YEDGE)])); + if (sEdgeGroupTable[cell][0] > 1) { + offsets[0] = (sEdgeGroupTable[cell][1] - 1); + offsets[1] = (sEdgeGroupTable[cell][9] - 1); + offsets[2] = (sEdgeGroupTable[cell][4] - 1); } - if (edgeFlags & ZEDGE) { - - quad[0] = v0; - coord[0] = ijk[0]; coord[1] = ijk[1]-1; coord[2] = ijk[2]; // i, j-1, k - quad[1] = auxAcc.getValue(coord); - coord[0] -= 1; // i-1, j-1, k - quad[2] = auxAcc.getValue(coord); - coord[1] = ijk[1]; // i, j, k - quad[3] = auxAcc.getValue(coord); - - mesher.addPrim(quad, !isInside, - (isSemLinePoly | isExteriorPoly[bool(refEdgeFlags & ZEDGE)])); + if (ijk[0] > origin[0] && ijk[1] > origin[1] && ijk[2] > origin[2]) { + constructPolygons(flags, refFlags, offsets, ijk, *signleafPt, *idxLeafPt, mesher, mPointListSize); + } else { + constructPolygons(flags, refFlags, offsets, ijk, signAcc, idxAcc, mesher, mPointListSize); } } @@ -1653,474 +2756,699 @@ MeshGen<DistTreeT, MeshingOp>::operator()( //////////////////////////////////////// +// Masking and mesh partitioning -template<class DistTreeT, class AuxDataT = int> -class AuxiliaryData +struct PartOp { -public: - typedef openvdb::tree::ValueAccessor<const DistTreeT> SourceAccessorT; - typedef typename DistTreeT::ValueType ValueT; - typedef typename DistTreeT::template ValueConverter<char>::Type CharTreeT; - typedef openvdb::tree::ValueAccessor<CharTreeT> EdgeAccessorT; + PartOp(size_t leafCount, size_t partitions, size_t activePart) + { + size_t leafSegments = leafCount / partitions; + mStart = leafSegments * activePart; + mEnd = activePart >= (partitions - 1) ? leafCount : mStart + leafSegments; + } - typedef typename DistTreeT::template ValueConverter<AuxDataT>::Type AuxTreeT; - typedef openvdb::tree::ValueAccessor<AuxTreeT> AuxAccessorT; + template <typename LeafNodeType> + void operator()(LeafNodeType &leaf, size_t leafIndex) const + { + if (leafIndex < mStart || leafIndex >= mEnd) leaf.setValuesOff(); + } - AuxiliaryData(const DistTreeT&, const LeafCPtrList<DistTreeT>&, - double iso = 0.0, bool extraCheck = false); - AuxiliaryData(AuxiliaryData&, tbb::split); +private: + size_t mStart, mEnd; +}; - void runParallel(); - void runSerial(); - typename CharTreeT::Ptr edgeTree() const { return mEdgeTree; } - typename AuxTreeT::Ptr auxTree() const { return mAuxTree; } +//////////////////////////////////////// - void operator()(const tbb::blocked_range<size_t>&); - void join(const AuxiliaryData& rhs) - { - mEdgeTree->merge(*rhs.mEdgeTree); - mAuxTree->merge(*rhs.mAuxTree); - } +template<typename SrcTreeT> +class PartGen +{ +public: + typedef tree::LeafManager<const SrcTreeT> LeafManagerT; + typedef typename SrcTreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef tree::ValueAccessor<BoolTreeT> BoolAccessorT; -private: - const LeafCPtrList<DistTreeT>& mLeafNodes; - const DistTreeT& mSourceTree; - SourceAccessorT mSourceAccessor; + ////////// - typename CharTreeT::Ptr mEdgeTree; - EdgeAccessorT mEdgeAccessor; - typename AuxTreeT::Ptr mAuxTree; - AuxAccessorT mAuxAccessor; + PartGen(const LeafManagerT& leafs, size_t partitions, size_t activePart); - const double mIsovalue; - const bool mExtraCheck; + void run(bool threaded = true); + + BoolTreeT& tree() { return mTree; } - int edgeCheck(const Coord& ijk, const bool thisInside); + + ////////// + + PartGen(PartGen&, tbb::split); + void operator()(const tbb::blocked_range<size_t>&); + void join(PartGen& rhs) { mTree.merge(rhs.mTree); } + +private: + const LeafManagerT& mLeafManager; + BoolTreeT mTree; + size_t mStart, mEnd; }; -template<class DistTreeT, class AuxDataT> -AuxiliaryData<DistTreeT, AuxDataT>::AuxiliaryData(const DistTreeT& tree, - const LeafCPtrList<DistTreeT>& leafNodes, double iso, bool extraCheck) - : mLeafNodes(leafNodes) - , mSourceTree(tree) - , mSourceAccessor(mSourceTree) - , mEdgeTree(new CharTreeT(0)) - , mEdgeAccessor(*mEdgeTree) - , mAuxTree(new AuxTreeT(AuxDataT(0))) - , mAuxAccessor(*mAuxTree) - , mIsovalue(iso) - , mExtraCheck(extraCheck) +template<typename SrcTreeT> +PartGen<SrcTreeT>::PartGen(const LeafManagerT& leafs, size_t partitions, size_t activePart) + : mLeafManager(leafs) + , mTree(false) + , mStart(0) + , mEnd(0) { + size_t leafCount = leafs.leafCount(); + size_t leafSegments = leafCount / partitions; + mStart = leafSegments * activePart; + mEnd = activePart >= (partitions - 1) ? leafCount : mStart + leafSegments; } -template<class DistTreeT, class AuxDataT> -AuxiliaryData<DistTreeT, AuxDataT>::AuxiliaryData(AuxiliaryData& rhs, tbb::split) - : mLeafNodes(rhs.mLeafNodes) - , mSourceTree(rhs.mSourceTree) - , mSourceAccessor(mSourceTree) - , mEdgeTree(new CharTreeT(0)) - , mEdgeAccessor(*mEdgeTree) - , mAuxTree(new AuxTreeT(AuxDataT(0))) - , mAuxAccessor(*mAuxTree) - , mIsovalue(rhs.mIsovalue) - , mExtraCheck(rhs.mExtraCheck) +template<typename SrcTreeT> +PartGen<SrcTreeT>::PartGen(PartGen& rhs, tbb::split) + : mLeafManager(rhs.mLeafManager) + , mTree(false) + , mStart(rhs.mStart) + , mEnd(rhs.mEnd) { } +template<typename SrcTreeT> +void +PartGen<SrcTreeT>::run(bool threaded) +{ + if (threaded) tbb::parallel_reduce(mLeafManager.getRange(), *this); + else (*this)(mLeafManager.getRange()); +} + -template<class DistTreeT, typename AuxDataT> +template<typename SrcTreeT> void -AuxiliaryData<DistTreeT, AuxDataT>::runParallel() +PartGen<SrcTreeT>::operator()(const tbb::blocked_range<size_t>& range) { - tbb::parallel_reduce(mLeafNodes.getRange(), *this); + Coord ijk; + BoolAccessorT acc(mTree); + + typedef typename BoolTreeT::LeafNodeType BoolLeafT; + typename SrcTreeT::LeafNodeType::ValueOnCIter iter; + + for (size_t n = range.begin(); n != range.end(); ++n) { + if (n < mStart || n >= mEnd) continue; + BoolLeafT* leaf = acc.touchLeaf(mLeafManager.leaf(n).origin()); + leaf->topologyUnion(mLeafManager.leaf(n)); + } } -template<class DistTreeT, typename AuxDataT> + +//////////////////////////////////////// + + +template<typename TreeT, typename LeafManagerT> +class GenSeamMask +{ +public: + typedef typename TreeT::template ValueConverter<bool>::Type BoolTreeT; + + ////////// + + GenSeamMask(const LeafManagerT& leafs, const TreeT& tree); + + void run(bool threaded = true); + + BoolTreeT& mask() { return mMaskTree; } + + ////////// + + GenSeamMask(GenSeamMask&, tbb::split); + void operator()(const tbb::blocked_range<size_t>&); + void join(GenSeamMask& rhs) { mMaskTree.merge(rhs.mMaskTree); } + +private: + + const LeafManagerT& mLeafManager; + const TreeT& mTree; + + BoolTreeT mMaskTree; +}; + + +template<typename TreeT, typename LeafManagerT> +GenSeamMask<TreeT, LeafManagerT>::GenSeamMask(const LeafManagerT& leafs, const TreeT& tree) + : mLeafManager(leafs) + , mTree(tree) + , mMaskTree(false) +{ +} + + +template<typename TreeT, typename LeafManagerT> +GenSeamMask<TreeT, LeafManagerT>::GenSeamMask(GenSeamMask& rhs, tbb::split) + : mLeafManager(rhs.mLeafManager) + , mTree(rhs.mTree) + , mMaskTree(false) +{ +} + + +template<typename TreeT, typename LeafManagerT> void -AuxiliaryData<DistTreeT, AuxDataT>::runSerial() +GenSeamMask<TreeT, LeafManagerT>::run(bool threaded) { - (*this)(mLeafNodes.getRange()); + if (threaded) tbb::parallel_reduce(mLeafManager.getRange(), *this); + else (*this)(mLeafManager.getRange()); } -template<class DistTreeT, typename AuxDataT> + +template<typename TreeT, typename LeafManagerT> void -AuxiliaryData<DistTreeT, AuxDataT>::operator()(const tbb::blocked_range<size_t>& range) +GenSeamMask<TreeT, LeafManagerT>::operator()(const tbb::blocked_range<size_t>& range) { - typename DistTreeT::LeafNodeType::ValueOnCIter iter; Coord ijk; - bool thisInside; - int edgeFlags; - ValueT val; + tree::ValueAccessor<const TreeT> acc(mTree); + tree::ValueAccessor<BoolTreeT> maskAcc(mMaskTree); - if (!mExtraCheck) { - for (size_t n = range.begin(); n != range.end(); ++n) { - for (iter = mLeafNodes[n]->cbeginValueOn(); iter; ++iter) { - ijk = iter.getCoord(); - thisInside = iter.getValue() < mIsovalue; - edgeFlags = edgeCheck(ijk, thisInside); + typename LeafManagerT::TreeType::LeafNodeType::ValueOnCIter it; + + for (size_t n = range.begin(); n != range.end(); ++n) { + + it = mLeafManager.leaf(n).cbeginValueOn(); + + for (; it; ++it) { + + ijk = it.getCoord(); - if (edgeFlags != 0) { - edgeFlags |= int(thisInside); - mEdgeAccessor.setValue(ijk, char(edgeFlags)); + unsigned char rhsSigns = acc.getValue(ijk) & SIGNS; + + if (sEdgeGroupTable[rhsSigns][0] > 0) { + unsigned char lhsSigns = it.getValue() & SIGNS; + if (rhsSigns != lhsSigns) { + maskAcc.setValueOn(ijk); } } } - } else { - for (size_t n = range.begin(); n != range.end(); ++n) { - for (iter = mLeafNodes[n]->cbeginValueOn(); iter; ++iter) { + } +} - ijk = iter.getCoord(); - thisInside = iter.getValue() < mIsovalue; - edgeFlags = edgeCheck(ijk, thisInside); - if (edgeFlags != 0) { - edgeFlags |= int(thisInside); - mEdgeAccessor.setValue(ijk, char(edgeFlags)); - } +//////////////////////////////////////// - --ijk[0]; - if (!mSourceAccessor.probeValue(ijk, val)) { - thisInside = val < mIsovalue; - edgeFlags = edgeCheck(ijk, thisInside); - if (edgeFlags != 0) { - edgeFlags |= int(thisInside); - mEdgeAccessor.setValue(ijk, char(edgeFlags)); - } - } +template<typename TreeT> +class TagSeamEdges +{ +public: + typedef tree::ValueAccessor<const TreeT> AccessorT; - ++ijk[0]; - --ijk[1]; - if (!mSourceAccessor.probeValue(ijk, val)) { - thisInside = val < mIsovalue; - edgeFlags = edgeCheck(ijk, thisInside); + TagSeamEdges(const TreeT& tree) : mAcc(tree) {} - if (edgeFlags != 0) { - edgeFlags |= int(thisInside); - mEdgeAccessor.setValue(ijk, char(edgeFlags)); - } - } + template <typename LeafNodeType> + void operator()(LeafNodeType &leaf, size_t/*leafIndex*/) const + { + const typename TreeT::LeafNodeType *maskLeaf = + mAcc.probeConstLeaf(leaf.origin()); - ++ijk[1]; - --ijk[2]; - if (!mSourceAccessor.probeValue(ijk, val)) { - thisInside = val < mIsovalue; - edgeFlags = edgeCheck(ijk, thisInside); + if (!maskLeaf) return; - if (edgeFlags != 0) { - edgeFlags |= int(thisInside); - mEdgeAccessor.setValue(ijk, char(edgeFlags)); - } - } + typename LeafNodeType::ValueOnIter it = leaf.beginValueOn(); + + for (; it; ++it) { + + if (maskLeaf->isValueOn(it.pos())) { + it.setValue(it.getValue() | SEAM); } } } -} -template<class DistTreeT, typename AuxDataT> -inline int -AuxiliaryData<DistTreeT, AuxDataT>::edgeCheck(const Coord& ijk, const bool thisInside) +private: + AccessorT mAcc; +}; + + + +template<typename BoolTreeT> +struct MaskEdges { - int edgeFlags = 0; - Coord n_ijk, coord; + typedef tree::ValueAccessor<const BoolTreeT> BoolAccessorT; + + MaskEdges(const BoolTreeT& valueMask) : mMaskAcc(valueMask) {} + + template <typename LeafNodeType> + void operator()(LeafNodeType &leaf, size_t /*leafIndex*/) const + { + typename LeafNodeType::ValueOnIter it = leaf.beginValueOn(); + + const typename BoolTreeT::LeafNodeType * maskLeaf = + mMaskAcc.probeConstLeaf(leaf.origin()); - // Eval upwind x-edge - n_ijk = ijk; ++n_ijk[0]; - bool otherInside = (mSourceAccessor.getValue(n_ijk) < mIsovalue); - if (otherInside != thisInside) { + if (maskLeaf) { + for (; it; ++it) { + if (!maskLeaf->isValueOn(it.pos())) { + it.setValue(0x1FF & it.getValue()); + } + } + } else { + for (; it; ++it) { + it.setValue(0x1FF & it.getValue()); + } + } + } - edgeFlags = XEDGE; +private: + BoolAccessorT mMaskAcc; +}; - mAuxAccessor.setActiveState(ijk, true); - coord[0] = ijk[0]; coord[1] = ijk[1]-1; coord[2] = ijk[2]; - mAuxAccessor.setActiveState(coord, true); +class FlagUsedPoints +{ +public: + ////////// - coord[0] = ijk[0]; coord[1] = ijk[1]-1; coord[2] = ijk[2]-1; - mAuxAccessor.setActiveState(coord, true); + FlagUsedPoints(const PolygonPoolList& polygons, size_t polyListCount, + std::vector<unsigned char>& usedPointMask) + : mPolygons(polygons) + , mPolyListCount(polyListCount) + , mUsedPointMask(usedPointMask) + { + } - coord[0] = ijk[0]; coord[1] = ijk[1]; coord[2] = ijk[2]-1; - mAuxAccessor.setActiveState(coord, true); + void run(bool threaded = true) + { + if (threaded) { + tbb::parallel_for(tbb::blocked_range<size_t>(0, mPolyListCount), *this); + } else { + (*this)(tbb::blocked_range<size_t>(0, mPolyListCount)); + } } - // Eval upwind y-edge - n_ijk[0] = ijk[0]; ++n_ijk[1]; - otherInside = (mSourceAccessor.getValue(n_ijk) < mIsovalue); - if (otherInside != thisInside) { + ////////// - edgeFlags |= YEDGE; + void operator()(const tbb::blocked_range<size_t>& range) const + { + // Concurrent writes to same memory address can occur, but + // all threads are writing the same value and char is atomic. + for (size_t n = range.begin(); n != range.end(); ++n) { + const PolygonPool& polygons = mPolygons[n]; + for (size_t i = 0; i < polygons.numQuads(); ++i) { + const Vec4I& quad = polygons.quad(i); + mUsedPointMask[quad[0]] = 1; + mUsedPointMask[quad[1]] = 1; + mUsedPointMask[quad[2]] = 1; + mUsedPointMask[quad[3]] = 1; + } - mAuxAccessor.setActiveState(ijk, true); + for (size_t i = 0; i < polygons.numTriangles(); ++i) { + const Vec3I& triangle = polygons.triangle(i); + mUsedPointMask[triangle[0]] = 1; + mUsedPointMask[triangle[1]] = 1; + mUsedPointMask[triangle[2]] = 1; + } + } + } - coord[0] = ijk[0]; coord[1] = ijk[1]; coord[2] = ijk[2]-1; - mAuxAccessor.setActiveState(coord, true); - coord[0] = ijk[0]-1; coord[1] = ijk[1]; coord[2] = ijk[2]; - mAuxAccessor.setActiveState(coord, true); +private: + const PolygonPoolList& mPolygons; + size_t mPolyListCount; + std::vector<unsigned char>& mUsedPointMask; +}; + +class RemapIndices +{ +public: + ////////// - coord[0] = ijk[0]-1; coord[1] = ijk[1]; coord[2] = ijk[2]-1; - mAuxAccessor.setActiveState(coord, true); + RemapIndices(PolygonPoolList& polygons, + size_t polyListCount, const std::vector<unsigned>& indexMap) + : mPolygons(polygons) + , mPolyListCount(polyListCount) + , mIndexMap(indexMap) + { } - // Eval upwind z-edge - n_ijk[1] = ijk[1]; ++n_ijk[2]; - otherInside = (mSourceAccessor.getValue(n_ijk) < mIsovalue); - if (otherInside != thisInside) { + void run(bool threaded = true) + { + if (threaded) { + tbb::parallel_for(tbb::blocked_range<size_t>(0, mPolyListCount), *this); + } else { + (*this)(tbb::blocked_range<size_t>(0, mPolyListCount)); + } + } - edgeFlags |= ZEDGE; + ////////// - mAuxAccessor.setActiveState(ijk, true); + void operator()(const tbb::blocked_range<size_t>& range) const + { + for (size_t n = range.begin(); n != range.end(); ++n) { + PolygonPool& polygons = mPolygons[n]; + for (size_t i = 0; i < polygons.numQuads(); ++i) { + Vec4I& quad = polygons.quad(i); + quad[0] = mIndexMap[quad[0]]; + quad[1] = mIndexMap[quad[1]]; + quad[2] = mIndexMap[quad[2]]; + quad[3] = mIndexMap[quad[3]]; + } - coord[0] = ijk[0]; coord[1] = ijk[1]-1; coord[2] = ijk[2]; - mAuxAccessor.setActiveState(coord, true); + for (size_t i = 0; i < polygons.numTriangles(); ++i) { + Vec3I& triangle = polygons.triangle(i); + triangle[0] = mIndexMap[triangle[0]]; + triangle[1] = mIndexMap[triangle[1]]; + triangle[2] = mIndexMap[triangle[2]]; + } + } + } + + +private: + PolygonPoolList& mPolygons; + size_t mPolyListCount; + const std::vector<unsigned>& mIndexMap; +}; - coord[0] = ijk[0]-1; coord[1] = ijk[1]; coord[2] = ijk[2]; - mAuxAccessor.setActiveState(coord, true); - coord[0] = ijk[0]-1; coord[1] = ijk[1]-1; coord[2] = ijk[2]; - mAuxAccessor.setActiveState(coord, true); +class MovePoints +{ +public: + ////////// + + MovePoints( + internal::UniquePtr<openvdb::Vec3s>::type& newPointList, + const PointList& oldPointList, + const std::vector<unsigned>& indexMap, + const std::vector<unsigned char>& usedPointMask) + : mNewPointList(newPointList) + , mOldPointList(oldPointList) + , mIndexMap(indexMap) + , mUsedPointMask(usedPointMask) + { + } + + void run(bool threaded = true) + { + if (threaded) { + tbb::parallel_for(tbb::blocked_range<size_t>(0, mIndexMap.size()), *this); + } else { + (*this)(tbb::blocked_range<size_t>(0, mIndexMap.size())); + } } - return edgeFlags; -} + + ////////// + + void operator()(const tbb::blocked_range<size_t>& range) const + { + for (size_t n = range.begin(); n != range.end(); ++n) { + if (mUsedPointMask[n]) { + const size_t index = mIndexMap[n]; + mNewPointList.get()[index] = mOldPointList[n]; + } + } + } + +private: + internal::UniquePtr<openvdb::Vec3s>::type& mNewPointList; + const PointList& mOldPointList; + const std::vector<unsigned>& mIndexMap; + const std::vector<unsigned char>& mUsedPointMask; +}; //////////////////////////////////////// -template <class DistTreeT> -class SeamMaskGen +template<typename SrcTreeT> +class GenTopologyMask { public: - typedef typename DistTreeT::template ValueConverter<bool>::Type BoolTreeT; - typedef typename DistTreeT::template ValueConverter<int>::Type IntTreeT; - typedef tree::ValueAccessor<const BoolTreeT> BoolTreeAccessorT; - typedef tree::ValueAccessor<const IntTreeT> IntTreeAccessorT; + typedef tree::LeafManager<const SrcTreeT> LeafManagerT; + typedef typename SrcTreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef tree::ValueAccessor<const SrcTreeT> SrcAccessorT; + typedef tree::ValueAccessor<BoolTreeT> BoolAccessorT; + typedef Grid<BoolTreeT> BoolGridT; - SeamMaskGen(LeafPtrList<BoolTreeT>& seamMaskLeafs, - const BoolTreeT& topologyMaskTree, const IntTreeT& auxTree); - SeamMaskGen(const SeamMaskGen<DistTreeT>&); + ////////// - void runParallel(); - void runSerial(); - void operator()(const tbb::blocked_range<size_t>&) const; + GenTopologyMask(const BoolGridT& mask, const LeafManagerT& srcLeafs, + const math::Transform& srcXForm, bool invertMask); + + void run(bool threaded = true); + + BoolTreeT& tree() { return mTree; } + + + ////////// + + GenTopologyMask(GenTopologyMask&, tbb::split); + + void operator()(const tbb::blocked_range<size_t>&); + + void join(GenTopologyMask& rhs) { mTree.merge(rhs.mTree); } private: - LeafPtrList<BoolTreeT>& mSeamMaskLeafs; - const BoolTreeT& mTopologyMaskTree; - BoolTreeAccessorT mTopologyMaskAcc; - const IntTreeT& mAuxTree; - IntTreeAccessorT mAuxAcc; + + const BoolGridT& mMask; + const LeafManagerT& mLeafManager; + const math::Transform& mSrcXForm; + bool mInvertMask; + BoolTreeT mTree; }; -template <class DistTreeT> -SeamMaskGen<DistTreeT>::SeamMaskGen(LeafPtrList<BoolTreeT>& seamMaskLeafs, - const BoolTreeT& topologyMaskTree, const IntTreeT& auxTree) - : mSeamMaskLeafs(seamMaskLeafs) - , mTopologyMaskTree(topologyMaskTree) - , mTopologyMaskAcc(mTopologyMaskTree) - , mAuxTree(auxTree) - , mAuxAcc(mAuxTree) +template<typename SrcTreeT> +GenTopologyMask<SrcTreeT>::GenTopologyMask(const BoolGridT& mask, const LeafManagerT& srcLeafs, + const math::Transform& srcXForm, bool invertMask) + : mMask(mask) + , mLeafManager(srcLeafs) + , mSrcXForm(srcXForm) + , mInvertMask(invertMask) + , mTree(false) { } -template <class DistTreeT> -SeamMaskGen<DistTreeT>::SeamMaskGen(const SeamMaskGen<DistTreeT>& rhs) - : mSeamMaskLeafs(rhs.mSeamMaskLeafs) - , mTopologyMaskTree(rhs.mTopologyMaskTree) - , mTopologyMaskAcc(mTopologyMaskTree) - , mAuxTree(rhs.mAuxTree) - , mAuxAcc(mAuxTree) -{ -} -template <class DistTreeT> -void -SeamMaskGen<DistTreeT>::runParallel() +template<typename SrcTreeT> +GenTopologyMask<SrcTreeT>::GenTopologyMask(GenTopologyMask& rhs, tbb::split) + : mMask(rhs.mMask) + , mLeafManager(rhs.mLeafManager) + , mSrcXForm(rhs.mSrcXForm) + , mInvertMask(rhs.mInvertMask) + , mTree(false) { - tbb::parallel_for(mSeamMaskLeafs.getRange(), *this); } -template <class DistTreeT> + +template<typename SrcTreeT> void -SeamMaskGen<DistTreeT>::runSerial() +GenTopologyMask<SrcTreeT>::run(bool threaded) { - (*this)(mSeamMaskLeafs.getRange()); + if (threaded) { + tbb::parallel_reduce(mLeafManager.getRange(), *this); + } else { + (*this)(mLeafManager.getRange()); + } } -template <class DistTreeT> + +template<typename SrcTreeT> void -SeamMaskGen<DistTreeT>::operator()(const tbb::blocked_range<size_t>& range) const +GenTopologyMask<SrcTreeT>::operator()(const tbb::blocked_range<size_t>& range) { - typedef typename BoolTreeT::LeafNodeType::ValueOnIter ValueOnIterT; - Coord ijk, n_ijk; - for (size_t leafIdx = range.begin(); leafIdx != range.end(); ++leafIdx) { - ValueOnIterT it = mSeamMaskLeafs[leafIdx]->beginValueOn(); - for (; it; ++it) { - ijk = it.getCoord(); - if (!mTopologyMaskAcc.isValueOn(ijk)) { - it.setValueOff(); - } else { - bool turnOff = true; - for (size_t n = 0; n < 6; ++n) { - n_ijk = ijk + util::COORD_OFFSETS[n]; - if (!mAuxTree.isValueOn(n_ijk) && mTopologyMaskAcc.isValueOn(n_ijk)) { - turnOff = false; - break; + Coord ijk; + Vec3d xyz; + typedef typename BoolTreeT::LeafNodeType BoolLeafT; + const math::Transform& maskXForm = mMask.transform(); + tree::ValueAccessor<const BoolTreeT> maskAcc(mMask.tree()); + tree::ValueAccessor<BoolTreeT> acc(mTree); + + typename SrcTreeT::LeafNodeType::ValueOnCIter iter; + for (size_t n = range.begin(); n != range.end(); ++n) { + + ijk = mLeafManager.leaf(n).origin(); + BoolLeafT* leaf = new BoolLeafT(ijk, false); + bool addLeaf = false; + + if (maskXForm == mSrcXForm) { + + const BoolLeafT* maskLeaf = maskAcc.probeConstLeaf(ijk); + + if (maskLeaf) { + + for (iter = mLeafManager.leaf(n).cbeginValueOn(); iter; ++iter) { + Index pos = iter.pos(); + if(maskLeaf->isValueOn(pos) != mInvertMask) { + leaf->setValueOn(pos); + addLeaf = true; } } - if (turnOff) it.setValueOff(); + + } else if (maskAcc.isValueOn(ijk) != mInvertMask) { + leaf->topologyUnion(mLeafManager.leaf(n)); + addLeaf = true; + } + + } else { + for (iter = mLeafManager.leaf(n).cbeginValueOn(); iter; ++iter) { + ijk = iter.getCoord(); + xyz = maskXForm.worldToIndex(mSrcXForm.indexToWorld(ijk)); + if(maskAcc.isValueOn(util::nearestCoord(xyz)) != mInvertMask) { + leaf->setValueOn(iter.pos()); + addLeaf = true; + } } } + + if (addLeaf) acc.addLeaf(leaf); + else delete leaf; } } + //////////////////////////////////////// -template <class DistTreeT> -class EdgeSmooth +template<typename SrcTreeT> +class GenBoundaryMask { public: - typedef typename DistTreeT::template ValueConverter<bool>::Type BoolTreeT; - typedef typename DistTreeT::template ValueConverter<int>::Type IntTreeT; - typedef tree::ValueAccessor<const BoolTreeT> BoolTreeAccessorT; - typedef tree::ValueAccessor<const IntTreeT> IntTreeAccessorT; + typedef typename SrcTreeT::template ValueConverter<int>::Type IntTreeT; + typedef typename SrcTreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef tree::LeafManager<const SrcTreeT> LeafManagerT; - EdgeSmooth( - LeafPtrList<BoolTreeT>& leafs, - const BoolTreeT& edgeMaskTree, - const IntTreeT& auxTree, - PointList& points, - const math::Transform& xform); + ////////// - EdgeSmooth(const EdgeSmooth<DistTreeT>&); + GenBoundaryMask(const LeafManagerT& leafs, const BoolTreeT&, const IntTreeT&); - void runParallel(const size_t iterations); - void runSerial(const size_t iterations); + void run(bool threaded = true); - void operator()(const tbb::blocked_range<size_t>&) const; + BoolTreeT& tree() { return mTree; } + + ////////// + + GenBoundaryMask(GenBoundaryMask&, tbb::split); + void operator()(const tbb::blocked_range<size_t>&); + void join(GenBoundaryMask& rhs) { mTree.merge(rhs.mTree); } private: - LeafPtrList<BoolTreeT>& mLeafs; - const BoolTreeT& mEdgeMaskTree; - const IntTreeT& mAuxTree; - PointList& mPoints; - const math::Transform& mTransform; + // This typedef is needed for Windows + typedef tree::ValueAccessor<const IntTreeT> IntTreeAccessorT; - bool pointInAABB(const Vec3s& p, const Vec3s& bmin, const Vec3s& bmax) const - { - for (int i = 0; i < 3; ++i) { - if (p[i] < bmin[i] || p[i] > bmax[i]) { - return false; - } - } - return true; - } + bool neighboringLeaf(const Coord&, const IntTreeAccessorT&) const; + const LeafManagerT& mLeafManager; + const BoolTreeT& mMaskTree; + const IntTreeT& mIdxTree; + BoolTreeT mTree; + CoordBBox mLeafBBox; }; -template <class DistTreeT> -EdgeSmooth<DistTreeT>::EdgeSmooth( - LeafPtrList<BoolTreeT>& leafs, - const BoolTreeT& edgeMaskTree, - const IntTreeT& auxTree, - PointList& points, - const math::Transform& xform) - : mLeafs(leafs) - , mEdgeMaskTree(edgeMaskTree) - , mAuxTree(auxTree) - , mPoints(points) - , mTransform(xform) +template<typename SrcTreeT> +GenBoundaryMask<SrcTreeT>::GenBoundaryMask(const LeafManagerT& leafs, + const BoolTreeT& maskTree, const IntTreeT& auxTree) + : mLeafManager(leafs) + , mMaskTree(maskTree) + , mIdxTree(auxTree) + , mTree(false) { + mIdxTree.evalLeafBoundingBox(mLeafBBox); + mLeafBBox.expand(IntTreeT::LeafNodeType::DIM); } -template <class DistTreeT> -EdgeSmooth<DistTreeT>::EdgeSmooth(const EdgeSmooth<DistTreeT>& rhs) - : mLeafs(rhs.mLeafs) - , mEdgeMaskTree(rhs.mEdgeMaskTree) - , mAuxTree(rhs.mAuxTree) - , mPoints(rhs.mPoints) - , mTransform(rhs.mTransform) + +template<typename SrcTreeT> +GenBoundaryMask<SrcTreeT>::GenBoundaryMask(GenBoundaryMask& rhs, tbb::split) + : mLeafManager(rhs.mLeafManager) + , mMaskTree(rhs.mMaskTree) + , mIdxTree(rhs.mIdxTree) + , mTree(false) + , mLeafBBox(rhs.mLeafBBox) { } -template <class DistTreeT> + +template<typename SrcTreeT> void -EdgeSmooth<DistTreeT>::runParallel(const size_t iterations) +GenBoundaryMask<SrcTreeT>::run(bool threaded) { - for (size_t i = 0; i < iterations; ++i) { - tbb::parallel_for(mLeafs.getRange(), *this); + if (threaded) { + tbb::parallel_reduce(mLeafManager.getRange(), *this); + } else { + (*this)(mLeafManager.getRange()); } } -template <class DistTreeT> -void -EdgeSmooth<DistTreeT>::runSerial(const size_t iterations) + +template<typename SrcTreeT> +bool +GenBoundaryMask<SrcTreeT>::neighboringLeaf(const Coord& ijk, const IntTreeAccessorT& acc) const { - for (size_t i = 0; i < iterations; ++i) { - (*this)(mLeafs.getRange()); - } + if (acc.probeConstLeaf(ijk)) return true; + + const int dim = IntTreeT::LeafNodeType::DIM; + + // face adjacent neghbours + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1], ijk[2]))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1], ijk[2]))) return true; + if (acc.probeConstLeaf(Coord(ijk[0], ijk[1] + dim, ijk[2]))) return true; + if (acc.probeConstLeaf(Coord(ijk[0], ijk[1] - dim, ijk[2]))) return true; + if (acc.probeConstLeaf(Coord(ijk[0], ijk[1], ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0], ijk[1], ijk[2] - dim))) return true; + + // edge adjacent neighbors + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1], ijk[2] - dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1], ijk[2] - dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1], ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1], ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1] + dim, ijk[2]))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1] + dim, ijk[2]))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1] - dim, ijk[2]))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1] - dim, ijk[2]))) return true; + if (acc.probeConstLeaf(Coord(ijk[0], ijk[1] - dim, ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0], ijk[1] - dim, ijk[2] - dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0], ijk[1] + dim, ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0], ijk[1] + dim, ijk[2] - dim))) return true; + + // corner adjacent neighbors + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1] - dim, ijk[2] - dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1] - dim, ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1] - dim, ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1] - dim, ijk[2] - dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1] + dim, ijk[2] - dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1] + dim, ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1] + dim, ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1] + dim, ijk[2] - dim))) return true; + + return false; } -template <class DistTreeT> + +template<typename SrcTreeT> void -EdgeSmooth<DistTreeT>::operator()(const tbb::blocked_range<size_t>& range) const +GenBoundaryMask<SrcTreeT>::operator()(const tbb::blocked_range<size_t>& range) { - typedef typename BoolTreeT::LeafNodeType::ValueOnIter ValueOnIterT; - BoolTreeAccessorT maskAcc(mEdgeMaskTree); - IntTreeAccessorT auxAcc(mAuxTree); - - float dx = float(mTransform.voxelSize()[0]); - openvdb::Vec3s avg, bmin, bmax; - Coord ijk, nijk; - int count; - - for (size_t leafIdx = range.begin(); leafIdx != range.end(); ++leafIdx) { - typename BoolTreeT::LeafNodeType::ValueOnIter valueIt = mLeafs[leafIdx]->beginValueOn(); - for ( ; valueIt; ++valueIt) { - - ijk = valueIt.getCoord(); - openvdb::Vec3s& ptn = mPoints[auxAcc.getValue(ijk)]; - - avg = ptn; - count = 1; - for (int n = 0; n < 26; ++n) { - nijk = ijk + util::COORD_OFFSETS[n]; - if (maskAcc.isValueOn(nijk)) { - avg += mPoints[auxAcc.getValue(nijk)]; - ++count; - } - } + Coord ijk; + tree::ValueAccessor<const BoolTreeT> maskAcc(mMaskTree); + tree::ValueAccessor<const IntTreeT> idxAcc(mIdxTree); + tree::ValueAccessor<BoolTreeT> acc(mTree); - if (count > 1) { - avg *= (1.0 / float(count)); + typename SrcTreeT::LeafNodeType::ValueOnCIter iter; - // Constrain to current cell - bmin = openvdb::Vec3s(mTransform.indexToWorld(ijk)); - bmax = bmin + dx; + for (size_t n = range.begin(); n != range.end(); ++n) { - bool inCell = true; - for (int i = 0; i < 10; ++i) { + const typename SrcTreeT::LeafNodeType& + leaf = mLeafManager.leaf(n); - inCell = pointInAABB(avg, bmin, bmax); + ijk = leaf.origin(); - if (inCell) break; + if (!mLeafBBox.isInside(ijk) || !neighboringLeaf(ijk, idxAcc)) continue; - avg += ptn; - avg *= 0.5; - } + const typename BoolTreeT::LeafNodeType* + maskLeaf = maskAcc.probeConstLeaf(ijk); - if (inCell) ptn = avg; - } + if (!maskLeaf || !leaf.hasSameTopology(maskLeaf)) { + acc.touchLeaf(ijk)->topologyUnion(leaf); } } } @@ -2129,235 +3457,239 @@ EdgeSmooth<DistTreeT>::operator()(const tbb::blocked_range<size_t>& range) const //////////////////////////////////////// -template<class CharAccessorT, typename AuxAccessorT> -class AuxDataGenerator +template<typename TreeT> +class GenTileMask { public: - AuxDataGenerator(CharAccessorT& edgeAcc, AuxAccessorT& auxAcc) - : mEdgeAcc(edgeAcc), mAuxAcc(auxAcc) {} + typedef typename TreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef typename TreeT::ValueType ValueT; - void setXEdge(char edgeFlags, const Coord& ijk) - { - mEdgeAcc.setValue(ijk, edgeFlags | XEDGE); + ////////// - mAuxAcc.setActiveState(ijk, true); + GenTileMask(const std::vector<Vec4i>& tiles, const TreeT& distTree, ValueT iso); - Coord coord = ijk; - coord[1] = ijk[1]-1; - mAuxAcc.setActiveState(coord, true); + void run(bool threaded = true); - coord[0] = ijk[0]; coord[1] = ijk[1]-1; coord[2] = ijk[2]-1; - mAuxAcc.setActiveState(coord, true); + BoolTreeT& tree() { return mTree; } - coord[0] = ijk[0]; coord[1] = ijk[1]; coord[2] = ijk[2]-1; - mAuxAcc.setActiveState(coord, true); - } + ////////// - void setYEdge(char edgeFlags, const Coord& ijk) - { - mEdgeAcc.setValue(ijk, edgeFlags | YEDGE); - - mAuxAcc.setActiveState(ijk, true); - - Coord coord = ijk; - coord[2] = ijk[2]-1; - mAuxAcc.setActiveState(coord, true); - - coord[0] = ijk[0]-1; coord[1] = ijk[1]; coord[2] = ijk[2]; - mAuxAcc.setActiveState(coord, true); + GenTileMask(GenTileMask&, tbb::split); + void operator()(const tbb::blocked_range<size_t>&); + void join(GenTileMask& rhs) { mTree.merge(rhs.mTree); } - coord[0] = ijk[0]-1; coord[1] = ijk[1]; coord[2] = ijk[2]-1; - mAuxAcc.setActiveState(coord, true); - } +private: - void setZEdge(char edgeFlags, const Coord& ijk) - { - mEdgeAcc.setValue(ijk, edgeFlags | ZEDGE); + const std::vector<Vec4i>& mTiles; + const TreeT& mDistTree; + ValueT mIsovalue; - mAuxAcc.setActiveState(ijk, true); + BoolTreeT mTree; +}; - Coord coord = ijk; - coord[1] = ijk[1]-1; - mAuxAcc.setActiveState(coord, true); - coord[0] = ijk[0]-1; coord[1] = ijk[1]; coord[2] = ijk[2]; - mAuxAcc.setActiveState(coord, true); +template<typename TreeT> +GenTileMask<TreeT>::GenTileMask( + const std::vector<Vec4i>& tiles, const TreeT& distTree, ValueT iso) + : mTiles(tiles) + , mDistTree(distTree) + , mIsovalue(iso) + , mTree(false) +{ +} - coord[0] = ijk[0]-1; coord[1] = ijk[1]-1; coord[2] = ijk[2]; - mAuxAcc.setActiveState(coord, true); - } -private: - CharAccessorT& mEdgeAcc; - AuxAccessorT& mAuxAcc; -}; +template<typename TreeT> +GenTileMask<TreeT>::GenTileMask(GenTileMask& rhs, tbb::split) + : mTiles(rhs.mTiles) + , mDistTree(rhs.mDistTree) + , mIsovalue(rhs.mIsovalue) + , mTree(false) +{ +} -//////////////////////////////////////// +template<typename TreeT> +void +GenTileMask<TreeT>::run(bool threaded) +{ + if (threaded) tbb::parallel_reduce(tbb::blocked_range<size_t>(0, mTiles.size()), *this); + else (*this)(tbb::blocked_range<size_t>(0, mTiles.size())); +} -template<class DistTreeT, class AuxTreeT, class CharTreeT> -inline void -tileAuxiliaryData( - const DistTreeT& distTree, CharTreeT& edgeTree, AuxTreeT& auxTree, - double iso) +template<typename TreeT> +void +GenTileMask<TreeT>::operator()(const tbb::blocked_range<size_t>& range) { - typedef tree::ValueAccessor<const DistTreeT> DistAccessorT; - typedef tree::ValueAccessor<AuxTreeT> AuxTreeAccessorT; - typedef tree::ValueAccessor<CharTreeT> CharTreeAccessorT; - - typename DistTreeT::ValueType isoValue = typename DistTreeT::ValueType(iso); + tree::ValueAccessor<const TreeT> distAcc(mDistTree); + CoordBBox region, bbox; + Coord ijk, nijk; + bool processRegion = true; + ValueT value; - DistAccessorT distAcc(distTree); - CharTreeAccessorT edgeAcc(edgeTree); - AuxTreeAccessorT auxAcc(auxTree); - AuxDataGenerator<CharTreeAccessorT, AuxTreeAccessorT> auxData(edgeAcc, auxAcc); + for (size_t n = range.begin(); n != range.end(); ++n) { - Coord ijk, nijk; - typename DistTreeT::ValueType value; - CoordBBox bbox; - bool processTileFace; + const Vec4i& tile = mTiles[n]; - typename DistTreeT::ValueOnCIter tileIter(distTree); - tileIter.setMaxDepth(DistTreeT::ValueOnCIter::LEAF_DEPTH - 1); + bbox.min()[0] = tile[0]; + bbox.min()[1] = tile[1]; + bbox.min()[2] = tile[2]; - for ( ; tileIter; ++tileIter) { - tileIter.getBoundingBox(bbox); + bbox.max() = bbox.min(); + bbox.max().offset(tile[3]); - const bool thisInside = (distAcc.getValue(bbox.min()) < isoValue); + const bool thisInside = (distAcc.getValue(bbox.min()) < mIsovalue); const int thisDepth = distAcc.getValueDepth(bbox.min()); - - // Eval x-edges + // eval x-edges ijk = bbox.max(); nijk = ijk; ++nijk[0]; - processTileFace = true; + processRegion = true; if (thisDepth >= distAcc.getValueDepth(nijk)) { - processTileFace = thisInside != (distAcc.getValue(nijk) < isoValue); + processRegion = thisInside != (distAcc.getValue(nijk) < mIsovalue); } - if (processTileFace) { - for (ijk[1] = bbox.min()[1]; ijk[1] <= bbox.max()[1]; ++ijk[1]) { - nijk[1] = ijk[1]; - for (ijk[2] = bbox.min()[2]; ijk[2] <= bbox.max()[2]; ++ijk[2]) { - nijk[2] = ijk[2]; - if ((distAcc.getValue(nijk) < isoValue) != thisInside) { - auxData.setXEdge(edgeAcc.getValue(ijk) | char(thisInside), ijk); - } - } - } + + if (processRegion) { + region = bbox; + region.min()[0] = region.max()[0] = ijk[0]; + mTree.fill(region, true); } + ijk = bbox.min(); --ijk[0]; - processTileFace = true; + processRegion = true; if (thisDepth >= distAcc.getValueDepth(ijk)) { - processTileFace = !distAcc.probeValue(ijk, value) && thisInside != (value < isoValue); + processRegion = !distAcc.probeValue(ijk, value) && thisInside != (value < mIsovalue); } - if (processTileFace) { - for (ijk[1] = bbox.min()[1]; ijk[1] <= bbox.max()[1]; ++ijk[1]) { - for (ijk[2] = bbox.min()[2]; ijk[2] <= bbox.max()[2]; ++ijk[2]) { - if (!distAcc.probeValue(ijk, value) && (value < isoValue) != thisInside) { - auxData.setXEdge(edgeAcc.getValue(ijk) | char(!thisInside), ijk); - } - } - } + if (processRegion) { + region = bbox; + region.min()[0] = region.max()[0] = ijk[0]; + mTree.fill(region, true); } - // Eval y-edges + // eval y-edges ijk = bbox.max(); nijk = ijk; ++nijk[1]; - processTileFace = true; + processRegion = true; if (thisDepth >= distAcc.getValueDepth(nijk)) { - processTileFace = thisInside != (distAcc.getValue(nijk) < isoValue); + processRegion = thisInside != (distAcc.getValue(nijk) < mIsovalue); } - if (processTileFace) { - for (ijk[0] = bbox.min()[0]; ijk[0] <= bbox.max()[0]; ++ijk[0]) { - nijk[0] = ijk[0]; - for (ijk[2] = bbox.min()[2]; ijk[2] <= bbox.max()[2]; ++ijk[2]) { - nijk[2] = ijk[2]; - - if ((distAcc.getValue(nijk) < isoValue) != thisInside) { - auxData.setYEdge(edgeAcc.getValue(ijk) | char(thisInside), ijk); - } - } - } + if (processRegion) { + region = bbox; + region.min()[1] = region.max()[1] = ijk[1]; + mTree.fill(region, true); } ijk = bbox.min(); --ijk[1]; - processTileFace = true; + processRegion = true; if (thisDepth >= distAcc.getValueDepth(ijk)) { - processTileFace = !distAcc.probeValue(ijk, value) && thisInside != (value < isoValue); + processRegion = !distAcc.probeValue(ijk, value) && thisInside != (value < mIsovalue); } - if (processTileFace) { - for (ijk[0] = bbox.min()[0]; ijk[0] <= bbox.max()[0]; ++ijk[0]) { - for (ijk[2] = bbox.min()[2]; ijk[2] <= bbox.max()[2]; ++ijk[2]) { - - if (!distAcc.probeValue(ijk, value) && (value < isoValue) != thisInside) { - auxData.setYEdge(edgeAcc.getValue(ijk) | char(!thisInside), ijk); - } - } - } + if (processRegion) { + region = bbox; + region.min()[1] = region.max()[1] = ijk[1]; + mTree.fill(region, true); } - // Eval z-edges + // eval z-edges ijk = bbox.max(); nijk = ijk; ++nijk[2]; - processTileFace = true; + processRegion = true; if (thisDepth >= distAcc.getValueDepth(nijk)) { - processTileFace = thisInside != (distAcc.getValue(nijk) < isoValue); + processRegion = thisInside != (distAcc.getValue(nijk) < mIsovalue); } - if (processTileFace) { - for (ijk[0] = bbox.min()[0]; ijk[0] <= bbox.max()[0]; ++ijk[0]) { - nijk[0] = ijk[0]; - for (ijk[1] = bbox.min()[1]; ijk[1] <= bbox.max()[1]; ++ijk[1]) { - nijk[1] = ijk[1]; + if (processRegion) { + region = bbox; + region.min()[2] = region.max()[2] = ijk[2]; + mTree.fill(region, true); + } - if ((distAcc.getValue(nijk) < isoValue) != thisInside) { - auxData.setZEdge(edgeAcc.getValue(ijk) | char(thisInside), ijk); - } - } - } + ijk = bbox.min(); + --ijk[2]; + + processRegion = true; + if (thisDepth >= distAcc.getValueDepth(ijk)) { + processRegion = !distAcc.probeValue(ijk, value) && thisInside != (value < mIsovalue); } + if (processRegion) { + region = bbox; + region.min()[2] = region.max()[2] = ijk[2]; + mTree.fill(region, true); + } + + ijk = bbox.min(); + --ijk[1]; --ijk[2]; - processTileFace = true; + processRegion = true; if (thisDepth >= distAcc.getValueDepth(ijk)) { - processTileFace = !distAcc.probeValue(ijk, value) && thisInside != (value < isoValue); + processRegion = !distAcc.probeValue(ijk, value) && thisInside != (value < mIsovalue); } - if (processTileFace) { - for (ijk[0] = bbox.min()[0]; ijk[0] <= bbox.max()[0]; ++ijk[0]) { - for (ijk[1] = bbox.min()[1]; ijk[1] <= bbox.max()[1]; ++ijk[1]) { + if (processRegion) { + region = bbox; + region.min()[1] = region.max()[1] = ijk[1]; + region.min()[2] = region.max()[2] = ijk[2]; + mTree.fill(region, true); + } - if (!distAcc.probeValue(ijk, value) && (value < isoValue) != thisInside) { - auxData.setZEdge(edgeAcc.getValue(ijk) | char(!thisInside), ijk); - } - } - } + + ijk = bbox.min(); + --ijk[0]; + --ijk[1]; + + processRegion = true; + if (thisDepth >= distAcc.getValueDepth(ijk)) { + processRegion = !distAcc.probeValue(ijk, value) && thisInside != (value < mIsovalue); + } + + if (processRegion) { + region = bbox; + region.min()[1] = region.max()[1] = ijk[1]; + region.min()[0] = region.max()[0] = ijk[0]; + mTree.fill(region, true); + } + + ijk = bbox.min(); + --ijk[0]; + --ijk[2]; + + processRegion = true; + if (thisDepth >= distAcc.getValueDepth(ijk)) { + processRegion = !distAcc.probeValue(ijk, value) && thisInside != (value < mIsovalue); + } + + if (processRegion) { + region = bbox; + region.min()[2] = region.max()[2] = ijk[2]; + region.min()[0] = region.max()[0] = ijk[0]; + mTree.fill(region, true); } } } @@ -2366,6 +3698,58 @@ tileAuxiliaryData( //////////////////////////////////////// +template<class DistTreeT, class SignTreeT, class IdxTreeT> +inline void +tileData(const DistTreeT& distTree, SignTreeT& signTree, IdxTreeT& idxTree, double iso) +{ + typename DistTreeT::ValueOnCIter tileIter(distTree); + tileIter.setMaxDepth(DistTreeT::ValueOnCIter::LEAF_DEPTH - 1); + + if (!tileIter) return; // volume has no active tiles. + + size_t tileCount = 0; + for ( ; tileIter; ++tileIter) { + ++tileCount; + } + + std::vector<Vec4i> tiles(tileCount); + + tileCount = 0; + tileIter = distTree.cbeginValueOn(); + tileIter.setMaxDepth(DistTreeT::ValueOnCIter::LEAF_DEPTH - 1); + + CoordBBox bbox; + for (; tileIter; ++tileIter) { + Vec4i& tile = tiles[tileCount++]; + tileIter.getBoundingBox(bbox); + tile[0] = bbox.min()[0]; + tile[1] = bbox.min()[1]; + tile[2] = bbox.min()[2]; + tile[3] = bbox.max()[0] - bbox.min()[0]; + } + + typename DistTreeT::ValueType isovalue = typename DistTreeT::ValueType(iso); + + GenTileMask<DistTreeT> tileMask(tiles, distTree, isovalue); + tileMask.run(); + + typedef typename DistTreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef tree::LeafManager<BoolTreeT> BoolLeafManagerT; + + BoolLeafManagerT leafs(tileMask.tree()); + + + internal::SignData<DistTreeT, BoolLeafManagerT> op(distTree, leafs, isovalue); + op.run(); + + signTree.merge(*op.signTree()); + idxTree.merge(*op.idxTree()); +} + + +//////////////////////////////////////// + + // Utility class for the volumeToMesh wrapper class PointListCopy { @@ -2388,30 +3772,216 @@ private: }; -} // namespace internal +// Checks if the isovalue is in proximity to the active voxel boundary. +template <typename LeafManagerT> +inline bool +needsActiveVoxePadding(const LeafManagerT& leafs, double iso, double voxelSize) +{ + double interiorWidth = 0.0, exteriorWidth = 0.0; + { + typename LeafManagerT::TreeType::LeafNodeType::ValueOffCIter it; + bool foundInterior = false, foundExterior = false; + for (size_t n = 0, N = leafs.leafCount(); n < N; ++n) { + + for (it = leafs.leaf(n).cbeginValueOff(); it; ++it) { + double value = double(it.getValue()); + if (value < 0.0) { + interiorWidth = value; + foundInterior = true; + } else if (value > 0.0) { + exteriorWidth = value; + foundExterior = true; + } + + if (foundInterior && foundExterior) break; + } + + if (foundInterior && foundExterior) break; + } + + } + + double minDist = std::min(std::abs(interiorWidth - iso), std::abs(exteriorWidth - iso)); + return !(minDist > (2.0 * voxelSize)); +} + + +} // end namespace internal + + +//////////////////////////////////////// + + +inline +PolygonPool::PolygonPool() + : mNumQuads(0) + , mNumTriangles(0) + , mQuads(NULL) + , mTriangles(NULL) + , mQuadFlags(NULL) + , mTriangleFlags(NULL) +{ +} + + +inline +PolygonPool::PolygonPool(const size_t numQuads, const size_t numTriangles) + : mNumQuads(numQuads) + , mNumTriangles(numTriangles) + , mQuads(new openvdb::Vec4I[mNumQuads]) + , mTriangles(new openvdb::Vec3I[mNumTriangles]) + , mQuadFlags(new char[mNumQuads]) + , mTriangleFlags(new char[mNumTriangles]) +{ +} + + +inline void +PolygonPool::copy(const PolygonPool& rhs) +{ + resetQuads(rhs.numQuads()); + resetTriangles(rhs.numTriangles()); + + for (size_t i = 0; i < mNumQuads; ++i) { + mQuads[i] = rhs.mQuads[i]; + mQuadFlags[i] = rhs.mQuadFlags[i]; + } + + for (size_t i = 0; i < mNumTriangles; ++i) { + mTriangles[i] = rhs.mTriangles[i]; + mTriangleFlags[i] = rhs.mTriangleFlags[i]; + } +} + + +inline void +PolygonPool::resetQuads(size_t size) +{ + mNumQuads = size; + mQuads.reset(new openvdb::Vec4I[mNumQuads]); + mQuadFlags.reset(new char[mNumQuads]); +} + + +inline void +PolygonPool::clearQuads() +{ + mNumQuads = 0; + mQuads.reset(NULL); + mQuadFlags.reset(NULL); +} + + +inline void +PolygonPool::resetTriangles(size_t size) +{ + mNumTriangles = size; + mTriangles.reset(new openvdb::Vec3I[mNumTriangles]); + mTriangleFlags.reset(new char[mNumTriangles]); +} + + +inline void +PolygonPool::clearTriangles() +{ + mNumTriangles = 0; + mTriangles.reset(NULL); + mTriangleFlags.reset(NULL); +} + + +inline bool +PolygonPool::trimQuads(const size_t n, bool reallocate) +{ + if (!(n < mNumQuads)) return false; + + if (reallocate) { + + if (n == 0) { + mQuads.reset(NULL); + } else { + + boost::scoped_array<openvdb::Vec4I> quads(new openvdb::Vec4I[n]); + boost::scoped_array<char> flags(new char[n]); + + for (size_t i = 0; i < n; ++i) { + quads[i] = mQuads[i]; + flags[i] = mQuadFlags[i]; + } + + mQuads.swap(quads); + mQuadFlags.swap(flags); + } + } + + mNumQuads = n; + return true; +} + + +inline bool +PolygonPool::trimTrinagles(const size_t n, bool reallocate) +{ + if (!(n < mNumTriangles)) return false; + + if (reallocate) { + + if (n == 0) { + mTriangles.reset(NULL); + } else { + + boost::scoped_array<openvdb::Vec3I> triangles(new openvdb::Vec3I[n]); + boost::scoped_array<char> flags(new char[n]); + + for (size_t i = 0; i < n; ++i) { + triangles[i] = mTriangles[i]; + flags[i] = mTriangleFlags[i]; + } + + mTriangles.swap(triangles); + mTriangleFlags.swap(flags); + } + } + + mNumTriangles = n; + return true; +} //////////////////////////////////////// inline VolumeToMesh::VolumeToMesh(double isovalue, double adaptivity) - : mPointListSize(0) + : mPoints(NULL) + , mPolygons() + , mPointListSize(0) + , mSeamPointListSize(0) , mPolygonPoolListSize(0) , mIsovalue(isovalue) , mPrimAdaptivity(adaptivity) , mSecAdaptivity(0.0) , mRefGrid(GridBase::ConstPtr()) - , mRefEdgeTree(TreeBase::Ptr()) - , mRefTopologyMaskTree(TreeBase::Ptr()) + , mSurfaceMaskGrid(GridBase::ConstPtr()) + , mAdaptivityGrid(GridBase::ConstPtr()) + , mAdaptivityMaskTree(TreeBase::ConstPtr()) + , mRefSignTree(TreeBase::Ptr()) + , mRefIdxTree(TreeBase::Ptr()) + , mInvertSurfaceMask(false) + , mPartitions(1) + , mActivePart(0) + , mQuantizedSeamPoints(NULL) + , mPointFlags(0) { } + inline PointList& VolumeToMesh::pointList() { return mPoints; } + inline const size_t& VolumeToMesh::pointListSize() const { @@ -2441,15 +4011,60 @@ VolumeToMesh::polygonPoolListSize() const inline void -VolumeToMesh::setRefGrid(const GridBase::ConstPtr& grid, double secAdaptivity, bool smoothSeams) +VolumeToMesh::setRefGrid(const GridBase::ConstPtr& grid, double secAdaptivity) { mRefGrid = grid; mSecAdaptivity = secAdaptivity; + // Clear out old auxiliary data - mRefEdgeTree = TreeBase::Ptr(); - mRefTopologyMaskTree = TreeBase::Ptr(); - mSeamPointTree = TreeBase::Ptr(); - mSmoothSeams = smoothSeams; + mRefSignTree = TreeBase::Ptr(); + mRefIdxTree = TreeBase::Ptr(); + mSeamPointListSize = 0; + mQuantizedSeamPoints.reset(NULL); +} + + +inline void +VolumeToMesh::setSurfaceMask(const GridBase::ConstPtr& mask, bool invertMask) +{ + mSurfaceMaskGrid = mask; + mInvertSurfaceMask = invertMask; +} + + +inline void +VolumeToMesh::setSpatialAdaptivity(const GridBase::ConstPtr& grid) +{ + mAdaptivityGrid = grid; +} + + +inline void +VolumeToMesh::setAdaptivityMask(const TreeBase::ConstPtr& tree) +{ + mAdaptivityMaskTree = tree; +} + + +inline void +VolumeToMesh::partition(unsigned partitions, unsigned activePart) +{ + mPartitions = std::max(partitions, unsigned(1)); + mActivePart = std::min(activePart, mPartitions-1); +} + + +inline std::vector<unsigned char>& +VolumeToMesh::pointFlags() +{ + return mPointFlags; +} + + +inline const std::vector<unsigned char>& +VolumeToMesh::pointFlags() const +{ + return mPointFlags; } @@ -2458,172 +4073,542 @@ inline void VolumeToMesh::operator()(const GridT& distGrid) { typedef typename GridT::TreeType DistTreeT; + typedef tree::LeafManager<const DistTreeT> DistLeafManagerT; typedef typename DistTreeT::ValueType DistValueT; - typedef typename DistTreeT::template ValueConverter<char>::Type CharTreeT; - typedef typename DistTreeT::template ValueConverter<int>::Type IntTreeT; - typedef typename DistTreeT::template ValueConverter<Vec3s>::Type Vec3sTreeT; typedef typename DistTreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef tree::LeafManager<BoolTreeT> BoolLeafManagerT; + typedef Grid<BoolTreeT> BoolGridT; + + typedef typename DistTreeT::template ValueConverter<Int16>::Type Int16TreeT; + typedef tree::LeafManager<Int16TreeT> Int16LeafManagerT; + + typedef typename DistTreeT::template ValueConverter<int>::Type IntTreeT; + typedef typename DistTreeT::template ValueConverter<float>::Type FloatTreeT; + typedef Grid<FloatTreeT> FloatGridT; - const bool noAdaptivity = mPrimAdaptivity < 1e-6 && mSecAdaptivity < 1e-6; const openvdb::math::Transform& transform = distGrid.transform(); const DistTreeT& distTree = distGrid.tree(); - typename CharTreeT::Ptr edgeTree; // edge flags - typename IntTreeT::Ptr auxTree; // auxiliary data + const DistValueT isovalue = DistValueT(mIsovalue); + + typename Int16TreeT::Ptr signTreePt; + typename IntTreeT::Ptr idxTreePt; + typename BoolTreeT::Ptr pointMask; - const bool nonLevelSetGrid = distGrid.getGridClass() != GRID_LEVEL_SET; + BoolTreeT valueMask(false), seamMask(false); + const bool adaptive = mPrimAdaptivity > 1e-7 || mSecAdaptivity > 1e-7; + bool maskEdges = false; - const bool extraSignCheck = nonLevelSetGrid || - (std::abs(mIsovalue - double(distGrid.background())) < (1.5 * transform.voxelSize()[0])); + const BoolGridT * surfaceMask = NULL; + if (mSurfaceMaskGrid && mSurfaceMaskGrid->type() == BoolGridT::gridType()) { + surfaceMask = static_cast<const BoolGridT*>(mSurfaceMaskGrid.get()); + } + + const FloatGridT * adaptivityField = NULL; + if (mAdaptivityGrid && mAdaptivityGrid->type() == FloatGridT::gridType()) { + adaptivityField = static_cast<const FloatGridT*>(mAdaptivityGrid.get()); + } + + if (mAdaptivityMaskTree && mAdaptivityMaskTree->type() == BoolTreeT::treeType()) { + const BoolTreeT *adaptivityMaskPt = + static_cast<const BoolTreeT*>(mAdaptivityMaskTree.get()); + seamMask.topologyUnion(*adaptivityMaskPt); + } // Collect auxiliary data { - internal::LeafCPtrList<DistTreeT> sourceLeafs(distTree); - internal::AuxiliaryData<DistTreeT> op(distTree, sourceLeafs, mIsovalue, extraSignCheck); - op.runParallel(); - edgeTree = op.edgeTree(); - auxTree = op.auxTree(); + DistLeafManagerT distLeafs(distTree); - // Collect auxiliary data from active tiles - if (nonLevelSetGrid) { - internal::tileAuxiliaryData(distTree, *edgeTree, *auxTree, mIsovalue); + // Check if the isovalue is in proximity to the active voxel boundary. + bool padActiveVoxels = false; + int padVoxels = 3; + + if (distGrid.getGridClass() != GRID_LEVEL_SET) { + padActiveVoxels = true; + } else { + padActiveVoxels = internal::needsActiveVoxePadding(distLeafs, + mIsovalue, transform.voxelSize()[0]); } - } + // always pad the active region for small volumes (the performance hit is neglectable). + if (!padActiveVoxels) { + Coord dim; + distTree.evalActiveVoxelDim(dim); + int maxDim = std::max(std::max(dim[0], dim[1]), dim[2]); + if (maxDim < 1000) { + padActiveVoxels = true; + padVoxels = 1; + } + } - // Optionally collect auxiliary data from a reference surface. - internal::ReferenceData<DistTreeT> refData; - if (mRefGrid) { - const GridT* refGrid = static_cast<const GridT*>(mRefGrid.get()); - if (refGrid && refGrid->activeVoxelCount() > 0) { - - refData.mDistTree = &refGrid->tree(); - refData.mInternalAdaptivity = DistValueT(mSecAdaptivity); - - // Cache reference data for reuse. - if (!mRefEdgeTree && !mRefTopologyMaskTree) { - internal::LeafCPtrList<DistTreeT> leafs(*refData.mDistTree); - internal::AuxiliaryData<DistTreeT, bool> op( - *refData.mDistTree, leafs, mIsovalue, extraSignCheck); - op.runParallel(); - mRefEdgeTree = op.edgeTree(); - mRefTopologyMaskTree = op.auxTree(); - mSeamPointTree = typename Vec3sTreeT::Ptr(new Vec3sTreeT(Vec3s(0.0))); + if (surfaceMask || mPartitions > 1) { + + maskEdges = true; + + if (surfaceMask) { + + { // Mask + internal::GenTopologyMask<DistTreeT> masking( + *surfaceMask, distLeafs, transform, mInvertSurfaceMask); + masking.run(); + valueMask.merge(masking.tree()); + } + + if (mPartitions > 1) { // Partition + tree::LeafManager<BoolTreeT> leafs(valueMask); + leafs.foreach(internal::PartOp(leafs.leafCount() , mPartitions, mActivePart)); + valueMask.pruneInactive(); + } + + } else { // Partition + + internal::PartGen<DistTreeT> partitioner(distLeafs, mPartitions, mActivePart); + partitioner.run(); + valueMask.merge(partitioner.tree()); + } + + { + if (padActiveVoxels) tools::dilateVoxels(valueMask, padVoxels); + BoolLeafManagerT leafs(valueMask); + + internal::SignData<DistTreeT, BoolLeafManagerT> + signDataOp(distTree, leafs, isovalue); + signDataOp.run(); + + signTreePt = signDataOp.signTree(); + idxTreePt = signDataOp.idxTree(); } - if (mRefEdgeTree && mRefTopologyMaskTree) { - refData.mEdgeTree = static_cast<CharTreeT*>(mRefEdgeTree.get()); - refData.mTopologyMaskTree = static_cast<BoolTreeT*>(mRefTopologyMaskTree.get()); - refData.mSeamPointTree = static_cast<Vec3sTreeT*>(mSeamPointTree.get()); - refData.mSeamMaskTree = typename BoolTreeT::Ptr(new BoolTreeT(false)); - refData.mSmoothingMaskTree = typename BoolTreeT::Ptr(new BoolTreeT(false)); + { + internal::GenBoundaryMask<DistTreeT> boundary(distLeafs, valueMask, *idxTreePt); + boundary.run(); + + BoolLeafManagerT bleafs(boundary.tree()); + + internal::SignData<DistTreeT, BoolLeafManagerT> + signDataOp(distTree, bleafs, isovalue); + signDataOp.run(); + + signTreePt->merge(*signDataOp.signTree()); + idxTreePt->merge(*signDataOp.idxTree()); + } + + } else { + + // Collect voxel-sign configurations + if (padActiveVoxels) { + + BoolTreeT regionMask(false); + regionMask.topologyUnion(distTree); + tools::dilateVoxels(regionMask, padVoxels); + + BoolLeafManagerT leafs(regionMask); + + internal::SignData<DistTreeT, BoolLeafManagerT> + signDataOp(distTree, leafs, isovalue); + signDataOp.run(); + + signTreePt = signDataOp.signTree(); + idxTreePt = signDataOp.idxTree(); + } else { + + internal::SignData<DistTreeT, DistLeafManagerT> + signDataOp(distTree, distLeafs, isovalue); + signDataOp.run(); + + signTreePt = signDataOp.signTree(); + idxTreePt = signDataOp.idxTree(); } } + } - BoolTreeT edgeMaskTree(0.0); - // Generate the seamline mask - if (refData.mSeamMaskTree) { - refData.mSeamMaskTree->topologyUnion(*auxTree.get()); + // Collect auxiliary data from active tiles + internal::tileData(distTree, *signTreePt, *idxTreePt, isovalue); + + // Optionally collect auxiliary data from a reference level set. + Int16TreeT *refSignTreePt = NULL; + IntTreeT *refIdxTreePt = NULL; + const DistTreeT *refDistTreePt = NULL; + + if (mRefGrid && mRefGrid->type() == GridT::gridType()) { + + const GridT* refGrid = static_cast<const GridT*>(mRefGrid.get()); + refDistTreePt = &refGrid->tree(); + + // Collect and cache auxiliary data from the reference grid. + if (!mRefSignTree && !mRefIdxTree) { + + DistLeafManagerT refDistLeafs(*refDistTreePt); + internal::SignData<DistTreeT, DistLeafManagerT> + signDataOp(*refDistTreePt, refDistLeafs, isovalue); - internal::LeafPtrList<BoolTreeT> leafs(*refData.mSeamMaskTree.get()); - internal::SeamMaskGen<DistTreeT> op(leafs, *refData.mTopologyMaskTree, *auxTree.get()); - op.runParallel(); + signDataOp.run(); - refData.mSeamMaskTree->pruneInactive(); - edgeMaskTree.topologyUnion(*refData.mSeamMaskTree); - dilateVoxels(*refData.mSeamMaskTree); - dilateVoxels(*refData.mSeamMaskTree); - dilateVoxels(*refData.mSeamMaskTree); + mRefSignTree = signDataOp.signTree(); + mRefIdxTree = signDataOp.idxTree(); + } + + // Get cached auxiliary data + if (mRefSignTree && mRefIdxTree) { + refSignTreePt = static_cast<Int16TreeT*>(mRefSignTree.get()); + refIdxTreePt = static_cast<IntTreeT*>(mRefIdxTree.get()); + } } // Process auxiliary data + Int16LeafManagerT signLeafs(*signTreePt); + + if (maskEdges) { + signLeafs.foreach(internal::MaskEdges<BoolTreeT>(valueMask)); + valueMask.clear(); + } + + + // Generate the seamline mask + if (refSignTreePt) { + internal::GenSeamMask<Int16TreeT, Int16LeafManagerT> seamOp(signLeafs, *refSignTreePt); + seamOp.run(); + + tools::dilateVoxels(seamOp.mask(), 3); + signLeafs.foreach(internal::TagSeamEdges<BoolTreeT>(seamOp.mask())); + + seamMask.merge(seamOp.mask()); + } + + + std::vector<size_t> regions(signLeafs.leafCount(), 0); + if (regions.empty()) return; + + if (adaptive) { + + internal::MergeVoxelRegions<DistTreeT, Int16LeafManagerT> merge( + signLeafs, *signTreePt, distTree, *idxTreePt, isovalue, DistValueT(mPrimAdaptivity)); + + if (adaptivityField) { + merge.setSpatialAdaptivity(transform, *adaptivityField); + } + + if (refSignTreePt || mAdaptivityMaskTree) { + merge.setAdaptivityMask(&seamMask); + } + + if (refSignTreePt) { + merge.setRefData(refSignTreePt, DistValueT(mSecAdaptivity)); + } + + merge.run(); + + signLeafs.foreach(internal::CountRegions<IntTreeT>(*idxTreePt, regions)); + + } else { + signLeafs.foreach(internal::CountPoints(regions)); + } + + { - internal::LeafPtrList<IntTreeT> auxLeafs(*auxTree); - std::vector<size_t> regions(auxLeafs.size(), 0); + mPointListSize = 0; + size_t tmp = 0; + for (size_t n = 0, N = regions.size(); n < N; ++n) { + tmp = regions[n]; + regions[n] = mPointListSize; + mPointListSize += tmp; + } + } - { - if (noAdaptivity) { - internal::Count<IntTreeT> count(auxLeafs, regions); - count.runParallel(); - } else { - internal::Merge<DistTreeT> merge(distTree, auxLeafs, - regions, DistValueT(mIsovalue), DistValueT(mPrimAdaptivity)); - merge.setRefData(refData); - merge.runParallel(); + + // Generate the unique point list + mPoints.reset(new openvdb::Vec3s[mPointListSize]); + mPointFlags.clear(); + + // Generate seam line sample points + if (refSignTreePt && refIdxTreePt) { + + if (mSeamPointListSize == 0) { + + std::vector<size_t> pointMap; + + { + Int16LeafManagerT refSignLeafs(*refSignTreePt); + pointMap.resize(refSignLeafs.leafCount(), 0); + + refSignLeafs.foreach(internal::CountPoints(pointMap)); + + size_t tmp = 0; + for (size_t n = 0, N = pointMap.size(); n < N; ++n) { + tmp = pointMap[n]; + pointMap[n] = mSeamPointListSize; + mSeamPointListSize += tmp; + } } - mPointListSize = 0; - size_t tmp = 0; - for (size_t n = 0, N = regions.size(); n < N; ++n) { - tmp = regions[n]; - regions[n] = mPointListSize; - mPointListSize += tmp; + if (!pointMap.empty() && mSeamPointListSize != 0) { + + mQuantizedSeamPoints.reset(new uint32_t[mSeamPointListSize]); + memset(mQuantizedSeamPoints.get(), 0, sizeof(uint32_t) * mSeamPointListSize); + + typedef tree::LeafManager<IntTreeT> IntLeafManagerT; + + IntLeafManagerT refIdxLeafs(*refIdxTreePt); + refIdxLeafs.foreach(internal::MapPoints<Int16TreeT>(pointMap, *refSignTreePt)); } } - if (refData.isValid()) { // match leaf topology - tree::ValueAccessor<BoolTreeT> acc(*refData.mSmoothingMaskTree); - for (size_t n = 0, N = auxLeafs.size(); n < N; ++n) { - acc.touchLeaf(auxLeafs[n]->getOrigin()); - } + if (mSeamPointListSize != 0) { + signLeafs.foreach(internal::SeamWeights<DistTreeT>( + distTree, *refSignTreePt, *refIdxTreePt, mQuantizedSeamPoints, mIsovalue)); + } + } + + + internal::GenPoints<DistTreeT, Int16LeafManagerT> + pointOp(signLeafs, distTree, *idxTreePt, mPoints, regions, transform, mIsovalue); + + + if (mSeamPointListSize != 0) { + mPointFlags.resize(mPointListSize); + pointOp.setRefData(refSignTreePt, refDistTreePt, refIdxTreePt, + &mQuantizedSeamPoints, &mPointFlags); + } + + pointOp.run(); + + + mPolygonPoolListSize = signLeafs.leafCount(); + mPolygons.reset(new PolygonPool[mPolygonPoolListSize]); + + + if (adaptive) { + + internal::GenPolygons<Int16LeafManagerT, internal::AdaptivePrimBuilder> + mesher(signLeafs, *signTreePt, *idxTreePt, mPolygons, Index32(mPointListSize)); + + mesher.setRefSignTree(refSignTreePt); + mesher.run(); + + } else { + + internal::GenPolygons<Int16LeafManagerT, internal::UniformPrimBuilder> + mesher(signLeafs, *signTreePt, *idxTreePt, mPolygons, Index32(mPointListSize)); + + mesher.setRefSignTree(refSignTreePt); + mesher.run(); + } + + // Clean up unused points, only necessary if masking and/or + // automatic mesh partitioning is enabled. + if ((surfaceMask || mPartitions > 1) && mPointListSize > 0) { + + // Flag used points + std::vector<unsigned char> usedPointMask(mPointListSize, 0); + + internal::FlagUsedPoints flagPoints(mPolygons, mPolygonPoolListSize, usedPointMask); + flagPoints.run(); + + // Create index map + std::vector<unsigned> indexMap(mPointListSize); + size_t usedPointCount = 0; + for (size_t p = 0; p < mPointListSize; ++p) { + if (usedPointMask[p]) indexMap[p] = usedPointCount++; } - // Generate the unique point list - mPoints.reset(new openvdb::Vec3s[mPointListSize]); + if (usedPointCount < mPointListSize) { + + // move points + internal::UniquePtr<openvdb::Vec3s>::type + newPointList(new openvdb::Vec3s[usedPointCount]); - internal::PointGen<DistTreeT> - pointGen(distTree, auxLeafs, regions, transform, mPoints, mIsovalue); - pointGen.setRefData(refData); - pointGen.runParallel(); + internal::MovePoints movePoints(newPointList, mPoints, indexMap, usedPointMask); + movePoints.run(); + + mPointListSize = usedPointCount; + mPoints.reset(newPointList.release()); + + // update primitives + internal::RemapIndices remap(mPolygons, mPolygonPoolListSize, indexMap); + remap.run(); + } } - // Smooth seam line edges - if (mSmoothSeams && refData.isValid()) { - refData.mSmoothingMaskTree->pruneInactive(); - internal::LeafPtrList<BoolTreeT> leafs(*refData.mSmoothingMaskTree); - internal::EdgeSmooth<DistTreeT> op(leafs, edgeMaskTree, *auxTree, mPoints, transform); - op.runParallel(3); - // Cache shared points - tree::ValueAccessor<Vec3sTreeT> ptnAcc(*refData.mSeamPointTree); - tree::ValueAccessor<IntTreeT> auxAcc(*auxTree); - Coord ijk; + // Subdivide nonplanar quads near the seamline edges + // todo: thread and clean up + if (refSignTreePt || refIdxTreePt || refDistTreePt) { + std::vector<Vec3s> newPoints; + + for (size_t n = 0; n < mPolygonPoolListSize; ++n) { + + PolygonPool& polygons = mPolygons[n]; + + std::vector<size_t> nonPlanarQuads; + nonPlanarQuads.reserve(polygons.numQuads()); + + for (size_t i = 0; i < polygons.numQuads(); ++i) { + + char& flags = polygons.quadFlags(i); + + if ((flags & POLYFLAG_FRACTURE_SEAM) && !(flags & POLYFLAG_EXTERIOR)) { - typename BoolTreeT::LeafIter leafIt = refData.mSeamMaskTree->beginLeaf(); - for ( ; leafIt; ++leafIt) { - typename BoolTreeT::LeafNodeType::ValueOnIter valueIt = leafIt->beginValueOn(); - for ( ; valueIt; ++valueIt) { - ijk = valueIt.getCoord(); - const int idx = auxAcc.getValue(ijk); - if (idx != 0 && !ptnAcc.isValueOn(ijk)) { - ptnAcc.setValue(ijk, mPoints[idx]); + openvdb::Vec4I& quad = polygons.quad(i); + + const bool edgePoly = mPointFlags[quad[0]] || mPointFlags[quad[1]] + || mPointFlags[quad[2]] || mPointFlags[quad[3]]; + + if (!edgePoly) continue; + + const Vec3s& p0 = mPoints[quad[0]]; + const Vec3s& p1 = mPoints[quad[1]]; + const Vec3s& p2 = mPoints[quad[2]]; + const Vec3s& p3 = mPoints[quad[3]]; + + if (!internal::isPlanarQuad(p0, p1, p2, p3, 1e-6f)) { + nonPlanarQuads.push_back(i); + } } } + + + if (!nonPlanarQuads.empty()) { + + PolygonPool tmpPolygons; + + tmpPolygons.resetQuads(polygons.numQuads() - nonPlanarQuads.size()); + tmpPolygons.resetTriangles(polygons.numTriangles() + 4 * nonPlanarQuads.size()); + + size_t triangleIdx = 0; + for (size_t i = 0; i < nonPlanarQuads.size(); ++i) { + + size_t& quadIdx = nonPlanarQuads[i]; + + openvdb::Vec4I& quad = polygons.quad(quadIdx); + char& quadFlags = polygons.quadFlags(quadIdx); + //quadFlags |= POLYFLAG_SUBDIVIDED; + + Vec3s centroid = (mPoints[quad[0]] + mPoints[quad[1]] + + mPoints[quad[2]] + mPoints[quad[3]]) * 0.25; + + size_t pointIdx = newPoints.size() + mPointListSize; + + newPoints.push_back(centroid); + + + { + Vec3I& triangle = tmpPolygons.triangle(triangleIdx); + + triangle[0] = quad[0]; + triangle[1] = pointIdx; + triangle[2] = quad[3]; + + tmpPolygons.triangleFlags(triangleIdx) = quadFlags; + + if (mPointFlags[triangle[0]] || mPointFlags[triangle[2]]) { + tmpPolygons.triangleFlags(triangleIdx) |= POLYFLAG_SUBDIVIDED; + } + } + + ++triangleIdx; + + { + Vec3I& triangle = tmpPolygons.triangle(triangleIdx); + + triangle[0] = quad[0]; + triangle[1] = quad[1]; + triangle[2] = pointIdx; + + tmpPolygons.triangleFlags(triangleIdx) = quadFlags; + + if (mPointFlags[triangle[0]] || mPointFlags[triangle[1]]) { + tmpPolygons.triangleFlags(triangleIdx) |= POLYFLAG_SUBDIVIDED; + } + } + + ++triangleIdx; + + { + Vec3I& triangle = tmpPolygons.triangle(triangleIdx); + + triangle[0] = quad[1]; + triangle[1] = quad[2]; + triangle[2] = pointIdx; + + tmpPolygons.triangleFlags(triangleIdx) = quadFlags; + + if (mPointFlags[triangle[0]] || mPointFlags[triangle[1]]) { + tmpPolygons.triangleFlags(triangleIdx) |= POLYFLAG_SUBDIVIDED; + } + } + + + ++triangleIdx; + + { + Vec3I& triangle = tmpPolygons.triangle(triangleIdx); + + triangle[0] = quad[2]; + triangle[1] = quad[3]; + triangle[2] = pointIdx; + + tmpPolygons.triangleFlags(triangleIdx) = quadFlags; + + if (mPointFlags[triangle[0]] || mPointFlags[triangle[1]]) { + tmpPolygons.triangleFlags(triangleIdx) |= POLYFLAG_SUBDIVIDED; + } + } + + ++triangleIdx; + + quad[0] = util::INVALID_IDX; + } + + + for (size_t i = 0; i < polygons.numTriangles(); ++i) { + tmpPolygons.triangle(triangleIdx) = polygons.triangle(i); + tmpPolygons.triangleFlags(triangleIdx) = polygons.triangleFlags(i); + ++triangleIdx; + } + + + size_t quadIdx = 0; + for (size_t i = 0; i < polygons.numQuads(); ++i) { + openvdb::Vec4I& quad = polygons.quad(i); + + if (quad[0] != util::INVALID_IDX) { + tmpPolygons.quad(quadIdx) = quad; + tmpPolygons.quadFlags(quadIdx) = polygons.quadFlags(i); + ++quadIdx; + } + } + + + polygons.copy(tmpPolygons); + } + } - } - // Generate mesh - { - internal::LeafCPtrList<CharTreeT> edgeLeafs(*edgeTree); - mPolygonPoolListSize = edgeLeafs.size(); - mPolygons.reset(new PolygonPool[mPolygonPoolListSize]); - - if (noAdaptivity) { - internal::MeshGen<DistTreeT, internal::QuadMeshOp> - meshGen(edgeLeafs, *auxTree, mPolygons); - meshGen.setRefData(refData); - meshGen.runParallel(); - } else { - internal::MeshGen<DistTreeT, internal::AdaptiveMeshOp> - meshGen(edgeLeafs, *auxTree, mPolygons); - meshGen.setRefData(refData); - meshGen.runParallel(); + + if (!newPoints.empty()) { + + size_t newPointCount = newPoints.size() + mPointListSize; + + internal::UniquePtr<openvdb::Vec3s>::type + newPointList(new openvdb::Vec3s[newPointCount]); + + for (size_t i = 0; i < mPointListSize; ++i) { + newPointList.get()[i] = mPoints[i]; + } + + for (size_t i = mPointListSize; i < newPointCount; ++i) { + newPointList.get()[i] = newPoints[i - mPointListSize]; + } + + mPointListSize = newPointCount; + mPoints.reset(newPointList.release()); + mPointFlags.resize(mPointListSize, 0); } } } @@ -2699,6 +4684,10 @@ volumeToMesh( volumeToMesh(grid,points, triangles, quads, isovalue, 0.0); } + +//////////////////////////////////////// + + } // namespace tools } // namespace OPENVDB_VERSION_NAME } // namespace openvdb diff --git a/extern/openvdb/internal/openvdb/tools/VolumeToSpheres.h b/extern/openvdb/internal/openvdb/tools/VolumeToSpheres.h new file mode 100644 index 00000000000..f233eef1330 --- /dev/null +++ b/extern/openvdb/internal/openvdb/tools/VolumeToSpheres.h @@ -0,0 +1,1034 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +// +// Redistributions of source code must retain the above copyright +// and license notice and the following restrictions and disclaimer. +// +// * Neither the name of DreamWorks Animation nor the names of +// its contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE +// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef OPENVDB_TOOLS_VOLUME_TO_SPHERES_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_VOLUME_TO_SPHERES_HAS_BEEN_INCLUDED + +#include <openvdb/tree/ValueAccessor.h> +#include <openvdb/tree/LeafManager.h> +#include <openvdb/tools/Morphology.h> // for erodeVoxels() + +#include <openvdb/tools/PointScatter.h> +#include <openvdb/tools/LevelSetUtil.h> +#include <openvdb/tools/VolumeToMesh.h> + +#include <boost/scoped_array.hpp> +#include <boost/scoped_ptr.hpp> +#include <tbb/blocked_range.h> +#include <tbb/parallel_for.h> +#include <tbb/parallel_reduce.h> + +#include <vector> +#include <limits> // std::numeric_limits + +////////// + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +/// @brief Threaded method to fill a closed level set or fog volume +/// with adaptively sized spheres. +/// +/// @param grid a scalar gird to fill with spheres. +/// +/// @param spheres a @c Vec4 array representing the spheres that returned by this +/// method. The first three components specify the sphere center +/// and the fourth is the radius. The spheres in this array are +/// ordered by radius, biggest to smallest. +/// +/// @param maxSphereCount no more than this number of spheres are generated. +/// +/// @param overlapping toggle to allow spheres to overlap/intersect +/// +/// @param minRadius determines the smallest sphere size in voxel units. +/// +/// @param maxRadius determines the largest sphere size in voxel units. +/// +/// @param isovalue the crossing point of the volume values that is considered +/// the surface. The zero default value works for signed distance +/// fields while fog volumes require a larger positive value, +/// 0.5 is a good initial guess. +/// +/// @param instanceCount how many interior points to consider for the sphere placement, +/// increasing this count increases the chances of finding optimal +/// sphere sizes. +/// +/// @param interrupter a pointer adhering to the util::NullInterrupter interface +/// +template<typename GridT, typename InterrupterT> +inline void +fillWithSpheres( + const GridT& grid, + std::vector<openvdb::Vec4s>& spheres, + int maxSphereCount, + bool overlapping = false, + float minRadius = 1.0, + float maxRadius = std::numeric_limits<float>::max(), + float isovalue = 0.0, + int instanceCount = 10000, + InterrupterT* interrupter = NULL); + + +/// @brief @c fillWithSpheres method variant that automatically infers +/// the util::NullInterrupter. +template<typename GridT> +inline void +fillWithSpheres( + const GridT& grid, + std::vector<openvdb::Vec4s>& spheres, + int maxSphereCount, + bool overlapping = false, + float minRadius = 1.0, + float maxRadius = std::numeric_limits<float>::max(), + float isovalue = 0.0, + int instanceCount = 10000) +{ + fillWithSpheres<GridT, util::NullInterrupter>(grid, spheres, + maxSphereCount, overlapping, minRadius, maxRadius, isovalue, instanceCount); +} + + +//////////////////////////////////////// + + +/// @brief Accelerated closest surface point queries for narrow band level sets. +/// Supports queries that originate at arbitrary worldspace locations, is +/// not confined to the narrow band region of the input volume geometry. +template<typename GridT> +class ClosestSurfacePoint +{ +public: + typedef typename GridT::TreeType TreeT; + typedef typename TreeT::template ValueConverter<int>::Type IntTreeT; + typedef typename TreeT::template ValueConverter<Int16>::Type Int16TreeT; + + + ClosestSurfacePoint(); + + + /// @brief Extracts the surface points and constructs a spatial acceleration structure. + /// + /// @param grid a scalar gird, level set or fog volume. + /// + /// @param isovalue the crossing point of the volume values that is considered + /// the surface. The zero default value works for signed distance + /// fields while fog volumes require a larger positive value, + /// 0.5 is a good initial guess. + /// + /// @param interrupter a pointer adhering to the util::NullInterrupter interface. + /// + template<typename InterrupterT> + void initialize(const GridT& grid, float isovalue = 0.0, InterrupterT* interrupter = NULL); + + + /// @brief @c initialize method variant that automatically infers + /// the util::NullInterrupter. + void initialize(const GridT& grid, float isovalue = 0.0); + + + + /// @brief Computes distance to closest surface. + /// + /// @param points search locations in world space. + /// + /// @param distances list of closest surface point distances, populated by this method. + /// + bool search(const std::vector<Vec3R>& points, std::vector<float>& distances); + + + /// @brief Performs closest point searches. + /// + /// @param points search locations in world space to be replaced by their closest + /// surface point. + /// + /// @param distances list of closest surface point distances, populated by this method. + /// + bool searchAndReplace(std::vector<Vec3R>& points, std::vector<float>& distances); + + + /// @{ + /// @brief Tree accessors + const IntTreeT& indexTree() const { return *mIdxTreePt; } + const Int16TreeT& signTree() const { return *mSignTreePt; } + /// @} + +private: + typedef typename IntTreeT::LeafNodeType IntLeafT; + typedef std::pair<size_t, size_t> IndexRange; + + bool mIsInitialized; + std::vector<Vec4R> mLeafBoundingSpheres, mNodeBoundingSpheres; + std::vector<IndexRange> mLeafRanges; + std::vector<const IntLeafT*> mLeafNodes; + PointList mSurfacePointList; + size_t mPointListSize, mMaxNodeLeafs; + float mMaxRadiusSqr; + typename IntTreeT::Ptr mIdxTreePt; + typename Int16TreeT::Ptr mSignTreePt; + + bool search(std::vector<Vec3R>&, std::vector<float>&, bool transformPoints); +}; + + +//////////////////////////////////////// + + + + +// Internal utility methods + + +namespace internal { + +struct PointAccessor +{ + PointAccessor(std::vector<Vec3R>& points) + : mPoints(points) + { + } + + void add(const Vec3R &pos) + { + mPoints.push_back(pos); + } +private: + std::vector<Vec3R>& mPoints; +}; + + +template<typename IntLeafT> +class LeafBS +{ +public: + + LeafBS(std::vector<Vec4R>& leafBoundingSpheres, + const std::vector<const IntLeafT*>& leafNodes, + const math::Transform& transform, + const PointList& surfacePointList); + + void run(bool threaded = true); + + + void operator()(const tbb::blocked_range<size_t>&) const; + +private: + std::vector<Vec4R>& mLeafBoundingSpheres; + const std::vector<const IntLeafT*>& mLeafNodes; + const math::Transform& mTransform; + const PointList& mSurfacePointList; +}; + +template<typename IntLeafT> +LeafBS<IntLeafT>::LeafBS( + std::vector<Vec4R>& leafBoundingSpheres, + const std::vector<const IntLeafT*>& leafNodes, + const math::Transform& transform, + const PointList& surfacePointList) + : mLeafBoundingSpheres(leafBoundingSpheres) + , mLeafNodes(leafNodes) + , mTransform(transform) + , mSurfacePointList(surfacePointList) +{ +} + +template<typename IntLeafT> +void +LeafBS<IntLeafT>::run(bool threaded) +{ + if (threaded) { + tbb::parallel_for(tbb::blocked_range<size_t>(0, mLeafNodes.size()), *this); + } else { + (*this)(tbb::blocked_range<size_t>(0, mLeafNodes.size())); + } +} + +template<typename IntLeafT> +void +LeafBS<IntLeafT>::operator()(const tbb::blocked_range<size_t>& range) const +{ + typename IntLeafT::ValueOnCIter iter; + Vec3s avg; + + for (size_t n = range.begin(); n != range.end(); ++n) { + + avg[0] = 0.0; + avg[1] = 0.0; + avg[2] = 0.0; + + int count = 0; + for (iter = mLeafNodes[n]->cbeginValueOn(); iter; ++iter) { + avg += mSurfacePointList[iter.getValue()]; + ++count; + } + + if (count > 1) avg *= float(1.0 / double(count)); + + float maxDist = 0.0; + + for (iter = mLeafNodes[n]->cbeginValueOn(); iter; ++iter) { + float tmpDist = (mSurfacePointList[iter.getValue()] - avg).lengthSqr(); + if (tmpDist > maxDist) maxDist = tmpDist; + } + + Vec4R& sphere = mLeafBoundingSpheres[n]; + + sphere[0] = avg[0]; + sphere[1] = avg[1]; + sphere[2] = avg[2]; + sphere[3] = maxDist * 2.0; // padded radius + } +} + + +class NodeBS +{ +public: + typedef std::pair<size_t, size_t> IndexRange; + + NodeBS(std::vector<Vec4R>& nodeBoundingSpheres, + const std::vector<IndexRange>& leafRanges, + const std::vector<Vec4R>& leafBoundingSpheres); + + void run(bool threaded = true); + + + void operator()(const tbb::blocked_range<size_t>&) const; + +private: + std::vector<Vec4R>& mNodeBoundingSpheres; + const std::vector<IndexRange>& mLeafRanges; + const std::vector<Vec4R>& mLeafBoundingSpheres; +}; + +inline +NodeBS::NodeBS(std::vector<Vec4R>& nodeBoundingSpheres, + const std::vector<IndexRange>& leafRanges, + const std::vector<Vec4R>& leafBoundingSpheres) + : mNodeBoundingSpheres(nodeBoundingSpheres) + , mLeafRanges(leafRanges) + , mLeafBoundingSpheres(leafBoundingSpheres) +{ +} + +inline void +NodeBS::run(bool threaded) +{ + if (threaded) { + tbb::parallel_for(tbb::blocked_range<size_t>(0, mLeafRanges.size()), *this); + } else { + (*this)(tbb::blocked_range<size_t>(0, mLeafRanges.size())); + } +} + +inline void +NodeBS::operator()(const tbb::blocked_range<size_t>& range) const +{ + Vec3s avg, pos; + + for (size_t n = range.begin(); n != range.end(); ++n) { + + avg[0] = 0.0; + avg[1] = 0.0; + avg[2] = 0.0; + + int count = mLeafRanges[n].second - mLeafRanges[n].first; + + for (size_t i = mLeafRanges[n].first; i < mLeafRanges[n].second; ++i) { + avg[0] += mLeafBoundingSpheres[i][0]; + avg[1] += mLeafBoundingSpheres[i][1]; + avg[2] += mLeafBoundingSpheres[i][2]; + } + + if (count > 1) avg *= float(1.0 / double(count)); + + + float maxDist = 0.0; + + for (size_t i = mLeafRanges[n].first; i < mLeafRanges[n].second; ++i) { + pos[0] = mLeafBoundingSpheres[i][0]; + pos[1] = mLeafBoundingSpheres[i][1]; + pos[2] = mLeafBoundingSpheres[i][2]; + + float tmpDist = (pos - avg).lengthSqr() + mLeafBoundingSpheres[i][3]; + if (tmpDist > maxDist) maxDist = tmpDist; + } + + Vec4R& sphere = mNodeBoundingSpheres[n]; + + sphere[0] = avg[0]; + sphere[1] = avg[1]; + sphere[2] = avg[2]; + sphere[3] = maxDist * 2.0; // padded radius + } +} + + + +//////////////////////////////////////// + + +template<typename IntLeafT> +class ClosestPointDist +{ +public: + typedef std::pair<size_t, size_t> IndexRange; + + ClosestPointDist( + std::vector<Vec3R>& instancePoints, + std::vector<float>& instanceDistances, + const PointList& surfacePointList, + const std::vector<const IntLeafT*>& leafNodes, + const std::vector<IndexRange>& leafRanges, + const std::vector<Vec4R>& leafBoundingSpheres, + const std::vector<Vec4R>& nodeBoundingSpheres, + size_t maxNodeLeafs, + bool transformPoints = false); + + + void run(bool threaded = true); + + + void operator()(const tbb::blocked_range<size_t>&) const; + +private: + + void evalLeaf(size_t index, const IntLeafT& leaf) const; + void evalNode(size_t pointIndex, size_t nodeIndex) const; + + + std::vector<Vec3R>& mInstancePoints; + std::vector<float>& mInstanceDistances; + + const PointList& mSurfacePointList; + + const std::vector<const IntLeafT*>& mLeafNodes; + const std::vector<IndexRange>& mLeafRanges; + const std::vector<Vec4R>& mLeafBoundingSpheres; + const std::vector<Vec4R>& mNodeBoundingSpheres; + + std::vector<float> mLeafDistances, mNodeDistances; + + const bool mTransformPoints; + size_t mClosestPointIndex; +}; + + +template<typename IntLeafT> +ClosestPointDist<IntLeafT>::ClosestPointDist( + std::vector<Vec3R>& instancePoints, + std::vector<float>& instanceDistances, + const PointList& surfacePointList, + const std::vector<const IntLeafT*>& leafNodes, + const std::vector<IndexRange>& leafRanges, + const std::vector<Vec4R>& leafBoundingSpheres, + const std::vector<Vec4R>& nodeBoundingSpheres, + size_t maxNodeLeafs, + bool transformPoints) + : mInstancePoints(instancePoints) + , mInstanceDistances(instanceDistances) + , mSurfacePointList(surfacePointList) + , mLeafNodes(leafNodes) + , mLeafRanges(leafRanges) + , mLeafBoundingSpheres(leafBoundingSpheres) + , mNodeBoundingSpheres(nodeBoundingSpheres) + , mLeafDistances(maxNodeLeafs, 0.0) + , mNodeDistances(leafRanges.size(), 0.0) + , mTransformPoints(transformPoints) + , mClosestPointIndex(0) +{ +} + + +template<typename IntLeafT> +void +ClosestPointDist<IntLeafT>::run(bool threaded) +{ + if (threaded) { + tbb::parallel_for(tbb::blocked_range<size_t>(0, mInstancePoints.size()), *this); + } else { + (*this)(tbb::blocked_range<size_t>(0, mInstancePoints.size())); + } +} + +template<typename IntLeafT> +void +ClosestPointDist<IntLeafT>::evalLeaf(size_t index, const IntLeafT& leaf) const +{ + typename IntLeafT::ValueOnCIter iter; + const Vec3s center = mInstancePoints[index]; + size_t& closestPointIndex = const_cast<size_t&>(mClosestPointIndex); + + for (iter = leaf.cbeginValueOn(); iter; ++iter) { + + const Vec3s& point = mSurfacePointList[iter.getValue()]; + float tmpDist = (point - center).lengthSqr(); + + if (tmpDist < mInstanceDistances[index]) { + mInstanceDistances[index] = tmpDist; + closestPointIndex = iter.getValue(); + } + } +} + + +template<typename IntLeafT> +void +ClosestPointDist<IntLeafT>::evalNode(size_t pointIndex, size_t nodeIndex) const +{ + const Vec3R& pos = mInstancePoints[pointIndex]; + float minDist = mInstanceDistances[pointIndex]; + size_t minDistIdx = 0; + Vec3R center; + bool updatedDist = false; + + for (size_t i = mLeafRanges[nodeIndex].first, n = 0; i < mLeafRanges[nodeIndex].second; ++i, ++n) { + + float& distToLeaf = const_cast<float&>(mLeafDistances[n]); + + center[0] = mLeafBoundingSpheres[i][0]; + center[1] = mLeafBoundingSpheres[i][1]; + center[2] = mLeafBoundingSpheres[i][2]; + + distToLeaf = (pos - center).lengthSqr() - mLeafBoundingSpheres[i][3]; + + if (distToLeaf < minDist) { + minDist = distToLeaf; + minDistIdx = i; + updatedDist = true; + } + } + + if (!updatedDist) return; + + evalLeaf(pointIndex, *mLeafNodes[minDistIdx]); + + for (size_t i = mLeafRanges[nodeIndex].first, n = 0; i < mLeafRanges[nodeIndex].second; ++i, ++n) { + if (mLeafDistances[n] < mInstanceDistances[pointIndex] && i != minDistIdx) { + evalLeaf(pointIndex, *mLeafNodes[i]); + } + } +} + + +template<typename IntLeafT> +void +ClosestPointDist<IntLeafT>::operator()(const tbb::blocked_range<size_t>& range) const +{ + Vec3R center; + for (size_t n = range.begin(); n != range.end(); ++n) { + + const Vec3R& pos = mInstancePoints[n]; + float minDist = mInstanceDistances[n]; + size_t minDistIdx = 0; + + for (size_t i = 0, I = mNodeDistances.size(); i < I; ++i) { + float& distToNode = const_cast<float&>(mNodeDistances[i]); + + center[0] = mNodeBoundingSpheres[i][0]; + center[1] = mNodeBoundingSpheres[i][1]; + center[2] = mNodeBoundingSpheres[i][2]; + + distToNode = (pos - center).lengthSqr() - mNodeBoundingSpheres[i][3]; + + if (distToNode < minDist) { + minDist = distToNode; + minDistIdx = i; + } + } + + evalNode(n, minDistIdx); + + for (size_t i = 0, I = mNodeDistances.size(); i < I; ++i) { + if (mNodeDistances[i] < mInstanceDistances[n] && i != minDistIdx) { + evalNode(n, i); + } + } + + mInstanceDistances[n] = std::sqrt(mInstanceDistances[n]); + + if (mTransformPoints) mInstancePoints[n] = mSurfacePointList[mClosestPointIndex]; + } +} + + +class UpdatePoints +{ +public: + UpdatePoints( + const Vec4s& sphere, + const std::vector<Vec3R>& points, + std::vector<float>& distances, + std::vector<unsigned char>& mask, + bool overlapping); + + float radius() const { return mRadius; } + int index() const { return mIndex; }; + + void run(bool threaded = true); + + + UpdatePoints(UpdatePoints&, tbb::split); + void operator()(const tbb::blocked_range<size_t>& range); + void join(const UpdatePoints& rhs) + { + if (rhs.mRadius > mRadius) { + mRadius = rhs.mRadius; + mIndex = rhs.mIndex; + } + } + +private: + + const Vec4s& mSphere; + const std::vector<Vec3R>& mPoints; + + std::vector<float>& mDistances; + std::vector<unsigned char>& mMask; + + bool mOverlapping; + float mRadius; + int mIndex; +}; + +inline +UpdatePoints::UpdatePoints( + const Vec4s& sphere, + const std::vector<Vec3R>& points, + std::vector<float>& distances, + std::vector<unsigned char>& mask, + bool overlapping) + : mSphere(sphere) + , mPoints(points) + , mDistances(distances) + , mMask(mask) + , mOverlapping(overlapping) + , mRadius(0.0) + , mIndex(0) +{ +} + +inline +UpdatePoints::UpdatePoints(UpdatePoints& rhs, tbb::split) + : mSphere(rhs.mSphere) + , mPoints(rhs.mPoints) + , mDistances(rhs.mDistances) + , mMask(rhs.mMask) + , mOverlapping(rhs.mOverlapping) + , mRadius(rhs.mRadius) + , mIndex(rhs.mIndex) +{ +} + +inline void +UpdatePoints::run(bool threaded) +{ + if (threaded) { + tbb::parallel_reduce(tbb::blocked_range<size_t>(0, mPoints.size()), *this); + } else { + (*this)(tbb::blocked_range<size_t>(0, mPoints.size())); + } +} + +inline void +UpdatePoints::operator()(const tbb::blocked_range<size_t>& range) +{ + Vec3s pos; + for (size_t n = range.begin(); n != range.end(); ++n) { + if (mMask[n]) continue; + + pos.x() = float(mPoints[n].x()) - mSphere[0]; + pos.y() = float(mPoints[n].y()) - mSphere[1]; + pos.z() = float(mPoints[n].z()) - mSphere[2]; + + float dist = pos.length(); + + if (dist < mSphere[3]) { + mMask[n] = 1; + continue; + } + + if (!mOverlapping) { + mDistances[n] = std::min(mDistances[n], (dist - mSphere[3])); + } + + if (mDistances[n] > mRadius) { + mRadius = mDistances[n]; + mIndex = n; + } + } +} + + +} // namespace internal + + +//////////////////////////////////////// + + +template<typename GridT, typename InterrupterT> +inline void +fillWithSpheres( + const GridT& grid, + std::vector<openvdb::Vec4s>& spheres, + int maxSphereCount, + bool overlapping, + float minRadius, + float maxRadius, + float isovalue, + int instanceCount, + InterrupterT* interrupter) +{ + spheres.clear(); + spheres.reserve(maxSphereCount); + + const bool addNBPoints = grid.activeVoxelCount() < 10000; + int instances = std::max(instanceCount, maxSphereCount); + + typedef typename GridT::TreeType TreeT; + typedef typename GridT::ValueType ValueT; + + typedef typename TreeT::template ValueConverter<bool>::Type BoolTreeT; + typedef typename TreeT::template ValueConverter<int>::Type IntTreeT; + typedef typename TreeT::template ValueConverter<Int16>::Type Int16TreeT; + + typedef tree::LeafManager<const TreeT> LeafManagerT; + typedef tree::LeafManager<IntTreeT> IntLeafManagerT; + typedef tree::LeafManager<Int16TreeT> Int16LeafManagerT; + + + typedef boost::mt11213b RandGen; + RandGen mtRand(/*seed=*/0); + + const TreeT& tree = grid.tree(); + const math::Transform& transform = grid.transform(); + + std::vector<Vec3R> instancePoints; + + { // Scatter candidate sphere centroids (instancePoints) + typename Grid<BoolTreeT>::Ptr interiorMaskPtr; + + if (grid.getGridClass() == GRID_LEVEL_SET) { + interiorMaskPtr = sdfInteriorMask(grid, ValueT(isovalue)); + } else { + interiorMaskPtr = typename Grid<BoolTreeT>::Ptr(Grid<BoolTreeT>::create(false)); + interiorMaskPtr->setTransform(transform.copy()); + interiorMaskPtr->tree().topologyUnion(tree); + } + + if (interrupter && interrupter->wasInterrupted()) return; + + erodeVoxels(interiorMaskPtr->tree(), 1); + + instancePoints.reserve(instances); + internal::PointAccessor ptnAcc(instancePoints); + + UniformPointScatter<internal::PointAccessor, RandGen, InterrupterT> + scatter(ptnAcc, (addNBPoints ? (instances / 2) : instances), mtRand, interrupter); + + scatter(*interiorMaskPtr); + } + + if (interrupter && interrupter->wasInterrupted()) return; + + std::vector<float> instanceRadius; + + ClosestSurfacePoint<GridT> csp; + csp.initialize(grid, isovalue, interrupter); + + // add extra instance points in the interior narrow band. + if (instancePoints.size() < instances) { + const Int16TreeT& signTree = csp.signTree(); + typename Int16TreeT::LeafNodeType::ValueOnCIter it; + typename Int16TreeT::LeafCIter leafIt = signTree.cbeginLeaf(); + + for (; leafIt; ++leafIt) { + for (it = leafIt->cbeginValueOn(); it; ++it) { + const int flags = it.getValue(); + if (!(0xE00 & flags) && (flags & 0x100)) { + instancePoints.push_back(transform.indexToWorld(it.getCoord())); + } + + if (instancePoints.size() == instances) break; + } + if (instancePoints.size() == instances) break; + } + } + + + if (interrupter && interrupter->wasInterrupted()) return; + + if (!csp.search(instancePoints, instanceRadius)) return; + + std::vector<unsigned char> instanceMask(instancePoints.size(), 0); + float largestRadius = 0.0; + int largestRadiusIdx = 0; + + for (size_t n = 0, N = instancePoints.size(); n < N; ++n) { + if (instanceRadius[n] > largestRadius) { + largestRadius = instanceRadius[n]; + largestRadiusIdx = n; + } + } + + Vec3s pos; + Vec4s sphere; + minRadius *= transform.voxelSize()[0]; + maxRadius *= transform.voxelSize()[0]; + + for (size_t s = 0, S = std::min(size_t(maxSphereCount), instancePoints.size()); s < S; ++s) { + + if (interrupter && interrupter->wasInterrupted()) return; + + largestRadius = std::min(maxRadius, largestRadius); + + if (s != 0 && largestRadius < minRadius) break; + + sphere[0] = float(instancePoints[largestRadiusIdx].x()); + sphere[1] = float(instancePoints[largestRadiusIdx].y()); + sphere[2] = float(instancePoints[largestRadiusIdx].z()); + sphere[3] = largestRadius; + + spheres.push_back(sphere); + instanceMask[largestRadiusIdx] = 1; + + internal::UpdatePoints op(sphere, instancePoints, instanceRadius, instanceMask, overlapping); + op.run(); + + largestRadius = op.radius(); + largestRadiusIdx = op.index(); + } +} + +//////////////////////////////////////// + + +template<typename GridT> +ClosestSurfacePoint<GridT>::ClosestSurfacePoint() + : mIsInitialized(false) + , mLeafBoundingSpheres(0) + , mNodeBoundingSpheres(0) + , mLeafRanges(0) + , mLeafNodes(0) + , mSurfacePointList() + , mPointListSize(0) + , mMaxNodeLeafs(0) + , mMaxRadiusSqr(0.0) + , mIdxTreePt() +{ +} + +template<typename GridT> +void +ClosestSurfacePoint<GridT>::initialize(const GridT& grid, float isovalue) +{ + initialize<GridT, util::NullInterrupter>(grid, isovalue, NULL); +} + + +template<typename GridT> +template<typename InterrupterT> +void +ClosestSurfacePoint<GridT>::initialize( + const GridT& grid, float isovalue, InterrupterT* interrupter) +{ + mIsInitialized = false; + typedef tree::LeafManager<const TreeT> LeafManagerT; + typedef tree::LeafManager<IntTreeT> IntLeafManagerT; + typedef tree::LeafManager<Int16TreeT> Int16LeafManagerT; + typedef typename GridT::ValueType ValueT; + + const TreeT& tree = grid.tree(); + const math::Transform& transform = grid.transform(); + + { // Extract surface point cloud + + { + LeafManagerT leafs(tree); + internal::SignData<TreeT, LeafManagerT> + signDataOp(tree, leafs, ValueT(isovalue)); + + signDataOp.run(); + + mSignTreePt = signDataOp.signTree(); + mIdxTreePt = signDataOp.idxTree(); + } + + if (interrupter && interrupter->wasInterrupted()) return; + + Int16LeafManagerT signLeafs(*mSignTreePt); + + std::vector<size_t> regions(signLeafs.leafCount(), 0); + signLeafs.foreach(internal::CountPoints(regions)); + + mPointListSize = 0; + for (size_t tmp = 0, n = 0, N = regions.size(); n < N; ++n) { + tmp = regions[n]; + regions[n] = mPointListSize; + mPointListSize += tmp; + } + + if (mPointListSize == 0) return; + + mSurfacePointList.reset(new Vec3s[mPointListSize]); + + internal::GenPoints<TreeT, Int16LeafManagerT> + pointOp(signLeafs, tree, *mIdxTreePt, mSurfacePointList, regions, transform, isovalue); + + pointOp.run(); + + mIdxTreePt->topologyUnion(*mSignTreePt); + } + + if (interrupter && interrupter->wasInterrupted()) return; + + // estimate max sphere radius (sqr dist) + CoordBBox bbox = grid.evalActiveVoxelBoundingBox(); + + Vec3s dim = transform.indexToWorld(bbox.min()) - + transform.indexToWorld(bbox.max()); + + dim[0] = std::abs(dim[0]); + dim[1] = std::abs(dim[1]); + dim[2] = std::abs(dim[2]); + + mMaxRadiusSqr = std::min(std::min(dim[0], dim[1]), dim[2]); + mMaxRadiusSqr *= 0.51; + mMaxRadiusSqr *= mMaxRadiusSqr; + + + IntLeafManagerT idxLeafs(*mIdxTreePt); + + + typedef typename IntTreeT::RootNodeType IntRootNodeT; + typedef typename IntRootNodeT::NodeChainType IntNodeChainT; + BOOST_STATIC_ASSERT(boost::mpl::size<IntNodeChainT>::value > 1); + typedef typename boost::mpl::at<IntNodeChainT, boost::mpl::int_<1> >::type IntInternalNodeT; + + + typename IntTreeT::NodeCIter nIt = mIdxTreePt->cbeginNode(); + nIt.setMinDepth(IntTreeT::NodeCIter::LEAF_DEPTH - 1); + nIt.setMaxDepth(IntTreeT::NodeCIter::LEAF_DEPTH - 1); + + std::vector<const IntInternalNodeT*> internalNodes; + + const IntInternalNodeT* node = NULL; + for (; nIt; ++nIt) { + nIt.getNode(node); + if (node) internalNodes.push_back(node); + } + + std::vector<IndexRange>().swap(mLeafRanges); + mLeafRanges.resize(internalNodes.size()); + + std::vector<const IntLeafT*>().swap(mLeafNodes); + mLeafNodes.reserve(idxLeafs.leafCount()); + + typename IntInternalNodeT::ChildOnCIter leafIt; + mMaxNodeLeafs = 0; + for (size_t n = 0, N = internalNodes.size(); n < N; ++n) { + + mLeafRanges[n].first = mLeafNodes.size(); + + size_t leafCount = 0; + for (leafIt = internalNodes[n]->cbeginChildOn(); leafIt; ++leafIt) { + mLeafNodes.push_back(&(*leafIt)); + ++leafCount; + } + + mMaxNodeLeafs = std::max(leafCount, mMaxNodeLeafs); + + mLeafRanges[n].second = mLeafNodes.size(); + } + + std::vector<Vec4R>().swap(mLeafBoundingSpheres); + mLeafBoundingSpheres.resize(mLeafNodes.size()); + + internal::LeafBS<IntLeafT> leafBS(mLeafBoundingSpheres, mLeafNodes, transform, mSurfacePointList); + leafBS.run(); + + + std::vector<Vec4R>().swap(mNodeBoundingSpheres); + mNodeBoundingSpheres.resize(internalNodes.size()); + + internal::NodeBS nodeBS(mNodeBoundingSpheres, mLeafRanges, mLeafBoundingSpheres); + nodeBS.run(); + mIsInitialized = true; +} + + +template<typename GridT> +bool +ClosestSurfacePoint<GridT>::search(std::vector<Vec3R>& points, + std::vector<float>& distances, bool transformPoints) +{ + if (!mIsInitialized) return false; + + distances.clear(); + distances.resize(points.size(), mMaxRadiusSqr); + + internal::ClosestPointDist<IntLeafT> cpd(points, distances, mSurfacePointList, + mLeafNodes, mLeafRanges, mLeafBoundingSpheres, mNodeBoundingSpheres, + mMaxNodeLeafs, transformPoints); + + cpd.run(); + + return true; +} + + +template<typename GridT> +bool +ClosestSurfacePoint<GridT>::search(const std::vector<Vec3R>& points, std::vector<float>& distances) +{ + return search(const_cast<std::vector<Vec3R>& >(points), distances, false); +} + + +template<typename GridT> +bool +ClosestSurfacePoint<GridT>::searchAndReplace(std::vector<Vec3R>& points, std::vector<float>& distances) +{ + return search(points, distances, true); +} + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_VOLUME_TO_MESH_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2013 DreamWorks Animation LLC +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/extern/openvdb/internal/openvdb/tree/InternalNode.h b/extern/openvdb/internal/openvdb/tree/InternalNode.h index cb7d40c7124..d727fce3ed3 100644 --- a/extern/openvdb/internal/openvdb/tree/InternalNode.h +++ b/extern/openvdb/internal/openvdb/tree/InternalNode.h @@ -53,6 +53,9 @@ OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { namespace tree { +template<typename, Index, typename> struct SameInternalConfig; // forward declaration + + template<typename _ChildNodeType, Index Log2Dim> class InternalNode { @@ -80,6 +83,15 @@ public: OtherValueType>::Type, Log2Dim> Type; }; + /// @brief SameConfiguration<OtherNodeType>::value is @c true if and only if OtherNodeType + /// is the type of an InternalNode with the same dimensions as this node and whose + /// ChildNodeType has the same configuration as this node's ChildNodeType. + template<typename OtherNodeType> + struct SameConfiguration { + static const bool value = + SameInternalConfig<ChildNodeType, Log2Dim, OtherNodeType>::value; + }; + InternalNode() {} @@ -90,6 +102,10 @@ public: /// Deep copy constructor InternalNode(const InternalNode&); + /// Value conversion copy constructor + template<typename OtherChildNodeType> + explicit InternalNode(const InternalNode<OtherChildNodeType, Log2Dim>& other); + /// Topology copy constructor template<typename OtherChildNodeType> InternalNode(const InternalNode<OtherChildNodeType, Log2Dim>& other, @@ -98,8 +114,7 @@ public: /// Topology copy constructor template<typename OtherChildNodeType> InternalNode(const InternalNode<OtherChildNodeType, Log2Dim>& other, - const ValueType& offValue, const ValueType& onValue, - TopologyCopy); + const ValueType& offValue, const ValueType& onValue, TopologyCopy); virtual ~InternalNode(); @@ -113,7 +128,7 @@ protected: struct ChildOn {}; struct ChildOff {}; struct ChildAll {}; // The following class templates implement the iterator interfaces specified in Iterator.h - // by providing getItem() and setItem() methods for active values and/or inactive values. + // by providing getItem(), setItem() and/or modifyItem() methods. template<typename NodeT, typename ChildT, typename MaskIterT, typename TagT> struct ChildIter: public SparseIteratorBase< @@ -123,10 +138,16 @@ protected: ChildIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase< MaskIterT, ChildIter<NodeT, ChildT, MaskIterT, TagT>, NodeT, ChildT>(iter, parent) {} - ChildT& getItem(Index pos) const { return *(this->parent().getChildNode(pos)); } + ChildT& getItem(Index pos) const + { + assert(this->parent().isChildMaskOn(pos)); + return *(this->parent().getChildNode(pos)); + } // Note: setItem() can't be called on const iterators. - void setItem(Index pos, const ChildT& c) const { this->parent().setChildNode(pos, &c); } + void setItem(Index pos, const ChildT& c) const { this->parent().resetChildNode(pos, &c); } + + // Note: modifyItem() isn't implemented, since it's not useful for child node pointers. };// ChildIter template<typename NodeT, typename ValueT, typename MaskIterT, typename TagT> @@ -141,6 +162,13 @@ protected: // Note: setItem() can't be called on const iterators. void setItem(Index pos, const ValueT& v) const { this->parent().mNodes[pos].setValue(v); } + + // Note: modifyItem() can't be called on const iterators. + template<typename ModifyOp> + void modifyItem(Index pos, const ModifyOp& op) const + { + op(this->parent().mNodes[pos].getValue()); + } };// ValueIter template<typename NodeT, typename ChildT, typename ValueT, typename TagT> @@ -156,15 +184,19 @@ protected: bool getItem(Index pos, ChildT*& child, NonConstValueT& value) const { - child = this->parent().getChildNode(pos); - if (!child) value = this->parent().mNodes[pos].getValue(); - return (child != NULL); + if (this->parent().isChildMaskOn(pos)) { + child = this->parent().getChildNode(pos); + return true; + } + child = NULL; + value = this->parent().mNodes[pos].getValue(); + return false; } // Note: setItem() can't be called on const iterators. void setItem(Index pos, ChildT* child) const { - this->parent().setChildNode(pos, child); + this->parent().resetChildNode(pos, child); } // Note: unsetItem() can't be called on const iterators. @@ -216,11 +248,18 @@ public: static void getNodeLog2Dims(std::vector<Index>& dims); static Index getChildDim() { return ChildNodeType::DIM; } - static Index coord2offset(const Coord& xyz); - static void offset2coord(Index n, Coord& xyz); - Coord offset2globalCoord(Index n) const; + /// Return the linear table offset of the given global or local coordinates. + static Index coordToOffset(const Coord& xyz); + /// @brief Return the local coordinates for a linear table offset, + /// where offset 0 has coordinates (0, 0, 0). + static void offsetToLocalCoord(Index n, Coord& xyz); + /// Return the global coordinates for a linear table offset. + Coord offsetToGlobalCoord(Index n) const; - Coord getOrigin() const { return mOrigin; } + /// Return the grid index coordinates of this node's local origin. + const Coord& origin() const { return mOrigin; } + /// Set the grid index coordinates of this node's local origin. + void setOrigin(const Coord& origin) { mOrigin = origin; } Index32 leafCount() const; Index32 nonLeafCount() const; @@ -228,13 +267,16 @@ public: Index64 offVoxelCount() const; Index64 onLeafVoxelCount() const; Index64 offLeafVoxelCount() const; + Index64 onTileCount() const; /// Return the total amount of memory in bytes occupied by this node and its children. Index64 memUsage() const; /// @brief Expand the specified bounding box so that it includes the active tiles /// of this internal node as well as all the active values in its child nodes. - void evalActiveVoxelBoundingBox(CoordBBox& bbox) const; + /// If visitVoxels is false LeafNodes will be approximated as dense, i.e. with all + /// voxels active. Else the individual active voxels are visited to produce a tight bbox. + void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) const; /// @brief Return the bounding box of this node, i.e., the full index space /// spanned by the node regardless of its content. @@ -272,36 +314,26 @@ public: /// Otherwise, return the result of calling getLastValue() on the child. const ValueType& getLastValue() const; - /// Set the active state at the given coordinates, but don't change its value. + /// Set the active state of the voxel at the given coordinates but don't change its value. void setActiveState(const Coord& xyz, bool on); - - /// Mark the voxel at the given coordinates as inactive, but don't change its value. - void setValueOff(const Coord& xyz); - /// Change the value of the voxel at the given coordinates and mark the voxel as inactive. - void setValueOff(const Coord& xyz, const ValueType& value); - + /// Set the value of the voxel at the given coordinates but don't change its active state. + void setValueOnly(const Coord& xyz, const ValueType& value); + /// Mark the voxel at the given coordinates as active but don't change its value. void setValueOn(const Coord& xyz); + /// Set the value of the voxel at the given coordinates and mark the voxel as active. void setValueOn(const Coord& xyz, const ValueType& value); - void setValueOnly(const Coord& xyz, const ValueType& value); - void setValueOnMin(const Coord& xyz, const ValueType& value); - void setValueOnMax(const Coord& xyz, const ValueType& value); - void setValueOnSum(const Coord& xyz, const ValueType& value); - - /// @brief Set all voxels within an axis-aligned box to a constant value. - /// (The min and max coordinates are inclusive.) - void fill(const CoordBBox& bbox, const ValueType&, bool active = true); + /// Mark the voxel at the given coordinates as inactive but don't change its value. + void setValueOff(const Coord& xyz); + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, const ValueType& value); - /// @brief Copy into a dense grid the values of the voxels that lie within - /// a given bounding box. - /// - /// @param bbox inclusive bounding box of the voxels to be copied into the dense grid - /// @param dense dense grid with a stride in @e z of one (see tools::Dense - /// in tools/Dense.h for the required API) - /// - /// @note @a bbox is assumed to be identical to or contained in the coordinate domains - /// of both the dense grid and this node, i.e., no bounds checking is performed. - template<typename DenseT> - void copyToDense(const CoordBBox& bbox, DenseT& dense) const; + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + template<typename ModifyOp> + void modifyValue(const Coord& xyz, const ModifyOp& op); + /// Apply a functor to the voxel at the given coordinates. + template<typename ModifyOp> + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op); /// Return the value of the voxel at the given coordinates and, if necessary, update /// the accessor with pointers to the nodes along the path from the root node to @@ -331,13 +363,20 @@ public: template<typename AccessorT> void setValueOnlyAndCache(const Coord& xyz, const ValueType& value, AccessorT&); - /// Set the value of the voxel at the given coordinates to the sum of its current - /// value and the given value, and mark the voxel as active. + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. /// If necessary, update the accessor with pointers to the nodes along the path /// from the root node to the node containing the voxel. /// @note Used internally by ValueAccessor. - template<typename AccessorT> - void setValueOnSumAndCache(const Coord& xyz, const ValueType& value, AccessorT&); + template<typename ModifyOp, typename AccessorT> + void modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&); + + /// Apply a functor to the voxel at the given coordinates. + /// If necessary, update the accessor with pointers to the nodes along the path + /// from the root node to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template<typename ModifyOp, typename AccessorT> + void modifyValueAndActiveStateAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&); /// Change the value of the voxel at the given coordinates and mark it as inactive. /// If necessary, update the accessor with pointers to the nodes along the path @@ -353,9 +392,10 @@ public: template<typename AccessorT> void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&); - /// Return @c true if the voxel at the given coordinates is active, change the voxel's - /// value, and, if necessary, update the accessor with pointers to the nodes along + /// Return, in @a value, the value of the voxel at the given coordinates and, + /// if necessary, update the accessor with pointers to the nodes along /// the path from the root node to the node containing the voxel. + /// @return @c true if the voxel at the given coordinates is active /// @note Used internally by ValueAccessor. template<typename AccessorT> bool probeValueAndCache(const Coord& xyz, ValueType& value, AccessorT&) const; @@ -372,6 +412,7 @@ public: /// Mark all values (both tiles and voxels) as active. void setValuesOn(); + // // I/O // @@ -380,39 +421,54 @@ public: void writeBuffers(std::ostream&, bool toHalf = false) const; void readBuffers(std::istream&, bool fromHalf = false); - /// @brief Overwrites the inactive values with a new value whos - /// magnitude is equal to the specified background value and sign - /// is consistant with the lexicographically closest active value. - /// The net effect is a propagation of signs from the active to the - /// inactive values. Note this flood-filling is also performed on - /// any child nodes. - /// - /// @note This method is primarily useful for propagating the sign - /// from the (active) voxels in a narrow-band level set to the - /// inactive values outside the narrow band. + + // + // Aux methods + // + /// @brief Set all voxels within an axis-aligned box to a constant value. + /// (The min and max coordinates are inclusive.) + void fill(const CoordBBox& bbox, const ValueType&, bool active = true); + + /// @brief Overwrite each inactive value in this node and in any child nodes with + /// a new value whose magnitude is equal to the specified background value and whose + /// sign is consistent with that of the lexicographically closest active value. + /// @details This is primarily useful for propagating the sign from the (active) voxels + /// in a narrow-band level set to the inactive voxels outside the narrow band. void signedFloodFill(const ValueType& background); - /// @brief Sets the inactive values to either the outside or inside - /// value, depending on the sign of the closest corresponding - /// active value. More specefically, an inactive value is set to - /// the outside value if the closest active value in the - /// lexicographic direction is positive, else it is set to the - /// inside value. Note this operation is also performed on any child nodes. + /// @brief Overwrite each inactive value in this node and in any child nodes with + /// either @a outside or @a inside, depending on the sign of the lexicographically + /// closest active value. + /// @details Specifically, an inactive value is set to @a outside if the closest active + /// value in the lexicographic direction is positive, otherwise it is set to @a inside. void signedFloodFill(const ValueType& outside, const ValueType& inside); /// Change the sign of all the values represented in this node and /// its child nodes. void negate(); - /// Replace active tiles with dense voxels, i.e., with active leaf nodes. + /// Densify active tiles, i.e., replace them with leaf-level active voxels. void voxelizeActiveTiles(); - /// @brief Simple merge: Nodes and values of this node are always unchanged! - /// - /// @note Nodes and values of the other node are simply merged into this - /// node and the other tree is cannibalized in the process! + /// @brief Copy into a dense grid the values of the voxels that lie within + /// a given bounding box. + /// @param bbox inclusive bounding box of the voxels to be copied into the dense grid + /// @param dense dense grid with a stride in @e z of one (see tools::Dense + /// in tools/Dense.h for the required API) + /// @note @a bbox is assumed to be identical to or contained in the coordinate domains + /// of both the dense grid and this node, i.e., no bounds checking is performed. + template<typename DenseT> + void copyToDense(const CoordBBox& bbox, DenseT& dense) const; + + /// @brief Efficiently merge another tree into this tree using one of several schemes. + /// @warning This operation cannibalizes the other tree. + template<MergePolicy Policy> void merge(InternalNode& other, const ValueType& background, const ValueType& otherBackground); + /// @brief Merge, using one of several schemes, this node (and its descendants) + /// with a tile of the same dimensions and the given value and active state. + template<MergePolicy Policy> void merge(const ValueType& tileValue, bool tileActive); + /// @brief Union this branch's set of active values with the other branch's /// active values. The value type of the other branch can be different. /// @details The resulting state of a value is active if the corresponding value @@ -428,19 +484,49 @@ public: template<typename OtherChildNodeType> void topologyUnion(const InternalNode<OtherChildNodeType, Log2Dim>& other); + /// @brief Intersects this tree's set of active values with the active values + /// of the other tree, whose @c ValueType may be different. + /// @details The resulting state of a value is active only if the corresponding + /// value was already active AND if it is active in the other tree. Also, a + /// resulting value maps to a voxel if the corresponding value + /// already mapped to an active voxel in either of the two grids + /// and it maps to an active tile or voxel in the other grid. + /// + /// @note This operation can delete branches in this grid if they + /// overlap with inactive tiles in the other grid. Likewise active + /// voxels can be turned into unactive voxels resulting in leaf + /// nodes with no active values. Thus, it is recommended to + /// subsequently call prune. + template<typename OtherChildNodeType> + void topologyIntersection(const InternalNode<OtherChildNodeType, Log2Dim>& other, + const ValueType& background); + + /// @brief Difference this node's set of active values with the active values + /// of the other node, whose @c ValueType may be different. So a + /// resulting voxel will be active only if the original voxel is + /// active in this node and inactive in the other node. + /// + /// @details The last dummy argument is required to match the signature + /// for InternalNode::topologyDifference. + /// + /// @note This operation modifies only active states, not + /// values. Also note that this operation can result in all voxels + /// being inactive so consider subsequnetly calling prune. + template<typename OtherChildNodeType> + void topologyDifference(const InternalNode<OtherChildNodeType, Log2Dim>& other, + const ValueType& background); + template<typename CombineOp> void combine(InternalNode& other, CombineOp&); template<typename CombineOp> void combine(const ValueType& value, bool valueIsActive, CombineOp&); - template<typename CombineOp> - void combine2(const InternalNode& other0, const InternalNode& other1, CombineOp&); - template<typename CombineOp> - void combine2(const ValueType& value, const InternalNode& other, - bool valueIsActive, CombineOp&); - template<typename CombineOp> - void combine2(const InternalNode& other, const ValueType& value, - bool valueIsActive, CombineOp&); + template<typename CombineOp, typename OtherNodeType /*= InternalNode*/> + void combine2(const InternalNode& other0, const OtherNodeType& other1, CombineOp&); + template<typename CombineOp, typename OtherNodeType /*= InternalNode*/> + void combine2(const ValueType& value, const OtherNodeType& other, bool valIsActive, CombineOp&); + template<typename CombineOp, typename OtherValueType> + void combine2(const InternalNode& other, const OtherValueType&, bool valIsActive, CombineOp&); /// @brief Calls the templated functor BBoxOp with bounding box /// information for all active tiles and leaf nodes in this node. @@ -486,7 +572,7 @@ public: /// in the process. If the leaf node already exists, replace it. void addLeaf(LeafNodeType* leaf); - /// @brief Same as addLeaf except, if necessary, it update the accessor with pointers + /// @brief Same as addLeaf() except, if necessary, update the accessor with pointers /// to the nodes along the path from the root node to the node containing the coordinate. template<typename AccessorT> void addLeafAndCache(LeafNodeType* leaf, AccessorT&); @@ -506,35 +592,48 @@ public: /// possibly creating a parent branch or deleting a child branch in the process. void addTile(Index level, const Coord& xyz, const ValueType& value, bool state); + /// @brief Delete any existing child branch at the specified offset and add a tile. + void addTile(Index offset, const ValueType& value, bool state); + /// @brief Same as addTile() except, if necessary, update the accessor with pointers /// to the nodes along the path from the root node to the node containing (x, y, z). template<typename AccessorT> - void addTileAndCache(Index level, const Coord& xyz, const ValueType& value, - bool state, AccessorT&); + void addTileAndCache(Index level, const Coord& xyz, const ValueType&, bool state, AccessorT&); - /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). + //@{ + /// @brief Return a pointer to the node that contains voxel (x, y, z). /// If no such node exists, return NULL. - LeafNodeType* probeLeaf(const Coord& xyz); + template<typename NodeType> NodeType* probeNode(const Coord& xyz); + template<typename NodeType> const NodeType* probeConstNode(const Coord& xyz) const; + //@} + + //@{ + /// @brief Same as probeNode() except, if necessary, update the accessor with pointers + /// to the nodes along the path from the root node to the node containing (x, y, z). + template<typename NodeType, typename AccessorT> + NodeType* probeNodeAndCache(const Coord& xyz, AccessorT&); + template<typename NodeType, typename AccessorT> + const NodeType* probeConstNodeAndCache(const Coord& xyz, AccessorT&) const; + //@} - /// @brief Return a const pointer to the leaf node that contains voxel (x, y, z). + //@{ + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). /// If no such node exists, return NULL. + LeafNodeType* probeLeaf(const Coord& xyz); const LeafNodeType* probeConstLeaf(const Coord& xyz) const; - const LeafNodeType* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); } + const LeafNodeType* probeLeaf(const Coord& xyz) const; + //@} - /// @brief Same as probeLeaf except, if necessary, it update the accessor with pointers - /// to the nodes along the path from the root node to the node containing the coordinate. + //@{ + /// @brief Same as probeLeaf() except, if necessary, update the accessor with pointers + /// to the nodes along the path from the root node to the node containing (x, y, z). template<typename AccessorT> - LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT&); - - /// @brief Same as probeLeaf except, if necessary, it update the accessor with pointers - /// to the nodes along the path from the root node to the node containing the coordinate. + LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc); template<typename AccessorT> const LeafNodeType* probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const; template<typename AccessorT> - const LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc) const - { - return this->probeConstLeafAndCache(xyz, acc); - } + const LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc) const; + //@} /// @brief Return the leaf node that contains voxel (x, y, z). /// If no such node exists, create one, but preserve the values and @@ -544,7 +643,7 @@ public: /// over which to safely perform multithreaded processing. LeafNodeType* touchLeaf(const Coord& xyz); - /// @brief Same as touchLeaf except, if necessary, it update the accessor with pointers + /// @brief Same as touchLeaf() except, if necessary, update the accessor with pointers /// to the nodes along the path from the root node to the node containing the coordinate. template<typename AccessorT> LeafNodeType* touchLeafAndCache(const Coord& xyz, AccessorT&); @@ -588,12 +687,10 @@ protected: //@} void makeChildNodeEmpty(Index n, const ValueType& value); - void setChildNode(Index i, ChildNodeType* child); + void setChildNode( Index i, ChildNodeType* child);//assumes a tile + void resetChildNode(Index i, ChildNodeType* child);//checks for an existing child ChildNodeType* unsetChildNode(Index i, const ValueType& value); - template<typename NodeT> - NodeT* doStealNode(const Coord& xyz, const ValueType& value, bool state); - template<typename NodeT, typename VisitorOp, typename ChildAllIterT> static inline void doVisit(NodeT&, VisitorOp&); @@ -605,8 +702,13 @@ protected: typename ChildAllIterT, typename OtherChildAllIterT> static inline void doVisit2(NodeT&, OtherChildAllIterT&, VisitorOp&, bool otherIsLHS); + ///@{ + /// @brief Returns a pointer to the child node at the linear offset n. + /// @warning This protected method assumes that a child node exists at + /// the specified linear offset! ChildNodeType* getChildNode(Index n); const ChildNodeType* getChildNode(Index n) const; + ///@} UnionType mNodes[NUM_VALUES]; @@ -619,6 +721,24 @@ protected: //////////////////////////////////////// +//@{ +/// Helper metafunction used to implement InternalNode::SameConfiguration +/// (which, as an inner class, can't be independently specialized) +template<typename ChildT1, Index Dim1, typename NodeT2> +struct SameInternalConfig { + static const bool value = false; +}; + +template<typename ChildT1, Index Dim1, typename ChildT2> +struct SameInternalConfig<ChildT1, Dim1, InternalNode<ChildT2, Dim1> > { + static const bool value = ChildT1::template SameConfiguration<ChildT2>::value; +}; +//@} + + +//////////////////////////////////////// + + template<typename ChildT, Index Log2Dim> inline InternalNode<ChildT, Log2Dim>::InternalNode(const ValueType& background) @@ -655,6 +775,32 @@ InternalNode<ChildT, Log2Dim>::InternalNode(const InternalNode& other): } } + +// Copy-construct from a node with the same configuration but a different ValueType. +template<typename ChildT, Index Log2Dim> +template<typename OtherChildNodeType> +inline +InternalNode<ChildT, Log2Dim>::InternalNode(const InternalNode<OtherChildNodeType, Log2Dim>& other) + : mChildMask(other.mChildMask) + , mValueMask(other.mValueMask) + , mOrigin(other.mOrigin) +{ + struct Local { + /// @todo Consider using a value conversion functor passed as an argument instead. + static inline ValueType + convertValue(const typename OtherChildNodeType::ValueType& val) { return ValueType(val); } + }; + + for (Index i = 0; i < NUM_VALUES; ++i) { + if (other.mChildMask.isOff(i)) { + mNodes[i].setValue(Local::convertValue(other.mNodes[i].getValue())); + } else { + mNodes[i].setChild(new ChildNodeType(*(other.mNodes[i].getChild()))); + } + } +} + + template<typename ChildT, Index Log2Dim> template<typename OtherChildNodeType> inline @@ -734,13 +880,9 @@ template<typename ChildT, Index Log2Dim> inline Index64 InternalNode<ChildT, Log2Dim>::onVoxelCount() const { - Index64 sum = 0; - for (Index i = 0; i < NUM_VALUES; ++i) { - if (isChildMaskOff(i)) { - if (isValueMaskOn(i)) sum += ChildT::NUM_VOXELS; - } else { - sum += mNodes[i].getChild()->onVoxelCount(); - } + Index64 sum = ChildT::NUM_VOXELS * mValueMask.countOn(); + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + sum += iter->onVoxelCount(); } return sum; } @@ -750,13 +892,9 @@ template<typename ChildT, Index Log2Dim> inline Index64 InternalNode<ChildT, Log2Dim>::offVoxelCount() const { - Index64 sum = 0; - for (Index i = 0; i < NUM_VALUES; ++i) { - if (isChildMaskOff(i)) { - if (isValueMaskOff(i)) sum += ChildT::NUM_VOXELS; - } else { - sum += mNodes[i].getChild()->offVoxelCount(); - } + Index64 sum = ChildT::NUM_VOXELS * (NUM_VALUES-mValueMask.countOn()-mChildMask.countOn()); + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + sum += iter->offVoxelCount(); } return sum; } @@ -785,6 +923,16 @@ InternalNode<ChildT, Log2Dim>::offLeafVoxelCount() const return sum; } +template<typename ChildT, Index Log2Dim> +inline Index64 +InternalNode<ChildT, Log2Dim>::onTileCount() const +{ + Index64 sum = mValueMask.countOn(); + for (ChildOnCIter iter = this->cbeginChildOn(); LEVEL>1 && iter; ++iter) { + sum += iter->onTileCount(); + } + return sum; +} template<typename ChildT, Index Log2Dim> inline Index64 @@ -801,17 +949,15 @@ InternalNode<ChildT, Log2Dim>::memUsage() const template<typename ChildT, Index Log2Dim> inline void -InternalNode<ChildT, Log2Dim>::evalActiveVoxelBoundingBox(CoordBBox& bbox) const +InternalNode<ChildT, Log2Dim>::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const { if (bbox.isInside(this->getNodeBoundingBox())) return; - ValueType dummy; - for (ChildAllCIter iter = this->cbeginChildAll(); iter; ++iter) { - if (const ChildT* child = iter.probeChild(dummy)) { - child->evalActiveVoxelBoundingBox(bbox); - } else if (iter.isValueOn()) { - bbox.expand(iter.getCoord(), ChildT::DIM); - } + for (ValueOnCIter i = this->cbeginValueOn(); i; ++i) { + bbox.expand(i.getCoord(), ChildT::DIM); + } + for (ChildOnCIter i = this->cbeginChildOn(); i; ++i) { + i->evalActiveBoundingBox(bbox, visitVoxels); } } @@ -866,13 +1012,15 @@ InternalNode<ChildT, Log2Dim>::pruneInactive() //////////////////////////////////////// -// Helper method that implements stealNode() template<typename ChildT, Index Log2Dim> template<typename NodeT> inline NodeT* -InternalNode<ChildT, Log2Dim>::doStealNode(const Coord& xyz, const ValueType& value, bool state) +InternalNode<ChildT, Log2Dim>::stealNode(const Coord& xyz, const ValueType& value, bool state) { - const Index n = this->coord2offset(xyz); + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same<NodeT, ChildT>::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + const Index n = this->coordToOffset(xyz); if (mChildMask.isOff(n)) return NULL; ChildT* child = mNodes[n].getChild(); if (boost::is_same<NodeT, ChildT>::value) { @@ -883,24 +1031,130 @@ InternalNode<ChildT, Log2Dim>::doStealNode(const Coord& xyz, const ValueType& va return (boost::is_same<NodeT, ChildT>::value) ? reinterpret_cast<NodeT*>(child) : child->template stealNode<NodeT>(xyz, value, state); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END } +//////////////////////////////////////// + + template<typename ChildT, Index Log2Dim> template<typename NodeT> inline NodeT* -InternalNode<ChildT, Log2Dim>::stealNode(const Coord& xyz, const ValueType& value, bool state) +InternalNode<ChildT, Log2Dim>::probeNode(const Coord& xyz) +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same<NodeT, ChildT>::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) return NULL; + ChildT* child = mNodes[n].getChild(); + return (boost::is_same<NodeT, ChildT>::value) + ? reinterpret_cast<NodeT*>(child) + : child->template probeNode<NodeT>(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template<typename ChildT, Index Log2Dim> +template<typename NodeT, typename AccessorT> +inline NodeT* +InternalNode<ChildT, Log2Dim>::probeNodeAndCache(const Coord& xyz, AccessorT& acc) +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same<NodeT, ChildT>::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) return NULL; + ChildT* child = mNodes[n].getChild(); + acc.insert(xyz, child); + return (boost::is_same<NodeT, ChildT>::value) + ? reinterpret_cast<NodeT*>(child) + : child->template probeNodeAndCache<NodeT>(xyz, acc); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template<typename ChildT, Index Log2Dim> +template<typename NodeT> +inline const NodeT* +InternalNode<ChildT, Log2Dim>::probeConstNode(const Coord& xyz) const +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same<NodeT, ChildT>::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) return NULL; + const ChildT* child = mNodes[n].getChild(); + return (boost::is_same<NodeT, ChildT>::value) + ? reinterpret_cast<const NodeT*>(child) + : child->template probeConstNode<NodeT>(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template<typename ChildT, Index Log2Dim> +template<typename NodeT, typename AccessorT> +inline const NodeT* +InternalNode<ChildT, Log2Dim>::probeConstNodeAndCache(const Coord& xyz, AccessorT& acc) const +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same<NodeT, ChildT>::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) return NULL; + const ChildT* child = mNodes[n].getChild(); + acc.insert(xyz, child); + return (boost::is_same<NodeT, ChildT>::value) + ? reinterpret_cast<const NodeT*>(child) + : child->template probeConstNodeAndCache<NodeT>(xyz, acc); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + + +template<typename ChildT, Index Log2Dim> +inline typename ChildT::LeafNodeType* +InternalNode<ChildT, Log2Dim>::probeLeaf(const Coord& xyz) +{ + return this->template probeNode<LeafNodeType>(xyz); +} + + +template<typename ChildT, Index Log2Dim> +template<typename AccessorT> +inline typename ChildT::LeafNodeType* +InternalNode<ChildT, Log2Dim>::probeLeafAndCache(const Coord& xyz, AccessorT& acc) +{ + return this->template probeNodeAndCache<LeafNodeType>(xyz, acc); +} + + +template<typename ChildT, Index Log2Dim> +template<typename AccessorT> +inline const typename ChildT::LeafNodeType* +InternalNode<ChildT, Log2Dim>::probeLeafAndCache(const Coord& xyz, AccessorT& acc) const +{ + return this->probeConstLeafAndCache(xyz, acc); +} + + +template<typename ChildT, Index Log2Dim> +inline const typename ChildT::LeafNodeType* +InternalNode<ChildT, Log2Dim>::probeConstLeaf(const Coord& xyz) const +{ + return this->template probeConstNode<LeafNodeType>(xyz); +} + + +template<typename ChildT, Index Log2Dim> +template<typename AccessorT> +inline const typename ChildT::LeafNodeType* +InternalNode<ChildT, Log2Dim>::probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const { - // The following conditional is resolved at compile time, and the ternary operator - // and helper method are used to avoid "unreachable code" warnings (with - // "if (<cond>) { <A> } else { <B> }", either <A> or <B> is unreachable if <cond> - // is a compile-time constant expression). Partial specialization on NodeT would be - // impractical because a method template can't be specialized without also - // specializing its class template. - return (NodeT::LEVEL > ChildT::LEVEL || - (NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same<NodeT, ChildT>::value))) - ? static_cast<NodeT*>(NULL) - : this->doStealNode<NodeT>(xyz, value, state); + return this->template probeConstNodeAndCache<LeafNodeType>(xyz, acc); } @@ -913,7 +1167,7 @@ InternalNode<ChildT, Log2Dim>::addLeaf(LeafNodeType* leaf) { assert(leaf != NULL); const Coord& xyz = leaf->origin(); - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); ChildT* child = NULL; if (mChildMask.isOff(n)) { if (ChildT::LEVEL>0) { @@ -921,9 +1175,7 @@ InternalNode<ChildT, Log2Dim>::addLeaf(LeafNodeType* leaf) } else { child = reinterpret_cast<ChildT*>(leaf); } - mNodes[n].setChild(child); - mChildMask.setOn(n); - mValueMask.setOff(n); + this->setChildNode(n, child); } else { if (ChildT::LEVEL>0) { child = mNodes[n].getChild(); @@ -944,7 +1196,7 @@ InternalNode<ChildT, Log2Dim>::addLeafAndCache(LeafNodeType* leaf, AccessorT& ac { assert(leaf != NULL); const Coord& xyz = leaf->origin(); - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); ChildT* child = NULL; if (mChildMask.isOff(n)) { if (ChildT::LEVEL>0) { @@ -953,9 +1205,7 @@ InternalNode<ChildT, Log2Dim>::addLeafAndCache(LeafNodeType* leaf, AccessorT& ac } else { child = reinterpret_cast<ChildT*>(leaf); } - mNodes[n].setChild(child); - mChildMask.setOn(n); - mValueMask.setOff(n); + this->setChildNode(n, child); } else { if (ChildT::LEVEL>0) { child = mNodes[n].getChild(); @@ -975,18 +1225,25 @@ InternalNode<ChildT, Log2Dim>::addLeafAndCache(LeafNodeType* leaf, AccessorT& ac template<typename ChildT, Index Log2Dim> inline void +InternalNode<ChildT, Log2Dim>::addTile(Index n, const ValueType& value, bool state) +{ + assert(n < NUM_VALUES); + this->makeChildNodeEmpty(n, value); + mValueMask.set(n, state); +} + + +template<typename ChildT, Index Log2Dim> +inline void InternalNode<ChildT, Log2Dim>::addTile(Index level, const Coord& xyz, const ValueType& value, bool state) { - assert(level > 0); if (LEVEL >= level) { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); if (mChildMask.isOff(n)) {// tile case if (LEVEL > level) { ChildT* child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n)); - mNodes[n].setChild(child); - mChildMask.setOn(n); - mValueMask.setOff(n); + this->setChildNode(n, child); child->addTile(level, xyz, value, state); } else { mValueMask.set(n, state); @@ -1013,15 +1270,12 @@ inline void InternalNode<ChildT, Log2Dim>::addTileAndCache(Index level, const Coord& xyz, const ValueType& value, bool state, AccessorT& acc) { - assert(level > 0); if (LEVEL >= level) { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); if (mChildMask.isOff(n)) {// tile case if (LEVEL > level) { ChildT* child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n)); - mNodes[n].setChild(child); - mChildMask.setOn(n); - mValueMask.setOff(n); + this->setChildNode(n, child); acc.insert(xyz, child); child->addTileAndCache(level, xyz, value, state, acc); } else { @@ -1051,13 +1305,11 @@ template<typename ChildT, Index Log2Dim> inline typename ChildT::LeafNodeType* InternalNode<ChildT, Log2Dim>::touchLeaf(const Coord& xyz) { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); ChildT* child = NULL; if (mChildMask.isOff(n)) { child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n)); - mNodes[n].setChild(child); - mChildMask.setOn(n); - mValueMask.setOff(n); + this->setChildNode(n, child); } else { child = mNodes[n].getChild(); } @@ -1070,11 +1322,9 @@ template<typename AccessorT> inline typename ChildT::LeafNodeType* InternalNode<ChildT, Log2Dim>::touchLeafAndCache(const Coord& xyz, AccessorT& acc) { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); if (mChildMask.isOff(n)) { - mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), mValueMask.isOn(n))); - mChildMask.setOn(n); - mValueMask.setOff(n); + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), mValueMask.isOn(n))); } acc.insert(xyz, mNodes[n].getChild()); return mNodes[n].getChild()->touchLeafAndCache(xyz, acc); @@ -1085,51 +1335,6 @@ InternalNode<ChildT, Log2Dim>::touchLeafAndCache(const Coord& xyz, AccessorT& ac template<typename ChildT, Index Log2Dim> -inline typename ChildT::LeafNodeType* -InternalNode<ChildT, Log2Dim>::probeLeaf(const Coord& xyz) -{ - const Index n = this->coord2offset(xyz); - return mChildMask.isOn(n) ? mNodes[n].getChild()->probeLeaf(xyz) : NULL; -} - - -template<typename ChildT, Index Log2Dim> -inline const typename ChildT::LeafNodeType* -InternalNode<ChildT, Log2Dim>::probeConstLeaf(const Coord& xyz) const -{ - const Index n = this->coord2offset(xyz); - return mChildMask.isOn(n) ? mNodes[n].getChild()->probeConstLeaf(xyz) : NULL; -} - - -template<typename ChildT, Index Log2Dim> -template<typename AccessorT> -inline typename ChildT::LeafNodeType* -InternalNode<ChildT, Log2Dim>::probeLeafAndCache(const Coord& xyz, AccessorT& acc) -{ - const Index n = this->coord2offset(xyz); - if (mChildMask.isOff(n)) return NULL; - acc.insert(xyz, mNodes[n].getChild()); - return mNodes[n].getChild()->probeLeafAndCache(xyz, acc); -} - - -template<typename ChildT, Index Log2Dim> -template<typename AccessorT> -inline const typename ChildT::LeafNodeType* -InternalNode<ChildT, Log2Dim>::probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const -{ - const Index n = this->coord2offset(xyz); - if (mChildMask.isOff(n)) return NULL; - acc.insert(xyz, mNodes[n].getChild()); - return mNodes[n].getChild()->probeConstLeafAndCache(xyz, acc); -} - - -//////////////////////////////////////// - - -template<typename ChildT, Index Log2Dim> inline bool InternalNode<ChildT, Log2Dim>::isConstant(ValueType& constValue, bool& state, const ValueType& tolerance) const @@ -1195,7 +1400,7 @@ template<typename ChildT, Index Log2Dim> inline bool InternalNode<ChildT, Log2Dim>::isValueOn(const Coord& xyz) const { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); if (this->isChildMaskOff(n)) return this->isValueMaskOn(n); return mNodes[n].getChild()->isValueOn(xyz); } @@ -1205,7 +1410,7 @@ template<typename AccessorT> inline bool InternalNode<ChildT, Log2Dim>::isValueOnAndCache(const Coord& xyz, AccessorT& acc) const { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); if (this->isChildMaskOff(n)) return this->isValueMaskOn(n); acc.insert(xyz, mNodes[n].getChild()); return mNodes[n].getChild()->isValueOnAndCache(xyz, acc); @@ -1216,7 +1421,7 @@ template<typename ChildT, Index Log2Dim> inline const typename ChildT::ValueType& InternalNode<ChildT, Log2Dim>::getValue(const Coord& xyz) const { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); return this->isChildMaskOff(n) ? mNodes[n].getValue() : mNodes[n].getChild()->getValue(xyz); } @@ -1226,7 +1431,7 @@ template<typename AccessorT> inline const typename ChildT::ValueType& InternalNode<ChildT, Log2Dim>::getValueAndCache(const Coord& xyz, AccessorT& acc) const { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); if (this->isChildMaskOn(n)) { acc.insert(xyz, mNodes[n].getChild()); return mNodes[n].getChild()->getValueAndCache(xyz, acc); @@ -1239,7 +1444,7 @@ template<typename ChildT, Index Log2Dim> inline Index InternalNode<ChildT, Log2Dim>::getValueLevel(const Coord& xyz) const { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); return this->isChildMaskOff(n) ? LEVEL : mNodes[n].getChild()->getValueLevel(xyz); } @@ -1248,7 +1453,7 @@ template<typename AccessorT> inline Index InternalNode<ChildT, Log2Dim>::getValueLevelAndCache(const Coord& xyz, AccessorT& acc) const { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); if (this->isChildMaskOn(n)) { acc.insert(xyz, mNodes[n].getChild()); return mNodes[n].getChild()->getValueLevelAndCache(xyz, acc); @@ -1261,7 +1466,7 @@ template<typename ChildT, Index Log2Dim> inline bool InternalNode<ChildT, Log2Dim>::probeValue(const Coord& xyz, ValueType& value) const { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); if (this->isChildMaskOff(n)) { value = mNodes[n].getValue(); return this->isValueMaskOn(n); @@ -1275,7 +1480,7 @@ inline bool InternalNode<ChildT, Log2Dim>::probeValueAndCache(const Coord& xyz, ValueType& value, AccessorT& acc) const { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); if (this->isChildMaskOn(n)) { acc.insert(xyz, mNodes[n].getChild()); return mNodes[n].getChild()->probeValueAndCache(xyz, value, acc); @@ -1289,15 +1494,13 @@ template<typename ChildT, Index Log2Dim> inline void InternalNode<ChildT, Log2Dim>::setValueOff(const Coord& xyz) { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); bool hasChild = this->isChildMaskOn(n); if (!hasChild && this->isValueMaskOn(n)) { // If the voxel belongs to a constant tile that is active, // a child subtree must be constructed. - mChildMask.setOn(n); // we're adding a child node so set the mask on - mValueMask.setOff(n); // value mask is always off if child mask is on hasChild = true; - mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), /*active=*/true)); + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), /*active=*/true)); } if (hasChild) mNodes[n].getChild()->setValueOff(xyz); } @@ -1307,15 +1510,13 @@ template<typename ChildT, Index Log2Dim> inline void InternalNode<ChildT, Log2Dim>::setValueOn(const Coord& xyz) { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); bool hasChild = this->isChildMaskOn(n); if (!hasChild && !this->isValueMaskOn(n)) { // If the voxel belongs to a constant tile that is inactive, // a child subtree must be constructed. - mChildMask.setOn(n); // we're adding a child node so set the mask on - mValueMask.setOff(n); // value mask is always off if child mask is on hasChild = true; - mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), /*active=*/false)); + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), /*active=*/false)); } if (hasChild) mNodes[n].getChild()->setValueOn(xyz); } @@ -1325,7 +1526,7 @@ template<typename ChildT, Index Log2Dim> inline void InternalNode<ChildT, Log2Dim>::setValueOff(const Coord& xyz, const ValueType& value) { - const Index n = InternalNode::coord2offset(xyz); + const Index n = InternalNode::coordToOffset(xyz); bool hasChild = this->isChildMaskOn(n); if (!hasChild) { const bool active = this->isValueMaskOn(n); @@ -1333,10 +1534,8 @@ InternalNode<ChildT, Log2Dim>::setValueOff(const Coord& xyz, const ValueType& va // If the voxel belongs to a tile that is either active or that // has a constant value that is different from the one provided, // a child subtree must be constructed. - mChildMask.setOn(n); // we're adding a child node so set the mask on - mValueMask.setOff(n); // value mask is always off if child mask is on hasChild = true; - mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active)); + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); } } if (hasChild) mNodes[n].getChild()->setValueOff(xyz, value); @@ -1348,7 +1547,7 @@ inline void InternalNode<ChildT, Log2Dim>::setValueOffAndCache(const Coord& xyz, const ValueType& value, AccessorT& acc) { - const Index n = InternalNode::coord2offset(xyz); + const Index n = InternalNode::coordToOffset(xyz); bool hasChild = this->isChildMaskOn(n); if (!hasChild) { const bool active = this->isValueMaskOn(n); @@ -1356,10 +1555,8 @@ InternalNode<ChildT, Log2Dim>::setValueOffAndCache(const Coord& xyz, // If the voxel belongs to a tile that is either active or that // has a constant value that is different from the one provided, // a child subtree must be constructed. - mChildMask.setOn(n); // we're adding a child node so set the mask on - mValueMask.setOff(n); // value mask is always off if child mask is on hasChild = true; - mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active)); + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); } } if (hasChild) { @@ -1374,7 +1571,7 @@ template<typename ChildT, Index Log2Dim> inline void InternalNode<ChildT, Log2Dim>::setValueOn(const Coord& xyz, const ValueType& value) { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); bool hasChild = this->isChildMaskOn(n); if (!hasChild) { const bool active = this->isValueMaskOn(n); // tile's active state @@ -1382,10 +1579,8 @@ InternalNode<ChildT, Log2Dim>::setValueOn(const Coord& xyz, const ValueType& val // If the voxel belongs to a tile that is either inactive or that // has a constant value that is different from the one provided, // a child subtree must be constructed. - mChildMask.setOn(n); // we're adding a child node so set the mask on - mValueMask.setOff(n); // value mask is always off if child mask is on hasChild = true; - mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active)); + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); } } if (hasChild) mNodes[n].getChild()->setValueOn(xyz, value); @@ -1397,7 +1592,7 @@ inline void InternalNode<ChildT, Log2Dim>::setValueAndCache(const Coord& xyz, const ValueType& value, AccessorT& acc) { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); bool hasChild = this->isChildMaskOn(n); if (!hasChild) { const bool active = this->isValueMaskOn(n); @@ -1405,10 +1600,8 @@ InternalNode<ChildT, Log2Dim>::setValueAndCache(const Coord& xyz, // If the voxel belongs to a tile that is either inactive or that // has a constant value that is different from the one provided, // a child subtree must be constructed. - mChildMask.setOn(n); // we're adding a child node so set the mask on - mValueMask.setOff(n); // value mask is always off if child mask is on hasChild = true; - mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active)); + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); } } if (hasChild) { @@ -1422,16 +1615,14 @@ template<typename ChildT, Index Log2Dim> inline void InternalNode<ChildT, Log2Dim>::setValueOnly(const Coord& xyz, const ValueType& value) { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); bool hasChild = this->isChildMaskOn(n); if (!hasChild && !math::isExactlyEqual(mNodes[n].getValue(), value)) { // If the voxel has a tile value that is different from the one provided, // a child subtree must be constructed. const bool active = this->isValueMaskOn(n); - mChildMask.setOn(n); // we're adding a child node so set the mask on - mValueMask.setOff(n); // value mask is always off if child mask is on hasChild = true; - mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active)); + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); } if (hasChild) mNodes[n].getChild()->setValueOnly(xyz, value); } @@ -1442,16 +1633,14 @@ inline void InternalNode<ChildT, Log2Dim>::setValueOnlyAndCache(const Coord& xyz, const ValueType& value, AccessorT& acc) { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); bool hasChild = this->isChildMaskOn(n); if (!hasChild && !math::isExactlyEqual(mNodes[n].getValue(), value)) { // If the voxel has a tile value that is different from the one provided, // a child subtree must be constructed. const bool active = this->isValueMaskOn(n); - mChildMask.setOn(n); // we're adding a child node so set the mask on - mValueMask.setOff(n); // value mask is always off if child mask is on hasChild = true; - mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active)); + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); } if (hasChild) { acc.insert(xyz, mNodes[n].getChild()); @@ -1464,17 +1653,15 @@ template<typename ChildT, Index Log2Dim> inline void InternalNode<ChildT, Log2Dim>::setActiveState(const Coord& xyz, bool on) { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); bool hasChild = this->isChildMaskOn(n); if (!hasChild) { if (on != this->isValueMaskOn(n)) { // If the voxel belongs to a tile with the wrong active state, // then a child subtree must be constructed. - mChildMask.setOn(n); // we're adding a child node so set the mask on - mValueMask.setOff(n); // value mask is always off if child mask is on - mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), !on)); - // 'on' is the voxel's new state, therefore '!on' is the tile's current state + // 'on' is the voxel's new state, therefore '!on' is the tile's current state hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), !on)); } } if (hasChild) mNodes[n].getChild()->setActiveState(xyz, on); @@ -1485,17 +1672,15 @@ template<typename AccessorT> inline void InternalNode<ChildT, Log2Dim>::setActiveStateAndCache(const Coord& xyz, bool on, AccessorT& acc) { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); bool hasChild = this->isChildMaskOn(n); if (!hasChild) { if (on != this->isValueMaskOn(n)) { // If the voxel belongs to a tile with the wrong active state, // then a child subtree must be constructed. - mChildMask.setOn(n); // we're adding a child node so set the mask on - mValueMask.setOff(n); // value mask is always off if child mask is on - mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), !on)); - // 'on' is the voxel's new state, therefore '!on' is the tile's current state + // 'on' is the voxel's new state, therefore '!on' is the tile's current state hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), !on)); } } if (hasChild) { @@ -1518,91 +1703,115 @@ InternalNode<ChildT, Log2Dim>::setValuesOn() template<typename ChildT, Index Log2Dim> +template<typename ModifyOp> inline void -InternalNode<ChildT, Log2Dim>::setValueOnMin(const Coord& xyz, const ValueType& value) +InternalNode<ChildT, Log2Dim>::modifyValue(const Coord& xyz, const ModifyOp& op) { - const Index n = InternalNode::coord2offset(xyz); + const Index n = InternalNode::coordToOffset(xyz); bool hasChild = this->isChildMaskOn(n); if (!hasChild) { + // Need to create a child if the tile is inactive, + // in order to activate voxel (x, y, z). const bool active = this->isValueMaskOn(n); - if (!active || (mNodes[n].getValue() > value)) { - // If the voxel belongs to a tile that is either inactive or that - // has a constant value that is greater than the one provided, - // a child subtree must be constructed. - mChildMask.setOn(n); // we're adding a child node so set the mask on - mValueMask.setOff(n); // value mask is always off if child mask is on + bool createChild = !active; + if (!createChild) { + // Need to create a child if applying the functor + // to the tile value produces a different value. + const ValueType& tileVal = mNodes[n].getValue(); + ValueType modifiedVal = tileVal; + op(modifiedVal); + createChild = !math::isExactlyEqual(tileVal, modifiedVal); + } + if (createChild) { hasChild = true; - mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active)); + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); } } - if (hasChild) mNodes[n].getChild()->setValueOnMin(xyz, value); + if (hasChild) mNodes[n].getChild()->modifyValue(xyz, op); } - template<typename ChildT, Index Log2Dim> +template<typename ModifyOp, typename AccessorT> inline void -InternalNode<ChildT, Log2Dim>::setValueOnMax(const Coord& xyz, const ValueType& value) +InternalNode<ChildT, Log2Dim>::modifyValueAndCache(const Coord& xyz, const ModifyOp& op, + AccessorT& acc) { - const Index n = InternalNode::coord2offset(xyz); + const Index n = InternalNode::coordToOffset(xyz); bool hasChild = this->isChildMaskOn(n); if (!hasChild) { + // Need to create a child if the tile is inactive, + // in order to activate voxel (x, y, z). const bool active = this->isValueMaskOn(n); - if (!active || (value > mNodes[n].getValue())) { - // If the voxel belongs to a tile that is either inactive or that - // has a constant value that is less than the one provided, - // a child subtree must be constructed. - mChildMask.setOn(n); // we're adding a child node so set the mask on - mValueMask.setOff(n); // value mask is always off if child mask is on + bool createChild = !active; + if (!createChild) { + // Need to create a child if applying the functor + // to the tile value produces a different value. + const ValueType& tileVal = mNodes[n].getValue(); + ValueType modifiedVal = tileVal; + op(modifiedVal); + createChild = !math::isExactlyEqual(tileVal, modifiedVal); + } + if (createChild) { hasChild = true; - mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active)); + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); } } - if (hasChild) mNodes[n].getChild()->setValueOnMax(xyz, value); + if (hasChild) { + ChildNodeType* child = mNodes[n].getChild(); + acc.insert(xyz, child); + child->modifyValueAndCache(xyz, op, acc); + } } template<typename ChildT, Index Log2Dim> +template<typename ModifyOp> inline void -InternalNode<ChildT, Log2Dim>::setValueOnSum(const Coord& xyz, const ValueType& addend) +InternalNode<ChildT, Log2Dim>::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) { - const Index n = InternalNode::coord2offset(xyz); + const Index n = InternalNode::coordToOffset(xyz); bool hasChild = this->isChildMaskOn(n); if (!hasChild) { - const bool active = this->isValueMaskOn(n); - if (!active || !math::isExactlyEqual(addend, zeroVal<ValueType>())) { - // If the voxel belongs to a tile that is inactive or if - // the addend is nonzero, a child subtree must be constructed. - mChildMask.setOn(n);// we're adding a child node so set the mask on - mValueMask.setOff(n);// value mask is always off if child mask is on + const bool tileState = this->isValueMaskOn(n); + const ValueType& tileVal = mNodes[n].getValue(); + bool modifiedState = !tileState; + ValueType modifiedVal = tileVal; + op(modifiedVal, modifiedState); + // Need to create a child if applying the functor to the tile + // produces a different value or active state. + if (modifiedState != tileState || !math::isExactlyEqual(modifiedVal, tileVal)) { hasChild = true; - mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active)); + this->setChildNode(n, new ChildNodeType(xyz, tileVal, tileState)); } } - if (hasChild) mNodes[n].getChild()->setValueOnSum(xyz, addend); + if (hasChild) mNodes[n].getChild()->modifyValueAndActiveState(xyz, op); } template<typename ChildT, Index Log2Dim> -template<typename AccessorT> +template<typename ModifyOp, typename AccessorT> inline void -InternalNode<ChildT, Log2Dim>::setValueOnSumAndCache(const Coord& xyz, - const ValueType& addend, AccessorT& acc) +InternalNode<ChildT, Log2Dim>::modifyValueAndActiveStateAndCache( + const Coord& xyz, const ModifyOp& op, AccessorT& acc) { - const Index n = this->coord2offset(xyz); + const Index n = InternalNode::coordToOffset(xyz); bool hasChild = this->isChildMaskOn(n); if (!hasChild) { - const bool active = this->isValueMaskOn(n); - if (!active || !math::isExactlyEqual(addend, zeroVal<ValueType>())) { - // If the voxel belongs to a tile that is inactive or if - // the addend is nonzero, a child subtree must be constructed. - mChildMask.setOn(n); // we're adding a child node so set the mask on - mValueMask.setOff(n); // value mask is always off if child mask is on + const bool tileState = this->isValueMaskOn(n); + const ValueType& tileVal = mNodes[n].getValue(); + bool modifiedState = !tileState; + ValueType modifiedVal = tileVal; + op(modifiedVal, modifiedState); + // Need to create a child if applying the functor to the tile + // produces a different value or active state. + if (modifiedState != tileState || !math::isExactlyEqual(modifiedVal, tileVal)) { hasChild = true; - mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active)); + this->setChildNode(n, new ChildNodeType(xyz, tileVal, tileState)); } } if (hasChild) { - acc.insert(xyz, mNodes[n].getChild()); - mNodes[n].getChild()->setValueOnSumAndCache(xyz, addend, acc); + ChildNodeType* child = mNodes[n].getChild(); + acc.insert(xyz, child); + child->modifyValueAndActiveStateAndCache(xyz, op, acc); } } @@ -1623,8 +1832,8 @@ InternalNode<ChildT, Log2Dim>::fill(const CoordBBox& bbox, const ValueType& valu xyz.setZ(z); // Get the bounds of the tile that contains voxel (x, y, z). - const Index n = this->coord2offset(xyz); - tileMin = this->offset2globalCoord(n); + const Index n = this->coordToOffset(xyz); + tileMin = this->offsetToGlobalCoord(n); tileMax = tileMin.offsetBy(ChildT::DIM - 1); if (xyz != tileMin || Coord::lessThan(bbox.max(), tileMax)) { @@ -1636,9 +1845,7 @@ InternalNode<ChildT, Log2Dim>::fill(const CoordBBox& bbox, const ValueType& valu // Replace the tile with a newly-created child that is initialized // with the tile's value and active state. child = new ChildT(xyz, mNodes[n].getValue(), this->isValueMaskOn(n)); - mChildMask.setOn(n); - mValueMask.setOff(n); - mNodes[n].setChild(child); + this->setChildNode(n, child); } else { child = mNodes[n].getChild(); } @@ -1670,14 +1877,16 @@ template<typename DenseT> inline void InternalNode<ChildT, Log2Dim>::copyToDense(const CoordBBox& bbox, DenseT& dense) const { - const size_t xStride = dense.xStride(), yStride = dense.yStride();// zStride=1 + typedef typename DenseT::ValueType DenseValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); const Coord& min = dense.bbox().min(); for (Coord xyz = bbox.min(), max; xyz[0] <= bbox.max()[0]; xyz[0] = max[0] + 1) { for (xyz[1] = bbox.min()[1]; xyz[1] <= bbox.max()[1]; xyz[1] = max[1] + 1) { for (xyz[2] = bbox.min()[2]; xyz[2] <= bbox.max()[2]; xyz[2] = max[2] + 1) { - const Index n = this->coord2offset(xyz); + const Index n = this->coordToOffset(xyz); // Get max coordinates of the child node that contains voxel xyz. - max = this->offset2globalCoord(n).offsetBy(ChildT::DIM-1); + max = this->offsetToGlobalCoord(n).offsetBy(ChildT::DIM-1); // Get the bbox of the interection of bbox and the child node CoordBBox sub(xyz, Coord::minComponent(bbox.max(), max)); @@ -1687,12 +1896,14 @@ InternalNode<ChildT, Log2Dim>::copyToDense(const CoordBBox& bbox, DenseT& dense) } else {//a tile value const ValueType value = mNodes[n].getValue(); sub.translate(-min); - ValueType* a0 = dense.data() + sub.min()[2]; + DenseValueType* a0 = dense.data() + zStride*sub.min()[2]; for (Int32 x=sub.min()[0], ex=sub.max()[0]+1; x<ex; ++x) { - ValueType* a1 = a0 + x*xStride; + DenseValueType* a1 = a0 + x*xStride; for (Int32 y=sub.min()[1], ey=sub.max()[1]+1; y<ey; ++y) { - ValueType* a2 = a1 + y*yStride; - for (Int32 z=sub.min()[2], ez=sub.max()[2]+1; z<ez; ++z) *a2++ = value; + DenseValueType* a2 = a1 + y*yStride; + for (Int32 z=sub.min()[2], ez=sub.max()[2]+1; z<ez; ++z, a2 += zStride) { + *a2 = DenseValueType(value); + } } } } @@ -1740,7 +1951,7 @@ InternalNode<ChildT, Log2Dim>::readTopology(std::istream& is, bool fromHalf) for (Index i = 0; i < NUM_VALUES; ++i) { if (this->isChildMaskOn(i)) { ChildNodeType* child = - new ChildNodeType(offset2globalCoord(i), zeroVal<ValueType>()); + new ChildNodeType(offsetToGlobalCoord(i), zeroVal<ValueType>()); mNodes[i].setChild(child); child->readTopology(is); } else { @@ -1809,7 +2020,7 @@ template<typename ChildT, Index Log2Dim> inline void InternalNode<ChildT, Log2Dim>::signedFloodFill(const ValueType& background) { - this->signedFloodFill(background, negative(background)); + this->signedFloodFill(background, math::negative(background)); } @@ -1863,7 +2074,7 @@ InternalNode<ChildT, Log2Dim>::negate() if (this->isChildMaskOn(i)) { mNodes[i].getChild()->negate(); } else { - mNodes[i].setValue(negative(mNodes[i].getValue())); + mNodes[i].setValue(math::negative(mNodes[i].getValue())); } } @@ -1875,48 +2086,155 @@ inline void InternalNode<ChildT, Log2Dim>::voxelizeActiveTiles() { for (ValueOnIter iter = this->beginValueOn(); iter; ++iter) { - const Index n = iter.pos(); - ChildNodeType* child = new ChildNodeType(iter.getCoord(), iter.getValue(), true); - mValueMask.setOff(n); - mChildMask.setOn(n); - mNodes[n].setChild(child); + this->setChildNode(iter.pos(), new ChildNodeType(iter.getCoord(), iter.getValue(), true)); } for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) iter->voxelizeActiveTiles(); } +//////////////////////////////////////// + + template<typename ChildT, Index Log2Dim> +template<MergePolicy Policy> inline void InternalNode<ChildT, Log2Dim>::merge(InternalNode& other, const ValueType& background, const ValueType& otherBackground) { - for (ChildOnIter iter = other.beginChildOn(); iter; ++iter) { - const Index n = iter.pos(); - if (mChildMask.isOff(n)) { // transfer node - ChildNodeType* child = other.mNodes[n].getChild(); - other.mChildMask.setOff(n); - // Note other's new tile value is undefined which is okay since - // other is assumed to be cannibalized in the process of merging! - child->resetBackground(otherBackground, background); - mChildMask.setOn(n); - mValueMask.setOff(n); - mNodes[n].setChild(child); - } else { - mNodes[n].getChild()->merge(*iter, background, otherBackground); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + + switch (Policy) { + + case MERGE_ACTIVE_STATES: + default: + { + for (ChildOnIter iter = other.beginChildOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mChildMask.isOn(n)) { + // Merge this node's child with the other node's child. + mNodes[n].getChild()->template merge<MERGE_ACTIVE_STATES>(*iter, + background, otherBackground); + } else if (mValueMask.isOff(n)) { + // Replace this node's inactive tile with the other node's child + // and replace the other node's child with a tile of undefined value + // (which is okay since the other tree is assumed to be cannibalized + // in the process of merging). + ChildNodeType* child = other.mNodes[n].getChild(); + other.mChildMask.setOff(n); + child->resetBackground(otherBackground, background); + this->setChildNode(n, child); + } + } + + // Copy active tile values. + for (ValueOnCIter iter = other.cbeginValueOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mValueMask.isOff(n)) { + // Replace this node's child or inactive tile with the other node's active tile. + this->makeChildNodeEmpty(n, iter.getValue()); + mValueMask.setOn(n); + } + } + break; + } + + case MERGE_NODES: + { + for (ChildOnIter iter = other.beginChildOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mChildMask.isOn(n)) { + // Merge this node's child with the other node's child. + mNodes[n].getChild()->template merge<Policy>(*iter, background, otherBackground); + } else { + // Replace this node's tile (regardless of its active state) with + // the other node's child and replace the other node's child with + // a tile of undefined value (which is okay since the other tree + // is assumed to be cannibalized in the process of merging). + ChildNodeType* child = other.mNodes[n].getChild(); + other.mChildMask.setOff(n); + child->resetBackground(otherBackground, background); + this->setChildNode(n, child); + } + } + break; + } + + case MERGE_ACTIVE_STATES_AND_NODES: + { + // Transfer children from the other tree to this tree. + for (ChildOnIter iter = other.beginChildOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mChildMask.isOn(n)) { + // Merge this node's child with the other node's child. + mNodes[n].getChild()->template merge<Policy>(*iter, background, otherBackground); + } else { + // Replace this node's tile with the other node's child, leaving the other + // node with an inactive tile of undefined value (which is okay since + // the other tree is assumed to be cannibalized in the process of merging). + ChildNodeType* child = other.mNodes[n].getChild(); + other.mChildMask.setOff(n); + child->resetBackground(otherBackground, background); + if (mValueMask.isOn(n)) { + // Merge the child with this node's active tile. + child->template merge<Policy>(mNodes[n].getValue(), /*on=*/true); + mValueMask.setOff(n); + } + mChildMask.setOn(n); + mNodes[n].setChild(child); + } + } + + // Merge active tiles into this tree. + for (ValueOnCIter iter = other.cbeginValueOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mChildMask.isOn(n)) { + // Merge the other node's active tile into this node's child. + mNodes[n].getChild()->template merge<Policy>(iter.getValue(), /*on=*/true); + } else if (mValueMask.isOff(n)) { + // Replace this node's inactive tile with the other node's active tile. + mNodes[n].setValue(iter.getValue()); + mValueMask.setOn(n); + } } + break; } - // Copy active tile values. - for (ValueOnCIter iter = other.cbeginValueOn(); iter; ++iter) { + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template<typename ChildT, Index Log2Dim> +template<MergePolicy Policy> +inline void +InternalNode<ChildT, Log2Dim>::merge(const ValueType& tileValue, bool tileActive) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + + if (Policy != MERGE_ACTIVE_STATES_AND_NODES) return; + + // For MERGE_ACTIVE_STATES_AND_NODES, inactive tiles in the other tree are ignored. + if (!tileActive) return; + + // Iterate over this node's children and inactive tiles. + for (ValueOffIter iter = this->beginValueOff(); iter; ++iter) { const Index n = iter.pos(); - if (mChildMask.isOff(n) && mValueMask.isOff(n)) { - mNodes[n].setValue(iter.getValue()); + if (mChildMask.isOn(n)) { + // Merge the other node's active tile into this node's child. + mNodes[n].getChild()->template merge<Policy>(tileValue, /*on=*/true); + } else { + // Replace this node's inactive tile with the other node's active tile. + iter.setValue(tileValue); mValueMask.setOn(n); } } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END } +//////////////////////////////////////// + + template<typename ChildT, Index Log2Dim> template<typename OtherChildT> inline void @@ -1925,6 +2243,7 @@ InternalNode<ChildT, Log2Dim>::topologyUnion(const InternalNode<OtherChildT, Log typedef typename InternalNode<OtherChildT, Log2Dim>::ChildOnCIter OtherChildIter; typedef typename InternalNode<OtherChildT, Log2Dim>::ValueOnCIter OtherValueIter; + // Loop over other node's child nodes for (OtherChildIter iter = other.cbeginChildOn(); iter; ++iter) { const Index i = iter.pos(); if (mChildMask.isOn(i)) {//this has a child node @@ -1939,6 +2258,7 @@ InternalNode<ChildT, Log2Dim>::topologyUnion(const InternalNode<OtherChildT, Log mNodes[i].setChild(child); } } + // Loop over other node's active tiles for (OtherValueIter iter = other.cbeginValueOn(); iter; ++iter) { const Index i = iter.pos(); if (mChildMask.isOn(i)) { @@ -1949,6 +2269,72 @@ InternalNode<ChildT, Log2Dim>::topologyUnion(const InternalNode<OtherChildT, Log } } +template<typename ChildT, Index Log2Dim> +template<typename OtherChildT> +inline void +InternalNode<ChildT, Log2Dim>::topologyIntersection(const InternalNode<OtherChildT, Log2Dim>& other, + const ValueType& background) +{ + // Loop over this node's child nodes + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) { + const Index i = iter.pos(); + if (other.mChildMask.isOn(i)) {//other also has a child node + iter->topologyIntersection(*(other.mNodes[i].getChild()), background); + } else if (other.mValueMask.isOff(i)) {//other is an inactive tile + delete mNodes[i].getChild();//convert child to an inactive tile + mNodes[i].setValue(background); + mChildMask.setOff(i); + mValueMask.setOff(i); + } + } + + // Loop over this node's active tiles + for (ValueOnCIter iter = this->cbeginValueOn(); iter; ++iter) { + const Index i = iter.pos(); + if (other.mChildMask.isOn(i)) {//other has a child node + ChildNodeType* child = new ChildNodeType(*(other.mNodes[i].getChild()), + *iter, TopologyCopy()); + this->setChildNode(i, child);//replace the active tile with a child branch + } else if (other.mValueMask.isOff(i)) {//other is an inactive tile + mValueMask.setOff(i);//convert active tile to an inactive tile + } + } +} + +template<typename ChildT, Index Log2Dim> +template<typename OtherChildT> +inline void +InternalNode<ChildT, Log2Dim>::topologyDifference(const InternalNode<OtherChildT, Log2Dim>& other, + const ValueType& background) +{ + typedef typename InternalNode<OtherChildT, Log2Dim>::ChildOnCIter OtherChildIter; + typedef typename InternalNode<OtherChildT, Log2Dim>::ValueOnCIter OtherValueIter; + + // Loop over other node's child nodes + for (OtherChildIter iter = other.cbeginChildOn(); iter; ++iter) { + const Index i = iter.pos(); + if (mChildMask.isOn(i)) {//this has a child node + mNodes[i].getChild()->topologyDifference(*iter, background); + } else if (mValueMask.isOn(i)) {// this is an active tile + ChildNodeType* child = new ChildNodeType(iter.getCoord(), mNodes[i].getValue(), true); + child->topologyDifference(*iter, background); + this->setChildNode(i, child);//we're replacing the active tile with a child branch + } + } + + // Loop over other node's active tiles + for (OtherValueIter iter = other.cbeginValueOn(); iter; ++iter) { + const Index i = iter.pos(); + if (mChildMask.isOn(i)) {//this has a child node + delete mNodes[i].getChild();//convert child to an inactive tile + mNodes[i].setValue(background); + mChildMask.setOff(i); + mValueMask.setOff(i); + } else if (mValueMask.isOn(i)) {//this is an active tile + mValueMask.setOff(i);//convert active tile to an inactive tile + } + } +} //////////////////////////////////////// @@ -1993,9 +2379,7 @@ InternalNode<ChildT, Log2Dim>::combine(InternalNode& other, CombineOp& op) // Steal the other node's child. other.mChildMask.setOff(i); other.mNodes[i].setValue(zero); - mChildMask.setOn(i); - mValueMask.setOff(i); - mNodes[i].setChild(child); + this->setChildNode(i, child); } } else /*if (isChildMaskOn(i) && other.isChildMaskOn(i))*/ { @@ -2043,12 +2427,12 @@ InternalNode<ChildT, Log2Dim>::combine(const ValueType& value, bool valueIsActiv template<typename ChildT, Index Log2Dim> -template<typename CombineOp> +template<typename CombineOp, typename OtherNodeType> inline void -InternalNode<ChildT, Log2Dim>::combine2(const InternalNode& other0, const InternalNode& other1, +InternalNode<ChildT, Log2Dim>::combine2(const InternalNode& other0, const OtherNodeType& other1, CombineOp& op) { - CombineArgs<ValueType> args; + CombineArgs<ValueType, typename OtherNodeType::ValueType> args; for (Index i = 0; i < NUM_VALUES; ++i) { if (other0.isChildMaskOff(i) && other1.isChildMaskOff(i)) { @@ -2060,16 +2444,12 @@ InternalNode<ChildT, Log2Dim>::combine2(const InternalNode& other0, const Intern this->makeChildNodeEmpty(i, args.result()); mValueMask.set(i, args.resultIsActive()); } else { - ChildNodeType* otherChild = other0.isChildMaskOn(i) - ? other0.mNodes[i].getChild() : other1.mNodes[i].getChild(); - assert(otherChild); if (this->isChildMaskOff(i)) { - // Add a new child with the same coordinates, etc. - // as the other node's child. - mChildMask.setOn(i); - mValueMask.setOff(i); - mNodes[i].setChild(new ChildNodeType(otherChild->getOrigin(), - mNodes[i].getValue())); + // Add a new child with the same coordinates, etc. as the other node's child. + const Coord& childOrigin = other0.isChildMaskOn(i) + ? other0.mNodes[i].getChild()->origin() + : other1.mNodes[i].getChild()->origin(); + this->setChildNode(i, new ChildNodeType(childOrigin, mNodes[i].getValue())); } if (other0.isChildMaskOff(i)) { @@ -2094,12 +2474,12 @@ InternalNode<ChildT, Log2Dim>::combine2(const InternalNode& other0, const Intern template<typename ChildT, Index Log2Dim> -template<typename CombineOp> +template<typename CombineOp, typename OtherNodeType> inline void -InternalNode<ChildT, Log2Dim>::combine2(const ValueType& value, const InternalNode& other, +InternalNode<ChildT, Log2Dim>::combine2(const ValueType& value, const OtherNodeType& other, bool valueIsActive, CombineOp& op) { - CombineArgs<ValueType> args; + CombineArgs<ValueType, typename OtherNodeType::ValueType> args; for (Index i = 0; i < NUM_VALUES; ++i) { if (other.isChildMaskOff(i)) { @@ -2111,15 +2491,12 @@ InternalNode<ChildT, Log2Dim>::combine2(const ValueType& value, const InternalNo this->makeChildNodeEmpty(i, args.result()); mValueMask.set(i, args.resultIsActive()); } else { - ChildNodeType* otherChild = other.mNodes[i].getChild(); + typename OtherNodeType::ChildNodeType* otherChild = other.mNodes[i].getChild(); assert(otherChild); if (this->isChildMaskOff(i)) { // Add a new child with the same coordinates, etc. // as the other node's child. - /// @todo Could the other node's ChildNodeType be different from this node's? - mChildMask.setOn(i); - mValueMask.setOff(i); - mNodes[i].setChild(new ChildNodeType(*otherChild)); + this->setChildNode(i, new ChildNodeType(*otherChild)); } // Combine the other node's child with a constant value // and write the result into child i. @@ -2130,12 +2507,12 @@ InternalNode<ChildT, Log2Dim>::combine2(const ValueType& value, const InternalNo template<typename ChildT, Index Log2Dim> -template<typename CombineOp> +template<typename CombineOp, typename OtherValueType> inline void -InternalNode<ChildT, Log2Dim>::combine2(const InternalNode& other, const ValueType& value, +InternalNode<ChildT, Log2Dim>::combine2(const InternalNode& other, const OtherValueType& value, bool valueIsActive, CombineOp& op) { - CombineArgs<ValueType> args; + CombineArgs<ValueType, OtherValueType> args; for (Index i = 0; i < NUM_VALUES; ++i) { if (other.isChildMaskOff(i)) { @@ -2150,12 +2527,9 @@ InternalNode<ChildT, Log2Dim>::combine2(const InternalNode& other, const ValueTy ChildNodeType* otherChild = other.mNodes[i].getChild(); assert(otherChild); if (this->isChildMaskOff(i)) { - // Add a new child with the same coordinates, etc. - // as the other node's child. - mChildMask.setOn(i); - mValueMask.setOff(i); - mNodes[i].setChild(new ChildNodeType(otherChild->getOrigin(), - mNodes[i].getValue())); + // Add a new child with the same coordinates, etc. as the other node's child. + this->setChildNode(i, + new ChildNodeType(otherChild->origin(), mNodes[i].getValue())); } // Combine the other node's child with a constant value // and write the result into child i. @@ -2375,7 +2749,7 @@ InternalNode<ChildT, Log2Dim>::getNodeLog2Dims(std::vector<Index>& dims) template<typename ChildT, Index Log2Dim> inline void -InternalNode<ChildT, Log2Dim>::offset2coord(Index n, Coord &xyz) +InternalNode<ChildT, Log2Dim>::offsetToLocalCoord(Index n, Coord &xyz) { assert(n<(1<<3*Log2Dim)); xyz.setX(n >> 2*Log2Dim); @@ -2387,22 +2761,22 @@ InternalNode<ChildT, Log2Dim>::offset2coord(Index n, Coord &xyz) template<typename ChildT, Index Log2Dim> inline Index -InternalNode<ChildT, Log2Dim>::coord2offset(const Coord& xyz) +InternalNode<ChildT, Log2Dim>::coordToOffset(const Coord& xyz) { - return (((xyz[0]&DIM-1u)>>ChildNodeType::TOTAL)<<2*Log2Dim) - + (((xyz[1]&DIM-1u)>>ChildNodeType::TOTAL)<< Log2Dim) - + ((xyz[2]&DIM-1u)>>ChildNodeType::TOTAL); + return (((xyz[0] & (DIM-1u)) >> ChildNodeType::TOTAL) << 2*Log2Dim) + + (((xyz[1] & (DIM-1u)) >> ChildNodeType::TOTAL) << Log2Dim) + + ((xyz[2] & (DIM-1u)) >> ChildNodeType::TOTAL); } template<typename ChildT, Index Log2Dim> inline Coord -InternalNode<ChildT, Log2Dim>::offset2globalCoord(Index n) const +InternalNode<ChildT, Log2Dim>::offsetToGlobalCoord(Index n) const { Coord local; - this->offset2coord(n, local); + this->offsetToLocalCoord(n, local); local <<= ChildT::TOTAL; - return local + this->getOrigin(); + return local + this->origin(); } @@ -2421,8 +2795,8 @@ InternalNode<ChildT, Log2Dim>::resetBackground(const ValueType& oldBackground, } else if (this->isValueMaskOff(i)) { if (math::isApproxEqual(mNodes[i].getValue(), oldBackground)) { mNodes[i].setValue(newBackground); - } else if (math::isApproxEqual(mNodes[i].getValue(), negative(oldBackground))) { - mNodes[i].setValue(negative(newBackground)); + } else if (math::isApproxEqual(mNodes[i].getValue(), math::negative(oldBackground))) { + mNodes[i].setValue(math::negative(newBackground)); } } } @@ -2446,7 +2820,7 @@ InternalNode<ChildT, Log2Dim>::hasSameTopology( template<typename ChildT, Index Log2Dim> inline void -InternalNode<ChildT, Log2Dim>::setChildNode(Index i, ChildNodeType* child) +InternalNode<ChildT, Log2Dim>::resetChildNode(Index i, ChildNodeType* child) { assert(child); if (this->isChildMaskOn(i)) { @@ -2458,6 +2832,17 @@ InternalNode<ChildT, Log2Dim>::setChildNode(Index i, ChildNodeType* child) mNodes[i].setChild(child); } +template<typename ChildT, Index Log2Dim> +inline void +InternalNode<ChildT, Log2Dim>::setChildNode(Index i, ChildNodeType* child) +{ + assert(child); + assert(mChildMask.isOff(i)); + mChildMask.setOn(i); + mValueMask.setOff(i); + mNodes[i].setChild(child); +} + template<typename ChildT, Index Log2Dim> inline ChildT* @@ -2485,7 +2870,8 @@ template<typename ChildT, Index Log2Dim> inline ChildT* InternalNode<ChildT, Log2Dim>::getChildNode(Index n) { - return (this->isChildMaskOn(n) ? mNodes[n].getChild() : NULL); + assert(this->isChildMaskOn(n)); + return mNodes[n].getChild(); } @@ -2493,7 +2879,8 @@ template<typename ChildT, Index Log2Dim> inline const ChildT* InternalNode<ChildT, Log2Dim>::getChildNode(Index n) const { - return (this->isChildMaskOn(n) ? mNodes[n].getChild() : NULL); + assert(this->isChildMaskOn(n)); + return mNodes[n].getChild(); } } // namespace tree diff --git a/extern/openvdb/internal/openvdb/tree/Iterator.h b/extern/openvdb/internal/openvdb/tree/Iterator.h index d2bfa35abb1..dec27e406a3 100644 --- a/extern/openvdb/internal/openvdb/tree/Iterator.h +++ b/extern/openvdb/internal/openvdb/tree/Iterator.h @@ -36,6 +36,8 @@ #define OPENVDB_TREE_ITERATOR_HAS_BEEN_INCLUDED #include <sstream> +#include <boost/static_assert.hpp> +#include <boost/type_traits/is_const.hpp> #include <boost/type_traits/remove_const.hpp> #include <openvdb/util/NodeMasks.h> #include <openvdb/Exceptions.h> @@ -118,7 +120,7 @@ public: void setValueOff() const { parent().mValueMask.setOff(this->pos()); } /// Return the coordinates of the item to which this iterator is pointing. - Coord getCoord() const { return parent().offset2globalCoord(this->pos()); } + Coord getCoord() const { return parent().offsetToGlobalCoord(this->pos()); } /// Return in @a xyz the coordinates of the item to which this iterator is pointing. void getCoord(Coord& xyz) const { xyz = this->getCoord(); } @@ -176,8 +178,20 @@ struct SparseIteratorBase: public IteratorBase<MaskIterT, NodeT> /// (Not valid for const iterators.) void setValue(const ItemT& value) const { + BOOST_STATIC_ASSERT(!boost::is_const<NodeT>::value); static_cast<const IterT*>(this)->setItem(this->pos(), value); // static polymorphism } + /// @brief Apply a functor to the item to which this iterator is pointing. + /// (Not valid for const iterators.) + /// @param op a functor of the form <tt>void op(ValueType&) const</tt> that modifies + /// its argument in place + /// @see Tree::modifyValue() + template<typename ModifyOp> + void modifyValue(const ModifyOp& op) const + { + BOOST_STATIC_ASSERT(!boost::is_const<NodeT>::value); + static_cast<const IterT*>(this)->modifyItem(this->pos(), op); // static polymorphism + } }; // class SparseIteratorBase diff --git a/extern/openvdb/internal/openvdb/tree/LeafManager.h b/extern/openvdb/internal/openvdb/tree/LeafManager.h index 2024951d6d4..73b9c052723 100644 --- a/extern/openvdb/internal/openvdb/tree/LeafManager.h +++ b/extern/openvdb/internal/openvdb/tree/LeafManager.h @@ -91,6 +91,9 @@ struct LeafManagerImpl }; +//////////////////////////////////////// + + /// @brief This class manages a linear array of pointers to a given tree's /// leaf nodes, as well as optional auxiliary buffers (one or more per leaf) /// that can be swapped with the leaf nodes' voxel data buffers. @@ -126,6 +129,10 @@ public: { assert(this->isValid()); } + Iterator& operator=(const Iterator& other) + { + mRange = other.mRange; mPos = other.mPos; return *this; + } /// Advance to the next leaf node. Iterator& operator++() { ++mPos; return *this; } /// Return a reference to the leaf node to which this iterator is pointing. @@ -147,8 +154,6 @@ public: operator bool() const { return this->test(); } /// Return @c true if this iterator is exhausted. bool empty() const { return !this->test(); } - //bool operator<( const Iterator& other ) const { return mPos < other.mPos; } - void operator=( const Iterator& other) { mRange = other.mRange; mPos = other.mPos; } bool operator!=(const Iterator& other) const { return (mPos != other.mPos) || (&mRange != &other.mRange); @@ -280,8 +285,6 @@ public: this->removeAuxBuffers(); this->initLeafArray(); } - /// @deprecated Use rebuildLeafArray() instead. - OPENVDB_DEPRECATED void rebuildLeafs() { this->rebuildLeafArray(); } /// Return the total number of allocated auxiliary buffers. size_t auxBufferCount() const { return mAuxBufferCount; } @@ -397,6 +400,72 @@ public: return true;//success } + /// @brief Threaded method that applies a user-supplied functor + /// to each leaf node in the LeafManager + /// + /// @param op user-supplied functor, see examples for interface details. + /// @param threaded optional toggle to disable threading, on by default. + /// @param grainSize optional parameter to specify the grainsize + /// for threading, one by default. + /// + /// @warning The functor object is deep-copied to create TBB tasks. + /// + /// @par Example: + /// @code + /// // Functor to offset a tree's voxel values with values from another tree. + /// template<typename TreeType> + /// struct OffsetOp + /// { + /// typedef tree::ValueAccessor<const TreeType> Accessor; + /// + /// OffsetOp(const TreeType& tree): mRhsTreeAcc(tree) {} + /// + /// template <typename LeafNodeType> + /// void operator()(LeafNodeType &lhsLeaf, size_t) const + /// { + /// const LeafNodeType * rhsLeaf = mRhsTreeAcc.probeConstLeaf(lhsLeaf.origin()); + /// if (rhsLeaf) { + /// typename LeafNodeType::ValueOnIter iter = lhsLeaf.beginValueOn(); + /// for (; iter; ++iter) { + /// iter.setValue(iter.getValue() + rhsLeaf->getValue(iter.pos())); + /// } + /// } + /// } + /// private: + /// Accessor mRhsTreeAcc; + /// }; + /// + /// // usage: + /// tree::LeafManager<FloatTree> leafNodes(lhsTree); + /// leafNodes.foreach(OffsetOp<FloatTree>(rhsTree)); + /// + /// // A functor that performs a min operation between different auxiliary buffers. + /// template<typename LeafManagerType> + /// struct MinOp + /// { + /// typedef typename LeafManagerType::BufferType BufferType; + /// + /// MinOp(LeafManagerType& leafNodes): mLeafs(leafNodes) {} + /// + /// template <typename LeafNodeType> + /// void operator()(LeafNodeType &leaf, size_t leafIndex) const + /// { + /// // get the first buffer + /// BufferType& buffer = mLeafs.getBuffer(leafIndex, 1); + /// + /// // min ... + /// } + /// private: + /// LeafManagerType& mLeafs; + /// }; + /// @endcode + template<typename LeafOp> + void foreach(const LeafOp& op, bool threaded = true, size_t grainSize=1) + { + LeafTransformer<LeafOp> transform(op); + transform.run(this->leafRange(grainSize), threaded); + } + //////////////////////////////////////////////////////////////////////////////////// // All methods below are for internal use only and should never be called directly @@ -407,6 +476,8 @@ public: else OPENVDB_THROW(ValueError, "task is undefined"); } + + private: void initLeafArray() { @@ -484,6 +555,23 @@ private: } } + /// @brief Private member class that applies a user-defined + /// functor to all the leaf nodes. + template<typename LeafOp> + struct LeafTransformer + { + LeafTransformer(const LeafOp& leafOp) : mLeafOp(leafOp) {} + void run(const LeafRange& range, bool threaded = true) + { + threaded ? tbb::parallel_for(range, *this) : (*this)(range); + } + void operator()(const LeafRange& range) const + { + for (typename LeafRange::Iterator it = range.begin(); it; ++it) mLeafOp(*it, it.pos()); + } + const LeafOp mLeafOp; + }; + typedef typename boost::function<void (LeafManager*, const RangeType&)> FuncType; TreeType* mTree; diff --git a/extern/openvdb/internal/openvdb/tree/LeafNode.h b/extern/openvdb/internal/openvdb/tree/LeafNode.h index 5487d62d7c4..cd2e1b6574d 100644 --- a/extern/openvdb/internal/openvdb/tree/LeafNode.h +++ b/extern/openvdb/internal/openvdb/tree/LeafNode.h @@ -54,6 +54,9 @@ OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { namespace tree { +template<Index, typename> struct SameLeafConfig; // forward declaration + + /// @brief Templated block class to hold specific data types and a fixed /// number of values determined by Log2Dim. The actual coordinate /// dimension of the block is 2^Log2Dim, i.e. Log2Dim=3 corresponds to @@ -77,12 +80,20 @@ public: LEVEL = 0; // level 0 = leaf /// @brief ValueConverter<T>::Type is the type of a LeafNode having the same - /// child hierarchy and dimensions as this node but a different value type, T. + /// dimensions as this node but a different value type, T. template<typename OtherValueType> struct ValueConverter { typedef LeafNode<OtherValueType, Log2Dim> Type; }; + /// @brief SameConfiguration<OtherNodeType>::value is @c true if and only if + /// OtherNodeType is the type of a LeafNode with the same dimensions as this node. + template<typename OtherNodeType> + struct SameConfiguration { + static const bool value = SameLeafConfig<LOG2DIM, OtherNodeType>::value; + }; + + /// @brief Stores the actual values in the LeafNode. Its dimension /// it fixed to 2^(3*Log2Dim) class Buffer @@ -170,6 +181,10 @@ public: /// Deep copy constructor LeafNode(const LeafNode&); + /// Value conversion copy constructor + template<typename OtherValueType> + explicit LeafNode(const LeafNode<OtherValueType, Log2Dim>& other); + /// Topology copy constructor template<typename OtherValueType> LeafNode(const LeafNode<OtherValueType, Log2Dim>& other, @@ -211,6 +226,8 @@ public: Index64 offVoxelCount() const { return mValueMask.countOff(); } Index64 onLeafVoxelCount() const { return onVoxelCount(); } Index64 offLeafVoxelCount() const { return offVoxelCount(); } + static Index64 onTileCount() { return 0; } + static Index64 offTileCount() { return 0; } /// Return @c true if this node has no active voxels. bool isEmpty() const { return mValueMask.isOff(); } /// Return @c true if this node contains only active voxels. @@ -220,26 +237,30 @@ public: Index64 memUsage() const; /// Expand the given bounding box so that it includes this leaf node's active voxels. - void evalActiveVoxelBoundingBox(CoordBBox&) const; + /// If visitVoxels is false this LeafNode will be approximated as dense, i.e. with all + /// voxels active. Else the individual active voxels are visited to produce a tight bbox. + void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) const; /// @brief Return the bounding box of this node, i.e., the full index space /// spanned by this leaf node. CoordBBox getNodeBoundingBox() const { return CoordBBox::createCube(mOrigin, DIM); } /// Set the grid index coordinates of this node's local origin. - void setOrigin(const Coord& origin) { mOrigin = origin; } - /// Get the grid index coordinates of this node's local origin. + void setOrigin(const Coord& origin) { mOrigin = origin; } + //@{ + /// Return the grid index coordinates of this node's local origin. const Coord& origin() const { return mOrigin; } - const Coord& getOrigin() const { return mOrigin; } void getOrigin(Coord& origin) const { origin = mOrigin; } void getOrigin(Int32& x, Int32& y, Int32& z) const { mOrigin.asXYZ(x, y, z); } + //@} - /// Return the linear table offset of the given coordinates. - static Index coord2offset(const Coord& xyz); - /// Get the local coordinates for a linear table offset. - static void offset2coord(Index n, Coord &xyz); - /// Get the global coordinates for a linear table offset. - Coord offset2globalCoord(Index n) const; + /// Return the linear table offset of the given global or local coordinates. + static Index coordToOffset(const Coord& xyz); + /// @brief Return the local coordinates for a linear table offset, + /// where offset 0 has coordinates (0, 0, 0). + static Coord offsetToLocalCoord(Index n); + /// Return the global coordinates for a linear table offset. + Coord offsetToGlobalCoord(Index n) const; /// Return a string representation of this node. std::string str() const; @@ -287,6 +308,13 @@ protected: { this->parent().setValueOnly(this->pos(), value); } + + // Note: modifyItem() can't be called on const iterators. + template<typename ModifyOp> + void modifyItem(Index n, const ModifyOp& op) const { this->parent().modifyValue(n, op); } + // Note: modifyValue() can't be called on const iterators. + template<typename ModifyOp> + void modifyValue(const ModifyOp& op) const { this->parent().modifyValue(this->pos(), op); } }; /// Leaf nodes have no children, so their child iterators have no get/set accessors. @@ -434,110 +462,87 @@ public: /// Return the level (i.e., 0) at which leaf node values reside. static Index getValueLevel(const Coord&) { return LEVEL; } - /// Set the active state at the given coordinates, but don't change its value. + /// Set the active state of the voxel at the given coordinates but don't change its value. void setActiveState(const Coord& xyz, bool on); + /// Set the active state of the voxel at the given offset but don't change its value. + void setActiveState(Index offset, bool on) { assert(offset<SIZE); mValueMask.set(offset, on); } - /// Mark the voxel at the given coordinates as inactive, but don't change its value. - void setValueOff(const Coord& xyz) { mValueMask.setOff(LeafNode::coord2offset(xyz)); } - /// Mark the voxel at the given offset as inactive, but don't change its value. + /// Set the value of the voxel at the given coordinates but don't change its active state. + void setValueOnly(const Coord& xyz, const ValueType& val); + /// Set the value of the voxel at the given offset but don't change its active state. + void setValueOnly(Index offset, const ValueType& val); + + /// Mark the voxel at the given coordinates as inactive but don't change its value. + void setValueOff(const Coord& xyz) { mValueMask.setOff(LeafNode::coordToOffset(xyz)); } + /// Mark the voxel at the given offset as inactive but don't change its value. void setValueOff(Index offset) { assert(offset < SIZE); mValueMask.setOff(offset); } - /// Change the value of the voxel at the given coordinates and mark the voxel as inactive. + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. void setValueOff(const Coord& xyz, const ValueType& val); - /// Change the value of the voxel at the given offset and mark the voxel as inactive. + /// Set the value of the voxel at the given offset and mark the voxel as inactive. void setValueOff(Index offset, const ValueType& val); - /// Mark the voxel at the given coordinates as active, but don't change its value. - void setValueOn(const Coord& xyz) { mValueMask.setOn(LeafNode::coord2offset(xyz)); } - /// Mark the voxel at the given offset as active, but don't change its value. + /// Mark the voxel at the given coordinates as active but don't change its value. + void setValueOn(const Coord& xyz) { mValueMask.setOn(LeafNode::coordToOffset(xyz)); } + /// Mark the voxel at the given offset as active but don't change its value. void setValueOn(Index offset) { assert(offset < SIZE); mValueMask.setOn(offset); } /// Set the value of the voxel at the given coordinates and mark the voxel as active. void setValueOn(const Coord& xyz, const ValueType& val) { - this->setValueOn(LeafNode::coord2offset(xyz), val); + this->setValueOn(LeafNode::coordToOffset(xyz), val); } - /// Identical to setValueOn + /// Set the value of the voxel at the given coordinates and mark the voxel as active. void setValue(const Coord& xyz, const ValueType& val) { this->setValueOn(xyz, val); }; - /// Change the value of the voxel at the given offset and mark the voxel as active. + /// Set the value of the voxel at the given offset and mark the voxel as active. void setValueOn(Index offset, const ValueType& val) { mBuffer[offset] = val; mValueMask.setOn(offset); } - /// @brief Set the value of the voxel at the given coordinates to the minimum of - /// its current value and the given value, and mark the voxel as active. - void setValueOnMin(const Coord& xyz, const ValueType& val) { - this->setValueOnMin(LeafNode::coord2offset(xyz), val); - } - /// @brief Set the value of the voxel at the given offset to the minimum of - /// its current value and the given value, and mark the voxel as active. - void setValueOnMin(Index offset, const ValueType& val) { - mBuffer[offset] = std::min(val, mBuffer[offset]); - mValueMask.setOn(offset); - } - - /// @brief Set the value of the voxel at the given coordinates to the maximum of - /// its current value and the given value, and mark the voxel as active. - void setValueOnMax(const Coord& xyz, const ValueType& val) { - this->setValueOnMax(LeafNode::coord2offset(xyz), val); - } - /// @brief Set the value of the voxel at the given offset to the maximum of - /// its current value and the given value, and mark the voxel as active. - void setValueOnMax(Index offset, const ValueType& val) { - mBuffer[offset] = std::max(val, mBuffer[offset]); + /// @brief Apply a functor to the value of the voxel at the given offset + /// and mark the voxel as active. + template<typename ModifyOp> + void modifyValue(Index offset, const ModifyOp& op) + { + op(mBuffer[offset]); mValueMask.setOn(offset); } - - /// @brief Set the value of the voxel at the given coordinates to the sum of - /// its current value and the given value, and mark the voxel as active. - void setValueOnSum(const Coord& xyz, const ValueType& val) { - this->setValueOnSum(LeafNode::coord2offset(xyz), val); - } - /// @brief Set the value of the voxel at the given offset to the sum of - /// its current value and the given value, and mark the voxel as active. - void setValueOnSum(Index offset, const ValueType& val) { - mBuffer[offset] += val; - mValueMask.setOn(offset); + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + template<typename ModifyOp> + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + this->modifyValue(this->coordToOffset(xyz), op); } - /// @brief Change the value of the voxel at the given coordinates without altering - /// the voxel's active state. - void setValueOnly(const Coord& xyz, const ValueType& val) { - this->setValueOnly(LeafNode::coord2offset(xyz), val); - } - /// @brief Change the value of the voxel at the given offset without altering - /// the voxel's active state. - /// @details This method is very fast since it effectively - /// ignores the active state of the voxel. - void setValueOnly(Index offset, const ValueType& val) { - assert(offset<SIZE); mBuffer[offset] = val; + /// Apply a functor to the voxel at the given coordinates. + template<typename ModifyOp> + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + const Index offset = this->coordToOffset(xyz); + bool state = mValueMask.isOn(offset); + op(mBuffer[offset], state); + mValueMask.set(offset, state); } - /// Add the given value to all active voxels, leaving inactive voxels unchanged. - void addValue(const ValueType& val); - /// Multiply all active voxels by the given value, leaving inactive voxels unchanged. - void scaleValue(const ValueType& scale); - - /// Mark all voxels as active, but don't change their values. + /// Mark all voxels as active but don't change their values. void setValuesOn() { mValueMask.setOn(); } - /// Mark all voxels as inactive, but don't change their values. + /// Mark all voxels as inactive but don't change their values. void setValuesOff() { mValueMask.setOff(); } /// Return @c true if the voxel at the given coordinates is active. - bool isValueOn(const Coord& xyz) const { return this->isValueOn(LeafNode::coord2offset(xyz)); } + bool isValueOn(const Coord& xyz) const {return this->isValueOn(LeafNode::coordToOffset(xyz));} /// Return @c true if the voxel at the given offset is active. bool isValueOn(Index offset) const { return mValueMask.isOn(offset); } /// Return @c false since leaf nodes never contain tiles. static bool hasActiveTiles() { return false; } - /// @brief Set all voxels within an axis-aligned box to a constant value. - /// (The bbox coordinates are inclusive.) + /// Set all voxels within an axis-aligned box to the specified value and active state. void fill(const CoordBBox& bbox, const ValueType&, bool active = true); - /// @brief Sets all values to the specified value. Their state is unchanged. + /// Set all voxels to the specified value but don't change their active states. void fill(const ValueType& value); - - /// @brief Sets all values to the specified value and state + /// Set all voxels to the specified value and active state. void fill(const ValueType& value, bool active); /// @brief Copy into a dense grid the values of the voxels that lie within @@ -575,7 +580,7 @@ public: const ValueType& background, const ValueType& tolerance); /// @brief Return the value of the voxel at the given coordinates. - /// @note Used internally by ValueAccessor (note last argument is a dummy). + /// @note Used internally by ValueAccessor. template<typename AccessorT> const ValueType& getValueAndCache(const Coord& xyz, AccessorT&) const { @@ -583,12 +588,12 @@ public: } /// @brief Return @c true if the voxel at the given coordinates is active. - /// @note Used internally by ValueAccessor (note last argument is a dummy). + /// @note Used internally by ValueAccessor. template<typename AccessorT> bool isValueOnAndCache(const Coord& xyz, AccessorT&) const { return this->isValueOn(xyz); } /// @brief Change the value of the voxel at the given coordinates and mark it as active. - /// @note Used internally by ValueAccessor (note last argument is a dummy). + /// @note Used internally by ValueAccessor. template<typename AccessorT> void setValueAndCache(const Coord& xyz, const ValueType& val, AccessorT&) { @@ -597,25 +602,32 @@ public: /// @brief Change the value of the voxel at the given coordinates /// but preserve its state. - /// @note Used internally by ValueAccessor (last argument is a dummy). + /// @note Used internally by ValueAccessor. template<typename AccessorT> void setValueOnlyAndCache(const Coord& xyz, const ValueType& val, AccessorT&) { this->setValueOnly(xyz, val); } - /// Set the value of the voxel at the given coordinates to the sum of - /// its current value and the given value, and mark the voxel as - /// active. - /// @note Used internally by ValueAccessor (note last argument is a dummy). - template<typename AccessorT> - void setValueOnSumAndCache(const Coord& xyz, const ValueType& val, AccessorT&) + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @note Used internally by ValueAccessor. + template<typename ModifyOp, typename AccessorT> + void modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) { - this->setValueOnSum(xyz, val); + this->modifyValue(xyz, op); + } + + /// Apply a functor to the voxel at the given coordinates. + /// @note Used internally by ValueAccessor. + template<typename ModifyOp, typename AccessorT> + void modifyValueAndActiveStateAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) + { + this->modifyValueAndActiveState(xyz, op); } /// @brief Change the value of the voxel at the given coordinates and mark it as inactive. - /// @note Used internally by ValueAccessor (note last argument is a dummy). + /// @note Used internally by ValueAccessor. template<typename AccessorT> void setValueOffAndCache(const Coord& xyz, const ValueType& value, AccessorT&) { @@ -624,7 +636,7 @@ public: /// @brief Set the active state of the voxel at the given coordinates /// without changing its value. - /// @note Used internally by ValueAccessor (note last argument is a dummy). + /// @note Used internally by ValueAccessor. template<typename AccessorT> void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&) { @@ -633,7 +645,7 @@ public: /// @brief Return @c true if the voxel at the given coordinates is active /// and return the voxel value in @a val. - /// @note Used internally by ValueAccessor (note last argument is a dummy). + /// @note Used internally by ValueAccessor. template<typename AccessorT> bool probeValueAndCache(const Coord& xyz, ValueType& val, AccessorT&) const { @@ -642,11 +654,11 @@ public: /// @brief Return the value of the voxel at the given coordinates and return /// its active state and level (i.e., 0) in @a state and @a level. - /// @note Used internally by ValueAccessor (note last argument is a dummy). + /// @note Used internally by ValueAccessor. template<typename AccessorT> const ValueType& getValue(const Coord& xyz, bool& state, int& level, AccessorT&) const { - const Index offset = this->coord2offset(xyz); + const Index offset = this->coordToOffset(xyz); state = mValueMask.isOn(offset); level = LEVEL; return mBuffer[offset]; @@ -668,34 +680,29 @@ public: /// and inactive occurrences of @a -oldBackground with @a -newBackground. void resetBackground(const ValueType& oldBackground, const ValueType& newBackground); - /// @brief Overwrites the inactive voxels with a new value whos - /// magnitude is equal to the specified background value and sign - /// is consistant with the lexicographically closest active voxel. - /// The net effect is a propagation of signs from the active - /// voxels to the inactive voxels. - /// - /// @note This method is primarily useful for propagating the sign - /// from the (active) voxels in a narrow-band level set to the - /// inactive values outside the narrow band. + /// @brief Overwrite each inactive value in this node and in any child nodes with + /// a new value whose magnitude is equal to the specified background value and whose + /// sign is consistent with that of the lexicographically closest active value. + /// @details This is primarily useful for propagating the sign from the (active) voxels + /// in a narrow-band level set to the inactive voxels outside the narrow band. void signedFloodFill(const ValueType& background); - /// @brief Sets the inactive voxels to either the outside or inside - /// value, depending on the sign of the closest corresponding - /// active voxels. More specefically, an inactive voxel is set to - /// the outside value if the closest active voxel in the - /// lexicographic direction is positive, else it is set to the - /// inside value. + /// @brief Overwrite each inactive value in this node and in any child nodes with + /// either @a outside or @a inside, depending on the sign of the lexicographically + /// closest active value. + /// @details Specifically, an inactive value is set to @a outside if the closest active + /// value in the lexicographic direction is positive, otherwise it is set to @a inside. void signedFloodFill(const ValueType& outside, const ValueType& inside); void negate(); - void merge(const LeafNode&); - void merge(const LeafNode& other, const ValueType& /*bg*/, const ValueType& /*otherBG*/) - { - LeafNode::merge(other); - } void voxelizeActiveTiles() {}; + template<MergePolicy Policy> void merge(const LeafNode&); + template<MergePolicy Policy> void merge(const ValueType& tileValue, bool tileActive); + template<MergePolicy Policy> + void merge(const LeafNode& other, const ValueType& /*bg*/, const ValueType& /*otherBG*/); + /// @brief Union this node's set of active values with the active values /// of the other node, whose @c ValueType may be different. So a /// resulting voxel will be active if either of the original voxels @@ -721,15 +728,15 @@ public: /// @brief Difference this node's set of active values with the active values /// of the other node, whose @c ValueType may be different. So a - /// resulting voxel will be active only if both of the original - /// voxel is active in this LeafNode and inactive in the other LeafNode. + /// resulting voxel will be active only if the original voxel is + /// active in this LeafNode and inactive in the other LeafNode. /// /// @details The last dummy argument is required to match the signature /// for InternalNode::topologyDifference. /// - /// @note This operation modifies only active states, not - /// values. Also note that this operation can result in all voxels - /// being inactive so consider subsequnetly calling prune. + /// @note This operation modifies only active states, not values. + /// Also, because it can deactivate all of this node's voxels, + /// consider subsequently calling prune. template<typename OtherType> void topologyDifference(const LeafNode<OtherType, Log2Dim>& other, const ValueType&); @@ -738,12 +745,12 @@ public: template<typename CombineOp> void combine(const ValueType& value, bool valueIsActive, CombineOp& op); - template<typename CombineOp> - void combine2(const LeafNode& other, const ValueType&, bool valueIsActive, CombineOp&); - template<typename CombineOp> - void combine2(const ValueType&, const LeafNode& other, bool valueIsActive, CombineOp&); - template<typename CombineOp> - void combine2(const LeafNode& b0, const LeafNode& b1, CombineOp&); + template<typename CombineOp, typename OtherType /*= ValueType*/> + void combine2(const LeafNode& other, const OtherType&, bool valueIsActive, CombineOp&); + template<typename CombineOp, typename OtherNodeT /*= LeafNode*/> + void combine2(const ValueType&, const OtherNodeT& other, bool valueIsActive, CombineOp&); + template<typename CombineOp, typename OtherNodeT /*= LeafNode*/> + void combine2(const LeafNode& b0, const OtherNodeT& b1, CombineOp&); /// @brief Calls the templated functor BBoxOp with bounding box /// information. An additional level argument is provided to the @@ -774,15 +781,30 @@ public: void addLeafAndCache(LeafNode*, AccessorT&) {} template<typename NodeT> NodeT* stealNode(const Coord&, const ValueType&, bool) { return NULL; } - void addTile(Index, const Coord&, const ValueType&, bool) {} - template<typename AccessorT> - void addTileAndCache(Index, const Coord&, const ValueType&, bool, AccessorT&) {} + template<typename NodeT> + NodeT* probeNode(const Coord&) { return NULL; } + template<typename NodeT> + const NodeT* probeConstNode(const Coord&) const { return NULL; } //@} + + void addTile(Index level, const Coord&, const ValueType&, bool); + void addTile(Index offset, const ValueType&, bool); + template<typename AccessorT> + void addTileAndCache(Index, const Coord&, const ValueType&, bool, AccessorT&); + //@{ /// @brief Return a pointer to this node. LeafNode* touchLeaf(const Coord&) { return this; } template<typename AccessorT> LeafNode* touchLeafAndCache(const Coord&, AccessorT&) { return this; } + template<typename NodeT, typename AccessorT> + NodeT* probeNodeAndCache(const Coord&, AccessorT&) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(boost::is_same<NodeT,LeafNode>::value)) return NULL; + return reinterpret_cast<NodeT*>(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } LeafNode* probeLeaf(const Coord&) { return this; } template<typename AccessorT> LeafNode* probeLeafAndCache(const Coord&, AccessorT&) { return this; } @@ -795,13 +817,21 @@ public: template<typename AccessorT> const LeafNode* probeLeafAndCache(const Coord&, AccessorT&) const { return this; } const LeafNode* probeLeaf(const Coord&) const { return this; } + template<typename NodeT, typename AccessorT> + const NodeT* probeConstNodeAndCache(const Coord&, AccessorT&) const + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(boost::is_same<NodeT,LeafNode>::value)) return NULL; + return reinterpret_cast<const NodeT*>(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } //@} /// Return @c true if all of this node's values have the same active state /// and are equal to within the given tolerance, and return the value in @a constValue /// and the active state in @a state. bool isConstant(ValueType& constValue, bool& state, - const ValueType& tolerance = zeroVal<ValueType>()) const; + const ValueType& tolerance = zeroVal<ValueType>()) const; /// Return @c true if all of this node's values are inactive. bool isInactive() const { return mValueMask.isOff(); } @@ -864,16 +894,26 @@ protected: typename ChildAllIterT, typename OtherChildAllIterT> static inline void doVisit2(NodeT& self, OtherChildAllIterT&, VisitorOp&, bool otherIsLHS); -private: +}; // end of LeafNode class + - // Disallow copying. - LeafNode& operator=(const LeafNode&); +//////////////////////////////////////// -}; // end of LeafNode class + +//@{ +/// Helper metafunction used to implement LeafNode::SameConfiguration +/// (which, as an inner class, can't be independently specialized) +template<Index Dim1, typename NodeT2> +struct SameLeafConfig { static const bool value = false; }; + +template<Index Dim1, typename T2> +struct SameLeafConfig<Dim1, LeafNode<T2, Dim1> > { static const bool value = true; }; +//@} //////////////////////////////////////// + template<typename T, Index Log2Dim> inline LeafNode<T, Log2Dim>::LeafNode(): @@ -892,37 +932,59 @@ LeafNode<T, Log2Dim>::LeafNode(const Coord& xyz, const ValueType& val, bool acti { } + template<typename T, Index Log2Dim> -template<typename OtherValueType> inline -LeafNode<T, Log2Dim>::LeafNode(const LeafNode<OtherValueType, Log2Dim>& other, - const ValueType& background, TopologyCopy): - mBuffer(background), +LeafNode<T, Log2Dim>::LeafNode(const LeafNode &other): + mBuffer(other.mBuffer), mValueMask(other.mValueMask), mOrigin(other.mOrigin) { } + +// Copy-construct from a leaf node with the same configuration but a different ValueType. template<typename T, Index Log2Dim> template<typename OtherValueType> inline -LeafNode<T, Log2Dim>::LeafNode(const LeafNode<OtherValueType, Log2Dim>& other, - const ValueType& offValue, const ValueType& onValue, TopologyCopy): +LeafNode<T, Log2Dim>::LeafNode(const LeafNode<OtherValueType, Log2Dim>& other): mValueMask(other.mValueMask), mOrigin(other.mOrigin) { + struct Local { + /// @todo Consider using a value conversion functor passed as an argument instead. + static inline ValueType convertValue(const OtherValueType& val) { return ValueType(val); } + }; + for (Index i = 0; i < SIZE; ++i) { - mBuffer[i] = (mValueMask.isOn(i) ? onValue : offValue); + mBuffer[i] = Local::convertValue(other.mBuffer[i]); } } + +template<typename T, Index Log2Dim> +template<typename OtherValueType> +inline +LeafNode<T, Log2Dim>::LeafNode(const LeafNode<OtherValueType, Log2Dim>& other, + const ValueType& background, TopologyCopy): + mBuffer(background), + mValueMask(other.mValueMask), + mOrigin(other.mOrigin) +{ +} + + template<typename T, Index Log2Dim> +template<typename OtherValueType> inline -LeafNode<T, Log2Dim>::LeafNode(const LeafNode &other): - mBuffer(other.mBuffer), +LeafNode<T, Log2Dim>::LeafNode(const LeafNode<OtherValueType, Log2Dim>& other, + const ValueType& offValue, const ValueType& onValue, TopologyCopy): mValueMask(other.mValueMask), mOrigin(other.mOrigin) { + for (Index i = 0; i < SIZE; ++i) { + mBuffer[i] = (mValueMask.isOn(i) ? onValue : offValue); + } } @@ -948,34 +1010,33 @@ LeafNode<T, Log2Dim>::str() const template<typename T, Index Log2Dim> inline Index -LeafNode<T, Log2Dim>::coord2offset(const Coord& xyz) +LeafNode<T, Log2Dim>::coordToOffset(const Coord& xyz) { - assert ((xyz[0]&DIM-1u)<DIM && (xyz[1]&DIM-1u)<DIM && (xyz[2]&DIM-1u)<DIM); - return ((xyz[0]&DIM-1u)<<2*Log2Dim) - + ((xyz[1]&DIM-1u)<< Log2Dim) - + (xyz[2]&DIM-1u); + assert ((xyz[0] & (DIM-1u)) < DIM && (xyz[1] & (DIM-1u)) < DIM && (xyz[2] & (DIM-1u)) < DIM); + return ((xyz[0] & (DIM-1u)) << 2*Log2Dim) + + ((xyz[1] & (DIM-1u)) << Log2Dim) + + (xyz[2] & (DIM-1u)); } - template<typename T, Index Log2Dim> -inline void -LeafNode<T, Log2Dim>::offset2coord(Index n, Coord &xyz) +inline Coord +LeafNode<T, Log2Dim>::offsetToLocalCoord(Index n) { assert(n<(1<< 3*Log2Dim)); + Coord xyz; xyz.setX(n >> 2*Log2Dim); n &= ((1<<2*Log2Dim)-1); xyz.setY(n >> Log2Dim); xyz.setZ(n & ((1<<Log2Dim)-1)); + return xyz; } template<typename T, Index Log2Dim> inline Coord -LeafNode<T, Log2Dim>::offset2globalCoord(Index n) const +LeafNode<T, Log2Dim>::offsetToGlobalCoord(Index n) const { - Coord local; - this->offset2coord(n, local); - return Coord(local + this->getOrigin()); + return (this->offsetToLocalCoord(n) + this->origin()); } @@ -986,7 +1047,7 @@ template<typename ValueT, Index Log2Dim> inline const ValueT& LeafNode<ValueT, Log2Dim>::getValue(const Coord& xyz) const { - return this->getValue(LeafNode::coord2offset(xyz)); + return this->getValue(LeafNode::coordToOffset(xyz)); } template<typename ValueT, Index Log2Dim> @@ -1002,7 +1063,7 @@ template<typename T, Index Log2Dim> inline bool LeafNode<T, Log2Dim>::probeValue(const Coord& xyz, ValueType& val) const { - return this->probeValue(LeafNode::coord2offset(xyz), val); + return this->probeValue(LeafNode::coordToOffset(xyz), val); } template<typename T, Index Log2Dim> @@ -1019,7 +1080,7 @@ template<typename T, Index Log2Dim> inline void LeafNode<T, Log2Dim>::setValueOff(const Coord& xyz, const ValueType& val) { - this->setValueOff(LeafNode::coord2offset(xyz), val); + this->setValueOff(LeafNode::coordToOffset(xyz), val); } template<typename T, Index Log2Dim> @@ -1036,27 +1097,22 @@ template<typename T, Index Log2Dim> inline void LeafNode<T, Log2Dim>::setActiveState(const Coord& xyz, bool on) { - mValueMask.set(this->coord2offset(xyz), on); + mValueMask.set(this->coordToOffset(xyz), on); } template<typename T, Index Log2Dim> inline void -LeafNode<T, Log2Dim>::addValue(const ValueType& val) +LeafNode<T, Log2Dim>::setValueOnly(const Coord& xyz, const ValueType& val) { - for (typename NodeMaskType::OnIterator iter = mValueMask.beginOn(); iter; ++iter) { - mBuffer[iter.pos()] += val; - } + this->setValueOnly(LeafNode::coordToOffset(xyz), val); } - template<typename T, Index Log2Dim> inline void -LeafNode<T, Log2Dim>::scaleValue(const ValueType& scale) +LeafNode<T, Log2Dim>::setValueOnly(Index offset, const ValueType& val) { - for (typename NodeMaskType::OnIterator iter = mValueMask.beginOn(); iter; ++iter) { - mBuffer[iter.pos()] *= scale; - } + assert(offset<SIZE); mBuffer[offset] = val; } @@ -1068,11 +1124,11 @@ inline void LeafNode<T, Log2Dim>::fill(const CoordBBox& bbox, const ValueType& value, bool active) { for (Int32 x = bbox.min().x(); x <= bbox.max().x(); ++x) { - const Index offsetX = (x&DIM-1u)<<2*Log2Dim; + const Index offsetX = (x & (DIM-1u)) << 2*Log2Dim; for (Int32 y = bbox.min().y(); y <= bbox.max().y(); ++y) { - const Index offsetXY = offsetX + ((y&DIM-1u)<< Log2Dim); + const Index offsetXY = offsetX + ((y & (DIM-1u)) << Log2Dim); for (Int32 z = bbox.min().z(); z <= bbox.max().z(); ++z) { - const Index offset = offsetXY + (z&DIM-1u); + const Index offset = offsetXY + (z & (DIM-1u)); mBuffer[offset] = value; mValueMask.set(offset, active); } @@ -1095,58 +1151,68 @@ LeafNode<T, Log2Dim>::fill(const ValueType& value, bool active) mValueMask.set(active); } + //////////////////////////////////////// + template<typename T, Index Log2Dim> template<typename DenseT> inline void LeafNode<T, Log2Dim>::copyToDense(const CoordBBox& bbox, DenseT& dense) const { - const size_t xStride = dense.xStride(), yStride = dense.yStride();// zStride=1 + typedef typename DenseT::ValueType DenseValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); const Coord& min = dense.bbox().min(); - T* t0 = dense.data() + bbox.min()[2]-min[2];//target array - const T* s0 = &mBuffer[bbox.min()[2]&DIM-1u];//source array - for (Int32 x = bbox.min()[0], ex=bbox.max()[0]+1; x<ex; ++x) { - T* t1 = t0 + xStride*(x-min[0]); - const T* s1 = s0 + ((x&DIM-1u)<<2*Log2Dim); - for (Int32 y = bbox.min()[1], ey=bbox.max()[1]+1; y<ey; ++y) { - T* t2 = t1 + yStride*(y-min[1]); - const T* s2 = s1 + ((y&DIM-1u)<<Log2Dim); - for (Int32 z = bbox.min()[2], ez=bbox.max()[2]+1; z<ez ; ++z) *t2++ = *s2++; + DenseValueType* t0 = dense.data() + zStride * (bbox.min()[2] - min[2]); // target array + const T* s0 = &mBuffer[bbox.min()[2] & (DIM-1u)]; // source array + for (Int32 x = bbox.min()[0], ex = bbox.max()[0] + 1; x < ex; ++x) { + DenseValueType* t1 = t0 + xStride * (x - min[0]); + const T* s1 = s0 + ((x & (DIM-1u)) << 2*Log2Dim); + for (Int32 y = bbox.min()[1], ey = bbox.max()[1] + 1; y < ey; ++y) { + DenseValueType* t2 = t1 + yStride * (y - min[1]); + const T* s2 = s1 + ((y & (DIM-1u)) << Log2Dim); + for (Int32 z = bbox.min()[2], ez = bbox.max()[2] + 1; z < ez; ++z, t2 += zStride) { + *t2 = DenseValueType(*s2++); + } } } } + template<typename T, Index Log2Dim> template<typename DenseT> inline void LeafNode<T, Log2Dim>::copyFromDense(const CoordBBox& bbox, const DenseT& dense, const ValueType& background, const ValueType& tolerance) { - const size_t xStride = dense.xStride(), yStride = dense.yStride();// zStride=1 + typedef typename DenseT::ValueType DenseValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); const Coord& min = dense.bbox().min(); - const ValueType* s0 = dense.data() + bbox.min()[2]-min[2];//source - const Int32 n0 = bbox.min()[2]&DIM-1u; - for (Int32 x = bbox.min()[0], ex=bbox.max()[0]+1; x<ex; ++x) { - const ValueType* s1 = s0 + xStride*(x-min[0]); - const Int32 n1 = n0 + ((x&DIM-1u)<<2*LOG2DIM); - for (Int32 y = bbox.min()[1], ey=bbox.max()[1]+1; y<ey; ++y) { - const ValueType* s2 = s1 + yStride*(y-min[1]); - Int32 n2 = n1 + ((y&DIM-1u)<<LOG2DIM) ; - for (Int32 z = bbox.min()[2], ez=bbox.max()[2]+1; z<ez ; ++z, ++n2, ++s2) { - if (math::isApproxEqual(background, *s2, tolerance)) { + const DenseValueType* s0 = dense.data() + zStride * (bbox.min()[2] - min[2]); // source + const Int32 n0 = bbox.min()[2] & (DIM-1u); + for (Int32 x = bbox.min()[0], ex = bbox.max()[0]+1; x < ex; ++x) { + const DenseValueType* s1 = s0 + xStride * (x - min[0]); + const Int32 n1 = n0 + ((x & (DIM-1u)) << 2*LOG2DIM); + for (Int32 y = bbox.min()[1], ey = bbox.max()[1]+1; y < ey; ++y) { + const DenseValueType* s2 = s1 + yStride * (y - min[1]); + Int32 n2 = n1 + ((y & (DIM-1u)) << LOG2DIM); + for (Int32 z = bbox.min()[2], ez = bbox.max()[2]+1; z < ez; ++z, ++n2, s2 += zStride) { + if (math::isApproxEqual(background, ValueType(*s2), tolerance)) { mValueMask.setOff(n2); mBuffer[n2] = background; } else { mValueMask.setOn(n2); - mBuffer[n2] = *s2; + mBuffer[n2] = ValueType(*s2); } } } } } + //////////////////////////////////////// @@ -1238,15 +1304,17 @@ LeafNode<T, Log2Dim>::memUsage() const template<typename T, Index Log2Dim> inline void -LeafNode<T, Log2Dim>::evalActiveVoxelBoundingBox(CoordBBox& bbox) const +LeafNode<T, Log2Dim>::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const { - const CoordBBox this_bbox = this->getNodeBoundingBox(); - if (bbox.isInside(this_bbox)) { - // nothing to do - } else if (this->isDense()) { + CoordBBox this_bbox = this->getNodeBoundingBox(); + if (bbox.isInside(this_bbox)) return;//this LeafNode is already enclosed in the bbox + if (ValueOnCIter iter = this->cbeginValueOn()) {//any active values? + if (visitVoxels) {//use voxel granularity? + this_bbox.reset(); + for(; iter; ++iter) this_bbox.expand(this->offsetToLocalCoord(iter.pos())); + this_bbox.translate(this->origin()); + } bbox.expand(this_bbox); - } else { - for (ValueOnCIter iter=this->cbeginValueOn(); iter; ++iter) bbox.expand(iter.getCoord()); } } @@ -1264,12 +1332,12 @@ LeafNode<T, Log2Dim>::hasSameTopology(const LeafNode<OtherType, OtherLog2Dim>* o template<typename T, Index Log2Dim> inline bool LeafNode<T, Log2Dim>::isConstant(ValueType& constValue, - bool& state, const ValueType& tolerance) const + bool& state, const ValueType& tolerance) const { - if (!mValueMask.isOn() && !mValueMask.isOff()) return false; - state = mValueMask.isOn(); + if (!(state || mValueMask.isOff())) return false; + bool allEqual = true; const T value = mBuffer[0]; for (Index i = 1; allEqual && i < SIZE; ++i) { @@ -1280,11 +1348,45 @@ LeafNode<T, Log2Dim>::isConstant(ValueType& constValue, return allEqual; } + +//////////////////////////////////////// + + +template<typename T, Index Log2Dim> +inline void +LeafNode<T, Log2Dim>::addTile(Index level, const Coord& xyz, const ValueType& val, bool active) +{ + assert(level == 0); + this->addTile(this->coordToOffset(xyz), val, active); +} + +template<typename T, Index Log2Dim> +inline void +LeafNode<T, Log2Dim>::addTile(Index offset, const ValueType& val, bool active) +{ + assert(offset < SIZE); + setValueOnly(offset, val); + setActiveState(offset, active); +} + +template<typename T, Index Log2Dim> +template<typename AccessorT> +inline void +LeafNode<T, Log2Dim>::addTileAndCache(Index level, const Coord& xyz, + const ValueType& val, bool active, AccessorT&) +{ + this->addTile(level, xyz, val, active); +} + + +//////////////////////////////////////// + + template<typename T, Index Log2Dim> inline void LeafNode<T, Log2Dim>::signedFloodFill(const ValueType& background) { - this->signedFloodFill(background, negative(background)); + this->signedFloodFill(background, math::negative(background)); } template<typename T, Index Log2Dim> @@ -1334,24 +1436,55 @@ LeafNode<T, Log2Dim>::resetBackground(const ValueType& oldBackground, ValueType &inactiveValue = mBuffer[iter.pos()]; if (math::isApproxEqual(inactiveValue, oldBackground)) { inactiveValue = newBackground; - } else if (math::isApproxEqual(inactiveValue, negative(oldBackground))) { - inactiveValue = negative(newBackground); + } else if (math::isApproxEqual(inactiveValue, math::negative(oldBackground))) { + inactiveValue = math::negative(newBackground); } } } template<typename T, Index Log2Dim> +template<MergePolicy Policy> inline void LeafNode<T, Log2Dim>::merge(const LeafNode& other) { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Policy == MERGE_NODES) return; typename NodeMaskType::OnIterator iter = other.mValueMask.beginOn(); for (; iter; ++iter) { const Index n = iter.pos(); - if (mValueMask.isOn(n)) continue; - mBuffer[n] = other.mBuffer[n]; + if (mValueMask.isOff(n)) { + mBuffer[n] = other.mBuffer[n]; + mValueMask.setOn(n); + } + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + +template<typename T, Index Log2Dim> +template<MergePolicy Policy> +inline void +LeafNode<T, Log2Dim>::merge(const LeafNode& other, + const ValueType& /*bg*/, const ValueType& /*otherBG*/) +{ + this->template merge<Policy>(other); +} + +template<typename T, Index Log2Dim> +template<MergePolicy Policy> +inline void +LeafNode<T, Log2Dim>::merge(const ValueType& tileValue, bool tileActive) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Policy != MERGE_ACTIVE_STATES_AND_NODES) return; + if (!tileActive) return; + // Replace all inactive values with the active tile value. + for (typename NodeMaskType::OffIterator iter = mValueMask.beginOff(); iter; ++iter) { + const Index n = iter.pos(); + mBuffer[n] = tileValue; mValueMask.setOn(n); } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END } @@ -1431,12 +1564,12 @@ LeafNode<T, Log2Dim>::combine(const ValueType& value, bool valueIsActive, Combin template<typename T, Index Log2Dim> -template<typename CombineOp> +template<typename CombineOp, typename OtherType> inline void -LeafNode<T, Log2Dim>::combine2(const LeafNode& other, const ValueType& value, +LeafNode<T, Log2Dim>::combine2(const LeafNode& other, const OtherType& value, bool valueIsActive, CombineOp& op) { - CombineArgs<T> args; + CombineArgs<T, OtherType> args; args.setBRef(value).setBIsActive(valueIsActive); for (Index i = 0; i < SIZE; ++i) { op(args.setARef(other.mBuffer[i]) @@ -1448,12 +1581,12 @@ LeafNode<T, Log2Dim>::combine2(const LeafNode& other, const ValueType& value, template<typename T, Index Log2Dim> -template<typename CombineOp> +template<typename CombineOp, typename OtherNodeT> inline void -LeafNode<T, Log2Dim>::combine2(const ValueType& value, const LeafNode& other, +LeafNode<T, Log2Dim>::combine2(const ValueType& value, const OtherNodeT& other, bool valueIsActive, CombineOp& op) { - CombineArgs<T> args; + CombineArgs<T, typename OtherNodeT::ValueType> args; args.setARef(value).setAIsActive(valueIsActive); for (Index i = 0; i < SIZE; ++i) { op(args.setBRef(other.mBuffer[i]) @@ -1465,11 +1598,11 @@ LeafNode<T, Log2Dim>::combine2(const ValueType& value, const LeafNode& other, template<typename T, Index Log2Dim> -template<typename CombineOp> +template<typename CombineOp, typename OtherNodeT> inline void -LeafNode<T, Log2Dim>::combine2(const LeafNode& b0, const LeafNode& b1, CombineOp& op) +LeafNode<T, Log2Dim>::combine2(const LeafNode& b0, const OtherNodeT& b1, CombineOp& op) { - CombineArgs<T> args; + CombineArgs<T, typename OtherNodeT::ValueType> args; for (Index i = 0; i < SIZE; ++i) { mValueMask.set(i, b0.mValueMask.isOn(i) || b1.mValueMask.isOn(i)); op(args.setARef(b0.mBuffer[i]) diff --git a/extern/openvdb/internal/openvdb/tree/LeafNodeBool.h b/extern/openvdb/internal/openvdb/tree/LeafNodeBool.h index ce07c2f372e..f5831dcf94f 100644 --- a/extern/openvdb/internal/openvdb/tree/LeafNodeBool.h +++ b/extern/openvdb/internal/openvdb/tree/LeafNodeBool.h @@ -73,6 +73,14 @@ public: template<typename ValueType> struct ValueConverter { typedef LeafNode<ValueType, Log2Dim> Type; }; + /// @brief SameConfiguration<OtherNodeType>::value is @c true if and only if + /// OtherNodeType is the type of a LeafNode with the same dimensions as this node. + template<typename OtherNodeType> + struct SameConfiguration { + static const bool value = SameLeafConfig<LOG2DIM, OtherNodeType>::value; + }; + + class Buffer { public: @@ -122,18 +130,22 @@ public: /// Deep copy constructor LeafNode(const LeafNode&); + /// Value conversion copy constructor + template<typename OtherValueType> + explicit LeafNode(const LeafNode<OtherValueType, Log2Dim>& other); + /// Topology copy constructor template<typename ValueType> LeafNode(const LeafNode<ValueType, Log2Dim>& other, TopologyCopy); - /// Topology copy constructor + //@{ + /// @brief Topology copy constructor /// @note This variant exists mainly to enable template instantiation. template<typename ValueType> - LeafNode(const LeafNode<ValueType, Log2Dim>& other, - bool offValue, bool onValue, TopologyCopy); + LeafNode(const LeafNode<ValueType, Log2Dim>& other, bool offValue, bool onValue, TopologyCopy); template<typename ValueType> - LeafNode(const LeafNode<ValueType, Log2Dim>& other, - bool background, TopologyCopy); + LeafNode(const LeafNode<ValueType, Log2Dim>& other, bool background, TopologyCopy); + //@} /// Destructor ~LeafNode(); @@ -160,6 +172,8 @@ public: Index64 offVoxelCount() const { return mValueMask.countOff(); } Index64 onLeafVoxelCount() const { return onVoxelCount(); } Index64 offLeafVoxelCount() const { return offVoxelCount(); } + static Index64 onTileCount() { return 0; } + static Index64 offTileCount() { return 0; } /// Return @c true if this node has no active voxels. bool isEmpty() const { return mValueMask.isOff(); } @@ -169,31 +183,31 @@ public: /// Return the memory in bytes occupied by this node. Index64 memUsage() const; - /// Expand the specified bbox so it includes the active voxels of - /// this leaf node. - void evalActiveVoxelBoundingBox(CoordBBox& bbox) const; + /// Expand the given bounding box so that it includes this leaf node's active voxels. + /// If visitVoxels is false this LeafNode will be approximated as dense, i.e. with all + /// voxels active. Else the individual active voxels are visited to produce a tight bbox. + void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) const; /// @brief Return the bounding box of this node, i.e., the full index space /// spanned by this leaf node. CoordBBox getNodeBoundingBox() const { return CoordBBox::createCube(mOrigin, DIM); } /// Set the grid index coordinates of this node's local origin. - void setOrigin(const Coord& origin) { mOrigin = origin; } + void setOrigin(const Coord& origin) { mOrigin = origin; } //@{ - /// Get the grid index coordinates of this node's local origin. + /// Return the grid index coordinates of this node's local origin. const Coord& origin() const { return mOrigin; } - const Coord& getOrigin() const { return mOrigin; } void getOrigin(Coord& origin) const { origin = mOrigin; } void getOrigin(Int32& x, Int32& y, Int32& z) const { mOrigin.asXYZ(x, y, z); } //@} - /// Return the linear table offset of the given coordinates. - static Index coord2offset(const Coord& xyz); + /// Return the linear table offset of the given global or local coordinates. + static Index coordToOffset(const Coord& xyz); /// @brief Return the local coordinates for a linear table offset, /// where offset 0 has coordinates (0, 0, 0). - static Coord offset2coord(Index n); + static Coord offsetToLocalCoord(Index n); /// Return the global coordinates for a linear table offset. - Coord offset2globalCoord(Index n) const; + Coord offsetToGlobalCoord(Index n) const; /// Return a string representation of this node. std::string str() const; @@ -245,70 +259,70 @@ public: /// Return the level (0) at which leaf node values reside. static Index getValueLevel(const Coord&) { return LEVEL; } - /// Set the active state at the given coordinates, but don't change its value. + /// Set the active state of the voxel at the given coordinates but don't change its value. void setActiveState(const Coord& xyz, bool on); + /// Set the active state of the voxel at the given offset but don't change its value. + void setActiveState(Index offset, bool on) { assert(offset<SIZE); mValueMask.set(offset, on); } - /// Mark the voxel at the given coordinates as inactive, but don't change its value. - void setValueOff(const Coord& xyz) { mValueMask.setOff(this->coord2offset(xyz)); } - /// Mark the voxel at the given offset as inactive, but don't change its value. + /// Set the value of the voxel at the given coordinates but don't change its active state. + void setValueOnly(const Coord& xyz, bool val); + /// Set the value of the voxel at the given offset but don't change its active state. + void setValueOnly(Index offset, bool val) { assert(offset<SIZE); mBuffer.setValue(offset,val); } + + /// Mark the voxel at the given coordinates as inactive but don't change its value. + void setValueOff(const Coord& xyz) { mValueMask.setOff(this->coordToOffset(xyz)); } + /// Mark the voxel at the given offset as inactive but don't change its value. void setValueOff(Index offset) { assert(offset < SIZE); mValueMask.setOff(offset); } - /// Change the value of the voxel at the given coordinates and mark the voxel as inactive. + + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. void setValueOff(const Coord& xyz, bool val); + /// Set the value of the voxel at the given offset and mark the voxel as inactive. + void setValueOff(Index offset, bool val); - /// Mark the voxel at the given coordinates as active, but don't change its value. - void setValueOn(const Coord& xyz) { mValueMask.setOn(this->coord2offset(xyz)); } - /// Mark the voxel at the given offset as active, but don't change its value. + /// Mark the voxel at the given coordinates as active but don't change its value. + void setValueOn(const Coord& xyz) { mValueMask.setOn(this->coordToOffset(xyz)); } + /// Mark the voxel at the given offset as active but don't change its value. void setValueOn(Index offset) { assert(offset < SIZE); mValueMask.setOn(offset); } - /// Change the value of the voxel at the given coordinates and mark the voxel as active. + + /// Set the value of the voxel at the given coordinates and mark the voxel as active. void setValueOn(const Coord& xyz, bool val); - /// Identical to setValueOn + /// Set the value of the voxel at the given coordinates and mark the voxel as active. void setValue(const Coord& xyz, bool val) { this->setValueOn(xyz, val); }; - - /// @brief Set the value of the voxel at the given coordinates to the minimum - /// of its current value and the given value, and mark the voxel as active. - void setValueOnMin(const Coord& xyz, bool val); - /// @brief Set the value of the voxel at the given coordinates to the maximum - /// of its current value and the given value, and mark the voxel as active. - void setValueOnMax(const Coord& xyz, bool val); - /// @brief Set the value of the voxel at the given coordinates to the sum - /// of its current value and the given value, and mark the voxel as active. - void setValueOnSum(const Coord& xyz, bool val); - - /// @brief Change the value of the voxel at the given coordinates without altering - /// the voxel's active state. - void setValueOnly(const Coord& xyz, bool val) { - this->setValueOnly(LeafNode::coord2offset(xyz), val); - } - /// @brief Change the value of the voxel at the given offset without altering - /// the voxel's active state. - void setValueOnly(Index offset, bool val) { assert(offset<SIZE); mBuffer.setValue(offset,val); } - - /// Add the given value to all active voxels, leaving inactive voxels unchanged. - void addValue(bool val); - /// Multiply all active voxels by the given value, leaving inactive voxels unchanged. - void scaleValue(bool scale); - - /// Mark all voxels as active, but don't change their values. + /// Set the value of the voxel at the given offset and mark the voxel as active. + void setValueOn(Index offset, bool val); + + /// @brief Apply a functor to the value of the voxel at the given offset + /// and mark the voxel as active. + template<typename ModifyOp> + void modifyValue(Index offset, const ModifyOp& op); + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + template<typename ModifyOp> + void modifyValue(const Coord& xyz, const ModifyOp& op); + + /// Apply a functor to the voxel at the given coordinates. + template<typename ModifyOp> + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op); + + /// Mark all voxels as active but don't change their values. void setValuesOn() { mValueMask.setOn(); } - /// Mark all voxels as inactive, but don't change their values. + /// Mark all voxels as inactive but don't change their values. void setValuesOff() { mValueMask.setOff(); } /// Return @c true if the voxel at the given coordinates is active. - bool isValueOn(const Coord& xyz) const { return mValueMask.isOn(this->coord2offset(xyz)); } + bool isValueOn(const Coord& xyz) const { return mValueMask.isOn(this->coordToOffset(xyz)); } /// Return @c true if the voxel at the given offset is active. bool isValueOn(Index offset) const { assert(offset < SIZE); return mValueMask.isOn(offset); } /// Return @c false since leaf nodes never contain tiles. static bool hasActiveTiles() { return false; } - /// @brief Set all voxels within an axis-aligned box to the given active state. - /// (The bbox coordinates are inclusive.) + /// Set all voxels within an axis-aligned box to the specified value and active state. void fill(const CoordBBox& bbox, bool value, bool active = true); - /// @brief Sets all values to the specified value. Their state is unchanged. + /// Set all voxels to the specified value but don't change their active states. void fill(const bool& value); - - /// @brief Sets all values to the specified value and state + /// Set all voxels to the specified value and active state. void fill(const bool& value, bool active); /// @brief Copy into a dense grid the values of the voxels that lie within @@ -345,37 +359,54 @@ public: void copyFromDense(const CoordBBox& bbox, const DenseT& dense, bool background, bool tolerance); /// @brief Return the value of the voxel at the given coordinates. - /// @note Used internally by ValueAccessor (note last argument is a dummy). + /// @note Used internally by ValueAccessor. template<typename AccessorT> const bool& getValueAndCache(const Coord& xyz, AccessorT&) const {return this->getValue(xyz);} /// @brief Return @c true if the voxel at the given coordinates is active. - /// @note Used internally by ValueAccessor (note last argument is a dummy). + /// @note Used internally by ValueAccessor. template<typename AccessorT> bool isValueOnAndCache(const Coord& xyz, AccessorT&) const { return this->isValueOn(xyz); } /// @brief Change the value of the voxel at the given coordinates and mark it as active. - /// @note Used internally by ValueAccessor (note last argument is a dummy). + /// @note Used internally by ValueAccessor. template<typename AccessorT> void setValueAndCache(const Coord& xyz, bool val, AccessorT&) { this->setValueOn(xyz, val); } /// @brief Change the value of the voxel at the given coordinates /// but preserve its state. - /// @note Used internally by ValueAccessor (last argument is a dummy). + /// @note Used internally by ValueAccessor. template<typename AccessorT> void setValueOnlyAndCache(const Coord& xyz, bool val, AccessorT&) {this->setValueOnly(xyz,val);} /// @brief Change the value of the voxel at the given coordinates and mark it as inactive. - /// @note Used internally by ValueAccessor (note last argument is a dummy). + /// @note Used internally by ValueAccessor. template<typename AccessorT> void setValueOffAndCache(const Coord& xyz, bool value, AccessorT&) { this->setValueOff(xyz, value); } + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @note Used internally by ValueAccessor. + template<typename ModifyOp, typename AccessorT> + void modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) + { + this->modifyValue(xyz, op); + } + + /// Apply a functor to the voxel at the given coordinates. + /// @note Used internally by ValueAccessor. + template<typename ModifyOp, typename AccessorT> + void modifyValueAndActiveStateAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) + { + this->modifyValueAndActiveState(xyz, op); + } + /// @brief Set the active state of the voxel at the given coordinates /// without changing its value. - /// @note Used internally by ValueAccessor (note last argument is a dummy). + /// @note Used internally by ValueAccessor. template<typename AccessorT> void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&) { @@ -384,7 +415,7 @@ public: /// @brief Return @c true if the voxel at the given coordinates is active /// and return the voxel value in @a val. - /// @note Used internally by ValueAccessor (note last argument is a dummy). + /// @note Used internally by ValueAccessor. template<typename AccessorT> bool probeValueAndCache(const Coord& xyz, bool& val, AccessorT&) const { @@ -392,7 +423,7 @@ public: } /// @brief Return the LEVEL (=0) at which leaf node values reside. - /// @note Used internally by ValueAccessor (note last argument is a dummy). + /// @note Used internally by ValueAccessor. template<typename AccessorT> static Index getValueLevelAndCache(const Coord&, AccessorT&) { return LEVEL; } @@ -416,7 +447,9 @@ public: void negate() { mBuffer.mData.toggle(); } - void merge(const LeafNode& other); + template<MergePolicy Policy> + void merge(const LeafNode& other, bool bg = false, bool otherBG = false); + template<MergePolicy Policy> void merge(bool tileValue, bool tileActive); void voxelizeActiveTiles() {}; @@ -445,15 +478,15 @@ public: /// @brief Difference this node's set of active values with the active values /// of the other node, whose @c ValueType may be different. So a - /// resulting voxel will be active only if both of the original - /// voxel is active in this LeafNode and inactive in the other LeafNode. + /// resulting voxel will be active only if the original voxel is + /// active in this LeafNode and inactive in the other LeafNode. /// /// @details The last dummy argument is required to match the signature /// for InternalNode::topologyDifference. /// - /// @note This operation modifies only active states, not - /// values. Also note that this operation can result in all voxels - /// being inactive so consider subsequnetly calling prune. + /// @note This operation modifies only active states, not values. + /// Also, because it can deactivate all of this node's voxels, + /// consider subsequently calling prune. template<typename OtherType> void topologyDifference(const LeafNode<OtherType, Log2Dim>& other, const bool&); @@ -462,12 +495,12 @@ public: template<typename CombineOp> void combine(bool, bool valueIsActive, CombineOp& op); - template<typename CombineOp> - void combine2(const LeafNode& other, bool, bool valueIsActive, CombineOp&); - template<typename CombineOp> - void combine2(bool, const LeafNode& other, bool valueIsActive, CombineOp&); - template<typename CombineOp> - void combine2(const LeafNode& b0, const LeafNode& b1, CombineOp&); + template<typename CombineOp, typename OtherType /*= bool*/> + void combine2(const LeafNode& other, const OtherType&, bool valueIsActive, CombineOp&); + template<typename CombineOp, typename OtherNodeT /*= LeafNode*/> + void combine2(bool, const OtherNodeT& other, bool valueIsActive, CombineOp&); + template<typename CombineOp, typename OtherNodeT /*= LeafNode*/> + void combine2(const LeafNode& b0, const OtherNodeT& b1, CombineOp&); /// @brief Calls the templated functor BBoxOp with bounding box information. /// An additional level argument is provided to the callback. @@ -498,32 +531,53 @@ public: void addLeaf(LeafNode*) {} template<typename AccessorT> void addLeafAndCache(LeafNode*, AccessorT&) {} - LeafNode* stealLeaf(const Coord&, bool, bool) { return this; } - void addTile(Index, const Coord&, bool, bool) {} - template<typename AccessorT> - void addTileAndCache(Index, const Coord&, const ValueType&, bool, AccessorT&) {} + template<typename NodeT> + NodeT* stealNode(const Coord&, const ValueType&, bool) { return NULL; } + template<typename NodeT> + NodeT* probeNode(const Coord&) { return NULL; } + template<typename NodeT> + const NodeT* probeConstNode(const Coord&) const { return NULL; } //@} + void addTile(Index level, const Coord&, bool val, bool active); + void addTile(Index offset, bool val, bool active); + template<typename AccessorT> + void addTileAndCache(Index level, const Coord&, bool val, bool active, AccessorT&); + //@{ - /// @brief return a pointer to itself + /// @brief Return a pointer to this node. LeafNode* touchLeaf(const Coord&) { return this; } template<typename AccessorT> LeafNode* touchLeafAndCache(const Coord&, AccessorT&) { return this; } LeafNode* probeLeaf(const Coord&) { return this; } template<typename AccessorT> LeafNode* probeLeafAndCache(const Coord&, AccessorT&) { return this; } + template<typename NodeT, typename AccessorT> + NodeT* probeNodeAndCache(const Coord&, AccessorT&) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(boost::is_same<NodeT,LeafNode>::value)) return NULL; + return reinterpret_cast<NodeT*>(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } //@} //@{ - /// @brief return a const pointer to itself + /// @brief Return a @const pointer to this node. const LeafNode* probeLeaf(const Coord&) const { return this; } template<typename AccessorT> const LeafNode* probeLeafAndCache(const Coord&, AccessorT&) const { return this; } const LeafNode* probeConstLeaf(const Coord&) const { return this; } template<typename AccessorT> const LeafNode* probeConstLeafAndCache(const Coord&, AccessorT&) const { return this; } + template<typename NodeT, typename AccessorT> + const NodeT* probeConstNodeAndCache(const Coord&, AccessorT&) const + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(boost::is_same<NodeT,LeafNode>::value)) return NULL; + return reinterpret_cast<const NodeT*>(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } //@} - - void merge(const LeafNode& other, bool, bool) { this->merge(other); } // // Iterators @@ -551,6 +605,13 @@ protected: void setItem(Index pos, bool value) const { this->parent().setValueOnly(pos, value); } // Note: setValue() can't be called on const iterators. void setValue(bool value) const { this->setItem(this->pos(), value); } + + // Note: modifyItem() can't be called on const iterators. + template<typename ModifyOp> + void modifyItem(Index n, const ModifyOp& op) const { this->parent().modifyValue(n, op); } + // Note: modifyValue() can't be called on const iterators. + template<typename ModifyOp> + void modifyValue(const ModifyOp& op) const { this->modifyItem(this->pos(), op); } }; /// Leaf nodes have no children, so their child iterators have no get/set accessors. @@ -588,11 +649,11 @@ protected: }; public: - typedef ValueIter<MaskOnIter, LeafNode, bool> ValueOnIter; + typedef ValueIter<MaskOnIter, LeafNode, const bool> ValueOnIter; typedef ValueIter<MaskOnIter, const LeafNode, const bool> ValueOnCIter; - typedef ValueIter<MaskOffIter, LeafNode, bool> ValueOffIter; + typedef ValueIter<MaskOffIter, LeafNode, const bool> ValueOffIter; typedef ValueIter<MaskOffIter, const LeafNode, const bool> ValueOffCIter; - typedef ValueIter<MaskDenseIter, LeafNode, bool> ValueAllIter; + typedef ValueIter<MaskDenseIter, LeafNode, const bool> ValueAllIter; typedef ValueIter<MaskDenseIter, const LeafNode, const bool> ValueAllCIter; typedef ChildIter<MaskOnIter, LeafNode> ChildOnIter; typedef ChildIter<MaskOnIter, const LeafNode> ChildOnCIter; @@ -707,9 +768,6 @@ private: friend class IteratorBase<MaskDenseIter, LeafNode>; //@} - // Disallow copying. - LeafNode& operator=(const LeafNode&); - }; // class LeafNode<bool> @@ -742,26 +800,31 @@ LeafNode<bool, Log2Dim>::LeafNode(const Coord& xyz, bool value, bool active): template<Index Log2Dim> -template<typename ValueT> inline -LeafNode<bool, Log2Dim>::LeafNode(const LeafNode<ValueT, Log2Dim>& other, TopologyCopy): - mValueMask(other.getValueMask()), - mBuffer(other.getValueMask()), // value = active state - mOrigin(other.getOrigin()) +LeafNode<bool, Log2Dim>::LeafNode(const LeafNode &other): + mValueMask(other.mValueMask), + mBuffer(other.mBuffer), + mOrigin(other.mOrigin) { } +// Copy-construct from a leaf node with the same configuration but a different ValueType. template<Index Log2Dim> template<typename ValueT> inline -LeafNode<bool, Log2Dim>::LeafNode(const LeafNode<ValueT, Log2Dim>& other, - bool offValue, bool onValue, TopologyCopy): +LeafNode<bool, Log2Dim>::LeafNode(const LeafNode<ValueT, Log2Dim>& other): mValueMask(other.getValueMask()), - mBuffer(other.getValueMask()), - mOrigin(other.getOrigin()) + mOrigin(other.origin()) { - if (offValue) { if (!onValue) mBuffer.mData.toggle(); else mBuffer.mData.setOn(); } + struct Local { + /// @todo Consider using a value conversion functor passed as an argument instead. + static inline bool convertValue(const ValueT& val) { return bool(val); } + }; + + for (Index i = 0; i < SIZE; ++i) { + mBuffer.setValue(i, Local::convertValue(other.mBuffer[i])); + } } @@ -772,22 +835,36 @@ LeafNode<bool, Log2Dim>::LeafNode(const LeafNode<ValueT, Log2Dim>& other, bool background, TopologyCopy): mValueMask(other.getValueMask()), mBuffer(background), - mOrigin(other.getOrigin()) + mOrigin(other.origin()) { } template<Index Log2Dim> +template<typename ValueT> inline -LeafNode<bool, Log2Dim>::LeafNode(const LeafNode &other): - mValueMask(other.mValueMask), - mBuffer(other.mBuffer), - mOrigin(other.mOrigin) +LeafNode<bool, Log2Dim>::LeafNode(const LeafNode<ValueT, Log2Dim>& other, TopologyCopy): + mValueMask(other.getValueMask()), + mBuffer(other.getValueMask()), // value = active state + mOrigin(other.origin()) { } template<Index Log2Dim> +template<typename ValueT> +inline +LeafNode<bool, Log2Dim>::LeafNode(const LeafNode<ValueT, Log2Dim>& other, + bool offValue, bool onValue, TopologyCopy): + mValueMask(other.getValueMask()), + mBuffer(other.getValueMask()), + mOrigin(other.origin()) +{ + if (offValue) { if (!onValue) mBuffer.mData.toggle(); else mBuffer.mData.setOn(); } +} + + +template<Index Log2Dim> inline LeafNode<bool, Log2Dim>::~LeafNode() { @@ -807,15 +884,17 @@ LeafNode<bool, Log2Dim>::memUsage() const template<Index Log2Dim> inline void -LeafNode<bool, Log2Dim>::evalActiveVoxelBoundingBox(CoordBBox& bbox) const +LeafNode<bool, Log2Dim>::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const { - const CoordBBox this_bbox = this->getNodeBoundingBox(); - if (bbox.isInside(this_bbox)) { - // nothing to do - } else if (this->isDense()) { + CoordBBox this_bbox = this->getNodeBoundingBox(); + if (bbox.isInside(this_bbox)) return;//this LeafNode is already enclosed in the bbox + if (ValueOnCIter iter = this->cbeginValueOn()) {//any active values? + if (visitVoxels) {//use voxel granularity? + this_bbox.reset(); + for(; iter; ++iter) this_bbox.expand(this->offsetToLocalCoord(iter.pos())); + this_bbox.translate(this->origin()); + } bbox.expand(this_bbox); - } else { - for (ValueOnCIter iter=this->cbeginValueOn(); iter; ++iter) bbox.expand(iter.getCoord()); } } @@ -846,16 +925,18 @@ LeafNode<bool, Log2Dim>::str() const template<Index Log2Dim> inline Index -LeafNode<bool, Log2Dim>::coord2offset(const Coord& xyz) +LeafNode<bool, Log2Dim>::coordToOffset(const Coord& xyz) { - assert ((xyz[0] & DIM-1u) < DIM && (xyz[1] & DIM-1u) < DIM && (xyz[2] & DIM-1u) < DIM); - return ((xyz[0] & DIM-1u) << 2*Log2Dim) + ((xyz[1] & DIM-1u) << Log2Dim) + (xyz[2] & DIM-1u); + assert ((xyz[0] & (DIM-1u)) < DIM && (xyz[1] & (DIM-1u)) < DIM && (xyz[2] & (DIM-1u)) < DIM); + return ((xyz[0] & (DIM-1u)) << 2*Log2Dim) + + ((xyz[1] & (DIM-1u)) << Log2Dim) + + (xyz[2] & (DIM-1u)); } template<Index Log2Dim> inline Coord -LeafNode<bool, Log2Dim>::offset2coord(Index n) +LeafNode<bool, Log2Dim>::offsetToLocalCoord(Index n) { assert(n < (1 << 3*Log2Dim)); Coord xyz; @@ -869,9 +950,9 @@ LeafNode<bool, Log2Dim>::offset2coord(Index n) template<Index Log2Dim> inline Coord -LeafNode<bool, Log2Dim>::offset2globalCoord(Index n) const +LeafNode<bool, Log2Dim>::offsetToGlobalCoord(Index n) const { - return (this->offset2coord(n) + this->getOrigin()); + return (this->offsetToLocalCoord(n) + this->origin()); } @@ -974,7 +1055,9 @@ template<Index Log2Dim> inline bool LeafNode<bool, Log2Dim>::isConstant(bool& constValue, bool& state, bool tolerance) const { - if (!(mValueMask.isOn() || mValueMask.isOff())) return false; + state = mValueMask.isOn(); + + if (!(state || mValueMask.isOff())) return false; // Note: if tolerance is true (i.e., 1), then all boolean values compare equal. if (!tolerance && !(mBuffer.mData.isOn() || mBuffer.mData.isOff())) return false; @@ -989,11 +1072,41 @@ LeafNode<bool, Log2Dim>::isConstant(bool& constValue, bool& state, bool toleranc template<Index Log2Dim> +inline void +LeafNode<bool, Log2Dim>::addTile(Index level, const Coord& xyz, bool val, bool active) +{ + assert(level == 0); + this->addTile(this->coordToOffset(xyz), val, active); +} + +template<Index Log2Dim> +inline void +LeafNode<bool, Log2Dim>::addTile(Index offset, bool val, bool active) +{ + assert(offset < SIZE); + setValueOnly(offset, val); + setActiveState(offset, active); +} + +template<Index Log2Dim> +template<typename AccessorT> +inline void +LeafNode<bool, Log2Dim>::addTileAndCache(Index level, const Coord& xyz, + bool val, bool active, AccessorT&) +{ + this->addTile(level, xyz, val, active); +} + + +//////////////////////////////////////// + + +template<Index Log2Dim> inline const bool& LeafNode<bool, Log2Dim>::getValue(const Coord& xyz) const { // This *CANNOT* use operator ? because Visual C++ - if (mBuffer.mData.isOn(this->coord2offset(xyz))) return sOn; else return sOff; + if (mBuffer.mData.isOn(this->coordToOffset(xyz))) return sOn; else return sOff; } @@ -1011,7 +1124,7 @@ template<Index Log2Dim> inline bool LeafNode<bool, Log2Dim>::probeValue(const Coord& xyz, bool& val) const { - const Index offset = this->coord2offset(xyz); + const Index offset = this->coordToOffset(xyz); val = mBuffer.mData.isOn(offset); return mValueMask.isOn(offset); } @@ -1021,7 +1134,15 @@ template<Index Log2Dim> inline void LeafNode<bool, Log2Dim>::setValueOn(const Coord& xyz, bool val) { - const Index offset = this->coord2offset(xyz); + this->setValueOn(this->coordToOffset(xyz), val); +} + + +template<Index Log2Dim> +inline void +LeafNode<bool, Log2Dim>::setValueOn(Index offset, bool val) +{ + assert(offset < SIZE); mValueMask.setOn(offset); mBuffer.mData.set(offset, val); } @@ -1029,65 +1150,69 @@ LeafNode<bool, Log2Dim>::setValueOn(const Coord& xyz, bool val) template<Index Log2Dim> inline void -LeafNode<bool, Log2Dim>::setActiveState(const Coord& xyz, bool on) +LeafNode<bool, Log2Dim>::setValueOnly(const Coord& xyz, bool val) { - mValueMask.set(this->coord2offset(xyz), on); + this->setValueOnly(this->coordToOffset(xyz), val); } template<Index Log2Dim> inline void -LeafNode<bool, Log2Dim>::setValueOff(const Coord& xyz, bool val) +LeafNode<bool, Log2Dim>::setActiveState(const Coord& xyz, bool on) { - const Index offset = this->coord2offset(xyz); - mValueMask.setOff(offset); - mBuffer.mData.set(offset, val); + mValueMask.set(this->coordToOffset(xyz), on); } template<Index Log2Dim> inline void -LeafNode<bool, Log2Dim>::setValueOnMin(const Coord& xyz, bool val) +LeafNode<bool, Log2Dim>::setValueOff(const Coord& xyz, bool val) { - const Index offset = this->coord2offset(xyz); - mValueMask.setOn(offset); - mBuffer.mData.set(offset, val && mBuffer.mData.isOn(offset)); + this->setValueOff(this->coordToOffset(xyz), val); } template<Index Log2Dim> inline void -LeafNode<bool, Log2Dim>::setValueOnMax(const Coord& xyz, bool val) +LeafNode<bool, Log2Dim>::setValueOff(Index offset, bool val) { - const Index offset = this->coord2offset(xyz); - mValueMask.setOn(offset); - mBuffer.mData.set(offset, val || mBuffer.mData.isOn(offset)); + assert(offset < SIZE); + mValueMask.setOff(offset); + mBuffer.mData.set(offset, val); } template<Index Log2Dim> +template<typename ModifyOp> inline void -LeafNode<bool, Log2Dim>::setValueOnSum(const Coord& xyz, bool val) +LeafNode<bool, Log2Dim>::modifyValue(Index offset, const ModifyOp& op) { - const Index offset = this->coord2offset(xyz); + bool val = mBuffer.mData.isOn(offset); + op(val); + mBuffer.mData.set(offset, val); mValueMask.setOn(offset); - mBuffer.mData.set(offset, val || mBuffer.mData.isOn(offset)); // true + true = true } template<Index Log2Dim> +template<typename ModifyOp> inline void -LeafNode<bool, Log2Dim>::addValue(bool val) +LeafNode<bool, Log2Dim>::modifyValue(const Coord& xyz, const ModifyOp& op) { - if (val) mBuffer.mData |= mValueMask; // set all active voxels to true + this->modifyValue(this->coordToOffset(xyz), op); } template<Index Log2Dim> +template<typename ModifyOp> inline void -LeafNode<bool, Log2Dim>::scaleValue(bool val) +LeafNode<bool, Log2Dim>::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) { - if (!val) mBuffer.mData &= !mValueMask; // set all active voxels to false + const Index offset = this->coordToOffset(xyz); + bool val = mBuffer.mData.isOn(offset), state = mValueMask.isOn(offset); + op(val, state); + mBuffer.mData.set(offset, val); + mValueMask.set(offset, state); } @@ -1107,19 +1232,45 @@ LeafNode<bool, Log2Dim>::resetBackground(bool oldBackground, bool newBackground) } +//////////////////////////////////////// + + template<Index Log2Dim> +template<MergePolicy Policy> inline void -LeafNode<bool, Log2Dim>::merge(const LeafNode& other) +LeafNode<bool, Log2Dim>::merge(const LeafNode& other, bool /*bg*/, bool /*otherBG*/) { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Policy == MERGE_NODES) return; for (typename NodeMaskType::OnIterator iter = other.mValueMask.beginOn(); iter; ++iter) { const Index n = iter.pos(); - if (mValueMask.isOn(n)) continue; - mBuffer.mData.set(n, other.mBuffer.mData.isOn(n)); - mValueMask.setOn(n); + if (mValueMask.isOff(n)) { + mBuffer.mData.set(n, other.mBuffer.mData.isOn(n)); + mValueMask.setOn(n); + } } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + +template<Index Log2Dim> +template<MergePolicy Policy> +inline void +LeafNode<bool, Log2Dim>::merge(bool tileValue, bool tileActive) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Policy != MERGE_ACTIVE_STATES_AND_NODES) return; + if (!tileActive) return; + // Replace all inactive values with the active tile value. + if (tileValue) mBuffer.mData |= !mValueMask; // -0=>1, +0=>0, -1=>1, +1=>1 (-,+ = off,on) + else mBuffer.mData &= mValueMask; // -0=>0, +0=>0, -1=>0, +1=>1 + mValueMask.setOn(); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END } +//////////////////////////////////////// + + template<Index Log2Dim> template<typename OtherType> inline void @@ -1128,6 +1279,7 @@ LeafNode<bool, Log2Dim>::topologyUnion(const LeafNode<OtherType, Log2Dim>& other mValueMask |= other.getValueMask(); } + template<Index Log2Dim> template<typename OtherType> inline void @@ -1137,6 +1289,7 @@ LeafNode<bool, Log2Dim>::topologyIntersection(const LeafNode<OtherType, Log2Dim> mValueMask &= other.getValueMask(); } + template<Index Log2Dim> template<typename OtherType> inline void @@ -1146,16 +1299,20 @@ LeafNode<bool, Log2Dim>::topologyDifference(const LeafNode<OtherType, Log2Dim>& mValueMask &= !other.getValueMask(); } + +//////////////////////////////////////// + + template<Index Log2Dim> inline void LeafNode<bool, Log2Dim>::fill(const CoordBBox& bbox, bool value, bool active) { for (Int32 x = bbox.min().x(); x <= bbox.max().x(); ++x) { - const Index offsetX = (x&DIM-1u)<<2*Log2Dim; + const Index offsetX = (x & (DIM-1u))<<2*Log2Dim; for (Int32 y = bbox.min().y(); y <= bbox.max().y(); ++y) { - const Index offsetXY = offsetX + ((y&DIM-1u)<< Log2Dim); + const Index offsetXY = offsetX + ((y & (DIM-1u))<< Log2Dim); for (Int32 z = bbox.min().z(); z <= bbox.max().z(); ++z) { - const Index offset = offsetXY + (z&DIM-1u); + const Index offset = offsetXY + (z & (DIM-1u)); mValueMask.set(offset, active); mBuffer.mData.set(offset, value); } @@ -1187,18 +1344,20 @@ template<typename DenseT> inline void LeafNode<bool, Log2Dim>::copyToDense(const CoordBBox& bbox, DenseT& dense) const { - const size_t xStride = dense.xStride(), yStride = dense.yStride();// zStride=1 + typedef typename DenseT::ValueType DenseValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); const Coord& min = dense.bbox().min(); - bool* t0 = dense.data() + bbox.min()[2]-min[2];//target array - const Int32 n0 = bbox.min()[2]&DIM-1u; - for (Int32 x = bbox.min()[0], ex=bbox.max()[0]+1; x<ex; ++x) { - bool* t1 = t0 + xStride*(x-min[0]); - const Int32 n1 = n0 + ((x&DIM-1u)<<2*LOG2DIM); - for (Int32 y = bbox.min()[1], ey=bbox.max()[1]+1; y<ey; ++y) { - bool* t2 = t1 + yStride*(y-min[1]); - Int32 n2 = n1 + ((y&DIM-1u)<<LOG2DIM) ; - for (Int32 z = bbox.min()[2], ez=bbox.max()[2]+1; z<ez ; ++z) { - *t2++ = mBuffer.mData.isOn(n2++); + DenseValueType* t0 = dense.data() + zStride * (bbox.min()[2] - min[2]); // target array + const Int32 n0 = bbox.min()[2] & (DIM-1u); + for (Int32 x = bbox.min()[0], ex = bbox.max()[0] + 1; x < ex; ++x) { + DenseValueType* t1 = t0 + xStride * (x - min[0]); + const Int32 n1 = n0 + ((x & (DIM-1u)) << 2*LOG2DIM); + for (Int32 y = bbox.min()[1], ey = bbox.max()[1] + 1; y < ey; ++y) { + DenseValueType* t2 = t1 + yStride * (y - min[1]); + Int32 n2 = n1 + ((y & (DIM-1u)) << LOG2DIM); + for (Int32 z = bbox.min()[2], ez = bbox.max()[2] + 1; z < ez; ++z, t2 += zStride) { + *t2 = DenseValueType(mBuffer.mData.isOn(n2++)); } } } @@ -1211,24 +1370,26 @@ inline void LeafNode<bool, Log2Dim>::copyFromDense(const CoordBBox& bbox, const DenseT& dense, bool background, bool tolerance) { - const size_t xStride = dense.xStride(), yStride = dense.yStride();// zStride=1 + typedef typename DenseT::ValueType DenseValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); const Coord& min = dense.bbox().min(); - const bool* s0 = dense.data() + bbox.min()[2]-min[2];//source - const Int32 n0 = bbox.min()[2]&DIM-1u; - for (Int32 x = bbox.min()[0], ex=bbox.max()[0]+1; x<ex; ++x) { - const bool* s1 = s0 + xStride*(x-min[0]); - const Int32 n1 = n0 + ((x&DIM-1u)<<2*LOG2DIM); - for (Int32 y = bbox.min()[1], ey=bbox.max()[1]+1; y<ey; ++y) { - const bool* s2 = s1 + yStride*(y-min[1]); - Int32 n2 = n1 + ((y&DIM-1u)<<LOG2DIM) ; - for (Int32 z = bbox.min()[2], ez=bbox.max()[2]+1; z<ez ; ++z, ++n2, ++s2) { + const DenseValueType* s0 = dense.data() + zStride * (bbox.min()[2] - min[2]); // source + const Int32 n0 = bbox.min()[2] & (DIM-1u); + for (Int32 x = bbox.min()[0], ex = bbox.max()[0] + 1; x < ex; ++x) { + const DenseValueType* s1 = s0 + xStride * (x - min[0]); + const Int32 n1 = n0 + ((x & (DIM-1u)) << 2*LOG2DIM); + for (Int32 y = bbox.min()[1], ey = bbox.max()[1] + 1; y < ey; ++y) { + const DenseValueType* s2 = s1 + yStride * (y - min[1]); + Int32 n2 = n1 + ((y & (DIM-1u)) << LOG2DIM); + for (Int32 z = bbox.min()[2], ez = bbox.max()[2]+1; z < ez; ++z, ++n2, s2 += zStride) { // Note: if tolerance is true (i.e., 1), then all boolean values compare equal. - if (tolerance || background == *s2) { + if (tolerance || background == bool(*s2)) { mValueMask.setOff(n2); mBuffer.mData.set(n2, background); } else { mValueMask.setOn(n2); - mBuffer.mData.set(n2, *s2); + mBuffer.mData.set(n2, bool(*s2)); } } } @@ -1280,12 +1441,12 @@ LeafNode<bool, Log2Dim>::combine(bool value, bool valueIsActive, CombineOp& op) template<Index Log2Dim> -template<typename CombineOp> +template<typename CombineOp, typename OtherType> inline void -LeafNode<bool, Log2Dim>::combine2(const LeafNode& other, bool value, +LeafNode<bool, Log2Dim>::combine2(const LeafNode& other, const OtherType& value, bool valueIsActive, CombineOp& op) { - CombineArgs<bool> args; + CombineArgs<bool, OtherType> args; args.setBRef(value).setBIsActive(valueIsActive); for (Index i = 0; i < SIZE; ++i) { bool result = false, aVal = other.mBuffer.mData.isOn(i); @@ -1299,12 +1460,12 @@ LeafNode<bool, Log2Dim>::combine2(const LeafNode& other, bool value, template<Index Log2Dim> -template<typename CombineOp> +template<typename CombineOp, typename OtherNodeT> inline void -LeafNode<bool, Log2Dim>::combine2(bool value, const LeafNode& other, +LeafNode<bool, Log2Dim>::combine2(bool value, const OtherNodeT& other, bool valueIsActive, CombineOp& op) { - CombineArgs<bool> args; + CombineArgs<bool, typename OtherNodeT::ValueType> args; args.setARef(value).setAIsActive(valueIsActive); for (Index i = 0; i < SIZE; ++i) { bool result = false, bVal = other.mBuffer.mData.isOn(i); @@ -1318,11 +1479,11 @@ LeafNode<bool, Log2Dim>::combine2(bool value, const LeafNode& other, template<Index Log2Dim> -template<typename CombineOp> +template<typename CombineOp, typename OtherNodeT> inline void -LeafNode<bool, Log2Dim>::combine2(const LeafNode& b0, const LeafNode& b1, CombineOp& op) +LeafNode<bool, Log2Dim>::combine2(const LeafNode& b0, const OtherNodeT& b1, CombineOp& op) { - CombineArgs<bool> args; + CombineArgs<bool, typename OtherNodeT::ValueType> args; for (Index i = 0; i < SIZE; ++i) { // Default behavior: output voxel is active if either input voxel is active. mValueMask.set(i, b0.mValueMask.isOn(i) || b1.mValueMask.isOn(i)); @@ -1349,9 +1510,9 @@ LeafNode<bool, Log2Dim>::visitActiveBBox(BBoxOp& op) const if (op.template descent<LEVEL>()) { for (ValueOnCIter i=this->cbeginValueOn(); i; ++i) { #ifdef _MSC_VER - op.operator()<LEVEL>(CoordBBox(i.getCoord(),1)); + op.operator()<LEVEL>(CoordBBox::createCube(i.getCoord(), 1)); #else - op.template operator()<LEVEL>(CoordBBox(i.getCoord(),1)); + op.template operator()<LEVEL>(CoordBBox::createCube(i.getCoord(), 1)); #endif } } else { diff --git a/extern/openvdb/internal/openvdb/tree/NodeUnion.h b/extern/openvdb/internal/openvdb/tree/NodeUnion.h index f17d32a0a60..c9f8b5a60c1 100644 --- a/extern/openvdb/internal/openvdb/tree/NodeUnion.h +++ b/extern/openvdb/internal/openvdb/tree/NodeUnion.h @@ -33,13 +33,12 @@ /// @author Peter Cucka /// /// NodeUnion is a templated helper class that controls access to either -/// the child node pointer or the fill value for a particular element -/// of a root or internal node. For space efficiency, the child pointer -/// and the fill value are unioned, since the two are never in use -/// simultaneously. Template specializations of NodeUnion allow for -/// fill values of either POD (int, float, pointer, etc.) or class -/// (std::string, math::Vec, etc.) types. (The latter cannot be -/// stored directly in a union.) +/// the child node pointer or the value for a particular element of a root +/// or internal node. For space efficiency, the child pointer and the value +/// are unioned, since the two are never in use simultaneously. +/// Template specializations of NodeUnion allow for values of either POD +/// (int, float, pointer, etc.) or class (std::string, math::Vec, etc.) types. +/// (The latter cannot be stored directly in a union.) #ifndef OPENVDB_TREE_NODEUNION_HAS_BEEN_INCLUDED #define OPENVDB_TREE_NODEUNION_HAS_BEEN_INCLUDED @@ -52,11 +51,11 @@ OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { namespace tree { -// Internal implementation of a union of a child node pointer and a fill value +// Internal implementation of a union of a child node pointer and a value template<bool ValueIsClass, class ValueT, class ChildT> class NodeUnionImpl; -// Partial specialization for fill values of non-class types +// Partial specialization for values of non-class types // (int, float, pointer, etc.) that stores elements by value template<typename ValueT, typename ChildT> class NodeUnionImpl</*ValueIsClass=*/false, ValueT, ChildT> @@ -69,12 +68,13 @@ public: ChildT* getChild() const { return mUnion.child; } const ValueT& getValue() const { return mUnion.value; } + ValueT& getValue() { return mUnion.value; } void setChild(ChildT* child) { mUnion.child = child; } void setValue(const ValueT& val) { mUnion.value = val; } }; -// Partial specialization for fill values of class types (std::string, +// Partial specialization for values of class types (std::string, // math::Vec, etc.) that stores elements by pointer template<typename ValueT, typename ChildT> class NodeUnionImpl</*ValueIsClass=*/true, ValueT, ChildT> @@ -97,7 +97,7 @@ public: } ~NodeUnionImpl() { setChild(NULL); } - ChildT* getChild() const ///< @todo throw if !mHasChild? + ChildT* getChild() const { return mHasChild ? mUnion.child : NULL; } void setChild(ChildT* child) { @@ -107,11 +107,11 @@ public: } const ValueT& getValue() const { return *mUnion.value; } - ///< @todo throw if mHasChild? + ValueT& getValue() { return *mUnion.value; } void setValue(const ValueT& val) { /// @todo To minimize storage across nodes, intern and reuse - /// common fill values, using, e.g., boost::flyweight. + /// common values, using, e.g., boost::flyweight. if (!mHasChild) delete mUnion.value; mUnion.value = new ValueT(val); mHasChild = false; diff --git a/extern/openvdb/internal/openvdb/tree/RootNode.h b/extern/openvdb/internal/openvdb/tree/RootNode.h index 03ecb63b01c..cd68c75b5a7 100644 --- a/extern/openvdb/internal/openvdb/tree/RootNode.h +++ b/extern/openvdb/internal/openvdb/tree/RootNode.h @@ -39,6 +39,10 @@ #include <set> #include <sstream> #include <boost/type_traits/remove_const.hpp> +#include <boost/mpl/vector.hpp>//for boost::mpl::vector +#include <boost/mpl/at.hpp> +#include <boost/mpl/push_back.hpp> +#include <boost/mpl/size.hpp> #include <openvdb/Exceptions.h> #include <openvdb/Types.h> #include <openvdb/io/Compression.h> // for truncateRealToHalf() @@ -54,6 +58,13 @@ OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { namespace tree { +// Forward declarations +template<typename HeadType, int HeadLevel> struct NodeChain; +template<typename, typename> struct SameRootConfig; +template<typename, typename, bool> struct RootNodeCopyHelper; +template<typename, typename, typename, bool> struct RootNodeCombineHelper; + + template<typename ChildType> class RootNode { @@ -64,6 +75,10 @@ public: static const Index LEVEL = 1 + ChildType::LEVEL; // level 0 = leaf + /// NodeChainType is a list of this tree's node types, from LeafNodeType to RootNode. + typedef typename NodeChain<RootNode, LEVEL>::Type NodeChainType; + BOOST_STATIC_ASSERT(boost::mpl::size<NodeChainType>::value == LEVEL + 1); + /// @brief ValueConverter<T>::Type is the type of a RootNode having the same /// child hierarchy as this node but a different value type, T. template<typename OtherValueType> @@ -71,6 +86,14 @@ public: typedef RootNode<typename ChildType::template ValueConverter<OtherValueType>::Type> Type; }; + /// @brief SameConfiguration<OtherNodeType>::value is @c true if and only if + /// OtherNodeType is the type of a RootNode whose ChildNodeType has the same + /// configuration as this node's ChildNodeType. + template<typename OtherNodeType> + struct SameConfiguration { + static const bool value = SameRootConfig<ChildNodeType, OtherNodeType>::value; + }; + /// Construct a new tree with a background value of 0. RootNode(); @@ -80,42 +103,51 @@ public: RootNode(const RootNode& other) { *this = other; } - /// @brief Topology copy constructor that guarantees the - /// configuration of the constructed tree is topologically - /// identical to the other tree - /// - /// @details Reproduce the topology and active states of the other tree - /// (which may have a different ValueType), but don't copy values. - /// All values that are active in the other tree are set to the foreground value - /// and all other values to the background value. + /// @brief Construct a new tree that reproduces the topology and active states + /// of a tree of a different ValueType but the same configuration (levels, + /// node dimensions and branching factors). Cast the other tree's values to + /// this tree's ValueType. + /// @throw TypeError if the other tree's configuration doesn't match this tree's + /// or if this tree's ValueType is not constructible from the other tree's ValueType. + template<typename OtherChildType> + explicit RootNode(const RootNode<OtherChildType>& other) { *this = other; } + + /// @brief Construct a new tree that reproduces the topology and active states of + /// another tree (which may have a different ValueType), but not the other tree's values. + /// @details All tiles and voxels that are active in the other tree are set to + /// @a foreground in the new tree, and all inactive tiles and voxels are set to @a background. /// @param other the root node of a tree having (possibly) a different ValueType /// @param background the value to which inactive tiles and voxels are initialized /// @param foreground the value to which active tiles and voxels are initialized + /// @throw TypeError if the other tree's configuration doesn't match this tree's. template<typename OtherChildType> RootNode(const RootNode<OtherChildType>& other, - const ValueType& background, const ValueType& foreground, - TopologyCopy); + const ValueType& background, const ValueType& foreground, TopologyCopy); - /// @brief Topology copy constructor that guarantees the - /// configuration of the constructed tree is topologically - /// identical to the other tree - /// - /// @note this copy constructor is generally faster then the one - /// above that takes both a forground and a background value. Its - /// main application is multithreading where the topology of - /// the output tree exactly matches the input tree. - /// - /// @details Reproduce the topology and active states of the other node - /// (which may have a different ValueType), but don't copy values. - /// All values in the constructed tree are set to the background value - /// regardless of their active states. + /// @brief Construct a new tree that reproduces the topology and active states of + /// another tree (which may have a different ValueType), but not the other tree's values. + /// All tiles and voxels in the new tree are set to @a background regardless of + /// their active states in the other tree. /// @param other the root node of a tree having (possibly) a different ValueType /// @param background the value to which inactive tiles and voxels are initialized + /// @note This copy constructor is generally faster than the one that takes both + /// a foreground and a background value. Its main application is in multithreaded + /// operations where the topology of the output tree exactly matches the input tree. + /// @throw TypeError if the other tree's configuration doesn't match this tree's. template<typename OtherChildType> - RootNode(const RootNode<OtherChildType>& other, const ValueType& background, - TopologyCopy); + RootNode(const RootNode<OtherChildType>& other, const ValueType& background, TopologyCopy); + /// @brief Copy a root node of the same type as this node. RootNode& operator=(const RootNode& other); + /// @brief Copy a root node of the same tree configuration as this node + /// but a different ValueType. + /// @throw TypeError if the other tree's configuration doesn't match this tree's. + /// @note This node's ValueType must be constructible from the other node's ValueType. + /// For example, a root node with values of type float can be assigned to a root node + /// with values of type Vec3s, because a Vec3s can be constructed from a float. + /// But a Vec3s root node cannot be assigned to a float root node. + template<typename OtherChildType> + RootNode& operator=(const RootNode<OtherChildType>& other); ~RootNode() { this->clearTable(); } @@ -127,7 +159,7 @@ private: bool active; }; - // This lightweight struct pairs child pointers and tiles + // This lightweight struct pairs child pointers and tiles. struct NodeStruct { ChildType* child; Tile tile; @@ -297,6 +329,13 @@ private: ValueT* operator->() const { return &(this->getValue()); } void setValue(const ValueT& v) const { assert(isTile(mIter)); getTile(mIter).value = v; } + + template<typename ModifyOp> + void modifyValue(const ModifyOp& op) const + { + assert(isTile(mIter)); + op(getTile(mIter).value); + } }; // ValueIter template<typename RootNodeT, typename MapIterT, typename ChildNodeT, typename ValueT> @@ -384,8 +423,11 @@ public: Index64 memUsage() const; /// @brief Expand the specified bbox so it includes the active tiles of - /// this root node as well as all the active values in its child nodes. - void evalActiveVoxelBoundingBox(CoordBBox& bbox) const; + /// this root node as well as all the active values in its child + /// nodes. If visitVoxels is false LeafNodes will be approximated + /// as dense, i.e. with all voxels active. Else the individual + /// active voxels are visited to produce a tight bbox. + void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) const; /// Return the bounding box of this RootNode, i.e., an infinite bounding box. static CoordBBox getNodeBoundingBox() { return CoordBBox::inf(); } @@ -393,11 +435,14 @@ public: /// @brief Change inactive tiles or voxels with a value equal to +/- the /// old background to the specified value (with the same sign). Active values /// are unchanged. - void setBackground(const ValueType& value); - /// Return the background value + /// @param value The new background value + /// @param updateChildNodes If true (which is the default) the + /// background values of the child nodes is also updated. Else + /// only the background value stored in the RootNode itself is changed. + void setBackground(const ValueType& value, bool updateChildNodes = true); + + /// Return this node's background value. const ValueType& background() const { return mBackground; } - /// @deprecated Use background() - OPENVDB_DEPRECATED ValueType getBackground() const { return mBackground; } /// Return @c true if the given tile is inactive and has the background value. bool isBackgroundTile(const Tile&) const; @@ -449,12 +494,18 @@ public: template<typename OtherChildType> static bool hasSameConfiguration(const RootNode<OtherChildType>& other); + /// Return @c true if values of the other node's ValueType can be converted + /// to values of this node's ValueType. + template<typename OtherChildType> + static bool hasCompatibleValueType(const RootNode<OtherChildType>& other); + Index32 leafCount() const; Index32 nonLeafCount() const; Index64 onVoxelCount() const; Index64 offVoxelCount() const; Index64 onLeafVoxelCount() const; Index64 offLeafVoxelCount() const; + Index64 onTileCount() const; bool isValueOn(const Coord& xyz) const; @@ -468,37 +519,42 @@ public: /// it is implicitly a background voxel), return -1. int getValueDepth(const Coord& xyz) const; - /// Set the active state of the voxel at the given coordinates, but don't change its value. + /// Set the active state of the voxel at the given coordinates but don't change its value. void setActiveState(const Coord& xyz, bool on); - - /// Mark the voxel at the given coordinates as inactive, but don't change its value. + /// Set the value of the voxel at the given coordinates but don't change its active state. + void setValueOnly(const Coord& xyz, const ValueType& value); + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValueOn(const Coord& xyz, const ValueType& value); + /// Mark the voxel at the given coordinates as inactive but don't change its value. void setValueOff(const Coord& xyz); - /// Change the value of the voxel at the given coordinates and mark the voxel as inactive. + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. void setValueOff(const Coord& xyz, const ValueType& value); - void setValueOn(const Coord& xyz, const ValueType& value); - void setValueOnly(const Coord& xyz, const ValueType& value); - void setValueOnMin(const Coord& xyz, const ValueType& value); - void setValueOnMax(const Coord& xyz, const ValueType& value); - void setValueOnSum(const Coord& xyz, const ValueType& value); + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + template<typename ModifyOp> + void modifyValue(const Coord& xyz, const ModifyOp& op); + /// Apply a functor to the voxel at the given coordinates. + template<typename ModifyOp> + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op); /// @brief Set all voxels within a given box to a constant value, if necessary /// subdividing tiles that intersect the box. - /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box - /// @param value the value to which to set voxels within the box - /// @param active if true, mark voxels within the box as active, - /// otherwise mark them as inactive + /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box + /// @param value the value to which to set voxels within the box + /// @param active if true, mark voxels within the box as active, + /// otherwise mark them as inactive void fill(const CoordBBox& bbox, const ValueType& value, bool active = true); /// @brief Copy into a dense grid the values of all voxels, both active and inactive, /// that intersect a given bounding box. - /// /// @param bbox inclusive bounding box of the voxels to be copied into the dense grid /// @param dense dense grid with a stride in @e z of one (see tools::Dense /// in tools/Dense.h for the required API) template<typename DenseT> void copyToDense(const CoordBBox& bbox, DenseT& dense) const; + // // I/O // @@ -508,6 +564,10 @@ public: void writeBuffers(std::ostream&, bool toHalf = false) const; void readBuffers(std::istream&, bool fromHalf = false); + + // + // Voxel access + // /// Return the value of the voxel at the given coordinates and, if necessary, update /// the accessor with pointers to the nodes along the path from the root node to /// the node containing the voxel. @@ -528,20 +588,27 @@ public: template<typename AccessorT> void setValueAndCache(const Coord& xyz, const ValueType& value, AccessorT&); - /// Set the value of the voxel at the given coordinate but preserves its active state. + /// Set the value of the voxel at the given coordinates without changing its active state. /// If necessary, update the accessor with pointers to the nodes along the path /// from the root node to the node containing the voxel. /// @note Used internally by ValueAccessor. template<typename AccessorT> void setValueOnlyAndCache(const Coord& xyz, const ValueType& value, AccessorT&); - /// Set the value of the voxel at the given coordinates to the sum of its current - /// value and the given value, and mark the voxel as active. + /// Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. /// If necessary, update the accessor with pointers to the nodes along the path /// from the root node to the node containing the voxel. /// @note Used internally by ValueAccessor. - template<typename AccessorT> - void setValueOnSumAndCache(const Coord& xyz, const ValueType& value, AccessorT&); + template<typename ModifyOp, typename AccessorT> + void modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&); + + /// Apply a functor to the voxel at the given coordinates. + /// If necessary, update the accessor with pointers to the nodes along the path + /// from the root node to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template<typename ModifyOp, typename AccessorT> + void modifyValueAndActiveStateAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&); /// Change the value of the voxel at the given coordinates and mark it as inactive. /// If necessary, update the accessor with pointers to the nodes along the path @@ -557,9 +624,10 @@ public: template<typename AccessorT> void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&); - /// Return @c true if the voxel at the given coordinates is active, assigns the voxel - /// value, and, if necessary, update the accessor with pointers to the nodes along + /// Return, in @a value, the value of the voxel at the given coordinates and, + /// if necessary, update the accessor with pointers to the nodes along /// the path from the root node to the node containing the voxel. + /// @return @c true if the voxel at the given coordinates is active /// @note Used internally by ValueAccessor. template<typename AccessorT> bool probeValueAndCache(const Coord& xyz, ValueType& value, AccessorT&) const; @@ -593,13 +661,16 @@ public: /// background tiles any nodes whose values are all inactive. void pruneInactive(); - void pruneTiles(const ValueType&); + /// @brief Reduce the memory footprint of this tree by replacing with tiles + /// any non-leaf nodes whose values are all the same (optionally to within a tolerance) + /// and have the same active state. + void pruneTiles(const ValueType& tolerance); - /// @brief Add the specified leaf to this node, possibly creating a child branch - /// in the process. If the leaf node already exists, replace it. + /// @brief Add the given leaf node to this tree, creating a new branch if necessary. + /// If a leaf node with the same origin already exists, replace it. void addLeaf(LeafNodeType* leaf); - /// @brief Same as addLeaf except, if necessary, it update the accessor with pointers + /// @brief Same as addLeaf() but, if necessary, update the given accessor with pointers /// to the nodes along the path from the root node to the node containing the coordinate. template<typename AccessorT> void addLeafAndCache(LeafNodeType* leaf, AccessorT&); @@ -615,78 +686,94 @@ public: template<typename NodeT> NodeT* stealNode(const Coord& xyz, const ValueType& value, bool state); - /// @brief Add a tile at the specified tree level that contains voxel (x, y, z), - /// possibly creating a parent branch or deleting a child branch in the process. + /// @brief Add a tile containing voxel (x, y, z) at the specified tree level, + /// creating a new branch if necessary. Delete any existing lower-level nodes + /// that contain (x, y, z). void addTile(Index level, const Coord& xyz, const ValueType& value, bool state); - /// @brief Same as addTile() except, if necessary, update the accessor with pointers - /// to the nodes along the path from the root node to the node containing (x, y, z). + /// @brief Same as addTile() but, if necessary, update the given accessor with pointers + /// to the nodes along the path from the root node to the node containing the coordinate. template<typename AccessorT> - void addTileAndCache(Index level, const Coord& xyz, const ValueType& value, - bool state, AccessorT&); + void addTileAndCache(Index level, const Coord& xyz, const ValueType&, bool state, AccessorT&); - /// @brief Return the leaf node that contains voxel (x, y, z). - /// If no such node exists, create one, but preserve the values and + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). + /// If no such node exists, create one that preserves the values and /// active states of all voxels. - /// /// @details Use this method to preallocate a static tree topology /// over which to safely perform multithreaded processing. LeafNodeType* touchLeaf(const Coord& xyz); - /// @brief Same as touchLeaf except, if necessary, it update the accessor with pointers + /// @brief Same as touchLeaf() but, if necessary, update the given accessor with pointers /// to the nodes along the path from the root node to the node containing the coordinate. template<typename AccessorT> LeafNodeType* touchLeafAndCache(const Coord& xyz, AccessorT& acc); - /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). + //@{ + /// @brief Return a pointer to the node that contains voxel (x, y, z). /// If no such node exists, return NULL. - LeafNodeType* probeLeaf(const Coord& xyz); + template <typename NodeT> + NodeT* probeNode(const Coord& xyz); + template <typename NodeT> + const NodeT* probeConstNode(const Coord& xyz) const; + //@} + + //@{ + /// @brief Same as probeNode() but, if necessary, update the given accessor with pointers + /// to the nodes along the path from the root node to the node containing the coordinate. + template<typename NodeT, typename AccessorT> + NodeT* probeNodeAndCache(const Coord& xyz, AccessorT& acc); + template<typename NodeT, typename AccessorT> + const NodeT* probeConstNodeAndCache(const Coord& xyz, AccessorT& acc) const; + //@} - /// @brief Return a const pointer to the leaf node that contains voxel (x, y, z). + //@{ + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). /// If no such node exists, return NULL. + LeafNodeType* probeLeaf(const Coord& xyz); const LeafNodeType* probeConstLeaf(const Coord& xyz) const; - const LeafNodeType* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); } + const LeafNodeType* probeLeaf(const Coord& xyz) const; + //@} - /// @brief Same as probeLeaf except, if necessary, it update the accessor with pointers + //@{ + /// @brief Same as probeLeaf() but, if necessary, update the given accessor with pointers /// to the nodes along the path from the root node to the node containing the coordinate. template<typename AccessorT> LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc); - - /// @brief Same as probeConstLeaf except, if necessary, it update the accessor with pointers - /// to the nodes along the path from the root node to the node containing the coordinate. template<typename AccessorT> const LeafNodeType* probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const; template<typename AccessorT> - const LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc) const - { - return this->probeConstLeafAndCache(xyz, acc); - } + const LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc) const; + //@} + + // + // Aux methods + // /// @brief Set the values of all inactive voxels and tiles of a narrow-band /// level set from the signs of the active voxels, setting outside values to /// +background and inside values to -background. - /// - /// @note This method should only be used on closed, narrow-band level sets! + /// @warning This method should only be used on closed, narrow-band level sets. void signedFloodFill(); /// @brief Set the values of all inactive voxels and tiles of a narrow-band /// level set from the signs of the active voxels, setting exterior values to /// @a outside and interior values to @a inside. Set the background value /// of this tree to @a outside. - /// - /// @note This method should only be used on closed, narrow-band level sets! + /// @warning This method should only be used on closed, narrow-band level sets. void signedFloodFill(const ValueType& outside, const ValueType& inside); - /// Move child nodes from the other tree into this tree wherever those nodes - /// correspond to constant-value tiles in this tree, and replace leaf-level - /// inactive voxels in this tree with corresponding voxels in the other tree - /// that are active. - /// @note This operation always empties the other tree, whether or not any nodes were moved. - void merge(RootNode& other); - - /// Turn active tiles into dense voxels, i.e., into leaf nodes that are entirely active. + /// Densify active tiles, i.e., replace them with leaf-level active voxels. void voxelizeActiveTiles(); + /// @brief Efficiently merge another tree into this tree using one of several schemes. + /// @details This operation is primarily intended to combine trees that are mostly + /// non-overlapping (for example, intermediate trees from computations that are + /// parallelized across disjoint regions of space). + /// @note This operation is not guaranteed to produce an optimally sparse tree. + /// Follow merge() with prune() for optimal sparseness. + /// @warning This operation always empties the other tree. + template<MergePolicy Policy> void merge(RootNode& other); + /// @brief Union this tree's set of active values with the active values /// of the other tree, whose @c ValueType may be different. /// @details The resulting state of a value is active if the corresponding value @@ -703,18 +790,47 @@ public: template<typename OtherChildType> void topologyUnion(const RootNode<OtherChildType>& other); + /// @brief Intersects this tree's set of active values with the active values + /// of the other tree, whose @c ValueType may be different. + /// @details The resulting state of a value is active only if the corresponding + /// value was already active AND if it is active in the other tree. Also, a + /// resulting value maps to a voxel if the corresponding value + /// already mapped to an active voxel in either of the two grids + /// and it maps to an active tile or voxel in the other grid. + /// + /// @note This operation can delete branches in this grid if they + /// overlap with inactive tiles in the other grid. Likewise active + /// voxels can be turned into inactive voxels resulting in leaf + /// nodes with no active values. Thus, it is recommended to + /// subsequently call prune. + template<typename OtherChildType> + void topologyIntersection(const RootNode<OtherChildType>& other); + + /// @brief Difference this tree's set of active values with the active values + /// of the other tree, whose @c ValueType may be different. So a + /// resulting voxel will be active only if the original voxel is + /// active in this tree and inactive in the other tree. + /// + /// @note This operation can delete branches in this grid if they + /// overlap with active tiles in the other grid. Likewise active + /// voxels can be turned into inactive voxels resulting in leaf + /// nodes with no active values. Thus, it is recommended to + /// subsequently call prune. + template<typename OtherChildType> + void topologyDifference(const RootNode<OtherChildType>& other); + template<typename CombineOp> void combine(RootNode& other, CombineOp&, bool prune = false); - template<typename CombineOp> - void combine2(const RootNode& other0, const RootNode& other1, + template<typename CombineOp, typename OtherRootNode /*= RootNode*/> + void combine2(const RootNode& other0, const OtherRootNode& other1, CombineOp& op, bool prune = false); - /// @brief Calls the templated functor BBoxOp with bounding box + /// @brief Call the templated functor BBoxOp with bounding box /// information for all active tiles and leaf nodes in the tree. /// An additional level argument is provided for each callback. /// - /// @note The bounding boxes are guarenteed to be non-overlapping. + /// @note The bounding boxes are guaranteed to be non-overlapping. template<typename BBoxOp> void visitActiveBBox(BBoxOp&) const; template<typename VisitorOp> void visit(VisitorOp&); @@ -730,6 +846,9 @@ private: /// to protected/private members of other template instances. template<typename> friend class RootNode; + template<typename, typename, bool> friend struct RootNodeCopyHelper; + template<typename, typename, typename, bool> friend struct RootNodeCombineHelper; + /// Currently no-op, but can be used to define empty and delete keys for mTable void initTable() {} inline void clearTable(); @@ -769,13 +888,24 @@ private: /// @return an iterator pointing to the matching mTable entry. MapIter findOrAddCoord(const Coord& xyz); - template<typename NodeT> - NodeT* doStealNode(const Coord&, const ValueType&, bool state); - - /// @throw TypeError if the other node's dimensions don't match this node's. + /// @brief Verify that the tree rooted at @a other has the same configuration + /// (levels, branching factors and node dimensions) as this tree, but allow + /// their ValueTypes to differ. + /// @throw TypeError if the other tree's configuration doesn't match this tree's. template<typename OtherChildType> static void enforceSameConfiguration(const RootNode<OtherChildType>& other); + /// @brief Verify that @a other has values of a type that can be converted + /// to this node's ValueType. + /// @details For example, values of type float are compatible with values of type Vec3s, + /// because a Vec3s can be constructed from a float. But the reverse is not true. + /// @throw TypeError if the other node's ValueType is not convertible into this node's. + template<typename OtherChildType> + static void enforceCompatibleValueTypes(const RootNode<OtherChildType>& other); + + template<typename CombineOp, typename OtherRootNode /*= RootNode*/> + void doCombine2(const RootNode&, const OtherRootNode&, CombineOp&, bool prune); + template<typename RootNodeT, typename VisitorOp, typename ChildAllIterT> static inline void doVisit(RootNodeT&, VisitorOp&); @@ -792,6 +922,60 @@ private: //////////////////////////////////////// +/// @brief NodeChain<RootNodeType, RootNodeType::LEVEL>::Type is a boost::mpl::vector +/// that lists the types of the nodes of the tree rooted at RootNodeType in reverse order, +/// from LeafNode to RootNode. +/// @details For example, if RootNodeType is +/// @code +/// RootNode<InternalNode<InternalNode<LeafNode> > > +/// @endcode +/// then NodeChain::Type is +/// @code +/// boost::mpl::vector< +/// LeafNode, +/// InternalNode<LeafNode>, +/// InternalNode<InternalNode<LeafNode> >, +/// RootNode<InternalNode<InternalNode<LeafNode> > > > +/// @endcode +/// +/// @note Use the following to get the Nth node type, where N=0 is the LeafNodeType: +/// @code +/// boost::mpl::at<NodeChainType, boost::mpl::int_<N> >::type +/// @endcode +template<typename HeadT, int HeadLevel> +struct NodeChain { + typedef typename NodeChain<typename HeadT::ChildNodeType, HeadLevel-1>::Type SubtreeT; + typedef typename boost::mpl::push_back<SubtreeT, HeadT>::type Type; +}; + +/// Specialization to terminate NodeChain +template<typename HeadT> +struct NodeChain<HeadT, /*HeadLevel=*/1> { + typedef typename boost::mpl::vector<typename HeadT::ChildNodeType, HeadT>::type Type; +}; + + +//////////////////////////////////////// + + +//@{ +/// Helper metafunction used to implement RootNode::SameConfiguration +/// (which, as an inner class, can't be independently specialized) +template<typename ChildT1, typename NodeT2> +struct SameRootConfig { + static const bool value = false; +}; + +template<typename ChildT1, typename ChildT2> +struct SameRootConfig<ChildT1, RootNode<ChildT2> > { + static const bool value = ChildT1::template SameConfiguration<ChildT2>::value; +}; +//@} + + +//////////////////////////////////////// + + template<typename ChildT> inline RootNode<ChildT>::RootNode(): mBackground(zeroVal<ValueType>()) @@ -802,7 +986,7 @@ RootNode<ChildT>::RootNode(): mBackground(zeroVal<ValueType>()) template<typename ChildT> inline -RootNode<ChildT>::RootNode(const ValueType& background) : mBackground(background) +RootNode<ChildT>::RootNode(const ValueType& background): mBackground(background) { this->initTable(); } @@ -817,7 +1001,6 @@ RootNode<ChildT>::RootNode(const RootNode<OtherChildType>& other, { typedef RootNode<OtherChildType> OtherRootT; - /// @todo Can this be avoided with partial specialization? enforceSameConfiguration(other); const Tile bgTile(backgd, /*active=*/false), fgTile(foregd, true); @@ -840,7 +1023,6 @@ RootNode<ChildT>::RootNode(const RootNode<OtherChildType>& other, { typedef RootNode<OtherChildType> OtherRootT; - /// @todo Can this be avoided with partial specialization? enforceSameConfiguration(other); const Tile bgTile(backgd, /*active=*/false), fgTile(backgd, true); @@ -853,45 +1035,127 @@ RootNode<ChildT>::RootNode(const RootNode<OtherChildType>& other, } +//////////////////////////////////////// + + +// This helper class is a friend of RootNode and is needed so that assignment +// with value conversion can be specialized for compatible and incompatible +// pairs of RootNode types. +template<typename RootT, typename OtherRootT, bool Compatible = false> +struct RootNodeCopyHelper +{ + static inline void copyWithValueConversion(RootT& self, const OtherRootT& other) + { + // If the two root nodes have different configurations or incompatible ValueTypes, + // throw an exception. + self.enforceSameConfiguration(other); + self.enforceCompatibleValueTypes(other); + // One of the above two tests should throw, so we should never get here: + std::ostringstream ostr; + ostr << "cannot convert a " << typeid(OtherRootT).name() + << " to a " << typeid(RootT).name(); + OPENVDB_THROW(TypeError, ostr.str()); + } +}; + +// Specialization for root nodes of compatible types +template<typename RootT, typename OtherRootT> +struct RootNodeCopyHelper<RootT, OtherRootT, /*Compatible=*/true> +{ + static inline void copyWithValueConversion(RootT& self, const OtherRootT& other) + { + typedef typename RootT::ValueType ValueT; + typedef typename RootT::ChildNodeType ChildT; + typedef typename RootT::NodeStruct NodeStruct; + typedef typename RootT::Tile Tile; + typedef typename OtherRootT::ValueType OtherValueT; + typedef typename OtherRootT::ChildNodeType OtherChildT; + typedef typename OtherRootT::MapCIter OtherMapCIter; + typedef typename OtherRootT::Tile OtherTile; + + struct Local { + /// @todo Consider using a value conversion functor passed as an argument instead. + static inline ValueT convertValue(const OtherValueT& val) { return ValueT(val); } + }; + + self.mBackground = Local::convertValue(other.mBackground); + + self.clearTable(); + self.initTable(); + + for (OtherMapCIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { + if (other.isTile(i)) { + // Copy the other node's tile, but convert its value to this node's ValueType. + const OtherTile& otherTile = other.getTile(i); + self.mTable[i->first] = NodeStruct( + Tile(Local::convertValue(otherTile.value), otherTile.active)); + } else { + // Copy the other node's child, but convert its values to this node's ValueType. + self.mTable[i->first] = NodeStruct(*(new ChildT(other.getChild(i)))); + } + } + } +}; + + +// Overload for root nodes of the same type as this node template<typename ChildT> inline RootNode<ChildT>& RootNode<ChildT>::operator=(const RootNode& other) { - mBackground = other.mBackground; + if (&other != this) { + mBackground = other.mBackground; - this->clearTable(); - this->initTable(); + this->clearTable(); + this->initTable(); - for (MapCIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { - mTable[i->first] = - isTile(i) ? NodeStruct(getTile(i)) : NodeStruct(*(new ChildT(getChild(i)))); + for (MapCIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { + mTable[i->first] = + isTile(i) ? NodeStruct(getTile(i)) : NodeStruct(*(new ChildT(getChild(i)))); + } } return *this; } +// Overload for root nodes of different types +template<typename ChildT> +template<typename OtherChildType> +inline RootNode<ChildT>& +RootNode<ChildT>::operator=(const RootNode<OtherChildType>& other) +{ + typedef RootNode<OtherChildType> OtherRootT; + typedef typename OtherRootT::ValueType OtherValueT; + static const bool compatible = (SameConfiguration<OtherRootT>::value + && CanConvertType</*from=*/OtherValueT, /*to=*/ValueType>::value); + RootNodeCopyHelper<RootNode, OtherRootT, compatible>::copyWithValueConversion(*this, other); + return *this; +} + //////////////////////////////////////// template<typename ChildT> inline void -RootNode<ChildT>::setBackground(const ValueType& background) +RootNode<ChildT>::setBackground(const ValueType& background, bool updateChildNodes) { if (math::isExactlyEqual(background, mBackground)) return; - // Traverse the tree, replacing occurrences of mBackground with background - // and -mBackground with -background. - for (MapIter iter=mTable.begin(); iter!=mTable.end(); ++iter) { - ChildT *child = iter->second.child; - if (child) { - child->resetBackground(/*old=*/mBackground, /*new=*/background); - } else { - Tile& tile = getTile(iter); - if (tile.active) continue;//only change inactive tiles - if (math::isApproxEqual(tile.value, mBackground)) { - tile.value = background; - } else if (math::isApproxEqual(tile.value, negative(mBackground))) { - tile.value = negative(background); + if (updateChildNodes) { + // Traverse the tree, replacing occurrences of mBackground with background + // and -mBackground with -background. + for (MapIter iter=mTable.begin(); iter!=mTable.end(); ++iter) { + ChildT *child = iter->second.child; + if (child) { + child->resetBackground(/*old=*/mBackground, /*new=*/background); + } else { + Tile& tile = getTile(iter); + if (tile.active) continue;//only change inactive tiles + if (math::isApproxEqual(tile.value, mBackground)) { + tile.value = background; + } else if (math::isApproxEqual(tile.value, math::negative(mBackground))) { + tile.value = math::negative(background); + } } } } @@ -1101,6 +1365,31 @@ RootNode<ChildT>::enforceSameConfiguration(const RootNode<OtherChildType>&) } +template<typename ChildT> +template<typename OtherChildType> +inline bool +RootNode<ChildT>::hasCompatibleValueType(const RootNode<OtherChildType>&) +{ + typedef typename OtherChildType::ValueType OtherValueType; + return CanConvertType</*from=*/OtherValueType, /*to=*/ValueType>::value; +} + + +template<typename ChildT> +template<typename OtherChildType> +inline void +RootNode<ChildT>::enforceCompatibleValueTypes(const RootNode<OtherChildType>&) +{ + typedef typename OtherChildType::ValueType OtherValueType; + if (!CanConvertType</*from=*/OtherValueType, /*to=*/ValueType>::value) { + std::ostringstream ostr; + ostr << "values of type " << typeNameAsString<OtherValueType>() + << " cannot be converted to type " << typeNameAsString<ValueType>(); + OPENVDB_THROW(TypeError, ostr.str()); + } +} + + //////////////////////////////////////// @@ -1117,28 +1406,29 @@ RootNode<ChildT>::memUsage() const return sum; } + template<typename ChildT> inline void -RootNode<ChildT>::evalActiveVoxelBoundingBox(CoordBBox& bbox) const +RootNode<ChildT>::clearTable() { - for (MapCIter iter=mTable.begin(); iter!=mTable.end(); ++iter) { - if (const ChildT *child = iter->second.child) { - child->evalActiveVoxelBoundingBox(bbox); - } else if (isTileOn(iter)) { - bbox.expand(iter->first, ChildT::DIM); - } + for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + delete i->second.child; } + mTable.clear(); } template<typename ChildT> inline void -RootNode<ChildT>::clearTable() +RootNode<ChildT>::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const { - for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { - delete i->second.child; + for (MapCIter iter=mTable.begin(); iter!=mTable.end(); ++iter) { + if (const ChildT *child = iter->second.child) { + child->evalActiveBoundingBox(bbox, visitVoxels); + } else if (isTileOn(iter)) { + bbox.expand(iter->first, ChildT::DIM); + } } - mTable.clear(); } @@ -1270,6 +1560,20 @@ RootNode<ChildT>::offLeafVoxelCount() const return sum; } +template<typename ChildT> +inline Index64 +RootNode<ChildT>::onTileCount() const +{ + Index64 sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) { + sum += getChild(i).onTileCount(); + } else if (isTileOn(i)) { + sum += 1; + } + } + return sum; +} //////////////////////////////////////// @@ -1543,8 +1847,9 @@ RootNode<ChildT>::setValueOnlyAndCache(const Coord& xyz, const ValueType& value, template<typename ChildT> +template<typename ModifyOp> inline void -RootNode<ChildT>::setValueOnMin(const Coord& xyz, const ValueType& value) +RootNode<ChildT>::modifyValue(const Coord& xyz, const ModifyOp& op) { ChildT* child = NULL; MapIter iter = this->findCoord(xyz); @@ -1553,17 +1858,30 @@ RootNode<ChildT>::setValueOnMin(const Coord& xyz, const ValueType& value) mTable[this->coordToKey(xyz)] = NodeStruct(*child); } else if (isChild(iter)) { child = &getChild(iter); - } else if (isTileOff(iter) || getTile(iter).value > value) { - child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); - setChild(iter, *child); + } else { + // Need to create a child if the tile is inactive, + // in order to activate voxel (x, y, z). + bool createChild = isTileOff(iter); + if (!createChild) { + // Need to create a child if applying the functor + // to the tile value produces a different value. + const ValueType& tileVal = getTile(iter).value; + ValueType modifiedVal = tileVal; + op(modifiedVal); + createChild = !math::isExactlyEqual(tileVal, modifiedVal); + } + if (createChild) { + child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + setChild(iter, *child); + } } - if (child) child->setValueOnMin(xyz, value); + if (child) child->modifyValue(xyz, op); } - template<typename ChildT> +template<typename ModifyOp, typename AccessorT> inline void -RootNode<ChildT>::setValueOnMax(const Coord& xyz, const ValueType& value) +RootNode<ChildT>::modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT& acc) { ChildT* child = NULL; MapIter iter = this->findCoord(xyz); @@ -1572,17 +1890,34 @@ RootNode<ChildT>::setValueOnMax(const Coord& xyz, const ValueType& value) mTable[this->coordToKey(xyz)] = NodeStruct(*child); } else if (isChild(iter)) { child = &getChild(iter); - } else if (isTileOff(iter) || getTile(iter).value < value) { - child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); - setChild(iter, *child); + } else { + // Need to create a child if the tile is inactive, + // in order to activate voxel (x, y, z). + bool createChild = isTileOff(iter); + if (!createChild) { + // Need to create a child if applying the functor + // to the tile value produces a different value. + const ValueType& tileVal = getTile(iter).value; + ValueType modifiedVal = tileVal; + op(modifiedVal); + createChild = !math::isExactlyEqual(tileVal, modifiedVal); + } + if (createChild) { + child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + setChild(iter, *child); + } + } + if (child) { + acc.insert(xyz, child); + child->modifyValueAndCache(xyz, op, acc); } - if (child) child->setValueOnMax(xyz, value); } template<typename ChildT> +template<typename ModifyOp> inline void -RootNode<ChildT>::setValueOnSum(const Coord& xyz, const ValueType& addend) +RootNode<ChildT>::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) { ChildT* child = NULL; MapIter iter = this->findCoord(xyz); @@ -1591,18 +1926,26 @@ RootNode<ChildT>::setValueOnSum(const Coord& xyz, const ValueType& addend) mTable[this->coordToKey(xyz)] = NodeStruct(*child); } else if (isChild(iter)) { child = &getChild(iter); - } else if (isTileOff(iter) || !math::isZero(addend)) { - child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); - setChild(iter, *child); + } else { + const Tile& tile = getTile(iter); + bool modifiedState = tile.active; + ValueType modifiedVal = tile.value; + op(modifiedVal, modifiedState); + // Need to create a child if applying the functor to the tile + // produces a different value or active state. + if (modifiedState != tile.active || !math::isExactlyEqual(modifiedVal, tile.value)) { + child = new ChildT(xyz, tile.value, tile.active); + setChild(iter, *child); + } } - if (child) child->setValueOnSum(xyz, addend); + if (child) child->modifyValueAndActiveState(xyz, op); } template<typename ChildT> -template<typename AccessorT> +template<typename ModifyOp, typename AccessorT> inline void -RootNode<ChildT>::setValueOnSumAndCache(const Coord& xyz, - const ValueType& addend, AccessorT& acc) +RootNode<ChildT>::modifyValueAndActiveStateAndCache( + const Coord& xyz, const ModifyOp& op, AccessorT& acc) { ChildT* child = NULL; MapIter iter = this->findCoord(xyz); @@ -1611,13 +1954,21 @@ RootNode<ChildT>::setValueOnSumAndCache(const Coord& xyz, mTable[this->coordToKey(xyz)] = NodeStruct(*child); } else if (isChild(iter)) { child = &getChild(iter); - } else if (isTileOff(iter) || !math::isZero(addend)) { - child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); - setChild(iter, *child); + } else { + const Tile& tile = getTile(iter); + bool modifiedState = tile.active; + ValueType modifiedVal = tile.value; + op(modifiedVal, modifiedState); + // Need to create a child if applying the functor to the tile + // produces a different value or active state. + if (modifiedState != tile.active || !math::isExactlyEqual(modifiedVal, tile.value)) { + child = new ChildT(xyz, tile.value, tile.active); + setChild(iter, *child); + } } if (child) { acc.insert(xyz, child); - child->setValueOnSumAndCache(xyz, addend, acc); + child->modifyValueAndActiveStateAndCache(xyz, op, acc); } } @@ -1718,7 +2069,9 @@ template<typename DenseT> inline void RootNode<ChildT>::copyToDense(const CoordBBox& bbox, DenseT& dense) const { - const size_t xStride = dense.xStride(), yStride = dense.yStride();// zStride=1 + typedef typename DenseT::ValueType DenseValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); const Coord& min = dense.bbox().min(); CoordBBox nodeBBox; for (Coord xyz = bbox.min(); xyz[0] <= bbox.max()[0]; xyz[0] = nodeBBox.max()[0] + 1) { @@ -1737,12 +2090,14 @@ RootNode<ChildT>::copyToDense(const CoordBBox& bbox, DenseT& dense) const } else {//is background or a tile value const ValueType value = iter==mTable.end() ? mBackground : getTile(iter).value; sub.translate(-min); - ValueType* a0 = dense.data() + sub.min()[2]; + DenseValueType* a0 = dense.data() + zStride*sub.min()[2]; for (Int32 x=sub.min()[0], ex=sub.max()[0]+1; x<ex; ++x) { - ValueType* a1 = a0 + x*xStride; + DenseValueType* a1 = a0 + x*xStride; for (Int32 y=sub.min()[1], ey=sub.max()[1]+1; y<ey; ++y) { - ValueType* a2 = a1 + y*yStride; - for (Int32 z=sub.min()[2], ez=sub.max()[2]+1; z<ez; ++z) *a2++ = value; + DenseValueType* a2 = a1 + y*yStride; + for (Int32 z=sub.min()[2], ez=sub.max()[2]+1; z<ez; ++z, a2 += zStride) { + *a2 = DenseValueType(value); + } } } } @@ -1970,35 +2325,20 @@ RootNode<ChildT>::pruneTiles(const ValueType& tolerance) //////////////////////////////////////// -// Helper method that implements stealNode() template<typename ChildT> template<typename NodeT> inline NodeT* -RootNode<ChildT>::doStealNode(const Coord& xyz, const ValueType& value, bool state) +RootNode<ChildT>::stealNode(const Coord& xyz, const ValueType& value, bool state) { + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same<NodeT, ChildT>::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN MapIter iter = this->findCoord(xyz); if (iter == mTable.end() || isTile(iter)) return NULL; return (boost::is_same<NodeT, ChildT>::value) ? reinterpret_cast<NodeT*>(&stealChild(iter, Tile(value, state))) : getChild(iter).template stealNode<NodeT>(xyz, value, state); -} - - -template<typename ChildT> -template<typename NodeT> -inline NodeT* -RootNode<ChildT>::stealNode(const Coord& xyz, const ValueType& value, bool state) -{ - // The following conditional is resolved at compile time, and the ternary operator - // and helper method are used to avoid "unreachable code" warnings (with - // "if (<cond>) { <A> } else { <B> }", either <A> or <B> is unreachable if <cond> - // is a compile-time constant expression). Partial specialization on NodeT would be - // impractical because a method template can't be specialized without also - // specializing its class template. - return (NodeT::LEVEL > ChildT::LEVEL || - (NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same<NodeT, ChildT>::value))) - ? static_cast<NodeT*>(NULL) - : this->doStealNode<NodeT>(xyz, value, state); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END } @@ -2080,7 +2420,7 @@ inline void RootNode<ChildT>::addTile(Index level, const Coord& xyz, const ValueType& value, bool state) { - if (LEVEL >= level && level > 0) { + if (LEVEL >= level) { MapIter iter = this->findCoord(xyz); if (iter == mTable.end()) {//background if (LEVEL > level) { @@ -2115,7 +2455,7 @@ inline void RootNode<ChildT>::addTileAndCache(Index level, const Coord& xyz, const ValueType& value, bool state, AccessorT& acc) { - if (LEVEL >= level && level > 0) { + if (LEVEL >= level) { MapIter iter = this->findCoord(xyz); if (iter == mTable.end()) {//background if (LEVEL > level) { @@ -2195,45 +2535,119 @@ RootNode<ChildT>::touchLeafAndCache(const Coord& xyz, AccessorT& acc) template<typename ChildT> -inline typename ChildT::LeafNodeType* -RootNode<ChildT>::probeLeaf(const Coord& xyz) +template<typename NodeT> +inline NodeT* +RootNode<ChildT>::probeNode(const Coord& xyz) { + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same<NodeT, ChildT>::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN MapIter iter = this->findCoord(xyz); if (iter == mTable.end() || isTile(iter)) return NULL; - return getChild(iter).probeLeaf(xyz); + ChildT* child = &getChild(iter); + return (boost::is_same<NodeT, ChildT>::value) + ? reinterpret_cast<NodeT*>(child) + : child->template probeNode<NodeT>(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END } + template<typename ChildT> -template<typename AccessorT> -inline typename ChildT::LeafNodeType* -RootNode<ChildT>::probeLeafAndCache(const Coord& xyz, AccessorT& acc) +template<typename NodeT> +inline const NodeT* +RootNode<ChildT>::probeConstNode(const Coord& xyz) const { - MapIter iter = this->findCoord(xyz); + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same<NodeT, ChildT>::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + MapCIter iter = this->findCoord(xyz); if (iter == mTable.end() || isTile(iter)) return NULL; - ChildT* child = &getChild(iter); - acc.insert(xyz, child); - return child->probeLeafAndCache(xyz, acc); + const ChildT* child = &getChild(iter); + return (boost::is_same<NodeT, ChildT>::value) + ? reinterpret_cast<const NodeT*>(child) + : child->template probeConstNode<NodeT>(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END } + +template<typename ChildT> +inline typename ChildT::LeafNodeType* +RootNode<ChildT>::probeLeaf(const Coord& xyz) +{ + return this->template probeNode<LeafNodeType>(xyz); +} + + template<typename ChildT> inline const typename ChildT::LeafNodeType* RootNode<ChildT>::probeConstLeaf(const Coord& xyz) const { - MapCIter iter = this->findCoord(xyz); - if (iter == mTable.end() || isTile(iter)) return NULL; - return getChild(iter).probeConstLeaf(xyz); + return this->template probeConstNode<LeafNodeType>(xyz); } + +template<typename ChildT> +template<typename AccessorT> +inline typename ChildT::LeafNodeType* +RootNode<ChildT>::probeLeafAndCache(const Coord& xyz, AccessorT& acc) +{ + return this->template probeNodeAndCache<LeafNodeType>(xyz, acc); +} + + template<typename ChildT> template<typename AccessorT> inline const typename ChildT::LeafNodeType* RootNode<ChildT>::probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const { + return this->template probeConstNodeAndCache<LeafNodeType>(xyz, acc); +} + + +template<typename ChildT> +template<typename AccessorT> +inline const typename ChildT::LeafNodeType* +RootNode<ChildT>::probeLeafAndCache(const Coord& xyz, AccessorT& acc) const +{ + return this->probeConstLeafAndCache(xyz, acc); +} + + +template<typename ChildT> +template<typename NodeT, typename AccessorT> +inline NodeT* +RootNode<ChildT>::probeNodeAndCache(const Coord& xyz, AccessorT& acc) +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same<NodeT, ChildT>::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end() || isTile(iter)) return NULL; + ChildT* child = &getChild(iter); + acc.insert(xyz, child); + return (boost::is_same<NodeT, ChildT>::value) + ? reinterpret_cast<NodeT*>(child) + : child->template probeNodeAndCache<NodeT>(xyz, acc); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template<typename ChildT> +template<typename NodeT,typename AccessorT> +inline const NodeT* +RootNode<ChildT>::probeConstNodeAndCache(const Coord& xyz, AccessorT& acc) const +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same<NodeT, ChildT>::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN MapCIter iter = this->findCoord(xyz); if (iter == mTable.end() || isTile(iter)) return NULL; const ChildT* child = &getChild(iter); acc.insert(xyz, child); - return child->probeConstLeafAndCache(xyz, acc); + return (boost::is_same<NodeT, ChildT>::value) + ? reinterpret_cast<const NodeT*>(child) + : child->template probeConstNodeAndCache<NodeT>(xyz, acc); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END } @@ -2244,7 +2658,7 @@ template<typename ChildT> inline void RootNode<ChildT>::signedFloodFill() { - this->signedFloodFill(mBackground, negative(mBackground)); + this->signedFloodFill(mBackground, math::negative(mBackground)); } @@ -2306,27 +2720,111 @@ RootNode<ChildT>::voxelizeActiveTiles() template<typename ChildT> +template<MergePolicy Policy> inline void RootNode<ChildT>::merge(RootNode& other) { - for (MapIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { - MapIter j = mTable.find(i->first); - if (other.isChild(i)) { - if (j == mTable.end()) { // insert other child node - mTable[i->first]=NodeStruct(stealChild(i, Tile(other.mBackground, /*on=*/false))); - } else if (isTile(j)) { // replace tile with other child node - setChild(j, stealChild(i, Tile(other.mBackground, /*on=*/false))); - } else { // merge both child nodes - getChild(j).merge(getChild(i),other.mBackground, mBackground); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + + switch (Policy) { + + default: + case MERGE_ACTIVE_STATES: + for (MapIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { + MapIter j = mTable.find(i->first); + if (other.isChild(i)) { + if (j == mTable.end()) { // insert other node's child + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + mTable[i->first] = NodeStruct(child); + } else if (isTile(j)) { + if (isTileOff(j)) { // replace inactive tile with other node's child + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + setChild(j, child); + } + } else { // merge both child nodes + getChild(j).template merge<MERGE_ACTIVE_STATES>(getChild(i), + other.mBackground, mBackground); + } + } else if (other.isTileOn(i)) { + if (j == mTable.end()) { // insert other node's active tile + mTable[i->first] = i->second; + } else if (!isTileOn(j)) { + // Replace anything except an active tile with the other node's active tile. + setTile(j, Tile(other.getTile(i).value, true)); + } } - } else { // other is a tile - if (j == mTable.end()) { // insert other tile - mTable[i->first] = i->second; - } else continue; // ignore other tile } + break; + + case MERGE_NODES: + for (MapIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { + MapIter j = mTable.find(i->first); + if (other.isChild(i)) { + if (j == mTable.end()) { // insert other node's child + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + mTable[i->first] = NodeStruct(child); + } else if (isTile(j)) { // replace tile with other node's child + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + setChild(j, child); + } else { // merge both child nodes + getChild(j).template merge<MERGE_NODES>( + getChild(i), other.mBackground, mBackground); + } + } + } + break; + + case MERGE_ACTIVE_STATES_AND_NODES: + for (MapIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { + MapIter j = mTable.find(i->first); + if (other.isChild(i)) { + if (j == mTable.end()) { + // Steal and insert the other node's child. + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + mTable[i->first] = NodeStruct(child); + } else if (isTile(j)) { + // Replace this node's tile with the other node's child. + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + const Tile tile = getTile(j); + setChild(j, child); + if (tile.active) { + // Merge the other node's child with this node's active tile. + child.template merge<MERGE_ACTIVE_STATES_AND_NODES>( + tile.value, tile.active); + } + } else /*if (isChild(j))*/ { + // Merge the other node's child into this node's child. + getChild(j).template merge<MERGE_ACTIVE_STATES_AND_NODES>(getChild(i), + other.mBackground, mBackground); + } + } else if (other.isTileOn(i)) { + if (j == mTable.end()) { + // Insert a copy of the other node's active tile. + mTable[i->first] = i->second; + } else if (isTileOff(j)) { + // Replace this node's inactive tile with a copy of the other's active tile. + setTile(j, Tile(other.getTile(i).value, true)); + } else if (isChild(j)) { + // Merge the other node's active tile into this node's child. + const Tile& tile = getTile(i); + getChild(j).template merge<MERGE_ACTIVE_STATES_AND_NODES>( + tile.value, tile.active); + } + } // else if (other.isTileOff(i)) {} // ignore the other node's inactive tiles + } + break; } + // Empty the other tree so as not to leave it in a partially cannibalized state. other.clear(); + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END } @@ -2369,6 +2867,72 @@ RootNode<ChildT>::topologyUnion(const RootNode<OtherChildType>& other) } } +template<typename ChildT> +template<typename OtherChildType> +inline void +RootNode<ChildT>::topologyIntersection(const RootNode<OtherChildType>& other) +{ + typedef RootNode<OtherChildType> OtherRootT; + typedef typename OtherRootT::MapCIter OtherCIterT; + + enforceSameConfiguration(other); + + std::set<Coord> tmp;//keys to erase + for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + OtherCIterT j = other.mTable.find(i->first); + if (this->isChild(i)) { + if (j == other.mTable.end() || other.isTileOff(j)) { + tmp.insert(i->first);//delete child branch + } else if (other.isChild(j)) { // intersect with child branch + this->getChild(i).topologyIntersection(other.getChild(j), mBackground); + } + } else if (this->isTileOn(i)) { + if (j == other.mTable.end() || other.isTileOff(j)) { + this->setTile(i, Tile(this->getTile(i).value, false));//turn inactive + } else if (other.isChild(j)) { //replace with a child branch with identical topology + ChildT* child = + new ChildT(other.getChild(j), this->getTile(i).value, TopologyCopy()); + this->setChild(i, *child); + } + } + } + for (std::set<Coord>::iterator i = tmp.begin(), e = tmp.end(); i != e; ++i) mTable.erase(*i); +} + +template<typename ChildT> +template<typename OtherChildType> +inline void +RootNode<ChildT>::topologyDifference(const RootNode<OtherChildType>& other) +{ + typedef RootNode<OtherChildType> OtherRootT; + typedef typename OtherRootT::MapCIter OtherCIterT; + + enforceSameConfiguration(other); + + for (OtherCIterT i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { + MapIter j = mTable.find(i->first); + if (other.isChild(i)) { + if (j == mTable.end() || this->isTileOff(j)) { + //do nothing + } else if (this->isChild(j)) { // difference with child branch + this->getChild(j).topologyDifference(other.getChild(i), mBackground); + } else if (this->isTileOn(j)) { + // this is an active tile so create a child node and descent + ChildT* child = new ChildT(j->first, this->getTile(j).value, true); + child->topologyDifference(other.getChild(i), mBackground); + this->setChild(j, *child); + } + } else if (other.isTileOn(i)) { // other is an active tile + if (j == mTable.end() || this->isTileOff(j)) { + // do nothing + } else if (this->isChild(j)) { + mTable.erase(j->first);//delete child + } else if (this->isTileOn(j)) { + this->setTile(j, Tile(this->getTile(j).value, false)); + } + } + } +} //////////////////////////////////////// @@ -2431,28 +2995,80 @@ RootNode<ChildT>::combine(RootNode& other, CombineOp& op, bool prune) //////////////////////////////////////// +// This helper class is a friend of RootNode and is needed so that combine2 +// can be specialized for compatible and incompatible pairs of RootNode types. +template<typename CombineOp, typename RootT, typename OtherRootT, bool Compatible = false> +struct RootNodeCombineHelper +{ + static inline void combine2(RootT& self, const RootT&, const OtherRootT& other1, + CombineOp&, bool) + { + // If the two root nodes have different configurations or incompatible ValueTypes, + // throw an exception. + self.enforceSameConfiguration(other1); + self.enforceCompatibleValueTypes(other1); + // One of the above two tests should throw, so we should never get here: + std::ostringstream ostr; + ostr << "cannot combine a " << typeid(OtherRootT).name() + << " into a " << typeid(RootT).name(); + OPENVDB_THROW(TypeError, ostr.str()); + } +}; + +// Specialization for root nodes of compatible types +template<typename CombineOp, typename RootT, typename OtherRootT> +struct RootNodeCombineHelper<CombineOp, RootT, OtherRootT, /*Compatible=*/true> +{ + static inline void combine2(RootT& self, const RootT& other0, const OtherRootT& other1, + CombineOp& op, bool prune) + { + self.doCombine2(other0, other1, op, prune); + } +}; + + template<typename ChildT> -template<typename CombineOp> +template<typename CombineOp, typename OtherRootNode> inline void -RootNode<ChildT>::combine2(const RootNode& other0, const RootNode& other1, +RootNode<ChildT>::combine2(const RootNode& other0, const OtherRootNode& other1, CombineOp& op, bool prune) { - CombineArgs<ValueType> args; + typedef typename OtherRootNode::ValueType OtherValueType; + static const bool compatible = (SameConfiguration<OtherRootNode>::value + && CanConvertType</*from=*/OtherValueType, /*to=*/ValueType>::value); + RootNodeCombineHelper<CombineOp, RootNode, OtherRootNode, compatible>::combine2( + *this, other0, other1, op, prune); +} + + +template<typename ChildT> +template<typename CombineOp, typename OtherRootNode> +inline void +RootNode<ChildT>::doCombine2(const RootNode& other0, const OtherRootNode& other1, + CombineOp& op, bool prune) +{ + enforceSameConfiguration(other1); + + typedef typename OtherRootNode::ValueType OtherValueT; + typedef typename OtherRootNode::Tile OtherTileT; + typedef typename OtherRootNode::NodeStruct OtherNodeStructT; + typedef typename OtherRootNode::MapCIter OtherMapCIterT; + + CombineArgs<ValueType, OtherValueT> args; CoordSet keys; other0.insertKeys(keys); other1.insertKeys(keys); - const NodeStruct - bg0(Tile(other0.mBackground, /*active=*/false)), - bg1(Tile(other1.mBackground, /*active=*/false)); + const NodeStruct bg0(Tile(other0.mBackground, /*active=*/false)); + const OtherNodeStructT bg1(OtherTileT(other1.mBackground, /*active=*/false)); for (CoordSetCIter i = keys.begin(), e = keys.end(); i != e; ++i) { MapIter thisIter = this->findOrAddCoord(*i); - MapCIter iter0 = other0.findKey(*i), iter1 = other1.findKey(*i); - const NodeStruct - &ns0 = (iter0 != other0.mTable.end()) ? iter0->second : bg0, - &ns1 = (iter1 != other1.mTable.end()) ? iter1->second : bg1; + MapCIter iter0 = other0.findKey(*i); + OtherMapCIterT iter1 = other1.findKey(*i); + const NodeStruct& ns0 = (iter0 != other0.mTable.end()) ? iter0->second : bg0; + const OtherNodeStructT& ns1 = (iter1 != other1.mTable.end()) ? iter1->second : bg1; if (ns0.isTile() && ns1.isTile()) { // Both input nodes have constant values (tiles). // Combine the two values and add a new tile to this node with the result. @@ -2462,11 +3078,11 @@ RootNode<ChildT>::combine2(const RootNode& other0, const RootNode& other1, .setBIsActive(ns1.isTileOn())); setTile(thisIter, Tile(args.result(), args.resultIsActive())); } else { - ChildT& otherChild = ns0.isChild() ? *ns0.child : *ns1.child; if (!isChild(thisIter)) { // Add a new child with the same coordinates, etc. as the other node's child. - setChild(thisIter, - *(new ChildT(otherChild.getOrigin(), getTile(thisIter).value))); + const Coord& childOrigin = + ns0.isChild() ? ns0.child->origin() : ns1.child->origin(); + setChild(thisIter, *(new ChildT(childOrigin, getTile(thisIter).value))); } ChildT& child = getChild(thisIter); @@ -2583,8 +3199,6 @@ template< inline void RootNode<ChildT>::doVisit2(RootNodeT& self, OtherRootNodeT& other, VisitorOp& op) { - /// @todo Allow the two nodes to have different ValueTypes, but not - /// different fan-out factors or different index space bounds. enforceSameConfiguration(other); typename RootNodeT::ValueType val; diff --git a/extern/openvdb/internal/openvdb/tree/Tree.h b/extern/openvdb/internal/openvdb/tree/Tree.h index dfc0ece8ac0..e0715caf645 100644 --- a/extern/openvdb/internal/openvdb/tree/Tree.h +++ b/extern/openvdb/internal/openvdb/tree/Tree.h @@ -72,13 +72,13 @@ public: /// Return the name of this tree's type. virtual const Name& type() const = 0; + /// Return the name of the type of a voxel's value (e.g., "float" or "vec3d"). virtual Name valueType() const = 0; /// Return a pointer to a deep copy of this tree virtual TreeBase::Ptr copy() const = 0; - // // Tree methods // @@ -86,10 +86,15 @@ public: /// @note Query the metadata object for the value's type. virtual Metadata::Ptr getBackgroundValue() const { return Metadata::Ptr(); } - /// @brief Return in @a bbox the axis-aligned bounding box of all leaf nodes. + /// @brief Return in @a bbox the axis-aligned bounding box of all + /// leaf nodes and active tiles. + /// @details This is faster then calling evalActiveVoxelBoundingBox, + /// which visits the individual active voxels, and hence + /// evalLeafBoundingBox produces a less tight, i.e. approximate, bbox. /// @return @c false if the bounding box is empty (in which case /// the bbox is set to its default value). virtual bool evalLeafBoundingBox(CoordBBox& bbox) const = 0; + /// @brief Return in @a dim the dimensions of the axis-aligned bounding box /// of all leaf nodes. /// @return @c false if the bounding box is empty. @@ -97,10 +102,13 @@ public: /// @brief Return in @a bbox the axis-aligned bounding box of all /// active voxels and tiles. - /// This is a tighter bounding box than the leaf node bounding box. + /// @details This method produces a more accurate, i.e. tighter, + /// bounding box than evalLeafBoundingBox which is approximate but + /// faster. /// @return @c false if the bounding box is empty (in which case /// the bbox is set to its default value). virtual bool evalActiveVoxelBoundingBox(CoordBBox& bbox) const = 0; + /// @brief Return in @a dim the dimensions of the axis-aligned bounding box of all /// active voxels. This is a tighter bounding box than the leaf node bounding box. /// @return @c false if the bounding box is empty. @@ -191,13 +199,24 @@ public: }; - Tree(){} + Tree() {} /// Deep copy constructor Tree(const Tree& other): TreeBase(other), mRoot(other.mRoot) { } + /// @brief Value conversion deep copy constructor + /// + /// Deep copy a tree of the same configuration as this tree type but a different + /// ValueType, casting the other tree's values to this tree's ValueType. + /// @throw TypeError if the other tree's configuration doesn't match this tree's + /// or if this tree's ValueType is not constructible from the other tree's ValueType. + template<typename OtherRootType> + explicit Tree(const Tree<OtherRootType>& other): TreeBase(other), mRoot(other.root()) + { + } + /// @brief Topology copy constructor from a tree of a different type /// /// Copy the structure, i.e., the active states of tiles and voxels, of another @@ -207,13 +226,14 @@ public: /// @param inactiveValue background value for this tree, and the value to which /// all inactive tiles and voxels are initialized /// @param activeValue value to which active tiles and voxels are initialized + /// @throw TypeError if the other tree's configuration doesn't match this tree's. template<typename OtherTreeType> Tree(const OtherTreeType& other, const ValueType& inactiveValue, const ValueType& activeValue, TopologyCopy): TreeBase(other), - mRoot(other.getRootNode(), inactiveValue, activeValue, TopologyCopy()) + mRoot(other.root(), inactiveValue, activeValue, TopologyCopy()) { } @@ -227,10 +247,11 @@ public: /// Instead, initialize tiles and voxels with the given background value. /// @param other a tree having (possibly) a different ValueType /// @param background the value to which tiles and voxels are initialized + /// @throw TypeError if the other tree's configuration doesn't match this tree's. template<typename OtherTreeType> Tree(const OtherTreeType& other, const ValueType& background, TopologyCopy): TreeBase(other), - mRoot(other.getRootNode(), background, TopologyCopy()) + mRoot(other.root(), background, TopologyCopy()) { } @@ -248,7 +269,7 @@ public: /// Return the name of this type of tree. static const Name& treeType(); /// Return the name of this type of tree. - virtual const Name& type() const { return treeType(); } + virtual const Name& type() const { return this->treeType(); } bool operator==(const Tree&) const { OPENVDB_THROW(NotImplementedError, ""); } bool operator!=(const Tree&) const { OPENVDB_THROW(NotImplementedError, ""); } @@ -257,9 +278,12 @@ public: /// Return this tree's root node. RootNodeType& root() { return mRoot; } const RootNodeType& root() const { return mRoot; } - // Deprecate the methods below - RootNodeType& getRootNode() { return mRoot; } - const RootNodeType& getRootNode() const { return mRoot; } + //@} + //@{ + /// @brief Return this tree's root node. + /// @deprecated Use root() instead. + OPENVDB_DEPRECATED RootNodeType& getRootNode() { return mRoot; } + OPENVDB_DEPRECATED const RootNodeType& getRootNode() const { return mRoot; } //@} @@ -267,7 +291,7 @@ public: // Tree methods // /// @brief Return @c true if the given tree has the same node and active value - /// topology as this tree (but possibly a different @c ValueType). + /// topology as this tree, whether or not it has the same @c ValueType. template<typename OtherRootNodeType> bool hasSameTopology(const Tree<OtherRootNodeType>& other) const; @@ -321,6 +345,10 @@ public: /// Return the number of inactive voxels within the bounding box of all active voxels. virtual Index64 inactiveVoxelCount() const; + /// Return the total number of active tiles. + /// @note This method is not virtual so as to not change the ABI. + Index64 activeTileCount() const { return mRoot.onTileCount(); } + /// Return the minimum and maximum active values in this tree. void evalMinMax(ValueType &min, ValueType &max) const; @@ -337,39 +365,70 @@ public: template<typename AccessT> const ValueType& getValue(const Coord& xyz, AccessT&) const; /// @brief Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides. - /// - /// If (x, y, z) isn't explicitly represented in the tree (i.e., it is implicitly - /// a background voxel), return -1. + /// @details If (x, y, z) isn't explicitly represented in the tree (i.e., it is + /// implicitly a background voxel), return -1. int getValueDepth(const Coord& xyz) const; + /// Set the active state of the voxel at the given coordinates but don't change its value. + void setActiveState(const Coord& xyz, bool on); + /// Set the value of the voxel at the given coordinates but don't change its active state. + void setValueOnly(const Coord& xyz, const ValueType& value); + /// Mark the voxel at the given coordinates as active but don't change its value. + void setValueOn(const Coord& xyz); + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValueOn(const Coord& xyz, const ValueType& value); /// Set the value of the voxel at the given coordinates and mark the voxel as active. void setValue(const Coord& xyz, const ValueType& value); - /// Set the value of the voxel at the given coordinates but preserve it active state. - void setValueOnly(const Coord& xyz, const ValueType& value); /// @brief Set the value of the voxel at the given coordinates, mark the voxel as active, /// and update the given accessor's node cache. template<typename AccessT> void setValue(const Coord& xyz, const ValueType& value, AccessT&); - /// Mark the voxel at the given coordinates as active, but don't change its value. - void setValueOn(const Coord& xyz); - /// Set the value of the voxel at the given coordinates and mark the voxel as active. - void setValueOn(const Coord& xyz, const ValueType& value); - /// @brief Set the value of the voxel at the given coordinates to the minimum - /// of its current value and the given value, and mark the voxel as active. - void setValueOnMin(const Coord& xyz, const ValueType& value); - /// @brief Set the value of the voxel at the given coordinates to the maximum - /// of its current value and the given value, and mark the voxel as active. - void setValueOnMax(const Coord& xyz, const ValueType& value); - /// @brief Set the value of the voxel at the given coordinates to the sum - /// of its current value and the given value, and mark the voxel as active. - void setValueOnSum(const Coord& xyz, const ValueType& value); - - /// Mark the voxel at the given coordinates as inactive, but don't change its value. + /// Mark the voxel at the given coordinates as inactive but don't change its value. void setValueOff(const Coord& xyz); - /// Change the value of the voxel at the given coordinates and mark the voxel as inactive. + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. void setValueOff(const Coord& xyz, const ValueType& value); - /// Set the active state of the voxel at the given coordinates, but don't change its value. - void setActiveState(const Coord& xyz, bool on); + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details Provided that the functor can be inlined, this is typically + /// significantly faster than calling getValue() followed by setValueOn(). + /// @param xyz the coordinates of a voxel whose value is to be modified + /// @param op a functor of the form <tt>void op(ValueType&) const</tt> that modifies + /// its argument in place + /// @par Example: + /// @code + /// Coord xyz(1, 0, -2); + /// // Multiply the value of a voxel by a constant and mark the voxel as active. + /// floatTree.modifyValue(xyz, [](float& f) { f *= 0.25; }); // C++11 + /// // Set the value of a voxel to the maximum of its current value and 0.25, + /// // and mark the voxel as active. + /// floatTree.modifyValue(xyz, [](float& f) { f = std::max(f, 0.25f); }); // C++11 + /// @endcode + /// @note The functor is not guaranteed to be called only once. + /// @see tools::foreach() + template<typename ModifyOp> + void modifyValue(const Coord& xyz, const ModifyOp& op); + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details Provided that the functor can be inlined, this is typically + /// significantly faster than calling getValue() followed by setValue(). + /// @param xyz the coordinates of a voxel to be modified + /// @param op a functor of the form <tt>void op(ValueType&, bool&) const</tt> that + /// modifies its arguments, a voxel's value and active state, in place + /// @par Example: + /// @code + /// Coord xyz(1, 0, -2); + /// // Multiply the value of a voxel by a constant and mark the voxel as inactive. + /// floatTree.modifyValueAndActiveState(xyz, + /// [](float& f, bool& b) { f *= 0.25; b = false; }); // C++11 + /// // Set the value of a voxel to the maximum of its current value and 0.25, + /// // but don't change the voxel's active state. + /// floatTree.modifyValueAndActiveState(xyz, + /// [](float& f, bool&) { f = std::max(f, 0.25f); }); // C++11 + /// @endcode + /// @note The functor is not guaranteed to be called only once. + /// @see tools::foreach() + template<typename ModifyOp> + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op); /// @brief Get the value of the voxel at the given coordinates. /// @return @c true if the value is active. @@ -393,7 +452,7 @@ public: /// operation for optimal sparseness. void fill(const CoordBBox& bbox, const ValueType& value, bool active = true); - /// Call the @c PruneOp functor for each non-root node in the tree. + /// @brief Call the @c PruneOp functor for each non-root node in the tree. /// If the functor returns @c true, prune the node and replace it with a tile. /// /// This method is used to implement all of the various pruning algorithms @@ -414,13 +473,12 @@ public: /// background tiles any nodes whose values are all inactive. void pruneInactive(); - /// @brief Prune any descendants whose values are all inactive and replace them - /// with inactive tiles having a values equal to the first value - /// encountered in the (inactive) child. - /// - /// @note This method is faster then tolerance based prune and + /// @brief Reduce the memory footprint of this tree by replacing nodes + /// whose values are all inactive with inactive tiles having a value equal to + /// the first value encountered in the (inactive) child. + /// @details This method is faster than tolerance-based prune and /// useful for narrow-band level set applications where inactive - /// values are limited to either an inside or outside value. + /// values are limited to either an inside or an outside value. void pruneLevelSet(); /// @brief Add the given leaf node to this tree, creating a new branch if necessary. @@ -430,8 +488,7 @@ public: /// @brief Add a tile containing voxel (x, y, z) at the specified tree level, /// creating a new branch if necessary. Delete any existing lower-level nodes /// that contain (x, y, z). - /// @note @c Level must be greater than zero (i.e., the level of leaf nodes) - /// and less than this tree's depth. + /// @note @a level must be less than this tree's depth. void addTile(Index level, const Coord& xyz, const ValueType& value, bool active); /// @brief Return a pointer to the node of type @c NodeT that contains voxel (x, y, z) @@ -441,22 +498,29 @@ public: template<typename NodeT> NodeT* stealNode(const Coord& xyz, const ValueType& value, bool active); - /// @brief @return the leaf node that contains voxel (x, y, z) and - /// if it doesn't exist, create it, but preserve the values and + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). + /// If no such node exists, create one that preserves the values and /// active states of all voxels. - /// - /// Use this method to preallocate a static tree topology over which to + /// @details Use this method to preallocate a static tree topology over which to /// safely perform multithreaded processing. LeafNodeType* touchLeaf(const Coord& xyz); - /// @brief @return a pointer to the leaf node that contains - /// voxel (x, y, z) and if it doesn't exist, return NULL. - LeafNodeType* probeLeaf(const Coord& xyz); + //@{ + /// @brief Return a pointer to the node of type @c NodeType that contains + /// voxel (x, y, z). If no such node exists, return NULL. + template<typename NodeType> NodeType* probeNode(const Coord& xyz); + template<typename NodeType> const NodeType* probeConstNode(const Coord& xyz) const; + template<typename NodeType> const NodeType* probeNode(const Coord& xyz) const; + //@} - /// @brief @return a const pointer to the leaf node that contains - /// voxel (x, y, z) and if it doesn't exist, return NULL. + //@{ + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). + /// If no such node exists, return NULL. + LeafNodeType* probeLeaf(const Coord& xyz); const LeafNodeType* probeConstLeaf(const Coord& xyz) const; const LeafNodeType* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); } + //@} + // // Aux methods @@ -489,8 +553,6 @@ public: /// Return this tree's background value. const ValueType& background() const { return mRoot.background(); } - /// @deprecated Use background() - OPENVDB_DEPRECATED ValueType getBackground() const { return mRoot.background(); } /// Replace this tree's background value. void setBackground(const ValueType& background) { mRoot.setBackground(background); } @@ -500,28 +562,28 @@ public: /// @brief Set the values of all inactive voxels and tiles of a narrow-band /// level set from the signs of the active voxels, setting outside values to /// +background and inside values to -background. - /// - /// @note This method should only be used on closed, narrow-band level sets! + /// @warning This method should only be used on closed, narrow-band level sets. void signedFloodFill() { mRoot.signedFloodFill(); } /// @brief Set the values of all inactive voxels and tiles of a narrow-band /// level set from the signs of the active voxels, setting exterior values to /// @a outside and interior values to @a inside. Set the background value /// of this tree to @a outside. - /// - /// @note This method should only be used on closed, narrow-band level sets! + /// @warning This method should only be used on closed, narrow-band level sets. void signedFloodFill(const ValueType& outside, const ValueType& inside); - /// Move child nodes from the other tree into this tree wherever those nodes - /// correspond to constant-value tiles in this tree, and replace leaf-level - /// inactive voxels in this tree with corresponding voxels in the other tree - /// that are active. - /// @note This operation always empties the other tree. - void merge(Tree& other); - - /// Turns active tiles into dense voxels, i.e. active leaf nodes + /// Densify active tiles, i.e., replace them with leaf-level active voxels. void voxelizeActiveTiles(); + /// @brief Efficiently merge another tree into this tree using one of several schemes. + /// @details This operation is primarily intended to combine trees that are mostly + /// non-overlapping (for example, intermediate trees from computations that are + /// parallelized across disjoint regions of space). + /// @note This operation is not guaranteed to produce an optimally sparse tree. + /// Follow merge() with prune() for optimal sparseness. + /// @warning This operation always empties the other tree. + void merge(Tree& other, MergePolicy = MERGE_ACTIVE_STATES); + /// @brief Union this tree's set of active values with the active values /// of the other tree, whose @c ValueType may be different. /// @details The resulting state of a value is active if the corresponding value @@ -538,51 +600,79 @@ public: template<typename OtherRootNodeType> void topologyUnion(const Tree<OtherRootNodeType>& other); - /*! For a given function @c f, use sparse traversal to compute <tt>f(this, other)</tt> - * over all corresponding pairs of values (tile or voxel) of this tree and the other tree - * and store the result in this tree. - * This method is typically more space-efficient than the two-tree combine2(), - * since it moves rather than copies nodes from the other tree into this tree. - * @note This operation always empties the other tree. - * @param other a tree of the same type as this tree - * @param op a functor of the form <tt>void op(const T& a, const T& b, T& result)</tt>, - * where @c T is this tree's @c ValueType, that computes - * <tt>result = f(a, b)</tt> - * @param prune if true, prune the resulting tree one branch at a time (this is usually - * more space-efficient than pruning the entire tree in one pass) - * - * @par Example: - * Compute the per-voxel difference between two floating-point trees, - * @c aTree and @c bTree, and store the result in @c aTree (leaving @c bTree empty). - * @code - * { - * struct Local { - * static inline void diff(const float& a, const float& b, float& result) { - * result = a - b; - * } - * }; - * aTree.combine(bTree, Local::diff); - * } - * @endcode - * - * @par Example: - * Compute <tt>f * a + (1 - f) * b</tt> over all voxels of two floating-point trees, - * @c aTree and @c bTree, and store the result in @c aTree (leaving @c bTree empty). - * @code - * namespace { - * struct Blend { - * Blend(float f): frac(f) {} - * inline void operator()(const float& a, const float& b, float& result) const { - * result = frac * a + (1.0 - frac) * b; - * } - * float frac; - * }; - * } - * { - * aTree.combine(bTree, Blend(0.25)); // 0.25 * a + 0.75 * b - * } - * @endcode - */ + /// @brief Intersects this tree's set of active values with the active values + /// of the other tree, whose @c ValueType may be different. + /// @details The resulting state of a value is active only if the corresponding + /// value was already active AND if it is active in the other tree. Also, a + /// resulting value maps to a voxel if the corresponding value + /// already mapped to an active voxel in either of the two grids + /// and it maps to an active tile or voxel in the other grid. + /// + /// @note This operation can delete branches in this grid if they + /// overlap with inactive tiles in the other grid. Likewise active + /// voxels can be turned into unactive voxels resulting in leaf + /// nodes with no active values. Thus, it is recommended to + /// subsequently call prune. + template<typename OtherRootNodeType> + void topologyIntersection(const Tree<OtherRootNodeType>& other); + + /// @brief Difference this tree's set of active values with the active values + /// of the other tree, whose @c ValueType may be different. So a + /// resulting voxel will be active only if the original voxel is + /// active in this tree and inactive in the other tree. + /// + /// @note This operation can delete branches in this grid if they + /// overlap with active tiles in the other grid. Likewise active + /// voxels can be turned into inactive voxels resulting in leaf + /// nodes with no active values. Thus, it is recommended to + /// subsequently call prune. + template<typename OtherRootNodeType> + void topologyDifference(const Tree<OtherRootNodeType>& other); + + /// For a given function @c f, use sparse traversal to compute <tt>f(this, other)</tt> + /// over all corresponding pairs of values (tile or voxel) of this tree and the other tree + /// and store the result in this tree. + /// This method is typically more space-efficient than the two-tree combine2(), + /// since it moves rather than copies nodes from the other tree into this tree. + /// @note This operation always empties the other tree. + /// @param other a tree of the same type as this tree + /// @param op a functor of the form <tt>void op(const T& a, const T& b, T& result)</tt>, + /// where @c T is this tree's @c ValueType, that computes + /// <tt>result = f(a, b)</tt> + /// @param prune if true, prune the resulting tree one branch at a time (this is usually + /// more space-efficient than pruning the entire tree in one pass) + /// + /// @par Example: + /// Compute the per-voxel difference between two floating-point trees, + /// @c aTree and @c bTree, and store the result in @c aTree (leaving @c bTree empty). + /// @code + /// { + /// struct Local { + /// static inline void diff(const float& a, const float& b, float& result) { + /// result = a - b; + /// } + /// }; + /// aTree.combine(bTree, Local::diff); + /// } + /// @endcode + /// + /// @par Example: + /// Compute <tt>f * a + (1 - f) * b</tt> over all voxels of two floating-point trees, + /// @c aTree and @c bTree, and store the result in @c aTree (leaving @c bTree empty). + /// @code + /// namespace { + /// struct Blend { + /// Blend(float f): frac(f) {} + /// inline void operator()(const float& a, const float& b, float& result) const { + /// result = frac * a + (1.0 - frac) * b; + /// } + /// float frac; + /// }; + /// } + /// { + /// aTree.combine(bTree, Blend(0.25)); // 0.25 * a + 0.75 * b + /// } + /// @endcode template<typename CombineOp> void combine(Tree& other, CombineOp& op, bool prune = false); #ifndef _MSC_VER @@ -590,45 +680,44 @@ public: void combine(Tree& other, const CombineOp& op, bool prune = false); #endif - /*! Like combine(), but with - * @param other a tree of the same type as this tree - * @param op a functor of the form <tt>void op(CombineArgs<ValueType>& args)</tt> that - * computes <tt>args.setResult(f(args.a(), args.b()))</tt> and, optionally, - * <tt>args.setResultIsActive(g(args.aIsActive(), args.bIsActive()))</tt> - * for some functions @c f and @c g - * @param prune if true, prune the resulting tree one branch at a time (this is usually - * more space-efficient than pruning the entire tree in one pass) - * - * This variant passes not only the @em a and @em b values but also the active states - * of the @em a and @em b values to the functor, which may then return, by calling - * @c args.setResultIsActive(), a computed active state for the result value. - * By default, the result is active if either the @em a or the @em b value is active. - * - * @see openvdb/Types.h for the definition of the CombineArgs struct. - * - * @par Example: - * Replace voxel values in floating-point @c aTree with corresponding values - * from floating-point @c bTree (leaving @c bTree empty) wherever the @c bTree - * values are larger. Also, preserve the active states of any transferred values. - * @code - * { - * struct Local { - * static inline void max(CombineArgs<float>& args) { - * if (args.b() > args.a()) { - * // Transfer the B value and its active state. - * args.setResult(args.b()); - * args.setResultIsActive(args.bIsActive()); - * } else { - * // Preserve the A value and its active state. - * args.setResult(args.a()); - * args.setResultIsActive(args.aIsActive()); - * } - * } - * }; - * aTree.combineExtended(bTree, Local::max); - * } - * @endcode - */ + /// Like combine(), but with + /// @param other a tree of the same type as this tree + /// @param op a functor of the form <tt>void op(CombineArgs<ValueType>& args)</tt> that + /// computes <tt>args.setResult(f(args.a(), args.b()))</tt> and, optionally, + /// <tt>args.setResultIsActive(g(args.aIsActive(), args.bIsActive()))</tt> + /// for some functions @c f and @c g + /// @param prune if true, prune the resulting tree one branch at a time (this is usually + /// more space-efficient than pruning the entire tree in one pass) + /// + /// This variant passes not only the @em a and @em b values but also the active states + /// of the @em a and @em b values to the functor, which may then return, by calling + /// @c args.setResultIsActive(), a computed active state for the result value. + /// By default, the result is active if either the @em a or the @em b value is active. + /// + /// @see openvdb/Types.h for the definition of the CombineArgs struct. + /// + /// @par Example: + /// Replace voxel values in floating-point @c aTree with corresponding values + /// from floating-point @c bTree (leaving @c bTree empty) wherever the @c bTree + /// values are larger. Also, preserve the active states of any transferred values. + /// @code + /// { + /// struct Local { + /// static inline void max(CombineArgs<float>& args) { + /// if (args.b() > args.a()) { + /// // Transfer the B value and its active state. + /// args.setResult(args.b()); + /// args.setResultIsActive(args.bIsActive()); + /// } else { + /// // Preserve the A value and its active state. + /// args.setResult(args.a()); + /// args.setResultIsActive(args.aIsActive()); + /// } + /// } + /// }; + /// aTree.combineExtended(bTree, Local::max); + /// } + /// @endcode template<typename ExtendedCombineOp> void combineExtended(Tree& other, ExtendedCombineOp& op, bool prune = false); #ifndef _MSC_VER @@ -636,188 +725,218 @@ public: void combineExtended(Tree& other, const ExtendedCombineOp& op, bool prune = false); #endif - /*! For a given function @c f, use sparse traversal to compute <tt>f(a, b)</tt> over all - * corresponding pairs of values (tile or voxel) of trees A and B and store the result - * in this tree. - * @param a,b two trees of the same type - * @param op a functor of the form <tt>void op(const T& a, const T& b, T& result)</tt>, - * where @c T is this tree's @c ValueType, that computes - * <tt>result = f(a, b)</tt> - * @param prune if true, prune the resulting tree one branch at a time (this is usually - * more space-efficient than pruning the entire tree in one pass) - * - * @par Example: - * Compute the per-voxel difference between two floating-point trees, - * @c aTree and @c bTree, and store the result in a third tree. - * @code - * { - * struct Local { - * static inline void diff(const float& a, const float& b, float& result) { - * result = a - b; - * } - * }; - * FloatTree resultTree; - * resultTree.combine2(aTree, bTree, Local::diff); - * } - * @endcode - */ - template<typename CombineOp> - void combine2(const Tree& a, const Tree& b, CombineOp& op, bool prune = false); + /// For a given function @c f, use sparse traversal to compute <tt>f(a, b)</tt> over all + /// corresponding pairs of values (tile or voxel) of trees A and B and store the result + /// in this tree. + /// @param a,b two trees with the same configuration (levels and node dimensions) + /// as this tree but with the B tree possibly having a different value type + /// @param op a functor of the form <tt>void op(const T1& a, const T2& b, T1& result)</tt>, + /// where @c T1 is this tree's and the A tree's @c ValueType and @c T2 is the + /// B tree's @c ValueType, that computes <tt>result = f(a, b)</tt> + /// @param prune if true, prune the resulting tree one branch at a time (this is usually + /// more space-efficient than pruning the entire tree in one pass) + /// + /// @throw TypeError if the B tree's configuration doesn't match this tree's + /// or if this tree's ValueType is not constructible from the B tree's ValueType. + /// + /// @par Example: + /// Compute the per-voxel difference between two floating-point trees, + /// @c aTree and @c bTree, and store the result in a third tree. + /// @code + /// { + /// struct Local { + /// static inline void diff(const float& a, const float& b, float& result) { + /// result = a - b; + /// } + /// }; + /// FloatTree resultTree; + /// resultTree.combine2(aTree, bTree, Local::diff); + /// } + /// @endcode + template<typename CombineOp, typename OtherTreeType /*= Tree*/> + void combine2(const Tree& a, const OtherTreeType& b, CombineOp& op, bool prune = false); #ifndef _MSC_VER - template<typename CombineOp> - void combine2(const Tree& a, const Tree& b, const CombineOp& op, bool prune = false); + template<typename CombineOp, typename OtherTreeType /*= Tree*/> + void combine2(const Tree& a, const OtherTreeType& b, const CombineOp& op, bool prune = false); #endif - /*! Like combine2(), but with - * @param a,b two trees of the same type - * @param op a functor of the form <tt>void op(CombineArgs<ValueType>& args)</tt> that - * computes <tt>args.setResult(f(args.a(), args.b()))</tt> and, optionally, - * <tt>args.setResultIsActive(g(args.aIsActive(), args.bIsActive()))</tt> - * for some functions @c f and @c g - * @param prune if true, prune the resulting tree one branch at a time (this is usually - * more space-efficient than pruning the entire tree in one pass) - * This variant passes not only the @em a and @em b values but also the active states - * of the @em a and @em b values to the functor, which may then return, by calling - * <tt>args.setResultIsActive()</tt>, a computed active state for the result value. - * By default, the result is active if either the @em a or the @em b value is active. - * - * @see openvdb/Types.h for the definition of the CombineArgs struct. - * - * @par Example: - * Compute the per-voxel maximum values of two floating-point trees, @c aTree - * and @c bTree, and store the result in a third tree. Set the active state - * of each output value to that of the larger of the two input values. - * @code - * { - * struct Local { - * static inline void max(CombineArgs<float>& args) { - * if (args.b() > args.a()) { - * // Transfer the B value and its active state. - * args.setResult(args.b()); - * args.setResultIsActive(args.bIsActive()); - * } else { - * // Preserve the A value and its active state. - * args.setResult(args.a()); - * args.setResultIsActive(args.aIsActive()); - * } - * } - * }; - * FloatTree resultTree; - * resultTree.combine2Extended(aTree, bTree, Local::max); - * } - * @endcode - */ - template<typename ExtendedCombineOp> - void combine2Extended(const Tree& a, const Tree& b, ExtendedCombineOp& op, + /// Like combine2(), but with + /// @param a,b two trees with the same configuration (levels and node dimensions) + /// as this tree but with the B tree possibly having a different value type + /// @param op a functor of the form <tt>void op(CombineArgs<T1, T2>& args)</tt>, where + /// @c T1 is this tree's and the A tree's @c ValueType and @c T2 is the B tree's + /// @c ValueType, that computes <tt>args.setResult(f(args.a(), args.b()))</tt> + /// and, optionally, + /// <tt>args.setResultIsActive(g(args.aIsActive(), args.bIsActive()))</tt> + /// for some functions @c f and @c g + /// @param prune if true, prune the resulting tree one branch at a time (this is usually + /// more space-efficient than pruning the entire tree in one pass) + /// This variant passes not only the @em a and @em b values but also the active states + /// of the @em a and @em b values to the functor, which may then return, by calling + /// <tt>args.setResultIsActive()</tt>, a computed active state for the result value. + /// By default, the result is active if either the @em a or the @em b value is active. + /// + /// @throw TypeError if the B tree's configuration doesn't match this tree's + /// or if this tree's ValueType is not constructible from the B tree's ValueType. + /// + /// @see openvdb/Types.h for the definition of the CombineArgs struct. + /// + /// @par Example: + /// Compute the per-voxel maximum values of two single-precision floating-point trees, + /// @c aTree and @c bTree, and store the result in a third tree. Set the active state + /// of each output value to that of the larger of the two input values. + /// @code + /// { + /// struct Local { + /// static inline void max(CombineArgs<float>& args) { + /// if (args.b() > args.a()) { + /// // Transfer the B value and its active state. + /// args.setResult(args.b()); + /// args.setResultIsActive(args.bIsActive()); + /// } else { + /// // Preserve the A value and its active state. + /// args.setResult(args.a()); + /// args.setResultIsActive(args.aIsActive()); + /// } + /// } + /// }; + /// FloatTree aTree = ...; + /// FloatTree bTree = ...; + /// FloatTree resultTree; + /// resultTree.combine2Extended(aTree, bTree, Local::max); + /// } + /// @endcode + /// + /// @par Example: + /// Compute the per-voxel maximum values of a double-precision and a single-precision + /// floating-point tree, @c aTree and @c bTree, and store the result in a third, + /// double-precision tree. Set the active state of each output value to that of + /// the larger of the two input values. + /// @code + /// { + /// struct Local { + /// static inline void max(CombineArgs<double, float>& args) { + /// if (args.b() > args.a()) { + /// // Transfer the B value and its active state. + /// args.setResult(args.b()); + /// args.setResultIsActive(args.bIsActive()); + /// } else { + /// // Preserve the A value and its active state. + /// args.setResult(args.a()); + /// args.setResultIsActive(args.aIsActive()); + /// } + /// } + /// }; + /// DoubleTree aTree = ...; + /// FloatTree bTree = ...; + /// DoubleTree resultTree; + /// resultTree.combine2Extended(aTree, bTree, Local::max); + /// } + /// @endcode + template<typename ExtendedCombineOp, typename OtherTreeType /*= Tree*/> + void combine2Extended(const Tree& a, const OtherTreeType& b, ExtendedCombineOp& op, bool prune = false); #ifndef _MSC_VER - template<typename ExtendedCombineOp> - void combine2Extended(const Tree& a, const Tree& b, const ExtendedCombineOp&, + template<typename ExtendedCombineOp, typename OtherTreeType /*= Tree*/> + void combine2Extended(const Tree& a, const OtherTreeType& b, const ExtendedCombineOp&, bool prune = false); #endif - /*! For a given function use sparse traversal to call it with - * bounding box information for all active tiles and leaf nodes - * or active voxels in the tree. - * - * @note The bounding boxes are guarenteed to be non-overlapping. - * @param op a template functor of the form - * <tt>template<Index LEVEL> void op(const - * CoordBBox& bbox)</tt>, where <tt>bbox</tt> - * defines the bbox of an active tile if <tt>LEVEL>0</tt>, - * and else a LeafNode or active voxel. The functor - * must also provide a template method of the form - * <tt>template<Index LEVEL> bool descent()</tt> - * that returns false if no bboxes - * are to be derived below the templated tree level. In - * such cases of early tree termination a bbox is - * instead derived from each terminating child node. - * - * - * @par Example: - * Render all active tiles and leaf nodes in a tree. Note in - * this example descent returns false if LEVEL==0 which means - * the functor will never descent to the active voxels. In - * other words the smallest BBoxes correspond to LeafNodes or - * active tiles at LEVEL=1! - * @code - * { - * struct RenderTilesAndLeafs { - * template<Index LEVEL> - * inline bool descent() { return LEVEL>0; }//only descent to leaf nodes - * //inline bool descent() { return true; }//use this to decent to voxels - * - * template<Index LEVEL> - * inline void operator()(const CoordBBox &bbox) { - * if (LEVEL>0) { - * // code to render active tile - * } else { - * // code to render leaf node - * } - * } - * }; - * RenderTilesAndLeafs op; - * aTree.visitActiveBBox(op); - * } - * @endcode - * @see openvdb/unittest/TestTree.cc for another example. - */ + /// @brief Use sparse traversal to call the given functor with bounding box + /// information for all active tiles and leaf nodes or active voxels in the tree. + /// + /// @note The bounding boxes are guaranteed to be non-overlapping. + /// @param op a functor with a templated call operator of the form + /// <tt>template<Index LEVEL> void operator()(const CoordBBox& bbox)</tt>, + /// where <tt>bbox</tt> is the bounding box of either an active tile + /// (if @c LEVEL > 0), a leaf node or an active voxel. + /// The functor must also provide a templated method of the form + /// <tt>template<Index LEVEL> bool descent()</tt> that returns @c false + /// if bounding boxes below the specified tree level are not to be visited. + /// In such cases of early tree termination, a bounding box is instead + /// derived from each terminating child node. + /// + /// @par Example: + /// Visit and process all active tiles and leaf nodes in a tree, but don't + /// descend to the active voxels. The smallest bounding boxes that will be + /// visited are those of leaf nodes or level-1 active tiles. + /// @code + /// { + /// struct ProcessTilesAndLeafNodes { + /// // Descend to leaf nodes, but no further. + /// template<Index LEVEL> inline bool descent() { return LEVEL > 0; } + /// // Use this version to descend to voxels: + /// //template<Index LEVEL> inline bool descent() { return true; } + /// + /// template<Index LEVEL> + /// inline void operator()(const CoordBBox &bbox) { + /// if (LEVEL > 0) { + /// // code to process an active tile + /// } else { + /// // code to process a leaf node + /// } + /// } + /// }; + /// ProcessTilesAndLeafNodes op; + /// aTree.visitActiveBBox(op); + /// } + /// @endcode + /// @see openvdb/unittest/TestTree.cc for another example. template<typename BBoxOp> void visitActiveBBox(BBoxOp& op) const { mRoot.visitActiveBBox(op); } - /*! Traverse this tree in depth-first order, and at each node call the given functor - * with a @c DenseIterator (see Iterator.h) that points to either a child node or a - * tile value. If the iterator points to a child node and the functor returns true, - * do not descend to the child node; instead, continue the traversal at the next - * iterator position. - * @param op a functor of the form <tt>template<typename IterT> bool op(IterT&)</tt>, - * where @c IterT is either a RootNode::ChildAllIter, - * an InternalNode::ChildAllIter or a LeafNode::ChildAllIter - * - * @note There is no iterator that points to a RootNode, so to visit the root node, - * retrieve the @c parent() of a RootNode::ChildAllIter. - * - * @par Example: - * Print information about the nodes and tiles of a tree, but not individual voxels. - * @code - * namespace { - * template<typename TreeT> - * struct PrintTreeVisitor - * { - * typedef typename TreeT::RootNodeType RootT; - * bool visitedRoot; - * - * PrintTreeVisitor(): visitedRoot(false) {} - * - * template<typename IterT> - * inline bool operator()(IterT& iter) - * { - * if (!visitedRoot && iter.parent().getLevel() == RootT::LEVEL) { - * visitedRoot = true; - * std::cout << "Level-" << RootT::LEVEL << " node" << std::endl; - * } - * typename IterT::NonConstValueType value; - * typename IterT::ChildNodeType* child = iter.probeChild(value); - * if (child == NULL) { - * std::cout << "Tile with value " << value << std::endl; - * return true; // no child to visit, so stop descending - * } - * std::cout << "Level-" << child->getLevel() << " node" << std::endl; - * return (child->getLevel() == 0); // don't visit leaf nodes - * } - * - * // The generic method, above, calls iter.probeChild(), which is not defined - * // for LeafNode::ChildAllIter. These overloads ensure that the generic - * // method template doesn't get instantiated for LeafNode iterators. - * bool operator()(typename TreeT::LeafNodeType::ChildAllIter&) { return true; } - * bool operator()(typename TreeT::LeafNodeType::ChildAllCIter&) { return true; } - * }; - * } - * { - * PrintTreeVisitor visitor; - * tree.visit(visitor); - * } - * @endcode - */ + /// Traverse this tree in depth-first order, and at each node call the given functor + /// with a @c DenseIterator (see Iterator.h) that points to either a child node or a + /// tile value. If the iterator points to a child node and the functor returns true, + /// do not descend to the child node; instead, continue the traversal at the next + /// iterator position. + /// @param op a functor of the form <tt>template<typename IterT> bool op(IterT&)</tt>, + /// where @c IterT is either a RootNode::ChildAllIter, + /// an InternalNode::ChildAllIter or a LeafNode::ChildAllIter + /// + /// @note There is no iterator that points to a RootNode, so to visit the root node, + /// retrieve the @c parent() of a RootNode::ChildAllIter. + /// + /// @par Example: + /// Print information about the nodes and tiles of a tree, but not individual voxels. + /// @code + /// namespace { + /// template<typename TreeT> + /// struct PrintTreeVisitor + /// { + /// typedef typename TreeT::RootNodeType RootT; + /// bool visitedRoot; + /// + /// PrintTreeVisitor(): visitedRoot(false) {} + /// + /// template<typename IterT> + /// inline bool operator()(IterT& iter) + /// { + /// if (!visitedRoot && iter.parent().getLevel() == RootT::LEVEL) { + /// visitedRoot = true; + /// std::cout << "Level-" << RootT::LEVEL << " node" << std::endl; + /// } + /// typename IterT::NonConstValueType value; + /// typename IterT::ChildNodeType* child = iter.probeChild(value); + /// if (child == NULL) { + /// std::cout << "Tile with value " << value << std::endl; + /// return true; // no child to visit, so stop descending + /// } + /// std::cout << "Level-" << child->getLevel() << " node" << std::endl; + /// return (child->getLevel() == 0); // don't visit leaf nodes + /// } + /// + /// // The generic method, above, calls iter.probeChild(), which is not defined + /// // for LeafNode::ChildAllIter. These overloads ensure that the generic + /// // method template doesn't get instantiated for LeafNode iterators. + /// bool operator()(typename TreeT::LeafNodeType::ChildAllIter&) { return true; } + /// bool operator()(typename TreeT::LeafNodeType::ChildAllCIter&) { return true; } + /// }; + /// } + /// { + /// PrintTreeVisitor visitor; + /// tree.visit(visitor); + /// } + /// @endcode template<typename VisitorOp> void visit(VisitorOp& op); template<typename VisitorOp> void visit(const VisitorOp& op); @@ -828,54 +947,53 @@ public: template<typename VisitorOp> void visit(VisitorOp& op) const; template<typename VisitorOp> void visit(const VisitorOp& op) const; - /*! Traverse this tree and another tree in depth-first order, and for corresponding - * subregions of index space call the given functor with two @c DenseIterators - * (see Iterator.h), each of which points to either a child node or a tile value - * of this tree and the other tree. If the A iterator points to a child node - * and the functor returns a nonzero value with bit 0 set (e.g., 1), do not descend - * to the child node; instead, continue the traversal at the next A iterator position. - * Similarly, if the B iterator points to a child node and the functor returns a value - * with bit 1 set (e.g., 2), continue the traversal at the next B iterator position. - * @note The other tree must have the same index space and fan-out factors as - * this tree, but it may have a different @c ValueType and a different topology. - * @param other a tree of the same type as this tree - * @param op a functor of the form - * <tt>template<class AIterT, class BIterT> int op(AIterT&, BIterT&)</tt>, - * where @c AIterT and @c BIterT are any combination of a - * RootNode::ChildAllIter, an InternalNode::ChildAllIter or a - * LeafNode::ChildAllIter with an @c OtherTreeType::RootNode::ChildAllIter, - * an @c OtherTreeType::InternalNode::ChildAllIter - * or an @c OtherTreeType::LeafNode::ChildAllIter - * - * @par Example: - * Given two trees of the same type, @c aTree and @c bTree, replace leaf nodes of - * @c aTree with corresponding leaf nodes of @c bTree, leaving @c bTree partially empty. - * @code - * namespace { - * template<typename AIterT, typename BIterT> - * inline int stealLeafNodes(AIterT& aIter, BIterT& bIter) - * { - * typename AIterT::NonConstValueType aValue; - * typename AIterT::ChildNodeType* aChild = aIter.probeChild(aValue); - * typename BIterT::NonConstValueType bValue; - * typename BIterT::ChildNodeType* bChild = bIter.probeChild(bValue); - * - * const Index aLevel = aChild->getLevel(), bLevel = bChild->getLevel(); - * if (aChild && bChild && aLevel == 0 && bLevel == 0) { // both are leaf nodes - * aIter.setChild(bChild); // give B's child to A - * bIter.setValue(bValue); // replace B's child with a constant tile value - * } - * // Don't iterate over leaf node voxels of either A or B. - * int skipBranch = (aLevel == 0) ? 1 : 0; - * if (bLevel == 0) skipBranch = skipBranch | 2; - * return skipBranch; - * } - * } - * { - * aTree.visit2(bTree, stealLeafNodes); - * } - * @endcode - */ + /// Traverse this tree and another tree in depth-first order, and for corresponding + /// subregions of index space call the given functor with two @c DenseIterators + /// (see Iterator.h), each of which points to either a child node or a tile value + /// of this tree and the other tree. If the A iterator points to a child node + /// and the functor returns a nonzero value with bit 0 set (e.g., 1), do not descend + /// to the child node; instead, continue the traversal at the next A iterator position. + /// Similarly, if the B iterator points to a child node and the functor returns a value + /// with bit 1 set (e.g., 2), continue the traversal at the next B iterator position. + /// @note The other tree must have the same index space and fan-out factors as + /// this tree, but it may have a different @c ValueType and a different topology. + /// @param other a tree of the same type as this tree + /// @param op a functor of the form + /// <tt>template<class AIterT, class BIterT> int op(AIterT&, BIterT&)</tt>, + /// where @c AIterT and @c BIterT are any combination of a + /// RootNode::ChildAllIter, an InternalNode::ChildAllIter or a + /// LeafNode::ChildAllIter with an @c OtherTreeType::RootNode::ChildAllIter, + /// an @c OtherTreeType::InternalNode::ChildAllIter + /// or an @c OtherTreeType::LeafNode::ChildAllIter + /// + /// @par Example: + /// Given two trees of the same type, @c aTree and @c bTree, replace leaf nodes of + /// @c aTree with corresponding leaf nodes of @c bTree, leaving @c bTree partially empty. + /// @code + /// namespace { + /// template<typename AIterT, typename BIterT> + /// inline int stealLeafNodes(AIterT& aIter, BIterT& bIter) + /// { + /// typename AIterT::NonConstValueType aValue; + /// typename AIterT::ChildNodeType* aChild = aIter.probeChild(aValue); + /// typename BIterT::NonConstValueType bValue; + /// typename BIterT::ChildNodeType* bChild = bIter.probeChild(bValue); + /// + /// const Index aLevel = aChild->getLevel(), bLevel = bChild->getLevel(); + /// if (aChild && bChild && aLevel == 0 && bLevel == 0) { // both are leaf nodes + /// aIter.setChild(bChild); // give B's child to A + /// bIter.setValue(bValue); // replace B's child with a constant tile value + /// } + /// // Don't iterate over leaf node voxels of either A or B. + /// int skipBranch = (aLevel == 0) ? 1 : 0; + /// if (bLevel == 0) skipBranch = skipBranch | 2; + /// return skipBranch; + /// } + /// } + /// { + /// aTree.visit2(bTree, stealLeafNodes); + /// } + /// @endcode template<typename OtherTreeType, typename VisitorOp> void visit2(OtherTreeType& other, VisitorOp& op); template<typename OtherTreeType, typename VisitorOp> @@ -1003,16 +1121,37 @@ protected: }; // end of Tree class -/// Tree4<T, N1, N2, N3>::Type is the type of a four-level tree +/// @brief Tree3<T, N1, N2>::Type is the type of a three-level tree +/// (Root, Internal, Leaf) with value type T and +/// internal and leaf node log dimensions N1 and N2, respectively. +/// @note This is NOT the standard tree configuration (Tree4 is). +template<typename T, Index N1, Index N2> +struct Tree3 { + typedef Tree<RootNode<InternalNode<LeafNode<T, N2>, N1> > > Type; +}; + + +/// @brief Tree4<T, N1, N2, N3>::Type is the type of a four-level tree /// (Root, Internal, Internal, Leaf) with value type T and /// internal and leaf node log dimensions N1, N2 and N3, respectively. -/// This is the standard tree configuration. +/// @note This is the standard tree configuration. template<typename T, Index N1, Index N2, Index N3> struct Tree4 { typedef Tree<RootNode<InternalNode<InternalNode<LeafNode<T, N3>, N2>, N1> > > Type; }; +/// @brief Tree5<T, N1, N2, N3, N4>::Type is the type of a five-level tree +/// (Root, Internal, Internal, Internal, Leaf) with value type T and +/// internal and leaf node log dimensions N1, N2, N3 and N4, respectively. +/// @note This is NOT the standard tree configuration (Tree4 is). +template<typename T, Index N1, Index N2, Index N3, Index N4> +struct Tree5 { + typedef Tree<RootNode<InternalNode<InternalNode<InternalNode<LeafNode<T, N4>, N3>, N2>, N1> > > + Type; +}; + + //////////////////////////////////////// @@ -1358,26 +1497,20 @@ Tree<RootNodeType>::setValueOn(const Coord& xyz, const ValueType& value) template<typename RootNodeType> +template<typename ModifyOp> inline void -Tree<RootNodeType>::setValueOnMin(const Coord& xyz, const ValueType& value) +Tree<RootNodeType>::modifyValue(const Coord& xyz, const ModifyOp& op) { - mRoot.setValueOnMin(xyz, value); + mRoot.modifyValue(xyz, op); } template<typename RootNodeType> +template<typename ModifyOp> inline void -Tree<RootNodeType>::setValueOnMax(const Coord& xyz, const ValueType& value) +Tree<RootNodeType>::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) { - mRoot.setValueOnMax(xyz, value); -} - - -template<typename RootNodeType> -inline void -Tree<RootNodeType>::setValueOnSum(const Coord& xyz, const ValueType& value) -{ - mRoot.setValueOnSum(xyz, value); + mRoot.modifyValueAndActiveState(xyz, op); } @@ -1445,6 +1578,7 @@ Tree<RootNodeType>::addTile(Index level, const Coord& xyz, mRoot.addTile(level, xyz, value, active); } + template<typename RootNodeType> template<typename NodeT> inline NodeT* @@ -1454,6 +1588,7 @@ Tree<RootNodeType>::stealNode(const Coord& xyz, const ValueType& value, bool act return mRoot.template stealNode<NodeT>(xyz, value, active); } + template<typename RootNodeType> inline typename RootNodeType::LeafNodeType* Tree<RootNodeType>::touchLeaf(const Coord& xyz) @@ -1478,6 +1613,33 @@ Tree<RootNodeType>::probeConstLeaf(const Coord& xyz) const } +template<typename RootNodeType> +template<typename NodeType> +inline NodeType* +Tree<RootNodeType>::probeNode(const Coord& xyz) +{ + return mRoot.template probeNode<NodeType>(xyz); +} + + +template<typename RootNodeType> +template<typename NodeType> +inline const NodeType* +Tree<RootNodeType>::probeNode(const Coord& xyz) const +{ + return this->template probeConstNode<NodeType>(xyz); +} + + +template<typename RootNodeType> +template<typename NodeType> +inline const NodeType* +Tree<RootNodeType>::probeConstNode(const Coord& xyz) const +{ + return mRoot.template probeConstNode<NodeType>(xyz); +} + + //////////////////////////////////////// @@ -1519,20 +1681,27 @@ Tree<RootNodeType>::getBackgroundValue() const template<typename RootNodeType> inline void -Tree<RootNodeType>::merge(Tree& other) +Tree<RootNodeType>::voxelizeActiveTiles() { this->clearAllAccessors(); - other.clearAllAccessors(); - mRoot.merge(other.mRoot); + mRoot.voxelizeActiveTiles(); } template<typename RootNodeType> inline void -Tree<RootNodeType>::voxelizeActiveTiles() +Tree<RootNodeType>::merge(Tree& other, MergePolicy policy) { this->clearAllAccessors(); - mRoot.voxelizeActiveTiles(); + other.clearAllAccessors(); + switch (policy) { + case MERGE_ACTIVE_STATES: + mRoot.template merge<MERGE_ACTIVE_STATES>(other.mRoot); break; + case MERGE_NODES: + mRoot.template merge<MERGE_NODES>(other.mRoot); break; + case MERGE_ACTIVE_STATES_AND_NODES: + mRoot.template merge<MERGE_ACTIVE_STATES_AND_NODES>(other.mRoot); break; + } } @@ -1542,21 +1711,38 @@ inline void Tree<RootNodeType>::topologyUnion(const Tree<OtherRootNodeType>& other) { this->clearAllAccessors(); - mRoot.topologyUnion(other.getRootNode()); + mRoot.topologyUnion(other.root()); } +template<typename RootNodeType> +template<typename OtherRootNodeType> +inline void +Tree<RootNodeType>::topologyIntersection(const Tree<OtherRootNodeType>& other) +{ + this->clearAllAccessors(); + mRoot.topologyIntersection(other.root()); +} + +template<typename RootNodeType> +template<typename OtherRootNodeType> +inline void +Tree<RootNodeType>::topologyDifference(const Tree<OtherRootNodeType>& other) +{ + this->clearAllAccessors(); + mRoot.topologyDifference(other.root()); +} //////////////////////////////////////// /// @brief Helper class to adapt a three-argument (a, b, result) CombineOp functor /// into a single-argument functor that accepts a CombineArgs struct -template<typename ValueT, typename CombineOp> +template<typename AValueT, typename CombineOp, typename BValueT = AValueT> struct CombineOpAdapter { - CombineOpAdapter(CombineOp& op): op(op) {} + CombineOpAdapter(CombineOp& _op): op(_op) {} - void operator()(CombineArgs<ValueT>& args) const { + void operator()(CombineArgs<AValueT, BValueT>& args) const { op(args.a(), args.b(), args.result()); } @@ -1594,7 +1780,7 @@ inline void Tree<RootNodeType>::combineExtended(Tree& other, ExtendedCombineOp& op, bool prune) { this->clearAllAccessors(); - mRoot.combine(other.getRootNode(), op, prune); + mRoot.combine(other.root(), op, prune); } @@ -1613,11 +1799,11 @@ Tree<RootNodeType>::combineExtended(Tree& other, const ExtendedCombineOp& op, bo template<typename RootNodeType> -template<typename CombineOp> +template<typename CombineOp, typename OtherTreeType> inline void -Tree<RootNodeType>::combine2(const Tree& a, const Tree& b, CombineOp& op, bool prune) +Tree<RootNodeType>::combine2(const Tree& a, const OtherTreeType& b, CombineOp& op, bool prune) { - CombineOpAdapter<ValueType, CombineOp> extendedOp(op); + CombineOpAdapter<ValueType, CombineOp, typename OtherTreeType::ValueType> extendedOp(op); this->combine2Extended(a, b, extendedOp, prune); } @@ -1626,38 +1812,39 @@ Tree<RootNodeType>::combine2(const Tree& a, const Tree& b, CombineOp& op, bool p /// code like this: <tt>tree.combine2(aTree, bTree, MyCombineOp(...))</tt>. #ifndef _MSC_VER template<typename RootNodeType> -template<typename CombineOp> +template<typename CombineOp, typename OtherTreeType> inline void -Tree<RootNodeType>::combine2(const Tree& a, const Tree& b, const CombineOp& op, bool prune) +Tree<RootNodeType>::combine2(const Tree& a, const OtherTreeType& b, const CombineOp& op, bool prune) { - CombineOpAdapter<ValueType, const CombineOp> extendedOp(op); + CombineOpAdapter<ValueType, const CombineOp, typename OtherTreeType::ValueType> extendedOp(op); this->combine2Extended(a, b, extendedOp, prune); } #endif template<typename RootNodeType> -template<typename ExtendedCombineOp> +template<typename ExtendedCombineOp, typename OtherTreeType> inline void -Tree<RootNodeType>::combine2Extended(const Tree& a, const Tree& b, +Tree<RootNodeType>::combine2Extended(const Tree& a, const OtherTreeType& b, ExtendedCombineOp& op, bool prune) { this->clearAllAccessors(); - mRoot.combine2(a.mRoot, b.mRoot, op, prune); + mRoot.combine2(a.root(), b.root(), op, prune); } /// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate -/// code like this: <tt>tree.combine2Extended(aTree, bTree, MyCombineOp(...))</tt>. +/// code like the following, where the functor argument is a temporary: +/// <tt>tree.combine2Extended(aTree, bTree, MyCombineOp(...))</tt>. #ifndef _MSC_VER template<typename RootNodeType> -template<typename ExtendedCombineOp> +template<typename ExtendedCombineOp, typename OtherTreeType> inline void -Tree<RootNodeType>::combine2Extended(const Tree& a, const Tree& b, +Tree<RootNodeType>::combine2Extended(const Tree& a, const OtherTreeType& b, const ExtendedCombineOp& op, bool prune) { this->clearAllAccessors(); - mRoot.template combine2<const ExtendedCombineOp>(a.mRoot, b.mRoot, op, prune); + mRoot.template combine2<const ExtendedCombineOp>(a.root(), b.root(), op, prune); } #endif @@ -1717,7 +1904,7 @@ Tree<RootNodeType>::visit2(OtherTreeType& other, VisitorOp& op) { this->clearAllAccessors(); typedef typename OtherTreeType::RootNodeType OtherRootNodeType; - mRoot.template visit2<OtherRootNodeType, VisitorOp>(other.getRootNode(), op); + mRoot.template visit2<OtherRootNodeType, VisitorOp>(other.root(), op); } @@ -1727,7 +1914,7 @@ inline void Tree<RootNodeType>::visit2(OtherTreeType& other, VisitorOp& op) const { typedef typename OtherTreeType::RootNodeType OtherRootNodeType; - mRoot.template visit2<OtherRootNodeType, VisitorOp>(other.getRootNode(), op); + mRoot.template visit2<OtherRootNodeType, VisitorOp>(other.root(), op); } @@ -1740,7 +1927,7 @@ Tree<RootNodeType>::visit2(OtherTreeType& other, const VisitorOp& op) { this->clearAllAccessors(); typedef typename OtherTreeType::RootNodeType OtherRootNodeType; - mRoot.template visit2<OtherRootNodeType, const VisitorOp>(other.getRootNode(), op); + mRoot.template visit2<OtherRootNodeType, const VisitorOp>(other.root(), op); } @@ -1752,7 +1939,7 @@ inline void Tree<RootNodeType>::visit2(OtherTreeType& other, const VisitorOp& op) const { typedef typename OtherTreeType::RootNodeType OtherRootNodeType; - mRoot.template visit2<OtherRootNodeType, const VisitorOp>(other.getRootNode(), op); + mRoot.template visit2<OtherRootNodeType, const VisitorOp>(other.root(), op); } @@ -1773,8 +1960,7 @@ Tree<RootNodeType>::treeType() ostr << "_" << dims[i]; } Name* s = new Name(ostr.str()); - sTypeName.compare_and_swap(s, NULL); - if (sTypeName != s) delete s; + if (sTypeName.compare_and_swap(s, NULL) != NULL) delete s; } return *sTypeName; } @@ -1785,7 +1971,7 @@ template<typename OtherRootNodeType> inline bool Tree<RootNodeType>::hasSameTopology(const Tree<OtherRootNodeType>& other) const { - return mRoot.hasSameTopology(other.getRootNode()); + return mRoot.hasSameTopology(other.root()); } @@ -1807,31 +1993,24 @@ template<typename RootNodeType> inline bool Tree<RootNodeType>::evalLeafBoundingBox(CoordBBox& bbox) const { - if (this->empty()) { - bbox = CoordBBox(); // return default bbox - return false;// empty - } + bbox.reset(); // default invalid bbox + + if (this->empty()) return false; // empty - bbox.min() = Coord::max(); - bbox.max() = -Coord::max(); + mRoot.evalActiveBoundingBox(bbox, false); - Coord ijk; - for (LeafCIter bIter(*this); bIter; ++bIter) { - bIter->getOrigin(ijk); - bbox.expand(ijk); - } - bbox.max() += Coord(LeafNodeType::dim()-1); - return true; // not empty + return true;// not empty } template<typename RootNodeType> inline bool Tree<RootNodeType>::evalActiveVoxelBoundingBox(CoordBBox& bbox) const { - bbox = CoordBBox(); // default invalid bbox + bbox.reset(); // default invalid bbox + if (this->empty()) return false; // empty - mRoot.evalActiveVoxelBoundingBox(bbox); + mRoot.evalActiveBoundingBox(bbox, true); return true;// not empty } @@ -1893,7 +2072,7 @@ Tree<RootNodeType>::print(std::ostream& os, int verboseLevel) const struct OnExit { std::ostream& os; std::streamsize savedPrecision; - OnExit(std::ostream& os): os(os), savedPrecision(os.precision()) {} + OnExit(std::ostream& _os): os(_os), savedPrecision(os.precision()) {} ~OnExit() { os.precision(savedPrecision); } }; OnExit restorePrecision(os); diff --git a/extern/openvdb/internal/openvdb/tree/TreeIterator.h b/extern/openvdb/internal/openvdb/tree/TreeIterator.h index 7741571e972..7d5cfba25b0 100644 --- a/extern/openvdb/internal/openvdb/tree/TreeIterator.h +++ b/extern/openvdb/internal/openvdb/tree/TreeIterator.h @@ -401,6 +401,14 @@ public: if (lvl == Level) mIter.setValueOff(); else mNext.setValueOff(lvl); } + /// @brief Apply a functor to the item to which this iterator is pointing. + /// @note Not valid when @c IterT is a const iterator type + template<typename ModifyOp> + void modifyValue(Index lvl, const ModifyOp& op) const + { + if (lvl == Level) mIter.modifyValue(op); else mNext.modifyValue(lvl, op); + } + private: typedef typename boost::mpl::pop_front<NodeVecT>::type RestT; // NodeVecT minus its first item typedef IterListItem<IterListItem, RestT, VecSize - 1, Level + 1> NextItem; @@ -518,6 +526,12 @@ public: if (lvl == 0) mIter.setValueOff(); else mNext.setValueOff(lvl); } + template<typename ModifyOp> + void modifyValue(Index lvl, const ModifyOp& op) const + { + if (lvl == 0) mIter.modifyValue(op); else mNext.modifyValue(lvl, op); + } + private: typedef typename boost::mpl::pop_front<NodeVecT>::type RestT; // NodeVecT minus its first item typedef IterListItem<IterListItem, RestT, VecSize - 1, /*Level=*/1> NextItem; @@ -622,6 +636,12 @@ public: void setValueOn(Index lvl, bool on = true) const { if (lvl == Level) mIter.setValueOn(on); } void setValueOff(Index lvl) const { if (lvl == Level) mIter.setValueOff(); } + template<typename ModifyOp> + void modifyValue(Index lvl, const ModifyOp& op) const + { + if (lvl == Level) mIter.modifyValue(op); + } + private: IterT mIter; PrevItemT* mPrev; @@ -724,6 +744,14 @@ public: /// Mark the tile or voxel value to which this iterator is currently pointing as inactive. void setValueOff() const { mValueIterList.setValueOff(mLevel); } + /// @brief Apply a functor to the item to which this iterator is pointing. + /// (Not valid for const iterators.) + /// @param op a functor of the form <tt>void op(ValueType&) const</tt> that modifies + /// its argument in place + /// @see Tree::modifyValue() + template<typename ModifyOp> + void modifyValue(const ModifyOp& op) const { mValueIterList.modifyValue(mLevel, op); } + /// Return a pointer to the tree over which this iterator is iterating. TreeT* getTree() const { return mTree; } @@ -755,8 +783,8 @@ TreeValueIteratorBase<TreeT, ValueIterT>::TreeValueIteratorBase(TreeT& tree): mMaxLevel(int(ROOT_LEVEL)), mTree(&tree) { - mChildIterList.setIter(IterTraits<NodeT, ChildOnIterT>::begin(tree.getRootNode())); - mValueIterList.setIter(IterTraits<NodeT, ValueIterT>::begin(tree.getRootNode())); + mChildIterList.setIter(IterTraits<NodeT, ChildOnIterT>::begin(tree.root())); + mValueIterList.setIter(IterTraits<NodeT, ValueIterT>::begin(tree.root())); this->advance(/*dontIncrement=*/true); } @@ -1039,7 +1067,7 @@ NodeIteratorBase<TreeT, RootChildOnIterT>::NodeIteratorBase(TreeT& tree): mDone(false), mTree(&tree) { - mIterList.setIter(RootIterTraits::begin(tree.getRootNode())); + mIterList.setIter(RootIterTraits::begin(tree.root())); } @@ -1199,7 +1227,7 @@ public: LeafIteratorBase(TreeT& tree): mIterList(NULL), mTree(&tree) { // Initialize the iterator list with a root node iterator. - mIterList.setIter(RootIterTraits::begin(tree.getRootNode())); + mIterList.setIter(RootIterTraits::begin(tree.root())); // Descend along the first branch, initializing the node iterator at each level. Index lvl = ROOT_LEVEL; for ( ; lvl > 0 && mIterList.down(lvl); --lvl) {} diff --git a/extern/openvdb/internal/openvdb/tree/Util.h b/extern/openvdb/internal/openvdb/tree/Util.h index 803bc163dc4..9adb0967e42 100644 --- a/extern/openvdb/internal/openvdb/tree/Util.h +++ b/extern/openvdb/internal/openvdb/tree/Util.h @@ -33,7 +33,8 @@ #ifndef OPENVDB_TREE_UTIL_HAS_BEEN_INCLUDED #define OPENVDB_TREE_UTIL_HAS_BEEN_INCLUDED -#include <openvdb/math/Math.h> // for zeroVal +#include <openvdb/math/Math.h> // for isNegative and negative +#include <openvdb/Types.h> // for Index typedef namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE @@ -104,7 +105,7 @@ struct LevelSetPrune { child.pruneOp(*this); if (!child.isInactive()) return false; - value = child.getFirstValue() < zeroVal<ValueType>() ? -outside : outside; + value = math::isNegative(child.getFirstValue()) ? math::negative(outside) : outside; return true; } diff --git a/extern/openvdb/internal/openvdb/tree/ValueAccessor.h b/extern/openvdb/internal/openvdb/tree/ValueAccessor.h index 29e605c7d4e..46a240a4bf3 100644 --- a/extern/openvdb/internal/openvdb/tree/ValueAccessor.h +++ b/extern/openvdb/internal/openvdb/tree/ValueAccessor.h @@ -86,7 +86,6 @@ template<typename TreeType> class ValueAccessor0; template<typename TreeType, Index L0 = 0> class ValueAccessor1; template<typename TreeType, Index L0 = 0, Index L1 = 1> class ValueAccessor2; template<typename TreeType, Index L0 = 0, Index L1 = 1, Index L2 = 2> class ValueAccessor3; -template<typename HeadT, int HeadLevel> struct InvertedTree; template<typename TreeCacheT, typename NodeVecT, bool AtRoot> class CacheItem; @@ -109,8 +108,13 @@ public: virtual ~ValueAccessorBase() { if (mTree) mTree->releaseAccessor(*this); } - /// @return a pointer to the tree associated by this ValueAccessor + /// @brief Return a pointer to the tree associated with this accessor. + /// @details The pointer will be null only if the tree from which this accessor + /// was constructed was subsequently deleted (which generally leaves the + /// accessor in an unsafe state). TreeType* getTree() const { return mTree; } + /// Return a reference to the tree associated with this accessor. + TreeType& tree() const { assert(mTree); return *mTree; } ValueAccessorBase(const ValueAccessorBase& other): mTree(other.mTree) { @@ -147,35 +151,30 @@ protected: /// (with Grid::getAccessor()) and use the accessor's @c getValue() and /// @c setValue() methods. These will typically be significantly faster /// than accessing voxels directly in the grid's tree. -/// @note If @c MutexType is a TBB-compatible mutex, then multiple threads -/// may safely access a single, shared accessor. However, it is -/// highly recommended that, instead, each thread be assigned its own, -/// non-mutex-protected accessor. /// -/// Conceptually this ValueAccessor is a node-cache with accessor -/// methods. Specefically the tree nodes from a previous access are -/// cached and re-used starting with the LeafNode and moving up -/// throug the node levels of the tree. Thus this node caching -/// essentiall leads to acceleration of spatially coherent -/// access by means of inverted tree traversal! +/// A ValueAccessor caches pointers to tree nodes along the path to a voxel (x, y, z). +/// A subsequent access to voxel (x', y', z') starts from the cached leaf node and +/// moves up until a cached node that encloses (x', y', z') is found, then traverses +/// down the tree from that node to a leaf, updating the cache with the new path. +/// This leads to significant acceleration of spatially-coherent accesses. /// -/// @param _TreeType This is the only template paramter that -/// always has to be specified. -/// @param CacheLevels Used to specify the number of bottom nodes -/// that are cached. The default caches all (non-root) -/// nodes. The maximum allowed number of CacheLevels -/// correspond to the number of non-root nodes, i.e. -/// CacheLevels <= DEPTH-1! -/// @param MutexType This defines the type of mutex-lock and -/// should almost always be left untouched (unless you're -/// and expert!) +/// @param _TreeType the type of the tree to be accessed [required] +/// @param CacheLevels the number of nodes to be cached, starting from the leaf level +/// and not including the root (i.e., CacheLevels < DEPTH), +/// and defaulting to all non-root nodes +/// @param MutexType the type of mutex to use (see note) +/// +/// @note If @c MutexType is a TBB-compatible mutex, then multiple threads may +/// safely access a single, shared accessor. However, it is highly recommended +/// that, instead, each thread be assigned its own, non-mutex-protected accessor. template<typename _TreeType, Index CacheLevels = _TreeType::DEPTH-1, typename MutexType = tbb::null_mutex> class ValueAccessor: public ValueAccessorBase<_TreeType> { public: - BOOST_STATIC_ASSERT(CacheLevels <= _TreeType::DEPTH-1); + BOOST_STATIC_ASSERT(CacheLevels < _TreeType::DEPTH); + typedef _TreeType TreeType; typedef typename TreeType::RootNodeType RootNodeT; typedef typename TreeType::LeafNodeType LeafNodeT; @@ -186,7 +185,7 @@ public: ValueAccessor(TreeType& tree): BaseT(tree), mCache(*this) { - mCache.insert(Coord(), &tree.getRootNode()); + mCache.insert(Coord(), &tree.root()); } ValueAccessor(const ValueAccessor& other): BaseT(other), mCache(*this, other.mCache) {} @@ -201,7 +200,7 @@ public: } virtual ~ValueAccessor() {} - /// Return the number of cache levels employed by this ValueAccessor + /// Return the number of cache levels employed by this accessor. static Index numCacheLevels() { return CacheLevels; } /// Return @c true if nodes along the path to the given voxel have been cached. @@ -247,7 +246,7 @@ public: void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); } //@} - /// Set the value of the voxel at the given coordinate but preserves its active state. + /// Set the value of the voxel at the given coordinate but don't change its active state. void setValueOnly(const Coord& xyz, const ValueType& value) { LockT lock(mMutex); @@ -269,23 +268,34 @@ public: mCache.setValueOff(xyz, value); } - /// Set the value of the voxel at the given coordinates to the sum of its current - /// value and the given value, and mark the voxel as active. - void setValueOnSum(const Coord& xyz, const ValueType& value) + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template<typename ModifyOp> + void modifyValue(const Coord& xyz, const ModifyOp& op) { LockT lock(mMutex); - mCache.setValueOnSum(xyz, value); + mCache.modifyValue(xyz, op); } - /// Set the active state of the voxel at the given coordinates without changing its value. + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template<typename ModifyOp> + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + LockT lock(mMutex); + mCache.modifyValueAndActiveState(xyz, op); + } + + /// Set the active state of the voxel at the given coordinates but don't change its value. void setActiveState(const Coord& xyz, bool on = true) { LockT lock(mMutex); mCache.setActiveState(xyz, on); } - /// Mark the voxel at the given coordinates as active without changing its value. + /// Mark the voxel at the given coordinates as active but don't change its value. void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); } - /// Mark the voxel at the given coordinates as inactive without changing its value. + /// Mark the voxel at the given coordinates as inactive but don't change its value. void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); } /// Return the cached node of type @a NodeType. [Mainly for internal use] @@ -312,7 +322,7 @@ public: /// that node. [Mainly for internal use] template<typename NodeType> void eraseNode() { LockT lock(mMutex); NodeType* node = NULL; mCache.erase(node); } - + /// @brief Add the specified leaf to this tree, possibly creating a child branch /// in the process. If the leaf node already exists, replace it. void addLeaf(LeafNodeT* leaf) @@ -320,54 +330,70 @@ public: LockT lock(mMutex); mCache.addLeaf(leaf); } - - /// @brief Add a tile at the specified tree level that contains - /// xyz, possibly creating a child branch in the process. If a - /// Node that contains xyz already exists it is replaced by a tile. + + /// @brief Add a tile at the specified tree level that contains voxel (x, y, z), + /// possibly deleting existing nodes or creating new nodes in the process. void addTile(Index level, const Coord& xyz, const ValueType& value, bool state) { LockT lock(mMutex); mCache.addTile(level, xyz, value, state); } - - /// @brief @return the leaf node that contains voxel (x, y, z) and - /// if it doesn't exist, create it, but preserve the values and + + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). + /// If no such node exists, create one, but preserve the values and /// active states of all voxels. - /// - /// Use this method to preallocate a static tree topology over which to - /// safely perform multithreaded processing. + /// @details Use this method to preallocate a static tree topology + /// over which to safely perform multithreaded processing. LeafNodeT* touchLeaf(const Coord& xyz) { LockT lock(mMutex); return mCache.touchLeaf(xyz); } - /// @brief @return a pointer to the leaf node that contains - /// voxel (x, y, z) and if it doesn't exist, return NULL. + //@{ + /// @brief Return a pointer to the node of the specified type that contains + /// voxel (x, y, z), or NULL if no such node exists. + template<typename NodeT> + NodeT* probeNode(const Coord& xyz) + { + LockT lock(mMutex); + return mCache.template probeNode<NodeT>(xyz); + } + template<typename NodeT> + const NodeT* probeConstNode(const Coord& xyz) const + { + LockT lock(mMutex); + return mCache.template probeConstNode<NodeT>(xyz); + } + template<typename NodeT> + const NodeT* probeNode(const Coord& xyz) const + { + return this->template probeConstNode<NodeT>(xyz); + } + //@} + + //@{ + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z), + /// or NULL if no such node exists. LeafNodeT* probeLeaf(const Coord& xyz) { LockT lock(mMutex); return mCache.probeLeaf(xyz); } - - /// @brief @return a const pointer to the leaf node that contains - /// voxel (x, y, z) and if it doesn't exist, return NULL. const LeafNodeT* probeConstLeaf(const Coord& xyz) const { LockT lock(mMutex); return mCache.probeConstLeaf(xyz); } - const LeafNodeT* probeLeaf(const Coord& xyz) const - { - return this->probeConstLeaf(xyz); - } + const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); } + //@} /// Remove all nodes from this cache, then reinsert the root node. virtual void clear() { LockT lock(mMutex); mCache.clear(); - if (this->mTree) mCache.insert(Coord(), &(this->mTree->getRootNode())); + if (this->mTree) mCache.insert(Coord(), &(this->mTree->root())); } private: @@ -386,6 +412,7 @@ private: this->BaseT::release(); mCache.clear(); } + /// Cache the given node, which should lie along the path from the root node to /// the node containing voxel (x, y, z). /// @note This operation is not mutex-protected and is intended to be called @@ -394,7 +421,7 @@ private: void insert(const Coord& xyz, NodeType* node) { mCache.insert(xyz, node); } // Define a list of all tree node types from LeafNode to RootNode - typedef typename InvertedTree<RootNodeT, RootNodeT::LEVEL>::Type InvTreeT; + typedef typename RootNodeT::NodeChainType InvTreeT; // Remove all tree node types that are excluded from the cache typedef typename boost::mpl::begin<InvTreeT>::type BeginT; typedef typename boost::mpl::advance<BeginT,boost::mpl::int_<CacheLevels> >::type FirstT; @@ -409,10 +436,9 @@ private: }; // class ValueAccessor -/// Template specialization of the ValueAccessor with no mutex and no cache levels -/// -/// @note This specialization is mosty useful for benchmark comparisions -/// since the cached versions (above) are always expected to be faster. +/// @brief Template specialization of the ValueAccessor with no mutex and no cache levels +/// @details This specialization is provided mainly for benchmarking. +/// Accessors with caching will almost always be faster. template<typename TreeType> struct ValueAccessor<TreeType, 0, tbb::null_mutex>: public ValueAccessor0<TreeType> { @@ -422,7 +448,7 @@ struct ValueAccessor<TreeType, 0, tbb::null_mutex>: public ValueAccessor0<TreeTy }; -/// Template specialization of the ValueAccessor with no mutex and 1 cache level +/// Template specialization of the ValueAccessor with no mutex and one cache level template<typename TreeType> struct ValueAccessor<TreeType, 1, tbb::null_mutex>: public ValueAccessor1<TreeType> { @@ -432,7 +458,7 @@ struct ValueAccessor<TreeType, 1, tbb::null_mutex>: public ValueAccessor1<TreeTy }; -/// Template specialization of the ValueAccessor with no mutex and 2 cache levels +/// Template specialization of the ValueAccessor with no mutex and two cache levels template<typename TreeType> struct ValueAccessor<TreeType, 2, tbb::null_mutex>: public ValueAccessor2<TreeType> { @@ -442,7 +468,7 @@ struct ValueAccessor<TreeType, 2, tbb::null_mutex>: public ValueAccessor2<TreeTy }; -/// Template specialization of the ValueAccessor with no mutex and 3 cache levels +/// Template specialization of the ValueAccessor with no mutex and three cache levels template<typename TreeType> struct ValueAccessor<TreeType, 3, tbb::null_mutex>: public ValueAccessor3<TreeType> { @@ -455,10 +481,14 @@ struct ValueAccessor<TreeType, 3, tbb::null_mutex>: public ValueAccessor3<TreeTy //////////////////////////////////////// -/// This accessor is thread-safe (at the cost of speed) for both reading and +/// @brief This accessor is thread-safe (at the cost of speed) for both reading and /// writing to a tree. That is, multiple threads may safely access a single, -/// shared ValueAccessorRW. For better performance, however, it is recommended -/// that, instead, each thread be assigned its own (non-mutex protected) accessor. +/// shared ValueAccessorRW. +/// +/// @warning Since the mutex-locking employed by the ValueAccessorRW +/// can seriously impair performance of multithreaded applications, it +/// is recommended that, instead, each thread be assigned its own +/// (non-mutex protected) accessor. template<typename TreeType> struct ValueAccessorRW: public ValueAccessor<TreeType, TreeType::DEPTH-1, tbb::spin_mutex> { @@ -472,32 +502,9 @@ struct ValueAccessorRW: public ValueAccessor<TreeType, TreeType::DEPTH-1, tbb::s //////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////// -/// The classes below are for experts only and should rarely be used directly /// -///////////////////////////////////////////////////////////////////////////////// - -/// InvertedTree<RootNodeType, RootNodeType::LEVEL>::Type is a boost::mpl::vector -/// that lists the types of the nodes of the tree rooted at RootNodeType -/// in reverse order, from LeafNode to RootNode. For example, for RootNodeType -/// RootNode<InternalNode<InternalNode<LeafNode> > >, InvertedTree::Type is -/// boost::mpl::vector< -/// LeafNode, -/// InternalNode<LeafNode>, -/// InternalNode<InternalNode<LeafNode> >, -/// RootNode<InternalNode<InternalNode<LeafNode> > > >. -template<typename HeadT, int HeadLevel> -struct InvertedTree { - typedef typename InvertedTree<typename HeadT::ChildNodeType, HeadLevel-1>::Type SubtreeT; - typedef typename boost::mpl::push_back<SubtreeT, HeadT>::type Type; -}; -/// For level one nodes (either RootNode<LeafNode> or InternalNode<LeafNode>), -/// InvertedTree::Type is either boost::mpl::vector<LeafNode, RootNode<LeafNode> > -/// or boost::mpl::vector<LeafNode, InternalNode<LeafNode> >. -template<typename HeadT> -struct InvertedTree<HeadT, /*HeadLevel=*/1> { - typedef typename boost::mpl::vector<typename HeadT::ChildNodeType, HeadT>::type Type; -}; - +// +// The classes below are for internal use and should rarely be used directly. +// // An element of a compile-time linked list of node pointers, ordered from LeafNode to RootNode template<typename TreeCacheT, typename NodeVecT, bool AtRoot> @@ -584,7 +591,7 @@ public: } return mNext.getValue(xyz); } - + void addLeaf(LeafNodeType* leaf) { BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); @@ -595,18 +602,19 @@ public: } mNext.addLeaf(leaf); } - + void addTile(Index level, const Coord& xyz, const ValueType& value, bool state) { BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); if (NodeType::LEVEL < level) return; if (this->isHashed(xyz)) { assert(mNode); - return const_cast<NodeType*>(mNode)->addTileAndCache(level, xyz, value, state, *mParent); + return const_cast<NodeType*>(mNode)->addTileAndCache( + level, xyz, value, state, *mParent); } mNext.addTile(level, xyz, value, state); } - + LeafNodeType* touchLeaf(const Coord& xyz) { BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); @@ -636,6 +644,37 @@ public: return mNext.probeConstLeaf(xyz); } + template<typename NodeT> + NodeT* probeNode(const Coord& xyz) + { + BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (this->isHashed(xyz)) { + if ((boost::is_same<NodeT, NodeType>::value)) { + assert(mNode); + return reinterpret_cast<NodeT*>(const_cast<NodeType*>(mNode)); + } + return const_cast<NodeType*>(mNode)->template probeNodeAndCache<NodeT>(xyz, *mParent); + } + return mNext.template probeNode<NodeT>(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + + template<typename NodeT> + const NodeT* probeConstNode(const Coord& xyz) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (this->isHashed(xyz)) { + if ((boost::is_same<NodeT, NodeType>::value)) { + assert(mNode); + return reinterpret_cast<const NodeT*>(mNode); + } + return mNode->template probeConstNodeAndCache<NodeT>(xyz, *mParent); + } + return mNext.template probeConstNode<NodeT>(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + /// Return the active state of the voxel at the given coordinates. bool isValueOn(const Coord& xyz) { @@ -700,16 +739,32 @@ public: } void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); } - /// Set the value of the voxel at the given coordinates to the sum of its current - /// value and the given value, and mark the voxel as active. - void setValueOnSum(const Coord& xyz, const ValueType& value) + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template<typename ModifyOp> + void modifyValue(const Coord& xyz, const ModifyOp& op) { if (this->isHashed(xyz)) { assert(mNode); BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); - const_cast<NodeType*>(mNode)->setValueOnSumAndCache(xyz, value, *mParent); + const_cast<NodeType*>(mNode)->modifyValueAndCache(xyz, op, *mParent); } else { - mNext.setValueOnSum(xyz, value); + mNext.modifyValue(xyz, op); + } + } + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template<typename ModifyOp> + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + if (this->isHashed(xyz)) { + assert(mNode); + BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); + const_cast<NodeType*>(mNode)->modifyValueAndActiveStateAndCache(xyz, op, *mParent); + } else { + mNext.modifyValueAndActiveState(xyz, op); } } @@ -793,21 +848,21 @@ public: node = const_cast<RootNodeType*>(mRoot); } void getNode(const RootNodeType*& node) const { node = mRoot; } - + void addLeaf(LeafNodeType* leaf) { assert(mRoot); BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); const_cast<RootNodeType*>(mRoot)->addLeafAndCache(leaf, *mParent); } - + void addTile(Index level, const Coord& xyz, const ValueType& value, bool state) { assert(mRoot); BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); const_cast<RootNodeType*>(mRoot)->addTileAndCache(level, xyz, value, state, *mParent); } - + LeafNodeType* touchLeaf(const Coord& xyz) { assert(mRoot); @@ -828,6 +883,21 @@ public: return mRoot->probeConstLeafAndCache(xyz, *mParent); } + template<typename NodeType> + NodeType* probeNode(const Coord& xyz) + { + assert(mRoot); + BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); + return const_cast<RootNodeType*>(mRoot)->template probeNodeAndCache<NodeType>(xyz, *mParent); + } + + template<typename NodeType> + const NodeType* probeConstNode(const Coord& xyz) + { + assert(mRoot); + return mRoot->template probeConstNodeAndCache<NodeType>(xyz, *mParent); + } + int getValueDepth(const Coord& xyz) { assert(mRoot); @@ -870,11 +940,20 @@ public: } void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); } - void setValueOnSum(const Coord& xyz, const ValueType& value) + template<typename ModifyOp> + void modifyValue(const Coord& xyz, const ModifyOp& op) { assert(mRoot); BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); - const_cast<RootNodeType*>(mRoot)->setValueOnSumAndCache(xyz, value, *mParent); + const_cast<RootNodeType*>(mRoot)->modifyValueAndCache(xyz, op, *mParent); + } + + template<typename ModifyOp> + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + assert(mRoot); + BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); + const_cast<RootNodeType*>(mRoot)->modifyValueAndActiveStateAndCache(xyz, op, *mParent); } void setValueOff(const Coord& xyz, const ValueType& value) @@ -906,11 +985,10 @@ private: /// @brief ValueAccessor with no mutex and no node caching. -/// -/// @note This specialization is mostly useful for benchmark comparisons, -/// since the cached versions are always expected to be faster. +/// @details This specialization is provided mainly for benchmarking. +/// Accessors with caching will almost always be faster. template<typename _TreeType> -class ValueAccessor0 : public ValueAccessorBase<_TreeType> +class ValueAccessor0: public ValueAccessorBase<_TreeType> { public: typedef _TreeType TreeType; @@ -919,11 +997,11 @@ public: typedef typename TreeType::LeafNodeType LeafNodeT; typedef ValueAccessorBase<TreeType> BaseT; - ValueAccessor0(TreeType& tree) : BaseT(tree) {} + ValueAccessor0(TreeType& tree): BaseT(tree) {} - ValueAccessor0(const ValueAccessor0& other) : BaseT(other) {} + ValueAccessor0(const ValueAccessor0& other): BaseT(other) {} - /// Return the number of cache levels employed by this ValueAccessor + /// Return the number of cache levels employed by this accessor. static Index numCacheLevels() { return 0; } ValueAccessor0& operator=(const ValueAccessor0& other) @@ -951,7 +1029,7 @@ public: return BaseT::mTree->isValueOn(xyz); } - /// Return the active state of the voxel as well as its value + /// Return the active state and, in @a value, the value of the voxel at the given coordinates. bool probeValue(const Coord& xyz, ValueType& value) const { assert(BaseT::mTree); @@ -986,7 +1064,7 @@ public: void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); } //@} - /// Set the value of the voxel at the given coordinate but preserves its active state. + /// Set the value of the voxel at the given coordinate but don't change its active state. void setValueOnly(const Coord& xyz, const ValueType& value) { assert(BaseT::mTree); @@ -999,28 +1077,40 @@ public: { assert(BaseT::mTree); BOOST_STATIC_ASSERT(!BaseT::IsConstTree); - BaseT::mTree->getRootNode().setValueOff(xyz, value); + BaseT::mTree->root().setValueOff(xyz, value); } - /// Set the value of the voxel at the given coordinates to the sum of its current - /// value and the given value, and mark the voxel as active. - void setValueOnSum(const Coord& xyz, const ValueType& value) + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template<typename ModifyOp> + void modifyValue(const Coord& xyz, const ModifyOp& op) { assert(BaseT::mTree); BOOST_STATIC_ASSERT(!BaseT::IsConstTree); - BaseT::mTree->setValueOnSum(xyz, value); + BaseT::mTree->modifyValue(xyz, op); } - /// Set the active state of the voxel at the given coordinates without changing its value. + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template<typename ModifyOp> + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + BaseT::mTree->modifyValueAndActiveState(xyz, op); + } + + /// Set the active state of the voxel at the given coordinates but don't change its value. void setActiveState(const Coord& xyz, bool on = true) { assert(BaseT::mTree); BOOST_STATIC_ASSERT(!BaseT::IsConstTree); BaseT::mTree->setActiveState(xyz, on); } - /// Mark the voxel at the given coordinates as active without changing its value. + /// Mark the voxel at the given coordinates as active but don't change its value. void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); } - /// Mark the voxel at the given coordinates as inactive without changing its value. + /// Mark the voxel at the given coordinates as inactive but don't change its value. void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); } /// Return the cached node of type @a NodeType. [Mainly for internal use] @@ -1038,10 +1128,9 @@ public: BOOST_STATIC_ASSERT(!BaseT::IsConstTree); BaseT::mTree->root().addLeaf(leaf); } - - /// @brief Add a tile at the specified tree level that contains - /// xyz, possibly creating a child branch in the process. If a - /// Node that contains xyz already exists it is replaced by a tile. + + /// @brief Add a tile at the specified tree level that contains voxel (x, y, z), + /// possibly deleting existing nodes or creating new nodes in the process. void addTile(Index level, const Coord& xyz, const ValueType& value, bool state) { assert(BaseT::mTree); @@ -1061,17 +1150,29 @@ public: return BaseT::mTree->touchLeaf(xyz); } - LeafNodeT* probeLeaf(const Coord& xyz) + template <typename NodeT> + NodeT* probeNode(const Coord& xyz) { assert(BaseT::mTree); BOOST_STATIC_ASSERT(!BaseT::IsConstTree); - return BaseT::mTree->probeLeaf(xyz); + return BaseT::mTree->template probeNode<NodeT>(xyz); } - const LeafNodeT* probeConstLeaf(const Coord& xyz) const + template <typename NodeT> + const NodeT* probeConstNode(const Coord& xyz) const { assert(BaseT::mTree); - return BaseT::mTree->probeConstLeaf(xyz); + return BaseT::mTree->template probeConstNode<NodeT>(xyz); + } + + LeafNodeT* probeLeaf(const Coord& xyz) + { + return this->template probeNode<LeafNodeT>(xyz); + } + + const LeafNodeT* probeConstLeaf(const Coord& xyz) const + { + return this->template probeConstNode<LeafNodeT>(xyz); } const LeafNodeT* probeLeaf(const Coord& xyz) const @@ -1110,7 +1211,7 @@ public: typedef typename TreeType::RootNodeType RootNodeT; typedef typename TreeType::LeafNodeType LeafNodeT; typedef ValueAccessorBase<TreeType> BaseT; - typedef typename InvertedTree<RootNodeT, RootNodeT::LEVEL>::Type InvTreeT; + typedef typename RootNodeT::NodeChainType InvTreeT; typedef typename boost::mpl::at<InvTreeT, boost::mpl::int_<L0> >::type NodeT0; /// Constructor from a tree @@ -1153,7 +1254,7 @@ public: assert(mNode0); return mNode0->getValueAndCache(xyz, this->self()); } - return BaseT::mTree->getRootNode().getValueAndCache(xyz, this->self()); + return BaseT::mTree->root().getValueAndCache(xyz, this->self()); } /// Return the active state of the voxel at the given coordinates. @@ -1164,7 +1265,7 @@ public: assert(mNode0); return mNode0->isValueOnAndCache(xyz, this->self()); } - return BaseT::mTree->getRootNode().isValueOnAndCache(xyz, this->self()); + return BaseT::mTree->root().isValueOnAndCache(xyz, this->self()); } /// Return the active state of the voxel as well as its value @@ -1175,7 +1276,7 @@ public: assert(mNode0); return mNode0->probeValueAndCache(xyz, value, this->self()); } - return BaseT::mTree->getRootNode().probeValueAndCache(xyz, value, this->self()); + return BaseT::mTree->root().probeValueAndCache(xyz, value, this->self()); } /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides, @@ -1188,7 +1289,7 @@ public: assert(mNode0); return RootNodeT::LEVEL - mNode0->getValueLevelAndCache(xyz, this->self()); } - return BaseT::mTree->getRootNode().getValueDepthAndCache(xyz, this->self()); + return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self()); } /// Return @c true if the value of voxel (x, y, z) resides at the leaf level @@ -1200,7 +1301,7 @@ public: assert(mNode0); return mNode0->getValueLevelAndCache(xyz, this->self()) == 0; } - return BaseT::mTree->getRootNode().getValueDepthAndCache(xyz, this->self()) == + return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self()) == static_cast<int>(RootNodeT::LEVEL); } @@ -1214,7 +1315,7 @@ public: assert(mNode0); const_cast<NodeT0*>(mNode0)->setValueAndCache(xyz, value, *this); } else { - BaseT::mTree->getRootNode().setValueAndCache(xyz, value, *this); + BaseT::mTree->root().setValueAndCache(xyz, value, *this); } } void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); } @@ -1229,7 +1330,7 @@ public: assert(mNode0); const_cast<NodeT0*>(mNode0)->setValueOnlyAndCache(xyz, value, *this); } else { - BaseT::mTree->getRootNode().setValueOnlyAndCache(xyz, value, *this); + BaseT::mTree->root().setValueOnlyAndCache(xyz, value, *this); } } @@ -1242,25 +1343,42 @@ public: assert(mNode0); const_cast<NodeT0*>(mNode0)->setValueOffAndCache(xyz, value, *this); } else { - BaseT::mTree->getRootNode().setValueOffAndCache(xyz, value, *this); + BaseT::mTree->root().setValueOffAndCache(xyz, value, *this); } } - /// Set the value of the voxel at the given coordinates to the sum of its current - /// value and the given value, and mark the voxel as active. - void setValueOnSum(const Coord& xyz, const ValueType& value) + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template<typename ModifyOp> + void modifyValue(const Coord& xyz, const ModifyOp& op) { assert(BaseT::mTree); BOOST_STATIC_ASSERT(!BaseT::IsConstTree); if (this->isHashed(xyz)) { assert(mNode0); - const_cast<NodeT0*>(mNode0)->setValueOnSumAndCache(xyz, value, *this); + const_cast<NodeT0*>(mNode0)->modifyValueAndCache(xyz, op, *this); } else { - BaseT::mTree->getRootNode().setValueOnSumAndCache(xyz, value, *this); + BaseT::mTree->root().modifyValueAndCache(xyz, op, *this); } } - /// Set the active state of the voxel at the given coordinates without changing its value. + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template<typename ModifyOp> + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + if (this->isHashed(xyz)) { + assert(mNode0); + const_cast<NodeT0*>(mNode0)->modifyValueAndActiveStateAndCache(xyz, op, *this); + } else { + BaseT::mTree->root().modifyValueAndActiveStateAndCache(xyz, op, *this); + } + } + + /// Set the active state of the voxel at the given coordinates but don't change its value. void setActiveState(const Coord& xyz, bool on = true) { assert(BaseT::mTree); @@ -1269,12 +1387,12 @@ public: assert(mNode0); const_cast<NodeT0*>(mNode0)->setActiveStateAndCache(xyz, on, *this); } else { - BaseT::mTree->getRootNode().setActiveStateAndCache(xyz, on, *this); + BaseT::mTree->root().setActiveStateAndCache(xyz, on, *this); } } - /// Mark the voxel at the given coordinates as active without changing its value. + /// Mark the voxel at the given coordinates as active but don't change its value. void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); } - /// Mark the voxel at the given coordinates as inactive without changing its value. + /// Mark the voxel at the given coordinates as inactive but don't change its value. void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); } /// Return the cached node of type @a NodeType. [Mainly for internal use] @@ -1300,7 +1418,7 @@ public: const NodeT* node = NULL; this->eraseNode(node); } - + /// @brief Add the specified leaf to this tree, possibly creating a child branch /// in the process. If the leaf node already exists, replace it. void addLeaf(LeafNodeT* leaf) @@ -1309,10 +1427,9 @@ public: BOOST_STATIC_ASSERT(!BaseT::IsConstTree); BaseT::mTree->root().addLeaf(leaf); } - - /// @brief Add a tile at the specified tree level that contains - /// xyz, possibly creating a child branch in the process. If a - /// Node that contains xyz already exists it is replaced by a tile. + + /// @brief Add a tile at the specified tree level that contains voxel (x, y, z), + /// possibly deleting existing nodes or creating new nodes in the process. void addTile(Index level, const Coord& xyz, const ValueType& value, bool state) { assert(BaseT::mTree); @@ -1334,32 +1451,52 @@ public: assert(mNode0); return const_cast<NodeT0*>(mNode0)->touchLeafAndCache(xyz, *this); } - return BaseT::mTree->getRootNode().touchLeafAndCache(xyz, *this); + return BaseT::mTree->root().touchLeafAndCache(xyz, *this); } - /// @brief @return a pointer to the leaf node that contains + /// @brief @return a pointer to the node of the specified type that contains /// voxel (x, y, z) and if it doesn't exist, return NULL. - LeafNodeT* probeLeaf(const Coord& xyz) + template <typename NodeT> + NodeT* probeNode(const Coord& xyz) { assert(BaseT::mTree); BOOST_STATIC_ASSERT(!BaseT::IsConstTree); - if (this->isHashed(xyz)) { - assert(mNode0); - return const_cast<NodeT0*>(mNode0)->probeLeafAndCache(xyz, *this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((boost::is_same<NodeT, NodeT0>::value)) { + if (this->isHashed(xyz)) { + assert(mNode0); + return reinterpret_cast<NodeT*>(const_cast<NodeT0*>(mNode0)); + } + return BaseT::mTree->root().template probeNodeAndCache<NodeT>(xyz, *this); } - return BaseT::mTree->getRootNode().probeLeafAndCache(xyz, *this); + return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + LeafNodeT* probeLeaf(const Coord& xyz) + { + return this->template probeNode<LeafNodeT>(xyz); } - /// @brief @return a const pointer to the leaf node that contains + /// @brief @return a const pointer to the nodeof the specified type that contains /// voxel (x, y, z) and if it doesn't exist, return NULL. - const LeafNodeT* probeConstLeaf(const Coord& xyz) const + template <typename NodeT> + const NodeT* probeConstNode(const Coord& xyz) const { assert(BaseT::mTree); - if (this->isHashed(xyz)) { - assert(mNode0); - return mNode0->probeConstLeafAndCache(xyz, this->self()); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((boost::is_same<NodeT, NodeT0>::value)) { + if (this->isHashed(xyz)) { + assert(mNode0); + return reinterpret_cast<const NodeT*>(mNode0); + } + return BaseT::mTree->root().template probeConstNodeAndCache<NodeT>(xyz, this->self()); } - return BaseT::mTree->getRootNode().probeConstLeafAndCache(xyz, this->self()); + return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + const LeafNodeT* probeConstLeaf(const Coord& xyz) const + { + return this->template probeConstNode<LeafNodeT>(xyz); } const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); } @@ -1384,7 +1521,7 @@ private: void getNode(const NodeT0*& node) { node = mNode0; } void getNode(const RootNodeT*& node) { - node = (BaseT::mTree ? &BaseT::mTree->getRootNode() : NULL); + node = (BaseT::mTree ? &BaseT::mTree->root() : NULL); } template <typename OtherNodeType> void getNode(const OtherNodeType*& node) { node = NULL; } void eraseNode(const NodeT0*) { mKey0 = Coord::max(); mNode0 = NULL; } @@ -1443,12 +1580,12 @@ class ValueAccessor2 : public ValueAccessorBase<_TreeType> public: BOOST_STATIC_ASSERT(_TreeType::DEPTH >= 3); BOOST_STATIC_ASSERT( L0 < L1 && L1 < _TreeType::RootNodeType::LEVEL ); - typedef _TreeType TreeType; - typedef typename TreeType::ValueType ValueType; - typedef typename TreeType::RootNodeType RootNodeT; - typedef typename TreeType::LeafNodeType LeafNodeT; - typedef ValueAccessorBase<TreeType> BaseT; - typedef typename InvertedTree<RootNodeT, RootNodeT::LEVEL>::Type InvTreeT; + typedef _TreeType TreeType; + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::RootNodeType RootNodeT; + typedef typename TreeType::LeafNodeType LeafNodeT; + typedef ValueAccessorBase<TreeType> BaseT; + typedef typename RootNodeT::NodeChainType InvTreeT; typedef typename boost::mpl::at<InvTreeT, boost::mpl::int_<L0> >::type NodeT0; typedef typename boost::mpl::at<InvTreeT, boost::mpl::int_<L1> >::type NodeT1; @@ -1495,7 +1632,7 @@ public: assert(mNode1); return mNode1->getValueAndCache(xyz, this->self()); } - return BaseT::mTree->getRootNode().getValueAndCache(xyz, this->self()); + return BaseT::mTree->root().getValueAndCache(xyz, this->self()); } /// Return the active state of the voxel at the given coordinates. @@ -1509,7 +1646,7 @@ public: assert(mNode1); return mNode1->isValueOnAndCache(xyz, this->self()); } - return BaseT::mTree->getRootNode().isValueOnAndCache(xyz, this->self()); + return BaseT::mTree->root().isValueOnAndCache(xyz, this->self()); } /// Return the active state of the voxel as well as its value @@ -1523,7 +1660,7 @@ public: assert(mNode1); return mNode1->probeValueAndCache(xyz, value, this->self()); } - return BaseT::mTree->getRootNode().probeValueAndCache(xyz, value, this->self()); + return BaseT::mTree->root().probeValueAndCache(xyz, value, this->self()); } /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides, @@ -1539,7 +1676,7 @@ public: assert(mNode1); return RootNodeT::LEVEL - mNode1->getValueLevelAndCache(xyz, this->self()); } - return BaseT::mTree->getRootNode().getValueDepthAndCache(xyz, this->self()); + return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self()); } /// Return @c true if the value of voxel (x, y, z) resides at the leaf level @@ -1554,7 +1691,7 @@ public: assert(mNode1); return mNode1->getValueLevelAndCache(xyz, this->self())==0; } - return BaseT::mTree->getRootNode().getValueDepthAndCache(xyz, this->self()) == + return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self()) == static_cast<int>(RootNodeT::LEVEL); } @@ -1571,7 +1708,7 @@ public: assert(mNode1); const_cast<NodeT1*>(mNode1)->setValueAndCache(xyz, value, *this); } else { - BaseT::mTree->getRootNode().setValueAndCache(xyz, value, *this); + BaseT::mTree->root().setValueAndCache(xyz, value, *this); } } void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); } @@ -1589,7 +1726,7 @@ public: assert(mNode1); const_cast<NodeT1*>(mNode1)->setValueOnlyAndCache(xyz, value, *this); } else { - BaseT::mTree->getRootNode().setValueOnlyAndCache(xyz, value, *this); + BaseT::mTree->root().setValueOnlyAndCache(xyz, value, *this); } } @@ -1605,24 +1742,44 @@ public: assert(mNode1); const_cast<NodeT1*>(mNode1)->setValueOffAndCache(xyz, value, *this); } else { - BaseT::mTree->getRootNode().setValueOffAndCache(xyz, value, *this); + BaseT::mTree->root().setValueOffAndCache(xyz, value, *this); } } - /// Set the value of the voxel at the given coordinates to the sum of its current - /// value and the given value, and mark the voxel as active. - void setValueOnSum(const Coord& xyz, const ValueType& value) + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template<typename ModifyOp> + void modifyValue(const Coord& xyz, const ModifyOp& op) { assert(BaseT::mTree); BOOST_STATIC_ASSERT(!BaseT::IsConstTree); if (this->isHashed0(xyz)) { assert(mNode0); - const_cast<NodeT0*>(mNode0)->setValueOnSumAndCache(xyz, value, *this); + const_cast<NodeT0*>(mNode0)->modifyValueAndCache(xyz, op, *this); } else if (this->isHashed1(xyz)) { assert(mNode1); - const_cast<NodeT1*>(mNode1)->setValueOnSumAndCache(xyz, value, *this); + const_cast<NodeT1*>(mNode1)->modifyValueAndCache(xyz, op, *this); } else { - BaseT::mTree->getRootNode().setValueOnSumAndCache(xyz, value, *this); + BaseT::mTree->root().modifyValueAndCache(xyz, op, *this); + } + } + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template<typename ModifyOp> + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast<NodeT0*>(mNode0)->modifyValueAndActiveStateAndCache(xyz, op, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast<NodeT1*>(mNode1)->modifyValueAndActiveStateAndCache(xyz, op, *this); + } else { + BaseT::mTree->root().modifyValueAndActiveStateAndCache(xyz, op, *this); } } @@ -1638,7 +1795,7 @@ public: assert(mNode1); const_cast<NodeT1*>(mNode1)->setActiveStateAndCache(xyz, on, *this); } else { - BaseT::mTree->getRootNode().setActiveStateAndCache(xyz, on, *this); + BaseT::mTree->root().setActiveStateAndCache(xyz, on, *this); } } /// Mark the voxel at the given coordinates as active without changing its value. @@ -1669,7 +1826,7 @@ public: const NodeT* node = NULL; this->eraseNode(node); } - + /// @brief Add the specified leaf to this tree, possibly creating a child branch /// in the process. If the leaf node already exists, replace it. void addLeaf(LeafNodeT* leaf) @@ -1682,10 +1839,9 @@ public: } BaseT::mTree->root().addLeafAndCache(leaf, *this); } - - /// @brief Add a tile at the specified tree level that contains - /// xyz, possibly creating a child branch in the process. If a - /// Node that contains xyz already exists it is replaced by a tile. + + /// @brief Add a tile at the specified tree level that contains voxel (x, y, z), + /// possibly deleting existing nodes or creating new nodes in the process. void addTile(Index level, const Coord& xyz, const ValueType& value, bool state) { assert(BaseT::mTree); @@ -1696,7 +1852,7 @@ public: } BaseT::mTree->root().addTileAndCache(level, xyz, value, state, *this); } - + /// @brief @return the leaf node that contains voxel (x, y, z) and /// if it doesn't exist, create it, but preserve the values and /// active states of all voxels. @@ -1714,40 +1870,98 @@ public: assert(mNode1); return const_cast<NodeT1*>(mNode1)->touchLeafAndCache(xyz, *this); } - return BaseT::mTree->getRootNode().touchLeafAndCache(xyz, *this); + return BaseT::mTree->root().touchLeafAndCache(xyz, *this); } - - /// @brief @return a pointer to the leaf node that contains + /// @brief @return a pointer to the node of the specified type that contains /// voxel (x, y, z) and if it doesn't exist, return NULL. - LeafNodeT* probeLeaf(const Coord& xyz) + template <typename NodeT> + NodeT* probeNode(const Coord& xyz) { assert(BaseT::mTree); BOOST_STATIC_ASSERT(!BaseT::IsConstTree); - if (this->isHashed0(xyz)) { - assert(mNode0); - return const_cast<NodeT0*>(mNode0)->probeLeafAndCache(xyz, *this); - } else if (this->isHashed1(xyz)) { - assert(mNode1); - return const_cast<NodeT1*>(mNode1)->probeLeafAndCache(xyz, *this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((boost::is_same<NodeT, NodeT0>::value)) { + if (this->isHashed0(xyz)) { + assert(mNode0); + return reinterpret_cast<NodeT*>(const_cast<NodeT0*>(mNode0)); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return const_cast<NodeT1*>(mNode1)->template probeNodeAndCache<NodeT>(xyz, *this); + } + return BaseT::mTree->root().template probeNodeAndCache<NodeT>(xyz, *this); + } else if ((boost::is_same<NodeT, NodeT1>::value)) { + if (this->isHashed1(xyz)) { + assert(mNode1); + return reinterpret_cast<NodeT*>(const_cast<NodeT1*>(mNode1)); + } + return BaseT::mTree->root().template probeNodeAndCache<NodeT>(xyz, *this); } - return BaseT::mTree->getRootNode().probeLeafAndCache(xyz, *this); + return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END } + /// @brief @return a pointer to the leaf node that contains + /// voxel (x, y, z) and if it doesn't exist, return NULL. + LeafNodeT* probeLeaf(const Coord& xyz) { return this->template probeNode<LeafNodeT>(xyz); } + /// @brief @return a const pointer to the node of the specified type that contains + /// voxel (x, y, z) and if it doesn't exist, return NULL. + template <typename NodeT> + const NodeT* probeConstLeaf(const Coord& xyz) const + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((boost::is_same<NodeT, NodeT0>::value)) { + if (this->isHashed0(xyz)) { + assert(mNode0); + return reinterpret_cast<const NodeT*>(mNode0); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->template probeConstNodeAndCache<NodeT>(xyz, this->self()); + } + return BaseT::mTree->root().template probeConstNodeAndCache<NodeT>(xyz, this->self()); + } else if ((boost::is_same<NodeT, NodeT1>::value)) { + if (this->isHashed1(xyz)) { + assert(mNode1); + return reinterpret_cast<const NodeT*>(mNode1); + } + return BaseT::mTree->root().template probeConstNodeAndCache<NodeT>(xyz, this->self()); + } + return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } /// @brief @return a const pointer to the leaf node that contains /// voxel (x, y, z) and if it doesn't exist, return NULL. const LeafNodeT* probeConstLeaf(const Coord& xyz) const { + return this->template probeConstNode<LeafNodeT>(xyz); + } + const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); } + + /// @brief @return a const pointer to the node of the specified type that contains + /// voxel (x, y, z) and if it doesn't exist, return NULL. + template <typename NodeT> + const NodeT* probeConstNode(const Coord& xyz) const + { assert(BaseT::mTree); - if (this->isHashed0(xyz)) { - assert(mNode0); - return mNode0->probeConstLeafAndCache(xyz, this->self()); - } else if (this->isHashed1(xyz)) { - assert(mNode1); - return mNode1->probeConstLeafAndCache(xyz, this->self()); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((boost::is_same<NodeT, NodeT0>::value)) { + if (this->isHashed0(xyz)) { + assert(mNode0); + return reinterpret_cast<const NodeT*>(mNode0); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->template probeConstNodeAndCache<NodeT>(xyz, this->self()); + } + return BaseT::mTree->root().template probeConstNodeAndCache<NodeT>(xyz, this->self()); + } else if ((boost::is_same<NodeT, NodeT1>::value)) { + if (this->isHashed1(xyz)) { + assert(mNode1); + return reinterpret_cast<const NodeT*>(mNode1); + } + return BaseT::mTree->root().template probeConstNodeAndCache<NodeT>(xyz, this->self()); } - return BaseT::mTree->getRootNode().probeConstLeafAndCache(xyz, this->self()); + return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END } - const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); } /// Remove all the cached nodes and invalidate the corresponding hash-keys. virtual void clear() @@ -1773,7 +1987,7 @@ private: void getNode(const NodeT1*& node) { node = mNode1; } void getNode(const RootNodeT*& node) { - node = (BaseT::mTree ? &BaseT::mTree->getRootNode() : NULL); + node = (BaseT::mTree ? &BaseT::mTree->root() : NULL); } template <typename OtherNodeType> void getNode(const OtherNodeType*& node) { node = NULL; } @@ -1853,12 +2067,12 @@ class ValueAccessor3 : public ValueAccessorBase<_TreeType> public: BOOST_STATIC_ASSERT(_TreeType::DEPTH >= 4); BOOST_STATIC_ASSERT(L0 < L1 && L1 < L2 && L2 < _TreeType::RootNodeType::LEVEL); - typedef _TreeType TreeType; - typedef typename TreeType::ValueType ValueType; - typedef typename TreeType::RootNodeType RootNodeT; - typedef typename TreeType::LeafNodeType LeafNodeT; - typedef ValueAccessorBase<TreeType> BaseT; - typedef typename InvertedTree<RootNodeT, RootNodeT::LEVEL>::Type InvTreeT; + typedef _TreeType TreeType; + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::RootNodeType RootNodeT; + typedef typename TreeType::LeafNodeType LeafNodeT; + typedef ValueAccessorBase<TreeType> BaseT; + typedef typename RootNodeT::NodeChainType InvTreeT; typedef typename boost::mpl::at<InvTreeT, boost::mpl::int_<L0> >::type NodeT0; typedef typename boost::mpl::at<InvTreeT, boost::mpl::int_<L1> >::type NodeT1; typedef typename boost::mpl::at<InvTreeT, boost::mpl::int_<L2> >::type NodeT2; @@ -1910,7 +2124,7 @@ public: assert(mNode2); return mNode2->getValueAndCache(xyz, this->self()); } - return BaseT::mTree->getRootNode().getValueAndCache(xyz, this->self()); + return BaseT::mTree->root().getValueAndCache(xyz, this->self()); } /// Return the active state of the voxel at the given coordinates. @@ -1927,7 +2141,7 @@ public: assert(mNode2); return mNode2->isValueOnAndCache(xyz, this->self()); } - return BaseT::mTree->getRootNode().isValueOnAndCache(xyz, this->self()); + return BaseT::mTree->root().isValueOnAndCache(xyz, this->self()); } /// Return the active state of the voxel as well as its value @@ -1944,7 +2158,7 @@ public: assert(mNode2); return mNode2->probeValueAndCache(xyz, value, this->self()); } - return BaseT::mTree->getRootNode().probeValueAndCache(xyz, value, this->self()); + return BaseT::mTree->root().probeValueAndCache(xyz, value, this->self()); } /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides, @@ -1963,7 +2177,7 @@ public: assert(mNode2); return RootNodeT::LEVEL - mNode2->getValueLevelAndCache(xyz, this->self()); } - return BaseT::mTree->getRootNode().getValueDepthAndCache(xyz, this->self()); + return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self()); } /// Return @c true if the value of voxel (x, y, z) resides at the leaf level @@ -1981,7 +2195,7 @@ public: assert(mNode2); return mNode2->getValueLevelAndCache(xyz, this->self())==0; } - return BaseT::mTree->getRootNode().getValueDepthAndCache(xyz, this->self()) == + return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self()) == static_cast<int>(RootNodeT::LEVEL); } @@ -2001,7 +2215,7 @@ public: assert(mNode2); const_cast<NodeT2*>(mNode2)->setValueAndCache(xyz, value, *this); } else { - BaseT::mTree->getRootNode().setValueAndCache(xyz, value, *this); + BaseT::mTree->root().setValueAndCache(xyz, value, *this); } } void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); } @@ -2022,7 +2236,7 @@ public: assert(mNode2); const_cast<NodeT2*>(mNode2)->setValueOnlyAndCache(xyz, value, *this); } else { - BaseT::mTree->getRootNode().setValueOnlyAndCache(xyz, value, *this); + BaseT::mTree->root().setValueOnlyAndCache(xyz, value, *this); } } @@ -2041,27 +2255,50 @@ public: assert(mNode2); const_cast<NodeT2*>(mNode2)->setValueOffAndCache(xyz, value, *this); } else { - BaseT::mTree->getRootNode().setValueOffAndCache(xyz, value, *this); + BaseT::mTree->root().setValueOffAndCache(xyz, value, *this); + } + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template<typename ModifyOp> + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast<NodeT0*>(mNode0)->modifyValueAndCache(xyz, op, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast<NodeT1*>(mNode1)->modifyValueAndCache(xyz, op, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + const_cast<NodeT2*>(mNode2)->modifyValueAndCache(xyz, op, *this); + } else { + BaseT::mTree->root().modifyValueAndCache(xyz, op, *this); } } - /// Set the value of the voxel at the given coordinates to the sum of its current - /// value and the given value, and mark the voxel as active. - void setValueOnSum(const Coord& xyz, const ValueType& value) + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template<typename ModifyOp> + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) { assert(BaseT::mTree); BOOST_STATIC_ASSERT(!BaseT::IsConstTree); if (this->isHashed0(xyz)) { assert(mNode0); - const_cast<NodeT0*>(mNode0)->setValueOnSumAndCache(xyz, value, *this); + const_cast<NodeT0*>(mNode0)->modifyValueAndActiveStateAndCache(xyz, op, *this); } else if (this->isHashed1(xyz)) { assert(mNode1); - const_cast<NodeT1*>(mNode1)->setValueOnSumAndCache(xyz, value, *this); + const_cast<NodeT1*>(mNode1)->modifyValueAndActiveStateAndCache(xyz, op, *this); } else if (this->isHashed2(xyz)) { assert(mNode2); - const_cast<NodeT2*>(mNode2)->setValueOnSumAndCache(xyz, value, *this); + const_cast<NodeT2*>(mNode2)->modifyValueAndActiveStateAndCache(xyz, op, *this); } else { - BaseT::mTree->getRootNode().setValueOnSumAndCache(xyz, value, *this); + BaseT::mTree->root().modifyValueAndActiveStateAndCache(xyz, op, *this); } } @@ -2080,7 +2317,7 @@ public: assert(mNode2); const_cast<NodeT2*>(mNode2)->setActiveStateAndCache(xyz, on, *this); } else { - BaseT::mTree->getRootNode().setActiveStateAndCache(xyz, on, *this); + BaseT::mTree->root().setActiveStateAndCache(xyz, on, *this); } } /// Mark the voxel at the given coordinates as active without changing its value. @@ -2127,10 +2364,9 @@ public: } BaseT::mTree->root().addLeafAndCache(leaf, *this); } - - /// @brief Add a tile at the specified tree level that contains - /// xyz, possibly creating a child branch in the process. If a - /// Node that contains xyz already exists it is replaced by a tile. + + /// @brief Add a tile at the specified tree level that contains voxel (x, y, z), + /// possibly deleting existing nodes or creating new nodes in the process. void addTile(Index level, const Coord& xyz, const ValueType& value, bool state) { assert(BaseT::mTree); @@ -2157,7 +2393,7 @@ public: BOOST_STATIC_ASSERT(!BaseT::IsConstTree); if (this->isHashed0(xyz)) { assert(mNode0); - return const_cast<NodeT0*>(mNode0)->touchLeafAndCache(xyz, *this); + return const_cast<NodeT0*>(mNode0); } else if (this->isHashed1(xyz)) { assert(mNode1); return const_cast<NodeT1*>(mNode1)->touchLeafAndCache(xyz, *this); @@ -2165,44 +2401,94 @@ public: assert(mNode2); return const_cast<NodeT2*>(mNode2)->touchLeafAndCache(xyz, *this); } - return BaseT::mTree->getRootNode().touchLeafAndCache(xyz, *this); + return BaseT::mTree->root().touchLeafAndCache(xyz, *this); } - - /// @brief @return a pointer to the leaf node that contains + /// @brief @return a pointer to the node of the specified type that contains /// voxel (x, y, z) and if it doesn't exist, return NULL. - LeafNodeT* probeLeaf(const Coord& xyz) + template <typename NodeT> + NodeT* probeNode(const Coord& xyz) { assert(BaseT::mTree); BOOST_STATIC_ASSERT(!BaseT::IsConstTree); - if (this->isHashed0(xyz)) { - assert(mNode0); - return const_cast<NodeT0*>(mNode0)->probeLeafAndCache(xyz, *this); - } else if (this->isHashed1(xyz)) { - assert(mNode1); - return const_cast<NodeT1*>(mNode1)->probeLeafAndCache(xyz, *this); - } else if (this->isHashed2(xyz)) { - assert(mNode2); - return const_cast<NodeT2*>(mNode2)->probeLeafAndCache(xyz, *this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((boost::is_same<NodeT, NodeT0>::value)) { + if (this->isHashed0(xyz)) { + assert(mNode0); + return reinterpret_cast<NodeT*>(const_cast<NodeT0*>(mNode0)); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return const_cast<NodeT1*>(mNode1)->template probeNodeAndCache<NodeT>(xyz, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return const_cast<NodeT2*>(mNode2)->template probeNodeAndCache<NodeT>(xyz, *this); + } + return BaseT::mTree->root().template probeNodeAndCache<NodeT>(xyz, *this); + } else if ((boost::is_same<NodeT, NodeT1>::value)) { + if (this->isHashed1(xyz)) { + assert(mNode1); + return reinterpret_cast<NodeT*>(const_cast<NodeT1*>(mNode1)); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return const_cast<NodeT2*>(mNode2)->template probeNodeAndCache<NodeT>(xyz, *this); + } + return BaseT::mTree->root().template probeNodeAndCache<NodeT>(xyz, *this); + } else if ((boost::is_same<NodeT, NodeT2>::value)) { + if (this->isHashed2(xyz)) { + assert(mNode2); + return reinterpret_cast<NodeT*>(const_cast<NodeT2*>(mNode2)); + } + return BaseT::mTree->root().template probeNodeAndCache<NodeT>(xyz, *this); } - return BaseT::mTree->getRootNode().probeLeafAndCache(xyz, *this); + return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END } + /// @brief @return a pointer to the leaf node that contains + /// voxel (x, y, z) and if it doesn't exist, return NULL. + LeafNodeT* probeLeaf(const Coord& xyz) { return this->template probeNode<LeafNodeT>(xyz); } - /// @brief @return a const pointer to the leaf node that contains + /// @brief @return a const pointer to the node of the specified type that contains /// voxel (x, y, z) and if it doesn't exist, return NULL. - const LeafNodeT* probeConstLeaf(const Coord& xyz) const + template <typename NodeT> + const NodeT* probeConstNode(const Coord& xyz) const { assert(BaseT::mTree); - if (this->isHashed0(xyz)) { - assert(mNode0); - return mNode0->probeConstLeafAndCache(xyz, this->self()); - } else if (this->isHashed1(xyz)) { - assert(mNode1); - return mNode1->probeConstLeafAndCache(xyz, this->self()); - } else if (this->isHashed2(xyz)) { - assert(mNode2); - return mNode2->probeConstLeafAndCache(xyz, this->self()); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((boost::is_same<NodeT, NodeT0>::value)) { + if (this->isHashed0(xyz)) { + assert(mNode0); + return reinterpret_cast<const NodeT*>(mNode0); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->template probeConstNodeAndCache<NodeT>(xyz, this->self()); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return mNode2->template probeConstNodeAndCache<NodeT>(xyz, this->self()); + } + return BaseT::mTree->root().template probeConstNodeAndCache<NodeT>(xyz, this->self()); + } else if ((boost::is_same<NodeT, NodeT1>::value)) { + if (this->isHashed1(xyz)) { + assert(mNode1); + return reinterpret_cast<const NodeT*>(mNode1); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return mNode2->template probeConstNodeAndCache<NodeT>(xyz, this->self()); + } + return BaseT::mTree->root().template probeConstNodeAndCache<NodeT>(xyz, this->self()); + } else if ((boost::is_same<NodeT, NodeT2>::value)) { + if (this->isHashed2(xyz)) { + assert(mNode2); + return reinterpret_cast<const NodeT*>(mNode2); + } + return BaseT::mTree->root().template probeConstNodeAndCache<NodeT>(xyz, this->self()); } - return BaseT::mTree->getRootNode().probeConstLeafAndCache(xyz, this->self()); + return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + /// @brief @return a const pointer to the leaf node that contains + /// voxel (x, y, z) and if it doesn't exist, return NULL. + const LeafNodeT* probeConstLeaf(const Coord& xyz) const + { + return this->template probeConstNode<LeafNodeT>(xyz); } const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); } @@ -2251,7 +2537,7 @@ private: void getNode(const NodeT2*& node) { node = mNode2; } void getNode(const RootNodeT*& node) { - node = (BaseT::mTree ? &BaseT::mTree->getRootNode() : NULL); + node = (BaseT::mTree ? &BaseT::mTree->root() : NULL); } template <typename OtherNodeType> void getNode(const OtherNodeType*& node) { node = NULL; } diff --git a/extern/openvdb/internal/openvdb/util/MapsUtil.h b/extern/openvdb/internal/openvdb/util/MapsUtil.h index 8865169f1d4..e8faa387aae 100644 --- a/extern/openvdb/internal/openvdb/util/MapsUtil.h +++ b/extern/openvdb/internal/openvdb/util/MapsUtil.h @@ -202,7 +202,7 @@ calculateBounds<math::NonlinearFrustumMap>(const math::NonlinearFrustumMap& frus calculateBounds<math::NonlinearFrustumMap>(frustum, bounding_box, out); return; } - + // for convenience Vec3d& out_min = out.min(); Vec3d& out_max = out.max(); @@ -251,8 +251,6 @@ calculateBounds<math::NonlinearFrustumMap>(const math::NonlinearFrustumMap& frus // convert back to voxel space pre_image = frustum.applyInverseMap(extreme2); out_max.x() = std::max(x_min, std::min(x_max, pre_image.x())); - - const Vec3d tmpPlus = extreme2; extreme.x() = xm; extreme.y() = centerLS.y(); @@ -260,8 +258,6 @@ calculateBounds<math::NonlinearFrustumMap>(const math::NonlinearFrustumMap& frus // location in world space of the tangent point extreme2 = secondMap.applyMap(extreme); - const Vec3d tmpMinus = extreme2; - // convert back to voxel space pre_image = frustum.applyInverseMap(extreme2); out_min.x() = std::max(x_min, std::min(x_max, pre_image.x())); @@ -285,15 +281,11 @@ calculateBounds<math::NonlinearFrustumMap>(const math::NonlinearFrustumMap& frus pre_image = frustum.applyInverseMap(extreme2); out_max.y() = std::max(y_min, std::min(y_max, pre_image.y())); - const Vec3d tmpPlus = extreme2; - extreme.x() = centerLS.x(); extreme.y() = xm; extreme.z() = zm; extreme2 = secondMap.applyMap(extreme); - const Vec3d tmpMinus = extreme2; - // convert back to voxel space pre_image = frustum.applyInverseMap(extreme2); out_min.y() = std::max(y_min, std::min(y_max, pre_image.y())); diff --git a/extern/openvdb/internal/openvdb/util/NodeMasks.h b/extern/openvdb/internal/openvdb/util/NodeMasks.h index a38f0c651f8..88862f746cd 100644 --- a/extern/openvdb/internal/openvdb/util/NodeMasks.h +++ b/extern/openvdb/internal/openvdb/util/NodeMasks.h @@ -77,7 +77,7 @@ CountOn(Index32 v) { v = v - ((v >> 1) & 0x55555555U); v = (v & 0x33333333U) + ((v >> 2) & 0x33333333U); - return ((v + (v >> 4) & 0xF0F0F0FU) * 0x1010101U) >> 24; + return (((v + (v >> 4)) & 0xF0F0F0FU) * 0x1010101U) >> 24; } /// Return the number of off bits in the given 32-bit value. @@ -89,7 +89,7 @@ CountOn(Index64 v) { v = v - ((v >> 1) & UINT64_C(0x5555555555555555)); v = (v & UINT64_C(0x3333333333333333)) + ((v >> 2) & UINT64_C(0x3333333333333333)); - return ((v + (v >> 4) & UINT64_C(0xF0F0F0F0F0F0F0F)) * UINT64_C(0x101010101010101)) >> 56; + return (((v + (v >> 4)) & UINT64_C(0xF0F0F0F0F0F0F0F)) * UINT64_C(0x101010101010101)) >> 56; } /// Return the number of off bits in the given 64-bit value. @@ -168,10 +168,9 @@ public: bool operator==(const BaseMaskIterator &iter) const {return mPos == iter.mPos;} bool operator!=(const BaseMaskIterator &iter) const {return mPos != iter.mPos;} bool operator< (const BaseMaskIterator &iter) const {return mPos < iter.mPos;} - void operator= (const BaseMaskIterator &iter) + BaseMaskIterator& operator=(const BaseMaskIterator& iter) { - mPos = iter.mPos; - mParent = iter.mParent; + mPos = iter.mPos; mParent = iter.mParent; return *this; } Index32 offset() const {return mPos;} Index32 pos() const {return mPos;} @@ -318,11 +317,12 @@ public: /// Destructor ~NodeMask() {} /// Assignment operator - void operator = (const NodeMask &other) + NodeMask& operator=(const NodeMask& other) { Index32 n = WORD_COUNT; const Word* w2 = other.mWords; - for ( Word* w1 = mWords; n--; ++w1, ++w2) *w1 = *w2; + for (Word* w1 = mWords; n--; ++w1, ++w2) *w1 = *w2; + return *this; } typedef OnMaskIterator<NodeMask> OnIterator; @@ -375,8 +375,6 @@ public: NodeMask operator^(const NodeMask& other) const { NodeMask m(*this); m ^= other; return m; } /// Return the byte size of this NodeMask static Index32 memUsage() { return WORD_COUNT*sizeof(Word); } - /// Return the byte size of this NodeMask - OPENVDB_DEPRECATED Index32 getMemUsage() const {return sizeof(*this);} /// Return the total number of on bits Index32 countOn() const { @@ -613,8 +611,6 @@ public: NodeMask operator^(const NodeMask& other) const { NodeMask m(*this); m ^= other; return m; } /// Return the byte size of this NodeMask static Index32 memUsage() { return 1; } - /// Return the byte size of this NodeMask - OPENVDB_DEPRECATED Index32 getMemUsage() const {return sizeof(*this);} /// Return the total number of on bits Index32 countOn() const { return CountOn(mByte); } /// Return the total number of on bits @@ -796,8 +792,6 @@ public: NodeMask operator^(const NodeMask& other) const { NodeMask m(*this); m ^= other; return m; } /// Return the byte size of this NodeMask static Index32 memUsage() { return 8; } - /// Return the byte size of this NodeMask - OPENVDB_DEPRECATED Index32 getMemUsage() const {return sizeof(*this);} /// Return the total number of on bits Index32 countOn() const { return CountOn(mWord); } /// Return the total number of on bits @@ -946,7 +940,7 @@ public: Index getIntSize() const {return mIntSize;} - void operator = (const RootNodeMask &B) { + RootNodeMask& operator=(const RootNodeMask& B) { if (mBitSize!=B.mBitSize) { mBitSize=B.mBitSize; mIntSize=B.mIntSize; @@ -954,6 +948,7 @@ public: mBits = new Index32[mIntSize]; } for (Index32 i=0; i<mIntSize; ++i) mBits[i]=B.mBits[i]; + return *this; } class BaseIterator @@ -971,10 +966,11 @@ public: bool operator==(const BaseIterator &iter) const {return mPos == iter.mPos;} bool operator!=(const BaseIterator &iter) const {return mPos != iter.mPos;} bool operator< (const BaseIterator &iter) const {return mPos < iter.mPos;} - void operator=(const BaseIterator &iter) { + BaseIterator& operator=(const BaseIterator& iter) { mPos = iter.mPos; mBitSize = iter.mBitSize; mParent = iter.mParent; + return *this; } Index32 offset() const {return mPos;} diff --git a/extern/openvdb/internal/openvdb/util/Util.h b/extern/openvdb/internal/openvdb/util/Util.h index a70a28a295b..2acb3f3ef16 100644 --- a/extern/openvdb/internal/openvdb/util/Util.h +++ b/extern/openvdb/internal/openvdb/util/Util.h @@ -75,7 +75,7 @@ public: inline void operator()(const typename TreeType1::LeafIter& lIter) const { - const Coord xyz = lIter->getOrigin(); + const Coord xyz = lIter->origin(); const typename TreeType2::LeafNodeType* leaf = mOtherTree->probeConstLeaf(xyz); if (leaf) {//leaf node lIter->topologyIntersection(*leaf, zeroVal<typename TreeType1::ValueType>()); @@ -99,7 +99,7 @@ public: inline void operator()(const typename TreeType1::LeafIter& lIter) const { - const Coord xyz = lIter->getOrigin(); + const Coord xyz = lIter->origin(); const typename TreeType2::LeafNodeType* leaf = mOtherTree->probeConstLeaf(xyz); if (leaf) {//leaf node lIter->topologyDifference(*leaf, zeroVal<typename TreeType1::ValueType>()); diff --git a/extern/openvdb/internal/openvdb/util/logging.h b/extern/openvdb/internal/openvdb/util/logging.h index c8bcb79fe27..4646e07a220 100644 --- a/extern/openvdb/internal/openvdb/util/logging.h +++ b/extern/openvdb/internal/openvdb/util/logging.h @@ -33,32 +33,46 @@ #ifndef OPENVDB_USE_LOG4CPLUS -/// Macros to log messages of various severity levels -/// The message is of the form 'someVar << "some text" << ...'. +/// Log an info message of the form '<TT>someVar << "some text" << ...</TT>'. #define OPENVDB_LOG_INFO(message) -#define OPENVDB_LOG_WARN(message) do { std::cerr << message << std::endl; } while (0); -#define OPENVDB_LOG_ERROR(message) do { std::cerr << message << std::endl; } while (0); -#define OPENVDB_LOG_FATAL(message) do { std::cerr << message << std::endl; } while (0); +/// Log a warning message of the form '<TT>someVar << "some text" << ...</TT>'. +#define OPENVDB_LOG_WARN(message) do { std::cerr << message << std::endl; } while (0); +/// Log an error message of the form '<TT>someVar << "some text" << ...</TT>'. +#define OPENVDB_LOG_ERROR(message) do { std::cerr << message << std::endl; } while (0); +/// Log a fatal error message of the form '<TT>someVar << "some text" << ...</TT>'. +#define OPENVDB_LOG_FATAL(message) do { std::cerr << message << std::endl; } while (0); +/// In debug builds only, log a debugging message of the form '<TT>someVar << "text" << ...</TT>'. #define OPENVDB_LOG_DEBUG(message) +/// @brief Log a debugging message in both debug and optimized builds. +/// @warning Don't use this in performance-critical code. #define OPENVDB_LOG_DEBUG_RUNTIME(message) #else // ifdef OPENVDB_USE_LOG4CPLUS -#include <logging_base/logging.h> +#include <log4cplus/logger.h> +#include <log4cplus/loglevel.h> +#include <sstream> -#define OPENVDB_LOG_INFO(message) LOG_INFO(message) -#define OPENVDB_LOG_WARN(message) LOG_WARN(message) -#define OPENVDB_LOG_ERROR(message) LOG_ERROR(message) -#define OPENVDB_LOG_FATAL(message) LOG_FATAL(message) +#define OPENVDB_LOG(level, message) \ + do { \ + log4cplus::Logger _log = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("main")); \ + if (_log.isEnabledFor(log4cplus::level##_LOG_LEVEL)) { \ + std::ostringstream _buf; \ + _buf << message; \ + _log.forcedLog(log4cplus::level##_LOG_LEVEL, _buf.str(), __FILE__, __LINE__); \ + } \ + } while (0); + +#define OPENVDB_LOG_INFO(message) OPENVDB_LOG(INFO, message) +#define OPENVDB_LOG_WARN(message) OPENVDB_LOG(WARN, message) +#define OPENVDB_LOG_ERROR(message) OPENVDB_LOG(ERROR, message) +#define OPENVDB_LOG_FATAL(message) OPENVDB_LOG(FATAL, message) #ifdef DEBUG -/// Log debugging messages in debug builds only. -#define OPENVDB_LOG_DEBUG(message) LOG_DEBUG(message) +#define OPENVDB_LOG_DEBUG(message) OPENVDB_LOG(DEBUG, message) #else #define OPENVDB_LOG_DEBUG(message) #endif -/// Log debugging messages even in non-debug builds. -/// Don't use this in performance-critical code. -#define OPENVDB_LOG_DEBUG_RUNTIME(message) LOG_DEBUG_RUNTIME(message) +#define OPENVDB_LOG_DEBUG_RUNTIME(message) OPENVDB_LOG(DEBUG, message) #endif // OPENVDB_USE_LOG4CPLUS diff --git a/extern/openvdb/internal/openvdb/version.h b/extern/openvdb/internal/openvdb/version.h index f4287a81797..706c7c0480d 100644 --- a/extern/openvdb/internal/openvdb/version.h +++ b/extern/openvdb/internal/openvdb/version.h @@ -42,7 +42,18 @@ /// vdb::vX_Y_Z::Vec3i, vdb::vX_Y_Z::io::File, vdb::vX_Y_Z::tree::Tree, etc., /// where X, Y and Z are OPENVDB_LIBRARY_MAJOR_VERSION, OPENVDB_LIBRARY_MINOR_VERSION /// and OPENVDB_LIBRARY_PATCH_VERSION, respectively (defined below). -#define OPENVDB_VERSION_NAME v1_1_1 +#define OPENVDB_VERSION_NAME v2_3_0 + +// Library major, minor and patch version numbers +#define OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER 2 +#define OPENVDB_LIBRARY_MINOR_VERSION_NUMBER 3 +#define OPENVDB_LIBRARY_PATCH_VERSION_NUMBER 0 + +/// Library version number as a packed integer ("%02x%02x%04x", major, minor, patch) +#define OPENVDB_LIBRARY_VERSION_NUMBER \ + ((OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER << 24) | \ + ((OPENVDB_LIBRARY_MINOR_VERSION_NUMBER & 0xFF) << 16) | \ + (OPENVDB_LIBRARY_PATCH_VERSION_NUMBER & 0xFFFF)) /// If OPENVDB_REQUIRE_VERSION_NAME is undefined, symbols from the version /// namespace are promoted to the top-level namespace (e.g., vdb::v1_0_0::io::File @@ -67,10 +78,13 @@ namespace OPENVDB_VERSION_NAME { /// @details This can be used to quickly test whether we have a valid file or not. const int32_t OPENVDB_MAGIC = 0x56444220; +// Library major, minor and patch version numbers const uint32_t - OPENVDB_LIBRARY_MAJOR_VERSION = 1, - OPENVDB_LIBRARY_MINOR_VERSION = 1, - OPENVDB_LIBRARY_PATCH_VERSION = 1; + OPENVDB_LIBRARY_MAJOR_VERSION = OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER, + OPENVDB_LIBRARY_MINOR_VERSION = OPENVDB_LIBRARY_MINOR_VERSION_NUMBER, + OPENVDB_LIBRARY_PATCH_VERSION = OPENVDB_LIBRARY_PATCH_VERSION_NUMBER; +/// Library version number as a packed integer ("%02x%02x%04x", major, minor, patch) +const uint32_t OPENVDB_LIBRARY_VERSION = OPENVDB_LIBRARY_VERSION_NUMBER; /// @brief The current version number of the VDB file format /// @details This can be used to enable various backwards compatability switches @@ -89,7 +103,7 @@ enum { OPENVDB_FILE_VERSION_NEW_TRANSFORM = 219, OPENVDB_FILE_VERSION_SELECTIVE_COMPRESSION = 220, OPENVDB_FILE_VERSION_FLOAT_FRUSTUM_BBOX = 221, - OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION = 222, + OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION = 222 }; diff --git a/intern/cycles/kernel/textures/vdb_lookup.h b/intern/cycles/kernel/textures/vdb_lookup.h index 3a0a3e57bb9..7b4ee184937 100644 --- a/intern/cycles/kernel/textures/vdb_lookup.h +++ b/intern/cycles/kernel/textures/vdb_lookup.h @@ -154,8 +154,8 @@ typename GridType::ValueType VDBAccessor::vdb_lookup_single_point(float x, float { typename GridType::Ptr grid = openvdb::gridPtrCast<GridType>(getGridPtr()); typename GridType::Accessor acc = grid->getAccessor(); - - openvdb::tools::GridSampler<openvdb::tree::ValueAccessor<typename GridType::TreeType>, openvdb::tools::PointSampler> sampler(acc); + // needs to be checked. + openvdb::tools::GridSampler<typename GridType::TreeType, openvdb::tools::PointSampler> sampler(*grid.get()); openvdb::Vec3d p(x, y, z); return sampler.wsSample(p); diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject cb1967cc63a6d2d75d2b59cdf91c5f5645285ae +Subproject d3e0405103f9ccabb8e567a8c69e9d4015aaa3b diff --git a/release/scripts/addons b/release/scripts/addons -Subproject c50944e808d6c74148237e85866e893628f0fee +Subproject 6501c6cadfef1804dfa4b7aa2eb208207570a42 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib -Subproject 31545d25c9cb41d271a3f3ef84d327708572290 +Subproject 6695fc64a51fcc64c96bc31c5b209a54d8ea9ba |