Skip to main content

ggplot_rs/scale/
transform.rs

1use crate::data::Value;
2
3/// Scale transformation types.
4#[derive(Clone, Debug)]
5pub enum ScaleTransform {
6    Identity,
7    Log10,
8    Log2,
9    Ln,
10    Sqrt,
11    Reverse,
12}
13
14impl ScaleTransform {
15    /// Apply the forward transformation.
16    pub fn apply(&self, value: f64) -> f64 {
17        match self {
18            ScaleTransform::Identity => value,
19            ScaleTransform::Log10 => {
20                if value > 0.0 {
21                    value.log10()
22                } else {
23                    f64::NEG_INFINITY
24                }
25            }
26            ScaleTransform::Log2 => {
27                if value > 0.0 {
28                    value.log2()
29                } else {
30                    f64::NEG_INFINITY
31                }
32            }
33            ScaleTransform::Ln => {
34                if value > 0.0 {
35                    value.ln()
36                } else {
37                    f64::NEG_INFINITY
38                }
39            }
40            ScaleTransform::Sqrt => {
41                if value >= 0.0 {
42                    value.sqrt()
43                } else {
44                    f64::NAN
45                }
46            }
47            ScaleTransform::Reverse => -value,
48        }
49    }
50
51    /// Apply the inverse transformation.
52    pub fn inverse(&self, value: f64) -> f64 {
53        match self {
54            ScaleTransform::Identity => value,
55            ScaleTransform::Log10 => 10f64.powf(value),
56            ScaleTransform::Log2 => 2f64.powf(value),
57            ScaleTransform::Ln => value.exp(),
58            ScaleTransform::Sqrt => value * value,
59            ScaleTransform::Reverse => -value,
60        }
61    }
62
63    /// Transform a Value.
64    pub fn transform_value(&self, value: &Value) -> Value {
65        match value.as_f64() {
66            Some(f) => {
67                let t = self.apply(f);
68                if t.is_finite() {
69                    Value::Float(t)
70                } else {
71                    Value::Na
72                }
73            }
74            None => value.clone(),
75        }
76    }
77
78    pub fn is_identity(&self) -> bool {
79        matches!(self, ScaleTransform::Identity)
80    }
81}