1 // Copyright Contributors to the OpenVDB Project 2 // SPDX-License-Identifier: MPL-2.0 3 4 #ifndef OPENVDB_TREE_LEAFNODE_HAS_BEEN_INCLUDED 5 #define OPENVDB_TREE_LEAFNODE_HAS_BEEN_INCLUDED 6 7 #include <openvdb/Types.h> 8 #include <openvdb/util/NodeMasks.h> 9 #include <openvdb/io/Compression.h> // for io::readData(), etc. 10 #include "Iterator.h" 11 #include "LeafBuffer.h" 12 #include <algorithm> // for std::nth_element() 13 #include <iostream> 14 #include <memory> 15 #include <sstream> 16 #include <string> 17 #include <type_traits> 18 #include <vector> 19 20 21 class TestLeaf; 22 template<typename> class TestLeafIO; 23 24 namespace openvdb { 25 OPENVDB_USE_VERSION_NAMESPACE 26 namespace OPENVDB_VERSION_NAME { 27 namespace tree { 28 29 template<Index, typename> struct SameLeafConfig; // forward declaration 30 31 32 /// @brief Templated block class to hold specific data types and a fixed 33 /// number of values determined by Log2Dim. The actual coordinate 34 /// dimension of the block is 2^Log2Dim, i.e. Log2Dim=3 corresponds to 35 /// a LeafNode that spans a 8^3 block. 36 template<typename T, Index Log2Dim> 37 class LeafNode 38 { 39 public: 40 using BuildType = T; 41 using ValueType = T; 42 using Buffer = LeafBuffer<ValueType, Log2Dim>; 43 using LeafNodeType = LeafNode<ValueType, Log2Dim>; 44 using NodeMaskType = util::NodeMask<Log2Dim>; 45 using Ptr = SharedPtr<LeafNode>; 46 47 static const Index 48 LOG2DIM = Log2Dim, // needed by parent nodes 49 TOTAL = Log2Dim, // needed by parent nodes 50 DIM = 1 << TOTAL, // dimension along one coordinate direction 51 NUM_VALUES = 1 << 3 * Log2Dim, 52 NUM_VOXELS = NUM_VALUES, // total number of voxels represented by this node 53 SIZE = NUM_VALUES, 54 LEVEL = 0; // level 0 = leaf 55 56 /// @brief ValueConverter<T>::Type is the type of a LeafNode having the same 57 /// dimensions as this node but a different value type, T. 58 template<typename OtherValueType> 59 struct ValueConverter { using Type = LeafNode<OtherValueType, Log2Dim>; }; 60 61 /// @brief SameConfiguration<OtherNodeType>::value is @c true if and only if 62 /// OtherNodeType is the type of a LeafNode with the same dimensions as this node. 63 template<typename OtherNodeType> 64 struct SameConfiguration { 65 static const bool value = SameLeafConfig<LOG2DIM, OtherNodeType>::value; 66 }; 67 68 69 /// Default constructor 70 LeafNode(); 71 72 /// @brief Constructor 73 /// @param coords the grid index coordinates of a voxel 74 /// @param value a value with which to fill the buffer 75 /// @param active the active state to which to initialize all voxels 76 explicit LeafNode(const Coord& coords, 77 const ValueType& value = zeroVal<ValueType>(), 78 bool active = false); 79 80 /// @brief "Partial creation" constructor used during file input 81 /// @param coords the grid index coordinates of a voxel 82 /// @param value a value with which to fill the buffer 83 /// @param active the active state to which to initialize all voxels 84 /// @details This constructor does not allocate memory for voxel values. 85 LeafNode(PartialCreate, 86 const Coord& coords, 87 const ValueType& value = zeroVal<ValueType>(), 88 bool active = false); 89 90 /// Deep copy constructor 91 LeafNode(const LeafNode&); 92 93 /// Deep assignment operator 94 LeafNode& operator=(const LeafNode&) = default; 95 96 /// Value conversion copy constructor 97 template<typename OtherValueType> 98 explicit LeafNode(const LeafNode<OtherValueType, Log2Dim>& other); 99 100 /// Topology copy constructor 101 template<typename OtherValueType> 102 LeafNode(const LeafNode<OtherValueType, Log2Dim>& other, 103 const ValueType& offValue, const ValueType& onValue, TopologyCopy); 104 105 /// Topology copy constructor 106 template<typename OtherValueType> 107 LeafNode(const LeafNode<OtherValueType, Log2Dim>& other, 108 const ValueType& background, TopologyCopy); 109 110 /// Destructor. 111 ~LeafNode(); 112 113 // 114 // Statistics 115 // 116 /// Return log2 of the dimension of this LeafNode, e.g. 3 if dimensions are 8^3 log2dim()117 static Index log2dim() { return Log2Dim; } 118 /// Return the number of voxels in each coordinate dimension. dim()119 static Index dim() { return DIM; } 120 /// Return the total number of voxels represented by this LeafNode size()121 static Index size() { return SIZE; } 122 /// Return the total number of voxels represented by this LeafNode numValues()123 static Index numValues() { return SIZE; } 124 /// Return the level of this node, which by definition is zero for LeafNodes getLevel()125 static Index getLevel() { return LEVEL; } 126 /// Append the Log2Dim of this LeafNode to the specified vector getNodeLog2Dims(std::vector<Index> & dims)127 static void getNodeLog2Dims(std::vector<Index>& dims) { dims.push_back(Log2Dim); } 128 /// Return the dimension of child nodes of this LeafNode, which is one for voxels. getChildDim()129 static Index getChildDim() { return 1; } 130 /// Return the leaf count for this node, which is one. leafCount()131 static Index32 leafCount() { return 1; } 132 /// no-op nodeCount(std::vector<Index32> &)133 void nodeCount(std::vector<Index32> &) const {} 134 /// Return the non-leaf count for this node, which is zero. nonLeafCount()135 static Index32 nonLeafCount() { return 0; } 136 /// Return the child count for this node, which is zero. childCount()137 static Index32 childCount() { return 0; } 138 139 /// Return the number of voxels marked On. onVoxelCount()140 Index64 onVoxelCount() const { return mValueMask.countOn(); } 141 /// Return the number of voxels marked Off. offVoxelCount()142 Index64 offVoxelCount() const { return mValueMask.countOff(); } onLeafVoxelCount()143 Index64 onLeafVoxelCount() const { return onVoxelCount(); } offLeafVoxelCount()144 Index64 offLeafVoxelCount() const { return offVoxelCount(); } onTileCount()145 static Index64 onTileCount() { return 0; } offTileCount()146 static Index64 offTileCount() { return 0; } 147 /// Return @c true if this node has no active voxels. isEmpty()148 bool isEmpty() const { return mValueMask.isOff(); } 149 /// Return @c true if this node contains only active voxels. isDense()150 bool isDense() const { return mValueMask.isOn(); } 151 /// Return @c true if memory for this node's buffer has been allocated. isAllocated()152 bool isAllocated() const { return !mBuffer.isOutOfCore() && !mBuffer.empty(); } 153 /// Allocate memory for this node's buffer if it has not already been allocated. allocate()154 bool allocate() { return mBuffer.allocate(); } 155 156 /// Return the memory in bytes occupied by this node. 157 Index64 memUsage() const; 158 159 /// Expand the given bounding box so that it includes this leaf node's active voxels. 160 /// If visitVoxels is false this LeafNode will be approximated as dense, i.e. with all 161 /// voxels active. Else the individual active voxels are visited to produce a tight bbox. 162 void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) const; 163 164 /// @brief Return the bounding box of this node, i.e., the full index space 165 /// spanned by this leaf node. getNodeBoundingBox()166 CoordBBox getNodeBoundingBox() const { return CoordBBox::createCube(mOrigin, DIM); } 167 168 /// Set the grid index coordinates of this node's local origin. setOrigin(const Coord & origin)169 void setOrigin(const Coord& origin) { mOrigin = origin; } 170 //@{ 171 /// Return the grid index coordinates of this node's local origin. origin()172 const Coord& origin() const { return mOrigin; } getOrigin(Coord & origin)173 void getOrigin(Coord& origin) const { origin = mOrigin; } getOrigin(Int32 & x,Int32 & y,Int32 & z)174 void getOrigin(Int32& x, Int32& y, Int32& z) const { mOrigin.asXYZ(x, y, z); } 175 //@} 176 177 /// Return the linear table offset of the given global or local coordinates. 178 static Index coordToOffset(const Coord& xyz); 179 /// @brief Return the local coordinates for a linear table offset, 180 /// where offset 0 has coordinates (0, 0, 0). 181 static Coord offsetToLocalCoord(Index n); 182 /// Return the global coordinates for a linear table offset. 183 Coord offsetToGlobalCoord(Index n) const; 184 185 #if OPENVDB_ABI_VERSION_NUMBER >= 9 186 /// Return the transient data value. transientData()187 Index32 transientData() const { return mTransientData; } 188 /// Set the transient data value. setTransientData(Index32 transientData)189 void setTransientData(Index32 transientData) { mTransientData = transientData; } 190 #endif 191 192 /// Return a string representation of this node. 193 std::string str() const; 194 195 /// @brief Return @c true if the given node (which may have a different @c ValueType 196 /// than this node) has the same active value topology as this node. 197 template<typename OtherType, Index OtherLog2Dim> 198 bool hasSameTopology(const LeafNode<OtherType, OtherLog2Dim>* other) const; 199 200 /// Check for buffer, state and origin equivalence. 201 bool operator==(const LeafNode& other) const; 202 bool operator!=(const LeafNode& other) const { return !(other == *this); } 203 204 protected: 205 using MaskOnIterator = typename NodeMaskType::OnIterator; 206 using MaskOffIterator = typename NodeMaskType::OffIterator; 207 using MaskDenseIterator = typename NodeMaskType::DenseIterator; 208 209 // Type tags to disambiguate template instantiations 210 struct ValueOn {}; struct ValueOff {}; struct ValueAll {}; 211 struct ChildOn {}; struct ChildOff {}; struct ChildAll {}; 212 213 template<typename MaskIterT, typename NodeT, typename ValueT, typename TagT> 214 struct ValueIter: 215 // Derives from SparseIteratorBase, but can also be used as a dense iterator, 216 // if MaskIterT is a dense mask iterator type. 217 public SparseIteratorBase< 218 MaskIterT, ValueIter<MaskIterT, NodeT, ValueT, TagT>, NodeT, ValueT> 219 { 220 using BaseT = SparseIteratorBase<MaskIterT, ValueIter, NodeT, ValueT>; 221 ValueIterValueIter222 ValueIter() {} ValueIterValueIter223 ValueIter(const MaskIterT& iter, NodeT* parent): BaseT(iter, parent) {} 224 getItemValueIter225 ValueT& getItem(Index pos) const { return this->parent().getValue(pos); } getValueValueIter226 ValueT& getValue() const { return this->parent().getValue(this->pos()); } 227 228 // Note: setItem() can't be called on const iterators. setItemValueIter229 void setItem(Index pos, const ValueT& value) const 230 { 231 this->parent().setValueOnly(pos, value); 232 } 233 // Note: setValue() can't be called on const iterators. setValueValueIter234 void setValue(const ValueT& value) const 235 { 236 this->parent().setValueOnly(this->pos(), value); 237 } 238 239 // Note: modifyItem() can't be called on const iterators. 240 template<typename ModifyOp> modifyItemValueIter241 void modifyItem(Index n, const ModifyOp& op) const { this->parent().modifyValue(n, op); } 242 // Note: modifyValue() can't be called on const iterators. 243 template<typename ModifyOp> modifyValueValueIter244 void modifyValue(const ModifyOp& op) const { this->parent().modifyValue(this->pos(), op); } 245 }; 246 247 /// Leaf nodes have no children, so their child iterators have no get/set accessors. 248 template<typename MaskIterT, typename NodeT, typename TagT> 249 struct ChildIter: 250 public SparseIteratorBase<MaskIterT, ChildIter<MaskIterT, NodeT, TagT>, NodeT, ValueType> 251 { ChildIterChildIter252 ChildIter() {} ChildIterChildIter253 ChildIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase< 254 MaskIterT, ChildIter<MaskIterT, NodeT, TagT>, NodeT, ValueType>(iter, parent) {} 255 }; 256 257 template<typename NodeT, typename ValueT, typename TagT> 258 struct DenseIter: public DenseIteratorBase< 259 MaskDenseIterator, DenseIter<NodeT, ValueT, TagT>, NodeT, /*ChildT=*/void, ValueT> 260 { 261 using BaseT = DenseIteratorBase<MaskDenseIterator, DenseIter, NodeT, void, ValueT>; 262 using NonConstValueT = typename BaseT::NonConstValueType; 263 DenseIterDenseIter264 DenseIter() {} DenseIterDenseIter265 DenseIter(const MaskDenseIterator& iter, NodeT* parent): BaseT(iter, parent) {} 266 getItemDenseIter267 bool getItem(Index pos, void*& child, NonConstValueT& value) const 268 { 269 value = this->parent().getValue(pos); 270 child = nullptr; 271 return false; // no child 272 } 273 274 // Note: setItem() can't be called on const iterators. 275 //void setItem(Index pos, void* child) const {} 276 277 // Note: unsetItem() can't be called on const iterators. unsetItemDenseIter278 void unsetItem(Index pos, const ValueT& value) const 279 { 280 this->parent().setValueOnly(pos, value); 281 } 282 }; 283 284 public: 285 using ValueOnIter = ValueIter<MaskOnIterator, LeafNode, const ValueType, ValueOn>; 286 using ValueOnCIter = ValueIter<MaskOnIterator, const LeafNode, const ValueType, ValueOn>; 287 using ValueOffIter = ValueIter<MaskOffIterator, LeafNode, const ValueType, ValueOff>; 288 using ValueOffCIter = ValueIter<MaskOffIterator,const LeafNode,const ValueType,ValueOff>; 289 using ValueAllIter = ValueIter<MaskDenseIterator, LeafNode, const ValueType, ValueAll>; 290 using ValueAllCIter = ValueIter<MaskDenseIterator,const LeafNode,const ValueType,ValueAll>; 291 using ChildOnIter = ChildIter<MaskOnIterator, LeafNode, ChildOn>; 292 using ChildOnCIter = ChildIter<MaskOnIterator, const LeafNode, ChildOn>; 293 using ChildOffIter = ChildIter<MaskOffIterator, LeafNode, ChildOff>; 294 using ChildOffCIter = ChildIter<MaskOffIterator, const LeafNode, ChildOff>; 295 using ChildAllIter = DenseIter<LeafNode, ValueType, ChildAll>; 296 using ChildAllCIter = DenseIter<const LeafNode, const ValueType, ChildAll>; 297 cbeginValueOn()298 ValueOnCIter cbeginValueOn() const { return ValueOnCIter(mValueMask.beginOn(), this); } beginValueOn()299 ValueOnCIter beginValueOn() const { return ValueOnCIter(mValueMask.beginOn(), this); } beginValueOn()300 ValueOnIter beginValueOn() { return ValueOnIter(mValueMask.beginOn(), this); } cbeginValueOff()301 ValueOffCIter cbeginValueOff() const { return ValueOffCIter(mValueMask.beginOff(), this); } beginValueOff()302 ValueOffCIter beginValueOff() const { return ValueOffCIter(mValueMask.beginOff(), this); } beginValueOff()303 ValueOffIter beginValueOff() { return ValueOffIter(mValueMask.beginOff(), this); } cbeginValueAll()304 ValueAllCIter cbeginValueAll() const { return ValueAllCIter(mValueMask.beginDense(), this); } beginValueAll()305 ValueAllCIter beginValueAll() const { return ValueAllCIter(mValueMask.beginDense(), this); } beginValueAll()306 ValueAllIter beginValueAll() { return ValueAllIter(mValueMask.beginDense(), this); } 307 cendValueOn()308 ValueOnCIter cendValueOn() const { return ValueOnCIter(mValueMask.endOn(), this); } endValueOn()309 ValueOnCIter endValueOn() const { return ValueOnCIter(mValueMask.endOn(), this); } endValueOn()310 ValueOnIter endValueOn() { return ValueOnIter(mValueMask.endOn(), this); } cendValueOff()311 ValueOffCIter cendValueOff() const { return ValueOffCIter(mValueMask.endOff(), this); } endValueOff()312 ValueOffCIter endValueOff() const { return ValueOffCIter(mValueMask.endOff(), this); } endValueOff()313 ValueOffIter endValueOff() { return ValueOffIter(mValueMask.endOff(), this); } cendValueAll()314 ValueAllCIter cendValueAll() const { return ValueAllCIter(mValueMask.endDense(), this); } endValueAll()315 ValueAllCIter endValueAll() const { return ValueAllCIter(mValueMask.endDense(), this); } endValueAll()316 ValueAllIter endValueAll() { return ValueAllIter(mValueMask.endDense(), this); } 317 318 // Note that [c]beginChildOn() and [c]beginChildOff() actually return end iterators, 319 // because leaf nodes have no children. cbeginChildOn()320 ChildOnCIter cbeginChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); } beginChildOn()321 ChildOnCIter beginChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); } beginChildOn()322 ChildOnIter beginChildOn() { return ChildOnIter(mValueMask.endOn(), this); } cbeginChildOff()323 ChildOffCIter cbeginChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); } beginChildOff()324 ChildOffCIter beginChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); } beginChildOff()325 ChildOffIter beginChildOff() { return ChildOffIter(mValueMask.endOff(), this); } cbeginChildAll()326 ChildAllCIter cbeginChildAll() const { return ChildAllCIter(mValueMask.beginDense(), this); } beginChildAll()327 ChildAllCIter beginChildAll() const { return ChildAllCIter(mValueMask.beginDense(), this); } beginChildAll()328 ChildAllIter beginChildAll() { return ChildAllIter(mValueMask.beginDense(), this); } 329 cendChildOn()330 ChildOnCIter cendChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); } endChildOn()331 ChildOnCIter endChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); } endChildOn()332 ChildOnIter endChildOn() { return ChildOnIter(mValueMask.endOn(), this); } cendChildOff()333 ChildOffCIter cendChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); } endChildOff()334 ChildOffCIter endChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); } endChildOff()335 ChildOffIter endChildOff() { return ChildOffIter(mValueMask.endOff(), this); } cendChildAll()336 ChildAllCIter cendChildAll() const { return ChildAllCIter(mValueMask.endDense(), this); } endChildAll()337 ChildAllCIter endChildAll() const { return ChildAllCIter(mValueMask.endDense(), this); } endChildAll()338 ChildAllIter endChildAll() { return ChildAllIter(mValueMask.endDense(), this); } 339 340 // 341 // Buffer management 342 // 343 /// @brief Exchange this node's data buffer with the given data buffer 344 /// without changing the active states of the values. swap(Buffer & other)345 void swap(Buffer& other) { mBuffer.swap(other); } buffer()346 const Buffer& buffer() const { return mBuffer; } buffer()347 Buffer& buffer() { return mBuffer; } 348 349 // 350 // I/O methods 351 // 352 /// @brief Read in just the topology. 353 /// @param is the stream from which to read 354 /// @param fromHalf if true, floating-point input values are assumed to be 16-bit 355 void readTopology(std::istream& is, bool fromHalf = false); 356 /// @brief Write out just the topology. 357 /// @param os the stream to which to write 358 /// @param toHalf if true, output floating-point values as 16-bit half floats 359 void writeTopology(std::ostream& os, bool toHalf = false) const; 360 361 /// @brief Read buffers from a stream. 362 /// @param is the stream from which to read 363 /// @param fromHalf if true, floating-point input values are assumed to be 16-bit 364 void readBuffers(std::istream& is, bool fromHalf = false); 365 /// @brief Read buffers that intersect the given bounding box. 366 /// @param is the stream from which to read 367 /// @param bbox an index-space bounding box 368 /// @param fromHalf if true, floating-point input values are assumed to be 16-bit 369 void readBuffers(std::istream& is, const CoordBBox& bbox, bool fromHalf = false); 370 /// @brief Write buffers to a stream. 371 /// @param os the stream to which to write 372 /// @param toHalf if true, output floating-point values as 16-bit half floats 373 void writeBuffers(std::ostream& os, bool toHalf = false) const; 374 375 size_t streamingSize(bool toHalf = false) const; 376 377 // 378 // Accessor methods 379 // 380 /// Return the value of the voxel at the given coordinates. 381 const ValueType& getValue(const Coord& xyz) const; 382 /// Return the value of the voxel at the given linear offset. 383 const ValueType& getValue(Index offset) const; 384 385 /// @brief Return @c true if the voxel at the given coordinates is active. 386 /// @param xyz the coordinates of the voxel to be probed 387 /// @param[out] val the value of the voxel at the given coordinates 388 bool probeValue(const Coord& xyz, ValueType& val) const; 389 /// @brief Return @c true if the voxel at the given offset is active. 390 /// @param offset the linear offset of the voxel to be probed 391 /// @param[out] val the value of the voxel at the given coordinates 392 bool probeValue(Index offset, ValueType& val) const; 393 394 /// Return the level (i.e., 0) at which leaf node values reside. getValueLevel(const Coord &)395 static Index getValueLevel(const Coord&) { return LEVEL; } 396 397 /// Set the active state of the voxel at the given coordinates but don't change its value. 398 void setActiveState(const Coord& xyz, bool on); 399 /// Set the active state of the voxel at the given offset but don't change its value. setActiveState(Index offset,bool on)400 void setActiveState(Index offset, bool on) { assert(offset<SIZE); mValueMask.set(offset, on); } 401 402 /// Set the value of the voxel at the given coordinates but don't change its active state. 403 void setValueOnly(const Coord& xyz, const ValueType& val); 404 /// Set the value of the voxel at the given offset but don't change its active state. 405 void setValueOnly(Index offset, const ValueType& val); 406 407 /// Mark the voxel at the given coordinates as inactive but don't change its value. setValueOff(const Coord & xyz)408 void setValueOff(const Coord& xyz) { mValueMask.setOff(LeafNode::coordToOffset(xyz)); } 409 /// Mark the voxel at the given offset as inactive but don't change its value. setValueOff(Index offset)410 void setValueOff(Index offset) { assert(offset < SIZE); mValueMask.setOff(offset); } 411 412 /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. 413 void setValueOff(const Coord& xyz, const ValueType& val); 414 /// Set the value of the voxel at the given offset and mark the voxel as inactive. 415 void setValueOff(Index offset, const ValueType& val); 416 417 /// Mark the voxel at the given coordinates as active but don't change its value. setValueOn(const Coord & xyz)418 void setValueOn(const Coord& xyz) { mValueMask.setOn(LeafNode::coordToOffset(xyz)); } 419 /// Mark the voxel at the given offset as active but don't change its value. setValueOn(Index offset)420 void setValueOn(Index offset) { assert(offset < SIZE); mValueMask.setOn(offset); } 421 /// Set the value of the voxel at the given coordinates and mark the voxel as active. setValueOn(const Coord & xyz,const ValueType & val)422 void setValueOn(const Coord& xyz, const ValueType& val) { 423 this->setValueOn(LeafNode::coordToOffset(xyz), val); 424 } 425 /// Set the value of the voxel at the given coordinates and mark the voxel as active. setValue(const Coord & xyz,const ValueType & val)426 void setValue(const Coord& xyz, const ValueType& val) { this->setValueOn(xyz, val); } 427 /// Set the value of the voxel at the given offset and mark the voxel as active. setValueOn(Index offset,const ValueType & val)428 void setValueOn(Index offset, const ValueType& val) { 429 mBuffer.setValue(offset, val); 430 mValueMask.setOn(offset); 431 } 432 433 /// @brief Apply a functor to the value of the voxel at the given offset 434 /// and mark the voxel as active. 435 template<typename ModifyOp> modifyValue(Index offset,const ModifyOp & op)436 void modifyValue(Index offset, const ModifyOp& op) 437 { 438 mBuffer.loadValues(); 439 if (!mBuffer.empty()) { 440 // in-place modify value 441 ValueType& val = const_cast<ValueType&>(mBuffer[offset]); 442 op(val); 443 mValueMask.setOn(offset); 444 } 445 } 446 447 /// @brief Apply a functor to the value of the voxel at the given coordinates 448 /// and mark the voxel as active. 449 template<typename ModifyOp> modifyValue(const Coord & xyz,const ModifyOp & op)450 void modifyValue(const Coord& xyz, const ModifyOp& op) 451 { 452 this->modifyValue(this->coordToOffset(xyz), op); 453 } 454 455 /// Apply a functor to the voxel at the given coordinates. 456 template<typename ModifyOp> modifyValueAndActiveState(const Coord & xyz,const ModifyOp & op)457 void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) 458 { 459 mBuffer.loadValues(); 460 if (!mBuffer.empty()) { 461 const Index offset = this->coordToOffset(xyz); 462 bool state = mValueMask.isOn(offset); 463 // in-place modify value 464 ValueType& val = const_cast<ValueType&>(mBuffer[offset]); 465 op(val, state); 466 mValueMask.set(offset, state); 467 } 468 } 469 470 /// Mark all voxels as active but don't change their values. setValuesOn()471 void setValuesOn() { mValueMask.setOn(); } 472 /// Mark all voxels as inactive but don't change their values. setValuesOff()473 void setValuesOff() { mValueMask.setOff(); } 474 475 /// Return @c true if the voxel at the given coordinates is active. isValueOn(const Coord & xyz)476 bool isValueOn(const Coord& xyz) const {return this->isValueOn(LeafNode::coordToOffset(xyz));} 477 /// Return @c true if the voxel at the given offset is active. isValueOn(Index offset)478 bool isValueOn(Index offset) const { return mValueMask.isOn(offset); } 479 480 /// Return @c false since leaf nodes never contain tiles. hasActiveTiles()481 static bool hasActiveTiles() { return false; } 482 483 /// Set all voxels that lie outside the given axis-aligned box to the background. 484 void clip(const CoordBBox&, const ValueType& background); 485 486 /// Set all voxels within an axis-aligned box to the specified value and active state. 487 void fill(const CoordBBox& bbox, const ValueType&, bool active = true); 488 /// Set all voxels within an axis-aligned box to the specified value and active state. 489 void denseFill(const CoordBBox& bbox, const ValueType& value, bool active = true) 490 { 491 this->fill(bbox, value, active); 492 } 493 494 /// Set all voxels to the specified value but don't change their active states. 495 void fill(const ValueType& value); 496 /// Set all voxels to the specified value and active state. 497 void fill(const ValueType& value, bool active); 498 499 /// @brief Copy into a dense grid the values of the voxels that lie within 500 /// a given bounding box. 501 /// 502 /// @param bbox inclusive bounding box of the voxels to be copied into the dense grid 503 /// @param dense dense grid with a stride in @e z of one (see tools::Dense 504 /// in tools/Dense.h for the required API) 505 /// 506 /// @note @a bbox is assumed to be identical to or contained in the coordinate domains 507 /// of both the dense grid and this node, i.e., no bounds checking is performed. 508 /// @note Consider using tools::CopyToDense in tools/Dense.h 509 /// instead of calling this method directly. 510 template<typename DenseT> 511 void copyToDense(const CoordBBox& bbox, DenseT& dense) const; 512 513 /// @brief Copy from a dense grid into this node the values of the voxels 514 /// that lie within a given bounding box. 515 /// @details Only values that are different (by more than the given tolerance) 516 /// from the background value will be active. Other values are inactive 517 /// and truncated to the background value. 518 /// 519 /// @param bbox inclusive bounding box of the voxels to be copied into this node 520 /// @param dense dense grid with a stride in @e z of one (see tools::Dense 521 /// in tools/Dense.h for the required API) 522 /// @param background background value of the tree that this node belongs to 523 /// @param tolerance tolerance within which a value equals the background value 524 /// 525 /// @note @a bbox is assumed to be identical to or contained in the coordinate domains 526 /// of both the dense grid and this node, i.e., no bounds checking is performed. 527 /// @note Consider using tools::CopyFromDense in tools/Dense.h 528 /// instead of calling this method directly. 529 template<typename DenseT> 530 void copyFromDense(const CoordBBox& bbox, const DenseT& dense, 531 const ValueType& background, const ValueType& tolerance); 532 533 /// @brief Return the value of the voxel at the given coordinates. 534 /// @note Used internally by ValueAccessor. 535 template<typename AccessorT> getValueAndCache(const Coord & xyz,AccessorT &)536 const ValueType& getValueAndCache(const Coord& xyz, AccessorT&) const 537 { 538 return this->getValue(xyz); 539 } 540 541 /// @brief Return @c true if the voxel at the given coordinates is active. 542 /// @note Used internally by ValueAccessor. 543 template<typename AccessorT> isValueOnAndCache(const Coord & xyz,AccessorT &)544 bool isValueOnAndCache(const Coord& xyz, AccessorT&) const { return this->isValueOn(xyz); } 545 546 /// @brief Change the value of the voxel at the given coordinates and mark it as active. 547 /// @note Used internally by ValueAccessor. 548 template<typename AccessorT> setValueAndCache(const Coord & xyz,const ValueType & val,AccessorT &)549 void setValueAndCache(const Coord& xyz, const ValueType& val, AccessorT&) 550 { 551 this->setValueOn(xyz, val); 552 } 553 554 /// @brief Change the value of the voxel at the given coordinates 555 /// but preserve its state. 556 /// @note Used internally by ValueAccessor. 557 template<typename AccessorT> setValueOnlyAndCache(const Coord & xyz,const ValueType & val,AccessorT &)558 void setValueOnlyAndCache(const Coord& xyz, const ValueType& val, AccessorT&) 559 { 560 this->setValueOnly(xyz, val); 561 } 562 563 /// @brief Apply a functor to the value of the voxel at the given coordinates 564 /// and mark the voxel as active. 565 /// @note Used internally by ValueAccessor. 566 template<typename ModifyOp, typename AccessorT> modifyValueAndCache(const Coord & xyz,const ModifyOp & op,AccessorT &)567 void modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) 568 { 569 this->modifyValue(xyz, op); 570 } 571 572 /// Apply a functor to the voxel at the given coordinates. 573 /// @note Used internally by ValueAccessor. 574 template<typename ModifyOp, typename AccessorT> modifyValueAndActiveStateAndCache(const Coord & xyz,const ModifyOp & op,AccessorT &)575 void modifyValueAndActiveStateAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) 576 { 577 this->modifyValueAndActiveState(xyz, op); 578 } 579 580 /// @brief Change the value of the voxel at the given coordinates and mark it as inactive. 581 /// @note Used internally by ValueAccessor. 582 template<typename AccessorT> setValueOffAndCache(const Coord & xyz,const ValueType & value,AccessorT &)583 void setValueOffAndCache(const Coord& xyz, const ValueType& value, AccessorT&) 584 { 585 this->setValueOff(xyz, value); 586 } 587 588 /// @brief Set the active state of the voxel at the given coordinates 589 /// without changing its value. 590 /// @note Used internally by ValueAccessor. 591 template<typename AccessorT> setActiveStateAndCache(const Coord & xyz,bool on,AccessorT &)592 void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&) 593 { 594 this->setActiveState(xyz, on); 595 } 596 597 /// @brief Return @c true if the voxel at the given coordinates is active 598 /// and return the voxel value in @a val. 599 /// @note Used internally by ValueAccessor. 600 template<typename AccessorT> probeValueAndCache(const Coord & xyz,ValueType & val,AccessorT &)601 bool probeValueAndCache(const Coord& xyz, ValueType& val, AccessorT&) const 602 { 603 return this->probeValue(xyz, val); 604 } 605 606 /// @brief Return the value of the voxel at the given coordinates and return 607 /// its active state and level (i.e., 0) in @a state and @a level. 608 /// @note Used internally by ValueAccessor. 609 template<typename AccessorT> getValue(const Coord & xyz,bool & state,int & level,AccessorT &)610 const ValueType& getValue(const Coord& xyz, bool& state, int& level, AccessorT&) const 611 { 612 const Index offset = this->coordToOffset(xyz); 613 state = mValueMask.isOn(offset); 614 level = LEVEL; 615 return mBuffer[offset]; 616 } 617 618 /// @brief Return the LEVEL (=0) at which leaf node values reside. 619 /// @note Used internally by ValueAccessor (note last argument is a dummy). 620 template<typename AccessorT> getValueLevelAndCache(const Coord &,AccessorT &)621 static Index getValueLevelAndCache(const Coord&, AccessorT&) { return LEVEL; } 622 623 /// @brief Return a const reference to the first value in the buffer. 624 /// @note Though it is potentially risky you can convert this 625 /// to a non-const pointer by means of const_case<ValueType*>&. getFirstValue()626 const ValueType& getFirstValue() const { return mBuffer[0]; } 627 /// Return a const reference to the last value in the buffer. getLastValue()628 const ValueType& getLastValue() const { return mBuffer[SIZE - 1]; } 629 630 /// @brief Replace inactive occurrences of @a oldBackground with @a newBackground, 631 /// and inactive occurrences of @a -oldBackground with @a -newBackground. 632 void resetBackground(const ValueType& oldBackground, const ValueType& newBackground); 633 634 void negate(); 635 636 /// @brief No-op 637 /// @details This function exists only to enable template instantiation. 638 void voxelizeActiveTiles(bool = true) {} 639 640 template<MergePolicy Policy> void merge(const LeafNode&); 641 template<MergePolicy Policy> void merge(const ValueType& tileValue, bool tileActive); 642 template<MergePolicy Policy> 643 void merge(const LeafNode& other, const ValueType& /*bg*/, const ValueType& /*otherBG*/); 644 645 /// @brief Union this node's set of active values with the active values 646 /// of the other node, whose @c ValueType may be different. So a 647 /// resulting voxel will be active if either of the original voxels 648 /// were active. 649 /// 650 /// @note This operation modifies only active states, not values. 651 template<typename OtherType> 652 void topologyUnion(const LeafNode<OtherType, Log2Dim>& other, const bool preserveTiles = false); 653 654 /// @brief Intersect this node's set of active values with the active values 655 /// of the other node, whose @c ValueType may be different. So a 656 /// resulting voxel will be active only if both of the original voxels 657 /// were active. 658 /// 659 /// @details The last dummy argument is required to match the signature 660 /// for InternalNode::topologyIntersection. 661 /// 662 /// @note This operation modifies only active states, not 663 /// values. Also note that this operation can result in all voxels 664 /// being inactive so consider subsequently calling prune. 665 template<typename OtherType> 666 void topologyIntersection(const LeafNode<OtherType, Log2Dim>& other, const ValueType&); 667 668 /// @brief Difference this node's set of active values with the active values 669 /// of the other node, whose @c ValueType may be different. So a 670 /// resulting voxel will be active only if the original voxel is 671 /// active in this LeafNode and inactive in the other LeafNode. 672 /// 673 /// @details The last dummy argument is required to match the signature 674 /// for InternalNode::topologyDifference. 675 /// 676 /// @note This operation modifies only active states, not values. 677 /// Also, because it can deactivate all of this node's voxels, 678 /// consider subsequently calling prune. 679 template<typename OtherType> 680 void topologyDifference(const LeafNode<OtherType, Log2Dim>& other, const ValueType&); 681 682 template<typename CombineOp> 683 void combine(const LeafNode& other, CombineOp& op); 684 template<typename CombineOp> 685 void combine(const ValueType& value, bool valueIsActive, CombineOp& op); 686 687 template<typename CombineOp, typename OtherType /*= ValueType*/> 688 void combine2(const LeafNode& other, const OtherType&, bool valueIsActive, CombineOp&); 689 template<typename CombineOp, typename OtherNodeT /*= LeafNode*/> 690 void combine2(const ValueType&, const OtherNodeT& other, bool valueIsActive, CombineOp&); 691 template<typename CombineOp, typename OtherNodeT /*= LeafNode*/> 692 void combine2(const LeafNode& b0, const OtherNodeT& b1, CombineOp&); 693 694 /// @brief Calls the templated functor BBoxOp with bounding box 695 /// information. An additional level argument is provided to the 696 /// callback. 697 /// 698 /// @note The bounding boxes are guaranteed to be non-overlapping. 699 template<typename BBoxOp> void visitActiveBBox(BBoxOp&) const; 700 701 template<typename VisitorOp> void visit(VisitorOp&); 702 template<typename VisitorOp> void visit(VisitorOp&) const; 703 704 template<typename OtherLeafNodeType, typename VisitorOp> 705 void visit2Node(OtherLeafNodeType& other, VisitorOp&); 706 template<typename OtherLeafNodeType, typename VisitorOp> 707 void visit2Node(OtherLeafNodeType& other, VisitorOp&) const; 708 template<typename IterT, typename VisitorOp> 709 void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false); 710 template<typename IterT, typename VisitorOp> 711 void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false) const; 712 713 //@{ 714 /// This function exists only to enable template instantiation. 715 void prune(const ValueType& /*tolerance*/ = zeroVal<ValueType>()) {} addLeaf(LeafNode *)716 void addLeaf(LeafNode*) {} 717 template<typename AccessorT> addLeafAndCache(LeafNode *,AccessorT &)718 void addLeafAndCache(LeafNode*, AccessorT&) {} 719 template<typename NodeT> stealNode(const Coord &,const ValueType &,bool)720 NodeT* stealNode(const Coord&, const ValueType&, bool) { return nullptr; } 721 template<typename NodeT> probeNode(const Coord &)722 NodeT* probeNode(const Coord&) { return nullptr; } 723 template<typename NodeT> probeConstNode(const Coord &)724 const NodeT* probeConstNode(const Coord&) const { return nullptr; } getNodes(ArrayT &)725 template<typename ArrayT> void getNodes(ArrayT&) const {} stealNodes(ArrayT &,const ValueType &,bool)726 template<typename ArrayT> void stealNodes(ArrayT&, const ValueType&, bool) {} 727 //@} 728 729 void addTile(Index level, const Coord&, const ValueType&, bool); 730 void addTile(Index offset, const ValueType&, bool); 731 template<typename AccessorT> 732 void addTileAndCache(Index, const Coord&, const ValueType&, bool, AccessorT&); 733 734 //@{ 735 /// @brief Return a pointer to this node. touchLeaf(const Coord &)736 LeafNode* touchLeaf(const Coord&) { return this; } 737 template<typename AccessorT> touchLeafAndCache(const Coord &,AccessorT &)738 LeafNode* touchLeafAndCache(const Coord&, AccessorT&) { return this; } 739 template<typename NodeT, typename AccessorT> probeNodeAndCache(const Coord &,AccessorT &)740 NodeT* probeNodeAndCache(const Coord&, AccessorT&) 741 { 742 OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN 743 if (!(std::is_same<NodeT, LeafNode>::value)) return nullptr; 744 return reinterpret_cast<NodeT*>(this); 745 OPENVDB_NO_UNREACHABLE_CODE_WARNING_END 746 } probeLeaf(const Coord &)747 LeafNode* probeLeaf(const Coord&) { return this; } 748 template<typename AccessorT> probeLeafAndCache(const Coord &,AccessorT &)749 LeafNode* probeLeafAndCache(const Coord&, AccessorT&) { return this; } 750 //@} 751 //@{ 752 /// @brief Return a @const pointer to this node. probeConstLeaf(const Coord &)753 const LeafNode* probeConstLeaf(const Coord&) const { return this; } 754 template<typename AccessorT> probeConstLeafAndCache(const Coord &,AccessorT &)755 const LeafNode* probeConstLeafAndCache(const Coord&, AccessorT&) const { return this; } 756 template<typename AccessorT> probeLeafAndCache(const Coord &,AccessorT &)757 const LeafNode* probeLeafAndCache(const Coord&, AccessorT&) const { return this; } probeLeaf(const Coord &)758 const LeafNode* probeLeaf(const Coord&) const { return this; } 759 template<typename NodeT, typename AccessorT> probeConstNodeAndCache(const Coord &,AccessorT &)760 const NodeT* probeConstNodeAndCache(const Coord&, AccessorT&) const 761 { 762 OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN 763 if (!(std::is_same<NodeT, LeafNode>::value)) return nullptr; 764 return reinterpret_cast<const NodeT*>(this); 765 OPENVDB_NO_UNREACHABLE_CODE_WARNING_END 766 } 767 //@} 768 769 /// Return @c true if all of this node's values have the same active state 770 /// and are in the range this->getFirstValue() +/- @a tolerance. 771 /// 772 /// 773 /// @param firstValue Is updated with the first value of this leaf node. 774 /// @param state Is updated with the state of all values IF method 775 /// returns @c true. Else the value is undefined! 776 /// @param tolerance The tolerance used to determine if values are 777 /// approximately equal to the for value. 778 bool isConstant(ValueType& firstValue, bool& state, 779 const ValueType& tolerance = zeroVal<ValueType>()) const; 780 781 /// Return @c true if all of this node's values have the same active state 782 /// and the range (@a maxValue - @a minValue) < @a tolerance. 783 /// 784 /// @param minValue Is updated with the minimum of all values IF method 785 /// returns @c true. Else the value is undefined! 786 /// @param maxValue Is updated with the maximum of all values IF method 787 /// returns @c true. Else the value is undefined! 788 /// @param state Is updated with the state of all values IF method 789 /// returns @c true. Else the value is undefined! 790 /// @param tolerance The tolerance used to determine if values are 791 /// approximately constant. 792 bool isConstant(ValueType& minValue, ValueType& maxValue, 793 bool& state, const ValueType& tolerance = zeroVal<ValueType>()) const; 794 795 796 /// @brief Computes the median value of all the active AND inactive voxels in this node. 797 /// @return The median value of all values in this node. 798 /// 799 /// @param tmp Optional temporary storage that can hold at least NUM_VALUES values 800 /// Use of this temporary storage can improve performance 801 /// when this method is called multiple times. 802 /// 803 /// @note If tmp = this->buffer().data() then the median 804 /// value is computed very efficiently (in place) but 805 /// the voxel values in this node are re-shuffled! 806 /// 807 /// @warning If tmp != nullptr then it is the responsibility of 808 /// the client code that it points to enough memory to 809 /// hold NUM_VALUES elements of type ValueType. 810 ValueType medianAll(ValueType *tmp = nullptr) const; 811 812 /// @brief Computes the median value of all the active voxels in this node. 813 /// @return The number of active voxels. 814 /// 815 /// @param value If the return value is non zero @a value is updated 816 /// with the median value. 817 /// 818 /// @param tmp Optional temporary storage that can hold at least 819 /// as many values as there are active voxels in this node. 820 /// Use of this temporary storage can improve performance 821 /// when this method is called multiple times. 822 /// 823 /// @warning If tmp != nullptr then it is the responsibility of 824 /// the client code that it points to enough memory to 825 /// hold the number of active voxels of type ValueType. 826 Index medianOn(ValueType &value, ValueType *tmp = nullptr) const; 827 828 /// @brief Computes the median value of all the inactive voxels in this node. 829 /// @return The number of inactive voxels. 830 /// 831 /// @param value If the return value is non zero @a value is updated 832 /// with the median value. 833 /// 834 /// @param tmp Optional temporary storage that can hold at least 835 /// as many values as there are inactive voxels in this node. 836 /// Use of this temporary storage can improve performance 837 /// when this method is called multiple times. 838 /// 839 /// @warning If tmp != nullptr then it is the responsibility of 840 /// the client code that it points to enough memory to 841 /// hold the number of inactive voxels of type ValueType. 842 Index medianOff(ValueType &value, ValueType *tmp = nullptr) const; 843 844 /// Return @c true if all of this node's values are inactive. isInactive()845 bool isInactive() const { return mValueMask.isOff(); } 846 847 protected: 848 friend class ::TestLeaf; 849 template<typename> friend class ::TestLeafIO; 850 851 // During topology-only construction, access is needed 852 // to protected/private members of other template instances. 853 template<typename, Index> friend class LeafNode; 854 855 friend struct ValueIter<MaskOnIterator, LeafNode, ValueType, ValueOn>; 856 friend struct ValueIter<MaskOffIterator, LeafNode, ValueType, ValueOff>; 857 friend struct ValueIter<MaskDenseIterator, LeafNode, ValueType, ValueAll>; 858 friend struct ValueIter<MaskOnIterator, const LeafNode, ValueType, ValueOn>; 859 friend struct ValueIter<MaskOffIterator, const LeafNode, ValueType, ValueOff>; 860 friend struct ValueIter<MaskDenseIterator, const LeafNode, ValueType, ValueAll>; 861 862 // Allow iterators to call mask accessor methods (see below). 863 /// @todo Make mask accessors public? 864 friend class IteratorBase<MaskOnIterator, LeafNode>; 865 friend class IteratorBase<MaskOffIterator, LeafNode>; 866 friend class IteratorBase<MaskDenseIterator, LeafNode>; 867 868 // Mask accessors 869 public: 870 bool isValueMaskOn(Index n) const { return mValueMask.isOn(n); } 871 bool isValueMaskOn() const { return mValueMask.isOn(); } 872 bool isValueMaskOff(Index n) const { return mValueMask.isOff(n); } 873 bool isValueMaskOff() const { return mValueMask.isOff(); } 874 const NodeMaskType& getValueMask() const { return mValueMask; } 875 NodeMaskType& getValueMask() { return mValueMask; } 876 const NodeMaskType& valueMask() const { return mValueMask; } 877 void setValueMask(const NodeMaskType& mask) { mValueMask = mask; } 878 bool isChildMaskOn(Index) const { return false; } // leaf nodes have no children 879 bool isChildMaskOff(Index) const { return true; } 880 bool isChildMaskOff() const { return true; } 881 protected: 882 void setValueMask(Index n, bool on) { mValueMask.set(n, on); } 883 void setValueMaskOn(Index n) { mValueMask.setOn(n); } 884 void setValueMaskOff(Index n) { mValueMask.setOff(n); } 885 886 inline void skipCompressedValues(bool seekable, std::istream&, bool fromHalf); 887 888 /// Compute the origin of the leaf node that contains the voxel with the given coordinates. 889 static void evalNodeOrigin(Coord& xyz) { xyz &= ~(DIM - 1); } 890 891 template<typename NodeT, typename VisitorOp, typename ChildAllIterT> 892 static inline void doVisit(NodeT&, VisitorOp&); 893 894 template<typename NodeT, typename OtherNodeT, typename VisitorOp, 895 typename ChildAllIterT, typename OtherChildAllIterT> 896 static inline void doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp&); 897 898 template<typename NodeT, typename VisitorOp, 899 typename ChildAllIterT, typename OtherChildAllIterT> 900 static inline void doVisit2(NodeT& self, OtherChildAllIterT&, VisitorOp&, bool otherIsLHS); 901 902 private: 903 /// Buffer containing the actual data values 904 Buffer mBuffer; 905 /// Bitmask that determines which voxels are active 906 NodeMaskType mValueMask; 907 /// Global grid index coordinates (x,y,z) of the local origin of this node 908 Coord mOrigin; 909 #if OPENVDB_ABI_VERSION_NUMBER >= 9 910 /// Transient data (not serialized) 911 Index32 mTransientData = 0; 912 #endif 913 }; // end of LeafNode class 914 915 916 //////////////////////////////////////// 917 918 919 //@{ 920 /// Helper metafunction used to implement LeafNode::SameConfiguration 921 /// (which, as an inner class, can't be independently specialized) 922 template<Index Dim1, typename NodeT2> 923 struct SameLeafConfig { static const bool value = false; }; 924 925 template<Index Dim1, typename T2> 926 struct SameLeafConfig<Dim1, LeafNode<T2, Dim1> > { static const bool value = true; }; 927 //@} 928 929 930 //////////////////////////////////////// 931 932 933 template<typename T, Index Log2Dim> 934 inline 935 LeafNode<T, Log2Dim>::LeafNode(): 936 mValueMask(),//default is off! 937 mOrigin(0, 0, 0) 938 { 939 } 940 941 942 template<typename T, Index Log2Dim> 943 inline 944 LeafNode<T, Log2Dim>::LeafNode(const Coord& xyz, const ValueType& val, bool active): 945 mBuffer(val), 946 mValueMask(active), 947 mOrigin(xyz & (~(DIM - 1))) 948 { 949 } 950 951 952 template<typename T, Index Log2Dim> 953 inline 954 LeafNode<T, Log2Dim>::LeafNode(PartialCreate, const Coord& xyz, const ValueType& val, bool active): 955 mBuffer(PartialCreate(), val), 956 mValueMask(active), 957 mOrigin(xyz & (~(DIM - 1))) 958 { 959 } 960 961 962 template<typename T, Index Log2Dim> 963 inline 964 LeafNode<T, Log2Dim>::LeafNode(const LeafNode& other) 965 : mBuffer(other.mBuffer) 966 , mValueMask(other.valueMask()) 967 , mOrigin(other.mOrigin) 968 #if OPENVDB_ABI_VERSION_NUMBER >= 9 969 , mTransientData(other.mTransientData) 970 #endif 971 { 972 } 973 974 975 // Copy-construct from a leaf node with the same configuration but a different ValueType. 976 template<typename T, Index Log2Dim> 977 template<typename OtherValueType> 978 inline 979 LeafNode<T, Log2Dim>::LeafNode(const LeafNode<OtherValueType, Log2Dim>& other) 980 : mValueMask(other.valueMask()) 981 , mOrigin(other.mOrigin) 982 #if OPENVDB_ABI_VERSION_NUMBER >= 9 983 , mTransientData(other.mTransientData) 984 #endif 985 { 986 struct Local { 987 /// @todo Consider using a value conversion functor passed as an argument instead. 988 static inline ValueType convertValue(const OtherValueType& val) { return ValueType(val); } 989 }; 990 991 for (Index i = 0; i < SIZE; ++i) { 992 mBuffer[i] = Local::convertValue(other.mBuffer[i]); 993 } 994 } 995 996 997 template<typename T, Index Log2Dim> 998 template<typename OtherValueType> 999 inline 1000 LeafNode<T, Log2Dim>::LeafNode(const LeafNode<OtherValueType, Log2Dim>& other, 1001 const ValueType& background, TopologyCopy) 1002 : mBuffer(background) 1003 , mValueMask(other.valueMask()) 1004 , mOrigin(other.mOrigin) 1005 #if OPENVDB_ABI_VERSION_NUMBER >= 9 1006 , mTransientData(other.mTransientData) 1007 #endif 1008 { 1009 } 1010 1011 1012 template<typename T, Index Log2Dim> 1013 template<typename OtherValueType> 1014 inline 1015 LeafNode<T, Log2Dim>::LeafNode(const LeafNode<OtherValueType, Log2Dim>& other, 1016 const ValueType& offValue, const ValueType& onValue, TopologyCopy) 1017 : mValueMask(other.valueMask()) 1018 , mOrigin(other.mOrigin) 1019 #if OPENVDB_ABI_VERSION_NUMBER >= 9 1020 , mTransientData(other.mTransientData) 1021 #endif 1022 { 1023 for (Index i = 0; i < SIZE; ++i) { 1024 mBuffer[i] = (mValueMask.isOn(i) ? onValue : offValue); 1025 } 1026 } 1027 1028 1029 template<typename T, Index Log2Dim> 1030 inline 1031 LeafNode<T, Log2Dim>::~LeafNode() 1032 { 1033 } 1034 1035 1036 template<typename T, Index Log2Dim> 1037 inline std::string 1038 LeafNode<T, Log2Dim>::str() const 1039 { 1040 std::ostringstream ostr; 1041 ostr << "LeafNode @" << mOrigin << ": " << mBuffer; 1042 return ostr.str(); 1043 } 1044 1045 1046 //////////////////////////////////////// 1047 1048 1049 template<typename T, Index Log2Dim> 1050 inline Index 1051 LeafNode<T, Log2Dim>::coordToOffset(const Coord& xyz) 1052 { 1053 assert ((xyz[0] & (DIM-1u)) < DIM && (xyz[1] & (DIM-1u)) < DIM && (xyz[2] & (DIM-1u)) < DIM); 1054 return ((xyz[0] & (DIM-1u)) << 2*Log2Dim) 1055 + ((xyz[1] & (DIM-1u)) << Log2Dim) 1056 + (xyz[2] & (DIM-1u)); 1057 } 1058 1059 template<typename T, Index Log2Dim> 1060 inline Coord 1061 LeafNode<T, Log2Dim>::offsetToLocalCoord(Index n) 1062 { 1063 assert(n<(1<< 3*Log2Dim)); 1064 Coord xyz; 1065 xyz.setX(n >> 2*Log2Dim); 1066 n &= ((1<<2*Log2Dim)-1); 1067 xyz.setY(n >> Log2Dim); 1068 xyz.setZ(n & ((1<<Log2Dim)-1)); 1069 return xyz; 1070 } 1071 1072 1073 template<typename T, Index Log2Dim> 1074 inline Coord 1075 LeafNode<T, Log2Dim>::offsetToGlobalCoord(Index n) const 1076 { 1077 return (this->offsetToLocalCoord(n) + this->origin()); 1078 } 1079 1080 1081 //////////////////////////////////////// 1082 1083 1084 template<typename ValueT, Index Log2Dim> 1085 inline const ValueT& 1086 LeafNode<ValueT, Log2Dim>::getValue(const Coord& xyz) const 1087 { 1088 return this->getValue(LeafNode::coordToOffset(xyz)); 1089 } 1090 1091 template<typename ValueT, Index Log2Dim> 1092 inline const ValueT& 1093 LeafNode<ValueT, Log2Dim>::getValue(Index offset) const 1094 { 1095 assert(offset < SIZE); 1096 return mBuffer[offset]; 1097 } 1098 1099 1100 template<typename T, Index Log2Dim> 1101 inline bool 1102 LeafNode<T, Log2Dim>::probeValue(const Coord& xyz, ValueType& val) const 1103 { 1104 return this->probeValue(LeafNode::coordToOffset(xyz), val); 1105 } 1106 1107 template<typename T, Index Log2Dim> 1108 inline bool 1109 LeafNode<T, Log2Dim>::probeValue(Index offset, ValueType& val) const 1110 { 1111 assert(offset < SIZE); 1112 val = mBuffer[offset]; 1113 return mValueMask.isOn(offset); 1114 } 1115 1116 1117 template<typename T, Index Log2Dim> 1118 inline void 1119 LeafNode<T, Log2Dim>::setValueOff(const Coord& xyz, const ValueType& val) 1120 { 1121 this->setValueOff(LeafNode::coordToOffset(xyz), val); 1122 } 1123 1124 template<typename T, Index Log2Dim> 1125 inline void 1126 LeafNode<T, Log2Dim>::setValueOff(Index offset, const ValueType& val) 1127 { 1128 assert(offset < SIZE); 1129 mBuffer.setValue(offset, val); 1130 mValueMask.setOff(offset); 1131 } 1132 1133 1134 template<typename T, Index Log2Dim> 1135 inline void 1136 LeafNode<T, Log2Dim>::setActiveState(const Coord& xyz, bool on) 1137 { 1138 mValueMask.set(this->coordToOffset(xyz), on); 1139 } 1140 1141 1142 template<typename T, Index Log2Dim> 1143 inline void 1144 LeafNode<T, Log2Dim>::setValueOnly(const Coord& xyz, const ValueType& val) 1145 { 1146 this->setValueOnly(LeafNode::coordToOffset(xyz), val); 1147 } 1148 1149 template<typename T, Index Log2Dim> 1150 inline void 1151 LeafNode<T, Log2Dim>::setValueOnly(Index offset, const ValueType& val) 1152 { 1153 assert(offset<SIZE); mBuffer.setValue(offset, val); 1154 } 1155 1156 1157 //////////////////////////////////////// 1158 1159 1160 template<typename T, Index Log2Dim> 1161 inline void 1162 LeafNode<T, Log2Dim>::clip(const CoordBBox& clipBBox, const T& background) 1163 { 1164 CoordBBox nodeBBox = this->getNodeBoundingBox(); 1165 if (!clipBBox.hasOverlap(nodeBBox)) { 1166 // This node lies completely outside the clipping region. Fill it with the background. 1167 this->fill(background, /*active=*/false); 1168 } else if (clipBBox.isInside(nodeBBox)) { 1169 // This node lies completely inside the clipping region. Leave it intact. 1170 return; 1171 } 1172 1173 // This node isn't completely contained inside the clipping region. 1174 // Set any voxels that lie outside the region to the background value. 1175 1176 // Construct a boolean mask that is on inside the clipping region and off outside it. 1177 NodeMaskType mask; 1178 nodeBBox.intersect(clipBBox); 1179 Coord xyz; 1180 int &x = xyz.x(), &y = xyz.y(), &z = xyz.z(); 1181 for (x = nodeBBox.min().x(); x <= nodeBBox.max().x(); ++x) { 1182 for (y = nodeBBox.min().y(); y <= nodeBBox.max().y(); ++y) { 1183 for (z = nodeBBox.min().z(); z <= nodeBBox.max().z(); ++z) { 1184 mask.setOn(static_cast<Index32>(this->coordToOffset(xyz))); 1185 } 1186 } 1187 } 1188 1189 // Set voxels that lie in the inactive region of the mask (i.e., outside 1190 // the clipping region) to the background value. 1191 for (MaskOffIterator maskIter = mask.beginOff(); maskIter; ++maskIter) { 1192 this->setValueOff(maskIter.pos(), background); 1193 } 1194 } 1195 1196 1197 //////////////////////////////////////// 1198 1199 1200 template<typename T, Index Log2Dim> 1201 inline void 1202 LeafNode<T, Log2Dim>::fill(const CoordBBox& bbox, const ValueType& value, bool active) 1203 { 1204 if (!this->allocate()) return; 1205 1206 auto clippedBBox = this->getNodeBoundingBox(); 1207 clippedBBox.intersect(bbox); 1208 if (!clippedBBox) return; 1209 1210 for (Int32 x = clippedBBox.min().x(); x <= clippedBBox.max().x(); ++x) { 1211 const Index offsetX = (x & (DIM-1u)) << 2*Log2Dim; 1212 for (Int32 y = clippedBBox.min().y(); y <= clippedBBox.max().y(); ++y) { 1213 const Index offsetXY = offsetX + ((y & (DIM-1u)) << Log2Dim); 1214 for (Int32 z = clippedBBox.min().z(); z <= clippedBBox.max().z(); ++z) { 1215 const Index offset = offsetXY + (z & (DIM-1u)); 1216 mBuffer[offset] = value; 1217 mValueMask.set(offset, active); 1218 } 1219 } 1220 } 1221 } 1222 1223 template<typename T, Index Log2Dim> 1224 inline void 1225 LeafNode<T, Log2Dim>::fill(const ValueType& value) 1226 { 1227 mBuffer.fill(value); 1228 } 1229 1230 template<typename T, Index Log2Dim> 1231 inline void 1232 LeafNode<T, Log2Dim>::fill(const ValueType& value, bool active) 1233 { 1234 mBuffer.fill(value); 1235 mValueMask.set(active); 1236 } 1237 1238 1239 //////////////////////////////////////// 1240 1241 1242 template<typename T, Index Log2Dim> 1243 template<typename DenseT> 1244 inline void 1245 LeafNode<T, Log2Dim>::copyToDense(const CoordBBox& bbox, DenseT& dense) const 1246 { 1247 mBuffer.loadValues(); 1248 1249 using DenseValueType = typename DenseT::ValueType; 1250 1251 const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); 1252 const Coord& min = dense.bbox().min(); 1253 DenseValueType* t0 = dense.data() + zStride * (bbox.min()[2] - min[2]); // target array 1254 const T* s0 = &mBuffer[bbox.min()[2] & (DIM-1u)]; // source array 1255 for (Int32 x = bbox.min()[0], ex = bbox.max()[0] + 1; x < ex; ++x) { 1256 DenseValueType* t1 = t0 + xStride * (x - min[0]); 1257 const T* s1 = s0 + ((x & (DIM-1u)) << 2*Log2Dim); 1258 for (Int32 y = bbox.min()[1], ey = bbox.max()[1] + 1; y < ey; ++y) { 1259 DenseValueType* t2 = t1 + yStride * (y - min[1]); 1260 const T* s2 = s1 + ((y & (DIM-1u)) << Log2Dim); 1261 for (Int32 z = bbox.min()[2], ez = bbox.max()[2] + 1; z < ez; ++z, t2 += zStride) { 1262 *t2 = DenseValueType(*s2++); 1263 } 1264 } 1265 } 1266 } 1267 1268 1269 template<typename T, Index Log2Dim> 1270 template<typename DenseT> 1271 inline void 1272 LeafNode<T, Log2Dim>::copyFromDense(const CoordBBox& bbox, const DenseT& dense, 1273 const ValueType& background, const ValueType& tolerance) 1274 { 1275 if (!this->allocate()) return; 1276 1277 using DenseValueType = typename DenseT::ValueType; 1278 1279 const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); 1280 const Coord& min = dense.bbox().min(); 1281 1282 const DenseValueType* s0 = dense.data() + zStride * (bbox.min()[2] - min[2]); // source 1283 const Int32 n0 = bbox.min()[2] & (DIM-1u); 1284 for (Int32 x = bbox.min()[0], ex = bbox.max()[0]+1; x < ex; ++x) { 1285 const DenseValueType* s1 = s0 + xStride * (x - min[0]); 1286 const Int32 n1 = n0 + ((x & (DIM-1u)) << 2*LOG2DIM); 1287 for (Int32 y = bbox.min()[1], ey = bbox.max()[1]+1; y < ey; ++y) { 1288 const DenseValueType* s2 = s1 + yStride * (y - min[1]); 1289 Int32 n2 = n1 + ((y & (DIM-1u)) << LOG2DIM); 1290 for (Int32 z = bbox.min()[2], ez = bbox.max()[2]+1; z < ez; ++z, ++n2, s2 += zStride) { 1291 if (math::isApproxEqual(background, ValueType(*s2), tolerance)) { 1292 mValueMask.setOff(n2); 1293 mBuffer[n2] = background; 1294 } else { 1295 mValueMask.setOn(n2); 1296 mBuffer[n2] = ValueType(*s2); 1297 } 1298 } 1299 } 1300 } 1301 } 1302 1303 1304 //////////////////////////////////////// 1305 1306 1307 template<typename T, Index Log2Dim> 1308 inline void 1309 LeafNode<T, Log2Dim>::readTopology(std::istream& is, bool /*fromHalf*/) 1310 { 1311 mValueMask.load(is); 1312 } 1313 1314 1315 template<typename T, Index Log2Dim> 1316 inline void 1317 LeafNode<T, Log2Dim>::writeTopology(std::ostream& os, bool /*toHalf*/) const 1318 { 1319 mValueMask.save(os); 1320 } 1321 1322 1323 //////////////////////////////////////// 1324 1325 1326 1327 template<typename T, Index Log2Dim> 1328 inline void 1329 LeafNode<T,Log2Dim>::skipCompressedValues(bool seekable, std::istream& is, bool fromHalf) 1330 { 1331 if (seekable) { 1332 // Seek over voxel values. 1333 io::readCompressedValues<ValueType, NodeMaskType>( 1334 is, nullptr, SIZE, mValueMask, fromHalf); 1335 } else { 1336 // Read and discard voxel values. 1337 Buffer temp; 1338 io::readCompressedValues(is, temp.mData, SIZE, mValueMask, fromHalf); 1339 } 1340 } 1341 1342 1343 template<typename T, Index Log2Dim> 1344 inline void 1345 LeafNode<T,Log2Dim>::readBuffers(std::istream& is, bool fromHalf) 1346 { 1347 this->readBuffers(is, CoordBBox::inf(), fromHalf); 1348 } 1349 1350 1351 template<typename T, Index Log2Dim> 1352 inline void 1353 LeafNode<T,Log2Dim>::readBuffers(std::istream& is, const CoordBBox& clipBBox, bool fromHalf) 1354 { 1355 SharedPtr<io::StreamMetadata> meta = io::getStreamMetadataPtr(is); 1356 const bool seekable = meta && meta->seekable(); 1357 1358 std::streamoff maskpos = is.tellg(); 1359 1360 if (seekable) { 1361 // Seek over the value mask. 1362 mValueMask.seek(is); 1363 } else { 1364 // Read in the value mask. 1365 mValueMask.load(is); 1366 } 1367 1368 int8_t numBuffers = 1; 1369 if (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION) { 1370 // Read in the origin. 1371 is.read(reinterpret_cast<char*>(&mOrigin), sizeof(Coord::ValueType) * 3); 1372 1373 // Read in the number of buffers, which should now always be one. 1374 is.read(reinterpret_cast<char*>(&numBuffers), sizeof(int8_t)); 1375 } 1376 1377 CoordBBox nodeBBox = this->getNodeBoundingBox(); 1378 if (!clipBBox.hasOverlap(nodeBBox)) { 1379 // This node lies completely outside the clipping region. 1380 skipCompressedValues(seekable, is, fromHalf); 1381 mValueMask.setOff(); 1382 mBuffer.setOutOfCore(false); 1383 } else { 1384 // If this node lies completely inside the clipping region and it is being read 1385 // from a memory-mapped file, delay loading of its buffer until the buffer 1386 // is actually accessed. (If this node requires clipping, its buffer 1387 // must be accessed and therefore must be loaded.) 1388 io::MappedFile::Ptr mappedFile = io::getMappedFilePtr(is); 1389 const bool delayLoad = ((mappedFile.get() != nullptr) && clipBBox.isInside(nodeBBox)); 1390 1391 if (delayLoad) { 1392 mBuffer.setOutOfCore(true); 1393 mBuffer.mFileInfo = new typename Buffer::FileInfo; 1394 mBuffer.mFileInfo->meta = meta; 1395 mBuffer.mFileInfo->bufpos = is.tellg(); 1396 mBuffer.mFileInfo->mapping = mappedFile; 1397 // Save the offset to the value mask, because the in-memory copy 1398 // might change before the value buffer gets read. 1399 mBuffer.mFileInfo->maskpos = maskpos; 1400 // Skip over voxel values. 1401 skipCompressedValues(seekable, is, fromHalf); 1402 } else { 1403 mBuffer.allocate(); 1404 io::readCompressedValues(is, mBuffer.mData, SIZE, mValueMask, fromHalf); 1405 mBuffer.setOutOfCore(false); 1406 1407 // Get this tree's background value. 1408 T background = zeroVal<T>(); 1409 if (const void* bgPtr = io::getGridBackgroundValuePtr(is)) { 1410 background = *static_cast<const T*>(bgPtr); 1411 } 1412 this->clip(clipBBox, background); 1413 } 1414 } 1415 1416 if (numBuffers > 1) { 1417 // Read in and discard auxiliary buffers that were created with earlier 1418 // versions of the library. (Auxiliary buffers are not mask compressed.) 1419 const bool zipped = io::getDataCompression(is) & io::COMPRESS_ZIP; 1420 Buffer temp; 1421 for (int i = 1; i < numBuffers; ++i) { 1422 if (fromHalf) { 1423 io::HalfReader<io::RealToHalf<T>::isReal, T>::read(is, temp.mData, SIZE, zipped); 1424 } else { 1425 io::readData<T>(is, temp.mData, SIZE, zipped); 1426 } 1427 } 1428 } 1429 1430 // increment the leaf number 1431 if (meta) meta->setLeaf(meta->leaf() + 1); 1432 } 1433 1434 1435 template<typename T, Index Log2Dim> 1436 inline void 1437 LeafNode<T, Log2Dim>::writeBuffers(std::ostream& os, bool toHalf) const 1438 { 1439 // Write out the value mask. 1440 mValueMask.save(os); 1441 1442 mBuffer.loadValues(); 1443 1444 io::writeCompressedValues(os, mBuffer.mData, SIZE, 1445 mValueMask, /*childMask=*/NodeMaskType(), toHalf); 1446 } 1447 1448 1449 //////////////////////////////////////// 1450 1451 1452 template<typename T, Index Log2Dim> 1453 inline bool 1454 LeafNode<T, Log2Dim>::operator==(const LeafNode& other) const 1455 { 1456 return mOrigin == other.mOrigin && 1457 mValueMask == other.valueMask() && 1458 mBuffer == other.mBuffer; 1459 } 1460 1461 1462 template<typename T, Index Log2Dim> 1463 inline Index64 1464 LeafNode<T, Log2Dim>::memUsage() const 1465 { 1466 // Use sizeof(*this) to capture alignment-related padding 1467 // (but note that sizeof(*this) includes sizeof(mBuffer)). 1468 return sizeof(*this) + mBuffer.memUsage() - sizeof(mBuffer); 1469 } 1470 1471 1472 template<typename T, Index Log2Dim> 1473 inline void 1474 LeafNode<T, Log2Dim>::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const 1475 { 1476 CoordBBox this_bbox = this->getNodeBoundingBox(); 1477 if (bbox.isInside(this_bbox)) return;//this LeafNode is already enclosed in the bbox 1478 if (ValueOnCIter iter = this->cbeginValueOn()) {//any active values? 1479 if (visitVoxels) {//use voxel granularity? 1480 this_bbox.reset(); 1481 for(; iter; ++iter) this_bbox.expand(this->offsetToLocalCoord(iter.pos())); 1482 this_bbox.translate(this->origin()); 1483 } 1484 bbox.expand(this_bbox); 1485 } 1486 } 1487 1488 1489 template<typename T, Index Log2Dim> 1490 template<typename OtherType, Index OtherLog2Dim> 1491 inline bool 1492 LeafNode<T, Log2Dim>::hasSameTopology(const LeafNode<OtherType, OtherLog2Dim>* other) const 1493 { 1494 assert(other); 1495 return (Log2Dim == OtherLog2Dim && mValueMask == other->getValueMask()); 1496 } 1497 1498 template<typename T, Index Log2Dim> 1499 inline bool 1500 LeafNode<T, Log2Dim>::isConstant(ValueType& firstValue, 1501 bool& state, 1502 const ValueType& tolerance) const 1503 { 1504 if (!mValueMask.isConstant(state)) return false;// early termination 1505 firstValue = mBuffer[0]; 1506 for (Index i = 1; i < SIZE; ++i) { 1507 if ( !math::isApproxEqual(mBuffer[i], firstValue, tolerance) ) return false;// early termination 1508 } 1509 return true; 1510 } 1511 1512 template<typename T, Index Log2Dim> 1513 inline bool 1514 LeafNode<T, Log2Dim>::isConstant(ValueType& minValue, 1515 ValueType& maxValue, 1516 bool& state, 1517 const ValueType& tolerance) const 1518 { 1519 if (!mValueMask.isConstant(state)) return false;// early termination 1520 minValue = maxValue = mBuffer[0]; 1521 for (Index i = 1; i < SIZE; ++i) { 1522 const T& v = mBuffer[i]; 1523 if (v < minValue) { 1524 if ((maxValue - v) > tolerance) return false;// early termination 1525 minValue = v; 1526 } else if (v > maxValue) { 1527 if ((v - minValue) > tolerance) return false;// early termination 1528 maxValue = v; 1529 } 1530 } 1531 return true; 1532 } 1533 1534 template<typename T, Index Log2Dim> 1535 inline T 1536 LeafNode<T, Log2Dim>::medianAll(T *tmp) const 1537 { 1538 std::unique_ptr<T[]> data(nullptr); 1539 if (tmp == nullptr) {//allocate temporary storage 1540 data.reset(new T[NUM_VALUES]); 1541 tmp = data.get(); 1542 } 1543 if (tmp != mBuffer.data()) { 1544 const T* src = mBuffer.data(); 1545 for (T* dst = tmp; dst-tmp < NUM_VALUES;) *dst++ = *src++; 1546 } 1547 static const size_t midpoint = (NUM_VALUES - 1) >> 1; 1548 std::nth_element(tmp, tmp + midpoint, tmp + NUM_VALUES); 1549 return tmp[midpoint]; 1550 } 1551 1552 template<typename T, Index Log2Dim> 1553 inline Index 1554 LeafNode<T, Log2Dim>::medianOn(T &value, T *tmp) const 1555 { 1556 const Index count = mValueMask.countOn(); 1557 if (count == NUM_VALUES) {//special case: all voxels are active 1558 value = this->medianAll(tmp); 1559 return NUM_VALUES; 1560 } else if (count == 0) { 1561 return 0; 1562 } 1563 std::unique_ptr<T[]> data(nullptr); 1564 if (tmp == nullptr) {//allocate temporary storage 1565 data.reset(new T[count]);// 0 < count < NUM_VALUES 1566 tmp = data.get(); 1567 } 1568 for (auto iter=this->cbeginValueOn(); iter; ++iter) *tmp++ = *iter; 1569 T *begin = tmp - count; 1570 const size_t midpoint = (count - 1) >> 1; 1571 std::nth_element(begin, begin + midpoint, tmp); 1572 value = begin[midpoint]; 1573 return count; 1574 } 1575 1576 template<typename T, Index Log2Dim> 1577 inline Index 1578 LeafNode<T, Log2Dim>::medianOff(T &value, T *tmp) const 1579 { 1580 const Index count = mValueMask.countOff(); 1581 if (count == NUM_VALUES) {//special case: all voxels are inactive 1582 value = this->medianAll(tmp); 1583 return NUM_VALUES; 1584 } else if (count == 0) { 1585 return 0; 1586 } 1587 std::unique_ptr<T[]> data(nullptr); 1588 if (tmp == nullptr) {//allocate temporary storage 1589 data.reset(new T[count]);// 0 < count < NUM_VALUES 1590 tmp = data.get(); 1591 } 1592 for (auto iter=this->cbeginValueOff(); iter; ++iter) *tmp++ = *iter; 1593 T *begin = tmp - count; 1594 const size_t midpoint = (count - 1) >> 1; 1595 std::nth_element(begin, begin + midpoint, tmp); 1596 value = begin[midpoint]; 1597 return count; 1598 } 1599 1600 //////////////////////////////////////// 1601 1602 1603 template<typename T, Index Log2Dim> 1604 inline void 1605 LeafNode<T, Log2Dim>::addTile(Index /*level*/, const Coord& xyz, const ValueType& val, bool active) 1606 { 1607 this->addTile(this->coordToOffset(xyz), val, active); 1608 } 1609 1610 template<typename T, Index Log2Dim> 1611 inline void 1612 LeafNode<T, Log2Dim>::addTile(Index offset, const ValueType& val, bool active) 1613 { 1614 assert(offset < SIZE); 1615 setValueOnly(offset, val); 1616 setActiveState(offset, active); 1617 } 1618 1619 template<typename T, Index Log2Dim> 1620 template<typename AccessorT> 1621 inline void 1622 LeafNode<T, Log2Dim>::addTileAndCache(Index level, const Coord& xyz, 1623 const ValueType& val, bool active, AccessorT&) 1624 { 1625 this->addTile(level, xyz, val, active); 1626 } 1627 1628 1629 //////////////////////////////////////// 1630 1631 1632 template<typename T, Index Log2Dim> 1633 inline void 1634 LeafNode<T, Log2Dim>::resetBackground(const ValueType& oldBackground, 1635 const ValueType& newBackground) 1636 { 1637 if (!this->allocate()) return; 1638 1639 typename NodeMaskType::OffIterator iter; 1640 // For all inactive values... 1641 for (iter = this->mValueMask.beginOff(); iter; ++iter) { 1642 ValueType &inactiveValue = mBuffer[iter.pos()]; 1643 if (math::isApproxEqual(inactiveValue, oldBackground)) { 1644 inactiveValue = newBackground; 1645 } else if (math::isApproxEqual(inactiveValue, math::negative(oldBackground))) { 1646 inactiveValue = math::negative(newBackground); 1647 } 1648 } 1649 } 1650 1651 1652 template<typename T, Index Log2Dim> 1653 template<MergePolicy Policy> 1654 inline void 1655 LeafNode<T, Log2Dim>::merge(const LeafNode& other) 1656 { 1657 if (!this->allocate()) return; 1658 1659 OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN 1660 if (Policy == MERGE_NODES) return; 1661 typename NodeMaskType::OnIterator iter = other.valueMask().beginOn(); 1662 for (; iter; ++iter) { 1663 const Index n = iter.pos(); 1664 if (mValueMask.isOff(n)) { 1665 mBuffer[n] = other.mBuffer[n]; 1666 mValueMask.setOn(n); 1667 } 1668 } 1669 OPENVDB_NO_UNREACHABLE_CODE_WARNING_END 1670 } 1671 1672 template<typename T, Index Log2Dim> 1673 template<MergePolicy Policy> 1674 inline void 1675 LeafNode<T, Log2Dim>::merge(const LeafNode& other, 1676 const ValueType& /*bg*/, const ValueType& /*otherBG*/) 1677 { 1678 this->template merge<Policy>(other); 1679 } 1680 1681 template<typename T, Index Log2Dim> 1682 template<MergePolicy Policy> 1683 inline void 1684 LeafNode<T, Log2Dim>::merge(const ValueType& tileValue, bool tileActive) 1685 { 1686 if (!this->allocate()) return; 1687 1688 OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN 1689 if (Policy != MERGE_ACTIVE_STATES_AND_NODES) return; 1690 if (!tileActive) return; 1691 // Replace all inactive values with the active tile value. 1692 for (typename NodeMaskType::OffIterator iter = mValueMask.beginOff(); iter; ++iter) { 1693 const Index n = iter.pos(); 1694 mBuffer[n] = tileValue; 1695 mValueMask.setOn(n); 1696 } 1697 OPENVDB_NO_UNREACHABLE_CODE_WARNING_END 1698 } 1699 1700 1701 template<typename T, Index Log2Dim> 1702 template<typename OtherType> 1703 inline void 1704 LeafNode<T, Log2Dim>::topologyUnion(const LeafNode<OtherType, Log2Dim>& other, bool) 1705 { 1706 mValueMask |= other.valueMask(); 1707 } 1708 1709 template<typename T, Index Log2Dim> 1710 template<typename OtherType> 1711 inline void 1712 LeafNode<T, Log2Dim>::topologyIntersection(const LeafNode<OtherType, Log2Dim>& other, 1713 const ValueType&) 1714 { 1715 mValueMask &= other.valueMask(); 1716 } 1717 1718 template<typename T, Index Log2Dim> 1719 template<typename OtherType> 1720 inline void 1721 LeafNode<T, Log2Dim>::topologyDifference(const LeafNode<OtherType, Log2Dim>& other, 1722 const ValueType&) 1723 { 1724 mValueMask &= !other.valueMask(); 1725 } 1726 1727 template<typename T, Index Log2Dim> 1728 inline void 1729 LeafNode<T, Log2Dim>::negate() 1730 { 1731 if (!this->allocate()) return; 1732 1733 for (Index i = 0; i < SIZE; ++i) { 1734 mBuffer[i] = -mBuffer[i]; 1735 } 1736 } 1737 1738 1739 //////////////////////////////////////// 1740 1741 1742 template<typename T, Index Log2Dim> 1743 template<typename CombineOp> 1744 inline void 1745 LeafNode<T, Log2Dim>::combine(const LeafNode& other, CombineOp& op) 1746 { 1747 if (!this->allocate()) return; 1748 1749 CombineArgs<T> args; 1750 for (Index i = 0; i < SIZE; ++i) { 1751 op(args.setARef(mBuffer[i]) 1752 .setAIsActive(mValueMask.isOn(i)) 1753 .setBRef(other.mBuffer[i]) 1754 .setBIsActive(other.valueMask().isOn(i)) 1755 .setResultRef(mBuffer[i])); 1756 mValueMask.set(i, args.resultIsActive()); 1757 } 1758 } 1759 1760 1761 template<typename T, Index Log2Dim> 1762 template<typename CombineOp> 1763 inline void 1764 LeafNode<T, Log2Dim>::combine(const ValueType& value, bool valueIsActive, CombineOp& op) 1765 { 1766 if (!this->allocate()) return; 1767 1768 CombineArgs<T> args; 1769 args.setBRef(value).setBIsActive(valueIsActive); 1770 for (Index i = 0; i < SIZE; ++i) { 1771 op(args.setARef(mBuffer[i]) 1772 .setAIsActive(mValueMask.isOn(i)) 1773 .setResultRef(mBuffer[i])); 1774 mValueMask.set(i, args.resultIsActive()); 1775 } 1776 } 1777 1778 1779 //////////////////////////////////////// 1780 1781 1782 template<typename T, Index Log2Dim> 1783 template<typename CombineOp, typename OtherType> 1784 inline void 1785 LeafNode<T, Log2Dim>::combine2(const LeafNode& other, const OtherType& value, 1786 bool valueIsActive, CombineOp& op) 1787 { 1788 if (!this->allocate()) return; 1789 1790 CombineArgs<T, OtherType> args; 1791 args.setBRef(value).setBIsActive(valueIsActive); 1792 for (Index i = 0; i < SIZE; ++i) { 1793 op(args.setARef(other.mBuffer[i]) 1794 .setAIsActive(other.valueMask().isOn(i)) 1795 .setResultRef(mBuffer[i])); 1796 mValueMask.set(i, args.resultIsActive()); 1797 } 1798 } 1799 1800 1801 template<typename T, Index Log2Dim> 1802 template<typename CombineOp, typename OtherNodeT> 1803 inline void 1804 LeafNode<T, Log2Dim>::combine2(const ValueType& value, const OtherNodeT& other, 1805 bool valueIsActive, CombineOp& op) 1806 { 1807 if (!this->allocate()) return; 1808 1809 CombineArgs<T, typename OtherNodeT::ValueType> args; 1810 args.setARef(value).setAIsActive(valueIsActive); 1811 for (Index i = 0; i < SIZE; ++i) { 1812 op(args.setBRef(other.mBuffer[i]) 1813 .setBIsActive(other.valueMask().isOn(i)) 1814 .setResultRef(mBuffer[i])); 1815 mValueMask.set(i, args.resultIsActive()); 1816 } 1817 } 1818 1819 1820 template<typename T, Index Log2Dim> 1821 template<typename CombineOp, typename OtherNodeT> 1822 inline void 1823 LeafNode<T, Log2Dim>::combine2(const LeafNode& b0, const OtherNodeT& b1, CombineOp& op) 1824 { 1825 if (!this->allocate()) return; 1826 1827 CombineArgs<T, typename OtherNodeT::ValueType> args; 1828 for (Index i = 0; i < SIZE; ++i) { 1829 mValueMask.set(i, b0.valueMask().isOn(i) || b1.valueMask().isOn(i)); 1830 op(args.setARef(b0.mBuffer[i]) 1831 .setAIsActive(b0.valueMask().isOn(i)) 1832 .setBRef(b1.mBuffer[i]) 1833 .setBIsActive(b1.valueMask().isOn(i)) 1834 .setResultRef(mBuffer[i])); 1835 mValueMask.set(i, args.resultIsActive()); 1836 } 1837 } 1838 1839 1840 //////////////////////////////////////// 1841 1842 1843 template<typename T, Index Log2Dim> 1844 template<typename BBoxOp> 1845 inline void 1846 LeafNode<T, Log2Dim>::visitActiveBBox(BBoxOp& op) const 1847 { 1848 if (op.template descent<LEVEL>()) { 1849 for (ValueOnCIter i=this->cbeginValueOn(); i; ++i) { 1850 op.template operator()<LEVEL>(CoordBBox::createCube(i.getCoord(), 1)); 1851 } 1852 } else { 1853 op.template operator()<LEVEL>(this->getNodeBoundingBox()); 1854 } 1855 } 1856 1857 1858 template<typename T, Index Log2Dim> 1859 template<typename VisitorOp> 1860 inline void 1861 LeafNode<T, Log2Dim>::visit(VisitorOp& op) 1862 { 1863 doVisit<LeafNode, VisitorOp, ChildAllIter>(*this, op); 1864 } 1865 1866 1867 template<typename T, Index Log2Dim> 1868 template<typename VisitorOp> 1869 inline void 1870 LeafNode<T, Log2Dim>::visit(VisitorOp& op) const 1871 { 1872 doVisit<const LeafNode, VisitorOp, ChildAllCIter>(*this, op); 1873 } 1874 1875 1876 template<typename T, Index Log2Dim> 1877 template<typename NodeT, typename VisitorOp, typename ChildAllIterT> 1878 inline void 1879 LeafNode<T, Log2Dim>::doVisit(NodeT& self, VisitorOp& op) 1880 { 1881 for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { 1882 op(iter); 1883 } 1884 } 1885 1886 1887 //////////////////////////////////////// 1888 1889 1890 template<typename T, Index Log2Dim> 1891 template<typename OtherLeafNodeType, typename VisitorOp> 1892 inline void 1893 LeafNode<T, Log2Dim>::visit2Node(OtherLeafNodeType& other, VisitorOp& op) 1894 { 1895 doVisit2Node<LeafNode, OtherLeafNodeType, VisitorOp, ChildAllIter, 1896 typename OtherLeafNodeType::ChildAllIter>(*this, other, op); 1897 } 1898 1899 1900 template<typename T, Index Log2Dim> 1901 template<typename OtherLeafNodeType, typename VisitorOp> 1902 inline void 1903 LeafNode<T, Log2Dim>::visit2Node(OtherLeafNodeType& other, VisitorOp& op) const 1904 { 1905 doVisit2Node<const LeafNode, OtherLeafNodeType, VisitorOp, ChildAllCIter, 1906 typename OtherLeafNodeType::ChildAllCIter>(*this, other, op); 1907 } 1908 1909 1910 template<typename T, Index Log2Dim> 1911 template< 1912 typename NodeT, 1913 typename OtherNodeT, 1914 typename VisitorOp, 1915 typename ChildAllIterT, 1916 typename OtherChildAllIterT> 1917 inline void 1918 LeafNode<T, Log2Dim>::doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp& op) 1919 { 1920 // Allow the two nodes to have different ValueTypes, but not different dimensions. 1921 static_assert(OtherNodeT::SIZE == NodeT::SIZE, 1922 "can't visit nodes of different sizes simultaneously"); 1923 static_assert(OtherNodeT::LEVEL == NodeT::LEVEL, 1924 "can't visit nodes at different tree levels simultaneously"); 1925 1926 ChildAllIterT iter = self.beginChildAll(); 1927 OtherChildAllIterT otherIter = other.beginChildAll(); 1928 1929 for ( ; iter && otherIter; ++iter, ++otherIter) { 1930 op(iter, otherIter); 1931 } 1932 } 1933 1934 1935 //////////////////////////////////////// 1936 1937 1938 template<typename T, Index Log2Dim> 1939 template<typename IterT, typename VisitorOp> 1940 inline void 1941 LeafNode<T, Log2Dim>::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) 1942 { 1943 doVisit2<LeafNode, VisitorOp, ChildAllIter, IterT>( 1944 *this, otherIter, op, otherIsLHS); 1945 } 1946 1947 1948 template<typename T, Index Log2Dim> 1949 template<typename IterT, typename VisitorOp> 1950 inline void 1951 LeafNode<T, Log2Dim>::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) const 1952 { 1953 doVisit2<const LeafNode, VisitorOp, ChildAllCIter, IterT>( 1954 *this, otherIter, op, otherIsLHS); 1955 } 1956 1957 1958 template<typename T, Index Log2Dim> 1959 template< 1960 typename NodeT, 1961 typename VisitorOp, 1962 typename ChildAllIterT, 1963 typename OtherChildAllIterT> 1964 inline void 1965 LeafNode<T, Log2Dim>::doVisit2(NodeT& self, OtherChildAllIterT& otherIter, 1966 VisitorOp& op, bool otherIsLHS) 1967 { 1968 if (!otherIter) return; 1969 1970 if (otherIsLHS) { 1971 for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { 1972 op(otherIter, iter); 1973 } 1974 } else { 1975 for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { 1976 op(iter, otherIter); 1977 } 1978 } 1979 } 1980 1981 1982 //////////////////////////////////////// 1983 1984 1985 template<typename T, Index Log2Dim> 1986 inline std::ostream& 1987 operator<<(std::ostream& os, const typename LeafNode<T, Log2Dim>::Buffer& buf) 1988 { 1989 for (Index32 i = 0, N = buf.size(); i < N; ++i) os << buf.mData[i] << ", "; 1990 return os; 1991 } 1992 1993 } // namespace tree 1994 } // namespace OPENVDB_VERSION_NAME 1995 } // namespace openvdb 1996 1997 1998 //////////////////////////////////////// 1999 2000 2001 // Specialization for LeafNodes of type bool 2002 #include "LeafNodeBool.h" 2003 2004 // Specialization for LeafNodes with mask information only 2005 #include "LeafNodeMask.h" 2006 2007 #endif // OPENVDB_TREE_LEAFNODE_HAS_BEEN_INCLUDED 2008