facett-core 0.1.7

facett — visual kernel: render a node/edge Scene into egui (wgpu fast path to come)
Documentation
// L0 SDF quad shader — instanced screen-aligned quads, one fragment-shader signed
// distance function per shape (circle / ring / marker). The coverage math is
// byte-for-byte the same as `render/cpu/sdf.rs` so a GPU frame matches a CPU frame.
//
// Shape tags (match `prim::shape`):
const SHAPE_CIRCLE:   u32 = 0u;
const SHAPE_RING:     u32 = 1u;
const SHAPE_SQUARE:   u32 = 2u;
const SHAPE_TRIANGLE: u32 = 3u;
const SHAPE_DIAMOND:  u32 = 4u;

struct Uniforms {
    // viewport size in pixels (for the pixel→clip transform).
    viewport: vec2<f32>,
    _pad: vec2<f32>,
};
@group(0) @binding(0) var<uniform> U: Uniforms;

// One QuadInstance (matches `prim::QuadInstance`, 48 bytes).
struct Instance {
    @location(0) center: vec2<f32>,
    @location(1) radius: f32,
    @location(2) inner:  f32,
    @location(3) color:  vec4<f32>,
    @location(4) aa:     f32,
    @location(5) shape:  u32,
};

struct VsOut {
    @builtin(position) clip: vec4<f32>,
    @location(0) local: vec2<f32>,   // offset from the instance centre, pixels
    @location(1) color: vec4<f32>,
    @location(2) @interpolate(flat) radius: f32,
    @location(3) @interpolate(flat) inner:  f32,
    @location(4) @interpolate(flat) aa:     f32,
    @location(5) @interpolate(flat) shape:  u32,
};

// A unit quad expanded to ±(radius + aa) around the instance centre.
@vertex
fn vs_main(@builtin(vertex_index) vi: u32, inst: Instance) -> VsOut {
    // Two triangles: (0,0)(1,0)(0,1) / (1,0)(1,1)(0,1).
    var corners = array<vec2<f32>, 6>(
        vec2<f32>(-1.0, -1.0), vec2<f32>(1.0, -1.0), vec2<f32>(-1.0, 1.0),
        vec2<f32>(1.0, -1.0),  vec2<f32>(1.0, 1.0),  vec2<f32>(-1.0, 1.0),
    );
    let he = inst.radius + inst.aa;
    let corner = corners[vi];
    let local = corner * he;
    let px = inst.center + local;

    // pixel → clip (y down in pixels → y up in clip).
    let ndc = vec2<f32>(px.x / U.viewport.x * 2.0 - 1.0, 1.0 - px.y / U.viewport.y * 2.0);

    var out: VsOut;
    out.clip = vec4<f32>(ndc, 0.0, 1.0);
    out.local = local;
    out.color = inst.color;
    out.radius = inst.radius;
    out.inner = inst.inner;
    out.aa = inst.aa;
    out.shape = inst.shape;
    return out;
}

fn coverage_from_sd(d: f32, aa: f32) -> f32 {
    if (aa <= 0.0) {
        return select(0.0, 1.0, d <= 0.0);
    }
    return 1.0 - smoothstep(-aa, aa, d);
}

fn triangle_sd(p: vec2<f32>, half: f32) -> f32 {
    // Intersection of three half-planes (apex up). Identical to the CPU form.
    let bottom = p.y - half;
    let inv = 1.0 / sqrt(5.0);
    let right = (p.x * 2.0 + (p.y + half) * -1.0) * inv;
    let left  = (p.x * -2.0 + (p.y + half) * -1.0) * inv;
    return max(bottom, max(right, left));
}

fn box_sd(p: vec2<f32>, half: f32, corner: f32) -> f32 {
    let c = clamp(corner, 0.0, half);
    let d = abs(p) - vec2<f32>(half - c, half - c);
    let outside = length(max(d, vec2<f32>(0.0, 0.0)));
    let inside = min(max(d.x, d.y), 0.0);
    return outside + inside - c;
}

@fragment
fn fs_main(in: VsOut) -> @location(0) vec4<f32> {
    let dist = length(in.local);
    var cov = 0.0;
    if (in.shape == SHAPE_CIRCLE) {
        cov = coverage_from_sd(dist - in.radius, in.aa);
    } else if (in.shape == SHAPE_RING) {
        let sd = max(dist - in.radius, in.inner - dist);
        cov = coverage_from_sd(sd, in.aa);
    } else if (in.shape == SHAPE_SQUARE) {
        cov = coverage_from_sd(box_sd(in.local, in.radius, in.inner), in.aa);
    } else if (in.shape == SHAPE_TRIANGLE) {
        cov = coverage_from_sd(triangle_sd(in.local, in.radius), in.aa);
    } else if (in.shape == SHAPE_DIAMOND) {
        let sd = (abs(in.local.x) + abs(in.local.y)) - in.radius;
        cov = coverage_from_sd(sd, in.aa);
    }
    if (cov <= 0.0) {
        discard;
    }
    // Premultiplied output for the standard alpha-blend state.
    let a = in.color.a * cov;
    return vec4<f32>(in.color.rgb * a, a);
}