// Minimal family: a survey drawing of a landscape — nested contour lines on a
// ground plane running to the horizon, every fifth one heavier, and water
// standing in the low ground.
//
// Horizon Lines is a bundle of parallel rules and Ridge Terrain is stacked
// silhouettes: both describe a landscape by what it hides. Contours describe it
// by what it measures, so the lines are closed, nested and never cross, and the
// shape of the land is legible from the spacing alone. The water level is the
// slow tide of the piece — the same terrain drowning and draining.

export const contourMap = {
    name: 'Contour Map',
    family: 'minimal',
    kind: 'fragment',
    // Drafting, not photography.
    texture: 0.3,
    consumes: ['ink'],
    traits: ['camera', 'space', 'style'],

    params: {
        levels:  { type: 'float', range: [4, 30],      default: 12,   uniform: 'u_levels', bias: 'density' },
        relief:  { type: 'float', range: [0.4, 3.0],   default: 1.2,  uniform: 'u_relief' },
        sea:     { type: 'float', range: [0.1, 0.9],   default: 0.42, uniform: 'u_sea' },
        indexN:  { type: 'int',   range: [3, 8],       default: 5,    uniform: 'u_indexN' },
        weight:  { type: 'float', range: [0.03, 0.3],  default: 0.1,  uniform: 'u_weight' },
        shore:   { type: 'float', range: [0, 1.4],     default: 0.6,  uniform: 'u_shore', bias: 'energy' },
        drift:   { type: 'float', range: [0.002, 0.03], default: 0.008, uniform: 'u_drift', bias: 'motion', rate: true },
        palette: { type: 'palette', count: 5 },
    },

    reactive: {
        shore:  { feature: 'loudness', amount: 0.3,  response: 'smooth' },
        relief: { feature: 'bandLow',  amount: 0.2,  response: 'smooth' },
    },

    shader: `
// The land. Slow enough that the survey is being redrawn rather than animated.
float land(vec2 g, float t) {
    return fbm(g * u_relief + vec2(t, t * 0.6), 4);
}

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

    float horizon = sigHorizonY() * 0.4 + 0.35;
    float below = horizon - p.y;

    // Sky: the paper the survey is drawn on, lit from the horizon.
    vec3 col = mix(pal(1) * 0.2, pal(0) * 0.07, sat((p.y - horizon) * 1.4 + 0.15));
    col += pal(3) * exp(-abs(p.y - horizon) * 10.0) * 0.15;

    if (below > 0.002) {
        float depth = 1.0 / max(below, 0.03);
        vec2 g = vec2(p.x * depth, depth) * 0.5;

        float h = land(g, t);

        // Contours. The band is measured in HEIGHT, not in pixels, which is
        // what makes the lines crowd on steep ground and spread on flat ground
        // without anything having to compute a gradient.
        float f = h * u_levels;
        float band = abs(fract(f) - 0.5) * 2.0;
        float index = abs(fract(f / float(u_indexN)) - 0.5) * 2.0;

        float w = u_weight * (0.35 + u_sigLine * 1.8);
        float line = smoothstep(w, w * 0.2, band);
        float heavy = smoothstep(w * 1.6, w * 0.3, index);

        vec3 ground = pal(0) * 0.1;
        ground += palRamp(0.3 + h * 0.3) * line * 0.5;
        ground += pal(4) * heavy * 0.35;

        // Water standing in everything below the level. A flat tone, because
        // water has no contours — which is exactly what makes the level legible.
        float wet = smoothstep(u_sea + 0.02, u_sea - 0.02, h);
        vec3 water = mix(pal(2) * 0.7, pal(3) * 0.45, sat(below));
        // Water takes the sky, so a flooded frame is a bright one and the level
        // is legible from across the room.
        water += pal(3) * exp(-below * 1.6) * u_shore * 0.5;
        ground = mix(ground, water, wet);

        // Shoreline: the one bright edge in the drawing.
        float edge = smoothstep(0.05, 0.0, abs(h - u_sea));
        ground += pal(3) * edge * u_shore * 0.8;

        col = mix(col, ground, smoothstep(0.0, 0.03, below));
    }

    col = sigAir(col, p, sat(1.0 - below * 0.9));
    return vec4(inkValue(col), 1.0);
}
`,
};

export default contourMap;
