1 //! Simple "curve" like 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 Axes, Color, CurveDefault, Display, Figure, Label, LineType, LineWidth, Plot, PointSize, 10 PointType, Script, 11 }; 12 13 /// Properties common to simple "curve" like plots 14 pub struct Properties { 15 axes: Option<Axes>, 16 color: Option<Color>, 17 label: Option<Cow<'static, str>>, 18 line_type: LineType, 19 linewidth: Option<f64>, 20 point_type: Option<PointType>, 21 point_size: Option<f64>, 22 style: Style, 23 } 24 25 impl CurveDefault<Style> for Properties { default(style: Style) -> Properties26 fn default(style: Style) -> Properties { 27 Properties { 28 axes: None, 29 color: None, 30 label: None, 31 line_type: LineType::Solid, 32 linewidth: None, 33 point_size: None, 34 point_type: None, 35 style, 36 } 37 } 38 } 39 40 impl Script for Properties { 41 // Allow clippy::format_push_string even with older versions of rust (<1.62) which 42 // don't have it defined. 43 #[allow(clippy::all)] script(&self) -> String44 fn script(&self) -> String { 45 let mut script = if let Some(axes) = self.axes { 46 format!("axes {} ", axes.display()) 47 } else { 48 String::new() 49 }; 50 51 script.push_str(&format!("with {} ", self.style.display())); 52 script.push_str(&format!("lt {} ", self.line_type.display())); 53 54 if let Some(lw) = self.linewidth { 55 script.push_str(&format!("lw {} ", lw)) 56 } 57 58 if let Some(color) = self.color { 59 script.push_str(&format!("lc rgb '{}' ", color.display())) 60 } 61 62 if let Some(pt) = self.point_type { 63 script.push_str(&format!("pt {} ", pt.display())) 64 } 65 66 if let Some(ps) = self.point_size { 67 script.push_str(&format!("ps {} ", ps)) 68 } 69 70 if let Some(ref label) = self.label { 71 script.push_str("title '"); 72 script.push_str(label); 73 script.push('\'') 74 } else { 75 script.push_str("notitle") 76 } 77 78 script 79 } 80 } 81 82 impl Set<Axes> for Properties { 83 /// Select the axes to plot against 84 /// 85 /// **Note** By default, the `BottomXLeftY` axes are used set(&mut self, axes: Axes) -> &mut Properties86 fn set(&mut self, axes: Axes) -> &mut Properties { 87 self.axes = Some(axes); 88 self 89 } 90 } 91 92 impl Set<Color> for Properties { 93 /// Sets the line color set(&mut self, color: Color) -> &mut Properties94 fn set(&mut self, color: Color) -> &mut Properties { 95 self.color = Some(color); 96 self 97 } 98 } 99 100 impl Set<Label> for Properties { 101 /// Sets the legend label set(&mut self, label: Label) -> &mut Properties102 fn set(&mut self, label: Label) -> &mut Properties { 103 self.label = Some(label.0); 104 self 105 } 106 } 107 108 impl Set<LineType> for Properties { 109 /// Changes the line type 110 /// 111 /// **Note** By default `Solid` lines are used set(&mut self, lt: LineType) -> &mut Properties112 fn set(&mut self, lt: LineType) -> &mut Properties { 113 self.line_type = lt; 114 self 115 } 116 } 117 118 impl Set<LineWidth> for Properties { 119 /// Changes the width of the line 120 /// 121 /// # Panics 122 /// 123 /// Panics if `width` is a non-positive value set(&mut self, lw: LineWidth) -> &mut Properties124 fn set(&mut self, lw: LineWidth) -> &mut Properties { 125 let lw = lw.0; 126 127 assert!(lw > 0.); 128 129 self.linewidth = Some(lw); 130 self 131 } 132 } 133 134 impl Set<PointSize> for Properties { 135 /// Changes the size of the points 136 /// 137 /// # Panics 138 /// 139 /// Panics if `size` is a non-positive value set(&mut self, ps: PointSize) -> &mut Properties140 fn set(&mut self, ps: PointSize) -> &mut Properties { 141 let ps = ps.0; 142 143 assert!(ps > 0.); 144 145 self.point_size = Some(ps); 146 self 147 } 148 } 149 150 impl Set<PointType> for Properties { 151 /// Changes the point type set(&mut self, pt: PointType) -> &mut Properties152 fn set(&mut self, pt: PointType) -> &mut Properties { 153 self.point_type = Some(pt); 154 self 155 } 156 } 157 158 /// Types of "curve" plots 159 pub enum Curve<X, Y> { 160 /// A minimally sized dot on each data point 161 Dots { 162 /// X coordinate of the data points 163 x: X, 164 /// Y coordinate of the data points 165 y: Y, 166 }, 167 /// A vertical "impulse" on each data point 168 Impulses { 169 /// X coordinate of the data points 170 x: X, 171 /// Y coordinate of the data points 172 y: Y, 173 }, 174 /// Line that joins the data points 175 Lines { 176 /// X coordinate of the data points 177 x: X, 178 /// Y coordinate of the data points 179 y: Y, 180 }, 181 /// Line with a point on each data point 182 LinesPoints { 183 /// X coordinate of the data points 184 x: X, 185 /// Y coordinate of the data points 186 y: Y, 187 }, 188 /// A point on each data point 189 Points { 190 /// X coordinate of the data points 191 x: X, 192 /// Y coordinate of the data points 193 y: Y, 194 }, 195 /// An step `_|` between each data point 196 Steps { 197 /// X coordinate of the data points 198 x: X, 199 /// Y coordinate of the data points 200 y: Y, 201 }, 202 } 203 204 impl<X, Y> Curve<X, Y> { style(&self) -> Style205 fn style(&self) -> Style { 206 match *self { 207 Curve::Dots { .. } => Style::Dots, 208 Curve::Impulses { .. } => Style::Impulses, 209 Curve::Lines { .. } => Style::Lines, 210 Curve::LinesPoints { .. } => Style::LinesPoints, 211 Curve::Points { .. } => Style::Points, 212 Curve::Steps { .. } => Style::Steps, 213 } 214 } 215 } 216 217 #[derive(Clone, Copy)] 218 enum Style { 219 Dots, 220 Impulses, 221 Lines, 222 LinesPoints, 223 Points, 224 Steps, 225 } 226 227 impl Display<&'static str> for Style { display(&self) -> &'static str228 fn display(&self) -> &'static str { 229 match *self { 230 Style::Dots => "dots", 231 Style::Impulses => "impulses", 232 Style::Lines => "lines", 233 Style::LinesPoints => "linespoints", 234 Style::Points => "points", 235 Style::Steps => "steps", 236 } 237 } 238 } 239 240 impl<X, Y> traits::Plot<Curve<X, Y>> for Figure 241 where 242 X: IntoIterator, 243 X::Item: Data, 244 Y: IntoIterator, 245 Y::Item: Data, 246 { 247 type Properties = Properties; 248 plot<F>(&mut self, curve: Curve<X, Y>, configure: F) -> &mut Figure where F: FnOnce(&mut Properties) -> &mut Properties,249 fn plot<F>(&mut self, curve: Curve<X, Y>, configure: F) -> &mut Figure 250 where 251 F: FnOnce(&mut Properties) -> &mut Properties, 252 { 253 let style = curve.style(); 254 let (x, y) = match curve { 255 Curve::Dots { x, y } 256 | Curve::Impulses { x, y } 257 | Curve::Lines { x, y } 258 | Curve::LinesPoints { x, y } 259 | Curve::Points { x, y } 260 | Curve::Steps { x, y } => (x, y), 261 }; 262 263 let mut props = CurveDefault::default(style); 264 configure(&mut props); 265 266 let (x_factor, y_factor) = 267 crate::scale_factor(&self.axes, props.axes.unwrap_or(crate::Axes::BottomXLeftY)); 268 269 let data = Matrix::new(izip!(x, y), (x_factor, y_factor)); 270 self.plots.push(Plot::new(data, &props)); 271 self 272 } 273 } 274