Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright Contributors to the OpenVDB Project | ||
2 | // SPDX-License-Identifier: MPL-2.0 | ||
3 | |||
4 | /////////////////////////////////////////////////////////////////////////// | ||
5 | // | ||
6 | /// @file FindActiveValues.h | ||
7 | /// | ||
8 | /// @author Ken Museth | ||
9 | /// | ||
10 | /// @brief Finds the active values and tiles in a tree that intersects a bounding box. | ||
11 | /// Methods are provided that count the number of active values and tiles, | ||
12 | /// test for the existence of active values and tiles, and return a list of | ||
13 | /// the active tiles that intersect a bbox. | ||
14 | /// | ||
15 | /// @warning For repeated calls to the free-standing functions defined below | ||
16 | /// consider instead creating an instance of FindActiveValues | ||
17 | /// and then repeatedly call its member methods. This assumes the tree | ||
18 | /// to be constant between calls but is sightly faster. | ||
19 | /// | ||
20 | /////////////////////////////////////////////////////////////////////////// | ||
21 | |||
22 | #ifndef OPENVDB_TOOLS_FINDACTIVEVALUES_HAS_BEEN_INCLUDED | ||
23 | #define OPENVDB_TOOLS_FINDACTIVEVALUES_HAS_BEEN_INCLUDED | ||
24 | |||
25 | #include <vector> | ||
26 | #include <openvdb/version.h> // for OPENVDB_VERSION_NAME | ||
27 | #include <openvdb/Types.h> | ||
28 | #include <openvdb/tree/ValueAccessor.h> | ||
29 | #include <openvdb/openvdb.h> | ||
30 | |||
31 | #include "Count.h" // tools::countActiveVoxels() | ||
32 | |||
33 | #include <tbb/blocked_range.h> | ||
34 | #include <tbb/parallel_for.h> | ||
35 | #include <tbb/parallel_reduce.h> | ||
36 | |||
37 | namespace openvdb { | ||
38 | OPENVDB_USE_VERSION_NAMESPACE | ||
39 | namespace OPENVDB_VERSION_NAME { | ||
40 | namespace tools { | ||
41 | |||
42 | /// @brief Struct that encodes a bounding box, value and level of a tile | ||
43 | /// | ||
44 | /// @details The bbox of a tiles is trimmed to the bounding box that probed it. | ||
45 | /// The level is typically defined as: 1 is 8^3, 2 is 128^3, and 3 is 4096^3. | ||
46 | template<typename ValueType> | ||
47 | struct TileData; | ||
48 | |||
49 | /// @brief Returns true if the bounding box intersects any of the active | ||
50 | /// values in a tree, i.e. either active voxels or active tiles. | ||
51 | /// | ||
52 | /// @warning For repeated calls to this method consider instead creating an instance of | ||
53 | /// FindActiveValues and then repeatedly call anyActiveValues(). This assumes the tree | ||
54 | /// to be constant between calls but is slightly faster. | ||
55 | /// | ||
56 | /// @param tree const tree to be tested for active values. | ||
57 | /// @param bbox index bounding box which is intersected against the active values. | ||
58 | template<typename TreeT> | ||
59 | bool | ||
60 | anyActiveValues(const TreeT& tree, const CoordBBox &bbox); | ||
61 | |||
62 | /// @brief Returns true if the bounding box intersects any of the active | ||
63 | /// voxels in a tree, i.e. ignores active tile values. | ||
64 | /// | ||
65 | /// @note In VDB voxels by definition reside in the leaf nodes ONLY. So this method | ||
66 | /// ignores active tile values that reside higher up in the VDB tree structure. | ||
67 | /// | ||
68 | /// @warning For repeated calls to this method consider instead creating an instance of | ||
69 | /// FindActiveValues and then repeatedly call anyActiveVoxels(). This assumes the tree | ||
70 | /// to be constant between calls but is slightly faster. | ||
71 | /// | ||
72 | /// @param tree const tree to be tested for active voxels. | ||
73 | /// @param bbox index bounding box which is intersected against the active voxels. | ||
74 | template<typename TreeT> | ||
75 | bool | ||
76 | anyActiveVoxels(const TreeT& tree, const CoordBBox &bbox); | ||
77 | |||
78 | /// @brief Returns true if the bounding box intersects any of the active | ||
79 | /// tiles in a tree, i.e. ignores active leaf values. | ||
80 | /// | ||
81 | /// @warning For repeated calls to this method consider instead creating an instance of | ||
82 | /// FindActiveValues and then repeatedly call anyActiveTiles(). This assumes the tree | ||
83 | /// to be constant between calls but is slightly faster. | ||
84 | /// | ||
85 | /// @param tree const tree to be tested for active tiles. | ||
86 | /// @param bbox index bounding box which is intersected against the active tiles. | ||
87 | template<typename TreeT> | ||
88 | bool | ||
89 | anyActiveTiles(const TreeT& tree, const CoordBBox &bbox); | ||
90 | |||
91 | /// @brief Returns true if the bounding box intersects none of the active | ||
92 | /// values in a tree, i.e. neither active voxels or active tiles. | ||
93 | /// | ||
94 | /// @warning For repeated calls to this method consider instead creating an instance of | ||
95 | /// FindActiveValues and then repeatedly call noActiveValues(). This assumes the tree | ||
96 | /// to be constant between calls but is slightly faster. | ||
97 | /// | ||
98 | /// @param tree const tree to be tested for active values. | ||
99 | /// @param bbox index bounding box which is intersected against the active values. | ||
100 | template<typename TreeT> | ||
101 | bool | ||
102 | noActiveValues(const TreeT& tree, const CoordBBox &bbox); | ||
103 | |||
104 | /// @brief Returns the number of active values that intersects a bounding box intersects, | ||
105 | /// i.e. the count includes both active voxels and virtual voxels in active tiles. | ||
106 | /// | ||
107 | /// @warning For repeated calls to this method consider instead creating an instance of | ||
108 | /// FindActiveValues and then repeatedly call count(). This assumes the tree | ||
109 | /// to be constant between calls but is slightly faster. | ||
110 | /// | ||
111 | /// @param tree const tree to be tested for active values. | ||
112 | /// @param bbox index bounding box which is intersected against the active values. | ||
113 | template<typename TreeT> | ||
114 | Index64 | ||
115 | countActiveValues(const TreeT& tree, const CoordBBox &bbox); | ||
116 | |||
117 | /// @brief Return a vector with bounding boxes that represents all the intersections | ||
118 | /// between active tiles in the tree and the specified bounding box. | ||
119 | /// | ||
120 | /// @warning For repeated calls to this method consider instead creating an instance of | ||
121 | /// FindActiveValues and then repeatedly call count(). This assumes the tree | ||
122 | /// to be constant between calls but is slightly faster. | ||
123 | /// | ||
124 | /// @param tree const tree to be tested for active tiles. | ||
125 | /// @param bbox index bounding box which is intersected against the active tiles. | ||
126 | template<typename TreeT> | ||
127 | std::vector<TileData<typename TreeT::ValueType>> | ||
128 | activeTiles(const TreeT& tree, const CoordBBox &bbox); | ||
129 | |||
130 | ////////////////////////////////////////////////////////////////////////////////////////// | ||
131 | |||
132 | /// @brief Finds the active values in a tree which intersects a bounding box. | ||
133 | /// | ||
134 | /// @details Two methods are provided, one that count the number of active values | ||
135 | /// and one that simply tests if any active values intersect the bbox. | ||
136 | /// | ||
137 | /// @warning Tree nodes are cached by this class so it's important that the tree is not | ||
138 | /// modified after this class is instantiated and before its methods are called. | ||
139 | template<typename TreeT> | ||
140 | class FindActiveValues | ||
141 | { | ||
142 | public: | ||
143 | |||
144 | using TileDataT = TileData<typename TreeT::ValueType>; | ||
145 | |||
146 | /// @brief Constructor from a const tree, which is assumed not to be modified after construction. | ||
147 | FindActiveValues(const TreeT& tree); | ||
148 | |||
149 | /// @brief Default destructor | ||
150 | ~FindActiveValues(); | ||
151 | |||
152 | /// @brief Initiate this class with a new (or modified) tree. | ||
153 | void update(const TreeT& tree); | ||
154 | |||
155 | /// @brief Returns true if the specified bounding box intersects any active values. | ||
156 | /// | ||
157 | /// @warning Using a ValueAccessor (i.e. useAccessor = true) can improve performance for especially | ||
158 | /// small bounding boxes, but at the cost of no thread-safety. So if multiple threads are | ||
159 | /// calling this method concurrently use the default setting, useAccessor = false. | ||
160 | bool anyActiveValues(const CoordBBox &bbox, bool useAccessor = false) const; | ||
161 | |||
162 | /// @brief Returns true if the specified bounding box intersects any active tiles only. | ||
163 | bool anyActiveVoxels(const CoordBBox &bbox) const; | ||
164 | |||
165 | /// @brief Returns true if the specified bounding box intersects any active tiles only. | ||
166 | bool anyActiveTiles(const CoordBBox &bbox) const; | ||
167 | |||
168 | /// @brief Returns true if the specified bounding box does not intersect any active values. | ||
169 | /// | ||
170 | /// @warning Using a ValueAccessor (i.e. useAccessor = true) can improve performance for especially | ||
171 | /// small bounding boxes, but at the cost of no thread-safety. So if multiple threads are | ||
172 | /// calling this method concurrently use the default setting, useAccessor = false. | ||
173 |
4/18✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 58 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
|
62 | bool noActiveValues(const CoordBBox &bbox, bool useAccessor = false) const { return !this->anyActiveValues(bbox, useAccessor); } |
174 | |||
175 | /// @brief Returns the number of active voxels intersected by the specified bounding box. | ||
176 | Index64 count(const CoordBBox &bbox) const; | ||
177 | |||
178 | /// @brief Return a vector with bounding boxes that represents all the intersections | ||
179 | /// between active tiles in the tree and the specified bounding box. | ||
180 | std::vector<TileDataT> activeTiles(const CoordBBox &bbox) const; | ||
181 | |||
182 | OPENVDB_DEPRECATED_MESSAGE("Use anyActiveValues() instead") inline bool any(const CoordBBox &bbox, bool useAccessor = false) const | ||
183 | { | ||
184 | return this->anyActiveValues(bbox, useAccessor); | ||
185 | } | ||
186 | OPENVDB_DEPRECATED_MESSAGE("Use noActiveValues() instead") inline bool none(const CoordBBox &bbox, bool useAccessor = false) const | ||
187 | { | ||
188 | return this->noActiveValues(bbox, useAccessor); | ||
189 | } | ||
190 | |||
191 | private: | ||
192 | |||
193 | // Cleans up internal data structures | ||
194 | void clear(); | ||
195 | |||
196 | // builds internal data structures | ||
197 | void init(const TreeT &tree); | ||
198 | |||
199 | template<typename NodeT> | ||
200 | typename NodeT::NodeMaskType getBBoxMask(const CoordBBox &bbox, const NodeT* node) const; | ||
201 | |||
202 | // process leaf nodes | ||
203 | 76 | inline bool anyActiveValues(const typename TreeT::LeafNodeType* leaf, const CoordBBox &bbox ) const { return this->anyActiveVoxels(leaf, bbox); } | |
204 | inline bool anyActiveVoxels(const typename TreeT::LeafNodeType* leaf, const CoordBBox &bbox ) const; | ||
205 | static bool anyActiveTiles( const typename TreeT::LeafNodeType*, const CoordBBox& ) {return false;} | ||
206 | void activeTiles(const typename TreeT::LeafNodeType*, const CoordBBox&, std::vector<TileDataT>&) const {;}// no-op | ||
207 | inline Index64 count(const typename TreeT::LeafNodeType* leaf, const CoordBBox &bbox ) const; | ||
208 | |||
209 | // process internal nodes | ||
210 | template<typename NodeT> | ||
211 | bool anyActiveValues(const NodeT* node, const CoordBBox &bbox) const; | ||
212 | template<typename NodeT> | ||
213 | bool anyActiveVoxels(const NodeT* node, const CoordBBox &bbox) const; | ||
214 | template<typename NodeT> | ||
215 | bool anyActiveTiles(const NodeT* node, const CoordBBox &bbox) const; | ||
216 | template<typename NodeT> | ||
217 | void activeTiles(const NodeT* node, const CoordBBox &bbox, std::vector<TileDataT> &tiles) const; | ||
218 | template<typename NodeT> | ||
219 | Index64 count(const NodeT* node, const CoordBBox &bbox) const; | ||
220 | |||
221 | using AccT = tree::ValueAccessor<const TreeT, false/* IsSafe */>; | ||
222 | using RootChildType = typename TreeT::RootNodeType::ChildNodeType; | ||
223 | |||
224 | struct RootChild; | ||
225 | |||
226 | AccT mAcc; | ||
227 | std::vector<TileDataT> mRootTiles;// cache bbox of child nodes (faster to cache than access RootNode) | ||
228 | std::vector<RootChild> mRootNodes;// cache bbox of active tiles (faster to cache than access RootNode) | ||
229 | |||
230 | };// FindActiveValues class | ||
231 | |||
232 | ////////////////////////////////////////////////////////////////////////////////////////// | ||
233 | |||
234 | template<typename TreeT> | ||
235 | 100 | FindActiveValues<TreeT>::FindActiveValues(const TreeT& tree) : mAcc(tree), mRootTiles(), mRootNodes() | |
236 | { | ||
237 |
1/2✓ Branch 1 taken 100 times.
✗ Branch 2 not taken.
|
100 | this->init(tree); |
238 | 100 | } | |
239 | |||
240 | template<typename TreeT> | ||
241 | 100 | FindActiveValues<TreeT>::~FindActiveValues() | |
242 | { | ||
243 | 100 | this->clear(); | |
244 | 100 | } | |
245 | |||
246 | template<typename TreeT> | ||
247 | 1 | void FindActiveValues<TreeT>::update(const TreeT& tree) | |
248 | { | ||
249 | 1 | this->clear(); | |
250 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | mAcc = AccT(tree); |
251 | 1 | this->init(tree); | |
252 | 1 | } | |
253 | |||
254 | template<typename TreeT> | ||
255 |
2/2✓ Branch 0 taken 97 times.
✓ Branch 1 taken 4 times.
|
101 | void FindActiveValues<TreeT>::clear() |
256 | { | ||
257 | mRootNodes.clear(); | ||
258 | mRootTiles.clear(); | ||
259 | 101 | } | |
260 | |||
261 | template<typename TreeT> | ||
262 | 101 | void FindActiveValues<TreeT>::init(const TreeT& tree) | |
263 | { | ||
264 | const auto &root = tree.root(); | ||
265 |
2/2✓ Branch 1 taken 621 times.
✓ Branch 2 taken 101 times.
|
823 | for (auto i = root.cbeginChildOn(); i; ++i) { |
266 | 621 | mRootNodes.emplace_back(i.getCoord(), &*i); | |
267 | } | ||
268 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 101 times.
|
202 | for (auto i = root.cbeginValueOn(); i; ++i) { |
269 | ✗ | mRootTiles.emplace_back(root, i.getCoord(), *i); | |
270 | } | ||
271 | 101 | } | |
272 | |||
273 | template<typename TreeT> | ||
274 | 3210 | bool FindActiveValues<TreeT>::anyActiveValues(const CoordBBox &bbox, bool useAccessor) const | |
275 | { | ||
276 | // test early-out: the center of the bbox is active | ||
277 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3210 times.
|
3210 | if (useAccessor) { |
278 | ✗ | if (mAcc.isValueOn( (bbox.min() + bbox.max())>>1 )) return true; | |
279 | } else { | ||
280 |
2/2✓ Branch 1 taken 135 times.
✓ Branch 2 taken 3075 times.
|
3210 | if (mAcc.tree().isValueOn( (bbox.min() + bbox.max())>>1 )) return true; |
281 | } | ||
282 | |||
283 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 135 times.
|
135 | for (auto& tile : mRootTiles) { |
284 | ✗ | if (tile.bbox.hasOverlap(bbox)) return true; | |
285 | } | ||
286 |
2/2✓ Branch 0 taken 982 times.
✓ Branch 1 taken 118 times.
|
1100 | for (auto& node : mRootNodes) { |
287 | 63 | if (!node.bbox.hasOverlap(bbox)) {// no overlap | |
288 | 63 | continue; | |
289 | } else if (node.bbox.isInside(bbox)) {// bbox is inside the child node | ||
290 | 17 | return this->anyActiveValues(node.child, bbox); | |
291 |
2/2✓ Branch 1 taken 902 times.
✓ Branch 2 taken 5 times.
|
907 | } else if (this->anyActiveValues(node.child, bbox)) {// bbox overlaps the child node |
292 | return true; | ||
293 | } | ||
294 | } | ||
295 | 118 | return false; | |
296 | } | ||
297 | |||
298 | template<typename TreeT> | ||
299 | 517 | bool FindActiveValues<TreeT>::anyActiveVoxels(const CoordBBox &bbox) const | |
300 | { | ||
301 |
2/2✓ Branch 0 taken 2302 times.
✓ Branch 1 taken 3 times.
|
2305 | for (auto& node : mRootNodes) { |
302 | 1785 | if (!node.bbox.hasOverlap(bbox)) {// no overlap | |
303 | 1785 | continue; | |
304 | } else if (node.bbox.isInside(bbox)) {// bbox is inside the child node | ||
305 | 514 | return this->anyActiveVoxels(node.child, bbox); | |
306 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 4 times.
|
7 | } else if (this->anyActiveVoxels(node.child, bbox)) {// bbox overlaps the child node |
307 | return true; | ||
308 | } | ||
309 | } | ||
310 | 3 | return false; | |
311 | } | ||
312 | |||
313 | template<typename TreeT> | ||
314 | 12 | bool FindActiveValues<TreeT>::anyActiveTiles(const CoordBBox &bbox) const | |
315 | { | ||
316 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
12 | for (auto& tile : mRootTiles) { |
317 | ✗ | if (tile.bbox.hasOverlap(bbox)) return true; | |
318 | } | ||
319 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 3 times.
|
34 | for (auto& node : mRootNodes) { |
320 | ✗ | if (!node.bbox.hasOverlap(bbox)) {// no overlap | |
321 | ✗ | continue; | |
322 | } else if (node.bbox.isInside(bbox)) {// bbox is inside the child node | ||
323 | 6 | return this->anyActiveTiles(node.child, bbox); | |
324 |
2/2✓ Branch 1 taken 11 times.
✓ Branch 2 taken 2 times.
|
26 | } else if (this->anyActiveTiles(node.child, bbox)) {// bbox overlaps the child node |
325 | return true; | ||
326 | } | ||
327 | } | ||
328 | 6 | return false; | |
329 | } | ||
330 | |||
331 | template<typename TreeT> | ||
332 | 5 | Index64 FindActiveValues<TreeT>::count(const CoordBBox &bbox) const | |
333 | { | ||
334 | Index64 count = 0; | ||
335 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | for (auto& tile : mRootTiles) {//loop over active tiles only |
336 | ✗ | if (!tile.bbox.hasOverlap(bbox)) { | |
337 | ✗ | continue;//ignore non-overlapping tiles | |
338 | } else if (tile.bbox.isInside(bbox)) { | ||
339 | ✗ | return bbox.volume();// bbox is completely inside the active tile | |
340 | } else if (bbox.isInside(tile.bbox)) { | ||
341 | ✗ | count += RootChildType::NUM_VOXELS; | |
342 | } else {// partial overlap between tile and bbox | ||
343 | ✗ | auto tmp = tile.bbox; | |
344 | ✗ | tmp.intersect(bbox); | |
345 | ✗ | count += tmp.volume(); | |
346 | } | ||
347 | } | ||
348 |
2/2✓ Branch 0 taken 40 times.
✓ Branch 1 taken 5 times.
|
45 | for (auto &node : mRootNodes) {//loop over child nodes of the root node only |
349 | ✗ | if ( !node.bbox.hasOverlap(bbox) ) { | |
350 | ✗ | continue;//ignore non-overlapping child nodes | |
351 | } else if ( node.bbox.isInside(bbox) ) { | ||
352 | ✗ | return this->count(node.child, bbox);// bbox is completely inside the child node | |
353 | } else { | ||
354 | 40 | count += this->count(node.child, bbox); | |
355 | } | ||
356 | } | ||
357 | 5 | return count; | |
358 | } | ||
359 | |||
360 | template<typename TreeT> | ||
361 | std::vector<TileData<typename TreeT::ValueType> > | ||
362 | 12 | FindActiveValues<TreeT>::activeTiles(const CoordBBox &bbox) const | |
363 | { | ||
364 | std::vector<TileDataT> tiles; | ||
365 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
12 | for (auto& tile : mRootTiles) {//loop over active tiles only |
366 | ✗ | if (!tile.bbox.hasOverlap(bbox)) { | |
367 | ✗ | continue;//ignore non-overlapping tiles | |
368 | } else if (tile.bbox.isInside(bbox)) {// bbox is completely inside the active tile | ||
369 | ✗ | tiles.emplace_back(bbox, tile.value, tile.level); | |
370 | ✗ | return tiles; | |
371 | } else if (bbox.isInside(tile.bbox)) {// active tile is completely inside the bbox | ||
372 | ✗ | tiles.push_back(tile); | |
373 | } else {// partial overlap between tile and bbox | ||
374 | ✗ | auto tmp = tile.bbox; | |
375 | ✗ | tmp.intersect(bbox); | |
376 | ✗ | tiles.emplace_back(tmp, tile.value, tile.level); | |
377 | } | ||
378 | } | ||
379 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 5 times.
|
64 | for (auto &node : mRootNodes) {//loop over child nodes of the root node only |
380 | ✗ | if ( !node.bbox.hasOverlap(bbox) ) { | |
381 | ✗ | continue;//ignore non-overlapping child nodes | |
382 | } else if ( node.bbox.isInside(bbox) ) {// bbox is completely inside the child node | ||
383 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | this->activeTiles(node.child, bbox, tiles); |
384 | 2 | return tiles; | |
385 | } else {// partial overlap between tile and child node | ||
386 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
52 | this->activeTiles(node.child, bbox, tiles); |
387 | } | ||
388 | } | ||
389 | 10 | return tiles; | |
390 | } | ||
391 | |||
392 | template<typename TreeT> | ||
393 | template<typename NodeT> | ||
394 | 6638 | typename NodeT::NodeMaskType FindActiveValues<TreeT>::getBBoxMask(const CoordBBox &bbox, const NodeT* node) const | |
395 | { | ||
396 | typename NodeT::NodeMaskType mask;// typically 32^3 or 16^3 bit mask | ||
397 | 6638 | auto b = node->getNodeBoundingBox(); | |
398 | ✗ | assert( bbox.hasOverlap(b) ); | |
399 | if ( bbox.isInside(b) ) { | ||
400 | mask.setOn();//node is completely inside the bbox so early out | ||
401 | } else { | ||
402 | 5980 | b.intersect(bbox);// trim bounding box | |
403 | // transform bounding box from global to local coordinates | ||
404 | b.min() &= NodeT::DIM-1u; | ||
405 | b.min() >>= NodeT::ChildNodeType::TOTAL; | ||
406 | b.max() &= NodeT::DIM-1u; | ||
407 | b.max() >>= NodeT::ChildNodeType::TOTAL; | ||
408 | ✗ | assert( b.hasVolume() ); | |
409 | auto it = b.begin();// iterates over all the child nodes or tiles that intersects bbox | ||
410 |
2/2✓ Branch 0 taken 642615 times.
✓ Branch 1 taken 2990 times.
|
1291210 | for (const Coord& ijk = *it; it; ++it) { |
411 | 1285230 | mask.setOn(ijk[2] + (ijk[1] << NodeT::LOG2DIM) + (ijk[0] << 2*NodeT::LOG2DIM)); | |
412 | } | ||
413 | } | ||
414 | 6638 | return mask; | |
415 | } | ||
416 | |||
417 | template<typename TreeT> | ||
418 | template<typename NodeT> | ||
419 | 3670 | bool FindActiveValues<TreeT>::anyActiveValues(const NodeT* node, const CoordBBox &bbox) const | |
420 | { | ||
421 | // Generate a bit mask of the bbox coverage | ||
422 | 3670 | auto mask = this->getBBoxMask(bbox, node); | |
423 | |||
424 | // Check active tiles | ||
425 | const auto tmp = mask & node->getValueMask();// prune active the tile mask with the bbox mask | ||
426 |
2/2✓ Branch 0 taken 1834 times.
✓ Branch 1 taken 1 times.
|
3670 | if (!tmp.isOff()) return true; |
427 | |||
428 | // Check child nodes | ||
429 | mask &= node->getChildMask();// prune the child mask with the bbox mask | ||
430 | const auto* table = node->getTable(); | ||
431 | bool active = false; | ||
432 |
4/4✓ Branch 1 taken 2812 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 992 times.
✓ Branch 4 taken 1820 times.
|
11276 | for (auto i = mask.beginOn(); !active && i; ++i) { |
433 | 1984 | active = this->anyActiveValues(table[i.pos()].getChild(), bbox); | |
434 | } | ||
435 | 3668 | return active; | |
436 | } | ||
437 | |||
438 | template<typename TreeT> | ||
439 | template<typename NodeT> | ||
440 | 1558 | bool FindActiveValues<TreeT>::anyActiveVoxels(const NodeT* node, const CoordBBox &bbox) const | |
441 | { | ||
442 | // Generate a bit mask of the bbox coverage | ||
443 | 1558 | auto mask = this->getBBoxMask(bbox, node); | |
444 | |||
445 | // Check child nodes | ||
446 | mask &= node->getChildMask();// prune the child mask with the bbox mask | ||
447 | const auto* table = node->getTable(); | ||
448 | bool active = false; | ||
449 |
4/4✓ Branch 1 taken 784 times.
✓ Branch 2 taken 518 times.
✓ Branch 3 taken 523 times.
✓ Branch 4 taken 261 times.
|
4172 | for (auto i = mask.beginOn(); !active && i; ++i) { |
450 | 1046 | active = this->anyActiveVoxels(table[i.pos()].getChild(), bbox); | |
451 | } | ||
452 | 1558 | return active; | |
453 | } | ||
454 | |||
455 | template<typename TreeT> | ||
456 | 337 | inline bool FindActiveValues<TreeT>::anyActiveVoxels(const typename TreeT::LeafNodeType* leaf, const CoordBBox &bbox ) const | |
457 | { | ||
458 | const auto &mask = leaf->getValueMask(); | ||
459 | |||
460 | // check for two common cases that leads to early-out | ||
461 | 924 | if (bbox.isInside(leaf->getNodeBoundingBox())) return !mask.isOff();// leaf in inside the bbox | |
462 |
2/2✓ Branch 0 taken 80 times.
✓ Branch 1 taken 7 times.
|
87 | if (mask.isOn()) return true;// all values are active |
463 | |||
464 | bool active = false; | ||
465 |
4/4✓ Branch 0 taken 8919 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 71 times.
✓ Branch 3 taken 8848 times.
|
17847 | for (auto i = leaf->cbeginValueOn(); !active && i; ++i) { |
466 | 17696 | active = bbox.isInside(i.getCoord()); | |
467 | } | ||
468 | 80 | return active; | |
469 | } | ||
470 | |||
471 | template<typename TreeT> | ||
472 | template<typename NodeT> | ||
473 | 54 | bool FindActiveValues<TreeT>::anyActiveTiles(const NodeT* node, const CoordBBox &bbox) const | |
474 | { | ||
475 | // Generate a bit mask of the bbox coverage | ||
476 | 54 | auto mask = this->getBBoxMask(bbox, node); | |
477 | |||
478 | // Check active tiles | ||
479 | const auto tmp = mask & node->getValueMask();// prune active the tile mask with the bbox mask | ||
480 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 13 times.
|
54 | if (!tmp.isOff()) return true; |
481 | |||
482 | bool active = false; | ||
483 | if (NodeT::LEVEL>1) {// Only check child nodes if they are NOT leaf nodes | ||
484 | mask &= node->getChildMask();// prune the child mask with the bbox mask | ||
485 | const auto* table = node->getTable(); | ||
486 |
4/4✓ Branch 1 taken 25 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 13 times.
✓ Branch 4 taken 12 times.
|
102 | for (auto i = mask.beginOn(); !active && i; ++i) { |
487 | 26 | active = this->anyActiveTiles(table[i.pos()].getChild(), bbox); | |
488 | } | ||
489 | } | ||
490 | 26 | return active; | |
491 | } | ||
492 | |||
493 | template<typename TreeT> | ||
494 | 602812 | inline Index64 FindActiveValues<TreeT>::count(const typename TreeT::LeafNodeType* leaf, const CoordBBox &bbox ) const | |
495 | { | ||
496 | Index64 count = 0; | ||
497 | 602812 | auto b = leaf->getNodeBoundingBox(); | |
498 | if (b.isInside(bbox)) { // leaf node is completely inside bbox | ||
499 | count = leaf->onVoxelCount(); | ||
500 |
2/2✓ Branch 0 taken 524288 times.
✓ Branch 1 taken 78524 times.
|
602812 | } else if (leaf->isDense()) { |
501 | 524288 | b.intersect(bbox); | |
502 | count = b.volume(); | ||
503 | } else if (b.hasOverlap(bbox)) { | ||
504 |
2/2✓ Branch 0 taken 5478729 times.
✓ Branch 1 taken 78524 times.
|
5557253 | for (auto i = leaf->cbeginValueOn(); i; ++i) { |
505 | 10957458 | if (bbox.isInside(i.getCoord())) ++count; | |
506 | } | ||
507 | } | ||
508 | 602812 | return count; | |
509 | } | ||
510 | |||
511 | template<typename TreeT> | ||
512 | template<typename NodeT> | ||
513 | 1030 | Index64 FindActiveValues<TreeT>::count(const NodeT* node, const CoordBBox &bbox) const | |
514 | { | ||
515 | Index64 count = 0; | ||
516 | |||
517 | // Generate a bit masks | ||
518 | 1030 | auto mask = this->getBBoxMask(bbox, node); | |
519 | const auto childMask = mask & node->getChildMask();// prune the child mask with the bbox mask | ||
520 | mask &= node->getValueMask();// prune active tile mask with the bbox mask | ||
521 | const auto* table = node->getTable(); | ||
522 | |||
523 | {// Check child nodes | ||
524 | using ChildT = typename NodeT::ChildNodeType; | ||
525 | using RangeT = tbb::blocked_range<typename std::vector<const ChildT*>::iterator>; | ||
526 | 1030 | std::vector<const ChildT*> childNodes(childMask.countOn()); | |
527 | int j=0; | ||
528 |
2/2✓ Branch 1 taken 603287 times.
✓ Branch 2 taken 515 times.
|
1208634 | for (auto i = childMask.beginOn(); i; ++i, ++j) childNodes[j] = table[i.pos()].getChild(); |
529 |
3/6✓ Branch 1 taken 515 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 506 times.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
1030 | count += tbb::parallel_reduce( RangeT(childNodes.begin(), childNodes.end()), 0, |
530 | 56026 | [&](const RangeT& r, Index64 sum)->Index64 { | |
531 |
4/4✓ Branch 0 taken 602812 times.
✓ Branch 1 taken 55551 times.
✓ Branch 3 taken 475 times.
✓ Branch 4 taken 475 times.
|
659313 | for ( auto i = r.begin(); i != r.end(); ++i ) sum += this->count(*i, bbox); |
532 | 56026 | return sum; | |
533 | }, []( Index64 a, Index64 b )->Index64 { return a+b; } | ||
534 | ); | ||
535 | } | ||
536 | |||
537 | {// Check active tiles | ||
538 | 1030 | std::vector<Coord> coords(mask.countOn()); | |
539 | using RangeT = tbb::blocked_range<typename std::vector<Coord>::iterator>; | ||
540 | int j=0; | ||
541 |
2/2✓ Branch 1 taken 576 times.
✓ Branch 2 taken 515 times.
|
4364 | for (auto i = mask.beginOn(); i; ++i, ++j) coords[j] = node->offsetToGlobalCoord(i.pos()); |
542 |
3/6✓ Branch 1 taken 515 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
✓ Branch 4 taken 499 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
1030 | count += tbb::parallel_reduce( RangeT(coords.begin(), coords.end()), 0, |
543 | 576 | [&bbox](const RangeT& r, Index64 sum)->Index64 { | |
544 |
2/4✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 576 times.
✓ Branch 3 taken 576 times.
|
1152 | for ( auto i = r.begin(); i != r.end(); ++i ) { |
545 | auto b = CoordBBox::createCube(*i, NodeT::ChildNodeType::DIM); | ||
546 | 576 | b.intersect(bbox); | |
547 | 576 | sum += b.volume(); | |
548 | } | ||
549 | 576 | return sum; | |
550 | }, []( Index64 a, Index64 b )->Index64 { return a+b; } | ||
551 | ); | ||
552 | } | ||
553 | |||
554 | 1030 | return count; | |
555 | } | ||
556 | |||
557 | // process internal node | ||
558 | template<typename TreeT> | ||
559 | template<typename NodeT> | ||
560 | 326 | void FindActiveValues<TreeT>::activeTiles(const NodeT* node, const CoordBBox &bbox, std::vector<TileDataT> &tiles) const | |
561 | { | ||
562 | // Generate a bit masks | ||
563 | 326 | auto mask = this->getBBoxMask(bbox, node); | |
564 | 326 | const auto childMask = mask & node->getChildMask();// prune the child mask with the bbox mask | |
565 | 326 | mask &= node->getValueMask();// prune active tile mask with the bbox mask | |
566 | |||
567 | if (NodeT::LEVEL > 1) {// Only check child nodes if they are NOT leaf nodes | ||
568 | 54 | const auto* table = node->getTable(); | |
569 |
2/2✓ Branch 1 taken 136 times.
✓ Branch 2 taken 27 times.
|
380 | for (auto i = childMask.beginOn(); i; ++i) this->activeTiles(table[i.pos()].getChild(), bbox, tiles); |
570 | } | ||
571 | |||
572 | 326 | const size_t tileCount = mask.countOn(); | |
573 |
2/2✓ Branch 0 taken 154 times.
✓ Branch 1 taken 9 times.
|
326 | if (tileCount < 8) {// Serial processing of active tiles |
574 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 154 times.
|
616 | for (auto iter = mask.beginOn(); iter; ++iter) { |
575 | ✗ | tiles.emplace_back(*node, iter.pos()); | |
576 | ✗ | tiles.back().bbox.intersect(bbox); | |
577 | } | ||
578 | } else {// Parallel processing of active tiles | ||
579 | 18 | std::vector<TileDataT> tmp( tileCount );// for temporary thread-safe processing | |
580 | int n = 0; | ||
581 |
2/2✓ Branch 1 taken 82 times.
✓ Branch 2 taken 9 times.
|
200 | for (auto iter = mask.beginOn(); iter; ++iter) tmp[n++].level = iter.pos();// placeholder to support multi-threading |
582 |
2/4✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
|
112 | tbb::parallel_for(tbb::blocked_range<size_t>(0, tileCount, 8), [&](const tbb::blocked_range<size_t>& r) { |
583 |
4/32✗ 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 taken 18 times.
✓ Branch 9 taken 4 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✓ Branch 24 taken 64 times.
✓ Branch 25 taken 8 times.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
|
94 | for ( size_t i = r.begin(); i != r.end(); ++i ) { |
584 | 82 | tmp[i] = TileDataT(*node, tmp[i].level); | |
585 | 82 | tmp[i].bbox.intersect(bbox); | |
586 | } | ||
587 | }); | ||
588 |
2/6✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
18 | tiles.insert(tiles.end(), tmp.begin(), tmp.end()); |
589 | } | ||
590 | } | ||
591 | |||
592 | template<typename TreeT> | ||
593 | struct FindActiveValues<TreeT>::RootChild | ||
594 | { | ||
595 | const CoordBBox bbox; | ||
596 | const RootChildType* child; | ||
597 | 621 | RootChild(const Coord& ijk = Coord(), const RootChildType* ptr = nullptr) | |
598 | 621 | : bbox(CoordBBox::createCube(ijk, RootChildType::DIM)), child(ptr) | |
599 | { | ||
600 | 621 | } | |
601 | };// RootChild struct | ||
602 | |||
603 | ////////////////////////////////////////////////////////////////////////////////////////// | ||
604 | |||
605 | template<typename ValueType> | ||
606 | struct TileData | ||
607 | { | ||
608 | CoordBBox bbox; | ||
609 | ValueType value; | ||
610 | Index level; | ||
611 | bool state; | ||
612 | |||
613 | /// @brief Default constructor | ||
614 | 82 | TileData() = default; | |
615 | |||
616 | /// @brief Member data constructor | ||
617 | ✗ | TileData(const CoordBBox &b, const ValueType &v, Index l, bool active = true) | |
618 | ✗ | : bbox(b), value(v), level(l), state(active) {} | |
619 | |||
620 | /// @brief Constructor from a parent node and the linear offset to one of its tiles | ||
621 | /// | ||
622 | /// @warning This is an expert-only method since it assumes the linear offset to be valid, | ||
623 | /// i.e. within the rand of the dimension of the parent node and NOT corresponding | ||
624 | /// to a child node. | ||
625 | template <typename ParentNodeT> | ||
626 | 164 | TileData(const ParentNodeT &parent, Index childIdx) | |
627 | 164 | : bbox(CoordBBox::createCube(parent.offsetToGlobalCoord(childIdx), parent.getChildDim())) | |
628 | , level(parent.getLevel()) | ||
629 | 164 | , state(true) | |
630 | { | ||
631 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
|
164 | assert(childIdx < ParentNodeT::NUM_VALUES); |
632 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
|
164 | assert(parent.isChildMaskOff(childIdx)); |
633 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
|
164 | assert(parent.isValueMaskOn(childIdx)); |
634 | 164 | value = parent.getTable()[childIdx].getValue(); | |
635 | } | ||
636 | |||
637 | /// @brief Constructor form a parent node, the coordinate of the origin of one of its tiles, | ||
638 | /// and said tiles value. | ||
639 | template <typename ParentNodeT> | ||
640 | ✗ | TileData(const ParentNodeT &parent, const Coord &ijk, const ValueType &v) | |
641 | ✗ | : bbox(CoordBBox::createCube(ijk, parent.getChildDim())) | |
642 | , value(v) | ||
643 | , level(parent.getLevel()) | ||
644 | ✗ | , state(true) | |
645 | { | ||
646 | } | ||
647 | };// TileData struct | ||
648 | |||
649 | ////////////////////////////////////////////////////////////////////////////////////////// | ||
650 | |||
651 | // Implementation of stand-alone function | ||
652 | template<typename TreeT> | ||
653 | bool | ||
654 | 142 | anyActiveValues(const TreeT& tree, const CoordBBox &bbox) | |
655 | { | ||
656 | 284 | FindActiveValues<TreeT> op(tree); | |
657 |
1/2✓ Branch 1 taken 71 times.
✗ Branch 2 not taken.
|
284 | return op.anyActiveValues(bbox); |
658 | } | ||
659 | |||
660 | // Implementation of stand-alone function | ||
661 | template<typename TreeT> | ||
662 | bool | ||
663 | 14 | anyActiveVoxels(const TreeT& tree, const CoordBBox &bbox) | |
664 | { | ||
665 | 28 | FindActiveValues<TreeT> op(tree); | |
666 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
28 | return op.anyActiveVoxels(bbox); |
667 | } | ||
668 | |||
669 | // Implementation of stand-alone function | ||
670 | template<typename TreeT> | ||
671 | bool | ||
672 | 12 | anyActiveTiles(const TreeT& tree, const CoordBBox &bbox) | |
673 | { | ||
674 | 24 | FindActiveValues<TreeT> op(tree); | |
675 | 12 | return op.anyActiveTiles(bbox); | |
676 | } | ||
677 | |||
678 | // Implementation of stand-alone function | ||
679 | template<typename TreeT> | ||
680 | bool | ||
681 | 6 | noActiveValues(const TreeT& tree, const CoordBBox &bbox) | |
682 | { | ||
683 | 12 | FindActiveValues<TreeT> op(tree); | |
684 | 6 | return op.noActiveValues(bbox); | |
685 | } | ||
686 | |||
687 | // Implementation of stand-alone function | ||
688 | template<typename TreeT> | ||
689 | Index64 | ||
690 | ✗ | countActiveValues(const TreeT& tree, const CoordBBox &bbox) | |
691 | { | ||
692 | ✗ | return tools::countActiveVoxels(tree, bbox); | |
693 | } | ||
694 | |||
695 | // Implementation of stand-alone function | ||
696 | template<typename TreeT> | ||
697 | std::vector<TileData<typename TreeT::ValueType>> | ||
698 | 6 | activeTiles(const TreeT& tree, const CoordBBox &bbox) | |
699 | { | ||
700 | 12 | FindActiveValues<TreeT> op(tree); | |
701 | 12 | return op.activeTiles(bbox); | |
702 | } | ||
703 | |||
704 | |||
705 | //////////////////////////////////////// | ||
706 | |||
707 | |||
708 | // Explicit Template Instantiation | ||
709 | |||
710 | #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION | ||
711 | |||
712 | #ifdef OPENVDB_INSTANTIATE_FINDACTIVEVALUES | ||
713 | #include <openvdb/util/ExplicitInstantiation.h> | ||
714 | #endif | ||
715 | |||
716 | #define _FUNCTION(TreeT) \ | ||
717 | bool anyActiveValues(const TreeT&, const CoordBBox&) | ||
718 | OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION) | ||
719 | #undef _FUNCTION | ||
720 | |||
721 | #define _FUNCTION(TreeT) \ | ||
722 | bool anyActiveVoxels(const TreeT&, const CoordBBox&) | ||
723 | OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION) | ||
724 | #undef _FUNCTION | ||
725 | |||
726 | #define _FUNCTION(TreeT) \ | ||
727 | bool anyActiveTiles(const TreeT&, const CoordBBox&) | ||
728 | OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION) | ||
729 | #undef _FUNCTION | ||
730 | |||
731 | #define _FUNCTION(TreeT) \ | ||
732 | bool noActiveValues(const TreeT&, const CoordBBox&) | ||
733 | OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION) | ||
734 | #undef _FUNCTION | ||
735 | |||
736 | #define _FUNCTION(TreeT) \ | ||
737 | Index64 countActiveValues(const TreeT&, const CoordBBox&) | ||
738 | OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION) | ||
739 | #undef _FUNCTION | ||
740 | |||
741 | #define _FUNCTION(TreeT) \ | ||
742 | std::vector<TileData<TreeT::ValueType>> activeTiles(const TreeT&, const CoordBBox&) | ||
743 | OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION) | ||
744 | #undef _FUNCTION | ||
745 | |||
746 | #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION | ||
747 | |||
748 | |||
749 | } // namespace tools | ||
750 | } // namespace OPENVDB_VERSION_NAME | ||
751 | } // namespace openvdb | ||
752 | |||
753 | #endif // OPENVDB_TOOLS_FINDACTIVEVALUES_HAS_BEEN_INCLUDED | ||
754 |