use crate::float_types::Real;
use crate::sketch::Sketch;
use geo::{Coord, Geometry, GeometryCollection, LineString, MultiPolygon, Point, Polygon};
use geo_buf::{
buffer_multi_polygon, buffer_multi_polygon_rounded, buffer_point, buffer_polygon,
buffer_polygon_rounded, skeleton_of_multi_polygon_to_linestring,
skeleton_of_polygon_to_linestring,
};
use std::fmt::Debug;
use std::sync::OnceLock;
use geo::algorithm::map_coords::MapCoords;
macro_rules! cast_through_f64 {
($geom:expr, $op:expr) => {{
let g_f64 = $geom.map_coords(|c| Coord {
x: c.x as f64,
y: c.y as f64,
});
let out_f64 = $op(&g_f64);
out_f64.map_coords(|c| Coord {
x: c.x as Real,
y: c.y as Real,
})
}};
}
#[allow(clippy::unnecessary_cast)]
fn buf_poly(poly: &Polygon<Real>, d: Real) -> MultiPolygon<Real> {
cast_through_f64!(poly, |p: &Polygon<f64>| buffer_polygon(p, d as f64))
}
#[allow(clippy::unnecessary_cast)]
fn buf_poly_round(poly: &Polygon<Real>, d: Real) -> MultiPolygon<Real> {
cast_through_f64!(poly, |p: &Polygon<f64>| buffer_polygon_rounded(p, d as f64))
}
#[allow(clippy::unnecessary_cast)]
fn buf_multi_poly(mpoly: &MultiPolygon<Real>, d: Real) -> MultiPolygon<Real> {
cast_through_f64!(mpoly, |m: &MultiPolygon<f64>| buffer_multi_polygon(
m, d as f64
))
}
#[allow(clippy::unnecessary_cast)]
fn buf_multi_poly_round(mpoly: &MultiPolygon<Real>, d: Real) -> MultiPolygon<Real> {
cast_through_f64!(mpoly, |m: &MultiPolygon<f64>| buffer_multi_polygon_rounded(
m, d as f64
))
}
#[allow(clippy::unnecessary_cast)]
fn buf_point(pt: &Point<Real>, d: Real, res: usize) -> Polygon<Real> {
let pt_f64 = Point::new(pt.x() as f64, pt.y() as f64);
buffer_point(&pt_f64, d as f64, res).map_coords(|c| Coord {
x: c.x as Real,
y: c.y as Real,
})
}
#[allow(clippy::unnecessary_cast)]
fn skel_poly(poly: &Polygon<Real>, inward: bool) -> Vec<LineString<Real>> {
let poly_f64 = poly.map_coords(|c| Coord {
x: c.x as f64,
y: c.y as f64,
});
skeleton_of_polygon_to_linestring(&poly_f64, inward)
.into_iter()
.map(|ls| {
ls.map_coords(|c| Coord {
x: c.x as Real,
y: c.y as Real,
})
})
.collect()
}
#[allow(clippy::unnecessary_cast)]
fn skel_multi_poly(mpoly: &MultiPolygon<Real>, inward: bool) -> Vec<LineString<Real>> {
let mpoly_f64 = mpoly.map_coords(|c| Coord {
x: c.x as f64,
y: c.y as f64,
});
skeleton_of_multi_polygon_to_linestring(&mpoly_f64, inward)
.into_iter()
.map(|ls| {
ls.map_coords(|c| Coord {
x: c.x as Real,
y: c.y as Real,
})
})
.collect()
}
impl<S: Clone + Debug + Send + Sync> Sketch<S> {
#[allow(clippy::unnecessary_cast)]
pub fn offset(&self, distance: Real) -> Sketch<S> {
let offset_geoms = self
.geometry
.iter()
.filter_map(|geom| match geom {
Geometry::Polygon(poly) => {
let new_mpoly = buf_poly(poly, distance);
Some(Geometry::MultiPolygon(new_mpoly))
},
Geometry::MultiPolygon(mpoly) => {
let new_mpoly = buf_multi_poly(mpoly, distance);
Some(Geometry::MultiPolygon(new_mpoly))
},
Geometry::Point(point) => {
let new_poly = buf_point(point, distance, 64); let new_mpoly = MultiPolygon::new(vec![new_poly]);
Some(Geometry::MultiPolygon(new_mpoly))
},
_ => None, })
.collect();
let new_collection = GeometryCollection::<Real>(offset_geoms);
Sketch {
geometry: new_collection,
bounding_box: OnceLock::new(),
metadata: self.metadata.clone(),
}
}
#[allow(clippy::unnecessary_cast)]
pub fn offset_rounded(&self, distance: Real) -> Sketch<S> {
let offset_geoms = self
.geometry
.iter()
.filter_map(|geom| match geom {
Geometry::Polygon(poly) => {
let new_mpoly = buf_poly_round(poly, distance);
Some(Geometry::MultiPolygon(new_mpoly))
},
Geometry::MultiPolygon(mpoly) => {
let new_mpoly = buf_multi_poly_round(mpoly, distance);
Some(Geometry::MultiPolygon(new_mpoly))
},
Geometry::Point(point) => {
let new_poly = buf_point(point, distance, 64); let new_mpoly = MultiPolygon::new(vec![new_poly]);
Some(Geometry::MultiPolygon(new_mpoly))
},
_ => None, })
.collect();
let new_collection = GeometryCollection::<Real>(offset_geoms);
Sketch {
geometry: new_collection,
bounding_box: OnceLock::new(),
metadata: self.metadata.clone(),
}
}
pub fn straight_skeleton(&self, orientation: bool) -> Sketch<S> {
let skeleton = self
.geometry
.iter()
.filter_map(|geom| match geom {
Geometry::Polygon(poly) => {
let mls = geo::MultiLineString(skel_poly(poly, orientation));
Some(Geometry::MultiLineString(mls))
},
Geometry::MultiPolygon(mpoly) => {
let mls = geo::MultiLineString(skel_multi_poly(mpoly, orientation));
Some(Geometry::MultiLineString(mls))
},
_ => None, })
.collect();
let new_collection = GeometryCollection::<Real>(skeleton);
Sketch {
geometry: new_collection,
bounding_box: OnceLock::new(),
metadata: self.metadata.clone(),
}
}
}