1use crate::{Plane3D, Polygon};
4
5#[derive(Debug, Clone)]
21pub struct BspNode {
22 plane: Plane3D,
24
25 coplanar_front: Vec<Polygon>,
27
28 coplanar_back: Vec<Polygon>,
30
31 front: Option<Box<BspNode>>,
33
34 back: Option<Box<BspNode>>,
36}
37
38impl BspNode {
39 pub fn new(plane: Plane3D) -> Self {
43 Self {
44 plane,
45 coplanar_front: Vec::new(),
46 coplanar_back: Vec::new(),
47 front: None,
48 back: None,
49 }
50 }
51
52 pub fn with_coplanar(
54 plane: Plane3D,
55 coplanar_front: Vec<Polygon>,
56 coplanar_back: Vec<Polygon>,
57 ) -> Self {
58 Self {
59 plane,
60 coplanar_front,
61 coplanar_back,
62 front: None,
63 back: None,
64 }
65 }
66
67 #[inline]
69 pub fn plane(&self) -> &Plane3D {
70 &self.plane
71 }
72
73 #[inline]
75 pub fn coplanar_front(&self) -> &[Polygon] {
76 &self.coplanar_front
77 }
78
79 #[inline]
81 pub fn coplanar_back(&self) -> &[Polygon] {
82 &self.coplanar_back
83 }
84
85 pub fn all_coplanar(&self) -> impl Iterator<Item = &Polygon> {
87 self.coplanar_front.iter().chain(self.coplanar_back.iter())
88 }
89
90 pub fn coplanar_count(&self) -> usize {
92 self.coplanar_front.len() + self.coplanar_back.len()
93 }
94
95 #[inline]
97 pub fn front(&self) -> Option<&BspNode> {
98 self.front.as_deref()
99 }
100
101 #[inline]
103 pub fn back(&self) -> Option<&BspNode> {
104 self.back.as_deref()
105 }
106
107 #[inline]
109 pub fn front_mut(&mut self) -> Option<&mut BspNode> {
110 self.front.as_deref_mut()
111 }
112
113 #[inline]
115 pub fn back_mut(&mut self) -> Option<&mut BspNode> {
116 self.back.as_deref_mut()
117 }
118
119 #[inline]
121 pub fn set_front(&mut self, node: Option<BspNode>) {
122 self.front = node.map(Box::new);
123 }
124
125 #[inline]
127 pub fn set_back(&mut self, node: Option<BspNode>) {
128 self.back = node.map(Box::new);
129 }
130
131 #[inline]
133 pub fn add_coplanar_front(&mut self, polygon: Polygon) {
134 self.coplanar_front.push(polygon);
135 }
136
137 #[inline]
139 pub fn add_coplanar_back(&mut self, polygon: Polygon) {
140 self.coplanar_back.push(polygon);
141 }
142
143 #[inline]
145 pub fn is_leaf(&self) -> bool {
146 self.front.is_none() && self.back.is_none()
147 }
148
149 pub fn polygon_count(&self) -> usize {
151 let mut count = self.coplanar_count();
152
153 if let Some(ref front) = self.front {
154 count += front.polygon_count();
155 }
156 if let Some(ref back) = self.back {
157 count += back.polygon_count();
158 }
159
160 count
161 }
162
163 pub fn depth(&self) -> usize {
165 let front_depth = self.front.as_ref().map_or(0, |n| n.depth());
166 let back_depth = self.back.as_ref().map_or(0, |n| n.depth());
167 1 + front_depth.max(back_depth)
168 }
169}
170
171#[inline]
180pub fn faces_same_direction(polygon: &Polygon, plane: &Plane3D) -> bool {
181 let poly_normal = polygon
182 .unit_normal()
183 .expect("Polygon must have a valid normal for BSP operations");
184 poly_normal.dot(&plane.normal()) > 0.0
185}
186
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191 use nalgebra::{Point3, Vector3};
192
193 fn make_triangle(a: [f32; 3], b: [f32; 3], c: [f32; 3]) -> Polygon {
194 Polygon::new(vec![
195 Point3::new(a[0], a[1], a[2]),
196 Point3::new(b[0], b[1], b[2]),
197 Point3::new(c[0], c[1], c[2]),
198 ])
199 }
200
201 #[test]
202 fn new_node_is_empty_leaf() {
203 let plane = Plane3D::new(Vector3::new(0.0, 1.0, 0.0), 0.0);
204 let node = BspNode::new(plane);
205
206 assert!(node.is_leaf());
207 assert_eq!(node.coplanar_count(), 0);
208 assert_eq!(node.polygon_count(), 0);
209 assert_eq!(node.depth(), 1);
210 }
211
212 #[test]
213 fn with_coplanar_stores_polygons() {
214 let plane = Plane3D::new(Vector3::new(0.0, 1.0, 0.0), 0.0);
215 let poly1 = make_triangle([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]);
216 let poly2 = make_triangle([0.0, 0.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]);
217
218 let node = BspNode::with_coplanar(plane, vec![poly1], vec![poly2]);
219
220 assert_eq!(node.coplanar_front().len(), 1);
221 assert_eq!(node.coplanar_back().len(), 1);
222 assert_eq!(node.coplanar_count(), 2);
223 }
224
225 #[test]
226 fn set_children_updates_leaf_status() {
227 let plane = Plane3D::new(Vector3::new(0.0, 1.0, 0.0), 0.0);
228 let mut node = BspNode::new(plane.clone());
229
230 assert!(node.is_leaf());
231
232 node.set_front(Some(BspNode::new(plane.clone())));
233 assert!(!node.is_leaf());
234
235 node.set_front(None);
236 assert!(node.is_leaf());
237
238 node.set_back(Some(BspNode::new(plane)));
239 assert!(!node.is_leaf());
240 }
241
242 #[test]
243 fn depth_calculation() {
244 let plane = Plane3D::new(Vector3::new(0.0, 1.0, 0.0), 0.0);
245 let mut root = BspNode::new(plane.clone());
246 assert_eq!(root.depth(), 1);
247
248 let mut front = BspNode::new(plane.clone());
249 front.set_front(Some(BspNode::new(plane.clone())));
250 root.set_front(Some(front));
251
252 assert_eq!(root.depth(), 3);
254
255 root.set_back(Some(BspNode::new(plane)));
256 assert_eq!(root.depth(), 3);
258 }
259
260 #[test]
261 fn polygon_count_recursive() {
262 let plane = Plane3D::new(Vector3::new(0.0, 1.0, 0.0), 0.0);
263 let poly = make_triangle([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]);
264
265 let mut root = BspNode::with_coplanar(plane.clone(), vec![poly.clone()], vec![]);
266 assert_eq!(root.polygon_count(), 1);
267
268 let front = BspNode::with_coplanar(plane.clone(), vec![poly.clone(), poly.clone()], vec![]);
269 let back = BspNode::with_coplanar(plane, vec![], vec![poly]);
270 root.set_front(Some(front));
271 root.set_back(Some(back));
272
273 assert_eq!(root.polygon_count(), 4);
274 }
275
276 #[test]
277 fn faces_same_direction_positive() {
278 let poly = make_triangle([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]);
280 let plane = Plane3D::new(Vector3::new(0.0, 1.0, 0.0), 0.0);
281
282 let poly_normal = poly.unit_normal().unwrap();
285 assert!(poly_normal.y < 0.0);
287
288 assert!(!faces_same_direction(&poly, &plane));
290 }
291
292 #[test]
293 fn faces_same_direction_negative() {
294 let poly = make_triangle([0.0, 0.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]);
296 let plane = Plane3D::new(Vector3::new(0.0, 1.0, 0.0), 0.0);
297
298 let poly_normal = poly.unit_normal().unwrap();
300 assert!(poly_normal.y > 0.0);
301
302 assert!(faces_same_direction(&poly, &plane));
304 }
305}