1
0

basic_effects.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. #!/usr/bin/env python3
  2. """
  3. WLED Basic Effects for Raspberry Pi
  4. Effects 0-30: Static, Blink, Rainbow, Scan, etc.
  5. Ported from WLED FX.cpp
  6. """
  7. import random
  8. import math
  9. from ..segment import Segment
  10. from ..utils.colors import *
  11. # Effect return value is delay in milliseconds
  12. FRAMETIME = 24 # ~42 FPS
  13. def mode_static(seg: Segment) -> int:
  14. """Solid color"""
  15. seg.fill(seg.get_color(0))
  16. return 350 if seg.call == 0 else FRAMETIME
  17. def mode_blink(seg: Segment) -> int:
  18. """Blink between two colors"""
  19. cycle_time = (255 - seg.speed) * 20
  20. on_time = FRAMETIME + ((cycle_time * seg.intensity) >> 8)
  21. cycle_time += FRAMETIME * 2
  22. now = seg.now()
  23. iteration = now // cycle_time
  24. rem = now % cycle_time
  25. on = (iteration != seg.step) or (rem <= on_time)
  26. seg.step = iteration
  27. seg.fill(seg.get_color(0) if on else seg.get_color(1))
  28. return FRAMETIME
  29. def mode_strobe(seg: Segment) -> int:
  30. """Strobe effect"""
  31. cycle_time = (255 - seg.speed) * 20 + FRAMETIME * 2
  32. now = seg.now()
  33. iteration = now // cycle_time
  34. on = (iteration != seg.step)
  35. seg.step = iteration
  36. seg.fill(seg.get_color(0) if on else seg.get_color(1))
  37. return FRAMETIME
  38. def mode_breath(seg: Segment) -> int:
  39. """Breathing effect"""
  40. counter = (seg.now() * ((seg.speed >> 3) + 10)) & 0xFFFF
  41. counter = (counter >> 2) + (counter >> 4)
  42. var = 0
  43. if counter < 16384:
  44. if counter > 8192:
  45. counter = 8192 - (counter - 8192)
  46. var = sin16(counter) // 103
  47. lum = 30 + var
  48. for i in range(seg.length):
  49. seg.set_pixel_color(i, color_blend(seg.get_color(1),
  50. seg.color_from_palette(i),
  51. lum & 0xFF))
  52. return FRAMETIME
  53. def mode_fade(seg: Segment) -> int:
  54. """Fade between two colors"""
  55. counter = seg.now() * ((seg.speed >> 3) + 10)
  56. lum = triwave16(counter & 0xFFFF) >> 8
  57. for i in range(seg.length):
  58. seg.set_pixel_color(i, color_blend(seg.get_color(1),
  59. seg.color_from_palette(i),
  60. lum))
  61. return FRAMETIME
  62. def mode_scan(seg: Segment) -> int:
  63. """Scanning pixel"""
  64. if seg.length <= 1:
  65. return mode_static(seg)
  66. cycle_time = 750 + (255 - seg.speed) * 150
  67. perc = seg.now() % cycle_time
  68. prog = (perc * 65535) // cycle_time
  69. size = 1 + ((seg.intensity * seg.length) >> 9)
  70. led_index = (prog * ((seg.length * 2) - size * 2)) >> 16
  71. seg.fill(seg.get_color(1))
  72. led_offset = led_index - (seg.length - size)
  73. led_offset = abs(led_offset)
  74. for j in range(led_offset, min(led_offset + size, seg.length)):
  75. seg.set_pixel_color(j, seg.color_from_palette(j))
  76. return FRAMETIME
  77. def mode_dual_scan(seg: Segment) -> int:
  78. """Dual scanning pixels"""
  79. if seg.length <= 1:
  80. return mode_static(seg)
  81. cycle_time = 750 + (255 - seg.speed) * 150
  82. perc = seg.now() % cycle_time
  83. prog = (perc * 65535) // cycle_time
  84. size = 1 + ((seg.intensity * seg.length) >> 9)
  85. led_index = (prog * ((seg.length * 2) - size * 2)) >> 16
  86. seg.fill(seg.get_color(1))
  87. led_offset = led_index - (seg.length - size)
  88. led_offset = abs(led_offset)
  89. # First scanner
  90. for j in range(led_offset, min(led_offset + size, seg.length)):
  91. seg.set_pixel_color(j, seg.color_from_palette(j))
  92. # Second scanner (opposite direction)
  93. for j in range(led_offset, min(led_offset + size, seg.length)):
  94. i2 = seg.length - 1 - j
  95. seg.set_pixel_color(i2, seg.color_from_palette(i2))
  96. return FRAMETIME
  97. def mode_rainbow(seg: Segment) -> int:
  98. """Solid rainbow (cycles through hues)"""
  99. counter = (seg.now() * ((seg.speed >> 2) + 2)) & 0xFFFF
  100. counter = counter >> 8
  101. if seg.intensity < 128:
  102. color = color_blend(color_wheel(counter), WHITE,
  103. 128 - seg.intensity)
  104. else:
  105. color = color_wheel(counter)
  106. seg.fill(color)
  107. return FRAMETIME
  108. def mode_rainbow_cycle(seg: Segment) -> int:
  109. """Rainbow distributed across strip"""
  110. counter = (seg.now() * ((seg.speed >> 2) + 2)) & 0xFFFF
  111. counter = counter >> 8
  112. for i in range(seg.length):
  113. # intensity controls density
  114. index = (i * (16 << (seg.intensity // 29)) // seg.length) + counter
  115. seg.set_pixel_color(i, color_wheel(index & 0xFF))
  116. return FRAMETIME
  117. def mode_theater_chase(seg: Segment) -> int:
  118. """Theater chase effect"""
  119. width = 3 + (seg.intensity >> 4)
  120. cycle_time = 50 + (255 - seg.speed)
  121. iteration = seg.now() // cycle_time
  122. for i in range(seg.length):
  123. if (i % width) == seg.aux0:
  124. seg.set_pixel_color(i, seg.color_from_palette(i))
  125. else:
  126. seg.set_pixel_color(i, seg.get_color(1))
  127. if iteration != seg.step:
  128. seg.aux0 = (seg.aux0 + 1) % width
  129. seg.step = iteration
  130. return FRAMETIME
  131. def mode_running_lights(seg: Segment) -> int:
  132. """Running lights with sine wave"""
  133. x_scale = seg.intensity >> 2
  134. counter = (seg.now() * seg.speed) >> 9
  135. for i in range(seg.length):
  136. a = i * x_scale - counter
  137. s = sin8(a & 0xFF)
  138. color = color_blend(seg.get_color(1),
  139. seg.color_from_palette(i), s)
  140. seg.set_pixel_color(i, color)
  141. return FRAMETIME
  142. def mode_color_wipe(seg: Segment) -> int:
  143. """Color wipe effect"""
  144. if seg.length <= 1:
  145. return mode_static(seg)
  146. cycle_time = 750 + (255 - seg.speed) * 150
  147. perc = seg.now() % cycle_time
  148. prog = (perc * 65535) // cycle_time
  149. back = prog > 32767
  150. if back:
  151. prog -= 32767
  152. if seg.step == 0:
  153. seg.step = 1
  154. else:
  155. if seg.step == 2:
  156. seg.step = 3
  157. led_index = (prog * seg.length) >> 15
  158. rem = (prog * seg.length) * 2
  159. rem //= (seg.intensity + 1)
  160. rem = min(255, rem)
  161. col0 = seg.get_color(0)
  162. col1 = seg.get_color(1)
  163. for i in range(seg.length):
  164. if i < led_index:
  165. seg.set_pixel_color(i, col1 if back else col0)
  166. else:
  167. seg.set_pixel_color(i, col0 if back else col1)
  168. if i == led_index:
  169. blended = color_blend(col1 if back else col0,
  170. col0 if back else col1,
  171. rem)
  172. seg.set_pixel_color(i, blended)
  173. return FRAMETIME
  174. def mode_random_color(seg: Segment) -> int:
  175. """Random solid colors with fade"""
  176. cycle_time = 200 + (255 - seg.speed) * 50
  177. iteration = seg.now() // cycle_time
  178. rem = seg.now() % cycle_time
  179. fade_dur = (cycle_time * seg.intensity) >> 8
  180. fade = 255
  181. if fade_dur:
  182. fade = (rem * 255) // fade_dur
  183. fade = min(255, fade)
  184. if seg.call == 0:
  185. seg.aux0 = random.randint(0, 255)
  186. seg.step = 2
  187. if iteration != seg.step:
  188. seg.aux1 = seg.aux0
  189. seg.aux0 = random.randint(0, 255)
  190. seg.step = iteration
  191. color = color_blend(color_wheel(seg.aux1),
  192. color_wheel(seg.aux0), fade)
  193. seg.fill(color)
  194. return FRAMETIME
  195. def mode_dynamic(seg: Segment) -> int:
  196. """Dynamic random colors per pixel"""
  197. if seg.call == 0:
  198. seg.data = [random.randint(0, 255) for _ in range(seg.length)]
  199. cycle_time = 50 + (255 - seg.speed) * 15
  200. iteration = seg.now() // cycle_time
  201. if iteration != seg.step and seg.speed != 0:
  202. for i in range(seg.length):
  203. if random.randint(0, 255) <= seg.intensity:
  204. seg.data[i] = random.randint(0, 255)
  205. seg.step = iteration
  206. for i in range(seg.length):
  207. seg.set_pixel_color(i, color_wheel(seg.data[i]))
  208. return FRAMETIME
  209. def mode_twinkle(seg: Segment) -> int:
  210. """Twinkle effect"""
  211. seg.fade_out(224)
  212. cycle_time = 20 + (255 - seg.speed) * 5
  213. iteration = seg.now() // cycle_time
  214. if iteration != seg.step:
  215. max_on = max(1, (seg.intensity * seg.length) // 255)
  216. if seg.aux0 >= max_on:
  217. seg.aux0 = 0
  218. seg.aux1 = random.randint(0, 0xFFFF)
  219. seg.aux0 += 1
  220. seg.step = iteration
  221. prng = seg.aux1
  222. for _ in range(seg.aux0):
  223. prng = (prng * 2053 + 13849) & 0xFFFF
  224. j = (prng * seg.length) >> 16
  225. if j < seg.length:
  226. seg.set_pixel_color(j, seg.color_from_palette(j))
  227. return FRAMETIME
  228. def mode_sparkle(seg: Segment) -> int:
  229. """Single sparkle effect"""
  230. for i in range(seg.length):
  231. seg.set_pixel_color(i, seg.color_from_palette(i))
  232. cycle_time = 10 + (255 - seg.speed) * 2
  233. iteration = seg.now() // cycle_time
  234. if iteration != seg.step:
  235. seg.aux0 = random.randint(0, seg.length - 1)
  236. seg.step = iteration
  237. seg.set_pixel_color(seg.aux0, seg.get_color(0))
  238. return FRAMETIME
  239. # Effect registry
  240. EFFECTS = {
  241. 0: ("Static", mode_static),
  242. 1: ("Blink", mode_blink),
  243. 2: ("Breathe", mode_breath),
  244. 3: ("Wipe", mode_color_wipe),
  245. 4: ("Fade", mode_fade),
  246. 5: ("Scan", mode_scan),
  247. 6: ("Dual Scan", mode_dual_scan),
  248. 7: ("Rainbow", mode_rainbow),
  249. 8: ("Rainbow Cycle", mode_rainbow_cycle),
  250. 9: ("Theater Chase", mode_theater_chase),
  251. 10: ("Running Lights", mode_running_lights),
  252. 11: ("Random Color", mode_random_color),
  253. 12: ("Dynamic", mode_dynamic),
  254. 13: ("Twinkle", mode_twinkle),
  255. 14: ("Sparkle", mode_sparkle),
  256. 15: ("Strobe", mode_strobe),
  257. }
  258. def get_effect(effect_id: int):
  259. """Get effect function by ID"""
  260. if effect_id in EFFECTS:
  261. return EFFECTS[effect_id][1]
  262. return mode_static
  263. def get_effect_name(effect_id: int) -> str:
  264. """Get effect name by ID"""
  265. if effect_id in EFFECTS:
  266. return EFFECTS[effect_id][0]
  267. return "Unknown"
  268. def get_all_effects():
  269. """Get list of all effects"""
  270. return [(k, v[0]) for k, v in sorted(EFFECTS.items())]