use geometry_model::{
Box, Linestring, MultiLinestring, MultiPoint, MultiPolygon, Polygon, Ring, Segment,
};
use geometry_trait::{Point as PointTrait, PointMut};
use crate::make::make_point;
#[must_use]
pub fn convert<Src, Dst>(src: &Src) -> Dst
where
Src: Convert<Dst>,
{
src.convert()
}
#[doc(hidden)]
pub trait Convert<Dst> {
fn convert(&self) -> Dst;
}
impl<P, const CW: bool, const CL: bool> Convert<Polygon<P, CW, CL>> for Box<P>
where
P: PointMut + Default,
{
fn convert(&self) -> Polygon<P, CW, CL> {
let min_x = self.min().get::<0>();
let min_y = self.min().get::<1>();
let max_x = self.max().get::<0>();
let max_y = self.max().get::<1>();
let ring = Ring::<P, CW, CL>::from_vec(alloc::vec![
make_point(&[min_x, min_y]),
make_point(&[min_x, max_y]),
make_point(&[max_x, max_y]),
make_point(&[max_x, min_y]),
make_point(&[min_x, min_y]),
]);
Polygon::new(ring)
}
}
impl<P, const CW: bool, const CL: bool> Convert<Polygon<P, CW, CL>> for Ring<P, CW, CL>
where
P: PointTrait + Copy,
{
fn convert(&self) -> Polygon<P, CW, CL> {
Polygon::new(Ring::from_vec(self.0.clone()))
}
}
impl<P, const CW: bool, const CL: bool> Convert<Linestring<P>> for Ring<P, CW, CL>
where
P: PointTrait + Copy,
{
fn convert(&self) -> Linestring<P> {
Linestring(self.0.clone())
}
}
impl<P> Convert<Linestring<P>> for Segment<P>
where
P: PointTrait + Copy,
{
fn convert(&self) -> Linestring<P> {
Linestring(alloc::vec![*self.start(), *self.end()])
}
}
impl<P> Convert<MultiPoint<P>> for P
where
P: PointTrait + Copy,
{
fn convert(&self) -> MultiPoint<P> {
MultiPoint(alloc::vec![*self])
}
}
impl<P> Convert<MultiLinestring<Linestring<P>>> for Linestring<P>
where
P: PointTrait + Copy,
{
fn convert(&self) -> MultiLinestring<Linestring<P>> {
MultiLinestring(alloc::vec![self.clone()])
}
}
impl<P, const CW: bool, const CL: bool> Convert<MultiPolygon<Polygon<P, CW, CL>>>
for Polygon<P, CW, CL>
where
P: PointTrait + Copy,
{
fn convert(&self) -> MultiPolygon<Polygon<P, CW, CL>> {
MultiPolygon(alloc::vec![self.clone()])
}
}
#[cfg(test)]
#[allow(
clippy::float_cmp,
reason = "Converted corner coordinates are exact literals."
)]
mod tests {
use super::convert;
use geometry_cs::Cartesian;
use geometry_model::{Box, Linestring, MultiPoint, Point2D, Polygon, Segment};
use geometry_trait::{Point as _, Polygon as _, Ring as _};
type Pt = Point2D<f64, Cartesian>;
#[test]
fn box_to_polygon_five_points_and_corners() {
let b: Box<Pt> = Box::from_corners(Pt::new(0., 0.), Pt::new(4., 3.));
let pg: Polygon<Pt> = convert(&b);
let pts: alloc::vec::Vec<(f64, f64)> = pg
.exterior()
.points()
.map(|p| (p.get::<0>(), p.get::<1>()))
.collect();
assert_eq!(pts.len(), 5);
assert_eq!(pts[0], (0., 0.));
assert_eq!(pts[4], (0., 0.));
assert!(pts.contains(&(0., 3.)));
assert!(pts.contains(&(4., 3.)));
assert!(pts.contains(&(4., 0.)));
}
#[test]
fn segment_to_linestring_endpoints() {
let s = Segment::new(Pt::new(0., 0.), Pt::new(3., 4.));
let ls: Linestring<Pt> = convert(&s);
assert_eq!(ls.0.len(), 2);
assert_eq!(ls.0[0].get::<0>(), 0.);
assert_eq!(ls.0[1].get::<0>(), 3.);
}
#[test]
fn point_to_multi_point_single_member() {
let mp: MultiPoint<Pt> = convert(&Pt::new(1., 2.));
assert_eq!(mp.0.len(), 1);
assert_eq!(mp.0[0].get::<0>(), 1.);
}
}