use alloc::vec::Vec;
use geometry_tag::PolygonTag;
use geometry_trait::{Geometry, Point as PointTrait, Polygon as PolygonTrait};
use crate::Ring;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Polygon<P: PointTrait, const CLOCKWISE: bool = true, const CLOSED: bool = true> {
pub outer: Ring<P, CLOCKWISE, CLOSED>,
pub inners: Vec<Ring<P, CLOCKWISE, CLOSED>>,
}
impl<P: PointTrait, const CW: bool, const CL: bool> Polygon<P, CW, CL> {
#[must_use]
pub const fn new(outer: Ring<P, CW, CL>) -> Self {
Self {
outer,
inners: Vec::new(),
}
}
#[must_use]
pub const fn with_inners(outer: Ring<P, CW, CL>, inners: Vec<Ring<P, CW, CL>>) -> Self {
Self { outer, inners }
}
}
impl<P: PointTrait, const CW: bool, const CL: bool> Default for Polygon<P, CW, CL> {
#[inline]
fn default() -> Self {
Self {
outer: Ring::new(),
inners: Vec::new(),
}
}
}
impl<P: PointTrait, const CW: bool, const CL: bool> Geometry for Polygon<P, CW, CL> {
type Kind = PolygonTag;
type Point = P;
}
impl<P: PointTrait, const CW: bool, const CL: bool> PolygonTrait for Polygon<P, CW, CL> {
type Ring = Ring<P, CW, CL>;
fn exterior(&self) -> &Self::Ring {
&self.outer
}
fn interiors(&self) -> impl ExactSizeIterator<Item = &Self::Ring> {
self.inners.iter()
}
}
#[cfg(test)]
mod tests {
use super::Polygon;
use crate::point::Point2D;
use crate::ring::Ring;
use alloc::vec;
use geometry_cs::Cartesian;
use geometry_tag::PolygonTag;
use geometry_trait::{Geometry, Polygon as PolygonTrait, check_polygon};
type P = Point2D<f64, Cartesian>;
fn unit_square_outer() -> Ring<P> {
Ring::from_vec(vec![
Point2D::new(0.0, 0.0),
Point2D::new(10.0, 0.0),
Point2D::new(10.0, 10.0),
Point2D::new(0.0, 10.0),
Point2D::new(0.0, 0.0),
])
}
fn hole(x: f64, y: f64) -> Ring<P> {
Ring::from_vec(vec![
Point2D::new(x, y),
Point2D::new(x + 1.0, y),
Point2D::new(x + 1.0, y + 1.0),
Point2D::new(x, y + 1.0),
Point2D::new(x, y),
])
}
#[test]
fn polygon_new_has_no_holes() {
let poly: Polygon<P> = Polygon::new(unit_square_outer());
assert_eq!(poly.exterior().0.len(), 5);
assert_eq!(poly.interiors().count(), 0);
}
#[test]
fn polygon_with_two_holes() {
let mut poly: Polygon<P> = Polygon::new(unit_square_outer());
poly.inners.push(hole(1.0, 1.0));
poly.inners.push(hole(5.0, 5.0));
assert_eq!(poly.interiors().count(), 2);
}
#[test]
fn polygon_with_inners_constructor() {
let poly: Polygon<P> =
Polygon::with_inners(unit_square_outer(), vec![hole(1.0, 1.0), hole(5.0, 5.0)]);
assert_eq!(poly.interiors().count(), 2);
}
#[test]
fn polygon_kind_is_polygon_tag() {
fn k<T: Geometry<Kind = PolygonTag>>() {}
k::<Polygon<P>>();
k::<Polygon<P, false, false>>();
}
#[test]
fn polygon_satisfies_concept() {
check_polygon::<Polygon<P>>();
check_polygon::<Polygon<P, false, false>>();
}
}