feat(patterns): add northern wave, candle glow, starfall, ice sparkle
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
56
src/patterns/candle_glow.py
Normal file
56
src/patterns/candle_glow.py
Normal file
@@ -0,0 +1,56 @@
|
||||
import random
|
||||
import utime
|
||||
|
||||
|
||||
class CandleGlow:
|
||||
def __init__(self, driver):
|
||||
self.driver = driver
|
||||
|
||||
def run(self, preset):
|
||||
colors = preset.c if preset.c else [(255, 140, 40), (255, 200, 120), (255, 90, 20)]
|
||||
n_candles = max(1, min(self.driver.num_leds, int(preset.n1) if int(preset.n1) > 0 else 4))
|
||||
width = max(1, int(preset.n2) if int(preset.n2) > 0 else 3)
|
||||
flicker = max(1, min(255, int(preset.n3) if int(preset.n3) > 0 else 90))
|
||||
n_led = self.driver.num_leds
|
||||
centers = tuple(random.randint(0, max(0, n_led - 1)) for _ in range(n_candles))
|
||||
last = utime.ticks_ms()
|
||||
|
||||
while True:
|
||||
d = max(1, int(preset.d))
|
||||
now = utime.ticks_ms()
|
||||
if utime.ticks_diff(now, last) >= d:
|
||||
bg = self.driver.apply_brightness(preset.background_or(colors), preset.b)
|
||||
for i in range(n_led):
|
||||
self.driver.n[i] = bg
|
||||
base_lo = 180 - flicker // 2
|
||||
if base_lo < 40:
|
||||
base_lo = 40
|
||||
for ci, c in enumerate(centers):
|
||||
warmth = colors[ci % len(colors)]
|
||||
pulse = base_lo + random.randint(0, flicker)
|
||||
if pulse > 255:
|
||||
pulse = 255
|
||||
for off in range(-width, width + 1):
|
||||
idx = c + off
|
||||
if 0 <= idx < n_led:
|
||||
dist = abs(off)
|
||||
fall = ((width - dist + 1) * 256) // (width + 1)
|
||||
fac = (fall * pulse) // 256
|
||||
px = (
|
||||
(warmth[0] * fac) // 255,
|
||||
(warmth[1] * fac) // 255,
|
||||
(warmth[2] * fac) // 255,
|
||||
)
|
||||
lit = self.driver.apply_brightness(px, 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]),
|
||||
)
|
||||
self.driver.n.write()
|
||||
last = utime.ticks_add(last, d)
|
||||
if not preset.a:
|
||||
yield
|
||||
return
|
||||
yield
|
||||
69
src/patterns/ice_sparkle.py
Normal file
69
src/patterns/ice_sparkle.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import random
|
||||
import utime
|
||||
|
||||
|
||||
class IceSparkle:
|
||||
def __init__(self, driver):
|
||||
self.driver = driver
|
||||
|
||||
def run(self, preset):
|
||||
colors = preset.c if preset.c else [(240, 248, 255), (200, 235, 255), (255, 255, 255)]
|
||||
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)))
|
||||
sparks = []
|
||||
cap = 28
|
||||
last = utime.ticks_ms()
|
||||
|
||||
while True:
|
||||
d = max(1, int(preset.d))
|
||||
now = utime.ticks_ms()
|
||||
if utime.ticks_diff(now, last) >= d:
|
||||
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()
|
||||
last = utime.ticks_add(last, d)
|
||||
if not preset.a:
|
||||
yield
|
||||
return
|
||||
yield
|
||||
53
src/patterns/northern_wave.py
Normal file
53
src/patterns/northern_wave.py
Normal file
@@ -0,0 +1,53 @@
|
||||
import math
|
||||
import utime
|
||||
|
||||
|
||||
class NorthernWave:
|
||||
def __init__(self, driver):
|
||||
self.driver = driver
|
||||
|
||||
def run(self, preset):
|
||||
colors = preset.c if preset.c else [(20, 55, 120), (60, 140, 220), (180, 220, 255)]
|
||||
period = max(4, int(preset.n1) if int(preset.n1) > 0 else 20)
|
||||
contrast = max(1, min(255, int(preset.n2) if int(preset.n2) > 0 else 200))
|
||||
drift = max(1, int(preset.n3) if int(preset.n3) > 0 else 2)
|
||||
phase = 0
|
||||
last = utime.ticks_ms()
|
||||
ncols = len(colors)
|
||||
if ncols < 2:
|
||||
colors = list(colors) + [(120, 180, 255)]
|
||||
ncols = len(colors)
|
||||
twopi = 6.2831853
|
||||
|
||||
def lerp3(a, b, f):
|
||||
return (
|
||||
a[0] + ((b[0] - a[0]) * f) // 255,
|
||||
a[1] + ((b[1] - a[1]) * f) // 255,
|
||||
a[2] + ((b[2] - a[2]) * f) // 255,
|
||||
)
|
||||
|
||||
while True:
|
||||
d = max(1, int(preset.d))
|
||||
now = utime.ticks_ms()
|
||||
if utime.ticks_diff(now, last) >= d:
|
||||
bg = self.driver.apply_brightness(preset.background_or(colors), preset.b)
|
||||
for i in range(self.driver.num_leds):
|
||||
t = (i * twopi / period) + (phase * twopi / 256.0)
|
||||
w = (math.sin(t) + 1.0) * 0.5
|
||||
u = w * (ncols - 1) * 256.0
|
||||
fi = int(u) >> 8
|
||||
frac = int(u) & 255
|
||||
if fi >= ncols - 1:
|
||||
fi = ncols - 2
|
||||
frac = 255
|
||||
peak = lerp3(colors[fi], colors[fi + 1], frac)
|
||||
peak = self.driver.apply_brightness(peak, preset.b)
|
||||
mixf = min(255, int(w * contrast * 2) >> 1)
|
||||
self.driver.n[i] = lerp3(bg, peak, mixf)
|
||||
self.driver.n.write()
|
||||
phase = (phase + drift) % 256
|
||||
last = utime.ticks_add(last, d)
|
||||
if not preset.a:
|
||||
yield
|
||||
return
|
||||
yield
|
||||
65
src/patterns/starfall.py
Normal file
65
src/patterns/starfall.py
Normal file
@@ -0,0 +1,65 @@
|
||||
import random
|
||||
import utime
|
||||
|
||||
|
||||
class Starfall:
|
||||
def __init__(self, driver):
|
||||
self.driver = driver
|
||||
|
||||
def run(self, preset):
|
||||
colors = preset.c if preset.c else [(255, 255, 255), (200, 230, 255), (255, 248, 220)]
|
||||
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)
|
||||
stars = []
|
||||
max_stars = 4
|
||||
last = utime.ticks_ms()
|
||||
|
||||
while True:
|
||||
d = max(1, int(preset.d))
|
||||
now = utime.ticks_ms()
|
||||
if utime.ticks_diff(now, last) >= d:
|
||||
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()
|
||||
last = utime.ticks_add(last, d)
|
||||
if not preset.a:
|
||||
yield
|
||||
return
|
||||
yield
|
||||
40
tests/patterns/candle_glow.py
Normal file
40
tests/patterns/candle_glow.py
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
import utime
|
||||
from machine import WDT
|
||||
from settings import Settings
|
||||
from presets import Presets, run_tick
|
||||
|
||||
|
||||
def run_for(p, wdt, ms):
|
||||
start = utime.ticks_ms()
|
||||
while utime.ticks_diff(utime.ticks_ms(), start) < ms:
|
||||
wdt.feed()
|
||||
run_tick(p)
|
||||
utime.sleep_ms(10)
|
||||
|
||||
|
||||
def main():
|
||||
s = Settings()
|
||||
p = Presets(pin=s.get("led_pin", 10), num_leds=s.get("num_leds", 30))
|
||||
wdt = WDT(timeout=10000)
|
||||
|
||||
p.edit("test_candle_glow", {
|
||||
"p": "candle_glow",
|
||||
"b": 200,
|
||||
"d": 60,
|
||||
"c": [(255, 0, 0), (0, 0, 255), (0, 255, 0)],
|
||||
"n1": 4,
|
||||
"n2": 2,
|
||||
"n3": 120,
|
||||
"a": True,
|
||||
})
|
||||
p.select("test_candle_glow")
|
||||
run_for(p, wdt, 3000)
|
||||
|
||||
p.edit("cleanup_off", {"p": "off"})
|
||||
p.select("cleanup_off")
|
||||
run_for(p, wdt, 100)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
40
tests/patterns/ice_sparkle.py
Normal file
40
tests/patterns/ice_sparkle.py
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
import utime
|
||||
from machine import WDT
|
||||
from settings import Settings
|
||||
from presets import Presets, run_tick
|
||||
|
||||
|
||||
def run_for(p, wdt, ms):
|
||||
start = utime.ticks_ms()
|
||||
while utime.ticks_diff(utime.ticks_ms(), start) < ms:
|
||||
wdt.feed()
|
||||
run_tick(p)
|
||||
utime.sleep_ms(10)
|
||||
|
||||
|
||||
def main():
|
||||
s = Settings()
|
||||
p = Presets(pin=s.get("led_pin", 10), num_leds=s.get("num_leds", 30))
|
||||
wdt = WDT(timeout=10000)
|
||||
|
||||
p.edit("test_ice_sparkle", {
|
||||
"p": "ice_sparkle",
|
||||
"b": 200,
|
||||
"d": 60,
|
||||
"c": [(255, 0, 0), (0, 0, 255), (0, 255, 0)],
|
||||
"n1": 4,
|
||||
"n2": 2,
|
||||
"n3": 120,
|
||||
"a": True,
|
||||
})
|
||||
p.select("test_ice_sparkle")
|
||||
run_for(p, wdt, 3000)
|
||||
|
||||
p.edit("cleanup_off", {"p": "off"})
|
||||
p.select("cleanup_off")
|
||||
run_for(p, wdt, 100)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
40
tests/patterns/northern_wave.py
Normal file
40
tests/patterns/northern_wave.py
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
import utime
|
||||
from machine import WDT
|
||||
from settings import Settings
|
||||
from presets import Presets, run_tick
|
||||
|
||||
|
||||
def run_for(p, wdt, ms):
|
||||
start = utime.ticks_ms()
|
||||
while utime.ticks_diff(utime.ticks_ms(), start) < ms:
|
||||
wdt.feed()
|
||||
run_tick(p)
|
||||
utime.sleep_ms(10)
|
||||
|
||||
|
||||
def main():
|
||||
s = Settings()
|
||||
p = Presets(pin=s.get("led_pin", 10), num_leds=s.get("num_leds", 30))
|
||||
wdt = WDT(timeout=10000)
|
||||
|
||||
p.edit("test_northern_wave", {
|
||||
"p": "northern_wave",
|
||||
"b": 200,
|
||||
"d": 60,
|
||||
"c": [(255, 0, 0), (0, 0, 255), (0, 255, 0)],
|
||||
"n1": 4,
|
||||
"n2": 2,
|
||||
"n3": 120,
|
||||
"a": True,
|
||||
})
|
||||
p.select("test_northern_wave")
|
||||
run_for(p, wdt, 3000)
|
||||
|
||||
p.edit("cleanup_off", {"p": "off"})
|
||||
p.select("cleanup_off")
|
||||
run_for(p, wdt, 100)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
40
tests/patterns/starfall.py
Normal file
40
tests/patterns/starfall.py
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
import utime
|
||||
from machine import WDT
|
||||
from settings import Settings
|
||||
from presets import Presets, run_tick
|
||||
|
||||
|
||||
def run_for(p, wdt, ms):
|
||||
start = utime.ticks_ms()
|
||||
while utime.ticks_diff(utime.ticks_ms(), start) < ms:
|
||||
wdt.feed()
|
||||
run_tick(p)
|
||||
utime.sleep_ms(10)
|
||||
|
||||
|
||||
def main():
|
||||
s = Settings()
|
||||
p = Presets(pin=s.get("led_pin", 10), num_leds=s.get("num_leds", 30))
|
||||
wdt = WDT(timeout=10000)
|
||||
|
||||
p.edit("test_starfall", {
|
||||
"p": "starfall",
|
||||
"b": 200,
|
||||
"d": 60,
|
||||
"c": [(255, 0, 0), (0, 0, 255), (0, 255, 0)],
|
||||
"n1": 4,
|
||||
"n2": 2,
|
||||
"n3": 120,
|
||||
"a": True,
|
||||
})
|
||||
p.select("test_starfall")
|
||||
run_for(p, wdt, 3000)
|
||||
|
||||
p.edit("cleanup_off", {"p": "off"})
|
||||
p.select("cleanup_off")
|
||||
run_for(p, wdt, 100)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user