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