use crate::{CoordFloat, CoordNum, LineString, Point, Rect, Triangle};
use alloc::vec;
use alloc::vec::Vec;
use num_traits::{Float, Signed};
#[derive(Eq, PartialEq, Clone, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Polygon<T: CoordNum = f64> {
exterior: LineString<T>,
interiors: Vec<LineString<T>>,
}
impl<T: CoordNum> Polygon<T> {
pub fn new(mut exterior: LineString<T>, mut interiors: Vec<LineString<T>>) -> Self {
exterior.close();
for interior in &mut interiors {
interior.close();
}
Self {
exterior,
interiors,
}
}
pub fn empty() -> Self {
Self::new(LineString::empty(), vec![])
}
pub fn into_inner(self) -> (LineString<T>, Vec<LineString<T>>) {
(self.exterior, self.interiors)
}
pub fn exterior(&self) -> &LineString<T> {
&self.exterior
}
pub fn exterior_mut<F>(&mut self, f: F)
where
F: FnOnce(&mut LineString<T>),
{
f(&mut self.exterior);
self.exterior.close();
}
pub fn try_exterior_mut<F, E>(&mut self, f: F) -> Result<(), E>
where
F: FnOnce(&mut LineString<T>) -> Result<(), E>,
{
f(&mut self.exterior)?;
self.exterior.close();
Ok(())
}
pub fn interiors(&self) -> &[LineString<T>] {
&self.interiors
}
pub fn interiors_mut<F>(&mut self, f: F)
where
F: FnOnce(&mut [LineString<T>]),
{
f(&mut self.interiors);
for interior in &mut self.interiors {
interior.close();
}
}
pub fn try_interiors_mut<F, E>(&mut self, f: F) -> Result<(), E>
where
F: FnOnce(&mut [LineString<T>]) -> Result<(), E>,
{
f(&mut self.interiors)?;
for interior in &mut self.interiors {
interior.close();
}
Ok(())
}
pub fn interiors_push(&mut self, new_interior: impl Into<LineString<T>>) {
let mut new_interior = new_interior.into();
new_interior.close();
self.interiors.push(new_interior);
}
fn previous_vertex(&self, current_vertex: usize) -> usize
where
T: Float,
{
(current_vertex + (self.exterior.0.len() - 1) - 1) % (self.exterior.0.len() - 1)
}
pub fn num_rings(&self) -> usize {
self.num_interior_rings() + 1
}
pub fn num_interior_rings(&self) -> usize {
self.interiors.len()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum ListSign {
Empty,
Positive,
Negative,
Mixed,
}
impl<T: CoordFloat + Signed> Polygon<T> {
#[deprecated(
since = "0.6.1",
note = "Please use `geo::is_convex` on `poly.exterior()` instead"
)]
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);
Point::from(self.exterior[prev_2]).cross_prod(
Point::from(self.exterior[prev_1]),
Point::from(self.exterior[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
}
}
impl<T: CoordNum> From<Rect<T>> for Polygon<T> {
fn from(r: Rect<T>) -> Self {
Polygon::new(
vec![
(r.min().x, r.min().y),
(r.max().x, r.min().y),
(r.max().x, r.max().y),
(r.min().x, r.max().y),
(r.min().x, r.min().y),
]
.into(),
Vec::new(),
)
}
}
impl<T: CoordNum> From<Triangle<T>> for Polygon<T> {
fn from(t: Triangle<T>) -> Self {
t.to_polygon()
}
}
#[cfg(any(feature = "approx", test))]
mod approx_integration {
use super::*;
use approx::{AbsDiffEq, RelativeEq, UlpsEq};
impl<T> RelativeEq for Polygon<T>
where
T: CoordNum + RelativeEq<Epsilon = T>,
{
#[inline]
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
if !self
.exterior
.relative_eq(&other.exterior, epsilon, max_relative)
{
return false;
}
if self.interiors.len() != other.interiors.len() {
return false;
}
let mut zipper = self.interiors.iter().zip(other.interiors.iter());
zipper.all(|(lhs, rhs)| lhs.relative_eq(rhs, epsilon, max_relative))
}
}
impl<T> AbsDiffEq for Polygon<T>
where
T: CoordNum + AbsDiffEq<Epsilon = T>,
{
type Epsilon = T;
#[inline]
fn default_epsilon() -> Self::Epsilon {
T::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
if !self.exterior.abs_diff_eq(&other.exterior, epsilon) {
return false;
}
if self.interiors.len() != other.interiors.len() {
return false;
}
let mut zipper = self.interiors.iter().zip(other.interiors.iter());
zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon))
}
}
impl<T> UlpsEq for Polygon<T>
where
T: CoordNum + UlpsEq<Epsilon = T>,
{
fn default_max_ulps() -> u32 {
T::default_max_ulps()
}
fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
if !self.exterior.ulps_eq(&other.exterior, epsilon, max_ulps) {
return false;
}
if self.interiors.len() != other.interiors.len() {
return false;
}
let mut zipper = self.interiors.iter().zip(other.interiors.iter());
zipper.all(|(lhs, rhs)| lhs.ulps_eq(rhs, epsilon, max_ulps))
}
}
}
#[cfg(any(
feature = "rstar_0_8",
feature = "rstar_0_9",
feature = "rstar_0_10",
feature = "rstar_0_11",
feature = "rstar_0_12"
))]
macro_rules! impl_rstar_polygon {
($rstar:ident) => {
impl<T> $rstar::RTreeObject for Polygon<T>
where
T: ::num_traits::Float + ::$rstar::RTreeNum,
{
type Envelope = ::$rstar::AABB<Point<T>>;
fn envelope(&self) -> Self::Envelope {
self.exterior.envelope()
}
}
};
}
#[cfg(feature = "rstar_0_8")]
impl_rstar_polygon!(rstar_0_8);
#[cfg(feature = "rstar_0_9")]
impl_rstar_polygon!(rstar_0_9);
#[cfg(feature = "rstar_0_10")]
impl_rstar_polygon!(rstar_0_10);
#[cfg(feature = "rstar_0_11")]
impl_rstar_polygon!(rstar_0_11);
#[cfg(feature = "rstar_0_12")]
impl_rstar_polygon!(rstar_0_12);
#[cfg(test)]
mod tests {
use super::*;
use crate::wkt;
#[test]
fn empty() {
let empty = Polygon::<f64>::empty();
let empty_2 = wkt! { POLYGON EMPTY };
assert_eq!(empty, empty_2);
}
}