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}
202