Skip to main content

bsp_tree/bsp/
visitor.rs

1//! Visitor pattern for BSP tree traversal.
2//!
3//! Visitors allow custom processing of polygons during tree traversal
4//! without coupling traversal logic to specific use cases.
5
6use crate::Polygon;
7
8/// Visitor for processing polygons during BSP tree traversal.
9///
10/// Implement this trait to define custom behavior when traversing the tree.
11/// Common uses include:
12/// - Rendering (painter's algorithm)
13/// - Collecting polygons in sorted order
14/// - Computing visibility
15pub trait BspVisitor {
16    /// Called for each group of coplanar polygons during traversal.
17    ///
18    /// The polygons passed to this method are all coplanar with each other
19    /// and belong to the same BSP node.
20    fn visit(&mut self, polygons: &[Polygon]);
21}
22
23/// A simple visitor that collects all visited polygons.
24#[derive(Debug, Default)]
25pub struct CollectingVisitor {
26    collected: Vec<Polygon>,
27}
28
29impl CollectingVisitor {
30    /// Creates a new empty collecting visitor.
31    pub fn new() -> Self {
32        Self::default()
33    }
34
35    /// Returns the collected polygons.
36    pub fn into_polygons(self) -> Vec<Polygon> {
37        self.collected
38    }
39
40    /// Returns a reference to the collected polygons.
41    pub fn polygons(&self) -> &[Polygon] {
42        &self.collected
43    }
44}
45
46impl BspVisitor for CollectingVisitor {
47    fn visit(&mut self, polygons: &[Polygon]) {
48        self.collected.extend(polygons.iter().cloned());
49    }
50}
51
52/// A visitor that calls a closure for each polygon group.
53pub struct FnVisitor<F>
54where
55    F: FnMut(&[Polygon]),
56{
57    func: F,
58}
59
60impl<F> FnVisitor<F>
61where
62    F: FnMut(&[Polygon]),
63{
64    /// Creates a new visitor from a closure.
65    pub fn new(func: F) -> Self {
66        Self { func }
67    }
68}
69
70impl<F> BspVisitor for FnVisitor<F>
71where
72    F: FnMut(&[Polygon]),
73{
74    fn visit(&mut self, polygons: &[Polygon]) {
75        (self.func)(polygons);
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use nalgebra::Point3;
83
84    fn make_triangle(a: [f32; 3], b: [f32; 3], c: [f32; 3]) -> Polygon {
85        Polygon::new(vec![
86            Point3::new(a[0], a[1], a[2]),
87            Point3::new(b[0], b[1], b[2]),
88            Point3::new(c[0], c[1], c[2]),
89        ])
90    }
91
92    #[test]
93    fn collecting_visitor_empty() {
94        let visitor = CollectingVisitor::new();
95        assert!(visitor.polygons().is_empty());
96    }
97
98    #[test]
99    fn collecting_visitor_collects() {
100        let mut visitor = CollectingVisitor::new();
101        let poly1 = make_triangle([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
102        let poly2 = make_triangle([0.0, 0.0, 1.0], [1.0, 0.0, 1.0], [0.0, 1.0, 1.0]);
103
104        visitor.visit(&[poly1.clone()]);
105        visitor.visit(&[poly2.clone()]);
106
107        let collected = visitor.into_polygons();
108        assert_eq!(collected.len(), 2);
109        assert_eq!(collected[0], poly1);
110        assert_eq!(collected[1], poly2);
111    }
112
113    #[test]
114    fn fn_visitor_calls_closure() {
115        let mut count = 0;
116        {
117            let mut visitor = FnVisitor::new(|polys: &[Polygon]| {
118                count += polys.len();
119            });
120
121            let poly = make_triangle([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
122            visitor.visit(&[poly.clone(), poly]);
123        }
124        assert_eq!(count, 2);
125    }
126}