blob: bbddeff1a7b166a2fefb58a71fbc8d7c3652d873 [file] [log] [blame]
Jakub Kotur835fea42020-12-21 17:28:14 +01001//! Simple "curve" like plots
2
3use std::borrow::Cow;
4use std::iter::IntoIterator;
5
6use crate::data::Matrix;
7use crate::traits::{self, Data, Set};
8use 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
14pub 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
25impl CurveDefault<Style> for Properties {
26 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
40impl Script for Properties {
41 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
79impl Set<Axes> for Properties {
80 /// Select the axes to plot against
81 ///
82 /// **Note** By default, the `BottomXLeftY` axes are used
83 fn set(&mut self, axes: Axes) -> &mut Properties {
84 self.axes = Some(axes);
85 self
86 }
87}
88
89impl Set<Color> for Properties {
90 /// Sets the line color
91 fn set(&mut self, color: Color) -> &mut Properties {
92 self.color = Some(color);
93 self
94 }
95}
96
97impl Set<Label> for Properties {
98 /// Sets the legend label
99 fn set(&mut self, label: Label) -> &mut Properties {
100 self.label = Some(label.0);
101 self
102 }
103}
104
105impl Set<LineType> for Properties {
106 /// Changes the line type
107 ///
108 /// **Note** By default `Solid` lines are used
109 fn set(&mut self, lt: LineType) -> &mut Properties {
110 self.line_type = lt;
111 self
112 }
113}
114
115impl Set<LineWidth> for Properties {
116 /// Changes the width of the line
117 ///
118 /// # Panics
119 ///
120 /// Panics if `width` is a non-positive value
121 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
131impl Set<PointSize> for Properties {
132 /// Changes the size of the points
133 ///
134 /// # Panics
135 ///
136 /// Panics if `size` is a non-positive value
137 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
147impl Set<PointType> for Properties {
148 /// Changes the point type
149 fn set(&mut self, pt: PointType) -> &mut Properties {
150 self.point_type = Some(pt);
151 self
152 }
153}
154
155/// Types of "curve" plots
156pub 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
201impl<X, Y> Curve<X, Y> {
202 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)]
215enum Style {
216 Dots,
217 Impulses,
218 Lines,
219 LinesPoints,
220 Points,
221 Steps,
222}
223
224impl Display<&'static str> for Style {
225 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
237impl<X, Y> traits::Plot<Curve<X, Y>> for Figure
238where
239 X: IntoIterator,
240 X::Item: Data,
241 Y: IntoIterator,
242 Y::Item: Data,
243{
244 type Properties = Properties;
245
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}