Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael Campos <rafaelcdn@gmail.com>2014-04-30 23:47:09 +0400
committerRafael Campos <rafaelcdn@gmail.com>2014-04-30 23:47:09 +0400
commit724750f47d836df1e71a538283e1c99f4e69f8fc (patch)
treeda5eb3fa7e0672cfee75e0b779848e0f7a85ef82
parent155805b20959a7a41eb7e4fbbc4c5fad4c546869 (diff)
Updated OpenVDB library to 2.3.0;soc-2013-cycles_volume
-rw-r--r--extern/openvdb/CMakeLists.txt11
-rw-r--r--extern/openvdb/internal/openvdb/Grid.h145
-rw-r--r--extern/openvdb/internal/openvdb/Platform.h34
-rw-r--r--extern/openvdb/internal/openvdb/Types.h140
-rw-r--r--extern/openvdb/internal/openvdb/io/Archive.cc22
-rw-r--r--extern/openvdb/internal/openvdb/io/Archive.h11
-rw-r--r--extern/openvdb/internal/openvdb/io/Compression.h42
-rw-r--r--extern/openvdb/internal/openvdb/io/File.cc35
-rw-r--r--extern/openvdb/internal/openvdb/io/File.h40
-rw-r--r--extern/openvdb/internal/openvdb/io/GridDescriptor.cc5
-rw-r--r--extern/openvdb/internal/openvdb/io/Queue.cc337
-rw-r--r--extern/openvdb/internal/openvdb/io/Queue.h277
-rw-r--r--extern/openvdb/internal/openvdb/io/Stream.cc16
-rw-r--r--extern/openvdb/internal/openvdb/io/Stream.h53
-rw-r--r--extern/openvdb/internal/openvdb/math/BBox.h245
-rw-r--r--extern/openvdb/internal/openvdb/math/Coord.h101
-rw-r--r--extern/openvdb/internal/openvdb/math/DDA.h351
-rw-r--r--extern/openvdb/internal/openvdb/math/FiniteDifference.h799
-rw-r--r--extern/openvdb/internal/openvdb/math/Hermite.h4
-rw-r--r--extern/openvdb/internal/openvdb/math/LegacyFrustum.h12
-rw-r--r--extern/openvdb/internal/openvdb/math/Maps.cc105
-rw-r--r--extern/openvdb/internal/openvdb/math/Maps.h432
-rw-r--r--extern/openvdb/internal/openvdb/math/Mat.h174
-rw-r--r--extern/openvdb/internal/openvdb/math/Mat3.h6
-rw-r--r--extern/openvdb/internal/openvdb/math/Mat4.h4
-rw-r--r--extern/openvdb/internal/openvdb/math/Math.h629
-rw-r--r--extern/openvdb/internal/openvdb/math/Operators.h402
-rw-r--r--extern/openvdb/internal/openvdb/math/Proximity.cc317
-rw-r--r--extern/openvdb/internal/openvdb/math/Proximity.h91
-rw-r--r--extern/openvdb/internal/openvdb/math/QuantizedUnitVec.cc5
-rw-r--r--extern/openvdb/internal/openvdb/math/Ray.h342
-rw-r--r--extern/openvdb/internal/openvdb/math/Stats.h108
-rw-r--r--extern/openvdb/internal/openvdb/math/Stencils.h302
-rw-r--r--extern/openvdb/internal/openvdb/math/Transform.cc68
-rw-r--r--extern/openvdb/internal/openvdb/math/Transform.h2
-rw-r--r--extern/openvdb/internal/openvdb/math/Tuple.h3
-rw-r--r--extern/openvdb/internal/openvdb/math/Vec2.h33
-rw-r--r--extern/openvdb/internal/openvdb/math/Vec3.h35
-rw-r--r--extern/openvdb/internal/openvdb/math/Vec4.h36
-rw-r--r--extern/openvdb/internal/openvdb/tools/Composite.h25
-rw-r--r--extern/openvdb/internal/openvdb/tools/Dense.h357
-rw-r--r--extern/openvdb/internal/openvdb/tools/DenseSparseTools.h1259
-rw-r--r--extern/openvdb/internal/openvdb/tools/Filter.h372
-rw-r--r--extern/openvdb/internal/openvdb/tools/GridOperators.h566
-rw-r--r--extern/openvdb/internal/openvdb/tools/GridTransformer.h4
-rw-r--r--extern/openvdb/internal/openvdb/tools/Interpolation.h260
-rw-r--r--extern/openvdb/internal/openvdb/tools/LevelSetAdvect.h93
-rw-r--r--extern/openvdb/internal/openvdb/tools/LevelSetFilter.h440
-rw-r--r--extern/openvdb/internal/openvdb/tools/LevelSetMeasure.h560
-rw-r--r--extern/openvdb/internal/openvdb/tools/LevelSetMorph.h601
-rw-r--r--extern/openvdb/internal/openvdb/tools/LevelSetRebuild.h5
-rw-r--r--extern/openvdb/internal/openvdb/tools/LevelSetTracker.h47
-rw-r--r--extern/openvdb/internal/openvdb/tools/LevelSetUtil.h87
-rw-r--r--extern/openvdb/internal/openvdb/tools/MeshToVolume.h2678
-rw-r--r--extern/openvdb/internal/openvdb/tools/Morphology.h235
-rw-r--r--extern/openvdb/internal/openvdb/tools/ParticlesToLevelSet.h941
-rw-r--r--extern/openvdb/internal/openvdb/tools/PointScatter.h16
-rw-r--r--extern/openvdb/internal/openvdb/tools/RayIntersector.h690
-rw-r--r--extern/openvdb/internal/openvdb/tools/RayTracer.h977
-rw-r--r--extern/openvdb/internal/openvdb/tools/ValueTransformer.h149
-rw-r--r--extern/openvdb/internal/openvdb/tools/VectorTransformer.h158
-rw-r--r--extern/openvdb/internal/openvdb/tools/VolumeToMesh.h4875
-rw-r--r--extern/openvdb/internal/openvdb/tools/VolumeToSpheres.h1034
-rw-r--r--extern/openvdb/internal/openvdb/tree/InternalNode.h1147
-rw-r--r--extern/openvdb/internal/openvdb/tree/Iterator.h16
-rw-r--r--extern/openvdb/internal/openvdb/tree/LeafManager.h96
-rw-r--r--extern/openvdb/internal/openvdb/tree/LeafNode.h567
-rw-r--r--extern/openvdb/internal/openvdb/tree/LeafNodeBool.h533
-rw-r--r--extern/openvdb/internal/openvdb/tree/NodeUnion.h26
-rw-r--r--extern/openvdb/internal/openvdb/tree/RootNode.h1066
-rw-r--r--extern/openvdb/internal/openvdb/tree/Tree.h1039
-rw-r--r--extern/openvdb/internal/openvdb/tree/TreeIterator.h36
-rw-r--r--extern/openvdb/internal/openvdb/tree/Util.h5
-rw-r--r--extern/openvdb/internal/openvdb/tree/ValueAccessor.h822
-rw-r--r--extern/openvdb/internal/openvdb/util/MapsUtil.h10
-rw-r--r--extern/openvdb/internal/openvdb/util/NodeMasks.h26
-rw-r--r--extern/openvdb/internal/openvdb/util/Util.h4
-rw-r--r--extern/openvdb/internal/openvdb/util/logging.h44
-rw-r--r--extern/openvdb/internal/openvdb/version.h24
-rw-r--r--intern/cycles/kernel/textures/vdb_lookup.h4
m---------release/datafiles/locale0
m---------release/scripts/addons0
m---------release/scripts/addons_contrib0
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, &notifier, _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