criterion_plot/
curve.rs

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}