grim_rs/
geometry.rs

1use std::fmt;
2
3#[derive(Debug, Clone, Copy, PartialEq)]
4pub struct Box {
5    x: i32,
6    y: i32,
7    width: i32,
8    height: i32,
9}
10
11impl Box {
12    pub fn new(x: i32, y: i32, width: i32, height: i32) -> Self {
13        Self {
14            x,
15            y,
16            width,
17            height,
18        }
19    }
20
21    pub fn x(&self) -> i32 {
22        self.x
23    }
24
25    pub fn y(&self) -> i32 {
26        self.y
27    }
28
29    pub fn width(&self) -> i32 {
30        self.width
31    }
32
33    pub fn height(&self) -> i32 {
34        self.height
35    }
36
37    pub fn is_empty(&self) -> bool {
38        self.width <= 0 || self.height <= 0
39    }
40
41    pub fn intersects(&self, other: &Box) -> bool {
42        if self.is_empty() || other.is_empty() {
43            return false;
44        }
45
46        let x1 = self.x.max(other.x);
47        let y1 = self.y.max(other.y);
48        let x2 = (self.x + self.width).min(other.x + other.width);
49        let y2 = (self.y + self.height).min(other.y + other.height);
50
51        x2 > x1 && y2 > y1
52    }
53
54    pub fn intersection(&self, other: &Box) -> Option<Box> {
55        if !self.intersects(other) {
56            return None;
57        }
58
59        let x1 = self.x.max(other.x);
60        let y1 = self.y.max(other.y);
61        let x2 = (self.x + self.width).min(other.x + other.width);
62        let y2 = (self.y + self.height).min(other.y + other.height);
63
64        Some(Box::new(x1, y1, x2 - x1, y2 - y1))
65    }
66}
67
68impl fmt::Display for Box {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        write!(f, "{},{} {}x{}", self.x, self.y, self.width, self.height)
71    }
72}
73
74impl std::str::FromStr for Box {
75    type Err = crate::Error;
76
77    fn from_str(s: &str) -> Result<Self, Self::Err> {
78        let parts: Vec<&str> = s.split(' ').collect();
79        if parts.len() != 2 {
80            return Err(crate::Error::InvalidGeometry(s.to_string()));
81        }
82
83        let xy: Vec<&str> = parts[0].split(',').collect();
84        let wh: Vec<&str> = parts[1].split('x').collect();
85
86        if xy.len() != 2 || wh.len() != 2 {
87            return Err(crate::Error::InvalidGeometry(s.to_string()));
88        }
89
90        let x = xy[0]
91            .parse()
92            .map_err(|_| crate::Error::InvalidGeometry(s.to_string()))?;
93        let y = xy[1]
94            .parse()
95            .map_err(|_| crate::Error::InvalidGeometry(s.to_string()))?;
96        let width = wh[0]
97            .parse()
98            .map_err(|_| crate::Error::InvalidGeometry(s.to_string()))?;
99        let height = wh[1]
100            .parse()
101            .map_err(|_| crate::Error::InvalidGeometry(s.to_string()))?;
102
103        Ok(Box::new(x, y, width, height))
104    }
105}