/** * Single source of truth for 3D engine colors and perceptual colormaps. */ export const PALETTE_3D = { // Scene environment & lighting BACKGROUND: 0x050505, FOG: 0x050505, GRID_PRIMARY: 0x222222, GRID_SECONDARY: 0x111111, AMBIENT_LIGHT: 0xffffff, DIRECTIONAL_LIGHT: 0xffffff, // Axis Gizmo / Navigation AXIS_X: 0xff4040, AXIS_Y: 0x40e060, AXIS_Z: 0x4090ff, // Default thread & state colors THREAD_DEFAULT: 0x00ff00, THREAD_COMPARE_A: 0x00ff00, THREAD_COMPARE_B: 0xff0000, THREAD_COMPARE_C: 0x00ff00, THREAD_RESULT: 0xFF00FF, STATE_GHOST: 0xFF6B9D, STATE_PALETTE: [ 0x6C63FF, 0xFF6B9D, 0x3B82F6, 0x22C55E, 0xF59E0B, 0xEC4899, 0x14B8A6 ] }; // Viridis Perceptual Colormap LUT (9 key control points from t=0.0 to t=1.0) export const VIRIDIS_LUT = [ [0.267004, 0.004874, 0.329415], // 0.000 [0.282327, 0.160431, 0.457811], // 0.125 [0.253935, 0.265254, 0.529983], // 0.250 [0.191090, 0.364218, 0.548574], // 0.375 [0.127568, 0.456911, 0.550996], // 0.500 [0.121148, 0.547463, 0.506309], // 0.625 [0.236780, 0.636730, 0.420680], // 0.750 [0.460673, 0.723062, 0.303117], // 0.875 [0.993248, 0.906157, 0.143936] // 1.000 ]; /** * Samples the Viridis colormap at position t in [0, 1]. * @param {number} t Position in range [0, 1] * @returns {{ r: number, g: number, b: number }} RGB values in range [0, 1] */ export function sampleViridis(t) { const clampedT = Math.max(0.0, Math.min(1.0, t)); const scaled = clampedT * (VIRIDIS_LUT.length - 1); const idx = Math.floor(scaled); const frac = scaled - idx; if (idx >= VIRIDIS_LUT.length - 1) { const c = VIRIDIS_LUT[VIRIDIS_LUT.length - 1]; return { r: c[0], g: c[1], b: c[2] }; } const c1 = VIRIDIS_LUT[idx]; const c2 = VIRIDIS_LUT[idx + 1]; return { r: c1[0] + frac * (c2[0] - c1[0]), g: c1[1] + frac * (c2[1] - c1[1]), b: c1[2] + frac * (c2[2] - c1[2]) }; } /** * Maps activation v in [-1, 1] to a perceptual Viridis RGBA color. * Replaces the old HSL rainbow mapping across JS visualizers. * @param {number} val Activation value in [-1, 1] * @returns {{ r: number, g: number, b: number, alpha: number }} */ export function getActivationColorPerceptual(val) { const clampedV = Math.max(-1.0, Math.min(1.0, val)); const absV = Math.abs(clampedV); const alpha = Math.min(Math.max(Math.pow(absV, 1.2), 0.05), 1.0); const t01 = (clampedV + 1.0) * 0.5; const rgb = sampleViridis(t01); return { r: rgb.r, g: rgb.g, b: rgb.b, alpha }; }