use std::ops::RangeInclusive;
use egui::Color32;
use egui::Id;
use egui::Shape;
use egui::Stroke;
use egui::Ui;
use egui::epaint::PathStroke;
use crate::aesthetics::LineStyle;
use crate::axis::PlotTransform;
use crate::bounds::PlotBounds;
use crate::colors::DEFAULT_FILL_ALPHA;
use crate::data::PlotPoints;
use crate::items::PlotGeometry;
use crate::items::PlotItem;
use crate::items::PlotItemBase;
pub struct Polygon<'a> {
base: PlotItemBase,
pub(crate) series: PlotPoints<'a>,
pub(crate) stroke: Stroke,
pub(crate) fill_color: Option<Color32>,
pub(crate) style: LineStyle,
}
impl<'a> Polygon<'a> {
pub fn new(name: impl Into<String>, series: impl Into<PlotPoints<'a>>) -> Self {
Self {
base: PlotItemBase::new(name.into()),
series: series.into(),
stroke: Stroke::new(1.0, Color32::TRANSPARENT),
fill_color: None,
style: LineStyle::Solid,
}
}
#[inline]
pub fn stroke(mut self, stroke: impl Into<Stroke>) -> Self {
self.stroke = stroke.into();
self
}
#[inline]
pub fn width(mut self, width: impl Into<f32>) -> Self {
self.stroke.width = width.into();
self
}
#[inline]
pub fn fill_color(mut self, color: impl Into<Color32>) -> Self {
self.fill_color = Some(color.into());
self
}
#[inline]
pub fn style(mut self, style: LineStyle) -> Self {
self.style = style;
self
}
#[expect(clippy::needless_pass_by_value, reason = "to allow various string types")]
#[inline]
pub fn name(mut self, name: impl ToString) -> Self {
self.base_mut().name = name.to_string();
self
}
#[inline]
pub fn highlight(mut self, highlight: bool) -> Self {
self.base_mut().highlight = highlight;
self
}
#[inline]
pub fn allow_hover(mut self, hovering: bool) -> Self {
self.base_mut().allow_hover = hovering;
self
}
#[inline]
pub fn id(mut self, id: impl Into<Id>) -> Self {
self.base_mut().id = id.into();
self
}
}
impl PlotItem for Polygon<'_> {
fn shapes(&self, _ui: &Ui, transform: &PlotTransform, shapes: &mut Vec<Shape>) {
let Self {
base,
series,
stroke,
fill_color,
style,
..
} = self;
let mut values_tf: Vec<_> = series
.points()
.iter()
.map(|v| transform.position_from_point(v))
.collect();
let fill_color = fill_color.unwrap_or(stroke.color.linear_multiply(DEFAULT_FILL_ALPHA));
let shape = Shape::convex_polygon(values_tf.clone(), fill_color, Stroke::NONE);
shapes.push(shape);
if let Some(first) = values_tf.first() {
values_tf.push(*first); }
style.style_line(
values_tf,
PathStroke::new(stroke.width, stroke.color),
base.highlight,
shapes,
);
}
fn initialize(&mut self, x_range: RangeInclusive<f64>) {
self.series.generate_points(x_range);
}
fn color(&self) -> Color32 {
self.stroke.color
}
fn geometry(&self) -> PlotGeometry<'_> {
PlotGeometry::Points(self.series.points())
}
fn bounds(&self) -> PlotBounds {
self.series.bounds()
}
fn base(&self) -> &PlotItemBase {
&self.base
}
fn base_mut(&mut self) -> &mut PlotItemBase {
&mut self.base
}
}