diff options
Diffstat (limited to 'extern/openvdb/internal/openvdb/tree/RootNode.h')
-rw-r--r-- | extern/openvdb/internal/openvdb/tree/RootNode.h | 1066 |
1 files changed, 840 insertions, 226 deletions
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; |