Skip to main content

niko/
rectangle.rs

1use crate::Point;
2
3#[derive(Debug, Clone)]
4pub struct Rectangle {
5    pub x: i32,
6    pub y: i32,
7    pub width: i32,
8    pub height: i32,
9}
10
11impl Rectangle {
12    /// creates a new rectangle
13    pub fn new(x: i32, y: i32, width: i32, height: i32) -> Self {
14        Self {
15            x,
16            y,
17            width,
18            height,
19        }
20    }
21
22    /// returns if the other rectangle overlaps with this one
23    pub fn overlaps_rectangle(&self, other: &Self) -> bool {
24        self.x < (other.x + other.width) &&
25        (self.x + self.width) > other.x &&
26
27        self.y < (other.y + other.height) &&
28        (self.y + self.height) > other.y
29    }
30
31    /// returns if the other rectangle is fully contained by this one
32    pub fn contains_rectangle(&self, other: &Self) -> bool {
33        other.x >= self.x &&
34        other.x + other.width <= self.x + self.width &&
35        other.y >= self.y &&
36        other.y + other.height <= self.y + self.height
37    }
38
39    /// returns if the point is inside this rectangle
40    pub fn contains_point(&self, point: Point) -> bool {
41        point.x >= self.x && point.x <= (self.x + self.width) &&
42        point.y >= self.y && point.y <= (self.y + self.height)
43    }
44
45    /// creates a new rectangle and inflates it in all directions
46    pub fn inflate(&self, horizontal: i32, vertical: i32) -> Self {
47        Self::new(
48            self.x - horizontal,
49            self.y - vertical,
50            self.width + horizontal,
51            self.height + vertical,
52        )
53    }
54
55    /// creates a new rectangle and moves it
56    pub fn translate(&self, x: i32, y: i32) -> Self {
57        Self::new(
58            self.x + x,
59            self.y + y,
60            self.width,
61            self.height,
62        )
63    }
64
65    /// creates a new rectangle that contains both rectangles
66    pub fn union(&self, other: Self) -> Self {
67        let x = self.x.min(other.x);
68        let y = self.y.min(other.y);
69        let width = (self.x + self.width).max(other.x + other.width) - x;
70        let height = (self.y + self.height).max(other.y + other.height) - y;
71        Self::new(
72            x,
73            y,
74            width,
75            height,
76        )
77    }
78
79    pub(crate) fn to_rendering_position(&self, bounds: &Self) -> (f32, f32, f32, f32) {
80        // TODO assume bounds x and y is 0 for now
81
82        let left = self.x as f32 / bounds.width as f32;
83        let right = (self.x + self.width) as f32 / bounds.width as f32;
84
85        
86        let top = self.y as f32 / bounds.height as f32;
87        let bottom = (self.y + self.height) as f32 / bounds.height as f32;
88
89        (left, right, top, bottom)
90    }
91}
92
93impl Into<(i32, i32, i32, i32)> for Rectangle {
94    fn into(self) -> (i32, i32, i32, i32) {
95        (self.x, self.y, self.width, self.height)
96    }
97}
98
99impl From<(i32, i32, i32, i32)> for Rectangle {
100    fn from(values: (i32, i32, i32, i32)) -> Self {
101        Self::new(values.0, values.1, values.2, values.3)
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108    
109    #[test]
110    fn it_works() {
111        let a = Rectangle::new(0, 0, 3, 3);
112        let b = Rectangle::new(1, 1, 1, 1);
113
114        assert!(a.overlaps_rectangle(&b));
115        assert!(b.overlaps_rectangle(&a));
116
117        assert!(a.contains_rectangle(&b));
118        assert!(!b.contains_rectangle(&a));
119    }
120}