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  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>; next(&mut self) -> Option<Self::Item>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 */ new<I: IntoIterator<Item = Coord>, S: Into<ShapeStyle>>(iter: I, style: S) -> Self66 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 */ point_size(mut self, size: u32) -> Self81 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] test_line_series()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