1use std::fmt::{self, Formatter, Result as FmtResult};
5use std::ops::{Bound, Range, RangeBounds};
6
7pub struct ScaleValue(pub f64, pub f64);
8
9impl fmt::Display for ScaleValue {
10 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
11 format_axis_tick(self.0, self.1, f)
12 }
13}
14
15pub fn format_axis_tick(value: f64, step: f64, f: &mut Formatter<'_>) -> FmtResult {
17 let rounded = (value / step).round() * step;
18 if approx::abs_diff_eq!(rounded, 0.0, epsilon = 1e-10) {
19 return write!(f, "0");
21 }
22 let precision = match f.precision() {
24 Some(p) => p,
25 None => {
26 if step >= 1.0 {
27 0
28 } else {
29 let mut digits = 0;
31 let mut s = step;
32 while s < 1.0 {
33 s *= 10.0;
34 digits += 1;
35 if digits > 10 {
36 break;
37 } }
39 digits
40 }
41 }
42 };
43
44 write!(f, "{rounded:.precision$}")
45}
46
47#[derive(Debug, Clone, Copy)]
48pub struct ScaleRange {
49 pub start: f64,
50 pub end: f64,
51 pub end_included: bool,
52}
53
54impl ScaleRange {
55 pub fn new<R: RangeBounds<f64>>(range: R) -> Self {
56 let start = match range.start_bound() {
57 Bound::Included(&s) => s,
58 Bound::Excluded(&s) => s,
59 Bound::Unbounded => 0.0,
60 };
61 let (end, end_included) = match range.end_bound() {
62 Bound::Included(&e) => (e, true),
63 Bound::Excluded(&e) => (e, false),
64 Bound::Unbounded => panic!("Unbounded end not allowed"),
65 };
66
67 Self {
68 start,
69 end,
70 end_included,
71 }
72 }
73
74 pub fn as_range(&self) -> Range<f64> {
75 self.start..self.end
76 }
77
78 pub fn span(&self) -> f64 {
79 self.end - self.start
80 }
81
82 pub fn scale(&self, value: f64) -> f64 {
83 (value - self.start) / self.span()
84 }
85
86 pub fn unscale(&self, value: f64) -> f64 {
87 self.start + value * self.span()
88 }
89
90 pub fn scale_descent(&self, value: f64) -> f64 {
91 (self.end - value) / self.span()
92 }
93
94 pub fn unscale_descent(&self, value: f64) -> f64 {
95 self.end - value * self.span()
96 }
97
98 pub fn iter_with_step(&self, step: f64) -> ScaleRangeIterator {
101 let start = (self.start / step).ceil() * step; ScaleRangeIterator {
104 start,
105 end: self.end,
106 step,
107 end_included: self.end_included,
108 }
109 }
110}
111
112pub struct ScaleRangeIterator {
113 start: f64,
114 end: f64,
115 end_included: bool,
116 step: f64,
117}
118
119impl Iterator for ScaleRangeIterator {
120 type Item = f64;
121
122 fn next(&mut self) -> Option<Self::Item> {
123 if self.start < self.end
124 || (self.end_included && approx::abs_diff_eq!(self.start, self.end, epsilon = 1e-10))
125 {
126 let current = self.start;
127 self.start += self.step;
128 Some(current)
129 } else {
130 None
131 }
132 }
133}
134
135pub struct ScaleRangeWithStep {
136 pub range: ScaleRange,
137 pub step: f64,
138}
139
140impl ScaleRangeWithStep {
141 pub fn new(range: ScaleRange, step: f64) -> Self {
142 Self { range, step }
143 }
144
145 pub fn iter(&self) -> ScaleRangeIterator {
146 self.range.iter_with_step(self.step)
147 }
148}
149
150impl<R: RangeBounds<f64>> From<(R, f64)> for ScaleRangeWithStep {
151 fn from((range, step): (R, f64)) -> Self {
152 Self {
153 range: ScaleRange::new(range),
154 step,
155 }
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use approx::assert_abs_diff_eq;
162
163 use super::*;
164
165 #[test]
166 fn test_chart_range() {
167 let range = ScaleRange::new(0.0..1.0);
168 assert_abs_diff_eq!(range.end, 1.0);
169 assert_abs_diff_eq!(range.span(), 1.0);
170 assert_abs_diff_eq!(range.scale(0.5), 0.5);
171 assert_abs_diff_eq!(range.scale_descent(0.5), 0.5);
172 }
173
174 #[test]
175 fn test_iter_with_step() {
176 let range = ScaleRange::new(0.0..=1.0);
177 let mut iter = range.iter_with_step(0.2);
178 assert_abs_diff_eq!(iter.next().unwrap(), 0.0);
179 assert_abs_diff_eq!(iter.next().unwrap(), 0.2);
180 assert_abs_diff_eq!(iter.next().unwrap(), 0.4);
181 assert_abs_diff_eq!(iter.next().unwrap(), 0.6);
182 assert_abs_diff_eq!(iter.next().unwrap(), 0.8);
183 assert_abs_diff_eq!(iter.next().unwrap(), 1.0);
184 assert_eq!(iter.next(), None);
185 }
186
187 #[test]
188 fn test_iter_with_step_excluding_end() {
189 let range = ScaleRange::new(0.0..1.0);
190 let mut iter = range.iter_with_step(0.2);
191 assert_abs_diff_eq!(iter.next().unwrap(), 0.0);
192 assert_abs_diff_eq!(iter.next().unwrap(), 0.2);
193 assert_abs_diff_eq!(iter.next().unwrap(), 0.4);
194 assert_abs_diff_eq!(iter.next().unwrap(), 0.6);
195 assert_abs_diff_eq!(iter.next().unwrap(), 0.8);
196 assert_eq!(iter.next(), None);
197 }
198
199 #[test]
200 fn test_iter_with_open_start() {
201 let range = ScaleRange::new(..1.0);
202 let mut iter = range.iter_with_step(0.2);
203 assert_abs_diff_eq!(iter.next().unwrap(), 0.0);
204 assert_abs_diff_eq!(iter.next().unwrap(), 0.2);
205 assert_abs_diff_eq!(iter.next().unwrap(), 0.4);
206 assert_abs_diff_eq!(iter.next().unwrap(), 0.6);
207 assert_abs_diff_eq!(iter.next().unwrap(), 0.8);
208 assert_eq!(iter.next(), None);
209 }
210
211 #[test]
212 fn test_range_with_step() {
213 let rws: ScaleRangeWithStep = (0.0..=1.0, 0.2).into();
214 let mut iter = rws.iter();
215 assert_abs_diff_eq!(iter.next().unwrap(), 0.0);
216 assert_abs_diff_eq!(iter.next().unwrap(), 0.2);
217 assert_abs_diff_eq!(iter.next().unwrap(), 0.4);
218 assert_abs_diff_eq!(iter.next().unwrap(), 0.6);
219 assert_abs_diff_eq!(iter.next().unwrap(), 0.8);
220 assert_abs_diff_eq!(iter.next().unwrap(), 1.0);
221 assert_eq!(iter.next(), None);
222 }
223}