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 let mut enter = 0.0f64;
101 let mut exit = 1.0f64;
102 for index in 0..3 {
103 let origin = axis(start, index);
104 let direction = axis(delta, index);
105 let minimum = axis(self.minimum, index) - tolerance;
106 let maximum = axis(self.maximum, index) + tolerance;
107 if direction.abs() < 1e-300 {
108 if origin < minimum || origin > maximum {
109 return false;
110 }
111 continue;
112 }
113 let inverse = 1.0 / direction;
114 let mut near = (minimum - origin) * inverse;
115 let mut far = (maximum - origin) * inverse;
116 if near > far {
117 std::mem::swap(&mut near, &mut far);
118 }
119 enter = enter.max(near);
120 exit = exit.min(far);
121 if enter > exit {
122 return false;
123 }
124 }
125 true
126 }
127}
128
129const LEAF_SIZE: usize = 4;
130
131struct Node {
132 bounds: Aabb,
133 left: u32,
136 right: u32,
139 leaf: bool,
140}
141
142pub struct Bvh {
145 nodes: Vec<Node>,
146 order: Vec<u32>,
147 boxes: Vec<Aabb>,
148}
149
150impl Bvh {
151 pub fn build(boxes: &[Aabb]) -> Self {
152 let mut order: Vec<u32> = (0..boxes.len() as u32).collect();
153 let mut nodes = Vec::new();
154 if !boxes.is_empty() {
155 let count = order.len();
156 build_node(boxes, &mut order, 0, count, &mut nodes);
157 }
158 Self {
159 nodes,
160 order,
161 boxes: boxes.to_vec(),
162 }
163 }
164
165 pub fn overlapping(&self, query: Aabb, tolerance: f64, out: &mut Vec<usize>) {
167 self.visit(|bounds| bounds.intersects(query, tolerance), out);
168 }
169
170 pub fn intersecting_segment(
173 &self,
174 start: Vec3,
175 end: Vec3,
176 tolerance: f64,
177 out: &mut Vec<usize>,
178 ) {
179 let delta = end.sub(start);
180 self.visit(
181 |bounds| bounds.intersects_segment(start, delta, tolerance),
182 out,
183 );
184 }
185
186 pub fn containing_point(&self, point: Vec3, tolerance: f64, out: &mut Vec<usize>) {
188 self.visit(|bounds| bounds.expanded(tolerance).contains(point), out);
189 }
190
191 fn visit(&self, hit: impl Fn(Aabb) -> bool, out: &mut Vec<usize>) {
192 if self.nodes.is_empty() {
193 return;
194 }
195 let mut stack = vec![0usize];
196 while let Some(index) = stack.pop() {
197 let node = &self.nodes[index];
198 if !hit(node.bounds) {
199 continue;
200 }
201 if node.leaf {
202 for &item in &self.order[node.left as usize..node.right as usize] {
203 if hit(self.boxes[item as usize]) {
204 out.push(item as usize);
205 }
206 }
207 } else {
208 stack.push(node.left as usize);
209 stack.push(node.right as usize);
210 }
211 }
212 }
213}
214
215fn build_node(
216 boxes: &[Aabb],
217 order: &mut [u32],
218 start: usize,
219 end: usize,
220 nodes: &mut Vec<Node>,
221) -> usize {
222 let mut bounds = Aabb::empty();
223 for &item in &order[start..end] {
224 bounds.include(boxes[item as usize]);
225 }
226 let index = nodes.len();
227 if end - start <= LEAF_SIZE {
228 nodes.push(Node {
229 bounds,
230 left: start as u32,
231 right: end as u32,
232 leaf: true,
233 });
234 return index;
235 }
236 let extent = bounds.maximum.sub(bounds.minimum);
237 let split_axis = if extent.x >= extent.y && extent.x >= extent.z {
238 0
239 } else if extent.y >= extent.z {
240 1
241 } else {
242 2
243 };
244 let middle = start + (end - start) / 2;
245 order[start..end].select_nth_unstable_by(middle - start, |&a, &b| {
246 boxes[a as usize]
247 .center_along(split_axis)
248 .total_cmp(&boxes[b as usize].center_along(split_axis))
249 });
250 nodes.push(Node {
251 bounds,
252 left: 0,
253 right: 0,
254 leaf: false,
255 });
256 let left = build_node(boxes, order, start, middle, nodes);
257 let right = build_node(boxes, order, middle, end, nodes);
258 nodes[index].left = left as u32;
259 nodes[index].right = right as u32;
260 index
261}
262
263