1 | use crate::{BackendCoord, BackendStyle, DrawingBackend, DrawingErrorKind}; |
2 | |
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 || 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 | |