Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

facett-core/tests/thick_line.rs

//! **INJECT-ASSERT (CONS-CORE §6)** — the L0 thick-AA line raster: inject
//! endpoints + width and assert the coverage width ≈ requested, the AA falls off at
//! the edges, and the caps behave (butt = flush, round = stadium). Swept over width
//! × AA × orientation (LAW #3).

use facett_core::render::cpu::sdf;
use facett_core::render::prim::{cap, LineInstance};
use facett_core::render::{Camera, Canvas, CpuCanvas};

fn alpha(rgba: &[u8], w: u32, x: u32, y: u32) -> u8 {
    rgba[((y * w + x) * 4 + 3) as usize]
}

#[test]
fn line_coverage_width_matches_request_swept() {
    // A horizontal segment; sweep half_width × aa. Measure the lit cross-section at
    // the midpoint and assert it ≈ 2*half_width (± an AA pixel either side).
    for &hw in &[1.0f32, 2.0, 4.0, 8.0] {
        for &aa in &[0.5f32, 1.0, 2.0] {
            let l = LineInstance::round([10.0, 50.0], [90.0, 50.0], hw, aa, [1.0; 4]);
            // On-axis is the most-lit; full opacity once the AA band ≤ half_width.
            let axis = sdf::line_coverage(&l, [50.0, 50.0]);
            if aa < hw {
                assert!(axis > 0.99, "axis fully lit hw={hw} aa={aa}");
                // Inside half_width is lit.
                assert!(sdf::line_coverage(&l, [50.0, 50.0 + hw - aa - 0.1]) > 0.9, "within width lit");
            } else {
                assert!(axis > 0.5, "axis substantially lit (aa≥hw) hw={hw} aa={aa}");
            }
            assert!(sdf::line_coverage(&l, [50.0, 50.0 + hw + aa + 1.0]) < 0.05, "beyond width dark");
            // The edge (at exactly half_width) sits near half coverage (AA falloff).
            assert!((sdf::line_coverage(&l, [50.0, 50.0 + hw]) - 0.5).abs() < 0.12, "edge≈0.5");
            // Symmetric across the axis.
            let up = sdf::line_coverage(&l, [50.0, 50.0 + hw * 0.5]);
            let dn = sdf::line_coverage(&l, [50.0, 50.0 - hw * 0.5]);
            assert!((up - dn).abs() < 1e-4, "symmetric across axis");
        }
    }
}

#[test]
fn line_aa_falls_off_monotonically_at_the_edge() {
    let hw = 5.0;
    let aa = 2.0;
    let l = LineInstance::round([0.0, 0.0], [100.0, 0.0], hw, aa, [1.0; 4]);
    // Walk perpendicular outward from the axis at x=50: coverage must be monotone
    // non-increasing across the AA band.
    let mut prev = 1.01;
    let mut d = 0.0;
    while d <= hw + aa + 2.0 {
        let cov = sdf::line_coverage(&l, [50.0, d]);
        assert!(cov <= prev + 1e-5, "AA monotone at d={d}");
        prev = cov;
        d += 0.25;
    }
}

#[test]
fn caps_butt_ends_flush_round_overshoots_swept() {
    for &hw in &[2.0f32, 4.0, 6.0] {
        let butt = LineInstance::butt([20.0, 50.0], [80.0, 50.0], hw, 1.0, [1.0; 4]);
        let round = LineInstance::round([20.0, 50.0], [80.0, 50.0], hw, 1.0, [1.0; 4]);
        assert_eq!(butt.cap, cap::BUTT);
        assert_eq!(round.cap, cap::ROUND);
        // Just past the endpoint along the axis (within half_width of the end):
        // the round cap still covers, the butt cap does not.
        let past = [80.0 + hw * 0.5, 50.0];
        assert!(sdf::line_coverage(&round, past) > 0.5, "round overshoots hw={hw}");
        assert!(sdf::line_coverage(&butt, past) < 0.2, "butt flush hw={hw}");
        // Interiors identical.
        assert!(sdf::line_coverage(&butt, [50.0, 50.0]) > 0.99);
        assert!(sdf::line_coverage(&round, [50.0, 50.0]) > 0.99);
    }
}

#[test]
fn diagonal_line_raster_has_requested_thickness() {
    // A diagonal line through a canvas; sample its perpendicular cross-section as
    // rastered pixels — the lit width should be ≈ 2*half_width.
    let w = 100;
    let h = 100;
    let hw = 3.0;
    let aa = 1.0;
    let mut canvas = CpuCanvas::new(w, h, Camera::default(), [0, 0, 0, 0]);
    let l = LineInstance::round([10.0, 10.0], [90.0, 90.0], hw, aa, [0.1, 0.9, 0.2, 1.0]);
    canvas.push_lines(&[l]);
    let frame = canvas.rasterize();

    // On the line (the diagonal) a pixel is lit.
    assert!(alpha(&frame.rgba, w, 50, 50) > 200, "on-line pixel lit");
    // Far off the line is background.
    assert_eq!(alpha(&frame.rgba, w, 90, 10), 0, "off-line corner background");

    // Perpendicular cross-section through (50,50): count lit pixels along the
    // anti-diagonal direction. Expected ≈ 2*(hw+aa)/sqrt? — just assert it is a
    // narrow band (a few pixels), not the whole row, and not zero.
    let mut lit = 0;
    for k in -15i32..=15 {
        // step along (1,-1) normalized — the perpendicular to the (1,1) line.
        let x = 50 + k;
        let y = 50 - k;
        if (0..w as i32).contains(&x) && (0..h as i32).contains(&y) && alpha(&frame.rgba, w, x as u32, y as u32) > 32 {
            lit += 1;
        }
    }
    // 2*(hw+aa) ≈ 8 px across, sampled on a √2 grid → a handful of cells.
    assert!((3..=14).contains(&lit), "perpendicular band is the requested thickness, got {lit}");
    assert!(frame.lit_px() > 100, "the line drew a real stroke, got {}", frame.lit_px());
}