fimg/drawing/line.rs
1//! adds a `line` function to Image
2use crate::Image;
3use vecto::Vec2;
4
5impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
6 /// Draw a half-open line from point a to point b.
7 ///
8 /// Points not in bounds will not be included.
9 pub fn line(&mut self, a: (i32, i32), b: (i32, i32), color: [u8; CHANNELS]) {
10 clipline::Clip::<i32>::from_size(self.width(), self.height())
11 .unwrap() // fixme: panics if width or height > i32::MAX + 1
12 .line_b_proj(a.0, a.1, b.0, b.1)
13 .into_iter()
14 .flatten()
15 .for_each(|(x, y)| {
16 // SAFETY: x, y are clipped to self.
17 unsafe { self.set_pixel(x, y, color) }
18 });
19 }
20
21 /// Draw a thick line from point a to point b.
22 /// Prefer [`Image::line`] when possible.
23 ///
24 /// Points not in bounds will not be included.
25 ///
26 /// Uses [`Image::quad`].
27 /// ```
28 /// # use fimg::Image;
29 /// let mut i = Image::alloc(10, 10);
30 /// i.thick_line((2.0, 2.0), (8.0, 8.0), 2.0, [255]);
31 /// # assert_eq!(i.buffer(), b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00");
32 /// ```
33 pub fn thick_line(
34 &mut self,
35 a: impl Into<Vec2>,
36 b: impl Into<Vec2>,
37 stroke: f32,
38 color: [u8; CHANNELS],
39 ) {
40 let a = a.into();
41 let b = b.into();
42 let w = (a - b).orthogonal().normalized() * (stroke / 2.0);
43 macro_rules! p {
44 ($x:expr) => {
45 #[allow(clippy::cast_possible_truncation)]
46 ($x.x.round() as i32, $x.y.round() as i32)
47 };
48 }
49 // order:
50 // v x1 v x2
51 // [ ]
52 // ^ x3 ^ x4
53 self.quad(
54 p!(a - w), // x1
55 p!(b - w), // x2
56 p!(b + w), // x3
57 p!(a + w), // x4
58 color,
59 );
60 }
61}
62
63#[test]
64fn line() {
65 let mut a = Image::build(5, 5).alloc();
66 a.as_mut().line((0, 1), (6, 4), [255]);
67 assert_eq!(
68 a.buffer,
69 b"\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00"
70 )
71}