1 use crate::{BackendCoord, BackendStyle, DrawingBackend, DrawingErrorKind};
2 
draw_line<DB: DrawingBackend, S: BackendStyle>( back: &mut DB, mut from: BackendCoord, mut to: BackendCoord, style: &S, ) -> Result<(), DrawingErrorKind<DB::ErrorType>>3 pub fn draw_line<DB: DrawingBackend, S: BackendStyle>(
4     back: &mut DB,
5     mut from: BackendCoord,
6     mut to: BackendCoord,
7     style: &S,
8 ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
9     if style.color().alpha == 0.0 {
10         return Ok(());
11     }
12 
13     if style.stroke_width() != 1 {
14         // If the line is wider than 1px, then we need to make it a polygon
15         let v = (i64::from(to.0 - from.0), i64::from(to.1 - from.1));
16         let l = ((v.0 * v.0 + v.1 * v.1) as f64).sqrt();
17 
18         if l < 1e-5 {
19             return Ok(());
20         }
21 
22         let v = (v.0 as f64 / l, v.1 as f64 / l);
23 
24         let r = f64::from(style.stroke_width()) / 2.0;
25         let mut trans = [(v.1 * r, -v.0 * r), (-v.1 * r, v.0 * r)];
26         let mut vertices = vec![];
27 
28         for point in [from, to].iter() {
29             for t in trans.iter() {
30                 vertices.push((
31                     (f64::from(point.0) + t.0) as i32,
32                     (f64::from(point.1) + t.1) as i32,
33                 ))
34             }
35 
36             trans.swap(0, 1);
37         }
38 
39         return back.fill_polygon(vertices, style);
40     }
41 
42     if from.0 == to.0 {
43         if from.1 > to.1 {
44             std::mem::swap(&mut from, &mut to);
45         }
46         for y in from.1..=to.1 {
47             check_result!(back.draw_pixel((from.0, y), style.color()));
48         }
49         return Ok(());
50     }
51 
52     if from.1 == to.1 {
53         if from.0 > to.0 {
54             std::mem::swap(&mut from, &mut to);
55         }
56         for x in from.0..=to.0 {
57             check_result!(back.draw_pixel((x, from.1), style.color()));
58         }
59         return Ok(());
60     }
61 
62     let steep = (from.0 - to.0).abs() < (from.1 - to.1).abs();
63 
64     if steep {
65         from = (from.1, from.0);
66         to = (to.1, to.0);
67     }
68 
69     let (from, to) = if from.0 > to.0 {
70         (to, from)
71     } else {
72         (from, to)
73     };
74 
75     let mut size_limit = back.get_size();
76 
77     if steep {
78         size_limit = (size_limit.1, size_limit.0);
79     }
80 
81     let grad = f64::from(to.1 - from.1) / f64::from(to.0 - from.0);
82 
83     let mut put_pixel = |(x, y): BackendCoord, b: f64| {
84         if steep {
85             back.draw_pixel((y, x), style.color().mix(b))
86         } else {
87             back.draw_pixel((x, y), style.color().mix(b))
88         }
89     };
90 
91     let y_step_limit =
92         (f64::from(to.1.min(size_limit.1 as i32 - 1).max(0) - from.1) / grad).floor() as i32;
93 
94     let batch_start = (f64::from(from.1.min(size_limit.1 as i32 - 2).max(0) - from.1) / grad)
95         .abs()
96         .ceil() as i32
97         + from.0;
98 
99     let batch_limit =
100         to.0.min(size_limit.0 as i32 - 2)
101             .min(from.0 + y_step_limit - 1);
102 
103     let mut y = f64::from(from.1) + f64::from(batch_start - from.0) * grad;
104 
105     for x in batch_start..=batch_limit {
106         check_result!(put_pixel((x, y as i32), 1.0 + y.floor() - y));
107         check_result!(put_pixel((x, y as i32 + 1), y - y.floor()));
108 
109         y += grad;
110     }
111 
112     if to.0 >= batch_limit + 1 && y < f64::from(to.1) {
113         let x = batch_limit as i32 + 1;
114         if 1.0 + y.floor() - y > 1e-5 {
115             check_result!(put_pixel((x, y as i32), 1.0 + y.floor() - y));
116         }
117         if y - y.floor() > 1e-5 && y + 1.0 < f64::from(to.1) {
118             check_result!(put_pixel((x, y as i32 + 1), y - y.floor()));
119         }
120     }
121 
122     Ok(())
123 }
124