| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- #!/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
- # 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", mode_rainbow),
- 8: ("Rainbow Cycle", 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),
- }
- 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())]
|