1use std::cmp::{max, min};
2
3use Canvas;
4use Drawable;
5use Endian;
6
7pub struct Line {
9 pub pt1: (usize, usize),
11 pub pt2: (usize, usize),
13 pub color: [u8; 4],
15 pub antialiased: bool,
17}
18
19impl Line {
20 pub fn new(
22 pt1: (usize, usize),
23 pt2: (usize, usize),
24 color: [u8; 4],
25 antialiased: bool,
26 ) -> Line {
27 Line {
28 pt1,
29 pt2,
30 color,
31 antialiased,
32 }
33 }
34}
35
36impl Drawable for Line {
37 fn draw(&self, canvas: &mut Canvas) {
38 if !self.antialiased {
39 if self.pt1.0 == self.pt2.0 && self.pt1.0 < canvas.width {
40 let min_y = min(self.pt1.1, self.pt2.1);
41 let max_y = min(max(self.pt1.1, self.pt2.1), canvas.height - 1);
42 for y in min_y..=max_y {
43 canvas.draw_point(self.pt1.0, y, self.color)
44 }
45 } else if self.pt1.1 == self.pt2.1 && self.pt1.1 < canvas.height {
46 let min_x = min(self.pt1.0, self.pt2.0);
47 let max_x = min(max(self.pt1.0, self.pt2.0), canvas.width - 1);
48 for x in min_x..=max_x {
49 canvas.draw_point(x, self.pt1.1, self.color)
50 }
51 } else {
52 for (x, y) in bresenham(
54 self.pt1.0 as isize,
55 self.pt1.1 as isize,
56 self.pt2.0 as isize,
57 self.pt2.1 as isize,
58 ) {
59 if x < canvas.width && y < canvas.height {
60 canvas.draw_point(x, y, self.color)
61 }
62 }
63 }
64 } else {
65 for (x, y, coverage) in xiaolin_wu(
67 self.pt1.0 as f32,
68 self.pt1.1 as f32,
69 self.pt2.0 as f32,
70 self.pt2.1 as f32,
71 ) {
72 if x < canvas.width && y < canvas.height {
73 let mut color = self.color;
74 let base = canvas.stride * y + canvas.pixel_size * x;
75 if coverage != 1.0 {
76 if canvas.endianness == Endian::Little {
77 color[1] = (canvas.buffer[base + 2] as f32 * (1.0 - coverage)
78 + color[1] as f32 * coverage)
79 as u8;
80 color[2] = (canvas.buffer[base + 1] as f32 * (1.0 - coverage)
81 + color[2] as f32 * coverage)
82 as u8;
83 color[3] = (canvas.buffer[base] as f32 * (1.0 - coverage)
84 + color[3] as f32 * coverage)
85 as u8;
86 } else {
87 color[1] = (canvas.buffer[base + 1] as f32 * (1.0 - coverage)
88 + color[1] as f32 * coverage)
89 as u8;
90 color[2] = (canvas.buffer[base + 2] as f32 * (1.0 - coverage)
91 + color[2] as f32 * coverage)
92 as u8;
93 color[3] = (canvas.buffer[base + 3] as f32 * (1.0 - coverage)
94 + color[3] as f32 * coverage)
95 as u8;
96 }
97 }
98 canvas.draw_point(x as usize, y as usize, color)
99 }
100 }
101 }
102 }
103}
104
105fn bresenham(mut x0: isize, mut y0: isize, x1: isize, y1: isize) -> Vec<(usize, usize)> {
106 let mut points: Vec<(usize, usize)> = Vec::new();
107 let dx = (x1 - x0).abs();
108 let sx = if x0 < x1 { 1 } else { -1 };
109 let dy = -((y1 - y0).abs());
110 let sy = if y0 < y1 { 1 } else { -1 };
111 let mut err = dx + dy;
112
113 loop {
114 points.push((x0 as usize, y0 as usize));
115 if x0 == x1 && y0 == y1 {
116 break;
117 };
118 let e2 = 2 * err;
119 if e2 >= dy {
120 err += dy;
121 x0 += sx;
122 }
123 if e2 <= dx {
124 err += dx;
125 y0 += sy;
126 }
127 }
128 points
129}
130
131fn xiaolin_wu(mut x0: f32, mut y0: f32, mut x1: f32, mut y1: f32) -> Vec<(usize, usize, f32)> {
132 let mut points: Vec<(usize, usize, f32)> = Vec::new();
133 let steep = (y1 - y0).abs() > (x1 - x0).abs();
134 if steep {
135 std::mem::swap(&mut x0, &mut y0);
136 std::mem::swap(&mut x1, &mut y1);
137 }
138 if x0 > x1 {
139 std::mem::swap(&mut x0, &mut x1);
140 std::mem::swap(&mut y0, &mut y1);
141 }
142 let dx = x1 - x0;
143 let dy = y1 - y0;
144 let gradient = if dx == 0.0 {
145 1.0
146 } else {
147 dy as f32 / dx as f32
148 };
149
150 let mut intery = y0 + gradient;
151 points.push((x0 as usize, y0 as usize, 1.0));
152 points.push((x1 as usize, y1 as usize, 1.0));
153 if steep {
154 for x in x0 as usize + 1..=x1 as usize - 1 {
155 points.push((intery as usize, x, 1.0 - intery.fract()));
156 points.push((intery as usize + 1, x, intery.fract()));
157 intery = intery + gradient;
158 }
159 } else {
160 for x in x0 as usize + 1..=x1 as usize - 1 {
161 points.push((x, intery as usize, 1.0 - intery.fract()));
162 points.push((x, intery as usize + 1, intery.fract()));
163 intery = intery + gradient;
164 }
165 }
166 points
167}