1use std::f64;
2use DisplayPoint;
3use tick;
4
5const W_POINT: u8 = 1; const W_BORDER: usize = 1; const W_DECIMAL_SEPARATOR: usize = 1; const W_ARROW: usize = 4; const W_NUMBER: usize = 4; const H_NUMBER: usize = 5; const MAX_INTERVALS: u8 = 10; const START_SHIFT: usize = W_BORDER + H_NUMBER + W_NUMBER;
13const DEFAULT_SIZE: usize = 100;
14
15#[derive(Debug, Clone)]
16pub struct Axis {
17 pub min_value: f64,
18 pub max_value: f64,
19 pub interval_count: u8,
20 pub scale_interval_pix: f64,
21 scale_interval_value: f64,
22 pub decimal_places: u8,
23 size: usize,
24 rotated: bool,
25}
26
27
28impl Axis {
29 pub fn rotate(self) -> Self {
30 Axis { rotated: true, ..self }
31 }
32
33 pub fn create_points(&self) -> Vec<DisplayPoint> {
34 let mut v: Vec<DisplayPoint> = vec![];
35 let ticks = self.create_ticks_points();
36 let line = self.calculate_axis_line();
37 let arrow = self.calculate_axis_arrow();
38 v.extend(ticks);
39 v.extend(line);
40 v.extend(arrow);
41 if self.rotated {
42 v.into_iter()
43 .map(|p| DisplayPoint { x: p.y, y: p.x })
44 .collect::<Vec<DisplayPoint>>()
45 } else {
46 v
47 }
48 }
49
50 pub fn set_axis_auto(max: f64, min: f64, total_size: usize) -> Axis {
51 let available_size = total_size - 2 * W_BORDER - H_NUMBER - W_NUMBER - W_ARROW;
52 let (s_max, decimal_places) = determine_max_numbers_count(max, min);
53 let interval_count = calculate_intervals_count(available_size, s_max);
54 let scale_interval_pix = (available_size as f64) / (interval_count as f64);
55 let min_value = calc(f64::floor, min, decimal_places as i32);
56 let max_value = calc(f64::ceil, max, decimal_places as i32);
57 let scale_interval_value = (max_value - min_value) / (interval_count as f64);
58 let scale_interval_value = calc(f64::ceil, scale_interval_value, decimal_places as i32);
59
60 Axis {
61 min_value: min_value,
62 max_value: max_value,
63 scale_interval_value: scale_interval_value,
64 scale_interval_pix: scale_interval_pix,
65 interval_count: interval_count,
66 decimal_places: decimal_places,
67 size: total_size,
68 rotated: false,
69 }
70 }
71
72
73 pub fn set_axis_manual(min_value: f64,
74 max_value: f64,
75 interval_count: u8,
76 decimal_places: u8,
77 size: usize)
78 -> Axis {
79 let available_size = size - 2 * W_BORDER - H_NUMBER - W_NUMBER - W_ARROW;
80 let scale_interval_pix = (available_size as f64) / (interval_count as f64);
81 let min = calc(f64::floor, min_value, decimal_places as i32);
82 let max = calc(f64::ceil, max_value, decimal_places as i32);
83 let mut scale_interval_value = (max - min) / (interval_count as f64);
84 scale_interval_value = calc(f64::ceil, scale_interval_value, decimal_places as i32);
85
86 Axis {
87 min_value: min,
88 max_value: max,
89 scale_interval_value: scale_interval_value,
90 scale_interval_pix: scale_interval_pix,
91 interval_count: interval_count,
92 decimal_places: decimal_places,
93 size: size,
94 rotated: false,
95 }
96 }
97
98 pub fn new(min_value: f64, max_value: f64, interval_count: u8, decimal_places: u8) -> Axis {
99 Axis {
100 min_value: min_value,
101 max_value: max_value,
102 scale_interval_value: 0f64,
103 scale_interval_pix: 0f64,
104 interval_count: interval_count,
105 decimal_places: decimal_places,
106 size: DEFAULT_SIZE,
107 rotated: false,
108 }
109 }
110
111 fn create_ticks_points(&self) -> Vec<DisplayPoint> {
112 let mut v: Vec<DisplayPoint> = vec![];
113 for i in 0..self.interval_count {
114 let value = round((self.min_value + self.scale_interval_value * (i as f64)),
115 self.decimal_places as i32);
116 let value_s = &*value.to_string();
117 let shift = (self.scale_interval_pix * (i as f64)).round() as usize;
118 v.extend(tick::create_tick_with_label(START_SHIFT + shift, value_s, self.rotated));
119 }
120 v
121 }
122
123 fn calculate_axis_line(&self) -> Vec<DisplayPoint> {
124 let mut v = vec![];
125 for x in START_SHIFT..self.size {
126 v.push(DisplayPoint {
127 x: x,
128 y: START_SHIFT,
129 });
130 }
131 v
132 }
133
134
135 fn calculate_axis_arrow(&self) -> Vec<DisplayPoint> {
136 vec![(4, 13), (3, 12), (2, 11), (4, 7), (3, 8), (2, 9)]
137 .into_iter()
138 .map(move |(x, y)| {
139 DisplayPoint {
140 x: self.size - x,
141 y: y,
142 }
143 })
144 .collect()
145 }
146}
147
148
149fn round(value: f64, decimal_places: i32) -> f64 {
150 let k = 10f64.powi(decimal_places);
151 (value * k).round() / k
152}
153
154fn calc<F>(f: F, value: f64, decimal_places: i32) -> f64
155 where F: Fn(f64) -> f64
156{
157 let k = 10f64.powi(decimal_places);
158 let new_value = f(value * k);
159 new_value / k
160}
161
162fn determine_max_numbers_count(max: f64, min: f64) -> (u8, u8) {
163 let mut d = max - min;
164 let c_max = get_numbers_count(max as i64);
165 let c_min = get_numbers_count(min as i64);
166
167 let maxc = if c_max >= c_min { c_max } else { c_min };
168
169 if d > 10.0 {
170 (maxc, 0)
171 } else {
172 let mut decimal_places = 0;
173 while d < 10.0 {
174 d *= 10.0;
175 decimal_places += 1;
176 }
177 (maxc + W_POINT + decimal_places, decimal_places)
178 }
179}
180
181fn get_numbers_count(value: i64) -> u8 {
182 value.to_string().len() as u8
183}
184
185fn calculate_intervals_count(available_size: usize, s_max: u8) -> u8 {
186 let k = (available_size / ((W_NUMBER + W_DECIMAL_SEPARATOR) * (s_max as usize))) - 1;
187 if k > MAX_INTERVALS as usize {
188 MAX_INTERVALS
189 } else {
190 k as u8
191 }
192
193}
194
195
196#[cfg(test)]
197mod tests {
198 use axis;
199
200 #[test]
201 fn get_numbers_count_test() {
202 let val = 34234;
203 let c = axis::get_numbers_count(val);
204 assert_eq!(c, 5);
205 }
206
207 #[test]
208 fn determine_max_numbers_count_test_diff_more_10() {
209 let max = 13.54543;
210 let min = 1.34;
211 let (s_max, decimal_places) = axis::determine_max_numbers_count(max, min);
212 assert_eq!(s_max, 2);
213 assert_eq!(decimal_places, 0);
214 }
215
216 #[test]
217 fn determine_max_numbers_count_test_diff_less_10() {
218 let max = 1.54543;
219 let min = 1.34;
220 let (s_max, decimal_places) = axis::determine_max_numbers_count(max, min);
221 assert_eq!(s_max, 4);
222 assert_eq!(decimal_places, 2);
223 }
224
225 #[test]
226 fn calculate_intervals_count_test_less_10() {
227 let available_width = 100;
228 let s_max = 5;
229 let interval_count = axis::calculate_intervals_count(available_width, s_max);
230 assert_eq!(interval_count, 3);
231 }
232
233 #[test]
234 fn calculate_intervals_count_test_more_10() {
235 let width = 1000;
236 let s_max = 5;
237 let interval_count = axis::calculate_intervals_count(width, s_max);
238 assert_eq!(interval_count, 10);
239 }
240}
241
242#[cfg(all(feature = "dev", test))]
243mod bench {
244 extern crate test;
245 use super::*;
246
247 #[bench]
248 fn create_axis_bench(b: &mut test::Bencher) {
249 b.iter(|| {
250 let axis = Axis::set_axis_auto(100.0, 0.0, 1000);
251 let _ = axis.create_points();
252 })
253 }
254}