Files
led-driver/src/patterns/sparkle.py
Jimmy 55a97ac51c feat(patterns): merge pattern styles and add mode support
Consolidate legacy pattern ids into meteor, particles, sparkle, chase,
and colour_cycle with n6/mode style selection; add pattern_modes helper,
self-contained tests/all.py, and preset mode alias on wire.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-16 21:14:54 +12:00

148 lines
5.6 KiB
Python

import random
import utime
from patterns.pattern_modes import style_mode
_LEGACY = {"sparkle_trail": 0, "ice_sparkle": 1, "fireflies": 2}
class Sparkle:
def __init__(self, driver):
self.driver = driver
def _run_trail(self, preset, colors, last):
density = max(1, int(preset.n1) if int(preset.n1) > 0 else 24)
decay = max(1, min(255, int(preset.n2) if int(preset.n2) > 0 else 210))
d = max(1, int(preset.d))
now = utime.ticks_ms()
if utime.ticks_diff(now, last) < d:
return last, False
for i in range(self.driver.num_leds):
r, g, b = self.driver.n[i]
self.driver.n[i] = ((r * decay) // 255, (g * decay) // 255, (b * decay) // 255)
sparks = max(1, self.driver.num_leds * density // 255)
for _ in range(sparks):
idx = random.randint(0, max(0, self.driver.num_leds - 1))
c = self.driver.apply_brightness(
colors[random.randint(0, len(colors) - 1)], preset.b
)
self.driver.n[idx] = c
self.driver.n.write()
return utime.ticks_add(last, d), True
def _run_ice(self, preset, colors, sparks, last):
rate = max(1, min(255, int(preset.n1) if int(preset.n1) > 0 else 55))
decay = max(1, min(255, int(preset.n2) if int(preset.n2) > 0 else 140))
halo = max(0, min(3, int(preset.n3)))
cap = 28
d = max(1, int(preset.d))
now = utime.ticks_ms()
if utime.ticks_diff(now, last) < d:
return sparks, last, False
bg = self.driver.apply_brightness(preset.background_or(colors), preset.b)
for i in range(self.driver.num_leds):
self.driver.n[i] = bg
ns = []
for s in sparks:
lv = s["lv"] - decay
if lv > 0:
s["lv"] = lv
ns.append(s)
sparks = ns
if len(sparks) < cap and random.randint(0, 255) < rate:
sparks.append(
{
"p": random.randint(0, max(0, self.driver.num_leds - 1)),
"lv": 255,
"ci": random.randint(0, len(colors) - 1),
}
)
for s in sparks:
p = s["p"]
lv = s["lv"]
ci = s["ci"]
base = colors[ci]
for off in range(-halo, halo + 1):
idx = p + off
if 0 <= idx < self.driver.num_leds:
dist = abs(off)
fac = lv if dist == 0 else (lv * (halo - dist + 1)) // (halo + 1)
lit = self.driver.apply_brightness(
(
(base[0] * fac) // 255,
(base[1] * fac) // 255,
(base[2] * fac) // 255,
),
preset.b,
)
o = self.driver.n[idx]
self.driver.n[idx] = (
min(255, o[0] + lit[0]),
min(255, o[1] + lit[1]),
min(255, o[2] + lit[2]),
)
self.driver.n.write()
return sparks, utime.ticks_add(last, d), True
def _run_fireflies(self, preset, colors, bugs, last):
count = max(1, int(preset.n1) if int(preset.n1) > 0 else 6)
speed = max(1, int(preset.n2) if int(preset.n2) > 0 else 8)
d = max(1, int(preset.d))
now = utime.ticks_ms()
if utime.ticks_diff(now, last) < d:
return bugs, last, False
bg_color = self.driver.apply_brightness(preset.background_or(colors), preset.b)
for i in range(self.driver.num_leds):
self.driver.n[i] = bg_color
for b in bugs:
idx, ph = b
tri = 255 - abs(128 - ph) * 2
c = self.driver.apply_brightness(colors[idx % len(colors)], preset.b)
self.driver.n[idx] = ((c[0] * tri) // 255, (c[1] * tri) // 255, (c[2] * tri) // 255)
b[1] = (ph + speed) & 255
if random.randint(0, 31) == 0:
b[0] = random.randint(0, max(0, self.driver.num_leds - 1))
self.driver.n.write()
return bugs, utime.ticks_add(last, d), True
def run(self, preset):
"""Sparkles: n6 0 trail decay, 1 ice burst+halo, 2 fireflies."""
mode = style_mode(preset, 0, _LEGACY)
colors = preset.c if preset.c else [(120, 120, 255)]
last = utime.ticks_ms()
if mode == 2:
colors = preset.c if preset.c else [(255, 210, 80), (120, 255, 120)]
count = max(1, int(preset.n1) if int(preset.n1) > 0 else 6)
bugs = [
[random.randint(0, max(0, self.driver.num_leds - 1)), random.randint(0, 255)]
for _ in range(count)
]
while True:
bugs, last, stepped = self._run_fireflies(preset, colors, bugs, last)
if stepped and not preset.a:
yield
return
yield
if mode == 1:
colors = preset.c if preset.c else [
(240, 248, 255),
(200, 235, 255),
(255, 255, 255),
]
sparks = []
while True:
sparks, last, stepped = self._run_ice(preset, colors, sparks, last)
if stepped and not preset.a:
yield
return
yield
while True:
last, stepped = self._run_trail(preset, colors, last)
if stepped and not preset.a:
yield
return
yield