• 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 {
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