1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
13pub struct Rect {
14 pub x: u32,
16 pub y: u32,
18 pub width: u32,
20 pub height: u32,
22}
23
24impl Rect {
25 #[inline]
27 pub const fn new(x: u32, y: u32, width: u32, height: u32) -> Self {
28 Self {
29 x,
30 y,
31 width,
32 height,
33 }
34 }
35
36 #[inline]
38 pub const fn area(&self) -> u32 {
39 self.width * self.height
40 }
41
42 #[inline]
46 pub const fn right(&self) -> u32 {
47 self.x + self.width
48 }
49
50 #[inline]
54 pub const fn bottom(&self) -> u32 {
55 self.y + self.height
56 }
57
58 #[inline]
60 pub const fn is_empty(&self) -> bool {
61 self.width == 0 || self.height == 0
62 }
63
64 #[inline]
78 pub fn centered(&self, inner_w: u32, inner_h: u32) -> Rect {
79 let w = inner_w.min(self.width);
80 let h = inner_h.min(self.height);
81 let x = self.x + (self.width.saturating_sub(w)) / 2;
82 let y = self.y + (self.height.saturating_sub(h)) / 2;
83 Rect {
84 x,
85 y,
86 width: w,
87 height: h,
88 }
89 }
90
91 #[inline]
105 pub fn union(&self, other: Rect) -> Rect {
106 let x = self.x.min(other.x);
107 let y = self.y.min(other.y);
108 let right = self.right().max(other.right());
109 let bottom = self.bottom().max(other.bottom());
110 Rect {
111 x,
112 y,
113 width: right - x,
114 height: bottom - y,
115 }
116 }
117
118 #[inline]
132 pub fn intersection(&self, other: Rect) -> Option<Rect> {
133 let x = self.x.max(other.x);
134 let y = self.y.max(other.y);
135 let right = self.right().min(other.right());
136 let bottom = self.bottom().min(other.bottom());
137
138 if x < right && y < bottom {
139 Some(Rect {
140 x,
141 y,
142 width: right - x,
143 height: bottom - y,
144 })
145 } else {
146 None
147 }
148 }
149
150 #[inline]
164 pub fn contains(&self, x: u32, y: u32) -> bool {
165 x >= self.x && x < self.right() && y >= self.y && y < self.bottom()
166 }
167
168 #[inline]
180 pub fn rows(&self) -> impl Iterator<Item = u32> {
181 self.y..self.bottom()
182 }
183
184 #[inline]
197 pub fn positions(&self) -> impl Iterator<Item = (u32, u32)> {
198 let x_start = self.x;
199 let x_end = self.right();
200 let y_start = self.y;
201 let y_end = self.bottom();
202
203 (y_start..y_end).flat_map(move |y| (x_start..x_end).map(move |x| (x, y)))
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn test_centered_normal() {
213 let outer = Rect::new(0, 0, 10, 10);
214 let inner = outer.centered(4, 4);
215 assert_eq!(inner, Rect::new(3, 3, 4, 4));
216 }
217
218 #[test]
219 fn test_centered_larger_than_self() {
220 let outer = Rect::new(0, 0, 10, 10);
221 let inner = outer.centered(20, 20);
222 assert_eq!(inner, Rect::new(0, 0, 10, 10));
223 }
224
225 #[test]
226 fn test_centered_zero_size() {
227 let outer = Rect::new(5, 5, 10, 10);
228 let inner = outer.centered(0, 0);
229 assert_eq!(inner, Rect::new(10, 10, 0, 0));
230 }
231
232 #[test]
233 fn test_centered_offset() {
234 let outer = Rect::new(10, 20, 20, 20);
235 let inner = outer.centered(10, 10);
236 assert_eq!(inner, Rect::new(15, 25, 10, 10));
237 }
238
239 #[test]
240 fn test_union_overlapping() {
241 let r1 = Rect::new(0, 0, 5, 5);
242 let r2 = Rect::new(3, 3, 5, 5);
243 let union = r1.union(r2);
244 assert_eq!(union, Rect::new(0, 0, 8, 8));
245 }
246
247 #[test]
248 fn test_union_non_overlapping() {
249 let r1 = Rect::new(0, 0, 5, 5);
250 let r2 = Rect::new(10, 10, 5, 5);
251 let union = r1.union(r2);
252 assert_eq!(union, Rect::new(0, 0, 15, 15));
253 }
254
255 #[test]
256 fn test_union_same_rect() {
257 let r = Rect::new(5, 5, 10, 10);
258 let union = r.union(r);
259 assert_eq!(union, r);
260 }
261
262 #[test]
263 fn test_intersection_overlapping() {
264 let r1 = Rect::new(0, 0, 5, 5);
265 let r2 = Rect::new(3, 3, 5, 5);
266 let overlap = r1.intersection(r2);
267 assert_eq!(overlap, Some(Rect::new(3, 3, 2, 2)));
268 }
269
270 #[test]
271 fn test_intersection_non_overlapping() {
272 let r1 = Rect::new(0, 0, 5, 5);
273 let r2 = Rect::new(10, 10, 5, 5);
274 let overlap = r1.intersection(r2);
275 assert_eq!(overlap, None);
276 }
277
278 #[test]
279 fn test_intersection_adjacent() {
280 let r1 = Rect::new(0, 0, 5, 5);
281 let r2 = Rect::new(5, 0, 5, 5);
282 let overlap = r1.intersection(r2);
283 assert_eq!(overlap, None);
284 }
285
286 #[test]
287 fn test_intersection_same_rect() {
288 let r = Rect::new(5, 5, 10, 10);
289 let overlap = r.intersection(r);
290 assert_eq!(overlap, Some(r));
291 }
292
293 #[test]
294 fn test_contains_inside() {
295 let r = Rect::new(5, 5, 10, 10);
296 assert!(r.contains(5, 5));
297 assert!(r.contains(10, 10));
298 assert!(r.contains(14, 14));
299 }
300
301 #[test]
302 fn test_contains_outside() {
303 let r = Rect::new(5, 5, 10, 10);
304 assert!(!r.contains(4, 5));
305 assert!(!r.contains(5, 4));
306 assert!(!r.contains(15, 15));
307 assert!(!r.contains(15, 10));
308 }
309
310 #[test]
311 fn test_contains_on_edge() {
312 let r = Rect::new(5, 5, 10, 10);
313 assert!(r.contains(5, 5)); assert!(!r.contains(15, 5)); assert!(!r.contains(5, 15)); }
317
318 #[test]
319 fn test_rows_correct_range() {
320 let r = Rect::new(0, 2, 5, 3);
321 let rows: Vec<u32> = r.rows().collect();
322 assert_eq!(rows, vec![2, 3, 4]);
323 }
324
325 #[test]
326 fn test_rows_single_row() {
327 let r = Rect::new(0, 5, 10, 1);
328 let rows: Vec<u32> = r.rows().collect();
329 assert_eq!(rows, vec![5]);
330 }
331
332 #[test]
333 fn test_rows_empty() {
334 let r = Rect::new(0, 5, 10, 0);
335 let rows: Vec<u32> = r.rows().collect();
336 assert!(rows.is_empty());
337 }
338
339 #[test]
340 fn test_positions_correct_count() {
341 let r = Rect::new(0, 0, 3, 2);
342 let positions: Vec<(u32, u32)> = r.positions().collect();
343 assert_eq!(positions.len(), 6);
344 }
345
346 #[test]
347 fn test_positions_order() {
348 let r = Rect::new(0, 0, 2, 2);
349 let positions: Vec<(u32, u32)> = r.positions().collect();
350 assert_eq!(positions, vec![(0, 0), (1, 0), (0, 1), (1, 1)]);
351 }
352
353 #[test]
354 fn test_positions_offset() {
355 let r = Rect::new(5, 3, 2, 2);
356 let positions: Vec<(u32, u32)> = r.positions().collect();
357 assert_eq!(positions, vec![(5, 3), (6, 3), (5, 4), (6, 4)]);
358 }
359
360 #[test]
361 fn test_positions_empty() {
362 let r = Rect::new(0, 0, 0, 5);
363 let positions: Vec<(u32, u32)> = r.positions().collect();
364 assert!(positions.is_empty());
365 }
366}