facett-core 0.1.19

facett — visual kernel: render a node/edge Scene into egui (wgpu fast path to come)
Documentation
// Line-draw shader for OSM ways.
// Vertices are in **origin-local** Mercator space: `pos = mercator_f64(lonlat) −
// origin`, where `origin` is the dataset's Mercator bbox-min computed once in f64
// on the CPU. Baking the vertices relative to a data-local origin keeps every
// coordinate the VS touches SMALL, so the projection no longer suffers f32
// catastrophic cancellation at high zoom (the old `mercator*zoom + offset` added
// two ~4e7 f32 whose true pixel was their tiny difference → multi-pixel drift).
//
// The transform mirrors the CPU `to_px` (subtract-before-scale):
//   screen = (pos − ref) * zoom + screen_center
// with `ref = camera_center − origin` (small) and `screen_center` the pixel the
// camera centre lands on. `zoom` is per-axis so the GPU line lane matches a
// non-uniform CPU fit exactly.
//
// `px_to_ndc` comes from the shared prelude `common.wgsl`, prepended by
// `render::gpu::wgsl()`. Do not re-declare it here.

struct Uniforms {
    zoom_x:          f32,
    zoom_y:          f32,
    screen_center_x: f32,
    screen_center_y: f32,
    ref_x:           f32,
    ref_y:           f32,
    viewport_w:      f32,
    viewport_h:      f32,
    // The 2D ground frame (bearing rotation + cos(tilt) foreshorten) — see
    // `DrawUniforms` and `project_ground_px` in the shared prelude. Identity is
    // rot = (1,0,0,1), foreshorten = 1.
    rot:             vec4<f32>,
    foreshorten:     f32,
    // Frame-uniform RGB multiplier on the vertex colour (alpha untouched).
    // 1.0 changes nothing; the map's fill draw passes the pitched-horizon fade here
    // so the baked colours can stay static in VRAM under a moving tilt slider.
    tint:            f32,
    _pad0:           f32,
    _pad1:           f32,
}

@group(0) @binding(0) var<uniform> u: Uniforms;

struct VertexOut {
    @builtin(position) pos: vec4<f32>,
    @location(0)       col: vec4<f32>,
}

@vertex
fn vs_main(
    @location(0) pos:   vec2<f32>,
    @location(1) color: vec4<f32>,
) -> VertexOut {
    // Origin-local Mercator → screen pixel (logical), through the shared prelude's
    // ONE projection writer: subtract-before-scale (so the (pos − ref) difference is
    // formed on two SMALL f32 and stays precise at huge zoom), then the ground frame,
    // then the pane centre.
    let s = project_ground_px(
        pos,
        vec2<f32>(u.ref_x, u.ref_y),
        vec2<f32>(u.zoom_x, u.zoom_y),
        u.rot,
        u.foreshorten,
        vec2<f32>(u.screen_center_x, u.screen_center_y),
    );
    // Screen pixel → clip space [-1, 1]; y is flipped (screen y=0 is top).
    let ndc = px_to_ndc(s, vec2<f32>(u.viewport_w, u.viewport_h));
    return VertexOut(vec4<f32>(ndc, 0.0, 1.0), color);
}

@fragment
fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
    // `tint` is 1.0 for every stream but the map's fills, where it carries the
    // tilt fade. Alpha is deliberately untouched — the fade darkens, it does not
    // dissolve, which is exactly what the CPU painter's `layer::fade` does.
    return vec4<f32>(in.col.rgb * u.tint, in.col.a);
}