1use crate::aes::Aesthetic;
2use crate::data::Value;
3
4use super::format::LabelFormatter;
5use super::sec_axis::SecAxis;
6use super::transform::ScaleTransform;
7use super::util::extended_breaks;
8use super::Scale;
9
10#[derive(Clone)]
12pub struct ScaleContinuous {
13 aesthetic: Aesthetic,
14 name: String,
15 min: f64,
16 max: f64,
17 trained: bool,
18 filter_oob: bool,
19 expand: (f64, f64, f64, f64),
21 position_opposite: bool,
24 pub(crate) scale_transform: ScaleTransform,
25 custom_breaks: Option<Vec<f64>>,
26 custom_labels: Option<Vec<String>>,
27 pub(crate) sec_axis: Option<SecAxis>,
28 label_formatter: Option<LabelFormatter>,
29 locale: Option<crate::locale::Locale>,
31}
32
33impl ScaleContinuous {
34 pub fn new() -> Self {
35 ScaleContinuous {
36 aesthetic: Aesthetic::X,
37 name: String::new(),
38 min: f64::INFINITY,
39 max: f64::NEG_INFINITY,
40 trained: false,
41 filter_oob: false,
42 expand: (0.05, 0.0, 0.05, 0.0),
43 position_opposite: false,
44 scale_transform: ScaleTransform::Identity,
45 custom_breaks: None,
46 custom_labels: None,
47 sec_axis: None,
48 label_formatter: None,
49 locale: None,
50 }
51 }
52
53 pub fn for_aesthetic(mut self, aes: Aesthetic) -> Self {
54 self.aesthetic = aes;
55 self
56 }
57
58 pub fn with_name(mut self, name: &str) -> Self {
59 self.name = name.to_string();
60 self
61 }
62
63 pub fn with_limits(mut self, min: f64, max: f64) -> Self {
64 self.min = min;
65 self.max = max;
66 self.trained = true;
67 self.filter_oob = true;
68 self
69 }
70
71 pub fn with_transform(mut self, transform: ScaleTransform) -> Self {
72 self.scale_transform = transform;
73 self
74 }
75
76 pub fn with_breaks(mut self, breaks: Vec<f64>) -> Self {
78 self.custom_breaks = Some(breaks);
79 self
80 }
81
82 pub fn with_labels(mut self, labels: Vec<String>) -> Self {
84 self.custom_labels = Some(labels);
85 self
86 }
87
88 pub fn with_expand(mut self, mult: f64, add: f64) -> Self {
91 self.expand = (mult, add, mult, add);
92 self
93 }
94
95 pub fn with_expand_sides(
98 mut self,
99 mult_lower: f64,
100 add_lower: f64,
101 mult_upper: f64,
102 add_upper: f64,
103 ) -> Self {
104 self.expand = (mult_lower, add_lower, mult_upper, add_upper);
105 self
106 }
107
108 pub fn with_position_opposite(mut self) -> Self {
110 self.position_opposite = true;
111 self
112 }
113
114 pub fn with_label_formatter<F>(mut self, f: F) -> Self
117 where
118 F: Fn(f64) -> String + Send + Sync + 'static,
119 {
120 self.label_formatter = Some(std::sync::Arc::new(f));
121 self
122 }
123
124 pub fn with_locale(mut self, locale: crate::locale::Locale) -> Self {
129 self.locale = Some(locale);
130 self
131 }
132
133 pub fn with_sec_axis(mut self, sec: SecAxis) -> Self {
135 self.sec_axis = Some(sec);
136 self
137 }
138
139 pub fn sec_axis(&self) -> Option<&SecAxis> {
141 self.sec_axis.as_ref()
142 }
143
144 fn format_label(&self, v: f64) -> String {
145 if let Some(f) = &self.label_formatter {
146 f(v)
147 } else {
148 super::util::format_number_opt(v, self.locale.as_ref())
149 }
150 }
151
152 fn expanded_range(&self) -> (f64, f64) {
153 let range = self.max - self.min;
154 let (ml, al, mu, au) = self.expand;
155 (self.min - range * ml - al, self.max + range * mu + au)
156 }
157
158 pub fn is_opposite(&self) -> bool {
160 self.position_opposite
161 }
162}
163
164impl Default for ScaleContinuous {
165 fn default() -> Self {
166 Self::new()
167 }
168}
169
170impl Scale for ScaleContinuous {
171 fn set_default_locale(&mut self, locale: &crate::locale::Locale) {
172 if self.locale.is_none() {
173 self.locale = Some(locale.clone());
174 }
175 }
176
177 fn locale(&self) -> Option<&crate::locale::Locale> {
178 self.locale.as_ref()
179 }
180
181 fn aesthetic(&self) -> Aesthetic {
182 self.aesthetic.clone()
183 }
184
185 fn train(&mut self, values: &[Value]) {
186 for v in values {
187 if let Some(f) = v.as_f64() {
188 if f.is_finite() {
189 if f < self.min {
190 self.min = f;
191 }
192 if f > self.max {
193 self.max = f;
194 }
195 }
196 }
197 }
198 self.trained = true;
199 }
200
201 fn map(&self, value: &Value) -> f64 {
202 let f = match value.as_f64() {
203 Some(f) => f,
204 None => return 0.0,
205 };
206 if f == f64::INFINITY {
208 return 1.0;
209 }
210 if f == f64::NEG_INFINITY {
211 return 0.0;
212 }
213 if !self.trained || !self.min.is_finite() || !self.max.is_finite() {
216 return 0.5;
217 }
218 let (emin, emax) = self.expanded_range();
219 let range = emax - emin;
220 if range.abs() < f64::EPSILON {
221 0.5
222 } else {
223 (f - emin) / range
224 }
225 }
226
227 fn expanded_domain(&self) -> Option<(f64, f64)> {
228 if !self.trained || !self.min.is_finite() || !self.max.is_finite() {
229 return None;
230 }
231 let (a, b) = self.expanded_range();
232 if !(a.is_finite() && b.is_finite()) {
233 return None;
234 }
235 if (b - a).abs() < f64::EPSILON {
236 return Some((a - 0.5, b + 0.5));
238 }
239 Some((a, b))
240 }
241
242 fn breaks(&self) -> Vec<(f64, String)> {
243 if !self.trained || self.min > self.max {
244 return vec![];
245 }
246
247 if let Some(ref custom) = self.custom_breaks {
249 return custom
250 .iter()
251 .enumerate()
252 .map(|(i, &v)| {
253 let pos = self.map(&Value::Float(v));
254 let label = if let Some(ref labels) = self.custom_labels {
255 labels
256 .get(i)
257 .cloned()
258 .unwrap_or_else(|| self.format_label(v))
259 } else {
260 self.format_label(self.scale_transform.inverse(v))
261 };
262 (pos, label)
263 })
264 .collect();
265 }
266
267 if super::util::is_degenerate_range(self.min, self.max) {
268 let label = self.format_label(self.scale_transform.inverse(self.min));
269 return vec![(0.5, label)];
270 }
271
272 let (emin, emax) = self.expanded_range();
276 let tol = (emax - emin).abs() * 1e-9;
277 let breaks: Vec<f64> = extended_breaks(self.min, self.max, 5)
278 .into_iter()
279 .filter(|&v| v >= emin - tol && v <= emax + tol)
280 .collect();
281 let shown: Vec<f64> = breaks
282 .iter()
283 .map(|&v| self.scale_transform.inverse(v))
284 .collect();
285 let labels: Vec<String> = match &self.label_formatter {
288 Some(f) => shown.iter().map(|&v| f(v)).collect(),
289 None => super::util::format_numbers_opt(&shown, self.locale.as_ref()),
290 };
291 breaks
292 .iter()
293 .zip(labels)
294 .map(|(&v, label)| (self.map(&Value::Float(v)), label))
295 .collect()
296 }
297
298 fn name(&self) -> &str {
299 &self.name
300 }
301
302 fn set_name(&mut self, name: &str) {
303 self.name = name.to_string();
304 }
305
306 fn transform(&self, value: &Value) -> Value {
307 self.scale_transform.transform_value(value)
308 }
309
310 fn sec_axis(&self) -> Option<&SecAxis> {
311 self.sec_axis.as_ref()
312 }
313
314 fn set_limits(&mut self, min: f64, max: f64) {
315 self.min = min;
316 self.max = max;
317 self.trained = true;
318 }
319
320 fn filter_limits(&self) -> Option<(f64, f64)> {
321 if self.filter_oob && self.trained {
322 Some((self.min, self.max))
323 } else {
324 None
325 }
326 }
327
328 fn domain(&self) -> Option<(f64, f64)> {
329 if self.trained {
330 Some((self.min, self.max))
331 } else {
332 None
333 }
334 }
335
336 fn axis_position_opposite(&self) -> bool {
337 self.position_opposite
338 }
339
340 fn clone_box(&self) -> Box<dyn Scale> {
341 Box::new(self.clone())
342 }
343
344 fn reset_training(&mut self) {
345 self.min = f64::INFINITY;
346 self.max = f64::NEG_INFINITY;
347 self.trained = false;
348 }
349}