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 n_breaks: usize,
44 time: bool,
47 binned: Option<usize>,
49 bin_edges: Vec<f64>,
52 log_ticks: bool,
54}
55
56impl ScaleContinuous {
57 pub fn new() -> Self {
58 ScaleContinuous {
59 aesthetic: Aesthetic::X,
60 name: String::new(),
61 min: f64::INFINITY,
62 max: f64::NEG_INFINITY,
63 trained: false,
64 filter_oob: false,
65 n_breaks: 5,
66 time: false,
67 binned: None,
68 bin_edges: Vec::new(),
69 log_ticks: false,
70 expand: (0.05, 0.0, 0.05, 0.0),
71 position_opposite: false,
72 scale_transform: ScaleTransform::Identity,
73 custom_breaks: None,
74 custom_labels: None,
75 localized_labels: None,
76 sec_axis: None,
77 label_formatter: None,
78 locale: None,
79 value_formatter: None,
80 }
81 }
82
83 pub fn for_aesthetic(mut self, aes: Aesthetic) -> Self {
84 self.aesthetic = aes;
85 self
86 }
87
88 pub fn with_name(mut self, name: &str) -> Self {
89 self.name = name.to_string();
90 self
91 }
92
93 pub fn with_limits(mut self, min: f64, max: f64) -> Self {
94 self.min = min;
95 self.max = max;
96 self.trained = true;
97 self.filter_oob = true;
98 self
99 }
100
101 pub fn with_transform(mut self, transform: ScaleTransform) -> Self {
102 self.scale_transform = transform;
103 self
104 }
105
106 pub fn with_n_breaks(mut self, n: usize) -> Self {
110 self.n_breaks = n.max(2);
111 self
112 }
113
114 pub fn time() -> Self {
118 let mut s = ScaleContinuous::new().with_label_formatter(super::util::format_hms);
119 s.time = true;
120 s
121 }
122
123 pub fn binned(n: usize) -> Self {
129 let mut s = ScaleContinuous::new();
130 s.binned = Some(n.max(1));
131 s
132 }
133
134 pub fn with_log_ticks(mut self) -> Self {
138 self.log_ticks = true;
139 self
140 }
141
142 pub fn with_breaks(mut self, breaks: Vec<f64>) -> Self {
143 self.custom_breaks = Some(breaks);
144 self
145 }
146
147 pub fn with_labels(mut self, labels: Vec<String>) -> Self {
149 self.custom_labels = Some(labels);
150 self
151 }
152
153 pub fn with_localized_labels<F>(mut self, f: F) -> Self
158 where
159 F: Fn(Option<&crate::locale::Locale>) -> Vec<String> + Send + Sync + 'static,
160 {
161 self.localized_labels = Some(Arc::new(f));
162 self
163 }
164
165 pub fn with_expand(mut self, mult: f64, add: f64) -> Self {
168 self.expand = (mult, add, mult, add);
169 self
170 }
171
172 pub fn with_expand_sides(
175 mut self,
176 mult_lower: f64,
177 add_lower: f64,
178 mult_upper: f64,
179 add_upper: f64,
180 ) -> Self {
181 self.expand = (mult_lower, add_lower, mult_upper, add_upper);
182 self
183 }
184
185 pub fn with_position_opposite(mut self) -> Self {
187 self.position_opposite = true;
188 self
189 }
190
191 pub fn with_label_formatter<F>(mut self, f: F) -> Self
194 where
195 F: Fn(f64) -> String + Send + Sync + 'static,
196 {
197 self.label_formatter = Some(std::sync::Arc::new(f));
198 self
199 }
200
201 pub fn with_value_formatter<F>(mut self, f: F) -> Self
207 where
208 F: Fn(f64) -> String + Send + Sync + 'static,
209 {
210 self.value_formatter = Some(std::sync::Arc::new(f));
211 self
212 }
213
214 pub fn with_locale(mut self, locale: crate::locale::Locale) -> Self {
219 self.locale = Some(locale);
220 self
221 }
222
223 pub fn with_sec_axis(mut self, sec: SecAxis) -> Self {
225 self.sec_axis = Some(sec);
226 self
227 }
228
229 pub fn sec_axis(&self) -> Option<&SecAxis> {
231 self.sec_axis.as_ref()
232 }
233
234 fn format_label(&self, v: f64) -> String {
235 if let Some(f) = &self.label_formatter {
236 f(v)
237 } else {
238 super::util::format_number_opt(v, self.locale.as_ref())
239 }
240 }
241
242 fn expanded_range(&self) -> (f64, f64) {
243 let range = self.max - self.min;
244 let (ml, al, mu, au) = self.expand;
245 (self.min - range * ml - al, self.max + range * mu + au)
246 }
247
248 pub fn is_opposite(&self) -> bool {
250 self.position_opposite
251 }
252}
253
254impl Default for ScaleContinuous {
255 fn default() -> Self {
256 Self::new()
257 }
258}
259
260impl Scale for ScaleContinuous {
261 fn set_default_locale(&mut self, locale: &crate::locale::Locale) {
262 if self.locale.is_none() {
263 self.locale = Some(locale.clone());
264 }
265 }
266
267 fn locale(&self) -> Option<&crate::locale::Locale> {
268 self.locale.as_ref()
269 }
270
271 fn format_value(&self, value: &Value) -> Option<String> {
272 let f = value.as_f64()?;
273 if !f.is_finite() {
274 return None;
275 }
276 let f = self.scale_transform.inverse(f);
283 if !f.is_finite() {
284 return None;
285 }
286 Some(match &self.value_formatter {
287 Some(fmt) => fmt(f),
288 None => self.format_label(f),
289 })
290 }
291
292 fn aesthetic(&self) -> Aesthetic {
293 self.aesthetic.clone()
294 }
295
296 fn train(&mut self, values: &[Value]) {
297 for v in values {
298 if let Some(f) = v.as_f64() {
299 if f.is_finite() {
300 if f < self.min {
301 self.min = f;
302 }
303 if f > self.max {
304 self.max = f;
305 }
306 }
307 }
308 }
309 self.trained = true;
310 }
311
312 fn map(&self, value: &Value) -> f64 {
313 let f = match value.as_f64() {
314 Some(f) => f,
315 None => return 0.0,
316 };
317 if f == f64::INFINITY {
319 return 1.0;
320 }
321 if f == f64::NEG_INFINITY {
322 return 0.0;
323 }
324 if !self.trained || !self.min.is_finite() || !self.max.is_finite() {
327 return 0.5;
328 }
329 let (emin, emax) = self.expanded_range();
330 let range = emax - emin;
331 if range.abs() < f64::EPSILON {
332 0.5
333 } else {
334 (f - emin) / range
335 }
336 }
337
338 fn expanded_domain(&self) -> Option<(f64, f64)> {
339 if !self.trained || !self.min.is_finite() || !self.max.is_finite() {
340 return None;
341 }
342 let (a, b) = self.expanded_range();
343 if !(a.is_finite() && b.is_finite()) {
344 return None;
345 }
346 if (b - a).abs() < f64::EPSILON {
347 return Some((a - 0.5, b + 0.5));
349 }
350 Some((a, b))
351 }
352
353 fn breaks(&self) -> Vec<(f64, String)> {
354 if !self.trained || self.min > self.max {
355 return vec![];
356 }
357
358 if let Some(ref custom) = self.custom_breaks {
360 let localized = self
361 .localized_labels
362 .as_ref()
363 .map(|f| f(self.locale.as_ref()));
364 let custom_labels = localized.as_ref().or(self.custom_labels.as_ref());
365 return custom
366 .iter()
367 .enumerate()
368 .map(|(i, &v)| {
369 let pos = self.map(&Value::Float(v));
370 let label = if let Some(labels) = custom_labels {
371 labels
372 .get(i)
373 .cloned()
374 .unwrap_or_else(|| self.format_label(v))
375 } else {
376 self.format_label(self.scale_transform.inverse(v))
377 };
378 (pos, label)
379 })
380 .collect();
381 }
382
383 if super::util::is_degenerate_range(self.min, self.max) {
384 let label = self.format_label(self.scale_transform.inverse(self.min));
385 return vec![(0.5, label)];
386 }
387
388 let (emin, emax) = self.expanded_range();
392 if !self.bin_edges.is_empty() {
393 let shown: Vec<f64> = self
394 .bin_edges
395 .iter()
396 .map(|&v| self.scale_transform.inverse(v))
397 .collect();
398 let labels: Vec<String> = match &self.label_formatter {
399 Some(f) => shown.iter().map(|&v| f(v)).collect(),
400 None => super::util::format_numbers_opt(&shown, self.locale.as_ref()),
401 };
402 return self
403 .bin_edges
404 .iter()
405 .zip(labels)
406 .map(|(&v, l)| (self.map(&Value::Float(v)), l))
407 .collect();
408 }
409 let tol = (emax - emin).abs() * 1e-9;
410 let breaks: Vec<f64> = if self.scale_transform.uses_data_space_breaks() {
411 let data_min = self.scale_transform.inverse(self.min);
418 let data_max = self.scale_transform.inverse(self.max);
419 if data_min.is_finite() && data_max.is_finite() && data_max > data_min {
420 self.scale_transform
421 .breaks_in_data_space(data_min, data_max, self.n_breaks)
422 .into_iter()
423 .map(|d| self.scale_transform.apply(d))
424 .filter(|&v| v.is_finite() && v >= emin - tol && v <= emax + tol)
425 .collect()
426 } else {
427 vec![]
428 }
429 } else {
430 if self.time {
431 super::util::time_breaks(self.min, self.max, self.n_breaks)
432 } else {
433 extended_breaks(self.min, self.max, self.n_breaks)
434 }
435 .into_iter()
436 .filter(|&v| v >= emin - tol && v <= emax + tol)
437 .collect()
438 };
439 let shown: Vec<f64> = breaks
440 .iter()
441 .map(|&v| self.scale_transform.inverse(v))
442 .collect();
443 let labels: Vec<String> = match &self.label_formatter {
446 Some(f) => shown.iter().map(|&v| f(v)).collect(),
447 None => super::util::format_numbers_opt(&shown, self.locale.as_ref()),
448 };
449 breaks
450 .iter()
451 .zip(labels)
452 .map(|(&v, label)| (self.map(&Value::Float(v)), label))
453 .collect()
454 }
455
456 fn name(&self) -> &str {
457 &self.name
458 }
459
460 fn set_name(&mut self, name: &str) {
461 self.name = name.to_string();
462 }
463
464 fn transform(&self, value: &Value) -> Value {
465 self.scale_transform.transform_value(value)
466 }
467
468 fn set_log_ticks(&mut self, on: bool) {
469 self.log_ticks = on;
470 }
471
472 fn log_ticks(&self) -> Vec<(f64, u8)> {
473 if !self.log_ticks
474 || !self.trained
475 || !matches!(self.scale_transform, ScaleTransform::Log10)
476 {
477 return Vec::new();
478 }
479 let (lo, hi) = self.expanded_range();
480 if !(lo.is_finite() && hi.is_finite()) || hi <= lo || hi - lo > 30.0 {
481 return Vec::new();
482 }
483 let mut out = Vec::new();
484 for k in (lo.floor() as i64)..=(hi.ceil() as i64) {
485 for m in 1..=9u32 {
486 let t = k as f64 + (m as f64).log10();
487 if t >= lo - 1e-9 && t <= hi + 1e-9 {
488 let kind = match m {
489 1 => 0,
490 5 => 1,
491 _ => 2,
492 };
493 out.push((self.map(&Value::Float(t)), kind));
494 }
495 }
496 }
497 out
498 }
499
500 fn bin_positions(&mut self, values: &[Value]) -> Option<Vec<Value>> {
501 let n = self.binned?;
502 if self.bin_edges.is_empty() {
503 let (lo, hi) = match self.filter_limits() {
504 Some(l) => l,
505 None => {
506 let fin = values
507 .iter()
508 .filter_map(Value::as_f64)
509 .filter(|f| f.is_finite());
510 let (lo, hi) = fin.fold((f64::INFINITY, f64::NEG_INFINITY), |(a, b), f| {
511 (a.min(f), b.max(f))
512 });
513 if lo > hi {
514 return None;
515 }
516 (lo, hi)
517 }
518 };
519 let (lo, hi) = if hi > lo {
520 (lo, hi)
521 } else {
522 (lo - 0.5, hi + 0.5)
523 };
524 let mut e = super::util::extended_breaks(lo, hi, n);
525 if e.len() < 2 {
526 e = vec![lo, hi];
527 }
528 let step = e[1] - e[0];
529 while e[0] > lo + step * 1e-9 {
530 e.insert(0, e[0] - step);
531 }
532 while *e.last().unwrap() < hi - step * 1e-9 {
533 e.push(e.last().unwrap() + step);
534 }
535 self.min = self.min.min(e[0]);
537 self.max = self.max.max(*e.last().unwrap());
538 self.trained = true;
539 self.bin_edges = e;
540 }
541 let e = &self.bin_edges;
542 Some(
543 values
544 .iter()
545 .map(|v| match v.as_f64().filter(|f| f.is_finite()) {
546 Some(f) => {
547 let k = e.windows(2).position(|w| f <= w[1]).unwrap_or(e.len() - 2);
549 Value::Float((e[k] + e[k + 1]) / 2.0)
550 }
551 None => v.clone(),
552 })
553 .collect(),
554 )
555 }
556
557 fn sec_axis(&self) -> Option<&SecAxis> {
558 self.sec_axis.as_ref()
559 }
560
561 fn set_limits(&mut self, min: f64, max: f64) {
562 self.min = min;
563 self.max = max;
564 self.trained = true;
565 }
566
567 fn set_expand(&mut self, mult: f64, add: f64) {
568 self.expand = (mult, add, mult, add);
569 }
570
571 fn clear_expand(&mut self, lower: bool, upper: bool) {
572 if lower {
573 (self.expand.0, self.expand.1) = (0.0, 0.0);
574 }
575 if upper {
576 (self.expand.2, self.expand.3) = (0.0, 0.0);
577 }
578 }
579
580 fn filter_limits(&self) -> Option<(f64, f64)> {
581 if self.filter_oob && self.trained {
582 Some((self.min, self.max))
583 } else {
584 None
585 }
586 }
587
588 fn domain(&self) -> Option<(f64, f64)> {
589 if self.trained {
590 Some((self.min, self.max))
591 } else {
592 None
593 }
594 }
595
596 fn axis_position_opposite(&self) -> bool {
597 self.position_opposite
598 }
599
600 fn clone_box(&self) -> Box<dyn Scale> {
601 Box::new(self.clone())
602 }
603
604 fn reset_training(&mut self) {
605 self.min = f64::INFINITY;
606 self.max = f64::NEG_INFINITY;
607 self.trained = false;
608 }
609}