ifc_lite_geometry/kernel/broadphase/
bounded.rs1use super::{aabb_contains, seg_hits_aabb, Bvh};
5
6impl Bvh {
7 pub fn point_candidates_bounded(
11 &self,
12 point: [f64; 3],
13 radius: f64,
14 out: &mut Vec<u32>,
15 remaining: &mut usize,
16 ) -> Result<(), &'static str> {
17 if !point.iter().all(|v| v.is_finite()) || !radius.is_finite() || radius < 0. {
18 return Err("BVH query requires a finite point and nonnegative radius");
19 }
20 if self.root == u32::MAX {
21 return Ok(());
22 }
23 let mut pending = [0_u32; 64];
24 pending[0] = self.root;
25 let mut count = 1;
26 while count > 0 {
27 count -= 1;
28 let index = pending[count];
29 *remaining = remaining
30 .checked_sub(1)
31 .ok_or("BVH query work budget exhausted")?;
32 let node = &self.nodes[index as usize];
33 if !aabb_contains(point, &node.aabb, self.pad + radius) {
34 continue;
35 }
36 if node.tri != u32::MAX {
37 out.push(node.tri);
38 } else {
39 if count + 2 > pending.len() {
40 return Err("BVH query stack budget exceeded");
41 }
42 pending[count] = node.right;
43 pending[count + 1] = node.left;
44 count += 2;
45 }
46 }
47 Ok(())
48 }
49}
50
51impl Bvh {
52 pub fn ray_candidates_bounded(
56 &self,
57 p: [f64; 3],
58 far: [f64; 3],
59 out: &mut Vec<u32>,
60 remaining: &mut usize,
61 ) -> Result<(), &'static str> {
62 if !p.iter().chain(&far).all(|v| v.is_finite()) {
63 return Err("BVH query requires finite segment endpoints");
64 }
65 if self.root == u32::MAX {
66 return Ok(());
67 }
68 let mut pending = [0_u32; 64];
69 pending[0] = self.root;
70 let mut count = 1;
71 while count > 0 {
72 count -= 1;
73 let index = pending[count];
74 *remaining = remaining
75 .checked_sub(1)
76 .ok_or("BVH query work budget exhausted")?;
77 let node = &self.nodes[index as usize];
78 if !seg_hits_aabb(p, far, &node.aabb, self.pad) {
79 continue;
80 }
81 if node.tri != u32::MAX {
82 out.push(node.tri);
83 } else {
84 if count + 2 > pending.len() {
85 return Err("BVH query stack budget exceeded");
86 }
87 pending[count] = node.right;
88 pending[count + 1] = node.left;
89 count += 2;
90 }
91 }
92 Ok(())
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 #[test]
100 fn issue_4381_bounded_ray_query_matches_candidates_and_reports_exhaustion() {
101 let triangles = [
102 [[0., 0., 0.], [1., 0., 0.], [0., 1., 0.]],
103 [[0., 0., 0.004], [1., 0., 0.004], [0., 1., 0.004]],
104 [[5., 5., 0.], [6., 5., 0.], [5., 6., 0.]],
105 ];
106 let tree = Bvh::build(&triangles);
107 let mut old = Vec::new();
108 tree.ray_candidates([0.2, 0.2, -0.001], [0.2, 0.2, 0.01], &mut old);
109 let mut actual = Vec::new();
110 let mut budget = 5;
111 tree.ray_candidates_bounded([0.2, 0.2, -0.001], [0.2, 0.2, 0.01], &mut actual, &mut budget)
112 .unwrap();
113 actual.sort_unstable();
114 old.sort_unstable();
115 assert_eq!(actual, old);
116 assert_eq!(actual, vec![0, 1]);
117 assert!(tree
118 .ray_candidates_bounded([0.2, 0.2, -0.001], [0.2, 0.2, 0.01], &mut Vec::new(), &mut 1)
119 .is_err());
120 assert!(tree
121 .ray_candidates_bounded([f64::NAN, 0., 0.], [0., 0., 1.], &mut Vec::new(), &mut 10)
122 .is_err());
123 let mut none = Vec::new();
124 tree.ray_candidates_bounded([0.2, 0.2, 0.001], [0.2, 0.2, 0.003], &mut none, &mut 10).unwrap();
125 assert!(none.is_empty(), "a segment strictly between the sheets touches neither");
126 }
127 #[test]
128 fn issue_4381_bounded_query_matches_candidates_and_reports_exhaustion() {
129 let triangles = [
130 [[0., 0., 0.], [1., 0., 0.], [0., 1., 0.]],
131 [[0., 0., 0.01], [1., 0., 0.01], [0., 1., 0.01]],
132 ];
133 let tree = Bvh::build(&triangles);
134 let mut old = Vec::new();
135 tree.point_candidates([0.2, 0.2, 0.], 0.02, &mut old);
136 let mut actual = Vec::new();
137 let mut budget = 3;
138 tree.point_candidates_bounded([0.2, 0.2, 0.], 0.02, &mut actual, &mut budget)
139 .unwrap();
140 assert_eq!(actual, old);
141 assert_eq!(actual.len(), 2);
142 assert_eq!(budget, 0);
143 assert!(tree
144 .point_candidates_bounded([0.2, 0.2, 0.], 0.02, &mut Vec::new(), &mut 2)
145 .is_err());
146 assert!(tree
147 .point_candidates_bounded([f64::NAN, 0., 0.], 0.02, &mut Vec::new(), &mut 10)
148 .is_err());
149 }
150}