nvgx 0.3.3

Pure-rust implementation of NanoVG
Documentation
use super::Vertex;
use super::{PointFlags, VPoint};
use clamped::Clamp;
use std::f32::consts::PI;

fn choose_bevel(bevel: bool, p0: &mut VPoint, p1: &mut VPoint, w: f32) -> (f32, f32, f32, f32) {
    if bevel {
        let x0 = p1.xy.x + p0.d.y * w;
        let y0 = p1.xy.y - p0.d.x * w;
        let x1 = p1.xy.x + p1.d.y * w;
        let y1 = p1.xy.y - p1.d.x * w;
        (x0, y0, x1, y1)
    } else {
        let x0 = p1.xy.x + p1.dm.x * w;
        let y0 = p1.xy.y + p1.dm.y * w;
        let x1 = p1.xy.x + p1.dm.x * w;
        let y1 = p1.xy.y + p1.dm.y * w;
        (x0, y0, x1, y1)
    }
}

pub(super) unsafe fn round_join(
    mut dst: *mut Vertex,
    p0: &mut VPoint,
    p1: &mut VPoint,
    lw: f32,
    rw: f32,
    lu: f32,
    ru: f32,
    ncap: usize,
    _fringe: f32,
) -> *mut Vertex {
    let dlx0 = p0.d.y;
    let dly0 = -p0.d.x;
    let dlx1 = p1.d.y;
    let dly1 = -p1.d.x;

    if p1.flags.contains(PointFlags::PT_LEFT) {
        let (lx0, ly0, lx1, ly1) =
            choose_bevel(p1.flags.contains(PointFlags::PR_INNERBEVEL), p0, p1, lw);
        let a0 = -dly0.atan2(-dlx0);
        let mut a1 = -dly1.atan2(-dlx1);
        if a1 > a0 {
            a1 -= PI * 2.0;
        }

        *dst = Vertex::new(lx0, ly0, lu, 1.0);
        dst = dst.add(1);

        *dst = Vertex::new(p1.xy.x - dlx0 * rw, p1.xy.y - dly0 * rw, ru, 1.0);
        dst = dst.add(1);

        let n = ((((a0 - a1) / PI) * (ncap as f32)).ceil() as i32).clamped(2, ncap as i32);
        for i in 0..n {
            let u = (i as f32) / ((n - 1) as f32);
            let a = a0 + u * (a1 - a0);
            let rx = p1.xy.x + a.cos() * rw;
            let ry = p1.xy.y + a.sin() * rw;

            *dst = Vertex::new(p1.xy.x, p1.xy.y, 0.5, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(rx, ry, ru, 1.0);
            dst = dst.add(1);
        }

        *dst = Vertex::new(lx1, ly1, lu, 1.0);
        dst = dst.add(1);

        *dst = Vertex::new(p1.xy.x - dlx1 * rw, p1.xy.y - dly1 * rw, ru, 1.0);
        dst = dst.add(1);
    } else {
        let (rx0, ry0, rx1, ry1) =
            choose_bevel(p1.flags.contains(PointFlags::PR_INNERBEVEL), p0, p1, -rw);
        let a0 = dly0.atan2(dlx0);
        let mut a1 = dly1.atan2(dlx1);
        if a1 < a0 {
            a1 += PI * 2.0;
        }

        *dst = Vertex::new(p1.xy.x + dlx0 * rw, p1.xy.y + dly0 * rw, lu, 1.0);
        dst = dst.add(1);

        *dst = Vertex::new(rx0, ry0, ru, 1.0);
        dst = dst.add(1);

        let n = ((((a0 - a1) / PI) * (ncap as f32)).ceil() as i32).clamped(2, ncap as i32);
        for i in 0..n {
            let u = (i as f32) / ((n - 1) as f32);
            let a = a0 + u * (a1 - a0);
            let lx = p1.xy.x + a.cos() * lw;
            let ly = p1.xy.y + a.cos() * lw;

            *dst = Vertex::new(lx, ly, lu, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(p1.xy.x, p1.xy.y, 0.5, 1.0);
            dst = dst.add(1);
        }

        *dst = Vertex::new(p1.xy.x + dlx1 * rw, p1.xy.y + dly1 * rw, lu, 1.0);
        dst = dst.add(1);

        *dst = Vertex::new(rx1, ry1, ru, 1.0);
        dst = dst.add(1);
    }

    dst
}

pub(super) unsafe fn bevel_join(
    mut dst: *mut Vertex,
    p0: &mut VPoint,
    p1: &mut VPoint,
    lw: f32,
    rw: f32,
    lu: f32,
    ru: f32,
    _fringe: f32,
) -> *mut Vertex {
    let dlx0 = p0.d.y;
    let dly0 = -p0.d.x;
    let dlx1 = p1.d.y;
    let dly1 = -p1.d.x;

    if p1.flags.contains(PointFlags::PT_LEFT) {
        let (lx0, ly0, lx1, ly1) =
            choose_bevel(p1.flags.contains(PointFlags::PR_INNERBEVEL), p0, p1, lw);

        *dst = Vertex::new(lx0, ly0, lu, 1.0);
        dst = dst.add(1);

        *dst = Vertex::new(p1.xy.x - dlx0 * rw, p1.xy.y - dly0 * rw, ru, 1.0);
        dst = dst.add(1);

        if p1.flags.contains(PointFlags::PT_BEVEL) {
            *dst = Vertex::new(lx0, ly0, lu, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(p1.xy.x - dlx0 * rw, p1.xy.y - dly0 * rw, ru, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(lx1, ly1, lu, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(p1.xy.x - dlx1 * rw, p1.xy.y - dly1 * rw, ru, 1.0);
            dst = dst.add(1);
        } else {
            let rx0 = p1.xy.x - p1.dm.x * rw;
            let ry0 = p1.xy.y - p1.dm.y * rw;

            *dst = Vertex::new(p1.xy.x, p1.xy.y, 0.5, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(p1.xy.x - dlx0 * rw, p1.xy.y - dly0 * rw, ru, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(rx0, ry0, ru, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(rx0, ry0, ru, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(p1.xy.x, p1.xy.y, 0.5, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(p1.xy.x - dlx1 * rw, p1.xy.y - dly1 * rw, ru, 1.0);
            dst = dst.add(1);
        }

        *dst = Vertex::new(lx1, ly1, lu, 1.0);
        dst = dst.add(1);

        *dst = Vertex::new(p1.xy.x - dlx1 * rw, p1.xy.y - dly1 * rw, ru, 1.0);
        dst = dst.add(1);
    } else {
        let (rx0, ry0, rx1, ry1) =
            choose_bevel(p1.flags.contains(PointFlags::PR_INNERBEVEL), p0, p1, -rw);

        *dst = Vertex::new(p1.xy.x + dlx0 * lw, p1.xy.y + dly0 * lw, lu, 1.0);
        dst = dst.add(1);

        *dst = Vertex::new(rx0, ry0, ru, 1.0);
        dst = dst.add(1);

        if p1.flags.contains(PointFlags::PT_BEVEL) {
            *dst = Vertex::new(p1.xy.x + dlx0 * lw, p1.xy.y + dly0 * lw, lu, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(rx0, ry0, ru, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(p1.xy.x + dlx1 * lw, p1.xy.y + dly1 * lw, lu, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(rx1, ry1, ru, 1.0);
            dst = dst.add(1);
        } else {
            let lx0 = p1.xy.x + p1.dm.x * lw;
            let ly0 = p1.xy.y + p1.dm.y * lw;

            *dst = Vertex::new(p1.xy.x + dlx0 * lw, p1.xy.y + dly0 * lw, lu, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(p1.xy.x, p1.xy.y, 0.5, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(lx0, ly0, lu, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(lx0, ly0, lu, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(p1.xy.x + dlx1 * lw, p1.xy.y + dly1 * lw, lu, 1.0);
            dst = dst.add(1);

            *dst = Vertex::new(p1.xy.x, p1.xy.y, 0.5, 1.0);
            dst = dst.add(1);
        }

        *dst = Vertex::new(p1.xy.x + dlx1 * lw, p1.xy.y + dly1 * lw, lu, 1.0);
        dst = dst.add(1);

        *dst = Vertex::new(rx1, ry1, ru, 1.0);
        dst = dst.add(1);
    }

    dst
}

pub(super) unsafe fn butt_cap_start(
    mut dst: *mut Vertex,
    p: &mut VPoint,
    dx: f32,
    dy: f32,
    w: f32,
    d: f32,
    aa: f32,
    u0: f32,
    u1: f32,
) -> *mut Vertex {
    let px = p.xy.x - dx * d;
    let py = p.xy.y - dy * d;
    let dlx = dy;
    let dly = -dx;

    *dst = Vertex::new(px + dlx * w - dx * aa, py + dly * w - dy * aa, u0, 0.0);
    dst = dst.add(1);

    *dst = Vertex::new(px - dlx * w - dx * aa, py - dly * w - dy * aa, u1, 0.0);
    dst = dst.add(1);

    *dst = Vertex::new(px + dlx * w, py + dly * w, u0, 1.0);
    dst = dst.add(1);

    *dst = Vertex::new(px - dlx * w, py - dly * w, u1, 1.0);
    dst = dst.add(1);

    dst
}

pub(super) unsafe fn butt_cap_end(
    mut dst: *mut Vertex,
    p: &mut VPoint,
    dx: f32,
    dy: f32,
    w: f32,
    d: f32,
    aa: f32,
    u0: f32,
    u1: f32,
) -> *mut Vertex {
    let px = p.xy.x - dx * d;
    let py = p.xy.y - dy * d;
    let dlx = dy;
    let dly = -dx;

    *dst = Vertex::new(px + dlx * w, py + dly * w, u0, 1.0);
    dst = dst.add(1);

    *dst = Vertex::new(px - dlx * w, py - dly * w, u1, 1.0);
    dst = dst.add(1);

    *dst = Vertex::new(px + dlx * w + dx * aa, py + dly * w + dy * aa, u0, 0.0);
    dst = dst.add(1);

    *dst = Vertex::new(px - dlx * w + dx * aa, py - dly * w + dy * aa, u1, 0.0);
    dst = dst.add(1);

    dst
}

pub(super) unsafe fn round_cap_start(
    mut dst: *mut Vertex,
    p: &mut VPoint,
    dx: f32,
    dy: f32,
    w: f32,
    ncap: usize,
    _aa: f32,
    u0: f32,
    u1: f32,
) -> *mut Vertex {
    let px = p.xy.x;
    let py = p.xy.y;
    let dlx = dy;
    let dly = -dx;

    for i in 0..ncap {
        let a = (i as f32) / ((ncap - 1) as f32) * PI;
        let ax = a.cos() * w;
        let ay = a.sin() * w;

        *dst = Vertex::new(px - dlx * ax - dx * ay, py - dly * ax - dy * ay, u0, 1.0);
        dst = dst.add(1);

        *dst = Vertex::new(px, py, 0.5, 1.0);
        dst = dst.add(1);
    }

    *dst = Vertex::new(px + dlx * w, py + dly * w, u0, 1.0);
    dst = dst.add(1);

    *dst = Vertex::new(px - dlx * w, py - dly * w, u1, 1.0);
    dst = dst.add(1);

    dst
}

pub(super) unsafe fn round_cap_end(
    mut dst: *mut Vertex,
    p: &mut VPoint,
    dx: f32,
    dy: f32,
    w: f32,
    ncap: usize,
    _aa: f32,
    u0: f32,
    u1: f32,
) -> *mut Vertex {
    let px = p.xy.x;
    let py = p.xy.y;
    let dlx = dy;
    let dly = -dx;

    *dst = Vertex::new(px + dlx * w, py + dly * w, u0, 1.0);
    dst = dst.add(1);

    *dst = Vertex::new(px - dlx * w, py - dly * w, u1, 1.0);
    dst = dst.add(1);

    for i in 0..ncap {
        let a = (i as f32) / ((ncap - 1) as f32) * PI;
        let ax = a.cos() * w;
        let ay = a.sin() * w;

        *dst = Vertex::new(px, py, 0.5, 1.0);
        dst = dst.add(1);

        *dst = Vertex::new(px - dlx * ax + dx * ay, py - dly * ax + dy * ay, u0, 1.0);
        dst = dst.add(1);
    }

    dst
}