Loading...
Searching...
No Matches
RootNode.h
Go to the documentation of this file.
// Copyright Contributors to the OpenVDB Project
// SPDX-License-Identifier: MPL-2.0
///
/// @file RootNode.h
///
/// @brief The root node of an OpenVDB tree
#ifndef OPENVDB_TREE_ROOTNODE_HAS_BEEN_INCLUDED
#define OPENVDB_TREE_ROOTNODE_HAS_BEEN_INCLUDED
#include <openvdb/Exceptions.h>
#include <openvdb/Types.h>
#include <openvdb/io/Compression.h> // for truncateRealToHalf()
#include <openvdb/math/Math.h> // for isZero(), isExactlyEqual(), etc.
#include <openvdb/math/BBox.h>
#include <openvdb/util/NodeMasks.h> // for backward compatibility only (see readTopology())
#include <openvdb/version.h>
#include <tbb/parallel_for.h>
#include <map>
#include <set>
#include <sstream>
#include <vector>
namespace openvdb {
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
{
public:
using ChildNodeType = ChildType;
using LeafNodeType = typename ChildType::LeafNodeType;
using ValueType = typename ChildType::ValueType;
using BuildType = typename ChildType::BuildType;
static const Index LEVEL = 1 + ChildType::LEVEL; // level 0 = leaf
/// NodeChainType is a list of this tree's node types, from LeafNodeType to RootNode.
using NodeChainType = typename NodeChain<RootNode, LEVEL>::Type;
static_assert(NodeChainType::Size == LEVEL + 1,
"wrong number of entries in RootNode node chain");
/// @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>
struct ValueConverter {
using Type = RootNode<typename ChildType::template ValueConverter<OtherValueType>::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();
/// Construct a new tree with the given background value.
explicit RootNode(const ValueType& background);
RootNode(const RootNode& other) { *this = other; }
/// @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);
/// @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);
/// @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->clear(); }
private:
struct Tile {
Tile(): value(zeroVal<ValueType>()), active(false) {}
Tile(const ValueType& v, bool b): value(v), active(b) {}
ValueType value;
bool active;
};
// This lightweight struct pairs child pointers and tiles.
struct NodeStruct {
ChildType* child;
Tile tile;
NodeStruct(): child(nullptr) {}
NodeStruct(ChildType& c): child(&c) {}
NodeStruct(const Tile& t): child(nullptr), tile(t) {}
NodeStruct(const NodeStruct&) = default;
NodeStruct& operator=(const NodeStruct&) = default;
~NodeStruct() {} ///< @note doesn't delete child
bool isChild() const { return child != nullptr; }
bool isTile() const { return child == nullptr; }
bool isTileOff() const { return isTile() && !tile.active; }
bool isTileOn() const { return isTile() && tile.active; }
void set(ChildType& c) { delete child; child = &c; }
void set(const Tile& t) { delete child; child = nullptr; tile = t; }
ChildType& steal(const Tile& t) { ChildType* c=child; child=nullptr; tile=t; return *c; }
};
using MapType = std::map<Coord, NodeStruct>;
using MapIter = typename MapType::iterator;
using MapCIter = typename MapType::const_iterator;
using CoordSet = std::set<Coord>;
using CoordSetIter = typename CoordSet::iterator;
using CoordSetCIter = typename CoordSet::const_iterator;
static void setTile(const MapIter& i, const Tile& t) { i->second.set(t); }
static void setChild(const MapIter& i, ChildType& c) { i->second.set(c); }
static Tile& getTile(const MapIter& i) { return i->second.tile; }
static const Tile& getTile(const MapCIter& i) { return i->second.tile; }
static ChildType& getChild(const MapIter& i) { return *(i->second.child); }
static const ChildType& getChild(const MapCIter& i) { return *(i->second.child); }
static ChildType& stealChild(const MapIter& i, const Tile& t) {return i->second.steal(t);}
static const ChildType& stealChild(const MapCIter& i,const Tile& t) {return i->second.steal(t);}
static bool isChild(const MapCIter& i) { return i->second.isChild(); }
static bool isChild(const MapIter& i) { return i->second.isChild(); }
static bool isTile(const MapCIter& i) { return i->second.isTile(); }
static bool isTile(const MapIter& i) { return i->second.isTile(); }
static bool isTileOff(const MapCIter& i) { return i->second.isTileOff(); }
static bool isTileOff(const MapIter& i) { return i->second.isTileOff(); }
static bool isTileOn(const MapCIter& i) { return i->second.isTileOn(); }
static bool isTileOn(const MapIter& i) { return i->second.isTileOn(); }
struct NullPred {
static inline bool test(const MapIter&) { return true; }
static inline bool test(const MapCIter&) { return true; }
};
struct ValueOnPred {
static inline bool test(const MapIter& i) { return isTileOn(i); }
static inline bool test(const MapCIter& i) { return isTileOn(i); }
};
struct ValueOffPred {
static inline bool test(const MapIter& i) { return isTileOff(i); }
static inline bool test(const MapCIter& i) { return isTileOff(i); }
};
struct ValueAllPred {
static inline bool test(const MapIter& i) { return isTile(i); }
static inline bool test(const MapCIter& i) { return isTile(i); }
};
struct ChildOnPred {
static inline bool test(const MapIter& i) { return isChild(i); }
static inline bool test(const MapCIter& i) { return isChild(i); }
};
struct ChildOffPred {
static inline bool test(const MapIter& i) { return isTile(i); }
static inline bool test(const MapCIter& i) { return isTile(i); }
};
template<typename _RootNodeT, typename _MapIterT, typename FilterPredT>
class BaseIter
{
public:
using RootNodeT = _RootNodeT;
using MapIterT = _MapIterT; // either MapIter or MapCIter
bool operator==(const BaseIter& other) const
{
return (mParentNode == other.mParentNode) && (mIter == other.mIter);
}
bool operator!=(const BaseIter& other) const { return !(*this == other); }
RootNodeT* getParentNode() const { return mParentNode; }
/// Return a reference to the node over which this iterator iterates.
RootNodeT& parent() const
{
if (!mParentNode) OPENVDB_THROW(ValueError, "iterator references a null parent node");
return *mParentNode;
}
bool test() const { assert(mParentNode); return mIter != mParentNode->mTable.end(); }
operator bool() const { return this->test(); }
void increment() { if (this->test()) { ++mIter; } this->skip(); }
bool next() { this->increment(); return this->test(); }
void increment(Index n) { for (Index i = 0; i < n && this->next(); ++i) {} }
/// @brief Return this iterator's position as an offset from
/// the beginning of the parent node's map.
Index pos() const
{
return !mParentNode ? 0U : Index(std::distance(mParentNode->mTable.begin(), mIter));
}
bool isValueOn() const { return RootNodeT::isTileOn(mIter); }
bool isValueOff() const { return RootNodeT::isTileOff(mIter); }
void setValueOn(bool on = true) const { mIter->second.tile.active = on; }
void setValueOff() const { mIter->second.tile.active = false; }
/// Return the coordinates of the item to which this iterator is pointing.
Coord getCoord() const { return mIter->first; }
/// Return in @a xyz the coordinates of the item to which this iterator is pointing.
void getCoord(Coord& xyz) const { xyz = this->getCoord(); }
protected:
BaseIter(): mParentNode(nullptr) {}
BaseIter(RootNodeT& parent, const MapIterT& iter): mParentNode(&parent), mIter(iter) {}
void skip() { while (this->test() && !FilterPredT::test(mIter)) ++mIter; }
RootNodeT* mParentNode;
MapIterT mIter;
}; // BaseIter
template<typename RootNodeT, typename MapIterT, typename FilterPredT, typename ChildNodeT>
class ChildIter: public BaseIter<RootNodeT, MapIterT, FilterPredT>
{
public:
using BaseT = BaseIter<RootNodeT, MapIterT, FilterPredT>;
using NodeType = RootNodeT;
using ValueType = NodeType;
using ChildNodeType = ChildNodeT;
using NonConstNodeType = typename std::remove_const<NodeType>::type;
using NonConstValueType = typename std::remove_const<ValueType>::type;
using NonConstChildNodeType = typename std::remove_const<ChildNodeType>::type;
using BaseT::mIter;
ChildIter() {}
ChildIter(RootNodeT& parent, const MapIterT& iter): BaseT(parent, iter) { BaseT::skip(); }
ChildIter& operator++() { BaseT::increment(); return *this; }
ChildNodeT& getValue() const { return getChild(mIter); }
ChildNodeT& operator*() const { return this->getValue(); }
ChildNodeT* operator->() const { return &this->getValue(); }
}; // ChildIter
template<typename RootNodeT, typename MapIterT, typename FilterPredT, typename ValueT>
class ValueIter: public BaseIter<RootNodeT, MapIterT, FilterPredT>
{
public:
using BaseT = BaseIter<RootNodeT, MapIterT, FilterPredT>;
using NodeType = RootNodeT;
using ValueType = ValueT;
using NonConstNodeType = typename std::remove_const<NodeType>::type;
using NonConstValueType = typename std::remove_const<ValueT>::type;
using BaseT::mIter;
ValueIter() {}
ValueIter(RootNodeT& parent, const MapIterT& iter): BaseT(parent, iter) { BaseT::skip(); }
ValueIter& operator++() { BaseT::increment(); return *this; }
ValueT& getValue() const { return getTile(mIter).value; }
ValueT& operator*() const { return this->getValue(); }
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>
class DenseIter: public BaseIter<RootNodeT, MapIterT, NullPred>
{
public:
using BaseT = BaseIter<RootNodeT, MapIterT, NullPred>;
using NodeType = RootNodeT;
using ValueType = ValueT;
using ChildNodeType = ChildNodeT;
using NonConstNodeType = typename std::remove_const<NodeType>::type;
using NonConstValueType = typename std::remove_const<ValueT>::type;
using NonConstChildNodeType = typename std::remove_const<ChildNodeT>::type;
using BaseT::mIter;
DenseIter() {}
DenseIter(RootNodeT& parent, const MapIterT& iter): BaseT(parent, iter) {}
DenseIter& operator++() { BaseT::increment(); return *this; }
bool isChildNode() const { return isChild(mIter); }
ChildNodeT* probeChild(NonConstValueType& value) const
{
if (isChild(mIter)) return &getChild(mIter);
value = getTile(mIter).value;
return nullptr;
}
bool probeChild(ChildNodeT*& child, NonConstValueType& value) const
{
child = this->probeChild(value);
return child != nullptr;
}
bool probeValue(NonConstValueType& value) const { return !this->probeChild(value); }
void setChild(ChildNodeT& c) const { RootNodeT::setChild(mIter, c); }
void setChild(ChildNodeT* c) const { assert(c != nullptr); RootNodeT::setChild(mIter, *c); }
void setValue(const ValueT& v) const
{
if (isTile(mIter)) getTile(mIter).value = v;
/// @internal For consistency with iterators for other node types
/// (see, e.g., InternalNode::DenseIter::unsetItem()), we don't call
/// setTile() here, because that would also delete the child.
else stealChild(mIter, Tile(v, /*active=*/true));
}
}; // DenseIter
public:
using ChildOnIter = ChildIter<RootNode, MapIter, ChildOnPred, ChildType>;
using ChildOnCIter = ChildIter<const RootNode, MapCIter, ChildOnPred, const ChildType>;
using ChildOffIter = ValueIter<RootNode, MapIter, ChildOffPred, const ValueType>;
using ChildOffCIter = ValueIter<const RootNode, MapCIter, ChildOffPred, ValueType>;
using ChildAllIter = DenseIter<RootNode, MapIter, ChildType, ValueType>;
using ChildAllCIter = DenseIter<const RootNode, MapCIter, const ChildType, const ValueType>;
using ValueOnIter = ValueIter<RootNode, MapIter, ValueOnPred, ValueType>;
using ValueOnCIter = ValueIter<const RootNode, MapCIter, ValueOnPred, const ValueType>;
using ValueOffIter = ValueIter<RootNode, MapIter, ValueOffPred, ValueType>;
using ValueOffCIter = ValueIter<const RootNode, MapCIter, ValueOffPred, const ValueType>;
using ValueAllIter = ValueIter<RootNode, MapIter, ValueAllPred, ValueType>;
using ValueAllCIter = ValueIter<const RootNode, MapCIter, ValueAllPred, const ValueType>;
ChildOnCIter cbeginChildOn() const { return ChildOnCIter(*this, mTable.begin()); }
ChildOffCIter cbeginChildOff() const { return ChildOffCIter(*this, mTable.begin()); }
ChildAllCIter cbeginChildAll() const { return ChildAllCIter(*this, mTable.begin()); }
ChildOnCIter beginChildOn() const { return cbeginChildOn(); }
ChildOffCIter beginChildOff() const { return cbeginChildOff(); }
ChildAllCIter beginChildAll() const { return cbeginChildAll(); }
ChildOnIter beginChildOn() { return ChildOnIter(*this, mTable.begin()); }
ChildOffIter beginChildOff() { return ChildOffIter(*this, mTable.begin()); }
ChildAllIter beginChildAll() { return ChildAllIter(*this, mTable.begin()); }
ValueOnCIter cbeginValueOn() const { return ValueOnCIter(*this, mTable.begin()); }
ValueOffCIter cbeginValueOff() const { return ValueOffCIter(*this, mTable.begin()); }
ValueAllCIter cbeginValueAll() const { return ValueAllCIter(*this, mTable.begin()); }
ValueOnCIter beginValueOn() const { return cbeginValueOn(); }
ValueOffCIter beginValueOff() const { return cbeginValueOff(); }
ValueAllCIter beginValueAll() const { return cbeginValueAll(); }
ValueOnIter beginValueOn() { return ValueOnIter(*this, mTable.begin()); }
ValueOffIter beginValueOff() { return ValueOffIter(*this, mTable.begin()); }
ValueAllIter beginValueAll() { return ValueAllIter(*this, mTable.begin()); }
/// Return the total amount of memory in bytes occupied by this node and its children.
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. 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(); }
/// Return the transient data value.
Index32 transientData() const { return mTransientData; }
/// Set the transient data value.
void setTransientData(Index32 transientData) { mTransientData = transientData; }
/// @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.
///
/// @param value The new background value
/// @param updateChildNodes If true the background values of the
/// child nodes is also updated. Else only the background value
/// stored in the RootNode itself is changed.
///
/// @note Instead of setting @a updateChildNodes to true, consider
/// using tools::changeBackground or
/// tools::changeLevelSetBackground which are multi-threaded!
void setBackground(const ValueType& value, bool updateChildNodes);
/// Return this node's background value.
const ValueType& background() const { return mBackground; }
/// Return @c true if the given tile is inactive and has the background value.
bool isBackgroundTile(const Tile&) const;
//@{
/// Return @c true if the given iterator points to an inactive tile with the background value.
bool isBackgroundTile(const MapIter&) const;
bool isBackgroundTile(const MapCIter&) const;
//@}
/// Return the number of background tiles.
size_t numBackgroundTiles() const;
/// @brief Remove all background tiles.
/// @return the number of tiles removed.
size_t eraseBackgroundTiles();
inline void clear();
/// Return @c true if this node's table is either empty or contains only background tiles.
bool empty() const { return mTable.size() == numBackgroundTiles(); }
/// @brief Expand this node's table so that (x, y, z) is included in the index range.
/// @return @c true if an expansion was performed (i.e., if (x, y, z) was not already
/// included in the index range).
bool expand(const Coord& xyz);
static Index getLevel() { return LEVEL; }
static void getNodeLog2Dims(std::vector<Index>& dims);
static Index getChildDim() { return ChildType::DIM; }
/// Return the number of entries in this node's table.
Index getTableSize() const { return static_cast< Index >(mTable.size()); }
Index getWidth() const { return this->getMaxIndex()[0] - this->getMinIndex()[0]; }
Index getHeight() const { return this->getMaxIndex()[1] - this->getMinIndex()[1]; }
Index getDepth() const { return this->getMaxIndex()[2] - this->getMinIndex()[2]; }
/// Return the smallest index of the current tree.
Coord getMinIndex() const;
/// Return the largest index of the current tree.
Coord getMaxIndex() const;
/// Return the current index range. Both min and max are inclusive.
void getIndexRange(CoordBBox& bbox) const;
/// @brief Return @c true if the given tree has the same node and active value
/// topology as this tree (but possibly a different @c ValueType).
template<typename OtherChildType>
bool hasSameTopology(const RootNode<OtherChildType>& other) const;
/// Return @c false if the other node's dimensions don't match this node's.
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;
Index32 childCount() const;
Index64 onVoxelCount() const;
Index64 offVoxelCount() const;
Index64 onLeafVoxelCount() const;
Index64 offLeafVoxelCount() const;
Index64 onTileCount() const;
void nodeCount(std::vector<Index32> &vec) const;
bool isValueOn(const Coord& xyz) const;
/// Return @c true if this root node, or any of its child nodes, have active tiles.
bool hasActiveTiles() const;
const ValueType& getValue(const Coord& xyz) const;
bool probeValue(const Coord& xyz, ValueType& value) const;
/// @brief Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides.
/// @details If (x, y, z) isn't explicitly represented in the tree (i.e.,
/// it is implicitly a background voxel), return -1.
int getValueDepth(const Coord& xyz) const;
/// Set the active state of the voxel at the given coordinates but don't change its value.
void setActiveState(const Coord& xyz, bool on);
/// Set the value of the voxel at the given coordinates but don't change its active state.
void setValueOnly(const Coord& xyz, const ValueType& value);
/// 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);
/// 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 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 axis-aligned box to a constant value.
/// @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
/// @note This operation generates a sparse, but not always optimally sparse,
/// representation of the filled box. Follow fill operations with a prune()
/// operation for optimal sparseness.
void fill(const CoordBBox& bbox, const ValueType& value, bool active = true);
void sparseFill(const CoordBBox& bbox, const ValueType& value, bool active = true)
{
this->fill(bbox, value, active);
}
//@}
/// @brief Set all voxels within a given axis-aligned box to a constant value
/// and ensure that those voxels are all represented at the leaf level.
/// @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.
/// @sa voxelizeActiveTiles()
void denseFill(const CoordBBox& bbox, const ValueType& value, bool active = true);
/// @brief Densify active tiles, i.e., replace them with leaf-level active voxels.
///
/// @param threaded if true, this operation is multi-threaded (over the internal nodes).
///
/// @warning This method can explode the tree's memory footprint, especially if it
/// contains active tiles at the upper levels (in particular the root level)!
///
/// @sa denseFill()
void voxelizeActiveTiles(bool threaded = 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
//
bool writeTopology(std::ostream&, bool toHalf = false) const;
bool readTopology(std::istream&, bool fromHalf = false);
void writeBuffers(std::ostream&, bool toHalf = false) const;
void readBuffers(std::istream&, bool fromHalf = false);
void readBuffers(std::istream&, const CoordBBox&, 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.
/// @note Used internally by ValueAccessor.
template<typename AccessorT>
const ValueType& getValueAndCache(const Coord& xyz, AccessorT&) const;
/// Return @c true if the voxel at the given coordinates is active and, 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>
bool isValueOnAndCache(const Coord& xyz, AccessorT&) const;
/// Change the value of the voxel at the given coordinates and mark it 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 setValueAndCache(const Coord& xyz, const ValueType& value, AccessorT&);
/// 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&);
/// 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 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
/// from the root node to the node containing the voxel.
/// @note Used internally by ValueAccessor.
template<typename AccessorT>
void setValueOffAndCache(const Coord& xyz, const ValueType& value, AccessorT&);
/// Set the active state of the voxel at the given coordinates without changing its value.
/// 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 setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&);
/// 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;
/// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides.
/// If (x, y, z) isn't explicitly represented in the tree (i.e., it is implicitly
/// a background voxel), return -1. 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>
int getValueDepthAndCache(const Coord& xyz, AccessorT&) const;
/// Set all voxels that lie outside the given axis-aligned box to the background.
void clip(const CoordBBox&);
/// @brief Reduce the memory footprint of this tree by replacing with tiles
/// any nodes whose values are all the same (optionally to within a tolerance)
/// and have the same active state.
///
/// @note Consider instead using tools::prune which is multi-threaded!
void prune(const ValueType& tolerance = zeroVal<ValueType>());
/// @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() 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&);
/// @brief Return a pointer to the node of type @c NodeT that contains voxel (x, y, z)
/// and replace it with a tile of the specified value and state.
/// If no such node exists, leave the tree unchanged and return @c nullptr.
///
/// @note The caller takes ownership of the node and is responsible for deleting it.
///
/// @warning Since this method potentially removes nodes and branches of the tree,
/// it is important to clear the caches of all ValueAccessors associated with this tree.
template<typename NodeT>
NodeT* stealNode(const Coord& xyz, const ValueType& value, bool state);
/// @brief Add the given child node at the root level.
/// If a child node with the same origin already exists, delete the old node and add
/// the new node in its place (i.e. ownership of the new child node is transferred
/// to this RootNode).
/// @return @c true (for consistency with InternalNode::addChild)
bool addChild(ChildType* child);
/// @brief Add a tile containing voxel (x, y, z) at the root level,
/// deleting the existing branch if necessary.
void addTile(const Coord& xyz, const ValueType& value, bool state);
/// @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() 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&, bool state, AccessorT&);
/// @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() 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 node that contains voxel (x, y, z).
/// If no such node exists, return @c nullptr.
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 pointer to the leaf node that contains voxel (x, y, z).
/// If no such node exists, return @c nullptr.
LeafNodeType* probeLeaf(const Coord& xyz);
const LeafNodeType* probeConstLeaf(const Coord& xyz) const;
const LeafNodeType* probeLeaf(const Coord& xyz) const;
//@}
//@{
/// @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);
template<typename AccessorT>
const LeafNodeType* probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const;
template<typename AccessorT>
const LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc) const;
//@}
//
// Aux methods
//
//@{
/// @brief Adds all nodes of a certain type to a container with the following API:
/// @code
/// struct ArrayT {
/// using value_type = ...;// defines the type of nodes to be added to the array
/// void push_back(value_type nodePtr);// method that add nodes to the array
/// };
/// @endcode
/// @details An example of a wrapper around a c-style array is:
/// @code
/// struct MyArray {
/// using value_type = LeafType*;
/// value_type* ptr;
/// MyArray(value_type* array) : ptr(array) {}
/// void push_back(value_type leaf) { *ptr++ = leaf; }
///};
/// @endcode
/// @details An example that constructs a list of pointer to all leaf nodes is:
/// @code
/// std::vector<const LeafNodeType*> array;//most std contains have the required API
/// array.reserve(tree.leafCount());//this is a fast preallocation.
/// tree.getNodes(array);
/// @endcode
template<typename ArrayT> void getNodes(ArrayT& array);
template<typename ArrayT> void getNodes(ArrayT& array) const;
//@}
//@{
/// @brief Steals all nodes of a certain type from the tree and
/// adds them to a container with the following API:
/// @code
/// struct ArrayT {
/// using value_type = ...;// defines the type of nodes to be added to the array
/// void push_back(value_type nodePtr);// method that add nodes to the array
/// };
/// @endcode
/// @details An example of a wrapper around a c-style array is:
/// @code
/// struct MyArray {
/// using value_type = LeafType*;
/// value_type* ptr;
/// MyArray(value_type* array) : ptr(array) {}
/// void push_back(value_type leaf) { *ptr++ = leaf; }
///};
/// @endcode
/// @details An example that constructs a list of pointer to all leaf nodes is:
/// @code
/// std::vector<const LeafNodeType*> array;//most std contains have the required API
/// array.reserve(tree.leafCount());//this is a fast preallocation.
/// tree.stealNodes(array);
/// @endcode
template<typename ArrayT>
void stealNodes(ArrayT& array, const ValueType& value, bool state);
template<typename ArrayT>
void stealNodes(ArrayT& array) { this->stealNodes(array, mBackground, false); }
//@}
/// @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
/// was already active OR if it is active in the other tree. Also, a resulting
/// value maps to a voxel if the corresponding value already mapped to a voxel
/// OR if it is a voxel in the other tree. Thus, a resulting value can only
/// map to a tile if the corresponding value already mapped to a tile
/// AND if it is a tile value in other tree.
///
/// @note This operation modifies only active states, not values.
/// Specifically, active tiles and voxels in this tree are not changed, and
/// tiles or voxels that were inactive in this tree but active in the other tree
/// are marked as active in this tree but left with their original values.
///
/// @note If preserveTiles is true, any active tile in this topology
/// will not be densified by overlapping child topology.
template<typename OtherChildType>
void topologyUnion(const RootNode<OtherChildType>& other, const bool preserveTiles = false);
/// @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, typename OtherRootNode /*= RootNode*/>
void combine2(const RootNode& other0, const OtherRootNode& other1,
CombineOp& op, bool prune = false);
#if OPENVDB_ABI_VERSION_NUMBER >= 10
/// Return the grid index coordinates of this node's local origin.
const Coord& origin() const { return mOrigin; }
/// @brief change the origin on this root node
/// @param origin the index coordinates of the new alignment
///
/// @warning This method will throw if the origin is non-zero, since
/// other tools do not yet support variable offsets.
void setOrigin(const Coord &origin);
#endif
private:
/// During topology-only construction, access is needed
/// 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() {}
//@{
/// @internal Used by doVisit2().
void resetTable(MapType& table) { mTable.swap(table); table.clear(); }
void resetTable(const MapType&) const {}
//@}
Index getChildCount() const;
Index getTileCount() const;
Index getActiveTileCount() const;
Index getInactiveTileCount() const;
#if OPENVDB_ABI_VERSION_NUMBER < 10
/// Static method that returns a MapType key for the given coordinates.
static Coord coordToKey(const Coord& xyz) {return xyz & ~(ChildType::DIM - 1); }
#else
/// Return a MapType key for the given coordinates, offset by the mOrigin.
Coord coordToKey(const Coord& xyz) const { return (xyz - mOrigin) & ~(ChildType::DIM - 1); }
#endif
/// Insert this node's mTable keys into the given set.
void insertKeys(CoordSet&) const;
/// Return @c true if this node's mTable contains the given key.
bool hasKey(const Coord& key) const { return mTable.find(key) != mTable.end(); }
//@{
/// @brief Look up the given key in this node's mTable.
/// @return an iterator pointing to the matching mTable entry or to mTable.end().
MapIter findKey(const Coord& key) { return mTable.find(key); }
MapCIter findKey(const Coord& key) const { return mTable.find(key); }
//@}
//@{
/// @brief Convert the given coordinates to a key and look the key up in this node's mTable.
/// @return an iterator pointing to the matching mTable entry or to mTable.end().
MapIter findCoord(const Coord& xyz) { return mTable.find(coordToKey(xyz)); }
MapCIter findCoord(const Coord& xyz) const { return mTable.find(coordToKey(xyz)); }
//@}
/// @brief Convert the given coordinates to a key and look the key up in this node's mTable.
/// @details If the key is not found, insert a background tile with that key.
/// @return an iterator pointing to the matching mTable entry.
MapIter findOrAddCoord(const Coord& xyz);
/// @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);
MapType mTable;
ValueType mBackground;
#if OPENVDB_ABI_VERSION_NUMBER >= 10
Coord mOrigin;
#endif
/// Transient Data (not serialized)
Index32 mTransientData = 0;
}; // end of RootNode class
////////////////////////////////////////
/// @brief NodeChain<RootNodeType, RootNodeType::LEVEL>::Type is a openvdb::TypeList
/// 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
/// openvdb::TypeList<
/// 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
/// NodeChainType::Get<N>;
/// @endcode
template<typename HeadT, int HeadLevel>
struct NodeChain {
using SubtreeT = typename NodeChain<typename HeadT::ChildNodeType, HeadLevel-1>::Type;
using Type = typename SubtreeT::template Append<HeadT>;
};
/// Specialization to terminate NodeChain
template<typename HeadT>
struct NodeChain<HeadT, /*HeadLevel=*/1> {
using Type = TypeList<typename HeadT::ChildNodeType, HeadT>;
};
////////////////////////////////////////
//@{
/// 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>())
#if OPENVDB_ABI_VERSION_NUMBER >= 10
, mOrigin(0, 0, 0)
#endif
{
this->initTable();
}
template<typename ChildT>
inline
RootNode<ChildT>::RootNode(const ValueType& background)
: mBackground(background)
#if OPENVDB_ABI_VERSION_NUMBER >= 10
, mOrigin(0, 0, 0)
#endif
{
this->initTable();
}
template<typename ChildT>
template<typename OtherChildType>
inline
RootNode<ChildT>::RootNode(const RootNode<OtherChildType>& other,
const ValueType& backgd, const ValueType& foregd, TopologyCopy)
: mBackground(backgd)
#if OPENVDB_ABI_VERSION_NUMBER >= 10
, mOrigin(other.mOrigin)
#endif
, mTransientData(other.mTransientData)
{
using OtherRootT = RootNode<OtherChildType>;
#if OPENVDB_ABI_VERSION_NUMBER >= 10
if (mOrigin != Coord(0,0,0)) {
OPENVDB_THROW(ValueError, "RootNode::RootNode: non-zero offsets are currently not supported");
}
#endif
enforceSameConfiguration(other);
const Tile bgTile(backgd, /*active=*/false), fgTile(foregd, true);
this->initTable();
for (typename OtherRootT::MapCIter i=other.mTable.begin(), e=other.mTable.end(); i != e; ++i) {
mTable[i->first] = OtherRootT::isTile(i)
? NodeStruct(OtherRootT::isTileOn(i) ? fgTile : bgTile)
: NodeStruct(*(new ChildT(OtherRootT::getChild(i), backgd, foregd, TopologyCopy())));
}
}
template<typename ChildT>
template<typename OtherChildType>
inline
RootNode<ChildT>::RootNode(const RootNode<OtherChildType>& other,
const ValueType& backgd, TopologyCopy)
: mBackground(backgd)
#if OPENVDB_ABI_VERSION_NUMBER >= 10
, mOrigin(other.mOrigin)
#endif
, mTransientData(other.mTransientData)
{
using OtherRootT = RootNode<OtherChildType>;
#if OPENVDB_ABI_VERSION_NUMBER >= 10
if (mOrigin != Coord(0,0,0)) {
OPENVDB_THROW(ValueError, "RootNode::RootNode: non-zero offsets are currently not supported");
}
#endif
enforceSameConfiguration(other);
const Tile bgTile(backgd, /*active=*/false), fgTile(backgd, true);
this->initTable();
for (typename OtherRootT::MapCIter i=other.mTable.begin(), e=other.mTable.end(); i != e; ++i) {
mTable[i->first] = OtherRootT::isTile(i)
? NodeStruct(OtherRootT::isTileOn(i) ? fgTile : bgTile)
: NodeStruct(*(new ChildT(OtherRootT::getChild(i), backgd, TopologyCopy())));
}
}
////////////////////////////////////////
// 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)
{
using ValueT = typename RootT::ValueType;
using ChildT = typename RootT::ChildNodeType;
using NodeStruct = typename RootT::NodeStruct;
using Tile = typename RootT::Tile;
using OtherValueT = typename OtherRootT::ValueType;
using OtherMapCIter = typename OtherRootT::MapCIter;
using OtherTile = typename OtherRootT::Tile;
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);
#if OPENVDB_ABI_VERSION_NUMBER >= 10
if (other.mOrigin != Coord(0,0,0)) {
OPENVDB_THROW(ValueError, "RootNodeCopyHelper::copyWithValueConversion: non-zero offsets are currently not supported");
}
self.mOrigin = other.mOrigin;
#endif
self.mTransientData = other.mTransientData;
self.clear();
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)
{
if (&other != this) {
mBackground = other.mBackground;
#if OPENVDB_ABI_VERSION_NUMBER >= 10
mOrigin = other.mOrigin;
if (mOrigin != Coord(0,0,0)) {
OPENVDB_THROW(ValueError, "RootNode::operator=: non-zero offsets are currently not supported");
}
#endif
mTransientData = other.mTransientData;
this->clear();
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))));
}
}
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)
{
using OtherRootT = RootNode<OtherChildType>;
using OtherValueT = typename OtherRootT::ValueType;
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, bool updateChildNodes)
{
if (math::isExactlyEqual(background, mBackground)) return;
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);
}
}
}
}
mBackground = background;
}
template<typename ChildT>
inline bool
RootNode<ChildT>::isBackgroundTile(const Tile& tile) const
{
return !tile.active && math::isApproxEqual(tile.value, mBackground);
}
template<typename ChildT>
inline bool
RootNode<ChildT>::isBackgroundTile(const MapIter& iter) const
{
return isTileOff(iter) && math::isApproxEqual(getTile(iter).value, mBackground);
}
template<typename ChildT>
inline bool
RootNode<ChildT>::isBackgroundTile(const MapCIter& iter) const
{
return isTileOff(iter) && math::isApproxEqual(getTile(iter).value, mBackground);
}
template<typename ChildT>
inline size_t
RootNode<ChildT>::numBackgroundTiles() const
{
size_t count = 0;
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (this->isBackgroundTile(i)) ++count;
}
return count;
}
template<typename ChildT>
inline size_t
RootNode<ChildT>::eraseBackgroundTiles()
{
std::set<Coord> keysToErase;
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (this->isBackgroundTile(i)) keysToErase.insert(i->first);
}
for (std::set<Coord>::iterator i = keysToErase.begin(), e = keysToErase.end(); i != e; ++i) {
mTable.erase(*i);
}
return keysToErase.size();
}
////////////////////////////////////////
template<typename ChildT>
inline void
RootNode<ChildT>::insertKeys(CoordSet& keys) const
{
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
keys.insert(i->first);
}
}
template<typename ChildT>
inline typename RootNode<ChildT>::MapIter
RootNode<ChildT>::findOrAddCoord(const Coord& xyz)
{
const Coord key = coordToKey(xyz);
std::pair<MapIter, bool> result = mTable.insert(
typename MapType::value_type(key, NodeStruct(Tile(mBackground, /*active=*/false))));
return result.first;
}
template<typename ChildT>
inline bool
RootNode<ChildT>::expand(const Coord& xyz)
{
const Coord key = coordToKey(xyz);
std::pair<MapIter, bool> result = mTable.insert(
typename MapType::value_type(key, NodeStruct(Tile(mBackground, /*active=*/false))));
return result.second; // return true if the key did not already exist
}
////////////////////////////////////////
template<typename ChildT>
inline void
RootNode<ChildT>::getNodeLog2Dims(std::vector<Index>& dims)
{
dims.push_back(0); // magic number; RootNode has no Log2Dim
ChildT::getNodeLog2Dims(dims);
}
template<typename ChildT>
inline Coord
RootNode<ChildT>::getMinIndex() const
{
return mTable.empty() ? Coord(0) : mTable.begin()->first;
}
template<typename ChildT>
inline Coord
RootNode<ChildT>::getMaxIndex() const
{
return mTable.empty() ? Coord(0) : mTable.rbegin()->first + Coord(ChildT::DIM - 1);
}
template<typename ChildT>
inline void
RootNode<ChildT>::getIndexRange(CoordBBox& bbox) const
{
bbox.min() = this->getMinIndex();
bbox.max() = this->getMaxIndex();
}
////////////////////////////////////////
template<typename ChildT>
template<typename OtherChildType>
inline bool
RootNode<ChildT>::hasSameTopology(const RootNode<OtherChildType>& other) const
{
using OtherRootT = RootNode<OtherChildType>;
using OtherMapT = typename OtherRootT::MapType;
using OtherIterT = typename OtherRootT::MapIter;
using OtherCIterT = typename OtherRootT::MapCIter;
if (!hasSameConfiguration(other)) return false;
// Create a local copy of the other node's table.
OtherMapT copyOfOtherTable = other.mTable;
// For each entry in this node's table...
for (MapCIter thisIter = mTable.begin(); thisIter != mTable.end(); ++thisIter) {
if (this->isBackgroundTile(thisIter)) continue; // ignore background tiles
// Fail if there is no corresponding entry in the other node's table.
OtherCIterT otherIter = other.findKey(thisIter->first);
if (otherIter == other.mTable.end()) return false;
// Fail if this entry is a tile and the other is a child or vice-versa.
if (isChild(thisIter)) {//thisIter points to a child
if (OtherRootT::isTile(otherIter)) return false;
// Fail if both entries are children, but the children have different topology.
if (!getChild(thisIter).hasSameTopology(&OtherRootT::getChild(otherIter))) return false;
} else {//thisIter points to a tile
if (OtherRootT::isChild(otherIter)) return false;
if (getTile(thisIter).active != OtherRootT::getTile(otherIter).active) return false;
}
// Remove tiles and child nodes with matching topology from
// the copy of the other node's table. This is required since
// the two root tables can include an arbitrary number of
// background tiles and still have the same topology!
copyOfOtherTable.erase(otherIter->first);
}
// Fail if the remaining entries in copyOfOtherTable are not all background tiles.
for (OtherIterT i = copyOfOtherTable.begin(), e = copyOfOtherTable.end(); i != e; ++i) {
if (!other.isBackgroundTile(i)) return false;
}
return true;
}
template<typename ChildT>
template<typename OtherChildType>
inline bool
RootNode<ChildT>::hasSameConfiguration(const RootNode<OtherChildType>&)
{
std::vector<Index> thisDims, otherDims;
RootNode::getNodeLog2Dims(thisDims);
RootNode<OtherChildType>::getNodeLog2Dims(otherDims);
return (thisDims == otherDims);
}
template<typename ChildT>
template<typename OtherChildType>
inline void
RootNode<ChildT>::enforceSameConfiguration(const RootNode<OtherChildType>&)
{
std::vector<Index> thisDims, otherDims;
RootNode::getNodeLog2Dims(thisDims);
RootNode<OtherChildType>::getNodeLog2Dims(otherDims);
if (thisDims != otherDims) {
std::ostringstream ostr;
ostr << "grids have incompatible configurations (" << thisDims[0];
for (size_t i = 1, N = thisDims.size(); i < N; ++i) ostr << " x " << thisDims[i];
ostr << " vs. " << otherDims[0];
for (size_t i = 1, N = otherDims.size(); i < N; ++i) ostr << " x " << otherDims[i];
ostr << ")";
OPENVDB_THROW(TypeError, ostr.str());
}
}
template<typename ChildT>
template<typename OtherChildType>
inline bool
RootNode<ChildT>::hasCompatibleValueType(const RootNode<OtherChildType>&)
{
using OtherValueType = typename OtherChildType::ValueType;
return CanConvertType</*from=*/OtherValueType, /*to=*/ ValueType>::value;
}
template<typename ChildT>
template<typename OtherChildType>
inline void
RootNode<ChildT>::enforceCompatibleValueTypes(const RootNode<OtherChildType>&)
{
using OtherValueType = typename OtherChildType::ValueType;
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());
}
}
////////////////////////////////////////
template<typename ChildT>
inline Index64
RootNode<ChildT>::memUsage() const
{
Index64 sum = sizeof(*this);
for (MapCIter iter=mTable.begin(); iter!=mTable.end(); ++iter) {
if (const ChildT *child = iter->second.child) {
sum += child->memUsage();
}
}
return sum;
}
template<typename ChildT>
inline void
RootNode<ChildT>::clear()
{
for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
delete i->second.child;
}
mTable.clear();
}
template<typename ChildT>
inline void
RootNode<ChildT>::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const
{
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);
}
}
}
template<typename ChildT>
inline Index
RootNode<ChildT>::getChildCount() const {
return this->childCount();
}
template<typename ChildT>
inline Index
RootNode<ChildT>::getTileCount() const
{
Index sum = 0;
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isTile(i)) ++sum;
}
return sum;
}
template<typename ChildT>
inline Index
RootNode<ChildT>::getActiveTileCount() const
{
Index sum = 0;
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isTileOn(i)) ++sum;
}
return sum;
}
template<typename ChildT>
inline Index
RootNode<ChildT>::getInactiveTileCount() const
{
Index sum = 0;
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isTileOff(i)) ++sum;
}
return sum;
}
template<typename ChildT>
inline Index32
RootNode<ChildT>::leafCount() const
{
Index32 sum = 0;
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isChild(i)) sum += getChild(i).leafCount();
}
return sum;
}
template<typename ChildT>
inline Index32
RootNode<ChildT>::nonLeafCount() const
{
Index32 sum = 1;
if (ChildT::LEVEL != 0) {
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isChild(i)) sum += getChild(i).nonLeafCount();
}
}
return sum;
}
template<typename ChildT>
inline Index32
RootNode<ChildT>::childCount() const
{
Index sum = 0;
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isChild(i)) ++sum;
}
return sum;
}
template<typename ChildT>
inline Index64
RootNode<ChildT>::onVoxelCount() const
{
Index64 sum = 0;
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isChild(i)) {
sum += getChild(i).onVoxelCount();
} else if (isTileOn(i)) {
sum += ChildT::NUM_VOXELS;
}
}
return sum;
}
template<typename ChildT>
inline Index64
RootNode<ChildT>::offVoxelCount() const
{
Index64 sum = 0;
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isChild(i)) {
sum += getChild(i).offVoxelCount();
} else if (isTileOff(i) && !this->isBackgroundTile(i)) {
sum += ChildT::NUM_VOXELS;
}
}
return sum;
}
template<typename ChildT>
inline Index64
RootNode<ChildT>::onLeafVoxelCount() const
{
Index64 sum = 0;
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isChild(i)) sum += getChild(i).onLeafVoxelCount();
}
return sum;
}
template<typename ChildT>
inline Index64
RootNode<ChildT>::offLeafVoxelCount() const
{
Index64 sum = 0;
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isChild(i)) sum += getChild(i).offLeafVoxelCount();
}
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;
}
template<typename ChildT>
inline void
RootNode<ChildT>::nodeCount(std::vector<Index32> &vec) const
{
assert(vec.size() > LEVEL);
Index32 sum = 0;
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isChild(i)) {
++sum;
getChild(i).nodeCount(vec);
}
}
vec[LEVEL] = 1;// one root node
vec[ChildNodeType::LEVEL] = sum;
}
////////////////////////////////////////
template<typename ChildT>
inline bool
RootNode<ChildT>::isValueOn(const Coord& xyz) const
{
MapCIter iter = this->findCoord(xyz);
if (iter == mTable.end() || isTileOff(iter)) return false;
return isTileOn(iter) ? true : getChild(iter).isValueOn(xyz);
}
template<typename ChildT>
inline bool
RootNode<ChildT>::hasActiveTiles() const
{
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isChild(i) ? getChild(i).hasActiveTiles() : getTile(i).active) return true;
}
return false;
}
template<typename ChildT>
template<typename AccessorT>
inline bool
RootNode<ChildT>::isValueOnAndCache(const Coord& xyz, AccessorT& acc) const
{
MapCIter iter = this->findCoord(xyz);
if (iter == mTable.end() || isTileOff(iter)) return false;
if (isTileOn(iter)) return true;
acc.insert(xyz, &getChild(iter));
return getChild(iter).isValueOnAndCache(xyz, acc);
}
template<typename ChildT>
inline const typename ChildT::ValueType&
RootNode<ChildT>::getValue(const Coord& xyz) const
{
MapCIter iter = this->findCoord(xyz);
return iter == mTable.end() ? mBackground
: (isTile(iter) ? getTile(iter).value : getChild(iter).getValue(xyz));
}
template<typename ChildT>
template<typename AccessorT>
inline const typename ChildT::ValueType&
RootNode<ChildT>::getValueAndCache(const Coord& xyz, AccessorT& acc) const
{
MapCIter iter = this->findCoord(xyz);
if (iter == mTable.end()) return mBackground;
if (isChild(iter)) {
acc.insert(xyz, &getChild(iter));
return getChild(iter).getValueAndCache(xyz, acc);
}
return getTile(iter).value;
}
template<typename ChildT>
inline int
RootNode<ChildT>::getValueDepth(const Coord& xyz) const
{
MapCIter iter = this->findCoord(xyz);
return iter == mTable.end() ? -1
: (isTile(iter) ? 0 : int(LEVEL) - int(getChild(iter).getValueLevel(xyz)));
}
template<typename ChildT>
template<typename AccessorT>
inline int
RootNode<ChildT>::getValueDepthAndCache(const Coord& xyz, AccessorT& acc) const
{
MapCIter iter = this->findCoord(xyz);
if (iter == mTable.end()) return -1;
if (isTile(iter)) return 0;
acc.insert(xyz, &getChild(iter));
return int(LEVEL) - int(getChild(iter).getValueLevelAndCache(xyz, acc));
}
template<typename ChildT>
inline void
RootNode<ChildT>::setValueOff(const Coord& xyz)
{
MapIter iter = this->findCoord(xyz);
if (iter != mTable.end() && !isTileOff(iter)) {
if (isTileOn(iter)) {
setChild(iter, *new ChildT(xyz, getTile(iter).value, /*active=*/true));
}
getChild(iter).setValueOff(xyz);
}
}
template<typename ChildT>
inline void
RootNode<ChildT>::setActiveState(const Coord& xyz, bool on)
{
ChildT* child = nullptr;
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
if (on) {
child = new ChildT(xyz, mBackground);
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
} else {
// Nothing to do; (x, y, z) is background and therefore already inactive.
}
} else if (isChild(iter)) {
child = &getChild(iter);
} else if (on != getTile(iter).active) {
child = new ChildT(xyz, getTile(iter).value, !on);
setChild(iter, *child);
}
if (child) child->setActiveState(xyz, on);
}
template<typename ChildT>
template<typename AccessorT>
inline void
RootNode<ChildT>::setActiveStateAndCache(const Coord& xyz, bool on, AccessorT& acc)
{
ChildT* child = nullptr;
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
if (on) {
child = new ChildT(xyz, mBackground);
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
} else {
// Nothing to do; (x, y, z) is background and therefore already inactive.
}
} else if (isChild(iter)) {
child = &getChild(iter);
} else if (on != getTile(iter).active) {
child = new ChildT(xyz, getTile(iter).value, !on);
setChild(iter, *child);
}
if (child) {
acc.insert(xyz, child);
child->setActiveStateAndCache(xyz, on, acc);
}
}
template<typename ChildT>
inline void
RootNode<ChildT>::setValueOff(const Coord& xyz, const ValueType& value)
{
ChildT* child = nullptr;
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
if (!math::isExactlyEqual(mBackground, value)) {
child = new ChildT(xyz, mBackground);
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
}
} else if (isChild(iter)) {
child = &getChild(iter);
} else if (isTileOn(iter) || !math::isExactlyEqual(getTile(iter).value, value)) {
child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
setChild(iter, *child);
}
if (child) child->setValueOff(xyz, value);
}
template<typename ChildT>
template<typename AccessorT>
inline void
RootNode<ChildT>::setValueOffAndCache(const Coord& xyz, const ValueType& value, AccessorT& acc)
{
ChildT* child = nullptr;
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
if (!math::isExactlyEqual(mBackground, value)) {
child = new ChildT(xyz, mBackground);
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
}
} else if (isChild(iter)) {
child = &getChild(iter);
} else if (isTileOn(iter) || !math::isExactlyEqual(getTile(iter).value, value)) {
child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
setChild(iter, *child);
}
if (child) {
acc.insert(xyz, child);
child->setValueOffAndCache(xyz, value, acc);
}
}
template<typename ChildT>
inline void
RootNode<ChildT>::setValueOn(const Coord& xyz, const ValueType& value)
{
ChildT* child = nullptr;
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
child = new ChildT(xyz, mBackground);
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
} else if (isChild(iter)) {
child = &getChild(iter);
} else if (isTileOff(iter) || !math::isExactlyEqual(getTile(iter).value, value)) {
child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
setChild(iter, *child);
}
if (child) child->setValueOn(xyz, value);
}
template<typename ChildT>
template<typename AccessorT>
inline void
RootNode<ChildT>::setValueAndCache(const Coord& xyz, const ValueType& value, AccessorT& acc)
{
ChildT* child = nullptr;
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
child = new ChildT(xyz, mBackground);
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
} else if (isChild(iter)) {
child = &getChild(iter);
} else if (isTileOff(iter) || !math::isExactlyEqual(getTile(iter).value, value)) {
child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
setChild(iter, *child);
}
if (child) {
acc.insert(xyz, child);
child->setValueAndCache(xyz, value, acc);
}
}
template<typename ChildT>
inline void
RootNode<ChildT>::setValueOnly(const Coord& xyz, const ValueType& value)
{
ChildT* child = nullptr;
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
child = new ChildT(xyz, mBackground);
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
} else if (isChild(iter)) {
child = &getChild(iter);
} else if (!math::isExactlyEqual(getTile(iter).value, value)) {
child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
setChild(iter, *child);
}
if (child) child->setValueOnly(xyz, value);
}
template<typename ChildT>
template<typename AccessorT>
inline void
RootNode<ChildT>::setValueOnlyAndCache(const Coord& xyz, const ValueType& value, AccessorT& acc)
{
ChildT* child = nullptr;
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
child = new ChildT(xyz, mBackground);
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
} else if (isChild(iter)) {
child = &getChild(iter);
} else if (!math::isExactlyEqual(getTile(iter).value, value)) {
child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
setChild(iter, *child);
}
if (child) {
acc.insert(xyz, child);
child->setValueOnlyAndCache(xyz, value, acc);
}
}
template<typename ChildT>
template<typename ModifyOp>
inline void
RootNode<ChildT>::modifyValue(const Coord& xyz, const ModifyOp& op)
{
ChildT* child = nullptr;
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
child = new ChildT(xyz, mBackground);
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
} else if (isChild(iter)) {
child = &getChild(iter);
} 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->modifyValue(xyz, op);
}
template<typename ChildT>
template<typename ModifyOp, typename AccessorT>
inline void
RootNode<ChildT>::modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT& acc)
{
ChildT* child = nullptr;
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
child = new ChildT(xyz, mBackground);
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
} else if (isChild(iter)) {
child = &getChild(iter);
} 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);
}
}
template<typename ChildT>
template<typename ModifyOp>
inline void
RootNode<ChildT>::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op)
{
ChildT* child = nullptr;
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
child = new ChildT(xyz, mBackground);
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
} else if (isChild(iter)) {
child = &getChild(iter);
} 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->modifyValueAndActiveState(xyz, op);
}
template<typename ChildT>
template<typename ModifyOp, typename AccessorT>
inline void
RootNode<ChildT>::modifyValueAndActiveStateAndCache(
const Coord& xyz, const ModifyOp& op, AccessorT& acc)
{
ChildT* child = nullptr;
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
child = new ChildT(xyz, mBackground);
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
} else if (isChild(iter)) {
child = &getChild(iter);
} 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->modifyValueAndActiveStateAndCache(xyz, op, acc);
}
}
template<typename ChildT>
inline bool
RootNode<ChildT>::probeValue(const Coord& xyz, ValueType& value) const
{
MapCIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
value = mBackground;
return false;
} else if (isChild(iter)) {
return getChild(iter).probeValue(xyz, value);
}
value = getTile(iter).value;
return isTileOn(iter);
}
template<typename ChildT>
template<typename AccessorT>
inline bool
RootNode<ChildT>::probeValueAndCache(const Coord& xyz, ValueType& value, AccessorT& acc) const
{
MapCIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
value = mBackground;
return false;
} else if (isChild(iter)) {
acc.insert(xyz, &getChild(iter));
return getChild(iter).probeValueAndCache(xyz, value, acc);
}
value = getTile(iter).value;
return isTileOn(iter);
}
////////////////////////////////////////
template<typename ChildT>
inline void
RootNode<ChildT>::fill(const CoordBBox& bbox, const ValueType& value, bool active)
{
if (bbox.empty()) return;
// Iterate over the fill region in axis-aligned, tile-sized chunks.
// (The first and last chunks along each axis might be smaller than a tile.)
Coord xyz, tileMax;
for (int x = bbox.min().x(); x <= bbox.max().x(); x = tileMax.x() + 1) {
xyz.setX(x);
for (int y = bbox.min().y(); y <= bbox.max().y(); y = tileMax.y() + 1) {
xyz.setY(y);
for (int z = bbox.min().z(); z <= bbox.max().z(); z = tileMax.z() + 1) {
xyz.setZ(z);
// Get the bounds of the tile that contains voxel (x, y, z).
Coord tileMin = coordToKey(xyz);
tileMax = tileMin.offsetBy(ChildT::DIM - 1);
if (xyz != tileMin || Coord::lessThan(bbox.max(), tileMax)) {
// If the box defined by (xyz, bbox.max()) doesn't completely enclose
// the tile to which xyz belongs, create a child node (or retrieve
// the existing one).
ChildT* child = nullptr;
MapIter iter = this->findKey(tileMin);
if (iter == mTable.end()) {
// No child or tile exists. Create a child and initialize it
// with the background value.
child = new ChildT(xyz, mBackground);
mTable[tileMin] = NodeStruct(*child);
} else if (isTile(iter)) {
// Replace the tile with a newly-created child that is filled
// with the tile's value and active state.
const Tile& tile = getTile(iter);
child = new ChildT(xyz, tile.value, tile.active);
mTable[tileMin] = NodeStruct(*child);
} else if (isChild(iter)) {
child = &getChild(iter);
}
// Forward the fill request to the child.
if (child) {
const Coord tmp = Coord::minComponent(bbox.max(), tileMax);
child->fill(CoordBBox(xyz, tmp), value, active);
}
} else {
// If the box given by (xyz, bbox.max()) completely encloses
// the tile to which xyz belongs, create the tile (if it
// doesn't already exist) and give it the fill value.
MapIter iter = this->findOrAddCoord(tileMin);
setTile(iter, Tile(value, active));
}
}
}
}
}
template<typename ChildT>
inline void
RootNode<ChildT>::denseFill(const CoordBBox& bbox, const ValueType& value, bool active)
{
if (bbox.empty()) return;
if (active && mTable.empty()) {
// If this tree is empty, then a sparse fill followed by (threaded)
// densification of active tiles is the more efficient approach.
sparseFill(bbox, value, active);
voxelizeActiveTiles(/*threaded=*/true);
return;
}
// Iterate over the fill region in axis-aligned, tile-sized chunks.
// (The first and last chunks along each axis might be smaller than a tile.)
Coord xyz, tileMin, tileMax;
for (int x = bbox.min().x(); x <= bbox.max().x(); x = tileMax.x() + 1) {
xyz.setX(x);
for (int y = bbox.min().y(); y <= bbox.max().y(); y = tileMax.y() + 1) {
xyz.setY(y);
for (int z = bbox.min().z(); z <= bbox.max().z(); z = tileMax.z() + 1) {
xyz.setZ(z);
// Get the bounds of the tile that contains voxel (x, y, z).
tileMin = coordToKey(xyz);
tileMax = tileMin.offsetBy(ChildT::DIM - 1);
// Retrieve the table entry for the tile that contains xyz,
// or, if there is no table entry, add a background tile.
const auto iter = findOrAddCoord(tileMin);
if (isTile(iter)) {
// If the table entry is a tile, replace it with a child node
// that is filled with the tile's value and active state.
const auto& tile = getTile(iter);
auto* child = new ChildT{tileMin, tile.value, tile.active};
setChild(iter, *child);
}
// Forward the fill request to the child.
getChild(iter).denseFill(bbox, value, active);
}
}
}
}
////////////////////////////////////////
template<typename ChildT>
inline void
RootNode<ChildT>::voxelizeActiveTiles(bool threaded)
{
// There is little point in threading over the root table since each tile
// spans a huge index space (by default 4096^3) and hence we expect few
// active tiles if any at all. In fact, you're very likely to run out of
// memory if this method is called on a tree with root-level active tiles!
for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (this->isTileOff(i)) continue;
ChildT* child = i->second.child;
if (child == nullptr) {
// If this table entry is an active tile (i.e., not off and not a child node),
// replace it with a child node filled with active tiles of the same value.
child = new ChildT{i->first, this->getTile(i).value, true};
i->second.child = child;
}
child->voxelizeActiveTiles(threaded);
}
}
////////////////////////////////////////
template<typename ChildT>
template<typename DenseT>
inline void
RootNode<ChildT>::copyToDense(const CoordBBox& bbox, DenseT& dense) const
{
using DenseValueType = typename DenseT::ValueType;
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) {
for (xyz[1] = bbox.min()[1]; xyz[1] <= bbox.max()[1]; xyz[1] = nodeBBox.max()[1] + 1) {
for (xyz[2] = bbox.min()[2]; xyz[2] <= bbox.max()[2]; xyz[2] = nodeBBox.max()[2] + 1) {
// Get the coordinate bbox of the child node that contains voxel xyz.
nodeBBox = CoordBBox::createCube(coordToKey(xyz), ChildT::DIM);
// Get the coordinate bbox of the interection of inBBox and nodeBBox
CoordBBox sub(xyz, Coord::minComponent(bbox.max(), nodeBBox.max()));
MapCIter iter = this->findKey(nodeBBox.min());
if (iter != mTable.end() && isChild(iter)) {//is a child
getChild(iter).copyToDense(sub, dense);
} else {//is background or a tile value
const ValueType value = iter==mTable.end() ? mBackground : getTile(iter).value;
sub.translate(-min);
DenseValueType* a0 = dense.data() + zStride*sub.min()[2];
for (Int32 x=sub.min()[0], ex=sub.max()[0]+1; x<ex; ++x) {
DenseValueType* a1 = a0 + x*xStride;
for (Int32 y=sub.min()[1], ey=sub.max()[1]+1; y<ey; ++y) {
DenseValueType* a2 = a1 + y*yStride;
for (Int32 z=sub.min()[2], ez=sub.max()[2]+1; z<ez; ++z, a2 += zStride) {
*a2 = DenseValueType(value);
}
}
}
}
}
}
}
}
////////////////////////////////////////
template<typename ChildT>
inline bool
RootNode<ChildT>::writeTopology(std::ostream& os, bool toHalf) const
{
if (!toHalf) {
os.write(reinterpret_cast<const char*>(&mBackground), sizeof(ValueType));
} else {
ValueType truncatedVal = io::truncateRealToHalf(mBackground);
os.write(reinterpret_cast<const char*>(&truncatedVal), sizeof(ValueType));
}
io::setGridBackgroundValuePtr(os, &mBackground);
const Index numTiles = this->getTileCount(), numChildren = this->childCount();
os.write(reinterpret_cast<const char*>(&numTiles), sizeof(Index));
os.write(reinterpret_cast<const char*>(&numChildren), sizeof(Index));
if (numTiles == 0 && numChildren == 0) return false;
// Write tiles.
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isChild(i)) continue;
os.write(reinterpret_cast<const char*>(i->first.asPointer()), 3 * sizeof(Int32));
os.write(reinterpret_cast<const char*>(&getTile(i).value), sizeof(ValueType));
os.write(reinterpret_cast<const char*>(&getTile(i).active), sizeof(bool));
}
// Write child nodes.
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isTile(i)) continue;
os.write(reinterpret_cast<const char*>(i->first.asPointer()), 3 * sizeof(Int32));
getChild(i).writeTopology(os, toHalf);
}
return true; // not empty
}
template<typename ChildT>
inline bool
RootNode<ChildT>::readTopology(std::istream& is, bool fromHalf)
{
// Delete the existing tree.
this->clear();
if (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_ROOTNODE_MAP) {
// Read and convert an older-format RootNode.
// For backward compatibility with older file formats, read both
// outside and inside background values.
is.read(reinterpret_cast<char*>(&mBackground), sizeof(ValueType));
ValueType inside;
is.read(reinterpret_cast<char*>(&inside), sizeof(ValueType));
io::setGridBackgroundValuePtr(is, &mBackground);
// Read the index range.
Coord rangeMin, rangeMax;
is.read(reinterpret_cast<char*>(rangeMin.asPointer()), 3 * sizeof(Int32));
is.read(reinterpret_cast<char*>(rangeMax.asPointer()), 3 * sizeof(Int32));
this->initTable();
Index tableSize = 0, log2Dim[4] = { 0, 0, 0, 0 };
Int32 offset[3];
for (int i = 0; i < 3; ++i) {
offset[i] = rangeMin[i] >> ChildT::TOTAL;
rangeMin[i] = offset[i] << ChildT::TOTAL;
log2Dim[i] = 1 + util::FindHighestOn((rangeMax[i] >> ChildT::TOTAL) - offset[i]);
tableSize += log2Dim[i];
rangeMax[i] = (((1 << log2Dim[i]) + offset[i]) << ChildT::TOTAL) - 1;
}
log2Dim[3] = log2Dim[1] + log2Dim[2];
tableSize = 1U << tableSize;
// Read masks.
util::RootNodeMask childMask(tableSize), valueMask(tableSize);
childMask.load(is);
valueMask.load(is);
// Read child nodes/values.
for (Index i = 0; i < tableSize; ++i) {
// Compute origin = offset2coord(i).
Index n = i;
Coord origin;
origin[0] = (n >> log2Dim[3]) + offset[0];
n &= (1U << log2Dim[3]) - 1;
origin[1] = (n >> log2Dim[2]) + offset[1];
origin[2] = (n & ((1U << log2Dim[2]) - 1)) + offset[1];
origin <<= ChildT::TOTAL;
if (childMask.isOn(i)) {
// Read in and insert a child node.
ChildT* child = new ChildT(PartialCreate(), origin, mBackground);
child->readTopology(is);
mTable[origin] = NodeStruct(*child);
} else {
// Read in a tile value and insert a tile, but only if the value
// is either active or non-background.
ValueType value;
is.read(reinterpret_cast<char*>(&value), sizeof(ValueType));
if (valueMask.isOn(i) || (!math::isApproxEqual(value, mBackground))) {
mTable[origin] = NodeStruct(Tile(value, valueMask.isOn(i)));
}
}
}
return true;
}
// Read a RootNode that was stored in the current format.
is.read(reinterpret_cast<char*>(&mBackground), sizeof(ValueType));
io::setGridBackgroundValuePtr(is, &mBackground);
Index numTiles = 0, numChildren = 0;
is.read(reinterpret_cast<char*>(&numTiles), sizeof(Index));
is.read(reinterpret_cast<char*>(&numChildren), sizeof(Index));
if (numTiles == 0 && numChildren == 0) return false;
Int32 vec[3];
ValueType value;
bool active;
// Read tiles.
for (Index n = 0; n < numTiles; ++n) {
is.read(reinterpret_cast<char*>(vec), 3 * sizeof(Int32));
is.read(reinterpret_cast<char*>(&value), sizeof(ValueType));
is.read(reinterpret_cast<char*>(&active), sizeof(bool));
mTable[Coord(vec)] = NodeStruct(Tile(value, active));
}
// Read child nodes.
for (Index n = 0; n < numChildren; ++n) {
is.read(reinterpret_cast<char*>(vec), 3 * sizeof(Int32));
Coord origin(vec);
ChildT* child = new ChildT(PartialCreate(), origin, mBackground);
child->readTopology(is, fromHalf);
mTable[Coord(vec)] = NodeStruct(*child);
}
return true; // not empty
}
template<typename ChildT>
inline void
RootNode<ChildT>::writeBuffers(std::ostream& os, bool toHalf) const
{
for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isChild(i)) getChild(i).writeBuffers(os, toHalf);
}
}
template<typename ChildT>
inline void
RootNode<ChildT>::readBuffers(std::istream& is, bool fromHalf)
{
for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isChild(i)) getChild(i).readBuffers(is, fromHalf);
}
}
template<typename ChildT>
inline void
RootNode<ChildT>::readBuffers(std::istream& is, const CoordBBox& clipBBox, bool fromHalf)
{
const Tile bgTile(mBackground, /*active=*/false);
for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (isChild(i)) {
// Stream in and clip the branch rooted at this child.
// (We can't skip over children that lie outside the clipping region,
// because buffers are serialized in depth-first order and need to be
// unserialized in the same order.)
ChildT& child = getChild(i);
child.readBuffers(is, clipBBox, fromHalf);
}
}
// Clip root-level tiles and prune children that were clipped.
this->clip(clipBBox);
}
////////////////////////////////////////
template<typename ChildT>
inline void
RootNode<ChildT>::clip(const CoordBBox& clipBBox)
{
const Tile bgTile(mBackground, /*active=*/false);
// Iterate over a copy of this node's table so that we can modify the original.
// (Copying the table copies child node pointers, not the nodes themselves.)
MapType copyOfTable(mTable);
for (MapIter i = copyOfTable.begin(), e = copyOfTable.end(); i != e; ++i) {
const Coord& xyz = i->first; // tile or child origin
CoordBBox tileBBox(xyz, xyz.offsetBy(ChildT::DIM - 1)); // tile or child bounds
if (!clipBBox.hasOverlap(tileBBox)) {
// This table entry lies completely outside the clipping region. Delete it.
setTile(this->findCoord(xyz), bgTile); // delete any existing child node first
mTable.erase(xyz);
} else if (!clipBBox.isInside(tileBBox)) {
// This table entry does not lie completely inside the clipping region
// and must be clipped.
if (isChild(i)) {
getChild(i).clip(clipBBox, mBackground);
} else {
// Replace this tile with a background tile, then fill the clip region
// with the tile's original value. (This might create a child branch.)
tileBBox.intersect(clipBBox);
const Tile& origTile = getTile(i);
setTile(this->findCoord(xyz), bgTile);
this->sparseFill(tileBBox, origTile.value, origTile.active);
}
} else {
// This table entry lies completely inside the clipping region. Leave it intact.
}
}
this->prune(); // also erases root-level background tiles
}
////////////////////////////////////////
template<typename ChildT>
inline void
RootNode<ChildT>::prune(const ValueType& tolerance)
{
bool state = false;
ValueType value = zeroVal<ValueType>();
for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
if (this->isTile(i)) continue;
this->getChild(i).prune(tolerance);
if (this->getChild(i).isConstant(value, state, tolerance)) {
this->setTile(i, Tile(value, state));
}
}
this->eraseBackgroundTiles();
}
////////////////////////////////////////
template<typename ChildT>
template<typename NodeT>
inline NodeT*
RootNode<ChildT>::stealNode(const Coord& xyz, const ValueType& value, bool state)
{
if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same<NodeT, ChildT>::value)) ||
NodeT::LEVEL > ChildT::LEVEL) return nullptr;
OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end() || isTile(iter)) return nullptr;
return (std::is_same<NodeT, ChildT>::value)
? reinterpret_cast<NodeT*>(&stealChild(iter, Tile(value, state)))
: getChild(iter).template stealNode<NodeT>(xyz, value, state);
OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
}
////////////////////////////////////////
template<typename ChildT>
inline void
RootNode<ChildT>::addLeaf(LeafNodeType* leaf)
{
if (leaf == nullptr) return;
ChildT* child = nullptr;
const Coord& xyz = leaf->origin();
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
if (ChildT::LEVEL>0) {
child = new ChildT(xyz, mBackground, false);
} else {
child = reinterpret_cast<ChildT*>(leaf);
}
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
} else if (isChild(iter)) {
if (ChildT::LEVEL>0) {
child = &getChild(iter);
} else {
child = reinterpret_cast<ChildT*>(leaf);
setChild(iter, *child);//this also deletes the existing child node
}
} else {//tile
if (ChildT::LEVEL>0) {
child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
} else {
child = reinterpret_cast<ChildT*>(leaf);
}
setChild(iter, *child);
}
child->addLeaf(leaf);
}
template<typename ChildT>
template<typename AccessorT>
inline void
RootNode<ChildT>::addLeafAndCache(LeafNodeType* leaf, AccessorT& acc)
{
if (leaf == nullptr) return;
ChildT* child = nullptr;
const Coord& xyz = leaf->origin();
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
if (ChildT::LEVEL>0) {
child = new ChildT(xyz, mBackground, false);
} else {
child = reinterpret_cast<ChildT*>(leaf);
}
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
} else if (isChild(iter)) {
if (ChildT::LEVEL>0) {
child = &getChild(iter);
} else {
child = reinterpret_cast<ChildT*>(leaf);
setChild(iter, *child);//this also deletes the existing child node
}
} else {//tile
if (ChildT::LEVEL>0) {
child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
} else {
child = reinterpret_cast<ChildT*>(leaf);
}
setChild(iter, *child);
}
acc.insert(xyz, child);
child->addLeafAndCache(leaf, acc);
}
template<typename ChildT>
inline bool
RootNode<ChildT>::addChild(ChildT* child)
{
if (!child) return false;
const Coord& xyz = child->origin();
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {//background
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
} else {//child or tile
setChild(iter, *child);//this also deletes the existing child node
}
return true;
}
#if OPENVDB_ABI_VERSION_NUMBER >= 10
template<typename ChildT>
inline void
RootNode<ChildT>::setOrigin(const Coord &origin)
{
mOrigin = origin;
if (mOrigin != Coord(0,0,0)) {
OPENVDB_THROW(ValueError, "RootNode::setOrigin: non-zero offsets are currently not supported");
}
}
#endif
template<typename ChildT>
inline void
RootNode<ChildT>::addTile(const Coord& xyz, const ValueType& value, bool state)
{
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {//background
mTable[this->coordToKey(xyz)] = NodeStruct(Tile(value, state));
} else {//child or tile
setTile(iter, Tile(value, state));//this also deletes the existing child node
}
}
template<typename ChildT>
inline void
RootNode<ChildT>::addTile(Index level, const Coord& xyz,
const ValueType& value, bool state)
{
if (LEVEL >= level) {
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {//background
if (LEVEL > level) {
ChildT* child = new ChildT(xyz, mBackground, false);
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
child->addTile(level, xyz, value, state);
} else {
mTable[this->coordToKey(xyz)] = NodeStruct(Tile(value, state));
}
} else if (isChild(iter)) {//child
if (LEVEL > level) {
getChild(iter).addTile(level, xyz, value, state);
} else {
setTile(iter, Tile(value, state));//this also deletes the existing child node
}
} else {//tile
if (LEVEL > level) {
ChildT* child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
setChild(iter, *child);
child->addTile(level, xyz, value, state);
} else {
setTile(iter, Tile(value, state));
}
}
}
}
template<typename ChildT>
template<typename AccessorT>
inline void
RootNode<ChildT>::addTileAndCache(Index level, const Coord& xyz, const ValueType& value,
bool state, AccessorT& acc)
{
if (LEVEL >= level) {
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {//background
if (LEVEL > level) {
ChildT* child = new ChildT(xyz, mBackground, false);
acc.insert(xyz, child);
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
child->addTileAndCache(level, xyz, value, state, acc);
} else {
mTable[this->coordToKey(xyz)] = NodeStruct(Tile(value, state));
}
} else if (isChild(iter)) {//child
if (LEVEL > level) {
ChildT* child = &getChild(iter);
acc.insert(xyz, child);
child->addTileAndCache(level, xyz, value, state, acc);
} else {
setTile(iter, Tile(value, state));//this also deletes the existing child node
}
} else {//tile
if (LEVEL > level) {
ChildT* child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
acc.insert(xyz, child);
setChild(iter, *child);
child->addTileAndCache(level, xyz, value, state, acc);
} else {
setTile(iter, Tile(value, state));
}
}
}
}
////////////////////////////////////////
template<typename ChildT>
inline typename ChildT::LeafNodeType*
RootNode<ChildT>::touchLeaf(const Coord& xyz)
{
ChildT* child = nullptr;
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
child = new ChildT(xyz, mBackground, false);
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
} else if (isChild(iter)) {
child = &getChild(iter);
} else {
child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
setChild(iter, *child);
}
return child->touchLeaf(xyz);
}
template<typename ChildT>
template<typename AccessorT>
inline typename ChildT::LeafNodeType*
RootNode<ChildT>::touchLeafAndCache(const Coord& xyz, AccessorT& acc)
{
ChildT* child = nullptr;
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end()) {
child = new ChildT(xyz, mBackground, false);
mTable[this->coordToKey(xyz)] = NodeStruct(*child);
} else if (isChild(iter)) {
child = &getChild(iter);
} else {
child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
setChild(iter, *child);
}
acc.insert(xyz, child);
return child->touchLeafAndCache(xyz, acc);
}
////////////////////////////////////////
template<typename ChildT>
template<typename NodeT>
inline NodeT*
RootNode<ChildT>::probeNode(const Coord& xyz)
{
if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same<NodeT, ChildT>::value)) ||
NodeT::LEVEL > ChildT::LEVEL) return nullptr;
OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end() || isTile(iter)) return nullptr;
ChildT* child = &getChild(iter);
return (std::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 NodeT>
inline const NodeT*
RootNode<ChildT>::probeConstNode(const Coord& xyz) const
{
if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same<NodeT, ChildT>::value)) ||
NodeT::LEVEL > ChildT::LEVEL) return nullptr;
OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
MapCIter iter = this->findCoord(xyz);
if (iter == mTable.end() || isTile(iter)) return nullptr;
const ChildT* child = &getChild(iter);
return (std::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
{
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 && !(std::is_same<NodeT, ChildT>::value)) ||
NodeT::LEVEL > ChildT::LEVEL) return nullptr;
OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
MapIter iter = this->findCoord(xyz);
if (iter == mTable.end() || isTile(iter)) return nullptr;
ChildT* child = &getChild(iter);
acc.insert(xyz, child);
return (std::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 && !(std::is_same<NodeT, ChildT>::value)) ||
NodeT::LEVEL > ChildT::LEVEL) return nullptr;
OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
MapCIter iter = this->findCoord(xyz);
if (iter == mTable.end() || isTile(iter)) return nullptr;
const ChildT* child = &getChild(iter);
acc.insert(xyz, child);
return (std::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>
template<typename ArrayT>
inline void
RootNode<ChildT>::getNodes(ArrayT& array)
{
using NodePtr = typename ArrayT::value_type;
static_assert(std::is_pointer<NodePtr>::value,
"argument to getNodes() must be a pointer array");
using NodeType = typename std::remove_pointer<NodePtr>::type;
using NonConstNodeType = typename std::remove_const<NodeType>::type;
static_assert(NodeChainType::template Contains<NonConstNodeType>,
"can't extract non-const nodes from a const tree");
using ArrayChildT = typename std::conditional<
std::is_const<NodeType>::value, const ChildT, ChildT>::type;
for (MapIter iter=mTable.begin(); iter!=mTable.end(); ++iter) {
if (ChildT* child = iter->second.child) {
OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
if (std::is_same<NodePtr, ArrayChildT*>::value) {
array.push_back(reinterpret_cast<NodePtr>(iter->second.child));
} else {
child->getNodes(array);//descent
}
OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
}
}
}
template<typename ChildT>
template<typename ArrayT>
inline void
RootNode<ChildT>::getNodes(ArrayT& array) const
{
using NodePtr = typename ArrayT::value_type;
static_assert(std::is_pointer<NodePtr>::value,
"argument to getNodes() must be a pointer array");
using NodeType = typename std::remove_pointer<NodePtr>::type;
static_assert(std::is_const<NodeType>::value,
"argument to getNodes() must be an array of const node pointers");
using NonConstNodeType = typename std::remove_const<NodeType>::type;
static_assert(NodeChainType::template Contains<NonConstNodeType>,
"can't extract non-const nodes from a const tree");
for (MapCIter iter=mTable.begin(); iter!=mTable.end(); ++iter) {
if (const ChildNodeType *child = iter->second.child) {
OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
if (std::is_same<NodePtr, const ChildT*>::value) {
array.push_back(reinterpret_cast<NodePtr>(iter->second.child));
} else {
child->getNodes(array);//descent
}
OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
}
}
}
////////////////////////////////////////
template<typename ChildT>
template<typename ArrayT>
inline void
RootNode<ChildT>::stealNodes(ArrayT& array, const ValueType& value, bool state)
{
using NodePtr = typename ArrayT::value_type;
static_assert(std::is_pointer<NodePtr>::value,
"argument to stealNodes() must be a pointer array");
using NodeType = typename std::remove_pointer<NodePtr>::type;
using NonConstNodeType = typename std::remove_const<NodeType>::type;
static_assert(NodeChainType::template Contains<NonConstNodeType>,
"can't extract non-const nodes from a const tree");
using ArrayChildT = typename std::conditional<
std::is_const<NodeType>::value, const ChildT, ChildT>::type;
for (MapIter iter=mTable.begin(); iter!=mTable.end(); ++iter) {
if (ChildT* child = iter->second.child) {
OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
if (std::is_same<NodePtr, ArrayChildT*>::value) {
array.push_back(reinterpret_cast<NodePtr>(&stealChild(iter, Tile(value, state))));
} else {
child->stealNodes(array, value, state);//descent
}
OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
}
}
}
////////////////////////////////////////
template<typename ChildT>
template<MergePolicy Policy>
inline void
RootNode<ChildT>::merge(RootNode& other)
{
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));
}
}
}
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
}
////////////////////////////////////////
template<typename ChildT>
template<typename OtherChildType>
inline void
RootNode<ChildT>::topologyUnion(const RootNode<OtherChildType>& other, const bool preserveTiles)
{
using OtherRootT = RootNode<OtherChildType>;
using OtherCIterT = typename OtherRootT::MapCIter;
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()) { // create child branch with identical topology
mTable[i->first] = NodeStruct(
*(new ChildT(other.getChild(i), mBackground, TopologyCopy())));
} else if (this->isChild(j)) { // union with child branch
this->getChild(j).topologyUnion(other.getChild(i), preserveTiles);
} else {// this is a tile so replace it with a child branch with identical topology
if (!preserveTiles || this->isTileOff(j)) { // force child topology
ChildT* child = new ChildT(
other.getChild(i), this->getTile(j).value, TopologyCopy());
if (this->isTileOn(j)) child->setValuesOn();//this is an active tile
this->setChild(j, *child);
}
}
} else if (other.isTileOn(i)) { // other is an active tile
if (j == mTable.end()) { // insert an active tile
mTable[i->first] = NodeStruct(Tile(mBackground, true));
} else if (this->isChild(j)) {
this->getChild(j).setValuesOn();
} else if (this->isTileOff(j)) {
this->setTile(j, Tile(this->getTile(j).value, true));
}
}
}
}
template<typename ChildT>
template<typename OtherChildType>
inline void
RootNode<ChildT>::topologyIntersection(const RootNode<OtherChildType>& other)
{
using OtherRootT = RootNode<OtherChildType>;
using OtherCIterT = typename OtherRootT::MapCIter;
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) {
MapIter it = this->findCoord(*i);
setTile(it, Tile()); // delete any existing child node first
mTable.erase(it);
}
}
template<typename ChildT>
template<typename OtherChildType>
inline void
RootNode<ChildT>::topologyDifference(const RootNode<OtherChildType>& other)
{
using OtherRootT = RootNode<OtherChildType>;
using OtherCIterT = typename OtherRootT::MapCIter;
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)) {
setTile(j, Tile()); // delete any existing child node first
mTable.erase(j);
} else if (this->isTileOn(j)) {
this->setTile(j, Tile(this->getTile(j).value, false));
}
}
}
}
////////////////////////////////////////
template<typename ChildT>
template<typename CombineOp>
inline void
RootNode<ChildT>::combine(RootNode& other, CombineOp& op, bool prune)
{
CombineArgs<ValueType> args;
CoordSet keys;
this->insertKeys(keys);
other.insertKeys(keys);
for (CoordSetCIter i = keys.begin(), e = keys.end(); i != e; ++i) {
MapIter iter = findOrAddCoord(*i), otherIter = other.findOrAddCoord(*i);
if (isTile(iter) && isTile(otherIter)) {
// Both this node and the other node have constant values (tiles).
// Combine the two values and store the result as this node's new tile value.
op(args.setARef(getTile(iter).value)
.setAIsActive(isTileOn(iter))
.setBRef(getTile(otherIter).value)
.setBIsActive(isTileOn(otherIter)));
setTile(iter, Tile(args.result(), args.resultIsActive()));
} else if (isChild(iter) && isTile(otherIter)) {
// Combine this node's child with the other node's constant value.
ChildT& child = getChild(iter);
child.combine(getTile(otherIter).value, isTileOn(otherIter), op);
} else if (isTile(iter) && isChild(otherIter)) {
// Combine this node's constant value with the other node's child,
// but use a new functor in which the A and B values are swapped,
// since the constant value is the A value, not the B value.
SwappedCombineOp<ValueType, CombineOp> swappedOp(op);
ChildT& child = getChild(otherIter);
child.combine(getTile(iter).value, isTileOn(iter), swappedOp);
// Steal the other node's child.
setChild(iter, stealChild(otherIter, Tile()));
} else /*if (isChild(iter) && isChild(otherIter))*/ {
// Combine this node's child with the other node's child.
ChildT &child = getChild(iter), &otherChild = getChild(otherIter);
child.combine(otherChild, op);
}
if (prune && isChild(iter)) getChild(iter).prune();
}
// Combine background values.
op(args.setARef(mBackground).setBRef(other.mBackground));
mBackground = args.result();
// Empty the other tree so as not to leave it in a partially cannibalized state.
other.clear();
}
////////////////////////////////////////
// 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, typename OtherRootNode>
inline void
RootNode<ChildT>::combine2(const RootNode& other0, const OtherRootNode& other1,
CombineOp& op, bool prune)
{
using OtherValueType = typename OtherRootNode::ValueType;
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);
using OtherValueT = typename OtherRootNode::ValueType;
using OtherTileT = typename OtherRootNode::Tile;
using OtherNodeStructT = typename OtherRootNode::NodeStruct;
using OtherMapCIterT = typename OtherRootNode::MapCIter;
CombineArgs<ValueType, OtherValueT> args;
CoordSet keys;
other0.insertKeys(keys);
other1.insertKeys(keys);
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);
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.
op(args.setARef(ns0.tile.value)
.setAIsActive(ns0.isTileOn())
.setBRef(ns1.tile.value)
.setBIsActive(ns1.isTileOn()));
setTile(thisIter, Tile(args.result(), args.resultIsActive()));
} else {
if (!isChild(thisIter)) {
// Add a new child with the same coordinates, etc. as the other node's child.
const Coord& childOrigin =
ns0.isChild() ? ns0.child->origin() : ns1.child->origin();
setChild(thisIter, *(new ChildT(childOrigin, getTile(thisIter).value)));
}
ChildT& child = getChild(thisIter);
if (ns0.isTile()) {
// Combine node1's child with node0's constant value
// and write the result into this node's child.
child.combine2(ns0.tile.value, *ns1.child, ns0.isTileOn(), op);
} else if (ns1.isTile()) {
// Combine node0's child with node1's constant value
// and write the result into this node's child.
child.combine2(*ns0.child, ns1.tile.value, ns1.isTileOn(), op);
} else {
// Combine node0's child with node1's child
// and write the result into this node's child.
child.combine2(*ns0.child, *ns1.child, op);
}
}
if (prune && isChild(thisIter)) getChild(thisIter).prune();
}
// Combine background values.
op(args.setARef(other0.mBackground).setBRef(other1.mBackground));
mBackground = args.result();
}
} // namespace tree
} // namespace OPENVDB_VERSION_NAME
} // namespace openvdb
#endif // OPENVDB_TREE_ROOTNODE_HAS_BEEN_INCLUDED
BBox.h
Compression.h
Exceptions.h
OPENVDB_THROW
#define OPENVDB_THROW(exception, message)
Definition Exceptions.h:74
Math.h
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
NodeMasks.h
OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
#define OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
Definition Platform.h:141
OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
#define OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
Definition Platform.h:140
Types.h
openvdb::v11_0::CombineArgs
This struct collects both input and output arguments to "grid combiner" functors used with the tree::...
Definition Types.h:569
openvdb::v11_0::CombineArgs::setARef
CombineArgs & setARef(const AValueType &a)
Redirect the A value to a new external source.
Definition Types.h:621
openvdb::v11_0::CombineArgs::result
const AValueType & result() const
Get the output value.
Definition Types.h:613
openvdb::v11_0::CombineArgs::setBIsActive
CombineArgs & setBIsActive(bool b)
Set the active state of the B value.
Definition Types.h:637
openvdb::v11_0::CombineArgs::setBRef
CombineArgs & setBRef(const BValueType &b)
Redirect the B value to a new external source.
Definition Types.h:623
openvdb::v11_0::CombineArgs::resultIsActive
bool resultIsActive() const
Definition Types.h:632
openvdb::v11_0::CombineArgs::setAIsActive
CombineArgs & setAIsActive(bool b)
Set the active state of the A value.
Definition Types.h:635
openvdb::v11_0::PartialCreate
Tag dispatch class that distinguishes constructors during file input.
Definition Types.h:689
openvdb::v11_0::TopologyCopy
Tag dispatch class that distinguishes topology copy constructors from deep copy constructors.
Definition Types.h:683
openvdb::v11_0::TypeError
Definition Exceptions.h:64
openvdb::v11_0::ValueError
Definition Exceptions.h:65
openvdb::v11_0::math::CoordBBox
Axis-aligned bounding box of signed integer coordinates.
Definition Coord.h:251
openvdb::v11_0::math::CoordBBox::translate
void translate(const Coord &t)
Translate this bounding box by (tx, ty, tz).
Definition Coord.h:460
openvdb::v11_0::math::CoordBBox::expand
void expand(ValueType padding)
Pad this bounding box with the specified padding.
Definition Coord.h:420
openvdb::v11_0::math::CoordBBox::empty
bool empty() const
Return true if this bounding box is empty (i.e., encloses no coordinates).
Definition Coord.h:358
openvdb::v11_0::math::CoordBBox::min
const Coord & min() const
Definition Coord.h:323
openvdb::v11_0::math::CoordBBox::hasOverlap
bool hasOverlap(const CoordBBox &b) const
Return true if the given bounding box overlaps with this bounding box.
Definition Coord.h:414
openvdb::v11_0::math::CoordBBox::max
const Coord & max() const
Definition Coord.h:324
openvdb::v11_0::math::CoordBBox::isInside
bool isInside(const Coord &xyz) const
Return true if point (x, y, z) is inside this bounding box.
Definition Coord.h:402
openvdb::v11_0::math::CoordBBox::intersect
void intersect(const CoordBBox &bbox)
Intersect this bounding box with the given bounding box.
Definition Coord.h:446
openvdb::v11_0::math::CoordBBox::createCube
static CoordBBox createCube(const Coord &min, ValueType dim)
Definition Coord.h:315
openvdb::v11_0::math::Coord
Signed (x, y, z) 32-bit integer coordinates.
Definition Coord.h:25
openvdb::v11_0::math::Coord::y
Int32 y() const
Definition Coord.h:131
openvdb::v11_0::math::Coord::offsetBy
Coord offsetBy(Int32 dx, Int32 dy, Int32 dz) const
Definition Coord.h:91
openvdb::v11_0::math::Coord::minComponent
void minComponent(const Coord &other)
Perform a component-wise minimum with the other Coord.
Definition Coord.h:175
openvdb::v11_0::math::Coord::asPointer
const Int32 * asPointer() const
Definition Coord.h:141
openvdb::v11_0::math::Coord::x
Int32 x() const
Definition Coord.h:130
openvdb::v11_0::math::Coord::setZ
Coord & setZ(Int32 z)
Definition Coord.h:81
openvdb::v11_0::math::Coord::setY
Coord & setY(Int32 y)
Definition Coord.h:80
openvdb::v11_0::math::Coord::lessThan
static bool lessThan(const Coord &a, const Coord &b)
Definition Coord.h:208
openvdb::v11_0::math::Coord::z
Int32 z() const
Definition Coord.h:132
openvdb::v11_0::math::Coord::setX
Coord & setX(Int32 x)
Definition Coord.h:79
openvdb::v11_0::math::Coord::read
void read(std::istream &is)
Definition Coord.h:219
openvdb::v11_0::tree::RootNode
Definition RootNode.h:39
openvdb::v11_0::tree::RootNode::getNodeBoundingBox
static CoordBBox getNodeBoundingBox()
Return the bounding box of this RootNode, i.e., an infinite bounding box.
Definition RootNode.h:406
openvdb::v11_0::tree::RootNode::setValueAndCache
void setValueAndCache(const Coord &xyz, const ValueType &value, AccessorT &)
Definition RootNode.h:1902
openvdb::v11_0::tree::RootNode::ChildAllIter
DenseIter< RootNode, MapIter, ChildType, ValueType > ChildAllIter
Definition RootNode.h:364
openvdb::v11_0::tree::RootNode::hasSameConfiguration
static bool hasSameConfiguration(const RootNode< OtherChildType > &other)
Return false if the other node's dimensions don't match this node's.
Definition RootNode.h:1429
openvdb::v11_0::tree::RootNode::getValueDepth
int getValueDepth(const Coord &xyz) const
Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides.
Definition RootNode.h:1753
openvdb::v11_0::tree::RootNode::ValueOffCIter
ValueIter< const RootNode, MapCIter, ValueOffPred, const ValueType > ValueOffCIter
Definition RootNode.h:370
openvdb::v11_0::tree::RootNode::RootNode
RootNode(const RootNode &other)
Definition RootNode.h:75
openvdb::v11_0::tree::RootNode::cbeginChildOn
ChildOnCIter cbeginChildOn() const
Definition RootNode.h:375
openvdb::v11_0::tree::RootNode::beginChildOn
ChildOnCIter beginChildOn() const
Definition RootNode.h:378
openvdb::v11_0::tree::RootNode::beginChildOn
ChildOnIter beginChildOn()
Definition RootNode.h:381
openvdb::v11_0::tree::RootNode::getDepth
Index getDepth() const
Definition RootNode.h:462
openvdb::v11_0::tree::RootNode::isValueOn
bool isValueOn(const Coord &xyz) const
Definition RootNode.h:1697
openvdb::v11_0::tree::RootNode::ValueOnCIter
ValueIter< const RootNode, MapCIter, ValueOnPred, const ValueType > ValueOnCIter
Definition RootNode.h:368
openvdb::v11_0::tree::RootNode::getIndexRange
void getIndexRange(CoordBBox &bbox) const
Return the current index range. Both min and max are inclusive.
Definition RootNode.h:1369
openvdb::v11_0::tree::RootNode::copyToDense
void copyToDense(const CoordBBox &bbox, DenseT &dense) const
Copy into a dense grid the values of all voxels, both active and inactive, that intersect a given bou...
Definition RootNode.h:2262
openvdb::v11_0::tree::RootNode::operator=
RootNode & operator=(const RootNode &other)
Copy a root node of the same type as this node.
Definition RootNode.h:1189
openvdb::v11_0::tree::RootNode::getMaxIndex
Coord getMaxIndex() const
Return the largest index of the current tree.
Definition RootNode.h:1361
openvdb::v11_0::tree::RootNode::cbeginValueOff
ValueOffCIter cbeginValueOff() const
Definition RootNode.h:386
openvdb::v11_0::tree::RootNode::probeLeafAndCache
LeafNodeType * probeLeafAndCache(const Coord &xyz, AccessorT &acc)
Same as probeLeaf() but, if necessary, update the given accessor with pointers to the nodes along the...
openvdb::v11_0::tree::RootNode::transientData
Index32 transientData() const
Return the transient data value.
Definition RootNode.h:409
openvdb::v11_0::tree::RootNode::addLeafAndCache
void addLeafAndCache(LeafNodeType *leaf, AccessorT &)
Same as addLeaf() but, if necessary, update the given accessor with pointers to the nodes along the p...
Definition RootNode.h:2607
openvdb::v11_0::tree::RootNode::getChildDim
static Index getChildDim()
Definition RootNode.h:455
openvdb::v11_0::tree::RootNode::operator=
RootNode & operator=(const RootNode< OtherChildType > &other)
Copy a root node of the same tree configuration as this node but a different ValueType.
openvdb::v11_0::tree::RootNode::nonLeafCount
Index32 nonLeafCount() const
Definition RootNode.h:1582
openvdb::v11_0::tree::RootNode::combine
void combine(RootNode &other, CombineOp &, bool prune=false)
Definition RootNode.h:3234
openvdb::v11_0::tree::RootNode::addChild
bool addChild(ChildType *child)
Add the given child node at the root level. If a child node with the same origin already exists,...
openvdb::v11_0::tree::RootNode::getNodes
void getNodes(ArrayT &array)
Adds all nodes of a certain type to a container with the following API:
Definition RootNode.h:2919
openvdb::v11_0::tree::RootNode::stealNode
NodeT * stealNode(const Coord &xyz, const ValueType &value, bool state)
Return a pointer to the node of type NodeT that contains voxel (x, y, z) and replace it with a tile o...
Definition RootNode.h:2553
openvdb::v11_0::tree::RootNode::voxelizeActiveTiles
void voxelizeActiveTiles(bool threaded=true)
Densify active tiles, i.e., replace them with leaf-level active voxels.
Definition RootNode.h:2236
openvdb::v11_0::tree::RootNode::touchLeafAndCache
LeafNodeType * touchLeafAndCache(const Coord &xyz, AccessorT &acc)
Same as touchLeaf() but, if necessary, update the given accessor with pointers to the nodes along the...
openvdb::v11_0::tree::RootNode::ValueOffIter
ValueIter< RootNode, MapIter, ValueOffPred, ValueType > ValueOffIter
Definition RootNode.h:369
openvdb::v11_0::tree::RootNode::ValueAllIter
ValueIter< RootNode, MapIter, ValueAllPred, ValueType > ValueAllIter
Definition RootNode.h:371
openvdb::v11_0::tree::RootNode::topologyUnion
void topologyUnion(const RootNode< OtherChildType > &other, const bool preserveTiles=false)
Union this tree's set of active values with the active values of the other tree, whose ValueType may ...
Definition RootNode.h:3121
openvdb::v11_0::tree::RootNode::onLeafVoxelCount
Index64 onLeafVoxelCount() const
Definition RootNode.h:1640
openvdb::v11_0::tree::RootNode::probeConstLeafAndCache
const LeafNodeType * probeConstLeafAndCache(const Coord &xyz, AccessorT &acc) const
openvdb::v11_0::tree::RootNode::clip
void clip(const CoordBBox &)
Set all voxels that lie outside the given axis-aligned box to the background.
Definition RootNode.h:2492
openvdb::v11_0::tree::RootNode::probeLeaf
LeafNodeType * probeLeaf(const Coord &xyz)
Return a pointer to the leaf node that contains voxel (x, y, z). If no such node exists,...
Definition RootNode.h:2835
openvdb::v11_0::tree::RootNode::hasCompatibleValueType
static bool hasCompatibleValueType(const RootNode< OtherChildType > &other)
Definition RootNode.h:1461
openvdb::v11_0::tree::RootNode::cbeginValueAll
ValueAllCIter cbeginValueAll() const
Definition RootNode.h:387
openvdb::v11_0::tree::RootNode::probeNodeAndCache
NodeT * probeNodeAndCache(const Coord &xyz, AccessorT &acc)
Same as probeNode() but, if necessary, update the given accessor with pointers to the nodes along the...
Definition RootNode.h:2879
openvdb::v11_0::tree::RootNode::prune
void prune(const ValueType &tolerance=zeroVal< ValueType >())
Reduce the memory footprint of this tree by replacing with tiles any nodes whose values are all the s...
Definition RootNode.h:2532
openvdb::v11_0::tree::RootNode::NodeChainType
typename NodeChain< RootNode, LEVEL >::Type NodeChainType
NodeChainType is a list of this tree's node types, from LeafNodeType to RootNode.
Definition RootNode.h:49
openvdb::v11_0::tree::RootNode::isBackgroundTile
bool isBackgroundTile(const Tile &) const
Return true if the given tile is inactive and has the background value.
Definition RootNode.h:1258
openvdb::v11_0::tree::RootNode::LeafNodeType
typename ChildType::LeafNodeType LeafNodeType
Definition RootNode.h:42
openvdb::v11_0::tree::RootNode::eraseBackgroundTiles
size_t eraseBackgroundTiles()
Remove all background tiles.
Definition RootNode.h:1292
openvdb::v11_0::tree::RootNode::topologyDifference
void topologyDifference(const RootNode< OtherChildType > &other)
Difference this tree's set of active values with the active values of the other tree,...
Definition RootNode.h:3195
openvdb::v11_0::tree::RootNode::ValueOnIter
ValueIter< RootNode, MapIter, ValueOnPred, ValueType > ValueOnIter
Definition RootNode.h:367
openvdb::v11_0::tree::RootNode::beginValueOn
ValueOnCIter beginValueOn() const
Definition RootNode.h:388
openvdb::v11_0::tree::RootNode::merge
void merge(RootNode &other)
Efficiently merge another tree into this tree using one of several schemes.
Definition RootNode.h:3009
openvdb::v11_0::tree::RootNode::ChildOnIter
ChildIter< RootNode, MapIter, ChildOnPred, ChildType > ChildOnIter
Definition RootNode.h:360
openvdb::v11_0::tree::RootNode::offLeafVoxelCount
Index64 offLeafVoxelCount() const
Definition RootNode.h:1652
openvdb::v11_0::tree::RootNode::getWidth
Index getWidth() const
Definition RootNode.h:460
openvdb::v11_0::tree::RootNode::getTableSize
Index getTableSize() const
Return the number of entries in this node's table.
Definition RootNode.h:458
openvdb::v11_0::tree::RootNode::setOrigin
void setOrigin(const Coord &origin)
change the origin on this root node
Definition RootNode.h:2657
openvdb::v11_0::tree::RootNode::combine2
void combine2(const RootNode &other0, const OtherRootNode &other1, CombineOp &op, bool prune=false)
Definition RootNode.h:3324
openvdb::v11_0::tree::RootNode::origin
const Coord & origin() const
Return the grid index coordinates of this node's local origin.
Definition RootNode.h:885
openvdb::v11_0::tree::RootNode::modifyValueAndActiveState
void modifyValueAndActiveState(const Coord &xyz, const ModifyOp &op)
Apply a functor to the voxel at the given coordinates.
Definition RootNode.h:2034
openvdb::v11_0::tree::RootNode::setValueOnlyAndCache
void setValueOnlyAndCache(const Coord &xyz, const ValueType &value, AccessorT &)
Definition RootNode.h:1943
openvdb::v11_0::tree::RootNode::numBackgroundTiles
size_t numBackgroundTiles() const
Return the number of background tiles.
Definition RootNode.h:1280
openvdb::v11_0::tree::RootNode::ChildOffCIter
ValueIter< const RootNode, MapCIter, ChildOffPred, ValueType > ChildOffCIter
Definition RootNode.h:363
openvdb::v11_0::tree::RootNode::setValueOnly
void setValueOnly(const Coord &xyz, const ValueType &value)
Set the value of the voxel at the given coordinates but don't change its active state.
Definition RootNode.h:1924
openvdb::v11_0::tree::RootNode::empty
bool empty() const
Return true if this node's table is either empty or contains only background tiles.
Definition RootNode.h:446
openvdb::v11_0::tree::RootNode::probeConstLeaf
const LeafNodeType * probeConstLeaf(const Coord &xyz) const
Definition RootNode.h:2843
openvdb::v11_0::tree::RootNode::fill
void fill(const CoordBBox &bbox, const ValueType &value, bool active=true)
Set all voxels within a given axis-aligned box to a constant value.
Definition RootNode.h:2128
openvdb::v11_0::tree::RootNode::beginValueOff
ValueOffCIter beginValueOff() const
Definition RootNode.h:389
openvdb::v11_0::tree::RootNode::setValueOffAndCache
void setValueOffAndCache(const Coord &xyz, const ValueType &value, AccessorT &)
Definition RootNode.h:1859
openvdb::v11_0::tree::RootNode::addLeaf
void addLeaf(LeafNodeType *leaf)
Add the given leaf node to this tree, creating a new branch if necessary. If a leaf node with the sam...
Definition RootNode.h:2572
openvdb::v11_0::tree::RootNode::getValueAndCache
const ValueType & getValueAndCache(const Coord &xyz, AccessorT &) const
openvdb::v11_0::tree::RootNode::cbeginChildAll
ChildAllCIter cbeginChildAll() const
Definition RootNode.h:377
openvdb::v11_0::tree::RootNode::addTile
void addTile(const Coord &xyz, const ValueType &value, bool state)
Add a tile containing voxel (x, y, z) at the root level, deleting the existing branch if necessary.
Definition RootNode.h:2668
openvdb::v11_0::tree::RootNode::beginChildAll
ChildAllIter beginChildAll()
Definition RootNode.h:383
openvdb::v11_0::tree::RootNode::getLevel
static Index getLevel()
Definition RootNode.h:453
openvdb::v11_0::tree::RootNode::writeTopology
bool writeTopology(std::ostream &, bool toHalf=false) const
Definition RootNode.h:2306
openvdb::v11_0::tree::RootNode::ValueAllCIter
ValueIter< const RootNode, MapCIter, ValueAllPred, const ValueType > ValueAllCIter
Definition RootNode.h:372
openvdb::v11_0::tree::RootNode::isValueOnAndCache
bool isValueOnAndCache(const Coord &xyz, AccessorT &) const
Definition RootNode.h:1717
openvdb::v11_0::tree::RootNode::probeConstNodeAndCache
const NodeT * probeConstNodeAndCache(const Coord &xyz, AccessorT &acc) const
Definition RootNode.h:2898
openvdb::v11_0::tree::RootNode::probeValue
bool probeValue(const Coord &xyz, ValueType &value) const
Definition RootNode.h:2092
openvdb::v11_0::tree::RootNode::setActiveState
void setActiveState(const Coord &xyz, bool on)
Set the active state of the voxel at the given coordinates but don't change its value.
Definition RootNode.h:1789
openvdb::v11_0::tree::RootNode::beginValueOn
ValueOnIter beginValueOn()
Definition RootNode.h:391
openvdb::v11_0::tree::RootNode::modifyValueAndCache
void modifyValueAndCache(const Coord &xyz, const ModifyOp &op, AccessorT &)
Definition RootNode.h:1998
openvdb::v11_0::tree::RootNode::cbeginChildOff
ChildOffCIter cbeginChildOff() const
Definition RootNode.h:376
openvdb::v11_0::tree::RootNode::beginChildOff
ChildOffIter beginChildOff()
Definition RootNode.h:382
openvdb::v11_0::tree::RootNode::onVoxelCount
Index64 onVoxelCount() const
Definition RootNode.h:1608
openvdb::v11_0::tree::RootNode::beginChildOff
ChildOffCIter beginChildOff() const
Definition RootNode.h:379
openvdb::v11_0::tree::RootNode::addTileAndCache
void addTileAndCache(Index level, const Coord &xyz, const ValueType &, bool state, AccessorT &)
Same as addTile() but, if necessary, update the given accessor with pointers to the nodes along the p...
Definition RootNode.h:2715
openvdb::v11_0::tree::RootNode::ValueType
typename ChildType::ValueType ValueType
Definition RootNode.h:43
openvdb::v11_0::tree::RootNode::setValueOff
void setValueOff(const Coord &xyz)
Mark the voxel at the given coordinates as inactive but don't change its value.
Definition RootNode.h:1775
openvdb::v11_0::tree::RootNode::writeBuffers
void writeBuffers(std::ostream &, bool toHalf=false) const
Definition RootNode.h:2448
openvdb::v11_0::tree::RootNode::cbeginValueOn
ValueOnCIter cbeginValueOn() const
Definition RootNode.h:385
openvdb::v11_0::tree::RootNode::leafCount
Index32 leafCount() const
Definition RootNode.h:1570
openvdb::v11_0::tree::RootNode::probeLeaf
const LeafNodeType * probeLeaf(const Coord &xyz) const
openvdb::v11_0::tree::RootNode::childCount
Index32 childCount() const
Definition RootNode.h:1596
openvdb::v11_0::tree::RootNode::onTileCount
Index64 onTileCount() const
Definition RootNode.h:1663
openvdb::v11_0::tree::RootNode::modifyValue
void modifyValue(const Coord &xyz, const ModifyOp &op)
Apply a functor to the value of the voxel at the given coordinates and mark the voxel as active.
Definition RootNode.h:1966
openvdb::v11_0::tree::RootNode::~RootNode
~RootNode()
Definition RootNode.h:123
openvdb::v11_0::tree::RootNode::offVoxelCount
Index64 offVoxelCount() const
Definition RootNode.h:1624
openvdb::v11_0::tree::RootNode::touchLeaf
LeafNodeType * touchLeaf(const Coord &xyz)
Return a pointer to the leaf node that contains voxel (x, y, z). If no such node exists,...
Definition RootNode.h:2756
openvdb::v11_0::tree::RootNode::BuildType
typename ChildType::BuildType BuildType
Definition RootNode.h:44
openvdb::v11_0::tree::RootNode::setBackground
void setBackground(const ValueType &value, bool updateChildNodes)
Change inactive tiles or voxels with a value equal to +/- the old background to the specified value (...
Definition RootNode.h:1231
openvdb::v11_0::tree::RootNode::ChildAllCIter
DenseIter< const RootNode, MapCIter, const ChildType, const ValueType > ChildAllCIter
Definition RootNode.h:365
openvdb::v11_0::tree::RootNode::hasSameTopology
bool hasSameTopology(const RootNode< OtherChildType > &other) const
Return true if the given tree has the same node and active value topology as this tree (but possibly ...
Definition RootNode.h:1382
openvdb::v11_0::tree::RootNode::denseFill
void denseFill(const CoordBBox &bbox, const ValueType &value, bool active=true)
Set all voxels within a given axis-aligned box to a constant value and ensure that those voxels are a...
Definition RootNode.h:2186
openvdb::v11_0::tree::RootNode::beginChildAll
ChildAllCIter beginChildAll() const
Definition RootNode.h:380
openvdb::v11_0::tree::RootNode::setActiveStateAndCache
void setActiveStateAndCache(const Coord &xyz, bool on, AccessorT &)
Definition RootNode.h:1812
openvdb::v11_0::tree::RootNode::topologyIntersection
void topologyIntersection(const RootNode< OtherChildType > &other)
Intersects this tree's set of active values with the active values of the other tree,...
Definition RootNode.h:3159
openvdb::v11_0::tree::RootNode::setTransientData
void setTransientData(Index32 transientData)
Set the transient data value.
Definition RootNode.h:411
openvdb::v11_0::tree::RootNode::stealNodes
void stealNodes(ArrayT &array, const ValueType &value, bool state)
Steals all nodes of a certain type from the tree and adds them to a container with the following API:
Definition RootNode.h:2977
openvdb::v11_0::tree::RootNode::getHeight
Index getHeight() const
Definition RootNode.h:461
openvdb::v11_0::tree::RootNode::clear
void clear()
Definition RootNode.h:1502
openvdb::v11_0::tree::RootNode::probeLeafAndCache
const LeafNodeType * probeLeafAndCache(const Coord &xyz, AccessorT &acc) const
openvdb::v11_0::tree::RootNode::probeValueAndCache
bool probeValueAndCache(const Coord &xyz, ValueType &value, AccessorT &) const
Definition RootNode.h:2108
openvdb::v11_0::tree::RootNode::probeConstNode
const NodeT * probeConstNode(const Coord &xyz) const
Definition RootNode.h:2818
openvdb::v11_0::tree::RootNode::evalActiveBoundingBox
void evalActiveBoundingBox(CoordBBox &bbox, bool visitVoxels=true) const
Expand the specified bbox so it includes the active tiles of this root node as well as all the active...
Definition RootNode.h:1513
openvdb::v11_0::tree::RootNode::background
const ValueType & background() const
Return this node's background value.
Definition RootNode.h:428
openvdb::v11_0::tree::RootNode::getNodeLog2Dims
static void getNodeLog2Dims(std::vector< Index > &dims)
Definition RootNode.h:1345
openvdb::v11_0::tree::RootNode::RootNode
RootNode(const RootNode< OtherChildType > &other)
Construct a new tree that reproduces the topology and active states of a tree of a different ValueTyp...
Definition RootNode.h:84
openvdb::v11_0::tree::RootNode::beginValueOff
ValueOffIter beginValueOff()
Definition RootNode.h:392
openvdb::v11_0::tree::RootNode::ChildOffIter
ValueIter< RootNode, MapIter, ChildOffPred, const ValueType > ChildOffIter
Definition RootNode.h:362
openvdb::v11_0::tree::RootNode::sparseFill
void sparseFill(const CoordBBox &bbox, const ValueType &value, bool active=true)
Definition RootNode.h:537
openvdb::v11_0::tree::RootNode::getMinIndex
Coord getMinIndex() const
Return the smallest index of the current tree.
Definition RootNode.h:1354
openvdb::v11_0::tree::RootNode::ChildNodeType
ChildType ChildNodeType
Definition RootNode.h:41
openvdb::v11_0::tree::RootNode::hasActiveTiles
bool hasActiveTiles() const
Return true if this root node, or any of its child nodes, have active tiles.
Definition RootNode.h:1706
openvdb::v11_0::tree::RootNode::getValueDepthAndCache
int getValueDepthAndCache(const Coord &xyz, AccessorT &) const
Definition RootNode.h:1763
openvdb::v11_0::tree::RootNode::getValue
const ValueType & getValue(const Coord &xyz) const
Definition RootNode.h:1729
openvdb::v11_0::tree::RootNode::nodeCount
void nodeCount(std::vector< Index32 > &vec) const
Definition RootNode.h:1678
openvdb::v11_0::tree::RootNode::memUsage
Index64 memUsage() const
Return the total amount of memory in bytes occupied by this node and its children.
Definition RootNode.h:1488
openvdb::v11_0::tree::RootNode::setValueOn
void setValueOn(const Coord &xyz, const ValueType &value)
Set the value of the voxel at the given coordinates and mark the voxel as active.
Definition RootNode.h:1883
openvdb::v11_0::tree::RootNode::stealNodes
void stealNodes(ArrayT &array)
Definition RootNode.h:816
openvdb::v11_0::tree::RootNode::expand
bool expand(const Coord &xyz)
Expand this node's table so that (x, y, z) is included in the index range.
Definition RootNode.h:1331
openvdb::v11_0::tree::RootNode::readBuffers
void readBuffers(std::istream &, bool fromHalf=false)
Definition RootNode.h:2458
openvdb::v11_0::tree::RootNode::modifyValueAndActiveStateAndCache
void modifyValueAndActiveStateAndCache(const Coord &xyz, const ModifyOp &op, AccessorT &)
Definition RootNode.h:2061
openvdb::v11_0::tree::RootNode::beginValueAll
ValueAllCIter beginValueAll() const
Definition RootNode.h:390
openvdb::v11_0::tree::RootNode::probeNode
NodeT * probeNode(const Coord &xyz)
Return a pointer to the node that contains voxel (x, y, z). If no such node exists,...
Definition RootNode.h:2800
openvdb::v11_0::tree::RootNode::readTopology
bool readTopology(std::istream &, bool fromHalf=false)
Definition RootNode.h:2342
openvdb::v11_0::tree::RootNode::ChildOnCIter
ChildIter< const RootNode, MapCIter, ChildOnPred, const ChildType > ChildOnCIter
Definition RootNode.h:361
openvdb::v11_0::tree::RootNode::beginValueAll
ValueAllIter beginValueAll()
Definition RootNode.h:393
openvdb::v11_0::tree::RootNode::RootNode
friend class RootNode
Definition RootNode.h:897
openvdb::v11_0::util::RootNodeMask
Definition NodeMasks.h:1067
openvdb::v11_0::util::RootNodeMask::isOn
bool isOn(Index32 i) const
Definition NodeMasks.h:1331
openvdb::v11_0::util::RootNodeMask::load
void load(std::istream &is)
Definition NodeMasks.h:1372
openvdb::v11_0::io::setGridBackgroundValuePtr
OPENVDB_API void setGridBackgroundValuePtr(std::ios_base &, const void *background)
Specify (a pointer to) the background value of the grid currently being read from or written to the g...
openvdb::v11_0::io::getFormatVersion
OPENVDB_API uint32_t getFormatVersion(std::ios_base &)
Return the file format version number associated with the given input stream.
openvdb::v11_0::io::truncateRealToHalf
T truncateRealToHalf(const T &val)
Return the given value truncated to 16-bit float precision.
Definition Compression.h:216
openvdb::v11_0::math::operator==
bool operator==(const Vec3< T0 > &v0, const Vec3< T1 > &v1)
Equality operator, does exact floating point comparisons.
Definition Vec3.h:473
openvdb::v11_0::math::operator*
Mat3< typename promote< T0, T1 >::type > operator*(const Mat3< T0 > &m0, const Mat3< T1 > &m1)
Multiply m0 by m1 and return the resulting matrix.
Definition Mat3.h:597
openvdb::v11_0::math::isApproxEqual
bool isApproxEqual(const Type &a, const Type &b, const Type &tolerance)
Return true if a is equal to b to within the given tolerance.
Definition Math.h:406
openvdb::v11_0::math::negative
T negative(const T &val)
Return the unary negation of the given value.
Definition Math.h:128
openvdb::v11_0::math::isExactlyEqual
bool isExactlyEqual(const T0 &a, const T1 &b)
Return true if a is exactly equal to b.
Definition Math.h:443
openvdb::v11_0::math::operator!=
bool operator!=(const Vec3< T0 > &v0, const Vec3< T1 > &v1)
Inequality operator, does exact floating point comparisons.
Definition Vec3.h:481
openvdb::v11_0::util::FindHighestOn
Index32 FindHighestOn(Index32 v)
Return the most significant on bit of the given 32-bit value.
Definition NodeMasks.h:159
openvdb::v11_0::Index
Index32 Index
Definition Types.h:54
openvdb::v11_0::zeroVal
constexpr T zeroVal()
Return the value of type T that corresponds to zero.
Definition Math.h:70
openvdb::v11_0::Index32
uint32_t Index32
Definition Types.h:52
openvdb::v11_0::OPENVDB_FILE_VERSION_ROOTNODE_MAP
@ OPENVDB_FILE_VERSION_ROOTNODE_MAP
Definition version.h.in:246
openvdb::v11_0::Int32
int32_t Int32
Definition Types.h:56
openvdb::v11_0::Index64
uint64_t Index64
Definition Types.h:53
openvdb::v11_0::MERGE_ACTIVE_STATES
@ MERGE_ACTIVE_STATES
Definition Types.h:507
openvdb::v11_0::MERGE_NODES
@ MERGE_NODES
Definition Types.h:508
openvdb::v11_0::MERGE_ACTIVE_STATES_AND_NODES
@ MERGE_ACTIVE_STATES_AND_NODES
Definition Types.h:509
openvdb
Definition Exceptions.h:13
openvdb::v11_0::CanConvertType
CanConvertType<FromType, ToType>::value is true if a value of type ToType can be constructed from a v...
Definition Types.h:403
openvdb::v11_0::SwappedCombineOp
Definition Types.h:660
openvdb::v11_0::TypeList
A list of types (not necessarily unique)
Definition TypeList.h:578
openvdb::v11_0::tree::NodeChain
NodeChain<RootNodeType, RootNodeType::LEVEL>::Type is a openvdb::TypeList that lists the types of the...
Definition RootNode.h:997
openvdb::v11_0::tree::NodeChain::SubtreeT
typename NodeChain< typename HeadT::ChildNodeType, HeadLevel-1 >::Type SubtreeT
Definition RootNode.h:998
openvdb::v11_0::tree::NodeChain::Type
typename SubtreeT::template Append< HeadT > Type
Definition RootNode.h:999
openvdb::v11_0::tree::RootNode::SameConfiguration
SameConfiguration<OtherNodeType>::value is true if and only if OtherNodeType is the type of a RootNod...
Definition RootNode.h:64
openvdb::v11_0::tree::RootNode::ValueConverter
ValueConverter<T>::Type is the type of a RootNode having the same child hierarchy as this node but a ...
Definition RootNode.h:56
openvdb::v11_0::tree::RootNodeCombineHelper< CombineOp, RootT, OtherRootT, true >::combine2
static void combine2(RootT &self, const RootT &other0, const OtherRootT &other1, CombineOp &op, bool prune)
Definition RootNode.h:3313
openvdb::v11_0::tree::RootNodeCombineHelper
Definition RootNode.h:3293
openvdb::v11_0::tree::RootNodeCombineHelper::combine2
static void combine2(RootT &self, const RootT &, const OtherRootT &other1, CombineOp &, bool)
Definition RootNode.h:3294
openvdb::v11_0::tree::RootNodeCopyHelper< RootT, OtherRootT, true >::copyWithValueConversion
static void copyWithValueConversion(RootT &self, const OtherRootT &other)
Definition RootNode.h:1144
openvdb::v11_0::tree::RootNodeCopyHelper
Definition RootNode.h:1125
openvdb::v11_0::tree::RootNodeCopyHelper::copyWithValueConversion
static void copyWithValueConversion(RootT &self, const OtherRootT &other)
Definition RootNode.h:1126
openvdb::v11_0::tree::SameRootConfig
Definition RootNode.h:1016
OPENVDB_VERSION_NAME
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
OPENVDB_USE_VERSION_NAMESPACE
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:212