OpenVDB 13.0.1
Loading...
Searching...
No Matches
InternalNode.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-2.0
3//
4/// @file InternalNode.h
5///
6/// @brief Internal table nodes for OpenVDB trees
7
8#ifndef OPENVDB_TREE_INTERNALNODE_HAS_BEEN_INCLUDED
9#define OPENVDB_TREE_INTERNALNODE_HAS_BEEN_INCLUDED
10
11#include <openvdb/Platform.h>
13#include <openvdb/util/Assert.h>
14#include <openvdb/io/Compression.h> // for io::readCompressedValues(), etc.
15#include <openvdb/math/Math.h> // for math::isExactlyEqual(), etc.
16#include <openvdb/version.h>
17#include <openvdb/Types.h>
18#include "Iterator.h"
19#include "NodeUnion.h"
20#include <tbb/parallel_for.h>
21#include <memory>
22#include <type_traits>
23
24
25namespace openvdb {
27namespace OPENVDB_VERSION_NAME {
28namespace tree {
29
30template<typename, Index, typename> struct SameInternalConfig; // forward declaration
31
32
33template<typename _ChildNodeType, Index Log2Dim>
34class InternalNode
35{
36public:
37 using ChildNodeType = _ChildNodeType;
38 using LeafNodeType = typename ChildNodeType::LeafNodeType;
39 using ValueType = typename ChildNodeType::ValueType;
40 using BuildType = typename ChildNodeType::BuildType;
41 using UnionType = NodeUnion<ValueType, ChildNodeType>;
42 using NodeMaskType = util::NodeMask<Log2Dim>;
43
44 static const Index
45 LOG2DIM = Log2Dim, // log2 of tile count in one dimension
46 TOTAL = Log2Dim + ChildNodeType::TOTAL, // log2 of voxel count in one dimension
47 DIM = 1 << TOTAL, // total voxel count in one dimension
48 NUM_VALUES = 1 << (3 * Log2Dim), // total voxel count represented by this node
49 LEVEL = 1 + ChildNodeType::LEVEL; // level 0 = leaf
50 static const Index64
51 NUM_VOXELS = uint64_t(1) << (3 * TOTAL); // total voxel count represented by this node
52
53 /// @brief ValueConverter<T>::Type is the type of an InternalNode having the same
54 /// child hierarchy and dimensions as this node but a different value type, T.
55 template<typename OtherValueType>
56 struct ValueConverter {
57 using Type = InternalNode<typename ChildNodeType::template ValueConverter<
58 OtherValueType>::Type, Log2Dim>;
59 };
60
61 /// @brief SameConfiguration<OtherNodeType>::value is @c true if and only if OtherNodeType
62 /// is the type of an InternalNode with the same dimensions as this node and whose
63 /// ChildNodeType has the same configuration as this node's ChildNodeType.
64 template<typename OtherNodeType>
65 struct SameConfiguration {
66 static const bool value =
67 SameInternalConfig<ChildNodeType, Log2Dim, OtherNodeType>::value;
68 };
69
70
71 /// @brief Default constructor
72 /// @warning The resulting InternalNode is uninitialized
73 InternalNode() {}
74
75 /// @brief Constructor of an InternalNode with dense inactive tiles of the specified value.
76 /// @param offValue Background value used for inactive values
77 explicit InternalNode(const ValueType& offValue);
78
79 /// @brief Constructs an InternalNode with dense tiles
80 /// @param origin The location in index space of the fist tile value
81 /// @param fillValue Value assigned to all the tiles
82 /// @param active State assigned to all the tiles
83 InternalNode(const Coord& origin, const ValueType& fillValue, bool active = false);
84
85 InternalNode(PartialCreate, const Coord&, const ValueType& fillValue, bool active = false);
86
87 /// @brief Deep copy constructor
88 ///
89 /// @note This method is multi-threaded!
90 InternalNode(const InternalNode&);
91
92 /// @brief Value conversion copy constructor
93 ///
94 /// @note This method is multi-threaded!
95 template<typename OtherChildNodeType>
96 explicit InternalNode(const InternalNode<OtherChildNodeType, Log2Dim>& other);
97
98 /// @brief Topology copy constructor
99 ///
100 /// @note This method is multi-threaded!
101 template<typename OtherChildNodeType>
102 InternalNode(const InternalNode<OtherChildNodeType, Log2Dim>& other,
103 const ValueType& background, TopologyCopy);
104
105 /// @brief Topology copy constructor
106 ///
107 /// @note This method is multi-threaded!
108 template<typename OtherChildNodeType>
109 InternalNode(const InternalNode<OtherChildNodeType, Log2Dim>& other,
110 const ValueType& offValue, const ValueType& onValue, TopologyCopy);
111
112 ~InternalNode();
113
114protected:
115 using MaskOnIterator = typename NodeMaskType::OnIterator;
116 using MaskOffIterator = typename NodeMaskType::OffIterator;
117 using MaskDenseIterator = typename NodeMaskType::DenseIterator;
118
119 // Type tags to disambiguate template instantiations
120 struct ValueOn {}; struct ValueOff {}; struct ValueAll {};
121 struct ChildOn {}; struct ChildOff {}; struct ChildAll {};
122
123 // The following class templates implement the iterator interfaces specified in Iterator.h
124 // by providing getItem(), setItem() and/or modifyItem() methods.
125
126 // Sparse iterator that visits child nodes of an InternalNode
127 template<typename NodeT, typename ChildT, typename MaskIterT, typename TagT>
128 struct ChildIter: public SparseIteratorBase<
129 MaskIterT, ChildIter<NodeT, ChildT, MaskIterT, TagT>, NodeT, ChildT>
130 {
131 ChildIter() {}
132 ChildIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase<
133 MaskIterT, ChildIter<NodeT, ChildT, MaskIterT, TagT>, NodeT, ChildT>(iter, parent) {}
134
135 ChildT& getItem(Index pos) const
136 {
137 OPENVDB_ASSERT(this->parent().isChildMaskOn(pos));
138 return *(this->parent().getChildNode(pos));
139 }
140
141 // Note: setItem() can't be called on const iterators.
142 void setItem(Index pos, const ChildT& c) const { this->parent().resetChildNode(pos, &c); }
143
144 // Note: modifyItem() isn't implemented, since it's not useful for child node pointers.
145 };// ChildIter
146
147 // Sparse iterator that visits tile values of an InternalNode
148 template<typename NodeT, typename ValueT, typename MaskIterT, typename TagT>
149 struct ValueIter: public SparseIteratorBase<
150 MaskIterT, ValueIter<NodeT, ValueT, MaskIterT, TagT>, NodeT, ValueT>
151 {
152 ValueIter() {}
153 ValueIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase<
154 MaskIterT, ValueIter<NodeT, ValueT, MaskIterT, TagT>, NodeT, ValueT>(iter, parent) {}
155
156 const ValueT& getItem(Index pos) const { return this->parent().mNodes[pos].getValue(); }
157
158 // Note: setItem() can't be called on const iterators.
159 void setItem(Index pos, const ValueT& v) const { this->parent().mNodes[pos].setValue(v); }
160
161 // Note: modifyItem() can't be called on const iterators.
162 template<typename ModifyOp>
163 void modifyItem(Index pos, const ModifyOp& op) const
164 {
165 op(this->parent().mNodes[pos].getValue());
166 }
167 };// ValueIter
168
169 // Dense iterator that visits both tiles and child nodes of an InternalNode
170 template<typename NodeT, typename ChildT, typename ValueT, typename TagT>
171 struct DenseIter: public DenseIteratorBase<
172 MaskDenseIterator, DenseIter<NodeT, ChildT, ValueT, TagT>, NodeT, ChildT, ValueT>
173 {
174 using BaseT = DenseIteratorBase<MaskDenseIterator, DenseIter, NodeT, ChildT, ValueT>;
175 using NonConstValueT = typename BaseT::NonConstValueType;
176
177 DenseIter() {}
178 DenseIter(const MaskDenseIterator& iter, NodeT* parent):
179 DenseIteratorBase<MaskDenseIterator, DenseIter, NodeT, ChildT, ValueT>(iter, parent) {}
180
181 bool getItem(Index pos, ChildT*& child, NonConstValueT& value) const
182 {
183 if (this->parent().isChildMaskOn(pos)) {
184 child = this->parent().getChildNode(pos);
185 return true;
186 }
187 child = nullptr;
188 value = this->parent().mNodes[pos].getValue();
189 return false;
190 }
191
192 // Note: setItem() can't be called on const iterators.
193 void setItem(Index pos, ChildT* child) const
194 {
195 this->parent().resetChildNode(pos, child);
196 }
197
198 // Note: unsetItem() can't be called on const iterators.
199 void unsetItem(Index pos, const ValueT& value) const
200 {
201 this->parent().unsetChildNode(pos, value);
202 }
203 };// DenseIter
204
205public:
206 // Iterators (see Iterator.h for usage)
207 using ChildOnIter = ChildIter<InternalNode, ChildNodeType, MaskOnIterator, ChildOn>;
208 using ChildOnCIter = ChildIter<const InternalNode,const ChildNodeType,MaskOnIterator,ChildOn>;
209 using ChildOffIter = ValueIter<InternalNode, const ValueType, MaskOffIterator, ChildOff>;
210 using ChildOffCIter = ValueIter<const InternalNode,const ValueType,MaskOffIterator,ChildOff>;
211 using ChildAllIter = DenseIter<InternalNode, ChildNodeType, ValueType, ChildAll>;
212 using ChildAllCIter = DenseIter<const InternalNode,const ChildNodeType, ValueType, ChildAll>;
213
214 using ValueOnIter = ValueIter<InternalNode, const ValueType, MaskOnIterator, ValueOn>;
215 using ValueOnCIter = ValueIter<const InternalNode, const ValueType, MaskOnIterator, ValueOn>;
216 using ValueOffIter = ValueIter<InternalNode, const ValueType, MaskOffIterator, ValueOff>;
217 using ValueOffCIter = ValueIter<const InternalNode,const ValueType,MaskOffIterator,ValueOff>;
218 using ValueAllIter = ValueIter<InternalNode, const ValueType, MaskOffIterator, ValueAll>;
219 using ValueAllCIter = ValueIter<const InternalNode,const ValueType,MaskOffIterator,ValueAll>;
220
221 ChildOnCIter cbeginChildOn() const { return ChildOnCIter(mChildMask.beginOn(), this); }
222 ChildOffCIter cbeginChildOff() const { return ChildOffCIter(mChildMask.beginOff(), this); }
223 ChildAllCIter cbeginChildAll() const { return ChildAllCIter(mChildMask.beginDense(), this); }
224 ChildOnCIter beginChildOn() const { return cbeginChildOn(); }
225 ChildOffCIter beginChildOff() const { return cbeginChildOff(); }
226 ChildAllCIter beginChildAll() const { return cbeginChildAll(); }
227 ChildOnIter beginChildOn() { return ChildOnIter(mChildMask.beginOn(), this); }
228 ChildOffIter beginChildOff() { return ChildOffIter(mChildMask.beginOff(), this); }
229 ChildAllIter beginChildAll() { return ChildAllIter(mChildMask.beginDense(), this); }
230
231 ValueOnCIter cbeginValueOn() const { return ValueOnCIter(mValueMask.beginOn(), this); }
232 /// @warning This iterator will also visit child nodes so use isChildMaskOn to skip them!
233 ValueOffCIter cbeginValueOff() const { return ValueOffCIter(mValueMask.beginOff(), this); }
234 ValueAllCIter cbeginValueAll() const { return ValueAllCIter(mChildMask.beginOff(), this); }
235 ValueOnCIter beginValueOn() const { return cbeginValueOn(); }
236 /// @warning This iterator will also visit child nodes so use isChildMaskOn to skip them!
237 ValueOffCIter beginValueOff() const { return cbeginValueOff(); }
238 ValueAllCIter beginValueAll() const { return cbeginValueAll(); }
239 ValueOnIter beginValueOn() { return ValueOnIter(mValueMask.beginOn(), this); }
240 /// @warning This iterator will also visit child nodes so use isChildMaskOn to skip them!
241 ValueOffIter beginValueOff() { return ValueOffIter(mValueMask.beginOff(), this); }
242 ValueAllIter beginValueAll() { return ValueAllIter(mChildMask.beginOff(), this); }
243
244
245 /// @return The dimension of this InternalNode
246 /// @details The number of voxels in one coordinate direction covered by this node
247 static Index dim() { return DIM; }
248 /// @return The level of this node
249 /// @details Level 0 is by definition the level of the leaf nodes
250 static Index getLevel() { return LEVEL; }
251 /// @brief Populated an std::vector with the dimension of all the
252 /// nodes in the branch starting with this node.
253 static void getNodeLog2Dims(std::vector<Index>& dims);
254 /// @return The dimension of the child nodes of this node.
255 /// @details The number of voxels in one coordinate direction
256 /// covered by a child node of this node.
257 static Index getChildDim() { return ChildNodeType::DIM; }
258
259 /// Return the linear table offset of the given global or local coordinates.
260 static Index coordToOffset(const Coord& xyz);
261 /// @brief Return the local coordinates for a linear table offset,
262 /// where offset 0 has coordinates (0, 0, 0).
263 static void offsetToLocalCoord(Index n, Coord& xyz);
264 /// Return the global coordinates for a linear table offset.
265 Coord offsetToGlobalCoord(Index n) const;
266
267 /// Return the grid index coordinates of this node's local origin.
268 const Coord& origin() const { return mOrigin; }
269 /// Set the grid index coordinates of this node's local origin.
270 void setOrigin(const Coord& origin) { mOrigin = origin; }
271
272 /// Return the transient data value.
273 Index32 transientData() const { return mTransientData; }
274 /// Set the transient data value.
275 void setTransientData(Index32 transientData) { mTransientData = transientData; }
276
277 Index64 leafCount() const;
278 Index64 nonLeafCount() const;
279 void nodeCount(std::vector<Index64> &vec) const;
280 OPENVDB_DEPRECATED_MESSAGE("Use input type std::vector<Index64> for nodeCount.")
281 void nodeCount(std::vector<Index32> &vec) const;
282 Index32 childCount() const;
283 Index64 onVoxelCount() const;
284 Index64 offVoxelCount() const;
285 Index64 onLeafVoxelCount() const;
286 Index64 offLeafVoxelCount() const;
287 Index64 onTileCount() const;
288
289 /// Return the total amount of memory in bytes occupied by this node and its children.
290 Index64 memUsage() const;
291
292 /// @brief Expand the specified bounding box so that it includes the active tiles
293 /// of this internal node as well as all the active values in its child nodes.
294 /// If visitVoxels is false LeafNodes will be approximated as dense, i.e. with all
295 /// voxels active. Else the individual active voxels are visited to produce a tight bbox.
296 void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) const;
297
298 /// @brief Return the bounding box of this node, i.e., the full index space
299 /// spanned by the node regardless of its content.
300 CoordBBox getNodeBoundingBox() const { return CoordBBox::createCube(mOrigin, DIM); }
301
302 /// @return True if this node contains no child nodes.
303 bool isEmpty() const { return mChildMask.isOff(); }
304
305 /// Return @c true if all of this node's table entries have the same active state
306 /// and the same constant value to within the given tolerance,
307 /// and return that value in @a firstValue and the active state in @a state.
308 ///
309 /// @note This method also returns @c false if this node contains any child nodes.
310 bool isConstant(ValueType& firstValue, bool& state,
311 const ValueType& tolerance = zeroVal<ValueType>()) const;
312
313 /// Return @c true if all of this node's tables entries have
314 /// the same active @a state and the range of its values satisfy
315 /// (@a maxValue - @a minValue) <= @a tolerance.
316 ///
317 /// @param minValue Is updated with the minimum of all values IF method
318 /// returns @c true. Else the value is undefined!
319 /// @param maxValue Is updated with the maximum of all values IF method
320 /// returns @c true. Else the value is undefined!
321 /// @param state Is updated with the state of all values IF method
322 /// returns @c true. Else the value is undefined!
323 /// @param tolerance The tolerance used to determine if values are
324 /// approximately constant.
325 ///
326 /// @note This method also returns @c false if this node contains any child nodes.
327 bool isConstant(ValueType& minValue, ValueType& maxValue,
328 bool& state, const ValueType& tolerance = zeroVal<ValueType>()) const;
329
330 /// Return @c true if this node has no children and only contains inactive values.
331 bool isInactive() const { return this->isChildMaskOff() && this->isValueMaskOff(); }
332
333 /// Return @c true if the voxel at the given coordinates is active.
334 bool isValueOn(const Coord& xyz) const;
335 /// Return @c true if the voxel at the given offset is active.
336 bool isValueOn(Index offset) const { OPENVDB_ASSERT(offset < NUM_VALUES); return mValueMask.isOn(offset); }
337 /// Return @c true if the voxel at the given coordinates is inactive.
338 bool isValueOff(const Coord& xyz) const;
339 /// Return @c true if the voxel at the given offset is inactive.
340 bool isValueOff(Index offset) const { OPENVDB_ASSERT(offset < NUM_VALUES); return mValueMask.isOff(offset); }
341
342 /// Return @c true if this node or any of its child nodes have any active tiles.
343 bool hasActiveTiles() const;
344
345 const ValueType& getValue(const Coord& xyz) const;
346 bool probeValue(const Coord& xyz, ValueType& value) const;
347
348 /// @brief Return the level of the tree (0 = leaf) at which the value
349 /// at the given coordinates resides.
350 Index getValueLevel(const Coord& xyz) const;
351
352 /// @brief If the first entry in this node's table is a tile, return the tile's value.
353 /// Otherwise, return the result of calling getFirstValue() on the child.
354 const ValueType& getFirstValue() const;
355 /// @brief If the last entry in this node's table is a tile, return the tile's value.
356 /// Otherwise, return the result of calling getLastValue() on the child.
357 const ValueType& getLastValue() const;
358
359 /// Set the active state of the voxel at the given coordinates but don't change its value.
360 void setActiveState(const Coord& xyz, bool on);
361 /// Set the value of the voxel at the given coordinates but don't change its active state.
362 void setValueOnly(const Coord& xyz, const ValueType& value);
363 /// Mark the voxel at the given coordinates as active but don't change its value.
364 void setValueOn(const Coord& xyz);
365 /// Set the value of the voxel at the given coordinates and mark the voxel as active.
366 void setValueOn(const Coord& xyz, const ValueType& value);
367 /// Mark the voxel at the given coordinates as inactive but don't change its value.
368 void setValueOff(const Coord& xyz);
369 /// Set the value of the voxel at the given coordinates and mark the voxel as inactive.
370 void setValueOff(const Coord& xyz, const ValueType& value);
371
372 /// @brief Apply a functor to the value of the voxel at the given coordinates
373 /// and mark the voxel as active.
374 template<typename ModifyOp>
375 void modifyValue(const Coord& xyz, const ModifyOp& op);
376 /// Apply a functor to the voxel at the given coordinates.
377 template<typename ModifyOp>
378 void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op);
379
380 /// Return the value of the voxel at the given coordinates and, if necessary, update
381 /// the accessor with pointers to the nodes along the path from the root node to
382 /// the node containing the voxel.
383 /// @note Used internally by ValueAccessor.
384 template<typename AccessorT>
385 const ValueType& getValueAndCache(const Coord& xyz, AccessorT&) const;
386
387 /// Return @c true if the voxel at the given coordinates is active and, if necessary,
388 /// update the accessor with pointers to the nodes along the path from the root node
389 /// to the node containing the voxel.
390 /// @note Used internally by ValueAccessor.
391 template<typename AccessorT>
392 bool isValueOnAndCache(const Coord& xyz, AccessorT&) const;
393
394 /// Change the value of the voxel at the given coordinates and mark it as active.
395 /// If necessary, update the accessor with pointers to the nodes along the path
396 /// from the root node to the node containing the voxel.
397 /// @note Used internally by ValueAccessor.
398 template<typename AccessorT>
399 void setValueAndCache(const Coord& xyz, const ValueType& value, AccessorT&);
400
401 /// Set the value of the voxel at the given coordinate but preserves its active state.
402 /// If necessary, update the accessor with pointers to the nodes along the path
403 /// from the root node to the node containing the voxel.
404 /// @note Used internally by ValueAccessor.
405 template<typename AccessorT>
406 void setValueOnlyAndCache(const Coord& xyz, const ValueType& value, AccessorT&);
407
408 /// @brief Apply a functor to the value of the voxel at the given coordinates
409 /// and mark the voxel as active.
410 /// If necessary, update the accessor with pointers to the nodes along the path
411 /// from the root node to the node containing the voxel.
412 /// @note Used internally by ValueAccessor.
413 template<typename ModifyOp, typename AccessorT>
414 void modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&);
415
416 /// Apply a functor to the voxel at the given coordinates.
417 /// If necessary, update the accessor with pointers to the nodes along the path
418 /// from the root node to the node containing the voxel.
419 /// @note Used internally by ValueAccessor.
420 template<typename ModifyOp, typename AccessorT>
421 void modifyValueAndActiveStateAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&);
422
423 /// Change the value of the voxel at the given coordinates and mark it as inactive.
424 /// If necessary, update the accessor with pointers to the nodes along the path
425 /// from the root node to the node containing the voxel.
426 /// @note Used internally by ValueAccessor.
427 template<typename AccessorT>
428 void setValueOffAndCache(const Coord& xyz, const ValueType& value, AccessorT&);
429
430 /// Set the active state of the voxel at the given coordinates without changing its value.
431 /// If necessary, update the accessor with pointers to the nodes along the path
432 /// from the root node to the node containing the voxel.
433 /// @note Used internally by ValueAccessor.
434 template<typename AccessorT>
435 void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&);
436
437 /// Return, in @a value, the value of the voxel at the given coordinates and,
438 /// if necessary, update the accessor with pointers to the nodes along
439 /// the path from the root node to the node containing the voxel.
440 /// @return @c true if the voxel at the given coordinates is active
441 /// @note Used internally by ValueAccessor.
442 template<typename AccessorT>
443 bool probeValueAndCache(const Coord& xyz, ValueType& value, AccessorT&) const;
444
445 /// @brief Return the level of the tree (0 = leaf) at which the value
446 /// at the given coordinates resides.
447 ///
448 /// If necessary, update the accessor with pointers to the nodes along the path
449 /// from the root node to the node containing the voxel.
450 /// @note Used internally by ValueAccessor.
451 template<typename AccessorT>
452 Index getValueLevelAndCache(const Coord& xyz, AccessorT&) const;
453
454 /// Mark all values (both tiles and voxels) as active.
455 void setValuesOn();
456
457 //
458 // I/O
459 //
460 void writeTopology(std::ostream&, bool toHalf = false) const;
461 void readTopology(std::istream&, bool fromHalf = false);
462 void writeBuffers(std::ostream&, bool toHalf = false) const;
463 void readBuffers(std::istream&, bool fromHalf = false);
464 void readBuffers(std::istream&, const CoordBBox&, bool fromHalf = false);
465
466
467 //
468 // Unsafe methods
469 //
470 // WARNING: For improved performance, these unsafe methods do not check the value or
471 // child masks. If used incorrectly, at best they will leave the InternalNode in an
472 // invalid state and at worst cause the application to crash. Always use the safer
473 // alternative method(s) unless you really know what you're doing.
474 // Enabling OpenVDB asserts will catch where assumptions are incorrectly invalidated.
475
476 /// @brief Return the tile value at offset.
477 /// @note Use getValue(const Coord&) for a safer alternative.
478 /// @warning This method should only be used by experts seeking low-level optimizations.
479 const ValueType& getValueUnsafe(Index offset) const;
480 /// @brief Return the tile value and active state at offset.
481 /// @note Use probeValue(const Coord&, ValueType&) for a safer alternative.
482 /// @warning This method should only be used by experts seeking low-level optimizations.
483 bool getValueUnsafe(Index offset, ValueType& value) const;
484
485 /// @brief Return the child node at offset.
486 /// @note Use probeChild(const Coord&) for a safer alternative.
487 /// @warning This method should only be used by experts seeking low-level optimizations.
488 ChildNodeType* getChildUnsafe(Index offset);
489 /// @brief Return the child node at offset.
490 /// @note Use probeConstChild(const Coord&) for a safer alternative.
491 /// @warning This method should only be used by experts seeking low-level optimizations.
492 const ChildNodeType* getConstChildUnsafe(Index offset) const;
493 /// @brief Return the child node at offset.
494 /// @note Use probeChild(const Coord&) for a safer alternative.
495 /// @warning This method should only be used by experts seeking low-level optimizations.
496 const ChildNodeType* getChildUnsafe(Index offset) const;
497
498 /// @brief Set the tile active state at offset but don't change its value.
499 /// @note Use setActiveState(const Coord&, bool) for a safer alternative.
500 /// @warning This method should only be used by experts seeking low-level optimizations.
501 void setActiveStateUnsafe(Index offset, bool on);
502 /// @brief Set the tile value at offset but don't change its value.
503 /// @note Use setValueOnly(const Coord&, const ValueType&) for a safer alternative.
504 /// @warning This method should only be used by experts seeking low-level optimizations.
505 void setValueOnlyUnsafe(Index offset, const ValueType& value);
506 /// @brief Mark the tile active at offset but don't change its value.
507 /// @note Use setValueOn(const Coord&) for a safer alternative.
508 /// @warning This method should only be used by experts seeking low-level optimizations.
509 void setValueOnUnsafe(Index offset);
510 /// @brief Set the tile value at offset and mark the voxel as active.
511 /// @note Use setValueOn(const Coord&, const ValueType&) for a safer alternative.
512 /// @warning This method should only be used by experts seeking low-level optimizations.
513 void setValueOnUnsafe(Index offset, const ValueType& value);
514 /// @brief Mark the tile inactive at offset but don't change its value.
515 /// @note Use setValueOff(const Coord&) for a safer alternative.
516 /// @warning This method should only be used by experts seeking low-level optimizations.
517 void setValueOffUnsafe(Index offset);
518 /// @brief Set the tile value at offset and mark the voxel as inactive.
519 /// @note Use setValueOff(const Coord&, const ValueType&) for a safer alternative.
520 /// @warning This method should only be used by experts seeking low-level optimizations.
521 void setValueOffUnsafe(Index offset, const ValueType& value);
522
523 /// @brief Set the active/inactive value mask.
524 /// @note Use setValueOn(const Coord&)/setValueOff(const Coord&) for a safer alternative.
525 /// @warning This method should only be used by experts seeking low-level optimizations.
526 void setValueMaskUnsafe(const NodeMaskType& mask);
527
528 /// @brief Replace a tile at offset with the given child node.
529 /// @note Use addChild(ChildNodeType*) for a safer alternative.
530 /// @warning This method should only be used by experts seeking low-level optimizations.
531 void setChildUnsafe(Index offset, ChildNodeType* child);
532 /// @brief Replace a child node at offset with the given child node.
533 /// @note Use addChild(ChildNodeType*) for a safer alternative.
534 /// @warning This method should only be used by experts seeking low-level optimizations.
535 void resetChildUnsafe(Index offset, ChildNodeType* child);
536 /// @brief Replace a child node at offset with the given value and active state.
537 /// @note Use addChild(ChildNodeType*) for a safer alternative.
538 /// @warning This method should only be used by experts seeking low-level optimizations.
539 ChildNodeType* stealChildUnsafe(Index offset, const ValueType& value, bool active);
540 /// @brief Delete a child node at offset and replace with the given value and active state.
541 /// @note Use addTile(Index, const ValueType&, bool) for a safer alternative.
542 /// @warning This method should only be used by experts seeking low-level optimizations.
543 void deleteChildUnsafe(Index offset, const ValueType& value, bool active);
544
545 //
546 // Aux methods
547 //
548
549 /// Change the sign of all the values represented in this node and its child nodes.
550 void negate();
551
552 /// @brief Set all voxels within a given axis-aligned box to a constant value.
553 /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box
554 /// @param value the value to which to set voxels within the box
555 /// @param active if true, mark voxels within the box as active,
556 /// otherwise mark them as inactive
557 /// @note This operation generates a sparse, but not always optimally sparse,
558 /// representation of the filled box. Follow fill operations with a prune()
559 /// operation for optimal sparseness.
560 void fill(const CoordBBox& bbox, const ValueType& value, bool active = true);
561
562 /// @brief Set all voxels within a given axis-aligned box to a constant value
563 /// and ensure that those voxels are all represented at the leaf level.
564 /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box.
565 /// @param value the value to which to set voxels within the box.
566 /// @param active if true, mark voxels within the box as active,
567 /// otherwise mark them as inactive.
568 /// @sa voxelizeActiveTiles()
569 void denseFill(const CoordBBox& bbox, const ValueType& value, bool active = true);
570
571 /// @brief Densify active tiles, i.e., replace them with leaf-level active voxels.
572 /// @param threaded if true, this operation is multi-threaded (over the internal nodes).
573 /// @sa denseFill()
574 void voxelizeActiveTiles(bool threaded = true);
575
576 /// @brief Copy into a dense grid the values of the voxels that lie within
577 /// a given bounding box.
578 /// @param bbox inclusive bounding box of the voxels to be copied into the dense grid
579 /// @param dense dense grid with a stride in @e z of one (see tools::Dense
580 /// in tools/Dense.h for the required API)
581 /// @note @a bbox is assumed to be identical to or contained in the coordinate domains
582 /// of both the dense grid and this node, i.e., no bounds checking is performed.
583 template<typename DenseT>
584 void copyToDense(const CoordBBox& bbox, DenseT& dense) const;
585
586 /// @brief Efficiently merge another tree into this tree using one of several schemes.
587 /// @warning This operation cannibalizes the other tree.
588 template<MergePolicy Policy>
589 void merge(InternalNode& other, const ValueType& background, const ValueType& otherBackground);
590
591 /// @brief Merge, using one of several schemes, this node (and its descendants)
592 /// with a tile of the same dimensions and the given value and active state.
593 template<MergePolicy Policy> void merge(const ValueType& tileValue, bool tileActive);
594
595 /// @brief Union this branch's set of active values with the other branch's
596 /// active values. The value type of the other branch can be different.
597 /// @details The resulting state of a value is active if the corresponding value
598 /// was already active OR if it is active in the other tree. Also, a resulting
599 /// value maps to a voxel if the corresponding value already mapped to a voxel
600 /// OR if it is a voxel in the other tree. Thus, a resulting value can only
601 /// map to a tile if the corresponding value already mapped to a tile
602 /// AND if it is a tile value in other tree.
603 ///
604 /// Specifically, active tiles and voxels in this branch are not changed, and
605 /// tiles or voxels that were inactive in this branch but active in the other branch
606 /// are marked as active in this branch but left with their original values.
607 template<typename OtherChildNodeType>
608 void topologyUnion(const InternalNode<OtherChildNodeType, Log2Dim>& other, const bool preserveTiles = false);
609
610 /// @brief Intersects this tree's set of active values with the active values
611 /// of the other tree, whose @c ValueType may be different.
612 /// @details The resulting state of a value is active only if the corresponding
613 /// value was already active AND if it is active in the other tree. Also, a
614 /// resulting value maps to a voxel if the corresponding value
615 /// already mapped to an active voxel in either of the two grids
616 /// and it maps to an active tile or voxel in the other grid.
617 ///
618 /// @note This operation can delete branches in this grid if they
619 /// overlap with inactive tiles in the other grid. Likewise active
620 /// voxels can be turned into inactive voxels resulting in leaf
621 /// nodes with no active values. Thus, it is recommended to
622 /// subsequently call prune.
623 template<typename OtherChildNodeType>
624 void topologyIntersection(const InternalNode<OtherChildNodeType, Log2Dim>& other,
625 const ValueType& background);
626
627 /// @brief Difference this node's set of active values with the active values
628 /// of the other node, whose @c ValueType may be different. So a
629 /// resulting voxel will be active only if the original voxel is
630 /// active in this node and inactive in the other node.
631 ///
632 /// @details The last dummy argument is required to match the signature
633 /// for InternalNode::topologyDifference.
634 ///
635 /// @note This operation modifies only active states, not
636 /// values. Also note that this operation can result in all voxels
637 /// being inactive so consider subsequently calling prune.
638 template<typename OtherChildNodeType>
639 void topologyDifference(const InternalNode<OtherChildNodeType, Log2Dim>& other,
640 const ValueType& background);
641
642 template<typename CombineOp>
643 void combine(InternalNode& other, CombineOp&);
644 template<typename CombineOp>
645 void combine(const ValueType& value, bool valueIsActive, CombineOp&);
646
647 template<typename CombineOp, typename OtherNodeType /*= InternalNode*/>
648 void combine2(const InternalNode& other0, const OtherNodeType& other1, CombineOp&);
649 template<typename CombineOp, typename OtherNodeType /*= InternalNode*/>
650 void combine2(const ValueType& value, const OtherNodeType& other, bool valIsActive, CombineOp&);
651 template<typename CombineOp, typename OtherValueType>
652 void combine2(const InternalNode& other, const OtherValueType&, bool valIsActive, CombineOp&);
653
654 /// Set all voxels that lie outside the given axis-aligned box to the background.
655 void clip(const CoordBBox&, const ValueType& background);
656
657 /// @brief Reduce the memory footprint of this tree by replacing with tiles
658 /// any nodes whose values are all the same (optionally to within a tolerance)
659 /// and have the same active state.
660 void prune(const ValueType& tolerance = zeroVal<ValueType>());
661
662 /// @brief Add the specified leaf to this node, possibly creating a child branch
663 /// in the process. If the leaf node already exists, replace it.
664 void addLeaf(LeafNodeType* leaf);
665
666 /// @brief Same as addLeaf() except, if necessary, update the accessor with pointers
667 /// to the nodes along the path from the root node to the node containing the coordinate.
668 template<typename AccessorT>
669 void addLeafAndCache(LeafNodeType* leaf, AccessorT&);
670
671 /// @brief Return a pointer to the node of type @c NodeT that contains voxel (x, y, z)
672 /// and replace it with a tile of the specified value and state.
673 /// If no such node exists, leave the tree unchanged and return @c nullptr.
674 ///
675 /// @note The caller takes ownership of the node and is responsible for deleting it.
676 ///
677 /// @warning Since this method potentially removes nodes and branches of the tree,
678 /// it is important to clear the caches of all ValueAccessors associated with this tree.
679 template<typename NodeT>
680 NodeT* stealNode(const Coord& xyz, const ValueType& value, bool state);
681
682 /// @brief Add the given child node at this level deducing the offset from it's origin.
683 /// If a child node with this offset already exists, delete the old node and add the
684 /// new node in its place (i.e. ownership of the new child node is transferred to
685 /// this InternalNode)
686 /// @return @c true if inserting the child has been successful, otherwise the caller
687 /// retains ownership of the node and is responsible for deleting it.
688 bool addChild(ChildNodeType* child);
689
690 /// @brief Add a tile at the specified tree level that contains voxel (x, y, z),
691 /// possibly creating a parent branch or deleting a child branch in the process.
692 void addTile(Index level, const Coord& xyz, const ValueType& value, bool state);
693
694 /// @brief Delete any existing child branch at the specified offset and add a tile.
695 void addTile(Index offset, const ValueType& value, bool state);
696
697 /// @brief Same as addTile() except, if necessary, update the accessor with pointers
698 /// to the nodes along the path from the root node to the node containing (x, y, z).
699 template<typename AccessorT>
700 void addTileAndCache(Index level, const Coord& xyz, const ValueType&, bool state, AccessorT&);
701
702 //@{
703 /// @brief Return a pointer to the node that contains voxel (x, y, z).
704 /// If no such node exists, return nullptr.
705 template<typename NodeType> NodeType* probeNode(const Coord& xyz);
706 template<typename NodeType> const NodeType* probeConstNode(const Coord& xyz) const;
707 template<typename NodeType> const NodeType* probeNode(const Coord& xyz) const { return this->probeConstNode<NodeType>(xyz); }
708 //@}
709
710 //@{
711 /// @brief Return a pointer to the child node that contains voxel (x, y, z).
712 /// If no such node exists, return nullptr.
713 ChildNodeType* probeChild(const Coord& xyz);
714 const ChildNodeType* probeConstChild(const Coord& xyz) const;
715 const ChildNodeType* probeChild(const Coord& xyz) const { return this->probeConstChild(xyz); }
716 //@}
717
718 //@{
719 /// @brief Return a pointer to the child node that contains voxel (x, y, z).
720 /// If no such node exists, return nullptr.
721 ChildNodeType* probeChild(const Coord& xyz, ValueType& value, bool& active);
722 const ChildNodeType* probeConstChild(const Coord& xyz, ValueType& value, bool& active) const;
723 const ChildNodeType* probeChild(const Coord& xyz, ValueType& value, bool& active) const { return this->probeConstChild(xyz, value, active); }
724 //@}
725
726 //@{
727 /// @brief Return a pointer to the child node for a specific offset.
728 /// If no such node exists, return nullptr.
729 /// @warning This method should only be used by experts seeking low-level optimizations.
730 /// @note Out-of-bounds memory access attempts will wrap around using modulo indexing.
731 ChildNodeType* probeChildUnsafe(Index offset);
732 const ChildNodeType* probeConstChildUnsafe(Index offset) const;
733 const ChildNodeType* probeChildUnsafe(Index offset) const { return this->probeConstChildUnsafe(offset); }
734 //@}
735
736 //@{
737 /// @brief Return a pointer to the child node for a specific offset.
738 /// If no such node exists, return nullptr.
739 /// @warning This method should only be used by experts seeking low-level optimizations.
740 /// @note Out-of-bounds memory access attempts will wrap around using modulo indexing.
741 ChildNodeType* probeChildUnsafe(Index offset, ValueType& value, bool& active);
742 const ChildNodeType* probeConstChildUnsafe(Index offset, ValueType& value, bool& active) const;
743 const ChildNodeType* probeChildUnsafe(Index offset, ValueType& value, bool& active) const { return this->probeConstChildUnsafe(offset, value, active); }
744 //@}
745
746 //@{
747 /// @brief Same as probeNode() except, if necessary, update the accessor with pointers
748 /// to the nodes along the path from the root node to the node containing (x, y, z).
749 template<typename NodeType, typename AccessorT>
750 NodeType* probeNodeAndCache(const Coord& xyz, AccessorT&);
751 template<typename NodeType, typename AccessorT>
752 const NodeType* probeConstNodeAndCache(const Coord& xyz, AccessorT&) const;
753 //@}
754
755 //@{
756 /// @brief Return a pointer to the leaf node that contains voxel (x, y, z).
757 /// If no such node exists, return @c nullptr.
758 LeafNodeType* probeLeaf(const Coord& xyz);
759 const LeafNodeType* probeConstLeaf(const Coord& xyz) const;
760 const LeafNodeType* probeLeaf(const Coord& xyz) const;
761 //@}
762
763 //@{
764 /// @brief Same as probeLeaf() except, if necessary, update the accessor with pointers
765 /// to the nodes along the path from the root node to the node containing (x, y, z).
766 template<typename AccessorT>
767 LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc);
768 template<typename AccessorT>
769 const LeafNodeType* probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const;
770 template<typename AccessorT>
771 const LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc) const;
772 //@}
773
774 /// @brief Return the leaf node that contains voxel (x, y, z).
775 /// If no such node exists, create one, but preserve the values and
776 /// active states of all voxels.
777 ///
778 /// @details Use this method to preallocate a static tree topology
779 /// over which to safely perform multithreaded processing.
780 LeafNodeType* touchLeaf(const Coord& xyz);
781
782 /// @brief Same as touchLeaf() except, if necessary, update the accessor with pointers
783 /// to the nodes along the path from the root node to the node containing the coordinate.
784 template<typename AccessorT>
785 LeafNodeType* touchLeafAndCache(const Coord& xyz, AccessorT&);
786
787 //@{
788 /// @brief Adds all nodes of a certain type to a container with the following API:
789 /// @code
790 /// struct ArrayT {
791 /// using value_type = ...;// defines the type of nodes to be added to the array
792 /// void push_back(value_type nodePtr);// method that add nodes to the array
793 /// };
794 /// @endcode
795 /// @details An example of a wrapper around a c-style array is:
796 /// @code
797 /// struct MyArray {
798 /// using value_type = LeafType*;
799 /// value_type* ptr;
800 /// MyArray(value_type* array) : ptr(array) {}
801 /// void push_back(value_type leaf) { *ptr++ = leaf; }
802 ///};
803 /// @endcode
804 /// @details An example that constructs a list of pointer to all leaf nodes is:
805 /// @code
806 /// std::vector<const LeafNodeType*> array;//most std contains have the required API
807 /// array.reserve(tree.leafCount());//this is a fast preallocation.
808 /// tree.getNodes(array);
809 /// @endcode
810 template<typename ArrayT>
811 void getNodes(ArrayT& array);
812 template<typename ArrayT>
813 void getNodes(ArrayT& array) const;
814 //@}
815
816 /// @brief Steals all nodes of a certain type from the tree and
817 /// adds them to a container with the following API:
818 /// @code
819 /// struct ArrayT {
820 /// using value_type = ...;// defines the type of nodes to be added to the array
821 /// void push_back(value_type nodePtr);// method that add nodes to the array
822 /// };
823 /// @endcode
824 /// @details An example of a wrapper around a c-style array is:
825 /// @code
826 /// struct MyArray {
827 /// using value_type = LeafType*;
828 /// value_type* ptr;
829 /// MyArray(value_type* array) : ptr(array) {}
830 /// void push_back(value_type leaf) { *ptr++ = leaf; }
831 ///};
832 /// @endcode
833 /// @details An example that constructs a list of pointer to all leaf nodes is:
834 /// @code
835 /// std::vector<const LeafNodeType*> array;//most std contains have the required API
836 /// array.reserve(tree.leafCount());//this is a fast preallocation.
837 /// tree.stealNodes(array);
838 /// @endcode
839 template<typename ArrayT>
840 void stealNodes(ArrayT& array, const ValueType& value, bool state);
841
842 /// @brief Change inactive tiles or voxels with value oldBackground to newBackground
843 /// or -oldBackground to -newBackground. Active values are unchanged.
844 void resetBackground(const ValueType& oldBackground, const ValueType& newBackground);
845
846 /// @brief Return @c true if the given tree branch has the same node and active value
847 /// topology as this tree branch (but possibly a different @c ValueType).
848 template<typename OtherChildNodeType, Index OtherLog2Dim>
849 bool hasSameTopology(const InternalNode<OtherChildNodeType, OtherLog2Dim>* other) const;
850
851protected:
852 //@{
853 /// Allow iterators to call mask accessor methods (setValueMask(), setChildMask(), etc.).
854 /// @todo Make mask accessors public?
855 friend class IteratorBase<MaskOnIterator, InternalNode>;
856 friend class IteratorBase<MaskOffIterator, InternalNode>;
857 friend class IteratorBase<MaskDenseIterator, InternalNode>;
858 //@}
859
860 /// @brief During topology-only construction, access is needed
861 /// to protected/private members of other template instances.
862 template<typename, Index> friend class InternalNode;
863
864 // Mask accessors
865public:
866 bool isValueMaskOn(Index n) const { return mValueMask.isOn(n); }
867 bool isValueMaskOn() const { return mValueMask.isOn(); }
868 bool isValueMaskOff(Index n) const { return mValueMask.isOff(n); }
869 bool isValueMaskOff() const { return mValueMask.isOff(); }
870 bool isChildMaskOn(Index n) const { return mChildMask.isOn(n); }
871 bool isChildMaskOff(Index n) const { return mChildMask.isOff(n); }
872 bool isChildMaskOff() const { return mChildMask.isOff(); }
873 const NodeMaskType& getValueMask() const { return mValueMask; }
874 const NodeMaskType& getChildMask() const { return mChildMask; }
875 NodeMaskType getValueOffMask() const
876 {
877 NodeMaskType mask = mValueMask;
878 mask |= mChildMask;
879 mask.toggle();
880 return mask;
881 }
882 const UnionType* getTable() const { return mNodes; }
883protected:
884 //@{
885 /// Use a mask accessor to ensure consistency between the child and value masks;
886 /// i.e., the value mask should always be off wherever the child mask is on.
887 void setValueMask(Index n, bool on) { mValueMask.set(n, mChildMask.isOn(n) ? false : on); }
888 //@}
889
890 void makeChildNodeEmpty(Index n, const ValueType& value);
891 void setChildNode( Index i, ChildNodeType* child);//assumes a tile
892 void resetChildNode(Index i, ChildNodeType* child);//checks for an existing child
893 ChildNodeType* unsetChildNode(Index i, const ValueType& value);
894
895 ///@{
896 /// @brief Returns a pointer to the child node at the linear offset n.
897 /// @warning This protected method assumes that a child node exists at
898 /// the specified linear offset!
899 ChildNodeType* getChildNode(Index n);
900 const ChildNodeType* getChildNode(Index n) const;
901 ///@}
902
903 ///@{
904 /// @brief Protected member classes for recursive multi-threading
905 struct VoxelizeActiveTiles;
906 template<typename OtherInternalNode> struct DeepCopy;
907 template<typename OtherInternalNode> struct TopologyCopy1;
908 template<typename OtherInternalNode> struct TopologyCopy2;
909 template<typename OtherInternalNode> struct TopologyUnion;
910 template<typename OtherInternalNode> struct TopologyDifference;
911 template<typename OtherInternalNode> struct TopologyIntersection;
912 ///@}
913
914 UnionType mNodes[NUM_VALUES];
915 NodeMaskType mChildMask, mValueMask;
916 /// Global grid index coordinates (x,y,z) of the local origin of this node
917 Coord mOrigin;
918 /// Transient data (not serialized)
919 Index32 mTransientData = 0;
920}; // class InternalNode
921
922
923////////////////////////////////////////
924
925
926//@{
927/// Helper metafunction used to implement InternalNode::SameConfiguration
928/// (which, as an inner class, can't be independently specialized)
929template<typename ChildT1, Index Dim1, typename NodeT2>
930struct SameInternalConfig {
931 static const bool value = false;
932};
933
934template<typename ChildT1, Index Dim1, typename ChildT2>
935struct SameInternalConfig<ChildT1, Dim1, InternalNode<ChildT2, Dim1> > {
936 static const bool value = ChildT1::template SameConfiguration<ChildT2>::value;
937};
938//@}
939
940
941////////////////////////////////////////
942
943
944template<typename ChildT, Index Log2Dim>
945inline
946InternalNode<ChildT, Log2Dim>::InternalNode(const ValueType& background)
947{
948 for (Index i = 0; i < NUM_VALUES; ++i) mNodes[i].setValue(background);
949}
950
951
952template<typename ChildT, Index Log2Dim>
953inline
954InternalNode<ChildT, Log2Dim>::InternalNode(const Coord& origin, const ValueType& val, bool active):
955 mOrigin(origin[0] & ~(DIM - 1), // zero out the low-order bits
956 origin[1] & ~(DIM - 1),
957 origin[2] & ~(DIM - 1))
958{
959 if (active) mValueMask.setOn();
960 for (Index i = 0; i < NUM_VALUES; ++i) mNodes[i].setValue(val);
961}
962
963
964// For InternalNodes, the PartialCreate constructor is identical to its
965// non-PartialCreate counterpart.
966template<typename ChildT, Index Log2Dim>
967inline
968InternalNode<ChildT, Log2Dim>::InternalNode(PartialCreate,
969 const Coord& origin, const ValueType& val, bool active)
970 : mOrigin(origin[0] & ~(DIM-1), origin[1] & ~(DIM-1), origin[2] & ~(DIM-1))
971{
972 if (active) mValueMask.setOn();
973 for (Index i = 0; i < NUM_VALUES; ++i) mNodes[i].setValue(val);
974}
975
976template<typename ChildT, Index Log2Dim>
977template<typename OtherInternalNode>
978struct InternalNode<ChildT, Log2Dim>::DeepCopy
979{
980 DeepCopy(const OtherInternalNode* source, InternalNode* target) : s(source), t(target) {
981 tbb::parallel_for(tbb::blocked_range<Index>(0, NUM_VALUES), *this);
982 //(*this)(tbb::blocked_range<Index>(0, NUM_VALUES));//serial
983 }
984 void operator()(const tbb::blocked_range<Index> &r) const {
985 for (Index i = r.begin(), end=r.end(); i!=end; ++i) {
986 if (s->mChildMask.isOff(i)) {
987 t->mNodes[i].setValue(ValueType(s->mNodes[i].getValue()));
988 } else {
989 t->mNodes[i].setChild(new ChildNodeType(*(s->mNodes[i].getChild())));
990 }
991 }
992 }
993 const OtherInternalNode* s;
994 InternalNode* t;
995};// DeepCopy
996
997template<typename ChildT, Index Log2Dim>
998inline
999InternalNode<ChildT, Log2Dim>::InternalNode(const InternalNode& other)
1000 : mChildMask(other.mChildMask)
1001 , mValueMask(other.mValueMask)
1002 , mOrigin(other.mOrigin)
1003 , mTransientData(other.mTransientData)
1004{
1005 DeepCopy<InternalNode<ChildT, Log2Dim> > tmp(&other, this);
1006}
1007
1008
1009// Copy-construct from a node with the same configuration but a different ValueType.
1010template<typename ChildT, Index Log2Dim>
1011template<typename OtherChildNodeType>
1012inline
1013InternalNode<ChildT, Log2Dim>::InternalNode(const InternalNode<OtherChildNodeType, Log2Dim>& other)
1014 : mChildMask(other.mChildMask)
1015 , mValueMask(other.mValueMask)
1016 , mOrigin(other.mOrigin)
1017 , mTransientData(other.mTransientData)
1018{
1019 DeepCopy<InternalNode<OtherChildNodeType, Log2Dim> > tmp(&other, this);
1020}
1021
1022template<typename ChildT, Index Log2Dim>
1023template<typename OtherInternalNode>
1024struct InternalNode<ChildT, Log2Dim>::TopologyCopy1
1025{
1026 TopologyCopy1(const OtherInternalNode* source, InternalNode* target,
1027 const ValueType& background) : s(source), t(target), b(background) {
1028 tbb::parallel_for(tbb::blocked_range<Index>(0, NUM_VALUES), *this);
1029 //(*this)(tbb::blocked_range<Index>(0, NUM_VALUES));//serial
1030 }
1031 void operator()(const tbb::blocked_range<Index> &r) const {
1032 for (Index i = r.begin(), end=r.end(); i!=end; ++i) {
1033 if (s->isChildMaskOn(i)) {
1034 t->mNodes[i].setChild(new ChildNodeType(*(s->mNodes[i].getChild()),
1035 b, TopologyCopy()));
1036 } else {
1037 t->mNodes[i].setValue(b);
1038 }
1039 }
1040 }
1041 const OtherInternalNode* s;
1042 InternalNode* t;
1043 const ValueType &b;
1044};// TopologyCopy1
1045
1046template<typename ChildT, Index Log2Dim>
1047template<typename OtherChildNodeType>
1048inline
1049InternalNode<ChildT, Log2Dim>::InternalNode(const InternalNode<OtherChildNodeType, Log2Dim>& other,
1050 const ValueType& background, TopologyCopy)
1051 : mChildMask(other.mChildMask)
1052 , mValueMask(other.mValueMask)
1053 , mOrigin(other.mOrigin)
1054 , mTransientData(other.mTransientData)
1055{
1056 TopologyCopy1<InternalNode<OtherChildNodeType, Log2Dim> > tmp(&other, this, background);
1057}
1058
1059template<typename ChildT, Index Log2Dim>
1060template<typename OtherInternalNode>
1061struct InternalNode<ChildT, Log2Dim>::TopologyCopy2
1062{
1063 TopologyCopy2(const OtherInternalNode* source, InternalNode* target,
1064 const ValueType& offValue, const ValueType& onValue)
1065 : s(source), t(target), offV(offValue), onV(onValue) {
1066 tbb::parallel_for(tbb::blocked_range<Index>(0, NUM_VALUES), *this);
1067 }
1068 void operator()(const tbb::blocked_range<Index> &r) const {
1069 for (Index i = r.begin(), end=r.end(); i!=end; ++i) {
1070 if (s->isChildMaskOn(i)) {
1071 t->mNodes[i].setChild(new ChildNodeType(*(s->mNodes[i].getChild()),
1072 offV, onV, TopologyCopy()));
1073 } else {
1074 t->mNodes[i].setValue(s->isValueMaskOn(i) ? onV : offV);
1075 }
1076 }
1077 }
1078 const OtherInternalNode* s;
1079 InternalNode* t;
1080 const ValueType &offV, &onV;
1081 };// TopologyCopy2
1082
1083template<typename ChildT, Index Log2Dim>
1084template<typename OtherChildNodeType>
1085inline
1086InternalNode<ChildT, Log2Dim>::InternalNode(const InternalNode<OtherChildNodeType, Log2Dim>& other,
1087 const ValueType& offValue,
1088 const ValueType& onValue, TopologyCopy)
1089 : mChildMask(other.mChildMask)
1090 , mValueMask(other.mValueMask)
1091 , mOrigin(other.mOrigin)
1092 , mTransientData(other.mTransientData)
1093{
1094 TopologyCopy2<InternalNode<OtherChildNodeType, Log2Dim> > tmp(&other, this, offValue, onValue);
1095}
1096
1097
1098template<typename ChildT, Index Log2Dim>
1099inline
1100InternalNode<ChildT, Log2Dim>::~InternalNode()
1101{
1102 for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) {
1103 delete mNodes[iter.pos()].getChild();
1104 }
1105}
1106
1107
1108////////////////////////////////////////
1109
1110
1111template<typename ChildT, Index Log2Dim>
1112inline Index64
1113InternalNode<ChildT, Log2Dim>::leafCount() const
1114{
1115 if (ChildNodeType::getLevel() == 0) return mChildMask.countOn();
1116 Index64 sum = 0;
1117 for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
1118 sum += iter->leafCount();
1119 }
1120 return sum;
1121}
1122
1123template<typename ChildT, Index Log2Dim>
1124inline void
1125InternalNode<ChildT, Log2Dim>::nodeCount(std::vector<Index64> &vec) const
1126{
1127 OPENVDB_ASSERT(vec.size() > ChildNodeType::LEVEL);
1128 const auto count = mChildMask.countOn();
1129 if (ChildNodeType::LEVEL > 0 && count > 0) {
1130 for (auto iter = this->cbeginChildOn(); iter; ++iter) iter->nodeCount(vec);
1131 }
1132 vec[ChildNodeType::LEVEL] += count;
1133}
1134
1135template<typename ChildT, Index Log2Dim>
1136inline void
1137InternalNode<ChildT, Log2Dim>::nodeCount(std::vector<Index32> &vec) const
1138{
1139 OPENVDB_ASSERT(vec.size() > ChildNodeType::LEVEL);
1140 const auto count = mChildMask.countOn();
1141 if (ChildNodeType::LEVEL > 0 && count > 0) {
1142 for (auto iter = this->cbeginChildOn(); iter; ++iter) {
1144 iter->nodeCount(vec);
1146 }
1147 }
1148 vec[ChildNodeType::LEVEL] += count;
1149}
1150
1151
1152template<typename ChildT, Index Log2Dim>
1153inline Index64
1154InternalNode<ChildT, Log2Dim>::nonLeafCount() const
1155{
1156 Index64 sum = 1;
1157 if (ChildNodeType::getLevel() == 0) return sum;
1158 for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
1159 sum += iter->nonLeafCount();
1160 }
1161 return sum;
1162}
1163
1164
1165template<typename ChildT, Index Log2Dim>
1166inline Index32
1167InternalNode<ChildT, Log2Dim>::childCount() const
1168{
1169 return this->getChildMask().countOn();
1170}
1171
1172
1173template<typename ChildT, Index Log2Dim>
1174inline Index64
1175InternalNode<ChildT, Log2Dim>::onVoxelCount() const
1176{
1177 Index64 sum = ChildT::NUM_VOXELS * mValueMask.countOn();
1178 for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
1179 sum += iter->onVoxelCount();
1180 }
1181 return sum;
1182}
1183
1184
1185template<typename ChildT, Index Log2Dim>
1186inline Index64
1187InternalNode<ChildT, Log2Dim>::offVoxelCount() const
1188{
1189 Index64 sum = ChildT::NUM_VOXELS * (NUM_VALUES-mValueMask.countOn()-mChildMask.countOn());
1190 for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
1191 sum += iter->offVoxelCount();
1192 }
1193 return sum;
1194}
1195
1196
1197template<typename ChildT, Index Log2Dim>
1198inline Index64
1199InternalNode<ChildT, Log2Dim>::onLeafVoxelCount() const
1200{
1201 Index64 sum = 0;
1202 for (ChildOnCIter iter = this->beginChildOn(); iter; ++iter) {
1203 sum += mNodes[iter.pos()].getChild()->onLeafVoxelCount();
1204 }
1205 return sum;
1206}
1207
1208
1209template<typename ChildT, Index Log2Dim>
1210inline Index64
1211InternalNode<ChildT, Log2Dim>::offLeafVoxelCount() const
1212{
1213 Index64 sum = 0;
1214 for (ChildOnCIter iter = this->beginChildOn(); iter; ++iter) {
1215 sum += mNodes[iter.pos()].getChild()->offLeafVoxelCount();
1216 }
1217 return sum;
1218}
1219
1220template<typename ChildT, Index Log2Dim>
1221inline Index64
1222InternalNode<ChildT, Log2Dim>::onTileCount() const
1223{
1224 Index64 sum = mValueMask.countOn();
1225 for (ChildOnCIter iter = this->cbeginChildOn(); LEVEL>1 && iter; ++iter) {
1226 sum += iter->onTileCount();
1227 }
1228 return sum;
1229}
1230
1231template<typename ChildT, Index Log2Dim>
1232inline Index64
1233InternalNode<ChildT, Log2Dim>::memUsage() const
1234{
1235 Index64 sum = NUM_VALUES * sizeof(UnionType) + mChildMask.memUsage()
1236 + mValueMask.memUsage() + sizeof(mOrigin);
1237 for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
1238 sum += iter->memUsage();
1239 }
1240 return sum;
1241}
1242
1243
1244template<typename ChildT, Index Log2Dim>
1245inline void
1246InternalNode<ChildT, Log2Dim>::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const
1247{
1248 if (bbox.isInside(this->getNodeBoundingBox())) return;
1249
1250 for (ValueOnCIter i = this->cbeginValueOn(); i; ++i) {
1251 bbox.expand(i.getCoord(), ChildT::DIM);
1252 }
1253 for (ChildOnCIter i = this->cbeginChildOn(); i; ++i) {
1254 i->evalActiveBoundingBox(bbox, visitVoxels);
1255 }
1256}
1257
1258
1259////////////////////////////////////////
1260
1261
1262template<typename ChildT, Index Log2Dim>
1263inline void
1264InternalNode<ChildT, Log2Dim>::prune(const ValueType& tolerance)
1265{
1266 bool state = false;
1267 ValueType value = zeroVal<ValueType>();
1268 for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) {
1269 const Index i = iter.pos();
1270 ChildT* child = mNodes[i].getChild();
1271 child->prune(tolerance);
1272 if (child->isConstant(value, state, tolerance)) {
1273 delete child;
1274 mChildMask.setOff(i);
1275 mValueMask.set(i, state);
1276 mNodes[i].setValue(value);
1277 }
1278 }
1279}
1280
1281
1282////////////////////////////////////////
1283
1284
1285template<typename ChildT, Index Log2Dim>
1286template<typename NodeT>
1287inline NodeT*
1288InternalNode<ChildT, Log2Dim>::stealNode(const Coord& xyz, const ValueType& value, bool state)
1289{
1290 if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same<NodeT, ChildT>::value)) ||
1291 NodeT::LEVEL > ChildT::LEVEL) return nullptr;
1293 const Index n = this->coordToOffset(xyz);
1294 if (mChildMask.isOff(n)) return nullptr;
1295 ChildT* child = mNodes[n].getChild();
1296 if (std::is_same<NodeT, ChildT>::value) {
1297 mChildMask.setOff(n);
1298 mValueMask.set(n, state);
1299 mNodes[n].setValue(value);
1300 }
1301 return (std::is_same<NodeT, ChildT>::value)
1302 ? reinterpret_cast<NodeT*>(child)
1303 : child->template stealNode<NodeT>(xyz, value, state);
1305}
1306
1307
1308////////////////////////////////////////
1309
1310
1311template<typename ChildT, Index Log2Dim>
1312template<typename NodeT>
1313inline NodeT*
1314InternalNode<ChildT, Log2Dim>::probeNode(const Coord& xyz)
1315{
1316 if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same<NodeT, ChildT>::value)) ||
1317 NodeT::LEVEL > ChildT::LEVEL) return nullptr;
1319 const Index n = this->coordToOffset(xyz);
1320 if (mChildMask.isOff(n)) return nullptr;
1321 ChildT* child = mNodes[n].getChild();
1322 return (std::is_same<NodeT, ChildT>::value)
1323 ? reinterpret_cast<NodeT*>(child)
1324 : child->template probeNode<NodeT>(xyz);
1326}
1327
1328
1329template<typename ChildT, Index Log2Dim>
1330template<typename NodeT, typename AccessorT>
1331inline NodeT*
1332InternalNode<ChildT, Log2Dim>::probeNodeAndCache(const Coord& xyz, AccessorT& acc)
1333{
1334 if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same<NodeT, ChildT>::value)) ||
1335 NodeT::LEVEL > ChildT::LEVEL) return nullptr;
1337 const Index n = this->coordToOffset(xyz);
1338 if (mChildMask.isOff(n)) return nullptr;
1339 ChildT* child = mNodes[n].getChild();
1340 acc.insert(xyz, child);
1341 return (std::is_same<NodeT, ChildT>::value)
1342 ? reinterpret_cast<NodeT*>(child)
1343 : child->template probeNodeAndCache<NodeT>(xyz, acc);
1345}
1346
1347
1348template<typename ChildT, Index Log2Dim>
1349template<typename NodeT>
1350inline const NodeT*
1351InternalNode<ChildT, Log2Dim>::probeConstNode(const Coord& xyz) const
1352{
1353 if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same<NodeT, ChildT>::value)) ||
1354 NodeT::LEVEL > ChildT::LEVEL) return nullptr;
1356 const Index n = this->coordToOffset(xyz);
1357 if (mChildMask.isOff(n)) return nullptr;
1358 const ChildT* child = mNodes[n].getChild();
1359 return (std::is_same<NodeT, ChildT>::value)
1360 ? reinterpret_cast<const NodeT*>(child)
1361 : child->template probeConstNode<NodeT>(xyz);
1363}
1364
1365
1366template<typename ChildT, Index Log2Dim>
1367template<typename NodeT, typename AccessorT>
1368inline const NodeT*
1369InternalNode<ChildT, Log2Dim>::probeConstNodeAndCache(const Coord& xyz, AccessorT& acc) const
1370{
1371 if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same<NodeT, ChildT>::value)) ||
1372 NodeT::LEVEL > ChildT::LEVEL) return nullptr;
1374 const Index n = this->coordToOffset(xyz);
1375 if (mChildMask.isOff(n)) return nullptr;
1376 const ChildT* child = mNodes[n].getChild();
1377 acc.insert(xyz, child);
1378 return (std::is_same<NodeT, ChildT>::value)
1379 ? reinterpret_cast<const NodeT*>(child)
1380 : child->template probeConstNodeAndCache<NodeT>(xyz, acc);
1382}
1383
1384
1385////////////////////////////////////////
1386
1387
1388template<typename ChildT, Index Log2Dim>
1389inline ChildT*
1390InternalNode<ChildT, Log2Dim>::probeChild(const Coord& xyz)
1391{
1392 const Index n = this->coordToOffset(xyz);
1393 return this->probeChildUnsafe(n);
1394}
1395
1396template<typename ChildT, Index Log2Dim>
1397inline const ChildT*
1398InternalNode<ChildT, Log2Dim>::probeConstChild(const Coord& xyz) const
1399{
1400 const Index n = this->coordToOffset(xyz);
1401 return this->probeConstChildUnsafe(n);
1402}
1403
1404template<typename ChildT, Index Log2Dim>
1405inline ChildT*
1406InternalNode<ChildT, Log2Dim>::probeChild(const Coord& xyz, ValueType& value, bool& active)
1407{
1408 const Index n = this->coordToOffset(xyz);
1409 return this->probeChildUnsafe(n, value, active);
1410}
1411
1412template<typename ChildT, Index Log2Dim>
1413inline const ChildT*
1414InternalNode<ChildT, Log2Dim>::probeConstChild(const Coord& xyz, ValueType& value, bool& active) const
1415{
1416 const Index n = this->coordToOffset(xyz);
1417 return this->probeConstChildUnsafe(n, value, active);
1418}
1419
1420template<typename ChildT, Index Log2Dim>
1421inline ChildT*
1422InternalNode<ChildT, Log2Dim>::probeChildUnsafe(Index offset)
1423{
1424 OPENVDB_ASSERT(offset < NUM_VALUES);
1425 if (mChildMask.isOn(offset)) return mNodes[offset].getChild();
1426 return nullptr;
1427}
1428
1429template<typename ChildT, Index Log2Dim>
1430inline const ChildT*
1431InternalNode<ChildT, Log2Dim>::probeConstChildUnsafe(Index offset) const
1432{
1433 OPENVDB_ASSERT(offset < NUM_VALUES);
1434 if (mChildMask.isOn(offset)) return mNodes[offset].getChild();
1435 return nullptr;
1436}
1437
1438template<typename ChildT, Index Log2Dim>
1439inline ChildT*
1440InternalNode<ChildT, Log2Dim>::probeChildUnsafe(Index offset, ValueType& value, bool& active)
1441{
1442 OPENVDB_ASSERT(offset < NUM_VALUES);
1443 if (mChildMask.isOn(offset)) return mNodes[offset].getChild();
1444 value = mNodes[offset].getValue();
1445 active = mValueMask.isOn(offset);
1446 return nullptr;
1447}
1448
1449template<typename ChildT, Index Log2Dim>
1450inline const ChildT*
1451InternalNode<ChildT, Log2Dim>::probeConstChildUnsafe(Index offset, ValueType& value, bool& active) const
1452{
1453 OPENVDB_ASSERT(offset < NUM_VALUES);
1454 if (mChildMask.isOn(offset)) return mNodes[offset].getChild();
1455 value = mNodes[offset].getValue();
1456 active = mValueMask.isOn(offset);
1457 return nullptr;
1458}
1459
1460
1461////////////////////////////////////////
1462
1463
1464template<typename ChildT, Index Log2Dim>
1465inline typename ChildT::LeafNodeType*
1466InternalNode<ChildT, Log2Dim>::probeLeaf(const Coord& xyz)
1467{
1468 return this->template probeNode<LeafNodeType>(xyz);
1469}
1470
1471
1472template<typename ChildT, Index Log2Dim>
1473template<typename AccessorT>
1474inline typename ChildT::LeafNodeType*
1475InternalNode<ChildT, Log2Dim>::probeLeafAndCache(const Coord& xyz, AccessorT& acc)
1476{
1477 return this->template probeNodeAndCache<LeafNodeType>(xyz, acc);
1478}
1479
1480
1481template<typename ChildT, Index Log2Dim>
1482template<typename AccessorT>
1483inline const typename ChildT::LeafNodeType*
1484InternalNode<ChildT, Log2Dim>::probeLeafAndCache(const Coord& xyz, AccessorT& acc) const
1485{
1486 return this->probeConstLeafAndCache(xyz, acc);
1487}
1488
1489
1490template<typename ChildT, Index Log2Dim>
1491inline const typename ChildT::LeafNodeType*
1492InternalNode<ChildT, Log2Dim>::probeConstLeaf(const Coord& xyz) const
1493{
1494 return this->template probeConstNode<LeafNodeType>(xyz);
1495}
1496
1497
1498template<typename ChildT, Index Log2Dim>
1499template<typename AccessorT>
1500inline const typename ChildT::LeafNodeType*
1501InternalNode<ChildT, Log2Dim>::probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const
1502{
1503 return this->template probeConstNodeAndCache<LeafNodeType>(xyz, acc);
1504}
1505
1506
1507////////////////////////////////////////
1508
1509
1510template<typename ChildT, Index Log2Dim>
1511inline void
1512InternalNode<ChildT, Log2Dim>::addLeaf(LeafNodeType* leaf)
1513{
1514 OPENVDB_ASSERT(leaf != nullptr);
1515 const Coord& xyz = leaf->origin();
1516 const Index n = this->coordToOffset(xyz);
1517 ChildT* child = nullptr;
1518 if (mChildMask.isOff(n)) {
1519 if (ChildT::LEVEL>0) {
1520 child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n));
1521 } else {
1522 child = reinterpret_cast<ChildT*>(leaf);
1523 }
1524 this->setChildNode(n, child);
1525 } else {
1526 if (ChildT::LEVEL>0) {
1527 child = mNodes[n].getChild();
1528 } else {
1529 delete mNodes[n].getChild();
1530 child = reinterpret_cast<ChildT*>(leaf);
1531 mNodes[n].setChild(child);
1532 }
1533 }
1534 child->addLeaf(leaf);
1535}
1536
1537
1538template<typename ChildT, Index Log2Dim>
1539template<typename AccessorT>
1540inline void
1541InternalNode<ChildT, Log2Dim>::addLeafAndCache(LeafNodeType* leaf, AccessorT& acc)
1542{
1543 OPENVDB_ASSERT(leaf != nullptr);
1544 const Coord& xyz = leaf->origin();
1545 const Index n = this->coordToOffset(xyz);
1546 ChildT* child = nullptr;
1547 if (mChildMask.isOff(n)) {
1548 if (ChildT::LEVEL>0) {
1549 child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n));
1550 acc.insert(xyz, child);//we only cache internal nodes
1551 } else {
1552 child = reinterpret_cast<ChildT*>(leaf);
1553 }
1554 this->setChildNode(n, child);
1555 } else {
1556 if (ChildT::LEVEL>0) {
1557 child = mNodes[n].getChild();
1558 acc.insert(xyz, child);//we only cache internal nodes
1559 } else {
1560 delete mNodes[n].getChild();
1561 child = reinterpret_cast<ChildT*>(leaf);
1562 mNodes[n].setChild(child);
1563 }
1564 }
1565 child->addLeafAndCache(leaf, acc);
1566}
1567
1568
1569////////////////////////////////////////
1570
1571
1572template<typename ChildT, Index Log2Dim>
1573inline bool
1574InternalNode<ChildT, Log2Dim>::addChild(ChildT* child)
1575{
1576 OPENVDB_ASSERT(child);
1577 const Coord& xyz = child->origin();
1578 // verify that the child belongs in this internal node
1579 if (Coord((xyz & ~(DIM-1))) != this->origin()) return false;
1580 // compute the offset and insert the child node
1581 const Index n = this->coordToOffset(xyz);
1582 // this also deletes an existing child node
1583 this->resetChildNode(n, child);
1584 return true;
1585}
1586
1587
1588template<typename ChildT, Index Log2Dim>
1589inline void
1590InternalNode<ChildT, Log2Dim>::addTile(Index n, const ValueType& value, bool state)
1591{
1592 OPENVDB_ASSERT(n < NUM_VALUES);
1593 this->makeChildNodeEmpty(n, value);
1594 mValueMask.set(n, state);
1595}
1596
1597
1598template<typename ChildT, Index Log2Dim>
1599inline void
1600InternalNode<ChildT, Log2Dim>::addTile(Index level, const Coord& xyz,
1601 const ValueType& value, bool state)
1602{
1603 if (LEVEL >= level) {
1604 const Index n = this->coordToOffset(xyz);
1605 if (mChildMask.isOff(n)) {// tile case
1606 if (LEVEL > level) {
1607 ChildT* child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n));
1608 this->setChildNode(n, child);
1609 child->addTile(level, xyz, value, state);
1610 } else {
1611 mValueMask.set(n, state);
1612 mNodes[n].setValue(value);
1613 }
1614 } else {// child branch case
1615 ChildT* child = mNodes[n].getChild();
1616 if (LEVEL > level) {
1617 child->addTile(level, xyz, value, state);
1618 } else {
1619 delete child;
1620 mChildMask.setOff(n);
1621 mValueMask.set(n, state);
1622 mNodes[n].setValue(value);
1623 }
1624 }
1625 }
1626}
1627
1628
1629template<typename ChildT, Index Log2Dim>
1630template<typename AccessorT>
1631inline void
1632InternalNode<ChildT, Log2Dim>::addTileAndCache(Index level, const Coord& xyz,
1633 const ValueType& value, bool state, AccessorT& acc)
1634{
1635 if (LEVEL >= level) {
1636 const Index n = this->coordToOffset(xyz);
1637 if (mChildMask.isOff(n)) {// tile case
1638 if (LEVEL > level) {
1639 ChildT* child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n));
1640 this->setChildNode(n, child);
1641 acc.insert(xyz, child);
1642 child->addTileAndCache(level, xyz, value, state, acc);
1643 } else {
1644 mValueMask.set(n, state);
1645 mNodes[n].setValue(value);
1646 }
1647 } else {// child branch case
1648 ChildT* child = mNodes[n].getChild();
1649 if (LEVEL > level) {
1650 acc.insert(xyz, child);
1651 child->addTileAndCache(level, xyz, value, state, acc);
1652 } else {
1653 delete child;
1654 mChildMask.setOff(n);
1655 mValueMask.set(n, state);
1656 mNodes[n].setValue(value);
1657 }
1658 }
1659 }
1660}
1661
1662
1663////////////////////////////////////////
1664
1665
1666template<typename ChildT, Index Log2Dim>
1667inline typename ChildT::LeafNodeType*
1668InternalNode<ChildT, Log2Dim>::touchLeaf(const Coord& xyz)
1669{
1670 const Index n = this->coordToOffset(xyz);
1671 ChildT* child = nullptr;
1672 if (mChildMask.isOff(n)) {
1673 child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n));
1674 this->setChildNode(n, child);
1675 } else {
1676 child = mNodes[n].getChild();
1677 }
1678 return child->touchLeaf(xyz);
1679}
1680
1681
1682template<typename ChildT, Index Log2Dim>
1683template<typename AccessorT>
1684inline typename ChildT::LeafNodeType*
1685InternalNode<ChildT, Log2Dim>::touchLeafAndCache(const Coord& xyz, AccessorT& acc)
1686{
1687 const Index n = this->coordToOffset(xyz);
1688 if (mChildMask.isOff(n)) {
1689 this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), mValueMask.isOn(n)));
1690 }
1691 acc.insert(xyz, mNodes[n].getChild());
1692 return mNodes[n].getChild()->touchLeafAndCache(xyz, acc);
1693}
1694
1695
1696////////////////////////////////////////
1697
1698
1699template<typename ChildT, Index Log2Dim>
1700inline bool
1701InternalNode<ChildT, Log2Dim>::isConstant(ValueType& firstValue, bool& state,
1702 const ValueType& tolerance) const
1703{
1704 if (!mChildMask.isOff() || !mValueMask.isConstant(state)) return false;// early termination
1705
1706 firstValue = mNodes[0].getValue();
1707 for (Index i = 1; i < NUM_VALUES; ++i) {
1708 if (!math::isApproxEqual(mNodes[i].getValue(), firstValue, tolerance)) {
1709 return false; // early termination
1710 }
1711 }
1712 return true;
1713}
1714
1715
1716////////////////////////////////////////
1717
1718
1719template<typename ChildT, Index Log2Dim>
1720inline bool
1721InternalNode<ChildT, Log2Dim>::isConstant(ValueType& minValue,
1722 ValueType& maxValue,
1723 bool& state,
1724 const ValueType& tolerance) const
1725{
1726
1727 if (!mChildMask.isOff() || !mValueMask.isConstant(state)) return false;// early termination
1728 minValue = maxValue = mNodes[0].getValue();
1729 for (Index i = 1; i < NUM_VALUES; ++i) {
1730 const ValueType& v = mNodes[i].getValue();
1731 if (v < minValue) {
1732 if ((maxValue - v) > tolerance) return false;// early termination
1733 minValue = v;
1734 } else if (v > maxValue) {
1735 if ((v - minValue) > tolerance) return false;// early termination
1736 maxValue = v;
1737 }
1738 }
1739 return true;
1740}
1741
1742
1743////////////////////////////////////////
1744
1745
1746template<typename ChildT, Index Log2Dim>
1747inline bool
1748InternalNode<ChildT, Log2Dim>::hasActiveTiles() const
1749{
1751 const bool anyActiveTiles = !mValueMask.isOff();
1752 if (LEVEL==1 || anyActiveTiles) return anyActiveTiles;
1753 for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
1754 if (iter->hasActiveTiles()) return true;
1755 }
1756 return false;
1758}
1759
1760
1761template<typename ChildT, Index Log2Dim>
1762inline bool
1763InternalNode<ChildT, Log2Dim>::isValueOn(const Coord& xyz) const
1764{
1765 const Index n = this->coordToOffset(xyz);
1766 return this->isChildMaskOff(n) ? this->isValueMaskOn(n)
1767 : mNodes[n].getChild()->isValueOn(xyz);
1768}
1769
1770template<typename ChildT, Index Log2Dim>
1771inline bool
1772InternalNode<ChildT, Log2Dim>::isValueOff(const Coord& xyz) const
1773{
1774 const Index n = this->coordToOffset(xyz);
1775 return this->isChildMaskOff(n) ? this->isValueMaskOn(n)
1776 : mNodes[n].getChild()->isValueOff(xyz);
1777}
1778
1779template<typename ChildT, Index Log2Dim>
1780template<typename AccessorT>
1781inline bool
1782InternalNode<ChildT, Log2Dim>::isValueOnAndCache(const Coord& xyz, AccessorT& acc) const
1783{
1784 const Index n = this->coordToOffset(xyz);
1785 if (this->isChildMaskOff(n)) return this->isValueMaskOn(n);
1786 acc.insert(xyz, mNodes[n].getChild());
1787 return mNodes[n].getChild()->isValueOnAndCache(xyz, acc);
1788}
1789
1790
1791template<typename ChildT, Index Log2Dim>
1792inline const typename ChildT::ValueType&
1793InternalNode<ChildT, Log2Dim>::getValue(const Coord& xyz) const
1794{
1795 const Index n = this->coordToOffset(xyz);
1796 return this->isChildMaskOff(n) ? mNodes[n].getValue()
1797 : mNodes[n].getChild()->getValue(xyz);
1798}
1799
1800template<typename ChildT, Index Log2Dim>
1801template<typename AccessorT>
1802inline const typename ChildT::ValueType&
1803InternalNode<ChildT, Log2Dim>::getValueAndCache(const Coord& xyz, AccessorT& acc) const
1804{
1805 const Index n = this->coordToOffset(xyz);
1806 if (this->isChildMaskOn(n)) {
1807 acc.insert(xyz, mNodes[n].getChild());
1808 return mNodes[n].getChild()->getValueAndCache(xyz, acc);
1809 }
1810 return mNodes[n].getValue();
1811}
1812
1813
1814template<typename ChildT, Index Log2Dim>
1815inline Index
1816InternalNode<ChildT, Log2Dim>::getValueLevel(const Coord& xyz) const
1817{
1818 const Index n = this->coordToOffset(xyz);
1819 return this->isChildMaskOff(n) ? LEVEL : mNodes[n].getChild()->getValueLevel(xyz);
1820}
1821
1822template<typename ChildT, Index Log2Dim>
1823template<typename AccessorT>
1824inline Index
1825InternalNode<ChildT, Log2Dim>::getValueLevelAndCache(const Coord& xyz, AccessorT& acc) const
1826{
1827 const Index n = this->coordToOffset(xyz);
1828 if (this->isChildMaskOn(n)) {
1829 acc.insert(xyz, mNodes[n].getChild());
1830 return mNodes[n].getChild()->getValueLevelAndCache(xyz, acc);
1831 }
1832 return LEVEL;
1833}
1834
1835
1836template<typename ChildT, Index Log2Dim>
1837inline bool
1838InternalNode<ChildT, Log2Dim>::probeValue(const Coord& xyz, ValueType& value) const
1839{
1840 const Index n = this->coordToOffset(xyz);
1841 if (this->isChildMaskOff(n)) {
1842 value = mNodes[n].getValue();
1843 return this->isValueMaskOn(n);
1844 }
1845 return mNodes[n].getChild()->probeValue(xyz, value);
1846}
1847
1848template<typename ChildT, Index Log2Dim>
1849template<typename AccessorT>
1850inline bool
1851InternalNode<ChildT, Log2Dim>::probeValueAndCache(const Coord& xyz,
1852 ValueType& value, AccessorT& acc) const
1853{
1854 const Index n = this->coordToOffset(xyz);
1855 if (this->isChildMaskOn(n)) {
1856 acc.insert(xyz, mNodes[n].getChild());
1857 return mNodes[n].getChild()->probeValueAndCache(xyz, value, acc);
1858 }
1859 value = mNodes[n].getValue();
1860 return this->isValueMaskOn(n);
1861}
1862
1863
1864template<typename ChildT, Index Log2Dim>
1865inline void
1866InternalNode<ChildT, Log2Dim>::setValueOff(const Coord& xyz)
1867{
1868 const Index n = this->coordToOffset(xyz);
1869 bool hasChild = this->isChildMaskOn(n);
1870 if (!hasChild && this->isValueMaskOn(n)) {
1871 // If the voxel belongs to a constant tile that is active,
1872 // a child subtree must be constructed.
1873 hasChild = true;
1874 this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), /*active=*/true));
1875 }
1876 if (hasChild) mNodes[n].getChild()->setValueOff(xyz);
1877}
1878
1879
1880template<typename ChildT, Index Log2Dim>
1881inline void
1882InternalNode<ChildT, Log2Dim>::setValueOn(const Coord& xyz)
1883{
1884 const Index n = this->coordToOffset(xyz);
1885 bool hasChild = this->isChildMaskOn(n);
1886 if (!hasChild && !this->isValueMaskOn(n)) {
1887 // If the voxel belongs to a constant tile that is inactive,
1888 // a child subtree must be constructed.
1889 hasChild = true;
1890 this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), /*active=*/false));
1891 }
1892 if (hasChild) mNodes[n].getChild()->setValueOn(xyz);
1893}
1894
1895
1896template<typename ChildT, Index Log2Dim>
1897inline void
1898InternalNode<ChildT, Log2Dim>::setValueOff(const Coord& xyz, const ValueType& value)
1899{
1900 const Index n = InternalNode::coordToOffset(xyz);
1901 bool hasChild = this->isChildMaskOn(n);
1902 if (!hasChild) {
1903 const bool active = this->isValueMaskOn(n);
1904 if (active || !math::isExactlyEqual(mNodes[n].getValue(), value)) {
1905 // If the voxel belongs to a tile that is either active or that
1906 // has a constant value that is different from the one provided,
1907 // a child subtree must be constructed.
1908 hasChild = true;
1909 this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active));
1910 }
1911 }
1912 if (hasChild) mNodes[n].getChild()->setValueOff(xyz, value);
1913}
1914
1915template<typename ChildT, Index Log2Dim>
1916template<typename AccessorT>
1917inline void
1918InternalNode<ChildT, Log2Dim>::setValueOffAndCache(const Coord& xyz,
1919 const ValueType& value, AccessorT& acc)
1920{
1921 const Index n = InternalNode::coordToOffset(xyz);
1922 bool hasChild = this->isChildMaskOn(n);
1923 if (!hasChild) {
1924 const bool active = this->isValueMaskOn(n);
1925 if (active || !math::isExactlyEqual(mNodes[n].getValue(), value)) {
1926 // If the voxel belongs to a tile that is either active or that
1927 // has a constant value that is different from the one provided,
1928 // a child subtree must be constructed.
1929 hasChild = true;
1930 this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active));
1931 }
1932 }
1933 if (hasChild) {
1934 ChildT* child = mNodes[n].getChild();
1935 acc.insert(xyz, child);
1936 child->setValueOffAndCache(xyz, value, acc);
1937 }
1938}
1939
1940
1941template<typename ChildT, Index Log2Dim>
1942inline void
1943InternalNode<ChildT, Log2Dim>::setValueOn(const Coord& xyz, const ValueType& value)
1944{
1945 const Index n = this->coordToOffset(xyz);
1946 bool hasChild = this->isChildMaskOn(n);
1947 if (!hasChild) {
1948 const bool active = this->isValueMaskOn(n); // tile's active state
1949 if (!active || !math::isExactlyEqual(mNodes[n].getValue(), value)) {
1950 // If the voxel belongs to a tile that is either inactive or that
1951 // has a constant value that is different from the one provided,
1952 // a child subtree must be constructed.
1953 hasChild = true;
1954 this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active));
1955 }
1956 }
1957 if (hasChild) mNodes[n].getChild()->setValueOn(xyz, value);
1958}
1959
1960template<typename ChildT, Index Log2Dim>
1961template<typename AccessorT>
1962inline void
1963InternalNode<ChildT, Log2Dim>::setValueAndCache(const Coord& xyz,
1964 const ValueType& value, AccessorT& acc)
1965{
1966 const Index n = this->coordToOffset(xyz);
1967 bool hasChild = this->isChildMaskOn(n);
1968 if (!hasChild) {
1969 const bool active = this->isValueMaskOn(n);
1970 if (!active || !math::isExactlyEqual(mNodes[n].getValue(), value)) {
1971 // If the voxel belongs to a tile that is either inactive or that
1972 // has a constant value that is different from the one provided,
1973 // a child subtree must be constructed.
1974 hasChild = true;
1975 this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active));
1976 }
1977 }
1978 if (hasChild) {
1979 acc.insert(xyz, mNodes[n].getChild());
1980 mNodes[n].getChild()->setValueAndCache(xyz, value, acc);
1981 }
1982}
1983
1984
1985template<typename ChildT, Index Log2Dim>
1986inline void
1987InternalNode<ChildT, Log2Dim>::setValueOnly(const Coord& xyz, const ValueType& value)
1988{
1989 const Index n = this->coordToOffset(xyz);
1990 bool hasChild = this->isChildMaskOn(n);
1991 if (!hasChild && !math::isExactlyEqual(mNodes[n].getValue(), value)) {
1992 // If the voxel has a tile value that is different from the one provided,
1993 // a child subtree must be constructed.
1994 const bool active = this->isValueMaskOn(n);
1995 hasChild = true;
1996 this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active));
1997 }
1998 if (hasChild) mNodes[n].getChild()->setValueOnly(xyz, value);
1999}
2000
2001template<typename ChildT, Index Log2Dim>
2002template<typename AccessorT>
2003inline void
2004InternalNode<ChildT, Log2Dim>::setValueOnlyAndCache(const Coord& xyz,
2005 const ValueType& value, AccessorT& acc)
2006{
2007 const Index n = this->coordToOffset(xyz);
2008 bool hasChild = this->isChildMaskOn(n);
2009 if (!hasChild && !math::isExactlyEqual(mNodes[n].getValue(), value)) {
2010 // If the voxel has a tile value that is different from the one provided,
2011 // a child subtree must be constructed.
2012 const bool active = this->isValueMaskOn(n);
2013 hasChild = true;
2014 this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active));
2015 }
2016 if (hasChild) {
2017 acc.insert(xyz, mNodes[n].getChild());
2018 mNodes[n].getChild()->setValueOnlyAndCache(xyz, value, acc);
2019 }
2020}
2021
2022
2023template<typename ChildT, Index Log2Dim>
2024inline void
2025InternalNode<ChildT, Log2Dim>::setActiveState(const Coord& xyz, bool on)
2026{
2027 const Index n = this->coordToOffset(xyz);
2028 bool hasChild = this->isChildMaskOn(n);
2029 if (!hasChild) {
2030 if (on != this->isValueMaskOn(n)) {
2031 // If the voxel belongs to a tile with the wrong active state,
2032 // then a child subtree must be constructed.
2033 // 'on' is the voxel's new state, therefore '!on' is the tile's current state
2034 hasChild = true;
2035 this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), !on));
2036 }
2037 }
2038 if (hasChild) mNodes[n].getChild()->setActiveState(xyz, on);
2039}
2040
2041template<typename ChildT, Index Log2Dim>
2042template<typename AccessorT>
2043inline void
2044InternalNode<ChildT, Log2Dim>::setActiveStateAndCache(const Coord& xyz, bool on, AccessorT& acc)
2045{
2046 const Index n = this->coordToOffset(xyz);
2047 bool hasChild = this->isChildMaskOn(n);
2048 if (!hasChild) {
2049 if (on != this->isValueMaskOn(n)) {
2050 // If the voxel belongs to a tile with the wrong active state,
2051 // then a child subtree must be constructed.
2052 // 'on' is the voxel's new state, therefore '!on' is the tile's current state
2053 hasChild = true;
2054 this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), !on));
2055 }
2056 }
2057 if (hasChild) {
2058 ChildT* child = mNodes[n].getChild();
2059 acc.insert(xyz, child);
2060 child->setActiveStateAndCache(xyz, on, acc);
2061 }
2062}
2063
2064
2065template<typename ChildT, Index Log2Dim>
2066inline void
2067InternalNode<ChildT, Log2Dim>::setValuesOn()
2068{
2069 mValueMask = !mChildMask;
2070 for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) {
2071 mNodes[iter.pos()].getChild()->setValuesOn();
2072 }
2073}
2074
2075
2076template<typename ChildT, Index Log2Dim>
2077template<typename ModifyOp>
2078inline void
2079InternalNode<ChildT, Log2Dim>::modifyValue(const Coord& xyz, const ModifyOp& op)
2080{
2081 const Index n = InternalNode::coordToOffset(xyz);
2082 bool hasChild = this->isChildMaskOn(n);
2083 if (!hasChild) {
2084 // Need to create a child if the tile is inactive,
2085 // in order to activate voxel (x, y, z).
2086 const bool active = this->isValueMaskOn(n);
2087 bool createChild = !active;
2088 if (!createChild) {
2089 // Need to create a child if applying the functor
2090 // to the tile value produces a different value.
2091 const ValueType& tileVal = mNodes[n].getValue();
2092 ValueType modifiedVal = tileVal;
2093 op(modifiedVal);
2094 createChild = !math::isExactlyEqual(tileVal, modifiedVal);
2095 }
2096 if (createChild) {
2097 hasChild = true;
2098 this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active));
2099 }
2100 }
2101 if (hasChild) mNodes[n].getChild()->modifyValue(xyz, op);
2102}
2103
2104template<typename ChildT, Index Log2Dim>
2105template<typename ModifyOp, typename AccessorT>
2106inline void
2107InternalNode<ChildT, Log2Dim>::modifyValueAndCache(const Coord& xyz, const ModifyOp& op,
2108 AccessorT& acc)
2109{
2110 const Index n = InternalNode::coordToOffset(xyz);
2111 bool hasChild = this->isChildMaskOn(n);
2112 if (!hasChild) {
2113 // Need to create a child if the tile is inactive,
2114 // in order to activate voxel (x, y, z).
2115 const bool active = this->isValueMaskOn(n);
2116 bool createChild = !active;
2117 if (!createChild) {
2118 // Need to create a child if applying the functor
2119 // to the tile value produces a different value.
2120 const ValueType& tileVal = mNodes[n].getValue();
2121 ValueType modifiedVal = tileVal;
2122 op(modifiedVal);
2123 createChild = !math::isExactlyEqual(tileVal, modifiedVal);
2124 }
2125 if (createChild) {
2126 hasChild = true;
2127 this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active));
2128 }
2129 }
2130 if (hasChild) {
2131 ChildNodeType* child = mNodes[n].getChild();
2132 acc.insert(xyz, child);
2133 child->modifyValueAndCache(xyz, op, acc);
2134 }
2135}
2136
2137
2138template<typename ChildT, Index Log2Dim>
2139template<typename ModifyOp>
2140inline void
2141InternalNode<ChildT, Log2Dim>::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op)
2142{
2143 const Index n = InternalNode::coordToOffset(xyz);
2144 bool hasChild = this->isChildMaskOn(n);
2145 if (!hasChild) {
2146 const bool tileState = this->isValueMaskOn(n);
2147 const ValueType& tileVal = mNodes[n].getValue();
2148 bool modifiedState = !tileState;
2149 ValueType modifiedVal = tileVal;
2150 op(modifiedVal, modifiedState);
2151 // Need to create a child if applying the functor to the tile
2152 // produces a different value or active state.
2153 if (modifiedState != tileState || !math::isExactlyEqual(modifiedVal, tileVal)) {
2154 hasChild = true;
2155 this->setChildNode(n, new ChildNodeType(xyz, tileVal, tileState));
2156 }
2157 }
2158 if (hasChild) mNodes[n].getChild()->modifyValueAndActiveState(xyz, op);
2159}
2160
2161template<typename ChildT, Index Log2Dim>
2162template<typename ModifyOp, typename AccessorT>
2163inline void
2164InternalNode<ChildT, Log2Dim>::modifyValueAndActiveStateAndCache(
2165 const Coord& xyz, const ModifyOp& op, AccessorT& acc)
2166{
2167 const Index n = InternalNode::coordToOffset(xyz);
2168 bool hasChild = this->isChildMaskOn(n);
2169 if (!hasChild) {
2170 const bool tileState = this->isValueMaskOn(n);
2171 const ValueType& tileVal = mNodes[n].getValue();
2172 bool modifiedState = !tileState;
2173 ValueType modifiedVal = tileVal;
2174 op(modifiedVal, modifiedState);
2175 // Need to create a child if applying the functor to the tile
2176 // produces a different value or active state.
2177 if (modifiedState != tileState || !math::isExactlyEqual(modifiedVal, tileVal)) {
2178 hasChild = true;
2179 this->setChildNode(n, new ChildNodeType(xyz, tileVal, tileState));
2180 }
2181 }
2182 if (hasChild) {
2183 ChildNodeType* child = mNodes[n].getChild();
2184 acc.insert(xyz, child);
2185 child->modifyValueAndActiveStateAndCache(xyz, op, acc);
2186 }
2187}
2188
2189
2190////////////////////////////////////////
2191
2192
2193template<typename ChildT, Index Log2Dim>
2194inline void
2195InternalNode<ChildT, Log2Dim>::clip(const CoordBBox& clipBBox, const ValueType& background)
2196{
2197 CoordBBox nodeBBox = this->getNodeBoundingBox();
2198 if (!clipBBox.hasOverlap(nodeBBox)) {
2199 // This node lies completely outside the clipping region. Fill it with background tiles.
2200 this->fill(nodeBBox, background, /*active=*/false);
2201 } else if (clipBBox.isInside(nodeBBox)) {
2202 // This node lies completely inside the clipping region. Leave it intact.
2203 return;
2204 }
2205
2206 // This node isn't completely contained inside the clipping region.
2207 // Clip tiles and children, and replace any that lie outside the region
2208 // with background tiles.
2209
2210 for (Index pos = 0; pos < NUM_VALUES; ++pos) {
2211 const Coord xyz = this->offsetToGlobalCoord(pos); // tile or child origin
2212 CoordBBox tileBBox(xyz, xyz.offsetBy(ChildT::DIM - 1)); // tile or child bounds
2213 if (!clipBBox.hasOverlap(tileBBox)) {
2214 // This table entry lies completely outside the clipping region.
2215 // Replace it with a background tile.
2216 this->makeChildNodeEmpty(pos, background);
2217 mValueMask.setOff(pos);
2218 } else if (!clipBBox.isInside(tileBBox)) {
2219 // This table entry does not lie completely inside the clipping region
2220 // and must be clipped.
2221 if (this->isChildMaskOn(pos)) {
2222 mNodes[pos].getChild()->clip(clipBBox, background);
2223 } else {
2224 // Replace this tile with a background tile, then fill the clip region
2225 // with the tile's original value. (This might create a child branch.)
2226 tileBBox.intersect(clipBBox);
2227 const ValueType val = mNodes[pos].getValue();
2228 const bool on = this->isValueMaskOn(pos);
2229 mNodes[pos].setValue(background);
2230 mValueMask.setOff(pos);
2231 this->fill(tileBBox, val, on);
2232 }
2233 } else {
2234 // This table entry lies completely inside the clipping region. Leave it intact.
2235 }
2236 }
2237}
2238
2239
2240////////////////////////////////////////
2241
2242
2243template<typename ChildT, Index Log2Dim>
2244inline void
2245InternalNode<ChildT, Log2Dim>::fill(const CoordBBox& bbox, const ValueType& value, bool active)
2246{
2247 auto clippedBBox = this->getNodeBoundingBox();
2248 clippedBBox.intersect(bbox);
2249 if (!clippedBBox) return;
2250
2251 // Iterate over the fill region in axis-aligned, tile-sized chunks.
2252 // (The first and last chunks along each axis might be smaller than a tile.)
2253 Coord xyz, tileMin, tileMax;
2254 for (int x = clippedBBox.min().x(); x <= clippedBBox.max().x(); x = tileMax.x() + 1) {
2255 xyz.setX(x);
2256 for (int y = clippedBBox.min().y(); y <= clippedBBox.max().y(); y = tileMax.y() + 1) {
2257 xyz.setY(y);
2258 for (int z = clippedBBox.min().z(); z <= clippedBBox.max().z(); z = tileMax.z() + 1) {
2259 xyz.setZ(z);
2260
2261 // Get the bounds of the tile that contains voxel (x, y, z).
2262 const Index n = this->coordToOffset(xyz);
2263 tileMin = this->offsetToGlobalCoord(n);
2264 tileMax = tileMin.offsetBy(ChildT::DIM - 1);
2265
2266 if (xyz != tileMin || Coord::lessThan(clippedBBox.max(), tileMax)) {
2267 // If the box defined by (xyz, clippedBBox.max()) doesn't completely enclose
2268 // the tile to which xyz belongs, create a child node (or retrieve
2269 // the existing one).
2270 ChildT* child = nullptr;
2271 if (this->isChildMaskOff(n)) {
2272 // Replace the tile with a newly-created child that is initialized
2273 // with the tile's value and active state.
2274 child = new ChildT{xyz, mNodes[n].getValue(), this->isValueMaskOn(n)};
2275 this->setChildNode(n, child);
2276 } else {
2277 child = mNodes[n].getChild();
2278 }
2279
2280 // Forward the fill request to the child.
2281 if (child) {
2282 const Coord tmp = Coord::minComponent(clippedBBox.max(), tileMax);
2283 child->fill(CoordBBox(xyz, tmp), value, active);
2284 }
2285
2286 } else {
2287 // If the box given by (xyz, clippedBBox.max()) completely encloses
2288 // the tile to which xyz belongs, create the tile (if it
2289 // doesn't already exist) and give it the fill value.
2290 this->makeChildNodeEmpty(n, value);
2291 mValueMask.set(n, active);
2292 }
2293 }
2294 }
2295 }
2296}
2297
2298
2299template<typename ChildT, Index Log2Dim>
2300inline void
2301InternalNode<ChildT, Log2Dim>::denseFill(const CoordBBox& bbox, const ValueType& value, bool active)
2302{
2303 auto clippedBBox = this->getNodeBoundingBox();
2304 clippedBBox.intersect(bbox);
2305 if (!clippedBBox) return;
2306
2307 // Iterate over the fill region in axis-aligned, tile-sized chunks.
2308 // (The first and last chunks along each axis might be smaller than a tile.)
2309 Coord xyz, tileMin, tileMax;
2310 for (int x = clippedBBox.min().x(); x <= clippedBBox.max().x(); x = tileMax.x() + 1) {
2311 xyz.setX(x);
2312 for (int y = clippedBBox.min().y(); y <= clippedBBox.max().y(); y = tileMax.y() + 1) {
2313 xyz.setY(y);
2314 for (int z = clippedBBox.min().z(); z <= clippedBBox.max().z(); z = tileMax.z() + 1) {
2315 xyz.setZ(z);
2316
2317 // Get the table index of the tile that contains voxel (x, y, z).
2318 const auto n = this->coordToOffset(xyz);
2319
2320 // Retrieve the child node at index n, or replace the tile at index n with a child.
2321 ChildT* child = nullptr;
2322 if (this->isChildMaskOn(n)) {
2323 child = mNodes[n].getChild();
2324 } else {
2325 // Replace the tile with a newly-created child that is filled
2326 // with the tile's value and active state.
2327 child = new ChildT{xyz, mNodes[n].getValue(), this->isValueMaskOn(n)};
2328 this->setChildNode(n, child);
2329 }
2330
2331 // Get the bounds of the tile that contains voxel (x, y, z).
2332 tileMin = this->offsetToGlobalCoord(n);
2333 tileMax = tileMin.offsetBy(ChildT::DIM - 1);
2334
2335 // Forward the fill request to the child.
2336 child->denseFill(CoordBBox{xyz, clippedBBox.max()}, value, active);
2337 }
2338 }
2339 }
2340}
2341
2342
2343////////////////////////////////////////
2344
2345
2346template<typename ChildT, Index Log2Dim>
2347template<typename DenseT>
2348inline void
2349InternalNode<ChildT, Log2Dim>::copyToDense(const CoordBBox& bbox, DenseT& dense) const
2350{
2351 using DenseValueType = typename DenseT::ValueType;
2352
2353 const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride();
2354 const Coord& min = dense.bbox().min();
2355 for (Coord xyz = bbox.min(), max; xyz[0] <= bbox.max()[0]; xyz[0] = max[0] + 1) {
2356 for (xyz[1] = bbox.min()[1]; xyz[1] <= bbox.max()[1]; xyz[1] = max[1] + 1) {
2357 for (xyz[2] = bbox.min()[2]; xyz[2] <= bbox.max()[2]; xyz[2] = max[2] + 1) {
2358 const Index n = this->coordToOffset(xyz);
2359 // Get max coordinates of the child node that contains voxel xyz.
2360 max = this->offsetToGlobalCoord(n).offsetBy(ChildT::DIM-1);
2361
2362 // Get the bbox of the interection of bbox and the child node
2363 CoordBBox sub(xyz, Coord::minComponent(bbox.max(), max));
2364
2365 if (this->isChildMaskOn(n)) {//is a child
2366 mNodes[n].getChild()->copyToDense(sub, dense);
2367 } else {//a tile value
2368 const ValueType value = mNodes[n].getValue();
2369 sub.translate(-min);
2370 DenseValueType* a0 = dense.data() + zStride*sub.min()[2];
2371 for (Int32 x=sub.min()[0], ex=sub.max()[0]+1; x<ex; ++x) {
2372 DenseValueType* a1 = a0 + x*xStride;
2373 for (Int32 y=sub.min()[1], ey=sub.max()[1]+1; y<ey; ++y) {
2374 DenseValueType* a2 = a1 + y*yStride;
2375 for (Int32 z = sub.min()[2], ez = sub.max()[2]+1;
2376 z < ez; ++z, a2 += zStride)
2377 {
2378 *a2 = DenseValueType(value);
2379 }
2380 }
2381 }
2382 }
2383 }
2384 }
2385 }
2386}
2387
2388
2389////////////////////////////////////////
2390
2391
2392template<typename ChildT, Index Log2Dim>
2393inline void
2394InternalNode<ChildT, Log2Dim>::writeTopology(std::ostream& os, bool toHalf) const
2395{
2396 mChildMask.save(os);
2397 mValueMask.save(os);
2398
2399 {
2400 // Copy all of this node's values into an array.
2401 std::unique_ptr<ValueType[]> valuePtr(new ValueType[NUM_VALUES]);
2402 ValueType* values = valuePtr.get();
2403 const ValueType zero = zeroVal<ValueType>();
2404 for (Index i = 0; i < NUM_VALUES; ++i) {
2405 values[i] = (mChildMask.isOff(i) ? mNodes[i].getValue() : zero);
2406 }
2407 // Compress (optionally) and write out the contents of the array.
2408 io::writeCompressedValues(os, values, NUM_VALUES, mValueMask, mChildMask, toHalf);
2409 }
2410 // Write out the child nodes in order.
2411 for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
2412 iter->writeTopology(os, toHalf);
2413 }
2414}
2415
2416
2417template<typename ChildT, Index Log2Dim>
2418inline void
2419InternalNode<ChildT, Log2Dim>::readTopology(std::istream& is, bool fromHalf)
2420{
2421 io::checkFormatVersion(is);
2422
2423 const ValueType background = (!io::getGridBackgroundValuePtr(is) ? zeroVal<ValueType>()
2424 : *static_cast<const ValueType*>(io::getGridBackgroundValuePtr(is)));
2425
2426 mChildMask.load(is);
2427 mValueMask.load(is);
2428
2429 const Index numValues = NUM_VALUES;
2430 {
2431 // Read in (and uncompress, if necessary) all of this node's values
2432 // into a contiguous array.
2433 std::unique_ptr<ValueType[]> valuePtr(new ValueType[numValues]);
2434 ValueType* values = valuePtr.get();
2435 io::readCompressedValues(is, values, numValues, mValueMask, fromHalf);
2436
2437 // Copy values from the array into this node's table.
2438 for (ValueAllIter iter = this->beginValueAll(); iter; ++iter) {
2439 mNodes[iter.pos()].setValue(values[iter.pos()]);
2440 }
2441 }
2442
2443 // Read in all child nodes and insert them into the table at their proper locations.
2444 for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) {
2445 ChildNodeType* child = new ChildNodeType(PartialCreate(), iter.getCoord(), background);
2446 mNodes[iter.pos()].setChild(child);
2447 child->readTopology(is, fromHalf);
2448 }
2449}
2450
2451
2452////////////////////////////////////////
2453
2454
2455template<typename ChildT, Index Log2Dim>
2456inline const typename ChildT::ValueType&
2457InternalNode<ChildT, Log2Dim>::getFirstValue() const
2458{
2459 return (this->isChildMaskOn(0) ? mNodes[0].getChild()->getFirstValue() : mNodes[0].getValue());
2460}
2461
2462
2463template<typename ChildT, Index Log2Dim>
2464inline const typename ChildT::ValueType&
2465InternalNode<ChildT, Log2Dim>::getLastValue() const
2466{
2467 const Index n = NUM_VALUES - 1;
2468 return (this->isChildMaskOn(n) ? mNodes[n].getChild()->getLastValue() : mNodes[n].getValue());
2469}
2470
2471
2472////////////////////////////////////////
2473
2474
2475template<typename ChildT, Index Log2Dim>
2476inline const typename ChildT::ValueType&
2477InternalNode<ChildT, Log2Dim>::getValueUnsafe(Index n) const
2478{
2479 OPENVDB_ASSERT(n < NUM_VALUES);
2480 OPENVDB_ASSERT(mChildMask.isOff(n));
2481 return mNodes[n].getValue();
2482}
2483
2484template<typename ChildT, Index Log2Dim>
2485inline bool
2486InternalNode<ChildT, Log2Dim>::getValueUnsafe(Index n, ValueType& value) const
2487{
2488 OPENVDB_ASSERT(n < NUM_VALUES);
2489 OPENVDB_ASSERT(mChildMask.isOff(n));
2490 value = mNodes[n].getValue();
2491 return mValueMask.isOn(n);
2492}
2493
2494template<typename ChildT, Index Log2Dim>
2495inline ChildT*
2496InternalNode<ChildT, Log2Dim>::getChildUnsafe(Index n)
2497{
2498 OPENVDB_ASSERT(n < NUM_VALUES);
2499 OPENVDB_ASSERT(mChildMask.isOn(n));
2500 return mNodes[n].getChild();
2501}
2502
2503template<typename ChildT, Index Log2Dim>
2504inline const ChildT*
2505InternalNode<ChildT, Log2Dim>::getConstChildUnsafe(Index n) const
2506{
2507 OPENVDB_ASSERT(n < NUM_VALUES);
2508 OPENVDB_ASSERT(mChildMask.isOn(n));
2509 return mNodes[n].getChild();
2510}
2511
2512template<typename ChildT, Index Log2Dim>
2513inline const ChildT*
2514InternalNode<ChildT, Log2Dim>::getChildUnsafe(Index n) const
2515{
2516 return this->getConstChildUnsafe(n);
2517}
2518
2519template<typename ChildT, Index Log2Dim>
2520inline void
2521InternalNode<ChildT, Log2Dim>::setActiveStateUnsafe(Index n, bool on)
2522{
2523 OPENVDB_ASSERT(n < NUM_VALUES);
2524 OPENVDB_ASSERT(mChildMask.isOff(n));
2525 mValueMask.set(n, on);
2526}
2527
2528template<typename ChildT, Index Log2Dim>
2529inline void
2530InternalNode<ChildT, Log2Dim>::setValueOnlyUnsafe(Index n, const ValueType& value)
2531{
2532 OPENVDB_ASSERT(n < NUM_VALUES);
2533 OPENVDB_ASSERT(mChildMask.isOff(n));
2534 mNodes[n].setValue(value);
2535}
2536
2537template<typename ChildT, Index Log2Dim>
2538inline void
2539InternalNode<ChildT, Log2Dim>::setValueOnUnsafe(Index n)
2540{
2541 OPENVDB_ASSERT(n < NUM_VALUES);
2542 OPENVDB_ASSERT(mChildMask.isOff(n));
2543 mValueMask.setOn(n);
2544}
2545
2546template<typename ChildT, Index Log2Dim>
2547inline void
2548InternalNode<ChildT, Log2Dim>::setValueOnUnsafe(Index n, const ValueType& value)
2549{
2550 OPENVDB_ASSERT(n < NUM_VALUES);
2551 OPENVDB_ASSERT(mChildMask.isOff(n));
2552 mNodes[n].setValue(value);
2553 mValueMask.setOn(n);
2554}
2555
2556template<typename ChildT, Index Log2Dim>
2557inline void
2558InternalNode<ChildT, Log2Dim>::setValueOffUnsafe(Index n)
2559{
2560 OPENVDB_ASSERT(n < NUM_VALUES);
2561 OPENVDB_ASSERT(mChildMask.isOff(n));
2562 mValueMask.setOff(n);
2563}
2564
2565template<typename ChildT, Index Log2Dim>
2566inline void
2567InternalNode<ChildT, Log2Dim>::setValueOffUnsafe(Index n, const ValueType& value)
2568{
2569 OPENVDB_ASSERT(n < NUM_VALUES);
2570 OPENVDB_ASSERT(mChildMask.isOff(n));
2571 mNodes[n].setValue(value);
2572 mValueMask.setOff(n);
2573}
2574
2575template<typename ChildT, Index Log2Dim>
2576inline void
2577InternalNode<ChildT, Log2Dim>::setValueMaskUnsafe(const NodeMaskType& mask)
2578{
2579 OPENVDB_ASSERT(mChildMask.isOff());
2580 mValueMask = mask;
2581}
2582
2583template<typename ChildT, Index Log2Dim>
2584inline void
2585InternalNode<ChildT, Log2Dim>::setChildUnsafe(Index n, ChildNodeType* child)
2586{
2587 // replace tile with child
2588 OPENVDB_ASSERT(n < NUM_VALUES);
2589 OPENVDB_ASSERT(mChildMask.isOff(n));
2590 mNodes[n].setChild(child);
2591 mChildMask.setOn(n);
2592 mValueMask.setOff(n);
2593}
2594
2595template<typename ChildT, Index Log2Dim>
2596inline void
2597InternalNode<ChildT, Log2Dim>::resetChildUnsafe(Index n, ChildNodeType* child)
2598{
2599 // replace child with child
2600 OPENVDB_ASSERT(child);
2601 OPENVDB_ASSERT(n < NUM_VALUES);
2602 OPENVDB_ASSERT(mChildMask.isOn(n));
2603 delete mNodes[n].getChild();
2604 mNodes[n].setChild(child);
2605}
2606
2607template<typename ChildT, Index Log2Dim>
2608inline ChildT*
2609InternalNode<ChildT, Log2Dim>::stealChildUnsafe(Index n, const ValueType& value, bool active)
2610{
2611 // replace child with tile (and return child)
2612 OPENVDB_ASSERT(n < NUM_VALUES);
2613 OPENVDB_ASSERT(mChildMask.isOn(n));
2614 auto* child = mNodes[n].getChild();
2615 mChildMask.setOff(n);
2616 mValueMask.set(n, active);
2617 mNodes[n].setValue(value);
2618 return child;
2619}
2620
2621template<typename ChildT, Index Log2Dim>
2622inline void
2623InternalNode<ChildT, Log2Dim>::deleteChildUnsafe(Index n, const ValueType& value, bool active)
2624{
2625 // replace child with tile (and delete child)
2626 delete this->stealChildUnsafe(n, value, active);
2627}
2628
2629
2630////////////////////////////////////////
2631
2632
2633template<typename ChildT, Index Log2Dim>
2634inline void
2635InternalNode<ChildT, Log2Dim>::negate()
2636{
2637 for (Index i = 0; i < NUM_VALUES; ++i) {
2638 if (this->isChildMaskOn(i)) {
2639 mNodes[i].getChild()->negate();
2640 } else {
2641 mNodes[i].setValue(math::negative(mNodes[i].getValue()));
2642 }
2643 }
2644
2645}
2646
2647
2648////////////////////////////////////////
2649
2650
2651template<typename ChildT, Index Log2Dim>
2652struct InternalNode<ChildT, Log2Dim>::VoxelizeActiveTiles
2653{
2654 VoxelizeActiveTiles(InternalNode &node) : mNode(&node) {
2655 //(*this)(tbb::blocked_range<Index>(0, NUM_VALUES));//single thread for debugging
2656 tbb::parallel_for(tbb::blocked_range<Index>(0, NUM_VALUES), *this);
2657
2658 node.mChildMask |= node.mValueMask;
2659 node.mValueMask.setOff();
2660 }
2661 void operator()(const tbb::blocked_range<Index> &r) const
2662 {
2663 for (Index i = r.begin(), end=r.end(); i!=end; ++i) {
2664 if (mNode->mChildMask.isOn(i)) {// Loop over node's child nodes
2665 mNode->mNodes[i].getChild()->voxelizeActiveTiles(true);
2666 } else if (mNode->mValueMask.isOn(i)) {// Loop over node's active tiles
2667 const Coord &ijk = mNode->offsetToGlobalCoord(i);
2668 ChildNodeType *child = new ChildNodeType(ijk, mNode->mNodes[i].getValue(), true);
2669 child->voxelizeActiveTiles(true);
2670 mNode->mNodes[i].setChild(child);
2671 }
2672 }
2673 }
2674 InternalNode* mNode;
2675};// VoxelizeActiveTiles
2676
2677template<typename ChildT, Index Log2Dim>
2678inline void
2679InternalNode<ChildT, Log2Dim>::voxelizeActiveTiles(bool threaded)
2680{
2681 if (threaded) {
2682 VoxelizeActiveTiles tmp(*this);
2683 } else {
2684 for (ValueOnIter iter = this->beginValueOn(); iter; ++iter) {
2685 this->setChildNode(iter.pos(),
2686 new ChildNodeType(iter.getCoord(), iter.getValue(), true));
2687 }
2688 for (ChildOnIter iter = this->beginChildOn(); iter; ++iter)
2689 iter->voxelizeActiveTiles(false);
2690 }
2691}
2692
2693
2694////////////////////////////////////////
2695
2696
2697template<typename ChildT, Index Log2Dim>
2698template<MergePolicy Policy>
2699inline void
2700InternalNode<ChildT, Log2Dim>::merge(InternalNode& other,
2701 const ValueType& background, const ValueType& otherBackground)
2702{
2704
2705 switch (Policy) {
2706
2708 default:
2709 {
2710 for (ChildOnIter iter = other.beginChildOn(); iter; ++iter) {
2711 const Index n = iter.pos();
2712 if (mChildMask.isOn(n)) {
2713 // Merge this node's child with the other node's child.
2714 mNodes[n].getChild()->template merge<MERGE_ACTIVE_STATES>(*iter,
2715 background, otherBackground);
2716 } else if (mValueMask.isOff(n)) {
2717 // Replace this node's inactive tile with the other node's child
2718 // and replace the other node's child with a tile of undefined value
2719 // (which is okay since the other tree is assumed to be cannibalized
2720 // in the process of merging).
2721 ChildNodeType* child = other.mNodes[n].getChild();
2722 other.mChildMask.setOff(n);
2723 child->resetBackground(otherBackground, background);
2724 this->setChildNode(n, child);
2725 }
2726 }
2727
2728 // Copy active tile values.
2729 for (ValueOnCIter iter = other.cbeginValueOn(); iter; ++iter) {
2730 const Index n = iter.pos();
2731 if (mValueMask.isOff(n)) {
2732 // Replace this node's child or inactive tile with the other node's active tile.
2733 this->makeChildNodeEmpty(n, iter.getValue());
2734 mValueMask.setOn(n);
2735 }
2736 }
2737 break;
2738 }
2739
2740 case MERGE_NODES:
2741 {
2742 for (ChildOnIter iter = other.beginChildOn(); iter; ++iter) {
2743 const Index n = iter.pos();
2744 if (mChildMask.isOn(n)) {
2745 // Merge this node's child with the other node's child.
2746 mNodes[n].getChild()->template merge<Policy>(*iter, background, otherBackground);
2747 } else {
2748 // Replace this node's tile (regardless of its active state) with
2749 // the other node's child and replace the other node's child with
2750 // a tile of undefined value (which is okay since the other tree
2751 // is assumed to be cannibalized in the process of merging).
2752 ChildNodeType* child = other.mNodes[n].getChild();
2753 other.mChildMask.setOff(n);
2754 child->resetBackground(otherBackground, background);
2755 this->setChildNode(n, child);
2756 }
2757 }
2758 break;
2759 }
2760
2762 {
2763 // Transfer children from the other tree to this tree.
2764 for (ChildOnIter iter = other.beginChildOn(); iter; ++iter) {
2765 const Index n = iter.pos();
2766 if (mChildMask.isOn(n)) {
2767 // Merge this node's child with the other node's child.
2768 mNodes[n].getChild()->template merge<Policy>(*iter, background, otherBackground);
2769 } else {
2770 // Replace this node's tile with the other node's child, leaving the other
2771 // node with an inactive tile of undefined value (which is okay since
2772 // the other tree is assumed to be cannibalized in the process of merging).
2773 ChildNodeType* child = other.mNodes[n].getChild();
2774 other.mChildMask.setOff(n);
2775 child->resetBackground(otherBackground, background);
2776 if (mValueMask.isOn(n)) {
2777 // Merge the child with this node's active tile.
2778 child->template merge<Policy>(mNodes[n].getValue(), /*on=*/true);
2779 mValueMask.setOff(n);
2780 }
2781 mChildMask.setOn(n);
2782 mNodes[n].setChild(child);
2783 }
2784 }
2785
2786 // Merge active tiles into this tree.
2787 for (ValueOnCIter iter = other.cbeginValueOn(); iter; ++iter) {
2788 const Index n = iter.pos();
2789 if (mChildMask.isOn(n)) {
2790 // Merge the other node's active tile into this node's child.
2791 mNodes[n].getChild()->template merge<Policy>(iter.getValue(), /*on=*/true);
2792 } else if (mValueMask.isOff(n)) {
2793 // Replace this node's inactive tile with the other node's active tile.
2794 mNodes[n].setValue(iter.getValue());
2795 mValueMask.setOn(n);
2796 }
2797 }
2798 break;
2799 }
2800
2801 }
2803}
2804
2805
2806template<typename ChildT, Index Log2Dim>
2807template<MergePolicy Policy>
2808inline void
2809InternalNode<ChildT, Log2Dim>::merge(const ValueType& tileValue, bool tileActive)
2810{
2812
2813 if (Policy != MERGE_ACTIVE_STATES_AND_NODES) return;
2814
2815 // For MERGE_ACTIVE_STATES_AND_NODES, inactive tiles in the other tree are ignored.
2816 if (!tileActive) return;
2817
2818 // Iterate over this node's children and inactive tiles.
2819 for (ValueOffIter iter = this->beginValueOff(); iter; ++iter) {
2820 const Index n = iter.pos();
2821 if (mChildMask.isOn(n)) {
2822 // Merge the other node's active tile into this node's child.
2823 mNodes[n].getChild()->template merge<Policy>(tileValue, /*on=*/true);
2824 } else {
2825 // Replace this node's inactive tile with the other node's active tile.
2826 iter.setValue(tileValue);
2827 mValueMask.setOn(n);
2828 }
2829 }
2831}
2832
2833
2834////////////////////////////////////////
2835
2836
2837template<typename ChildT, Index Log2Dim>
2838template<typename OtherInternalNode>
2839struct InternalNode<ChildT, Log2Dim>::TopologyUnion
2840{
2841 using W = typename NodeMaskType::Word;
2842 struct A { inline void operator()(W &tV, const W& sV, const W& tC) const
2843 { tV = (tV | sV) & ~tC; }
2844 };
2845 TopologyUnion(const OtherInternalNode* source, InternalNode* target, const bool preserveTiles)
2846 : s(source), t(target), mPreserveTiles(preserveTiles) {
2847 //(*this)(tbb::blocked_range<Index>(0, NUM_VALUES));//single thread for debugging
2848 tbb::parallel_for(tbb::blocked_range<Index>(0, NUM_VALUES), *this);
2849
2850 // Bit processing is done in a single thread!
2851 if (!mPreserveTiles) t->mChildMask |= s->mChildMask;//serial but very fast bitwise post-process
2852 else t->mChildMask |= (s->mChildMask & !t->mValueMask);
2853
2854 A op;
2855 t->mValueMask.foreach(s->mValueMask, t->mChildMask, op);
2856 OPENVDB_ASSERT((t->mValueMask & t->mChildMask).isOff());//no overlapping active tiles or child nodes
2857 }
2858 void operator()(const tbb::blocked_range<Index> &r) const {
2859 for (Index i = r.begin(), end=r.end(); i!=end; ++i) {
2860 if (s->mChildMask.isOn(i)) {// Loop over other node's child nodes
2861 const typename OtherInternalNode::ChildNodeType& other = *(s->mNodes[i].getChild());
2862 if (t->mChildMask.isOn(i)) {//this has a child node
2863 t->mNodes[i].getChild()->topologyUnion(other, mPreserveTiles);
2864 } else {// this is a tile so replace it with a child branch with identical topology
2865 if (!mPreserveTiles || t->mValueMask.isOff(i)) { // force child topology
2866 ChildT* child = new ChildT(other, t->mNodes[i].getValue(), TopologyCopy());
2867 if (t->mValueMask.isOn(i)) child->setValuesOn();//activate all values
2868 t->mNodes[i].setChild(child);
2869 }
2870 }
2871 } else if (s->mValueMask.isOn(i) && t->mChildMask.isOn(i)) {
2872 t->mNodes[i].getChild()->setValuesOn();
2873 }
2874 }
2875 }
2876 const OtherInternalNode* s;
2877 InternalNode* t;
2878 const bool mPreserveTiles;
2879};// TopologyUnion
2880
2881template<typename ChildT, Index Log2Dim>
2882template<typename OtherChildT>
2883inline void
2884InternalNode<ChildT, Log2Dim>::topologyUnion(const InternalNode<OtherChildT, Log2Dim>& other, const bool preserveTiles)
2885{
2886 TopologyUnion<InternalNode<OtherChildT, Log2Dim> > tmp(&other, this, preserveTiles);
2887}
2888
2889template<typename ChildT, Index Log2Dim>
2890template<typename OtherInternalNode>
2891struct InternalNode<ChildT, Log2Dim>::TopologyIntersection
2892{
2893 using W = typename NodeMaskType::Word;
2894 struct A { inline void operator()(W &tC, const W& sC, const W& sV, const W& tV) const
2895 { tC = (tC & (sC | sV)) | (tV & sC); }
2896 };
2897 TopologyIntersection(const OtherInternalNode* source, InternalNode* target,
2898 const ValueType& background) : s(source), t(target), b(background) {
2899 //(*this)(tbb::blocked_range<Index>(0, NUM_VALUES));//single thread for debugging
2900 tbb::parallel_for(tbb::blocked_range<Index>(0, NUM_VALUES), *this);
2901
2902 // Bit processing is done in a single thread!
2903 A op;
2904 t->mChildMask.foreach(s->mChildMask, s->mValueMask, t->mValueMask, op);
2905
2906 t->mValueMask &= s->mValueMask;
2907 OPENVDB_ASSERT((t->mValueMask & t->mChildMask).isOff());//no overlapping active tiles or child nodes
2908 }
2909 void operator()(const tbb::blocked_range<Index> &r) const {
2910 for (Index i = r.begin(), end=r.end(); i!=end; ++i) {
2911 if (t->mChildMask.isOn(i)) {// Loop over this node's child nodes
2912 ChildT* child = t->mNodes[i].getChild();
2913 if (s->mChildMask.isOn(i)) {//other also has a child node
2914 child->topologyIntersection(*(s->mNodes[i].getChild()), b);
2915 } else if (s->mValueMask.isOff(i)) {//other is an inactive tile
2916 delete child;//convert child to an inactive tile
2917 t->mNodes[i].setValue(b);
2918 }
2919 } else if (t->mValueMask.isOn(i) && s->mChildMask.isOn(i)) {//active tile -> a branch
2920 t->mNodes[i].setChild(new ChildT(*(s->mNodes[i].getChild()),
2921 t->mNodes[i].getValue(), TopologyCopy()));
2922 }
2923 }
2924 }
2925 const OtherInternalNode* s;
2926 InternalNode* t;
2927 const ValueType& b;
2928};// TopologyIntersection
2929
2930template<typename ChildT, Index Log2Dim>
2931template<typename OtherChildT>
2932inline void
2933InternalNode<ChildT, Log2Dim>::topologyIntersection(
2934 const InternalNode<OtherChildT, Log2Dim>& other, const ValueType& background)
2935{
2936 TopologyIntersection<InternalNode<OtherChildT, Log2Dim> > tmp(&other, this, background);
2937}
2938
2939template<typename ChildT, Index Log2Dim>
2940template<typename OtherInternalNode>
2941struct InternalNode<ChildT, Log2Dim>::TopologyDifference
2942{
2943 using W = typename NodeMaskType::Word;
2944 struct A {inline void operator()(W &tC, const W& sC, const W& sV, const W& tV) const
2945 { tC = (tC & (sC | ~sV)) | (tV & sC); }
2946 };
2947 struct B {inline void operator()(W &tV, const W& sC, const W& sV, const W& tC) const
2948 { tV &= ~((tC & sV) | (sC | sV)); }
2949 };
2950 TopologyDifference(const OtherInternalNode* source, InternalNode* target,
2951 const ValueType& background) : s(source), t(target), b(background) {
2952 //(*this)(tbb::blocked_range<Index>(0, NUM_VALUES));//single thread for debugging
2953 tbb::parallel_for(tbb::blocked_range<Index>(0, NUM_VALUES), *this);
2954
2955 // Bit processing is done in a single thread!
2956 const NodeMaskType oldChildMask(t->mChildMask);//important to avoid cross pollution
2957 A op1;
2958 t->mChildMask.foreach(s->mChildMask, s->mValueMask, t->mValueMask, op1);
2959
2960 B op2;
2961 t->mValueMask.foreach(t->mChildMask, s->mValueMask, oldChildMask, op2);
2962 OPENVDB_ASSERT((t->mValueMask & t->mChildMask).isOff());//no overlapping active tiles or child nodes
2963 }
2964 void operator()(const tbb::blocked_range<Index> &r) const {
2965 for (Index i = r.begin(), end=r.end(); i!=end; ++i) {
2966 if (t->mChildMask.isOn(i)) {// Loop over this node's child nodes
2967 ChildT* child = t->mNodes[i].getChild();
2968 if (s->mChildMask.isOn(i)) {
2969 child->topologyDifference(*(s->mNodes[i].getChild()), b);
2970 } else if (s->mValueMask.isOn(i)) {
2971 delete child;//convert child to an inactive tile
2972 t->mNodes[i].setValue(b);
2973 }
2974 } else if (t->mValueMask.isOn(i)) {//this is an active tile
2975 if (s->mChildMask.isOn(i)) {
2976 const typename OtherInternalNode::ChildNodeType& other =
2977 *(s->mNodes[i].getChild());
2978 ChildT* child = new ChildT(other.origin(), t->mNodes[i].getValue(), true);
2979 child->topologyDifference(other, b);
2980 t->mNodes[i].setChild(child);//replace the active tile with a child branch
2981 }
2982 }
2983 }
2984 }
2985 const OtherInternalNode* s;
2986 InternalNode* t;
2987 const ValueType& b;
2988};// TopologyDifference
2989
2990template<typename ChildT, Index Log2Dim>
2991template<typename OtherChildT>
2992inline void
2993InternalNode<ChildT, Log2Dim>::topologyDifference(const InternalNode<OtherChildT, Log2Dim>& other,
2994 const ValueType& background)
2995{
2996 TopologyDifference<InternalNode<OtherChildT, Log2Dim> > tmp(&other, this, background);
2997}
2998
2999
3000////////////////////////////////////////
3001
3002
3003template<typename ChildT, Index Log2Dim>
3004template<typename CombineOp>
3005inline void
3006InternalNode<ChildT, Log2Dim>::combine(InternalNode& other, CombineOp& op)
3007{
3008 const ValueType zero = zeroVal<ValueType>();
3009
3010 CombineArgs<ValueType> args;
3011
3012 for (Index i = 0; i < NUM_VALUES; ++i) {
3013 if (this->isChildMaskOff(i) && other.isChildMaskOff(i)) {
3014 // Both this node and the other node have constant values (tiles).
3015 // Combine the two values and store the result as this node's new tile value.
3016 op(args.setARef(mNodes[i].getValue())
3017 .setAIsActive(isValueMaskOn(i))
3018 .setBRef(other.mNodes[i].getValue())
3019 .setBIsActive(other.isValueMaskOn(i)));
3020 mNodes[i].setValue(args.result());
3021 mValueMask.set(i, args.resultIsActive());
3022 } else if (this->isChildMaskOn(i) && other.isChildMaskOff(i)) {
3023 // Combine this node's child with the other node's constant value.
3024 ChildNodeType* child = mNodes[i].getChild();
3025 OPENVDB_ASSERT(child);
3026 if (child) {
3027 child->combine(other.mNodes[i].getValue(), other.isValueMaskOn(i), op);
3028 }
3029 } else if (this->isChildMaskOff(i) && other.isChildMaskOn(i)) {
3030 // Combine this node's constant value with the other node's child.
3031 ChildNodeType* child = other.mNodes[i].getChild();
3032 OPENVDB_ASSERT(child);
3033 if (child) {
3034 // Combine this node's constant value with the other node's child,
3035 // but use a new functor in which the A and B values are swapped,
3036 // since the constant value is the A value, not the B value.
3037 SwappedCombineOp<ValueType, CombineOp> swappedOp(op);
3038 child->combine(mNodes[i].getValue(), isValueMaskOn(i), swappedOp);
3039
3040 // Steal the other node's child.
3041 other.mChildMask.setOff(i);
3042 other.mNodes[i].setValue(zero);
3043 this->setChildNode(i, child);
3044 }
3045
3046 } else /*if (isChildMaskOn(i) && other.isChildMaskOn(i))*/ {
3047 // Combine this node's child with the other node's child.
3048 ChildNodeType
3049 *child = mNodes[i].getChild(),
3050 *otherChild = other.mNodes[i].getChild();
3051 OPENVDB_ASSERT(child);
3052 OPENVDB_ASSERT(otherChild);
3053 if (child && otherChild) {
3054 child->combine(*otherChild, op);
3055 }
3056 }
3057 }
3058}
3059
3060
3061template<typename ChildT, Index Log2Dim>
3062template<typename CombineOp>
3063inline void
3064InternalNode<ChildT, Log2Dim>::combine(const ValueType& value, bool valueIsActive, CombineOp& op)
3065{
3066 CombineArgs<ValueType> args;
3067
3068 for (Index i = 0; i < NUM_VALUES; ++i) {
3069 if (this->isChildMaskOff(i)) {
3070 // Combine this node's constant value with the given constant value.
3071 op(args.setARef(mNodes[i].getValue())
3072 .setAIsActive(isValueMaskOn(i))
3073 .setBRef(value)
3074 .setBIsActive(valueIsActive));
3075 mNodes[i].setValue(args.result());
3076 mValueMask.set(i, args.resultIsActive());
3077 } else /*if (isChildMaskOn(i))*/ {
3078 // Combine this node's child with the given constant value.
3079 ChildNodeType* child = mNodes[i].getChild();
3080 OPENVDB_ASSERT(child);
3081 if (child) child->combine(value, valueIsActive, op);
3082 }
3083 }
3084}
3085
3086
3087////////////////////////////////////////
3088
3089
3090template<typename ChildT, Index Log2Dim>
3091template<typename CombineOp, typename OtherNodeType>
3092inline void
3093InternalNode<ChildT, Log2Dim>::combine2(const InternalNode& other0, const OtherNodeType& other1,
3094 CombineOp& op)
3095{
3096 CombineArgs<ValueType, typename OtherNodeType::ValueType> args;
3097
3098 for (Index i = 0; i < NUM_VALUES; ++i) {
3099 if (other0.isChildMaskOff(i) && other1.isChildMaskOff(i)) {
3100 op(args.setARef(other0.mNodes[i].getValue())
3101 .setAIsActive(other0.isValueMaskOn(i))
3102 .setBRef(other1.mNodes[i].getValue())
3103 .setBIsActive(other1.isValueMaskOn(i)));
3104 // Replace child i with a constant value.
3105 this->makeChildNodeEmpty(i, args.result());
3106 mValueMask.set(i, args.resultIsActive());
3107 } else {
3108 if (this->isChildMaskOff(i)) {
3109 // Add a new child with the same coordinates, etc. as the other node's child.
3110 const Coord& childOrigin = other0.isChildMaskOn(i)
3111 ? other0.mNodes[i].getChild()->origin()
3112 : other1.mNodes[i].getChild()->origin();
3113 this->setChildNode(i, new ChildNodeType(childOrigin, mNodes[i].getValue()));
3114 }
3115
3116 if (other0.isChildMaskOff(i)) {
3117 // Combine node1's child with node0's constant value
3118 // and write the result into child i.
3119 mNodes[i].getChild()->combine2(other0.mNodes[i].getValue(),
3120 *other1.mNodes[i].getChild(), other0.isValueMaskOn(i), op);
3121 } else if (other1.isChildMaskOff(i)) {
3122 // Combine node0's child with node1's constant value
3123 // and write the result into child i.
3124 mNodes[i].getChild()->combine2(*other0.mNodes[i].getChild(),
3125 other1.mNodes[i].getValue(), other1.isValueMaskOn(i), op);
3126 } else {
3127 // Combine node0's child with node1's child
3128 // and write the result into child i.
3129 mNodes[i].getChild()->combine2(*other0.mNodes[i].getChild(),
3130 *other1.mNodes[i].getChild(), op);
3131 }
3132 }
3133 }
3134}
3135
3136
3137template<typename ChildT, Index Log2Dim>
3138template<typename CombineOp, typename OtherNodeType>
3139inline void
3140InternalNode<ChildT, Log2Dim>::combine2(const ValueType& value, const OtherNodeType& other,
3141 bool valueIsActive, CombineOp& op)
3142{
3143 CombineArgs<ValueType, typename OtherNodeType::ValueType> args;
3144
3145 for (Index i = 0; i < NUM_VALUES; ++i) {
3146 if (other.isChildMaskOff(i)) {
3147 op(args.setARef(value)
3148 .setAIsActive(valueIsActive)
3149 .setBRef(other.mNodes[i].getValue())
3150 .setBIsActive(other.isValueMaskOn(i)));
3151 // Replace child i with a constant value.
3152 this->makeChildNodeEmpty(i, args.result());
3153 mValueMask.set(i, args.resultIsActive());
3154 } else {
3155 typename OtherNodeType::ChildNodeType* otherChild = other.mNodes[i].getChild();
3156 OPENVDB_ASSERT(otherChild);
3157 if (this->isChildMaskOff(i)) {
3158 // Add a new child with the same coordinates, etc.
3159 // as the other node's child.
3160 this->setChildNode(i, new ChildNodeType(*otherChild));
3161 }
3162 // Combine the other node's child with a constant value
3163 // and write the result into child i.
3164 mNodes[i].getChild()->combine2(value, *otherChild, valueIsActive, op);
3165 }
3166 }
3167}
3168
3169
3170template<typename ChildT, Index Log2Dim>
3171template<typename CombineOp, typename OtherValueType>
3172inline void
3173InternalNode<ChildT, Log2Dim>::combine2(const InternalNode& other, const OtherValueType& value,
3174 bool valueIsActive, CombineOp& op)
3175{
3176 CombineArgs<ValueType, OtherValueType> args;
3177
3178 for (Index i = 0; i < NUM_VALUES; ++i) {
3179 if (other.isChildMaskOff(i)) {
3180 op(args.setARef(other.mNodes[i].getValue())
3181 .setAIsActive(other.isValueMaskOn(i))
3182 .setBRef(value)
3183 .setBIsActive(valueIsActive));
3184 // Replace child i with a constant value.
3185 this->makeChildNodeEmpty(i, args.result());
3186 mValueMask.set(i, args.resultIsActive());
3187 } else {
3188 ChildNodeType* otherChild = other.mNodes[i].getChild();
3189 OPENVDB_ASSERT(otherChild);
3190 if (this->isChildMaskOff(i)) {
3191 // Add a new child with the same coordinates, etc. as the other node's child.
3192 this->setChildNode(i,
3193 new ChildNodeType(otherChild->origin(), mNodes[i].getValue()));
3194 }
3195 // Combine the other node's child with a constant value
3196 // and write the result into child i.
3197 mNodes[i].getChild()->combine2(*otherChild, value, valueIsActive, op);
3198 }
3199 }
3200}
3201
3202
3203////////////////////////////////////////
3204
3205
3206template<typename ChildT, Index Log2Dim>
3207inline void
3208InternalNode<ChildT, Log2Dim>::writeBuffers(std::ostream& os, bool toHalf) const
3209{
3210 for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
3211 iter->writeBuffers(os, toHalf);
3212 }
3213}
3214
3215
3216template<typename ChildT, Index Log2Dim>
3217inline void
3218InternalNode<ChildT, Log2Dim>::readBuffers(std::istream& is, bool fromHalf)
3219{
3220 for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) {
3221 iter->readBuffers(is, fromHalf);
3222 }
3223}
3224
3225
3226template<typename ChildT, Index Log2Dim>
3227inline void
3228InternalNode<ChildT, Log2Dim>::readBuffers(std::istream& is,
3229 const CoordBBox& clipBBox, bool fromHalf)
3230{
3231 for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) {
3232 // Stream in the branch rooted at this child.
3233 // (We can't skip over children that lie outside the clipping region,
3234 // because buffers are serialized in depth-first order and need to be
3235 // unserialized in the same order.)
3236 iter->readBuffers(is, clipBBox, fromHalf);
3237 }
3238
3239 // Get this tree's background value.
3240 ValueType background = zeroVal<ValueType>();
3241 if (const void* bgPtr = io::getGridBackgroundValuePtr(is)) {
3242 background = *static_cast<const ValueType*>(bgPtr);
3243 }
3244 this->clip(clipBBox, background);
3245}
3246
3247
3248////////////////////////////////////////
3249
3250
3251template<typename ChildT, Index Log2Dim>
3252void
3253InternalNode<ChildT, Log2Dim>::getNodeLog2Dims(std::vector<Index>& dims)
3254{
3255 dims.push_back(Log2Dim);
3256 ChildNodeType::getNodeLog2Dims(dims);
3257}
3258
3259
3260template<typename ChildT, Index Log2Dim>
3261inline void
3262InternalNode<ChildT, Log2Dim>::offsetToLocalCoord(Index n, Coord &xyz)
3263{
3264 OPENVDB_ASSERT(n<(1<<3*Log2Dim));
3265 xyz.setX(n >> 2*Log2Dim);
3266 n &= ((1<<2*Log2Dim)-1);
3267 xyz.setY(n >> Log2Dim);
3268 xyz.setZ(n & ((1<<Log2Dim)-1));
3269}
3270
3271
3272template<typename ChildT, Index Log2Dim>
3273inline Index
3274InternalNode<ChildT, Log2Dim>::coordToOffset(const Coord& xyz)
3275{
3276 return (((xyz[0] & (DIM-1u)) >> ChildNodeType::TOTAL) << 2*Log2Dim)
3277 + (((xyz[1] & (DIM-1u)) >> ChildNodeType::TOTAL) << Log2Dim)
3278 + ((xyz[2] & (DIM-1u)) >> ChildNodeType::TOTAL);
3279}
3280
3281
3282template<typename ChildT, Index Log2Dim>
3283inline Coord
3284InternalNode<ChildT, Log2Dim>::offsetToGlobalCoord(Index n) const
3285{
3286 Coord local;
3287 this->offsetToLocalCoord(n, local);
3288 local <<= ChildT::TOTAL;
3289 return local + this->origin();
3290}
3291
3292
3293////////////////////////////////////////
3294
3295
3296template<typename ChildT, Index Log2Dim>
3297template<typename ArrayT>
3298inline void
3299InternalNode<ChildT, Log2Dim>::getNodes(ArrayT& array)
3300{
3301 using T = typename ArrayT::value_type;
3302 static_assert(std::is_pointer<T>::value, "argument to getNodes() must be a pointer array");
3303 using ArrayChildT = typename std::conditional<
3304 std::is_const<typename std::remove_pointer<T>::type>::value, const ChildT, ChildT>::type;
3305 for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) {
3307 if (std::is_same<T, ArrayChildT*>::value) {
3308 array.push_back(reinterpret_cast<T>(mNodes[iter.pos()].getChild()));
3309 } else {
3310 iter->getNodes(array);//descent
3311 }
3313 }
3314}
3315
3316template<typename ChildT, Index Log2Dim>
3317template<typename ArrayT>
3318inline void
3319InternalNode<ChildT, Log2Dim>::getNodes(ArrayT& array) const
3320{
3321 using T = typename ArrayT::value_type;
3322 static_assert(std::is_pointer<T>::value, "argument to getNodes() must be a pointer array");
3323 static_assert(std::is_const<typename std::remove_pointer<T>::type>::value,
3324 "argument to getNodes() must be an array of const node pointers");
3325 for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
3327 if (std::is_same<T, const ChildT*>::value) {
3328 array.push_back(reinterpret_cast<T>(mNodes[iter.pos()].getChild()));
3329 } else {
3330 iter->getNodes(array);//descent
3331 }
3333 }
3334}
3335
3336
3337////////////////////////////////////////
3338
3339
3340template<typename ChildT, Index Log2Dim>
3341template<typename ArrayT>
3342inline void
3343InternalNode<ChildT, Log2Dim>::stealNodes(ArrayT& array, const ValueType& value, bool state)
3344{
3345 using T = typename ArrayT::value_type;
3346 static_assert(std::is_pointer<T>::value, "argument to stealNodes() must be a pointer array");
3347 using ArrayChildT = typename std::conditional<
3348 std::is_const<typename std::remove_pointer<T>::type>::value, const ChildT, ChildT>::type;
3350 for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) {
3351 const Index n = iter.pos();
3352 if (std::is_same<T, ArrayChildT*>::value) {
3353 array.push_back(reinterpret_cast<T>(mNodes[n].getChild()));
3354 mValueMask.set(n, state);
3355 mNodes[n].setValue(value);
3356 } else {
3357 iter->stealNodes(array, value, state);//descent
3358 }
3359 }
3360 if (std::is_same<T, ArrayChildT*>::value) mChildMask.setOff();
3362}
3363
3364
3365////////////////////////////////////////
3366
3367
3368template<typename ChildT, Index Log2Dim>
3369inline void
3370InternalNode<ChildT, Log2Dim>::resetBackground(const ValueType& oldBackground,
3371 const ValueType& newBackground)
3372{
3373 if (math::isExactlyEqual(oldBackground, newBackground)) return;
3374 for (Index i = 0; i < NUM_VALUES; ++i) {
3375 if (this->isChildMaskOn(i)) {
3376 mNodes[i].getChild()->resetBackground(oldBackground, newBackground);
3377 } else if (this->isValueMaskOff(i)) {
3378 if (math::isApproxEqual(mNodes[i].getValue(), oldBackground)) {
3379 mNodes[i].setValue(newBackground);
3380 } else if (math::isApproxEqual(mNodes[i].getValue(), math::negative(oldBackground))) {
3381 mNodes[i].setValue(math::negative(newBackground));
3382 }
3383 }
3384 }
3385}
3386
3387template<typename ChildT, Index Log2Dim>
3388template<typename OtherChildNodeType, Index OtherLog2Dim>
3389inline bool
3390InternalNode<ChildT, Log2Dim>::hasSameTopology(
3391 const InternalNode<OtherChildNodeType, OtherLog2Dim>* other) const
3392{
3393 if (Log2Dim != OtherLog2Dim || mChildMask != other->mChildMask ||
3394 mValueMask != other->mValueMask) return false;
3395 for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
3396 if (!iter->hasSameTopology(other->mNodes[iter.pos()].getChild())) return false;
3397 }
3398 return true;
3399}
3400
3401
3402template<typename ChildT, Index Log2Dim>
3403inline void
3404InternalNode<ChildT, Log2Dim>::resetChildNode(Index i, ChildNodeType* child)
3405{
3406 OPENVDB_ASSERT(child);
3407 if (this->isChildMaskOn(i)) {
3408 delete mNodes[i].getChild();
3409 } else {
3410 mChildMask.setOn(i);
3411 mValueMask.setOff(i);
3412 }
3413 mNodes[i].setChild(child);
3414}
3415
3416template<typename ChildT, Index Log2Dim>
3417inline void
3418InternalNode<ChildT, Log2Dim>::setChildNode(Index i, ChildNodeType* child)
3419{
3420 OPENVDB_ASSERT(child);
3421 OPENVDB_ASSERT(mChildMask.isOff(i));
3422 mChildMask.setOn(i);
3423 mValueMask.setOff(i);
3424 mNodes[i].setChild(child);
3425}
3426
3427
3428template<typename ChildT, Index Log2Dim>
3429inline ChildT*
3430InternalNode<ChildT, Log2Dim>::unsetChildNode(Index i, const ValueType& value)
3431{
3432 if (this->isChildMaskOff(i)) {
3433 mNodes[i].setValue(value);
3434 return nullptr;
3435 }
3436 ChildNodeType* child = mNodes[i].getChild();
3437 mChildMask.setOff(i);
3438 mNodes[i].setValue(value);
3439 return child;
3440}
3441
3442
3443template<typename ChildT, Index Log2Dim>
3444inline void
3445InternalNode<ChildT, Log2Dim>::makeChildNodeEmpty(Index n, const ValueType& value)
3446{
3447 delete this->unsetChildNode(n, value);
3448}
3449
3450template<typename ChildT, Index Log2Dim>
3451inline ChildT*
3452InternalNode<ChildT, Log2Dim>::getChildNode(Index n)
3453{
3454 OPENVDB_ASSERT(this->isChildMaskOn(n));
3455 return mNodes[n].getChild();
3456}
3457
3458
3459template<typename ChildT, Index Log2Dim>
3460inline const ChildT*
3461InternalNode<ChildT, Log2Dim>::getChildNode(Index n) const
3462{
3463 OPENVDB_ASSERT(this->isChildMaskOn(n));
3464 return mNodes[n].getChild();
3465}
3466
3467} // namespace tree
3468} // namespace OPENVDB_VERSION_NAME
3469} // namespace openvdb
3470
3471#endif // OPENVDB_TREE_INTERNALNODE_HAS_BEEN_INCLUDED
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
#define OPENVDB_NO_DEPRECATION_WARNING_END
Definition Platform.h:218
#define OPENVDB_NO_DEPRECATION_WARNING_BEGIN
Bracket code with OPENVDB_NO_DEPRECATION_WARNING_BEGIN/_END, to inhibit warnings about deprecated cod...
Definition Platform.h:217
#define OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
Definition Platform.h:164
#define OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
Definition Platform.h:163
#define OPENVDB_DEPRECATED_MESSAGE(msg)
Definition Platform.h:171
OPENVDB_API const void * getGridBackgroundValuePtr(std::ios_base &)
Return a pointer to the background value of the grid currently being read from or written to the give...
Level getLevel()
Return the current logging level.
Definition logging.h:141
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition Composite.h:110
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition Composite.h:106
bool anyActiveTiles(const TreeT &tree, const CoordBBox &bbox)
Returns true if the bounding box intersects any of the active tiles in a tree, i.e....
Definition FindActiveValues.h:663
void prune(TreeT &tree, typename TreeT::ValueType tolerance=zeroVal< typename TreeT::ValueType >(), bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing with tiles any nodes whose values are all the same...
Definition Prune.h:335
GridType::Ptr clip(const GridType &grid, const BBoxd &bbox, bool keepInterior=true)
Clip the given grid against a world-space bounding box and return a new grid containing the result.
Definition Clip.h:352
void copyToDense(const GridOrTreeT &sparse, DenseT &dense, bool serial=false)
Populate a dense grid with the values of voxels from a sparse grid, where the sparse grid intersects ...
Definition Dense.h:422
Index32 Index
Definition Types.h:34
uint32_t Index32
Definition Types.h:32
uint64_t Index64
Definition Types.h:33
@ MERGE_ACTIVE_STATES
Definition Types.h:578
@ MERGE_NODES
Definition Types.h:579
@ MERGE_ACTIVE_STATES_AND_NODES
Definition Types.h:580
ValueType combine(const ValueType &v0, const ValueType &v1, const ValueType &v2, const openvdb::Vec3d &w)
Combine different value types.
Definition AttributeTransferUtil.h:141
Definition Exceptions.h:13
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:218