plotters_unstable/element/
errorbar.rs

1use std::marker::PhantomData;
2
3use crate::element::{Drawable, PointCollection};
4use crate::style::ShapeStyle;
5use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
6
7pub trait ErrorBarOrient<K, V> {
8    type XType;
9    type YType;
10
11    fn make_coord(key: K, val: V) -> (Self::XType, Self::YType);
12    fn ending_coord(coord: BackendCoord, w: u32) -> (BackendCoord, BackendCoord);
13}
14
15pub struct ErrorBarOrientH<K, V>(PhantomData<(K, V)>);
16
17pub struct ErrorBarOrientV<K, V>(PhantomData<(K, V)>);
18
19impl<K, V> ErrorBarOrient<K, V> for ErrorBarOrientH<K, V> {
20    type XType = V;
21    type YType = K;
22
23    fn make_coord(key: K, val: V) -> (V, K) {
24        (val, key)
25    }
26
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
35impl<K, V> ErrorBarOrient<K, V> for ErrorBarOrientV<K, V> {
36    type XType = K;
37    type YType = V;
38
39    fn make_coord(key: K, val: V) -> (K, V) {
40        (key, val)
41    }
42
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
51pub 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
59impl<K, V> ErrorBar<K, V, ErrorBarOrientV<K, V>> {
60    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
78impl<K, V> ErrorBar<K, V, ErrorBarOrientH<K, V>> {
79    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
97impl<'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>;
102    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
110impl<K, V, O: ErrorBarOrient<K, V>, DB: DrawingBackend> Drawable<DB> for ErrorBar<K, V, O> {
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]
135fn 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}