criterion_plot/
errorbar.rs

1//! Error bar plots
2
3use std::borrow::Cow;
4use std::iter::IntoIterator;
5
6use crate::data::Matrix;
7use crate::traits::{self, Data, Set};
8use crate::{
9    Color, Display, ErrorBarDefault, Figure, Label, LineType, LineWidth, Plot, PointSize,
10    PointType, Script,
11};
12
13/// Properties common to error bar plots
14pub struct Properties {
15    color: Option<Color>,
16    label: Option<Cow<'static, str>>,
17    line_type: LineType,
18    linewidth: Option<f64>,
19    point_size: Option<f64>,
20    point_type: Option<PointType>,
21    style: Style,
22}
23
24impl ErrorBarDefault<Style> for Properties {
25    fn default(style: Style) -> Properties {
26        Properties {
27            color: None,
28            label: None,
29            line_type: LineType::Solid,
30            linewidth: None,
31            point_type: None,
32            point_size: None,
33            style,
34        }
35    }
36}
37
38impl Script for Properties {
39    // Allow clippy::format_push_string even with older versions of rust (<1.62) which
40    // don't have it defined.
41    #[allow(clippy::all)]
42    fn script(&self) -> String {
43        let mut script = format!("with {} ", self.style.display());
44
45        script.push_str(&format!("lt {} ", self.line_type.display()));
46
47        if let Some(lw) = self.linewidth {
48            script.push_str(&format!("lw {} ", lw))
49        }
50
51        if let Some(color) = self.color {
52            script.push_str(&format!("lc rgb '{}' ", color.display()))
53        }
54
55        if let Some(pt) = self.point_type {
56            script.push_str(&format!("pt {} ", pt.display()))
57        }
58
59        if let Some(ps) = self.point_size {
60            script.push_str(&format!("ps {} ", ps))
61        }
62
63        if let Some(ref label) = self.label {
64            script.push_str("title '");
65            script.push_str(label);
66            script.push('\'')
67        } else {
68            script.push_str("notitle")
69        }
70
71        script
72    }
73}
74
75impl Set<Color> for Properties {
76    /// Changes the color of the error bars
77    fn set(&mut self, color: Color) -> &mut Properties {
78        self.color = Some(color);
79        self
80    }
81}
82
83impl Set<Label> for Properties {
84    /// Sets the legend label
85    fn set(&mut self, label: Label) -> &mut Properties {
86        self.label = Some(label.0);
87        self
88    }
89}
90
91impl Set<LineType> for Properties {
92    /// Change the line type
93    ///
94    /// **Note** By default `Solid` lines are used
95    fn set(&mut self, lt: LineType) -> &mut Properties {
96        self.line_type = lt;
97        self
98    }
99}
100
101impl Set<LineWidth> for Properties {
102    /// Changes the linewidth
103    ///
104    /// # Panics
105    ///
106    /// Panics if `lw` is a non-positive value
107    fn set(&mut self, lw: LineWidth) -> &mut Properties {
108        let lw = lw.0;
109
110        assert!(lw > 0.);
111
112        self.linewidth = Some(lw);
113        self
114    }
115}
116
117impl Set<PointSize> for Properties {
118    /// Changes the size of the points
119    ///
120    /// # Panics
121    ///
122    /// Panics if `size` is a non-positive value
123    fn set(&mut self, ps: PointSize) -> &mut Properties {
124        let ps = ps.0;
125
126        assert!(ps > 0.);
127
128        self.point_size = Some(ps);
129        self
130    }
131}
132
133impl Set<PointType> for Properties {
134    /// Changes the point type
135    fn set(&mut self, pt: PointType) -> &mut Properties {
136        self.point_type = Some(pt);
137        self
138    }
139}
140
141#[derive(Clone, Copy)]
142enum Style {
143    XErrorBars,
144    XErrorLines,
145    YErrorBars,
146    YErrorLines,
147}
148
149impl Display<&'static str> for Style {
150    fn display(&self) -> &'static str {
151        match *self {
152            Style::XErrorBars => "xerrorbars",
153            Style::XErrorLines => "xerrorlines",
154            Style::YErrorBars => "yerrorbars",
155            Style::YErrorLines => "yerrorlines",
156        }
157    }
158}
159
160/// Asymmetric error bar plots
161pub enum ErrorBar<X, Y, L, H> {
162    /// Horizontal error bars
163    XErrorBars {
164        /// X coordinate of the data points
165        x: X,
166        /// Y coordinate of the data points
167        y: Y,
168        /// X coordinate of the left end of the error bar
169        x_low: L,
170        /// Y coordinate of the right end of the error bar
171        x_high: H,
172    },
173    /// Horizontal error bars, where each point is joined by a line
174    XErrorLines {
175        /// X coordinate of the data points
176        x: X,
177        /// Y coordinate of the data points
178        y: Y,
179        /// X coordinate of the left end of the error bar
180        x_low: L,
181        /// Y coordinate of the right end of the error bar
182        x_high: H,
183    },
184    /// Vertical error bars
185    YErrorBars {
186        /// X coordinate of the data points
187        x: X,
188        /// Y coordinate of the data points
189        y: Y,
190        /// Y coordinate of the bottom of the error bar
191        y_low: L,
192        /// Y coordinate of the top of the error bar
193        y_high: H,
194    },
195    /// Vertical error bars, where each point is joined by a line
196    YErrorLines {
197        /// X coordinate of the data points
198        x: X,
199        /// Y coordinate of the data points
200        y: Y,
201        /// Y coordinate of the bottom of the error bar
202        y_low: L,
203        /// Y coordinate of the top of the error bar
204        y_high: H,
205    },
206}
207
208impl<X, Y, L, H> ErrorBar<X, Y, L, H> {
209    fn style(&self) -> Style {
210        match *self {
211            ErrorBar::XErrorBars { .. } => Style::XErrorBars,
212            ErrorBar::XErrorLines { .. } => Style::XErrorLines,
213            ErrorBar::YErrorBars { .. } => Style::YErrorBars,
214            ErrorBar::YErrorLines { .. } => Style::YErrorLines,
215        }
216    }
217}
218
219impl<X, Y, L, H> traits::Plot<ErrorBar<X, Y, L, H>> for Figure
220where
221    H: IntoIterator,
222    H::Item: Data,
223    L: IntoIterator,
224    L::Item: Data,
225    X: IntoIterator,
226    X::Item: Data,
227    Y: IntoIterator,
228    Y::Item: Data,
229{
230    type Properties = Properties;
231
232    fn plot<F>(&mut self, e: ErrorBar<X, Y, L, H>, configure: F) -> &mut Figure
233    where
234        F: FnOnce(&mut Properties) -> &mut Properties,
235    {
236        let (x_factor, y_factor) = crate::scale_factor(&self.axes, crate::Axes::BottomXLeftY);
237
238        let style = e.style();
239        let (x, y, length, height, e_factor) = match e {
240            ErrorBar::XErrorBars {
241                x,
242                y,
243                x_low,
244                x_high,
245            }
246            | ErrorBar::XErrorLines {
247                x,
248                y,
249                x_low,
250                x_high,
251            } => (x, y, x_low, x_high, x_factor),
252            ErrorBar::YErrorBars {
253                x,
254                y,
255                y_low,
256                y_high,
257            }
258            | ErrorBar::YErrorLines {
259                x,
260                y,
261                y_low,
262                y_high,
263            } => (x, y, y_low, y_high, y_factor),
264        };
265        let data = Matrix::new(
266            izip!(x, y, length, height),
267            (x_factor, y_factor, e_factor, e_factor),
268        );
269        self.plots.push(Plot::new(
270            data,
271            configure(&mut ErrorBarDefault::default(style)),
272        ));
273        self
274    }
275}
276
277// TODO XY error bar
278// pub struct XyErrorBar<X, Y, XL, XH, YL, YH> {
279// x: X,
280// y: Y,
281// x_low: XL,
282// x_high: XH,
283// y_low: YL,
284// y_high: YH,
285// }
286
287// TODO Symmetric error bars
288// pub enum SymmetricErrorBar {
289// XSymmetricErrorBar { x: X, y: Y, x_delta: D },
290// XSymmetricErrorLines { x: X, y: Y, x_delta: D },
291// YSymmetricErrorBar { x: X, y: Y, y_delta: D },
292// YSymmetricErrorLines { x: X, y: Y, y_delta: D },
293// }