use num::Zero;
use alga::linear::{NormedSpace, Translation};
use na::{self, Unit};
use shape::{self, AnnotatedPoint, SupportMap};
use query::algorithms::gjk::GJKResult;
use query::algorithms::gjk;
use query::algorithms::minkowski_sampling;
use query::algorithms::epa3;
use query::algorithms::epa2;
use query::algorithms::{EPA2, EPA3, Simplex, JohnsonSimplex, VoronoiSimplex2, VoronoiSimplex3};
use query::Contact;
use math::{Isometry, Point};
/// Contact between support-mapped shapes (`Cuboid`, `ConvexHull`, etc.)
pub fn support_map_against_support_map
(
m1: &M,
g1: &G1,
m2: &M,
g2: &G2,
prediction: P::Real,
) -> Option>
where
P: Point,
M: Isometry,
G1: SupportMap
,
G2: SupportMap
,
{
if na::dimension::() == 2 {
match support_map_against_support_map_with_params(
m1,
g1,
m2,
g2,
prediction,
&mut VoronoiSimplex2::new(),
None,
) {
GJKResult::Projection(c) => Some(c),
GJKResult::NoIntersection(_) => None,
GJKResult::Intersection => unreachable!(),
GJKResult::Proximity(_) => unreachable!(),
}
} else if na::dimension::() == 3 {
match support_map_against_support_map_with_params(
m1,
g1,
m2,
g2,
prediction,
&mut VoronoiSimplex3::new(),
None,
) {
GJKResult::Projection(c) => Some(c),
GJKResult::NoIntersection(_) => None,
GJKResult::Intersection => unreachable!(),
GJKResult::Proximity(_) => unreachable!(),
}
} else {
match support_map_against_support_map_with_params(
m1,
g1,
m2,
g2,
prediction,
&mut JohnsonSimplex::new_w_tls(),
None,
) {
GJKResult::Projection(c) => Some(c),
GJKResult::NoIntersection(_) => None,
GJKResult::Intersection => unreachable!(),
GJKResult::Proximity(_) => unreachable!(),
}
}
}
/// Contact between support-mapped shapes (`Cuboid`, `ConvexHull`, etc.)
///
/// This allows a more fine grained control other the underlying GJK algorigtm.
pub fn support_map_against_support_map_with_params(
m1: &M,
g1: &G1,
m2: &M,
g2: &G2,
prediction: P::Real,
simplex: &mut S,
init_dir: Option,
) -> GJKResult, P::Vector>
where
P: Point,
M: Isometry,
S: Simplex>,
G1: SupportMap,
G2: SupportMap
,
{
let mut dir = match init_dir {
// FIXME: or m2.translation - m1.translation ?
None => m1.translation().to_vector() - m2.translation().to_vector(),
Some(dir) => dir,
};
if dir.is_zero() {
dir[0] = na::one();
}
simplex.reset(shape::cso_support_point(m1, g1, m2, g2, dir));
match gjk::closest_points_with_max_dist(m1, g1, m2, g2, prediction, simplex) {
GJKResult::Projection((p1, p2)) => {
let p1p2 = p2 - p1;
let sqn = na::norm_squared(&p1p2);
if !sqn.is_zero() {
let (normal, depth) = Unit::new_and_get(p1p2);
return GJKResult::Projection(Contact::new(p1, p2, normal, -depth));
}
}
GJKResult::NoIntersection(dir) => return GJKResult::NoIntersection(dir),
GJKResult::Intersection => {} // fallback
GJKResult::Proximity(_) => unreachable!(),
}
// The point is inside of the CSO: use the fallback algorithm
if na::dimension::() == 2 {
let mut epa = EPA2::new();
let (p1, p2) = epa2::closest_points(&mut epa, m1, g1, m2, g2, simplex);
if let Some((normal, depth)) = Unit::try_new_and_get(p1 - p2, gjk::eps_tol()) {
return GJKResult::Projection(Contact::new(p1, p2, normal, depth));
}
// XXX: if the depth is exactly zero, we should retrieve the normal by intersectiong the
// inverse Gauss maps at p1 and p2.
} else if na::dimension::() == 3 {
let mut epa = EPA3::new();
let (p1, p2) = epa3::closest_points(&mut epa, m1, g1, m2, g2, simplex);
if let Some((normal, depth)) = Unit::try_new_and_get(p1 - p2, gjk::eps_tol()) {
return GJKResult::Projection(Contact::new(p1, p2, normal, depth));
}
// XXX: if the depth is exactly zero, we should retrieve the normal by intersectiong the
// inverse Gauss maps at p1 and p2.
}
// When all else fail (e.g. because of roundup errors or for penetration in dimension
// higher than 3), default to minkowski sampling.
match minkowski_sampling::closest_points(m1, g1, m2, g2, simplex) {
Some((p1, p2, normal)) => {
let depth = na::dot(&(p1 - p2), &normal);
GJKResult::Projection(Contact::new(p1, p2, normal, depth))
}
None => GJKResult::NoIntersection(na::zero()), // panic!("Both GJK and fallback algorithm failed.")
}
}