"""Color helpers for LED animations.""" from __future__ import annotations import math from typing import Tuple RGB = Tuple[int, int, int] def wheel(pos: int) -> RGB: pos = pos % 256 if pos < 85: return (255 - pos * 3, pos * 3, 0) if pos < 170: pos -= 85 return (0, 255 - pos * 3, pos * 3) pos -= 170 return (pos * 3, 0, 255 - pos * 3) def heat_color(temperature: float) -> RGB: """Map 0.0–1.0 heat to black → red → yellow → white.""" t = max(0.0, min(1.0, temperature)) if t < 0.25: return (int(t * 4 * 255), 0, 0) if t < 0.5: return (255, int((t - 0.25) * 4 * 255), 0) if t < 0.75: return (255, 255, int((t - 0.5) * 4 * 255)) return (255, 255, 255) def hsv_to_rgb(h: float, s: float, v: float) -> RGB: h = h % 1.0 i = int(h * 6) f = h * 6 - i p = int(v * (1 - s) * 255) q = int(v * (1 - f * s) * 255) t = int(v * (1 - (1 - f) * s) * 255) v255 = int(v * 255) match i % 6: case 0: return (v255, t, p) case 1: return (q, v255, p) case 2: return (p, v255, t) case 3: return (p, q, v255) case 4: return (t, p, v255) case _: return (v255, p, q) def dim(color: RGB, scale: float) -> RGB: scale = max(0.0, min(1.0, scale)) return (int(color[0] * scale), int(color[1] * scale), int(color[2] * scale)) def blend(a: RGB, b: RGB, t: float) -> RGB: t = max(0.0, min(1.0, t)) return ( int(a[0] + (b[0] - a[0]) * t), int(a[1] + (b[1] - a[1]) * t), int(a[2] + (b[2] - a[2]) * t), )