• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
script(&self) -> String41     fn script(&self) -> String {
42         let mut script = if let Some(axes) = self.axes {
43             format!("axes {} ", axes.display())
44         } else {
45             String::new()
46         };
47 
48         script.push_str(&format!("with {} ", self.style.display()));
49         script.push_str(&format!("lt {} ", self.line_type.display()));
50 
51         if let Some(lw) = self.linewidth {
52             script.push_str(&format!("lw {} ", lw))
53         }
54 
55         if let Some(color) = self.color {
56             script.push_str(&format!("lc rgb '{}' ", color.display()))
57         }
58 
59         if let Some(pt) = self.point_type {
60             script.push_str(&format!("pt {} ", pt.display()))
61         }
62 
63         if let Some(ps) = self.point_size {
64             script.push_str(&format!("ps {} ", ps))
65         }
66 
67         if let Some(ref label) = self.label {
68             script.push_str("title '");
69             script.push_str(label);
70             script.push('\'')
71         } else {
72             script.push_str("notitle")
73         }
74 
75         script
76     }
77 }
78 
79 impl Set<Axes> for Properties {
80     /// Select the axes to plot against
81     ///
82     /// **Note** By default, the `BottomXLeftY` axes are used
set(&mut self, axes: Axes) -> &mut Properties83     fn set(&mut self, axes: Axes) -> &mut Properties {
84         self.axes = Some(axes);
85         self
86     }
87 }
88 
89 impl Set<Color> for Properties {
90     /// Sets the line color
set(&mut self, color: Color) -> &mut Properties91     fn set(&mut self, color: Color) -> &mut Properties {
92         self.color = Some(color);
93         self
94     }
95 }
96 
97 impl Set<Label> for Properties {
98     /// Sets the legend label
set(&mut self, label: Label) -> &mut Properties99     fn set(&mut self, label: Label) -> &mut Properties {
100         self.label = Some(label.0);
101         self
102     }
103 }
104 
105 impl Set<LineType> for Properties {
106     /// Changes the line type
107     ///
108     /// **Note** By default `Solid` lines are used
set(&mut self, lt: LineType) -> &mut Properties109     fn set(&mut self, lt: LineType) -> &mut Properties {
110         self.line_type = lt;
111         self
112     }
113 }
114 
115 impl Set<LineWidth> for Properties {
116     /// Changes the width of the line
117     ///
118     /// # Panics
119     ///
120     /// Panics if `width` is a non-positive value
set(&mut self, lw: LineWidth) -> &mut Properties121     fn set(&mut self, lw: LineWidth) -> &mut Properties {
122         let lw = lw.0;
123 
124         assert!(lw > 0.);
125 
126         self.linewidth = Some(lw);
127         self
128     }
129 }
130 
131 impl Set<PointSize> for Properties {
132     /// Changes the size of the points
133     ///
134     /// # Panics
135     ///
136     /// Panics if `size` is a non-positive value
set(&mut self, ps: PointSize) -> &mut Properties137     fn set(&mut self, ps: PointSize) -> &mut Properties {
138         let ps = ps.0;
139 
140         assert!(ps > 0.);
141 
142         self.point_size = Some(ps);
143         self
144     }
145 }
146 
147 impl Set<PointType> for Properties {
148     /// Changes the point type
set(&mut self, pt: PointType) -> &mut Properties149     fn set(&mut self, pt: PointType) -> &mut Properties {
150         self.point_type = Some(pt);
151         self
152     }
153 }
154 
155 /// Types of "curve" plots
156 pub enum Curve<X, Y> {
157     /// A minimally sized dot on each data point
158     Dots {
159         /// X coordinate of the data points
160         x: X,
161         /// Y coordinate of the data points
162         y: Y,
163     },
164     /// A vertical "impulse" on each data point
165     Impulses {
166         /// X coordinate of the data points
167         x: X,
168         /// Y coordinate of the data points
169         y: Y,
170     },
171     /// Line that joins the data points
172     Lines {
173         /// X coordinate of the data points
174         x: X,
175         /// Y coordinate of the data points
176         y: Y,
177     },
178     /// Line with a point on each data point
179     LinesPoints {
180         /// X coordinate of the data points
181         x: X,
182         /// Y coordinate of the data points
183         y: Y,
184     },
185     /// A point on each data point
186     Points {
187         /// X coordinate of the data points
188         x: X,
189         /// Y coordinate of the data points
190         y: Y,
191     },
192     /// An step `_|` between each data point
193     Steps {
194         /// X coordinate of the data points
195         x: X,
196         /// Y coordinate of the data points
197         y: Y,
198     },
199 }
200 
201 impl<X, Y> Curve<X, Y> {
style(&self) -> Style202     fn style(&self) -> Style {
203         match *self {
204             Curve::Dots { .. } => Style::Dots,
205             Curve::Impulses { .. } => Style::Impulses,
206             Curve::Lines { .. } => Style::Lines,
207             Curve::LinesPoints { .. } => Style::LinesPoints,
208             Curve::Points { .. } => Style::Points,
209             Curve::Steps { .. } => Style::Steps,
210         }
211     }
212 }
213 
214 #[derive(Clone, Copy)]
215 enum Style {
216     Dots,
217     Impulses,
218     Lines,
219     LinesPoints,
220     Points,
221     Steps,
222 }
223 
224 impl Display<&'static str> for Style {
display(&self) -> &'static str225     fn display(&self) -> &'static str {
226         match *self {
227             Style::Dots => "dots",
228             Style::Impulses => "impulses",
229             Style::Lines => "lines",
230             Style::LinesPoints => "linespoints",
231             Style::Points => "points",
232             Style::Steps => "steps",
233         }
234     }
235 }
236 
237 impl<X, Y> traits::Plot<Curve<X, Y>> for Figure
238 where
239     X: IntoIterator,
240     X::Item: Data,
241     Y: IntoIterator,
242     Y::Item: Data,
243 {
244     type Properties = Properties;
245 
plot<F>(&mut self, curve: Curve<X, Y>, configure: F) -> &mut Figure where F: FnOnce(&mut Properties) -> &mut Properties,246     fn plot<F>(&mut self, curve: Curve<X, Y>, configure: F) -> &mut Figure
247     where
248         F: FnOnce(&mut Properties) -> &mut Properties,
249     {
250         let style = curve.style();
251         let (x, y) = match curve {
252             Curve::Dots { x, y }
253             | Curve::Impulses { x, y }
254             | Curve::Lines { x, y }
255             | Curve::LinesPoints { x, y }
256             | Curve::Points { x, y }
257             | Curve::Steps { x, y } => (x, y),
258         };
259 
260         let mut props = CurveDefault::default(style);
261         configure(&mut props);
262 
263         let (x_factor, y_factor) =
264             crate::scale_factor(&self.axes, props.axes.unwrap_or(crate::Axes::BottomXLeftY));
265 
266         let data = Matrix::new(izip!(x, y), (x_factor, y_factor));
267         self.plots.push(Plot::new(data, &props));
268         self
269     }
270 }
271