use crate::prelude::*;
use crate::traits::{MapPointwise, TryBoundingBox};
use num_traits::{Num, NumCast};
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Geometry<T> {
Point(Point<T>),
Edge(Edge<T>),
Rect(Rect<T>),
SimplePolygon(SimplePolygon<T>),
SimpleRPolygon(SimpleRPolygon<T>),
Polygon(Polygon<T>),
Path(Path<T>),
Text(Text<T>),
}
impl<T: CoordinateType> Geometry<T> {
pub fn transformed(&self, tf: &SimpleTransform<T>) -> Self {
let trans = |p| tf.transform_point(p);
match self {
Geometry::Point(p) => tf.transform_point(*p).into(),
Geometry::Edge(g) => g.transform(trans).into(),
Geometry::Rect(g) => g.transform(trans).into(),
Geometry::SimplePolygon(g) => g.transform(trans).into(),
Geometry::SimpleRPolygon(g) => g.transformed(tf).into(),
Geometry::Polygon(g) => g.transform(trans).into(),
Geometry::Path(p) => p.transform(tf).into(),
Geometry::Text(g) => g.transform(trans).into(),
}
}
}
macro_rules! geometry_from {
( $t:tt ) => {
impl<T> From<$t<T>> for Geometry<T> {
fn from(x: $t<T>) -> Geometry<T> {
Geometry::$t(x)
}
}
};
}
geometry_from!(Point);
geometry_from!(Edge);
geometry_from!(Rect);
geometry_from!(SimplePolygon);
geometry_from!(SimpleRPolygon);
geometry_from!(Polygon);
geometry_from!(Path);
geometry_from!(Text);
impl<T: Copy + PartialOrd + Num> TryBoundingBox<T> for Geometry<T> {
fn try_bounding_box(&self) -> Option<Rect<T>> {
match self {
Geometry::Point(p) => p.try_bounding_box(),
Geometry::Edge(e) => e.try_bounding_box(),
Geometry::Rect(e) => e.try_bounding_box(),
Geometry::SimplePolygon(e) => e.try_bounding_box(),
Geometry::SimpleRPolygon(e) => e.try_bounding_box(),
Geometry::Polygon(e) => e.try_bounding_box(),
Geometry::Path(p) => p.try_bounding_box(),
Geometry::Text(t) => t.try_bounding_box(),
}
}
}
impl<T: CoordinateType + NumCast> DoubledOrientedArea<T> for Geometry<T> {
fn area_doubled_oriented(&self) -> T {
match self {
Geometry::Point(_) => T::zero(),
Geometry::Edge(_) => T::zero(),
Geometry::Rect(e) => e.area_doubled_oriented(),
Geometry::SimplePolygon(e) => e.area_doubled_oriented(),
Geometry::SimpleRPolygon(e) => e.area_doubled_oriented(),
Geometry::Polygon(e) => e.area_doubled_oriented(),
Geometry::Path(p) => {
T::from(FloatType::round(p.area_approx::<FloatType>() * 2.0_f64)).unwrap()
}
Geometry::Text(_) => T::zero(),
}
}
}
impl<T: CoordinateType + NumCast> ToPolygon<T> for Geometry<T> {
fn to_polygon(&self) -> Polygon<T> {
match self {
Geometry::Point(_) => Polygon::empty(),
Geometry::Edge(_) => Polygon::empty(),
Geometry::Rect(e) => e.to_polygon(),
Geometry::SimplePolygon(e) => Polygon::from(e),
Geometry::SimpleRPolygon(p) => Polygon::from(p.to_simple_polygon()),
Geometry::Polygon(e) => e.clone(),
Geometry::Path(p) => p.to_polygon_approx().cast().into(),
Geometry::Text(_) => Polygon::empty(),
}
}
}
impl<T: CoordinateType + NumCast> From<Geometry<T>> for Polygon<T> {
fn from(g: Geometry<T>) -> Self {
g.to_polygon()
}
}
impl<T: CoordinateType + NumCast, Dst: CoordinateType + NumCast> TryCastCoord<T, Dst>
for Geometry<T>
{
type Output = Geometry<Dst>;
fn try_cast(&self) -> Option<Self::Output> {
match self {
Geometry::Point(p) => p.try_cast().map(|s| s.into()),
Geometry::Edge(e) => e.try_cast().map(|s| s.into()),
Geometry::Rect(r) => r.try_cast().map(|s| s.into()),
Geometry::SimplePolygon(p) => p.try_cast().map(|s| s.into()),
Geometry::SimpleRPolygon(p) => p.try_cast().map(|s| s.into()),
Geometry::Polygon(p) => p.try_cast().map(|s| s.into()),
Geometry::Path(p) => p.try_cast().map(|s| s.into()),
Geometry::Text(t) => t.try_cast().map(|s| s.into()),
}
}
}
#[test]
fn test_convert_to_polygon() {
let geometries: Vec<Geometry<_>> = vec![
Point::new(0, 0).into(),
Edge::new((0, 0), (1, 1)).into(),
Rect::new((0, 0), (1, 1)).into(),
SimplePolygon::from(vec![(0, 0), (1, 0), (1, 1)]).into(),
Polygon::new(vec![(0, 0), (1, 0), (1, 1)]).into(),
];
for g in geometries {
assert_eq!(
g.area_doubled_oriented(),
g.to_polygon().area_doubled_oriented()
);
}
}