//! Penetration depth computation algorithm approximating the Minkowskis sum. use num::{Bounded, Zero}; use alga::general::Id; use alga::linear::{NormedSpace, Translation}; use na::{self, Unit}; use shape::{AnnotatedPoint, MinkowskiSum, Reflection}; use shape::SupportMap; use query::algorithms::gjk; use query::algorithms::simplex::Simplex; use math::{Isometry, Point, Vector}; /// Computes the closest points between two implicit inter-penetrating shapes. Returns None if the /// shapes are not in penetration. This can be used as a fallback algorithm for the GJK algorithm. pub fn closest_points( m1: &M, g1: &G1, m2: &M, g2: &G2, simplex: &mut S, ) -> Option<(P, P, Unit)> where P: Point, M: Isometry

, S: Simplex>, G1: SupportMap, G2: SupportMap, { let reflect2 = Reflection::new(g2); let cso = MinkowskiSum::new(m1, g1, m2, &reflect2); // find an approximation of the smallest penetration direction let mut best_dir: P::Vector = na::zero(); let mut min_dist = Bounded::max_value(); P::Vector::sample_sphere(|sample: P::Vector| { let support = cso.support_point(&Id::new(), &sample); let distance = na::dot(&sample, &support.coordinates()); if distance < min_dist { best_dir = sample; min_dist = distance; } }); let extra_shift = na::convert(0.01f64); // FIXME: do not hard-code the extra shift? let shift = best_dir * (min_dist + extra_shift); let tm2 = m2.append_translation(&M::Translation::from_vector(shift).unwrap()); simplex.modify_pnts(&|pt| pt.translate_2(&(-shift))); match gjk::closest_points(m1, g1, &tm2, g2, simplex) { None => None, // panic!("Internal error: the origin was inside of the Simplex during phase 1."), Some((p1, p2)) => { // NOTE: at this point, p1 must *not* be concidered as a good contact point for the // first object. For example: // // // +-------------+ // | | // | obj2 | // +-------|-----+ | // | +-----+-------+ // | obj1 | // | | // +-------------+ // // May Become after shifting: // +-------------+ // | | // | obj2 | // | | // p2 -> x-------------+ // +-------------x <- p1 // | | // | obj1 | // | | // +-------------+ // // Thus, after un-shifting, p1 becomes clearly invalid: // // +-------------+ // | | // | obj2 | // +-------|-----+ <- p1 | // | p2 -> +-----+-------+ // | obj1 | // | | // +-------------+ let (normal, dist_err) = Unit::new_and_get(p2 - p1); if !dist_err.is_zero() { let p2 = p2 + (-shift); let center = na::center(&p1, &p2); let nmin_dist = na::dot(&*normal, &best_dir) * (min_dist + extra_shift); let p2 = center + (-*normal) * (nmin_dist - dist_err); Some((center, p2, normal)) } else { // FIXME: something went wrong here. None } } } } /// Projects the origin on a support-mapped shape. /// /// The origin is assumed to be inside of the shape. pub fn project_origin(m: &M, g: &G, simplex: &mut S) -> Option

where P: Point, M: Isometry

, S: Simplex

, G: SupportMap, { // find an approximation of the smallest penetration direction let mut best_dir: P::Vector = na::zero(); let mut min_dist = Bounded::max_value(); P::Vector::sample_sphere(|sample: P::Vector| { let support = g.support_point(m, &sample); let distance = na::dot(&sample, &support.coordinates()); if distance < min_dist { best_dir = sample; min_dist = distance; } }); let extra_shift = na::convert(0.01f64); // FIXME: do not hard-code the extra shift? let shift = best_dir * (min_dist + extra_shift); let tm = m.append_translation(&M::Translation::from_vector(-shift).unwrap()); simplex.modify_pnts(&|pt| *pt = *pt + (-shift)); match gjk::project_origin(&tm, g, simplex) { None => None, // panic!("Internal error: the origin was inside of the Simplex during phase 1."), Some(p) => { let mut normal = -p.coordinates(); let dist_err = normal.normalize_mut(); if !dist_err.is_zero() { let nmin_dist = na::dot(&normal, &best_dir) * (min_dist + extra_shift); Some(P::origin() + normal * (nmin_dist - dist_err)) } else { // FIXME: something went wrong here. None } } } }