1use std::sync::Arc;
2
3use crate::aes::Aesthetic;
4use crate::data::Value;
5
6use super::format::LabelFormatter;
7use super::sec_axis::SecAxis;
8use super::transform::ScaleTransform;
9use super::util::extended_breaks;
10use super::Scale;
11
12pub type LocalizedLabels = Arc<dyn Fn(Option<&crate::locale::Locale>) -> Vec<String> + Send + Sync>;
14
15#[derive(Clone)]
17pub struct ScaleContinuous {
18 aesthetic: Aesthetic,
19 name: String,
20 min: f64,
21 max: f64,
22 trained: bool,
23 filter_oob: bool,
24 expand: (f64, f64, f64, f64),
26 position_opposite: bool,
29 pub(crate) scale_transform: ScaleTransform,
30 custom_breaks: Option<Vec<f64>>,
31 custom_labels: Option<Vec<String>>,
32 localized_labels: Option<LocalizedLabels>,
34 pub(crate) sec_axis: Option<SecAxis>,
35 label_formatter: Option<LabelFormatter>,
36 locale: Option<crate::locale::Locale>,
38 value_formatter: Option<LabelFormatter>,
42}
43
44impl ScaleContinuous {
45 pub fn new() -> Self {
46 ScaleContinuous {
47 aesthetic: Aesthetic::X,
48 name: String::new(),
49 min: f64::INFINITY,
50 max: f64::NEG_INFINITY,
51 trained: false,
52 filter_oob: false,
53 expand: (0.05, 0.0, 0.05, 0.0),
54 position_opposite: false,
55 scale_transform: ScaleTransform::Identity,
56 custom_breaks: None,
57 custom_labels: None,
58 localized_labels: None,
59 sec_axis: None,
60 label_formatter: None,
61 locale: None,
62 value_formatter: None,
63 }
64 }
65
66 pub fn for_aesthetic(mut self, aes: Aesthetic) -> Self {
67 self.aesthetic = aes;
68 self
69 }
70
71 pub fn with_name(mut self, name: &str) -> Self {
72 self.name = name.to_string();
73 self
74 }
75
76 pub fn with_limits(mut self, min: f64, max: f64) -> Self {
77 self.min = min;
78 self.max = max;
79 self.trained = true;
80 self.filter_oob = true;
81 self
82 }
83
84 pub fn with_transform(mut self, transform: ScaleTransform) -> Self {
85 self.scale_transform = transform;
86 self
87 }
88
89 pub fn with_breaks(mut self, breaks: Vec<f64>) -> Self {
91 self.custom_breaks = Some(breaks);
92 self
93 }
94
95 pub fn with_labels(mut self, labels: Vec<String>) -> Self {
97 self.custom_labels = Some(labels);
98 self
99 }
100
101 pub fn with_localized_labels<F>(mut self, f: F) -> Self
106 where
107 F: Fn(Option<&crate::locale::Locale>) -> Vec<String> + Send + Sync + 'static,
108 {
109 self.localized_labels = Some(Arc::new(f));
110 self
111 }
112
113 pub fn with_expand(mut self, mult: f64, add: f64) -> Self {
116 self.expand = (mult, add, mult, add);
117 self
118 }
119
120 pub fn with_expand_sides(
123 mut self,
124 mult_lower: f64,
125 add_lower: f64,
126 mult_upper: f64,
127 add_upper: f64,
128 ) -> Self {
129 self.expand = (mult_lower, add_lower, mult_upper, add_upper);
130 self
131 }
132
133 pub fn with_position_opposite(mut self) -> Self {
135 self.position_opposite = true;
136 self
137 }
138
139 pub fn with_label_formatter<F>(mut self, f: F) -> Self
142 where
143 F: Fn(f64) -> String + Send + Sync + 'static,
144 {
145 self.label_formatter = Some(std::sync::Arc::new(f));
146 self
147 }
148
149 pub fn with_value_formatter<F>(mut self, f: F) -> Self
155 where
156 F: Fn(f64) -> String + Send + Sync + 'static,
157 {
158 self.value_formatter = Some(std::sync::Arc::new(f));
159 self
160 }
161
162 pub fn with_locale(mut self, locale: crate::locale::Locale) -> Self {
167 self.locale = Some(locale);
168 self
169 }
170
171 pub fn with_sec_axis(mut self, sec: SecAxis) -> Self {
173 self.sec_axis = Some(sec);
174 self
175 }
176
177 pub fn sec_axis(&self) -> Option<&SecAxis> {
179 self.sec_axis.as_ref()
180 }
181
182 fn format_label(&self, v: f64) -> String {
183 if let Some(f) = &self.label_formatter {
184 f(v)
185 } else {
186 super::util::format_number_opt(v, self.locale.as_ref())
187 }
188 }
189
190 fn expanded_range(&self) -> (f64, f64) {
191 let range = self.max - self.min;
192 let (ml, al, mu, au) = self.expand;
193 (self.min - range * ml - al, self.max + range * mu + au)
194 }
195
196 pub fn is_opposite(&self) -> bool {
198 self.position_opposite
199 }
200}
201
202impl Default for ScaleContinuous {
203 fn default() -> Self {
204 Self::new()
205 }
206}
207
208impl Scale for ScaleContinuous {
209 fn set_default_locale(&mut self, locale: &crate::locale::Locale) {
210 if self.locale.is_none() {
211 self.locale = Some(locale.clone());
212 }
213 }
214
215 fn locale(&self) -> Option<&crate::locale::Locale> {
216 self.locale.as_ref()
217 }
218
219 fn format_value(&self, value: &Value) -> Option<String> {
220 let f = value.as_f64()?;
221 if !f.is_finite() {
222 return None;
223 }
224 let f = self.scale_transform.inverse(f);
231 if !f.is_finite() {
232 return None;
233 }
234 Some(match &self.value_formatter {
235 Some(fmt) => fmt(f),
236 None => self.format_label(f),
237 })
238 }
239
240 fn aesthetic(&self) -> Aesthetic {
241 self.aesthetic.clone()
242 }
243
244 fn train(&mut self, values: &[Value]) {
245 for v in values {
246 if let Some(f) = v.as_f64() {
247 if f.is_finite() {
248 if f < self.min {
249 self.min = f;
250 }
251 if f > self.max {
252 self.max = f;
253 }
254 }
255 }
256 }
257 self.trained = true;
258 }
259
260 fn map(&self, value: &Value) -> f64 {
261 let f = match value.as_f64() {
262 Some(f) => f,
263 None => return 0.0,
264 };
265 if f == f64::INFINITY {
267 return 1.0;
268 }
269 if f == f64::NEG_INFINITY {
270 return 0.0;
271 }
272 if !self.trained || !self.min.is_finite() || !self.max.is_finite() {
275 return 0.5;
276 }
277 let (emin, emax) = self.expanded_range();
278 let range = emax - emin;
279 if range.abs() < f64::EPSILON {
280 0.5
281 } else {
282 (f - emin) / range
283 }
284 }
285
286 fn expanded_domain(&self) -> Option<(f64, f64)> {
287 if !self.trained || !self.min.is_finite() || !self.max.is_finite() {
288 return None;
289 }
290 let (a, b) = self.expanded_range();
291 if !(a.is_finite() && b.is_finite()) {
292 return None;
293 }
294 if (b - a).abs() < f64::EPSILON {
295 return Some((a - 0.5, b + 0.5));
297 }
298 Some((a, b))
299 }
300
301 fn breaks(&self) -> Vec<(f64, String)> {
302 if !self.trained || self.min > self.max {
303 return vec![];
304 }
305
306 if let Some(ref custom) = self.custom_breaks {
308 let localized = self
309 .localized_labels
310 .as_ref()
311 .map(|f| f(self.locale.as_ref()));
312 let custom_labels = localized.as_ref().or(self.custom_labels.as_ref());
313 return custom
314 .iter()
315 .enumerate()
316 .map(|(i, &v)| {
317 let pos = self.map(&Value::Float(v));
318 let label = if let Some(labels) = custom_labels {
319 labels
320 .get(i)
321 .cloned()
322 .unwrap_or_else(|| self.format_label(v))
323 } else {
324 self.format_label(self.scale_transform.inverse(v))
325 };
326 (pos, label)
327 })
328 .collect();
329 }
330
331 if super::util::is_degenerate_range(self.min, self.max) {
332 let label = self.format_label(self.scale_transform.inverse(self.min));
333 return vec![(0.5, label)];
334 }
335
336 let (emin, emax) = self.expanded_range();
340 let tol = (emax - emin).abs() * 1e-9;
341 let breaks: Vec<f64> = if self.scale_transform.uses_data_space_breaks() {
342 let data_min = self.scale_transform.inverse(self.min);
349 let data_max = self.scale_transform.inverse(self.max);
350 if data_min.is_finite() && data_max.is_finite() && data_max > data_min {
351 self.scale_transform
352 .breaks_in_data_space(data_min, data_max, 5)
353 .into_iter()
354 .map(|d| self.scale_transform.apply(d))
355 .filter(|&v| v.is_finite() && v >= emin - tol && v <= emax + tol)
356 .collect()
357 } else {
358 vec![]
359 }
360 } else {
361 extended_breaks(self.min, self.max, 5)
362 .into_iter()
363 .filter(|&v| v >= emin - tol && v <= emax + tol)
364 .collect()
365 };
366 let shown: Vec<f64> = breaks
367 .iter()
368 .map(|&v| self.scale_transform.inverse(v))
369 .collect();
370 let labels: Vec<String> = match &self.label_formatter {
373 Some(f) => shown.iter().map(|&v| f(v)).collect(),
374 None => super::util::format_numbers_opt(&shown, self.locale.as_ref()),
375 };
376 breaks
377 .iter()
378 .zip(labels)
379 .map(|(&v, label)| (self.map(&Value::Float(v)), label))
380 .collect()
381 }
382
383 fn name(&self) -> &str {
384 &self.name
385 }
386
387 fn set_name(&mut self, name: &str) {
388 self.name = name.to_string();
389 }
390
391 fn transform(&self, value: &Value) -> Value {
392 self.scale_transform.transform_value(value)
393 }
394
395 fn sec_axis(&self) -> Option<&SecAxis> {
396 self.sec_axis.as_ref()
397 }
398
399 fn set_limits(&mut self, min: f64, max: f64) {
400 self.min = min;
401 self.max = max;
402 self.trained = true;
403 }
404
405 fn set_expand(&mut self, mult: f64, add: f64) {
406 self.expand = (mult, add, mult, add);
407 }
408
409 fn filter_limits(&self) -> Option<(f64, f64)> {
410 if self.filter_oob && self.trained {
411 Some((self.min, self.max))
412 } else {
413 None
414 }
415 }
416
417 fn domain(&self) -> Option<(f64, f64)> {
418 if self.trained {
419 Some((self.min, self.max))
420 } else {
421 None
422 }
423 }
424
425 fn axis_position_opposite(&self) -> bool {
426 self.position_opposite
427 }
428
429 fn clone_box(&self) -> Box<dyn Scale> {
430 Box::new(self.clone())
431 }
432
433 fn reset_training(&mut self) {
434 self.min = f64::INFINITY;
435 self.max = f64::NEG_INFINITY;
436 self.trained = false;
437 }
438}