diff --git a/wled00/FX.cpp b/wled00/FX.cpp index e1af83383..72d540a92 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -962,10 +962,6 @@ uint16_t WS2812FX::mode_halloween(void) { * Random colored pixels running. */ uint16_t WS2812FX::mode_running_random(void) { - uint32_t cycleTime = 25 + (3 * (uint32_t)(255 - SEGMENT.speed)); - uint32_t it = now / cycleTime; - if (SEGENV.aux1 == it) return FRAMETIME; - for(uint16_t i=SEGLEN-1; i > 0; i--) { setPixelColor(SEGMENT.start + i, getPixelColor(SEGMENT.start + i - 1)); } @@ -980,9 +976,7 @@ uint16_t WS2812FX::mode_running_random(void) { { SEGENV.step = 0; } - - SEGENV.aux1 = it; - return FRAMETIME; + return SPEED_FORMULA_L; } @@ -1098,10 +1092,6 @@ uint16_t WS2812FX::mode_rain() * Fire flicker function */ uint16_t WS2812FX::mode_fire_flicker(void) { - uint32_t cycleTime = 40 + (255 - SEGMENT.speed); - uint32_t it = now / cycleTime; - if (SEGENV.step == it) return FRAMETIME; - byte w = (SEGCOLOR(0) >> 24) & 0xFF; byte r = (SEGCOLOR(0) >> 16) & 0xFF; byte g = (SEGCOLOR(0) >> 8) & 0xFF; @@ -1116,9 +1106,7 @@ uint16_t WS2812FX::mode_fire_flicker(void) { setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, 255 - flicker)); } } - - SEGENV.step = it; - return FRAMETIME; + return 20 + random((255 - SEGMENT.speed),(2 * (uint16_t)(255 - SEGMENT.speed))); } @@ -1382,7 +1370,10 @@ uint16_t WS2812FX::mode_tricolor_fade(void) setPixelColor(i, color); } - return FRAMETIME; + SEGENV.step += 4; + if(SEGENV.step >= 768) SEGENV.step = 0; + + return 5 + ((uint32_t)(255 - SEGMENT.speed) / 10); } @@ -1392,10 +1383,6 @@ uint16_t WS2812FX::mode_tricolor_fade(void) */ uint16_t WS2812FX::mode_multi_comet(void) { - uint32_t cycleTime = 20 + (2 * (uint32_t)(255 - SEGMENT.speed)); - uint32_t it = now / cycleTime; - if (SEGENV.step == it) return FRAMETIME; - fade_out(SEGMENT.intensity); static uint16_t comets[] = {UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX}; @@ -1417,9 +1404,7 @@ uint16_t WS2812FX::mode_multi_comet(void) } } } - - SEGENV.step = it; - return FRAMETIME; + return SPEED_FORMULA_L; } @@ -2416,6 +2401,110 @@ uint16_t WS2812FX::mode_spots_fade() } +//each needs 12 bytes +//Spark type is used for popcorn and 1D fireworks +typedef struct Ball { + unsigned long lastBounceTime; + float impactVelocity; + float height; +} ball; + +/* +* Bouncing Balls Effect +*/ +uint16_t WS2812FX::mode_bouncing_balls(void) { + //allocate segment data + uint16_t maxNumBalls = 16; + uint16_t dataSize = sizeof(ball) * maxNumBalls; + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + + Ball* balls = reinterpret_cast(SEGENV.data); + + // number of balls based on intensity setting to max of 7 (cycles colors) + // non-chosen color is a random color + uint8_t numBalls = int(((SEGMENT.intensity * (maxNumBalls - 0.8f)) / 255) + 1); + + float gravity = -9.81; // standard value of gravity + float impactVelocityStart = sqrt( -2 * gravity); + + unsigned long time = millis(); + + if (SEGENV.call == 0) { + for (uint8_t i = 0; i < maxNumBalls; i++) balls[i].lastBounceTime = time; + } + + bool hasCol2 = SEGCOLOR(2); + fill(hasCol2 ? BLACK : SEGCOLOR(1)); + + for (uint8_t i = 0; i < numBalls; i++) { + float timeSinceLastBounce = (time - balls[i].lastBounceTime)/((255-SEGMENT.speed)*8/256 +1); + balls[i].height = 0.5 * gravity * pow(timeSinceLastBounce/1000 , 2.0) + balls[i].impactVelocity * timeSinceLastBounce/1000; + + if (balls[i].height < 0) { //start bounce + balls[i].height = 0; + //damping for better effect using multiple balls + float dampening = 0.90 - float(i)/pow(numBalls,2); + balls[i].impactVelocity = dampening * balls[i].impactVelocity; + balls[i].lastBounceTime = time; + + if (balls[i].impactVelocity < 0.015) { + balls[i].impactVelocity = impactVelocityStart; + } + } + + uint32_t color = SEGCOLOR(0); + if (SEGMENT.palette) { + color = color_wheel(i*(256/max(numBalls, 8))); + } else if (hasCol2) { + color = SEGCOLOR(i % NUM_COLORS); + } + + uint16_t pos = round(balls[i].height * (SEGLEN - 1)); + setPixelColor(SEGMENT.start + pos, color); + } + + return FRAMETIME; +} + + +/* +* Sinelon stolen from FASTLED examples +*/ +uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow=false) { + fade_out(SEGMENT.intensity); + int pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1); + + uint32_t color1 = color_from_palette(pos, true, false, 0); + if (rainbow) { + color1 = color_wheel((pos & 0x07) * 32); + } + setPixelColor(SEGMENT.start + pos, color1); + + if (dual) { + uint32_t color2 = SEGCOLOR(2); + + if (!color2) color2 = color_from_palette(pos, true, false, 0); + if (rainbow) color2 = color1; //rainbow + + setPixelColor(SEGMENT.start + SEGLEN-1-pos, color2); + } + + return FRAMETIME; +} + +uint16_t WS2812FX::mode_sinelon(void) { + return sinelon_base(false); +} + +uint16_t WS2812FX::mode_sinelon_dual(void) { + return sinelon_base(true); +} + +uint16_t WS2812FX::mode_sinelon_rainbow(void) { + return sinelon_base(true, true); +} + + //Rainbow with glitter, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6 uint16_t WS2812FX::mode_glitter() { @@ -2430,6 +2519,72 @@ uint16_t WS2812FX::mode_glitter() } + +//each needs 12 bytes +//Spark type is used for popcorn and 1D fireworks +typedef struct Spark { + float pos; + float vel; + uint16_t col; + uint8_t colIndex; +} spark; + +/* +* POPCORN +* modified from https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Popcorn.h +*/ +uint16_t WS2812FX::mode_popcorn(void) { + //allocate segment data + uint16_t maxNumPopcorn = 24; + uint16_t dataSize = sizeof(spark) * maxNumPopcorn; + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + + Spark* popcorn = reinterpret_cast(SEGENV.data); + + float gravity = -0.0001 - (SEGMENT.speed/200000.0); // m/s/s + gravity *= SEGLEN; + + bool hasCol2 = SEGCOLOR(2); + fill(hasCol2 ? BLACK : SEGCOLOR(1)); + + uint8_t numPopcorn = SEGMENT.intensity*maxNumPopcorn/255; + if (numPopcorn == 0) numPopcorn = 1; + + for(uint8_t i = 0; i < numPopcorn; i++) { + bool isActive = popcorn[i].pos >= 0.0f; + + if(isActive) { // if kernel is active, update its position + popcorn[i].pos += popcorn[i].vel; + popcorn[i].vel += gravity; + uint32_t col = color_wheel(popcorn[i].colIndex); + if (!SEGMENT.palette && popcorn[i].colIndex < NUM_COLORS) col = SEGCOLOR(popcorn[i].colIndex); + + uint16_t ledIndex = SEGMENT.start + popcorn[i].pos; + if(ledIndex >= SEGMENT.start && ledIndex < SEGMENT.stop) setPixelColor(ledIndex, col); + } else { // if kernel is inactive, randomly pop it + if(random8() < 2) { // POP!!! + popcorn[i].pos = 0.01f; + + uint16_t peakHeight = 128 + random8(128); //0-255 + peakHeight = (peakHeight * (SEGLEN -1)) >> 8; + popcorn[i].vel = sqrt(-2.0 * gravity * peakHeight); + + if (SEGMENT.palette) + { + popcorn[i].colIndex = random8(); + } else { + byte col = random8(0, NUM_COLORS); + if (!hasCol2 || !SEGCOLOR(col)) col = 0; + popcorn[i].colIndex = col; + } + } + } + } + + return FRAMETIME; +} + + //values close to 100 produce 5Hz flicker, which looks very candle-y //Inspired by https://github.com/avanhanegem/ArduinoCandleEffectNeoPixel //and https://cpldcpu.wordpress.com/2016/01/05/reverse-engineering-a-real-candle/ @@ -2606,14 +2761,6 @@ uint16_t WS2812FX::mode_starburst(void) { * adapted from: http://www.anirama.com/1000leds/1d-fireworks/ */ -//each needs 12 byte -typedef struct Spark { - float pos; - float vel; - uint16_t col; - uint8_t colIndex; -} spark; - uint16_t WS2812FX::mode_exploding_fireworks(void) { //allocate segment data diff --git a/wled00/FX.h b/wled00/FX.h index 0b2d21afa..fdbbfb554 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -91,7 +91,7 @@ #define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) #define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED ) -#define MODE_COUNT 91 +#define MODE_COUNT 96 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -184,6 +184,11 @@ #define FX_MODE_CANDLE 88 #define FX_MODE_STARBURST 89 #define FX_MODE_EXPLODING_FIREWORKS 90 +#define FX_MODE_BOUNCINGBALLS 91 +#define FX_MODE_SINELON 92 +#define FX_MODE_SINELON_DUAL 93 +#define FX_MODE_SINELON_RAINBOW 94 +#define FX_MODE_POPCORN 95 class WS2812FX { @@ -355,6 +360,11 @@ class WS2812FX { _mode[FX_MODE_CANDLE] = &WS2812FX::mode_candle; _mode[FX_MODE_STARBURST] = &WS2812FX::mode_starburst; _mode[FX_MODE_EXPLODING_FIREWORKS] = &WS2812FX::mode_exploding_fireworks; + _mode[FX_MODE_BOUNCINGBALLS] = &WS2812FX::mode_bouncing_balls; + _mode[FX_MODE_SINELON] = &WS2812FX::mode_sinelon; + _mode[FX_MODE_SINELON_DUAL] = &WS2812FX::mode_sinelon_dual; + _mode[FX_MODE_SINELON_RAINBOW] = &WS2812FX::mode_sinelon_rainbow; + _mode[FX_MODE_POPCORN] = &WS2812FX::mode_popcorn; _brightness = DEFAULT_BRIGHTNESS; currentPalette = CRGBPalette16(CRGB::Black); @@ -532,7 +542,12 @@ class WS2812FX { mode_glitter(void), mode_candle(void), mode_starburst(void), - mode_exploding_fireworks(void); + mode_exploding_fireworks(void), + mode_bouncing_balls(void), + mode_sinelon(void), + mode_sinelon_dual(void), + mode_sinelon_rainbow(void), + mode_popcorn(void); private: @@ -573,6 +588,7 @@ class WS2812FX { theater_chase(uint32_t, uint32_t, bool), running_base(bool), larson_scanner(bool), + sinelon_base(bool,bool), dissolve(uint32_t), chase(uint32_t, uint32_t, uint32_t, bool), gradient_base(bool), @@ -609,7 +625,7 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Scanner Dual","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise", "Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple", "Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst", -"Fireworks 1D" +"Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn" ])====="; diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 52951f616..c706f51cd 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -98,7 +98,7 @@ //version code in format yymmddb (b = daily build) -#define VERSION 2001031 +#define VERSION 2001051 char versionString[] = "0.9.0-b2";