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// }
294