use crate::prelude::*;
use parry::query::{PersistentQueryDispatcher, Unsupported};
pub type UnsupportedShape = Unsupported;
pub fn contact(
collider1: &Collider,
position1: impl Into<Position>,
rotation1: impl Into<Rotation>,
collider2: &Collider,
position2: impl Into<Position>,
rotation2: impl Into<Rotation>,
prediction_distance: Scalar,
) -> Result<Option<ContactData>, UnsupportedShape> {
let rotation1: Rotation = rotation1.into();
let rotation2: Rotation = rotation2.into();
let isometry1 = utils::make_isometry(position1.into(), rotation1);
let isometry2 = utils::make_isometry(position2.into(), rotation2);
parry::query::contact(
&isometry1,
collider1.shape_scaled().0.as_ref(),
&isometry2,
collider2.shape_scaled().0.as_ref(),
prediction_distance,
)
.map(|contact| {
if let Some(contact) = contact {
let point1: Vector = rotation1.inverse().rotate(contact.point1.into());
let point2: Vector = rotation2.inverse().rotate(contact.point2.into());
let normal1: Vector = rotation1
.inverse()
.rotate(contact.normal1.into())
.normalize();
let normal2: Vector = rotation2
.inverse()
.rotate(contact.normal2.into())
.normalize();
if !normal1.is_normalized() || !normal2.is_normalized() {
return None;
}
Some(ContactData {
point1,
point2,
normal1,
normal2,
penetration: -contact.dist,
})
} else {
None
}
})
}
pub fn contact_manifolds(
collider1: &Collider,
position1: impl Into<Position>,
rotation1: impl Into<Rotation>,
collider2: &Collider,
position2: impl Into<Position>,
rotation2: impl Into<Rotation>,
prediction_distance: Scalar,
) -> Vec<ContactManifold> {
let isometry1 = utils::make_isometry(position1.into(), rotation1.into());
let isometry2 = utils::make_isometry(position2.into(), rotation2.into());
let isometry12 = isometry1.inv_mul(&isometry2);
let mut manifolds: Vec<parry::query::ContactManifold<(), ()>> = vec![];
let _ = parry::query::DefaultQueryDispatcher.contact_manifolds(
&isometry12,
collider1.shape_scaled().0.as_ref(),
collider2.shape_scaled().0.as_ref(),
prediction_distance,
&mut manifolds,
&mut None,
);
manifolds
.iter()
.filter_map(|manifold| {
let subpos1 = manifold.subshape_pos1.unwrap_or_default();
let subpos2 = manifold.subshape_pos2.unwrap_or_default();
let normal1: Vector = subpos1
.rotation
.transform_vector(&manifold.local_n1)
.normalize()
.into();
let normal2: Vector = subpos2
.rotation
.transform_vector(&manifold.local_n2)
.normalize()
.into();
if !normal1.is_normalized() || !normal2.is_normalized() {
return None;
}
Some(ContactManifold {
normal1,
normal2,
contacts: manifold
.contacts()
.iter()
.map(|contact| ContactData {
point1: subpos1.transform_point(&contact.local_p1).into(),
point2: subpos2.transform_point(&contact.local_p2).into(),
normal1,
normal2,
penetration: -contact.dist,
})
.collect(),
})
})
.collect()
}
#[derive(Reflect, Clone, Copy, Debug, PartialEq)]
pub enum ClosestPoints {
Intersecting,
WithinMargin(Vector, Vector),
OutsideMargin,
}
pub fn closest_points(
collider1: &Collider,
position1: impl Into<Position>,
rotation1: impl Into<Rotation>,
collider2: &Collider,
position2: impl Into<Position>,
rotation2: impl Into<Rotation>,
max_distance: Scalar,
) -> Result<ClosestPoints, UnsupportedShape> {
let rotation1: Rotation = rotation1.into();
let rotation2: Rotation = rotation2.into();
let isometry1 = utils::make_isometry(position1.into(), rotation1);
let isometry2 = utils::make_isometry(position2.into(), rotation2);
parry::query::closest_points(
&isometry1,
collider1.shape_scaled().0.as_ref(),
&isometry2,
collider2.shape_scaled().0.as_ref(),
max_distance,
)
.map(|closest_points| match closest_points {
parry::query::ClosestPoints::Intersecting => ClosestPoints::Intersecting,
parry::query::ClosestPoints::WithinMargin(point1, point2) => {
ClosestPoints::WithinMargin(point1.into(), point2.into())
}
parry::query::ClosestPoints::Disjoint => ClosestPoints::OutsideMargin,
})
}
pub fn distance(
collider1: &Collider,
position1: impl Into<Position>,
rotation1: impl Into<Rotation>,
collider2: &Collider,
position2: impl Into<Position>,
rotation2: impl Into<Rotation>,
) -> Result<Scalar, UnsupportedShape> {
let rotation1: Rotation = rotation1.into();
let rotation2: Rotation = rotation2.into();
let isometry1 = utils::make_isometry(position1.into(), rotation1);
let isometry2 = utils::make_isometry(position2.into(), rotation2);
parry::query::distance(
&isometry1,
collider1.shape_scaled().0.as_ref(),
&isometry2,
collider2.shape_scaled().0.as_ref(),
)
}
pub fn intersection_test(
collider1: &Collider,
position1: impl Into<Position>,
rotation1: impl Into<Rotation>,
collider2: &Collider,
position2: impl Into<Position>,
rotation2: impl Into<Rotation>,
) -> Result<bool, UnsupportedShape> {
let rotation1: Rotation = rotation1.into();
let rotation2: Rotation = rotation2.into();
let isometry1 = utils::make_isometry(position1.into(), rotation1);
let isometry2 = utils::make_isometry(position2.into(), rotation2);
parry::query::intersection_test(
&isometry1,
collider1.shape_scaled().0.as_ref(),
&isometry2,
collider2.shape_scaled().0.as_ref(),
)
}
pub type TimeOfImpactStatus = parry::query::details::TOIStatus;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct TimeOfImpact {
pub time_of_impact: Scalar,
pub point1: Vector,
pub point2: Vector,
pub normal1: Vector,
pub normal2: Vector,
pub status: TimeOfImpactStatus,
}
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
pub fn time_of_impact(
collider1: &Collider,
position1: impl Into<Position>,
rotation1: impl Into<Rotation>,
velocity1: impl Into<LinearVelocity>,
collider2: &Collider,
position2: impl Into<Position>,
rotation2: impl Into<Rotation>,
velocity2: impl Into<LinearVelocity>,
max_time_of_impact: Scalar,
) -> Result<Option<TimeOfImpact>, UnsupportedShape> {
let rotation1: Rotation = rotation1.into();
let rotation2: Rotation = rotation2.into();
let velocity1: LinearVelocity = velocity1.into();
let velocity2: LinearVelocity = velocity2.into();
let isometry1 = utils::make_isometry(position1.into(), rotation1);
let isometry2 = utils::make_isometry(position2.into(), rotation2);
parry::query::time_of_impact(
&isometry1,
&velocity1.0.into(),
collider1.shape_scaled().0.as_ref(),
&isometry2,
&velocity2.0.into(),
collider2.shape_scaled().0.as_ref(),
max_time_of_impact,
true,
)
.map(|toi| {
toi.map(|toi| TimeOfImpact {
time_of_impact: toi.toi,
point1: toi.witness1.into(),
point2: toi.witness2.into(),
normal1: toi.normal1.into(),
normal2: toi.normal2.into(),
status: toi.status,
})
})
}