1 | use crate::element::{Circle, DynElement, IntoDynElement, PathElement}; |
2 | use crate::style::ShapeStyle; |
3 | use plotters_backend::DrawingBackend; |
4 | use std::marker::PhantomData; |
5 | |
6 | /** |
7 | The line series object, which takes an iterator of data points in guest coordinate system |
8 | and creates appropriate lines and points with the given style. |
9 | |
10 | # Example |
11 | |
12 | ``` |
13 | use plotters::prelude::*; |
14 | let x_values = [0.0f64, 1., 2., 3., 4.]; |
15 | let drawing_area = SVGBackend::new("line_series_point_size.svg" , (300, 200)).into_drawing_area(); |
16 | drawing_area.fill(&WHITE).unwrap(); |
17 | let mut chart_builder = ChartBuilder::on(&drawing_area); |
18 | chart_builder.margin(10).set_left_and_bottom_label_area_size(20); |
19 | let mut chart_context = chart_builder.build_cartesian_2d(0.0..4.0, 0.0..3.0).unwrap(); |
20 | chart_context.configure_mesh().draw().unwrap(); |
21 | chart_context.draw_series(LineSeries::new(x_values.map(|x| (x, 0.3 * x)), BLACK)).unwrap(); |
22 | chart_context.draw_series(LineSeries::new(x_values.map(|x| (x, 2.5 - 0.05 * x * x)), RED) |
23 | .point_size(5)).unwrap(); |
24 | chart_context.draw_series(LineSeries::new(x_values.map(|x| (x, 2. - 0.1 * x * x)), BLUE.filled()) |
25 | .point_size(4)).unwrap(); |
26 | ``` |
27 | |
28 | The result is a chart with three line series; two of them have their data points highlighted: |
29 | |
30 | ![](https://cdn.jsdelivr.net/gh/facorread/plotters-doc-data@64e0a28/apidoc/line_series_point_size.svg) |
31 | */ |
32 | pub struct LineSeries<DB: DrawingBackend, Coord> { |
33 | style: ShapeStyle, |
34 | data: Vec<Coord>, |
35 | point_idx: usize, |
36 | point_size: u32, |
37 | phantom: PhantomData<DB>, |
38 | } |
39 | |
40 | impl<DB: DrawingBackend, Coord: Clone + 'static> Iterator for LineSeries<DB, Coord> { |
41 | type Item = DynElement<'static, DB, Coord>; |
42 | fn next(&mut self) -> Option<Self::Item> { |
43 | if !self.data.is_empty() { |
44 | if self.point_size > 0 && self.point_idx < self.data.len() { |
45 | let idx = self.point_idx; |
46 | self.point_idx += 1; |
47 | return Some( |
48 | Circle::new(self.data[idx].clone(), self.point_size, self.style).into_dyn(), |
49 | ); |
50 | } |
51 | let mut data = vec![]; |
52 | std::mem::swap(&mut self.data, &mut data); |
53 | Some(PathElement::new(data, self.style).into_dyn()) |
54 | } else { |
55 | None |
56 | } |
57 | } |
58 | } |
59 | |
60 | impl<DB: DrawingBackend, Coord> LineSeries<DB, Coord> { |
61 | /** |
62 | Creates a new line series based on a data iterator and a given style. |
63 | |
64 | See [`LineSeries`] for more information and examples. |
65 | */ |
66 | pub fn new<I: IntoIterator<Item = Coord>, S: Into<ShapeStyle>>(iter: I, style: S) -> Self { |
67 | Self { |
68 | style: style.into(), |
69 | data: iter.into_iter().collect(), |
70 | point_size: 0, |
71 | point_idx: 0, |
72 | phantom: PhantomData, |
73 | } |
74 | } |
75 | |
76 | /** |
77 | Sets the size of the points in the series, in pixels. |
78 | |
79 | See [`LineSeries`] for more information and examples. |
80 | */ |
81 | pub fn point_size(mut self, size: u32) -> Self { |
82 | self.point_size = size; |
83 | self |
84 | } |
85 | } |
86 | |
87 | #[cfg (test)] |
88 | mod test { |
89 | use crate::prelude::*; |
90 | |
91 | #[test] |
92 | fn test_line_series() { |
93 | let drawing_area = create_mocked_drawing_area(200, 200, |m| { |
94 | m.check_draw_path(|c, s, _path| { |
95 | assert_eq!(c, RED.to_rgba()); |
96 | assert_eq!(s, 3); |
97 | // TODO when cleanup the backend coordinate defination, then we uncomment the |
98 | // following check |
99 | //for i in 0..100 { |
100 | // assert_eq!(path[i], (i as i32 * 2, 199 - i as i32 * 2)); |
101 | //} |
102 | }); |
103 | |
104 | m.drop_check(|b| { |
105 | assert_eq!(b.num_draw_path_call, 1); |
106 | assert_eq!(b.draw_count, 1); |
107 | }); |
108 | }); |
109 | |
110 | let mut chart = ChartBuilder::on(&drawing_area) |
111 | .build_cartesian_2d(0..100, 0..100) |
112 | .expect("Build chart error" ); |
113 | |
114 | chart |
115 | .draw_series(LineSeries::new( |
116 | (0..100).map(|x| (x, x)), |
117 | Into::<ShapeStyle>::into(&RED).stroke_width(3), |
118 | )) |
119 | .expect("Drawing Error" ); |
120 | } |
121 | } |
122 | |