gpui_component/plot/scale/
point.rs1use itertools::Itertools;
4use num_traits::Zero;
5
6use super::Scale;
7
8#[derive(Clone)]
9pub struct ScalePoint<T> {
10 domain: Vec<T>,
11 range_tick: f32,
12}
13
14impl<T> ScalePoint<T>
15where
16 T: PartialEq,
17{
18 pub fn new(domain: Vec<T>, range: Vec<f32>) -> Self {
19 let len = domain.len();
20 let range_tick = if len.is_zero() {
21 0.
22 } else {
23 let range_diff = range
24 .iter()
25 .minmax()
26 .into_option()
27 .map_or(0., |(min, max)| max - min);
28
29 if len == 1 {
30 range_diff
31 } else {
32 range_diff / len.saturating_sub(1) as f32
33 }
34 };
35
36 Self { domain, range_tick }
37 }
38}
39
40impl<T> Scale<T> for ScalePoint<T>
41where
42 T: PartialEq,
43{
44 fn tick(&self, value: &T) -> Option<f32> {
45 if self.domain.len() == 1 {
46 Some(self.range_tick / 2.)
47 } else {
48 let index = self.domain.iter().position(|v| v == value)?;
49 Some(index as f32 * self.range_tick)
50 }
51 }
52
53 fn least_index(&self, tick: f32) -> usize {
54 if self.domain.is_empty() {
55 return 0;
56 }
57
58 let index = (tick / self.range_tick).round() as usize;
59 index.min(self.domain.len().saturating_sub(1))
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn test_scale_point() {
69 let scale = ScalePoint::new(vec![1, 2, 3], vec![0., 100.]);
70 assert_eq!(scale.tick(&1), Some(0.));
71 assert_eq!(scale.tick(&2), Some(50.));
72 assert_eq!(scale.tick(&3), Some(100.));
73 }
74
75 #[test]
76 fn test_scale_point_empty() {
77 let scale = ScalePoint::new(vec![], vec![0., 100.]);
78 assert_eq!(scale.tick(&1), None);
79 assert_eq!(scale.tick(&2), None);
80 assert_eq!(scale.tick(&3), None);
81
82 let scale = ScalePoint::new(vec![1, 2, 3], vec![]);
83 assert_eq!(scale.tick(&1), Some(0.));
84 assert_eq!(scale.tick(&2), Some(0.));
85 assert_eq!(scale.tick(&3), Some(0.));
86 }
87
88 #[test]
89 fn test_scale_point_single() {
90 let scale = ScalePoint::new(vec![1], vec![0., 100.]);
91 assert_eq!(scale.tick(&1), Some(50.));
92 }
93}