1pub fn ticks(start: f64, stop: f64, count: usize) -> Vec<f64> {
22 if count == 0 || start == stop {
23 return vec![start];
24 }
25
26 let (start, stop, reverse) = if start > stop {
27 (stop, start, true)
28 } else {
29 (start, stop, false)
30 };
31
32 let step = tick_step(start, stop, count);
33 if step == 0.0 || !step.is_finite() {
34 return vec![start];
35 }
36
37 let tick_start = (start / step).ceil() * step;
38 let tick_stop = (stop / step).floor() * step;
39
40 let n = ((tick_stop - tick_start) / step).round() as usize + 1;
41 let mut result: Vec<f64> = (0..n).map(|i| tick_start + step * i as f64).collect();
42
43 if reverse {
44 result.reverse();
45 }
46
47 result
48}
49
50pub fn tick_step(start: f64, stop: f64, count: usize) -> f64 {
61 if count == 0 {
62 return 0.0;
63 }
64
65 let step0 = (stop - start).abs() / count as f64;
66 let step1 = 10_f64.powf(step0.log10().floor());
67
68 let error = step0 / step1;
69
70 if error >= 10.0_f64.sqrt() * 5.0 {
71 step1 * 10.0
72 } else if error >= 10.0_f64.sqrt() * 2.0 {
73 step1 * 5.0
74 } else if error >= 10.0_f64.sqrt() {
75 step1 * 2.0
76 } else {
77 step1
78 }
79}
80
81pub fn tick_increment(start: f64, stop: f64, count: usize) -> f64 {
85 let step = (stop - start) / count.max(1) as f64;
86 let power = step.log10().floor();
87 let error = step / 10_f64.powf(power);
88
89 if error >= 10.0_f64.sqrt() * 5.0 {
90 10_f64.powf(power + 1.0)
91 } else if error >= 10.0_f64.sqrt() * 2.0 {
92 5.0 * 10_f64.powf(power)
93 } else if error >= 10.0_f64.sqrt() {
94 2.0 * 10_f64.powf(power)
95 } else {
96 10_f64.powf(power)
97 }
98}
99
100pub fn nice(start: f64, stop: f64, count: usize) -> (f64, f64) {
115 if start == stop {
116 return (start, stop);
117 }
118
119 let (start, stop, reverse) = if start > stop {
120 (stop, start, true)
121 } else {
122 (start, stop, false)
123 };
124
125 let step = tick_increment(start, stop, count);
126 if step == 0.0 || !step.is_finite() {
127 return if reverse {
128 (stop, start)
129 } else {
130 (start, stop)
131 };
132 }
133
134 let nice_start = (start / step).floor() * step;
135 let nice_stop = (stop / step).ceil() * step;
136
137 if reverse {
138 (nice_stop, nice_start)
139 } else {
140 (nice_start, nice_stop)
141 }
142}
143
144pub fn nice_number(range: f64, round: bool) -> f64 {
159 if range == 0.0 {
160 return 0.0;
161 }
162
163 let exponent = range.abs().log10().floor();
164 let fraction = range.abs() / 10_f64.powf(exponent);
165
166 let nice_fraction = if round {
167 if fraction < 1.5 {
168 1.0
169 } else if fraction < 3.0 {
170 2.0
171 } else if fraction < 7.0 {
172 5.0
173 } else {
174 10.0
175 }
176 } else if fraction <= 1.0 {
177 1.0
178 } else if fraction <= 2.0 {
179 2.0
180 } else if fraction <= 5.0 {
181 5.0
182 } else {
183 10.0
184 };
185
186 nice_fraction * 10_f64.powf(exponent) * range.signum()
187}
188
189pub fn log_ticks(min: f64, max: f64, base: f64, subdivisions: bool) -> Vec<f64> {
202 if min <= 0.0 || max <= 0.0 || base <= 1.0 {
203 return vec![];
204 }
205
206 let log_min = min.log(base).floor();
207 let log_max = max.log(base).ceil();
208
209 let mut ticks = Vec::new();
210
211 let mut exp = log_min;
212 while exp <= log_max {
213 let tick = base.powf(exp);
214
215 if tick >= min && tick <= max {
216 ticks.push(tick);
217 }
218
219 if subdivisions && exp < log_max {
221 for i in 2..base as i32 {
222 let sub_tick = tick * i as f64;
223 if sub_tick >= min && sub_tick <= max {
224 ticks.push(sub_tick);
225 }
226 }
227 }
228
229 exp += 1.0;
230 }
231
232 ticks.sort_by(|a, b| a.partial_cmp(b).unwrap());
233 ticks
234}
235
236pub fn ticks_interval(start: f64, stop: f64, interval: f64) -> Vec<f64> {
247 if interval <= 0.0 || start == stop {
248 return vec![start];
249 }
250
251 let tick_start = (start / interval).ceil() * interval;
252 let tick_stop = (stop / interval).floor() * interval;
253
254 let n = ((tick_stop - tick_start) / interval).round() as usize + 1;
255 (0..n).map(|i| tick_start + interval * i as f64).collect()
256}
257
258pub fn time_ticks(start: f64, stop: f64, count: usize) -> Vec<f64> {
262 ticks(start, stop, count)
263}
264
265#[cfg(test)]
266mod tests {
267 use super::*;
268
269 #[test]
270 fn test_ticks() {
271 let t = ticks(0.0, 100.0, 5);
272 assert!(t.len() >= 5);
273 assert!(t[0] <= 0.0);
274 assert!(*t.last().unwrap() >= 100.0);
275 }
276
277 #[test]
278 fn test_tick_step() {
279 assert_eq!(tick_step(0.0, 100.0, 10), 10.0);
280 assert_eq!(tick_step(0.0, 1.0, 10), 0.1);
281 }
282
283 #[test]
284 fn test_nice() {
285 let (start, stop) = nice(0.134, 0.867, 5);
286 assert!(start <= 0.134);
287 assert!(stop >= 0.867);
288 }
289
290 #[test]
291 fn test_nice_number() {
292 assert_eq!(nice_number(10.0, false), 10.0);
293 assert_eq!(nice_number(15.0, false), 20.0);
294 assert_eq!(nice_number(25.0, false), 50.0);
295 assert_eq!(nice_number(75.0, false), 100.0);
296 }
297
298 #[test]
299 fn test_log_ticks() {
300 let t = log_ticks(1.0, 1000.0, 10.0, false);
301 assert_eq!(t, vec![1.0, 10.0, 100.0, 1000.0]);
302 }
303
304 #[test]
305 fn test_log_ticks_subdivisions() {
306 let t = log_ticks(10.0, 100.0, 10.0, true);
307 assert!(t.contains(&10.0));
308 assert!(t.contains(&20.0));
309 assert!(t.contains(&50.0));
310 assert!(t.contains(&100.0));
311 }
312
313 #[test]
314 fn test_ticks_interval() {
315 let t = ticks_interval(0.0, 100.0, 20.0);
316 assert_eq!(t, vec![0.0, 20.0, 40.0, 60.0, 80.0, 100.0]);
317 }
318
319 #[test]
320 fn test_ticks_reverse() {
321 let t = ticks(100.0, 0.0, 5);
322 assert!(t[0] >= t[1]); }
324}