struct Frame {
    clip_from_world: mat4x4<f32>,
    glyph_color:     vec4<f32>,
    pixels_per_mm:   f32,
    offset_px:       f32,
    tile_px:         f32,
    _pad:            f32,
};

struct Instance {
    @location(0) anchor_mm:  vec2<f32>,
    @location(1) offset_dir: vec2<f32>,
    @location(2) pick_id:    u32,
    @location(3) tile_index: u32,
};

struct VsOut {
    @builtin(position) clip: vec4<f32>,
    @location(0) uv: vec2<f32>,
    @location(1) @interpolate(flat) pick_id: u32,
};

struct FsOut {
    @location(0) color:   vec4<f32>,
    @location(1) pick_id: u32,
};

@group(0) @binding(0) var<uniform> u: Frame;
@group(0) @binding(1) var atlas: texture_2d<f32>;
@group(0) @binding(2) var atlas_sampler: sampler;

const CORNERS: array<vec2<f32>, 6> = array<vec2<f32>, 6>(
    vec2<f32>(0.0, 0.0),
    vec2<f32>(1.0, 0.0),
    vec2<f32>(0.0, 1.0),
    vec2<f32>(0.0, 1.0),
    vec2<f32>(1.0, 0.0),
    vec2<f32>(1.0, 1.0),
);

const GRID_DIM: f32 = 3.0;
const TILE_UV: f32 = 1.0 / 3.0;
const HALF_TEXEL: f32 = 0.5 / 96.0;
const INNER_UV: f32 = TILE_UV - 2.0 * HALF_TEXEL;

@vertex
fn vs(@builtin(vertex_index) vid: u32, inst: Instance) -> VsOut {
    var corners = CORNERS;
    let corner = corners[vid];
    let center_mm = inst.anchor_mm + inst.offset_dir * (u.offset_px / u.pixels_per_mm);
    let half_mm = (u.tile_px * 0.5) / u.pixels_per_mm;
    let corner_signed = corner * 2.0 - vec2<f32>(1.0, 1.0);
    let world_mm = center_mm + corner_signed * half_mm;
    let clip = u.clip_from_world * vec4<f32>(world_mm, 0.0, 1.0);

    let col = f32(inst.tile_index % 3u);
    let row = f32(inst.tile_index / 3u);
    let local_u = corner.x;
    let local_v = 1.0 - corner.y;
    let u0 = col * TILE_UV + HALF_TEXEL;
    let v0 = row * TILE_UV + HALF_TEXEL;
    let uv = vec2<f32>(u0 + local_u * INNER_UV, v0 + local_v * INNER_UV);

    var out: VsOut;
    out.clip = clip;
    out.uv = uv;
    out.pick_id = inst.pick_id;
    return out;
}

@fragment
fn fs(in: VsOut) -> FsOut {
    let sample = textureSample(atlas, atlas_sampler, in.uv);
    let coverage = sample.a;
    if (coverage <= 1.0e-3) {
        discard;
    }
    let a = u.glyph_color.a * coverage;
    var out: FsOut;
    out.color = vec4<f32>(u.glyph_color.rgb * a, a);
    out.pick_id = in.pick_id;
    return out;
}
