Skip to content

Commit

Permalink
Add firework effect, num_leds data return
Browse files Browse the repository at this point in the history
  • Loading branch information
meowmeowahr committed Jun 11, 2024
1 parent c364bcb commit b0cbeb4
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 2 deletions.
12 changes: 11 additions & 1 deletion animator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@

import neopixel_emu

from animator import light_funcs
from . import light_funcs
from . import _firework
from ._firework import FireworkArgs

COLORS = [
(255, 0, 0), # Red
Expand Down Expand Up @@ -75,6 +77,7 @@ class AnimationArgs:
fade: FadeArgs = field(default_factory=FadeArgs)
flash: FlashArgs = field(default_factory=FlashArgs)
wipe: WipeArgs = field(default_factory=WipeArgs)
firework: FireworkArgs = field(default_factory=FireworkArgs)


# Set the desired FPS for your animation
Expand Down Expand Up @@ -265,6 +268,13 @@ def cycle(self) -> None:
else:
self.pixels[last_pixel + 1] = self.animation_args.wipe.colorb

self.pixels.brightness = self.animation_state.brightness / 255.0
time.sleep(1 / FAST_FPS)
elif (
self.animation_state.effect == "Firework"
and self.animation_state.state == "ON"
):
_firework.firework_step(self.animation_args.firework, self.pixels)
self.pixels.brightness = self.animation_state.brightness / 255.0
time.sleep(1 / FAST_FPS)
elif (
Expand Down
93 changes: 93 additions & 0 deletions animator/_firework.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import random
from dataclasses import dataclass

try:
import neopixel
except NotImplementedError:
pass

import neopixel_emu

@dataclass
class FireworkArgs:
num_sparks: int = 60
gravity: float = -0.004
brightness_decay: float = 0.985
flare_min_vel: float = 0.5
flare_max_vel: float = 0.9
c1: float = 120
c2: float = 50

def firework_step(settings: FireworkArgs, pixels: 'neopixel_emu.NeoPixel | neopixel.NeoPixel'):
# Reference: http://www.anirama.com/1000leds/1d-fireworks/

sparkPos = [0.0] * settings.num_sparks
sparkVel = [0.0] * settings.num_sparks
sparkCol = [0.0] * settings.num_sparks

flarePos = 0
flareVel = random.uniform(settings.flare_min_vel, settings.flare_max_vel)
brightness = 1.0

# Initialize launch sparks
for i in range(5):
sparkPos[i] = 0
sparkVel[i] = (random.uniform(0, 1) / 255) * (flareVel / 5)
sparkCol[i] = sparkVel[i] * 1000
sparkCol[i] = max(0, min(255, sparkCol[i]))

# Launch
pixels.fill((0, 0, 0))
while flareVel >= -0.2:
# Sparks
for i in range(5):
sparkPos[i] += sparkVel[i]
sparkPos[i] = max(0, min(pixels.n, sparkPos[i]))
sparkVel[i] += settings.gravity
sparkCol[i] += -0.8
sparkCol[i] = max(0, min(255, sparkCol[i]))
if 0 <= int(sparkPos[i]) < pixels.n:
color = (int(sparkCol[i]), int(sparkCol[i] * 0.5), 0) # Using a warm color similar to HeatColor
pixels[int(sparkPos[i])] = (color[0] // 5, color[1] // 5, color[2] // 5) # Reduce brightness

# Flare
if 0 <= int(flarePos) < pixels.n:
pixels[int(flarePos)] = (int(brightness * 255), int(brightness * 255), int(brightness * 255))
pixels.show()
pixels.fill((0, 0, 0))
flarePos += flareVel
flareVel += settings.gravity
brightness *= settings.brightness_decay

# Explode!
nSparks = int(flarePos / 2)
for i in range(nSparks):
sparkPos[i] = flarePos
sparkVel[i] = (random.uniform(0, 2) - 1)
sparkCol[i] = abs(sparkVel[i]) * 500
sparkCol[i] = max(0, min(255, sparkCol[i]))
sparkVel[i] *= flarePos / pixels.n

sparkCol[0] = 255 # This will be our known spark
dying_gravity = settings.gravity
while sparkCol[0] > settings.c2 / 128:
pixels.fill((0, 0, 0))
for i in range(nSparks):
sparkPos[i] += sparkVel[i]
sparkPos[i] = max(0, min(pixels.n, sparkPos[i]))
sparkVel[i] += dying_gravity
sparkCol[i] *= 0.99
sparkCol[i] = max(0, min(255, sparkCol[i]))

if 0 <= int(sparkPos[i]) < pixels.n:
if sparkCol[i] > settings.c1:
pixels[int(sparkPos[i])] = (255, 255, int(255 * (sparkCol[i] - settings.c1) / (255 - settings.c1)))
elif sparkCol[i] < settings.c2:
pixels[int(sparkPos[i])] = (int(255 * sparkCol[i] / settings.c2), 0, 0)
else:
pixels[int(sparkPos[i])] = (255, int(255 * (sparkCol[i] - settings.c2) / (settings.c1 - settings.c2)), 0)

dying_gravity *= 0.995
pixels.show()
pixels.fill((0, 0, 0))
pixels.show()
3 changes: 2 additions & 1 deletion mqtt_animator.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ def publish_state(cli):
json.dumps({"state": animation_state.state,
"brightness": animation_state.brightness,
"animation": animation_state.effect,
"args": json.dumps(dataclasses.asdict(animation_args))
"args": json.dumps(dataclasses.asdict(animation_args)),
"num_leds": pixels.n
})
)

Expand Down

0 comments on commit b0cbeb4

Please sign in to comment.