ncollide3d/query/time_of_impact/
time_of_impact_ball_ball.rs

1use na::{RealField, Unit};
2
3use crate::math::{Point, Vector};
4use crate::query::{self, Ray, TOIStatus, TOI};
5use crate::shape::Ball;
6
7/// Time Of Impact of two balls under translational movement.
8#[inline]
9pub fn time_of_impact_ball_ball<N: RealField + Copy>(
10    center1: &Point<N>,
11    vel1: &Vector<N>,
12    b1: &Ball<N>,
13    center2: &Point<N>,
14    vel2: &Vector<N>,
15    b2: &Ball<N>,
16    max_toi: N,
17    target_distance: N,
18) -> Option<TOI<N>> {
19    let vel = *vel1 - *vel2;
20    let rsum = b1.radius + b2.radius;
21    let radius = rsum + target_distance;
22    let center = *center1 + (-center2.coords);
23    let ray = Ray::new(Point::origin(), -vel);
24
25    if let (inside, Some(toi)) = query::ray_toi_with_ball(&center, radius, &ray, true) {
26        if toi > max_toi {
27            return None;
28        }
29
30        let dpt = ray.point_at(toi) - center;
31        let normal1;
32        let normal2;
33        let witness1;
34        let witness2;
35
36        if radius.is_zero() {
37            normal1 = Vector::x_axis();
38            normal2 = Vector::x_axis();
39            witness1 = Point::origin();
40            witness2 = Point::origin();
41        } else {
42            normal1 = Unit::new_unchecked(dpt / radius);
43            normal2 = -normal1;
44            witness1 = Point::from(*normal1 * b1.radius);
45            witness2 = Point::from(*normal2 * b2.radius);
46        }
47
48        let status = if inside && center.coords.norm_squared() < rsum * rsum {
49            TOIStatus::Penetrating
50        } else {
51            TOIStatus::Converged
52        };
53
54        Some(TOI {
55            toi,
56            normal1,
57            normal2,
58            witness1,
59            witness2,
60            status,
61        })
62    } else {
63        None
64    }
65}