leftwm_layouts/geometry/rect.rs
1/// Represents a rectangle with a position ([`Rect::x`], [`Rect::y`])
2/// and dimensions ([`Rect::w`], [`Rect::h`]).
3///
4/// ## Demonstration
5/// ```txt
6/// (x/y)
7/// x-------. ^
8/// | | |
9/// | | | h
10/// | | |
11/// '-------' v
12/// <------->
13/// w
14/// ```
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct Rect {
17 /// X-Coordinate, can be negative
18 pub x: i32,
19
20 /// Y-Coordinate, can be negative
21 pub y: i32,
22
23 /// Width, can not be negative
24 pub w: u32,
25
26 /// Height, can not be negative
27 pub h: u32,
28}
29
30impl Rect {
31 /// Shorthand method to create a new [`Rect`] with
32 /// the provided `x`, `y`, `w`, and `h`.
33 pub fn new(x: i32, y: i32, w: u32, h: u32) -> Self {
34 Self { x, y, w, h }
35 }
36
37 /// Calculate the surface area of the [`Rect`]
38 pub fn surface_area(&self) -> u32 {
39 self.w * self.h
40 }
41
42 /// Get the coordinate at the center of the [`Rect`].
43 ///
44 /// The center coordinate is rounded to the nearest integer
45 /// and might not be at the exact center position.
46 pub fn center(&self) -> (i32, i32) {
47 let x = self.x + (self.w as f32 / 2.0).round() as i32;
48 let y = self.y + (self.h as f32 / 2.0).round() as i32;
49 (x, y)
50 }
51
52 /// Check whether a point is contained in a [`Rect`].
53 ///
54 /// The boundary counts as part of the [`Rect`].
55 pub fn contains(&self, point: (i32, i32)) -> bool {
56 self.x <= point.0
57 && point.0 <= self.x + self.w as i32
58 && self.y <= point.1
59 && point.1 <= self.y + self.h as i32
60 }
61
62 /// Get the top left corner point of the [`Rect`].
63 ///
64 /// ```txt
65 /// O---------+
66 /// | |
67 /// | |
68 /// | |
69 /// +---------+
70 /// ```
71 pub fn top_left_corner(&self) -> (i32, i32) {
72 (self.x, self.y)
73 }
74
75 /// Get the top right corner point of the [`Rect`].
76 ///
77 /// ```txt
78 /// +---------O
79 /// | |
80 /// | |
81 /// | |
82 /// +---------+
83 /// ```
84 pub fn top_right_corner(&self) -> (i32, i32) {
85 (self.x + self.w as i32, self.y)
86 }
87
88 /// Get the bottom right corner point of the [`Rect`].
89 ///
90 /// ```txt
91 /// +---------+
92 /// | |
93 /// | |
94 /// | |
95 /// +---------O
96 /// ```
97 pub fn bottom_right_corner(&self) -> (i32, i32) {
98 (self.x + self.w as i32, self.y + self.h as i32)
99 }
100
101 /// Get the bottom left corner point of the [`Rect`].
102 ///
103 /// ```txt
104 /// +---------+
105 /// | |
106 /// | |
107 /// | |
108 /// O---------+
109 /// ```
110 pub fn bottom_left_corner(&self) -> (i32, i32) {
111 (self.x, self.y + self.h as i32)
112 }
113
114 /// Get the top edge of the [`Rect`].
115 ///
116 /// ```txt
117 /// ^
118 /// |
119 /// V
120 /// +---------+
121 /// | |
122 /// | |
123 /// | |
124 /// +---------+
125 /// ```
126 pub fn top_edge(&self) -> i32 {
127 self.y
128 }
129
130 /// Get the right edge of the [`Rect`].
131 ///
132 /// ```txt
133 /// <--------->
134 /// +---------+
135 /// | |
136 /// | |
137 /// | |
138 /// +---------+
139 /// ```
140 pub fn right_edge(&self) -> i32 {
141 self.x + self.w as i32
142 }
143
144 /// Get the bottom edge of the [`Rect`].
145 ///
146 /// ```txt
147 /// +---------+ ^
148 /// | | |
149 /// | | |
150 /// | | |
151 /// +---------+ V
152 /// ```
153 pub fn bottom_edge(&self) -> i32 {
154 self.y + self.h as i32
155 }
156
157 /// Get the left edge of the [`Rect`].
158 ///
159 /// ```txt
160 /// <---> +---------+
161 /// | |
162 /// | |
163 /// | |
164 /// +---------+
165 /// ```
166 pub fn left_edge(&self) -> i32 {
167 self.x
168 }
169}
170
171impl Default for Rect {
172 fn default() -> Self {
173 Self {
174 x: 0,
175 y: 0,
176 w: 500,
177 h: 250,
178 }
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use super::Rect;
185
186 #[test]
187 fn surface_area_calculation() {
188 let rect = Rect::new(0, 0, 1920, 1080);
189 assert_eq!(rect.surface_area(), 2_073_600);
190 }
191
192 #[test]
193 fn center_calculation() {
194 let rect = Rect::new(0, 0, 1920, 1080);
195 assert_eq!(rect.center(), (960, 540));
196 }
197
198 #[test]
199 fn center_calculation_with_offset() {
200 let rect = Rect::new(200, 120, 1920, 1080);
201 assert_eq!(rect.center(), (1160, 660));
202 }
203
204 #[test]
205 fn center_calculation_with_negative_offset() {
206 let rect = Rect::new(-200, -120, 1920, 1080);
207 assert_eq!(rect.center(), (760, 420));
208 }
209
210 #[test]
211 fn center_calculation_at_rounded_position() {
212 let rect = Rect::new(100, 100, 387, 399);
213 assert_eq!(rect.center(), (294, 300));
214 }
215
216 #[test]
217 fn contains_boundary() {
218 let rect = Rect::new(100, 100, 400, 100);
219 assert!(rect.contains((100, 100)));
220 assert!(rect.contains((500, 100)));
221 assert!(rect.contains((500, 200)));
222 assert!(rect.contains((100, 200)));
223 }
224
225 #[test]
226 fn does_not_contain_points_outside_rect() {
227 let rect = Rect::new(100, 100, 400, 100);
228 assert!(!rect.contains((99, 100)));
229 assert!(!rect.contains((501, 100)));
230 assert!(!rect.contains((501, 200)));
231 assert!(!rect.contains((99, 200)));
232 assert!(!rect.contains((100, 99)));
233 assert!(!rect.contains((500, 99)));
234 assert!(!rect.contains((500, 201)));
235 assert!(!rect.contains((100, 201)));
236 }
237}