1use std::ops::RangeInclusive;
2
3use egui::{pos2, Color32, Mesh, NumExt, Rgba, Shape, Stroke, Ui};
4
5use crate::items;
6use crate::items::plot_item::PlotItem;
7use crate::items::value::Value;
8use crate::items::values::Values;
9use crate::items::{LineStyle, DEFAULT_FILL_ALPHA};
10use crate::transform::{Bounds, ScreenTransform};
11
12impl PlotItem for Line {
13 fn shapes(&self, _ui: &mut Ui, transform: &ScreenTransform, shapes: &mut Vec<Shape>) {
14 let Self {
15 series,
16 stroke,
17 highlight,
18 mut fill,
19 style,
20 ..
21 } = self;
22
23 let values_tf: Vec<_> = series
24 .values
25 .iter()
26 .map(|v| transform.position_from_value(v))
27 .collect();
28 let n_values = values_tf.len();
29
30 if n_values < 2 {
32 fill = None;
33 }
34 if let Some(y_reference) = fill {
35 let mut fill_alpha = DEFAULT_FILL_ALPHA;
36 if *highlight {
37 fill_alpha = (2.0 * fill_alpha).at_most(1.0);
38 }
39 let y = transform
40 .position_from_value(&Value::new(0.0, y_reference))
41 .y;
42 let fill_color = Rgba::from(stroke.color)
43 .to_opaque()
44 .multiply(fill_alpha)
45 .into();
46 let mut mesh = Mesh::default();
47 let expected_intersections = 20;
48 mesh.reserve_triangles((n_values - 1) * 2);
49 mesh.reserve_vertices(n_values * 2 + expected_intersections);
50 values_tf[0..n_values - 1].windows(2).for_each(|w| {
51 let i = mesh.vertices.len() as u32;
52 mesh.colored_vertex(w[0], fill_color);
53 mesh.colored_vertex(pos2(w[0].x, y), fill_color);
54 if let Some(x) = items::y_intersection(&w[0], &w[1], y) {
55 let point = pos2(x, y);
56 mesh.colored_vertex(point, fill_color);
57 mesh.add_triangle(i, i + 1, i + 2);
58 mesh.add_triangle(i + 2, i + 3, i + 4);
59 } else {
60 mesh.add_triangle(i, i + 1, i + 2);
61 mesh.add_triangle(i + 1, i + 2, i + 3);
62 }
63 });
64 let last = values_tf[n_values - 1];
65 mesh.colored_vertex(last, fill_color);
66 mesh.colored_vertex(pos2(last.x, y), fill_color);
67 shapes.push(Shape::Mesh(mesh));
68 }
69
70 style.style_line(values_tf, *stroke, *highlight, shapes);
71 }
72
73 fn initialize(&mut self, x_range: RangeInclusive<f64>) {
74 self.series.generate_points(x_range);
75 }
76
77 fn name(&self) -> &str {
78 self.name.as_str()
79 }
80
81 fn color(&self) -> Color32 {
82 self.stroke.color
83 }
84
85 fn highlight(&mut self) {
86 self.highlight = true;
87 }
88
89 fn highlighted(&self) -> bool {
90 self.highlight
91 }
92
93 fn values(&self) -> Option<&Values> {
94 Some(&self.series)
95 }
96
97 fn bounds(&self) -> Bounds {
98 self.series.get_bounds()
99 }
100}
101
102pub struct Line {
104 pub series: Values,
105 pub stroke: Stroke,
106 pub name: String,
107 pub highlight: bool,
108 pub fill: Option<f32>,
109 pub style: LineStyle,
110}
111
112impl Line {
113 pub fn new(series: Values) -> Self {
114 Self {
115 series,
116 stroke: Stroke::new(1.0, Color32::TRANSPARENT),
117 name: Default::default(),
118 highlight: false,
119 fill: None,
120 style: LineStyle::Solid,
121 }
122 }
123
124 #[must_use]
127 pub fn color(mut self, color: impl Into<Color32>) -> Self {
128 self.stroke.color = color.into();
129 self
130 }
131}