export function hsvToRgb( h: number, s: number, v: number ): [number, number, number] { const c = v * s; const x = c * (1 - Math.abs(((h * 6) % 2) - 1)); const m = v - c; let r = 0, g = 0, b = 0; if (h < 1 / 6) { r = c; g = x; b = 0; } else if (h < 2 / 6) { r = x; g = c; b = 0; } else if (h < 3 / 6) { r = 0; g = c; b = x; } else if (h < 4 / 6) { r = 0; g = x; b = c; } else if (h < 5 / 6) { r = x; g = 0; b = c; } else { r = c; g = 0; b = x; } return [ Math.round((r + m) * 255), Math.round((g + m) * 255), Math.round((b + m) * 255), ]; } export function rainbowColor(value: number): [number, number, number] { const phase = (value / 255.0) * 6; const segment = Math.floor(phase); const remainder = phase - segment; const t = remainder; const q = 1 - t; switch (segment % 6) { case 0: return [255, Math.round(t * 255), 0]; case 1: return [Math.round(q * 255), 255, 0]; case 2: return [0, 255, Math.round(t * 255)]; case 3: return [0, Math.round(q * 255), 255]; case 4: return [Math.round(t * 255), 0, 255]; case 5: return [255, 0, Math.round(q * 255)]; default: return [255, 255, 255]; } } export function thermalColor(value: number): [number, number, number] { const t = value / 255.0; if (t < 0.25) { return [0, 0, Math.round(t * 4 * 255)]; } else if (t < 0.5) { return [0, Math.round((t - 0.25) * 4 * 255), 255]; } else if (t < 0.75) { return [ Math.round((t - 0.5) * 4 * 255), 255, Math.round((0.75 - t) * 4 * 255), ]; } else { return [255, 255, Math.round((t - 0.75) * 4 * 255)]; } } export function neonColor(value: number): [number, number, number] { const t = value / 255.0; const intensity = Math.pow(Math.sin(t * Math.PI), 2); const glow = Math.pow(intensity, 0.5); return [ Math.round(glow * 255), Math.round(intensity * 255), Math.round(Math.pow(intensity, 2) * 255), ]; } export function sunsetColor(value: number): [number, number, number] { const t = value / 255.0; if (t < 0.3) { return [Math.round(t * 3.33 * 255), 0, Math.round(t * 1.67 * 255)]; } else if (t < 0.6) { const p = (t - 0.3) / 0.3; return [255, Math.round(p * 100), Math.round(50 * (1 - p))]; } else { const p = (t - 0.6) / 0.4; return [255, Math.round(100 + p * 155), Math.round(p * 100)]; } } export function oceanColor(value: number): [number, number, number] { const t = value / 255.0; if (t < 0.25) { return [0, Math.round(t * 2 * 255), Math.round(100 + t * 4 * 155)]; } else if (t < 0.5) { const p = (t - 0.25) / 0.25; return [0, Math.round(128 + p * 127), 255]; } else if (t < 0.75) { const p = (t - 0.5) / 0.25; return [Math.round(p * 100), 255, Math.round(255 - p * 100)]; } else { const p = (t - 0.75) / 0.25; return [Math.round(100 + p * 155), 255, Math.round(155 + p * 100)]; } } export function forestColor(value: number): [number, number, number] { const t = value / 255.0; if (t < 0.3) { return [Math.round(t * 2 * 255), Math.round(50 + t * 3 * 205), 0]; } else if (t < 0.6) { const p = (t - 0.3) / 0.3; return [Math.round(150 - p * 100), 255, Math.round(p * 100)]; } else { const p = (t - 0.6) / 0.4; return [Math.round(50 + p * 100), Math.round(255 - p * 100), Math.round(100 + p * 55)]; } } export function copperColor(value: number): [number, number, number] { const t = value / 255.0; if (t < 0.4) { return [Math.round(t * 2.5 * 255), Math.round(t * 1.5 * 255), Math.round(t * 0.5 * 255)]; } else if (t < 0.7) { const p = (t - 0.4) / 0.3; return [255, Math.round(153 + p * 102), Math.round(51 + p * 51)]; } else { const p = (t - 0.7) / 0.3; return [255, 255, Math.round(102 + p * 153)]; } } export function ditheredColor(value: number): [number, number, number] { const levels = 4; const step = 255 / (levels - 1); const quantized = Math.round(value / step) * step; const error = value - quantized; const dither = (Math.random() - 0.5) * 32; const final = Math.max(0, Math.min(255, quantized + error + dither)); return [final, final, final]; } export function paletteColor(value: number): [number, number, number] { const palette = [ [0, 0, 0], [87, 29, 149], [191, 82, 177], [249, 162, 162], [255, 241, 165], [134, 227, 206], [29, 161, 242], [255, 255, 255], ]; const index = Math.floor((value / 255.0) * (palette.length - 1)); return palette[index] as [number, number, number]; } export function calculateColorDirect( absValue: number, renderMode: string ): [number, number, number] { switch (renderMode) { case 'classic': return [absValue, (absValue * 2) % 256, (absValue * 3) % 256]; case 'grayscale': return [absValue, absValue, absValue]; case 'red': return [absValue, 0, 0]; case 'green': return [0, absValue, 0]; case 'blue': return [0, 0, absValue]; case 'forest': return forestColor(absValue); case 'copper': return copperColor(absValue); case 'rainbow': return rainbowColor(absValue); case 'thermal': return thermalColor(absValue); case 'neon': return neonColor(absValue); case 'sunset': return sunsetColor(absValue); case 'ocean': return oceanColor(absValue); case 'dithered': return ditheredColor(absValue); case 'palette': return paletteColor(absValue); default: return [absValue, absValue, absValue]; } }