1use crate::{BackendCoord, BackendStyle, DrawingBackend, DrawingErrorKind};
2
3pub 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 || style.stroke_width() == 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 && 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