use alloc::vec::Vec;
use geometry_tag::RingTag;
use geometry_trait::{Closure, Geometry, Point as PointTrait, PointOrder, Ring as RingTrait};
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Ring<P: PointTrait, const CLOCKWISE: bool = true, const CLOSED: bool = true>(pub Vec<P>);
impl<P: PointTrait, const CW: bool, const CL: bool> Ring<P, CW, CL> {
#[must_use]
pub const fn new() -> Self {
Self(Vec::new())
}
#[must_use]
pub const fn from_vec(v: Vec<P>) -> Self {
Self(v)
}
pub fn push(&mut self, p: P) {
self.0.push(p);
}
}
impl<P: PointTrait, const CW: bool, const CL: bool> Default for Ring<P, CW, CL> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<P: PointTrait, const CW: bool, const CL: bool> Geometry for Ring<P, CW, CL> {
type Kind = RingTag;
type Point = P;
}
impl<P: PointTrait, const CW: bool, const CL: bool> RingTrait for Ring<P, CW, CL> {
fn points(&self) -> impl ExactSizeIterator<Item = &P> + Clone {
self.0.iter()
}
fn closure(&self) -> Closure {
if CL { Closure::Closed } else { Closure::Open }
}
fn point_order(&self) -> PointOrder {
if CW {
PointOrder::Clockwise
} else {
PointOrder::CounterClockwise
}
}
}
#[cfg(test)]
mod tests {
use super::Ring;
use crate::point::Point2D;
use alloc::vec;
use alloc::vec::Vec;
use geometry_cs::Cartesian;
use geometry_tag::RingTag;
use geometry_trait::{
Closure, Geometry, Point as _, PointOrder, Ring as RingTrait, check_ring,
};
#[test]
fn ring_iterates_in_declared_order() {
let mut r = Ring::<Point2D<f64, Cartesian>>::new();
r.push(Point2D::new(0.0, 0.0));
r.push(Point2D::new(1.0, 0.0));
r.push(Point2D::new(0.0, 1.0));
r.push(Point2D::new(0.0, 0.0));
assert_eq!(r.points().count(), 4);
let xs: Vec<u64> = r.points().map(|p| p.get::<0>().to_bits()).collect();
assert_eq!(
xs,
vec![
0.0_f64.to_bits(),
1.0_f64.to_bits(),
0.0_f64.to_bits(),
0.0_f64.to_bits(),
]
);
}
#[test]
fn ring_defaults_are_closed_clockwise() {
let r = Ring::<Point2D<f64, Cartesian>>::new();
assert_eq!(r.closure(), Closure::Closed);
assert_eq!(r.point_order(), PointOrder::Clockwise);
}
#[test]
fn ring_with_custom_const_generics() {
let r = Ring::<Point2D<f64, Cartesian>, false, false>::new();
assert_eq!(r.closure(), Closure::Open);
assert_eq!(r.point_order(), PointOrder::CounterClockwise);
}
#[test]
fn ring_kind_is_ring_tag() {
fn k<T: Geometry<Kind = RingTag>>() {}
k::<Ring<Point2D<f64, Cartesian>>>();
k::<Ring<Point2D<f64, Cartesian>, false, false>>();
}
#[test]
fn ring_satisfies_concept() {
check_ring::<Ring<Point2D<f64, Cartesian>>>();
check_ring::<Ring<Point2D<f64, Cartesian>, false, false>>();
}
}