{"version":3,"file":"scales.cjs","names":[],"sources":["../../src/br/scales.ts"],"sourcesContent":["import { lerpColor } from \"./svg-utils\";\n\n/** Maps a numeric value to a CSS color string. */\nexport type ColorScale = (value: number) => string;\n\n/**\n * Sequential, colorblind-safe palettes (ColorBrewer / Viridis families, public\n * domain). Ordered light → dark. Swap for your brand ramp if needed — any\n * ordered list of hex colors works with the scale builders below.\n */\nexport const SEQUENTIAL_BLUES = [\n    \"#eff3ff\",\n    \"#c6dbef\",\n    \"#9ecae1\",\n    \"#6baed6\",\n    \"#3182bd\",\n    \"#08519c\",\n] as const;\n\nexport const SEQUENTIAL_GREENS = [\n    \"#edf8e9\",\n    \"#c7e9c0\",\n    \"#a1d99b\",\n    \"#74c476\",\n    \"#31a354\",\n    \"#006d2c\",\n] as const;\n\nexport const SEQUENTIAL_VIRIDIS = [\n    \"#440154\",\n    \"#414487\",\n    \"#2a788e\",\n    \"#22a884\",\n    \"#7ad151\",\n    \"#fde725\",\n] as const;\n\n/** Diverging palette (red ↔ neutral ↔ blue), for signed metrics. */\nexport const DIVERGING_RDBU = [\n    \"#b2182b\",\n    \"#ef8a62\",\n    \"#fddbc7\",\n    \"#d1e5f0\",\n    \"#67a9cf\",\n    \"#2166ac\",\n] as const;\n\n/**\n * Interpolate a palette at `t ∈ [0, 1]`, blending between the two nearest\n * stops. `t <= 0` → first color, `t >= 1` → last.\n */\nexport function interpolatePalette(palette: readonly string[], t: number): string {\n    if (palette.length === 0) return \"#000000\";\n    if (palette.length === 1) return palette[0];\n    const clamped = Math.max(0, Math.min(1, t));\n    const scaled = clamped * (palette.length - 1);\n    const i = Math.floor(scaled);\n    if (i >= palette.length - 1) return palette[palette.length - 1];\n    return lerpColor(palette[i], palette[i + 1], scaled - i);\n}\n\n/**\n * Continuous scale: linearly interpolate `palette` across `[min, max]`.\n *\n * @example\n * const color = sequentialScale(0, 100, SEQUENTIAL_BLUES);\n * <BrazilMap values={data} colorScale={color} />;\n */\nexport function sequentialScale(\n    min: number,\n    max: number,\n    palette: readonly string[] = SEQUENTIAL_BLUES,\n): ColorScale {\n    const span = max - min || 1;\n    return (value) => interpolatePalette(palette, (value - min) / span);\n}\n\n/**\n * Reject an empty palette where a colour has to come out.\n *\n * `interpolatePalette` already guarded this and the two factories did not, so an\n * empty array made them index at `-1` and return `undefined` announced as\n * `string` — which reaches the DOM as `fill=\"undefined\"` and paints nothing,\n * with no error anywhere. Throwing at construction puts the failure on the line\n * that built the scale instead of on every shape it colours.\n *\n * @param palette - The palette handed to a scale factory.\n * @param factory - Name of the caller, used in the message.\n * @throws {Error} When the palette has no entries.\n * @returns Nothing.\n */\nfunction assertPalette(palette: readonly string[], factory: string): void {\n    if (palette.length === 0) {\n        throw new Error(`${factory}: palette must have at least one colour`);\n    }\n}\n\n/**\n * Discrete scale: split `[min, max]` into `palette.length` equal buckets and\n * return the bucket's color (a classic choropleth \"quantize\" scale).\n */\nexport function quantizeScale(\n    min: number,\n    max: number,\n    palette: readonly string[] = SEQUENTIAL_BLUES,\n): ColorScale {\n    assertPalette(palette, \"quantizeScale\");\n    const span = max - min || 1;\n    const n = palette.length;\n    return (value) => {\n        const idx = Math.min(n - 1, Math.max(0, Math.floor(((value - min) / span) * n)));\n        return palette[idx] as string;\n    };\n}\n\n/**\n * Threshold scale: `palette[i]` applies to values in\n * `[thresholds[i-1], thresholds[i])`. `palette` must have\n * `thresholds.length + 1` entries.\n *\n * @example\n * const color = thresholdScale([10, 50, 100], SEQUENTIAL_GREENS.slice(0, 4));\n */\nexport function thresholdScale(\n    thresholds: readonly number[],\n    palette: readonly string[],\n): ColorScale {\n    assertPalette(palette, \"thresholdScale\");\n    return (value) => {\n        let i = 0;\n        while (i < thresholds.length && value >= (thresholds[i] as number)) i += 1;\n        return palette[Math.min(i, palette.length - 1)] as string;\n    };\n}\n"],"mappings":"mCAUA,IAAa,EAAmB,CAC5B,UACA,UACA,UACA,UACA,UACA,SACJ,EAEa,EAAoB,CAC7B,UACA,UACA,UACA,UACA,UACA,SACJ,EAEa,EAAqB,CAC9B,UACA,UACA,UACA,UACA,UACA,SACJ,EAGa,EAAiB,CAC1B,UACA,UACA,UACA,UACA,UACA,SACJ,EAMA,SAAgB,EAAmB,EAA4B,EAAmB,CAC9E,GAAI,EAAQ,SAAW,EAAG,MAAO,UACjC,GAAI,EAAQ,SAAW,EAAG,OAAO,EAAQ,GAEzC,IAAM,EADU,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,CAAC,CAC1B,GAAW,EAAQ,OAAS,GACrC,EAAI,KAAK,MAAM,CAAM,EAE3B,OADI,GAAK,EAAQ,OAAS,EAAU,EAAQ,EAAQ,OAAS,GACtD,EAAA,UAAU,EAAQ,GAAI,EAAQ,EAAI,GAAI,EAAS,CAAC,CAC3D,CASA,SAAgB,EACZ,EACA,EACA,EAA6B,EACnB,CACV,IAAM,EAAO,EAAM,GAAO,EAC1B,MAAQ,IAAU,EAAmB,GAAU,EAAQ,GAAO,CAAI,CACtE,CAgBA,SAAS,EAAc,EAA4B,EAAuB,CACtE,GAAI,EAAQ,SAAW,EACnB,MAAU,MAAM,GAAG,EAAQ,wCAAwC,CAE3E,CAMA,SAAgB,EACZ,EACA,EACA,EAA6B,EACnB,CACV,EAAc,EAAS,eAAe,EACtC,IAAM,EAAO,EAAM,GAAO,EACpB,EAAI,EAAQ,OAClB,MAAQ,IAEG,EADK,KAAK,IAAI,EAAI,EAAG,KAAK,IAAI,EAAG,KAAK,OAAQ,EAAQ,GAAO,EAAQ,CAAC,CAAC,CAC/D,EAEvB,CAUA,SAAgB,EACZ,EACA,EACU,CAEV,OADA,EAAc,EAAS,gBAAgB,EAC/B,GAAU,CACd,IAAI,EAAI,EACR,KAAO,EAAI,EAAW,QAAU,GAAU,EAAW,IAAe,GAAK,EACzE,OAAO,EAAQ,KAAK,IAAI,EAAG,EAAQ,OAAS,CAAC,EACjD,CACJ"}