Files
led-driver/src/patterns/particles.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

109 lines
3.9 KiB
Python

import random
import utime
from patterns.pattern_modes import style_mode
_LEGACY = {"starfall": 1}
class Particles:
def __init__(self, driver):
self.driver = driver
def _run_snowfall(self, preset, colors, flakes, last):
density = max(1, int(preset.n1) if int(preset.n1) > 0 else 20)
speed = max(1, int(preset.n2) if int(preset.n2) > 0 else 1)
d = max(1, int(preset.d))
now = utime.ticks_ms()
if utime.ticks_diff(now, last) < d:
return flakes, last, False
bg_color = self.driver.apply_brightness(preset.background_or(colors), preset.b)
if random.randint(0, 255) < density:
flakes.append([self.driver.num_leds - 1, random.randint(0, len(colors) - 1)])
for i in range(self.driver.num_leds):
self.driver.n[i] = bg_color
nf = []
for pos, ci in flakes:
if 0 <= pos < self.driver.num_leds:
self.driver.n[pos] = self.driver.apply_brightness(colors[ci], preset.b)
pos -= speed
if pos >= -1:
nf.append([pos, ci])
self.driver.n.write()
return nf, utime.ticks_add(last, d), True
def _run_starfall(self, preset, colors, stars, last):
rate = max(1, min(255, int(preset.n1) if int(preset.n1) > 0 else 14))
speed = max(1, int(preset.n2) if int(preset.n2) > 0 else 2)
tail = max(2, int(preset.n3) if int(preset.n3) > 0 else 10)
max_stars = 4
d = max(1, int(preset.d))
now = utime.ticks_ms()
if utime.ticks_diff(now, last) < d:
return stars, 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
if len(stars) < max_stars and random.randint(0, 255) < rate:
top = self.driver.num_leds - 1 + random.randint(
0, min(8, self.driver.num_leds // 2)
)
stars.append({"h": float(top), "ci": random.randint(0, len(colors) - 1)})
ns = []
for s in stars:
h = s["h"]
ci = s["ci"]
ih = int(h)
for t in range(tail):
idx = ih + t
if 0 <= idx < self.driver.num_leds:
fade = 255 - (t * 255 // max(1, tail - 1))
base = colors[ci]
lit = (
(base[0] * fade) // 255,
(base[1] * fade) // 255,
(base[2] * fade) // 255,
)
lit = self.driver.apply_brightness(lit, preset.b)
o = self.driver.n[idx]
self.driver.n[idx] = (
max(o[0], lit[0]),
max(o[1], lit[1]),
max(o[2], lit[2]),
)
h -= speed
if h >= -tail:
s["h"] = h
ns.append(s)
stars = ns
self.driver.n.write()
return stars, utime.ticks_add(last, d), True
def run(self, preset):
"""Falling particles: n6 0 snowfall flakes, 1 starfall streaks."""
mode = style_mode(preset, 0, _LEGACY)
colors = preset.c if preset.c else [(255, 255, 255), (180, 220, 255)]
last = utime.ticks_ms()
if mode == 1:
colors = preset.c if preset.c else [
(255, 255, 255),
(200, 230, 255),
(255, 248, 220),
]
stars = []
while True:
stars, last, stepped = self._run_starfall(preset, colors, stars, last)
if stepped and not preset.a:
yield
return
yield
flakes = []
while True:
flakes, last, stepped = self._run_snowfall(preset, colors, flakes, last)
if stepped and not preset.a:
yield
return
yield