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 { 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)] script(&self) -> String42 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 75 impl Set<Color> for Properties { 76 /// Changes the color of the error bars set(&mut self, color: Color) -> &mut Properties77 fn set(&mut self, color: Color) -> &mut Properties { 78 self.color = Some(color); 79 self 80 } 81 } 82 83 impl Set<Label> for Properties { 84 /// Sets the legend label set(&mut self, label: Label) -> &mut Properties85 fn set(&mut self, label: Label) -> &mut Properties { 86 self.label = Some(label.0); 87 self 88 } 89 } 90 91 impl Set<LineType> for Properties { 92 /// Change the line type 93 /// 94 /// **Note** By default `Solid` lines are used set(&mut self, lt: LineType) -> &mut Properties95 fn set(&mut self, lt: LineType) -> &mut Properties { 96 self.line_type = lt; 97 self 98 } 99 } 100 101 impl Set<LineWidth> for Properties { 102 /// Changes the linewidth 103 /// 104 /// # Panics 105 /// 106 /// Panics if `lw` is a non-positive value set(&mut self, lw: LineWidth) -> &mut Properties107 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 117 impl Set<PointSize> for Properties { 118 /// Changes the size of the points 119 /// 120 /// # Panics 121 /// 122 /// Panics if `size` is a non-positive value set(&mut self, ps: PointSize) -> &mut Properties123 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 133 impl Set<PointType> for Properties { 134 /// Changes the point type set(&mut self, pt: PointType) -> &mut Properties135 fn set(&mut self, pt: PointType) -> &mut Properties { 136 self.point_type = Some(pt); 137 self 138 } 139 } 140 141 #[derive(Clone, Copy)] 142 enum Style { 143 XErrorBars, 144 XErrorLines, 145 YErrorBars, 146 YErrorLines, 147 } 148 149 impl Display<&'static str> for Style { display(&self) -> &'static str150 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 161 pub 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 208 impl<X, Y, L, H> ErrorBar<X, Y, L, H> { style(&self) -> Style209 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 219 impl<X, Y, L, H> traits::Plot<ErrorBar<X, Y, L, H>> for Figure 220 where 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 plot<F>(&mut self, e: ErrorBar<X, Y, L, H>, configure: F) -> &mut Figure where F: FnOnce(&mut Properties) -> &mut Properties,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