use std::ops::Add; #[derive(Debug, Copy, Clone, Hash)] pub struct Point { x: u32, y: u32, } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub struct Rectangle { top_left: Point, width: u32, height: u32, } impl Point { pub const fn new(x: u32, y: u32) -> Self { Self { x, y } } pub const fn origin() -> Self { Self { x: 0, y: 0 } } pub const fn with_x(self, x: u32) -> Self { Self { x, y: self.y } } pub const fn with_y(self, y: u32) -> Self { Self { x: self.x, y } } pub const fn x(self) -> u32 { self.x } pub const fn y(self) -> u32 { self.y } pub const fn left_of(self, other: Self) -> bool { self.x < other.x } pub const fn right_of(self, other: Self) -> bool { self.x > other.x } pub const fn above(self, other: Self) -> bool { self.y < other.y } pub const fn below(self, other: Self) -> bool { self.y > other.y } } impl Add for Point { type Output = Point; fn add(self, other: Point) -> Point { Point { x: self.x + other.x, y: self.y + other.y, } } } // These can be derived too, but implement them for demonstration. impl PartialEq for Point { fn eq(&self, other: &Self) -> bool { self.x == other.x && self.y == other.y } } // Marker trait to indicate reflexivity of equality (not just transitivity & symmetry) impl Eq for Point {} impl Rectangle { pub const fn from_corner_width_height(top_left: Point, width: u32, height: u32) -> Rectangle { Rectangle { top_left, width, height, } } pub fn from_corners(corner_a: Point, corner_b: Point) -> Rectangle { let left = corner_a.x().min(corner_b.x()); let top = corner_a.y().min(corner_b.y()); let width = (corner_a.x() as i64 - corner_b.x() as i64).abs() as u32; let height = (corner_a.y() as i64 - corner_b.y() as i64).abs() as u32; Rectangle::from_corner_width_height(Point::new(left, top), width, height) } pub const fn at_origin_with_size(width: u32, height: u32) -> Rectangle { Rectangle::from_corner_width_height(Point::origin(), width, height) } pub const fn width(&self) -> u32 { self.width } pub const fn height(&self) -> u32 { self.height } pub const fn with_width(&self, width: u32) -> Rectangle { Rectangle { top_left: self.top_left, width, height: self.height, } } pub const fn with_height(&self, height: u32) -> Rectangle { Rectangle { top_left: self.top_left, width: self.width, height, } } pub const fn top_left(&self) -> Point { self.top_left } pub const fn top_right(&self) -> Point { Point::new(self.top_left.x() + self.width, self.top_left.y()) } pub const fn bottom_right(&self) -> Point { Point::new( self.top_left.x() + self.width, self.top_left.y() + self.height, ) } pub const fn bottom_left(&self) -> Point { Point::new(self.top_left.x(), self.top_left.y() + self.height) } pub const fn area(&self) -> u32 { self.width * self.height } pub fn intersects(&self, other: &Rectangle) -> bool { !(self.top_left().right_of(other.top_right()) || self.top_left().below(other.bottom_left()) || self.top_right().left_of(other.top_left()) || self.bottom_left().above(other.top_left())) } pub fn intersection(&self, other: &Rectangle) -> Option { if !self.intersects(other) { return None; } // idk wtf this is lol let in_top_left; let in_bottom_right; if self.top_left().right_of(other.top_left()) { if self.top_left().below(other.top_left()) { in_top_left = self.top_left(); } else { in_top_left = Point::new(self.top_left().x(), other.top_left().y()); } } else if self.top_left().below(other.top_left()) { in_top_left = Point::new(other.top_left().x(), self.top_left().y()); } else { in_top_left = other.top_left(); } if self.bottom_right().left_of(other.bottom_right()) { if self.bottom_right().below(other.bottom_right()) { in_bottom_right = Point::new(self.bottom_right().x(), other.bottom_right().y()); } else { in_bottom_right = self.bottom_right(); } } else if self.bottom_right().below(other.bottom_right()) { in_bottom_right = other.bottom_right(); } else { in_bottom_right = Point::new(other.bottom_right().x(), self.bottom_right().y()); } Some(Rectangle::from_corners(in_top_left, in_bottom_right)) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_point_get_x() { assert_eq!(Point::new(42, 52).x(), 42); } #[test] fn test_point_get_y() { assert_eq!(Point::new(42, 52).y(), 52); } #[test] fn test_point_set_x() { assert_eq!(Point::new(42, 52).with_x(12), Point::new(12, 52)); } #[test] fn test_point_set_y() { assert_eq!(Point::new(42, 52).with_y(12), Point::new(42, 12)); } #[test] fn test_point_left_of() { assert!(Point::new(10, 20).left_of(Point::new(20, 20))); assert!(!Point::new(10, 11).left_of(Point::new(10, 11))); assert!(!Point::new(10, 11).left_of(Point::new(8, 11))); } #[test] fn test_point_right_of() { assert!(Point::new(10, 20).right_of(Point::new(0, 20))); assert!(!Point::new(10, 11).right_of(Point::new(10, 11))); assert!(!Point::new(10, 11).right_of(Point::new(12, 11))); } #[test] fn test_point_above() { assert!(Point::new(1, 1).above(Point::new(1, 2))); assert!(!Point::new(10, 11).above(Point::origin())); } #[test] fn test_point_below() { assert!(Point::new(1, 1).below(Point::new(1, 0))); assert!(!Point::new(10, 11).below(Point::new(12, 11))); } #[test] fn test_point_add() { assert_eq!(Point::new(1, 1) + Point::new(10, 10), Point::new(11, 11)); assert_eq!( Point::new(10, 0) + Point::new(0, 10), Point::new(0, 10) + Point::new(10, 0) ); } #[test] fn test_rectangle_get_width() { assert_eq!( Rectangle::from_corners(Point::new(1, 1), Point::new(11, 11)).width(), 10 ); assert_eq!( Rectangle::from_corner_width_height(Point::new(1, 1), 20, 30).width(), 20 ); } #[test] fn test_rectangle_get_height() { assert_eq!( Rectangle::from_corners(Point::new(1, 0), Point::new(11, 11)).height(), 11 ); assert_eq!( Rectangle::from_corner_width_height(Point::new(7, 6), 20, 30).height(), 30 ); } #[test] fn test_rectangle_set_width() { assert_eq!( Rectangle::from_corner_width_height(Point::new(1, 2), 3, 4).with_width(5), Rectangle::from_corner_width_height(Point::new(1, 2), 5, 4) ); assert_eq!( Rectangle::from_corners(Point::new(1, 2), Point::new(6, 7)).with_width(10), Rectangle::from_corners(Point::new(1, 2), Point::new(11, 7)) ); } #[test] fn test_rectangle_set_height() { assert_eq!( Rectangle::from_corner_width_height(Point::new(1, 2), 3, 4).with_height(5), Rectangle::from_corner_width_height(Point::new(1, 2), 3, 5) ); assert_eq!( Rectangle::from_corners(Point::new(1, 2), Point::new(6, 7)).with_height(10), Rectangle::from_corners(Point::new(1, 2), Point::new(6, 12)) ); } #[test] fn test_rectangle_get_top_left() { assert_eq!( Point::new(1, 2), Rectangle::from_corner_width_height(Point::new(1, 2), 3, 4).top_left() ); } #[test] fn test_rectangle_get_top_right() { assert_eq!( Point::new(4, 2), Rectangle::from_corner_width_height(Point::new(1, 2), 3, 4).top_right() ); } #[test] fn test_rectangle_get_bottom_left() { assert_eq!( Point::new(1, 6), Rectangle::from_corner_width_height(Point::new(1, 2), 3, 4).bottom_left() ); } #[test] fn test_rectangle_get_bottom_right() { assert_eq!( Point::new(4, 6), Rectangle::from_corner_width_height(Point::new(1, 2), 3, 4).bottom_right() ); } #[test] fn test_rectangle_get_area() { assert_eq!(100, Rectangle::at_origin_with_size(10, 10).area()); assert_eq!( 30, Rectangle::from_corners(Point::origin(), Point::new(6, 5)).area() ); } #[test] fn test_rectangle_intersect_1() { let r1 = Rectangle::from_corner_width_height(Point::new(0, 0), 10, 10); let r2 = Rectangle::from_corner_width_height(Point::new(8, 0), 10, 10); let r3 = Rectangle::from_corner_width_height(Point::new(0, 8), 10, 10); let r4 = Rectangle::from_corner_width_height(Point::new(8, 8), 10, 10); let r5 = Rectangle::from_corner_width_height(Point::new(0, 100), 5, 5); let r6 = Rectangle::from_corner_width_height(Point::new(100, 100), 5, 5); assert!(r1.intersects(&r2)); assert!(r1.intersects(&r3)); assert!(r1.intersects(&r4)); assert!(r2.intersects(&r3)); assert!(r2.intersects(&r4)); assert!(r3.intersects(&r4)); assert!(r1.intersects(&r1)); assert!(r2.intersects(&r2)); assert!(r3.intersects(&r3)); assert!(r4.intersects(&r4)); assert!(!r1.intersects(&r5)); assert!(!r2.intersects(&r5)); assert!(!r3.intersects(&r5)); assert!(!r4.intersects(&r5)); assert!(!r1.intersects(&r6)); assert!(!r2.intersects(&r6)); assert!(!r3.intersects(&r6)); assert!(!r4.intersects(&r6)); assert!(!r5.intersects(&r6)); } #[test] fn test_rectangle_intersect_2() { assert!(Rectangle::at_origin_with_size(3, 3).intersects( &Rectangle::from_corner_width_height(Point::new(3, 3), 10, 10) )); assert!(Rectangle::at_origin_with_size(3, 3).intersects( &Rectangle::from_corner_width_height(Point::new(3, 0), 10, 10) )); } #[test] fn test_rectangle_intersect_3() { assert!(!Rectangle::at_origin_with_size(3, 3).intersects( &Rectangle::from_corner_width_height(Point::new(4, 4), 10, 10) )); } }