1use crate::{Point, Size};
4use std::fmt;
5use serde::{Serialize, Deserialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10pub struct Rect {
11 pub x: i32,
12 pub y: i32,
13 pub width: i32,
14 pub height: i32,
15}
16
17impl Rect {
18 pub fn new(x: i32, y: i32, width: i32, height: i32) -> Self {
20 Self { x, y, width, height }
21 }
22
23 pub fn from_points(tl: Point, br: Point) -> Self {
25 Self::new(
26 tl.x,
27 tl.y,
28 br.x - tl.x,
29 br.y - tl.y,
30 )
31 }
32
33 pub fn from_center_size(center: Point, size: Size) -> Self {
35 let half_width = size.width / 2;
36 let half_height = size.height / 2;
37 Self::new(
38 center.x - half_width,
39 center.y - half_height,
40 size.width,
41 size.height,
42 )
43 }
44
45 pub fn tl(&self) -> Point {
47 Point::new(self.x, self.y)
48 }
49
50 pub fn br(&self) -> Point {
52 Point::new(self.x + self.width, self.y + self.height)
53 }
54
55 pub fn center(&self) -> Point {
57 Point::new(
58 self.x + self.width / 2,
59 self.y + self.height / 2,
60 )
61 }
62
63 pub fn size(&self) -> Size {
65 Size::new(self.width, self.height)
66 }
67
68 pub fn area(&self) -> i32 {
70 self.width * self.height
71 }
72
73 pub fn is_empty(&self) -> bool {
75 self.width <= 0 || self.height <= 0
76 }
77
78 pub fn contains(&self, point: Point) -> bool {
80 point.x >= self.x &&
81 point.y >= self.y &&
82 point.x < self.x + self.width &&
83 point.y < self.y + self.height
84 }
85
86 pub fn contains_rect(&self, other: &Rect) -> bool {
88 self.x <= other.x &&
89 self.y <= other.y &&
90 self.x + self.width >= other.x + other.width &&
91 self.y + self.height >= other.y + other.height
92 }
93
94 pub fn intersect(&self, other: &Rect) -> Option<Rect> {
96 let x1 = self.x.max(other.x);
97 let y1 = self.y.max(other.y);
98 let x2 = (self.x + self.width).min(other.x + other.width);
99 let y2 = (self.y + self.height).min(other.y + other.height);
100
101 if x1 < x2 && y1 < y2 {
102 Some(Rect::new(x1, y1, x2 - x1, y2 - y1))
103 } else {
104 None
105 }
106 }
107
108 pub fn union(&self, other: &Rect) -> Rect {
110 let x1 = self.x.min(other.x);
111 let y1 = self.y.min(other.y);
112 let x2 = (self.x + self.width).max(other.x + other.width);
113 let y2 = (self.y + self.height).max(other.y + other.height);
114
115 Rect::new(x1, y1, x2 - x1, y2 - y1)
116 }
117
118 pub fn inflate(&self, dx: i32, dy: i32) -> Rect {
120 Rect::new(
121 self.x - dx,
122 self.y - dy,
123 self.width + 2 * dx,
124 self.height + 2 * dy,
125 )
126 }
127
128 pub fn translate(&self, dx: i32, dy: i32) -> Rect {
130 Rect::new(
131 self.x + dx,
132 self.y + dy,
133 self.width,
134 self.height,
135 )
136 }
137}
138
139impl Default for Rect {
140 fn default() -> Self {
141 Self::new(0, 0, 0, 0)
142 }
143}
144
145impl fmt::Display for Rect {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 write!(f, "[{}, {}, {}x{}]", self.x, self.y, self.width, self.height)
148 }
149}
150
151impl From<(i32, i32, i32, i32)> for Rect {
152 fn from((x, y, width, height): (i32, i32, i32, i32)) -> Self {
153 Self::new(x, y, width, height)
154 }
155}
156
157impl From<Rect> for (i32, i32, i32, i32) {
158 fn from(rect: Rect) -> Self {
159 (rect.x, rect.y, rect.width, rect.height)
160 }
161}
162
163#[derive(Debug, Clone, Copy, PartialEq)]
165#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
166pub struct Rect2f {
167 pub x: f32,
168 pub y: f32,
169 pub width: f32,
170 pub height: f32,
171}
172
173impl Rect2f {
174 pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
176 Self { x, y, width, height }
177 }
178
179 pub fn area(&self) -> f32 {
181 self.width * self.height
182 }
183
184 pub fn is_empty(&self) -> bool {
186 self.width <= 0.0 || self.height <= 0.0
187 }
188
189 pub fn contains(&self, x: f32, y: f32) -> bool {
191 x >= self.x &&
192 y >= self.y &&
193 x < self.x + self.width &&
194 y < self.y + self.height
195 }
196
197 pub fn intersect(&self, other: &Rect2f) -> Option<Rect2f> {
199 let x1 = self.x.max(other.x);
200 let y1 = self.y.max(other.y);
201 let x2 = (self.x + self.width).min(other.x + other.width);
202 let y2 = (self.y + self.height).min(other.y + other.height);
203
204 if x1 < x2 && y1 < y2 {
205 Some(Rect2f::new(x1, y1, x2 - x1, y2 - y1))
206 } else {
207 None
208 }
209 }
210}
211
212impl Default for Rect2f {
213 fn default() -> Self {
214 Self::new(0.0, 0.0, 0.0, 0.0)
215 }
216}
217
218impl fmt::Display for Rect2f {
219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220 write!(f, "[{:.2}, {:.2}, {:.2}x{:.2}]", self.x, self.y, self.width, self.height)
221 }
222}
223
224impl From<Rect> for Rect2f {
225 fn from(rect: Rect) -> Self {
226 Self::new(
227 rect.x as f32,
228 rect.y as f32,
229 rect.width as f32,
230 rect.height as f32,
231 )
232 }
233}
234
235#[derive(Debug, Clone, Copy, PartialEq)]
237#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
238pub struct Rect2d {
239 pub x: f64,
240 pub y: f64,
241 pub width: f64,
242 pub height: f64,
243}
244
245impl Rect2d {
246 pub fn new(x: f64, y: f64, width: f64, height: f64) -> Self {
248 Self { x, y, width, height }
249 }
250
251 pub fn area(&self) -> f64 {
253 self.width * self.height
254 }
255
256 pub fn is_empty(&self) -> bool {
258 self.width <= 0.0 || self.height <= 0.0
259 }
260
261 pub fn contains(&self, x: f64, y: f64) -> bool {
263 x >= self.x &&
264 y >= self.y &&
265 x < self.x + self.width &&
266 y < self.y + self.height
267 }
268}
269
270impl Default for Rect2d {
271 fn default() -> Self {
272 Self::new(0.0, 0.0, 0.0, 0.0)
273 }
274}
275
276impl From<Rect2f> for Rect2d {
277 fn from(rect: Rect2f) -> Self {
278 Self::new(
279 rect.x as f64,
280 rect.y as f64,
281 rect.width as f64,
282 rect.height as f64,
283 )
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
292 fn test_rect_creation() {
293 let rect = Rect::new(10, 20, 100, 200);
294 assert_eq!(rect.x, 10);
295 assert_eq!(rect.y, 20);
296 assert_eq!(rect.width, 100);
297 assert_eq!(rect.height, 200);
298 assert_eq!(rect.area(), 20000);
299 }
300
301 #[test]
302 fn test_rect_points() {
303 let rect = Rect::new(10, 20, 100, 200);
304 assert_eq!(rect.tl(), Point::new(10, 20));
305 assert_eq!(rect.br(), Point::new(110, 220));
306 assert_eq!(rect.center(), Point::new(60, 120));
307 }
308
309 #[test]
310 fn test_rect_contains() {
311 let rect = Rect::new(10, 20, 100, 200);
312 assert!(rect.contains(Point::new(50, 50)));
313 assert!(!rect.contains(Point::new(5, 5)));
314 assert!(!rect.contains(Point::new(150, 150)));
315 }
316
317 #[test]
318 fn test_rect_intersection() {
319 let rect1 = Rect::new(0, 0, 100, 100);
320 let rect2 = Rect::new(50, 50, 100, 100);
321
322 let intersection = rect1.intersect(&rect2).unwrap();
323 assert_eq!(intersection, Rect::new(50, 50, 50, 50));
324 }
325
326 #[test]
327 fn test_rect_union() {
328 let rect1 = Rect::new(0, 0, 100, 100);
329 let rect2 = Rect::new(50, 50, 100, 100);
330
331 let union = rect1.union(&rect2);
332 assert_eq!(union, Rect::new(0, 0, 150, 150));
333 }
334
335 #[test]
336 fn test_rect_inflate() {
337 let rect = Rect::new(10, 10, 100, 100);
338 let inflated = rect.inflate(5, 5);
339 assert_eq!(inflated, Rect::new(5, 5, 110, 110));
340 }
341
342 #[test]
343 fn test_rect_translate() {
344 let rect = Rect::new(10, 10, 100, 100);
345 let translated = rect.translate(5, 5);
346 assert_eq!(translated, Rect::new(15, 15, 100, 100));
347 }
348}