diff --git a/crates/emath/src/rect.rs b/crates/emath/src/rect.rs index 6c0677ad55e..f4217ed89fb 100644 --- a/crates/emath/src/rect.rs +++ b/crates/emath/src/rect.rs @@ -671,6 +671,30 @@ impl Rect { 0.0 <= tmax && tmin <= tmax } + + /// Where does a ray from the center intersect the rectangle? + /// + /// `d` is the direction of the ray and assumed to be normalized. + pub fn intersects_ray_from_center(&self, d: Vec2) -> Pos2 { + let mut tmin = f32::NEG_INFINITY; + let mut tmax = f32::INFINITY; + + for i in 0..2 { + let inv_d = 1.0 / -d[i]; + let mut t0 = (self.min[i] - self.center()[i]) * inv_d; + let mut t1 = (self.max[i] - self.center()[i]) * inv_d; + + if inv_d < 0.0 { + std::mem::swap(&mut t0, &mut t1); + } + + tmin = tmin.max(t0); + tmax = tmax.min(t1); + } + + let t = tmax.min(tmin); // Pick the first intersection + self.center() + t * -d + } } impl fmt::Debug for Rect { @@ -793,4 +817,57 @@ mod tests { println!("Leftward ray from right:"); assert!(rect.intersects_ray(pos2(4.0, 2.0), Vec2::LEFT)); } + + #[test] + fn test_ray_from_center_intersection() { + let rect = Rect::from_min_max(pos2(1.0, 1.0), pos2(3.0, 3.0)); + + assert_eq!( + rect.intersects_ray_from_center(Vec2::RIGHT), + pos2(3.0, 2.0), + "rightward ray" + ); + + assert_eq!( + rect.intersects_ray_from_center(Vec2::UP), + pos2(2.0, 1.0), + "upward ray" + ); + + assert_eq!( + rect.intersects_ray_from_center(Vec2::LEFT), + pos2(1.0, 2.0), + "leftward ray" + ); + + assert_eq!( + rect.intersects_ray_from_center(Vec2::DOWN), + pos2(2.0, 3.0), + "downward ray" + ); + + assert_eq!( + rect.intersects_ray_from_center((Vec2::LEFT + Vec2::DOWN).normalized()), + pos2(1.0, 3.0), + "bottom-left corner ray" + ); + + assert_eq!( + rect.intersects_ray_from_center((Vec2::LEFT + Vec2::UP).normalized()), + pos2(1.0, 1.0), + "top-left corner ray" + ); + + assert_eq!( + rect.intersects_ray_from_center((Vec2::RIGHT + Vec2::DOWN).normalized()), + pos2(3.0, 3.0), + "bottom-right corner ray" + ); + + assert_eq!( + rect.intersects_ray_from_center((Vec2::RIGHT + Vec2::UP).normalized()), + pos2(3.0, 1.0), + "top-right corner ray" + ); + } }