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