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(crate) fn separation(self, other: Self) -> f64 {
29 let axis_gap = |a0: f64, a1: f64, b0: f64, b1: f64| {
30 if a1 < b0 {
31 b0 - a1
32 } else if b1 < a0 {
33 a0 - b1
34 } else {
35 0.0
36 }
37 };
38 Vec3::new(
39 axis_gap(self.minimum.x, self.maximum.x, other.minimum.x, other.maximum.x),
40 axis_gap(self.minimum.y, self.maximum.y, other.minimum.y, other.maximum.y),
41 axis_gap(self.minimum.z, self.maximum.z, other.minimum.z, other.maximum.z),
42 )
43 .length()
44 }
45
46 pub(crate) fn pair_scale(self, other: Self) -> f64 {
47 self.maximum
48 .sub(self.minimum)
49 .length()
50 .max(other.maximum.sub(other.minimum).length())
51 .max(1.0)
52 }
53
54
55 pub fn from_curve_controls(curve: &crate::NurbsCurve) -> Result<Self, String> {
56 let mut bounds = Self::empty();
57 for point in &curve.control_points {
58 bounds.include_point(point.point()?);
59 }
60 Ok(bounds)
61 }
62
63 pub(crate) fn intersects_upper(self, other: Self, tolerance: f64) -> bool {
65 self.minimum.x <= other.maximum.x + tolerance
66 && self.maximum.x + tolerance >= other.minimum.x
67 && self.minimum.y <= other.maximum.y + tolerance
68 && self.maximum.y + tolerance >= other.minimum.y
69 && self.minimum.z <= other.maximum.z + tolerance
70 && self.maximum.z + tolerance >= other.minimum.z
71 }
72
73 pub(crate) fn intersects_exact(self, other: Self) -> bool {
74 self.minimum.x <= other.maximum.x
75 && self.maximum.x >= other.minimum.x
76 && self.minimum.y <= other.maximum.y
77 && self.maximum.y >= other.minimum.y
78 && self.minimum.z <= other.maximum.z
79 && self.maximum.z >= other.minimum.z
80 }
81
82 pub(crate) fn from_nonempty_points(points: &[Vec3]) -> Option<Self> {
83 let first = *points.first()?;
84 let mut bounds = Self { minimum: first, maximum: first };
85 for &point in &points[1..] {
86 bounds.include_point(point);
87 }
88 Some(bounds)
89 }
90
91 pub fn empty() -> Self {
92 Self {
93 minimum: Vec3::new(f64::INFINITY, f64::INFINITY, f64::INFINITY),
94 maximum: Vec3::new(f64::NEG_INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY),
95 }
96 }
97
98 pub(crate) fn from_points(points: impl IntoIterator<Item = Vec3>) -> Self {
99 let mut bounds = Self::empty();
100 for point in points {
101 bounds.include_point(point);
102 }
103 bounds
104 }
105
106 pub fn include_point(&mut self, point: Vec3) {
107 self.minimum.x = self.minimum.x.min(point.x);
108 self.minimum.y = self.minimum.y.min(point.y);
109 self.minimum.z = self.minimum.z.min(point.z);
110 self.maximum.x = self.maximum.x.max(point.x);
111 self.maximum.y = self.maximum.y.max(point.y);
112 self.maximum.z = self.maximum.z.max(point.z);
113 }
114
115 pub fn include(&mut self, other: Self) {
116 self.include_point(other.minimum);
117 self.include_point(other.maximum);
118 }
119
120 pub fn from_surface_controls(surface: &NurbsSurface) -> Result<Self, String> {
121 let mut bounds = Self::empty();
122 for control in surface.control_points.iter().flatten() {
123 bounds.include_point(control.point()?);
124 }
125 Ok(bounds)
126 }
127
128 pub fn expanded(self, amount: f64) -> Self {
129 let delta = Vec3::new(amount, amount, amount);
130 Self {
131 minimum: self.minimum.sub(delta),
132 maximum: self.maximum.add(delta),
133 }
134 }
135
136 pub fn contains(self, point: Vec3) -> bool {
137 point.x >= self.minimum.x
138 && point.x <= self.maximum.x
139 && point.y >= self.minimum.y
140 && point.y <= self.maximum.y
141 && point.z >= self.minimum.z
142 && point.z <= self.maximum.z
143 }
144
145 pub fn intersects(self, other: Self, tolerance: f64) -> bool {
146 self.minimum.x - tolerance <= other.maximum.x
147 && self.maximum.x + tolerance >= other.minimum.x
148 && self.minimum.y - tolerance <= other.maximum.y
149 && self.maximum.y + tolerance >= other.minimum.y
150 && self.minimum.z - tolerance <= other.maximum.z
151 && self.maximum.z + tolerance >= other.minimum.z
152 }
153
154 pub fn diagonal(self) -> f64 {
155 self.maximum.sub(self.minimum).length()
156 }
157
158 fn center_along(self, index: usize) -> f64 {
159 (axis(self.minimum, index) + axis(self.maximum, index)) / 2.0
160 }
161
162 fn intersects_segment(self, start: Vec3, delta: Vec3, tolerance: f64) -> bool {
163 self.intersects_parametric(start, delta, tolerance, 1.0)
164 }
165
166 fn intersects_parametric(self, start: Vec3, delta: Vec3, tolerance: f64, limit: f64) -> bool {
167 let mut enter = 0.0f64;
168 let mut exit = limit;
169 for index in 0..3 {
170 let origin = axis(start, index);
171 let direction = axis(delta, index);
172 let minimum = axis(self.minimum, index) - tolerance;
173 let maximum = axis(self.maximum, index) + tolerance;
174 if direction.abs() < 1e-300 {
175 if origin < minimum || origin > maximum {
176 return false;
177 }
178 continue;
179 }
180 let inverse = 1.0 / direction;
181 let mut near = (minimum - origin) * inverse;
182 let mut far = (maximum - origin) * inverse;
183 if near > far {
184 std::mem::swap(&mut near, &mut far);
185 }
186 enter = enter.max(near);
187 exit = exit.min(far);
188 if enter > exit {
189 return false;
190 }
191 }
192 true
193 }
194}
195
196const LEAF_SIZE: usize = 4;
197
198#[derive(Debug)]
199struct Node {
200 bounds: Aabb,
201 left: u32,
204 right: u32,
207 leaf: bool,
208}
209
210#[derive(Debug)]
213pub struct Bvh {
214 nodes: Vec<Node>,
215 order: Vec<u32>,
216 boxes: Vec<Aabb>,
217}
218
219impl Bvh {
220 pub fn build(boxes: &[Aabb]) -> Self {
221 let mut order: Vec<u32> = (0..boxes.len() as u32).collect();
222 let mut nodes = Vec::new();
223 if !boxes.is_empty() {
224 let count = order.len();
225 build_node(boxes, &mut order, 0, count, &mut nodes);
226 }
227 Self {
228 nodes,
229 order,
230 boxes: boxes.to_vec(),
231 }
232 }
233
234 pub fn overlapping(&self, query: Aabb, tolerance: f64, out: &mut Vec<usize>) {
236 self.visit(|bounds| bounds.intersects(query, tolerance), out);
237 }
238
239 pub fn intersecting_segment(
242 &self,
243 start: Vec3,
244 end: Vec3,
245 tolerance: f64,
246 out: &mut Vec<usize>,
247 ) {
248 let delta = end.sub(start);
249 self.visit(
250 |bounds| bounds.intersects_segment(start, delta, tolerance),
251 out,
252 );
253 }
254
255 pub fn intersecting_ray(
260 &self,
261 origin: Vec3,
262 direction: Vec3,
263 tolerance: f64,
264 out: &mut Vec<usize>,
265 ) {
266 self.visit(
267 |bounds| bounds.intersects_parametric(origin, direction, tolerance, f64::INFINITY),
268 out,
269 );
270 }
271
272 pub fn containing_point(&self, point: Vec3, tolerance: f64, out: &mut Vec<usize>) {
274 self.visit(|bounds| bounds.expanded(tolerance).contains(point), out);
275 }
276
277 fn visit(&self, hit: impl Fn(Aabb) -> bool, out: &mut Vec<usize>) {
278 if self.nodes.is_empty() {
279 return;
280 }
281 let mut stack = vec![0usize];
282 while let Some(index) = stack.pop() {
283 let node = &self.nodes[index];
284 if !hit(node.bounds) {
285 continue;
286 }
287 if node.leaf {
288 for &item in &self.order[node.left as usize..node.right as usize] {
289 if hit(self.boxes[item as usize]) {
290 out.push(item as usize);
291 }
292 }
293 } else {
294 stack.push(node.left as usize);
295 stack.push(node.right as usize);
296 }
297 }
298 }
299}
300
301fn build_node(
302 boxes: &[Aabb],
303 order: &mut [u32],
304 start: usize,
305 end: usize,
306 nodes: &mut Vec<Node>,
307) -> usize {
308 let mut bounds = Aabb::empty();
309 for &item in &order[start..end] {
310 bounds.include(boxes[item as usize]);
311 }
312 let index = nodes.len();
313 if end - start <= LEAF_SIZE {
314 nodes.push(Node {
315 bounds,
316 left: start as u32,
317 right: end as u32,
318 leaf: true,
319 });
320 return index;
321 }
322 let extent = bounds.maximum.sub(bounds.minimum);
323 let split_axis = if extent.x >= extent.y && extent.x >= extent.z {
324 0
325 } else if extent.y >= extent.z {
326 1
327 } else {
328 2
329 };
330 let middle = start + (end - start) / 2;
331 order[start..end].select_nth_unstable_by(middle - start, |&a, &b| {
332 boxes[a as usize]
333 .center_along(split_axis)
334 .total_cmp(&boxes[b as usize].center_along(split_axis))
335 });
336 nodes.push(Node {
337 bounds,
338 left: 0,
339 right: 0,
340 leaf: false,
341 });
342 let left = build_node(boxes, order, start, middle, nodes);
343 let right = build_node(boxes, order, middle, end, nodes);
344 nodes[index].left = left as u32;
345 nodes[index].right = right as u32;
346 index
347}
348