Skip to main content

F32

Constant F32 

Source
pub const F32: &str = r#"const WORKGROUP_SIZE: u32 = {{WORKGROUP_SIZE}}u;

struct VertexRaw {
    data: array<f32, 12u>,
};

struct LineParams {
    color: vec4<f32>,
    count: u32,
    line_style: u32,
    half_width_px: f32,
    _pad0: f32,
    viewport_width_px: f32,
    viewport_height_px: f32,
    x_min: f32,
    x_span: f32,
    y_min: f32,
    y_span: f32,
    _pad1: vec2<f32>,
};

@group(0) @binding(0)
var<storage, read> buf_x: array<f32>;

@group(0) @binding(1)
var<storage, read> buf_y: array<f32>;

@group(0) @binding(2)
var<storage, read_write> out_vertices: array<VertexRaw>;

@group(0) @binding(3)
var<uniform> params: LineParams;

fn should_draw(segment: u32, style: u32) -> bool {
    switch(style) {
        case 0u: { return true; } // Solid
        case 1u: { return (segment % 4u) < 2u; } // Dashed: on,on,off,off
        case 2u: { return (segment % 4u) < 2u; } // Dotted approximated via dashed pattern
        case 3u: {
            let m = segment % 6u;
            return (m < 2u) || (m == 3u); // DashDot: on,on,off,on,off,off
        }
        default: { return true; }
    }
}

fn write_vertex(index: u32, pos: vec2<f32>, color: vec4<f32>) {
    var vertex: VertexRaw;
    vertex.data[0u] = pos.x;
    vertex.data[1u] = pos.y;
    vertex.data[2u] = 0.0;
    vertex.data[3u] = color.x;
    vertex.data[4u] = color.y;
    vertex.data[5u] = color.z;
    vertex.data[6u] = color.w;
    vertex.data[7u] = 0.0;
    vertex.data[8u] = 0.0;
    vertex.data[9u] = 1.0;
    vertex.data[10u] = 0.0;
    vertex.data[11u] = 0.0;
    out_vertices[index] = vertex;
}

fn data_to_px(p: vec2<f32>) -> vec2<f32> {
    return vec2<f32>(
        (p.x - params.x_min) * (params.viewport_width_px / max(params.x_span, 1e-12)),
        (p.y - params.y_min) * (params.viewport_height_px / max(params.y_span, 1e-12))
    );
}

fn px_to_data(p: vec2<f32>) -> vec2<f32> {
    return vec2<f32>(
        params.x_min + p.x * (max(params.x_span, 1e-12) / params.viewport_width_px),
        params.y_min + p.y * (max(params.y_span, 1e-12) / params.viewport_height_px)
    );
}

@compute @workgroup_size(WORKGROUP_SIZE)
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
    if (params.count < 2u) {
        return;
    }
    let segments = params.count - 1u;
    let idx = gid.x;
    if (idx >= segments) {
        return;
    }

    let p0_data = vec2<f32>(buf_x[idx], buf_y[idx]);
    let p1_data = vec2<f32>(buf_x[idx + 1u], buf_y[idx + 1u]);
    let p0 = data_to_px(p0_data);
    let p1 = data_to_px(p1_data);
    let delta = p1 - p0;
    let len = length(delta);
    let draw = should_draw(idx, params.line_style) && (len != 0.0);
    var color = params.color;
    if (!draw) {
        color.w = 0.0;
    }

    if (!draw) {
        let base = idx * 6u;
        // Emit fully transparent degenerate geometry for skipped/degenerate segments.
        write_vertex(base + 0u, p0, color);
        write_vertex(base + 1u, p0, color);
        write_vertex(base + 2u, p0, color);
        write_vertex(base + 3u, p0, color);
        write_vertex(base + 4u, p0, color);
        write_vertex(base + 5u, p0, color);
        return;
    }

    var half_width = params.half_width_px;
    if (half_width < 0.0001) {
        half_width = 0.0001;
    }
    let dir = normalize(delta);
    let normal = vec2<f32>(-dir.y, dir.x);
    let offset = normal * half_width;
    let v0 = p0 + offset;
    let v1 = p1 + offset;
    let v2 = p1 - offset;
    let v3 = p0 - offset;

    let base = idx * 6u;
    write_vertex(base + 0u, px_to_data(v0), params.color);
    write_vertex(base + 1u, px_to_data(v1), params.color);
    write_vertex(base + 2u, px_to_data(v2), params.color);
    write_vertex(base + 3u, px_to_data(v0), params.color);
    write_vertex(base + 4u, px_to_data(v2), params.color);
    write_vertex(base + 5u, px_to_data(v3), params.color);
}
"#;