diff options
author | Rafael Campos <rafaelcdn@gmail.com> | 2014-04-30 23:47:09 +0400 |
---|---|---|
committer | Rafael Campos <rafaelcdn@gmail.com> | 2014-04-30 23:47:09 +0400 |
commit | 724750f47d836df1e71a538283e1c99f4e69f8fc (patch) | |
tree | da5eb3fa7e0672cfee75e0b779848e0f7a85ef82 /extern/openvdb/internal/openvdb/tree/InternalNode.h | |
parent | 155805b20959a7a41eb7e4fbbc4c5fad4c546869 (diff) |
Updated OpenVDB library to 2.3.0;soc-2013-cycles_volume
Diffstat (limited to 'extern/openvdb/internal/openvdb/tree/InternalNode.h')
-rw-r--r-- | extern/openvdb/internal/openvdb/tree/InternalNode.h | 1147 |
1 files changed, 767 insertions, 380 deletions
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 |