parry2d/query/shape_cast/
shape_cast_ball_ball.rs1use crate::math::{Pose, Vector};
2use crate::query::details::ShapeCastOptions;
3use crate::query::{self, Ray, ShapeCastHit, ShapeCastStatus};
4use crate::shape::Ball;
5use num::Zero;
6
7#[inline]
9pub fn cast_shapes_ball_ball(
10 pos12: &Pose,
11 vel12: Vector,
12 b1: &Ball,
13 b2: &Ball,
14 options: ShapeCastOptions,
15) -> Option<ShapeCastHit> {
16 let rsum = b1.radius + b2.radius + options.target_distance;
17 let radius = rsum;
18 let center = -pos12.translation;
19 let ray = Ray::new(Vector::ZERO, vel12);
20
21 if let (inside, Some(time_of_impact)) =
22 query::details::ray_toi_with_ball(center, radius, &ray, true)
23 {
24 if time_of_impact > options.max_time_of_impact {
25 return None;
26 }
27
28 let dpt = ray.point_at(time_of_impact) - center;
29 let normal1;
30 let normal2;
31 let witness1;
32 let witness2;
33
34 if radius.is_zero() {
35 normal1 = Vector::X;
36 normal2 = pos12.rotation.inverse() * -Vector::X;
37 witness1 = Vector::ZERO;
38 witness2 = Vector::ZERO;
39 } else {
40 normal1 = dpt / radius;
41 normal2 = pos12.rotation.inverse() * -normal1;
42 witness1 = normal1 * b1.radius;
43 witness2 = normal2 * b2.radius;
44 }
45
46 if !options.stop_at_penetration && time_of_impact < 1.0e-5 && normal1.dot(vel12) >= 0.0 {
47 return None;
48 }
49
50 let status = if inside && center.length_squared() < rsum * rsum {
51 ShapeCastStatus::PenetratingOrWithinTargetDist
52 } else {
53 ShapeCastStatus::Converged
54 };
55
56 Some(ShapeCastHit {
57 time_of_impact,
58 normal1,
59 normal2,
60 witness1,
61 witness2,
62 status,
63 })
64 } else {
65 None
66 }
67}