mutils/geom/
rect.rs

1use std::iter::IntoIterator;
2use std::ops::Add;
3use std::ops::AddAssign;
4use std::ops::Div;
5use std::ops::DivAssign;
6use std::ops::Mul;
7use std::ops::MulAssign;
8use std::ops::Shl;
9use std::ops::ShlAssign;
10use std::ops::Shr;
11use std::ops::ShrAssign;
12use std::ops::Sub;
13use std::ops::SubAssign;
14
15use crate::num::INum;
16use crate::num::Num;
17use crate::num::ToRounded;
18
19use crate::geom::HorizontalPosition;
20use crate::geom::Line;
21use crate::geom::LinePosition;
22use crate::geom::Point;
23use crate::geom::PointPosition;
24use crate::geom::Size;
25use crate::geom::VerticalPosition;
26
27use crate::internal::macros::quick_n_div;
28use crate::internal::macros::quick_n_mul;
29
30mod rect_iterator;
31pub use self::rect_iterator::RectIterator;
32
33#[derive(Copy, Clone, Debug, PartialEq)]
34pub struct Rect<N: Num = f32>(pub Point<N>, pub Size<N>);
35
36impl<N: Num> Rect<N> {
37    pub fn new_zero_value() -> Self {
38        Rect(Point::new_zero_value(), Size::new_zero_value())
39    }
40
41    pub fn new_from_raw(bottom_left_x: N, bottom_left_y: N, width: N, height: N) -> Self {
42        Self::new(Point(bottom_left_x, bottom_left_y), Size(width, height))
43    }
44
45    pub fn new(bottom_left: Point<N>, size: Size<N>) -> Self {
46        Self(bottom_left, size)
47    }
48
49    pub fn new_from_centre(centre: Point<N>, size: Size<N>) -> Self {
50        Self(centre - size.half(), size)
51    }
52
53    pub fn new_from_top_left(position: Point<N>, size: Size<N>) -> Self {
54        Self(position - Point(N::zero(), size.height()), size)
55    }
56
57    pub fn new_from_top_right(position: Point<N>, size: Size<N>) -> Self {
58        Self(position - size.to_point(), size)
59    }
60
61    pub fn new_from_bottom_right(position: Point<N>, size: Size<N>) -> Self {
62        Self(position - Point(size.width(), N::zero()), size)
63    }
64
65    pub fn new_from_bottom_left(position: Point<N>, size: Size<N>) -> Self {
66        Self(position, size)
67    }
68
69    pub fn new_from_bottom_centre(position: Point<N>, size: Size<N>) -> Self {
70        Self(position - Point(size.width().half(), N::zero()), size)
71    }
72
73    pub fn new_from_left_centre(position: Point<N>, size: Size<N>) -> Self {
74        Self(position - Point(N::zero(), size.height().half()), size)
75    }
76
77    pub fn new_from_top_centre(position: Point<N>, size: Size<N>) -> Self {
78        Self(position - Point(size.width().half(), size.height()), size)
79    }
80
81    pub fn new_from_right_centre(position: Point<N>, size: Size<N>) -> Self {
82        Self(position - Point(size.width(), size.height().half()), size)
83    }
84
85    pub fn move_xy(&mut self, xy: Point<N>) {
86        self.0 += xy;
87    }
88
89    pub fn move_x(&mut self, x: N) {
90        self.0.move_x(x);
91    }
92
93    pub fn move_y(&mut self, y: N) {
94        self.0.move_y(y);
95    }
96
97    pub fn width(&self) -> N {
98        self.size().width()
99    }
100
101    pub fn height(&self) -> N {
102        self.size().height()
103    }
104
105    pub fn area(&self) -> N {
106        self.size().area()
107    }
108
109    pub fn size(&self) -> Size<N> {
110        self.1.abs()
111    }
112
113    pub fn bottom_left(&self) -> Point<N> {
114        self.0 + self.1.min(Size(N::zero(), N::zero()))
115    }
116
117    pub fn bottom_right(&self) -> Point<N> {
118        self.bottom_left() + Size(self.width(), N::zero())
119    }
120
121    pub fn top_left(&self) -> Point<N> {
122        self.bottom_left() + Size(N::zero(), self.height())
123    }
124
125    pub fn top_right(&self) -> Point<N> {
126        self.bottom_left() + self.size()
127    }
128
129    pub fn bottom_centre(&self) -> Point<N> {
130        let size = self.size().abs();
131        self.bottom_left() + Point(size.width().half(), N::zero())
132    }
133
134    pub fn top_centre(&self) -> Point<N> {
135        let size = self.size().abs();
136        self.top_left() + Point(size.width().half(), N::zero())
137    }
138
139    pub fn left_centre(&self) -> Point<N> {
140        let size = self.size().abs();
141        self.bottom_left() + Point(N::zero(), size.height().half())
142    }
143
144    pub fn right_centre(&self) -> Point<N> {
145        let size = self.size().abs();
146        self.bottom_right() + Point(N::zero(), size.height().half())
147    }
148
149    pub fn top_y(&self) -> N {
150        self.bottom_left().y() + self.size().height()
151    }
152
153    pub fn bottom_y(&self) -> N {
154        self.bottom_left().y()
155    }
156
157    pub fn left_x(&self) -> N {
158        self.bottom_left().x()
159    }
160
161    pub fn right_x(&self) -> N {
162        self.bottom_left().x() + self.size().width()
163    }
164
165    pub fn centre(&self) -> Point<N> {
166        self.bottom_left() + self.size().half()
167    }
168
169    pub fn set_bottom_left(&mut self, xy: Point<N>) {
170        self.0 = xy;
171    }
172
173    pub fn set_top_left(&mut self, xy: Point<N>) {
174        self.0 = Point(xy.x(), xy.y() - self.height());
175    }
176
177    pub fn set_top_right(&mut self, xy: Point<N>) {
178        self.0 = Point(xy.x() - self.width(), xy.y() - self.height());
179    }
180
181    pub fn set_bottom_right(&mut self, xy: Point<N>) {
182        self.0 = Point(xy.x() - self.width(), xy.y());
183    }
184
185    pub fn set_centre(&mut self, xy: Point<N>) {
186        self.0 = xy - self.size().half();
187    }
188
189    pub fn set_size(&mut self, size: Size<N>) {
190        self.1 = size;
191    }
192
193    pub fn overlaps(&self, other: Self) -> bool {
194        let bottom_left_a = self.bottom_left();
195        let bottom_left_b = other.bottom_left();
196
197        let top_right_a = self.top_right();
198        let top_right_b = other.top_right();
199
200        if top_right_b.x() <= bottom_left_a.x() {
201            return false;
202        }
203
204        if top_right_b.y() <= bottom_left_a.y() {
205            return false;
206        }
207
208        if top_right_a.x() <= bottom_left_b.x() {
209            return false;
210        }
211
212        if top_right_a.y() <= bottom_left_b.y() {
213            return false;
214        }
215
216        true
217    }
218
219    pub fn contains_point(&self, point: Point<N>) -> bool {
220        self.point_horizontal_position(point) == HorizontalPosition::Inside
221            && self.point_vertical_position(point) == VerticalPosition::Inside
222    }
223
224    pub fn line_position(&self, other: Line<N>) -> LinePosition {
225        LinePosition(
226            self.point_position(other.start()),
227            self.point_position(other.end()),
228        )
229    }
230
231    pub fn point_position(&self, other: Point<N>) -> PointPosition {
232        PointPosition(
233            self.point_horizontal_position(other),
234            self.point_vertical_position(other),
235        )
236    }
237
238    pub fn point_horizontal_position(&self, other: Point<N>) -> HorizontalPosition {
239        if other.x() < self.left_x() {
240            HorizontalPosition::Left
241        } else if other.x() > self.right_x() {
242            HorizontalPosition::Right
243        } else {
244            HorizontalPosition::Inside
245        }
246    }
247
248    pub fn point_vertical_position(&self, other: Point<N>) -> VerticalPosition {
249        if other.y() < self.bottom_y() {
250            VerticalPosition::Below
251        } else if other.y() > self.top_y() {
252            VerticalPosition::Above
253        } else {
254            VerticalPosition::Inside
255        }
256    }
257
258    pub fn intersect_rect(&self, other: Self) -> Option<Self> {
259        if !self.overlaps(other) {
260            return None;
261        }
262
263        let new_bottom_left = self.bottom_left().max(other.bottom_left());
264        let new_top_right = self.top_right().min(other.top_right());
265        let new_size = new_bottom_left.distance_to(new_top_right);
266        let new_rect = Rect(new_bottom_left, new_size);
267
268        Some(new_rect)
269    }
270
271    pub fn combine(&self, other: Self) -> Self {
272        let min_xy = self.bottom_left().min(other.bottom_left());
273        let max_xy = self.top_right().max(other.top_right());
274
275        min_xy.rect_to(max_xy)
276    }
277
278    pub fn get_scale_diff(&self, other: Self) -> Size<N> {
279        self.size().get_scale_diff(other.size())
280    }
281
282    pub fn to<T: Num + From<N>>(&self) -> Rect<T> {
283        Rect(self.bottom_left().to(), self.size().to())
284    }
285
286    pub fn min(self, other: Self) -> Self {
287        Self(
288            self.bottom_left().min(other.bottom_left()),
289            self.size().min(other.size()),
290        )
291    }
292
293    pub fn max(self, other: Self) -> Self {
294        Self(
295            self.bottom_left().max(other.bottom_left()),
296            self.size().max(other.size()),
297        )
298    }
299
300    pub(crate) fn to_f32(self) -> Rect<f32> {
301        self.to_rounded()
302    }
303
304    pub fn centre_to(self, other: Rect<N>) -> Line<N> {
305        Line(self.centre(), other.centre())
306    }
307
308    pub fn left_edge(self) -> Line<N> {
309        Line(self.bottom_left(), self.top_left())
310    }
311
312    pub fn right_edge(self) -> Line<N> {
313        Line(self.bottom_right(), self.top_right())
314    }
315
316    pub fn top_edge(self) -> Line<N> {
317        Line(self.top_left(), self.top_right())
318    }
319
320    pub fn bottom_edge(self) -> Line<N> {
321        Line(self.bottom_left(), self.bottom_right())
322    }
323
324    pub fn interpolate_to(self, other: Rect<N>, n: N) -> Rect<N> {
325        let new_centre = self.centre().interpolate_to(other.centre(), n);
326        let new_size = self.size().interpolate_to(other.size(), n);
327
328        Rect::new_from_centre(new_centre, new_size)
329    }
330
331    pub fn round_to_max_size(self) -> Rect<N> {
332        let self_f32 = self.to_f32();
333        let Point(bottom_left_x, bottom_left_y) = self_f32.bottom_left();
334        let Point(top_right_x, top_right_y) = self_f32.top_right();
335
336        let new_bottom_left_x;
337        let new_bottom_left_y;
338        let new_top_right_x;
339        let new_top_right_y;
340
341        if bottom_left_x < top_right_x {
342            new_bottom_left_x = bottom_left_x.floor();
343            new_top_right_x = top_right_x.ceil();
344        } else {
345            new_bottom_left_x = bottom_left_x.floor();
346            new_top_right_x = top_right_x.ceil();
347        }
348
349        if bottom_left_y < top_right_y {
350            new_bottom_left_y = bottom_left_y.floor();
351            new_top_right_y = top_right_y.ceil();
352        } else {
353            new_bottom_left_y = bottom_left_y.floor();
354            new_top_right_y = top_right_y.ceil();
355        }
356
357        let new_rect_f32 = Point(new_bottom_left_x, new_bottom_left_y)
358            .rect_to(Point(new_top_right_x, new_top_right_y));
359        new_rect_f32.from_f32()
360    }
361}
362
363impl Rect<f32> {
364    #[allow(dead_code)]
365    pub(crate) fn from_f32<N: Num>(self) -> Rect<N> {
366        Rect(self.bottom_left().from_f32(), self.size().from_f32())
367    }
368}
369
370impl<O: Num, N: Num + ToRounded<O>> ToRounded<Rect<O>> for Rect<N> {
371    fn to_rounded(self) -> Rect<O> {
372        Rect(self.bottom_left().to_rounded(), self.size().to_rounded())
373    }
374}
375
376quick_n_div!(f32, Rect<f32>);
377quick_n_div!(f64, Rect<f64>);
378
379quick_n_div!(usize, Rect<usize>);
380quick_n_div!(u8, Rect<u8>);
381quick_n_div!(u16, Rect<u16>);
382quick_n_div!(u32, Rect<u32>);
383quick_n_div!(u64, Rect<u64>);
384quick_n_div!(u128, Rect<u128>);
385
386quick_n_div!(isize, Rect<isize>);
387quick_n_div!(i8, Rect<i8>);
388quick_n_div!(i16, Rect<i16>);
389quick_n_div!(i32, Rect<i32>);
390quick_n_div!(i64, Rect<i64>);
391quick_n_div!(i128, Rect<i128>);
392
393quick_n_mul!(f32, Rect<f32>);
394quick_n_mul!(f64, Rect<f64>);
395
396quick_n_mul!(usize, Rect<usize>);
397quick_n_mul!(u8, Rect<u8>);
398quick_n_mul!(u16, Rect<u16>);
399quick_n_mul!(u32, Rect<u32>);
400quick_n_mul!(u64, Rect<u64>);
401quick_n_mul!(u128, Rect<u128>);
402
403quick_n_mul!(isize, Rect<isize>);
404quick_n_mul!(i8, Rect<i8>);
405quick_n_mul!(i16, Rect<i16>);
406quick_n_mul!(i32, Rect<i32>);
407quick_n_mul!(i64, Rect<i64>);
408quick_n_mul!(i128, Rect<i128>);
409
410impl<N: Num> Mul<N> for Rect<N> {
411    type Output = Self;
412
413    fn mul(self, other: N) -> Self {
414        Rect::new(self.0 * other, self.1 * other)
415    }
416}
417
418impl<N: Num> MulAssign<N> for Rect<N> {
419    fn mul_assign(&mut self, other: N) {
420        *self = *self * other;
421    }
422}
423
424impl<N: Num> Div<N> for Rect<N> {
425    type Output = Self;
426
427    fn div(self, other: N) -> Self {
428        Rect::new(self.0 / other, self.1 / other)
429    }
430}
431
432impl<N: Num> DivAssign<N> for Rect<N> {
433    fn div_assign(&mut self, other: N) {
434        *self = *self / other;
435    }
436}
437
438impl<N: Num> Add<Point<N>> for Rect<N> {
439    type Output = Self;
440
441    fn add(self, other: Point<N>) -> Self {
442        Rect(self.bottom_left() + other, self.size())
443    }
444}
445
446impl<N: Num> AddAssign<Point<N>> for Rect<N> {
447    fn add_assign(&mut self, other: Point<N>) {
448        self.0 += other;
449    }
450}
451
452impl<N: Num> Add<Size<N>> for Rect<N> {
453    type Output = Self;
454
455    fn add(self, other: Size<N>) -> Self {
456        Rect(self.bottom_left(), self.size() + other)
457    }
458}
459
460impl<N: Num> AddAssign<Size<N>> for Rect<N> {
461    fn add_assign(&mut self, other: Size<N>) {
462        self.1 += other;
463    }
464}
465
466impl<N: Num> Sub<Point<N>> for Rect<N> {
467    type Output = Self;
468
469    fn sub(self, other: Point<N>) -> Self {
470        Rect(self.bottom_left() - other, self.size())
471    }
472}
473
474impl<N: Num> SubAssign<Point<N>> for Rect<N> {
475    fn sub_assign(&mut self, other: Point<N>) {
476        self.0 -= other;
477    }
478}
479
480impl<N: Num> Sub<Size<N>> for Rect<N> {
481    type Output = Self;
482
483    fn sub(self, other: Size<N>) -> Self {
484        Rect(self.bottom_left(), self.size() - other)
485    }
486}
487
488impl<N: Num> SubAssign<Size<N>> for Rect<N> {
489    fn sub_assign(&mut self, other: Size<N>) {
490        self.1 -= other;
491    }
492}
493
494impl<N: INum> Shl<N> for Rect<N> {
495    type Output = Self;
496
497    fn shl(self, other: N) -> Self {
498        Self(self.0 << other, self.1 << other)
499    }
500}
501
502impl<N: INum> ShlAssign<N> for Rect<N> {
503    fn shl_assign(&mut self, other: N) {
504        *self = *self << other;
505    }
506}
507
508impl<N: INum> Shr<N> for Rect<N> {
509    type Output = Self;
510
511    fn shr(self, other: N) -> Self {
512        Self(self.0 >> other, self.1 >> other)
513    }
514}
515
516impl<N: INum> ShrAssign<N> for Rect<N> {
517    fn shr_assign(&mut self, other: N) {
518        *self = *self >> other;
519    }
520}
521
522impl<N: Num> IntoIterator for Rect<N> {
523    type Item = Point<N>;
524    type IntoIter = RectIterator<N>;
525
526    fn into_iter(self) -> Self::IntoIter {
527        RectIterator::new(self)
528    }
529}
530
531#[cfg(test)]
532mod overlaps {
533    use super::*;
534
535    #[test]
536    fn it_should_not_overlap_with_rectangles_outside_to_the_left() {
537        let a = Rect(Point(20, 0), Size(10, 10));
538        let b = Rect(Point(0, 0), Size(10, 10));
539
540        assert_eq!(a.overlaps(b), false);
541    }
542
543    #[test]
544    fn it_should_not_overlap_with_rectangles_outside_to_the_right() {
545        let a = Rect(Point(0, 0), Size(10, 10));
546        let b = Rect(Point(20, 0), Size(10, 10));
547
548        assert_eq!(a.overlaps(b), false);
549    }
550
551    #[test]
552    fn it_should_not_overlap_with_rectangles_outside_above() {
553        let a = Rect(Point(20, 20), Size(10, 10));
554        let b = Rect(Point(20, 0), Size(10, 10));
555
556        assert_eq!(a.overlaps(b), false);
557    }
558
559    #[test]
560    fn it_should_not_overlap_with_rectangles_outside_below() {
561        let a = Rect(Point(20, 20), Size(10, 10));
562        let b = Rect(Point(20, 40), Size(10, 10));
563
564        assert_eq!(a.overlaps(b), false);
565    }
566
567    #[test]
568    fn it_should_overlap_with_rectangles_intersecting_on_the_left() {
569        let a = Rect(Point(0, 0), Size(10, 10));
570        let b = Rect(Point(-5, 0), Size(10, 10));
571
572        assert_eq!(a.overlaps(b), true);
573    }
574
575    #[test]
576    fn it_should_overlap_with_rectangles_intersecting_left_above() {
577        let a = Rect(Point(0, 0), Size(10, 10));
578        let b = Rect(Point(-5, 5), Size(10, 10));
579
580        assert_eq!(a.overlaps(b), true);
581    }
582
583    #[test]
584    fn it_should_overlap_with_rectangles_intersecting_right_below() {
585        let a = Rect(Point(0, 0), Size(10, 10));
586        let b = Rect(Point(5, -5), Size(10, 10));
587
588        assert_eq!(a.overlaps(b), true);
589    }
590
591    #[test]
592    fn it_should_overlap_with_rectangles_intersecting_fully_inside() {
593        let a = Rect(Point(0, 0), Size(10, 10));
594        let b = Rect(Point(3, 3), Size(6, 6));
595
596        assert_eq!(a.overlaps(b), true);
597    }
598
599    #[test]
600    fn it_should_overlap_identical_rectangles() {
601        let a: Rect<i32> = Rect(Point(3, 2), Size(4, 5));
602        let b: Rect<i32> = Rect(Point(3, 2), Size(4, 5));
603
604        assert_eq!(a.overlaps(b), true);
605    }
606
607    #[test]
608    fn it_should_not_overlap_rectangles_next_to_each_other() {
609        let a: Rect<i32> = Rect(Point(2, 2), Size(2, 2));
610        let b: Rect<i32> = Rect(Point(4, 2), Size(2, 2));
611
612        assert_eq!(a.overlaps(b), false);
613    }
614
615    #[test]
616    fn it_should_overlap_with_negative_sizes() {
617        let a: Rect<i32> = Rect(Point(0, 0), Size(10, 10));
618        let b: Rect<i32> = Rect(Point(12, 12), Size(-10, -10));
619
620        assert_eq!(a.overlaps(b), true);
621        assert_eq!(b.overlaps(a), true);
622    }
623}
624
625#[cfg(test)]
626mod new_from_centre {
627    use super::*;
628
629    #[test]
630    fn it_should_create_a_rectangle_around_the_point_given() {
631        let rect: Rect<f32> = Rect::new_from_centre(Point(10.0, 10.0), Size(5.0, 8.0));
632        assert_eq!(rect, Rect(Point(7.5, 6.0), Size(5.0, 8.0)));
633    }
634}
635
636#[cfg(test)]
637mod new_from_top_left {
638    use super::*;
639
640    #[test]
641    fn it_should_place_rect_correctly() {
642        let rect: Rect<i32> = Rect::new_from_top_left(Point(10, 100), Size(10, 20));
643        let expected: Rect<i32> = Rect(Point(10, 80), Size(10, 20));
644
645        assert_eq!(rect, expected);
646    }
647}
648
649#[cfg(test)]
650mod new_from_top_right {
651    use super::*;
652
653    #[test]
654    fn it_should_place_rect_correctly() {
655        let rect: Rect<i32> = Rect::new_from_top_right(Point(10, 100), Size(10, 20));
656        let expected: Rect<i32> = Rect(Point(0, 80), Size(10, 20));
657
658        assert_eq!(rect, expected);
659    }
660}
661
662#[cfg(test)]
663mod new_from_bottom_left {
664    use super::*;
665
666    #[test]
667    fn it_should_place_rect_correctly() {
668        let rect: Rect<i32> = Rect::new_from_bottom_left(Point(10, 100), Size(10, 20));
669        let expected: Rect<i32> = Rect(Point(10, 100), Size(10, 20));
670
671        assert_eq!(rect, expected);
672    }
673}
674
675#[cfg(test)]
676mod new_from_bottom_right {
677    use super::*;
678
679    #[test]
680    fn it_should_place_rect_correctly() {
681        let rect: Rect<i32> = Rect::new_from_bottom_right(Point(10, 100), Size(10, 20));
682        let expected: Rect<i32> = Rect(Point(0, 100), Size(10, 20));
683
684        assert_eq!(rect, expected);
685    }
686}
687
688#[cfg(test)]
689mod new_from_bottom_centre {
690    use super::*;
691
692    #[test]
693    fn it_should_place_rect_correctly() {
694        let rect: Rect<i32> = Rect::new_from_bottom_centre(Point(10, 100), Size(10, 20));
695        let expected: Rect<i32> = Rect(Point(5, 100), Size(10, 20));
696
697        assert_eq!(rect, expected);
698    }
699}
700
701#[cfg(test)]
702mod new_from_top_centre {
703    use super::*;
704
705    #[test]
706    fn it_should_place_rect_correctly() {
707        let rect: Rect<i32> = Rect::new_from_top_centre(Point(10, 100), Size(10, 20));
708        let expected: Rect<i32> = Rect(Point(5, 80), Size(10, 20));
709
710        assert_eq!(rect, expected);
711    }
712}
713
714#[cfg(test)]
715mod new_from_left_centre {
716    use super::*;
717
718    #[test]
719    fn it_should_place_rect_correctly() {
720        let rect: Rect<i32> = Rect::new_from_left_centre(Point(10, 100), Size(10, 20));
721        let expected: Rect<i32> = Rect(Point(10, 90), Size(10, 20));
722
723        assert_eq!(rect, expected);
724    }
725}
726
727#[cfg(test)]
728mod new_from_right_centre {
729    use super::*;
730
731    #[test]
732    fn it_should_place_rect_correctly() {
733        let rect: Rect<i32> = Rect::new_from_right_centre(Point(10, 100), Size(10, 20));
734        let expected: Rect<i32> = Rect(Point(0, 90), Size(10, 20));
735
736        assert_eq!(rect, expected);
737    }
738}
739
740#[cfg(test)]
741mod bottom_left {
742    use super::*;
743
744    #[test]
745    fn it_should_return_bottom_left() {
746        let rect: Rect<u32> = Rect(Point(3, 4), Size(9, 13));
747        assert_eq!(rect.bottom_left(), Point(3, 4));
748    }
749
750    #[test]
751    fn it_should_return_bottom_left_for_positive_sizes() {
752        let rect: Rect<i32> = Rect(Point(10, 12), Size(5, 8));
753        assert_eq!(rect.bottom_left(), Point(10, 12));
754    }
755
756    #[test]
757    fn it_should_account_for_negative_sizes() {
758        let rect: Rect<i32> = Rect(Point(10, 12), Size(-5, -8));
759        assert_eq!(rect.bottom_left(), Point(5, 4));
760    }
761}
762
763#[cfg(test)]
764mod bottom_right {
765    use super::*;
766
767    #[test]
768    fn it_should_return_bottom_right() {
769        let rect: Rect<u32> = Rect(Point(3, 4), Size(9, 13));
770        assert_eq!(rect.bottom_right(), Point(12, 4));
771    }
772}
773
774#[cfg(test)]
775mod top_left {
776    use super::*;
777
778    #[test]
779    fn it_should_return_top_left() {
780        let rect: Rect<u32> = Rect(Point(3, 4), Size(9, 13));
781        assert_eq!(rect.top_left(), Point(3, 17));
782    }
783}
784
785#[cfg(test)]
786mod top_right {
787    use super::*;
788
789    #[test]
790    fn it_should_return_top_right() {
791        let rect: Rect<u32> = Rect(Point(3, 4), Size(9, 13));
792        assert_eq!(rect.top_right(), Point(12, 17));
793    }
794}
795
796#[cfg(test)]
797mod bottom_centre {
798    use super::*;
799
800    #[test]
801    fn it_should_return_bottom_centre() {
802        let rect: Rect<u32> = Rect(Point(3, 4), Size(10, 13));
803        assert_eq!(rect.bottom_centre(), Point(8, 4));
804    }
805}
806
807#[cfg(test)]
808mod top_centre {
809    use super::*;
810
811    #[test]
812    fn it_should_return_top_centre() {
813        let rect: Rect<u32> = Rect(Point(3, 4), Size(10, 13));
814        assert_eq!(rect.top_centre(), Point(8, 17));
815    }
816}
817
818#[cfg(test)]
819mod left_centre {
820    use super::*;
821
822    #[test]
823    fn it_should_return_left_centre() {
824        let rect: Rect<u32> = Rect(Point(3, 4), Size(10, 14));
825        assert_eq!(rect.left_centre(), Point(3, 11));
826    }
827}
828
829#[cfg(test)]
830mod right_centre {
831    use super::*;
832
833    #[test]
834    fn it_should_return_right_centre() {
835        let rect: Rect<u32> = Rect(Point(3, 4), Size(10, 14));
836        assert_eq!(rect.right_centre(), Point(13, 11));
837    }
838}
839
840#[cfg(test)]
841mod point_position {
842    use super::*;
843
844    #[test]
845    fn it_should_return_bottom_left() {
846        let rect: Rect<u32> = Rect(Point(10, 12), Size(10, 8));
847        assert_eq!(
848            rect.point_position(Point(3, 4)),
849            PointPosition(HorizontalPosition::Left, VerticalPosition::Below)
850        );
851    }
852
853    #[test]
854    fn it_should_return_middle_left() {
855        let rect: Rect<u32> = Rect(Point(10, 12), Size(10, 8));
856        assert_eq!(
857            rect.point_position(Point(3, 16)),
858            PointPosition(HorizontalPosition::Left, VerticalPosition::Inside)
859        );
860    }
861
862    #[test]
863    fn it_should_return_above_left() {
864        let rect: Rect<u32> = Rect(Point(10, 12), Size(10, 8));
865        assert_eq!(
866            rect.point_position(Point(3, 24)),
867            PointPosition(HorizontalPosition::Left, VerticalPosition::Above)
868        );
869    }
870
871    #[test]
872    fn it_should_return_bottom_middle() {
873        let rect: Rect<u32> = Rect(Point(10, 12), Size(10, 8));
874        assert_eq!(
875            rect.point_position(Point(13, 4)),
876            PointPosition(HorizontalPosition::Inside, VerticalPosition::Below)
877        );
878    }
879
880    #[test]
881    fn it_should_return_middle_middle() {
882        let rect: Rect<u32> = Rect(Point(10, 12), Size(10, 8));
883        assert_eq!(
884            rect.point_position(Point(13, 14)),
885            PointPosition(HorizontalPosition::Inside, VerticalPosition::Inside)
886        );
887    }
888
889    #[test]
890    fn it_should_return_above_middle() {
891        let rect: Rect<u32> = Rect(Point(10, 12), Size(10, 8));
892        assert_eq!(
893            rect.point_position(Point(13, 24)),
894            PointPosition(HorizontalPosition::Inside, VerticalPosition::Above)
895        );
896    }
897
898    #[test]
899    fn it_should_return_bottom_right() {
900        let rect: Rect<u32> = Rect(Point(10, 12), Size(10, 8));
901        assert_eq!(
902            rect.point_position(Point(30, 4)),
903            PointPosition(HorizontalPosition::Right, VerticalPosition::Below)
904        );
905    }
906
907    #[test]
908    fn it_should_return_middle_right() {
909        let rect: Rect<u32> = Rect(Point(10, 12), Size(10, 8));
910        assert_eq!(
911            rect.point_position(Point(30, 14)),
912            PointPosition(HorizontalPosition::Right, VerticalPosition::Inside)
913        );
914    }
915
916    #[test]
917    fn it_should_return_above_right() {
918        let rect: Rect<u32> = Rect(Point(10, 12), Size(10, 8));
919        assert_eq!(
920            rect.point_position(Point(30, 34)),
921            PointPosition(HorizontalPosition::Right, VerticalPosition::Above)
922        );
923    }
924}
925
926#[cfg(test)]
927mod centre_to {
928    use super::*;
929
930    #[test]
931    fn it_should_return_line_from_first_rect_to_second() {
932        let first_rect = Rect::new_from_centre(Point(24_i32, 123_i32), Size(100_i32, 200_i32));
933        let second_rect = Rect::new_from_centre(Point(999_i32, -283_i32), Size(100_i32, 200_i32));
934        let line = first_rect.centre_to(second_rect);
935
936        assert_eq!(line, Line(Point(24_i32, 123_i32), Point(999_i32, -283_i32)));
937    }
938}
939
940#[cfg(test)]
941mod interpolate_to {
942    use super::*;
943
944    #[test]
945    fn it_should_interpolate_centre() {
946        let first_rect: Rect<f32> = Rect::new_from_centre(Point(100.0, 200.0), Size(100.0, 200.0));
947        let second_rect: Rect<f32> =
948            Rect::new_from_centre(Point(1100.0, -400.0), Size(100.0, 200.0));
949        let interpolated_rect: Rect<f32> = first_rect.interpolate_to(second_rect, 0.5);
950
951        assert_eq!(
952            interpolated_rect,
953            Rect::new_from_centre(Point(600.0, -100.0), Size(100.0, 200.0))
954        );
955    }
956
957    #[test]
958    fn it_should_interpolate_size() {
959        let first_rect: Rect<f32> = Rect::new_from_centre(Point(100.0, 200.0), Size(100.0, 200.0));
960        let second_rect: Rect<f32> =
961            Rect::new_from_centre(Point(1100.0, -400.0), Size(200.0, 100.0));
962        let interpolated_rect: Rect<f32> = first_rect.interpolate_to(second_rect, 0.5);
963
964        assert_eq!(
965            interpolated_rect,
966            Rect::new_from_centre(Point(600.0, -100.0), Size(150.0, 150.0))
967        );
968    }
969}
970
971#[cfg(test)]
972mod round_to_max_size {
973    use super::*;
974    use crate::geom::testing_utils::assert_approx_rect_eq;
975    use testcat::*;
976
977    it!(
978        "should enlarge positive rectangles to make size larger",
979        test_positive_rect
980    );
981    it!(
982        "should enlarge positive rectangles with an inverse size",
983        test_positive_rect_with_negative_size
984    );
985    it!(
986        "should enlarge negative rectangles to make size larger",
987        test_negative_rect
988    );
989    it!(
990        "should enlarge negative rectangles with an inverse size",
991        test_negative_rect_with_negative_size
992    );
993    it!(
994        "should enlarge rectangles that cross zero to make size larger",
995        test_cross_zero_rect
996    );
997    it!(
998        "should enlarge rectangles that cross zero with an inverse size",
999        test_cross_zero_rect_with_negative_size
1000    );
1001
1002    fn test_positive_rect() {
1003        let rect: Rect<f32> = Point(10.2, 10.8).rect_to(Point(19.2, 19.8));
1004        let rounded = rect.round_to_max_size();
1005        let expected: Rect<f32> = Point(10.0, 10.0).rect_to(Point(20.0, 20.0));
1006
1007        assert_approx_rect_eq(expected, rounded);
1008    }
1009
1010    fn test_positive_rect_with_negative_size() {
1011        let rect: Rect<f32> = Point(19.2, 19.8).rect_to(Point(10.2, 10.8));
1012        let rounded = rect.round_to_max_size();
1013        let expected: Rect<f32> = Point(20.0, 20.0).rect_to(Point(10.0, 10.0));
1014
1015        assert_approx_rect_eq(expected, rounded);
1016    }
1017
1018    fn test_negative_rect() {
1019        let rect: Rect<f32> = Point(-10.2, -10.8).rect_to(Point(-19.2, -19.8));
1020        let rounded = rect.round_to_max_size();
1021        let expected: Rect<f32> = Point(-10.0, -10.0).rect_to(Point(-20.0, -20.0));
1022
1023        assert_approx_rect_eq(expected, rounded);
1024    }
1025
1026    fn test_negative_rect_with_negative_size() {
1027        let rect: Rect<f32> = Point(-19.2, -19.8).rect_to(Point(-10.2, -10.8));
1028        let rounded = rect.round_to_max_size();
1029        let expected: Rect<f32> = Point(-20.0, -20.0).rect_to(Point(-10.0, -10.0));
1030
1031        assert_approx_rect_eq(expected, rounded);
1032    }
1033
1034    fn test_cross_zero_rect() {
1035        let rect: Rect<f32> = Point(-10.2, -10.8).rect_to(Point(19.2, 19.8));
1036        let rounded = rect.round_to_max_size();
1037        let expected: Rect<f32> = Point(-11.0, -11.0).rect_to(Point(20.0, 20.0));
1038
1039        assert_approx_rect_eq(expected, rounded);
1040    }
1041
1042    fn test_cross_zero_rect_with_negative_size() {
1043        let rect: Rect<f32> = Point(19.2, 19.8).rect_to(Point(-10.2, -10.8));
1044        let rounded = rect.round_to_max_size();
1045        let expected: Rect<f32> = Point(20.0, 20.0).rect_to(Point(-11.0, -11.0));
1046
1047        assert_approx_rect_eq(expected, rounded);
1048    }
1049}
1050
1051#[cfg(test)]
1052mod mul_n {
1053    use super::*;
1054
1055    #[test]
1056    fn it_should_multiply_rect() {
1057        let rect: Rect<u32> = Rect::new_from_centre(Point(10, 12), Size(6, 8));
1058        let doubled = rect * 2;
1059
1060        assert_eq!(doubled, Rect::new_from_centre(Point(20, 24), Size(12, 16)));
1061    }
1062
1063    #[test]
1064    fn it_should_multiply_rect_using_left_hand() {
1065        let rect: Rect<u32> = Rect::new_from_centre(Point(10, 12), Size(6, 8));
1066        let doubled = 2 * rect;
1067
1068        assert_eq!(doubled, Rect::new_from_centre(Point(20, 24), Size(12, 16)));
1069    }
1070}
1071
1072#[cfg(test)]
1073mod div_n {
1074    use super::*;
1075
1076    #[test]
1077    fn it_should_divide_rect() {
1078        let rect: Rect<u32> = Rect::new_from_centre(Point(20, 24), Size(12, 16));
1079        let doubled = rect / 2;
1080
1081        assert_eq!(doubled, Rect::new_from_centre(Point(10, 12), Size(6, 8)));
1082    }
1083
1084    #[test]
1085    fn it_should_divide_rect_using_left_hand() {
1086        let rect: Rect<u32> = Rect::new_from_centre(Point(20, 24), Size(12, 16));
1087        let doubled = 2 / rect;
1088
1089        assert_eq!(doubled, Rect::new_from_centre(Point(10, 12), Size(6, 8)));
1090    }
1091}