lc_render/scale/
linear.rs1use crate::math::linear::{interpolate, normalize, range};
2use crate::{Scale, ScaleKind};
3
4const DEFAULT_TICK_COUNT: usize = 11;
5
6#[derive(Clone)]
8pub struct LinearScale {
9 domain_start: f32,
11
12 domain_end: f32,
14
15 range_start: i32,
17
18 range_end: i32,
20
21 tick_count: usize,
23}
24
25impl LinearScale {
26 pub fn new(domain_start: f32, domain_end: f32, range_start: i32, range_end: i32) -> Self {
28 Self {
29 domain_start,
30 domain_end,
31 range_start,
32 range_end,
33 tick_count: DEFAULT_TICK_COUNT,
34 }
35 }
36
37 pub fn range_start(&self) -> i32 {
39 self.range_start
40 }
41
42 pub fn range_end(&self) -> i32 {
44 self.range_end
45 }
46
47 fn compute_tick_step(&self, start: f32, end: f32) -> f32 {
49 let mut step_denominator = 0_f32;
50 if self.tick_count as f32 > step_denominator {
51 step_denominator = self.tick_count as f32;
52 }
53
54 let step = range(start, end) / step_denominator;
55 let power = (step.ln() / 10_f32.ln()).trunc() as i32;
56 let error = step / 10_f32.powi(power);
57
58 let mut dynamic = 1;
59 if error >= 50_f32.sqrt() {
60 dynamic = 10;
61 } else if error >= 10_f32.sqrt() {
62 dynamic = 5;
63 } else if error >= 2_f32.sqrt() {
64 dynamic = 2;
65 };
66
67 if power < 0 {
68 return -(10_f32.powi(-power)) / dynamic as f32;
69 }
70
71 dynamic as f32 * 10_f32.powi(power)
72 }
73
74 fn ticks_positive_step(&self, step: f32) -> Vec<f32> {
76 let mut res = Vec::new();
77
78 let start = (self.domain_start / step).ceil();
79 let end = (self.domain_end / step).floor();
80 let ticks_count = (range(start, end) + 1_f32).ceil() as i32;
81 for i in 0..ticks_count {
82 res.push((start + i as f32) * step);
83 }
84
85 res
86 }
87
88 fn ticks_negative_step(&self, step: f32) -> Vec<f32> {
90 let mut res = Vec::new();
91
92 let start = (self.domain_start as f32 * step).floor();
93 let end = (self.domain_end as f32 * step).ceil();
94 let ticks_count = (range(end, start) + 1_f32).ceil() as i32;
95 for i in 0..ticks_count {
96 res.push((start - i as f32) / step);
97 }
98
99 res
100 }
101}
102
103impl Scale<f32> for LinearScale {
104 fn scale(&self, domain: &f32) -> f32 {
105 let normalized = normalize(self.domain_start, self.domain_end, *domain);
106 interpolate(self.range_start as f32, self.range_end as f32, normalized)
107 }
108
109 fn ticks(&self) -> Vec<f32> {
110 if (self.domain_end - self.domain_start).abs() < f32::EPSILON && self.tick_count > 0 {
111 return vec![self.domain_start as f32];
112 }
113
114 let step = self.compute_tick_step(self.domain_start, self.domain_end);
115 if step > 0_f32 {
116 return self.ticks_positive_step(step);
117 }
118
119 self.ticks_negative_step(step)
120 }
121
122 fn kind(&self) -> ScaleKind {
123 ScaleKind::Linear
124 }
125
126 fn bandwidth(&self) -> f32 {
127 0_f32
128 }
129
130 fn is_range_reversed(&self) -> bool {
131 self.range_start > self.range_end
132 }
133
134 fn tick_offset(&self) -> f32 {
135 0_f32
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn linear_scale_basic() {
145 let linear_scale = LinearScale::new(0_f32, 200_f32, 540, 0);
146
147 assert_eq!(linear_scale.range_start(), 540);
148 assert_eq!(linear_scale.range_end(), 0);
149 assert_eq!(
150 *linear_scale.ticks(),
151 vec![
152 0_f32, 20_f32, 40_f32, 60_f32, 80_f32, 100_f32, 120_f32, 140_f32, 160_f32, 180_f32,
153 200_f32
154 ]
155 );
156 assert!((linear_scale.scale(&24_f32) - 475.2_f32).abs() < f32::EPSILON);
157 assert_eq!(linear_scale.kind(), ScaleKind::Linear);
158 assert!((linear_scale.bandwidth() - 0_f32).abs() < f32::EPSILON);
159 assert!(linear_scale.is_range_reversed());
160 assert!((linear_scale.tick_offset() - 0_f32).abs() < f32::EPSILON);
161 }
162}