1mod bounded;
34
35type Tri = [[f64; 3]; 3];
36pub(crate) type Aabb = ([f64; 3], [f64; 3]);
37
38pub fn tri_aabb(t: &Tri) -> Aabb {
39 let mut lo = t[0];
40 let mut hi = t[0];
41 for p in t.iter().skip(1) {
42 for k in 0..3 {
43 lo[k] = lo[k].min(p[k]);
44 hi[k] = hi[k].max(p[k]);
45 }
46 }
47 (lo, hi)
48}
49
50fn overlap(a: &Aabb, b: &Aabb) -> bool {
51 (0..3).all(|k| a.0[k] <= b.1[k] && b.0[k] <= a.1[k])
52}
53
54fn aabb_contains(p: [f64; 3], bb: &Aabb, pad: f64) -> bool {
56 (0..3).all(|k| p[k] >= bb.0[k] - pad && p[k] <= bb.1[k] + pad)
57}
58
59fn seg_hits_aabb(p: [f64; 3], far: [f64; 3], bb: &Aabb, pad: f64) -> bool {
63 let (mut tmin, mut tmax) = (0.0f64, 1.0f64);
64 for k in 0..3 {
65 let (lo, hi) = (bb.0[k] - pad, bb.1[k] + pad);
66 let d = far[k] - p[k];
67 if d.abs() <= f64::MIN_POSITIVE {
68 if p[k] < lo || p[k] > hi {
70 return false;
71 }
72 } else {
73 let inv = 1.0 / d;
74 let (mut t0, mut t1) = ((lo - p[k]) * inv, (hi - p[k]) * inv);
75 if t0 > t1 {
76 std::mem::swap(&mut t0, &mut t1);
77 }
78 tmin = tmin.max(t0);
79 tmax = tmax.min(t1);
80 if tmin > tmax {
81 return false;
82 }
83 }
84 }
85 true
86}
87
88struct Node {
89 aabb: Aabb,
90 tri: u32, left: u32,
92 right: u32,
93}
94
95pub struct Bvh {
96 nodes: Vec<Node>,
97 root: u32,
98 pad: f64,
106}
107
108pub(crate) const EMPTY_AABB: Aabb = ([f64::MAX; 3], [f64::MIN; 3]);
113
114pub(crate) fn tris_aabb(tris: &[Tri]) -> Aabb {
117 let (mut lo, mut hi) = EMPTY_AABB;
118 for t in tris {
119 for v in t {
120 for k in 0..3 {
121 lo[k] = lo[k].min(v[k]);
122 hi[k] = hi[k].max(v[k]);
123 }
124 }
125 }
126 (lo, hi)
127}
128
129impl Bvh {
130 pub fn build(tris: &[Tri]) -> Bvh {
131 let mut items: Vec<(u32, Aabb, [f64; 3])> = tris
132 .iter()
133 .enumerate()
134 .map(|(i, t)| {
135 let bb = tri_aabb(t);
136 let c = [
137 0.5 * (bb.0[0] + bb.1[0]),
138 0.5 * (bb.0[1] + bb.1[1]),
139 0.5 * (bb.0[2] + bb.1[2]),
140 ];
141 (i as u32, bb, c)
142 })
143 .collect();
144 let mut nodes = Vec::new();
145 let root = if items.is_empty() {
146 u32::MAX
147 } else {
148 build_node(&mut nodes, &mut items)
149 };
150 let pad = if root == u32::MAX {
151 0.0
152 } else {
153 let (lo, hi) = nodes[root as usize].aabb;
154 let diag = ((hi[0] - lo[0]).powi(2) + (hi[1] - lo[1]).powi(2) + (hi[2] - lo[2]).powi(2))
155 .sqrt();
156 (diag * 1.0e-9).max(1.0e-12)
157 };
158 Bvh { nodes, root, pad }
159 }
160
161 pub(crate) fn root_aabb(&self) -> Option<Aabb> {
165 (self.root != u32::MAX).then(|| self.nodes[self.root as usize].aabb)
166 }
167
168 pub fn ray_candidates(&self, p: [f64; 3], far: [f64; 3], out: &mut Vec<u32>) {
173 if self.root != u32::MAX {
174 self.descend_ray(self.root, p, far, out);
175 }
176 }
177
178 fn descend_ray(&self, idx: u32, p: [f64; 3], far: [f64; 3], out: &mut Vec<u32>) {
179 let n = &self.nodes[idx as usize];
180 if !seg_hits_aabb(p, far, &n.aabb, self.pad) {
181 return;
182 }
183 if n.tri != u32::MAX {
184 out.push(n.tri);
185 } else {
186 self.descend_ray(n.left, p, far, out);
187 self.descend_ray(n.right, p, far, out);
188 }
189 }
190
191 pub fn point_candidates(&self, p: [f64; 3], radius: f64, out: &mut Vec<u32>) {
196 if self.root != u32::MAX {
197 self.descend_point(self.root, p, radius, out);
198 }
199 }
200
201 fn descend_point(&self, idx: u32, p: [f64; 3], radius: f64, out: &mut Vec<u32>) {
202 let n = &self.nodes[idx as usize];
203 if !aabb_contains(p, &n.aabb, self.pad + radius) {
204 return;
205 }
206 if n.tri != u32::MAX {
207 out.push(n.tri);
208 } else {
209 self.descend_point(n.left, p, radius, out);
210 self.descend_point(n.right, p, radius, out);
211 }
212 }
213
214 pub fn query(&self, q: &Aabb, out: &mut Vec<u32>) {
216 if self.root != u32::MAX {
217 self.descend(self.root, q, out);
218 }
219 }
220
221 fn descend(&self, idx: u32, q: &Aabb, out: &mut Vec<u32>) {
222 let n = &self.nodes[idx as usize];
223 if !overlap(&n.aabb, q) {
224 return;
225 }
226 if n.tri != u32::MAX {
227 out.push(n.tri);
228 } else {
229 self.descend(n.left, q, out);
230 self.descend(n.right, q, out);
231 }
232 }
233}
234
235fn bounds(items: &[(u32, Aabb, [f64; 3])]) -> Aabb {
236 let mut lo = items[0].1 .0;
237 let mut hi = items[0].1 .1;
238 for it in &items[1..] {
239 for k in 0..3 {
240 lo[k] = lo[k].min(it.1 .0[k]);
241 hi[k] = hi[k].max(it.1 .1[k]);
242 }
243 }
244 (lo, hi)
245}
246
247fn build_node(nodes: &mut Vec<Node>, items: &mut [(u32, Aabb, [f64; 3])]) -> u32 {
248 let bb = bounds(items);
249 if items.len() == 1 {
250 let idx = nodes.len() as u32;
251 nodes.push(Node { aabb: bb, tri: items[0].0, left: 0, right: 0 });
252 return idx;
253 }
254 let mut span = [0.0f64; 3];
256 let (mut clo, mut chi) = (items[0].2, items[0].2);
257 for it in items.iter() {
258 for k in 0..3 {
259 clo[k] = clo[k].min(it.2[k]);
260 chi[k] = chi[k].max(it.2[k]);
261 }
262 }
263 for k in 0..3 {
264 span[k] = chi[k] - clo[k];
265 }
266 let axis = if span[0] >= span[1] && span[0] >= span[2] {
267 0
268 } else if span[1] >= span[2] {
269 1
270 } else {
271 2
272 };
273 items.sort_by(|a, b| a.2[axis].total_cmp(&b.2[axis]));
276 let mid = items.len() / 2;
277 let (l, r) = items.split_at_mut(mid);
278 let left = build_node(nodes, l);
279 let right = build_node(nodes, r);
280 let idx = nodes.len() as u32;
281 nodes.push(Node { aabb: bb, tri: u32::MAX, left, right });
282 idx
283}
284
285pub fn candidate_pairs(a: &[Tri], b: &[Tri]) -> Vec<(usize, usize)> {
288 let bvh = Bvh::build(b);
289 let mut pairs = Vec::new();
290 let mut cand = Vec::new();
291 for (i, ta) in a.iter().enumerate() {
292 cand.clear();
293 bvh.query(&tri_aabb(ta), &mut cand);
294 for &j in &cand {
295 pairs.push((i, j as usize));
296 }
297 }
298 pairs.sort_unstable();
299 pairs
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305
306 fn brute(a: &[Tri], b: &[Tri]) -> Vec<(usize, usize)> {
307 let mut p = Vec::new();
308 for (i, ta) in a.iter().enumerate() {
309 for (j, tb) in b.iter().enumerate() {
310 if overlap(&tri_aabb(ta), &tri_aabb(tb)) {
311 p.push((i, j));
312 }
313 }
314 }
315 p.sort_unstable();
316 p
317 }
318
319 #[test]
320 fn ray_and_point_candidates_are_conservative_supersets() {
321 let tris: Vec<Tri> = (0..60)
325 .map(|i| {
326 let (x, y, z) = (i as f64 * 0.7, (i % 7) as f64 * 1.3, (i % 5) as f64 * 0.9);
327 [[x, y, z], [x + 0.4, y, z], [x, y + 0.5, z + 0.3]]
328 })
329 .collect();
330 let bvh = Bvh::build(&tris);
331 let rays = [
332 ([0.0, 0.0, 0.0], [40.0, 9.0, 4.0]),
333 ([5.0, 3.0, 1.0], [5.0001, 3.0, 100.0]),
334 ([-2.0, -2.0, -2.0], [42.0, 12.0, 6.0]),
335 ];
336 for (p, far) in rays {
337 let mut cand = Vec::new();
338 bvh.ray_candidates(p, far, &mut cand);
339 let cset: std::collections::HashSet<u32> = cand.into_iter().collect();
340 for (i, t) in tris.iter().enumerate() {
341 if seg_hits_aabb(p, far, &tri_aabb(t), 0.0) {
342 assert!(cset.contains(&(i as u32)), "ray missed candidate {i}");
343 }
344 }
345 }
346 for p in [[5.2, 3.0, 0.1], [0.1, 0.0, 0.0], [41.0, 7.0, 3.6]] {
347 let mut cand = Vec::new();
348 bvh.point_candidates(p, 0.0, &mut cand);
349 let cset: std::collections::HashSet<u32> = cand.into_iter().collect();
350 for (i, t) in tris.iter().enumerate() {
351 if aabb_contains(p, &tri_aabb(t), 0.0) {
352 assert!(cset.contains(&(i as u32)), "point missed candidate {i}");
353 }
354 }
355 }
356 }
357
358 #[test]
359 fn bvh_candidate_pairs_match_brute_force() {
360 let mk = |dx: f64| -> Vec<Tri> {
363 (0..20)
364 .map(|i| {
365 let x = dx + i as f64 * 0.3;
366 [[x, 0., 0.], [x + 0.5, 0., 0.], [x, 0.5, 0.4]]
367 })
368 .collect()
369 };
370 let a = mk(0.0);
371 let b = mk(1.7);
372 assert_eq!(candidate_pairs(&a, &b), brute(&a, &b));
373 let far = mk(1000.0);
375 assert!(candidate_pairs(&a, &far).is_empty());
376 }
377}
378
379mod nearest;