// Structural family: a suspension bridge passing overhead — two towers in
// frame at a time, the main cable slung between them, hangers dropping to a
// deck that runs off both sides.
//
// Girder Lattice is a truss: hundreds of short members in tension and
// compression, and the image is a texture of triangles. This is the opposite
// structure and reads as one — two long curves and a rhythm of verticals under
// them, with almost nothing else in the frame. The curve is the whole subject,
// and the only thing that repeats is the spacing.

export const suspensionSpan = {
    name: 'Suspension Span',
    family: 'structural',
    kind: 'fragment',
    texture: 0.6,
    consumes: ['cast', 'ink'],
    traits: ['shape', 'camera', 'space', 'style'],

    params: {
        span:    { type: 'float', range: [0.5, 2.2],   default: 1.1,  uniform: 'u_span', bias: 'density' },
        sag:     { type: 'float', range: [0.15, 0.9],  default: 0.5,  uniform: 'u_sag' },
        tower:   { type: 'float', range: [0.3, 1.1],   default: 0.7,  uniform: 'u_tower' },
        hangers: { type: 'float', range: [2, 22],      default: 9,    uniform: 'u_hangers', bias: 'density' },
        deck:    { type: 'float', range: [-0.7, 0.25], default: -0.2, uniform: 'u_deck', slowAxis: true },
        lamp:    { type: 'float', range: [0, 0.12],    default: 0.045, uniform: 'u_lamp' },
        travel:  { type: 'float', range: [0.01, 0.3],  default: 0.06, uniform: 'u_travel', bias: 'motion', rate: true },
        palette: { type: 'palette', count: 5 },
    },

    reactive: {
        lamp: { feature: 'beat',     amount: 0.2,  response: 'spike' },
    },

    shader: `
// Distance to a segment, for the one-off members that are not on a grid.
float seg(vec2 q, vec2 a, vec2 b) {
    vec2 ab = b - a, ap = q - a;
    float h = sat(dot(ap, ab) / max(dot(ab, ab), 1e-6));
    return length(ap - ab * h);
}

vec4 scene(vec2 uv, vec2 p) {
    float t = u_time * u_travel + u_seed;
    p = sigCamera(p);

    float horizon = sigHorizonY() * 0.3;
    float deckY = horizon + u_deck;

    // Sky, and the water or ground under the deck.
    vec3 col = mix(pal(1) * 0.2, pal(0) * 0.06, sat((p.y - horizon) * 0.6 + 0.25));
    col += pal(3) * exp(-abs(p.y - horizon) * 7.0) * 0.2;
    col = mix(col, pal(0) * 0.09, smoothstep(deckY + 0.01, deckY - 0.03, p.y));

    // Bays: the bridge repeats every span, and travelling means the whole
    // structure slides through that repeat.
    float g = (p.x + t) / u_span;
    float bay = fract(g) - 0.5;
    float bayId = floor(g);
    float x = bay * u_span;                       // metres from this bay's centre

    float towerTop = deckY + u_tower;
    float w = 0.006 + u_sigLine * 0.02;

    // Main cable: a parabola from tower top to tower top, lowest mid-bay.
    float cableY = towerTop - u_sag * u_tower * (1.0 - 4.0 * bay * bay);
    float dCable = abs(p.y - cableY);
    col += palRamp(0.15) * smoothstep(w * 1.6, w * 0.3, dCable) * (0.6 + 0.55 * 0.6);

    // Hangers: verticals from the cable down to the deck, on their own spacing.
    float hg = (p.x + t) * u_hangers / u_span;
    float hx = (fract(hg) - 0.5) * u_span / u_hangers;
    float hangerCableY = towerTop - u_sag * u_tower *
        (1.0 - 4.0 * pow(fract(g + (floor(hg) - hg + 0.5) / u_hangers) - 0.5, 2.0));
    float inSpan = step(p.y, hangerCableY) * step(deckY, p.y);
    col += palRamp(0.3) * smoothstep(w, w * 0.25, abs(hx)) * inSpan * (0.3 + 0.55 * 0.35);

    // Deck: one heavy line all the way across, which is what stops the cable
    // reading as a piece of string.
    col += pal(4) * smoothstep(w * 2.5, w * 0.6, abs(p.y - deckY)) * (0.4 + 0.55 * 0.5);
    col += pal(4) * inkStroke(abs(p.y - deckY) - w * 3.0) * 0.25;

    // Towers: a pair of legs and a crossbeam, with the signature form as the
    // lamp on top — the only object in the frame that is not a line.
    float dTower = min(seg(vec2(x, p.y), vec2(0.0, deckY - 0.1), vec2(0.0, towerTop)),
                       seg(vec2(x, p.y), vec2(-0.03, towerTop - 0.12), vec2(0.03, towerTop - 0.12)));
    col += palRamp(0.05) * smoothstep(w * 3.0, w * 1.2, dTower) * 0.7;

    if (u_lamp > 0.004) {
        float d = castMain((vec2(x, p.y) - vec2(0.0, towerTop + u_lamp)) / u_lamp) * u_lamp;
        col += pal(3) * smoothstep(0.004, -0.004, d) * (0.6 + 0.55);
        col += pal(3) * inkStroke(d) * 0.6;
    }

    // Every other bay is a little further off, so the line of the bridge has
    // some air in it rather than being a flat elevation drawing.
    col = sigAir(col, p, smoothstep(0.2, 1.4, abs(bay) * 0.7 + hash11(bayId) * 0.5));
    return vec4(inkValue(col), 1.0);
}
`,
};

export default suspensionSpan;
