use num::{Bounded, Zero};
use na::{self, Unit};
use crate::shape::{AnnotatedPoint, MinkowskiSum, Reflection};
use crate::shape::SupportMap;
use crate::query::algorithms::gjk;
use crate::query::algorithms::simplex::Simplex;
use crate::math::{Isometry, Point, Vector};
pub fn closest_points<N, S, G1: ?Sized, G2: ?Sized>(
m1: &Isometry<N>,
g1: &G1,
m2: &Isometry<N>,
g2: &G2,
simplex: &mut S,
) -> Option<(Point<N>, Point<N>, Unit<Vector<N>>)>
where
N: RealField + Copy,
M: Isometry<P>,
S: Simplex<AnnotatedPoint<P>>,
G1: SupportMap<N>,
G2: SupportMap<N>,
{
let reflect2 = Reflection::new(g2);
let cso = MinkowskiSum::new(m1, g1, m2, &reflect2);
let mut best_dir: Vector<N> = na::zero();
let mut min_dist = Bounded::max_value();
Vector<N>::sample_sphere(|sample: Vector<N>| {
let support = cso.local_support_point( &sample);
let distance = sample.dot(&support.coords);
if distance < min_dist {
best_dir = sample;
min_dist = distance;
}
});
let extra_shift = na::convert(0.01f64); let shift = best_dir * (min_dist + extra_shift);
let tm2 = m2.append_translation(&Isometry<N>::Translation::from(shift).unwrap());
simplex.modify_pnts(&|pt| pt.translate_2(&(-shift)));
match gjk::closest_points(m1, g1, &tm2, g2, simplex) {
None => None, Some((p1, p2)) => {
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 = normal.dot(&best_dir) * (min_dist + extra_shift);
let p2 = center + (-*normal) * (nmin_dist - dist_err);
Some((center, p2, normal))
} else {
None
}
}
}
}
pub fn project_origin<N, S, G>(m: &Isometry<N>, g: &G, simplex: &mut S) -> Option<P>
where
N: RealField + Copy,
M: Isometry<P>,
S: Simplex<N>,
G: SupportMap<N>,
{
let mut best_dir: Vector<N> = na::zero();
let mut min_dist = Bounded::max_value();
Vector<N>::sample_sphere(|sample: Vector<N>| {
let support = g.support_point(m, &sample);
let distance = sample.dot(&support.coords);
if distance < min_dist {
best_dir = sample;
min_dist = distance;
}
});
let extra_shift = na::convert(0.01f64); let shift = best_dir * (min_dist + extra_shift);
let tm = m.append_translation(&Isometry<N>::Translation::from(-shift).unwrap());
simplex.modify_pnts(&|pt| *pt = *pt + (-shift));
match gjk::project_origin(&tm, g, simplex) {
None => None, Some(p) => {
let mut normal = -p.coords;
let dist_err = normal.normalize_mut();
if !dist_err.is_zero() {
let nmin_dist = normal.dot(&best_dir) * (min_dist + extra_shift);
Some(Point::origin() + normal * (nmin_dist - dist_err))
} else {
None
}
}
}
}