use num_traits::{Float, Signed};
use {CoordinateType, LineString};
#[derive(PartialEq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Polygon<T>
where
T: CoordinateType,
{
pub exterior: LineString<T>,
pub interiors: Vec<LineString<T>>,
}
impl<T> Polygon<T>
where
T: CoordinateType,
{
pub fn new(exterior: LineString<T>, interiors: Vec<LineString<T>>) -> Polygon<T> {
Polygon {
exterior: exterior,
interiors: interiors,
}
}
fn previous_vertex(&self, current_vertex: &usize) -> usize
where
T: Float,
{
(current_vertex + (self.exterior.0.len() - 1) - 1) % (self.exterior.0.len() - 1)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum ListSign {
Empty,
Positive,
Negative,
Mixed,
}
impl<T> Polygon<T>
where
T: Float + Signed,
{
pub fn is_convex(&self) -> bool {
let convex = self
.exterior
.0
.iter()
.enumerate()
.map(|(idx, _)| {
let prev_1 = self.previous_vertex(&idx);
let prev_2 = self.previous_vertex(&prev_1);
self.exterior.0[prev_2].cross_prod(
&self.exterior.0[prev_1],
&self.exterior.0[idx]
)
})
.fold(ListSign::Empty, |acc, n| {
match (acc, n.is_positive()) {
(ListSign::Empty, true) | (ListSign::Positive, true) => ListSign::Positive,
(ListSign::Empty, false) | (ListSign::Negative, false) => ListSign::Negative,
_ => ListSign::Mixed
}
});
convex != ListSign::Mixed
}
}