feat(patterns): reverse animation direction via preset n8
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -26,13 +26,13 @@ class Aurora:
|
|||||||
c = self.driver.apply_brightness(colors[idx], preset.b)
|
c = self.driver.apply_brightness(colors[idx], preset.b)
|
||||||
w = 255 - abs(128 - ((i * 8 + phase) & 255)) * 2
|
w = 255 - abs(128 - ((i * 8 + phase) & 255)) * 2
|
||||||
w = max(0, min(255, w + shimmer))
|
w = max(0, min(255, w + shimmer))
|
||||||
self.driver.n[i] = (
|
self.driver.n[self.driver.led_i(preset, i)] = (
|
||||||
(c[0] * w) // 255,
|
(c[0] * w) // 255,
|
||||||
(c[1] * w) // 255,
|
(c[1] * w) // 255,
|
||||||
(c[2] * w) // 255,
|
(c[2] * w) // 255,
|
||||||
)
|
)
|
||||||
self.driver.n.write()
|
self.driver.n.write()
|
||||||
phase = (phase + 1) & 255
|
phase = (phase + self.driver.signed(preset, 1)) & 255
|
||||||
self.driver.step = phase
|
self.driver.step = phase
|
||||||
last = utime.ticks_add(last, d)
|
last = utime.ticks_add(last, d)
|
||||||
if not preset.a:
|
if not preset.a:
|
||||||
@@ -76,9 +76,9 @@ class Aurora:
|
|||||||
peak = lerp3(colors[fi], colors[fi + 1], frac)
|
peak = lerp3(colors[fi], colors[fi + 1], frac)
|
||||||
peak = self.driver.apply_brightness(peak, preset.b)
|
peak = self.driver.apply_brightness(peak, preset.b)
|
||||||
mixf = min(255, int(w * contrast * 2) >> 1)
|
mixf = min(255, int(w * contrast * 2) >> 1)
|
||||||
self.driver.n[i] = lerp3(bg, peak, mixf)
|
self.driver.n[self.driver.led_i(preset, i)] = lerp3(bg, peak, mixf)
|
||||||
self.driver.n.write()
|
self.driver.n.write()
|
||||||
phase = (phase + drift) % 256
|
phase = (phase + self.driver.signed(preset, drift)) % 256
|
||||||
last = utime.ticks_add(last, d)
|
last = utime.ticks_add(last, d)
|
||||||
if not preset.a:
|
if not preset.a:
|
||||||
yield
|
yield
|
||||||
|
|||||||
@@ -46,12 +46,14 @@ class Blizzard:
|
|||||||
for pos, ci, wj in flakes:
|
for pos, ci, wj in flakes:
|
||||||
p = pos
|
p = pos
|
||||||
lateral = wind + (wj if wj else 0)
|
lateral = wind + (wj if wj else 0)
|
||||||
p -= speed
|
p -= self.driver.signed(preset, speed)
|
||||||
p += lateral
|
p += self.driver.signed(preset, lateral)
|
||||||
if p < -2 or p >= nled + 2:
|
if p < -2 or p >= nled + 2:
|
||||||
continue
|
continue
|
||||||
pi = max(0, min(nled - 1, int(p)))
|
pi = max(0, min(nled - 1, int(p)))
|
||||||
self.driver.n[pi] = self.driver.apply_brightness(colors[ci], preset.b)
|
self.driver.n[self.driver.led_i(preset, pi)] = self.driver.apply_brightness(
|
||||||
|
colors[ci], preset.b
|
||||||
|
)
|
||||||
nf.append([p, ci, wj])
|
nf.append([p, ci, wj])
|
||||||
flakes = nf
|
flakes = nf
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class Chase:
|
|||||||
def _run_marquee(self, preset, colors):
|
def _run_marquee(self, preset, colors):
|
||||||
on_len = max(1, int(preset.n1) if int(preset.n1) > 0 else 3)
|
on_len = max(1, int(preset.n1) if int(preset.n1) > 0 else 3)
|
||||||
off_len = max(1, int(preset.n2) if int(preset.n2) > 0 else 2)
|
off_len = max(1, int(preset.n2) if int(preset.n2) > 0 else 2)
|
||||||
step = max(1, int(preset.n3) if int(preset.n3) > 0 else 1)
|
step = max(1, abs(self.driver.signed(preset, int(preset.n3) if int(preset.n3) > 0 else 1)))
|
||||||
phase = self.driver.step % (on_len + off_len)
|
phase = self.driver.step % (on_len + off_len)
|
||||||
last = utime.ticks_ms()
|
last = utime.ticks_ms()
|
||||||
while True:
|
while True:
|
||||||
@@ -23,9 +23,9 @@ class Chase:
|
|||||||
bg_color = self.driver.apply_brightness(preset.background_or(colors), 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[self.driver.led_i(preset, i)] = c if m < on_len else bg_color
|
||||||
self.driver.n.write()
|
self.driver.n.write()
|
||||||
phase = (phase + step) % (on_len + off_len)
|
phase = (phase + self.driver.signed(preset, step)) % (on_len + off_len)
|
||||||
self.driver.step = phase
|
self.driver.step = phase
|
||||||
last = utime.ticks_add(last, d)
|
last = utime.ticks_add(last, d)
|
||||||
if not preset.a:
|
if not preset.a:
|
||||||
@@ -66,8 +66,8 @@ class Chase:
|
|||||||
|
|
||||||
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
|
||||||
n3 = int(preset.n3) # Step movement on even steps (can be negative)
|
n3 = self.driver.signed(preset, int(preset.n3)) # Step movement on even steps
|
||||||
n4 = int(preset.n4) # Step movement on odd steps (can be negative)
|
n4 = self.driver.signed(preset, int(preset.n4)) # Step movement on odd steps
|
||||||
|
|
||||||
segment_length = n1 + n2
|
segment_length = n1 + n2
|
||||||
|
|
||||||
@@ -101,9 +101,9 @@ class Chase:
|
|||||||
|
|
||||||
# Determine which color based on position in segment
|
# Determine which color based on position in segment
|
||||||
if relative_pos < n1:
|
if relative_pos < n1:
|
||||||
self.driver.n[i] = color0
|
self.driver.n[self.driver.led_i(preset, i)] = color0
|
||||||
else:
|
else:
|
||||||
self.driver.n[i] = color1
|
self.driver.n[self.driver.led_i(preset, i)] = color1
|
||||||
|
|
||||||
self.driver.n.write()
|
self.driver.n.write()
|
||||||
print("[chase] step", step_count)
|
print("[chase] step", step_count)
|
||||||
@@ -147,9 +147,9 @@ class Chase:
|
|||||||
|
|
||||||
# Determine which color based on position in segment
|
# Determine which color based on position in segment
|
||||||
if relative_pos < n1:
|
if relative_pos < n1:
|
||||||
self.driver.n[i] = color0
|
self.driver.n[self.driver.led_i(preset, i)] = color0
|
||||||
else:
|
else:
|
||||||
self.driver.n[i] = color1
|
self.driver.n[self.driver.led_i(preset, i)] = color1
|
||||||
|
|
||||||
self.driver.n.write()
|
self.driver.n.write()
|
||||||
print("[chase] step", step_count)
|
print("[chase] step", step_count)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class ColourCycle:
|
|||||||
pos -= 170
|
pos -= 170
|
||||||
return (0, pos * 3, 255 - pos * 3)
|
return (0, pos * 3, 255 - pos * 3)
|
||||||
|
|
||||||
def _render_gradient(self, colors, phase, brightness):
|
def _render_gradient(self, preset, colors, phase, brightness):
|
||||||
num_leds = self.driver.num_leds
|
num_leds = self.driver.num_leds
|
||||||
color_count = len(colors)
|
color_count = len(colors)
|
||||||
if num_leds <= 0 or color_count <= 0:
|
if num_leds <= 0 or color_count <= 0:
|
||||||
@@ -40,14 +40,16 @@ class ColourCycle:
|
|||||||
c1[1] + ((c2[1] - c1[1]) * frac) // 256,
|
c1[1] + ((c2[1] - c1[1]) * frac) // 256,
|
||||||
c1[2] + ((c2[2] - c1[2]) * frac) // 256,
|
c1[2] + ((c2[2] - c1[2]) * frac) // 256,
|
||||||
)
|
)
|
||||||
self.driver.n[i] = self.driver.apply_brightness(blended, brightness)
|
self.driver.n[self.driver.led_i(preset, i)] = self.driver.apply_brightness(
|
||||||
|
blended, brightness
|
||||||
|
)
|
||||||
self.driver.n.write()
|
self.driver.n.write()
|
||||||
|
|
||||||
def _render_rainbow(self, phase, brightness):
|
def _render_rainbow(self, preset, phase, brightness):
|
||||||
num_leds = self.driver.num_leds
|
num_leds = self.driver.num_leds
|
||||||
for i in range(num_leds):
|
for i in range(num_leds):
|
||||||
rc_index = (i * 256 // max(1, num_leds)) + phase
|
rc_index = (i * 256 // max(1, num_leds)) + phase
|
||||||
self.driver.n[i] = self.driver.apply_brightness(
|
self.driver.n[self.driver.led_i(preset, i)] = self.driver.apply_brightness(
|
||||||
self._wheel(rc_index & 255), brightness
|
self._wheel(rc_index & 255), brightness
|
||||||
)
|
)
|
||||||
self.driver.n.write()
|
self.driver.n.write()
|
||||||
@@ -64,8 +66,8 @@ class ColourCycle:
|
|||||||
|
|
||||||
if mode == 1:
|
if mode == 1:
|
||||||
if not preset.a:
|
if not preset.a:
|
||||||
self._render_rainbow(phase, preset.b)
|
self._render_rainbow(preset, phase, preset.b)
|
||||||
self.driver.step = (phase + step_amount) % 256
|
self.driver.step = (phase + self.driver.signed(preset, step_amount)) % 256
|
||||||
yield
|
yield
|
||||||
return
|
return
|
||||||
last_update = utime.ticks_ms()
|
last_update = utime.ticks_ms()
|
||||||
@@ -73,16 +75,16 @@ class ColourCycle:
|
|||||||
delay_ms = max(1, int(preset.d))
|
delay_ms = max(1, int(preset.d))
|
||||||
now = utime.ticks_ms()
|
now = utime.ticks_ms()
|
||||||
if utime.ticks_diff(now, last_update) >= delay_ms:
|
if utime.ticks_diff(now, last_update) >= delay_ms:
|
||||||
self._render_rainbow(phase, preset.b)
|
self._render_rainbow(preset, phase, preset.b)
|
||||||
phase = (phase + step_amount) % 256
|
phase = (phase + self.driver.signed(preset, step_amount)) % 256
|
||||||
self.driver.step = phase
|
self.driver.step = phase
|
||||||
last_update = utime.ticks_add(last_update, delay_ms)
|
last_update = utime.ticks_add(last_update, delay_ms)
|
||||||
yield
|
yield
|
||||||
|
|
||||||
colors = preset.c if preset.c else [(255, 0, 0), (0, 0, 255)]
|
colors = preset.c if preset.c else [(255, 0, 0), (0, 0, 255)]
|
||||||
if not preset.a:
|
if not preset.a:
|
||||||
self._render_gradient(colors, phase, preset.b)
|
self._render_gradient(preset, colors, phase, preset.b)
|
||||||
self.driver.step = (phase + step_amount) % 256
|
self.driver.step = (phase + self.driver.signed(preset, step_amount)) % 256
|
||||||
yield
|
yield
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -91,8 +93,8 @@ class ColourCycle:
|
|||||||
delay_ms = max(1, int(preset.d))
|
delay_ms = max(1, int(preset.d))
|
||||||
now = utime.ticks_ms()
|
now = utime.ticks_ms()
|
||||||
if utime.ticks_diff(now, last_update) >= delay_ms:
|
if utime.ticks_diff(now, last_update) >= delay_ms:
|
||||||
self._render_gradient(colors, phase, preset.b)
|
self._render_gradient(preset, colors, phase, preset.b)
|
||||||
phase = (phase + step_amount) % 256
|
phase = (phase + self.driver.signed(preset, step_amount)) % 256
|
||||||
self.driver.step = phase
|
self.driver.step = phase
|
||||||
last_update = utime.ticks_add(last_update, delay_ms)
|
last_update = utime.ticks_add(last_update, delay_ms)
|
||||||
yield
|
yield
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class Icicles:
|
|||||||
if idx >= nled:
|
if idx >= nled:
|
||||||
break
|
break
|
||||||
br = ((k + 1) * 255) // max(1, ic_len)
|
br = ((k + 1) * 255) // max(1, ic_len)
|
||||||
self.driver.n[idx] = (
|
self.driver.n[self.driver.led_i(preset, idx)] = (
|
||||||
(tip[0] * br + bg[0] * (255 - br)) // 255,
|
(tip[0] * br + bg[0] * (255 - br)) // 255,
|
||||||
(tip[1] * br + bg[1] * (255 - br)) // 255,
|
(tip[1] * br + bg[1] * (255 - br)) // 255,
|
||||||
(tip[2] * br + bg[2] * (255 - br)) // 255,
|
(tip[2] * br + bg[2] * (255 - br)) // 255,
|
||||||
@@ -52,7 +52,7 @@ class Icicles:
|
|||||||
aidx += 1
|
aidx += 1
|
||||||
|
|
||||||
self.driver.n.write()
|
self.driver.n.write()
|
||||||
phase = (phase + phase_step) % span
|
phase = (phase + self.driver.signed(preset, phase_step)) % span
|
||||||
last = utime.ticks_add(last, d_ms)
|
last = utime.ticks_add(last, d_ms)
|
||||||
|
|
||||||
if not preset.a:
|
if not preset.a:
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ class Meteor:
|
|||||||
base = colors[color_index % len(colors)]
|
base = colors[color_index % len(colors)]
|
||||||
lit = self.driver.apply_brightness(base, preset.b)
|
lit = self.driver.apply_brightness(base, preset.b)
|
||||||
if 0 <= head < self.driver.num_leds:
|
if 0 <= head < self.driver.num_leds:
|
||||||
self.driver.n[head] = lit
|
self.driver.n[self.driver.led_i(preset, head)] = lit
|
||||||
self.driver.n.write()
|
self.driver.n.write()
|
||||||
head += direction * speed
|
head += self.driver.signed(preset, direction * speed)
|
||||||
if head >= self.driver.num_leds + tail_len:
|
if head >= self.driver.num_leds + tail_len:
|
||||||
head = self.driver.num_leds - 1
|
head = self.driver.num_leds - 1
|
||||||
direction = -1
|
direction = -1
|
||||||
@@ -62,14 +62,22 @@ class Meteor:
|
|||||||
i1 = p1 - t
|
i1 = p1 - t
|
||||||
if 0 <= i1 < self.driver.num_leds:
|
if 0 <= i1 < self.driver.num_leds:
|
||||||
s = (255 * (tail - t)) // max(1, tail)
|
s = (255 * (tail - t)) // max(1, tail)
|
||||||
self.driver.n[i1] = ((c1[0] * s) // 255, (c1[1] * s) // 255, (c1[2] * s) // 255)
|
self.driver.n[self.driver.led_i(preset, i1)] = (
|
||||||
|
(c1[0] * s) // 255,
|
||||||
|
(c1[1] * s) // 255,
|
||||||
|
(c1[2] * s) // 255,
|
||||||
|
)
|
||||||
i2 = p2 + t
|
i2 = p2 + t
|
||||||
if 0 <= i2 < self.driver.num_leds:
|
if 0 <= i2 < self.driver.num_leds:
|
||||||
s = (255 * (tail - t)) // max(1, tail)
|
s = (255 * (tail - t)) // max(1, tail)
|
||||||
self.driver.n[i2] = ((c2[0] * s) // 255, (c2[1] * s) // 255, (c2[2] * s) // 255)
|
self.driver.n[self.driver.led_i(preset, i2)] = (
|
||||||
|
(c2[0] * s) // 255,
|
||||||
|
(c2[1] * s) // 255,
|
||||||
|
(c2[2] * s) // 255,
|
||||||
|
)
|
||||||
self.driver.n.write()
|
self.driver.n.write()
|
||||||
p1 += speed
|
p1 += self.driver.signed(preset, speed)
|
||||||
p2 -= speed
|
p2 -= self.driver.signed(preset, speed)
|
||||||
if p1 - tail > self.driver.num_leds and p2 + tail < 0:
|
if p1 - tail > self.driver.num_leds and p2 + tail < 0:
|
||||||
p1 = 0
|
p1 = 0
|
||||||
p2 = self.driver.num_leds - 1 - gap
|
p2 = self.driver.num_leds - 1 - gap
|
||||||
@@ -89,10 +97,10 @@ class Meteor:
|
|||||||
if dist < 0:
|
if dist < 0:
|
||||||
dist = -dist
|
dist = -dist
|
||||||
if dist > width:
|
if dist > width:
|
||||||
self.driver.n[i] = bg_color
|
self.driver.n[self.driver.led_i(preset, i)] = bg_color
|
||||||
else:
|
else:
|
||||||
scale = ((width - dist) * 255) // max(1, width)
|
scale = ((width - dist) * 255) // max(1, width)
|
||||||
self.driver.n[i] = (
|
self.driver.n[self.driver.led_i(preset, i)] = (
|
||||||
(base[0] * scale) // 255,
|
(base[0] * scale) // 255,
|
||||||
(base[1] * scale) // 255,
|
(base[1] * scale) // 255,
|
||||||
(base[2] * scale) // 255,
|
(base[2] * scale) // 255,
|
||||||
@@ -101,7 +109,7 @@ class Meteor:
|
|||||||
if pause_frames > 0:
|
if pause_frames > 0:
|
||||||
pause_frames -= 1
|
pause_frames -= 1
|
||||||
else:
|
else:
|
||||||
center += direction
|
center += self.driver.signed(preset, direction)
|
||||||
if center >= self.driver.num_leds - 1:
|
if center >= self.driver.num_leds - 1:
|
||||||
center = self.driver.num_leds - 1
|
center = self.driver.num_leds - 1
|
||||||
direction = -1
|
direction = -1
|
||||||
@@ -121,7 +129,11 @@ class Meteor:
|
|||||||
|
|
||||||
if mode == 1:
|
if mode == 1:
|
||||||
gap = max(0, int(preset.n3))
|
gap = max(0, int(preset.n3))
|
||||||
p1, p2 = 0, self.driver.num_leds - 1 - gap
|
nled = self.driver.num_leds
|
||||||
|
if self.driver.is_reversed(preset):
|
||||||
|
p1, p2 = nled - 1, gap
|
||||||
|
else:
|
||||||
|
p1, p2 = 0, nled - 1 - gap
|
||||||
last = utime.ticks_ms()
|
last = utime.ticks_ms()
|
||||||
while True:
|
while True:
|
||||||
p1, p2, last, stepped = self._run_comet_dual(preset, colors, p1, p2, last)
|
p1, p2, last, stepped = self._run_comet_dual(preset, colors, p1, p2, last)
|
||||||
@@ -131,6 +143,10 @@ class Meteor:
|
|||||||
yield
|
yield
|
||||||
|
|
||||||
if mode == 2:
|
if mode == 2:
|
||||||
|
nled = self.driver.num_leds
|
||||||
|
if self.driver.is_reversed(preset):
|
||||||
|
color_index, center, direction, pause_frames = 0, max(0, nled - 1), -1, 0
|
||||||
|
else:
|
||||||
color_index, center, direction, pause_frames = 0, 0, 1, 0
|
color_index, center, direction, pause_frames = 0, 0, 1, 0
|
||||||
last_update = utime.ticks_ms()
|
last_update = utime.ticks_ms()
|
||||||
while True:
|
while True:
|
||||||
@@ -144,6 +160,10 @@ class Meteor:
|
|||||||
return
|
return
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
nled = self.driver.num_leds
|
||||||
|
if self.driver.is_reversed(preset):
|
||||||
|
color_index, head, direction = 0, max(0, nled - 1), -1
|
||||||
|
else:
|
||||||
color_index, head, direction = 0, 0, 1
|
color_index, head, direction = 0, 0, 1
|
||||||
last_update = utime.ticks_ms()
|
last_update = utime.ticks_ms()
|
||||||
while True:
|
while True:
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class Particles:
|
|||||||
|
|
||||||
def _run_snowfall(self, preset, colors, flakes, last):
|
def _run_snowfall(self, preset, colors, flakes, last):
|
||||||
density = max(1, int(preset.n1) if int(preset.n1) > 0 else 20)
|
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)
|
speed = max(1, abs(self.driver.signed(preset, int(preset.n2) if int(preset.n2) > 0 else 1)))
|
||||||
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:
|
||||||
@@ -25,8 +25,10 @@ class Particles:
|
|||||||
nf = []
|
nf = []
|
||||||
for pos, ci in flakes:
|
for pos, ci in flakes:
|
||||||
if 0 <= pos < self.driver.num_leds:
|
if 0 <= pos < self.driver.num_leds:
|
||||||
self.driver.n[pos] = self.driver.apply_brightness(colors[ci], preset.b)
|
self.driver.n[self.driver.led_i(preset, pos)] = self.driver.apply_brightness(
|
||||||
pos -= speed
|
colors[ci], preset.b
|
||||||
|
)
|
||||||
|
pos -= self.driver.signed(preset, speed)
|
||||||
if pos >= -1:
|
if pos >= -1:
|
||||||
nf.append([pos, ci])
|
nf.append([pos, ci])
|
||||||
self.driver.n.write()
|
self.driver.n.write()
|
||||||
@@ -34,7 +36,7 @@ class Particles:
|
|||||||
|
|
||||||
def _run_starfall(self, preset, colors, stars, last):
|
def _run_starfall(self, preset, colors, stars, last):
|
||||||
rate = max(1, min(255, int(preset.n1) if int(preset.n1) > 0 else 14))
|
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)
|
speed = max(1, abs(self.driver.signed(preset, int(preset.n2) if int(preset.n2) > 0 else 2)))
|
||||||
tail = max(2, int(preset.n3) if int(preset.n3) > 0 else 10)
|
tail = max(2, int(preset.n3) if int(preset.n3) > 0 else 10)
|
||||||
max_stars = 4
|
max_stars = 4
|
||||||
d = max(1, int(preset.d))
|
d = max(1, int(preset.d))
|
||||||
@@ -65,13 +67,14 @@ class Particles:
|
|||||||
(base[2] * fade) // 255,
|
(base[2] * fade) // 255,
|
||||||
)
|
)
|
||||||
lit = self.driver.apply_brightness(lit, preset.b)
|
lit = self.driver.apply_brightness(lit, preset.b)
|
||||||
o = self.driver.n[idx]
|
pix = self.driver.led_i(preset, idx)
|
||||||
self.driver.n[idx] = (
|
o = self.driver.n[pix]
|
||||||
|
self.driver.n[pix] = (
|
||||||
max(o[0], lit[0]),
|
max(o[0], lit[0]),
|
||||||
max(o[1], lit[1]),
|
max(o[1], lit[1]),
|
||||||
max(o[2], lit[2]),
|
max(o[2], lit[2]),
|
||||||
)
|
)
|
||||||
h -= speed
|
h -= self.driver.signed(preset, speed)
|
||||||
if h >= -tail:
|
if h >= -tail:
|
||||||
s["h"] = h
|
s["h"] = h
|
||||||
ns.append(s)
|
ns.append(s)
|
||||||
|
|||||||
19
src/patterns/pattern_direction.py
Normal file
19
src/patterns/pattern_direction.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
"""Strip install direction: n5 bit 0 reverses along-strip motion (upside-down wiring)."""
|
||||||
|
|
||||||
|
|
||||||
|
def is_reversed(preset):
|
||||||
|
return bool(int(getattr(preset, "n5", 0) or 0) & 1)
|
||||||
|
|
||||||
|
|
||||||
|
def led_i(driver, preset, logical_index):
|
||||||
|
"""Map a logical strip index (0 = pattern start) to a physical pixel index."""
|
||||||
|
n = int(driver.num_leds)
|
||||||
|
i = int(logical_index)
|
||||||
|
if 0 <= i < n and is_reversed(preset):
|
||||||
|
return n - 1 - i
|
||||||
|
return i
|
||||||
|
|
||||||
|
|
||||||
|
def signed(preset, value):
|
||||||
|
v = int(value)
|
||||||
|
return -v if is_reversed(preset) else v
|
||||||
@@ -13,13 +13,8 @@ class Pulse:
|
|||||||
bg_base = preset.background_or(colors)
|
bg_base = preset.background_or(colors)
|
||||||
self.driver.fill(self.driver.apply_brightness(bg_base, preset.b))
|
self.driver.fill(self.driver.apply_brightness(bg_base, preset.b))
|
||||||
|
|
||||||
|
manual = not preset.a
|
||||||
color_index = self.driver.step % max(1, len(colors))
|
color_index = self.driver.step % max(1, len(colors))
|
||||||
if not preset.a:
|
|
||||||
# Manual / beat trigger: each select restarts this generator and resets
|
|
||||||
# cycle_start below. Advancing step here makes each beat the next colour
|
|
||||||
# without requiring a full wall-clock cycle between beats.
|
|
||||||
nclr = max(1, len(colors))
|
|
||||||
self.driver.step = (color_index + 1) % nclr
|
|
||||||
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
|
||||||
@@ -29,7 +24,7 @@ class Pulse:
|
|||||||
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
|
||||||
decay_ms = max(0, int(preset.n3)) # Decay time in ms
|
decay_ms = max(0, int(preset.n3)) # Decay time in ms
|
||||||
delay_ms = max(0, int(preset.d))
|
delay_ms = 0 if manual else max(0, int(preset.d))
|
||||||
|
|
||||||
total_ms = attack_ms + hold_ms + decay_ms + delay_ms
|
total_ms = attack_ms + hold_ms + decay_ms + delay_ms
|
||||||
if total_ms <= 0:
|
if total_ms <= 0:
|
||||||
@@ -58,12 +53,13 @@ class Pulse:
|
|||||||
# Delay phase: LEDs off between pulses
|
# Delay phase: LEDs off between pulses
|
||||||
self.driver.fill(bg_color)
|
self.driver.fill(bg_color)
|
||||||
else:
|
else:
|
||||||
# End of cycle: auto advances colour and loops; manual already
|
# End of cycle: advance colour for the next run, then loop or stop.
|
||||||
# advanced step at run start for the next beat.
|
nclr = max(1, len(colors))
|
||||||
if not preset.a:
|
color_index = (color_index + 1) % nclr
|
||||||
break
|
|
||||||
color_index = (color_index + 1) % max(1, len(colors))
|
|
||||||
self.driver.step = color_index
|
self.driver.step = color_index
|
||||||
|
if manual:
|
||||||
|
self.driver.fill(bg_color)
|
||||||
|
break
|
||||||
cycle_start = now
|
cycle_start = now
|
||||||
yield
|
yield
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -32,6 +32,18 @@ class Preset:
|
|||||||
int_fields = {"d", "b", "n1", "n2", "n3", "n4", "n5", "n6"}
|
int_fields = {"d", "b", "n1", "n2", "n3", "n4", "n5", "n6"}
|
||||||
allowed_fields = {"p", "c", "d", "b", "a", "bg", "n1", "n2", "n3", "n4", "n5", "n6"}
|
allowed_fields = {"p", "c", "d", "b", "a", "bg", "n1", "n2", "n3", "n4", "n5", "n6"}
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
|
if key == "reverse":
|
||||||
|
try:
|
||||||
|
if isinstance(value, bool):
|
||||||
|
self.n5 = 1 if value else 0
|
||||||
|
elif isinstance(value, (int, float)):
|
||||||
|
self.n5 = 1 if int(value) else 0
|
||||||
|
elif isinstance(value, str):
|
||||||
|
lowered = value.lower()
|
||||||
|
self.n5 = 1 if lowered in ("true", "1", "yes", "on") else 0
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
pass
|
||||||
|
continue
|
||||||
key = aliases.get(key, key)
|
key = aliases.get(key, key)
|
||||||
if key not in allowed_fields:
|
if key not in allowed_fields:
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ class Presets:
|
|||||||
if (
|
if (
|
||||||
preset_name == self.selected
|
preset_name == self.selected
|
||||||
and not preset.a
|
and not preset.a
|
||||||
and preset.p == "chase"
|
and preset.p in ("chase", "pulse")
|
||||||
and self.generator is not None
|
and self.generator is not None
|
||||||
):
|
):
|
||||||
while self.generator is not None:
|
while self.generator is not None:
|
||||||
@@ -222,6 +222,21 @@ class Presets:
|
|||||||
self.n = NeoPixel(Pin(pin, Pin.OUT), num_leds)
|
self.n = NeoPixel(Pin(pin, Pin.OUT), num_leds)
|
||||||
self.num_leds = num_leds
|
self.num_leds = num_leds
|
||||||
|
|
||||||
|
def is_reversed(self, preset):
|
||||||
|
from patterns.pattern_direction import is_reversed as _is_reversed
|
||||||
|
|
||||||
|
return _is_reversed(preset)
|
||||||
|
|
||||||
|
def led_i(self, preset, logical_index):
|
||||||
|
from patterns.pattern_direction import led_i as _led_i
|
||||||
|
|
||||||
|
return _led_i(self, preset, logical_index)
|
||||||
|
|
||||||
|
def signed(self, preset, value):
|
||||||
|
from patterns.pattern_direction import signed as _signed
|
||||||
|
|
||||||
|
return _signed(preset, value)
|
||||||
|
|
||||||
def apply_brightness(self, color, brightness_override=None):
|
def apply_brightness(self, color, brightness_override=None):
|
||||||
# Combine per-preset brightness (override) with global brightness self.b
|
# Combine per-preset brightness (override) with global brightness self.b
|
||||||
local = brightness_override if brightness_override is not None else 255
|
local = brightness_override if brightness_override is not None else 255
|
||||||
|
|||||||
Reference in New Issue
Block a user