ncollide3d/query/time_of_impact/time_of_impact.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
use na::{RealField, Unit};
use crate::math::{Isometry, Point, Vector};
use crate::query::{self, TOIDispatcher, Unsupported};
use crate::shape::{Ball, Plane, Shape};
/// The status of the time-of-impact computation algorithm.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TOIStatus {
/// The TOI algorithm ran out of iterations before achieving convergence.
///
/// If this happens, the content of the `NonlinearTOI` will still be a conservative approximation
/// of the actual result so it is often fine to interpret this case as a success.
OutOfIterations,
/// The TOI algorithm converged successfully.
Converged,
/// Something went wrong during the TOI computation, likely due to numerical instabilities.
///
/// If this happens, the content of the `NonlinearTOI` will still be a conservative approximation
/// of the actual result so it is often fine to interpret this case as a success.
Failed,
/// The two shape already overlap at the time 0.
///
/// If this happens, the witness points provided by the `NonlinearTOI` will be invalid.
Penetrating,
}
/// The result of a time-of-impact (TOI) computation.
#[derive(Clone, Debug)]
pub struct TOI<N: RealField + Copy> {
/// The time at which the objects touch.
pub toi: N,
/// The local-space closest point on the first shape at the time of impact.
pub witness1: Point<N>,
/// The local-space closest point on the second shape at the time of impact.
pub witness2: Point<N>,
/// The local-space outward normal on the first shape at the time of impact.
pub normal1: Unit<Vector<N>>,
/// The local-space outward normal on the second shape at the time of impact.
pub normal2: Unit<Vector<N>>,
/// The way the time-of-impact computation algorithm terminated.
pub status: TOIStatus,
}
impl<N: RealField + Copy> TOI<N> {
/// Swaps every data of this TOI result such that the role of both shapes are inverted.
///
/// In practice, this makes it so that `self.witness1` and `self.normal1` become `self.witness2` and `self.normal2` and vice-versa.
pub fn swapped(self) -> Self {
Self {
toi: self.toi,
witness1: self.witness2,
witness2: self.witness1,
normal1: self.normal2,
normal2: self.normal1,
status: self.status,
}
}
}
/// Computes the smallest time at with two shapes under translational movement are separated by a
/// distance smaller or equal to `distance`.
///
/// Returns `0.0` if the objects are touching or penetrating.
pub fn time_of_impact<N: RealField + Copy>(
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| {
// We have to transform back the points and vectors in the sphere's local space since
// the time_of_impact_ball_ball did not take rotation into account.
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)
}
}