Skip to main content

gpui_base/plot/scale/
linear.rs

1// @reference: https://d3js.org/d3-scale/linear
2
3use itertools::Itertools;
4
5use super::{PlotValue, Scale};
6
7#[derive(Clone)]
8pub struct ScaleLinear<T> {
9    domain_start: T,
10    domain_diff: T,
11    range_start: f32,
12    range_diff: f32,
13}
14
15impl<T: PlotValue> ScaleLinear<T> {
16    /// Map the extent of `domain` onto `range`: the smallest domain value to
17    /// `range[0]` and the largest to `range[1]`, so a reversed range such as
18    /// `[height, 0.]` puts larger values higher.
19    pub fn new(domain: impl IntoIterator<Item = T>, range: [f32; 2]) -> Self {
20        let (domain_start, domain_end) = domain
21            .into_iter()
22            .minmax_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
23            .into_option()
24            .unwrap_or((T::zero(), T::zero()));
25
26        Self {
27            domain_start,
28            domain_diff: domain_end - domain_start,
29            range_start: range[0],
30            range_diff: range[1] - range[0],
31        }
32    }
33}
34
35impl<T: PlotValue> Scale<T> for ScaleLinear<T> {
36    fn tick(&self, value: &T) -> Option<f32> {
37        if self.domain_diff.is_zero() {
38            return None;
39        }
40
41        let ratio = ((*value - self.domain_start) / self.domain_diff).to_f32()?;
42
43        Some(ratio * self.range_diff + self.range_start)
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn test_scale_linear() {
53        let scale = ScaleLinear::new(vec![1., 2., 3.], [0., 100.]);
54        assert_eq!(scale.tick(&1.), Some(0.));
55        assert_eq!(scale.tick(&2.), Some(50.));
56        assert_eq!(scale.tick(&3.), Some(100.));
57
58        let scale = ScaleLinear::new(vec![1., 2., 3.], [100., 0.]);
59        assert_eq!(scale.tick(&1.), Some(100.));
60        assert_eq!(scale.tick(&2.), Some(50.));
61        assert_eq!(scale.tick(&3.), Some(0.));
62    }
63
64    #[test]
65    fn test_scale_linear_unordered_domain() {
66        let scale = ScaleLinear::new([3., 1., 2.], [0., 100.]);
67        assert_eq!(scale.tick(&1.), Some(0.));
68        assert_eq!(scale.tick(&3.), Some(100.));
69    }
70
71    #[test]
72    fn test_scale_linear_f32() {
73        let scale = ScaleLinear::new([0f32, 4.], [0., 100.]);
74        assert_eq!(scale.tick(&1f32), Some(25.));
75        assert_eq!(scale.tick(&4f32), Some(100.));
76    }
77
78    #[test]
79    fn test_scale_linear_empty() {
80        let scale = ScaleLinear::<f64>::new(vec![], [0., 100.]);
81        assert_eq!(scale.tick(&1.), None);
82        assert_eq!(scale.tick(&2.), None);
83        assert_eq!(scale.tick(&3.), None);
84
85        let scale = ScaleLinear::new(vec![1., 2., 3.], [0., 0.]);
86        assert_eq!(scale.tick(&1.), Some(0.));
87        assert_eq!(scale.tick(&2.), Some(0.));
88        assert_eq!(scale.tick(&3.), Some(0.));
89    }
90}