use nalgebra::{Point3, Vector3};
use crate::{Classification, Plane3D, PlaneSide, Rectangle, Triangle};
#[derive(Debug, Clone, PartialEq)]
pub struct Polygon {
vertices: Vec<Point3<f32>>,
}
impl Polygon {
pub fn new(vertices: Vec<Point3<f32>>) -> Self {
debug_assert!(
vertices.len() >= 3,
"Polygon must have at least 3 vertices"
);
debug_assert!(
Self::are_coplanar(&vertices),
"Polygon vertices must be coplanar"
);
Self { vertices }
}
fn are_coplanar(vertices: &[Point3<f32>]) -> bool {
if vertices.len() <= 3 {
return true;
}
let plane = Plane3D::from_three_points(vertices[0], vertices[1], vertices[2]);
vertices[3..]
.iter()
.all(|v| plane.classify_point(*v) == PlaneSide::OnPlane)
}
#[inline]
pub fn vertices(&self) -> &[Point3<f32>] {
&self.vertices
}
#[inline]
pub fn len(&self) -> usize {
self.vertices.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.vertices.is_empty()
}
pub fn normal(&self) -> Vector3<f32> {
let a = &self.vertices[0];
let b = &self.vertices[1];
let c = &self.vertices[2];
let ab = b - a;
let ac = c - a;
ab.cross(&ac)
}
pub fn unit_normal(&self) -> Option<Vector3<f32>> {
let n = self.normal();
let len = n.norm();
if len > f32::EPSILON {
Some(n / len)
} else {
None
}
}
pub fn plane(&self) -> Plane3D {
Plane3D::from_three_points(self.vertices[0], self.vertices[1], self.vertices[2])
}
pub fn centroid(&self) -> Point3<f32> {
let sum: Vector3<f32> = self.vertices.iter().map(|p| p.coords).sum();
Point3::from(sum / self.vertices.len() as f32)
}
pub fn classify(&self, plane: &Plane3D) -> Classification {
let mut front = 0;
let mut back = 0;
let mut on_plane = 0;
for vertex in &self.vertices {
match plane.classify_point(*vertex) {
PlaneSide::Front => front += 1,
PlaneSide::Back => back += 1,
PlaneSide::OnPlane => on_plane += 1,
}
}
if on_plane == self.vertices.len() {
Classification::Coplanar
} else if back == 0 {
Classification::Front
} else if front == 0 {
Classification::Back
} else {
Classification::Spanning
}
}
}
impl From<Triangle> for Polygon {
fn from(triangle: Triangle) -> Self {
Self {
vertices: triangle.vertices().to_vec(),
}
}
}
impl From<&Triangle> for Polygon {
fn from(triangle: &Triangle) -> Self {
Self {
vertices: triangle.vertices().to_vec(),
}
}
}
impl From<Rectangle> for Polygon {
fn from(rectangle: Rectangle) -> Self {
Self {
vertices: rectangle.vertices().to_vec(),
}
}
}
impl From<&Rectangle> for Polygon {
fn from(rectangle: &Rectangle) -> Self {
Self {
vertices: rectangle.vertices().to_vec(),
}
}
}
impl From<Polygon> for Plane3D {
fn from(polygon: Polygon) -> Self {
polygon.plane()
}
}
impl From<&Polygon> for Plane3D {
fn from(polygon: &Polygon) -> Self {
polygon.plane()
}
}