Add Docker deployment support, richer Selenium/LED pattern tests, in-browser diagnostics, responsive UI improvements, and 16x16 panel simulation tooling to speed iteration and hardware-style prototyping. Made-with: Cursor
68 lines
1.9 KiB
Python
68 lines
1.9 KiB
Python
"""LED pattern helpers inspired by embedded NeoPixel drivers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import random
|
|
|
|
|
|
Color = tuple[int, int, int]
|
|
|
|
|
|
def _clamp(channel: int) -> int:
|
|
return max(0, min(255, int(channel)))
|
|
|
|
|
|
def wheel(pos: int) -> Color:
|
|
"""Return a rainbow color for position 0-255."""
|
|
pos = 255 - (pos & 255)
|
|
if pos < 85:
|
|
return (_clamp(255 - pos * 3), 0, _clamp(pos * 3))
|
|
if pos < 170:
|
|
pos -= 85
|
|
return (0, _clamp(pos * 3), _clamp(255 - pos * 3))
|
|
pos -= 170
|
|
return (_clamp(pos * 3), _clamp(255 - pos * 3), 0)
|
|
|
|
|
|
def rainbow_frame(led_count: int, frame: int, step: int = 4) -> list[Color]:
|
|
"""Generate one rainbow frame across all LEDs."""
|
|
if led_count <= 0:
|
|
return []
|
|
return [wheel((i * 256 // led_count + frame * step) & 255) for i in range(led_count)]
|
|
|
|
|
|
def chase_frame(
|
|
led_count: int,
|
|
frame: int,
|
|
color: Color = (255, 120, 0),
|
|
tail: Color = (16, 0, 0),
|
|
) -> list[Color]:
|
|
"""Generate a two-pixel chase pattern."""
|
|
if led_count <= 0:
|
|
return []
|
|
out: list[Color] = [(0, 0, 0) for _ in range(led_count)]
|
|
head = frame % led_count
|
|
trail = (head - 1) % led_count
|
|
out[trail] = tuple(_clamp(v) for v in tail) # type: ignore[assignment]
|
|
out[head] = tuple(_clamp(v) for v in color) # type: ignore[assignment]
|
|
return out
|
|
|
|
|
|
def twinkle_frame(
|
|
led_count: int,
|
|
frame: int,
|
|
base: Color = (0, 0, 8),
|
|
sparkle: Color = (255, 255, 180),
|
|
sparkles: int = 3,
|
|
seed: int = 1337,
|
|
) -> list[Color]:
|
|
"""Generate deterministic twinkle frames for testing/replay."""
|
|
if led_count <= 0:
|
|
return []
|
|
out: list[Color] = [tuple(_clamp(v) for v in base) for _ in range(led_count)] # type: ignore[list-item]
|
|
rng = random.Random(seed + frame)
|
|
for _ in range(min(max(0, sparkles), led_count)):
|
|
idx = rng.randrange(led_count)
|
|
out[idx] = tuple(_clamp(v) for v in sparkle) # type: ignore[assignment]
|
|
return out
|