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}