rate_ui/packages/masterpiece/
scale.rs1use derive_more::{From, Into};
2use std::f64::consts;
3
4#[derive(Debug, Clone, PartialEq)]
5pub struct Range {
6 start: f64,
7 stop: f64,
8 min: f64,
9 max: f64,
10 diff: f64,
11 reverse: bool,
12}
13
14impl Range {
15 pub fn new(start: f64, stop: f64) -> Self {
16 if start <= stop {
17 Self {
18 start,
19 stop,
20 min: start,
21 max: stop,
22 diff: stop - start,
23 reverse: false,
24 }
25 } else {
26 Self {
27 start,
28 stop,
29 min: stop,
30 max: start,
31 diff: start - stop,
32 reverse: true,
33 }
34 }
35 }
36
37 pub fn min(&self) -> f64 {
38 self.min
39 }
40
41 pub fn max(&self) -> f64 {
42 self.max
43 }
44
45 pub fn with_padding(&mut self, mut pad: f64) {
64 if self.reverse {
65 pad = -pad;
66 }
67 *self = Self::new(self.start + pad, self.stop - pad);
68 }
69
70 pub fn spread(&mut self, mut spread: f64) {
73 if self.reverse {
74 spread = -spread;
75 }
76 *self = Self::new(self.start * (1.0 - spread), self.stop * (1.0 + spread));
77 }
78
79 pub fn is_flat(&self) -> bool {
80 self.diff == 0.0
81 }
82}
83
84impl From<(f64, f64)> for Range {
85 fn from((min, max): (f64, f64)) -> Self {
86 Self::new(min, max)
87 }
88}
89
90pub struct LinearScale {
91 domain: Range,
92 range: Range,
93}
94
95impl LinearScale {
96 pub fn new(domain: Range, range: Range) -> Self {
97 Self { domain, range }
98 }
99
100 pub fn rescale(&self, value: f64) -> f64 {
101 self.range.interpolate(self.domain.normalize(value))
102 }
103}
104
105impl Range {
106 fn tick_increment(&self, count: f64) -> f64 {
107 let start = self.min;
108 let stop = self.max;
109
110 let e10 = 50_f64.sqrt();
111 let e5 = 10_f64.sqrt();
112 let e2 = 2_f64.sqrt();
113
114 let step = (stop - start) / count.max(0.0);
115 let power = (step.ln() / consts::LN_10).floor();
116 let error = step / 10_f64.powf(power);
117
118 let factor;
119 if error >= e10 {
120 factor = 10.0;
121 } else if error >= e5 {
122 factor = 5.0;
123 } else if error >= e2 {
124 factor = 2.0;
125 } else {
126 factor = 1.0;
127 }
128
129 if power >= 0.0 {
130 factor * 10_f64.powf(power)
131 } else {
132 -(10_f64.powf(-power)) / factor
133 }
134 }
135
136 pub fn ticks(&self, count: u16) -> Vec<f64> {
137 let start = self.min;
138 let stop = self.max;
139 let mut ticks = Vec::with_capacity(count as usize);
140 if self.diff == 0.0 && count > 0 {
141 ticks.push(start);
142 } else {
143 let step = self.tick_increment(count as f64);
144 if step.is_finite() {
145 if step > 0.0 {
146 let mut r_start = (start / step).round();
147 let mut r_stop = (stop / step).round();
148 if r_start * step < start {
149 r_start += 1.0;
150 }
151 if r_stop * step > stop {
152 r_stop -= 1.0;
153 }
154 let n = (r_stop - r_start + 1.0) as usize;
155 let iter = (0..n).map(|i| (r_start + i as f64) * step);
156 if !self.reverse {
157 ticks.extend(iter);
158 } else {
159 ticks.extend(iter.rev());
160 }
161 } else if step < 0.0 {
162 let step = -step;
163 let mut r_start = (start * step).round();
164 let mut r_stop = (stop * step).round();
165 if r_start / step < start {
166 r_start += 1.0;
167 }
168 if r_stop / step > stop {
169 r_stop -= 1.0;
170 }
171 let n = (r_stop - r_start + 1.0) as usize;
172 let iter = (0..n).map(|i| (r_start + i as f64) / step);
173 if !self.reverse {
174 ticks.extend(iter);
175 } else {
176 ticks.extend(iter.rev());
177 }
178 } else {
179 }
181 }
182 }
183 ticks
184 }
185
186 fn interpolate(&self, norm: Norm) -> f64 {
187 let value = f64::from(norm);
188 if !self.reverse {
189 self.min * (1.0 - value) + self.max * value
190 } else {
191 self.min * value + self.max * (1.0 - value)
192 }
193 }
194
195 fn normalize(&self, value: f64) -> Norm {
196 if !self.reverse {
197 ((value - self.min) / self.diff).into()
198 } else {
199 ((self.max - value) / self.diff).into()
200 }
201 }
202}
203
204#[derive(From, Into, Debug, PartialEq, PartialOrd)]
206struct Norm(pub f64);
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211 use approx::assert_abs_diff_eq;
212
213 #[test]
214 fn test_tick_increment() {
215 let range = Range::new(0.03310319276564422, 0.9859442826901874);
216 assert_abs_diff_eq!(range.tick_increment(5.0), -5.0);
217
218 let range = Range::new(0.12, 500.0);
219 assert_abs_diff_eq!(range.tick_increment(29.0), 20.0);
220 }
221
222 #[test]
223 fn test_range_ticks() {
224 let range = Range::new(1.0, 10.0);
225 assert_eq!(range.ticks(5), vec![2.0, 4.0, 6.0, 8.0, 10.0]);
226
227 let range = Range::new(0.03310319276564422, 0.9859442826901874);
228 assert_eq!(range.ticks(5), vec![0.2, 0.4, 0.6, 0.8]);
229 }
230
231 #[test]
232 fn test_linear_rescale() {
233 let domain = Range::new(10.0, 20.0);
234 assert_eq!(domain.normalize(11.0), Norm(0.1));
235 let range = Range::new(0.0, 600.0);
236 let linear = LinearScale::new(domain, range);
237 assert_abs_diff_eq!(linear.rescale(11.0), 60.0);
238 }
239}