1use super::{Point, Size};
2use std::ops::{Add, AddAssign, Div, Sub};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
10pub struct Rect<T: Copy> {
11 left: T,
12 top: T,
13 right: T,
14 bottom: T,
15}
16
17impl<T: Copy + Ord> Rect<T> {
18 #[cfg(not(feature = "perf"))]
26 #[inline]
27 pub fn new(left: T, top: T, right: T, bottom: T) -> Self {
28 let mut res = Self {
29 left,
30 top,
31 right,
32 bottom,
33 };
34 res.normalize();
35 res
36 }
37}
38
39impl<T: Copy> Rect<T> {
40 #[cfg(feature = "perf")]
48 #[inline]
49 pub fn new(left: T, top: T, right: T, bottom: T) -> Self {
50 Self {
51 left,
52 top,
53 right,
54 bottom,
55 }
56 }
57
58 #[inline]
64 pub fn left(&self) -> T {
65 self.left
66 }
67
68 #[inline]
74 pub fn top(&self) -> T {
75 self.top
76 }
77
78 #[inline]
84 pub fn right(&self) -> T {
85 self.right
86 }
87
88 #[inline]
94 pub fn bottom(&self) -> T {
95 self.bottom
96 }
97
98 #[inline]
104 pub fn left_top(&self) -> Point<T> {
105 Point::new(self.left, self.top)
106 }
107
108 #[inline]
114 pub fn right_top(&self) -> Point<T> {
115 Point::new(self.right, self.top)
116 }
117
118 #[inline]
124 pub fn left_bottom(&self) -> Point<T> {
125 Point::new(self.left, self.bottom)
126 }
127
128 #[inline]
134 pub fn right_bottom(&self) -> Point<T> {
135 Point::new(self.right, self.bottom)
136 }
137}
138
139#[cfg(feature = "perf")]
142impl<T: Copy> Rect<T> {
143 #[inline]
145 pub fn set_left(&mut self, left: T) {
146 self.left = left;
147 }
148
149 #[inline]
151 pub fn set_top(&mut self, top: T) {
152 self.top = top;
153 }
154
155 #[inline]
157 pub fn set_right(&mut self, right: T) {
158 self.right = right;
159 }
160
161 #[inline]
163 pub fn set_bottom(&mut self, bottom: T) {
164 self.bottom = bottom;
165 }
166
167 #[inline]
169 pub fn set_left_top(&mut self, left: T, top: T) {
170 self.left = left;
171 self.top = top;
172 }
173
174 #[inline]
176 pub fn set_right_top(&mut self, right: T, top: T) {
177 self.right = right;
178 self.top = top;
179 }
180
181 #[inline]
183 pub fn set_left_bottom(&mut self, left: T, bottom: T) {
184 self.left = left;
185 self.bottom = bottom;
186 }
187
188 #[inline]
190 pub fn set_right_bottom(&mut self, right: T, bottom: T) {
191 self.right = right;
192 self.bottom = bottom;
193 }
194}
195
196#[cfg(not(feature = "perf"))]
198impl<T: Copy + Ord> Rect<T> {
199 #[inline]
201 pub fn set_left(&mut self, left: T) {
202 self.left = left;
203 self.normalize();
204 }
205
206 #[inline]
208 pub fn set_top(&mut self, top: T) {
209 self.top = top;
210 self.normalize();
211 }
212
213 #[inline]
215 pub fn set_right(&mut self, right: T) {
216 self.right = right;
217 self.normalize();
218 }
219
220 #[inline]
222 pub fn set_bottom(&mut self, bottom: T) {
223 self.bottom = bottom;
224 self.normalize();
225 }
226
227 #[inline]
229 pub fn set_left_top(&mut self, left: T, top: T) {
230 self.left = left;
231 self.top = top;
232 self.normalize();
233 }
234
235 #[inline]
237 pub fn set_right_top(&mut self, right: T, top: T) {
238 self.right = right;
239 self.top = top;
240 self.normalize();
241 }
242
243 #[inline]
245 pub fn set_left_bottom(&mut self, left: T, bottom: T) {
246 self.left = left;
247 self.bottom = bottom;
248 self.normalize();
249 }
250
251 #[inline]
253 pub fn set_right_bottom(&mut self, right: T, bottom: T) {
254 self.right = right;
255 self.bottom = bottom;
256 self.normalize();
257 }
258}
259
260impl<T: Copy + Sub<Output = T>> Rect<T> {
261 #[inline]
267 pub fn width(&self) -> T {
268 self.right - self.left
269 }
270
271 #[inline]
277 pub fn height(&self) -> T {
278 self.bottom - self.top
279 }
280
281 #[inline]
287 pub fn size(&self) -> Size<T> {
288 Size::new(self.width(), self.height())
289 }
290}
291
292impl<T: Copy + Ord> Rect<T> {
293 #[cfg(feature = "perf")]
298 #[inline]
299 pub fn is_normalized(&self) -> bool {
300 self.left <= self.right && self.top <= self.bottom
301 }
302
303 #[cfg(feature = "perf")]
305 #[inline]
306 pub fn normalize(&mut self) {
307 if self.is_normalized() {
308 return;
309 }
310 if self.left > self.right {
311 std::mem::swap(&mut self.left, &mut self.right);
312 }
313 if self.top > self.bottom {
314 std::mem::swap(&mut self.top, &mut self.bottom);
315 }
316 }
317
318 #[allow(dead_code)]
319 #[cfg(not(feature = "perf"))]
320 #[inline]
321 fn is_normalized(&self) -> bool {
322 self.left <= self.right && self.top <= self.bottom
323 }
324
325 #[allow(dead_code)]
326 #[cfg(not(feature = "perf"))]
327 #[inline]
328 fn normalize(&mut self) {
329 if self.is_normalized() {
330 return;
331 }
332 if self.left > self.right {
333 std::mem::swap(&mut self.left, &mut self.right);
334 }
335 if self.top > self.bottom {
336 std::mem::swap(&mut self.top, &mut self.bottom);
337 }
338 }
339
340 #[inline]
346 pub fn contains(&self, point: Point<T>) -> bool {
347 point.x > self.left && point.x < self.right && point.y > self.top && point.y < self.bottom
348 }
349
350 #[inline]
356 pub fn contains_with_bound(&self, point: Point<T>) -> bool {
357 point.x >= self.left
358 && point.x <= self.right
359 && point.y >= self.top
360 && point.y <= self.bottom
361 }
362
363 #[inline]
367 pub fn intersects(&self, other: &Rect<T>) -> bool {
368 self.left <= other.right
369 && self.top <= other.bottom
370 && self.right >= other.left
371 && self.bottom >= other.top
372 }
373
374 #[inline]
381 pub fn intersected(&self, other: &Self) -> Option<Self> {
382 if self.intersects(other) {
383 Some(Self {
384 left: self.left.max(other.left),
385 top: self.top.max(other.top),
386 right: self.right.min(other.right),
387 bottom: self.bottom.min(other.bottom),
388 })
389 } else {
390 None
391 }
392 }
393
394 #[inline]
398 pub fn united(&self, other: &Rect<T>) -> Rect<T> {
399 Self {
400 left: self.left.min(other.left),
401 top: self.top.min(other.top),
402 right: self.right.max(other.right),
403 bottom: self.bottom.max(other.bottom),
404 }
405 }
406}
407
408#[cfg(feature = "perf")]
409impl<T: Copy + AddAssign> Rect<T> {
410 #[inline]
412 pub fn adjust(&mut self, left: T, top: T, right: T, bottom: T) {
413 self.left += left;
414 self.top += top;
415 self.right += right;
416 self.bottom += bottom;
417 }
418}
419
420#[cfg(not(feature = "perf"))]
421impl<T: Copy + AddAssign + Ord> Rect<T> {
422 #[inline]
424 pub fn adjust(&mut self, left: T, top: T, right: T, bottom: T) {
425 self.left += left;
426 self.top += top;
427 self.right += right;
428 self.bottom += bottom;
429 self.normalize();
430 }
431}
432
433impl<T: Copy + Add<Output = T> + Div<i32, Output = T>> Rect<T> {
434 #[inline]
436 pub fn center(&self) -> Point<T> {
437 Point::new((self.right + self.left) / 2, (self.bottom + self.top) / 2)
438 }
439}
440
441impl<T: Copy + Add<Output = T>> From<(Point<T>, Size<T>)> for Rect<T> {
442 #[inline]
443 fn from((pos, size): (Point<T>, Size<T>)) -> Self {
444 Self {
445 left: pos.x,
446 top: pos.y,
447 right: pos.x + size.width,
448 bottom: pos.y + size.height,
449 }
450 }
451}
452
453mod operator_overload {
454 use super::*;
455
456 use std::ops::BitAnd;
457 impl<T: Copy + Ord> BitAnd for Rect<T> {
458 type Output = Option<Self>;
459
460 fn bitand(self, rhs: Self) -> Self::Output {
461 self.intersected(&rhs)
462 }
463 }
464
465 use std::ops::BitOr;
466 impl<T: Copy + Ord> BitOr for Rect<T> {
467 type Output = Self;
468
469 fn bitor(self, rhs: Self) -> Self::Output {
470 self.united(&rhs)
471 }
472 }
473}
474
475#[cfg(test)]
476mod tests {
477 use super::*;
478
479 #[test]
480 #[cfg(feature = "perf")]
481 fn is_normalized_test() {
482 let rect = Rect::new(1, 2, 3, 4);
483 assert!(rect.is_normalized());
484 let rect = Rect::new(3, 2, 1, 4);
485 assert!(!rect.is_normalized());
486 }
487
488 #[test]
489 fn normalize_test() {
490 let mut rect = Rect::new(3, 2, 1, 4);
491 rect.normalize();
492 assert_eq!(rect, Rect::new(1, 2, 3, 4));
493 }
494
495 #[test]
496 fn contains_test() {
497 let rect = Rect::new(1, 2, 3, 4);
498 assert!(rect.contains(Point::new(2, 3)));
499 assert!(!rect.contains(Point::new(0, 0)));
500 }
501
502 #[test]
503 fn contains_with_bound_test() {
504 let rect = Rect::new(1, 2, 3, 4);
505 assert!(rect.contains_with_bound(Point::new(2, 3)));
506 assert!(rect.contains_with_bound(Point::new(1, 2)));
507 assert!(!rect.contains_with_bound(Point::new(0, 0)));
508 }
509
510 #[test]
511 fn intersects_test() {
512 let rect1 = Rect::new(1, 2, 3, 4);
513 let rect2 = Rect::new(2, 3, 4, 5);
514 assert!(rect1.intersects(&rect2));
515 let rect3 = Rect::new(4, 5, 6, 7);
516 assert!(!rect1.intersects(&rect3));
517 }
518
519 #[test]
520 fn intersected_test() {
521 let rect1 = Rect::new(1, 2, 3, 4);
522 let rect2 = Rect::new(2, 3, 4, 5);
523 assert_eq!(rect1.intersected(&rect2), Some(Rect::new(2, 3, 3, 4)));
524 let rect3 = Rect::new(4, 5, 6, 7);
525 assert_eq!(rect1.intersected(&rect3), None);
526 }
527
528 #[test]
529 fn united_test() {
530 let rect1 = Rect::new(1, 2, 3, 4);
531 let rect2 = Rect::new(2, 3, 4, 5);
532 assert_eq!(rect1.united(&rect2), Rect::new(1, 2, 4, 5));
533 }
534
535 #[test]
536 fn adjust_test() {
537 let mut rect = Rect::new(1, 2, 3, 4);
538 rect.adjust(1, 2, 3, 4);
539 assert_eq!(rect, Rect::new(2, 4, 6, 8));
540 }
541
542 #[test]
543 fn center_test() {
544 let rect = Rect::new(1, 2, 3, 4);
545 assert_eq!(rect.center(), Point::new(2, 3));
546 }
547}