dcc_lsystem/
image.rs

1use std::f32::consts::FRAC_PI_2;
2
3use image::{ImageBuffer, Rgb};
4use imageproc::drawing::{draw_filled_circle_mut, draw_polygon_mut};
5use imageproc::point::Point;
6
7///  Modified every pixel of `buffer` to be the provided color.
8///
9/// # Example
10/// ```rust,no_run
11/// use image::{ImageBuffer, Rgb};
12/// use dcc_lsystem::image::fill_mut;
13///
14/// let mut  buffer : ImageBuffer<Rgb<u8>, Vec<u8>> = unimplemented!();
15///
16/// // Make our image entirely black.
17/// fill_mut(&mut buffer, Rgb([0u8,0u8,0u8]));
18/// ```
19pub fn fill_mut(buffer: &mut ImageBuffer<Rgb<u8>, Vec<u8>>, color: Rgb<u8>) {
20    for pixel in buffer.pixels_mut() {
21        *pixel = color;
22    }
23}
24
25/// Draws a line to `buffer` between `(x1,y1)` and `(x2,y2)`.
26pub fn draw_line_mut(
27    buffer: &mut ImageBuffer<Rgb<u8>, Vec<u8>>,
28    x1: u32,
29    y1: u32,
30    x2: u32,
31    y2: u32,
32    thickness: f32,
33    color: Rgb<u8>,
34) {
35    assert!(thickness > 0.0);
36
37    // A thick line is really just a rectangle.  It seems that
38    // imageproc::drawing::draw_filled_rect() only allows drawing axis-aligned
39    // rectangles, so we'd need to think a little harder to use that method here.
40    //
41    // Instead, we compute the four vertices of our rectangle and use the
42    // the draw_polygon_mut function from imageproc.  This works fairly well,
43    // though there are some noticeable issues where two lines connect.  As a workaround,
44    // we also draw a circle at the start and endpoint of our line - this covers up most
45    // of the badness, and gives a reasonable looking end result.
46
47    // Compute the angle between the first and second points
48    let angle = {
49        if x1 == x2 {
50            FRAC_PI_2
51        } else {
52            ((i64::from(y2) - i64::from(y1)) as f64 / (i64::from(x2) - i64::from(x1)) as f64).atan()
53                as f32
54        }
55    };
56
57    // Compute the angle between the line perpendicular to P1 -> P2
58    // and the x-axis.
59    let perpendicular_angle = angle + FRAC_PI_2;
60
61    // We get the vertices of our rectangle by extending out in the perpendicular
62    // direction from our starting point.
63    let p1 = Point::new(
64        (x1 as f32 + thickness * perpendicular_angle.cos()) as i32,
65        (y1 as f32 + thickness * perpendicular_angle.sin()) as i32,
66    );
67    let p2 = Point::new(
68        (x1 as f32 - thickness * perpendicular_angle.cos()) as i32,
69        (y1 as f32 - thickness * perpendicular_angle.sin()) as i32,
70    );
71    let p3 = Point::new(
72        (x2 as f32 + thickness * perpendicular_angle.cos()) as i32,
73        (y2 as f32 + thickness * perpendicular_angle.sin()) as i32,
74    );
75    let p4 = Point::new(
76        (x2 as f32 - thickness * perpendicular_angle.cos()) as i32,
77        (y2 as f32 - thickness * perpendicular_angle.sin()) as i32,
78    );
79
80    // Now we just draw the line
81    draw_polygon_mut(buffer, &[p1, p3, p4, p2], color);
82    draw_filled_circle_mut(
83        buffer,
84        (x1 as i32, y1 as i32),
85        (thickness / 1.5) as i32,
86        color,
87    );
88    draw_filled_circle_mut(
89        buffer,
90        (x2 as i32, y2 as i32),
91        (thickness / 1.5) as i32,
92        color,
93    );
94}