Skip to main content

ggplot_rs/scale/
alpha.rs

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