From b828de0aebd09e5226869abf6ec848881dc45bb1 Mon Sep 17 00:00:00 2001 From: Jonathan Cornaz Date: Wed, 23 Aug 2023 20:51:00 +0200 Subject: [PATCH] v3: return normal for ray cast --- src/v3/mod.rs | 14 +++++++++++++- src/v3/vector.rs | 10 ++++++++++ tests/v3_ray_cast_spec.rs | 22 ++++++++++++++++++++-- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/v3/mod.rs b/src/v3/mod.rs index 615065d..3fb5ea2 100644 --- a/src/v3/mod.rs +++ b/src/v3/mod.rs @@ -64,19 +64,30 @@ where fn cast(&self, vector: Vec2, target: &B) -> Option { let mut max_t1 = f32::MIN; let mut min_t2 = f32::MAX; + let mut normal = Vec2::ZERO; for axis in sat_axes(self, target) { let Some((t1, t2)) = cast_projection( self.project_on(axis), vector.dot(axis), target.project_on(axis), ) else { return None }; + if t1 > max_t1 { + max_t1 = t1; + normal = axis; + } max_t1 = max_t1.max(t1); min_t2 = min_t2.min(t2); } if min_t2 < max_t1 || max_t1 > 1.0 || max_t1 <= 0.0 { return None; } - Some(CastHit { time: max_t1 }) + if normal.dot(vector) > 0.0 { + normal = -normal; + } + Some(CastHit { + time: max_t1, + normal, + }) } } @@ -84,6 +95,7 @@ where #[non_exhaustive] pub struct CastHit { pub time: f32, + pub normal: Vec2, } pub fn ray_cast(origin: Point, vector: Vec2, target: &impl Shape) -> Option { diff --git a/src/v3/vector.rs b/src/v3/vector.rs index a87d3e9..dd3992f 100644 --- a/src/v3/vector.rs +++ b/src/v3/vector.rs @@ -21,6 +21,16 @@ impl Vec2 { Self::new(value, value) } + #[must_use] + pub fn x(self) -> f32 { + self.x + } + + #[must_use] + pub fn y(self) -> f32 { + self.y + } + #[must_use] pub(super) fn dot(self, other: Self) -> f32 { (self.x * other.x) + (self.y * other.y) diff --git a/tests/v3_ray_cast_spec.rs b/tests/v3_ray_cast_spec.rs index e993bce..5846093 100644 --- a/tests/v3_ray_cast_spec.rs +++ b/tests/v3_ray_cast_spec.rs @@ -9,7 +9,7 @@ use rstest::rstest; Vec2::ZERO, Vec2::X, Aabb::from_size(Vec2::new(2.0, 2.0)).with_center_at(Point::new(1.9, 0.0)), - Vec2::new(0.9, 0.0) + Vec2::new(0.9, 0.0), )] #[case( Vec2::ZERO, @@ -59,7 +59,7 @@ use rstest::rstest; Aabb::from_size(Vec2::new(2.0, 2.0)).with_center_at(Point::new(0.5, 1.9)), Vec2::new(0.9, 0.9), )] -fn should_find_contact_point( +fn should_find_contact_time( #[case] origin: impl Into, #[case] vector: Vec2, #[case] target: impl Shape, @@ -73,6 +73,24 @@ fn should_find_contact_point( assert_abs_diff_eq!(point.y(), expected_point.y()); } +#[rstest] +#[case(Point::ORIGIN, Vec2::X, Aabb::from_size(Vec2::new(2.0, 2.0)).with_center_at(Point::new(1.9, 0.0)), -Vec2::X)] +#[case(Point::ORIGIN, -Vec2::X, Aabb::from_size(Vec2::new(2.0, 2.0)).with_center_at(Point::new(-1.9, 0.0)), Vec2::X)] +#[case(Point::ORIGIN, Vec2::Y, Aabb::from_size(Vec2::new(2.0, 2.0)).with_center_at(Point::new(0.0, 1.9)), -Vec2::Y)] +#[case(Point::ORIGIN, -Vec2::Y, Aabb::from_size(Vec2::new(2.0, 2.0)).with_center_at(Point::new(0.0, -1.9)), Vec2::Y)] +#[case(Point::ORIGIN, Vec2::new(1.0, 1.0), Aabb::from_size(Vec2::new(2.0, 2.0)).with_center_at(Point::new(1.9, 0.0)), -Vec2::X)] +fn should_find_contact_normal( + #[case] origin: impl Into, + #[case] vector: Vec2, + #[case] target: impl Shape, + #[case] expected_normal: Vec2, +) { + let origin = origin.into(); + let normal = ray_cast(origin, vector, &target).unwrap().normal; + assert_abs_diff_eq!(normal.x(), expected_normal.x()); + assert_abs_diff_eq!(normal.y(), expected_normal.y()); +} + #[rstest] #[case(Vec2::ZERO, Vec2::X, Aabb::from_size(Vec2::new(2.0, 2.0)).with_center_at(Point::new(2.1, 0.0)))] #[case(Vec2::ZERO, Vec2::X, Aabb::from_size(Vec2::new(2.0, 2.0)).with_center_at(Point::new(-2.1, 0.0)))]