1use crate::Padding;
2
3#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
4pub struct Rect {
5 pub x: u16,
6 pub y: u16,
7 pub width: u16,
8 pub height: u16,
9}
10
11impl Rect {
12 pub const fn new(x: u16, y: u16, width: u16, height: u16) -> Self {
13 Self {
14 x,
15 y,
16 width,
17 height,
18 }
19 }
20
21 pub const fn right(self) -> u16 {
22 self.x.saturating_add(self.width)
23 }
24
25 pub const fn bottom(self) -> u16 {
26 self.y.saturating_add(self.height)
27 }
28
29 pub const fn is_empty(self) -> bool {
30 self.width == 0 || self.height == 0
31 }
32
33 pub const fn intersects(self, other: Self) -> bool {
34 self.x < other.right()
35 && other.x < self.right()
36 && self.y < other.bottom()
37 && other.y < self.bottom()
38 }
39
40 pub fn intersection(self, other: Self) -> Option<Self> {
41 if !self.intersects(other) {
42 return None;
43 }
44
45 let x = self.x.max(other.x);
46 let y = self.y.max(other.y);
47 let right = self.right().min(other.right());
48 let bottom = self.bottom().min(other.bottom());
49
50 Some(Self::new(
51 x,
52 y,
53 right.saturating_sub(x),
54 bottom.saturating_sub(y),
55 ))
56 }
57
58 pub const fn contains(self, other: Self) -> bool {
59 self.x <= other.x
60 && self.y <= other.y
61 && self.right() >= other.right()
62 && self.bottom() >= other.bottom()
63 }
64
65 pub const fn can_merge_rect(self, other: Self) -> bool {
66 self.intersects(other)
67 || (self.x == other.x
68 && self.width == other.width
69 && (self.bottom() == other.y || other.bottom() == self.y))
70 || (self.y == other.y
71 && self.height == other.height
72 && (self.right() == other.x || other.right() == self.x))
73 }
74
75 pub fn union(self, other: Self) -> Self {
76 let x = self.x.min(other.x);
77 let y = self.y.min(other.y);
78 let right = self.right().max(other.right());
79 let bottom = self.bottom().max(other.bottom());
80 Self::new(x, y, right.saturating_sub(x), bottom.saturating_sub(y))
81 }
82
83 pub fn shrink(self, amount: u16) -> Self {
84 let next_x = self.x.saturating_add(amount.min(self.width));
85 let next_y = self.y.saturating_add(amount.min(self.height));
86
87 if self.width <= amount.saturating_mul(2) || self.height <= amount.saturating_mul(2) {
88 return Self::new(next_x, next_y, 0, 0);
89 }
90
91 Self::new(
92 self.x.saturating_add(amount),
93 self.y.saturating_add(amount),
94 self.width.saturating_sub(amount.saturating_mul(2)),
95 self.height.saturating_sub(amount.saturating_mul(2)),
96 )
97 }
98
99 pub fn inset(self, padding: Padding) -> Self {
100 let x = self.x.saturating_add(padding.left.min(self.width));
101 let y = self.y.saturating_add(padding.top.min(self.height));
102 let horizontal = padding.left.saturating_add(padding.right);
103 let vertical = padding.top.saturating_add(padding.bottom);
104
105 Self::new(
106 x,
107 y,
108 self.width.saturating_sub(horizontal),
109 self.height.saturating_sub(vertical),
110 )
111 }
112}