use na::{RealField, Unit};
use crate::math::{Isometry, Point, Vector};
use crate::query::{self, TOIDispatcher, Unsupported};
use crate::shape::{Ball, Plane, Shape};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TOIStatus {
OutOfIterations,
Converged,
Failed,
Penetrating,
}
#[derive(Clone, Debug)]
pub struct TOI<N: RealField> {
pub toi: N,
pub witness1: Point<N>,
pub witness2: Point<N>,
pub normal1: Unit<Vector<N>>,
pub normal2: Unit<Vector<N>>,
pub status: TOIStatus,
}
impl<N: RealField> TOI<N> {
pub fn swapped(self) -> Self {
Self {
toi: self.toi,
witness1: self.witness2,
witness2: self.witness1,
normal1: self.normal2,
normal2: self.normal1,
status: self.status,
}
}
}
pub fn time_of_impact<N: RealField>(
dispatcher: &dyn TOIDispatcher<N>,
m1: &Isometry<N>,
vel1: &Vector<N>,
g1: &dyn Shape<N>,
m2: &Isometry<N>,
vel2: &Vector<N>,
g2: &dyn Shape<N>,
max_toi: N,
target_distance: N,
) -> Result<Option<TOI<N>>, Unsupported> {
if let (Some(b1), Some(b2)) = (g1.as_shape::<Ball<N>>(), g2.as_shape::<Ball<N>>()) {
let p1 = Point::from(m1.translation.vector);
let p2 = Point::from(m2.translation.vector);
Ok(
query::time_of_impact_ball_ball(&p1, vel1, b1, &p2, vel2, b2, max_toi, target_distance)
.map(|toi| {
TOI {
toi: toi.toi,
witness1: m1.rotation.inverse_transform_point(&toi.witness1),
witness2: m2.rotation.inverse_transform_point(&toi.witness2),
normal1: m1.inverse_transform_unit_vector(&toi.normal1),
normal2: m2.inverse_transform_unit_vector(&toi.normal2),
status: toi.status,
}
}),
)
} else if let (Some(p1), Some(s2)) = (g1.as_shape::<Plane<N>>(), g2.as_support_map()) {
Ok(query::time_of_impact_plane_support_map(
m1,
vel1,
p1,
m2,
vel2,
s2,
max_toi,
target_distance,
))
} else if let (Some(s1), Some(p2)) = (g1.as_support_map(), g2.as_shape::<Plane<N>>()) {
Ok(query::time_of_impact_support_map_plane(
m1,
vel1,
s1,
m2,
vel2,
p2,
max_toi,
target_distance,
))
} else if let (Some(s1), Some(s2)) = (g1.as_support_map(), g2.as_support_map()) {
Ok(query::time_of_impact_support_map_support_map(
m1,
vel1,
s1,
m2,
vel2,
s2,
max_toi,
target_distance,
))
} else if let Some(c1) = g1.as_composite_shape() {
Ok(query::time_of_impact_composite_shape_shape(
dispatcher,
m1,
vel1,
c1,
m2,
vel2,
g2,
max_toi,
target_distance,
))
} else if let Some(c2) = g2.as_composite_shape() {
Ok(query::time_of_impact_shape_composite_shape(
dispatcher,
m1,
vel1,
g1,
m2,
vel2,
c2,
max_toi,
target_distance,
))
} else {
Err(Unsupported)
}
}