tessera-components 0.0.0

Basic components for tessera-ui, using md3e design principles.
Documentation
struct CheckmarkUniforms {
    size: vec2f,           // width, height of the checkmark area
    color: vec4f,          // RGBA color of the checkmark
    stroke_width: f32,     // thickness of the checkmark lines
    progress: f32,         // animation progress (0.0 to 1.0)
    padding: vec2f,        // padding around the checkmark
};

@group(0) @binding(0)
var<uniform> checkmark_params: CheckmarkUniforms;

struct VertexInput {
    @location(0) position: vec3f,
    @location(1) uv: vec2f,
};

struct VertexOutput {
    @builtin(position) clip_position: vec4f,
    @location(0) uv: vec2f,
};

@vertex
fn vs_main(model: VertexInput) -> VertexOutput {
    var out: VertexOutput;
    out.clip_position = vec4f(model.position.xy, 0.0, 1.0);
    out.uv = model.uv;
    return out;
}

// Distance from point to line segment
fn distance_to_line_segment(p: vec2f, a: vec2f, b: vec2f) -> f32 {
    let pa = p - a;
    let ba = b - a;
    let h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
    return length(pa - ba * h);
}

// Calculate the total length along the checkmark path
fn get_path_length_at_point(p: vec2f, line1_start: vec2f, line1_end: vec2f, line2_start: vec2f, line2_end: vec2f) -> f32 {
    let line1_length = length(line1_end - line1_start);
    let line2_length = length(line2_end - line2_start);
    let total_length = line1_length + line2_length;
    
    // Check which line segment the point is closest to
    let dist1 = distance_to_line_segment(p, line1_start, line1_end);
    let dist2 = distance_to_line_segment(p, line2_start, line2_end);
    
    if (dist1 < dist2) {
        // Point is on first line segment
        let pa = p - line1_start;
        let ba = line1_end - line1_start;
        let h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
        return h * line1_length;
    } else {
        // Point is on second line segment
        let pa = p - line2_start;
        let ba = line2_end - line2_start;
        let h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
        return line1_length + h * line2_length;
    }
}

@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
    let size = checkmark_params.size;
    let color = checkmark_params.color;
    let stroke_width = checkmark_params.stroke_width;
    let progress = checkmark_params.progress;
    let padding = checkmark_params.padding;
    
    // Convert UV coordinates to pixel coordinates within the padded area
    let padded_size = size - 2.0 * padding;
    let pixel_pos = (vec2f(in.uv.x, 1.0 - in.uv.y) - 0.5) * size;
    let local_pos = pixel_pos;
    
    // Define checkmark path points (normalized to padded area)
    let scale = min(padded_size.x, padded_size.y);
    let center_offset = (padded_size - scale) * 0.5;
    
    // Checkmark path: two line segments forming a check
    // First line: from bottom-left to middle-bottom
    let line1_start = vec2f(-0.35, -0.1) * scale + center_offset;
    let line1_end = vec2f(-0.05, -0.35) * scale + center_offset;
    
    // Second line: from middle-bottom to top-right  
    let line2_start = line1_end;
    let line2_end = vec2f(0.4, 0.25) * scale + center_offset;
    
    // Calculate total path length
    let line1_length = length(line1_end - line1_start);
    let line2_length = length(line2_end - line2_start);
    let total_length = line1_length + line2_length;
    let current_length = progress * total_length;
    
    // Calculate distance to the checkmark path
    let dist1 = distance_to_line_segment(local_pos, line1_start, line1_end);
    let dist2 = distance_to_line_segment(local_pos, line2_start, line2_end);
    let min_dist = min(dist1, dist2);
    
    // Calculate position along the path
    let path_pos = get_path_length_at_point(local_pos, line1_start, line1_end, line2_start, line2_end);
    
    // Only draw if we're within the current progress
    let should_draw = path_pos <= current_length;
    
    // Anti-aliased stroke
    let half_stroke = stroke_width * 0.5;
    let alpha = 1.0 - smoothstep(half_stroke - 1.0, half_stroke + 1.0, min_dist);
    
    // Apply progress masking
    let final_alpha = select(0.0, alpha, should_draw) * color.a;
    
    return vec4f(color.rgb, final_alpha);
}