fenris_geometry/
polytope.rs

1use crate::{HalfPlane, Line2d, LineSegment2d, SimplePolygon2d, Triangle, Triangle2d};
2use fenris_traits::Real;
3use itertools::Itertools;
4use nalgebra::{Point2, Scalar, Unit, Vector2};
5
6/// Type used to indicate conversion failure in the presence of concavity.
7#[derive(Debug, Copy, Clone, PartialEq, Eq)]
8pub struct ConcavePolygonError;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct ConvexPolygon<T>
12where
13    T: Scalar,
14{
15    // TODO: SmallVec?
16    // Edges are implicitly represented as (i, i + 1)
17    vertices: Vec<Point2<T>>,
18}
19
20impl<T> From<LineSegment2d<T>> for ConvexPolygon<T>
21where
22    T: Scalar,
23{
24    fn from(segment: LineSegment2d<T>) -> Self {
25        ConvexPolygon::from_vertices(vec![segment.start().clone(), segment.end().clone()])
26    }
27}
28
29impl<T> ConvexPolygon<T>
30where
31    T: Scalar,
32{
33    /// Construct a new convex polygon from the given vertices, assumed to be ordered in a
34    /// counter-clockwise way such that (i, i + 1) forms an edge between vertex i and i + 1.
35    ///
36    /// It is assumed that the polygon is convex.
37    pub fn from_vertices(vertices: Vec<Point2<T>>) -> ConvexPolygon<T> {
38        Self { vertices }
39    }
40
41    pub fn vertices(&self) -> &[Point2<T>] {
42        &self.vertices
43    }
44
45    /// Returns the number of edges in the polygon. Note that a single point has 1 edge,
46    /// pointing from itself to itself, a line segment has two edges, and in general
47    /// the number of edges is equal to the number of vertices.
48    pub fn num_edges(&self) -> usize {
49        self.vertices().len()
50    }
51
52    pub fn edges(&self) -> impl Iterator<Item = (&Point2<T>, &Point2<T>)> {
53        let num_vertices = self.vertices().len();
54        self.vertices()
55            .iter()
56            .cycle()
57            .take(num_vertices + 1)
58            .tuple_windows()
59    }
60
61    pub fn is_empty(&self) -> bool {
62        self.vertices.is_empty()
63    }
64
65    pub fn is_point(&self) -> bool {
66        self.vertices.len() == 1
67    }
68
69    pub fn is_line_segment(&self) -> bool {
70        self.vertices.len() == 2
71    }
72}
73
74impl<T> ConvexPolygon<T>
75where
76    T: Real,
77{
78    /// Iterates over the half planes that define the polygon.
79    ///
80    /// Every non-degenerate polygon can be represented by the intersection of a finite number
81    /// of closed half-planes.
82    ///
83    /// If the polygon is degenerate, the intersection of the half planes returned by this method
84    /// will in general not be sufficient to describe the polygon.
85    pub fn half_planes<'a>(&'a self) -> impl Iterator<Item = HalfPlane<T>> + 'a {
86        self.edges().filter_map(|(v1, v2)| {
87            if v1 != v2 {
88                let edge_dir = v2 - v1;
89                let edge_normal = Vector2::new(edge_dir.y, -edge_dir.x);
90                Some(HalfPlane::from_point_and_normal(*v1, Unit::new_normalize(edge_normal)))
91            } else {
92                None
93            }
94        })
95    }
96
97    /// Determines if the (closed) convex polygon contains the given point.
98    pub fn contains_point(&self, point: &Point2<T>) -> bool {
99        if self.is_point() {
100            self.vertices.first().unwrap() == point
101        } else if self.is_line_segment() {
102            unimplemented!()
103        } else {
104            self.half_planes()
105                .all(|half_plane| half_plane.contains_point(point))
106        }
107    }
108
109    /// Computes the intersection with the current polygon and the given half plane,
110    /// and returns a new polygon that holds the result.
111    ///
112    /// Note: No steps have been made to make this routine numerically robust.
113    /// TODO: Make numerically robust?
114    pub fn intersect_halfplane(&self, half_plane: &HalfPlane<T>) -> ConvexPolygon<T> {
115        let mut new_vertices = Vec::new();
116
117        // Handle special case of the polygon consisting of a single vertex
118        if self.vertices.len() == 1 {
119            let first = self.vertices().first().unwrap();
120            if half_plane.contains_point(first) {
121                new_vertices.push(first.clone());
122            }
123        } else {
124            for (v1, v2) in self.edges() {
125                let v1_contained = half_plane.contains_point(v1);
126                let v2_contained = half_plane.contains_point(v2);
127                if v1_contained {
128                    new_vertices.push(v1.clone());
129                }
130
131                if (v1_contained && !v2_contained) || (!v1_contained && v2_contained) {
132                    // Edge is intersected, add vertex at intersection point
133                    let dir = (v2 - v1).normalize();
134                    let intersection_point = half_plane
135                        .surface()
136                        .intersect(&Line2d::from_point_and_dir(v1.clone(), dir))
137                        .expect(
138                            "We already know that the line must intersect the edge, \
139                             so this should work unless we have some ugly numerical \
140                             artifacts.",
141                        );
142
143                    new_vertices.push(intersection_point);
144                }
145            }
146        }
147
148        ConvexPolygon::from_vertices(new_vertices)
149    }
150
151    /// Computes the intersection of this polygon and the given convex polygon.
152    pub fn intersect_polygon(&self, other: &ConvexPolygon<T>) -> Self {
153        // TODO: Deal with degeneracies
154        if self.is_point() || other.is_point() {
155            unimplemented!()
156        } else if self.is_line_segment() {
157            let segment = LineSegment2d::from_end_points(self.vertices[0], self.vertices[1]);
158            segment
159                .intersect_polygon(other)
160                .map(|segment| ConvexPolygon::from_vertices(vec![*segment.start(), *segment.end()]))
161                .unwrap_or_else(|| ConvexPolygon::from_vertices(Vec::new()))
162        } else if other.is_line_segment() {
163            other.intersect_polygon(self)
164        } else {
165            let mut result = self.clone();
166            for half_plane in other.half_planes() {
167                result = result.intersect_halfplane(&half_plane);
168            }
169            result
170        }
171    }
172
173    /// Splits the convex polygon into a set of disjoint triangles that exactly cover the area of the
174    /// polygon.
175    pub fn triangulate<'a>(&'a self) -> impl Iterator<Item = Triangle2d<T>> + 'a {
176        self.edges()
177            // Use saturating subtraction so that we don't overflow and get an empty
178            // iterator in the case that the polygon has no vertices
179            .take(self.num_edges().saturating_sub(1))
180            .skip(1)
181            .map(move |(a, b)| Triangle([*self.vertices.first().unwrap(), *a, *b]))
182    }
183
184    pub fn triangulate_into_vec(&self) -> Vec<Triangle2d<T>> {
185        self.triangulate().collect()
186    }
187}
188
189impl<T> From<ConvexPolygon<T>> for SimplePolygon2d<T>
190where
191    T: Scalar,
192{
193    fn from(poly: ConvexPolygon<T>) -> Self {
194        SimplePolygon2d::from_vertices(poly.vertices)
195    }
196}