1use axiolid_core::Point3;
19use axiolid_mesh::TriangleMeshView;
20
21use crate::proximity::{closest_points_on_triangles, ClosestPoints3, ProximityError};
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25#[non_exhaustive]
26pub enum MeshProximityError {
27 EmptyMesh,
32 IndexOutOfRange,
34 InvalidThreshold,
36 Primitive(ProximityError),
38}
39
40impl From<ProximityError> for MeshProximityError {
41 fn from(error: ProximityError) -> Self {
42 Self::Primitive(error)
43 }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq)]
48pub struct MeshDistance {
49 pub point_a: Point3,
51 pub point_b: Point3,
53 pub distance_squared: f64,
58 pub triangle_a: usize,
60 pub triangle_b: usize,
62 pub surfaces_cross: bool,
68}
69
70#[derive(Debug, Clone, PartialEq)]
72pub struct ProximityComponent {
73 pub witness: MeshDistance,
75 pub triangles_a: Vec<usize>,
77 pub triangles_b: Vec<usize>,
79}
80
81pub fn mesh_distance<A, B>(first: &A, second: &B) -> Result<MeshDistance, MeshProximityError>
89where
90 A: TriangleMeshView + ?Sized,
91 B: TriangleMeshView + ?Sized,
92{
93 let (triangles_a, _) = collect(first)?;
94 let (triangles_b, _) = collect(second)?;
95
96 let mut best: Option<MeshDistance> = None;
97 for (index_a, tri_a) in triangles_a.iter().enumerate() {
98 for (index_b, tri_b) in triangles_b.iter().enumerate() {
99 let pair = closest_points_on_triangles(*tri_a, *tri_b)?;
100 let candidate = to_distance(pair, index_a, index_b);
101 if best.is_none_or(|current| candidate.distance_squared < current.distance_squared) {
103 best = Some(candidate);
104 }
105 }
106 }
107
108 best.ok_or(MeshProximityError::EmptyMesh)
109}
110
111fn to_distance(pair: ClosestPoints3, triangle_a: usize, triangle_b: usize) -> MeshDistance {
112 MeshDistance {
113 point_a: pair.point_a,
114 point_b: pair.point_b,
115 distance_squared: pair.distance_squared,
116 triangle_a,
117 triangle_b,
118 surfaces_cross: pair.distance_squared == 0.0,
119 }
120}
121
122type Collected = (Vec<[Point3; 3]>, Vec<[usize; 3]>);
123
124fn collect<M: TriangleMeshView + ?Sized>(mesh: &M) -> Result<Collected, MeshProximityError> {
125 let positions = mesh.position_count();
126 let mut triangles = Vec::with_capacity(mesh.triangle_count());
127 let mut corner_indices = Vec::with_capacity(mesh.triangle_count());
128 for index in 0..mesh.triangle_count() {
129 let corners = mesh.triangle(index);
130 let mut points = [Point3::ZERO; 3];
131 let mut corner_ids = [0usize; 3];
132 for (slot, corner) in corners.iter().enumerate() {
133 let corner =
134 usize::try_from(*corner).map_err(|_| MeshProximityError::IndexOutOfRange)?;
135 if corner >= positions {
136 return Err(MeshProximityError::IndexOutOfRange);
137 }
138 points[slot] = mesh.position(corner);
139 corner_ids[slot] = corner;
140 }
141 triangles.push(points);
142 corner_indices.push(corner_ids);
143 }
144 if triangles.is_empty() {
145 return Err(MeshProximityError::EmptyMesh);
146 }
147 Ok((triangles, corner_indices))
148}
149
150pub fn proximity_components<A, B>(
160 first: &A,
161 second: &B,
162 threshold: f64,
163) -> Result<Vec<ProximityComponent>, MeshProximityError>
164where
165 A: TriangleMeshView + ?Sized,
166 B: TriangleMeshView + ?Sized,
167{
168 if !threshold.is_finite() || threshold < 0.0 {
169 return Err(MeshProximityError::InvalidThreshold);
170 }
171 let (triangles_a, corners_a) = collect(first)?;
172 let (triangles_b, corners_b) = collect(second)?;
173 let limit = threshold * threshold;
174
175 let mut close = Vec::new();
177 for (index_a, tri_a) in triangles_a.iter().enumerate() {
178 for (index_b, tri_b) in triangles_b.iter().enumerate() {
179 let pair = closest_points_on_triangles(*tri_a, *tri_b)?;
180 if pair.distance_squared <= limit {
181 close.push(to_distance(pair, index_a, index_b));
182 }
183 }
184 }
185 if close.is_empty() {
186 return Ok(Vec::new());
187 }
188
189 let mut parent: Vec<usize> = (0..close.len()).collect();
200 for i in 0..close.len() {
201 for j in i + 1..close.len() {
202 let linked_a = adjacent(&corners_a, close[i].triangle_a, close[j].triangle_a);
203 let linked_b = adjacent(&corners_b, close[i].triangle_b, close[j].triangle_b);
204 if linked_a && linked_b {
205 union(&mut parent, i, j);
206 }
207 }
208 }
209
210 let mut groups: Vec<(usize, Vec<usize>)> = Vec::new();
211 for index in 0..close.len() {
212 let root = find(&mut parent, index);
213 match groups.iter_mut().find(|(key, _)| *key == root) {
214 Some((_, members)) => members.push(index),
215 None => groups.push((root, vec![index])),
216 }
217 }
218
219 let mut components: Vec<ProximityComponent> = groups
220 .into_iter()
221 .map(|(_, members)| build_component(&close, &members))
222 .collect();
223 components.sort_by(|a, b| {
224 a.witness
225 .distance_squared
226 .total_cmp(&b.witness.distance_squared)
227 .then(a.witness.triangle_a.cmp(&b.witness.triangle_a))
228 .then(a.witness.triangle_b.cmp(&b.witness.triangle_b))
229 });
230 Ok(components)
231}
232
233fn build_component(close: &[MeshDistance], members: &[usize]) -> ProximityComponent {
234 let mut witness = close[members[0]];
235 let mut triangles_a = Vec::new();
236 let mut triangles_b = Vec::new();
237 for &index in members {
238 let entry = close[index];
239 if entry.distance_squared < witness.distance_squared {
240 witness = entry;
241 }
242 triangles_a.push(entry.triangle_a);
243 triangles_b.push(entry.triangle_b);
244 }
245 triangles_a.sort_unstable();
246 triangles_a.dedup();
247 triangles_b.sort_unstable();
248 triangles_b.dedup();
249 ProximityComponent {
250 witness,
251 triangles_a,
252 triangles_b,
253 }
254}
255
256fn find(parent: &mut [usize], mut node: usize) -> usize {
257 while parent[node] != node {
258 parent[node] = parent[parent[node]];
259 node = parent[node];
260 }
261 node
262}
263
264fn union(parent: &mut [usize], a: usize, b: usize) {
265 let (root_a, root_b) = (find(parent, a), find(parent, b));
266 if root_a != root_b {
267 parent[root_b] = root_a;
268 }
269}
270
271fn adjacent(corners: &[[usize; 3]], first: usize, second: usize) -> bool {
273 first == second
274 || corners[first]
275 .iter()
276 .any(|a| corners[second].iter().any(|b| a == b))
277}