diff --git a/include/effect_firework.h b/include/effect_firework.h index fbaa2b0..9a8260a 100644 --- a/include/effect_firework.h +++ b/include/effect_firework.h @@ -1,7 +1,61 @@ #pragma once +#include "prototypes.h" +#include "functions.h" #include "Effect.h" -class FireworkEffect : public Effect { - void loop(); +enum FireworkDotType { FIREWORK_DOT_NONE, FIREWORK_DOT_SHELL, FIREWORK_DOT_SPARK }; + +#define EFFECT_FIREWORK_DRAG 255 +#define EFFECT_FIREWORK_BOUNCE 200 +#define EFFECT_FIREWORK_GRAVITY 10 +#define EFFECT_FIREWORK_SPARKS 12 + +class FireworkEffect; + +class FireworkEffectDot { +private: + Window* _window; + FireworkEffect* _main; + accum88 _x; + accum88 _y; + saccum78 _xv; + saccum78 _yv; + accum88 _r; + CRGB _color; + + void _screenscale(accum88 a, byte n, byte& screen, byte& screenerr); + int16_t _scale15by8_local(int16_t i, fract8 scale); +public: + byte show; + FireworkDotType type; + + FireworkEffectDot(Window* w, FireworkEffect* e); + void draw(); + void move(); + void ground_launch(); + void sky_burst(accum88 basex, accum88 basey, saccum78 basedv, CRGB& basecolor); }; + +class FireworkEffect : public Effect { +private: + Window* window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6); + bool _skyburst = 0; + + accum88 _burst_x; + accum88 _burst_y; + saccum78 _burst_xv; + saccum78 _burst_yv; + CRGB _burst_color; + + FireworkEffectDot* _dot; + FireworkEffectDot* _sparks[EFFECT_FIREWORK_SPARKS]; +public: + FireworkEffect(); + ~FireworkEffect(); + void skyburst(accum88 x, accum88 y, saccum78 xv, saccum78 yv, CRGB c); + boolean supports_window = true; + boolean can_be_shown_with_clock(); + void loop(); +}; + diff --git a/src/effect_firework.cpp b/src/effect_firework.cpp index 59945bc..72430dc 100644 --- a/src/effect_firework.cpp +++ b/src/effect_firework.cpp @@ -1,13 +1,183 @@ +// Based on https://gist.github.com/kriegsman/68929cbd1d6de4535b20 #include "effect_firework.h" -#include "my_fastled.h" -#include "functions.h" -#include "config.h" + +FireworkEffectDot::FireworkEffectDot(Window* w, FireworkEffect* e) { + _window = w; + _main = e; + show = 0; + type = FIREWORK_DOT_NONE; + _x = 0; + _y = 0; + _xv = 0; + _yv = 0; + _r = 0; + _color.setRGB(0, 0, 0); +} + +void FireworkEffectDot::_screenscale(accum88 a, byte n, byte& screen, byte& screenerr) { + byte ia = a >> 8; + screen = scale8(ia, n); + byte m = screen * (256 / n); + screenerr = (ia - m) * scale8(255, n); +} + +int16_t FireworkEffectDot::_scale15by8_local(int16_t i, fract8 scale) { + int16_t result; + result = (int32_t)((int32_t) i*scale)/256; + return result; +} + +void FireworkEffectDot::draw() { + if (!show) return; + byte ix, xe, xc; + byte iy, ye, yc; + _screenscale(_x, _window->width, ix, xe); + _screenscale(_y, _window->height, iy, ye); + xc = 255 - xe; + yc = 255 - ye; + + CRGB c00 = CRGB(dim8_video( scale8( scale8( _color.r, yc), xc)), + dim8_video( scale8( scale8( _color.g, yc), xc)), + dim8_video( scale8( scale8( _color.b, yc), xc))); + CRGB c01 = CRGB(dim8_video( scale8( scale8( _color.r, ye), xc)), + dim8_video( scale8( scale8( _color.g, ye), xc)), + dim8_video( scale8( scale8( _color.b, ye), xc))); + CRGB c10 = CRGB(dim8_video( scale8( scale8( _color.r, yc), xe)), + dim8_video( scale8( scale8( _color.g, yc), xe)), + dim8_video( scale8( scale8( _color.b, yc), xe))); + CRGB c11 = CRGB(dim8_video( scale8( scale8( _color.r, ye), xe)), + dim8_video( scale8( scale8( _color.g, ye), xe)), + dim8_video( scale8( scale8( _color.b, ye), xe))); + _window->addPixelColor(ix, iy, &c00); + _window->addPixelColor(ix, iy+1, &c01); + _window->addPixelColor(ix+1, iy, &c10); + _window->addPixelColor(ix+1, iy+1, &c11); +} + +void FireworkEffectDot::move() { + if (!show) return; + _yv -= EFFECT_FIREWORK_GRAVITY; + _xv = _scale15by8_local(_xv, EFFECT_FIREWORK_DRAG); + _yv = _scale15by8_local(_yv, EFFECT_FIREWORK_DRAG); + + if (type == FIREWORK_DOT_SPARK) { + _xv = _scale15by8_local(_xv, EFFECT_FIREWORK_DRAG); + _yv = _scale15by8_local(_yv, EFFECT_FIREWORK_DRAG); + _color.nscale8(255); + if (!_color) { + show = 0; + } + } + + // Bounce if we hit the ground + if (_xv < 0 && _y < (-_yv)) { + if (type == FIREWORK_DOT_SPARK) { + show = 0; + } else { + _yv = -_yv; + _yv = _scale15by8_local(_yv, EFFECT_FIREWORK_BOUNCE); + if (_yv < 500) { + show = 0; + } + } + } + + if (_yv < 300) { + if (type == FIREWORK_DOT_SHELL) { + if (_y > (uint16_t)0x8000) { + // boom + CRGB white(0xFFFFFF); + _window->clear(&white); + } + show = 0; + _main->skyburst(_x, _y, _xv, _yv, _color); + } + } + + if (type == FIREWORK_DOT_SPARK) { + if ((_xv > 0 && _x>_xv) || (_xv < 0 && _x<(0xFFFF+_xv))) { + _x += _xv; + } else { + show = 0; + } + } else { + _x += _xv; + } + _y += _yv; +} + +void FireworkEffectDot::ground_launch() { + _xv = (int16_t)random16(600) - (int16_t)300; + _yv = 600 + random16(300 + (25 * _window->height)); + _x = 0x8000; + _y = 0; + hsv2rgb_rainbow(CHSV(random8(), 240, 200), _color); + show = 1; +} + +void FireworkEffectDot::sky_burst(accum88 basex, accum88 basey, saccum78 basedv, CRGB& basecolor) { + _xv = basedv + (int16_t)random16(2000) - (int16_t)1000; + _yv = (int16_t)random16(1500) - (int16_t)500; + _x = basex; + _y = basey; + _color = basecolor; + _color *= 4; + type = FIREWORK_DOT_SPARK; + show = 1; +} + +void FireworkEffect::skyburst(accum88 x, accum88 y, saccum78 xv, saccum78 yv, CRGB c) { + _skyburst = 1; + _burst_x = x; + _burst_y = y; + _burst_xv = xv; + _burst_yv = yv; + _burst_color = c; +} + +boolean FireworkEffect::can_be_shown_with_clock() { + return true; +} void FireworkEffect::loop() { - blur(EFFECT_FIREWORK_BLUR); - fadeToBlackBy(leds, LED_COUNT, EFFECT_FIREWORK_FADEOUT_SPEED); - - if (random8(EFFECT_FIREWORK_SHOT_CHANCE)==0) { - leds[random16(LED_COUNT)] = CHSV(random8(), 255, 255); + window->clear(); + _dot->move(); + _dot->draw(); + for (int i=0; imove(); + _sparks[i]->draw(); + } + static uint16_t launch_countdown = 0; + if (_dot->show == 0) { + if (launch_countdown == 0) { + _dot->ground_launch(); + _dot->type = FIREWORK_DOT_SHELL; + launch_countdown = random16(350) + 1; + } else { + launch_countdown--; + } + } + + if (_skyburst) { + int nsparks = random8(EFFECT_FIREWORK_SPARKS / 2, EFFECT_FIREWORK_SPARKS + 1); + for (int i=0; isky_burst(_burst_x, _burst_y, _burst_yv, _burst_color); + _skyburst = 0; + } } } + +FireworkEffect::FireworkEffect() { + _dot = new FireworkEffectDot(window, this); + for (int i=0; i