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