aboutsummaryrefslogtreecommitdiff
path: root/src/geometry.rs
diff options
context:
space:
mode:
authorRuben Beltran del Rio <jj@r.bdr.sh>2025-12-16 10:40:44 +0100
committerRuben Beltran del Rio <jj@r.bdr.sh>2025-12-16 10:55:34 +0100
commitf08648e4b5a9f7855a2ed9068a386e2f3c596322 (patch)
tree1f1567ea609a1f74aaeab3cbbfdb249c5c47d723 /src/geometry.rs
Initial implementation
Diffstat (limited to 'src/geometry.rs')
-rw-r--r--src/geometry.rs142
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));
+ }
+}