criterion_plot/
axis.rs

1//! Coordinate axis
2
3use std::borrow::Cow;
4use std::iter::IntoIterator;
5
6use crate::map;
7use crate::traits::{Configure, Data, Set};
8use crate::{
9    grid, Axis, Default, Display, Grid, Label, Range, Scale, ScaleFactor, Script, TicLabels,
10};
11
12/// Properties of the coordinate axes
13#[derive(Clone)]
14pub struct Properties {
15    grids: map::grid::Map<grid::Properties>,
16    hidden: bool,
17    label: Option<Cow<'static, str>>,
18    logarithmic: bool,
19    range: Option<(f64, f64)>,
20    scale_factor: f64,
21    tics: Option<String>,
22}
23
24impl Default for Properties {
25    fn default() -> Properties {
26        Properties {
27            grids: map::grid::Map::new(),
28            hidden: false,
29            label: None,
30            logarithmic: false,
31            range: None,
32            scale_factor: 1.,
33            tics: None,
34        }
35    }
36}
37
38impl Properties {
39    /// Hides the axis
40    ///
41    /// **Note** The `TopX` and `RightY` axes are hidden by default
42    pub fn hide(&mut self) -> &mut Properties {
43        self.hidden = true;
44        self
45    }
46
47    /// Makes the axis visible
48    ///
49    /// **Note** The `BottomX` and `LeftY` axes are visible by default
50    pub fn show(&mut self) -> &mut Properties {
51        self.hidden = false;
52        self
53    }
54}
55
56impl Configure<Grid> for Properties {
57    type Properties = grid::Properties;
58
59    /// Configures the gridlines
60    fn configure<F>(&mut self, grid: Grid, configure: F) -> &mut Properties
61    where
62        F: FnOnce(&mut grid::Properties) -> &mut grid::Properties,
63    {
64        if self.grids.contains_key(grid) {
65            configure(self.grids.get_mut(grid).unwrap());
66        } else {
67            let mut properties = Default::default();
68            configure(&mut properties);
69            self.grids.insert(grid, properties);
70        }
71
72        self
73    }
74}
75
76impl Set<Label> for Properties {
77    /// Attaches a label to the axis
78    fn set(&mut self, label: Label) -> &mut Properties {
79        self.label = Some(label.0);
80        self
81    }
82}
83
84impl Set<Range> for Properties {
85    /// Changes the range of the axis that will be shown
86    ///
87    /// **Note** All axes are auto-scaled by default
88    fn set(&mut self, range: Range) -> &mut Properties {
89        self.hidden = false;
90
91        match range {
92            Range::Auto => self.range = None,
93            Range::Limits(low, high) => self.range = Some((low, high)),
94        }
95
96        self
97    }
98}
99
100impl Set<Scale> for Properties {
101    /// Sets the scale of the axis
102    ///
103    /// **Note** All axes use a linear scale by default
104    fn set(&mut self, scale: Scale) -> &mut Properties {
105        self.hidden = false;
106
107        match scale {
108            Scale::Linear => self.logarithmic = false,
109            Scale::Logarithmic => self.logarithmic = true,
110        }
111
112        self
113    }
114}
115
116impl Set<ScaleFactor> for Properties {
117    /// Changes the *scale factor* of the axis.
118    ///
119    /// All the data plotted against this axis will have its corresponding coordinate scaled with
120    /// this factor before being plotted.
121    ///
122    /// **Note** The default scale factor is `1`.
123    fn set(&mut self, factor: ScaleFactor) -> &mut Properties {
124        self.scale_factor = factor.0;
125
126        self
127    }
128}
129
130impl<P, L> Set<TicLabels<P, L>> for Properties
131where
132    L: IntoIterator,
133    L::Item: AsRef<str>,
134    P: IntoIterator,
135    P::Item: Data,
136{
137    /// Attaches labels to the tics of an axis
138    fn set(&mut self, tics: TicLabels<P, L>) -> &mut Properties {
139        let TicLabels { positions, labels } = tics;
140
141        let pairs = positions
142            .into_iter()
143            .zip(labels.into_iter())
144            .map(|(pos, label)| format!("'{}' {}", label.as_ref(), pos.f64()))
145            .collect::<Vec<_>>();
146
147        if pairs.is_empty() {
148            self.tics = None
149        } else {
150            self.tics = Some(pairs.join(", "));
151        }
152
153        self
154    }
155}
156
157impl<'a> Script for (Axis, &'a Properties) {
158    // Allow clippy::format_push_string even with older versions of rust (<1.62) which
159    // don't have it defined.
160    #[allow(clippy::all)]
161    fn script(&self) -> String {
162        let &(axis, properties) = self;
163        let axis_ = axis.display();
164
165        let mut script = if properties.hidden {
166            return format!("unset {}tics\n", axis_);
167        } else {
168            format!("set {}tics nomirror ", axis_)
169        };
170
171        if let Some(ref tics) = properties.tics {
172            script.push_str(&format!("({})", tics))
173        }
174
175        script.push('\n');
176
177        if let Some(ref label) = properties.label {
178            script.push_str(&format!("set {}label '{}'\n", axis_, label))
179        }
180
181        if let Some((low, high)) = properties.range {
182            script.push_str(&format!("set {}range [{}:{}]\n", axis_, low, high))
183        }
184
185        if properties.logarithmic {
186            script.push_str(&format!("set logscale {}\n", axis_));
187        }
188
189        for (grid, properties) in properties.grids.iter() {
190            script.push_str(&(axis, grid, properties).script());
191        }
192
193        script
194    }
195}
196
197impl crate::ScaleFactorTrait for Properties {
198    fn scale_factor(&self) -> f64 {
199        self.scale_factor
200    }
201}