1 | //! Coordinate axis |
2 | |
3 | use std::borrow::Cow; |
4 | use std::iter::IntoIterator; |
5 | |
6 | use crate::map; |
7 | use crate::traits::{Configure, Data, Set}; |
8 | use crate::{ |
9 | grid, Axis, Default, Display, Grid, Label, Range, Scale, ScaleFactor, Script, TicLabels, |
10 | }; |
11 | |
12 | /// Properties of the coordinate axes |
13 | #[derive(Clone)] |
14 | pub 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 | |
24 | impl 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 | |
38 | impl 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 | |
56 | impl 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 | |
76 | impl 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 | |
84 | impl 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 | |
100 | impl 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 | |
116 | impl 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 | |
130 | impl<P, L> Set<TicLabels<P, L>> for Properties |
131 | where |
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 | |
157 | impl<'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 | |
197 | impl crate::ScaleFactorTrait for Properties { |
198 | fn scale_factor(&self) -> f64 { |
199 | self.scale_factor |
200 | } |
201 | } |
202 | |