Skip to content

Commit 4a4d673

Browse files
authored
Merge pull request #2154 from danrbailey/node_visitor_bottom_up
Extend visitNodesDepthFirst() to offer bottom-up traversal
2 parents 522ceca + 55b94e2 commit 4a4d673

3 files changed

Lines changed: 57 additions & 7 deletions

File tree

openvdb/openvdb/tools/NodeVisitor.h

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ namespace tools {
3030
/// @param op user-supplied functor, see examples for interface details.
3131
/// @param idx optional offset to start sequential node indexing from a
3232
/// non-zero index.
33+
/// @param topDown if true, visit parent nodes before their children
34+
/// (pre-order traversal). If false, visit children before
35+
/// their parent nodes (post-order traversal). Defaults to true.
3336
///
3437
/// @warning This method is single-threaded. Use the NodeManager or
3538
/// DynamicNodeManager for much greater threaded performance.
@@ -173,7 +176,7 @@ namespace tools {
173176
///
174177
/// @endcode
175178
template <typename TreeT, typename OpT>
176-
size_t visitNodesDepthFirst(TreeT& tree, OpT& op, size_t idx = 0);
179+
size_t visitNodesDepthFirst(TreeT& tree, OpT& op, size_t idx = 0, bool topDown = true);
177180

178181

179182
/// @brief Visit all nodes that are downstream of a specific node in
@@ -199,14 +202,15 @@ struct DepthFirstNodeVisitor
199202
using ChildNodeType = typename CopyConstness<NodeT, NonConstChildType>::Type;
200203

201204
template <typename OpT>
202-
static size_t visit(NodeT& node, OpT& op, size_t idx = 0)
205+
static size_t visit(NodeT& node, OpT& op, size_t idx = 0, bool topDown = true)
203206
{
204207
size_t offset = 0;
205-
op(node, idx + offset++);
208+
if (topDown) op(node, idx + offset++);
206209
for (auto iter = node.beginChildOn(); iter; ++iter) {
207210
offset += DepthFirstNodeVisitor<ChildNodeType>::visit(
208-
*iter, op, idx + offset);
211+
*iter, op, idx + offset, topDown);
209212
}
213+
if (!topDown) op(node, idx + offset++);
210214
return offset;
211215
}
212216
};
@@ -217,7 +221,7 @@ template <typename NodeT>
217221
struct DepthFirstNodeVisitor<NodeT, 0>
218222
{
219223
template <typename OpT>
220-
static size_t visit(NodeT& node, OpT& op, size_t idx = 0)
224+
static size_t visit(NodeT& node, OpT& op, size_t idx = 0, bool /*topDown*/ = true)
221225
{
222226
op(node, idx);
223227
return size_t(1);
@@ -226,12 +230,12 @@ struct DepthFirstNodeVisitor<NodeT, 0>
226230

227231

228232
template <typename TreeT, typename OpT>
229-
size_t visitNodesDepthFirst(TreeT& tree, OpT& op, size_t idx)
233+
size_t visitNodesDepthFirst(TreeT& tree, OpT& op, size_t idx, bool topDown)
230234
{
231235
using NonConstRootNodeType = typename TreeT::RootNodeType;
232236
using RootNodeType = typename CopyConstness<TreeT, NonConstRootNodeType>::Type;
233237

234-
return DepthFirstNodeVisitor<RootNodeType>::visit(tree.root(), op, idx);
238+
return DepthFirstNodeVisitor<RootNodeType>::visit(tree.root(), op, idx, topDown);
235239
}
236240

237241

openvdb/openvdb/unittest/TestNodeVisitor.cc

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,3 +286,46 @@ TEST_F(TestNodeVisitor, testOffset)
286286
OffsetByLevelOp<FloatTree> byLevelOp(3.0f);
287287
tools::visitNodesDepthFirst(grid->tree(), byLevelOp);
288288
}
289+
290+
291+
struct RecordLevelOrderOp
292+
{
293+
template <typename NodeT>
294+
void operator()(const NodeT&, size_t)
295+
{
296+
const openvdb::Index level = NodeT::LEVEL;
297+
levels.push_back(level);
298+
}
299+
300+
std::vector<openvdb::Index> levels;
301+
}; // struct RecordLevelOrderOp
302+
303+
304+
TEST_F(TestNodeVisitor, testTopDownBottomUp)
305+
{
306+
using namespace openvdb;
307+
308+
FloatGrid::Ptr grid = tools::createLevelSetCube<FloatGrid>(/*scale=*/10.0f);
309+
310+
// Visit with topDown=true (pre-order: parent before children)
311+
RecordLevelOrderOp topDownOp;
312+
tools::visitNodesDepthFirst(grid->tree(), topDownOp, 0, true);
313+
314+
// Visit with topDown=false (post-order: children before parent)
315+
RecordLevelOrderOp bottomUpOp;
316+
tools::visitNodesDepthFirst(grid->tree(), bottomUpOp, 0, false);
317+
318+
// Both should visit the same number of nodes
319+
EXPECT_EQ(topDownOp.levels.size(), bottomUpOp.levels.size());
320+
EXPECT_GT(topDownOp.levels.size(), size_t(0));
321+
322+
// Top-down: first element should be the root (highest level)
323+
const Index rootLevel = FloatTree::RootNodeType::LEVEL;
324+
EXPECT_EQ(topDownOp.levels.front(), rootLevel);
325+
326+
// Bottom-up: last element should be the root (highest level)
327+
EXPECT_EQ(bottomUpOp.levels.back(), rootLevel);
328+
329+
// The two sequences should not be identical (different traversal order)
330+
EXPECT_NE(topDownOp.levels, bottomUpOp.levels);
331+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
OpenVDB:
2+
Improvements:
3+
- Extend tools::visitNodesDepthFirst() to be able to perform bottom-up traversal in addition to top-down traversal.

0 commit comments

Comments
 (0)