#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rect {
pub x: u16,
pub y: u16,
pub width: u16,
pub height: u16,
}
impl Rect {
pub fn new(x: u16, y: u16, width: u16, height: u16) -> Self {
Self {
x,
y,
width,
height,
}
}
pub fn empty() -> Self {
Self {
x: 0,
y: 0,
width: 0,
height: 0,
}
}
pub fn right(&self) -> u16 {
self.x.saturating_add(self.width)
}
pub fn bottom(&self) -> u16 {
self.y.saturating_add(self.height)
}
pub fn contains_point(&self, x: u16, y: u16) -> bool {
x >= self.x && x < self.right() && y >= self.y && y < self.bottom()
}
pub fn is_empty(&self) -> bool {
self.width == 0 || self.height == 0
}
pub fn intersection(&self, other: &Rect) -> Rect {
let x = self.x.max(other.x);
let y = self.y.max(other.y);
let right = self.right().min(other.right());
let bottom = self.bottom().min(other.bottom());
if x < right && y < bottom {
Rect::new(x, y, right - x, bottom - y)
} else {
Rect::empty()
}
}
pub fn intersects(&self, other: &Rect) -> bool {
!self.intersection(other).is_empty()
}
pub fn union(&self, other: &Rect) -> Rect {
if self.is_empty() {
return *other;
}
if other.is_empty() {
return *self;
}
let x = self.x.min(other.x);
let y = self.y.min(other.y);
let right = self.right().max(other.right());
let bottom = self.bottom().max(other.bottom());
Rect::new(x, y, right - x, bottom - y)
}
pub fn clip_to(&self, bounds: &Rect) -> Rect {
self.intersection(bounds)
}
pub fn expand(&self, amount: u16) -> Rect {
Rect::new(
self.x.saturating_sub(amount),
self.y.saturating_sub(amount),
self.width.saturating_add(amount * 2),
self.height.saturating_add(amount * 2),
)
}
pub fn contract(&self, amount: u16) -> Rect {
if amount * 2 >= self.width || amount * 2 >= self.height {
Rect::empty()
} else {
Rect::new(
self.x + amount,
self.y + amount,
self.width - amount * 2,
self.height - amount * 2,
)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rect_edges() {
let rect = Rect::new(10, 20, 30, 40);
assert_eq!(rect.right(), 40);
assert_eq!(rect.bottom(), 60);
}
#[test]
fn test_contains_point() {
let rect = Rect::new(10, 10, 20, 20);
assert!(rect.contains_point(10, 10)); assert!(rect.contains_point(29, 29)); assert!(!rect.contains_point(30, 30)); assert!(!rect.contains_point(9, 15)); }
#[test]
fn test_intersection() {
let rect1 = Rect::new(10, 10, 20, 20);
let rect2 = Rect::new(20, 20, 20, 20);
let result = rect1.intersection(&rect2);
assert_eq!(result, Rect::new(20, 20, 10, 10));
let rect3 = Rect::new(50, 50, 10, 10);
assert!(rect1.intersection(&rect3).is_empty());
}
#[test]
fn test_union() {
let rect1 = Rect::new(10, 10, 10, 10);
let rect2 = Rect::new(30, 30, 10, 10);
let result = rect1.union(&rect2);
assert_eq!(result, Rect::new(10, 10, 30, 30));
}
}