1/** Colormaps as 256-entry RGBA lookup tables, built from control points. */
3function lut(stops: string[]): Uint8Array<ArrayBuffer> {
4 const rgb = stops.map((h) => [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16)));
5 const out = new Uint8Array(256 * 4);
6 for (let i = 0; i < 256; i++) {
7 const t = (i / 255) * (rgb.length - 1);
8 const j = Math.min(rgb.length - 2, Math.floor(t));
9 const f = t - j;
10 for (let c = 0; c < 3; c++) out[4 * i + c] = Math.round(rgb[j][c] * (1 - f) + rgb[j + 1][c] * f);
11 out[4 * i + 3] = 255;
12 }
13 return out;
14}
16/** ColorBrewer RdBu, reversed: blue for negative pressure, red for positive. */
17export const DIVERGING = lut(['#053061', '#2166ac', '#4393c3', '#92c5de', '#d1e5f0', '#f7f7f7', '#fddbc7', '#f4a582', '#d6604d', '#b2182b', '#67001f']);
19/** Viridis, for the level in dB. */
20export const SEQUENTIAL = lut(['#440154', '#482878', '#3e4989', '#31688e', '#26828e', '#1f9e89', '#35b779', '#6ece58', '#b5de2b', '#fde725']);
22/** A vertical colorbar into a canvas. */
23export function drawColorbar(canvas: HTMLCanvasElement, map: Uint8Array) {
24 const ctx = canvas.getContext('2d')!;
25 const img = ctx.createImageData(1, 256);
26 for (let i = 0; i < 256; i++) img.data.set(map.subarray(4 * (255 - i), 4 * (255 - i) + 4), 4 * i);
27 const tmp = document.createElement('canvas');
28 tmp.width = 1;
29 tmp.height = 256;
30 tmp.getContext('2d')!.putImageData(img, 0, 0);
31 ctx.imageSmoothingEnabled = true;
32 ctx.clearRect(0, 0, canvas.width, canvas.height);
33 ctx.drawImage(tmp, 0, 0, canvas.width, canvas.height);
34}