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(crate) fn from_points(points: impl IntoIterator<Item = Vec3>) -> Self {
36 let mut bounds = Self::empty();
37 for point in points {
38 bounds.include_point(point);
39 }
40 bounds
41 }
42
43 pub fn include_point(&mut self, point: Vec3) {
44 self.minimum.x = self.minimum.x.min(point.x);
45 self.minimum.y = self.minimum.y.min(point.y);
46 self.minimum.z = self.minimum.z.min(point.z);
47 self.maximum.x = self.maximum.x.max(point.x);
48 self.maximum.y = self.maximum.y.max(point.y);
49 self.maximum.z = self.maximum.z.max(point.z);
50 }
51
52 pub fn include(&mut self, other: Self) {
53 self.include_point(other.minimum);
54 self.include_point(other.maximum);
55 }
56
57 pub fn from_surface_controls(surface: &NurbsSurface) -> Result<Self, String> {
58 let mut bounds = Self::empty();
59 for control in surface.control_points.iter().flatten() {
60 bounds.include_point(control.point()?);
61 }
62 Ok(bounds)
63 }
64
65 pub fn expanded(self, amount: f64) -> Self {
66 let delta = Vec3::new(amount, amount, amount);
67 Self {
68 minimum: self.minimum.sub(delta),
69 maximum: self.maximum.add(delta),
70 }
71 }
72
73 pub fn contains(self, point: Vec3) -> bool {
74 point.x >= self.minimum.x
75 && point.x <= self.maximum.x
76 && point.y >= self.minimum.y
77 && point.y <= self.maximum.y
78 && point.z >= self.minimum.z
79 && point.z <= self.maximum.z
80 }
81
82 pub fn intersects(self, other: Self, tolerance: f64) -> bool {
83 self.minimum.x - tolerance <= other.maximum.x
84 && self.maximum.x + tolerance >= other.minimum.x
85 && self.minimum.y - tolerance <= other.maximum.y
86 && self.maximum.y + tolerance >= other.minimum.y
87 && self.minimum.z - tolerance <= other.maximum.z
88 && self.maximum.z + tolerance >= other.minimum.z
89 }
90
91 pub fn diagonal(self) -> f64 {
92 self.maximum.sub(self.minimum).length()
93 }
94
95 fn center_along(self, index: usize) -> f64 {
96 (axis(self.minimum, index) + axis(self.maximum, index)) / 2.0
97 }
98
99 fn intersects_segment(self, start: Vec3, delta: Vec3, tolerance: f64) -> bool {
100 self.intersects_parametric(start, delta, tolerance, 1.0)
101 }
102
103 fn intersects_parametric(self, start: Vec3, delta: Vec3, tolerance: f64, limit: f64) -> bool {
104 let mut enter = 0.0f64;
105 let mut exit = limit;
106 for index in 0..3 {
107 let origin = axis(start, index);
108 let direction = axis(delta, index);
109 let minimum = axis(self.minimum, index) - tolerance;
110 let maximum = axis(self.maximum, index) + tolerance;
111 if direction.abs() < 1e-300 {
112 if origin < minimum || origin > maximum {
113 return false;
114 }
115 continue;
116 }
117 let inverse = 1.0 / direction;
118 let mut near = (minimum - origin) * inverse;
119 let mut far = (maximum - origin) * inverse;
120 if near > far {
121 std::mem::swap(&mut near, &mut far);
122 }
123 enter = enter.max(near);
124 exit = exit.min(far);
125 if enter > exit {
126 return false;
127 }
128 }
129 true
130 }
131}
132
133const LEAF_SIZE: usize = 4;
134
135#[derive(Debug)]
136struct Node {
137 bounds: Aabb,
138 left: u32,
141 right: u32,
144 leaf: bool,
145}
146
147#[derive(Debug)]
150pub struct Bvh {
151 nodes: Vec<Node>,
152 order: Vec<u32>,
153 boxes: Vec<Aabb>,
154}
155
156impl Bvh {
157 pub fn build(boxes: &[Aabb]) -> Self {
158 let mut order: Vec<u32> = (0..boxes.len() as u32).collect();
159 let mut nodes = Vec::new();
160 if !boxes.is_empty() {
161 let count = order.len();
162 build_node(boxes, &mut order, 0, count, &mut nodes);
163 }
164 Self {
165 nodes,
166 order,
167 boxes: boxes.to_vec(),
168 }
169 }
170
171 pub fn overlapping(&self, query: Aabb, tolerance: f64, out: &mut Vec<usize>) {
173 self.visit(|bounds| bounds.intersects(query, tolerance), out);
174 }
175
176 pub fn intersecting_segment(
179 &self,
180 start: Vec3,
181 end: Vec3,
182 tolerance: f64,
183 out: &mut Vec<usize>,
184 ) {
185 let delta = end.sub(start);
186 self.visit(
187 |bounds| bounds.intersects_segment(start, delta, tolerance),
188 out,
189 );
190 }
191
192 pub fn intersecting_ray(
197 &self,
198 origin: Vec3,
199 direction: Vec3,
200 tolerance: f64,
201 out: &mut Vec<usize>,
202 ) {
203 self.visit(
204 |bounds| bounds.intersects_parametric(origin, direction, tolerance, f64::INFINITY),
205 out,
206 );
207 }
208
209 pub fn containing_point(&self, point: Vec3, tolerance: f64, out: &mut Vec<usize>) {
211 self.visit(|bounds| bounds.expanded(tolerance).contains(point), out);
212 }
213
214 fn visit(&self, hit: impl Fn(Aabb) -> bool, out: &mut Vec<usize>) {
215 if self.nodes.is_empty() {
216 return;
217 }
218 let mut stack = vec![0usize];
219 while let Some(index) = stack.pop() {
220 let node = &self.nodes[index];
221 if !hit(node.bounds) {
222 continue;
223 }
224 if node.leaf {
225 for &item in &self.order[node.left as usize..node.right as usize] {
226 if hit(self.boxes[item as usize]) {
227 out.push(item as usize);
228 }
229 }
230 } else {
231 stack.push(node.left as usize);
232 stack.push(node.right as usize);
233 }
234 }
235 }
236}
237
238fn build_node(
239 boxes: &[Aabb],
240 order: &mut [u32],
241 start: usize,
242 end: usize,
243 nodes: &mut Vec<Node>,
244) -> usize {
245 let mut bounds = Aabb::empty();
246 for &item in &order[start..end] {
247 bounds.include(boxes[item as usize]);
248 }
249 let index = nodes.len();
250 if end - start <= LEAF_SIZE {
251 nodes.push(Node {
252 bounds,
253 left: start as u32,
254 right: end as u32,
255 leaf: true,
256 });
257 return index;
258 }
259 let extent = bounds.maximum.sub(bounds.minimum);
260 let split_axis = if extent.x >= extent.y && extent.x >= extent.z {
261 0
262 } else if extent.y >= extent.z {
263 1
264 } else {
265 2
266 };
267 let middle = start + (end - start) / 2;
268 order[start..end].select_nth_unstable_by(middle - start, |&a, &b| {
269 boxes[a as usize]
270 .center_along(split_axis)
271 .total_cmp(&boxes[b as usize].center_along(split_axis))
272 });
273 nodes.push(Node {
274 bounds,
275 left: 0,
276 right: 0,
277 leaf: false,
278 });
279 let left = build_node(boxes, order, start, middle, nodes);
280 let right = build_node(boxes, order, middle, end, nodes);
281 nodes[index].left = left as u32;
282 nodes[index].right = right as u32;
283 index
284}
285