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     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
anyActiveValues(const typename TreeT::LeafNodeType * leaf,const CoordBBox & bbox)203     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;
anyActiveTiles(const typename TreeT::LeafNodeType *,const CoordBBox &)205     static bool anyActiveTiles( const typename TreeT::LeafNodeType*, const CoordBBox& ) {return false;}
activeTiles(const typename TreeT::LeafNodeType *,const CoordBBox &,std::vector<TileDataT> &)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>
FindActiveValues(const TreeT & tree)235 FindActiveValues<TreeT>::FindActiveValues(const TreeT& tree) : mAcc(tree), mRootTiles(), mRootNodes()
236 {
237     this->init(tree);
238 }
239 
240 template<typename TreeT>
~FindActiveValues()241 FindActiveValues<TreeT>::~FindActiveValues()
242 {
243     this->clear();
244 }
245 
246 template<typename TreeT>
update(const TreeT & tree)247 void FindActiveValues<TreeT>::update(const TreeT& tree)
248 {
249     this->clear();
250     mAcc = AccT(tree);
251     this->init(tree);
252 }
253 
254 template<typename TreeT>
clear()255 void FindActiveValues<TreeT>::clear()
256 {
257     mRootNodes.clear();
258     mRootTiles.clear();
259 }
260 
261 template<typename TreeT>
init(const TreeT & tree)262 void FindActiveValues<TreeT>::init(const TreeT& tree)
263 {
264     const auto &root = tree.root();
265     for (auto i = root.cbeginChildOn(); i; ++i) {
266         mRootNodes.emplace_back(i.getCoord(), &*i);
267     }
268     for (auto i = root.cbeginValueOn(); i; ++i) {
269         mRootTiles.emplace_back(root, i.getCoord(), *i);
270     }
271 }
272 
273 template<typename TreeT>
anyActiveValues(const CoordBBox & bbox,bool useAccessor)274 bool FindActiveValues<TreeT>::anyActiveValues(const CoordBBox &bbox, bool useAccessor) const
275 {
276     // test early-out: the center of the bbox is active
277     if (useAccessor) {
278         if (mAcc.isValueOn( (bbox.min() + bbox.max())>>1 )) return true;
279     } else {
280         if (mAcc.tree().isValueOn( (bbox.min() + bbox.max())>>1 )) return true;
281     }
282 
283     for (auto& tile : mRootTiles) {
284         if (tile.bbox.hasOverlap(bbox)) return true;
285     }
286     for (auto& node : mRootNodes) {
287         if (!node.bbox.hasOverlap(bbox)) {// no overlap
288             continue;
289         } else if (node.bbox.isInside(bbox)) {// bbox is inside the child node
290             return this->anyActiveValues(node.child, bbox);
291         } else if (this->anyActiveValues(node.child, bbox)) {// bbox overlaps the child node
292             return true;
293         }
294     }
295     return false;
296 }
297 
298 template<typename TreeT>
anyActiveVoxels(const CoordBBox & bbox)299 bool FindActiveValues<TreeT>::anyActiveVoxels(const CoordBBox &bbox) const
300 {
301     for (auto& node : mRootNodes) {
302         if (!node.bbox.hasOverlap(bbox)) {// no overlap
303             continue;
304         } else if (node.bbox.isInside(bbox)) {// bbox is inside the child node
305             return this->anyActiveVoxels(node.child, bbox);
306         } else if (this->anyActiveVoxels(node.child, bbox)) {// bbox overlaps the child node
307             return true;
308         }
309     }
310     return false;
311 }
312 
313 template<typename TreeT>
anyActiveTiles(const CoordBBox & bbox)314 bool FindActiveValues<TreeT>::anyActiveTiles(const CoordBBox &bbox) const
315 {
316     for (auto& tile : mRootTiles) {
317         if (tile.bbox.hasOverlap(bbox)) return true;
318     }
319     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             return this->anyActiveTiles(node.child, bbox);
324         } else if (this->anyActiveTiles(node.child, bbox)) {// bbox overlaps the child node
325             return true;
326         }
327     }
328     return false;
329 }
330 
331 template<typename TreeT>
count(const CoordBBox & bbox)332 Index64 FindActiveValues<TreeT>::count(const CoordBBox &bbox) const
333 {
334     Index64 count = 0;
335     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     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             count += this->count(node.child, bbox);
355         }
356     }
357     return count;
358 }
359 
360 template<typename TreeT>
361 std::vector<TileData<typename TreeT::ValueType> >
activeTiles(const CoordBBox & bbox)362 FindActiveValues<TreeT>::activeTiles(const CoordBBox &bbox) const
363 {
364     std::vector<TileDataT> tiles;
365     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     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             this->activeTiles(node.child, bbox, tiles);
384             return tiles;
385         } else {// partial overlap between tile and child node
386             this->activeTiles(node.child, bbox, tiles);
387         }
388     }
389     return tiles;
390 }
391 
392 template<typename TreeT>
393 template<typename NodeT>
getBBoxMask(const CoordBBox & bbox,const NodeT * node)394 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     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         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         for (const Coord& ijk = *it; it; ++it) {
411             mask.setOn(ijk[2] + (ijk[1] << NodeT::LOG2DIM) + (ijk[0] << 2*NodeT::LOG2DIM));
412         }
413     }
414     return mask;
415 }
416 
417 template<typename TreeT>
418 template<typename NodeT>
anyActiveValues(const NodeT * node,const CoordBBox & bbox)419 bool FindActiveValues<TreeT>::anyActiveValues(const NodeT* node, const CoordBBox &bbox) const
420 {
421     // Generate a bit mask of the bbox coverage
422     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     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     for (auto i = mask.beginOn(); !active && i; ++i) {
433         active = this->anyActiveValues(table[i.pos()].getChild(), bbox);
434     }
435     return active;
436 }
437 
438 template<typename TreeT>
439 template<typename NodeT>
anyActiveVoxels(const NodeT * node,const CoordBBox & bbox)440 bool FindActiveValues<TreeT>::anyActiveVoxels(const NodeT* node, const CoordBBox &bbox) const
441 {
442     // Generate a bit mask of the bbox coverage
443     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     for (auto i = mask.beginOn(); !active && i; ++i) {
450         active = this->anyActiveVoxels(table[i.pos()].getChild(), bbox);
451     }
452     return active;
453 }
454 
455 template<typename TreeT>
anyActiveVoxels(const typename TreeT::LeafNodeType * leaf,const CoordBBox & bbox)456 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     if (bbox.isInside(leaf->getNodeBoundingBox())) return !mask.isOff();// leaf in inside the bbox
462     if (mask.isOn()) return true;// all values are active
463 
464     bool active = false;
465     for (auto i = leaf->cbeginValueOn(); !active && i; ++i) {
466         active = bbox.isInside(i.getCoord());
467     }
468     return active;
469 }
470 
471 template<typename TreeT>
472 template<typename NodeT>
anyActiveTiles(const NodeT * node,const CoordBBox & bbox)473 bool FindActiveValues<TreeT>::anyActiveTiles(const NodeT* node, const CoordBBox &bbox) const
474 {
475     // Generate a bit mask of the bbox coverage
476     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     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         for (auto i = mask.beginOn(); !active && i; ++i) {
487             active = this->anyActiveTiles(table[i.pos()].getChild(), bbox);
488         }
489     }
490     return active;
491 }
492 
493 template<typename TreeT>
count(const typename TreeT::LeafNodeType * leaf,const CoordBBox & bbox)494 inline Index64 FindActiveValues<TreeT>::count(const typename TreeT::LeafNodeType* leaf, const CoordBBox &bbox ) const
495 {
496     Index64 count = 0;
497     auto b = leaf->getNodeBoundingBox();
498     if (b.isInside(bbox)) { // leaf node is completely inside bbox
499         count = leaf->onVoxelCount();
500     } else if (leaf->isDense()) {
501         b.intersect(bbox);
502         count = b.volume();
503     } else if (b.hasOverlap(bbox)) {
504         for (auto i = leaf->cbeginValueOn(); i; ++i) {
505             if (bbox.isInside(i.getCoord())) ++count;
506         }
507     }
508     return count;
509 }
510 
511 template<typename TreeT>
512 template<typename NodeT>
count(const NodeT * node,const CoordBBox & bbox)513 Index64 FindActiveValues<TreeT>::count(const NodeT* node, const CoordBBox &bbox) const
514 {
515     Index64 count = 0;
516 
517     // Generate a bit masks
518     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         std::vector<const ChildT*> childNodes(childMask.countOn());
527         int j=0;
528         for (auto i = childMask.beginOn(); i; ++i, ++j) childNodes[j] = table[i.pos()].getChild();
529         count += tbb::parallel_reduce( RangeT(childNodes.begin(), childNodes.end()), 0,
530             [&](const RangeT& r, Index64 sum)->Index64 {
531                 for ( auto i = r.begin(); i != r.end(); ++i ) sum += this->count(*i, bbox);
532                 return sum;
533             }, []( Index64 a, Index64 b )->Index64 { return a+b; }
534         );
535     }
536 
537     {// Check active tiles
538         std::vector<Coord> coords(mask.countOn());
539         using RangeT = tbb::blocked_range<typename std::vector<Coord>::iterator>;
540         int j=0;
541         for (auto i = mask.beginOn(); i; ++i, ++j) coords[j] = node->offsetToGlobalCoord(i.pos());
542         count += tbb::parallel_reduce( RangeT(coords.begin(), coords.end()), 0,
543             [&bbox](const RangeT& r, Index64 sum)->Index64 {
544                 for ( auto i = r.begin(); i != r.end(); ++i ) {
545                     auto b = CoordBBox::createCube(*i, NodeT::ChildNodeType::DIM);
546                     b.intersect(bbox);
547                     sum += b.volume();
548                 }
549                 return sum;
550             }, []( Index64 a, Index64 b )->Index64 { return a+b; }
551         );
552     }
553 
554     return count;
555 }
556 
557 // process internal node
558 template<typename TreeT>
559 template<typename NodeT>
activeTiles(const NodeT * node,const CoordBBox & bbox,std::vector<TileDataT> & tiles)560 void FindActiveValues<TreeT>::activeTiles(const NodeT* node, const CoordBBox &bbox, std::vector<TileDataT> &tiles) const
561 {
562     // Generate a bit masks
563     auto mask = this->getBBoxMask(bbox, node);
564     const auto childMask = mask & node->getChildMask();// prune the child mask with the bbox mask
565     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         const auto* table = node->getTable();
569         for (auto i = childMask.beginOn(); i; ++i) this->activeTiles(table[i.pos()].getChild(), bbox, tiles);
570     }
571 
572     const size_t tileCount = mask.countOn();
573     if (tileCount < 8) {// Serial processing of active tiles
574         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         std::vector<TileDataT> tmp( tileCount );// for temporary thread-safe processing
580         int n = 0;
581         for (auto iter = mask.beginOn(); iter; ++iter) tmp[n++].level = iter.pos();// placeholder to support multi-threading
582         tbb::parallel_for(tbb::blocked_range<size_t>(0, tileCount, 8), [&](const tbb::blocked_range<size_t>& r) {
583             for ( size_t i = r.begin(); i != r.end(); ++i ) {
584                 tmp[i] = TileDataT(*node, tmp[i].level);
585                 tmp[i].bbox.intersect(bbox);
586             }
587         });
588         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     RootChild(const Coord& ijk = Coord(), const RootChildType* ptr = nullptr)
598         : bbox(CoordBBox::createCube(ijk, RootChildType::DIM)), child(ptr)
599     {
600     }
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     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     TileData(const ParentNodeT &parent, Index childIdx)
627         : bbox(CoordBBox::createCube(parent.offsetToGlobalCoord(childIdx), parent.getChildDim()))
628         , level(parent.getLevel())
629         , state(true)
630     {
631         assert(childIdx < ParentNodeT::NUM_VALUES);
632         assert(parent.isChildMaskOff(childIdx));
633         assert(parent.isValueMaskOn(childIdx));
634         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 anyActiveValues(const TreeT& tree, const CoordBBox &bbox)
655 {
656     FindActiveValues<TreeT> op(tree);
657     return op.anyActiveValues(bbox);
658 }
659 
660 // Implementation of stand-alone function
661 template<typename TreeT>
662 bool
663 anyActiveVoxels(const TreeT& tree, const CoordBBox &bbox)
664 {
665     FindActiveValues<TreeT> op(tree);
666     return op.anyActiveVoxels(bbox);
667 }
668 
669 // Implementation of stand-alone function
670 template<typename TreeT>
671 bool
672 anyActiveTiles(const TreeT& tree, const CoordBBox &bbox)
673 {
674     FindActiveValues<TreeT> op(tree);
675     return op.anyActiveTiles(bbox);
676 }
677 
678 // Implementation of stand-alone function
679 template<typename TreeT>
680 bool
681 noActiveValues(const TreeT& tree, const CoordBBox &bbox)
682 {
683     FindActiveValues<TreeT> op(tree);
684     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 activeTiles(const TreeT& tree, const CoordBBox &bbox)
699 {
700     FindActiveValues<TreeT> op(tree);
701     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