/// An axis-aligned rectangle in screen space, used for window and monitor bounds. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct Rect { pub x: i32, pub y: i32, pub width: u32, pub height: u32, } impl Rect { pub fn new(x: i32, y: i32, width: u32, height: u32) -> Self { Self { x, y, width, height } } pub fn right(&self) -> i32 { self.x + self.width as i32 } pub fn bottom(&self) -> i32 { self.y + self.height as i32 } pub fn contains_point(&self, x: i32, y: i32) -> bool { x >= self.x && x < self.right() && y >= self.y && y < self.bottom() } pub fn overlaps(&self, other: &Rect) -> bool { !(self.right() <= other.x || other.right() <= self.x || self.bottom() <= other.y || other.bottom() <= self.y) } /// Shrinks the rect on all sides by `margin`, saturating at zero size. pub fn inset(&self, margin: u32) -> Rect { let m = margin as i32; let width = self.width.saturating_sub(margin * 2); let height = self.height.saturating_sub(margin * 2); Rect { x: self.x + m, y: self.y + m, width, height } } pub fn center(&self) -> (i32, i32) { (self.x + self.width as i32 / 2, self.y + self.height as i32 / 2) } } #[cfg(test)] mod tests { use super::*; #[test] fn overlap_detection_matches_aabb_semantics() { let a = Rect::new(0, 0, 100, 100); let b = Rect::new(50, 50, 100, 100); let c = Rect::new(100, 100, 50, 50); // touches corner, should not overlap (half-open) let d = Rect::new(200, 200, 10, 10); assert!(a.overlaps(&b)); assert!(!a.overlaps(&c)); assert!(!a.overlaps(&d)); } #[test] fn inset_shrinks_symmetrically() { let r = Rect::new(0, 0, 100, 60); let inset = r.inset(10); assert_eq!(inset, Rect::new(10, 10, 80, 40)); } #[test] fn contains_point_is_half_open() { let r = Rect::new(0, 0, 10, 10); assert!(r.contains_point(0, 0)); assert!(!r.contains_point(10, 10)); assert!(r.contains_point(9, 9)); } }