|
|
@@ -0,0 +1,1132 @@
|
|
|
+#!/usr/bin/env python3
|
|
|
+"""
|
|
|
+WLED Basic Effects for Raspberry Pi
|
|
|
+Effects 0-30: Static, Blink, Rainbow, Scan, etc.
|
|
|
+Ported from WLED FX.cpp
|
|
|
+"""
|
|
|
+import random
|
|
|
+import math
|
|
|
+from ..segment import Segment
|
|
|
+from ..utils.colors import *
|
|
|
+
|
|
|
+# Effect return value is delay in milliseconds
|
|
|
+FRAMETIME = 24 # ~42 FPS
|
|
|
+
|
|
|
+def mode_static(seg: Segment) -> int:
|
|
|
+ """Solid color"""
|
|
|
+ seg.fill(seg.get_color(0))
|
|
|
+ return 350 if seg.call == 0 else FRAMETIME
|
|
|
+
|
|
|
+def mode_blink(seg: Segment) -> int:
|
|
|
+ """Blink between two colors"""
|
|
|
+ cycle_time = (255 - seg.speed) * 20
|
|
|
+ on_time = FRAMETIME + ((cycle_time * seg.intensity) >> 8)
|
|
|
+ cycle_time += FRAMETIME * 2
|
|
|
+
|
|
|
+ now = seg.now()
|
|
|
+ iteration = now // cycle_time
|
|
|
+ rem = now % cycle_time
|
|
|
+
|
|
|
+ on = (iteration != seg.step) or (rem <= on_time)
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ seg.fill(seg.get_color(0) if on else seg.get_color(1))
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_strobe(seg: Segment) -> int:
|
|
|
+ """Strobe effect"""
|
|
|
+ cycle_time = (255 - seg.speed) * 20 + FRAMETIME * 2
|
|
|
+ now = seg.now()
|
|
|
+ iteration = now // cycle_time
|
|
|
+ on = (iteration != seg.step)
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ seg.fill(seg.get_color(0) if on else seg.get_color(1))
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_breath(seg: Segment) -> int:
|
|
|
+ """Breathing effect"""
|
|
|
+ counter = (seg.now() * ((seg.speed >> 3) + 10)) & 0xFFFF
|
|
|
+ counter = (counter >> 2) + (counter >> 4)
|
|
|
+
|
|
|
+ var = 0
|
|
|
+ if counter < 16384:
|
|
|
+ if counter > 8192:
|
|
|
+ counter = 8192 - (counter - 8192)
|
|
|
+ var = sin16(counter) // 103
|
|
|
+
|
|
|
+ lum = 30 + var
|
|
|
+ for i in range(seg.length):
|
|
|
+ seg.set_pixel_color(i, color_blend(seg.get_color(1),
|
|
|
+ seg.color_from_palette(i),
|
|
|
+ lum & 0xFF))
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_fade(seg: Segment) -> int:
|
|
|
+ """Fade between two colors"""
|
|
|
+ counter = seg.now() * ((seg.speed >> 3) + 10)
|
|
|
+ lum = triwave16(counter & 0xFFFF) >> 8
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ seg.set_pixel_color(i, color_blend(seg.get_color(1),
|
|
|
+ seg.color_from_palette(i),
|
|
|
+ lum))
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_scan(seg: Segment) -> int:
|
|
|
+ """Scanning pixel"""
|
|
|
+ if seg.length <= 1:
|
|
|
+ return mode_static(seg)
|
|
|
+
|
|
|
+ cycle_time = 750 + (255 - seg.speed) * 150
|
|
|
+ perc = seg.now() % cycle_time
|
|
|
+ prog = (perc * 65535) // cycle_time
|
|
|
+ size = 1 + ((seg.intensity * seg.length) >> 9)
|
|
|
+ led_index = (prog * ((seg.length * 2) - size * 2)) >> 16
|
|
|
+
|
|
|
+ seg.fill(seg.get_color(1))
|
|
|
+
|
|
|
+ led_offset = led_index - (seg.length - size)
|
|
|
+ led_offset = abs(led_offset)
|
|
|
+
|
|
|
+ for j in range(led_offset, min(led_offset + size, seg.length)):
|
|
|
+ seg.set_pixel_color(j, seg.color_from_palette(j))
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_dual_scan(seg: Segment) -> int:
|
|
|
+ """Dual scanning pixels"""
|
|
|
+ if seg.length <= 1:
|
|
|
+ return mode_static(seg)
|
|
|
+
|
|
|
+ cycle_time = 750 + (255 - seg.speed) * 150
|
|
|
+ perc = seg.now() % cycle_time
|
|
|
+ prog = (perc * 65535) // cycle_time
|
|
|
+ size = 1 + ((seg.intensity * seg.length) >> 9)
|
|
|
+ led_index = (prog * ((seg.length * 2) - size * 2)) >> 16
|
|
|
+
|
|
|
+ seg.fill(seg.get_color(1))
|
|
|
+
|
|
|
+ led_offset = led_index - (seg.length - size)
|
|
|
+ led_offset = abs(led_offset)
|
|
|
+
|
|
|
+ # First scanner
|
|
|
+ for j in range(led_offset, min(led_offset + size, seg.length)):
|
|
|
+ seg.set_pixel_color(j, seg.color_from_palette(j))
|
|
|
+
|
|
|
+ # Second scanner (opposite direction)
|
|
|
+ for j in range(led_offset, min(led_offset + size, seg.length)):
|
|
|
+ i2 = seg.length - 1 - j
|
|
|
+ seg.set_pixel_color(i2, seg.color_from_palette(i2))
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_rainbow(seg: Segment) -> int:
|
|
|
+ """Solid rainbow (cycles through hues)"""
|
|
|
+ counter = (seg.now() * ((seg.speed >> 2) + 2)) & 0xFFFF
|
|
|
+ counter = counter >> 8
|
|
|
+
|
|
|
+ if seg.intensity < 128:
|
|
|
+ color = color_blend(color_wheel(counter), WHITE,
|
|
|
+ 128 - seg.intensity)
|
|
|
+ else:
|
|
|
+ color = color_wheel(counter)
|
|
|
+
|
|
|
+ seg.fill(color)
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_rainbow_cycle(seg: Segment) -> int:
|
|
|
+ """Rainbow distributed across strip"""
|
|
|
+ counter = (seg.now() * ((seg.speed >> 2) + 2)) & 0xFFFF
|
|
|
+ counter = counter >> 8
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ # intensity controls density
|
|
|
+ index = (i * (16 << (seg.intensity // 29)) // seg.length) + counter
|
|
|
+ seg.set_pixel_color(i, color_wheel(index & 0xFF))
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_theater_chase(seg: Segment) -> int:
|
|
|
+ """Theater chase effect"""
|
|
|
+ width = 3 + (seg.intensity >> 4)
|
|
|
+ cycle_time = 50 + (255 - seg.speed)
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ if (i % width) == seg.aux0:
|
|
|
+ seg.set_pixel_color(i, seg.color_from_palette(i))
|
|
|
+ else:
|
|
|
+ seg.set_pixel_color(i, seg.get_color(1))
|
|
|
+
|
|
|
+ if iteration != seg.step:
|
|
|
+ seg.aux0 = (seg.aux0 + 1) % width
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_running_lights(seg: Segment) -> int:
|
|
|
+ """Running lights with sine wave"""
|
|
|
+ x_scale = seg.intensity >> 2
|
|
|
+ counter = (seg.now() * seg.speed) >> 9
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ a = i * x_scale - counter
|
|
|
+ s = sin8(a & 0xFF)
|
|
|
+ color = color_blend(seg.get_color(1),
|
|
|
+ seg.color_from_palette(i), s)
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_color_wipe(seg: Segment) -> int:
|
|
|
+ """Color wipe effect"""
|
|
|
+ if seg.length <= 1:
|
|
|
+ return mode_static(seg)
|
|
|
+
|
|
|
+ cycle_time = 750 + (255 - seg.speed) * 150
|
|
|
+ perc = seg.now() % cycle_time
|
|
|
+ prog = (perc * 65535) // cycle_time
|
|
|
+ back = prog > 32767
|
|
|
+
|
|
|
+ if back:
|
|
|
+ prog -= 32767
|
|
|
+ if seg.step == 0:
|
|
|
+ seg.step = 1
|
|
|
+ else:
|
|
|
+ if seg.step == 2:
|
|
|
+ seg.step = 3
|
|
|
+
|
|
|
+ led_index = (prog * seg.length) >> 15
|
|
|
+ rem = (prog * seg.length) * 2
|
|
|
+ rem //= (seg.intensity + 1)
|
|
|
+ rem = min(255, rem)
|
|
|
+
|
|
|
+ col0 = seg.get_color(0)
|
|
|
+ col1 = seg.get_color(1)
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ if i < led_index:
|
|
|
+ seg.set_pixel_color(i, col1 if back else col0)
|
|
|
+ else:
|
|
|
+ seg.set_pixel_color(i, col0 if back else col1)
|
|
|
+ if i == led_index:
|
|
|
+ blended = color_blend(col1 if back else col0,
|
|
|
+ col0 if back else col1,
|
|
|
+ rem)
|
|
|
+ seg.set_pixel_color(i, blended)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_random_color(seg: Segment) -> int:
|
|
|
+ """Random solid colors with fade"""
|
|
|
+ cycle_time = 200 + (255 - seg.speed) * 50
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+ rem = seg.now() % cycle_time
|
|
|
+ fade_dur = (cycle_time * seg.intensity) >> 8
|
|
|
+
|
|
|
+ fade = 255
|
|
|
+ if fade_dur:
|
|
|
+ fade = (rem * 255) // fade_dur
|
|
|
+ fade = min(255, fade)
|
|
|
+
|
|
|
+ if seg.call == 0:
|
|
|
+ seg.aux0 = random.randint(0, 255)
|
|
|
+ seg.step = 2
|
|
|
+
|
|
|
+ if iteration != seg.step:
|
|
|
+ seg.aux1 = seg.aux0
|
|
|
+ seg.aux0 = random.randint(0, 255)
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ color = color_blend(color_wheel(seg.aux1),
|
|
|
+ color_wheel(seg.aux0), fade)
|
|
|
+ seg.fill(color)
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_dynamic(seg: Segment) -> int:
|
|
|
+ """Dynamic random colors per pixel"""
|
|
|
+ if seg.call == 0:
|
|
|
+ seg.data = [random.randint(0, 255) for _ in range(seg.length)]
|
|
|
+
|
|
|
+ cycle_time = 50 + (255 - seg.speed) * 15
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+
|
|
|
+ if iteration != seg.step and seg.speed != 0:
|
|
|
+ for i in range(seg.length):
|
|
|
+ if random.randint(0, 255) <= seg.intensity:
|
|
|
+ seg.data[i] = random.randint(0, 255)
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ seg.set_pixel_color(i, color_wheel(seg.data[i]))
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_twinkle(seg: Segment) -> int:
|
|
|
+ """Twinkle effect"""
|
|
|
+ seg.fade_out(224)
|
|
|
+
|
|
|
+ cycle_time = 20 + (255 - seg.speed) * 5
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+
|
|
|
+ if iteration != seg.step:
|
|
|
+ max_on = max(1, (seg.intensity * seg.length) // 255)
|
|
|
+ if seg.aux0 >= max_on:
|
|
|
+ seg.aux0 = 0
|
|
|
+ seg.aux1 = random.randint(0, 0xFFFF)
|
|
|
+ seg.aux0 += 1
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ prng = seg.aux1
|
|
|
+ for _ in range(seg.aux0):
|
|
|
+ prng = (prng * 2053 + 13849) & 0xFFFF
|
|
|
+ j = (prng * seg.length) >> 16
|
|
|
+ if j < seg.length:
|
|
|
+ seg.set_pixel_color(j, seg.color_from_palette(j))
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_sparkle(seg: Segment) -> int:
|
|
|
+ """Single sparkle effect"""
|
|
|
+ for i in range(seg.length):
|
|
|
+ seg.set_pixel_color(i, seg.color_from_palette(i))
|
|
|
+
|
|
|
+ cycle_time = 10 + (255 - seg.speed) * 2
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+
|
|
|
+ if iteration != seg.step:
|
|
|
+ seg.aux0 = random.randint(0, seg.length - 1)
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ seg.set_pixel_color(seg.aux0, seg.get_color(0))
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_fire(seg: Segment) -> int:
|
|
|
+ """Fire/flame effect"""
|
|
|
+ if seg.call == 0:
|
|
|
+ seg.data = [0] * seg.length
|
|
|
+
|
|
|
+ # Cooling parameter (higher = cooler flames)
|
|
|
+ cooling = ((100 - (seg.intensity >> 1)) * 10) // seg.length + 2
|
|
|
+
|
|
|
+ # Heat decay for all pixels
|
|
|
+ for i in range(seg.length):
|
|
|
+ cool_down = random.randint(0, cooling)
|
|
|
+ seg.data[i] = max(0, seg.data[i] - cool_down)
|
|
|
+
|
|
|
+ # Heat drift upward
|
|
|
+ for i in range(seg.length - 1, 2, -1):
|
|
|
+ seg.data[i] = (seg.data[i - 1] + seg.data[i - 2] + seg.data[i - 2]) // 3
|
|
|
+
|
|
|
+ # Randomly ignite new sparks near bottom
|
|
|
+ if random.randint(0, 255) < seg.intensity:
|
|
|
+ spark_pos = random.randint(0, min(7, seg.length - 1))
|
|
|
+ seg.data[spark_pos] = min(255, seg.data[spark_pos] + random.randint(160, 255))
|
|
|
+
|
|
|
+ # Convert heat to colors
|
|
|
+ for i in range(seg.length):
|
|
|
+ heat = seg.data[i]
|
|
|
+
|
|
|
+ # Black -> Red -> Yellow -> White
|
|
|
+ if heat < 85:
|
|
|
+ color = (heat * 3, 0, 0)
|
|
|
+ elif heat < 170:
|
|
|
+ h = heat - 85
|
|
|
+ color = (255, h * 3, 0)
|
|
|
+ else:
|
|
|
+ h = heat - 170
|
|
|
+ color = (255, 255, h * 3)
|
|
|
+
|
|
|
+ r, g, b = color
|
|
|
+ seg.set_pixel_color(i, (r << 16) | (g << 8) | b)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_comet(seg: Segment) -> int:
|
|
|
+ """Comet/shooting star effect"""
|
|
|
+ if seg.call == 0:
|
|
|
+ seg.aux0 = 0
|
|
|
+ seg.aux1 = 0
|
|
|
+
|
|
|
+ seg.fade_out(128)
|
|
|
+
|
|
|
+ size = 1 + ((seg.intensity * seg.length) >> 9)
|
|
|
+ cycle_time = 10 + (255 - seg.speed)
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+
|
|
|
+ if iteration != seg.step:
|
|
|
+ seg.aux0 = (seg.aux0 + 1) % seg.length
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ # Draw comet
|
|
|
+ for i in range(size):
|
|
|
+ pos = (seg.aux0 - i) % seg.length
|
|
|
+ brightness = 255 - (i * 255 // max(1, size))
|
|
|
+ color = color_blend(0, seg.color_from_palette(pos), brightness)
|
|
|
+ seg.set_pixel_color(pos, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_chase(seg: Segment) -> int:
|
|
|
+ """Chase effect with colored segments"""
|
|
|
+ if seg.call == 0:
|
|
|
+ seg.aux0 = 0
|
|
|
+
|
|
|
+ size = max(1, seg.length // 4)
|
|
|
+ cycle_time = 10 + (255 - seg.speed)
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+
|
|
|
+ if iteration != seg.step:
|
|
|
+ seg.aux0 = (seg.aux0 + 1) % seg.length
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ seg.fill(seg.get_color(1))
|
|
|
+
|
|
|
+ for i in range(size):
|
|
|
+ pos = (seg.aux0 + i) % seg.length
|
|
|
+ seg.set_pixel_color(pos, seg.color_from_palette(pos))
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_police(seg: Segment) -> int:
|
|
|
+ """Police lights (red/blue alternating)"""
|
|
|
+ cycle_time = 25 + (255 - seg.speed)
|
|
|
+ on_time = cycle_time // 2
|
|
|
+
|
|
|
+ now = seg.now()
|
|
|
+ iteration = now // cycle_time
|
|
|
+ rem = now % cycle_time
|
|
|
+ on = rem < on_time
|
|
|
+
|
|
|
+ half = seg.length // 2
|
|
|
+
|
|
|
+ # Red on left, blue on right
|
|
|
+ red = (255, 0, 0)
|
|
|
+ blue = (0, 0, 255)
|
|
|
+ off_color = (0, 0, 0)
|
|
|
+
|
|
|
+ for i in range(half):
|
|
|
+ if (iteration % 2 == 0 and on) or (iteration % 2 == 1 and not on):
|
|
|
+ seg.set_pixel_color(i, (red[0] << 16) | (red[1] << 8) | red[2])
|
|
|
+ else:
|
|
|
+ seg.set_pixel_color(i, (off_color[0] << 16) | (off_color[1] << 8) | off_color[2])
|
|
|
+
|
|
|
+ for i in range(half, seg.length):
|
|
|
+ if (iteration % 2 == 1 and on) or (iteration % 2 == 0 and not on):
|
|
|
+ seg.set_pixel_color(i, (blue[0] << 16) | (blue[1] << 8) | blue[2])
|
|
|
+ else:
|
|
|
+ seg.set_pixel_color(i, (off_color[0] << 16) | (off_color[1] << 8) | off_color[2])
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_lightning(seg: Segment) -> int:
|
|
|
+ """Lightning flash effect"""
|
|
|
+ if seg.call == 0:
|
|
|
+ seg.aux0 = 0
|
|
|
+ seg.aux1 = 0
|
|
|
+
|
|
|
+ cycle_time = 50 + (255 - seg.speed) * 10
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+
|
|
|
+ if iteration != seg.step:
|
|
|
+ # Random chance of lightning
|
|
|
+ if random.randint(0, 255) < seg.intensity:
|
|
|
+ seg.aux0 = random.randint(3, 8) # Number of flashes
|
|
|
+ seg.aux1 = seg.now()
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ # Flash sequence
|
|
|
+ if seg.aux0 > 0:
|
|
|
+ flash_duration = 50
|
|
|
+ time_since = seg.now() - seg.aux1
|
|
|
+
|
|
|
+ if time_since < flash_duration:
|
|
|
+ # Flash on
|
|
|
+ brightness = 255 - (time_since * 255 // flash_duration)
|
|
|
+ for i in range(seg.length):
|
|
|
+ color = color_blend(0, WHITE, brightness)
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+ else:
|
|
|
+ # Flash off, wait for next
|
|
|
+ if time_since > flash_duration + random.randint(10, 100):
|
|
|
+ seg.aux0 -= 1
|
|
|
+ seg.aux1 = seg.now()
|
|
|
+ else:
|
|
|
+ seg.fill(seg.get_color(1))
|
|
|
+ else:
|
|
|
+ seg.fill(seg.get_color(1))
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_fireworks(seg: Segment) -> int:
|
|
|
+ """Fireworks effect"""
|
|
|
+ seg.fade_out(64)
|
|
|
+
|
|
|
+ cycle_time = 20 + (255 - seg.speed)
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+
|
|
|
+ if iteration != seg.step:
|
|
|
+ # Launch new firework
|
|
|
+ if random.randint(0, 255) < seg.intensity:
|
|
|
+ pos = random.randint(0, seg.length - 1)
|
|
|
+ color = color_wheel(random.randint(0, 255))
|
|
|
+
|
|
|
+ # Bright center
|
|
|
+ seg.set_pixel_color(pos, color)
|
|
|
+
|
|
|
+ # Dimmer neighbors
|
|
|
+ if pos > 0:
|
|
|
+ seg.set_pixel_color(pos - 1, color_blend(0, color, 128))
|
|
|
+ if pos < seg.length - 1:
|
|
|
+ seg.set_pixel_color(pos + 1, color_blend(0, color, 128))
|
|
|
+
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_ripple(seg: Segment) -> int:
|
|
|
+ """Ripple effect"""
|
|
|
+ if seg.call == 0:
|
|
|
+ seg.data = [0] * seg.length
|
|
|
+ seg.aux0 = seg.length // 2
|
|
|
+
|
|
|
+ seg.fade_out(250)
|
|
|
+
|
|
|
+ cycle_time = 50 + (255 - seg.speed) * 2
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+
|
|
|
+ if iteration != seg.step:
|
|
|
+ # New ripple
|
|
|
+ if random.randint(0, 255) < seg.intensity:
|
|
|
+ seg.aux0 = random.randint(0, seg.length - 1)
|
|
|
+ seg.data[seg.aux0] = 255
|
|
|
+
|
|
|
+ # Propagate ripple
|
|
|
+ new_data = seg.data.copy()
|
|
|
+ for i in range(1, seg.length - 1):
|
|
|
+ new_data[i] = (seg.data[i - 1] + seg.data[i + 1]) // 2
|
|
|
+ seg.data = new_data
|
|
|
+
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ if seg.data[i] > 0:
|
|
|
+ color = color_blend(seg.get_color(1),
|
|
|
+ seg.color_from_palette(i),
|
|
|
+ seg.data[i])
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_flow(seg: Segment) -> int:
|
|
|
+ """Smooth flowing color movement"""
|
|
|
+ counter = seg.now() * ((seg.speed >> 3) + 1)
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ pos = ((i * 256 // seg.length) + counter) & 0xFFFF
|
|
|
+ color = color_wheel((pos >> 8) & 0xFF)
|
|
|
+
|
|
|
+ # Apply intensity as brightness modulation
|
|
|
+ brightness = 128 + ((sin8((pos >> 7) & 0xFF) - 128) * seg.intensity // 255)
|
|
|
+ color = color_blend(0, color, brightness)
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_colorloop(seg: Segment) -> int:
|
|
|
+ """Smooth color loop across entire strip"""
|
|
|
+ counter = (seg.now() * ((seg.speed >> 3) + 1)) & 0xFFFF
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ # Create gradient based on position and time
|
|
|
+ hue = ((i * 256 // max(1, seg.length)) + (counter >> 7)) & 0xFF
|
|
|
+ color = color_wheel(hue)
|
|
|
+
|
|
|
+ # Intensity controls saturation
|
|
|
+ if seg.intensity < 255:
|
|
|
+ color = color_blend(color, WHITE, 255 - seg.intensity)
|
|
|
+
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_palette_flow(seg: Segment) -> int:
|
|
|
+ """Flowing palette colors"""
|
|
|
+ counter = seg.now() * ((seg.speed >> 3) + 1)
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ # Get color from palette based on position and time
|
|
|
+ palette_pos = ((i * 255 // max(1, seg.length)) + (counter >> 7)) & 0xFF
|
|
|
+ color = seg.color_from_palette(palette_pos)
|
|
|
+
|
|
|
+ # Intensity controls brightness modulation
|
|
|
+ if seg.intensity < 255:
|
|
|
+ brightness = 128 + ((sin8(palette_pos) - 128) * seg.intensity // 255)
|
|
|
+ color = color_blend(0, color, brightness)
|
|
|
+
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_gradient(seg: Segment) -> int:
|
|
|
+ """Smooth gradient between colors"""
|
|
|
+ for i in range(seg.length):
|
|
|
+ # Create gradient from color 0 to color 2
|
|
|
+ blend_amount = (i * 255) // max(1, seg.length - 1)
|
|
|
+ color = color_blend(seg.get_color(0), seg.get_color(2), blend_amount)
|
|
|
+
|
|
|
+ # Intensity controls a pulsing brightness
|
|
|
+ if seg.intensity > 0:
|
|
|
+ counter = (seg.now() * ((seg.speed >> 3) + 1)) & 0xFFFF
|
|
|
+ pulse = sin8((counter >> 8) & 0xFF)
|
|
|
+ brightness = 128 + ((pulse - 128) * seg.intensity // 255)
|
|
|
+ color = color_blend(0, color, brightness)
|
|
|
+
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_multi_strobe(seg: Segment) -> int:
|
|
|
+ """Multi-color strobe effect"""
|
|
|
+ cycle_time = 50 + (255 - seg.speed)
|
|
|
+ flash_duration = max(5, cycle_time // 4)
|
|
|
+
|
|
|
+ now = seg.now()
|
|
|
+ iteration = now // cycle_time
|
|
|
+ rem = now % cycle_time
|
|
|
+
|
|
|
+ if rem < flash_duration:
|
|
|
+ # Strobe on with color from palette
|
|
|
+ color_index = (iteration * 85) & 0xFF
|
|
|
+ color = color_wheel(color_index)
|
|
|
+ seg.fill(color)
|
|
|
+ else:
|
|
|
+ # Strobe off
|
|
|
+ seg.fill(seg.get_color(1))
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_waves(seg: Segment) -> int:
|
|
|
+ """Sine wave effect"""
|
|
|
+ counter = seg.now() * ((seg.speed >> 3) + 1)
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ # Create wave pattern
|
|
|
+ wave_pos = (i * 255 // max(1, seg.length)) + (counter >> 7)
|
|
|
+ brightness = sin8(wave_pos & 0xFF)
|
|
|
+
|
|
|
+ # Intensity controls wave amplitude
|
|
|
+ brightness = 128 + ((brightness - 128) * seg.intensity // 255)
|
|
|
+
|
|
|
+ color = color_blend(seg.get_color(1),
|
|
|
+ seg.color_from_palette(i),
|
|
|
+ brightness)
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_bpm(seg: Segment) -> int:
|
|
|
+ """BPM (beats per minute) pulse effect"""
|
|
|
+ # Calculate BPM based on speed (60-180 BPM)
|
|
|
+ bpm = 60 + ((seg.speed * 120) >> 8)
|
|
|
+ ms_per_beat = 60000 // bpm
|
|
|
+
|
|
|
+ beat_phase = (seg.now() % ms_per_beat) * 255 // ms_per_beat
|
|
|
+ brightness = sin8(beat_phase)
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ # Create traveling beat
|
|
|
+ offset = (i * 255 // max(1, seg.length))
|
|
|
+ local_brightness = sin8((beat_phase + offset) & 0xFF)
|
|
|
+
|
|
|
+ # Intensity controls brightness range
|
|
|
+ local_brightness = 128 + ((local_brightness - 128) * seg.intensity // 255)
|
|
|
+
|
|
|
+ color = color_blend(seg.get_color(1),
|
|
|
+ seg.color_from_palette(i),
|
|
|
+ local_brightness)
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_juggle(seg: Segment) -> int:
|
|
|
+ """Juggling colored dots"""
|
|
|
+ if seg.call == 0:
|
|
|
+ seg.data = [0] * 8 # Track 8 dot positions
|
|
|
+
|
|
|
+ seg.fade_out(224)
|
|
|
+
|
|
|
+ cycle_time = 10 + (255 - seg.speed) // 2
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+
|
|
|
+ if iteration != seg.step:
|
|
|
+ # Update dot positions using different sine waves
|
|
|
+ for dot in range(min(8, 1 + seg.intensity // 32)):
|
|
|
+ phase = (seg.now() * (dot + 1)) & 0xFFFF
|
|
|
+ pos = (sin16(phase) + 32768) * seg.length // 65536
|
|
|
+ pos = max(0, min(seg.length - 1, pos))
|
|
|
+
|
|
|
+ hue = (dot * 32) & 0xFF
|
|
|
+ color = color_wheel(hue)
|
|
|
+ seg.set_pixel_color(pos, color)
|
|
|
+
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_meteor(seg: Segment) -> int:
|
|
|
+ """Meteor shower effect with trails"""
|
|
|
+ if seg.call == 0:
|
|
|
+ seg.aux0 = 0
|
|
|
+ seg.aux1 = 0
|
|
|
+
|
|
|
+ # Fade all pixels
|
|
|
+ seg.fade_out(200)
|
|
|
+
|
|
|
+ size = 1 + ((seg.intensity * seg.length) >> 8)
|
|
|
+ cycle_time = 10 + (255 - seg.speed)
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+
|
|
|
+ if iteration != seg.step:
|
|
|
+ seg.aux0 = (seg.aux0 + 1) % (seg.length + size)
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ # Draw meteor head and tail
|
|
|
+ if seg.aux0 < seg.length:
|
|
|
+ for i in range(size):
|
|
|
+ pos = seg.aux0 - i
|
|
|
+ if 0 <= pos < seg.length:
|
|
|
+ brightness = 255 - (i * 200 // max(1, size))
|
|
|
+ color = color_blend(0, seg.color_from_palette(pos), brightness)
|
|
|
+ # Add to existing color for brighter effect
|
|
|
+ existing = seg.get_pixel_color(pos)
|
|
|
+ seg.set_pixel_color(pos, color_add(existing, color))
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_pride(seg: Segment) -> int:
|
|
|
+ """Pride flag colors moving effect"""
|
|
|
+ counter = seg.now() * ((seg.speed >> 3) + 1)
|
|
|
+
|
|
|
+ # Pride flag colors (6 stripes)
|
|
|
+ pride_colors = [
|
|
|
+ (0xE4, 0x00, 0x3A), # Red
|
|
|
+ (0xFF, 0x8C, 0x00), # Orange
|
|
|
+ (0xFF, 0xED, 0x00), # Yellow
|
|
|
+ (0x00, 0x81, 0x1F), # Green
|
|
|
+ (0x00, 0x4C, 0xFF), # Blue
|
|
|
+ (0x76, 0x01, 0x89), # Purple
|
|
|
+ ]
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ # Determine which stripe this pixel belongs to
|
|
|
+ stripe_size = max(1, seg.length // 6)
|
|
|
+ offset = (counter >> 7) & 0xFF
|
|
|
+ stripe_idx = ((i + offset) // stripe_size) % 6
|
|
|
+
|
|
|
+ r, g, b = pride_colors[stripe_idx]
|
|
|
+ color = (r << 16) | (g << 8) | b
|
|
|
+
|
|
|
+ # Intensity controls blending with background
|
|
|
+ if seg.intensity < 255:
|
|
|
+ color = color_blend(seg.get_color(1), color, seg.intensity)
|
|
|
+
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_pacifica(seg: Segment) -> int:
|
|
|
+ """Ocean/water simulation"""
|
|
|
+ if seg.call == 0:
|
|
|
+ seg.data = [0] * seg.length
|
|
|
+
|
|
|
+ counter = seg.now() * ((seg.speed >> 4) + 1)
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ # Create multiple layered waves
|
|
|
+ wave1 = sin8(((i * 5) + (counter >> 5)) & 0xFF)
|
|
|
+ wave2 = sin8(((i * 3) + (counter >> 3)) & 0xFF)
|
|
|
+ wave3 = sin8(((i * 7) + (counter >> 6)) & 0xFF)
|
|
|
+
|
|
|
+ # Combine waves
|
|
|
+ brightness = (wave1 + wave2 + wave3) // 3
|
|
|
+
|
|
|
+ # Blue-green ocean colors
|
|
|
+ if brightness < 64:
|
|
|
+ # Deep blue
|
|
|
+ r, g, b = 0, 0, 60 + brightness
|
|
|
+ elif brightness < 128:
|
|
|
+ # Blue-cyan
|
|
|
+ val = brightness - 64
|
|
|
+ r, g, b = 0, val * 2, 100 + val
|
|
|
+ else:
|
|
|
+ # Cyan-white (foam)
|
|
|
+ val = brightness - 128
|
|
|
+ r, g, b = val, 128 + val, 180 + (val // 2)
|
|
|
+
|
|
|
+ # Apply intensity
|
|
|
+ brightness = 128 + ((brightness - 128) * seg.intensity // 255)
|
|
|
+ color = (r << 16) | (g << 8) | b
|
|
|
+ color = color_blend(0, color, brightness)
|
|
|
+
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_plasma(seg: Segment) -> int:
|
|
|
+ """Plasma effect"""
|
|
|
+ counter = seg.now() * ((seg.speed >> 3) + 1)
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ # Create plasma using multiple sine waves
|
|
|
+ phase1 = sin8(((i * 16) + (counter >> 5)) & 0xFF)
|
|
|
+ phase2 = sin8(((i * 8) + (counter >> 6)) & 0xFF)
|
|
|
+ phase3 = sin8((counter >> 4) & 0xFF)
|
|
|
+
|
|
|
+ # Combine phases
|
|
|
+ hue = (phase1 + phase2 + phase3) & 0xFF
|
|
|
+ color = color_wheel(hue)
|
|
|
+
|
|
|
+ # Intensity controls saturation
|
|
|
+ if seg.intensity < 255:
|
|
|
+ color = color_blend(color, WHITE, 255 - seg.intensity)
|
|
|
+
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_dissolve(seg: Segment) -> int:
|
|
|
+ """Random pixel dissolve/fade"""
|
|
|
+ if seg.call == 0:
|
|
|
+ seg.data = [0] * seg.length
|
|
|
+ for i in range(seg.length):
|
|
|
+ seg.data[i] = random.randint(0, 255)
|
|
|
+
|
|
|
+ cycle_time = 20 + (255 - seg.speed)
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+
|
|
|
+ if iteration != seg.step:
|
|
|
+ # Randomly update some pixels
|
|
|
+ for i in range(seg.length):
|
|
|
+ if random.randint(0, 255) < seg.intensity:
|
|
|
+ seg.data[i] = random.randint(0, 255)
|
|
|
+
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ brightness = seg.data[i]
|
|
|
+ color = color_blend(seg.get_color(1),
|
|
|
+ seg.color_from_palette(i),
|
|
|
+ brightness)
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_glitter(seg: Segment) -> int:
|
|
|
+ """Sparkle glitter overlay"""
|
|
|
+ # Fill with base color
|
|
|
+ for i in range(seg.length):
|
|
|
+ seg.set_pixel_color(i, seg.color_from_palette(i))
|
|
|
+
|
|
|
+ # Add random sparkles based on intensity
|
|
|
+ sparkle_chance = seg.intensity
|
|
|
+ num_sparkles = max(1, (seg.length * sparkle_chance) // 255)
|
|
|
+
|
|
|
+ for _ in range(num_sparkles):
|
|
|
+ if random.randint(0, 255) < seg.speed:
|
|
|
+ pos = random.randint(0, seg.length - 1)
|
|
|
+ seg.set_pixel_color(pos, WHITE)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_confetti(seg: Segment) -> int:
|
|
|
+ """Random colored pixels"""
|
|
|
+ seg.fade_out(224)
|
|
|
+
|
|
|
+ cycle_time = 10 + (255 - seg.speed)
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+
|
|
|
+ if iteration != seg.step:
|
|
|
+ # Add random confetti
|
|
|
+ density = 1 + (seg.intensity >> 5)
|
|
|
+ for _ in range(density):
|
|
|
+ pos = random.randint(0, seg.length - 1)
|
|
|
+ hue = random.randint(0, 255)
|
|
|
+ color = color_wheel(hue)
|
|
|
+ seg.set_pixel_color(pos, color)
|
|
|
+
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_sinelon(seg: Segment) -> int:
|
|
|
+ """Sine wave colored dot"""
|
|
|
+ seg.fade_out(224)
|
|
|
+
|
|
|
+ counter = seg.now() * ((seg.speed >> 2) + 1)
|
|
|
+ phase = (counter >> 8) & 0xFFFF
|
|
|
+
|
|
|
+ # Calculate position using sine
|
|
|
+ pos = (sin16(phase) + 32768) * seg.length // 65536
|
|
|
+ pos = max(0, min(seg.length - 1, pos))
|
|
|
+
|
|
|
+ # Color rotates
|
|
|
+ hue = (counter >> 7) & 0xFF
|
|
|
+ color = color_wheel(hue)
|
|
|
+
|
|
|
+ # Intensity controls trail length (via fade)
|
|
|
+ if seg.intensity < 255:
|
|
|
+ color = color_blend(0, color, seg.intensity)
|
|
|
+
|
|
|
+ seg.set_pixel_color(pos, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_candle(seg: Segment) -> int:
|
|
|
+ """Flickering candle simulation"""
|
|
|
+ if seg.call == 0:
|
|
|
+ seg.data = [128] * seg.length
|
|
|
+
|
|
|
+ # Random flicker for each pixel
|
|
|
+ for i in range(seg.length):
|
|
|
+ # Small random changes
|
|
|
+ change = random.randint(-20, 20)
|
|
|
+ seg.data[i] = max(30, min(255, seg.data[i] + change))
|
|
|
+
|
|
|
+ # Speed affects flicker rate
|
|
|
+ if seg.speed > 128 and random.randint(0, 255) < (seg.speed - 128):
|
|
|
+ seg.data[i] = random.randint(50, 200)
|
|
|
+
|
|
|
+ # Warm orange/yellow color
|
|
|
+ brightness = seg.data[i]
|
|
|
+ r = brightness
|
|
|
+ g = (brightness * 2) // 3
|
|
|
+ b = (brightness * seg.intensity) // 512 # Intensity controls blue
|
|
|
+
|
|
|
+ color = (r << 16) | (g << 8) | b
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_aurora(seg: Segment) -> int:
|
|
|
+ """Northern lights effect"""
|
|
|
+ if seg.call == 0:
|
|
|
+ seg.data = [0] * seg.length
|
|
|
+
|
|
|
+ counter = seg.now() * ((seg.speed >> 4) + 1)
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ # Multiple slow-moving waves
|
|
|
+ wave1 = sin8(((i * 3) + (counter >> 6)) & 0xFF)
|
|
|
+ wave2 = sin8(((i * 5) + (counter >> 7) + 85) & 0xFF)
|
|
|
+ wave3 = sin8(((i * 2) + (counter >> 5) + 170) & 0xFF)
|
|
|
+
|
|
|
+ # Aurora colors: green, blue, purple
|
|
|
+ brightness = (wave1 + wave2 + wave3) // 3
|
|
|
+
|
|
|
+ if brightness < 85:
|
|
|
+ # Green
|
|
|
+ r, g, b = 0, brightness * 2, brightness // 2
|
|
|
+ elif brightness < 170:
|
|
|
+ # Blue-green
|
|
|
+ val = brightness - 85
|
|
|
+ r, g, b = 0, 100 + val, 100 + val
|
|
|
+ else:
|
|
|
+ # Purple-pink
|
|
|
+ val = brightness - 170
|
|
|
+ r, g, b = val * 2, val, 150 + val
|
|
|
+
|
|
|
+ # Apply intensity for brightness variation
|
|
|
+ brightness_mod = 128 + ((brightness - 128) * seg.intensity // 255)
|
|
|
+ color = (r << 16) | (g << 8) | b
|
|
|
+ color = color_blend(0, color, brightness_mod)
|
|
|
+
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_rain(seg: Segment) -> int:
|
|
|
+ """Rain drops falling"""
|
|
|
+ if seg.call == 0:
|
|
|
+ seg.data = [0] * seg.length
|
|
|
+ seg.aux0 = 0
|
|
|
+
|
|
|
+ seg.fade_out(235)
|
|
|
+
|
|
|
+ cycle_time = 15 + (255 - seg.speed)
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+
|
|
|
+ if iteration != seg.step:
|
|
|
+ # New rain drops
|
|
|
+ if random.randint(0, 255) < seg.intensity:
|
|
|
+ pos = random.randint(0, min(5, seg.length - 1)) # Start near beginning
|
|
|
+ seg.data[pos] = 255
|
|
|
+
|
|
|
+ # Move drops down
|
|
|
+ new_data = [0] * seg.length
|
|
|
+ for i in range(seg.length - 1):
|
|
|
+ if seg.data[i] > 0:
|
|
|
+ # Drop moves forward
|
|
|
+ if i + 1 < seg.length:
|
|
|
+ new_data[i + 1] = seg.data[i] - 10
|
|
|
+ seg.data = new_data
|
|
|
+
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ # Draw rain drops (blue)
|
|
|
+ for i in range(seg.length):
|
|
|
+ if seg.data[i] > 0:
|
|
|
+ brightness = seg.data[i]
|
|
|
+ color = color_blend(0, seg.color_from_palette(i), brightness)
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_halloween(seg: Segment) -> int:
|
|
|
+ """Halloween orange and purple"""
|
|
|
+ counter = seg.now() * ((seg.speed >> 3) + 1)
|
|
|
+
|
|
|
+ # Alternating orange and purple
|
|
|
+ orange = (0xFF << 16) | (0x44 << 8) | 0x00
|
|
|
+ purple = (0x88 << 16) | (0x00 << 8) | 0xFF
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ # Create moving pattern
|
|
|
+ phase = ((i * 255 // max(1, seg.length)) + (counter >> 7)) & 0xFF
|
|
|
+ wave = sin8(phase)
|
|
|
+
|
|
|
+ # Blend between orange and purple
|
|
|
+ if wave < 128:
|
|
|
+ color = orange
|
|
|
+ else:
|
|
|
+ color = purple
|
|
|
+
|
|
|
+ # Intensity controls blending smoothness
|
|
|
+ if seg.intensity > 0:
|
|
|
+ blend_amt = (wave * seg.intensity) // 255
|
|
|
+ color = color_blend(orange, purple, blend_amt)
|
|
|
+
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_noise(seg: Segment) -> int:
|
|
|
+ """Perlin-like noise pattern"""
|
|
|
+ if seg.call == 0:
|
|
|
+ seg.data = [random.randint(0, 255) for _ in range(seg.length)]
|
|
|
+
|
|
|
+ cycle_time = 20 + (255 - seg.speed) // 2
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+
|
|
|
+ if iteration != seg.step:
|
|
|
+ # Smooth noise by averaging neighbors
|
|
|
+ new_data = seg.data.copy()
|
|
|
+ for i in range(1, seg.length - 1):
|
|
|
+ avg = (seg.data[i - 1] + seg.data[i] * 2 + seg.data[i + 1]) // 4
|
|
|
+ variation = random.randint(-20, 20)
|
|
|
+ new_data[i] = max(0, min(255, avg + variation))
|
|
|
+
|
|
|
+ # Occasionally inject new random values
|
|
|
+ if random.randint(0, 255) < seg.intensity:
|
|
|
+ pos = random.randint(0, seg.length - 1)
|
|
|
+ new_data[pos] = random.randint(0, 255)
|
|
|
+
|
|
|
+ seg.data = new_data
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ # Map noise to colors
|
|
|
+ for i in range(seg.length):
|
|
|
+ hue = seg.data[i]
|
|
|
+ color = color_wheel(hue)
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+def mode_funky_plank(seg: Segment) -> int:
|
|
|
+ """Multiple colored bars moving"""
|
|
|
+ if seg.call == 0:
|
|
|
+ seg.aux0 = 0
|
|
|
+
|
|
|
+ cycle_time = 10 + (255 - seg.speed)
|
|
|
+ iteration = seg.now() // cycle_time
|
|
|
+
|
|
|
+ if iteration != seg.step:
|
|
|
+ seg.aux0 = (seg.aux0 + 1) % seg.length
|
|
|
+ seg.step = iteration
|
|
|
+
|
|
|
+ num_planks = max(2, 1 + (seg.intensity >> 5))
|
|
|
+ plank_size = max(1, seg.length // num_planks)
|
|
|
+
|
|
|
+ for i in range(seg.length):
|
|
|
+ plank_idx = ((i + seg.aux0) // plank_size) % num_planks
|
|
|
+ hue = (plank_idx * 256 // num_planks) & 0xFF
|
|
|
+ color = color_wheel(hue)
|
|
|
+ seg.set_pixel_color(i, color)
|
|
|
+
|
|
|
+ return FRAMETIME
|
|
|
+
|
|
|
+# Effect registry
|
|
|
+EFFECTS = {
|
|
|
+ 0: ("Static", mode_static),
|
|
|
+ 1: ("Blink", mode_blink),
|
|
|
+ 2: ("Breathe", mode_breath),
|
|
|
+ 3: ("Wipe", mode_color_wipe),
|
|
|
+ 4: ("Fade", mode_fade),
|
|
|
+ 5: ("Scan", mode_scan),
|
|
|
+ 6: ("Dual Scan", mode_dual_scan),
|
|
|
+ 7: ("Rainbow Cycle", mode_rainbow),
|
|
|
+ 8: ("Rainbow", mode_rainbow_cycle),
|
|
|
+ 9: ("Theater Chase", mode_theater_chase),
|
|
|
+ 10: ("Running Lights", mode_running_lights),
|
|
|
+ 11: ("Random Color", mode_random_color),
|
|
|
+ 12: ("Dynamic", mode_dynamic),
|
|
|
+ 13: ("Twinkle", mode_twinkle),
|
|
|
+ 14: ("Sparkle", mode_sparkle),
|
|
|
+ 15: ("Strobe", mode_strobe),
|
|
|
+ 16: ("Fire", mode_fire),
|
|
|
+ 17: ("Comet", mode_comet),
|
|
|
+ 18: ("Chase", mode_chase),
|
|
|
+ 19: ("Police", mode_police),
|
|
|
+ 20: ("Lightning", mode_lightning),
|
|
|
+ 21: ("Fireworks", mode_fireworks),
|
|
|
+ 22: ("Ripple", mode_ripple),
|
|
|
+ 23: ("Flow", mode_flow),
|
|
|
+ 24: ("Colorloop", mode_colorloop),
|
|
|
+ 25: ("Palette Flow", mode_palette_flow),
|
|
|
+ 26: ("Gradient", mode_gradient),
|
|
|
+ 27: ("Multi Strobe", mode_multi_strobe),
|
|
|
+ 28: ("Waves", mode_waves),
|
|
|
+ 29: ("BPM", mode_bpm),
|
|
|
+ 30: ("Juggle", mode_juggle),
|
|
|
+ 31: ("Meteor", mode_meteor),
|
|
|
+ 32: ("Pride", mode_pride),
|
|
|
+ 33: ("Pacifica", mode_pacifica),
|
|
|
+ 34: ("Plasma", mode_plasma),
|
|
|
+ 35: ("Dissolve", mode_dissolve),
|
|
|
+ 36: ("Glitter", mode_glitter),
|
|
|
+ 37: ("Confetti", mode_confetti),
|
|
|
+ 38: ("Sinelon", mode_sinelon),
|
|
|
+ 39: ("Candle", mode_candle),
|
|
|
+ 40: ("Aurora", mode_aurora),
|
|
|
+ 41: ("Rain", mode_rain),
|
|
|
+ 42: ("Halloween", mode_halloween),
|
|
|
+ 43: ("Noise", mode_noise),
|
|
|
+ 44: ("Funky Plank", mode_funky_plank),
|
|
|
+}
|
|
|
+
|
|
|
+def get_effect(effect_id: int):
|
|
|
+ """Get effect function by ID"""
|
|
|
+ if effect_id in EFFECTS:
|
|
|
+ return EFFECTS[effect_id][1]
|
|
|
+ return mode_static
|
|
|
+
|
|
|
+def get_effect_name(effect_id: int) -> str:
|
|
|
+ """Get effect name by ID"""
|
|
|
+ if effect_id in EFFECTS:
|
|
|
+ return EFFECTS[effect_id][0]
|
|
|
+ return "Unknown"
|
|
|
+
|
|
|
+def get_all_effects():
|
|
|
+ """Get list of all effects"""
|
|
|
+ return [(k, v[0]) for k, v in sorted(EFFECTS.items())]
|