use crate::{
fragment::{marker_line::Marker, Bounds},
Cell, Direction, Point,
};
use nalgebra::Point2;
use parry2d::shape::{shape::Shape, Polyline};
use sauron::{
html::attributes::*,
svg::{attributes::*, *},
Node,
};
use std::{cmp::Ordering, fmt};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PolygonTag {
ArrowTopLeft,
ArrowTop,
ArrowTopRight,
ArrowLeft,
ArrowRight,
ArrowBottomLeft,
ArrowBottom,
ArrowBottomRight,
DiamondBullet,
}
#[derive(Debug, Clone)]
pub struct Polygon {
pub points: Vec<Point>,
pub is_filled: bool,
pub tags: Vec<PolygonTag>,
}
impl PolygonTag {
pub(crate) fn get_marker(&self) -> Marker {
match self {
PolygonTag::ArrowTopLeft
| PolygonTag::ArrowTop
| PolygonTag::ArrowTopRight
| PolygonTag::ArrowLeft
| PolygonTag::ArrowRight
| PolygonTag::ArrowBottomLeft
| PolygonTag::ArrowBottom
| PolygonTag::ArrowBottomRight => Marker::Arrow,
PolygonTag::DiamondBullet => Marker::Diamond,
}
}
pub(crate) fn direction(&self) -> Option<Direction> {
match self {
PolygonTag::ArrowTopLeft => Some(Direction::TopLeft),
PolygonTag::ArrowTop => Some(Direction::Top),
PolygonTag::ArrowTopRight => Some(Direction::TopRight),
PolygonTag::ArrowLeft => Some(Direction::Left),
PolygonTag::ArrowRight => Some(Direction::Right),
PolygonTag::ArrowBottomLeft => Some(Direction::BottomLeft),
PolygonTag::ArrowBottom => Some(Direction::Bottom),
PolygonTag::ArrowBottomRight => Some(Direction::BottomRight),
PolygonTag::DiamondBullet => None,
}
}
pub(crate) fn matched_direction(&self, arg: Direction) -> bool {
if let Some(direction) = self.direction() {
direction == arg
} else {
true
}
}
}
impl Polygon {
pub(crate) fn new(
points: Vec<Point>,
is_filled: bool,
tags: Vec<PolygonTag>,
) -> Self {
Polygon {
points,
is_filled,
tags,
}
}
pub(crate) fn absolute_position(&self, cell: Cell) -> Self {
let points: Vec<Point> = self
.points
.iter()
.map(|p| cell.absolute_position(*p))
.collect();
Polygon {
points,
is_filled: self.is_filled,
tags: self.tags.clone(),
}
}
pub(crate) fn matched_direction(&self, direction: Direction) -> bool {
self.tags.iter().any(|tag| tag.matched_direction(direction))
}
fn is_diamond(&self) -> bool {
self.tags.len() == 1
&& self
.tags
.iter()
.all(|tag| *tag == PolygonTag::DiamondBullet)
}
pub(crate) fn get_marker(&self) -> Option<Marker> {
if !self.tags.is_empty() {
if self.is_diamond() {
Some(Marker::Diamond)
} else if self
.tags
.iter()
.all(|tag| tag.get_marker() == Marker::Arrow)
{
Some(Marker::Arrow)
} else {
None
}
} else {
None
}
}
pub(crate) fn scale(&self, scale: f32) -> Self {
let points: Vec<Point> =
self.points.iter().map(|p| p.scale(scale)).collect();
Polygon {
points,
is_filled: self.is_filled,
tags: self.tags.clone(),
}
}
fn first(&self) -> Point {
self.points[0]
}
fn last(&self) -> Point {
let n = self.points.len();
self.points[n - 1]
}
pub(crate) fn center(&self) -> Point {
let points: Vec<Point2<f32>> =
self.points.iter().map(|p| **p).collect();
(parry2d::utils::center(&points)).into()
}
}
impl Bounds for Polygon {
fn bounds(&self) -> (Point, Point) {
let pl: Polyline = self.clone().into();
let aabb = pl.local_aabb();
(Point::from(*aabb.mins), Point::from(*aabb.maxs))
}
}
impl fmt::Display for Polygon {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"P {}",
self.points
.iter()
.map(|p| p.to_string())
.collect::<Vec<String>>()
.join(" ")
)
}
}
impl From<Polygon> for Polyline {
fn from(polygon: Polygon) -> Polyline {
let points: Vec<Point2<f32>> =
polygon.points.iter().map(|p| **p).collect();
Polyline::new(points, None)
}
}
impl<MSG> From<Polygon> for Node<MSG> {
fn from(pl: Polygon) -> Node<MSG> {
polygon(
[
points(
pl.points
.iter()
.map(|p| format!("{},{}", p.x, p.y))
.collect::<Vec<String>>()
.join(" "),
),
classes_flag([
("filled", pl.is_filled),
("nofill", !pl.is_filled),
]),
],
[],
)
}
}
impl Eq for Polygon {}
impl Ord for Polygon {
fn cmp(&self, other: &Self) -> Ordering {
if self.points == other.points {
Ordering::Equal
} else {
self.first()
.cmp(&other.first())
.then(self.last().cmp(&other.last()))
.then(self.is_filled.cmp(&other.is_filled))
.then(self.points.len().cmp(&other.points.len()))
}
}
}
impl PartialOrd for Polygon {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Polygon {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}