1#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
5pub struct Position {
6 pub x: u16,
8 pub y: u16,
10}
11
12impl Position {
13 pub const fn new(x: u16, y: u16) -> Self {
15 Self { x, y }
16 }
17}
18
19impl From<(u16, u16)> for Position {
20 fn from((x, y): (u16, u16)) -> Self {
21 Self { x, y }
22 }
23}
24
25#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
27pub struct Size {
28 pub width: u16,
30 pub height: u16,
32}
33
34impl Size {
35 pub const fn new(width: u16, height: u16) -> Self {
37 Self { width, height }
38 }
39
40 pub const fn area(self) -> u32 {
42 self.width as u32 * self.height as u32
43 }
44
45 pub const fn is_empty(self) -> bool {
47 self.width == 0 || self.height == 0
48 }
49}
50
51impl From<(u16, u16)> for Size {
52 fn from((width, height): (u16, u16)) -> Self {
53 Self { width, height }
54 }
55}
56
57#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
59pub struct Rect {
60 pub position: Position,
62 pub size: Size,
64}
65
66impl Rect {
67 pub const fn new(x: u16, y: u16, width: u16, height: u16) -> Self {
69 Self {
70 position: Position::new(x, y),
71 size: Size::new(width, height),
72 }
73 }
74
75 pub const fn right(self) -> u16 {
77 self.position.x.saturating_add(self.size.width)
78 }
79
80 pub const fn bottom(self) -> u16 {
82 self.position.y.saturating_add(self.size.height)
83 }
84
85 pub const fn area(self) -> u32 {
87 self.size.area()
88 }
89
90 pub const fn is_empty(self) -> bool {
92 self.size.is_empty()
93 }
94
95 pub const fn contains(self, pos: Position) -> bool {
97 pos.x >= self.position.x
98 && pos.x < self.right()
99 && pos.y >= self.position.y
100 && pos.y < self.bottom()
101 }
102
103 pub const fn intersects(self, other: &Rect) -> bool {
105 self.position.x < other.right()
106 && self.right() > other.position.x
107 && self.position.y < other.bottom()
108 && self.bottom() > other.position.y
109 }
110
111 pub fn intersection(self, other: &Rect) -> Option<Rect> {
113 if !self.intersects(other) {
114 return None;
115 }
116 let x = self.position.x.max(other.position.x);
117 let y = self.position.y.max(other.position.y);
118 let right = self.right().min(other.right());
119 let bottom = self.bottom().min(other.bottom());
120 Some(Rect::new(x, y, right - x, bottom - y))
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn position_from_tuple() {
130 let pos: Position = (5, 10).into();
131 assert_eq!(pos, Position::new(5, 10));
132 }
133
134 #[test]
135 fn size_from_tuple() {
136 let sz: Size = (80, 24).into();
137 assert_eq!(sz, Size::new(80, 24));
138 }
139
140 #[test]
141 fn size_area() {
142 assert_eq!(Size::new(10, 5).area(), 50);
143 }
144
145 #[test]
146 fn size_empty() {
147 assert!(Size::new(0, 10).is_empty());
148 assert!(Size::new(10, 0).is_empty());
149 assert!(!Size::new(1, 1).is_empty());
150 }
151
152 #[test]
153 fn rect_right_bottom() {
154 let r = Rect::new(5, 10, 20, 15);
155 assert_eq!(r.right(), 25);
156 assert_eq!(r.bottom(), 25);
157 }
158
159 #[test]
160 fn rect_contains() {
161 let r = Rect::new(10, 10, 20, 20);
162 assert!(r.contains(Position::new(10, 10)));
163 assert!(r.contains(Position::new(29, 29)));
164 assert!(!r.contains(Position::new(30, 30)));
165 assert!(!r.contains(Position::new(9, 10)));
166 }
167
168 #[test]
169 fn rect_intersects() {
170 let a = Rect::new(0, 0, 10, 10);
171 let b = Rect::new(5, 5, 10, 10);
172 let c = Rect::new(20, 20, 5, 5);
173 assert!(a.intersects(&b));
174 assert!(b.intersects(&a));
175 assert!(!a.intersects(&c));
176 }
177
178 #[test]
179 fn rect_intersection() {
180 let a = Rect::new(0, 0, 10, 10);
181 let b = Rect::new(5, 5, 10, 10);
182 let i = a.intersection(&b);
183 assert_eq!(i, Some(Rect::new(5, 5, 5, 5)));
184 }
185
186 #[test]
187 fn rect_no_intersection() {
188 let a = Rect::new(0, 0, 5, 5);
189 let b = Rect::new(10, 10, 5, 5);
190 assert_eq!(a.intersection(&b), None);
191 }
192
193 #[test]
194 fn rect_empty() {
195 assert!(Rect::new(0, 0, 0, 5).is_empty());
196 assert!(!Rect::new(0, 0, 1, 1).is_empty());
197 }
198
199 #[test]
200 fn rect_area() {
201 assert_eq!(Rect::new(0, 0, 10, 5).area(), 50);
202 }
203
204 #[test]
205 fn rect_saturating_overflow() {
206 let r = Rect::new(u16::MAX, u16::MAX, 10, 10);
207 assert_eq!(r.right(), u16::MAX);
208 assert_eq!(r.bottom(), u16::MAX);
209 }
210}