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(feature = "serde_traits", derive(serde::Serialize, serde::Deserialize))]
60pub struct Point<T: Coord = i32> {
61 pub x: T,
62 pub y: T,
63}
64
65pub type PointF = Point<f32>;
66
67impl<T: Coord> Point<T> {
68 pub fn from_yx(y: T, x: T) -> Self {
70 Point { y, x }
71 }
72
73 pub fn move_to(&mut self, y: T, x: T) {
75 self.y = y;
76 self.x = x;
77 }
78
79 pub fn translate(self, y: T, x: T) -> Self {
80 Point {
81 y: self.y + y,
82 x: self.x + x,
83 }
84 }
85
86 pub fn move_by(&mut self, y: T, x: T) {
87 *self = self.translate(y, x);
88 }
89}
90
91impl Point<f32> {
92 pub fn distance(self, other: Self) -> f32 {
93 self.vec_to(other).length()
94 }
95
96 pub fn vec_to(self, other: Self) -> Vec2 {
98 let dx = other.x - self.x;
99 let dy = other.y - self.y;
100 Vec2::from_xy(dx, dy)
101 }
102
103 pub fn to_vec(self) -> Vec2 {
105 Vec2::from_xy(self.x, self.y)
106 }
107}
108
109impl Point<i32> {
110 pub fn coord(self) -> [usize; 2] {
115 assert!(self.y >= 0 && self.x >= 0, "Coordinates are negative");
116 [self.y as usize, self.x as usize]
117 }
118
119 pub fn neighbors(self) -> [Point; 8] {
122 [
123 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), ]
132 }
133
134 pub fn to_f32(self) -> Point<f32> {
135 Point {
136 x: self.x as f32,
137 y: self.y as f32,
138 }
139 }
140
141 pub fn distance(self, other: Point<i32>) -> f32 {
142 self.to_f32().distance(other.to_f32())
143 }
144}
145
146impl<T: Coord> fmt::Debug for Point<T> {
147 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148 write!(f, "({}, {})", self.y, self.x)
149 }
150}
151
152fn overlap<T: Coord>(a: (T, T), b: (T, T)) -> T {
157 let a = sort_pair(a);
158 let b = sort_pair(b);
159 let ((_a_start, a_end), (b_start, b_end)) = sort_pair((a, b));
160
161 let min_overlap = T::default();
162 let max_overlap = b_end - b_start;
163 let overlap = a_end - b_start;
164
165 if max_overlap.is_nan() {
168 return max_overlap;
169 }
170
171 if overlap < min_overlap {
172 min_overlap
173 } else if overlap > max_overlap {
174 max_overlap
175 } else {
176 overlap
177 }
178}
179
180fn sort_pair<T: PartialOrd>(pair: (T, T)) -> (T, T) {
183 if pair.0 > pair.1 {
184 (pair.1, pair.0)
185 } else {
186 pair
187 }
188}
189
190#[derive(Copy, Clone, PartialEq)]
192#[cfg_attr(feature = "serde_traits", derive(serde::Serialize, serde::Deserialize))]
193pub struct Line<T: Coord = i32> {
194 pub start: Point<T>,
195 pub end: Point<T>,
196}
197
198pub type LineF = Line<f32>;
199
200impl<T: Coord> fmt::Debug for Line<T> {
201 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
202 write!(f, "{:?} -> {:?}", self.start, self.end)
203 }
204}
205
206impl<T: Coord> Line<T> {
207 pub fn from_endpoints(start: Point<T>, end: Point<T>) -> Line<T> {
208 Line { start, end }
209 }
210
211 pub fn is_empty(&self) -> bool {
213 self.start == self.end
214 }
215
216 pub fn width(&self) -> T {
219 self.end.x - self.start.x
220 }
221
222 pub fn height(&self) -> T {
225 self.end.y - self.start.y
226 }
227
228 pub fn is_horizontal(&self) -> bool {
231 self.start.y == self.end.y
232 }
233
234 pub fn reverse(&self) -> Line<T> {
236 Line::from_endpoints(self.end, self.start)
237 }
238
239 pub fn downwards(&self) -> Line<T> {
242 if self.start.y <= self.end.y {
243 *self
244 } else {
245 self.reverse()
246 }
247 }
248
249 pub fn rightwards(&self) -> Line<T> {
252 if self.start.x <= self.end.x {
253 *self
254 } else {
255 self.reverse()
256 }
257 }
258
259 pub fn vertical_overlap(&self, other: Line<T>) -> T {
262 overlap((self.start.y, self.end.y), (other.start.y, other.end.y))
263 }
264
265 pub fn horizontal_overlap(&self, other: Line<T>) -> T {
268 overlap((self.start.x, self.end.x), (other.start.x, other.end.x))
269 }
270}
271
272impl Line<f32> {
273 pub fn distance(&self, p: PointF) -> f32 {
276 if self.is_empty() {
277 return self.start.distance(p);
278 }
279
280 let ab = self.start.vec_to(self.end);
287 let ac = self.start.vec_to(p);
288 let scalar_proj = ac.dot(ab) / (ab.length() * ab.length());
289
290 if scalar_proj <= 0. {
291 self.start.distance(p)
293 } else if scalar_proj >= 1. {
294 self.end.distance(p)
296 } else {
297 let intercept_x = self.start.x + ab.x * scalar_proj;
298 let intercept_y = self.start.y + ab.y * scalar_proj;
299 let proj_line = Vec2::from_yx(intercept_y - p.y, intercept_x - p.x);
300 proj_line.length()
301 }
302 }
303
304 pub fn intersects(&self, other: Line<f32>) -> bool {
309 let (x1, x2) = (self.start.x, self.end.x);
312 let (y1, y2) = (self.start.y, self.end.y);
313 let (x3, x4) = (other.start.x, other.end.x);
314 let (y3, y4) = (other.start.y, other.end.y);
315
316 let a = x2 - x1;
332 let b = -(x4 - x3);
333 let c = y2 - y1;
334 let d = -(y4 - y3);
335
336 let b0 = x3 - x1;
337 let b1 = y3 - y1;
338
339 let det_a = a * d - b * c;
340 if det_a == 0. {
341 return false;
343 }
344 let det_a0 = b0 * d - b * b1;
345 let det_a1 = a * b1 - b0 * c;
346
347 let s_ok = (det_a0 >= 0.) == (det_a > 0.) && det_a0.abs() <= det_a.abs();
351 let t_ok = (det_a1 >= 0.) == (det_a > 0.) && det_a1.abs() <= det_a.abs();
352
353 s_ok && t_ok
354 }
355}
356
357impl Line<f32> {
358 pub fn center(&self) -> PointF {
360 let cy = (self.start.y + self.end.y) / 2.;
361 let cx = (self.start.x + self.end.x) / 2.;
362 Point::from_yx(cy, cx)
363 }
364
365 fn slope_intercept(&self) -> Option<(f32, f32)> {
368 let dx = self.end.x - self.start.x;
369 if dx == 0. {
370 return None;
371 }
372 let slope = (self.end.y - self.start.y) / dx;
373 let intercept = self.start.y - slope * self.start.x;
374 Some((slope, intercept))
375 }
376
377 pub fn x_for_y(&self, y: f32) -> Option<f32> {
383 let (min_y, max_y) = sort_pair((self.start.y, self.end.y));
384 if y < min_y || y > max_y || min_y == max_y {
385 return None;
386 }
387 self.slope_intercept()
388 .map(|(slope, intercept)| (y - intercept) / slope)
389 .or(Some(self.start.x))
390 }
391
392 pub fn y_for_x(&self, x: f32) -> Option<f32> {
398 let (min_x, max_x) = sort_pair((self.start.x, self.end.x));
399 if x < min_x || x > max_x {
400 return None;
401 }
402 self.slope_intercept()
403 .map(|(slope, intercept)| slope * x + intercept)
404 }
405}
406
407impl Line<i32> {
408 pub fn to_f32(&self) -> LineF {
409 Line::from_endpoints(self.start.to_f32(), self.end.to_f32())
410 }
411
412 pub fn distance(&self, p: Point) -> f32 {
415 self.to_f32().distance(p.to_f32())
416 }
417
418 pub fn intersects(&self, other: Line) -> bool {
423 self.to_f32().intersects(other.to_f32())
424 }
425}
426
427impl<T: Coord> BoundingRect for Line<T> {
428 type Coord = T;
429
430 fn bounding_rect(&self) -> Rect<T> {
431 let d = self.downwards();
432 let r = self.rightwards();
433 Rect::from_tlbr(d.start.y, r.start.x, d.end.y, r.end.x)
434 }
435}
436
437#[derive(Copy, Clone, Debug, PartialEq, Eq)]
442#[cfg_attr(feature = "serde_traits", derive(serde::Serialize, serde::Deserialize))]
443pub struct Rect<T: Coord = i32> {
444 top_left: Point<T>,
445 bottom_right: Point<T>,
446}
447
448pub type RectF = Rect<f32>;
449
450impl<T: Coord> Rect<T> {
451 pub fn new(top_left: Point<T>, bottom_right: Point<T>) -> Rect<T> {
452 Rect {
453 top_left,
454 bottom_right,
455 }
456 }
457
458 pub fn width(&self) -> T {
459 self.bottom_right.x - self.top_left.x
461 }
462
463 pub fn height(&self) -> T {
464 self.bottom_right.y - self.top_left.y
466 }
467
468 pub fn top(&self) -> T {
469 self.top_left.y
470 }
471
472 pub fn left(&self) -> T {
473 self.top_left.x
474 }
475
476 pub fn right(&self) -> T {
477 self.bottom_right.x
478 }
479
480 pub fn bottom(&self) -> T {
481 self.bottom_right.y
482 }
483
484 pub fn corners(&self) -> [Point<T>; 4] {
487 [
488 self.top_left(),
489 self.top_right(),
490 self.bottom_right(),
491 self.bottom_left(),
492 ]
493 }
494
495 pub fn top_left(&self) -> Point<T> {
497 self.top_left
498 }
499
500 pub fn top_right(&self) -> Point<T> {
502 Point::from_yx(self.top_left.y, self.bottom_right.x)
503 }
504
505 pub fn bottom_left(&self) -> Point<T> {
507 Point::from_yx(self.bottom_right.y, self.top_left.x)
508 }
509
510 pub fn bottom_right(&self) -> Point<T> {
512 self.bottom_right
513 }
514
515 pub fn left_edge(&self) -> Line<T> {
517 Line::from_endpoints(self.top_left(), self.bottom_left())
518 }
519
520 pub fn top_edge(&self) -> Line<T> {
522 Line::from_endpoints(self.top_left(), self.top_right())
523 }
524
525 pub fn right_edge(&self) -> Line<T> {
527 Line::from_endpoints(self.top_right(), self.bottom_right())
528 }
529
530 pub fn bottom_edge(&self) -> Line<T> {
532 Line::from_endpoints(self.bottom_left(), self.bottom_right())
533 }
534
535 pub fn tlbr(&self) -> [T; 4] {
537 [
538 self.top_left.y,
539 self.top_left.x,
540 self.bottom_right.y,
541 self.bottom_right.x,
542 ]
543 }
544
545 pub fn from_hw(height: T, width: T) -> Rect<T> {
547 Self::new(Point::default(), Point::from_yx(height, width))
548 }
549
550 pub fn from_tlbr(top: T, left: T, bottom: T, right: T) -> Rect<T> {
552 Self::new(Point::from_yx(top, left), Point::from_yx(bottom, right))
553 }
554
555 pub fn from_tlhw(top: T, left: T, height: T, width: T) -> Rect<T> {
557 Self::from_tlbr(top, left, top + height, left + width)
558 }
559
560 pub fn area(&self) -> T
562 where
563 T: std::ops::Mul<Output = T>,
564 {
565 self.width() * self.height()
566 }
567
568 pub fn tlhw(&self) -> [T; 4] {
570 [
571 self.top_left.y,
572 self.top_left.x,
573 self.height(),
574 self.width(),
575 ]
576 }
577
578 pub fn contains_point(&self, other: Point<T>) -> bool {
580 self.top() <= other.y
581 && self.bottom() >= other.y
582 && self.left() <= other.x
583 && self.right() >= other.x
584 }
585
586 pub fn is_empty(&self) -> bool {
588 self.right() <= self.left() || self.bottom() <= self.top()
589 }
590
591 pub fn adjust_tlbr(&self, top: T, left: T, bottom: T, right: T) -> Rect<T> {
593 Rect {
594 top_left: self.top_left.translate(top, left),
595 bottom_right: self.bottom_right.translate(bottom, right),
596 }
597 }
598
599 pub fn intersects(&self, other: Rect<T>) -> bool {
601 self.left_edge().vertical_overlap(other.left_edge()) > T::default()
602 && self.top_edge().horizontal_overlap(other.top_edge()) > T::default()
603 }
604
605 pub fn union(&self, other: Rect<T>) -> Rect<T> {
607 let t = min_or_lhs(self.top(), other.top());
608 let l = min_or_lhs(self.left(), other.left());
609 let b = max_or_lhs(self.bottom(), other.bottom());
610 let r = max_or_lhs(self.right(), other.right());
611 Rect::from_tlbr(t, l, b, r)
612 }
613
614 pub fn intersection(&self, other: Rect<T>) -> Rect<T> {
616 let t = max_or_lhs(self.top(), other.top());
617 let l = max_or_lhs(self.left(), other.left());
618 let b = min_or_lhs(self.bottom(), other.bottom());
619 let r = min_or_lhs(self.right(), other.right());
620 Rect::from_tlbr(t, l, b, r)
621 }
622
623 pub fn contains(&self, other: Rect<T>) -> bool {
625 self.union(other) == *self
626 }
627
628 pub fn clamp(&self, rect: Rect<T>) -> Rect<T> {
631 self.intersection(rect)
632 }
633
634 pub fn to_polygon(&self) -> Polygon<T, [Point<T>; 4]> {
635 Polygon::new(self.corners())
636 }
637}
638
639impl Rect<i32> {
640 pub fn center(&self) -> Point {
642 let y = (self.top_left.y + self.bottom_right.y) / 2;
643 let x = (self.top_left.x + self.bottom_right.x) / 2;
644 Point::from_yx(y, x)
645 }
646
647 pub fn iou(&self, other: Rect) -> f32 {
651 self.intersection(other).area() as f32 / self.union(other).area() as f32
652 }
653
654 pub fn to_f32(&self) -> RectF {
655 Rect::from_tlbr(
656 self.top_left.y as f32,
657 self.top_left.x as f32,
658 self.bottom_right.y as f32,
659 self.bottom_right.x as f32,
660 )
661 }
662}
663
664impl Rect<f32> {
665 pub fn center(&self) -> PointF {
667 let y = (self.top_left.y + self.bottom_right.y) / 2.;
668 let x = (self.top_left.x + self.bottom_right.x) / 2.;
669 Point::from_yx(y, x)
670 }
671
672 pub fn iou(&self, other: RectF) -> f32 {
676 self.intersection(other).area() / self.union(other).area()
677 }
678
679 pub fn integral_bounding_rect(&self) -> Rect<i32> {
682 Rect::from_tlbr(
683 self.top() as i32,
684 self.left() as i32,
685 self.bottom().ceil() as i32,
686 self.right().ceil() as i32,
687 )
688 }
689}
690
691impl<T: Coord> BoundingRect for Rect<T> {
692 type Coord = T;
693
694 fn bounding_rect(&self) -> Rect<T> {
695 *self
696 }
697}
698
699#[derive(Copy, Clone, Debug)]
705#[cfg_attr(feature = "serde_traits", derive(serde::Serialize, serde::Deserialize))]
706pub struct RotatedRect {
707 center: PointF,
709
710 up: Vec2,
712
713 width: f32,
715
716 height: f32,
718}
719
720impl RotatedRect {
721 pub fn new(center: PointF, up_axis: Vec2, width: f32, height: f32) -> RotatedRect {
724 RotatedRect {
725 center,
726 up: up_axis.normalized(),
727 width,
728 height,
729 }
730 }
731
732 pub fn contains(&self, point: PointF) -> bool {
734 let height = self.height.max(1e-6);
737 let width = self.width.max(1e-6);
738
739 let ac = point.to_vec() - self.center.to_vec();
745 let ab = self.up * (height / 2.);
746 let up_proj = ac.dot(ab) / (height / 2.).powi(2);
747
748 let ad = self.up.perpendicular() * (width / 2.);
749 let cross_proj = ac.dot(ad) / (width / 2.).powi(2);
750
751 up_proj.abs() <= 1. && cross_proj.abs() <= 1.
752 }
753
754 pub fn expanded(&self, dw: f32, dh: f32) -> RotatedRect {
757 RotatedRect {
758 width: self.width + dw,
759 height: self.height + dh,
760 ..*self
761 }
762 }
763
764 pub fn corners(&self) -> [PointF; 4] {
770 let par_offset = self.up.perpendicular() * (self.width / 2.);
771 let perp_offset = self.up * (self.height / 2.);
772
773 let center = self.center.to_vec();
774 let coords: [Vec2; 4] = [
775 center - perp_offset - par_offset,
776 center - perp_offset + par_offset,
777 center + perp_offset + par_offset,
778 center + perp_offset - par_offset,
779 ];
780
781 coords.map(|c| Point::from_yx(c.y, c.x))
782 }
783
784 pub fn edges(&self) -> [LineF; 4] {
787 let corners = self.corners();
788 [
789 Line::from_endpoints(corners[0], corners[1]),
790 Line::from_endpoints(corners[1], corners[2]),
791 Line::from_endpoints(corners[2], corners[3]),
792 Line::from_endpoints(corners[3], corners[0]),
793 ]
794 }
795
796 pub fn center(&self) -> PointF {
798 self.center
799 }
800
801 pub fn up_axis(&self) -> Vec2 {
804 self.up
805 }
806
807 pub fn width(&self) -> f32 {
809 self.width
810 }
811
812 pub fn height(&self) -> f32 {
814 self.height
815 }
816
817 pub fn area(&self) -> f32 {
819 self.height * self.width
820 }
821
822 pub fn resize(&mut self, width: f32, height: f32) {
824 assert!(width >= 0. && height >= 0.);
825 self.width = width;
826 self.height = height;
827 }
828
829 pub fn intersects(&self, other: &RotatedRect) -> bool {
831 if !self.bounding_rect().intersects(other.bounding_rect()) {
832 return false;
833 }
834 let other_edges = other.edges();
835 self.edges()
836 .iter()
837 .any(|e| other_edges.iter().any(|oe| e.intersects(*oe)))
838 }
839
840 pub fn from_rect(r: RectF) -> RotatedRect {
843 RotatedRect::new(r.center(), Vec2::from_yx(1., 0.), r.width(), r.height())
844 }
845
846 pub fn orient_towards(&self, up: Vec2) -> RotatedRect {
849 let target_up = up.normalized();
850
851 let rot_90 = Vec2::from_xy(self.up.y, -self.up.x);
852 let rot_180 = Vec2::from_xy(-self.up.x, -self.up.y);
853 let rot_270 = Vec2::from_xy(-self.up.y, self.up.x);
854
855 let (rotation, _dotp) = [self.up, rot_90, rot_180, rot_270]
856 .map(|new_up| new_up.dot(target_up))
857 .into_iter()
858 .enumerate()
859 .max_by(|(_, a), (_, b)| a.total_cmp(b))
860 .unwrap_or((0, 0.));
861
862 match rotation {
863 0 => *self,
864 1 => RotatedRect::new(self.center, rot_90, self.height, self.width),
865 2 => RotatedRect::new(self.center, rot_180, self.width, self.height),
866 3 => RotatedRect::new(self.center, rot_270, self.height, self.width),
867 _ => unreachable!(),
868 }
869 }
870}
871
872impl BoundingRect for RotatedRect {
873 type Coord = f32;
874
875 fn bounding_rect(&self) -> RectF {
876 let corners = self.corners();
877
878 let mut xs = corners.map(|p| p.x);
879 xs.sort_unstable_by(f32::total_cmp);
880
881 let mut ys = corners.map(|p| p.y);
882 ys.sort_unstable_by(f32::total_cmp);
883
884 Rect::from_tlbr(ys[0], xs[0], ys[3], xs[3])
885 }
886}
887
888pub fn bounding_rect<'a, Shape: 'a + BoundingRect, I: Iterator<Item = &'a Shape>>(
892 objects: I,
893) -> Option<Rect<Shape::Coord>>
894where
895 Shape::Coord: Coord,
896{
897 objects.fold(None, |bounding_rect, shape| {
898 let sbr = shape.bounding_rect();
899 bounding_rect.map(|br| br.union(sbr)).or(Some(sbr))
900 })
901}
902
903#[derive(Copy, Clone, Debug)]
908#[cfg_attr(feature = "serde_traits", derive(serde::Serialize, serde::Deserialize))]
909pub struct Polygon<T: Coord = i32, S: AsRef<[Point<T>]> = Vec<Point<T>>> {
910 points: S,
911
912 element_type: PhantomData<T>,
914}
915
916pub type PolygonF<S = Vec<PointF>> = Polygon<f32, S>;
917
918impl<T: Coord, S: AsRef<[Point<T>]>> Polygon<T, S> {
919 pub fn new(points: S) -> Polygon<T, S> {
921 Polygon {
922 points,
923 element_type: PhantomData,
924 }
925 }
926
927 pub fn borrow(&self) -> Polygon<T, &[Point<T>]> {
929 Polygon::new(self.points.as_ref())
930 }
931
932 pub fn edges(&self) -> impl Iterator<Item = Line<T>> + '_ {
934 self.points
935 .as_ref()
936 .iter()
937 .zip(self.points.as_ref().iter().cycle().skip(1))
938 .map(|(p0, p1)| Line::from_endpoints(*p0, *p1))
939 }
940
941 pub fn vertices(&self) -> &[Point<T>] {
943 self.points.as_ref()
944 }
945
946 pub fn to_owned(&self) -> Polygon<T> {
948 Polygon::new(self.vertices().to_vec())
949 }
950}
951
952impl<S: AsRef<[Point]>> Polygon<i32, S> {
953 pub fn fill_iter(&self) -> FillIter {
972 FillIter::new(self.borrow())
973 }
974
975 pub fn contains_pixel(&self, p: Point) -> bool {
983 let mut edge_crossings = 0;
984
985 for edge in self.edges() {
986 let (min_y, max_y) = sort_pair((edge.start.y, edge.end.y));
987
988 if min_y == max_y {
990 continue;
991 }
992
993 if p.y < min_y || p.y >= max_y {
995 continue;
996 }
997
998 let edge_down = edge.downwards();
1000 let start_to_end = Vec2::from_yx(
1001 (edge_down.end.y - edge_down.start.y) as f32,
1002 (edge_down.end.x - edge_down.start.x) as f32,
1003 );
1004 let start_to_point = Vec2::from_yx(
1005 (p.y - edge_down.start.y) as f32,
1006 (p.x - edge_down.start.x) as f32,
1007 );
1008
1009 let cross = start_to_end.cross_product_norm(start_to_point);
1010 if cross > 0. {
1011 edge_crossings += 1;
1013 }
1014 }
1015
1016 edge_crossings % 2 == 1
1017 }
1018
1019 pub fn is_simple(&self) -> bool {
1021 for (i, e1) in self.edges().enumerate() {
1024 for (j, e2) in self.edges().enumerate() {
1025 if i != j && e1.intersects(e2) {
1026 let intersection_at_endpoints = e1.start == e2.start
1027 || e1.start == e2.end
1028 || e1.end == e2.start
1029 || e1.end == e2.end;
1030 if !intersection_at_endpoints {
1031 return false;
1032 }
1033 }
1034 }
1035 }
1036 true
1037 }
1038}
1039
1040macro_rules! impl_bounding_rect_for_polygon {
1041 ($coord:ty) => {
1042 impl<S: AsRef<[Point<$coord>]>> BoundingRect for Polygon<$coord, S> {
1043 type Coord = $coord;
1044
1045 fn bounding_rect(&self) -> Rect<$coord> {
1046 let mut min_x = <$coord>::MAX;
1047 let mut max_x = <$coord>::MIN;
1048 let mut min_y = <$coord>::MAX;
1049 let mut max_y = <$coord>::MIN;
1050
1051 for p in self.points.as_ref() {
1052 min_x = min_x.min(p.x);
1053 max_x = max_x.max(p.x);
1054 min_y = min_y.min(p.y);
1055 max_y = max_y.max(p.y);
1056 }
1057
1058 Rect::from_tlbr(min_y, min_x, max_y, max_x)
1059 }
1060 }
1061 };
1062}
1063
1064impl_bounding_rect_for_polygon!(i32);
1065impl_bounding_rect_for_polygon!(f32);
1066
1067#[cfg_attr(feature = "serde_traits", derive(serde::Serialize, serde::Deserialize))]
1073pub struct Polygons {
1074 points: Vec<Point>,
1075
1076 polygons: Vec<Range<usize>>,
1078}
1079
1080impl Polygons {
1081 pub fn new() -> Polygons {
1083 Polygons {
1084 points: Vec::new(),
1085 polygons: Vec::new(),
1086 }
1087 }
1088
1089 pub fn push(&mut self, points: &[Point]) {
1091 let range = self.points.len()..self.points.len() + points.len();
1092 self.polygons.push(range);
1093 self.points.extend_from_slice(points);
1094 }
1095
1096 pub fn len(&self) -> usize {
1098 self.polygons.len()
1099 }
1100
1101 pub fn is_empty(&self) -> bool {
1103 self.polygons.is_empty()
1104 }
1105
1106 pub fn iter(&self) -> PolygonsIter<'_> {
1108 PolygonsIter {
1109 points: &self.points,
1110 polygons: self.polygons.iter(),
1111 }
1112 }
1113}
1114
1115impl Default for Polygons {
1116 fn default() -> Self {
1117 Self::new()
1118 }
1119}
1120
1121pub struct PolygonsIter<'a> {
1123 points: &'a [Point],
1124 polygons: Iter<'a, Range<usize>>,
1125}
1126
1127impl<'a> Iterator for PolygonsIter<'a> {
1128 type Item = &'a [Point];
1129
1130 fn next(&mut self) -> Option<Self::Item> {
1131 if let Some(range) = self.polygons.next() {
1132 Some(&self.points[range.clone()])
1133 } else {
1134 None
1135 }
1136 }
1137
1138 fn size_hint(&self) -> (usize, Option<usize>) {
1139 self.polygons.size_hint()
1140 }
1141}
1142
1143impl ExactSizeIterator for PolygonsIter<'_> {}
1144
1145#[cfg(test)]
1146mod tests {
1147 use rten_tensor::test_util::ApproxEq;
1148 use rten_tensor::{MatrixLayout, NdTensor};
1149 use rten_testing::TestCases;
1150
1151 use crate::Vec2;
1152 use crate::tests::{points_from_coords, points_from_n_coords};
1153
1154 use super::{BoundingRect, Line, Point, PointF, Polygon, Rect, RotatedRect, bounding_rect};
1155
1156 #[test]
1157 fn test_bounding_rect() {
1158 let rects = [Rect::from_tlbr(0, 0, 5, 5), Rect::from_tlbr(10, 10, 15, 18)];
1159 assert_eq!(
1160 bounding_rect(rects.iter()),
1161 Some(Rect::from_tlbr(0, 0, 15, 18))
1162 );
1163
1164 let rects: &[Rect] = &[];
1165 assert_eq!(bounding_rect(rects.iter()), None);
1166 }
1167
1168 #[test]
1169 fn test_line_distance() {
1170 #[derive(Debug)]
1171 struct Case {
1172 start: Point,
1173 end: Point,
1174 point: Point,
1175 dist: f32,
1176 }
1177
1178 let cases = [
1180 Case {
1182 start: Point::default(),
1183 end: Point::default(),
1184 point: Point::from_yx(3, 4),
1185 dist: 5.,
1186 },
1187 Case {
1189 start: Point::from_yx(5, 2),
1190 end: Point::from_yx(5, 10),
1191 point: Point::from_yx(8, 5),
1192 dist: 3.,
1193 },
1194 Case {
1196 start: Point::from_yx(5, 3),
1197 end: Point::from_yx(10, 3),
1198 point: Point::from_yx(8, 5),
1199 dist: 2.,
1200 },
1201 Case {
1203 start: Point::default(),
1204 end: Point::from_yx(5, 5),
1205 point: Point::from_yx(4, 0),
1206 dist: (8f32).sqrt(), },
1208 Case {
1210 start: Point::default(),
1211 end: Point::from_yx(5, -5),
1212 point: Point::from_yx(4, 0),
1213 dist: (8f32).sqrt(), },
1215 Case {
1217 start: Point::default(),
1218 end: Point::from_yx(5, 5),
1219 point: Point::from_yx(0, 4),
1220 dist: (8f32).sqrt(), },
1222 Case {
1224 start: Point::from_yx(5, 2),
1225 end: Point::from_yx(5, 5),
1226 point: Point::from_yx(5, 10),
1227 dist: 5.,
1228 },
1229 ];
1230
1231 cases.test_each(|case| {
1232 let line = Line::from_endpoints(case.start, case.end);
1233 let dist = line.distance(case.point);
1234 assert!(
1235 dist.approx_eq(&case.dist),
1236 "line {:?}, {:?} point {:?} actual {} expected {}",
1237 line.start,
1238 line.end,
1239 case.point,
1240 dist,
1241 case.dist
1242 );
1243 });
1244 }
1245
1246 #[test]
1247 fn test_line_downwards() {
1248 #[derive(Debug)]
1249 struct Case {
1250 input: Line,
1251 down: Line,
1252 }
1253 let cases = [
1254 Case {
1255 input: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(5, 5)),
1256 down: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(5, 5)),
1257 },
1258 Case {
1259 input: Line::from_endpoints(Point::from_yx(5, 5), Point::from_yx(0, 0)),
1260 down: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(5, 5)),
1261 },
1262 ];
1263 cases.test_each(|case| {
1264 assert_eq!(case.input.downwards(), case.down);
1265 })
1266 }
1267
1268 #[test]
1269 fn test_line_rightwards() {
1270 #[derive(Debug)]
1271 struct Case {
1272 input: Line,
1273 right: Line,
1274 }
1275 let cases = [
1276 Case {
1277 input: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(5, 5)),
1278 right: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(5, 5)),
1279 },
1280 Case {
1281 input: Line::from_endpoints(Point::from_yx(5, 5), Point::from_yx(0, 0)),
1282 right: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(5, 5)),
1283 },
1284 ];
1285 cases.test_each(|case| {
1286 assert_eq!(case.input.rightwards(), case.right);
1287 })
1288 }
1289
1290 fn line_from_coords(coords: [i32; 4]) -> Line {
1292 Line::from_endpoints(
1293 Point::from_yx(coords[0], coords[1]),
1294 Point::from_yx(coords[2], coords[3]),
1295 )
1296 }
1297
1298 #[test]
1299 fn test_line_intersects() {
1300 #[derive(Debug)]
1301 struct Case {
1302 a: Line,
1303 b: Line,
1304 expected: bool,
1305 }
1306
1307 let cases = [
1308 Case {
1310 a: line_from_coords([0, 5, 10, 5]),
1311 b: line_from_coords([5, 0, 5, 10]),
1312 expected: true,
1313 },
1314 Case {
1316 a: line_from_coords([0, 0, 10, 10]),
1317 b: line_from_coords([10, 0, 0, 10]),
1318 expected: true,
1319 },
1320 Case {
1322 a: line_from_coords([0, 5, 10, 5]),
1323 b: line_from_coords([5, 6, 5, 10]),
1324 expected: false,
1325 },
1326 Case {
1327 a: line_from_coords([0, 5, 10, 5]),
1328 b: line_from_coords([5, 10, 5, 6]),
1329 expected: false,
1330 },
1331 Case {
1333 a: line_from_coords([0, 5, 5, 5]),
1334 b: line_from_coords([5, 0, 5, 10]),
1335 expected: true,
1336 },
1337 Case {
1339 a: line_from_coords([1, 1, 2, 3]),
1340 b: line_from_coords([4, 1, -1, 2]),
1341 expected: true,
1342 },
1343 Case {
1345 a: line_from_coords([0, 5, 0, 10]),
1346 b: line_from_coords([2, 5, 2, 10]),
1347 expected: false,
1348 },
1349 Case {
1351 a: line_from_coords([0, 5, 0, 10]),
1352 b: line_from_coords([0, 5, 0, 10]),
1353 expected: false,
1354 },
1355 ];
1356
1357 cases.test_each(|case| {
1358 assert_eq!(case.a.intersects(case.b), case.expected);
1359
1360 assert_eq!(case.b.intersects(case.a), case.expected);
1362 })
1363 }
1364
1365 #[test]
1366 fn test_line_is_horizontal() {
1367 assert_eq!(
1368 Line::from_endpoints(Point::from_yx(5, 0), Point::from_yx(5, 10)).is_horizontal(),
1369 true
1370 );
1371 assert_eq!(
1372 Line::from_endpoints(Point::from_yx(5, 0), Point::from_yx(6, 10)).is_horizontal(),
1373 false
1374 );
1375 }
1376
1377 #[test]
1378 fn test_line_overlap() {
1379 #[derive(Debug)]
1380 struct Case {
1381 a: (i32, i32),
1382 b: (i32, i32),
1383 overlap: i32,
1384 }
1385
1386 let cases = [
1387 Case {
1389 a: (0, 10),
1390 b: (15, 20),
1391 overlap: 0,
1392 },
1393 Case {
1395 a: (0, 10),
1396 b: (5, 15),
1397 overlap: 5,
1398 },
1399 Case {
1401 a: (0, 10),
1402 b: (2, 8),
1403 overlap: 6,
1404 },
1405 Case {
1407 a: (0, 5),
1408 b: (0, 10),
1409 overlap: 5,
1410 },
1411 ];
1412
1413 cases.test_each(|case| {
1414 let a = Line::from_endpoints(Point::from_yx(0, case.a.0), Point::from_yx(0, case.a.1));
1416 let b = Line::from_endpoints(Point::from_yx(0, case.b.0), Point::from_yx(0, case.b.1));
1417 assert_eq!(a.horizontal_overlap(b), case.overlap);
1418 assert_eq!(b.horizontal_overlap(a), case.overlap);
1419
1420 let a = Line::from_endpoints(Point::from_yx(case.a.0, 0), Point::from_yx(case.a.1, 0));
1422 let b = Line::from_endpoints(Point::from_yx(case.b.0, 0), Point::from_yx(case.b.1, 0));
1423 assert_eq!(a.vertical_overlap(b), case.overlap);
1424 assert_eq!(b.vertical_overlap(a), case.overlap);
1425 })
1426 }
1427
1428 #[test]
1429 fn test_line_width_height() {
1430 #[derive(Debug)]
1431 struct Case {
1432 line: Line,
1433 width: i32,
1434 height: i32,
1435 }
1436
1437 let cases = [
1438 Case {
1439 line: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(5, 3)),
1440 width: 3,
1441 height: 5,
1442 },
1443 Case {
1444 line: Line::from_endpoints(Point::from_yx(5, 3), Point::from_yx(0, 0)),
1445 width: -3,
1446 height: -5,
1447 },
1448 ];
1449
1450 cases.test_each(|case| {
1451 assert_eq!(case.line.width(), case.width);
1452 assert_eq!(case.line.height(), case.height);
1453 })
1454 }
1455
1456 #[test]
1457 fn test_line_y_for_x_and_x_for_y() {
1458 #[derive(Debug)]
1459 struct Case {
1460 line: Line,
1461
1462 points: Vec<(f32, Option<f32>)>,
1464 }
1465
1466 let cases = [
1467 Case {
1469 line: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(1, 1)),
1470 points: vec![
1471 (-1., None),
1472 (0., Some(0.)),
1473 (0.5, Some(0.5)),
1474 (1., Some(1.)),
1475 (1.2, None),
1476 ],
1477 },
1478 Case {
1480 line: Line::from_endpoints(Point::from_yx(0, 1), Point::from_yx(1, 2)),
1481 points: vec![
1482 (-1., None),
1483 (1., Some(0.)),
1484 (1.5, Some(0.5)),
1485 (2., Some(1.)),
1486 (2.2, None),
1487 ],
1488 },
1489 Case {
1491 line: Line::from_endpoints(Point::from_yx(0, 1), Point::from_yx(0, 2)),
1492 points: vec![(-1., None), (1., Some(0.)), (2., Some(0.)), (3., None)],
1493 },
1494 Case {
1496 line: Line::from_endpoints(Point::from_yx(0, 0), Point::from_yx(2, 0)),
1497 points: vec![(-1., None), (0., None), (1., None)],
1498 },
1499 ];
1500
1501 cases.test_each(|case| {
1502 for &(x, expected_y) in &case.points {
1503 assert_eq!(case.line.to_f32().y_for_x(x), expected_y);
1504 if let Some(y) = expected_y {
1505 assert_eq!(
1506 case.line.to_f32().x_for_y(y),
1507 if case.line.is_horizontal() {
1508 None
1509 } else {
1510 Some(x)
1511 }
1512 );
1513 }
1514 }
1515 })
1516 }
1517
1518 #[test]
1519 fn test_point_coord() {
1520 assert_eq!(Point::from_yx(3, 5).coord(), [3, 5]);
1521 }
1522
1523 #[test]
1524 #[should_panic(expected = "Coordinates are negative")]
1525 fn test_point_coord_negative() {
1526 Point::from_yx(-1, -1).coord();
1527 }
1528
1529 #[test]
1530 fn test_polygon_contains_pixel() {
1531 #[derive(Debug)]
1532 struct Case {
1533 poly: Polygon,
1534 }
1535
1536 let cases = [
1537 Case {
1539 poly: Polygon::new(Vec::new()),
1540 },
1541 Case {
1543 poly: Rect::from_tlbr(0, 0, 0, 5).to_polygon().to_owned(),
1544 },
1545 Case {
1547 poly: Rect::from_tlbr(2, 2, 5, 5).to_polygon().to_owned(),
1548 },
1549 Case {
1550 poly: Rect::from_tlbr(0, 0, 1, 1).to_polygon().to_owned(),
1551 },
1552 Case {
1554 poly: Rect::from_tlbr(5, 5, 2, 2).to_polygon().to_owned(),
1555 },
1556 Case {
1558 poly: Polygon::new(points_from_coords(&[[0, 2], [3, 0], [3, 4]])),
1559 },
1560 Case {
1561 poly: Polygon::new(points_from_coords(&[[1, 1], [4, 3], [6, 9]])),
1562 },
1563 ];
1564
1565 cases.test_each(|case| {
1566 let grid_size = case
1569 .poly
1570 .vertices()
1571 .iter()
1572 .fold([0, 0], |[h, w], point| {
1573 [h.max(point.y) + 2, w.max(point.x) + 2]
1574 })
1575 .map(|x| x as usize);
1576
1577 let mut fill_grid = NdTensor::zeros(grid_size);
1578 let mut contains_pixel_grid = NdTensor::zeros(grid_size);
1579
1580 for p in case.poly.fill_iter() {
1584 fill_grid[p.coord()] = 1;
1585 }
1586 for y in 0..contains_pixel_grid.rows() {
1587 for x in 0..contains_pixel_grid.cols() {
1588 if case.poly.contains_pixel(Point::from_yx(y as i32, x as i32)) {
1589 contains_pixel_grid[[y, x]] = 1;
1590 }
1591 }
1592 }
1593
1594 for y in 0..fill_grid.rows() {
1595 for x in 0..fill_grid.cols() {
1596 assert_eq!(fill_grid[[y, x]], contains_pixel_grid[[y, x]]);
1597 }
1598 }
1599 })
1600 }
1601
1602 #[test]
1603 fn test_polygon_is_simple() {
1604 #[derive(Debug)]
1605 struct Case {
1606 poly: Polygon,
1607 simple: bool,
1608 }
1609
1610 let cases = [
1611 Case {
1613 poly: Rect::from_tlbr(0, 0, 10, 10).to_polygon().to_owned(),
1614 simple: true,
1615 },
1616 Case {
1618 poly: Polygon::new(points_from_coords(&[[0, 0], [0, 10], [10, 10], [-2, 2]])),
1619 simple: false,
1620 },
1621 ];
1622
1623 cases.test_each(|case| assert_eq!(case.poly.is_simple(), case.simple))
1624 }
1625
1626 #[test]
1627 fn test_polygon_fill_iter() {
1628 #[derive(Debug)]
1629 struct Case {
1630 vertices: Vec<Point>,
1631 filled: Vec<Point>,
1632 }
1633
1634 let cases = [
1635 Case {
1637 vertices: Vec::new(),
1638 filled: Vec::new(),
1639 },
1640 Case {
1642 vertices: points_from_coords(&[[0, 0], [5, 5]]),
1643 filled: Vec::new(),
1644 },
1645 Case {
1647 vertices: Rect::from_tlbr(0, 0, 3, 3).to_polygon().vertices().to_vec(),
1648 filled: points_from_coords(&[
1649 [0, 0],
1650 [0, 1],
1651 [0, 2],
1652 [1, 0],
1653 [1, 1],
1654 [1, 2],
1655 [2, 0],
1656 [2, 1],
1657 [2, 2],
1658 ]),
1659 },
1660 Case {
1662 vertices: points_from_coords(&[[0, 0], [0, 4], [3, 4]]),
1663 filled: points_from_coords(&[
1664 [0, 0],
1665 [0, 1],
1666 [0, 2],
1667 [0, 3],
1668 [1, 2],
1669 [1, 3],
1670 [2, 3],
1671 ]),
1672 },
1673 ];
1674
1675 cases.test_each(|case| {
1676 let poly = Polygon::new(&case.vertices);
1677 let filled: Vec<_> = poly.fill_iter().collect();
1678 assert_eq!(filled, case.filled);
1679 })
1680 }
1681
1682 #[test]
1683 fn test_rect_clamp() {
1684 #[derive(Debug)]
1685 struct Case {
1686 rect: Rect,
1687 boundary: Rect,
1688 expected: Rect,
1689 }
1690
1691 let cases = [
1692 Case {
1693 rect: Rect::from_tlbr(-5, -10, 100, 200),
1694 boundary: Rect::from_tlbr(0, 0, 50, 100),
1695 expected: Rect::from_tlbr(0, 0, 50, 100),
1696 },
1697 Case {
1698 rect: Rect::from_tlbr(5, 10, 40, 80),
1699 boundary: Rect::from_tlbr(0, 0, 50, 100),
1700 expected: Rect::from_tlbr(5, 10, 40, 80),
1701 },
1702 ];
1703
1704 cases.test_each(|case| {
1705 assert_eq!(case.rect.clamp(case.boundary), case.expected);
1706 })
1707 }
1708
1709 #[test]
1710 fn test_rect_contains_point() {
1711 let r = Rect::from_tlbr(5, 5, 10, 10);
1712
1713 assert_eq!(r.contains_point(Point::from_yx(0, 0)), false);
1715 assert_eq!(r.contains_point(Point::from_yx(12, 12)), false);
1716
1717 assert_eq!(r.contains_point(Point::from_yx(8, 8)), true);
1719
1720 assert_eq!(r.contains_point(Point::from_yx(5, 5)), true);
1722 assert_eq!(r.contains_point(Point::from_yx(10, 10)), true);
1723 }
1724
1725 #[test]
1726 fn test_rect_tlbr() {
1727 let r = Rect::from_tlbr(0, 1, 2, 3);
1728 assert_eq!(r.tlbr(), [0, 1, 2, 3]);
1729 }
1730
1731 #[test]
1732 fn test_rotated_rect_contains() {
1733 #[derive(Debug)]
1734 struct Case {
1735 rrect: RotatedRect,
1736 }
1737
1738 let cases = [
1739 Case {
1741 rrect: RotatedRect::new(PointF::from_yx(0., 0.), Vec2::from_yx(1., 0.), 10., 5.),
1742 },
1743 Case {
1745 rrect: RotatedRect::new(PointF::from_yx(0., 0.), Vec2::from_yx(-1., 0.), 10., 5.),
1746 },
1747 Case {
1749 rrect: RotatedRect::new(PointF::from_yx(0., 0.), Vec2::from_yx(0.5, 0.5), 10., 5.),
1750 },
1751 ];
1752
1753 cases.test_each(|case| {
1754 let &Case { rrect: r } = case;
1755
1756 assert!(r.contains(r.center()));
1757
1758 for c in r.expanded(-1e-5, -1e-5).corners() {
1760 assert!(r.contains(c));
1761 }
1762
1763 for c in r.expanded(1e-5, 1e-5).corners() {
1765 assert!(!r.contains(c));
1766 }
1767 })
1768 }
1769
1770 #[test]
1771 fn test_rotated_rect_corners() {
1772 let r = RotatedRect::new(PointF::from_yx(5., 5.), Vec2::from_yx(1., 0.), 5., 5.);
1773 let expected = points_from_n_coords([[2.5, 2.5], [2.5, 7.5], [7.5, 7.5], [7.5, 2.5]]);
1774 assert_eq!(r.corners(), expected);
1775 }
1776
1777 #[test]
1778 fn test_rotated_rect_expanded() {
1779 let r = RotatedRect::new(PointF::from_yx(0., 0.), Vec2::from_yx(1., 0.), 10., 5.);
1780 let r = r.expanded(2., 3.);
1781 assert_eq!(r.width(), 12.);
1782 assert_eq!(r.height(), 8.);
1783 }
1784
1785 #[test]
1786 fn test_rotated_rect_from_rect() {
1787 let r = Rect::from_tlbr(5., 10., 50., 40.);
1788 let rr = RotatedRect::from_rect(r);
1789 assert_eq!(rr.width(), r.width());
1790 assert_eq!(rr.height(), r.height());
1791 assert_eq!(rr.bounding_rect(), r);
1792 }
1793
1794 #[test]
1795 fn test_rotated_rect_intersects() {
1796 #[derive(Debug)]
1797 struct Case {
1798 a: RotatedRect,
1799 b: RotatedRect,
1800 bounding_rect_intersects: bool,
1801 intersects: bool,
1802 }
1803
1804 let up_vec = Vec2::from_yx(-1., 0.);
1805 let up_left_vec = Vec2::from_yx(-1., -1.);
1806
1807 let cases = [
1808 Case {
1810 a: RotatedRect::new(PointF::from_yx(5., 5.), up_vec, 5., 5.),
1811 b: RotatedRect::new(PointF::from_yx(5., 5.), up_vec, 5., 5.),
1812 bounding_rect_intersects: true,
1813 intersects: true,
1814 },
1815 Case {
1817 a: RotatedRect::new(PointF::from_yx(5., 5.), up_vec, 5., 5.),
1818 b: RotatedRect::new(PointF::from_yx(5., 11.), up_vec, 5., 5.),
1819 bounding_rect_intersects: false,
1820 intersects: false,
1821 },
1822 Case {
1825 a: RotatedRect::new(PointF::from_yx(5., 5.), up_left_vec, 12., 1.),
1826 b: RotatedRect::new(PointF::from_yx(9., 9.), up_vec, 1., 1.),
1827 bounding_rect_intersects: true,
1828 intersects: false,
1829 },
1830 ];
1831
1832 cases.test_each(|case| {
1833 assert_eq!(
1834 case.a.bounding_rect().intersects(case.b.bounding_rect()),
1835 case.bounding_rect_intersects
1836 );
1837 assert_eq!(case.a.intersects(&case.b), case.intersects);
1838 assert_eq!(case.b.intersects(&case.a), case.intersects);
1840 })
1841 }
1842
1843 #[test]
1844 fn test_rotated_rect_normalizes_up_vector() {
1845 let up_axis = Vec2::from_yx(1., 2.);
1847 let center = PointF::from_yx(0., 0.);
1848 let rect = RotatedRect::new(center, up_axis, 2., 3.);
1849 assert!(rect.up_axis().length().approx_eq(&1.));
1850 }
1851
1852 #[test]
1853 fn test_rotated_rect_orient_towards() {
1854 let up_axis = Vec2::from_yx(-1., 0.);
1855 let center = PointF::from_yx(0., 0.);
1856 let rect = RotatedRect::new(center, up_axis, 2., 3.);
1857
1858 let sorted_corners = |rect: RotatedRect| {
1859 let mut corners = rect
1860 .corners()
1861 .map(|c| Point::from_yx(c.y.round() as i32, c.x.round() as i32));
1862 corners.sort_by_key(|p| (p.y, p.x));
1863 corners
1864 };
1865
1866 let targets = [
1867 Vec2::from_yx(-1., 0.),
1868 Vec2::from_yx(0., 1.),
1869 Vec2::from_yx(1., 0.),
1870 Vec2::from_yx(0., -1.),
1871 ];
1872 for target in targets {
1873 let oriented = rect.orient_towards(target);
1874 assert_eq!(sorted_corners(oriented), sorted_corners(rect));
1875 if target != up_axis {
1876 assert_ne!(oriented.corners(), rect.corners());
1877 }
1878 assert_eq!(oriented.up_axis(), target);
1879 }
1880 }
1881
1882 #[test]
1883 fn test_rotated_rect_resize() {
1884 let mut r = RotatedRect::new(PointF::from_yx(5., 5.), Vec2::from_yx(1., 0.), 5., 5.);
1885 assert_eq!(r.area(), 25.);
1886
1887 r.resize(3., 7.);
1888
1889 assert_eq!(r.width(), 3.);
1890 assert_eq!(r.height(), 7.);
1891 assert_eq!(r.area(), 21.);
1892 }
1893}