rten_imageproc/
shapes.rs

1use std::fmt;
2use std::marker::PhantomData;
3use std::ops::Range;
4use std::slice::Iter;
5
6use crate::{FillIter, Vec2};
7
8/// Trait for shapes which have a well-defined bounding rectangle.
9pub trait BoundingRect {
10    /// Coordinate type of bounding rect.
11    type Coord: Coord;
12
13    /// Return the smallest axis-aligned bounding rect which contains this
14    /// shape.
15    fn bounding_rect(&self) -> Rect<Self::Coord>;
16}
17
18/// Trait for types which can be used as coordinates of shapes.
19///
20/// This trait captures the most common requirements of integral and float
21/// coordinate types for various shape methods.
22pub 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    /// Return true if this coordinate is a float NaN value.
32    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
47/// Return the minimum of `a` and `b`, or `a` if `a` and `b` are unordered.
48fn min_or_lhs<T: PartialOrd>(a: T, b: T) -> T {
49    if b < a { b } else { a }
50}
51
52/// Return the maximum of `a` and `b`, or `a` if `a` and `b` are unordered.
53fn max_or_lhs<T: PartialOrd>(a: T, b: T) -> T {
54    if b > a { b } else { a }
55}
56
57/// A point defined by X and Y coordinates.
58#[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    /// Construct a point from X and Y coordinates.
72    pub fn from_yx(y: T, x: T) -> Self {
73        Point { y, x }
74    }
75
76    /// Set the coordinates of this point.
77    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    /// Return the vector from this point to another point.
100    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    /// Return the vector from the origin to this point.
107    pub fn to_vec(self) -> Vec2 {
108        Vec2::from_xy(self.x, self.y)
109    }
110}
111
112impl Point<i32> {
113    /// Return self as a [y, x] array. This is useful for indexing into an
114    /// image or matrix.
115    ///
116    /// Panics if the X or Y coordinates of the point are negative.
117    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    /// Return the neighbors of the current point in clockwise order, starting
123    /// from the point directly above `self`.
124    pub fn neighbors(self) -> [Point; 8] {
125        [
126            self.translate(-1, 0),  // N
127            self.translate(-1, 1),  // NE
128            self.translate(0, 1),   // E
129            self.translate(1, 1),   // SE
130            self.translate(1, 0),   // S
131            self.translate(1, -1),  // SW
132            self.translate(0, -1),  // W
133            self.translate(-1, -1), // NW
134        ]
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
155/// Compute the overlap between two 1D lines `a` and `b`, where each line is
156/// given as (start, end) coords.
157///
158/// Returns NaN if any input coordinate is NaN.
159fn 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    // This check handles NaN for two of the inputs. The checks below will
169    // return NaN if `overlap` is NaN, which handles the other two inputs.
170    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
183/// Sort the elements of a tuple. If the ordering of the elements is undefined,
184/// return the input unchanged.
185fn 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/// A bounded line segment defined by a start and end point.
194#[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    /// Return true if this line has zero length.
218    pub fn is_empty(&self) -> bool {
219        self.start == self.end
220    }
221
222    /// Return the difference between the starting and ending X coordinates of
223    /// the line.
224    pub fn width(&self) -> T {
225        self.end.x - self.start.x
226    }
227
228    /// Return the difference between the starting and ending Y coordinates of
229    /// the line.
230    pub fn height(&self) -> T {
231        self.end.y - self.start.y
232    }
233
234    /// Return true if the Y coordinate of the line's start and end points are
235    /// the same.
236    pub fn is_horizontal(&self) -> bool {
237        self.start.y == self.end.y
238    }
239
240    /// Return a copy of this line with the start and end points swapped.
241    pub fn reverse(&self) -> Line<T> {
242        Line::from_endpoints(self.end, self.start)
243    }
244
245    /// Return a copy of this line with the same endpoints but swapped if
246    /// needed so that `end.y >= start.y`.
247    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    /// Return a copy of this line with the same endpoints but swapped if
256    /// needed so that `end.x >= start.x`.
257    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    /// Return the number of pixels by which this line overlaps `other` in the
266    /// vertical direction.
267    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    /// Return the number of pixels by which this line overlaps `other` in the
272    /// horizontal direction.
273    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    /// Return the euclidean distance between a point and the closest coordinate
280    /// that lies on the line.
281    pub fn distance(&self, p: PointF) -> f32 {
282        if self.is_empty() {
283            return self.start.distance(p);
284        }
285
286        // Method taken from http://www.faqs.org/faqs/graphics/algorithms-faq/,
287        // "Subject 1.02: How do I find the distance from a point to a line?".
288
289        // Compute normalized scalar projection of line from `start` to `p` onto
290        // self. This indicates how far along the `self` line the nearest point
291        // to `p` is.
292        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            // Nearest point is start of line.
298            self.start.distance(p)
299        } else if scalar_proj >= 1. {
300            // Nearest point is end of line.
301            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    /// Test whether this line segment intersects `other` at a single point.
311    ///
312    /// Returns false if the line segments do not intersect, or are coincident
313    /// (ie. overlap for part of their lengths).
314    pub fn intersects(&self, other: Line<f32>) -> bool {
315        // See https://en.wikipedia.org/wiki/Intersection_(geometry)#Two_line_segments
316
317        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        // To find the intersection, we first represent the lines as functions
323        // parametrized by `s` and `t`:
324        //
325        // x(s), y(s) = x1 + s(x2 - x1), y1 + s(y2 - y1)
326        // x(t), y(t) = x3 + t(x4 - x3), y3 + t(y4 - y3)
327        //
328        // Then the coordinates of the intersection s0 and t0 are the solutions
329        // of:
330        //
331        // s(x2 - x1) - t(x4 - x3) = x3 - x1
332        // s(y2 - y1) - t(y4 - y3) = y3 - y1
333        //
334        // These equations are solved using Cramer's rule. The lines intersect
335        // if s0 and t0 are in [0, 1].
336
337        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            // Lines are either parallel or coincident.
348            return false;
349        }
350        let det_a0 = b0 * d - b * b1;
351        let det_a1 = a * b1 - b0 * c;
352
353        // We could calculate `s0` as `det_a0 / det_a` and `t0` as `det_a1 / det_a`
354        // (using float division). We only need to test whether s0 and t0 are
355        // in [0, 1] though, so this can be done without division.
356        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    /// Return the midpoint between the start and end points of the line.
365    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    /// Return `(slope, intercept)` tuple for line or None if the line is
372    /// vertical.
373    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    /// Return the X coordinate that corresponds to a given Y coordinate on
384    /// the line.
385    ///
386    /// Returns `None` if the Y coordinate is not on the line or the line is
387    /// horizontal.
388    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    /// Return the Y coordinate that corresponds to a given X coordinate on
399    /// the line.
400    ///
401    /// Returns `None` if the X coordinate is not on the line or the line
402    /// is vertical.
403    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    /// Return the euclidean distance between a point and the closest coordinate
419    /// that lies on the line.
420    pub fn distance(&self, p: Point) -> f32 {
421        self.to_f32().distance(p.to_f32())
422    }
423
424    /// Test whether this line segment intersects `other` at a single point.
425    ///
426    /// Returns false if the line segments do not intersect, or are coincident
427    /// (ie. overlap for part of their lengths).
428    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/// Rectangle defined by left, top, right and bottom coordinates.
444///
445/// The left and top coordinates are inclusive. The right and bottom coordinates
446/// are exclusive.
447#[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        // TODO - Handle inverted rects here
469        self.bottom_right.x - self.top_left.x
470    }
471
472    pub fn height(&self) -> T {
473        // TODO - Handle inverted rects here
474        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    /// Return the corners of the rect in clockwise order, starting from the
494    /// top left.
495    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    /// Return the coordinate of the top-left corner of the rect.
505    pub fn top_left(&self) -> Point<T> {
506        self.top_left
507    }
508
509    /// Return the coordinate of the top-right corner of the rect.
510    pub fn top_right(&self) -> Point<T> {
511        Point::from_yx(self.top_left.y, self.bottom_right.x)
512    }
513
514    /// Return the coordinate of the bottom-left corner of the rect.
515    pub fn bottom_left(&self) -> Point<T> {
516        Point::from_yx(self.bottom_right.y, self.top_left.x)
517    }
518
519    /// Return the coordinate of the bottom-right corner of the rect.
520    pub fn bottom_right(&self) -> Point<T> {
521        self.bottom_right
522    }
523
524    /// Return the line segment of the left edge of the rect.
525    pub fn left_edge(&self) -> Line<T> {
526        Line::from_endpoints(self.top_left(), self.bottom_left())
527    }
528
529    /// Return the line segment of the top edge of the rect.
530    pub fn top_edge(&self) -> Line<T> {
531        Line::from_endpoints(self.top_left(), self.top_right())
532    }
533
534    /// Return the line segment of the right edge of the rect.
535    pub fn right_edge(&self) -> Line<T> {
536        Line::from_endpoints(self.top_right(), self.bottom_right())
537    }
538
539    /// Return the line segment of the bottom edge of the rect.
540    pub fn bottom_edge(&self) -> Line<T> {
541        Line::from_endpoints(self.bottom_left(), self.bottom_right())
542    }
543
544    /// Return the top, left, bottom and right coordinates as an array.
545    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    /// Return a rect with top-left corner at 0, 0 and the given height/width.
555    pub fn from_hw(height: T, width: T) -> Rect<T> {
556        Self::new(Point::default(), Point::from_yx(height, width))
557    }
558
559    /// Return a rect with the given top, left, bottom and right coordinates.
560    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    /// Return a rect with the given top, left, height and width.
565    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    /// Return the signed area of this rect.
570    pub fn area(&self) -> T
571    where
572        T: std::ops::Mul<Output = T>,
573    {
574        self.width() * self.height()
575    }
576
577    /// Return the top, left, height and width as an array.
578    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    /// Return true if `other` lies on the boundary or interior of this rect.
588    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    /// Return true if the width or height of this rect are <= 0.
596    pub fn is_empty(&self) -> bool {
597        self.right() <= self.left() || self.bottom() <= self.top()
598    }
599
600    /// Return a new Rect with each coordinate adjusted by an offset.
601    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    /// Return true if the intersection of this rect and `other` is non-empty.
609    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    /// Return the smallest rect that contains both this rect and `other`.
615    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    /// Return the largest rect that is contained within this rect and `other`.
624    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    /// Return true if `other` lies entirely within this rect.
633    pub fn contains(&self, other: Rect<T>) -> bool {
634        self.union(other) == *self
635    }
636
637    /// Return a new with each side adjusted so that the result lies inside
638    /// `rect`.
639    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    /// Return the center point of the rect.
650    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    /// Return the Intersection over Union ratio for this rect and `other`.
657    ///
658    /// See <https://en.wikipedia.org/wiki/Jaccard_index>.
659    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    /// Return the center point of the rect.
675    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    /// Return the Intersection over Union ratio for this rect and `other`.
682    ///
683    /// See <https://en.wikipedia.org/wiki/Jaccard_index>.
684    pub fn iou(&self, other: RectF) -> f32 {
685        self.intersection(other).area() / self.union(other).area()
686    }
687
688    /// Return the smallest rect with integral coordinates that contains this
689    /// rect.
690    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/// An oriented rectangle.
709///
710/// This is characterized by a center point, an "up" direction indicating the
711/// orientation, width (extent along axis perpendicular to the up axis) and
712/// height (extent along up axis).
713#[derive(Copy, Clone, Debug)]
714#[cfg_attr(
715    feature = "serde_traits",
716    derive(serde_derive::Serialize, serde_derive::Deserialize)
717)]
718pub struct RotatedRect {
719    // Centroid of the rect.
720    center: PointF,
721
722    // Unit-length vector indicating the "up" direction for this rect.
723    up: Vec2,
724
725    // Extent of the rect along the axis perpendicular to `up`.
726    width: f32,
727
728    // Extent of the rect along the `up` axis.
729    height: f32,
730}
731
732impl RotatedRect {
733    /// Construct a new RotatedRect with a given `center`, up direction and
734    /// dimensions.
735    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    /// Return true if a point lies within this rotated rect.
745    pub fn contains(&self, point: PointF) -> bool {
746        // Treat zero width/height rectangles as being very thin rectangles
747        // rather than lines.
748        let height = self.height.max(1e-6);
749        let width = self.width.max(1e-6);
750
751        // Project line from center to `p` onto the up and cross axis. The
752        // results will be in the range [-1, 1] if the point is within the
753        // rect.
754        //
755        // See notes in `Line::distance` about distance from point to a line.
756        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    /// Return a copy of this rect with width increased by `dw` and height
767    /// increased by `dh`.
768    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    /// Return the coordinates of the rect's corners.
777    ///
778    /// The corners are returned in clockwise order starting from the corner
779    /// that is the top-left when the "up" axis has XY coordinates [0, 1], or
780    /// equivalently, bottom-right when the "up" axis has XY coords [0, -1].
781    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    /// Return the edges of this rect, in clockwise order starting from the
797    /// edge that is the top edge if the rect has no rotation.
798    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    /// Return the centroid of the rect.
809    pub fn center(&self) -> PointF {
810        self.center
811    }
812
813    /// Return the normalized vector that indicates the "up" direction for
814    /// this rect.
815    pub fn up_axis(&self) -> Vec2 {
816        self.up
817    }
818
819    /// Return the extent of the rect along the axis perpendicular to `self.up_axis()`.
820    pub fn width(&self) -> f32 {
821        self.width
822    }
823
824    /// Return the extent of the rect along `self.up_axis()`.
825    pub fn height(&self) -> f32 {
826        self.height
827    }
828
829    /// Return the signed area of this rect.
830    pub fn area(&self) -> f32 {
831        self.height * self.width
832    }
833
834    /// Set the extents of this rect. `width` and `height` must be >= 0.
835    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    /// Return true if the intersection of this rect and `other` is non-empty.
842    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    /// Return a new axis-aligned RotatedRect whose bounding rectangle matches
853    /// `r`.
854    pub fn from_rect(r: RectF) -> RotatedRect {
855        RotatedRect::new(r.center(), Vec2::from_yx(1., 0.), r.width(), r.height())
856    }
857
858    /// Return the rectangle with the same corner points as `self`, but with
859    /// an up axis that has a direction as close to `up` as possible.
860    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
900/// Return the bounding rectangle of a collection of shapes.
901///
902/// Returns `None` if the collection is empty.
903pub 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/// Polygon shape defined by a list of vertices.
916///
917/// Depending on the storage type `S`, a Polygon can own its vertices
918/// (eg. `Vec<Point>`) or they can borrowed (eg. `&[Point]`).
919#[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    /// Avoids compiler complaining `T` is unused.
928    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    /// Create a view of a set of points as a polygon.
935    pub fn new(points: S) -> Polygon<T, S> {
936        Polygon {
937            points,
938            element_type: PhantomData,
939        }
940    }
941
942    /// Return a polygon which borrows its points from this polygon.
943    pub fn borrow(&self) -> Polygon<T, &[Point<T>]> {
944        Polygon::new(self.points.as_ref())
945    }
946
947    /// Return an iterator over the edges of this polygon.
948    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    /// Return a slice of the endpoints of the polygon's edges.
957    pub fn vertices(&self) -> &[Point<T>] {
958        self.points.as_ref()
959    }
960
961    /// Return a clone of this polygon which owns its vertices.
962    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    /// Return an iterator over coordinates of pixels that fill the polygon.
969    ///
970    /// Polygon filling treats the polygon's vertices as being located at the
971    /// center of pixels with the corresponding coordinates. Pixels are deemed
972    /// inside the polygon if a ray from -infinity to the pixel's center crosses
973    /// an odd number of polygon edges, aka. the even-odd rule [^1]. Pixel
974    /// centers which lie exactly on a polygon edge are treated as inside
975    /// the polygon for top/left edges and outside the polygon for bottom/right
976    /// edges. This follows conventions in various graphics libraries (eg. [^2]).
977    ///
978    /// This treatment of polygon vertices differs from graphics libraries like
979    /// Skia or Qt which use float coordinates for paths. In those libraries
980    /// polygon filling is still based on the relationship between polygon edges
981    /// and pixel centers, but integer polygon vertex coordinates refer to the
982    /// corners of pixels.
983    ///
984    /// [^1]: <https://en.wikipedia.org/wiki/Even–odd_rule>
985    /// [^2]: <https://learn.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-rasterizer-stage-rules#triangle-rasterization-rules-without-multisampling>
986    pub fn fill_iter(&self) -> FillIter {
987        FillIter::new(self.borrow())
988    }
989
990    /// Return true if the pixel with coordinates `p` lies inside the polygon.
991    ///
992    /// The intent of this function is to align with [`Polygon::fill_iter`] such
993    /// that `polygon.contains_pixel(p)` is equivalent to
994    /// `polygon.fill_iter().any(|fp| fp == p)` but faster because it doesn't
995    /// iterate over every pixel inside the polygon. See [`Polygon::fill_iter`]
996    /// for notes on how the inside/outisde status of a pixel is determined.
997    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            // Ignore horizontal edges.
1004            if min_y == max_y {
1005                continue;
1006            }
1007
1008            // Skip edges that don't overlap this point vertically.
1009            if p.y < min_y || p.y >= max_y {
1010                continue;
1011            }
1012
1013            // Check which side of the edge this point is on.
1014            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 lies to the left of the pixel.
1027                edge_crossings += 1;
1028            }
1029        }
1030
1031        edge_crossings % 2 == 1
1032    }
1033
1034    /// Return true if this polygon has no self-intersections and no holes.
1035    pub fn is_simple(&self) -> bool {
1036        // Test for self intersections. We don't need to test for holes
1037        // because this struct can't model a polygon with holes.
1038        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/// A collection of polygons, where each polygon is defined by a slice of points.
1083///
1084/// `Polygons` is primarily useful when building up collections of many polygons
1085/// as it stores all points in a single Vec, which is more efficient than
1086/// allocating a separate Vec for each polygon's points.
1087#[cfg_attr(
1088    feature = "serde_traits",
1089    derive(serde_derive::Serialize, serde_derive::Deserialize)
1090)]
1091pub struct Polygons {
1092    points: Vec<Point>,
1093
1094    // Offsets within `points` where each polygon starts and ends.
1095    polygons: Vec<Range<usize>>,
1096}
1097
1098impl Polygons {
1099    /// Construct an empty polygon collection.
1100    pub fn new() -> Polygons {
1101        Polygons {
1102            points: Vec::new(),
1103            polygons: Vec::new(),
1104        }
1105    }
1106
1107    /// Add a new polygon to the list, defined by the given points.
1108    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    /// Return the number of polygons in the collection.
1115    pub fn len(&self) -> usize {
1116        self.polygons.len()
1117    }
1118
1119    /// Return true if this collection has no polygons.
1120    pub fn is_empty(&self) -> bool {
1121        self.polygons.is_empty()
1122    }
1123
1124    /// Return an iterator over individual polygons in the sequence.
1125    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
1139/// Iterator over polygons in a [`Polygons`] collection.
1140pub 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        // TODO - Test cases where intercept is beyond start/end of line.
1197        let cases = [
1198            // Single point
1199            Case {
1200                start: Point::default(),
1201                end: Point::default(),
1202                point: Point::from_yx(3, 4),
1203                dist: 5.,
1204            },
1205            // Horizontal line
1206            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            // Vertical line
1213            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            // Line with +ve gradient
1220            Case {
1221                start: Point::default(),
1222                end: Point::from_yx(5, 5),
1223                point: Point::from_yx(4, 0),
1224                dist: (8f32).sqrt(), // Closest point is at (2, 2)
1225            },
1226            // Line with -ve gradient
1227            Case {
1228                start: Point::default(),
1229                end: Point::from_yx(5, -5),
1230                point: Point::from_yx(4, 0),
1231                dist: (8f32).sqrt(), // Closest point is at (2, -2)
1232            },
1233            // Point below line
1234            Case {
1235                start: Point::default(),
1236                end: Point::from_yx(5, 5),
1237                point: Point::from_yx(0, 4),
1238                dist: (8f32).sqrt(), // Closest point is at (2, 2)
1239            },
1240            // Point beyond end of horizontal line
1241            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    /// Create a line from [y1, x1, y2, x2] coordinates.
1309    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            // Horizontal and vertical lines that intersect
1327            Case {
1328                a: line_from_coords([0, 5, 10, 5]),
1329                b: line_from_coords([5, 0, 5, 10]),
1330                expected: true,
1331            },
1332            // Diagonal lines that intersect
1333            Case {
1334                a: line_from_coords([0, 0, 10, 10]),
1335                b: line_from_coords([10, 0, 0, 10]),
1336                expected: true,
1337            },
1338            // Horizontal and vertical lines that do not intersect
1339            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            // Horizontal and vertical lines that touch
1350            Case {
1351                a: line_from_coords([0, 5, 5, 5]),
1352                b: line_from_coords([5, 0, 5, 10]),
1353                expected: true,
1354            },
1355            // Test case from https://en.wikipedia.org/wiki/Intersection_(geometry)#Two_line_segments
1356            Case {
1357                a: line_from_coords([1, 1, 2, 3]),
1358                b: line_from_coords([4, 1, -1, 2]),
1359                expected: true,
1360            },
1361            // Parallel lines that do not touch
1362            Case {
1363                a: line_from_coords([0, 5, 0, 10]),
1364                b: line_from_coords([2, 5, 2, 10]),
1365                expected: false,
1366            },
1367            // Coincident lines
1368            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            // `intersects` should be commutative.
1379            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            // No overlap
1406            Case {
1407                a: (0, 10),
1408                b: (15, 20),
1409                overlap: 0,
1410            },
1411            // End of `a` overlaps start of `b`
1412            Case {
1413                a: (0, 10),
1414                b: (5, 15),
1415                overlap: 5,
1416            },
1417            // `a` overlaps all of `b`
1418            Case {
1419                a: (0, 10),
1420                b: (2, 8),
1421                overlap: 6,
1422            },
1423            // `a` and `b` start together, but `a` is shorter
1424            Case {
1425                a: (0, 5),
1426                b: (0, 10),
1427                overlap: 5,
1428            },
1429        ];
1430
1431        cases.test_each(|case| {
1432            // Horizontal lines
1433            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            // Vertical lines
1439            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            // (X, expected Y) coordinate pairs.
1481            points: Vec<(f32, Option<f32>)>,
1482        }
1483
1484        let cases = [
1485            // Slope 1, intercept 0
1486            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            // Slope 1, intercept -1
1497            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            // Horizontal line
1508            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            // Vertical line
1513            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            // Empty polygon
1556            Case {
1557                poly: Polygon::new(Vec::new()),
1558            },
1559            // Zero-height polygon
1560            Case {
1561                poly: Rect::from_tlbr(0, 0, 0, 5).to_polygon().to_owned(),
1562            },
1563            // Rects
1564            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            // Inverted rect
1571            Case {
1572                poly: Rect::from_tlbr(5, 5, 2, 2).to_polygon().to_owned(),
1573            },
1574            // Triangles
1575            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            // Create two grids that are slightly larger than the max X + Y
1585            // coordinates.
1586            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            // Fill one grid using `fill_iter` and the other using
1599            // `contains_pixel` tests, then verify that the same pixels get
1600            // filled.
1601            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            // Simple rect
1630            Case {
1631                poly: Rect::from_tlbr(0, 0, 10, 10).to_polygon().to_owned(),
1632                simple: true,
1633            },
1634            // 4-vertex poly with intersection
1635            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            // Empty polygon
1654            Case {
1655                vertices: Vec::new(),
1656                filled: Vec::new(),
1657            },
1658            // Single line
1659            Case {
1660                vertices: points_from_coords(&[[0, 0], [5, 5]]),
1661                filled: Vec::new(),
1662            },
1663            // Rect
1664            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            // Triangle
1679            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        // Points outside rect
1732        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        // Points inside rect
1736        assert_eq!(r.contains_point(Point::from_yx(8, 8)), true);
1737
1738        // Points on boundary
1739        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            // Axis-aligned
1758            Case {
1759                rrect: RotatedRect::new(PointF::from_yx(0., 0.), Vec2::from_yx(1., 0.), 10., 5.),
1760            },
1761            // Axis-aligned, inverted.
1762            Case {
1763                rrect: RotatedRect::new(PointF::from_yx(0., 0.), Vec2::from_yx(-1., 0.), 10., 5.),
1764            },
1765            // Rotated
1766            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            // Test points slightly inside.
1777            for c in r.expanded(-1e-5, -1e-5).corners() {
1778                assert!(r.contains(c));
1779            }
1780
1781            // Test points slightly outside.
1782            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            // Identical rects
1827            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            // Separated rects
1834            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 where bounding rectangles intersect but rotated rects do
1841            // not.
1842            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            // `intersects` should be transitive
1857            assert_eq!(case.b.intersects(&case.a), case.intersects);
1858        })
1859    }
1860
1861    #[test]
1862    fn test_rotated_rect_normalizes_up_vector() {
1863        // Create rotated rect with non-normalized "up" vector.
1864        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}