1use std::f64;
2
3use BitMap;
4use line;
5use Axis;
6
7const W_ARROW: usize = 4; const W_NUMBER: usize = 4; const H_NUMBER: usize = 5; const W_BORDER: usize = 1; const H_ARROW_HALF: usize = 3;
12
13const LEFT_SHIFT: usize = W_BORDER + W_NUMBER + H_NUMBER;
14const RIGHT_SHIFT: usize = W_ARROW;
15
16
17quick_error! {
18 #[derive(Debug)]
19 pub enum GraphError {
20 NotEnoughPoints {
21 description("There are not enough points to display on graph.")
22 }
23 NotEnoughSpace {
24 description("There are not enough width and height to form graph with axis.")
25 }
26 NonUniquePoints {
27 description("There are only one unique point. Can't construct line.")
28 }
29 }
30}
31
32pub type GraphResult = Result<Vec<u8>, GraphError>;
33
34
35pub trait InPoint: Into<Point> + PartialEq {}
36impl<T: Into<Point> + PartialEq> InPoint for T {}
37
38pub trait IterInPoint<P: InPoint>: Iterator<Item = P> + Clone {}
39impl<T, P> IterInPoint<P> for T
40 where T: Iterator<Item = P> + Clone,
41 P: InPoint
42{
43}
44
45#[derive(Clone, Copy)]
46pub struct Point {
47 pub x: f64,
48 pub y: f64,
49}
50
51impl<'a> From<&'a (f64, f64)> for Point {
52 fn from(t: &'a (f64, f64)) -> Point {
53 Point { x: t.0, y: t.1 }
54 }
55}
56
57impl From<(f64, f64)> for Point {
58 fn from(t: (f64, f64)) -> Point {
59 Point { x: t.0, y: t.1 }
60 }
61}
62
63#[derive(PartialEq, Clone, Copy, Debug)]
64pub struct DisplayPoint {
65 pub x: usize,
66 pub y: usize,
67}
68
69
70#[derive(Debug, Clone)]
71pub struct Serie<T: IterInPoint<P, Item = P>, P: InPoint> {
72 pub iter: T,
73 color: String,
74 max_x: f64,
75 max_y: f64,
76 min_x: f64,
77 min_y: f64,
78}
79
80impl<P: InPoint, T: IterInPoint<P>> Serie<T, P> {
81 pub fn new<S: Into<String>>(iter: T, color: S) -> Result<Self, GraphError> {
82
83 let color = color.into();
84
85 if iter.clone().nth(1).is_none() {
86 return Err(GraphError::NotEnoughPoints);
87 }
88
89 let first = iter.clone().nth(0).unwrap();
90 if !iter.clone().skip(1).any(move |p| p != first) {
91 return Err(GraphError::NonUniquePoints);
92 }
93
94 let (max_x, min_x, max_y, min_y) = Self::calculate_max_min(iter.clone());
95
96 Ok(Serie {
97 iter: iter,
98 color: color,
99 max_x: max_x,
100 max_y: max_y,
101 min_x: min_x,
102 min_y: min_y,
103 })
104 }
105
106 fn calculate_max_min(iter: T) -> (f64, f64, f64, f64) {
107 let (mut min_x, mut max_x) = (f64::INFINITY, f64::NEG_INFINITY);
108 let (mut min_y, mut max_y) = (f64::INFINITY, f64::NEG_INFINITY);
109
110 for p in iter {
111 let p = p.into();
112 if p.x > max_x {
113 max_x = p.x;
114 }
115 if p.x < min_x {
116 min_x = p.x;
117 }
118 if p.y > max_y {
119 max_y = p.y;
120 }
121 if p.y < min_y {
122 min_y = p.y;
123 }
124 }
125 (max_x, min_x, max_y, min_y)
126 }
127}
128
129
130#[derive(Debug)]
131pub struct Chart {
132 width: usize,
133 height: usize,
134 background_color: u8,
135 axis_color: u8,
136 pixs: Vec<u8>,
137 picture: BitMap,
138 axis_x: Option<Axis>,
139 axis_y: Option<Axis>,
140}
141
142impl Chart {
143 pub fn new(width: usize,
144 height: usize,
145 background_color: &str,
146 axis_color: &str)
147 -> Result<Self, GraphError> {
148
149 if width < (2 * H_NUMBER + 2 * W_NUMBER + W_ARROW + 2 * W_BORDER) ||
150 height < (2 * H_NUMBER + 2 * W_NUMBER + W_ARROW + 2 * W_BORDER) {
151 return Err(GraphError::NotEnoughSpace);
152 };
153
154 let mut picture = BitMap::new(width, height);
155
156 let background_color_number = picture.add_color(background_color);
157
158 let axis_color_number = picture.add_color(axis_color);
159
160 let size = width * height;
161
162 let pixs = vec![background_color_number; size];
163
164 Ok(Chart {
165 width: width,
166 height: height,
167 background_color: background_color_number,
168 axis_color: axis_color_number,
169 pixs: pixs,
170 picture: picture,
171 axis_x: None,
172 axis_y: None,
173 })
174 }
175
176 pub fn add_axis_x(self, axis_x: Axis) -> Chart {
177 let new_axis_x = Some(Axis::set_axis_manual(axis_x.min_value,
178 axis_x.max_value,
179 axis_x.interval_count,
180 axis_x.decimal_places,
181 self.width));
182 Chart { axis_x: new_axis_x, ..self }
183 }
184
185
186 pub fn add_axis_y(self, axis_y: Axis) -> Chart {
187 let new_axis_y = Some(Axis::set_axis_manual(axis_y.min_value,
188 axis_y.max_value,
189 axis_y.interval_count,
190 axis_y.decimal_places,
191 self.height)
192 .rotate());
193 Chart { axis_y: new_axis_y, ..self }
194 }
195
196 fn draw_serie<P: InPoint, T: IterInPoint<P>>(&mut self, serie: Serie<T, P>) {
197
198 let func_points = {
199
200 let max_width = self.width - RIGHT_SHIFT;
201
202 let max_height = self.height - RIGHT_SHIFT;
203
204 let function = self.serie_to_points(&serie);
205
206 line::extrapolate(function)
207 .filter(|p| {
208 p.x >= LEFT_SHIFT && p.x <= max_width && p.y >= LEFT_SHIFT && p.y <= max_height
209 })
210 .collect::<Vec<DisplayPoint>>()
211
212 };
213
214 let points_color_number = self.picture.add_color(&*serie.color);
215
216 self.draw_pixels(func_points, points_color_number);
217 }
218
219
220 fn calc_axis<S, T, P>(&mut self, series: S)
221 where S: Iterator<Item = Serie<T, P>>,
222 T: IterInPoint<P>,
223 P: InPoint
224 {
225 let (mut min_x, mut max_x) = (f64::INFINITY, f64::NEG_INFINITY);
226 let (mut min_y, mut max_y) = (f64::INFINITY, f64::NEG_INFINITY);
227
228 for s in series {
229 if s.max_x > max_x {
230 max_x = s.max_x;
231 }
232 if s.min_x < min_x {
233 min_x = s.min_x;
234 }
235
236 if s.max_y > max_y {
237 max_y = s.max_y;
238 }
239 if s.min_y < min_y {
240 min_y = s.min_y;
241 }
242 }
243
244 if self.axis_x.is_none() {
245 self.axis_x = Some(Axis::set_axis_auto(max_x, min_x, self.width));
246 }
247
248 if self.axis_y.is_none() {
249 self.axis_y = Some(Axis::set_axis_auto(max_y, min_y, self.height).rotate());
250 }
251 }
252
253 pub fn draw<S, T, P>(&mut self, series: S) -> Vec<u8>
254 where S: Iterator<Item = Serie<T, P>> + Clone,
255 T: IterInPoint<P>,
256 P: InPoint
257 {
258
259 if self.axis_x.is_none() || self.axis_y.is_none() {
260 self.calc_axis(series.clone());
261 }
262
263 self.draw_axis();
264
265 for serie in series {
266 self.draw_serie(serie);
267 }
268
269 self.picture.add_pixels(&self.pixs);
270
271 self.picture.as_vec()
272 }
273
274 fn draw_axis(&mut self) {
275
276 let axis_x = self.axis_x.clone().unwrap();
277
278 let axis_y = self.axis_y.clone().unwrap();
279
280 let minor_net = self.get_minor_net(&axis_x, &axis_y);
281
282 let axis_color = self.axis_color;
283
284 self.draw_pixels(axis_x.create_points(), axis_color);
285
286 self.draw_pixels(axis_y.create_points(), axis_color);
287
288 self.draw_pixels(minor_net, axis_color);
289 }
290
291 fn get_minor_net(&self, axis_x: &Axis, axis_y: &Axis) -> Vec<DisplayPoint> {
292 let mut v: Vec<DisplayPoint> = vec![];
293 for i in 0..axis_x.interval_count {
294 let shift = LEFT_SHIFT + ((axis_x.scale_interval_pix * (i as f64)).round() as usize);
295 for j in LEFT_SHIFT..(self.height - H_ARROW_HALF) {
296 if j % 2 != 0 {
297 v.push(DisplayPoint { x: shift, y: j });
298 }
299 }
300 }
301
302 for i in 0..axis_y.interval_count {
303 let shift = LEFT_SHIFT + ((axis_y.scale_interval_pix * (i as f64)).round() as usize);
304 for j in LEFT_SHIFT..(self.width - H_ARROW_HALF) {
305 if j % 2 != 0 {
306 v.push(DisplayPoint { x: j, y: shift });
307 }
308 }
309 }
310 v
311 }
312
313 fn serie_to_points<'b, P: InPoint, T: IterInPoint<P>>
314 (&'b mut self,
315 serie: &'b Serie<T, P>)
316 -> Box<Iterator<Item = DisplayPoint> + 'b> {
317
318 let width_available = self.width - LEFT_SHIFT - RIGHT_SHIFT;
319
320 let height_available = self.height - LEFT_SHIFT - RIGHT_SHIFT;
321
322 let axis_x = self.axis_x.clone().unwrap();
323
324 let axis_y = self.axis_y.clone().unwrap();
325
326 let resolution_x: f64 = (axis_x.max_value - axis_x.min_value) / (width_available as f64);
327 let resolution_y: f64 = (axis_y.max_value - axis_y.min_value) / (height_available as f64);
328
329 let serie_iter = serie.iter.clone();
330
331 Box::new(serie_iter.map(move |p| {
332 let p = p.into();
333 let id_x = ((p.x - axis_x.min_value) / resolution_x).round();
334 let id_y = ((p.y - axis_y.min_value) / resolution_y).round();
335
336 let id_x = if id_x < 0f64 {
337 LEFT_SHIFT - 1
338 } else if id_x > (width_available as f64) {
339 width_available + LEFT_SHIFT + 1
340 } else {
341 (id_x as usize) + LEFT_SHIFT
342 };
343
344 let id_y = if id_y < 0f64 {
345 LEFT_SHIFT - 1
346 } else if id_y > (height_available as f64) {
347 height_available + LEFT_SHIFT + 1
348 } else {
349 (id_y as usize) + LEFT_SHIFT
350 };
351
352 DisplayPoint { x: id_x, y: id_y }
353 }))
354
355 }
356
357
358 fn draw_pixels(&mut self, points: Vec<DisplayPoint>, color: u8) {
359 for p in points {
360 let i = p.y * self.width + p.x;
361 self.pixs[i] = color;
362 }
363 }
364}
365
366
367
368#[cfg(test)]
369mod tests {
370 use super::*;
371 use Axis;
372
373 #[test]
374 fn not_enough_space_test() {
375 let result = Chart::new(10, 15, "#ffffff", "#000000");
376 assert_eq!(result.err().unwrap().to_string(),
377 "There are not enough width and height to form graph with axis.");
378 }
379
380 #[test]
381 fn not_enough_points_test() {
382 let v: Vec<(f64, f64)> = vec![];
383 let result = Serie::new(v.into_iter(), "#0000ff".to_string());
384 assert_eq!(result.err().unwrap().to_string(),
385 "There are not enough points to display on graph.");
386 }
387
388 #[test]
389 fn one_point_test() {
390 let p = vec![(1f64, 1f64)];
391 let result = Serie::new(p.into_iter(), "#0000ff".to_string());
392 assert_eq!(result.err().unwrap().to_string(),
393 "There are not enough points to display on graph.");
394 }
395
396 #[test]
397 fn two_identical_point_test() {
398 let p = vec![(1f64, 1f64), (1f64, 1f64)];
399 let result = Serie::new(p.into_iter(), "#0000ff".to_string());
400 assert_eq!(result.err().unwrap().to_string(),
401 "There are only one unique point. Can't construct line.");
402 }
403
404 #[test]
405 fn can_draw_array() {
406 let p = vec![(1f64, 1f64), (2f64, 2f64), (3f64, 3f64)];
407 let serie = Serie::new(p.into_iter(), "#0000ff".to_string()).unwrap();
408 let mut chart = Chart::new(100, 100, "#ffffff", "#000000").unwrap();
409 let series = vec![serie];
410 let bmp = chart.draw(series.into_iter());
411 for p in bmp {
412 println!("{}", p);
413 }
414 }
415
416 #[test]
417 fn can_draw_axis_manual() {
418 let p = vec![(1f64, 1f64), (2f64, 2f64), (3f64, 3f64)];
419 let serie = Serie::new(p.into_iter(), "#0000ff".to_string()).unwrap();
420 let axis_x = Axis::new(0f64, 2f64, 7, 2);
421 let mut chart = Chart::new(100, 100, "#ffffff", "#000000")
422 .unwrap()
423 .add_axis_x(axis_x);
424 let series = vec![serie];
425 let _ = chart.draw(series.into_iter());
426 }
427}
428
429#[cfg(all(feature = "dev", test))]
430mod bench {
431 extern crate test;
432 use super::*;
433
434 #[bench]
435 fn create_graph_2_points(b: &mut test::Bencher) {
436 b.iter(|| {
437 let p = vec![(1f64, 1f64), (2f64, 2f64), (3f64, 3f64)];
438 let serie = Serie::new(p.into_iter(), "#0000ff".to_string()).unwrap();
439 let mut chart = Chart::new(740, 480, "#ffffff", "#000000").unwrap();
440 let series = vec![serie];
441 let _ = chart.draw(series.into_iter());
442 })
443 }
444
445 #[bench]
446 fn create_graph_1000_points(b: &mut test::Bencher) {
447 b.iter(|| {
448 let p: Vec<_> = formula!(y(x) = {x*x}, x = [0, 1000; 1]).collect();
449 let serie = Serie::new(p.into_iter(), "#0000ff".to_string()).unwrap();
450 let mut chart = Chart::new(740, 480, "#ffffff", "#000000").unwrap();
451 let series = vec![serie];
452 let _ = chart.draw(series.into_iter());
453 })
454 }
455
456 #[bench]
457 #[ignore]
458 fn create_graph_1000000_points(b: &mut test::Bencher) {
459 b.iter(|| {
460 let p: Vec<_> = formula!(y(x) = {x*x}, x = [0, 1000; 0.001]).collect();
461 let serie = Serie::new(p.into_iter(), "#0000ff".to_string()).unwrap();
462 let mut chart = Chart::new(740, 480, "#ffffff", "#000000").unwrap();
463 let series = vec![serie];
464 let _ = chart.draw(series.into_iter());
465 })
466 }
467}