birdy 0.1.10

Screenshot tool
use crate::{line::draw_line, triangle::draw_triangle_filled};

pub fn draw_arrow_bordered(
    canvas: &mut [u8],
    x0: usize,
    y0: usize,
    x1: usize,
    y1: usize,
    width: usize,
    color: (u8, u8, u8, u8),
) {
    if (x1 as isize - x0 as isize).abs() < 3 && (y1 as isize - y0 as isize).abs() < 3 {
        return;
    }

    let (arrow_head_a, arrow_head_b) =
        calculate_arrow_head((x0 as f64, y0 as f64), (x1 as f64, y1 as f64));

    if !arrow_head_a.0.is_nan() && !arrow_head_a.1.is_nan() {
        draw_line(
            canvas,
            arrow_head_a.0 as usize,
            arrow_head_a.1 as usize,
            x1,
            y1,
            width,
            color,
        );
        draw_line(
            canvas,
            arrow_head_a.0 as usize + 1,
            arrow_head_a.1 as usize,
            x1 + 1,
            y1,
            width,
            color,
        );
        draw_line(
            canvas,
            (arrow_head_a.0 as usize).saturating_sub(1),
            arrow_head_a.1 as usize,
            x1.saturating_sub(1),
            y1,
            width,
            color,
        );
        draw_line(
            canvas,
            arrow_head_a.0 as usize,
            (arrow_head_a.1 as usize).saturating_sub(1),
            x1,
            y1.saturating_sub(1),
            width,
            color,
        );
        draw_line(
            canvas,
            arrow_head_a.0 as usize,
            arrow_head_a.1 as usize + 1,
            x1,
            y1 + 1,
            width,
            color,
        );
        draw_line(
            canvas,
            arrow_head_a.0 as usize,
            (arrow_head_a.1 as usize).saturating_sub(1),
            x1,
            y1.saturating_sub(1),
            width,
            color,
        );
    }

    if !arrow_head_b.0.is_nan() && !arrow_head_b.1.is_nan() {
        draw_line(
            canvas,
            arrow_head_b.0 as usize,
            arrow_head_b.1 as usize,
            x1,
            y1,
            width,
            color,
        );
        draw_line(
            canvas,
            arrow_head_b.0 as usize + 1,
            arrow_head_b.1 as usize,
            x1 + 1,
            y1,
            width,
            color,
        );
        draw_line(
            canvas,
            (arrow_head_b.0 as usize).saturating_sub(1),
            arrow_head_b.1 as usize,
            x1.saturating_sub(1),
            y1,
            width,
            color,
        );
        draw_line(
            canvas,
            arrow_head_b.0 as usize,
            arrow_head_b.1 as usize + 1,
            x1,
            y1 + 1,
            width,
            color,
        );
        draw_line(
            canvas,
            arrow_head_b.0 as usize,
            (arrow_head_b.1 as usize).saturating_sub(1),
            x1,
            y1.saturating_sub(1),
            width,
            color,
        );
    }

    draw_line(canvas, x0, y0, x1, y1, width, color);
    draw_line(canvas, x0, y0 + 1, x1, y1 + 1, width, color);
    draw_line(
        canvas,
        x0,
        y0.saturating_sub(1),
        x1,
        y1.saturating_sub(1),
        width,
        color,
    );
    draw_line(canvas, x0 + 1, y0, x1 + 1, y1, width, color);
    draw_line(
        canvas,
        x0.saturating_sub(1),
        y0,
        x1.saturating_sub(1),
        y1,
        width,
        color,
    );
}

pub fn draw_arrow_filled(
    canvas: &mut [u8],
    x0: usize,
    y0: usize,
    x1: usize,
    y1: usize,
    width: usize,
    color: (u8, u8, u8, u8),
) {
    draw_arrow_bordered(canvas, x0, y0, x1, y1, width, color);

    let (arrow_head_a, arrow_head_b) =
        calculate_arrow_head((x0 as f64, y0 as f64), (x1 as f64, y1 as f64));

    draw_triangle_filled(
        canvas,
        (x1, y1),
        (arrow_head_a.0 as usize, arrow_head_a.1 as usize),
        (arrow_head_b.0 as usize, arrow_head_b.1 as usize),
        width,
        color,
    );
}

pub fn calculate_arrow_head(start: (f64, f64), end: (f64, f64)) -> ((f64, f64), (f64, f64)) {
    let angle_in_deg: f64 = 30.0;
    let tip_length = 50.0;

    let (x1, y1) = end;
    let (x2, y2) = start;
    let alpha = angle_in_deg.to_radians();
    let l1 = ((x2 - x1).powi(2) + (y2 - y1).powi(2)).sqrt();
    let l2 = tip_length;

    let a = (y2 - y1).powi(2) + (x2 - x1).powi(2);
    let b = -2.0 * l1 * l2 * alpha.cos() * (y2 - y1);
    let c = l1.powi(2) * l2.powi(2) * alpha.cos().powi(2) - l2.powi(2) * (x2 - x1).powi(2);

    let s2a = (-b + (b.powi(2) - 4.0 * a * c).sqrt()) / (2.0 * a);
    let s2b = (-b - (b.powi(2) - 4.0 * a * c).sqrt()) / (2.0 * a);
    let s1a = (l1 * l2 * alpha.cos() - s2a * (y2 - y1)) / (x2 - x1);
    let s1b = (l1 * l2 * alpha.cos() - s2b * (y2 - y1)) / (x2 - x1);

    let x3a = s1a + x1;
    let y3a = s2a + y1;
    let x3b = s1b + x1;
    let y3b = s2b + y1;

    ((x3a, y3a), (x3b, y3b))
}