1use std::fmt;
2use std::marker::PhantomData;
3use std::ops::Range;
4use std::slice::Iter;
5
6use crate::{FillIter, Vec2};
7
8pub trait BoundingRect {
10 type Coord: Coord;
12
13 fn bounding_rect(&self) -> Rect<Self::Coord>;
16}
17
18pub trait Coord:
23 Copy
24 + Default
25 + PartialEq
26 + PartialOrd
27 + std::fmt::Display
28 + std::ops::Add<Output = Self>
29 + std::ops::Sub<Output = Self>
30{
31 fn is_nan(self) -> bool;
33}
34
35impl Coord for f32 {
36 fn is_nan(self) -> bool {
37 self.is_nan()
38 }
39}
40
41impl Coord for i32 {
42 fn is_nan(self) -> bool {
43 false
44 }
45}
46
47fn min_or_lhs<T: PartialOrd>(a: T, b: T) -> T {
49 if b < a { b } else { a }
50}
51
52fn max_or_lhs<T: PartialOrd>(a: T, b: T) -> T {
54 if b > a { b } else { a }
55}
56
57#[derive(Copy, Clone, Default, Eq, PartialEq)]
59#[cfg_attr(
60 feature = "serde_traits",
61 derive(serde_derive::Serialize, serde_derive::Deserialize)
62)]
63pub struct Point<T: Coord = i32> {
64 pub x: T,
65 pub y: T,
66}
67
68pub type PointF = Point<f32>;
69
70impl<T: Coord> Point<T> {
71 pub fn from_yx(y: T, x: T) -> Self {
73 Point { y, x }
74 }
75
76 pub fn move_to(&mut self, y: T, x: T) {
78 self.y = y;
79 self.x = x;
80 }
81
82 pub fn translate(self, y: T, x: T) -> Self {
83 Point {
84 y: self.y + y,
85 x: self.x + x,
86 }
87 }
88
89 pub fn move_by(&mut self, y: T, x: T) {
90 *self = self.translate(y, x);
91 }
92}
93
94impl Point<f32> {
95 pub fn distance(self, other: Self) -> f32 {
96 self.vec_to(other).length()
97 }
98
99 pub fn vec_to(self, other: Self) -> Vec2 {
101 let dx = other.x - self.x;
102 let dy = other.y - self.y;
103 Vec2::from_xy(dx, dy)
104 }
105
106 pub fn to_vec(self) -> Vec2 {
108 Vec2::from_xy(self.x, self.y)
109 }
110}
111
112impl Point<i32> {
113 pub fn coord(self) -> [usize; 2] {
118 assert!(self.y >= 0 && self.x >= 0, "Coordinates are negative");
119 [self.y as usize, self.x as usize]
120 }
121
122 pub fn neighbors(self) -> [Point; 8] {
125 [
126 self.translate(-1, 0), self.translate(-1, 1), self.translate(0, 1), self.translate(1, 1), self.translate(1, 0), self.translate(1, -1), self.translate(0, -1), self.translate(-1, -1), ]
135 }
136
137 pub fn to_f32(self) -> Point<f32> {
138 Point {
139 x: self.x as f32,
140 y: self.y as f32,
141 }
142 }
143
144 pub fn distance(self, other: Point<i32>) -> f32 {
145 self.to_f32().distance(other.to_f32())
146 }
147}
148
149impl<T: Coord> fmt::Debug for Point<T> {
150 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
151 write!(f, "({}, {})", self.y, self.x)
152 }
153}
154
155fn overlap<T: Coord>(a: (T, T), b: (T, T)) -> T {
160 let a = sort_pair(a);
161 let b = sort_pair(b);
162 let ((_a_start, a_end), (b_start, b_end)) = sort_pair((a, b));
163
164 let min_overlap = T::default();
165 let max_overlap = b_end - b_start;
166 let overlap = a_end - b_start;
167
168 if max_overlap.is_nan() {
171 return max_overlap;
172 }
173
174 if overlap < min_overlap {
175 min_overlap
176 } else if overlap > max_overlap {
177 max_overlap
178 } else {
179 overlap
180 }
181}
182
183fn sort_pair<T: PartialOrd>(pair: (T, T)) -> (T, T) {
186 if pair.0 > pair.1 {
187 (pair.1, pair.0)
188 } else {
189 pair
190 }
191}
192
193#[derive(Copy, Clone, PartialEq)]
195#[cfg_attr(
196 feature = "serde_traits",
197 derive(serde_derive::Serialize, serde_derive::Deserialize)
198)]
199pub struct Line<T: Coord = i32> {
200 pub start: Point<T>,
201 pub end: Point<T>,
202}
203
204pub type LineF = Line<f32>;
205
206impl<T: Coord> fmt::Debug for Line<T> {
207 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
208 write!(f, "{:?} -> {:?}", self.start, self.end)
209 }
210}
211
212impl<T: Coord> Line<T> {
213 pub fn from_endpoints(start: Point<T>, end: Point<T>) -> Line<T> {
214 Line { start, end }
215 }
216
217 pub fn is_empty(&self) -> bool {
219 self.start == self.end
220 }
221
222 pub fn width(&self) -> T {
225 self.end.x - self.start.x
226 }
227
228 pub fn height(&self) -> T {
231 self.end.y - self.start.y
232 }
233
234 pub fn is_horizontal(&self) -> bool {
237 self.start.y == self.end.y
238 }
239
240 pub fn reverse(&self) -> Line<T> {
242 Line::from_endpoints(self.end, self.start)
243 }
244
245 pub fn downwards(&self) -> Line<T> {
248 if self.start.y <= self.end.y {
249 *self
250 } else {
251 self.reverse()
252 }
253 }
254
255 pub fn rightwards(&self) -> Line<T> {
258 if self.start.x <= self.end.x {
259 *self
260 } else {
261 self.reverse()
262 }
263 }
264
265 pub fn vertical_overlap(&self, other: Line<T>) -> T {
268 overlap((self.start.y, self.end.y), (other.start.y, other.end.y))
269 }
270
271 pub fn horizontal_overlap(&self, other: Line<T>) -> T {
274 overlap((self.start.x, self.end.x), (other.start.x, other.end.x))
275 }
276}
277
278impl Line<f32> {
279 pub fn distance(&self, p: PointF) -> f32 {
282 if self.is_empty() {
283 return self.start.distance(p);
284 }
285
286 let ab = self.start.vec_to(self.end);
293 let ac = self.start.vec_to(p);
294 let scalar_proj = ac.dot(ab) / (ab.length() * ab.length());
295
296 if scalar_proj <= 0. {
297 self.start.distance(p)
299 } else if scalar_proj >= 1. {
300 self.end.distance(p)
302 } else {
303 let intercept_x = self.start.x + ab.x * scalar_proj;
304 let intercept_y = self.start.y + ab.y * scalar_proj;
305 let proj_line = Vec2::from_yx(intercept_y - p.y, intercept_x - p.x);
306 proj_line.length()
307 }
308 }
309
310 pub fn intersects(&self, other: Line<f32>) -> bool {
315 let (x1, x2) = (self.start.x, self.end.x);
318 let (y1, y2) = (self.start.y, self.end.y);
319 let (x3, x4) = (other.start.x, other.end.x);
320 let (y3, y4) = (other.start.y, other.end.y);
321
322 let a = x2 - x1;
338 let b = -(x4 - x3);
339 let c = y2 - y1;
340 let d = -(y4 - y3);
341
342 let b0 = x3 - x1;
343 let b1 = y3 - y1;
344
345 let det_a = a * d - b * c;
346 if det_a == 0. {
347 return false;
349 }
350 let det_a0 = b0 * d - b * b1;
351 let det_a1 = a * b1 - b0 * c;
352
353 let s_ok = (det_a0 >= 0.) == (det_a > 0.) && det_a0.abs() <= det_a.abs();
357 let t_ok = (det_a1 >= 0.) == (det_a > 0.) && det_a1.abs() <= det_a.abs();
358
359 s_ok && t_ok
360 }
361}
362
363impl Line<f32> {
364 pub fn center(&self) -> PointF {
366 let cy = (self.start.y + self.end.y) / 2.;
367 let cx = (self.start.x + self.end.x) / 2.;
368 Point::from_yx(cy, cx)
369 }
370
371 fn slope_intercept(&self) -> Option<(f32, f32)> {
374 let dx = self.end.x - self.start.x;
375 if dx == 0. {
376 return None;
377 }
378 let slope = (self.end.y - self.start.y) / dx;
379 let intercept = self.start.y - slope * self.start.x;
380 Some((slope, intercept))
381 }
382
383 pub fn x_for_y(&self, y: f32) -> Option<f32> {
389 let (min_y, max_y) = sort_pair((self.start.y, self.end.y));
390 if y < min_y || y > max_y || min_y == max_y {
391 return None;
392 }
393 self.slope_intercept()
394 .map(|(slope, intercept)| (y - intercept) / slope)
395 .or(Some(self.start.x))
396 }
397
398 pub fn y_for_x(&self, x: f32) -> Option<f32> {
404 let (min_x, max_x) = sort_pair((self.start.x, self.end.x));
405 if x < min_x || x > max_x {
406 return None;
407 }
408 self.slope_intercept()
409 .map(|(slope, intercept)| slope * x + intercept)
410 }
411}
412
413impl Line<i32> {
414 pub fn to_f32(&self) -> LineF {
415 Line::from_endpoints(self.start.to_f32(), self.end.to_f32())
416 }
417
418 pub fn distance(&self, p: Point) -> f32 {
421 self.to_f32().distance(p.to_f32())
422 }
423
424 pub fn intersects(&self, other: Line) -> bool {
429 self.to_f32().intersects(other.to_f32())
430 }
431}
432
433impl<T: Coord> BoundingRect for Line<T> {
434 type Coord = T;
435
436 fn bounding_rect(&self) -> Rect<T> {
437 let d = self.downwards();
438 let r = self.rightwards();
439 Rect::from_tlbr(d.start.y, r.start.x, d.end.y, r.end.x)
440 }
441}
442
443#[derive(Copy, Clone, Debug, PartialEq, Eq)]
448#[cfg_attr(
449 feature = "serde_traits",
450 derive(serde_derive::Serialize, serde_derive::Deserialize)
451)]
452pub struct Rect<T: Coord = i32> {
453 top_left: Point<T>,
454 bottom_right: Point<T>,
455}
456
457pub type RectF = Rect<f32>;
458
459impl<T: Coord> Rect<T> {
460 pub fn new(top_left: Point<T>, bottom_right: Point<T>) -> Rect<T> {
461 Rect {
462 top_left,
463 bottom_right,
464 }
465 }
466
467 pub fn width(&self) -> T {
468 self.bottom_right.x - self.top_left.x
470 }
471
472 pub fn height(&self) -> T {
473 self.bottom_right.y - self.top_left.y
475 }
476
477 pub fn top(&self) -> T {
478 self.top_left.y
479 }
480
481 pub fn left(&self) -> T {
482 self.top_left.x
483 }
484
485 pub fn right(&self) -> T {
486 self.bottom_right.x
487 }
488
489 pub fn bottom(&self) -> T {
490 self.bottom_right.y
491 }
492
493 pub fn corners(&self) -> [Point<T>; 4] {
496 [
497 self.top_left(),
498 self.top_right(),
499 self.bottom_right(),
500 self.bottom_left(),
501 ]
502 }
503
504 pub fn top_left(&self) -> Point<T> {
506 self.top_left
507 }
508
509 pub fn top_right(&self) -> Point<T> {
511 Point::from_yx(self.top_left.y, self.bottom_right.x)
512 }
513
514 pub fn bottom_left(&self) -> Point<T> {
516 Point::from_yx(self.bottom_right.y, self.top_left.x)
517 }
518
519 pub fn bottom_right(&self) -> Point<T> {
521 self.bottom_right
522 }
523
524 pub fn left_edge(&self) -> Line<T> {
526 Line::from_endpoints(self.top_left(), self.bottom_left())
527 }
528
529 pub fn top_edge(&self) -> Line<T> {
531 Line::from_endpoints(self.top_left(), self.top_right())
532 }
533
534 pub fn right_edge(&self) -> Line<T> {
536 Line::from_endpoints(self.top_right(), self.bottom_right())
537 }
538
539 pub fn bottom_edge(&self) -> Line<T> {
541 Line::from_endpoints(self.bottom_left(), self.bottom_right())
542 }
543
544 pub fn tlbr(&self) -> [T; 4] {
546 [
547 self.top_left.y,
548 self.top_left.x,
549 self.bottom_right.y,
550 self.bottom_right.x,
551 ]
552 }
553
554 pub fn from_hw(height: T, width: T) -> Rect<T> {
556 Self::new(Point::default(), Point::from_yx(height, width))
557 }
558
559 pub fn from_tlbr(top: T, left: T, bottom: T, right: T) -> Rect<T> {
561 Self::new(Point::from_yx(top, left), Point::from_yx(bottom, right))
562 }
563
564 pub fn from_tlhw(top: T, left: T, height: T, width: T) -> Rect<T> {
566 Self::from_tlbr(top, left, top + height, left + width)
567 }
568
569 pub fn area(&self) -> T
571 where
572 T: std::ops::Mul<Output = T>,
573 {
574 self.width() * self.height()
575 }
576
577 pub fn tlhw(&self) -> [T; 4] {
579 [
580 self.top_left.y,
581 self.top_left.x,
582 self.height(),
583 self.width(),
584 ]
585 }
586
587 pub fn contains_point(&self, other: Point<T>) -> bool {
589 self.top() <= other.y
590 && self.bottom() >= other.y
591 && self.left() <= other.x
592 && self.right() >= other.x
593 }
594
595 pub fn is_empty(&self) -> bool {
597 self.right() <= self.left() || self.bottom() <= self.top()
598 }
599
600 pub fn adjust_tlbr(&self, top: T, left: T, bottom: T, right: T) -> Rect<T> {
602 Rect {
603 top_left: self.top_left.translate(top, left),
604 bottom_right: self.bottom_right.translate(bottom, right),
605 }
606 }
607
608 pub fn intersects(&self, other: Rect<T>) -> bool {
610 self.left_edge().vertical_overlap(other.left_edge()) > T::default()
611 && self.top_edge().horizontal_overlap(other.top_edge()) > T::default()
612 }
613
614 pub fn union(&self, other: Rect<T>) -> Rect<T> {
616 let t = min_or_lhs(self.top(), other.top());
617 let l = min_or_lhs(self.left(), other.left());
618 let b = max_or_lhs(self.bottom(), other.bottom());
619 let r = max_or_lhs(self.right(), other.right());
620 Rect::from_tlbr(t, l, b, r)
621 }
622
623 pub fn intersection(&self, other: Rect<T>) -> Rect<T> {
625 let t = max_or_lhs(self.top(), other.top());
626 let l = max_or_lhs(self.left(), other.left());
627 let b = min_or_lhs(self.bottom(), other.bottom());
628 let r = min_or_lhs(self.right(), other.right());
629 Rect::from_tlbr(t, l, b, r)
630 }
631
632 pub fn contains(&self, other: Rect<T>) -> bool {
634 self.union(other) == *self
635 }
636
637 pub fn clamp(&self, rect: Rect<T>) -> Rect<T> {
640 self.intersection(rect)
641 }
642
643 pub fn to_polygon(&self) -> Polygon<T, [Point<T>; 4]> {
644 Polygon::new(self.corners())
645 }
646}
647
648impl Rect<i32> {
649 pub fn center(&self) -> Point {
651 let y = (self.top_left.y + self.bottom_right.y) / 2;
652 let x = (self.top_left.x + self.bottom_right.x) / 2;
653 Point::from_yx(y, x)
654 }
655
656 pub fn iou(&self, other: Rect) -> f32 {
660 self.intersection(other).area() as f32 / self.union(other).area() as f32
661 }
662
663 pub fn to_f32(&self) -> RectF {
664 Rect::from_tlbr(
665 self.top_left.y as f32,
666 self.top_left.x as f32,
667 self.bottom_right.y as f32,
668 self.bottom_right.x as f32,
669 )
670 }
671}
672
673impl Rect<f32> {
674 pub fn center(&self) -> PointF {
676 let y = (self.top_left.y + self.bottom_right.y) / 2.;
677 let x = (self.top_left.x + self.bottom_right.x) / 2.;
678 Point::from_yx(y, x)
679 }
680
681 pub fn iou(&self, other: RectF) -> f32 {
685 self.intersection(other).area() / self.union(other).area()
686 }
687
688 pub fn integral_bounding_rect(&self) -> Rect<i32> {
691 Rect::from_tlbr(
692 self.top() as i32,
693 self.left() as i32,
694 self.bottom().ceil() as i32,
695 self.right().ceil() as i32,
696 )
697 }
698}
699
700impl<T: Coord> BoundingRect for Rect<T> {
701 type Coord = T;
702
703 fn bounding_rect(&self) -> Rect<T> {
704 *self
705 }
706}
707
708#[derive(Copy, Clone, Debug)]
714#[cfg_attr(
715 feature = "serde_traits",
716 derive(serde_derive::Serialize, serde_derive::Deserialize)
717)]
718pub struct RotatedRect {
719 center: PointF,
721
722 up: Vec2,
724
725 width: f32,
727
728 height: f32,
730}
731
732impl RotatedRect {
733 pub fn new(center: PointF, up_axis: Vec2, width: f32, height: f32) -> RotatedRect {
736 RotatedRect {
737 center,
738 up: up_axis.normalized(),
739 width,
740 height,
741 }
742 }
743
744 pub fn contains(&self, point: PointF) -> bool {
746 let height = self.height.max(1e-6);
749 let width = self.width.max(1e-6);
750
751 let ac = point.to_vec() - self.center.to_vec();
757 let ab = self.up * (height / 2.);
758 let up_proj = ac.dot(ab) / (height / 2.).powi(2);
759
760 let ad = self.up.perpendicular() * (width / 2.);
761 let cross_proj = ac.dot(ad) / (width / 2.).powi(2);
762
763 up_proj.abs() <= 1. && cross_proj.abs() <= 1.
764 }
765
766 pub fn expanded(&self, dw: f32, dh: f32) -> RotatedRect {
769 RotatedRect {
770 width: self.width + dw,
771 height: self.height + dh,
772 ..*self
773 }
774 }
775
776 pub fn corners(&self) -> [PointF; 4] {
782 let par_offset = self.up.perpendicular() * (self.width / 2.);
783 let perp_offset = self.up * (self.height / 2.);
784
785 let center = self.center.to_vec();
786 let coords: [Vec2; 4] = [
787 center - perp_offset - par_offset,
788 center - perp_offset + par_offset,
789 center + perp_offset + par_offset,
790 center + perp_offset - par_offset,
791 ];
792
793 coords.map(|c| Point::from_yx(c.y, c.x))
794 }
795
796 pub fn edges(&self) -> [LineF; 4] {
799 let corners = self.corners();
800 [
801 Line::from_endpoints(corners[0], corners[1]),
802 Line::from_endpoints(corners[1], corners[2]),
803 Line::from_endpoints(corners[2], corners[3]),
804 Line::from_endpoints(corners[3], corners[0]),
805 ]
806 }
807
808 pub fn center(&self) -> PointF {
810 self.center
811 }
812
813 pub fn up_axis(&self) -> Vec2 {
816 self.up
817 }
818
819 pub fn width(&self) -> f32 {
821 self.width
822 }
823
824 pub fn height(&self) -> f32 {
826 self.height
827 }
828
829 pub fn area(&self) -> f32 {
831 self.height * self.width
832 }
833
834 pub fn resize(&mut self, width: f32, height: f32) {
836 assert!(width >= 0. && height >= 0.);
837 self.width = width;
838 self.height = height;
839 }
840
841 pub fn intersects(&self, other: &RotatedRect) -> bool {
843 if !self.bounding_rect().intersects(other.bounding_rect()) {
844 return false;
845 }
846 let other_edges = other.edges();
847 self.edges()
848 .iter()
849 .any(|e| other_edges.iter().any(|oe| e.intersects(*oe)))
850 }
851
852 pub fn from_rect(r: RectF) -> RotatedRect {
855 RotatedRect::new(r.center(), Vec2::from_yx(1., 0.), r.width(), r.height())
856 }
857
858 pub fn orient_towards(&self, up: Vec2) -> RotatedRect {
861 let target_up = up.normalized();
862
863 let rot_90 = Vec2::from_xy(self.up.y, -self.up.x);
864 let rot_180 = Vec2::from_xy(-self.up.x, -self.up.y);
865 let rot_270 = Vec2::from_xy(-self.up.y, self.up.x);
866
867 let (rotation, _dotp) = [self.up, rot_90, rot_180, rot_270]
868 .map(|new_up| new_up.dot(target_up))
869 .into_iter()
870 .enumerate()
871 .max_by(|(_, a), (_, b)| a.total_cmp(b))
872 .unwrap_or((0, 0.));
873
874 match rotation {
875 0 => *self,
876 1 => RotatedRect::new(self.center, rot_90, self.height, self.width),
877 2 => RotatedRect::new(self.center, rot_180, self.width, self.height),
878 3 => RotatedRect::new(self.center, rot_270, self.height, self.width),
879 _ => unreachable!(),
880 }
881 }
882}
883
884impl BoundingRect for RotatedRect {
885 type Coord = f32;
886
887 fn bounding_rect(&self) -> RectF {
888 let corners = self.corners();
889
890 let mut xs = corners.map(|p| p.x);
891 xs.sort_unstable_by(f32::total_cmp);
892
893 let mut ys = corners.map(|p| p.y);
894 ys.sort_unstable_by(f32::total_cmp);
895
896 Rect::from_tlbr(ys[0], xs[0], ys[3], xs[3])
897 }
898}
899
900pub fn bounding_rect<'a, Shape: 'a + BoundingRect, I: Iterator<Item = &'a Shape>>(
904 objects: I,
905) -> Option<Rect<Shape::Coord>>
906where
907 Shape::Coord: Coord,
908{
909 objects.fold(None, |bounding_rect, shape| {
910 let sbr = shape.bounding_rect();
911 bounding_rect.map(|br| br.union(sbr)).or(Some(sbr))
912 })
913}
914
915#[derive(Copy, Clone, Debug)]
920#[cfg_attr(
921 feature = "serde_traits",
922 derive(serde_derive::Serialize, serde_derive::Deserialize)
923)]
924pub struct Polygon<T: Coord = i32, S: AsRef<[Point<T>]> = Vec<Point<T>>> {
925 points: S,
926
927 element_type: PhantomData<T>,
929}
930
931pub type PolygonF<S = Vec<PointF>> = Polygon<f32, S>;
932
933impl<T: Coord, S: AsRef<[Point<T>]>> Polygon<T, S> {
934 pub fn new(points: S) -> Polygon<T, S> {
936 Polygon {
937 points,
938 element_type: PhantomData,
939 }
940 }
941
942 pub fn borrow(&self) -> Polygon<T, &[Point<T>]> {
944 Polygon::new(self.points.as_ref())
945 }
946
947 pub fn edges(&self) -> impl Iterator<Item = Line<T>> + '_ {
949 self.points
950 .as_ref()
951 .iter()
952 .zip(self.points.as_ref().iter().cycle().skip(1))
953 .map(|(p0, p1)| Line::from_endpoints(*p0, *p1))
954 }
955
956 pub fn vertices(&self) -> &[Point<T>] {
958 self.points.as_ref()
959 }
960
961 pub fn to_owned(&self) -> Polygon<T> {
963 Polygon::new(self.vertices().to_vec())
964 }
965}
966
967impl<S: AsRef<[Point]>> Polygon<i32, S> {
968 pub fn fill_iter(&self) -> FillIter {
987 FillIter::new(self.borrow())
988 }
989
990 pub fn contains_pixel(&self, p: Point) -> bool {
998 let mut edge_crossings = 0;
999
1000 for edge in self.edges() {
1001 let (min_y, max_y) = sort_pair((edge.start.y, edge.end.y));
1002
1003 if min_y == max_y {
1005 continue;
1006 }
1007
1008 if p.y < min_y || p.y >= max_y {
1010 continue;
1011 }
1012
1013 let edge_down = edge.downwards();
1015 let start_to_end = Vec2::from_yx(
1016 (edge_down.end.y - edge_down.start.y) as f32,
1017 (edge_down.end.x - edge_down.start.x) as f32,
1018 );
1019 let start_to_point = Vec2::from_yx(
1020 (p.y - edge_down.start.y) as f32,
1021 (p.x - edge_down.start.x) as f32,
1022 );
1023
1024 let cross = start_to_end.cross_product_norm(start_to_point);
1025 if cross > 0. {
1026 edge_crossings += 1;
1028 }
1029 }
1030
1031 edge_crossings % 2 == 1
1032 }
1033
1034 pub fn is_simple(&self) -> bool {
1036 for (i, e1) in self.edges().enumerate() {
1039 for (j, e2) in self.edges().enumerate() {
1040 if i != j && e1.intersects(e2) {
1041 let intersection_at_endpoints = e1.start == e2.start
1042 || e1.start == e2.end
1043 || e1.end == e2.start
1044 || e1.end == e2.end;
1045 if !intersection_at_endpoints {
1046 return false;
1047 }
1048 }
1049 }
1050 }
1051 true
1052 }
1053}
1054
1055macro_rules! impl_bounding_rect_for_polygon {
1056 ($coord:ty) => {
1057 impl<S: AsRef<[Point<$coord>]>> BoundingRect for Polygon<$coord, S> {
1058 type Coord = $coord;
1059
1060 fn bounding_rect(&self) -> Rect<$coord> {
1061 let mut min_x = <$coord>::MAX;
1062 let mut max_x = <$coord>::MIN;
1063 let mut min_y = <$coord>::MAX;
1064 let mut max_y = <$coord>::MIN;
1065
1066 for p in self.points.as_ref() {
1067 min_x = min_x.min(p.x);
1068 max_x = max_x.max(p.x);
1069 min_y = min_y.min(p.y);
1070 max_y = max_y.max(p.y);
1071 }
1072
1073 Rect::from_tlbr(min_y, min_x, max_y, max_x)
1074 }
1075 }
1076 };
1077}
1078
1079impl_bounding_rect_for_polygon!(i32);
1080impl_bounding_rect_for_polygon!(f32);
1081
1082#[cfg_attr(
1088 feature = "serde_traits",
1089 derive(serde_derive::Serialize, serde_derive::Deserialize)
1090)]
1091pub struct Polygons {
1092 points: Vec<Point>,
1093
1094 polygons: Vec<Range<usize>>,
1096}
1097
1098impl Polygons {
1099 pub fn new() -> Polygons {
1101 Polygons {
1102 points: Vec::new(),
1103 polygons: Vec::new(),
1104 }
1105 }
1106
1107 pub fn push(&mut self, points: &[Point]) {
1109 let range = self.points.len()..self.points.len() + points.len();
1110 self.polygons.push(range);
1111 self.points.extend_from_slice(points);
1112 }
1113
1114 pub fn len(&self) -> usize {
1116 self.polygons.len()
1117 }
1118
1119 pub fn is_empty(&self) -> bool {
1121 self.polygons.is_empty()
1122 }
1123
1124 pub fn iter(&self) -> PolygonsIter<'_> {
1126 PolygonsIter {
1127 points: &self.points,
1128 polygons: self.polygons.iter(),
1129 }
1130 }
1131}
1132
1133impl Default for Polygons {
1134 fn default() -> Self {
1135 Self::new()
1136 }
1137}
1138
1139pub struct PolygonsIter<'a> {
1141 points: &'a [Point],
1142 polygons: Iter<'a, Range<usize>>,
1143}
1144
1145impl<'a> Iterator for PolygonsIter<'a> {
1146 type Item = &'a [Point];
1147
1148 fn next(&mut self) -> Option<Self::Item> {
1149 if let Some(range) = self.polygons.next() {
1150 Some(&self.points[range.clone()])
1151 } else {
1152 None
1153 }
1154 }
1155
1156 fn size_hint(&self) -> (usize, Option<usize>) {
1157 self.polygons.size_hint()
1158 }
1159}
1160
1161impl ExactSizeIterator for PolygonsIter<'_> {}
1162
1163#[cfg(test)]
1164mod tests {
1165 use rten_tensor::test_util::ApproxEq;
1166 use rten_tensor::{MatrixLayout, NdTensor};
1167 use rten_testing::TestCases;
1168
1169 use crate::Vec2;
1170 use crate::tests::{points_from_coords, points_from_n_coords};
1171
1172 use super::{BoundingRect, Line, Point, PointF, Polygon, Rect, RotatedRect, bounding_rect};
1173
1174 #[test]
1175 fn test_bounding_rect() {
1176 let rects = [Rect::from_tlbr(0, 0, 5, 5), Rect::from_tlbr(10, 10, 15, 18)];
1177 assert_eq!(
1178 bounding_rect(rects.iter()),
1179 Some(Rect::from_tlbr(0, 0, 15, 18))
1180 );
1181
1182 let rects: &[Rect] = &[];
1183 assert_eq!(bounding_rect(rects.iter()), None);
1184 }
1185
1186 #[test]
1187 fn test_line_distance() {
1188 #[derive(Debug)]
1189 struct Case {
1190 start: Point,
1191 end: Point,
1192 point: Point,
1193 dist: f32,
1194 }
1195
1196 let cases = [
1198 Case {
1200 start: Point::default(),
1201 end: Point::default(),
1202 point: Point::from_yx(3, 4),
1203 dist: 5.,
1204 },
1205 Case {
1207 start: Point::from_yx(5, 2),
1208 end: Point::from_yx(5, 10),
1209 point: Point::from_yx(8, 5),
1210 dist: 3.,
1211 },
1212 Case {
1214 start: Point::from_yx(5, 3),
1215 end: Point::from_yx(10, 3),
1216 point: Point::from_yx(8, 5),
1217 dist: 2.,
1218 },
1219 Case {
1221 start: Point::default(),
1222 end: Point::from_yx(5, 5),
1223 point: Point::from_yx(4, 0),
1224 dist: (8f32).sqrt(), },
1226 Case {
1228 start: Point::default(),
1229 end: Point::from_yx(5, -5),
1230 point: Point::from_yx(4, 0),
1231 dist: (8f32).sqrt(), },
1233 Case {
1235 start: Point::default(),
1236 end: Point::from_yx(5, 5),
1237 point: Point::from_yx(0, 4),
1238 dist: (8f32).sqrt(), },
1240 Case {
1242 start: Point::from_yx(5, 2),
1243 end: Point::from_yx(5, 5),
1244 point: Point::from_yx(5, 10),
1245 dist: 5.,
1246 },
1247 ];
1248
1249 cases.test_each(|case| {
1250 let line = Line::from_endpoints(case.start, case.end);
1251 let dist = line.distance(case.point);
1252 assert!(
1253 dist.approx_eq(&case.dist),
1254 "line {:?}, {:?} point {:?} actual {} expected {}",
1255 line.start,
1256 line.end,
1257 case.point,
1258 dist,
1259 case.dist
1260 );
1261 });
1262 }
1263
1264 #[test]
1265 fn test_line_downwards() {
1266 #[derive(Debug)]
1267 struct Case {
1268 input: Line,
1269 down: Line,
1270 }
1271 let cases = [
1272 Case {
1273 input: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(5, 5)),
1274 down: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(5, 5)),
1275 },
1276 Case {
1277 input: Line::from_endpoints(Point::from_yx(5, 5), Point::from_yx(0, 0)),
1278 down: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(5, 5)),
1279 },
1280 ];
1281 cases.test_each(|case| {
1282 assert_eq!(case.input.downwards(), case.down);
1283 })
1284 }
1285
1286 #[test]
1287 fn test_line_rightwards() {
1288 #[derive(Debug)]
1289 struct Case {
1290 input: Line,
1291 right: Line,
1292 }
1293 let cases = [
1294 Case {
1295 input: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(5, 5)),
1296 right: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(5, 5)),
1297 },
1298 Case {
1299 input: Line::from_endpoints(Point::from_yx(5, 5), Point::from_yx(0, 0)),
1300 right: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(5, 5)),
1301 },
1302 ];
1303 cases.test_each(|case| {
1304 assert_eq!(case.input.rightwards(), case.right);
1305 })
1306 }
1307
1308 fn line_from_coords(coords: [i32; 4]) -> Line {
1310 Line::from_endpoints(
1311 Point::from_yx(coords[0], coords[1]),
1312 Point::from_yx(coords[2], coords[3]),
1313 )
1314 }
1315
1316 #[test]
1317 fn test_line_intersects() {
1318 #[derive(Debug)]
1319 struct Case {
1320 a: Line,
1321 b: Line,
1322 expected: bool,
1323 }
1324
1325 let cases = [
1326 Case {
1328 a: line_from_coords([0, 5, 10, 5]),
1329 b: line_from_coords([5, 0, 5, 10]),
1330 expected: true,
1331 },
1332 Case {
1334 a: line_from_coords([0, 0, 10, 10]),
1335 b: line_from_coords([10, 0, 0, 10]),
1336 expected: true,
1337 },
1338 Case {
1340 a: line_from_coords([0, 5, 10, 5]),
1341 b: line_from_coords([5, 6, 5, 10]),
1342 expected: false,
1343 },
1344 Case {
1345 a: line_from_coords([0, 5, 10, 5]),
1346 b: line_from_coords([5, 10, 5, 6]),
1347 expected: false,
1348 },
1349 Case {
1351 a: line_from_coords([0, 5, 5, 5]),
1352 b: line_from_coords([5, 0, 5, 10]),
1353 expected: true,
1354 },
1355 Case {
1357 a: line_from_coords([1, 1, 2, 3]),
1358 b: line_from_coords([4, 1, -1, 2]),
1359 expected: true,
1360 },
1361 Case {
1363 a: line_from_coords([0, 5, 0, 10]),
1364 b: line_from_coords([2, 5, 2, 10]),
1365 expected: false,
1366 },
1367 Case {
1369 a: line_from_coords([0, 5, 0, 10]),
1370 b: line_from_coords([0, 5, 0, 10]),
1371 expected: false,
1372 },
1373 ];
1374
1375 cases.test_each(|case| {
1376 assert_eq!(case.a.intersects(case.b), case.expected);
1377
1378 assert_eq!(case.b.intersects(case.a), case.expected);
1380 })
1381 }
1382
1383 #[test]
1384 fn test_line_is_horizontal() {
1385 assert_eq!(
1386 Line::from_endpoints(Point::from_yx(5, 0), Point::from_yx(5, 10)).is_horizontal(),
1387 true
1388 );
1389 assert_eq!(
1390 Line::from_endpoints(Point::from_yx(5, 0), Point::from_yx(6, 10)).is_horizontal(),
1391 false
1392 );
1393 }
1394
1395 #[test]
1396 fn test_line_overlap() {
1397 #[derive(Debug)]
1398 struct Case {
1399 a: (i32, i32),
1400 b: (i32, i32),
1401 overlap: i32,
1402 }
1403
1404 let cases = [
1405 Case {
1407 a: (0, 10),
1408 b: (15, 20),
1409 overlap: 0,
1410 },
1411 Case {
1413 a: (0, 10),
1414 b: (5, 15),
1415 overlap: 5,
1416 },
1417 Case {
1419 a: (0, 10),
1420 b: (2, 8),
1421 overlap: 6,
1422 },
1423 Case {
1425 a: (0, 5),
1426 b: (0, 10),
1427 overlap: 5,
1428 },
1429 ];
1430
1431 cases.test_each(|case| {
1432 let a = Line::from_endpoints(Point::from_yx(0, case.a.0), Point::from_yx(0, case.a.1));
1434 let b = Line::from_endpoints(Point::from_yx(0, case.b.0), Point::from_yx(0, case.b.1));
1435 assert_eq!(a.horizontal_overlap(b), case.overlap);
1436 assert_eq!(b.horizontal_overlap(a), case.overlap);
1437
1438 let a = Line::from_endpoints(Point::from_yx(case.a.0, 0), Point::from_yx(case.a.1, 0));
1440 let b = Line::from_endpoints(Point::from_yx(case.b.0, 0), Point::from_yx(case.b.1, 0));
1441 assert_eq!(a.vertical_overlap(b), case.overlap);
1442 assert_eq!(b.vertical_overlap(a), case.overlap);
1443 })
1444 }
1445
1446 #[test]
1447 fn test_line_width_height() {
1448 #[derive(Debug)]
1449 struct Case {
1450 line: Line,
1451 width: i32,
1452 height: i32,
1453 }
1454
1455 let cases = [
1456 Case {
1457 line: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(5, 3)),
1458 width: 3,
1459 height: 5,
1460 },
1461 Case {
1462 line: Line::from_endpoints(Point::from_yx(5, 3), Point::from_yx(0, 0)),
1463 width: -3,
1464 height: -5,
1465 },
1466 ];
1467
1468 cases.test_each(|case| {
1469 assert_eq!(case.line.width(), case.width);
1470 assert_eq!(case.line.height(), case.height);
1471 })
1472 }
1473
1474 #[test]
1475 fn test_line_y_for_x_and_x_for_y() {
1476 #[derive(Debug)]
1477 struct Case {
1478 line: Line,
1479
1480 points: Vec<(f32, Option<f32>)>,
1482 }
1483
1484 let cases = [
1485 Case {
1487 line: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(1, 1)),
1488 points: vec![
1489 (-1., None),
1490 (0., Some(0.)),
1491 (0.5, Some(0.5)),
1492 (1., Some(1.)),
1493 (1.2, None),
1494 ],
1495 },
1496 Case {
1498 line: Line::from_endpoints(Point::from_yx(0, 1), Point::from_yx(1, 2)),
1499 points: vec![
1500 (-1., None),
1501 (1., Some(0.)),
1502 (1.5, Some(0.5)),
1503 (2., Some(1.)),
1504 (2.2, None),
1505 ],
1506 },
1507 Case {
1509 line: Line::from_endpoints(Point::from_yx(0, 1), Point::from_yx(0, 2)),
1510 points: vec![(-1., None), (1., Some(0.)), (2., Some(0.)), (3., None)],
1511 },
1512 Case {
1514 line: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(2, 0)),
1515 points: vec![(-1., None), (0., None), (1., None)],
1516 },
1517 ];
1518
1519 cases.test_each(|case| {
1520 for &(x, expected_y) in &case.points {
1521 assert_eq!(case.line.to_f32().y_for_x(x), expected_y);
1522 if let Some(y) = expected_y {
1523 assert_eq!(
1524 case.line.to_f32().x_for_y(y),
1525 if case.line.is_horizontal() {
1526 None
1527 } else {
1528 Some(x)
1529 }
1530 );
1531 }
1532 }
1533 })
1534 }
1535
1536 #[test]
1537 fn test_point_coord() {
1538 assert_eq!(Point::from_yx(3, 5).coord(), [3, 5]);
1539 }
1540
1541 #[test]
1542 #[should_panic(expected = "Coordinates are negative")]
1543 fn test_point_coord_negative() {
1544 Point::from_yx(-1, -1).coord();
1545 }
1546
1547 #[test]
1548 fn test_polygon_contains_pixel() {
1549 #[derive(Debug)]
1550 struct Case {
1551 poly: Polygon,
1552 }
1553
1554 let cases = [
1555 Case {
1557 poly: Polygon::new(Vec::new()),
1558 },
1559 Case {
1561 poly: Rect::from_tlbr(0, 0, 0, 5).to_polygon().to_owned(),
1562 },
1563 Case {
1565 poly: Rect::from_tlbr(2, 2, 5, 5).to_polygon().to_owned(),
1566 },
1567 Case {
1568 poly: Rect::from_tlbr(0, 0, 1, 1).to_polygon().to_owned(),
1569 },
1570 Case {
1572 poly: Rect::from_tlbr(5, 5, 2, 2).to_polygon().to_owned(),
1573 },
1574 Case {
1576 poly: Polygon::new(points_from_coords(&[[0, 2], [3, 0], [3, 4]])),
1577 },
1578 Case {
1579 poly: Polygon::new(points_from_coords(&[[1, 1], [4, 3], [6, 9]])),
1580 },
1581 ];
1582
1583 cases.test_each(|case| {
1584 let grid_size = case
1587 .poly
1588 .vertices()
1589 .iter()
1590 .fold([0, 0], |[h, w], point| {
1591 [h.max(point.y) + 2, w.max(point.x) + 2]
1592 })
1593 .map(|x| x as usize);
1594
1595 let mut fill_grid = NdTensor::zeros(grid_size);
1596 let mut contains_pixel_grid = NdTensor::zeros(grid_size);
1597
1598 for p in case.poly.fill_iter() {
1602 fill_grid[p.coord()] = 1;
1603 }
1604 for y in 0..contains_pixel_grid.rows() {
1605 for x in 0..contains_pixel_grid.cols() {
1606 if case.poly.contains_pixel(Point::from_yx(y as i32, x as i32)) {
1607 contains_pixel_grid[[y, x]] = 1;
1608 }
1609 }
1610 }
1611
1612 for y in 0..fill_grid.rows() {
1613 for x in 0..fill_grid.cols() {
1614 assert_eq!(fill_grid[[y, x]], contains_pixel_grid[[y, x]]);
1615 }
1616 }
1617 })
1618 }
1619
1620 #[test]
1621 fn test_polygon_is_simple() {
1622 #[derive(Debug)]
1623 struct Case {
1624 poly: Polygon,
1625 simple: bool,
1626 }
1627
1628 let cases = [
1629 Case {
1631 poly: Rect::from_tlbr(0, 0, 10, 10).to_polygon().to_owned(),
1632 simple: true,
1633 },
1634 Case {
1636 poly: Polygon::new(points_from_coords(&[[0, 0], [0, 10], [10, 10], [-2, 2]])),
1637 simple: false,
1638 },
1639 ];
1640
1641 cases.test_each(|case| assert_eq!(case.poly.is_simple(), case.simple))
1642 }
1643
1644 #[test]
1645 fn test_polygon_fill_iter() {
1646 #[derive(Debug)]
1647 struct Case {
1648 vertices: Vec<Point>,
1649 filled: Vec<Point>,
1650 }
1651
1652 let cases = [
1653 Case {
1655 vertices: Vec::new(),
1656 filled: Vec::new(),
1657 },
1658 Case {
1660 vertices: points_from_coords(&[[0, 0], [5, 5]]),
1661 filled: Vec::new(),
1662 },
1663 Case {
1665 vertices: Rect::from_tlbr(0, 0, 3, 3).to_polygon().vertices().to_vec(),
1666 filled: points_from_coords(&[
1667 [0, 0],
1668 [0, 1],
1669 [0, 2],
1670 [1, 0],
1671 [1, 1],
1672 [1, 2],
1673 [2, 0],
1674 [2, 1],
1675 [2, 2],
1676 ]),
1677 },
1678 Case {
1680 vertices: points_from_coords(&[[0, 0], [0, 4], [3, 4]]),
1681 filled: points_from_coords(&[
1682 [0, 0],
1683 [0, 1],
1684 [0, 2],
1685 [0, 3],
1686 [1, 2],
1687 [1, 3],
1688 [2, 3],
1689 ]),
1690 },
1691 ];
1692
1693 cases.test_each(|case| {
1694 let poly = Polygon::new(&case.vertices);
1695 let filled: Vec<_> = poly.fill_iter().collect();
1696 assert_eq!(filled, case.filled);
1697 })
1698 }
1699
1700 #[test]
1701 fn test_rect_clamp() {
1702 #[derive(Debug)]
1703 struct Case {
1704 rect: Rect,
1705 boundary: Rect,
1706 expected: Rect,
1707 }
1708
1709 let cases = [
1710 Case {
1711 rect: Rect::from_tlbr(-5, -10, 100, 200),
1712 boundary: Rect::from_tlbr(0, 0, 50, 100),
1713 expected: Rect::from_tlbr(0, 0, 50, 100),
1714 },
1715 Case {
1716 rect: Rect::from_tlbr(5, 10, 40, 80),
1717 boundary: Rect::from_tlbr(0, 0, 50, 100),
1718 expected: Rect::from_tlbr(5, 10, 40, 80),
1719 },
1720 ];
1721
1722 cases.test_each(|case| {
1723 assert_eq!(case.rect.clamp(case.boundary), case.expected);
1724 })
1725 }
1726
1727 #[test]
1728 fn test_rect_contains_point() {
1729 let r = Rect::from_tlbr(5, 5, 10, 10);
1730
1731 assert_eq!(r.contains_point(Point::from_yx(0, 0)), false);
1733 assert_eq!(r.contains_point(Point::from_yx(12, 12)), false);
1734
1735 assert_eq!(r.contains_point(Point::from_yx(8, 8)), true);
1737
1738 assert_eq!(r.contains_point(Point::from_yx(5, 5)), true);
1740 assert_eq!(r.contains_point(Point::from_yx(10, 10)), true);
1741 }
1742
1743 #[test]
1744 fn test_rect_tlbr() {
1745 let r = Rect::from_tlbr(0, 1, 2, 3);
1746 assert_eq!(r.tlbr(), [0, 1, 2, 3]);
1747 }
1748
1749 #[test]
1750 fn test_rotated_rect_contains() {
1751 #[derive(Debug)]
1752 struct Case {
1753 rrect: RotatedRect,
1754 }
1755
1756 let cases = [
1757 Case {
1759 rrect: RotatedRect::new(PointF::from_yx(0., 0.), Vec2::from_yx(1., 0.), 10., 5.),
1760 },
1761 Case {
1763 rrect: RotatedRect::new(PointF::from_yx(0., 0.), Vec2::from_yx(-1., 0.), 10., 5.),
1764 },
1765 Case {
1767 rrect: RotatedRect::new(PointF::from_yx(0., 0.), Vec2::from_yx(0.5, 0.5), 10., 5.),
1768 },
1769 ];
1770
1771 cases.test_each(|case| {
1772 let &Case { rrect: r } = case;
1773
1774 assert!(r.contains(r.center()));
1775
1776 for c in r.expanded(-1e-5, -1e-5).corners() {
1778 assert!(r.contains(c));
1779 }
1780
1781 for c in r.expanded(1e-5, 1e-5).corners() {
1783 assert!(!r.contains(c));
1784 }
1785 })
1786 }
1787
1788 #[test]
1789 fn test_rotated_rect_corners() {
1790 let r = RotatedRect::new(PointF::from_yx(5., 5.), Vec2::from_yx(1., 0.), 5., 5.);
1791 let expected = points_from_n_coords([[2.5, 2.5], [2.5, 7.5], [7.5, 7.5], [7.5, 2.5]]);
1792 assert_eq!(r.corners(), expected);
1793 }
1794
1795 #[test]
1796 fn test_rotated_rect_expanded() {
1797 let r = RotatedRect::new(PointF::from_yx(0., 0.), Vec2::from_yx(1., 0.), 10., 5.);
1798 let r = r.expanded(2., 3.);
1799 assert_eq!(r.width(), 12.);
1800 assert_eq!(r.height(), 8.);
1801 }
1802
1803 #[test]
1804 fn test_rotated_rect_from_rect() {
1805 let r = Rect::from_tlbr(5., 10., 50., 40.);
1806 let rr = RotatedRect::from_rect(r);
1807 assert_eq!(rr.width(), r.width());
1808 assert_eq!(rr.height(), r.height());
1809 assert_eq!(rr.bounding_rect(), r);
1810 }
1811
1812 #[test]
1813 fn test_rotated_rect_intersects() {
1814 #[derive(Debug)]
1815 struct Case {
1816 a: RotatedRect,
1817 b: RotatedRect,
1818 bounding_rect_intersects: bool,
1819 intersects: bool,
1820 }
1821
1822 let up_vec = Vec2::from_yx(-1., 0.);
1823 let up_left_vec = Vec2::from_yx(-1., -1.);
1824
1825 let cases = [
1826 Case {
1828 a: RotatedRect::new(PointF::from_yx(5., 5.), up_vec, 5., 5.),
1829 b: RotatedRect::new(PointF::from_yx(5., 5.), up_vec, 5., 5.),
1830 bounding_rect_intersects: true,
1831 intersects: true,
1832 },
1833 Case {
1835 a: RotatedRect::new(PointF::from_yx(5., 5.), up_vec, 5., 5.),
1836 b: RotatedRect::new(PointF::from_yx(5., 11.), up_vec, 5., 5.),
1837 bounding_rect_intersects: false,
1838 intersects: false,
1839 },
1840 Case {
1843 a: RotatedRect::new(PointF::from_yx(5., 5.), up_left_vec, 12., 1.),
1844 b: RotatedRect::new(PointF::from_yx(9., 9.), up_vec, 1., 1.),
1845 bounding_rect_intersects: true,
1846 intersects: false,
1847 },
1848 ];
1849
1850 cases.test_each(|case| {
1851 assert_eq!(
1852 case.a.bounding_rect().intersects(case.b.bounding_rect()),
1853 case.bounding_rect_intersects
1854 );
1855 assert_eq!(case.a.intersects(&case.b), case.intersects);
1856 assert_eq!(case.b.intersects(&case.a), case.intersects);
1858 })
1859 }
1860
1861 #[test]
1862 fn test_rotated_rect_normalizes_up_vector() {
1863 let up_axis = Vec2::from_yx(1., 2.);
1865 let center = PointF::from_yx(0., 0.);
1866 let rect = RotatedRect::new(center, up_axis, 2., 3.);
1867 assert!(rect.up_axis().length().approx_eq(&1.));
1868 }
1869
1870 #[test]
1871 fn test_rotated_rect_orient_towards() {
1872 let up_axis = Vec2::from_yx(-1., 0.);
1873 let center = PointF::from_yx(0., 0.);
1874 let rect = RotatedRect::new(center, up_axis, 2., 3.);
1875
1876 let sorted_corners = |rect: RotatedRect| {
1877 let mut corners = rect
1878 .corners()
1879 .map(|c| Point::from_yx(c.y.round() as i32, c.x.round() as i32));
1880 corners.sort_by_key(|p| (p.y, p.x));
1881 corners
1882 };
1883
1884 let targets = [
1885 Vec2::from_yx(-1., 0.),
1886 Vec2::from_yx(0., 1.),
1887 Vec2::from_yx(1., 0.),
1888 Vec2::from_yx(0., -1.),
1889 ];
1890 for target in targets {
1891 let oriented = rect.orient_towards(target);
1892 assert_eq!(sorted_corners(oriented), sorted_corners(rect));
1893 if target != up_axis {
1894 assert_ne!(oriented.corners(), rect.corners());
1895 }
1896 assert_eq!(oriented.up_axis(), target);
1897 }
1898 }
1899
1900 #[test]
1901 fn test_rotated_rect_resize() {
1902 let mut r = RotatedRect::new(PointF::from_yx(5., 5.), Vec2::from_yx(1., 0.), 5., 5.);
1903 assert_eq!(r.area(), 25.);
1904
1905 r.resize(3., 7.);
1906
1907 assert_eq!(r.width(), 3.);
1908 assert_eq!(r.height(), 7.);
1909 assert_eq!(r.area(), 21.);
1910 }
1911}