1pub fn extended_breaks(dmin: f64, dmax: f64, m: usize) -> Vec<f64> {
5 if !(dmin.is_finite() && dmax.is_finite()) || dmax <= dmin || m < 2 {
6 return vec![];
7 }
8 const Q: [f64; 6] = [1.0, 5.0, 2.0, 2.5, 4.0, 3.0];
11 const W: [f64; 4] = [0.25, 0.2, 0.5, 0.05];
12 let m = m as f64;
13
14 let simplicity = |qi: usize, j: f64, lmin: f64, lmax: f64, step: f64| {
15 let eps = 1e-10;
16 let modulo = lmin.rem_euclid(step);
17 let v = if (modulo < eps || step - modulo < eps) && lmin <= 0.0 && lmax >= 0.0 {
18 1.0
19 } else {
20 0.0
21 };
22 1.0 - qi as f64 / (Q.len() as f64 - 1.0) - j + v
23 };
24 let simplicity_max = |qi: usize, j: f64| 1.0 - qi as f64 / (Q.len() as f64 - 1.0) - j + 1.0;
25 let coverage = |lmin: f64, lmax: f64| {
26 let r = dmax - dmin;
27 1.0 - 0.5 * ((dmax - lmax).powi(2) + (dmin - lmin).powi(2)) / (0.1 * r).powi(2)
28 };
29 let coverage_max = |span: f64| {
30 let r = dmax - dmin;
31 if span > r {
32 let half = (span - r) / 2.0;
33 1.0 - 0.5 * (half * half + half * half) / (0.1 * r).powi(2)
34 } else {
35 1.0
36 }
37 };
38 let density = |k: f64, lmin: f64, lmax: f64| {
39 let r = (k - 1.0) / (lmax - lmin);
40 let rt = (m - 1.0) / (lmax.max(dmax) - lmin.min(dmin));
41 2.0 - (r / rt).max(rt / r)
42 };
43 let density_max = |k: f64| {
44 if k >= m {
45 2.0 - (k - 1.0) / (m - 1.0)
46 } else {
47 1.0
48 }
49 };
50
51 let mut best_score = -2.0;
52 let mut best = (dmin, dmax, (dmax - dmin) / (m - 1.0));
53
54 for j_i in 1..=4u32 {
55 let j = j_i as f64;
56 for (qi, &q) in Q.iter().enumerate() {
57 let sm = simplicity_max(qi, j);
58 if W[0] * sm + W[1] + W[2] + W[3] < best_score {
59 break;
60 }
61 for k_i in 2..=(2.0 * m + 4.0) as usize {
62 let k = k_i as f64;
63 let dm = density_max(k);
64 if W[0] * sm + W[1] + W[2] * dm + W[3] < best_score {
65 break;
66 }
67 let delta = (dmax - dmin) / (k + 1.0) / j / q;
68 let z0 = delta.log10().ceil() as i32;
69 for z in z0..(z0 + 6) {
70 let step = j * q * 10f64.powi(z);
71 let cm = coverage_max(step * (k - 1.0));
72 if W[0] * sm + W[1] * cm + W[2] * dm + W[3] < best_score {
73 break;
74 }
75 let min_start = (dmax / step).floor() * j - (k - 1.0) * j;
76 let max_start = (dmin / step).ceil() * j;
77 if min_start > max_start {
78 continue;
79 }
80 let mut start = min_start;
81 while start <= max_start {
82 let lmin = start * step / j;
83 let lmax = lmin + step * (k - 1.0);
84 let s = simplicity(qi, j, lmin, lmax, step);
85 let c = coverage(lmin, lmax);
86 let g = density(k, lmin, lmax);
87 let score = W[0] * s + W[1] * c + W[2] * g + W[3];
88 if score > best_score {
89 best_score = score;
90 best = (lmin, lmax, step);
91 }
92 start += 1.0;
93 }
94 }
95 }
96 }
97 }
98
99 let (lmin, lmax, step) = best;
100 if step <= 0.0 {
101 return vec![];
102 }
103 let n = ((lmax - lmin) / step).round() as usize;
104 (0..=n).map(|i| lmin + i as f64 * step).collect()
105}
106
107pub fn nice_step(raw: f64) -> f64 {
109 let magnitude = 10f64.powf(raw.abs().log10().floor());
110 let fraction = raw / magnitude;
111
112 let nice = if fraction <= 1.5 {
113 1.0
114 } else if fraction <= 3.5 {
115 2.0
116 } else if fraction <= 7.5 {
117 5.0
118 } else {
119 10.0
120 };
121
122 nice * magnitude
123}
124
125pub fn format_number(v: f64) -> String {
127 if v == v.round() && v.abs() < 1e10 {
128 format!("{}", v as i64)
129 } else {
130 let s = format!("{:.2}", v);
131 s.trim_end_matches('0').trim_end_matches('.').to_string()
132 }
133}