prima/geom/
polygon.rs

1use super::{Float, Line2, Triangle, Vec2, PI};
2
3/// A complex polygon, defined by an array of vertices. It can be built from empty, as an ngon of n sides or with a set of verticies.
4/// # Examples
5///
6/// ```
7/// let poly = Polygon::new(Vec2::new(0.0, 0.0), Vec2::new(0.0, 8.0), Vec2::new(4.0, 8.0), Vec2::new(8.0, 8.0), Vec2::new(8.0, 0.0));
8/// assert_eq!(poly.n(), 5);
9/// 
10/// let poly2 = Polygon::new_ngon(Vec2::new(32.0, 32.0), 16.0, 8);
11/// assert_eq!(poly2.n(), 8);
12/// ```
13pub struct Polygon {
14    vertices: Vec<Vec2>,
15}
16
17impl Polygon {
18    /// Builds a new Polygon from the given Vec of points.
19    pub fn new(vertices: Vec<Vec2>) -> Self {
20        Self { vertices }
21    }
22    
23    /// Creates a new Polygon with no points or edges assigned.
24    pub fn empty() -> Self {
25        Self {
26            vertices: Vec::new(),
27        }
28    }
29
30    /// Creates an empty Polygon with a set capacity for the number of points it may contain.
31    pub fn with_capacity(capacity: usize) -> Self {
32        Self {
33            vertices: Vec::with_capacity(capacity),
34        }
35    }
36
37    /// Builds an ngon of equal length sides.
38    pub fn new_ngon(pos: Vec2, circumradius: Float, n: usize) -> Self {
39        if n < 3 {
40            panic!("Polygon must have at least 3 sides");
41        }
42
43        let mut poly = Self {
44            vertices: Vec::new(),
45        };
46
47        let angle = (2. * PI) / n as Float;
48
49        for i in 0..n {
50            // angle is adjusted by Pi/2 so triangulation starts from 12 O'clock
51            let a = angle * i as Float + (PI / 2.);
52            let x = a.cos() * circumradius;
53            let y = a.sin() * circumradius;
54            poly.vertices.push(Vec2::new(x, y) + pos);
55        }
56        poly
57    }
58
59    /// Adds a vertex to the polygon.
60    pub fn add_vertex(&mut self, v: Vec2) {
61        self.vertices.push(v);
62    }
63
64    /// The number of sides
65    pub fn n(&self) -> usize {
66        self.vertices.len()
67    }
68
69    /// Calculates the interior angle for a regular polygon of this size
70    pub fn interior_angle(&self) -> Float {
71        let n = self.vertices.len() as Float;
72        ((n as Float - 2.) * PI) / n
73    }
74
75    /// Returns all vertices in the polygon
76    pub fn vertices(&self) -> Vec<Vec2> {
77        self.vertices.clone()
78    }
79
80    /// Generates all edges
81    pub fn edges(&self) -> Vec<Line2> {
82        let mut lines = Vec::new();
83        for (i, _) in self.vertices.iter().enumerate() {
84            lines.push(self.edge(i, true).unwrap());
85        }
86        lines
87    }
88
89    /// Getter for vertex at given index
90    pub fn vertex(&self, i: usize) -> Option<Vec2> {
91        if i < self.vertices.len() {
92            return Some(self.vertices[i]);
93        }
94        return None;
95    }
96
97    /// Returns true if polygon is convex
98    pub fn is_convex(&self) -> bool {
99        let n = self.vertices.len();
100        if n < 3 {
101            true
102        } else {
103            let mut i = 0;
104            let l = n - 2;
105
106            while i < l {
107                let triangle = Triangle::new(
108                    self.vertices[i],
109                    self.vertices[i + 1],
110                    self.vertices[i + 2],
111                );
112                if !triangle.is_convex() {
113                    return false;
114                } else {
115                    i += 3;
116                }
117            }
118
119            let triangle =
120                Triangle::new(self.vertices[l], self.vertices[l + 1], self.vertices[0]);
121            if !triangle.is_convex() {
122                return false;
123            }
124            let triangle =
125                Triangle::new(self.vertices[l + 1], self.vertices[0], self.vertices[1]);
126            if !triangle.is_convex() {
127                return false;
128            }
129            true
130        }
131    }
132
133    /// Triangulates the polygon.
134    /// implements "Ear Clipping". See also: <https://gitlab.com/nathanfaucett/rs-polygon2/-/blob/master/src/triangulate.rs>
135    pub fn triangulate(&self) -> Vec<Triangle> {
136        let mut triangles = Vec::new();
137        let n = self.vertices.len();
138
139        if n < 3 {
140            //This is not going to triangulate- return nothing
141            return triangles;
142        }
143
144        if n == 3 {
145            //This IS a triangle, so simply return it as is
146            triangles.push(Triangle::new(
147                self.vertices[0],
148                self.vertices[1],
149                self.vertices[2],
150            ));
151            return triangles;
152        }
153
154        //time to impliment "Ear Clipping". Wont work for complex polys, but meh.
155        let mut avl = Vec::with_capacity(n);
156
157        for i in 0..n {
158            avl.push(i);
159        }
160
161        let mut i = 0;
162        let mut al = n;
163        while al > 3 {
164            let i0 = avl[i % al];
165            let i1 = avl[(i + 1) % al];
166            let i2 = avl[(i + 2) % al];
167
168            let a = self.vertices[i0];
169            let b = self.vertices[i1];
170            let c = self.vertices[i2];
171
172            let t = Triangle::new(a, b, c);
173
174            let mut ear_found = false;
175            if t.is_convex() {
176                ear_found = true;
177
178                for j in 0..al {
179                    let vi = avl[j];
180
181                    if vi != i0 && vi != i1 && vi != i2 {
182                        if t.contains_point(self.vertices[vi]) {
183                            ear_found = false;
184                            break;
185                        }
186                    }
187                }
188            }
189
190            if ear_found {
191                triangles.push(t);
192                avl.remove((i + 1) % al);
193                al -= 1;
194                i = 0;
195            } else if i > 3 * al {
196                break;
197            } else {
198                i += 1;
199            }
200        }
201
202        triangles.push(Triangle::new(
203            self.vertices[avl[0]],
204            self.vertices[avl[1]],
205            self.vertices[avl[2]],
206        ));
207        triangles
208    }
209
210    /// Getter for edge, going from a given vertex (either clockwise or counter).
211    pub fn edge(&self, i: usize, clockwise: bool) -> Option<Line2> {
212        let vert_count = self.vertices.len();
213
214        if clockwise {
215            if i + 1 < vert_count {
216                return Some(Line2 {
217                    a: self.vertices[i],
218                    b: self.vertices[i + 1],
219                });
220            } else if i < vert_count {
221                return Some(Line2 {
222                    a: self.vertices[i],
223                    b: self.vertices[0],
224                });
225            }
226        } else {
227            if i < vert_count {
228                if i > 0 {
229                    return Some(Line2 {
230                        a: self.vertices[i],
231                        b: self.vertices[i - 1],
232                    });
233                } else {
234                    return Some(Line2 {
235                        a: self.vertices[i],
236                        b: self.vertices[vert_count - 1],
237                    });
238                }
239            }
240        }
241        return None;
242    }
243}
244
245#[cfg(test)]
246mod tests {
247    use super::Vec2;
248    use crate::geom::polygon::Polygon;
249
250    const POLY_SIZE: usize = 12;
251
252    #[test]
253    fn polygon_test() {
254        let poly = Polygon::new_ngon(Vec2::new(256., 256.), 200., POLY_SIZE);
255        assert_eq!(poly.n(), POLY_SIZE)
256    }
257}