geop_core_editor/view/
tree.rs1use std::cell::Cell;
13
14use geop_core_math::{primitives::Ray, scalars::Scalar, vector::Vector3};
15
16use super::ViewFace;
17
18const LEAF: usize = 4;
20
21thread_local! {
22 static TRIANGLES_TESTED: Cell<usize> = const { Cell::new(0) };
23}
24
25pub fn triangles_tested() -> usize {
29 TRIANGLES_TESTED.get()
30}
31
32struct Node {
34 lo: [f64; 3],
35 hi: [f64; 3],
36 content: Content,
37}
38
39enum Content {
40 Boxes(usize, usize),
41 Triangles(usize, usize),
43}
44
45pub(crate) struct FaceTree {
47 nodes: Vec<Node>,
48 triangles: Vec<(usize, usize)>,
51 meshes: Vec<*const super::FaceMesh>,
53}
54
55impl std::fmt::Debug for FaceTree {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 write!(f, "FaceTree({} triangles)", self.triangles.len())
58 }
59}
60
61struct Entry {
64 at: (usize, usize),
65 lo: [f64; 3],
66 hi: [f64; 3],
67 middle: [f64; 3],
68}
69
70impl FaceTree {
71 pub(crate) fn of(faces: &[ViewFace]) -> Self {
73 let mut entries = Vec::new();
74 for (f, face) in faces.iter().enumerate() {
75 for (t, corners) in face.mesh.triangles.iter().enumerate() {
76 let mut lo = [f64::INFINITY; 3];
77 let mut hi = [f64::NEG_INFINITY; 3];
78 for p in corners {
79 widen(&mut lo, &mut hi, p.to_array().map(|x| (x.lo, x.hi)));
80 }
81 let middle = std::array::from_fn(|k| {
82 let (a, b) = (lo.get(k).copied(), hi.get(k).copied());
83 0.5 * (a.unwrap_or_default() + b.unwrap_or_default())
84 });
85 entries.push(Entry {
86 at: (f, t),
87 lo,
88 hi,
89 middle,
90 });
91 }
92 }
93 let mut tree = Self {
94 nodes: Vec::new(),
95 triangles: Vec::with_capacity(entries.len()),
96 meshes: faces.iter().map(|f| std::rc::Rc::as_ptr(&f.mesh)).collect(),
97 };
98 if !entries.is_empty() {
99 tree.build(&mut entries);
100 }
101 tree
102 }
103
104 pub(crate) fn is_of(&self, faces: &[ViewFace]) -> bool {
106 self.meshes.len() == faces.len()
107 && (self.meshes.iter().zip(faces)).all(|(m, f)| *m == std::rc::Rc::as_ptr(&f.mesh))
108 }
109
110 fn build(&mut self, entries: &mut [Entry]) -> usize {
112 let mut lo = [f64::INFINITY; 3];
113 let mut hi = [f64::NEG_INFINITY; 3];
114 for e in entries.iter() {
115 widen(
116 &mut lo,
117 &mut hi,
118 std::array::from_fn(|k| {
119 (
120 e.lo.get(k).copied().unwrap_or_default(),
121 e.hi.get(k).copied().unwrap_or_default(),
122 )
123 }),
124 );
125 }
126 let at = self.nodes.len();
127 self.nodes.push(Node {
128 lo,
129 hi,
130 content: Content::Triangles(0, 0),
131 });
132 let content = if entries.len() <= LEAF {
133 let start = self.triangles.len();
134 self.triangles.extend(entries.iter().map(|e| e.at));
135 Content::Triangles(start, self.triangles.len())
136 } else {
137 let spread = |k: usize| {
140 let (mut a, mut b) = (f64::INFINITY, f64::NEG_INFINITY);
141 for x in entries.iter().filter_map(|e| e.middle.get(k)) {
142 a = a.min(*x);
143 b = b.max(*x);
144 }
145 b - a
146 };
147 let axis = (0..3)
148 .max_by(|&a, &b| spread(a).total_cmp(&spread(b)))
149 .unwrap_or_default();
150 let half = entries.len() / 2;
151 let along = |e: &Entry| e.middle.get(axis).copied().unwrap_or_default();
152 entries.select_nth_unstable_by(half, |a, b| along(a).total_cmp(&along(b)));
153 let (left, right) = entries.split_at_mut(half);
154 Content::Boxes(self.build(left), self.build(right))
155 };
156 if let Some(node) = self.nodes.get_mut(at) {
157 node.content = content;
158 }
159 at
160 }
161
162 pub(crate) fn near(&self, ray: &Ray, mut test: impl FnMut(usize, usize)) {
165 let mut stack = vec![0];
166 while let Some(at) = stack.pop() {
167 let Some(node) = self.nodes.get(at) else {
168 continue;
169 };
170 if !could_pass(ray, &node.lo, &node.hi) {
171 continue;
172 }
173 match node.content {
174 Content::Boxes(a, b) => stack.extend([a, b]),
175 Content::Triangles(start, end) => {
176 for &(face, triangle) in self.triangles.get(start..end).unwrap_or_default() {
177 TRIANGLES_TESTED.set(TRIANGLES_TESTED.get() + 1);
178 test(face, triangle);
179 }
180 }
181 }
182 }
183 }
184}
185
186fn could_pass(ray: &Ray, lo: &[f64; 3], hi: &[f64; 3]) -> bool {
189 let (origin, dir): (&Vector3, &Vector3) = (ray.origin(), ray.dir());
190 let mut enter = 0.0f64;
193 let mut leave = f64::INFINITY;
194 let slabs = (lo.iter().zip(hi)).zip(origin.to_array().into_iter().zip(dir.to_array()));
195 for ((&a, &b), (o, d)) in slabs {
196 let (a, b) = (Scalar::from_f64(a), Scalar::from_f64(b));
197 if d.could_be_equal(Scalar::ZERO) {
198 if o.definitely_less(a) || o.definitely_greater(b) {
200 return false;
201 }
202 continue;
203 }
204 let (Ok(ta), Ok(tb)) = (a.sub(o).div(d), b.sub(o).div(d)) else {
205 continue;
206 };
207 enter = enter.max(ta.lo.min(tb.lo));
208 leave = leave.min(ta.hi.max(tb.hi));
209 }
210 enter <= leave
211}
212
213fn widen(lo: &mut [f64; 3], hi: &mut [f64; 3], bounds: [(f64, f64); 3]) {
215 for ((l, h), (a, b)) in lo.iter_mut().zip(hi.iter_mut()).zip(bounds) {
216 *l = l.min(a);
217 *h = h.max(b);
218 }
219}