fix(patterns): use preset background fallback across animations
Align pattern background rendering to use preset.background_or(...) and update pulse/radiate single-step behaviour to preserve visible frames and step progression.
This commit is contained in:
@@ -173,13 +173,6 @@ async def presets_loop():
|
|||||||
while True:
|
while True:
|
||||||
presets.tick()
|
presets.tick()
|
||||||
wdt.feed()
|
wdt.feed()
|
||||||
if bool(getattr(presets, "debug", False)):
|
|
||||||
now = utime.ticks_ms()
|
|
||||||
if utime.ticks_diff(now, last_mem_log) >= 5000:
|
|
||||||
gc.collect()
|
|
||||||
print("mem runtime:", {"free": gc.mem_free(), "alloc": gc.mem_alloc()})
|
|
||||||
last_mem_log = now
|
|
||||||
# tick() does not await; yield so UDP hello and HTTP/WebSocket can run.
|
|
||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class BarGraph:
|
|||||||
target = (self.driver.num_leds * level) // 100
|
target = (self.driver.num_leds * level) // 100
|
||||||
lit = self.driver.apply_brightness(colors[0], preset.b)
|
lit = self.driver.apply_brightness(colors[0], preset.b)
|
||||||
unlit = self.driver.apply_brightness(
|
unlit = self.driver.apply_brightness(
|
||||||
colors[-1],
|
preset.background_or(colors),
|
||||||
preset.b,
|
preset.b,
|
||||||
)
|
)
|
||||||
for i in range(self.driver.num_leds):
|
for i in range(self.driver.num_leds):
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ class Blink:
|
|||||||
"""Blink pattern: toggles LEDs on/off using preset delay, cycling through colors."""
|
"""Blink pattern: toggles LEDs on/off using preset delay, cycling through colors."""
|
||||||
# Use provided colors, or default to white if none
|
# Use provided colors, or default to white if none
|
||||||
colors = preset.c if preset.c else [(255, 255, 255)]
|
colors = preset.c if preset.c else [(255, 255, 255)]
|
||||||
|
bg_color = self.driver.apply_brightness(preset.background_or(colors), preset.b)
|
||||||
color_index = 0
|
color_index = 0
|
||||||
state = True # True = on, False = off
|
state = True # True = on, False = off
|
||||||
last_update = utime.ticks_ms()
|
last_update = utime.ticks_ms()
|
||||||
@@ -25,8 +26,8 @@ class Blink:
|
|||||||
# Advance to next color for the next "on" phase
|
# Advance to next color for the next "on" phase
|
||||||
color_index += 1
|
color_index += 1
|
||||||
else:
|
else:
|
||||||
# "Off" phase should actually be off.
|
# Inactive phase uses the preset background color.
|
||||||
self.driver.fill((0, 0, 0))
|
self.driver.fill(bg_color)
|
||||||
state = not state
|
state = not state
|
||||||
last_update = utime.ticks_add(last_update, delay_ms)
|
last_update = utime.ticks_add(last_update, delay_ms)
|
||||||
# Yield once per tick so other logic can run
|
# Yield once per tick so other logic can run
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class Chase:
|
|||||||
|
|
||||||
color0 = self.driver.apply_brightness(color0, preset.b)
|
color0 = self.driver.apply_brightness(color0, preset.b)
|
||||||
color1 = self.driver.apply_brightness(color1, preset.b)
|
color1 = self.driver.apply_brightness(color1, preset.b)
|
||||||
bg_color = self.driver.apply_brightness(colors[-1], preset.b)
|
bg_color = self.driver.apply_brightness(preset.background_or(colors), preset.b)
|
||||||
|
|
||||||
n1 = max(1, int(preset.n1)) # LEDs of color 0
|
n1 = max(1, int(preset.n1)) # LEDs of color 0
|
||||||
n2 = max(1, int(preset.n2)) # LEDs of color 1
|
n2 = max(1, int(preset.n2)) # LEDs of color 1
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ class Circle:
|
|||||||
base0 = base1 = (255, 255, 255)
|
base0 = base1 = (255, 255, 255)
|
||||||
elif len(colors) == 1:
|
elif len(colors) == 1:
|
||||||
base0 = colors[0]
|
base0 = colors[0]
|
||||||
base1 = colors[-1]
|
base1 = preset.background_or(colors)
|
||||||
else:
|
else:
|
||||||
base0 = colors[0]
|
base0 = colors[0]
|
||||||
base1 = colors[-1]
|
base1 = preset.background_or(colors)
|
||||||
|
|
||||||
color0 = self.driver.apply_brightness(base0, preset.b)
|
color0 = self.driver.apply_brightness(base0, preset.b)
|
||||||
color1 = self.driver.apply_brightness(base1, preset.b)
|
color1 = self.driver.apply_brightness(base1, preset.b)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class ClockSweep:
|
|||||||
d = max(1, int(preset.d))
|
d = max(1, int(preset.d))
|
||||||
now = utime.ticks_ms()
|
now = utime.ticks_ms()
|
||||||
if utime.ticks_diff(now, last) >= d:
|
if utime.ticks_diff(now, last) >= d:
|
||||||
bg = self.driver.apply_brightness(colors[-1], preset.b)
|
bg = self.driver.apply_brightness(preset.background_or(colors), preset.b)
|
||||||
fg = self.driver.apply_brightness(colors[0], preset.b)
|
fg = self.driver.apply_brightness(colors[0], preset.b)
|
||||||
for i in range(self.driver.num_leds):
|
for i in range(self.driver.num_leds):
|
||||||
self.driver.n[i] = bg
|
self.driver.n[i] = bg
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class CometDual:
|
|||||||
d = max(1, int(preset.d))
|
d = max(1, int(preset.d))
|
||||||
now = utime.ticks_ms()
|
now = utime.ticks_ms()
|
||||||
if utime.ticks_diff(now, last) >= d:
|
if utime.ticks_diff(now, last) >= d:
|
||||||
bg_color = self.driver.apply_brightness(colors[-1], preset.b)
|
bg_color = self.driver.apply_brightness(preset.background_or(colors), preset.b)
|
||||||
for i in range(self.driver.num_leds):
|
for i in range(self.driver.num_leds):
|
||||||
self.driver.n[i] = bg_color
|
self.driver.n[i] = bg_color
|
||||||
c1 = self.driver.apply_brightness(colors[0 % len(colors)], preset.b)
|
c1 = self.driver.apply_brightness(colors[0 % len(colors)], preset.b)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class Fireflies:
|
|||||||
d = max(1, int(preset.d))
|
d = max(1, int(preset.d))
|
||||||
now = utime.ticks_ms()
|
now = utime.ticks_ms()
|
||||||
if utime.ticks_diff(now, last) >= d:
|
if utime.ticks_diff(now, last) >= d:
|
||||||
bg_color = self.driver.apply_brightness(colors[-1], preset.b)
|
bg_color = self.driver.apply_brightness(preset.background_or(colors), preset.b)
|
||||||
for i in range(self.driver.num_leds):
|
for i in range(self.driver.num_leds):
|
||||||
self.driver.n[i] = bg_color
|
self.driver.n[i] = bg_color
|
||||||
for b in bugs:
|
for b in bugs:
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class Heartbeat:
|
|||||||
beat_gap = max(20, int(preset.d))
|
beat_gap = max(20, int(preset.d))
|
||||||
colors = preset.c if preset.c else [(255, 0, 40)]
|
colors = preset.c if preset.c else [(255, 0, 40)]
|
||||||
lit_color = self.driver.apply_brightness(colors[0], preset.b)
|
lit_color = self.driver.apply_brightness(colors[0], preset.b)
|
||||||
bg_color = self.driver.apply_brightness(colors[-1], preset.b)
|
bg_color = self.driver.apply_brightness(preset.background_or(colors), preset.b)
|
||||||
phase_durations = (p1, beat_gap, p2, pause)
|
phase_durations = (p1, beat_gap, p2, pause)
|
||||||
phase_colors = (lit_color, bg_color, lit_color, bg_color)
|
phase_colors = (lit_color, bg_color, lit_color, bg_color)
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class Marquee:
|
|||||||
now = utime.ticks_ms()
|
now = utime.ticks_ms()
|
||||||
if utime.ticks_diff(now, last) >= d:
|
if utime.ticks_diff(now, last) >= d:
|
||||||
c = self.driver.apply_brightness(colors[0], preset.b)
|
c = self.driver.apply_brightness(colors[0], preset.b)
|
||||||
bg_color = self.driver.apply_brightness(colors[-1], preset.b)
|
bg_color = self.driver.apply_brightness(preset.background_or(colors), preset.b)
|
||||||
for i in range(self.driver.num_leds):
|
for i in range(self.driver.num_leds):
|
||||||
m = (i + phase) % (on_len + off_len)
|
m = (i + phase) % (on_len + off_len)
|
||||||
self.driver.n[i] = c if m < on_len else bg_color
|
self.driver.n[i] = c if m < on_len else bg_color
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class Orbit:
|
|||||||
d = max(1, int(preset.d))
|
d = max(1, int(preset.d))
|
||||||
now = utime.ticks_ms()
|
now = utime.ticks_ms()
|
||||||
if utime.ticks_diff(now, last) >= d:
|
if utime.ticks_diff(now, last) >= d:
|
||||||
bg_color = self.driver.apply_brightness(colors[-1], preset.b)
|
bg_color = self.driver.apply_brightness(preset.background_or(colors), preset.b)
|
||||||
for i in range(self.driver.num_leds):
|
for i in range(self.driver.num_leds):
|
||||||
self.driver.n[i] = bg_color
|
self.driver.n[i] = bg_color
|
||||||
for k in range(orbits):
|
for k in range(orbits):
|
||||||
|
|||||||
@@ -6,19 +6,19 @@ class Pulse:
|
|||||||
self.driver = driver
|
self.driver = driver
|
||||||
|
|
||||||
def run(self, preset):
|
def run(self, preset):
|
||||||
self.driver.off()
|
|
||||||
|
|
||||||
# Get colors from preset
|
# Get colors from preset
|
||||||
colors = preset.c
|
colors = preset.c
|
||||||
if not colors:
|
if not colors:
|
||||||
colors = [(255, 255, 255)]
|
colors = [(255, 255, 255)]
|
||||||
|
bg_base = preset.background_or(colors)
|
||||||
|
self.driver.fill(self.driver.apply_brightness(bg_base, preset.b))
|
||||||
|
|
||||||
color_index = 0
|
color_index = self.driver.step % max(1, len(colors))
|
||||||
cycle_start = utime.ticks_ms()
|
cycle_start = utime.ticks_ms()
|
||||||
|
|
||||||
# State machine based pulse using a single generator loop
|
# State machine based pulse using a single generator loop
|
||||||
while True:
|
while True:
|
||||||
bg_color = self.driver.apply_brightness(colors[-1], preset.b)
|
bg_color = self.driver.apply_brightness(bg_base, preset.b)
|
||||||
# Read current timing parameters from preset
|
# Read current timing parameters from preset
|
||||||
attack_ms = max(0, int(preset.n1)) # Attack time in ms
|
attack_ms = max(0, int(preset.n1)) # Attack time in ms
|
||||||
hold_ms = max(0, int(preset.n2)) # Hold time in ms
|
hold_ms = max(0, int(preset.n2)) # Hold time in ms
|
||||||
@@ -53,7 +53,8 @@ class Pulse:
|
|||||||
self.driver.fill(bg_color)
|
self.driver.fill(bg_color)
|
||||||
else:
|
else:
|
||||||
# End of cycle, move to next color and restart timing
|
# End of cycle, move to next color and restart timing
|
||||||
color_index += 1
|
color_index = (color_index + 1) % max(1, len(colors))
|
||||||
|
self.driver.step = color_index
|
||||||
cycle_start = now
|
cycle_start = now
|
||||||
if not preset.a:
|
if not preset.a:
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class Radiate:
|
|||||||
"""
|
"""
|
||||||
colors = preset.c if preset.c else [(255, 255, 255)]
|
colors = preset.c if preset.c else [(255, 255, 255)]
|
||||||
base_on = colors[0]
|
base_on = colors[0]
|
||||||
base_off = colors[-1]
|
base_off = preset.background_or(colors)
|
||||||
|
|
||||||
spacing = max(1, int(preset.n1))
|
spacing = max(1, int(preset.n1))
|
||||||
outward_ms = max(1, int(preset.n2))
|
outward_ms = max(1, int(preset.n2))
|
||||||
@@ -34,8 +34,9 @@ class Radiate:
|
|||||||
dbg_banner = False
|
dbg_banner = False
|
||||||
|
|
||||||
if not preset.a:
|
if not preset.a:
|
||||||
# Single-step render uses only the first instant pulse.
|
# Single-shot mode exits after one rendered frame. Seed the pulse
|
||||||
active_pulses = [utime.ticks_ms()]
|
# slightly in the past so this frame is visible before returning.
|
||||||
|
active_pulses = [utime.ticks_add(utime.ticks_ms(), -1)]
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
now = utime.ticks_ms()
|
now = utime.ticks_ms()
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class RainDrops:
|
|||||||
d = max(1, int(preset.d))
|
d = max(1, int(preset.d))
|
||||||
now = utime.ticks_ms()
|
now = utime.ticks_ms()
|
||||||
if utime.ticks_diff(now, last) >= d:
|
if utime.ticks_diff(now, last) >= d:
|
||||||
bg_color = self.driver.apply_brightness(colors[-1], preset.b)
|
bg_color = self.driver.apply_brightness(preset.background_or(colors), preset.b)
|
||||||
for i in range(self.driver.num_leds):
|
for i in range(self.driver.num_leds):
|
||||||
self.driver.n[i] = bg_color
|
self.driver.n[i] = bg_color
|
||||||
if random.randint(0, 255) < rate:
|
if random.randint(0, 255) < rate:
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class Scanner:
|
|||||||
if utime.ticks_diff(now, last_update) >= delay_ms:
|
if utime.ticks_diff(now, last_update) >= delay_ms:
|
||||||
base = colors[color_index % len(colors)]
|
base = colors[color_index % len(colors)]
|
||||||
base = self.driver.apply_brightness(base, preset.b)
|
base = self.driver.apply_brightness(base, preset.b)
|
||||||
bg_color = self.driver.apply_brightness(colors[-1], preset.b)
|
bg_color = self.driver.apply_brightness(preset.background_or(colors), preset.b)
|
||||||
for i in range(self.driver.num_leds):
|
for i in range(self.driver.num_leds):
|
||||||
dist = i - center
|
dist = i - center
|
||||||
if dist < 0:
|
if dist < 0:
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class SegmentChase:
|
|||||||
d = max(1, int(preset.d))
|
d = max(1, int(preset.d))
|
||||||
now = utime.ticks_ms()
|
now = utime.ticks_ms()
|
||||||
if utime.ticks_diff(now, last) >= d:
|
if utime.ticks_diff(now, last) >= d:
|
||||||
bg_color = self.driver.apply_brightness(colors[-1], preset.b)
|
bg_color = self.driver.apply_brightness(preset.background_or(colors), preset.b)
|
||||||
for i in range(self.driver.num_leds):
|
for i in range(self.driver.num_leds):
|
||||||
seg_idx = i // seg
|
seg_idx = i // seg
|
||||||
in_seg = i % seg
|
in_seg = i % seg
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class Snowfall:
|
|||||||
d = max(1, int(preset.d))
|
d = max(1, int(preset.d))
|
||||||
now = utime.ticks_ms()
|
now = utime.ticks_ms()
|
||||||
if utime.ticks_diff(now, last) >= d:
|
if utime.ticks_diff(now, last) >= d:
|
||||||
bg_color = self.driver.apply_brightness(colors[-1], preset.b)
|
bg_color = self.driver.apply_brightness(preset.background_or(colors), preset.b)
|
||||||
if random.randint(0, 255) < density:
|
if random.randint(0, 255) < density:
|
||||||
flakes.append([self.driver.num_leds - 1, random.randint(0, len(colors)-1)])
|
flakes.append([self.driver.num_leds - 1, random.randint(0, len(colors)-1)])
|
||||||
for i in range(self.driver.num_leds):
|
for i in range(self.driver.num_leds):
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class StrobeBurst:
|
|||||||
cooldown = max(1, int(preset.n3) if int(preset.n3) > 0 else 400)
|
cooldown = max(1, int(preset.n3) if int(preset.n3) > 0 else 400)
|
||||||
on_ms = max(1, int(preset.d) // 2)
|
on_ms = max(1, int(preset.d) // 2)
|
||||||
c = self.driver.apply_brightness(colors[0], preset.b)
|
c = self.driver.apply_brightness(colors[0], preset.b)
|
||||||
bg_color = self.driver.apply_brightness(colors[-1], preset.b)
|
bg_color = self.driver.apply_brightness(preset.background_or(colors), preset.b)
|
||||||
now = utime.ticks_ms()
|
now = utime.ticks_ms()
|
||||||
|
|
||||||
if state == "flash_on":
|
if state == "flash_on":
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class Twinkle:
|
|||||||
"""Twinkle: n1 activity, n2 density; n3/n4 min/max length of adjacent on/off runs."""
|
"""Twinkle: n1 activity, n2 density; n3/n4 min/max length of adjacent on/off runs."""
|
||||||
palette = self._palette(preset)
|
palette = self._palette(preset)
|
||||||
num = self.driver.num_leds
|
num = self.driver.num_leds
|
||||||
bg_color = self.driver.apply_brightness(palette[-1], preset.b)
|
bg_color = self.driver.apply_brightness(preset.background_or(palette), preset.b)
|
||||||
if num <= 0:
|
if num <= 0:
|
||||||
while True:
|
while True:
|
||||||
yield
|
yield
|
||||||
|
|||||||
@@ -100,6 +100,9 @@ class Preset:
|
|||||||
def auto(self, value):
|
def auto(self, value):
|
||||||
self.a = value
|
self.a = value
|
||||||
|
|
||||||
|
def background_or(self, colors=None, default=(0, 0, 0)):
|
||||||
|
return default
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return {
|
return {
|
||||||
"p": self.p,
|
"p": self.p,
|
||||||
|
|||||||
Reference in New Issue
Block a user