parry3d/query/shape_cast/
shape_cast_ball_ball.rs1use na::Unit;
2
3use crate::math::{Isometry, Point, Real, Vector};
4use crate::query::details::ShapeCastOptions;
5use crate::query::{self, Ray, ShapeCastHit, ShapeCastStatus};
6use crate::shape::Ball;
7use num::Zero;
8
9#[inline]
11pub fn cast_shapes_ball_ball(
12 pos12: &Isometry<Real>,
13 vel12: &Vector<Real>,
14 b1: &Ball,
15 b2: &Ball,
16 options: ShapeCastOptions,
17) -> Option<ShapeCastHit> {
18 let rsum = b1.radius + b2.radius + options.target_distance;
19 let radius = rsum;
20 let center = Point::from(-pos12.translation.vector);
21 let ray = Ray::new(Point::origin(), *vel12);
22
23 if let (inside, Some(time_of_impact)) =
24 query::details::ray_toi_with_ball(¢er, radius, &ray, true)
25 {
26 if time_of_impact > options.max_time_of_impact {
27 return None;
28 }
29
30 let dpt = ray.point_at(time_of_impact) - 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 = pos12.inverse_transform_unit_vector(&(-Vector::x_axis()));
39 witness1 = Point::origin();
40 witness2 = Point::origin();
41 } else {
42 normal1 = Unit::new_unchecked(dpt / radius);
43 normal2 = pos12.inverse_transform_unit_vector(&(-normal1));
44 witness1 = Point::from(*normal1 * b1.radius);
45 witness2 = Point::from(*normal2 * b2.radius);
46 }
47
48 if !options.stop_at_penetration && time_of_impact < 1.0e-5 && normal1.dot(vel12) >= 0.0 {
49 return None;
50 }
51
52 let status = if inside && center.coords.norm_squared() < rsum * rsum {
53 ShapeCastStatus::PenetratingOrWithinTargetDist
54 } else {
55 ShapeCastStatus::Converged
56 };
57
58 Some(ShapeCastHit {
59 time_of_impact,
60 normal1,
61 normal2,
62 witness1,
63 witness2,
64 status,
65 })
66 } else {
67 None
68 }
69}