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