Skip to main content

sightloom_core/
polygon.rs

1//! Borrowed polygon membership geometry.
2
3use core::cmp::Ordering;
4
5use crate::{GeometryError, Point, line::point_on_segment, orientation::orientation_sign};
6
7/// A polygon borrowing its caller-owned vertex slice.
8#[derive(Clone, Copy, Debug, PartialEq)]
9pub struct Polygon<'a> {
10    points: &'a [Point],
11}
12
13impl<'a> Polygon<'a> {
14    /// Creates a polygon from at least three supplied points.
15    ///
16    /// # Errors
17    ///
18    /// Returns [`GeometryError::TooFewPoints`] when fewer than three points
19    /// are supplied.
20    pub fn new(points: &'a [Point]) -> Result<Self, GeometryError> {
21        if points.len() < 3 {
22            return Err(GeometryError::TooFewPoints);
23        }
24
25        Ok(Self { points })
26    }
27
28    /// Returns the caller-owned vertex slice.
29    #[must_use]
30    pub const fn points(self) -> &'a [Point] {
31        self.points
32    }
33
34    /// Returns whether a point is inside or on this polygon's boundary.
35    #[must_use]
36    pub fn contains(self, point: Point) -> bool {
37        let mut inside = false;
38
39        for index in 0..self.points.len() {
40            let start = self.points[index];
41            let end = self.points[(index + 1) % self.points.len()];
42            if point_on_segment(start, end, point) {
43                return true;
44            }
45
46            if (start.y() > point.y()) != (end.y() > point.y()) {
47                let side = orientation_sign(start, end, point);
48                if (end.y() > start.y() && side == Ordering::Greater)
49                    || (end.y() < start.y() && side == Ordering::Less)
50                {
51                    inside = !inside;
52                }
53            }
54        }
55
56        inside
57    }
58}