1use crate::surface::NurbsSurface;
11use crate::Vec3;
12
13#[derive(Clone, Copy, Debug)]
14pub struct Aabb {
15 pub minimum: Vec3,
16 pub maximum: Vec3,
17}
18
19fn axis(point: Vec3, index: usize) -> f64 {
20 match index {
21 0 => point.x,
22 1 => point.y,
23 _ => point.z,
24 }
25}
26
27impl Aabb {
28 pub fn empty() -> Self {
29 Self {
30 minimum: Vec3::new(f64::INFINITY, f64::INFINITY, f64::INFINITY),
31 maximum: Vec3::new(f64::NEG_INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY),
32 }
33 }
34
35 pub fn include_point(&mut self, point: Vec3) {
36 self.minimum.x = self.minimum.x.min(point.x);
37 self.minimum.y = self.minimum.y.min(point.y);
38 self.minimum.z = self.minimum.z.min(point.z);
39 self.maximum.x = self.maximum.x.max(point.x);
40 self.maximum.y = self.maximum.y.max(point.y);
41 self.maximum.z = self.maximum.z.max(point.z);
42 }
43
44 pub fn include(&mut self, other: Self) {
45 self.include_point(other.minimum);
46 self.include_point(other.maximum);
47 }
48
49 pub fn from_surface_controls(surface: &NurbsSurface) -> Result<Self, String> {
50 let mut bounds = Self::empty();
51 for control in surface.control_points.iter().flatten() {
52 bounds.include_point(control.point()?);
53 }
54 Ok(bounds)
55 }
56
57 pub fn expanded(self, amount: f64) -> Self {
58 let delta = Vec3::new(amount, amount, amount);
59 Self {
60 minimum: self.minimum.sub(delta),
61 maximum: self.maximum.add(delta),
62 }
63 }
64
65 pub fn contains(self, point: Vec3) -> bool {
66 point.x >= self.minimum.x
67 && point.x <= self.maximum.x
68 && point.y >= self.minimum.y
69 && point.y <= self.maximum.y
70 && point.z >= self.minimum.z
71 && point.z <= self.maximum.z
72 }
73
74 pub fn intersects(self, other: Self, tolerance: f64) -> bool {
75 self.minimum.x - tolerance <= other.maximum.x
76 && self.maximum.x + tolerance >= other.minimum.x
77 && self.minimum.y - tolerance <= other.maximum.y
78 && self.maximum.y + tolerance >= other.minimum.y
79 && self.minimum.z - tolerance <= other.maximum.z
80 && self.maximum.z + tolerance >= other.minimum.z
81 }
82
83 pub fn diagonal(self) -> f64 {
84 self.maximum.sub(self.minimum).length()
85 }
86
87 fn center_along(self, index: usize) -> f64 {
88 (axis(self.minimum, index) + axis(self.maximum, index)) / 2.0
89 }
90
91 fn intersects_segment(self, start: Vec3, delta: Vec3, tolerance: f64) -> bool {
92 let mut enter = 0.0f64;
93 let mut exit = 1.0f64;
94 for index in 0..3 {
95 let origin = axis(start, index);
96 let direction = axis(delta, index);
97 let minimum = axis(self.minimum, index) - tolerance;
98 let maximum = axis(self.maximum, index) + tolerance;
99 if direction.abs() < 1e-300 {
100 if origin < minimum || origin > maximum {
101 return false;
102 }
103 continue;
104 }
105 let inverse = 1.0 / direction;
106 let mut near = (minimum - origin) * inverse;
107 let mut far = (maximum - origin) * inverse;
108 if near > far {
109 std::mem::swap(&mut near, &mut far);
110 }
111 enter = enter.max(near);
112 exit = exit.min(far);
113 if enter > exit {
114 return false;
115 }
116 }
117 true
118 }
119}
120
121const LEAF_SIZE: usize = 4;
122
123struct Node {
124 bounds: Aabb,
125 left: u32,
128 right: u32,
131 leaf: bool,
132}
133
134pub struct Bvh {
137 nodes: Vec<Node>,
138 order: Vec<u32>,
139 boxes: Vec<Aabb>,
140}
141
142impl Bvh {
143 pub fn build(boxes: &[Aabb]) -> Self {
144 let mut order: Vec<u32> = (0..boxes.len() as u32).collect();
145 let mut nodes = Vec::new();
146 if !boxes.is_empty() {
147 let count = order.len();
148 build_node(boxes, &mut order, 0, count, &mut nodes);
149 }
150 Self {
151 nodes,
152 order,
153 boxes: boxes.to_vec(),
154 }
155 }
156
157 pub fn overlapping(&self, query: Aabb, tolerance: f64, out: &mut Vec<usize>) {
159 self.visit(|bounds| bounds.intersects(query, tolerance), out);
160 }
161
162 pub fn intersecting_segment(
165 &self,
166 start: Vec3,
167 end: Vec3,
168 tolerance: f64,
169 out: &mut Vec<usize>,
170 ) {
171 let delta = end.sub(start);
172 self.visit(
173 |bounds| bounds.intersects_segment(start, delta, tolerance),
174 out,
175 );
176 }
177
178 pub fn containing_point(&self, point: Vec3, tolerance: f64, out: &mut Vec<usize>) {
180 self.visit(|bounds| bounds.expanded(tolerance).contains(point), out);
181 }
182
183 fn visit(&self, hit: impl Fn(Aabb) -> bool, out: &mut Vec<usize>) {
184 if self.nodes.is_empty() {
185 return;
186 }
187 let mut stack = vec![0usize];
188 while let Some(index) = stack.pop() {
189 let node = &self.nodes[index];
190 if !hit(node.bounds) {
191 continue;
192 }
193 if node.leaf {
194 for &item in &self.order[node.left as usize..node.right as usize] {
195 if hit(self.boxes[item as usize]) {
196 out.push(item as usize);
197 }
198 }
199 } else {
200 stack.push(node.left as usize);
201 stack.push(node.right as usize);
202 }
203 }
204 }
205}
206
207fn build_node(
208 boxes: &[Aabb],
209 order: &mut [u32],
210 start: usize,
211 end: usize,
212 nodes: &mut Vec<Node>,
213) -> usize {
214 let mut bounds = Aabb::empty();
215 for &item in &order[start..end] {
216 bounds.include(boxes[item as usize]);
217 }
218 let index = nodes.len();
219 if end - start <= LEAF_SIZE {
220 nodes.push(Node {
221 bounds,
222 left: start as u32,
223 right: end as u32,
224 leaf: true,
225 });
226 return index;
227 }
228 let extent = bounds.maximum.sub(bounds.minimum);
229 let split_axis = if extent.x >= extent.y && extent.x >= extent.z {
230 0
231 } else if extent.y >= extent.z {
232 1
233 } else {
234 2
235 };
236 let middle = start + (end - start) / 2;
237 order[start..end].select_nth_unstable_by(middle - start, |&a, &b| {
238 boxes[a as usize]
239 .center_along(split_axis)
240 .total_cmp(&boxes[b as usize].center_along(split_axis))
241 });
242 nodes.push(Node {
243 bounds,
244 left: 0,
245 right: 0,
246 leaf: false,
247 });
248 let left = build_node(boxes, order, start, middle, nodes);
249 let right = build_node(boxes, order, middle, end, nodes);
250 nodes[index].left = left as u32;
251 nodes[index].right = right as u32;
252 index
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 fn unit_box_at(x: f64, y: f64, z: f64) -> Aabb {
260 Aabb {
261 minimum: Vec3::new(x, y, z),
262 maximum: Vec3::new(x + 1.0, y + 1.0, z + 1.0),
263 }
264 }
265
266 fn brute_overlap(boxes: &[Aabb], query: Aabb, tolerance: f64) -> Vec<usize> {
267 boxes
268 .iter()
269 .enumerate()
270 .filter(|(_, bounds)| bounds.intersects(query, tolerance))
271 .map(|(index, _)| index)
272 .collect()
273 }
274
275 #[test]
276 fn bvh_overlap_matches_brute_force() {
277 let mut boxes = Vec::new();
278 for i in 0..7 {
279 for j in 0..5 {
280 for k in 0..3 {
281 boxes.push(unit_box_at(i as f64 * 1.5, j as f64 * 1.5, k as f64 * 1.5));
282 }
283 }
284 }
285 let bvh = Bvh::build(&boxes);
286 for query in [
287 unit_box_at(0.0, 0.0, 0.0),
288 unit_box_at(3.2, 1.4, 0.7),
289 unit_box_at(100.0, 100.0, 100.0),
290 Aabb {
291 minimum: Vec3::new(-1.0, -1.0, -1.0),
292 maximum: Vec3::new(20.0, 20.0, 20.0),
293 },
294 ] {
295 for tolerance in [0.0, 0.25] {
296 let mut found = Vec::new();
297 bvh.overlapping(query, tolerance, &mut found);
298 found.sort_unstable();
299 assert_eq!(found, brute_overlap(&boxes, query, tolerance));
300 }
301 }
302 }
303
304 #[test]
305 fn bvh_segment_query_matches_brute_force() {
306 let mut boxes = Vec::new();
307 for i in 0..20 {
308 boxes.push(unit_box_at(i as f64 * 2.0, (i % 4) as f64, 0.0));
309 }
310 let bvh = Bvh::build(&boxes);
311 let start = Vec3::new(-1.0, 0.5, 0.5);
312 let end = Vec3::new(41.0, 2.5, 0.5);
313 let mut found = Vec::new();
314 bvh.intersecting_segment(start, end, 1e-9, &mut found);
315 found.sort_unstable();
316 let delta = end.sub(start);
317 let expected: Vec<usize> = boxes
318 .iter()
319 .enumerate()
320 .filter(|(_, bounds)| bounds.intersects_segment(start, delta, 1e-9))
321 .map(|(index, _)| index)
322 .collect();
323 assert_eq!(found, expected);
324 assert!(!found.is_empty());
325 let mut short = Vec::new();
327 bvh.intersecting_segment(start, Vec3::new(5.0, 0.75, 0.5), 1e-9, &mut short);
328 assert!(short.iter().all(|&index| index <= 3));
329 }
330
331 #[test]
332 fn bvh_point_query_and_empty_tree() {
333 let empty = Bvh::build(&[]);
334 let mut out = Vec::new();
335 empty.containing_point(Vec3::new(0.0, 0.0, 0.0), 1.0, &mut out);
336 assert!(out.is_empty());
337
338 let boxes = vec![unit_box_at(0.0, 0.0, 0.0), unit_box_at(5.0, 0.0, 0.0)];
339 let bvh = Bvh::build(&boxes);
340 let mut found = Vec::new();
341 bvh.containing_point(Vec3::new(0.5, 0.5, 0.5), 0.0, &mut found);
342 assert_eq!(found, vec![0]);
343 found.clear();
344 bvh.containing_point(Vec3::new(4.9, 0.5, 0.5), 0.2, &mut found);
345 assert_eq!(found, vec![1]);
346 }
347}