Skip to main content

macroquad_ply/math/
rect.rs

1use glam::*;
2
3/// A 2D rectangle, defined by its top-left corner, width and height.
4#[derive(Clone, Copy, Debug, Default, PartialEq)]
5pub struct Rect {
6    /// The x-coordinate of the top-left corner.
7    pub x: f32,
8    /// The y-coordinate of the top-left corner.
9    pub y: f32,
10    /// The width of the `Rect`, going to the right.
11    pub w: f32,
12    /// The height of the `Rect`, going down.
13    pub h: f32,
14}
15
16impl Rect {
17    /// Creates a new rectangle from its top-left corner, width and height.
18    ///
19    /// # Arguments:
20    ///   * `x` - x-coordinate of the top-left corner.
21    ///   * `y` - y-coordinate of the top-left corner.
22    ///   * `w` - width of the `Rect`, going to the right.
23    ///   * `h` - height of the `Rect`, going down.
24    pub const fn new(x: f32, y: f32, w: f32, h: f32) -> Rect {
25        Rect { x, y, w, h }
26    }
27
28    /// Returns the top-left corner of the `Rect`.
29    pub const fn point(&self) -> Vec2 {
30        vec2(self.x, self.y)
31    }
32
33    /// Returns the size (width and height) of the `Rect`.
34    pub const fn size(&self) -> Vec2 {
35        vec2(self.w, self.h)
36    }
37
38    /// Returns the center position of the `Rect`.
39    pub const fn center(&self) -> Vec2 {
40        vec2(self.x + self.w * 0.5f32, self.y + self.h * 0.5f32)
41    }
42
43    /// Returns the left edge of the `Rect`.
44    pub const fn left(&self) -> f32 {
45        self.x
46    }
47
48    /// Returns the right edge of the `Rect`.
49    pub const fn right(&self) -> f32 {
50        self.x + self.w
51    }
52
53    /// Returns the top edge of the `Rect`.
54    pub const fn top(&self) -> f32 {
55        self.y
56    }
57
58    /// Returns the bottom edge of the `Rect`.
59    pub const fn bottom(&self) -> f32 {
60        self.y + self.h
61    }
62
63    /// Moves the `Rect`'s origin to (x, y).
64    pub const fn move_to(&mut self, destination: Vec2) {
65        self.x = destination.x;
66        self.y = destination.y;
67    }
68
69    /// Scales the `Rect` by a factor of (sx, sy),
70    /// growing towards the bottom-left.
71    pub const fn scale(&mut self, sx: f32, sy: f32) {
72        self.w *= sx;
73        self.h *= sy;
74    }
75
76    /// Checks whether the `Rect` contains a `Point`.
77    pub const fn contains(&self, point: Vec2) -> bool {
78        point.x >= self.left()
79            && point.x <= self.right()
80            && point.y <= self.bottom()
81            && point.y >= self.top()
82    }
83
84    /// Checks whether the `Rect` overlaps another `Rect`.
85    pub const fn overlaps(&self, other: &Rect) -> bool {
86        self.left() <= other.right()
87            && self.right() >= other.left()
88            && self.top() <= other.bottom()
89            && self.bottom() >= other.top()
90    }
91
92    /// Returns a new `Rect` that includes all points of these two `Rect`s.
93    pub const fn combine_with(self, other: Rect) -> Rect {
94        let x = f32::min(self.x, other.x);
95        let y = f32::min(self.y, other.y);
96        let w = f32::max(self.right(), other.right()) - x;
97        let h = f32::max(self.bottom(), other.bottom()) - y;
98        Rect { x, y, w, h }
99    }
100
101    /// Returns an intersection rect if there is any intersection.
102    pub const fn intersect(&self, other: Rect) -> Option<Rect> {
103        let left = self.x.max(other.x);
104        let top = self.y.max(other.y);
105        let right = self.right().min(other.right());
106        let bottom = self.bottom().min(other.bottom());
107
108        if right < left || bottom < top {
109            return None;
110        }
111
112        Some(Rect {
113            x: left,
114            y: top,
115            w: right - left,
116            h: bottom - top,
117        })
118    }
119
120    /// Translate rect origin by `offset` vector.
121    pub const fn offset(self, offset: Vec2) -> Rect {
122        Rect::new(self.x + offset.x, self.y + offset.y, self.w, self.h)
123    }
124
125    /// Returns a normalized rect where width and height are guaranteed to be positive.
126    pub fn normalized(&self) -> Rect {
127        let x = self.x.min(self.x + self.w);
128        let y = self.y.min(self.y + self.h);
129        let w = self.w.abs();
130        let h = self.h.abs();
131        Rect { x, y, w, h }
132    }
133}
134
135#[derive(Clone, Copy, Debug, Default, PartialEq)]
136pub struct RectOffset {
137    pub left: f32,
138    pub right: f32,
139    pub bottom: f32,
140    pub top: f32,
141}
142
143impl RectOffset {
144    pub const fn new(left: f32, right: f32, top: f32, bottom: f32) -> RectOffset {
145        RectOffset {
146            left,
147            right,
148            top,
149            bottom,
150        }
151    }
152}
153
154#[test]
155fn rect_contains_border() {
156    let rect = Rect::new(1.0, 1.0, 2.0, 2.0);
157    assert!(rect.contains(vec2(1.0, 1.0)));
158    assert!(rect.contains(vec2(3.0, 1.0)));
159    assert!(rect.contains(vec2(1.0, 3.0)));
160    assert!(rect.contains(vec2(3.0, 3.0)));
161    assert!(rect.contains(vec2(2.0, 2.0)));
162}