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(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    /// Construct a point from X and Y coordinates.
69    pub fn from_yx(y: T, x: T) -> Self {
70        Point { y, x }
71    }
72
73    /// Set the coordinates of this point.
74    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    /// Return the vector from this point to another point.
97    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    /// Return the vector from the origin to this point.
104    pub fn to_vec(self) -> Vec2 {
105        Vec2::from_xy(self.x, self.y)
106    }
107}
108
109impl Point<i32> {
110    /// Return self as a [y, x] array. This is useful for indexing into an
111    /// image or matrix.
112    ///
113    /// Panics if the X or Y coordinates of the point are negative.
114    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    /// Return the neighbors of the current point in clockwise order, starting
120    /// from the point directly above `self`.
121    pub fn neighbors(self) -> [Point; 8] {
122        [
123            self.translate(-1, 0),  // N
124            self.translate(-1, 1),  // NE
125            self.translate(0, 1),   // E
126            self.translate(1, 1),   // SE
127            self.translate(1, 0),   // S
128            self.translate(1, -1),  // SW
129            self.translate(0, -1),  // W
130            self.translate(-1, -1), // NW
131        ]
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
152/// Compute the overlap between two 1D lines `a` and `b`, where each line is
153/// given as (start, end) coords.
154///
155/// Returns NaN if any input coordinate is NaN.
156fn 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    // This check handles NaN for two of the inputs. The checks below will
166    // return NaN if `overlap` is NaN, which handles the other two inputs.
167    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
180/// Sort the elements of a tuple. If the ordering of the elements is undefined,
181/// return the input unchanged.
182fn 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/// A bounded line segment defined by a start and end point.
191#[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    /// Return true if this line has zero length.
212    pub fn is_empty(&self) -> bool {
213        self.start == self.end
214    }
215
216    /// Return the difference between the starting and ending X coordinates of
217    /// the line.
218    pub fn width(&self) -> T {
219        self.end.x - self.start.x
220    }
221
222    /// Return the difference between the starting and ending Y coordinates of
223    /// the line.
224    pub fn height(&self) -> T {
225        self.end.y - self.start.y
226    }
227
228    /// Return true if the Y coordinate of the line's start and end points are
229    /// the same.
230    pub fn is_horizontal(&self) -> bool {
231        self.start.y == self.end.y
232    }
233
234    /// Return a copy of this line with the start and end points swapped.
235    pub fn reverse(&self) -> Line<T> {
236        Line::from_endpoints(self.end, self.start)
237    }
238
239    /// Return a copy of this line with the same endpoints but swapped if
240    /// needed so that `end.y >= start.y`.
241    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    /// Return a copy of this line with the same endpoints but swapped if
250    /// needed so that `end.x >= start.x`.
251    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    /// Return the number of pixels by which this line overlaps `other` in the
260    /// vertical direction.
261    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    /// Return the number of pixels by which this line overlaps `other` in the
266    /// horizontal direction.
267    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    /// Return the euclidean distance between a point and the closest coordinate
274    /// that lies on the line.
275    pub fn distance(&self, p: PointF) -> f32 {
276        if self.is_empty() {
277            return self.start.distance(p);
278        }
279
280        // Method taken from http://www.faqs.org/faqs/graphics/algorithms-faq/,
281        // "Subject 1.02: How do I find the distance from a point to a line?".
282
283        // Compute normalized scalar projection of line from `start` to `p` onto
284        // self. This indicates how far along the `self` line the nearest point
285        // to `p` is.
286        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            // Nearest point is start of line.
292            self.start.distance(p)
293        } else if scalar_proj >= 1. {
294            // Nearest point is end of line.
295            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    /// Test whether this line segment intersects `other` at a single point.
305    ///
306    /// Returns false if the line segments do not intersect, or are coincident
307    /// (ie. overlap for part of their lengths).
308    pub fn intersects(&self, other: Line<f32>) -> bool {
309        // See https://en.wikipedia.org/wiki/Intersection_(geometry)#Two_line_segments
310
311        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        // To find the intersection, we first represent the lines as functions
317        // parametrized by `s` and `t`:
318        //
319        // x(s), y(s) = x1 + s(x2 - x1), y1 + s(y2 - y1)
320        // x(t), y(t) = x3 + t(x4 - x3), y3 + t(y4 - y3)
321        //
322        // Then the coordinates of the intersection s0 and t0 are the solutions
323        // of:
324        //
325        // s(x2 - x1) - t(x4 - x3) = x3 - x1
326        // s(y2 - y1) - t(y4 - y3) = y3 - y1
327        //
328        // These equations are solved using Cramer's rule. The lines intersect
329        // if s0 and t0 are in [0, 1].
330
331        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            // Lines are either parallel or coincident.
342            return false;
343        }
344        let det_a0 = b0 * d - b * b1;
345        let det_a1 = a * b1 - b0 * c;
346
347        // We could calculate `s0` as `det_a0 / det_a` and `t0` as `det_a1 / det_a`
348        // (using float division). We only need to test whether s0 and t0 are
349        // in [0, 1] though, so this can be done without division.
350        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    /// Return the midpoint between the start and end points of the line.
359    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    /// Return `(slope, intercept)` tuple for line or None if the line is
366    /// vertical.
367    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    /// Return the X coordinate that corresponds to a given Y coordinate on
378    /// the line.
379    ///
380    /// Returns `None` if the Y coordinate is not on the line or the line is
381    /// horizontal.
382    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    /// Return the Y coordinate that corresponds to a given X coordinate on
393    /// the line.
394    ///
395    /// Returns `None` if the X coordinate is not on the line or the line
396    /// is vertical.
397    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    /// Return the euclidean distance between a point and the closest coordinate
413    /// that lies on the line.
414    pub fn distance(&self, p: Point) -> f32 {
415        self.to_f32().distance(p.to_f32())
416    }
417
418    /// Test whether this line segment intersects `other` at a single point.
419    ///
420    /// Returns false if the line segments do not intersect, or are coincident
421    /// (ie. overlap for part of their lengths).
422    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/// Rectangle defined by left, top, right and bottom coordinates.
438///
439/// The left and top coordinates are inclusive. The right and bottom coordinates
440/// are exclusive.
441#[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        // TODO - Handle inverted rects here
460        self.bottom_right.x - self.top_left.x
461    }
462
463    pub fn height(&self) -> T {
464        // TODO - Handle inverted rects here
465        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    /// Return the corners of the rect in clockwise order, starting from the
485    /// top left.
486    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    /// Return the coordinate of the top-left corner of the rect.
496    pub fn top_left(&self) -> Point<T> {
497        self.top_left
498    }
499
500    /// Return the coordinate of the top-right corner of the rect.
501    pub fn top_right(&self) -> Point<T> {
502        Point::from_yx(self.top_left.y, self.bottom_right.x)
503    }
504
505    /// Return the coordinate of the bottom-left corner of the rect.
506    pub fn bottom_left(&self) -> Point<T> {
507        Point::from_yx(self.bottom_right.y, self.top_left.x)
508    }
509
510    /// Return the coordinate of the bottom-right corner of the rect.
511    pub fn bottom_right(&self) -> Point<T> {
512        self.bottom_right
513    }
514
515    /// Return the line segment of the left edge of the rect.
516    pub fn left_edge(&self) -> Line<T> {
517        Line::from_endpoints(self.top_left(), self.bottom_left())
518    }
519
520    /// Return the line segment of the top edge of the rect.
521    pub fn top_edge(&self) -> Line<T> {
522        Line::from_endpoints(self.top_left(), self.top_right())
523    }
524
525    /// Return the line segment of the right edge of the rect.
526    pub fn right_edge(&self) -> Line<T> {
527        Line::from_endpoints(self.top_right(), self.bottom_right())
528    }
529
530    /// Return the line segment of the bottom edge of the rect.
531    pub fn bottom_edge(&self) -> Line<T> {
532        Line::from_endpoints(self.bottom_left(), self.bottom_right())
533    }
534
535    /// Return the top, left, bottom and right coordinates as an array.
536    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    /// Return a rect with top-left corner at 0, 0 and the given height/width.
546    pub fn from_hw(height: T, width: T) -> Rect<T> {
547        Self::new(Point::default(), Point::from_yx(height, width))
548    }
549
550    /// Return a rect with the given top, left, bottom and right coordinates.
551    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    /// Return a rect with the given top, left, height and width.
556    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    /// Return the signed area of this rect.
561    pub fn area(&self) -> T
562    where
563        T: std::ops::Mul<Output = T>,
564    {
565        self.width() * self.height()
566    }
567
568    /// Return the top, left, height and width as an array.
569    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    /// Return true if `other` lies on the boundary or interior of this rect.
579    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    /// Return true if the width or height of this rect are <= 0.
587    pub fn is_empty(&self) -> bool {
588        self.right() <= self.left() || self.bottom() <= self.top()
589    }
590
591    /// Return a new Rect with each coordinate adjusted by an offset.
592    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    /// Return true if the intersection of this rect and `other` is non-empty.
600    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    /// Return the smallest rect that contains both this rect and `other`.
606    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    /// Return the largest rect that is contained within this rect and `other`.
615    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    /// Return true if `other` lies entirely within this rect.
624    pub fn contains(&self, other: Rect<T>) -> bool {
625        self.union(other) == *self
626    }
627
628    /// Return a new with each side adjusted so that the result lies inside
629    /// `rect`.
630    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    /// Return the center point of the rect.
641    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    /// Return the Intersection over Union ratio for this rect and `other`.
648    ///
649    /// See <https://en.wikipedia.org/wiki/Jaccard_index>.
650    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    /// Return the center point of the rect.
666    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    /// Return the Intersection over Union ratio for this rect and `other`.
673    ///
674    /// See <https://en.wikipedia.org/wiki/Jaccard_index>.
675    pub fn iou(&self, other: RectF) -> f32 {
676        self.intersection(other).area() / self.union(other).area()
677    }
678
679    /// Return the smallest rect with integral coordinates that contains this
680    /// rect.
681    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/// An oriented rectangle.
700///
701/// This is characterized by a center point, an "up" direction indicating the
702/// orientation, width (extent along axis perpendicular to the up axis) and
703/// height (extent along up axis).
704#[derive(Copy, Clone, Debug)]
705#[cfg_attr(feature = "serde_traits", derive(serde::Serialize, serde::Deserialize))]
706pub struct RotatedRect {
707    // Centroid of the rect.
708    center: PointF,
709
710    // Unit-length vector indicating the "up" direction for this rect.
711    up: Vec2,
712
713    // Extent of the rect along the axis perpendicular to `up`.
714    width: f32,
715
716    // Extent of the rect along the `up` axis.
717    height: f32,
718}
719
720impl RotatedRect {
721    /// Construct a new RotatedRect with a given `center`, up direction and
722    /// dimensions.
723    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    /// Return true if a point lies within this rotated rect.
733    pub fn contains(&self, point: PointF) -> bool {
734        // Treat zero width/height rectangles as being very thin rectangles
735        // rather than lines.
736        let height = self.height.max(1e-6);
737        let width = self.width.max(1e-6);
738
739        // Project line from center to `p` onto the up and cross axis. The
740        // results will be in the range [-1, 1] if the point is within the
741        // rect.
742        //
743        // See notes in `Line::distance` about distance from point to a line.
744        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    /// Return a copy of this rect with width increased by `dw` and height
755    /// increased by `dh`.
756    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    /// Return the coordinates of the rect's corners.
765    ///
766    /// The corners are returned in clockwise order starting from the corner
767    /// that is the top-left when the "up" axis has XY coordinates [0, 1], or
768    /// equivalently, bottom-right when the "up" axis has XY coords [0, -1].
769    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    /// Return the edges of this rect, in clockwise order starting from the
785    /// edge that is the top edge if the rect has no rotation.
786    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    /// Return the centroid of the rect.
797    pub fn center(&self) -> PointF {
798        self.center
799    }
800
801    /// Return the normalized vector that indicates the "up" direction for
802    /// this rect.
803    pub fn up_axis(&self) -> Vec2 {
804        self.up
805    }
806
807    /// Return the extent of the rect along the axis perpendicular to `self.up_axis()`.
808    pub fn width(&self) -> f32 {
809        self.width
810    }
811
812    /// Return the extent of the rect along `self.up_axis()`.
813    pub fn height(&self) -> f32 {
814        self.height
815    }
816
817    /// Return the signed area of this rect.
818    pub fn area(&self) -> f32 {
819        self.height * self.width
820    }
821
822    /// Set the extents of this rect. `width` and `height` must be >= 0.
823    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    /// Return true if the intersection of this rect and `other` is non-empty.
830    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    /// Return a new axis-aligned RotatedRect whose bounding rectangle matches
841    /// `r`.
842    pub fn from_rect(r: RectF) -> RotatedRect {
843        RotatedRect::new(r.center(), Vec2::from_yx(1., 0.), r.width(), r.height())
844    }
845
846    /// Return the rectangle with the same corner points as `self`, but with
847    /// an up axis that has a direction as close to `up` as possible.
848    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
888/// Return the bounding rectangle of a collection of shapes.
889///
890/// Returns `None` if the collection is empty.
891pub 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/// Polygon shape defined by a list of vertices.
904///
905/// Depending on the storage type `S`, a Polygon can own its vertices
906/// (eg. `Vec<Point>`) or they can borrowed (eg. `&[Point]`).
907#[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    /// Avoids compiler complaining `T` is unused.
913    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    /// Create a view of a set of points as a polygon.
920    pub fn new(points: S) -> Polygon<T, S> {
921        Polygon {
922            points,
923            element_type: PhantomData,
924        }
925    }
926
927    /// Return a polygon which borrows its points from this polygon.
928    pub fn borrow(&self) -> Polygon<T, &[Point<T>]> {
929        Polygon::new(self.points.as_ref())
930    }
931
932    /// Return an iterator over the edges of this polygon.
933    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    /// Return a slice of the endpoints of the polygon's edges.
942    pub fn vertices(&self) -> &[Point<T>] {
943        self.points.as_ref()
944    }
945
946    /// Return a clone of this polygon which owns its vertices.
947    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    /// Return an iterator over coordinates of pixels that fill the polygon.
954    ///
955    /// Polygon filling treats the polygon's vertices as being located at the
956    /// center of pixels with the corresponding coordinates. Pixels are deemed
957    /// inside the polygon if a ray from -infinity to the pixel's center crosses
958    /// an odd number of polygon edges, aka. the even-odd rule [^1]. Pixel
959    /// centers which lie exactly on a polygon edge are treated as inside
960    /// the polygon for top/left edges and outside the polygon for bottom/right
961    /// edges. This follows conventions in various graphics libraries (eg. [^2]).
962    ///
963    /// This treatment of polygon vertices differs from graphics libraries like
964    /// Skia or Qt which use float coordinates for paths. In those libraries
965    /// polygon filling is still based on the relationship between polygon edges
966    /// and pixel centers, but integer polygon vertex coordinates refer to the
967    /// corners of pixels.
968    ///
969    /// [^1]: <https://en.wikipedia.org/wiki/Even–odd_rule>
970    /// [^2]: <https://learn.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-rasterizer-stage-rules#triangle-rasterization-rules-without-multisampling>
971    pub fn fill_iter(&self) -> FillIter {
972        FillIter::new(self.borrow())
973    }
974
975    /// Return true if the pixel with coordinates `p` lies inside the polygon.
976    ///
977    /// The intent of this function is to align with [`Polygon::fill_iter`] such
978    /// that `polygon.contains_pixel(p)` is equivalent to
979    /// `polygon.fill_iter().any(|fp| fp == p)` but faster because it doesn't
980    /// iterate over every pixel inside the polygon. See [`Polygon::fill_iter`]
981    /// for notes on how the inside/outisde status of a pixel is determined.
982    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            // Ignore horizontal edges.
989            if min_y == max_y {
990                continue;
991            }
992
993            // Skip edges that don't overlap this point vertically.
994            if p.y < min_y || p.y >= max_y {
995                continue;
996            }
997
998            // Check which side of the edge this point is on.
999            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 lies to the left of the pixel.
1012                edge_crossings += 1;
1013            }
1014        }
1015
1016        edge_crossings % 2 == 1
1017    }
1018
1019    /// Return true if this polygon has no self-intersections and no holes.
1020    pub fn is_simple(&self) -> bool {
1021        // Test for self intersections. We don't need to test for holes
1022        // because this struct can't model a polygon with holes.
1023        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/// A collection of polygons, where each polygon is defined by a slice of points.
1068///
1069/// `Polygons` is primarily useful when building up collections of many polygons
1070/// as it stores all points in a single Vec, which is more efficient than
1071/// allocating a separate Vec for each polygon's points.
1072#[cfg_attr(feature = "serde_traits", derive(serde::Serialize, serde::Deserialize))]
1073pub struct Polygons {
1074    points: Vec<Point>,
1075
1076    // Offsets within `points` where each polygon starts and ends.
1077    polygons: Vec<Range<usize>>,
1078}
1079
1080impl Polygons {
1081    /// Construct an empty polygon collection.
1082    pub fn new() -> Polygons {
1083        Polygons {
1084            points: Vec::new(),
1085            polygons: Vec::new(),
1086        }
1087    }
1088
1089    /// Add a new polygon to the list, defined by the given points.
1090    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    /// Return the number of polygons in the collection.
1097    pub fn len(&self) -> usize {
1098        self.polygons.len()
1099    }
1100
1101    /// Return true if this collection has no polygons.
1102    pub fn is_empty(&self) -> bool {
1103        self.polygons.is_empty()
1104    }
1105
1106    /// Return an iterator over individual polygons in the sequence.
1107    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
1121/// Iterator over polygons in a [`Polygons`] collection.
1122pub 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        // TODO - Test cases where intercept is beyond start/end of line.
1179        let cases = [
1180            // Single point
1181            Case {
1182                start: Point::default(),
1183                end: Point::default(),
1184                point: Point::from_yx(3, 4),
1185                dist: 5.,
1186            },
1187            // Horizontal line
1188            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            // Vertical line
1195            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            // Line with +ve gradient
1202            Case {
1203                start: Point::default(),
1204                end: Point::from_yx(5, 5),
1205                point: Point::from_yx(4, 0),
1206                dist: (8f32).sqrt(), // Closest point is at (2, 2)
1207            },
1208            // Line with -ve gradient
1209            Case {
1210                start: Point::default(),
1211                end: Point::from_yx(5, -5),
1212                point: Point::from_yx(4, 0),
1213                dist: (8f32).sqrt(), // Closest point is at (2, -2)
1214            },
1215            // Point below line
1216            Case {
1217                start: Point::default(),
1218                end: Point::from_yx(5, 5),
1219                point: Point::from_yx(0, 4),
1220                dist: (8f32).sqrt(), // Closest point is at (2, 2)
1221            },
1222            // Point beyond end of horizontal line
1223            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    /// Create a line from [y1, x1, y2, x2] coordinates.
1291    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            // Horizontal and vertical lines that intersect
1309            Case {
1310                a: line_from_coords([0, 5, 10, 5]),
1311                b: line_from_coords([5, 0, 5, 10]),
1312                expected: true,
1313            },
1314            // Diagonal lines that intersect
1315            Case {
1316                a: line_from_coords([0, 0, 10, 10]),
1317                b: line_from_coords([10, 0, 0, 10]),
1318                expected: true,
1319            },
1320            // Horizontal and vertical lines that do not intersect
1321            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            // Horizontal and vertical lines that touch
1332            Case {
1333                a: line_from_coords([0, 5, 5, 5]),
1334                b: line_from_coords([5, 0, 5, 10]),
1335                expected: true,
1336            },
1337            // Test case from https://en.wikipedia.org/wiki/Intersection_(geometry)#Two_line_segments
1338            Case {
1339                a: line_from_coords([1, 1, 2, 3]),
1340                b: line_from_coords([4, 1, -1, 2]),
1341                expected: true,
1342            },
1343            // Parallel lines that do not touch
1344            Case {
1345                a: line_from_coords([0, 5, 0, 10]),
1346                b: line_from_coords([2, 5, 2, 10]),
1347                expected: false,
1348            },
1349            // Coincident lines
1350            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            // `intersects` should be commutative.
1361            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            // No overlap
1388            Case {
1389                a: (0, 10),
1390                b: (15, 20),
1391                overlap: 0,
1392            },
1393            // End of `a` overlaps start of `b`
1394            Case {
1395                a: (0, 10),
1396                b: (5, 15),
1397                overlap: 5,
1398            },
1399            // `a` overlaps all of `b`
1400            Case {
1401                a: (0, 10),
1402                b: (2, 8),
1403                overlap: 6,
1404            },
1405            // `a` and `b` start together, but `a` is shorter
1406            Case {
1407                a: (0, 5),
1408                b: (0, 10),
1409                overlap: 5,
1410            },
1411        ];
1412
1413        cases.test_each(|case| {
1414            // Horizontal lines
1415            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            // Vertical lines
1421            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            // (X, expected Y) coordinate pairs.
1463            points: Vec<(f32, Option<f32>)>,
1464        }
1465
1466        let cases = [
1467            // Slope 1, intercept 0
1468            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            // Slope 1, intercept -1
1479            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            // Horizontal line
1490            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            // Vertical line
1495            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            // Empty polygon
1538            Case {
1539                poly: Polygon::new(Vec::new()),
1540            },
1541            // Zero-height polygon
1542            Case {
1543                poly: Rect::from_tlbr(0, 0, 0, 5).to_polygon().to_owned(),
1544            },
1545            // Rects
1546            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            // Inverted rect
1553            Case {
1554                poly: Rect::from_tlbr(5, 5, 2, 2).to_polygon().to_owned(),
1555            },
1556            // Triangles
1557            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            // Create two grids that are slightly larger than the max X + Y
1567            // coordinates.
1568            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            // Fill one grid using `fill_iter` and the other using
1581            // `contains_pixel` tests, then verify that the same pixels get
1582            // filled.
1583            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            // Simple rect
1612            Case {
1613                poly: Rect::from_tlbr(0, 0, 10, 10).to_polygon().to_owned(),
1614                simple: true,
1615            },
1616            // 4-vertex poly with intersection
1617            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            // Empty polygon
1636            Case {
1637                vertices: Vec::new(),
1638                filled: Vec::new(),
1639            },
1640            // Single line
1641            Case {
1642                vertices: points_from_coords(&[[0, 0], [5, 5]]),
1643                filled: Vec::new(),
1644            },
1645            // Rect
1646            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            // Triangle
1661            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        // Points outside rect
1714        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        // Points inside rect
1718        assert_eq!(r.contains_point(Point::from_yx(8, 8)), true);
1719
1720        // Points on boundary
1721        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            // Axis-aligned
1740            Case {
1741                rrect: RotatedRect::new(PointF::from_yx(0., 0.), Vec2::from_yx(1., 0.), 10., 5.),
1742            },
1743            // Axis-aligned, inverted.
1744            Case {
1745                rrect: RotatedRect::new(PointF::from_yx(0., 0.), Vec2::from_yx(-1., 0.), 10., 5.),
1746            },
1747            // Rotated
1748            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            // Test points slightly inside.
1759            for c in r.expanded(-1e-5, -1e-5).corners() {
1760                assert!(r.contains(c));
1761            }
1762
1763            // Test points slightly outside.
1764            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            // Identical rects
1809            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            // Separated rects
1816            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 where bounding rectangles intersect but rotated rects do
1823            // not.
1824            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            // `intersects` should be transitive
1839            assert_eq!(case.b.intersects(&case.a), case.intersects);
1840        })
1841    }
1842
1843    #[test]
1844    fn test_rotated_rect_normalizes_up_vector() {
1845        // Create rotated rect with non-normalized "up" vector.
1846        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}