blob: 7efd23e3dcc2deba62f4d8a105711a55870dc22c [file] [log] [blame]
Jakub Kotur835fea42020-12-21 17:28:14 +01001//! 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 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
72impl Set<Color> for Properties {
73 /// Changes the color of the error bars
74 fn set(&mut self, color: Color) -> &mut Properties {
75 self.color = Some(color);
76 self
77 }
78}
79
80impl Set<Label> for Properties {
81 /// Sets the legend label
82 fn set(&mut self, label: Label) -> &mut Properties {
83 self.label = Some(label.0);
84 self
85 }
86}
87
88impl Set<LineType> for Properties {
89 /// Change the line type
90 ///
91 /// **Note** By default `Solid` lines are used
92 fn set(&mut self, lt: LineType) -> &mut Properties {
93 self.line_type = lt;
94 self
95 }
96}
97
98impl Set<LineWidth> for Properties {
99 /// Changes the linewidth
100 ///
101 /// # Panics
102 ///
103 /// Panics if `lw` is a non-positive value
104 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
114impl Set<PointSize> for Properties {
115 /// Changes the size of the points
116 ///
117 /// # Panics
118 ///
119 /// Panics if `size` is a non-positive value
120 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
130impl Set<PointType> for Properties {
131 /// Changes the point type
132 fn set(&mut self, pt: PointType) -> &mut Properties {
133 self.point_type = Some(pt);
134 self
135 }
136}
137
138#[derive(Clone, Copy)]
139enum Style {
140 XErrorBars,
141 XErrorLines,
142 YErrorBars,
143 YErrorLines,
144}
145
146impl Display<&'static str> for Style {
147 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
158pub 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
205impl<X, Y, L, H> ErrorBar<X, Y, L, H> {
206 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
216impl<X, Y, L, H> traits::Plot<ErrorBar<X, Y, L, H>> for Figure
217where
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
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// }