Files
portal/leds/colors.py
Jimmy 5094c7bcee Add portal web simulator, SPI bridges, and Pico firmware updates.
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>
2026-07-30 14:54:51 +12:00

83 lines
2.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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.01.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),
)