Bring the five-panel hex portal online with a browser 3D/schematic preview, Pi SPI backends, and renamed multi-panel Pico UDP firmware. Co-authored-by: Cursor <cursoragent@cursor.com>
83 lines
2.0 KiB
Python
83 lines
2.0 KiB
Python
"""Color helpers for LED animations."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import math
|
||
from typing import Tuple
|
||
|
||
RGB = Tuple[int, int, int]
|
||
|
||
# Logical RGB (R, G, B) — same as neopixel: RED=(255,0,0), GREEN=(0,255,0), etc.
|
||
# Firmware and Pi SPI convert to GRB on the wire (NeoPixel default pixel_order).
|
||
RED: RGB = (255, 0, 0)
|
||
GREEN: RGB = (0, 255, 0)
|
||
BLUE: RGB = (0, 0, 255)
|
||
WHITE: RGB = (255, 255, 255)
|
||
OFF: RGB = (0, 0, 0)
|
||
|
||
RGB_CYCLE: tuple[tuple[str, RGB], ...] = (
|
||
("RED", RED),
|
||
("GREEN", GREEN),
|
||
("BLUE", BLUE),
|
||
)
|
||
|
||
|
||
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),
|
||
)
|