1use crate::aes::Aesthetic;
2use crate::data::Value;
3
4use super::util::format_number;
5use super::Scale;
6
7#[derive(Clone, Debug)]
9pub struct ScaleAlphaContinuous {
10 name: String,
11 min: f64,
12 max: f64,
13 trained: bool,
14 range: (f64, f64),
16}
17
18impl ScaleAlphaContinuous {
19 pub fn new() -> Self {
20 ScaleAlphaContinuous {
21 name: String::new(),
22 min: f64::INFINITY,
23 max: f64::NEG_INFINITY,
24 trained: false,
25 range: (0.1, 1.0),
26 }
27 }
28
29 pub fn with_range(mut self, min: f64, max: f64) -> Self {
30 self.range = (min, max);
31 self
32 }
33
34 pub fn with_name(mut self, name: &str) -> Self {
35 self.name = name.to_string();
36 self
37 }
38}
39
40impl Default for ScaleAlphaContinuous {
41 fn default() -> Self {
42 Self::new()
43 }
44}
45
46impl Scale for ScaleAlphaContinuous {
47 fn aesthetic(&self) -> Aesthetic {
48 Aesthetic::Alpha
49 }
50
51 fn train(&mut self, values: &[Value]) {
52 for v in values {
53 if let Some(f) = v.as_f64() {
54 if f.is_finite() {
55 if f < self.min {
56 self.min = f;
57 }
58 if f > self.max {
59 self.max = f;
60 }
61 }
62 }
63 }
64 self.trained = true;
65 }
66
67 fn map(&self, value: &Value) -> f64 {
68 let f = match value.as_f64() {
69 Some(f) => f,
70 None => return 0.0,
71 };
72 let range = self.max - self.min;
73 if range.abs() < f64::EPSILON {
74 0.5
75 } else {
76 ((f - self.min) / range).clamp(0.0, 1.0)
77 }
78 }
79
80 fn breaks(&self) -> Vec<(f64, String)> {
81 if !self.trained || self.min > self.max {
82 return vec![];
83 }
84 let range = self.max - self.min;
85 if super::util::is_degenerate_range(self.min, self.max) {
86 return vec![(0.5, format_number(self.min))];
87 }
88 let n_breaks = 4;
89 let step = super::util::nice_step(range / n_breaks as f64);
90 let start = (self.min / step).ceil() * step;
91 super::util::stepped_breaks(start, self.max, step)
92 .into_iter()
93 .map(|v| (self.map(&Value::Float(v)), format_number(v)))
94 .collect()
95 }
96
97 fn name(&self) -> &str {
98 &self.name
99 }
100
101 fn set_name(&mut self, name: &str) {
102 self.name = name.to_string();
103 }
104
105 fn map_to_alpha(&self, value: &Value) -> Option<f64> {
106 let t = self.map(value);
107 let (lo, hi) = self.range;
108 Some(lo + t * (hi - lo))
109 }
110
111 fn clone_box(&self) -> Box<dyn Scale> {
112 Box::new(self.clone())
113 }
114
115 fn reset_training(&mut self) {
116 self.min = f64::INFINITY;
117 self.max = f64::NEG_INFINITY;
118 self.trained = false;
119 }
120}