1use crate::element::{Circle, DynElement, IntoDynElement, PathElement};
2use crate::style::ShapeStyle;
3use plotters_backend::DrawingBackend;
4use std::marker::PhantomData;
5
6/**
7The line series object, which takes an iterator of data points in guest coordinate system
8and creates appropriate lines and points with the given style.
9
10# Example
11
12```
13use plotters::prelude::*;
14let x_values = [0.0f64, 1., 2., 3., 4.];
15let drawing_area = SVGBackend::new("line_series_point_size.svg", (300, 200)).into_drawing_area();
16drawing_area.fill(&WHITE).unwrap();
17let mut chart_builder = ChartBuilder::on(&drawing_area);
18chart_builder.margin(10).set_left_and_bottom_label_area_size(20);
19let mut chart_context = chart_builder.build_cartesian_2d(0.0..4.0, 0.0..3.0).unwrap();
20chart_context.configure_mesh().draw().unwrap();
21chart_context.draw_series(LineSeries::new(x_values.map(|x| (x, 0.3 * x)), BLACK)).unwrap();
22chart_context.draw_series(LineSeries::new(x_values.map(|x| (x, 2.5 - 0.05 * x * x)), RED)
23 .point_size(5)).unwrap();
24chart_context.draw_series(LineSeries::new(x_values.map(|x| (x, 2. - 0.1 * x * x)), BLUE.filled())
25 .point_size(4)).unwrap();
26```
27
28The 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*/
32pub 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
40impl<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
60impl<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)]
88mod 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