1//! 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 // Allow clippy::format_push_string even with older versions of rust (<1.62) which
42 // don't have it defined.
43 #[allow(clippy::all)]
44 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
82impl Set<Axes> for Properties {
83 /// Select the axes to plot against
84 ///
85 /// **Note** By default, the `BottomXLeftY` axes are used
86 fn set(&mut self, axes: Axes) -> &mut Properties {
87 self.axes = Some(axes);
88 self
89 }
90}
91
92impl Set<Color> for Properties {
93 /// Sets the line color
94 fn set(&mut self, color: Color) -> &mut Properties {
95 self.color = Some(color);
96 self
97 }
98}
99
100impl Set<Label> for Properties {
101 /// Sets the legend label
102 fn set(&mut self, label: Label) -> &mut Properties {
103 self.label = Some(label.0);
104 self
105 }
106}
107
108impl Set<LineType> for Properties {
109 /// Changes the line type
110 ///
111 /// **Note** By default `Solid` lines are used
112 fn set(&mut self, lt: LineType) -> &mut Properties {
113 self.line_type = lt;
114 self
115 }
116}
117
118impl Set<LineWidth> for Properties {
119 /// Changes the width of the line
120 ///
121 /// # Panics
122 ///
123 /// Panics if `width` is a non-positive value
124 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
134impl Set<PointSize> for Properties {
135 /// Changes the size of the points
136 ///
137 /// # Panics
138 ///
139 /// Panics if `size` is a non-positive value
140 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
150impl Set<PointType> for Properties {
151 /// Changes the point type
152 fn set(&mut self, pt: PointType) -> &mut Properties {
153 self.point_type = Some(pt);
154 self
155 }
156}
157
158/// Types of "curve" plots
159pub 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
204impl<X, Y> Curve<X, Y> {
205 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)]
218enum Style {
219 Dots,
220 Impulses,
221 Lines,
222 LinesPoints,
223 Points,
224 Steps,
225}
226
227impl Display<&'static str> for Style {
228 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
240impl<X, Y> traits::Plot<Curve<X, Y>> for Figure
241where
242 X: IntoIterator,
243 X::Item: Data,
244 Y: IntoIterator,
245 Y::Item: Data,
246{
247 type Properties = Properties;
248
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