/// Represents a line segment #[derive(Debug, Clone, Copy)] pub struct Line { pub start: (f64, f64), pub end: (f64, f64), } /// Represents a rectangle #[derive(Debug, Clone, Copy)] pub struct Rect { pub x: f64, pub y: f64, pub width: f64, pub height: f64, } impl Line { pub fn new(start: (f64, f64), end: (f64, f64)) -> Self { Self { start, end } } } impl Rect { pub fn new(x: f64, y: f64, width: f64, height: f64) -> Self { Self { x, y, width, height, } } } /// Checks if a point is inside a rectangle pub fn point_in_rect(point: (f64, f64), rect: &Rect) -> bool { point.0 >= rect.x && point.0 <= rect.x + rect.width && point.1 >= rect.y && point.1 <= rect.y + rect.height } /// Checks if two rectangles intersect (AABB collision) pub fn rect_intersects_rect(r1: &Rect, r2: &Rect) -> bool { !(r1.x + r1.width < r2.x || r2.x + r2.width < r1.x || r1.y + r1.height < r2.y || r2.y + r2.height < r1.y) } /// Checks if two line segments intersect using parametric equations pub fn line_intersects_line(l1: &Line, l2: &Line) -> bool { let x1 = l1.start.0; let y1 = l1.start.1; let x2 = l1.end.0; let y2 = l1.end.1; let x3 = l2.start.0; let y3 = l2.start.1; let x4 = l2.end.0; let y4 = l2.end.1; let denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); if denom.abs() < 1e-10 { return false; // Parallel or coincident } let t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom; let u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / denom; (0.0..=1.0).contains(&t) && (0.0..=1.0).contains(&u) } /// Checks if a line segment intersects a rectangle pub fn line_intersects_rect(line: &Line, rect: &Rect) -> bool { // Check if either endpoint is inside the rectangle if point_in_rect(line.start, rect) || point_in_rect(line.end, rect) { return true; } // Check intersection with all four edges let edges = [ Line::new((rect.x, rect.y), (rect.x + rect.width, rect.y)), // Top Line::new( (rect.x + rect.width, rect.y), (rect.x + rect.width, rect.y + rect.height), ), // Right Line::new( (rect.x + rect.width, rect.y + rect.height), (rect.x, rect.y + rect.height), ), // Bottom Line::new((rect.x, rect.y + rect.height), (rect.x, rect.y)), // Left ]; edges.iter().any(|edge| line_intersects_line(line, edge)) } #[cfg(test)] mod tests { use super::*; #[test] fn test_point_in_rect() { let rect = Rect::new(10.0, 10.0, 20.0, 20.0); assert!(point_in_rect((15.0, 15.0), &rect)); assert!(point_in_rect((10.0, 10.0), &rect)); assert!(point_in_rect((30.0, 30.0), &rect)); assert!(!point_in_rect((5.0, 15.0), &rect)); assert!(!point_in_rect((35.0, 15.0), &rect)); } #[test] fn test_rect_intersects_rect() { let r1 = Rect::new(0.0, 0.0, 10.0, 10.0); let r2 = Rect::new(5.0, 5.0, 10.0, 10.0); let r3 = Rect::new(20.0, 20.0, 10.0, 10.0); assert!(rect_intersects_rect(&r1, &r2)); assert!(rect_intersects_rect(&r2, &r1)); assert!(!rect_intersects_rect(&r1, &r3)); } #[test] fn test_line_intersects_line() { let l1 = Line::new((0.0, 0.0), (10.0, 10.0)); let l2 = Line::new((0.0, 10.0), (10.0, 0.0)); let l3 = Line::new((20.0, 20.0), (30.0, 30.0)); assert!(line_intersects_line(&l1, &l2)); assert!(!line_intersects_line(&l1, &l3)); } #[test] fn test_line_intersects_rect() { let rect = Rect::new(10.0, 10.0, 20.0, 20.0); let l1 = Line::new((0.0, 15.0), (40.0, 15.0)); // Horizontal through let l2 = Line::new((15.0, 0.0), (15.0, 40.0)); // Vertical through let l3 = Line::new((0.0, 0.0), (5.0, 5.0)); // Outside assert!(line_intersects_rect(&l1, &rect)); assert!(line_intersects_rect(&l2, &rect)); assert!(!line_intersects_rect(&l3, &rect)); } }