aprender-present-lib 0.50.0

WASM-first visualization and rapid application framework
Documentation
// Presentar WGSL Shader - Primitive Rendering
// Renders rectangles, circles, and basic shapes

struct VertexInput {
    @location(0) position: vec2<f32>,
    @location(1) uv: vec2<f32>,
};

struct VertexOutput {
    @builtin(position) clip_position: vec4<f32>,
    @location(0) uv: vec2<f32>,
    @location(1) color: vec4<f32>,
};

struct Uniforms {
    viewport: vec2<f32>,
    _padding: vec2<f32>,
};

struct Instance {
    bounds: vec4<f32>,      // x, y, width, height
    color: vec4<f32>,       // r, g, b, a
    corner_radius: f32,
    shape_type: u32,        // 0=rect, 1=circle, 2=rounded_rect
    _padding: vec2<f32>,
};

@group(0) @binding(0) var<uniform> uniforms: Uniforms;
@group(0) @binding(1) var<storage, read> instances: array<Instance>;

@vertex
fn vs_main(
    vertex: VertexInput,
    @builtin(instance_index) instance_idx: u32
) -> VertexOutput {
    let inst = instances[instance_idx];

    // Transform vertex position to instance bounds
    let pos = vec2<f32>(
        inst.bounds.x + vertex.position.x * inst.bounds.z,
        inst.bounds.y + vertex.position.y * inst.bounds.w
    );

    // Convert to clip space (-1 to 1)
    let clip_pos = vec2<f32>(
        (pos.x / uniforms.viewport.x) * 2.0 - 1.0,
        1.0 - (pos.y / uniforms.viewport.y) * 2.0
    );

    var output: VertexOutput;
    output.clip_position = vec4<f32>(clip_pos, 0.0, 1.0);
    output.uv = vertex.uv;
    output.color = inst.color;
    return output;
}

@fragment
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
    return input.color;
}

// Rounded rectangle SDF fragment shader
@fragment
fn fs_rounded_rect(input: VertexOutput, @builtin(instance_index) instance_idx: u32) -> @location(0) vec4<f32> {
    let inst = instances[instance_idx];
    let radius = inst.corner_radius;

    // UV is 0-1, convert to pixel coordinates relative to center
    let size = vec2<f32>(inst.bounds.z, inst.bounds.w);
    let half_size = size * 0.5;
    let p = (input.uv - 0.5) * size;

    // Rounded rectangle SDF
    let q = abs(p) - half_size + radius;
    let d = length(max(q, vec2<f32>(0.0))) + min(max(q.x, q.y), 0.0) - radius;

    // Anti-aliased edge
    let alpha = 1.0 - smoothstep(-1.0, 1.0, d);

    return vec4<f32>(input.color.rgb, input.color.a * alpha);
}

// Circle SDF fragment shader
@fragment
fn fs_circle(input: VertexOutput) -> @location(0) vec4<f32> {
    let dist = length(input.uv - 0.5) * 2.0;
    let alpha = 1.0 - smoothstep(0.98, 1.0, dist);
    return vec4<f32>(input.color.rgb, input.color.a * alpha);
}