esoc_chart/chart/
scatter.rs1use esoc_gfx::canvas::Canvas;
5use esoc_gfx::color::Color;
6use esoc_gfx::element::{DrawElement, Element};
7use esoc_gfx::layer::Layer;
8use esoc_gfx::style::Fill;
9use esoc_gfx::transform::CoordinateTransform;
10
11use crate::series::{DataBounds, SeriesRenderer};
12use crate::theme::Theme;
13
14#[derive(Clone, Debug)]
16pub struct ScatterSeries {
17 pub x: Vec<f64>,
19 pub y: Vec<f64>,
21 pub label: Option<String>,
23 pub color: Option<Color>,
25 pub radius: Option<f64>,
27}
28
29impl ScatterSeries {
30 pub fn new(x: &[f64], y: &[f64]) -> Self {
32 Self {
33 x: x.to_vec(),
34 y: y.to_vec(),
35 label: None,
36 color: None,
37 radius: None,
38 }
39 }
40}
41
42impl SeriesRenderer for ScatterSeries {
43 fn data_bounds(&self) -> DataBounds {
44 let x_min = self.x.iter().copied().fold(f64::INFINITY, f64::min);
45 let x_max = self.x.iter().copied().fold(f64::NEG_INFINITY, f64::max);
46 let y_min = self.y.iter().copied().fold(f64::INFINITY, f64::min);
47 let y_max = self.y.iter().copied().fold(f64::NEG_INFINITY, f64::max);
48 DataBounds::new(x_min, x_max, y_min, y_max)
49 }
50
51 fn render(
52 &self,
53 canvas: &mut Canvas,
54 transform: &CoordinateTransform,
55 theme: &Theme,
56 series_index: usize,
57 ) {
58 let color = self
59 .color
60 .unwrap_or_else(|| theme.palette.get(series_index));
61 let radius = self.radius.unwrap_or(theme.point_radius);
62
63 for (&x, &y) in self.x.iter().zip(self.y.iter()) {
64 let p = transform.to_pixel(x, y);
65 canvas.add(DrawElement::new(
66 Element::circle(p.x, p.y, radius, Fill::Solid(color)),
67 Layer::Data,
68 ));
69 }
70 }
71
72 fn label(&self) -> Option<&str> {
73 self.label.as_deref()
74 }
75}