use super::{Float, Line2, Triangle, Vec2, PI};
pub struct Polygon {
vertices: Vec<Vec2>,
}
impl Polygon {
pub fn new(vertices: Vec<Vec2>) -> Self {
Self { vertices }
}
pub fn empty() -> Self {
Self {
vertices: Vec::new(),
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
vertices: Vec::with_capacity(capacity),
}
}
pub fn new_ngon(pos: Vec2, circumradius: Float, n: usize) -> Self {
if n < 3 {
panic!("Polygon must have at least 3 sides");
}
let mut poly = Self {
vertices: Vec::new(),
};
let angle = (2. * PI) / n as Float;
for i in 0..n {
let a = angle * i as Float + (PI / 2.);
let x = a.cos() * circumradius;
let y = a.sin() * circumradius;
poly.vertices.push(Vec2::new(x, y) + pos);
}
poly
}
pub fn add_vertex(&mut self, v: Vec2) {
self.vertices.push(v);
}
pub fn n(&self) -> usize {
self.vertices.len()
}
pub fn interior_angle(&self) -> Float {
let n = self.vertices.len() as Float;
((n as Float - 2.) * PI) / n
}
pub fn vertices(&self) -> Vec<Vec2> {
self.vertices.clone()
}
pub fn edges(&self) -> Vec<Line2> {
let mut lines = Vec::new();
for (i, _) in self.vertices.iter().enumerate() {
lines.push(self.edge(i, true).unwrap());
}
lines
}
pub fn vertex(&self, i: usize) -> Option<Vec2> {
if i < self.vertices.len() {
return Some(self.vertices[i]);
}
return None;
}
pub fn is_convex(&self) -> bool {
let n = self.vertices.len();
if n < 3 {
true
} else {
let mut i = 0;
let l = n - 2;
while i < l {
let triangle = Triangle::new(
self.vertices[i],
self.vertices[i + 1],
self.vertices[i + 2],
);
if !triangle.is_convex() {
return false;
} else {
i += 3;
}
}
let triangle =
Triangle::new(self.vertices[l], self.vertices[l + 1], self.vertices[0]);
if !triangle.is_convex() {
return false;
}
let triangle =
Triangle::new(self.vertices[l + 1], self.vertices[0], self.vertices[1]);
if !triangle.is_convex() {
return false;
}
true
}
}
pub fn triangulate(&self) -> Vec<Triangle> {
let mut triangles = Vec::new();
let n = self.vertices.len();
if n < 3 {
return triangles;
}
if n == 3 {
triangles.push(Triangle::new(
self.vertices[0],
self.vertices[1],
self.vertices[2],
));
return triangles;
}
let mut avl = Vec::with_capacity(n);
for i in 0..n {
avl.push(i);
}
let mut i = 0;
let mut al = n;
while al > 3 {
let i0 = avl[i % al];
let i1 = avl[(i + 1) % al];
let i2 = avl[(i + 2) % al];
let a = self.vertices[i0];
let b = self.vertices[i1];
let c = self.vertices[i2];
let t = Triangle::new(a, b, c);
let mut ear_found = false;
if t.is_convex() {
ear_found = true;
for j in 0..al {
let vi = avl[j];
if vi != i0 && vi != i1 && vi != i2 {
if t.contains_point(self.vertices[vi]) {
ear_found = false;
break;
}
}
}
}
if ear_found {
triangles.push(t);
avl.remove((i + 1) % al);
al -= 1;
i = 0;
} else if i > 3 * al {
break;
} else {
i += 1;
}
}
triangles.push(Triangle::new(
self.vertices[avl[0]],
self.vertices[avl[1]],
self.vertices[avl[2]],
));
triangles
}
pub fn edge(&self, i: usize, clockwise: bool) -> Option<Line2> {
let vert_count = self.vertices.len();
if clockwise {
if i + 1 < vert_count {
return Some(Line2 {
a: self.vertices[i],
b: self.vertices[i + 1],
});
} else if i < vert_count {
return Some(Line2 {
a: self.vertices[i],
b: self.vertices[0],
});
}
} else {
if i < vert_count {
if i > 0 {
return Some(Line2 {
a: self.vertices[i],
b: self.vertices[i - 1],
});
} else {
return Some(Line2 {
a: self.vertices[i],
b: self.vertices[vert_count - 1],
});
}
}
}
return None;
}
}
#[cfg(test)]
mod tests {
use super::Vec2;
use crate::geom::polygon::Polygon;
const POLY_SIZE: usize = 12;
#[test]
fn polygon_test() {
let poly = Polygon::new_ngon(Vec2::new(256., 256.), 200., POLY_SIZE);
assert_eq!(poly.n(), POLY_SIZE)
}
}