1use super::super::gjk::{Simplex, SupportPoint};
6use crate::types::Contact;
7use oxiphysics_core::Transform;
8use oxiphysics_core::math::{Real, Vec3};
9use oxiphysics_geometry::Shape;
10
11use super::functions::{
12 MAX_ITERATIONS, TOLERANCE, add_edge, build_contact, build_initial_faces, compute_face_normal,
13 epa_add_edge, epa_dot3, epa_face_normal, epa_negate3, epa_scale3, epa_sub3, support_minkowski,
14};
15
16#[derive(Debug, Clone)]
18pub struct EpaFaceRaw {
19 pub vertices: [usize; 3],
21 pub normal: [f64; 3],
23 pub distance: f64,
25}
26pub struct EpaSolver {
33 pub(super) polytope: Option<EpaPolytope>,
35 pub(super) iterations: usize,
37 pub(super) tolerance: f64,
39}
40impl EpaSolver {
41 pub fn from_simplex(simplex: &[[f64; 3]; 4]) -> Self {
43 Self {
44 polytope: Some(EpaPolytope::from_gjk_simplex(simplex)),
45 iterations: 0,
46 tolerance: 1e-6,
47 }
48 }
49 pub fn with_tolerance(mut self, tol: f64) -> Self {
51 self.tolerance = tol;
52 self
53 }
54 pub fn compute_penetration_axis(&self) -> ([f64; 3], f64) {
62 let poly = match &self.polytope {
63 Some(p) => p,
64 None => return ([0.0, 1.0, 0.0], 0.0),
65 };
66 if poly.faces.is_empty() {
67 return ([0.0, 1.0, 0.0], 0.0);
68 }
69 let (_idx, dist, normal) = poly.closest_face();
70 (normal, dist)
71 }
72 pub fn refine_polytope(&mut self, support_fn: &mut dyn FnMut([f64; 3]) -> [f64; 3]) -> bool {
80 let poly = match self.polytope.as_mut() {
81 Some(p) => p,
82 None => return false,
83 };
84 if poly.faces.is_empty() {
85 return false;
86 }
87 self.iterations += 1;
88 let closest_idx = poly.find_closest_face();
89 let closest = poly.faces[closest_idx].clone();
90 let new_point = support_fn(closest.normal);
91 let new_dist = epa_dot3(new_point, closest.normal);
92 if (new_dist - closest.distance).abs() < self.tolerance {
93 return false;
94 }
95 poly.expand(new_point)
96 }
97 pub fn solve(
103 &mut self,
104 support_fn: &mut dyn FnMut([f64; 3]) -> [f64; 3],
105 max_iter: usize,
106 ) -> Option<EpaPenetration> {
107 for _ in 0..max_iter {
108 if !self.refine_polytope(support_fn) {
109 break;
110 }
111 }
112 let (normal, depth) = self.compute_penetration_axis();
113 if depth <= 0.0 && normal == [0.0, 1.0, 0.0] {
114 return None;
115 }
116 let poly = self.polytope.as_ref()?;
117 if poly.faces.is_empty() {
118 return None;
119 }
120 let closest_idx = poly.find_closest_face();
121 let closest = &poly.faces[closest_idx];
122 let witness_a = poly.vertices[closest.vertices[0]];
123 let witness_b = epa_sub3(witness_a, epa_scale3(normal, depth));
124 Some(EpaPenetration {
125 normal,
126 depth,
127 witness_a,
128 witness_b,
129 })
130 }
131 pub fn iterations(&self) -> usize {
133 self.iterations
134 }
135 pub fn face_count(&self) -> usize {
137 self.polytope.as_ref().map(|p| p.faces.len()).unwrap_or(0)
138 }
139 pub fn vertex_count(&self) -> usize {
141 self.polytope
142 .as_ref()
143 .map(|p| p.vertices.len())
144 .unwrap_or(0)
145 }
146}
147#[derive(Debug, Clone)]
149pub struct EpaPolytope {
150 pub vertices: Vec<[f64; 3]>,
152 pub faces: Vec<EpaFaceRaw>,
154}
155impl EpaPolytope {
156 pub fn from_gjk_simplex(simplex: &[[f64; 3]; 4]) -> Self {
158 let vertices = simplex.to_vec();
159 let face_indices: [[usize; 3]; 4] = [[0, 1, 2], [0, 2, 3], [0, 3, 1], [1, 3, 2]];
160 let mut faces = Vec::new();
161 for idx in &face_indices {
162 let a = vertices[idx[0]];
163 let b = vertices[idx[1]];
164 let c = vertices[idx[2]];
165 let normal = epa_face_normal(a, b, c);
166 let mut distance = epa_dot3(normal, a);
167 let mut n = normal;
168 if distance < 0.0 {
169 n = epa_negate3(n);
170 distance = -distance;
171 }
172 faces.push(EpaFaceRaw {
173 vertices: *idx,
174 normal: n,
175 distance,
176 });
177 }
178 Self { vertices, faces }
179 }
180 pub fn find_closest_face(&self) -> usize {
182 self.faces
183 .iter()
184 .enumerate()
185 .min_by(|(_, a), (_, b)| {
186 a.distance
187 .partial_cmp(&b.distance)
188 .unwrap_or(std::cmp::Ordering::Equal)
189 })
190 .map(|(i, _)| i)
191 .unwrap_or(0)
192 }
193 pub fn closest_face(&self) -> (usize, f64, [f64; 3]) {
195 let idx = self.find_closest_face();
196 if self.faces.is_empty() {
197 return (0, 0.0, [0.0, 1.0, 0.0]);
198 }
199 let f = &self.faces[idx];
200 (idx, f.distance, f.normal)
201 }
202 pub fn expand(&mut self, support_point: [f64; 3]) -> bool {
207 let new_idx = self.vertices.len();
208 let mut edges: Vec<(usize, usize)> = Vec::new();
209 let mut i = 0;
210 let mut any_removed = false;
211 while i < self.faces.len() {
212 let face = &self.faces[i];
213 let v = self.vertices[face.vertices[0]];
214 let to_point = epa_sub3(support_point, v);
215 if epa_dot3(face.normal, to_point) > 1e-10 {
216 let [a, b, c] = face.vertices;
217 epa_add_edge(&mut edges, a, b);
218 epa_add_edge(&mut edges, b, c);
219 epa_add_edge(&mut edges, c, a);
220 self.faces.swap_remove(i);
221 any_removed = true;
222 } else {
223 i += 1;
224 }
225 }
226 if !any_removed || edges.is_empty() {
227 return false;
228 }
229 self.vertices.push(support_point);
230 for &(ea, eb) in &edges {
231 let a = self.vertices[ea];
232 let b = self.vertices[eb];
233 let c = support_point;
234 let normal = epa_face_normal(a, b, c);
235 let mut distance = epa_dot3(normal, a);
236 let mut n = normal;
237 if distance < 0.0 {
238 n = epa_negate3(n);
239 distance = -distance;
240 }
241 self.faces.push(EpaFaceRaw {
242 vertices: [ea, eb, new_idx],
243 normal: n,
244 distance,
245 });
246 }
247 true
248 }
249}
250#[derive(Debug, Clone)]
252pub struct EpaConfig {
253 pub max_iter: usize,
255 pub tolerance: f64,
257 pub max_faces: usize,
259}
260impl Default for EpaConfig {
261 fn default() -> Self {
263 Self {
264 max_iter: 64,
265 tolerance: 1e-6,
266 max_faces: 256,
267 }
268 }
269}
270impl EpaConfig {
271 pub fn high_precision() -> Self {
273 Self {
274 max_iter: 128,
275 tolerance: 1e-9,
276 max_faces: 512,
277 }
278 }
279}
280#[derive(Debug, Clone, Default)]
282pub struct EpaPolytopeStats {
283 pub face_count: usize,
285 pub vertex_count: usize,
287 pub min_distance: f64,
289 pub max_distance: f64,
291 pub avg_distance: f64,
293 pub min_area: f64,
295 pub max_area: f64,
297}
298#[derive(Debug, Clone)]
300pub struct EpaWitness {
301 pub point_a: [f64; 3],
303 pub point_b: [f64; 3],
305 pub normal: [f64; 3],
307 pub depth: f64,
309}
310pub struct Epa;
312impl Epa {
313 pub fn penetration_depth(
316 shape_a: &dyn Shape,
317 transform_a: &Transform,
318 shape_b: &dyn Shape,
319 transform_b: &Transform,
320 simplex: &Simplex,
321 ) -> Option<Contact> {
322 if simplex.len() < 4 {
323 return Self::fallback_contact(simplex);
324 }
325 let mut vertices: Vec<SupportPoint> = simplex.points.clone();
326 let mut faces = build_initial_faces(&vertices);
327 for _ in 0..MAX_ITERATIONS {
328 let closest_idx = match faces.iter().enumerate().min_by(|(_, a), (_, b)| {
329 a.distance
330 .partial_cmp(&b.distance)
331 .unwrap_or(std::cmp::Ordering::Equal)
332 }) {
333 Some((idx, _)) => idx,
334 None => return None,
335 };
336 let closest = faces[closest_idx].clone();
337 let search_dir = closest.normal;
338 let new_point =
339 support_minkowski(shape_a, transform_a, shape_b, transform_b, &search_dir);
340 let new_dist = new_point.point.dot(&search_dir);
341 if (new_dist - closest.distance).abs() < TOLERANCE {
342 return Some(build_contact(&closest, &vertices));
343 }
344 let new_idx = vertices.len();
345 vertices.push(new_point);
346 let mut edges: Vec<(usize, usize)> = Vec::new();
347 let mut i = 0;
348 while i < faces.len() {
349 let face = &faces[i];
350 let v = vertices[face.indices[0]].point;
351 if face.normal.dot(&(new_point.point - v)) > 0.0 {
352 let [a, b, c] = face.indices;
353 add_edge(&mut edges, a, b);
354 add_edge(&mut edges, b, c);
355 add_edge(&mut edges, c, a);
356 faces.swap_remove(i);
357 } else {
358 i += 1;
359 }
360 }
361 for &(ea, eb) in &edges {
362 let normal = compute_face_normal(
363 &vertices[ea].point,
364 &vertices[eb].point,
365 &vertices[new_idx].point,
366 );
367 let distance = normal.dot(&vertices[ea].point);
368 let (normal, distance) = if distance < 0.0 {
369 (-normal, -distance)
370 } else {
371 (normal, distance)
372 };
373 faces.push(Face {
374 indices: [ea, eb, new_idx],
375 normal,
376 distance,
377 });
378 }
379 }
380 faces
381 .iter()
382 .min_by(|a, b| {
383 a.distance
384 .partial_cmp(&b.distance)
385 .unwrap_or(std::cmp::Ordering::Equal)
386 })
387 .map(|face| build_contact(face, &vertices))
388 }
389 fn fallback_contact(simplex: &Simplex) -> Option<Contact> {
390 if simplex.is_empty() {
391 return None;
392 }
393 let (closest, _) = simplex.closest_point_to_origin();
394 let depth = closest.norm();
395 let normal = if depth > 1e-10 {
396 -closest.normalize()
397 } else {
398 Vec3::new(0.0, 1.0, 0.0)
399 };
400 let point_a: Vec3 =
401 simplex.points.iter().map(|p| p.support_a).sum::<Vec3>() / simplex.len() as Real;
402 let point_b: Vec3 =
403 simplex.points.iter().map(|p| p.support_b).sum::<Vec3>() / simplex.len() as Real;
404 Some(Contact::new(point_a, point_b, normal, depth))
405 }
406}
407#[derive(Debug, Clone, Default)]
409pub struct EpaStats {
410 pub iterations: usize,
412 pub face_count: usize,
414 pub final_depth: f64,
416 pub converged: bool,
418}
419#[derive(Debug, Clone)]
421pub struct Face {
422 pub indices: [usize; 3],
424 pub normal: Vec3,
426 pub distance: Real,
428}
429pub struct EpaFaceQueue<'a> {
434 pub(super) polytope: &'a EpaPolytope,
435}
436impl<'a> EpaFaceQueue<'a> {
437 pub fn new(polytope: &'a EpaPolytope) -> Self {
439 Self { polytope }
440 }
441 pub fn pop_min(&self) -> Option<usize> {
443 if self.polytope.faces.is_empty() {
444 return None;
445 }
446 Some(self.polytope.find_closest_face())
447 }
448 pub fn peek_min(&self) -> Option<(usize, f64, [f64; 3])> {
450 let idx = self.pop_min()?;
451 let f = &self.polytope.faces[idx];
452 Some((idx, f.distance, f.normal))
453 }
454 pub fn len(&self) -> usize {
456 self.polytope.faces.len()
457 }
458 pub fn is_empty(&self) -> bool {
460 self.polytope.faces.is_empty()
461 }
462}
463#[derive(Debug, Clone)]
465pub struct EpaPenetration {
466 pub normal: [f64; 3],
468 pub depth: f64,
470 pub witness_a: [f64; 3],
472 pub witness_b: [f64; 3],
474}