1 use std::marker::PhantomData;
2 
3 use crate::element::{Drawable, PointCollection};
4 use crate::style::ShapeStyle;
5 use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
6 
7 pub trait ErrorBarOrient<K, V> {
8     type XType;
9     type YType;
10 
make_coord(key: K, val: V) -> (Self::XType, Self::YType)11     fn make_coord(key: K, val: V) -> (Self::XType, Self::YType);
ending_coord(coord: BackendCoord, w: u32) -> (BackendCoord, BackendCoord)12     fn ending_coord(coord: BackendCoord, w: u32) -> (BackendCoord, BackendCoord);
13 }
14 
15 pub struct ErrorBarOrientH<K, V>(PhantomData<(K, V)>);
16 
17 pub struct ErrorBarOrientV<K, V>(PhantomData<(K, V)>);
18 
19 impl<K, V> ErrorBarOrient<K, V> for ErrorBarOrientH<K, V> {
20     type XType = V;
21     type YType = K;
22 
make_coord(key: K, val: V) -> (V, K)23     fn make_coord(key: K, val: V) -> (V, K) {
24         (val, key)
25     }
26 
ending_coord(coord: BackendCoord, w: u32) -> (BackendCoord, BackendCoord)27     fn ending_coord(coord: BackendCoord, w: u32) -> (BackendCoord, BackendCoord) {
28         (
29             (coord.0, coord.1 - w as i32 / 2),
30             (coord.0, coord.1 + w as i32 / 2),
31         )
32     }
33 }
34 
35 impl<K, V> ErrorBarOrient<K, V> for ErrorBarOrientV<K, V> {
36     type XType = K;
37     type YType = V;
38 
make_coord(key: K, val: V) -> (K, V)39     fn make_coord(key: K, val: V) -> (K, V) {
40         (key, val)
41     }
42 
ending_coord(coord: BackendCoord, w: u32) -> (BackendCoord, BackendCoord)43     fn ending_coord(coord: BackendCoord, w: u32) -> (BackendCoord, BackendCoord) {
44         (
45             (coord.0 - w as i32 / 2, coord.1),
46             (coord.0 + w as i32 / 2, coord.1),
47         )
48     }
49 }
50 
51 pub struct ErrorBar<K, V, O: ErrorBarOrient<K, V>> {
52     style: ShapeStyle,
53     width: u32,
54     key: K,
55     values: [V; 3],
56     _p: PhantomData<O>,
57 }
58 
59 impl<K, V> ErrorBar<K, V, ErrorBarOrientV<K, V>> {
new_vertical<S: Into<ShapeStyle>>( key: K, min: V, avg: V, max: V, style: S, width: u32, ) -> Self60     pub fn new_vertical<S: Into<ShapeStyle>>(
61         key: K,
62         min: V,
63         avg: V,
64         max: V,
65         style: S,
66         width: u32,
67     ) -> Self {
68         Self {
69             style: style.into(),
70             width,
71             key,
72             values: [min, avg, max],
73             _p: PhantomData,
74         }
75     }
76 }
77 
78 impl<K, V> ErrorBar<K, V, ErrorBarOrientH<K, V>> {
new_horizontal<S: Into<ShapeStyle>>( key: K, min: V, avg: V, max: V, style: S, width: u32, ) -> Self79     pub fn new_horizontal<S: Into<ShapeStyle>>(
80         key: K,
81         min: V,
82         avg: V,
83         max: V,
84         style: S,
85         width: u32,
86     ) -> Self {
87         Self {
88             style: style.into(),
89             width,
90             key,
91             values: [min, avg, max],
92             _p: PhantomData,
93         }
94     }
95 }
96 
97 impl<'a, K: Clone, V: Clone, O: ErrorBarOrient<K, V>> PointCollection<'a, (O::XType, O::YType)>
98     for &'a ErrorBar<K, V, O>
99 {
100     type Point = (O::XType, O::YType);
101     type IntoIter = Vec<Self::Point>;
point_iter(self) -> Self::IntoIter102     fn point_iter(self) -> Self::IntoIter {
103         self.values
104             .iter()
105             .map(|v| O::make_coord(self.key.clone(), v.clone()))
106             .collect()
107     }
108 }
109 
110 impl<K, V, O: ErrorBarOrient<K, V>, DB: DrawingBackend> Drawable<DB> for ErrorBar<K, V, O> {
draw<I: Iterator<Item = BackendCoord>>( &self, points: I, backend: &mut DB, _: (u32, u32), ) -> Result<(), DrawingErrorKind<DB::ErrorType>>111     fn draw<I: Iterator<Item = BackendCoord>>(
112         &self,
113         points: I,
114         backend: &mut DB,
115         _: (u32, u32),
116     ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
117         let points: Vec<_> = points.take(3).collect();
118 
119         let (from, to) = O::ending_coord(points[0], self.width);
120         backend.draw_line(from, to, &self.style)?;
121 
122         let (from, to) = O::ending_coord(points[2], self.width);
123         backend.draw_line(from, to, &self.style)?;
124 
125         backend.draw_line(points[0], points[2], &self.style)?;
126 
127         backend.draw_circle(points[1], self.width / 2, &self.style, self.style.filled)?;
128 
129         Ok(())
130     }
131 }
132 
133 #[cfg(test)]
134 #[test]
test_preserve_stroke_width()135 fn test_preserve_stroke_width() {
136     let v = ErrorBar::new_vertical(100, 20, 50, 70, WHITE.filled().stroke_width(5), 3);
137     let h = ErrorBar::new_horizontal(100, 20, 50, 70, WHITE.filled().stroke_width(5), 3);
138 
139     use crate::prelude::*;
140     let da = crate::create_mocked_drawing_area(300, 300, |m| {
141         m.check_draw_line(|_, w, _, _| {
142             assert_eq!(w, 5);
143         });
144     });
145     da.draw(&h).expect("Drawing Failure");
146     da.draw(&v).expect("Drawing Failure");
147 }
148