1use esoc_gfx::canvas::Canvas;
5use esoc_gfx::color::Color;
6use esoc_gfx::element::{DrawElement, Element};
7use esoc_gfx::geom::Point;
8use esoc_gfx::layer::Layer;
9use esoc_gfx::style::{DashPattern, Stroke};
10use esoc_gfx::transform::CoordinateTransform;
11
12use crate::series::{DataBounds, SeriesRenderer};
13use crate::theme::Theme;
14
15#[derive(Clone, Debug)]
17pub struct LineSeries {
18 pub x: Vec<f64>,
20 pub y: Vec<f64>,
22 pub label: Option<String>,
24 pub color: Option<Color>,
26 pub width: Option<f64>,
28 pub dash: Option<DashPattern>,
30}
31
32impl LineSeries {
33 pub fn new(x: &[f64], y: &[f64]) -> Self {
35 Self {
36 x: x.to_vec(),
37 y: y.to_vec(),
38 label: None,
39 color: None,
40 width: None,
41 dash: None,
42 }
43 }
44
45 pub fn build_stroke(&self, theme: &Theme, series_index: usize) -> Stroke {
47 let color = self
48 .color
49 .unwrap_or_else(|| theme.palette.get(series_index));
50 let width = self.width.unwrap_or(theme.line_width);
51 match &self.dash {
52 Some(dash) => Stroke::dashed(color, width, &dash.dashes),
53 None => Stroke::solid(color, width),
54 }
55 }
56}
57
58impl SeriesRenderer for LineSeries {
59 fn data_bounds(&self) -> DataBounds {
60 let x_min = self.x.iter().copied().fold(f64::INFINITY, f64::min);
61 let x_max = self.x.iter().copied().fold(f64::NEG_INFINITY, f64::max);
62 let y_min = self.y.iter().copied().fold(f64::INFINITY, f64::min);
63 let y_max = self.y.iter().copied().fold(f64::NEG_INFINITY, f64::max);
64 DataBounds::new(x_min, x_max, y_min, y_max)
65 }
66
67 fn render(
68 &self,
69 canvas: &mut Canvas,
70 transform: &CoordinateTransform,
71 theme: &Theme,
72 series_index: usize,
73 ) {
74 if self.x.is_empty() {
75 return;
76 }
77
78 let points: Vec<Point> = self
79 .x
80 .iter()
81 .zip(self.y.iter())
82 .map(|(&x, &y)| transform.to_pixel(x, y))
83 .collect();
84
85 let stroke = self.build_stroke(theme, series_index);
86 canvas.add(DrawElement::new(
87 Element::polyline(points, stroke),
88 Layer::Data,
89 ));
90 }
91
92 fn label(&self) -> Option<&str> {
93 self.label.as_deref()
94 }
95}