use crate::CoordinateType;
use crate::point::Point;
use crate::edge::Edge;
use crate::rect::Rect;
pub use crate::traits::{DoubledOrientedArea, BoundingBox, MapPointwise, WindingNumber};
use crate::types::*;
pub use crate::simple_polygon::*;
use std::iter::FromIterator;
use std::cmp::{Ord, PartialEq};
use crate::traits::TryCastCoord;
use num_traits::NumCast;
use itertools::Itertools;
#[derive(Clone, Hash, Debug, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Polygon<T>
where T: CoordinateType {
pub exterior: SimplePolygon<T>,
pub interiors: Vec<SimplePolygon<T>>,
}
#[macro_export]
macro_rules! polygon {
($($x:expr),*) => {Polygon::new((vec![$($x),*]))}
}
impl<'a, T, P> From<&'a Vec<P>> for Polygon<T>
where T: CoordinateType,
Point<T>: From<&'a P>
{
fn from(vec: &'a Vec<P>) -> Self {
Polygon {
exterior: vec.into(),
interiors: Vec::new(),
}
}
}
impl<T, P> From<Vec<P>> for Polygon<T>
where T: CoordinateType,
Point<T>: From<P>
{
fn from(vec: Vec<P>) -> Self {
Polygon {
exterior: vec.into(),
interiors: Vec::new(),
}
}
}
impl<T, P> FromIterator<P> for Polygon<T>
where T: CoordinateType,
P: Into<Point<T>>
{
fn from_iter<I>(iter: I) -> Self
where I: IntoIterator<Item=P>
{
let exterior: SimplePolygon<T> = SimplePolygon::from_iter(iter);
Polygon {
exterior,
interiors: Vec::new(),
}
}
}
impl<T> From<SimplePolygon<T>> for Polygon<T>
where T: CoordinateType
{
fn from(simple_polygon: SimplePolygon<T>) -> Self {
Polygon {
exterior: simple_polygon,
interiors: Vec::new(),
}
}
}
impl<T> From<&SimplePolygon<T>> for Polygon<T>
where T: CoordinateType
{
fn from(simple_polygon: &SimplePolygon<T>) -> Self {
Polygon {
exterior: simple_polygon.clone(),
interiors: Vec::new(),
}
}
}
impl<T> From<Rect<T>> for Polygon<T>
where T: CoordinateType
{
fn from(rect: Rect<T>) -> Self {
Polygon::from(
vec![rect.lower_left(), rect.lower_right(),
rect.upper_right(), rect.upper_left()]
)
}
}
impl<T> From<&Rect<T>> for Polygon<T>
where T: CoordinateType
{
fn from(rect: &Rect<T>) -> Self {
Polygon::from(
vec![rect.lower_left(), rect.lower_right(),
rect.upper_right(), rect.upper_left()]
)
}
}
pub trait ToPolygon<T>
where T: CoordinateType {
fn to_polygon(&self) -> Polygon<T>;
}
impl<T: CoordinateType> Polygon<T> {
pub fn new<I>(i: I) -> Self
where I: Into<Self> {
i.into()
}
pub fn empty() -> Self {
Polygon {
exterior: SimplePolygon::empty(),
interiors: Vec::new(),
}
}
pub fn new_with_holes<E, I>(exterior: E, holes: Vec<I>) -> Self
where E: Into<SimplePolygon<T>>,
I: Into<SimplePolygon<T>> {
Polygon {
exterior: exterior.into(),
interiors: holes.into_iter().map(|i| i.into()).collect(),
}
}
pub fn len(&self) -> usize {
self.exterior.len()
}
pub fn edges(&self) -> Vec<Edge<T>> {
self.exterior.edges()
}
pub fn convex_hull(&self) -> Polygon<T>
where T: Ord {
self.exterior.convex_hull().into()
}
pub fn lower_left_vertex(&self) -> Point<T> {
self.exterior.lower_left_vertex()
}
pub fn orientation(&self) -> Orientation {
self.exterior.orientation()
}
}
impl<T> TryBoundingBox<T> for Polygon<T>
where T: CoordinateType {
fn try_bounding_box(&self) -> Option<Rect<T>> {
let bbox = self.exterior.try_bounding_box();
if let Some(bbox) = &bbox {
debug_assert!(
self.interiors.iter()
.filter_map(|p| p.try_bounding_box())
.all(|internal_bbox| bbox.contains_rectangle(&internal_bbox)),
"Bounding boxes of interior polygons exceed the bounding box of the exterior polygon."
);
} else {
let num_internal_bboxes = self.interiors.iter()
.filter_map(|p| p.try_bounding_box())
.count();
debug_assert_eq!(num_internal_bboxes, 0,
"Polygon with empty zero-vertex hull cannot contain holes.");
}
bbox
}
}
impl<T> WindingNumber<T> for Polygon<T>
where T: CoordinateType {
fn winding_number(&self, point: Point<T>) -> isize {
let ext = self.exterior.winding_number(point);
let int: isize = self.interiors.iter()
.map(|p| p.winding_number(point))
.sum();
ext + int
}
}
impl<T> MapPointwise<T> for Polygon<T>
where T: CoordinateType {
fn transform<F: Fn(Point<T>) -> Point<T>>(&self, tf: F) -> Self {
Polygon {
exterior: self.exterior.transform(&tf),
interiors: self.interiors.iter()
.map(|p| p.transform(&tf))
.collect(),
}
}
}
impl<T: CoordinateType> DoubledOrientedArea<T> for Polygon<T> {
fn area_doubled_oriented(&self) -> T {
let ext = self.exterior.area_doubled_oriented();
let int = self.interiors.iter()
.map(|p| p.area_doubled_oriented())
.fold(T::zero(), |a, b| a + b);
ext + int
}
}
impl<T> PartialEq for Polygon<T>
where T: CoordinateType {
fn eq(&self, rhs: &Self) -> bool {
assert!(self.interiors.is_empty() && rhs.interiors.is_empty(),
"Equality check for polygons with holes not yet implemented.");
self.exterior == rhs.exterior
}
}
impl<T: CoordinateType + NumCast, Dst: CoordinateType + NumCast> TryCastCoord<T, Dst> for Polygon<T> {
type Output = Polygon<Dst>;
fn try_cast(&self) -> Option<Self::Output> {
if let Some(new_hull) = self.exterior.try_cast() {
let new_holes: Vec<_> = self.interiors.iter()
.map(|hole| hole.try_cast())
.while_some()
.collect();
if new_holes.len() == self.interiors.len() {
Some(Polygon::new_with_holes(new_hull, new_holes))
} else {
None
}
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
#[test]
fn test_create_polygon() {
let coords = vec![(0, 0), (1, 0), (1, 1), (0, 1)];
let poly: Polygon<i32> = (&coords).into();
assert_eq!(poly.exterior.len(), coords.len());
}
#[test]
fn test_area() {
let coords = vec![(0, 0), (1, 0), (1, 1), (0, 1)];
let poly: Polygon<i32> = (&coords).into();
assert_eq!(poly.area_doubled_oriented(), 2);
}
#[test]
fn test_orientation() {
use crate::types::Orientation;
let coords = vec![(0, 0), (1, 0), (1, 1)];
let poly: Polygon<i32> = (&coords).into();
assert_eq!(poly.orientation(), Orientation::CounterClockWise);
}
#[test]
fn test_bounding_box() {
use crate::traits::TryBoundingBox;
use crate::rect::Rect;
let coords = vec![(1, 0), (-1, -2), (1, 0), (42, 37)];
let poly: Polygon<i32> = (&coords).into();
assert_eq!(poly.try_bounding_box(), Some(Rect::new((-1, -2), (42, 37))))
}
#[test]
fn test_winding_number() {
let coords = vec![(0, 0), (2, 0), (2, 2), (0, 2)];
let poly: Polygon<i32> = (&coords).into();
assert_eq!(poly.winding_number(Point::new(1, 1)), 1);
assert_eq!(poly.winding_number(Point::new(-1, -1)), 0);
assert_eq!(poly.winding_number(Point::new(10, 10)), 0);
assert_eq!(poly.winding_number(Point::new(1, 0)), 1);
assert_eq!(poly.winding_number(Point::new(2, 1)), 0);
assert_eq!(poly.winding_number(Point::new(1, 2)), 0);
assert_eq!(poly.winding_number(Point::new(0, 1)), 1);
assert_eq!(poly.winding_number(Point::new(0, 0)), 1);
assert_eq!(poly.winding_number(Point::new(2, 0)), 0);
assert_eq!(poly.winding_number(Point::new(2, 2)), 0);
assert_eq!(poly.winding_number(Point::new(0, 2)), 0);
}
#[test]
fn test_convex_hull() {
let poly = Polygon::new(vec![(0, 0), (1, 1), (2, 0), (2, 2), (0, 2)]);
let exp_hull = Polygon::new(vec![(0, 0), (2, 0), (2, 2), (0, 2)]);
assert_eq!(poly.convex_hull(), exp_hull);
let poly = Polygon::new(vec![(1, 0), (2, 1), (1, 2), (1, 1), (0, 1)]);
let exp_hull = Polygon::new(vec![(1, 0), (2, 1), (1, 2), (0, 1)]);
assert_eq!(poly.convex_hull(), exp_hull);
let poly = Polygon::new(vec![(0, 0), (0, 1), (0, 7)]);
let exp_hull = Polygon::new(vec![(0, 0), (0, 7)]);
assert_eq!(poly.convex_hull(), exp_hull);
let poly = Polygon::new(vec![(0, 0), (1, 0), (7, 0)]);
let exp_hull = Polygon::new(vec![(0, 0), (7, 0)]);
assert_eq!(poly.convex_hull(), exp_hull);
let poly4 = Polygon::new(vec![(0, 0), (0, 0), (0, 0)]);
let exp_hull4 = Polygon::new(vec![(0, 0)]);
assert_eq!(poly4.convex_hull(), exp_hull4);
}
#[test]
fn test_partial_eq() {
let poly1 = Polygon::new(vec![(0, 0), (1, 0), (1, 1)]);
let poly2 = Polygon::new(vec![(1, 1), (0, 0), (1, 0)]);
let poly3 = Polygon::new(vec![(0, 0), (1, 0), (1, 2)]);
assert_eq!(poly1, poly2);
assert_eq!(poly2, poly1);
assert_ne!(poly1, poly3)
}
}