/ concept-collection / mesh-pde-solver
Sign in
concept-collection / mesh-pde-solver
mesh-pde-solver / src / render / colormap.ts
42 lines · 1.4 KBBlameHistoryRaw
1/** Parula-inspired colormap (same table surfacefun-interactive uses). */
3const COLORMAP: [number, number, number][] = [
4 [0.2422, 0.1504, 0.6603],
5 [0.281, 0.3228, 0.9579],
6 [0.1786, 0.5289, 0.9682],
7 [0.0689, 0.6948, 0.8394],
8 [0.128, 0.789, 0.5927],
9 [0.3391, 0.849, 0.3798],
10 [0.633, 0.8518, 0.2091],
11 [0.8902, 0.8044, 0.1137],
12 [0.9905, 0.6816, 0.0235],
13 [0.9763, 0.517, 0.034],
16export function colormapLookup(t: number): [number, number, number] {
17 const safe = Number.isFinite(t) ? t : 0
18 const clamped = Math.max(0, Math.min(1, safe))
19 const idx = clamped * (COLORMAP.length - 1)
20 const lo = Math.floor(idx)
21 const hi = Math.min(lo + 1, COLORMAP.length - 1)
22 const frac = idx - lo
23 return [
24 COLORMAP[lo][0] + frac * (COLORMAP[hi][0] - COLORMAP[lo][0]),
25 COLORMAP[lo][1] + frac * (COLORMAP[hi][1] - COLORMAP[lo][1]),
26 COLORMAP[lo][2] + frac * (COLORMAP[hi][2] - COLORMAP[lo][2]),
27 ]
30/** CSS gradient stops sampling the colormap, for the colorbar overlay. */
31export function colormapGradient(direction: string): string {
32 const stops: string[] = []
33 const n = 32
34 for (let i = 0; i < n; i++) {
35 const t = i / (n - 1)
36 const [r, g, b] = colormapLookup(t)
37 stops.push(
38 `rgb(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)}) ${((t * 100).toFixed(2))}%`,
39 )
40 }
41 return `linear-gradient(${direction}, ${stops.join(',')})`
moveopenescclose