diff options
| author | Ruben Beltran del Rio <jj@r.bdr.sh> | 2025-12-16 10:40:44 +0100 |
|---|---|---|
| committer | Ruben Beltran del Rio <jj@r.bdr.sh> | 2025-12-16 10:55:34 +0100 |
| commit | f08648e4b5a9f7855a2ed9068a386e2f3c596322 (patch) | |
| tree | 1f1567ea609a1f74aaeab3cbbfdb249c5c47d723 /src/geometry.rs | |
Initial implementation
Diffstat (limited to 'src/geometry.rs')
| -rw-r--r-- | src/geometry.rs | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/src/geometry.rs b/src/geometry.rs new file mode 100644 index 0000000..5099ada --- /dev/null +++ b/src/geometry.rs @@ -0,0 +1,142 @@ +/// 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)); + } +} |