From 805fe1d47e3a99f357ba8120a03ea40c78614dca Mon Sep 17 00:00:00 2001 From: fishbone-git Date: Tue, 24 Dec 2019 15:05:12 +0100 Subject: [PATCH 1/4] Revert "more effects to FRAMETIME" This reverts commit f3371c443e16599dff26610e311d627ac4ce7875. --- wled00/FX.cpp | 178 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 146 insertions(+), 32 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 1791b710c..b1e348849 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -961,10 +961,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)); } @@ -979,9 +975,7 @@ uint16_t WS2812FX::mode_running_random(void) { { SEGENV.step = 0; } - - SEGENV.aux1 = it; - return FRAMETIME; + return SPEED_FORMULA_L; } @@ -1097,10 +1091,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; @@ -1115,9 +1105,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))); } @@ -1297,7 +1285,6 @@ uint16_t WS2812FX::mode_icu(void) { setPixelColor(SEGMENT.start + dest, col); setPixelColor(SEGMENT.start + dest + SEGLEN/2, col); - if (SEGMENT.intensity > 127) return FRAMETIME; return SPEED_FORMULA_L; } @@ -1307,11 +1294,6 @@ uint16_t WS2812FX::mode_icu(void) { */ uint16_t WS2812FX::mode_tricolor_wipe(void) { - uint32_t cycleTime = 40 + (3 * (uint32_t)(255 - SEGMENT.speed)); - uint32_t it = now / cycleTime; - if (SEGENV.step == it) return FRAMETIME; - uint8_t incr = it-SEGENV.step; - if(SEGENV.step < SEGLEN) { uint32_t led_offset = SEGENV.step; setPixelColor(SEGMENT.start + led_offset, SEGCOLOR(0)); @@ -1325,7 +1307,7 @@ uint16_t WS2812FX::mode_tricolor_wipe(void) } SEGENV.step = (SEGENV.step + 1) % (SEGLEN * 3); - return FRAMETIME; + return SPEED_FORMULA_L; } @@ -1336,9 +1318,6 @@ uint16_t WS2812FX::mode_tricolor_wipe(void) */ uint16_t WS2812FX::mode_tricolor_fade(void) { - uint16_t counter = now * ((SEGMENT.speed >> 3) +1); - SEGENV.step = (counter * 768) >> 16; - uint32_t color1 = 0, color2 = 0; byte stage = 0; @@ -1369,7 +1348,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); } @@ -1379,10 +1361,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}; @@ -1404,9 +1382,7 @@ uint16_t WS2812FX::mode_multi_comet(void) } } } - - SEGENV.step = it; - return FRAMETIME; + return SPEED_FORMULA_L; } @@ -2365,6 +2341,95 @@ uint16_t WS2812FX::mode_spots_fade() } +/* +* Bouncing Balls Effect +* Adapted from: https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ +*/ +uint16_t WS2812FX::mode_BouncingBalls(void) { + // number of balls based on intensity setting, + // only has 4 for a few of the higher settings as there is no colour selection + // fourth ball is a random colour + int balls = int(((SEGMENT.intensity * 6.2) / 255) + 1); + + // ideally use speed for gravity effect on the bounce + float Gravity = -9.81; + + const int maxBallCount = 7; + static float Height[maxBallCount]; + static float ImpactVelocity[maxBallCount]; + static int Position[maxBallCount]; + static float TimeSinceLastBounce[maxBallCount]; + static long ClockTimeSinceLastBounce[maxBallCount]; + static int StartHeight = 1; // height in metres (strip length) + static float Dampening[maxBallCount] = {0}; // Coefficient of Restitution (bounce damping) + static float ImpactVelocityStart=sqrt( -2 * Gravity * StartHeight); + + // Different from the examples, to allow for initialisation of the first bounce + if (Dampening[0] == 0) { + for (int i = 0 ; i < maxBallCount ; i++) { + ClockTimeSinceLastBounce[i] = millis(); + ImpactVelocity[i] = ImpactVelocityStart; + TimeSinceLastBounce[i] = 0; + Dampening[i] = 0.90 - float(i)/pow(maxBallCount,2); + } + } + + for (int i = 0 ; i < balls ; i++) { + TimeSinceLastBounce[i] = millis() - ClockTimeSinceLastBounce[i]; + Height[i] = 0.5 * Gravity * pow( TimeSinceLastBounce[i]/1000 , 2.0 ) + ImpactVelocity[i] * TimeSinceLastBounce[i]/1000; + + if ( Height[i] < 0 ) { + Height[i] = 0; + ImpactVelocity[i] = Dampening[i] * ImpactVelocity[i]; + ClockTimeSinceLastBounce[i] = millis(); + + if ( ImpactVelocity[i] < 0.01 ) { + ImpactVelocity[i] = ImpactVelocityStart; + } + } + Position[i] = round( Height[i] * (SEGLEN - 1) / StartHeight); + } + + fill(BLACK); + + for (int i = 0 ; i < balls ; i++) { + uint32_t color = SEGCOLOR(i % NUM_COLORS); + if (!color) { + color = color_wheel(random8()); + } + + setPixelColor(Position[i],color); + } + + return 20; +} + +/* +* Sinelon stolen from FASTLED examples +*/ +uint16_t WS2812FX::mode_sinelon(void) { + + fade_out(SEGMENT.intensity); + int pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1); + static int prevpos = 0; + + // setRange seems great to use, but doesn't work here for some reason + if( pos < prevpos ) { + for (uint16_t i = pos; i < prevpos; i++) + { + setPixelColor(i, color_from_palette(pos, false, false, 0)); + } + } else { + for (uint16_t i = prevpos; i < pos; i++) + { + setPixelColor(i, color_from_palette(pos, false, false, 0)); + } + } + prevpos = pos; + return FRAMETIME; +} + + //Rainbow with glitter, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6 uint16_t WS2812FX::mode_glitter() { @@ -2379,6 +2444,55 @@ uint16_t WS2812FX::mode_glitter() } +/* +* POPCORN +*/ +typedef struct Kernel { + float position; + float velocity; + int32_t color; +} kernel; + +#define MAX_NUM_POPCORN 12 +#define GRAVITY 0.06 + +uint16_t WS2812FX::mode_popcorn(void) { + uint32_t popcornColor = SEGCOLOR(0); + uint32_t bgColor = SEGCOLOR(1); + if(popcornColor == bgColor) popcornColor = color_wheel(random8()); + + static kernel popcorn[MAX_NUM_POPCORN]; + static float coeff = 0.0f; + if(coeff == 0.0f) { // calculate the velocity coeff once (the secret sauce) + coeff = pow((float)SEGLEN, 0.5223324f) * 0.3944296f; + } + + fill(SEGCOLOR(1)); + + uint16_t ledIndex; + for(int8_t i=0; i < MAX_NUM_POPCORN; i++) { + bool isActive = popcorn[i].position >= 0.0f; + + if(isActive) { // if kernel is active, update its position + popcorn[i].position += popcorn[i].velocity; + popcorn[i].velocity -= (GRAVITY * SEGMENT.intensity/25); + ledIndex = SEGMENT.start + popcorn[i].position; + if(ledIndex >= SEGMENT.start && ledIndex <= SEGMENT.stop) setPixelColor(ledIndex, popcorn[i].color); + } else { // if kernel is inactive, randomly pop it + if(random8() < 2) { // POP!!! + popcorn[i].position = 0.0f; + popcorn[i].velocity = coeff * (random(66, 100) / 100.0f); + popcorn[i].color = popcornColor; + ledIndex = SEGMENT.start; + setPixelColor(ledIndex, popcorn[i].color); + } + } + } + + return SPEED_FORMULA_L; +} + + //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/ From 3083ccddcc68db29c07509d280b304403e1e579a Mon Sep 17 00:00:00 2001 From: fishbone-git Date: Tue, 24 Dec 2019 20:41:03 +0100 Subject: [PATCH 2/4] rework of new effects, sync to FRAMETIME add options for sinelon --- wled00/FX.cpp | 63 ++++++++++++++++++++++++++++++++++++++++----------- wled00/FX.h | 15 ++++++++---- 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index b1e348849..5461347e3 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2346,9 +2346,8 @@ uint16_t WS2812FX::mode_spots_fade() * Adapted from: https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ uint16_t WS2812FX::mode_BouncingBalls(void) { - // number of balls based on intensity setting, - // only has 4 for a few of the higher settings as there is no colour selection - // fourth ball is a random colour + // number of balls based on intensity setting to max of 7 (cycles colors) + // non-chosen color is a random color int balls = int(((SEGMENT.intensity * 6.2) / 255) + 1); // ideally use speed for gravity effect on the bounce @@ -2375,7 +2374,7 @@ uint16_t WS2812FX::mode_BouncingBalls(void) { } for (int i = 0 ; i < balls ; i++) { - TimeSinceLastBounce[i] = millis() - ClockTimeSinceLastBounce[i]; + TimeSinceLastBounce[i] = (millis() - ClockTimeSinceLastBounce[i])/((255-SEGMENT.speed)*8/256 +1); Height[i] = 0.5 * Gravity * pow( TimeSinceLastBounce[i]/1000 , 2.0 ) + ImpactVelocity[i] * TimeSinceLastBounce[i]/1000; if ( Height[i] < 0 ) { @@ -2398,37 +2397,75 @@ uint16_t WS2812FX::mode_BouncingBalls(void) { color = color_wheel(random8()); } - setPixelColor(Position[i],color); + setPixelColor(SEGMENT.start+Position[i],color); } - return 20; + return FRAMETIME; } /* * Sinelon stolen from FASTLED examples */ -uint16_t WS2812FX::mode_sinelon(void) { +uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow=false) { fade_out(SEGMENT.intensity); int pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1); static int prevpos = 0; + + uint32_t color1 = color_from_palette(pos, true, false, 0); + if (rainbow) { + color1 = color_wheel((pos % 8) * 32); + } - // setRange seems great to use, but doesn't work here for some reason if( pos < prevpos ) { for (uint16_t i = pos; i < prevpos; i++) { - setPixelColor(i, color_from_palette(pos, false, false, 0)); + setPixelColor(i, color1); } } else { for (uint16_t i = prevpos; i < pos; i++) { - setPixelColor(i, color_from_palette(pos, false, false, 0)); + setPixelColor(SEGMENT.start + i, color1); + } + } + + if (dual) { + uint32_t color2 = SEGCOLOR(2); + + if (color2 == 0) { + color2 = color_from_palette(pos, true, false, 0); + } + if (rainbow) { + color2 = color_wheel((pos % 8) * 32); + } + if( pos < prevpos ) { + for (uint16_t i = pos; i < prevpos; i++) + { + setPixelColor(SEGMENT.start + SEGLEN-1-i, color2); + } + } else { + for (uint16_t i = prevpos; i < pos; i++) + { + setPixelColor(SEGMENT.start + SEGLEN-1-i, color2); + } } } prevpos = pos; 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() @@ -2470,12 +2507,12 @@ uint16_t WS2812FX::mode_popcorn(void) { fill(SEGCOLOR(1)); uint16_t ledIndex; - for(int8_t i=0; i < MAX_NUM_POPCORN; i++) { + for(int8_t i=0; i < SEGMENT.intensity*MAX_NUM_POPCORN/255; i++) { bool isActive = popcorn[i].position >= 0.0f; if(isActive) { // if kernel is active, update its position popcorn[i].position += popcorn[i].velocity; - popcorn[i].velocity -= (GRAVITY * SEGMENT.intensity/25); + popcorn[i].velocity -= (GRAVITY * ((255-SEGMENT.speed)*8/256 + 1)); ledIndex = SEGMENT.start + popcorn[i].position; if(ledIndex >= SEGMENT.start && ledIndex <= SEGMENT.stop) setPixelColor(ledIndex, popcorn[i].color); } else { // if kernel is inactive, randomly pop it @@ -2489,7 +2526,7 @@ uint16_t WS2812FX::mode_popcorn(void) { } } - return SPEED_FORMULA_L; + return FRAMETIME; } diff --git a/wled00/FX.h b/wled00/FX.h index 92f3cf78d..29a667939 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -84,7 +84,7 @@ #define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) #define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED ) -#define MODE_COUNT 92 +#define MODE_COUNT 94 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -177,7 +177,9 @@ #define FX_MODE_CANDLE 88 #define FX_MODE_BOUNCINGBALLS 89 #define FX_MODE_SINELON 90 -#define FX_MODE_POPCORN 91 +#define FX_MODE_SINELON_DUAL 91 +#define FX_MODE_SINELON_RAINBOW 92 +#define FX_MODE_POPCORN 93 class WS2812FX { @@ -324,6 +326,8 @@ class WS2812FX { _mode[FX_MODE_SPOTS_FADE] = &WS2812FX::mode_spots_fade; _mode[FX_MODE_BOUNCINGBALLS] = &WS2812FX::mode_BouncingBalls; _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; _mode[FX_MODE_GLITTER] = &WS2812FX::mode_glitter; _mode[FX_MODE_CANDLE] = &WS2812FX::mode_candle; @@ -510,6 +514,8 @@ class WS2812FX { mode_spots_fade(void), mode_BouncingBalls(void), mode_sinelon(void), + mode_sinelon_dual(void), + mode_sinelon_rainbow(void), mode_popcorn(void), mode_glitter(void), mode_candle(void); @@ -553,6 +559,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), @@ -587,8 +594,8 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Two Dots","Two Areas","Circus","Halloween","Tri Chase","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet", "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", -"Bouncing Balls", "Sinelon","Popcorn" +"Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Bouncing Balls", +"Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn" ])====="; From 67758b49806e281596ab4e31a067f4214771e5f3 Mon Sep 17 00:00:00 2001 From: fishbone-git Date: Mon, 30 Dec 2019 00:20:05 +0100 Subject: [PATCH 3/4] rework sinelon, bouncing, popcorn to remove some unnecessary statics --- wled00/FX.cpp | 62 +++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 5461347e3..79837df0d 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2343,34 +2343,28 @@ uint16_t WS2812FX::mode_spots_fade() /* * Bouncing Balls Effect -* Adapted from: https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ uint16_t WS2812FX::mode_BouncingBalls(void) { // number of balls based on intensity setting to max of 7 (cycles colors) // non-chosen color is a random color int balls = int(((SEGMENT.intensity * 6.2) / 255) + 1); - // ideally use speed for gravity effect on the bounce - float Gravity = -9.81; + const int maxBallCount = 7; + + float Gravity = -9.81; // standard value of gravity + int Position[maxBallCount]; // current position of the ball normalized to segment size + float TimeSinceLastBounce[maxBallCount] = {0}; // time difference for physics calculations + int StartHeight = 1; // height in metres (strip length) + float Dampening[maxBallCount] = {0}; // Coefficient of Restitution (bounce damping) + float ImpactVelocityStart = sqrt( -2 * Gravity * StartHeight); - const int maxBallCount = 7; + static float ImpactVelocity[maxBallCount] = {ImpactVelocityStart}; + static long ClockTimeSinceLastBounce[maxBallCount] = {millis()}; static float Height[maxBallCount]; - static float ImpactVelocity[maxBallCount]; - static int Position[maxBallCount]; - static float TimeSinceLastBounce[maxBallCount]; - static long ClockTimeSinceLastBounce[maxBallCount]; - static int StartHeight = 1; // height in metres (strip length) - static float Dampening[maxBallCount] = {0}; // Coefficient of Restitution (bounce damping) - static float ImpactVelocityStart=sqrt( -2 * Gravity * StartHeight); - - // Different from the examples, to allow for initialisation of the first bounce - if (Dampening[0] == 0) { - for (int i = 0 ; i < maxBallCount ; i++) { - ClockTimeSinceLastBounce[i] = millis(); - ImpactVelocity[i] = ImpactVelocityStart; - TimeSinceLastBounce[i] = 0; - Dampening[i] = 0.90 - float(i)/pow(maxBallCount,2); - } + + //set up variable damping for better effect using multiple balls + for (int i = 0 ; i < maxBallCount ; i++) { + Dampening[i] = 0.90 - float(i)/pow(maxBallCount,2); } for (int i = 0 ; i < balls ; i++) { @@ -2410,7 +2404,7 @@ uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow=false) { fade_out(SEGMENT.intensity); int pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1); - static int prevpos = 0; + int prevpos = SEGENV.aux0; uint32_t color1 = color_from_palette(pos, true, false, 0); if (rainbow) { @@ -2418,13 +2412,11 @@ uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow=false) { } if( pos < prevpos ) { - for (uint16_t i = pos; i < prevpos; i++) - { + for (uint16_t i = pos; i < prevpos; i++) { setPixelColor(i, color1); } } else { - for (uint16_t i = prevpos; i < pos; i++) - { + for (uint16_t i = prevpos; i < pos; i++) { setPixelColor(SEGMENT.start + i, color1); } } @@ -2438,19 +2430,18 @@ uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow=false) { if (rainbow) { color2 = color_wheel((pos % 8) * 32); } - if( pos < prevpos ) { - for (uint16_t i = pos; i < prevpos; i++) - { + if ( pos < prevpos ) { + for (uint16_t i = pos; i < prevpos; i++) { setPixelColor(SEGMENT.start + SEGLEN-1-i, color2); } } else { - for (uint16_t i = prevpos; i < pos; i++) - { + for (uint16_t i = prevpos; i < pos; i++) { setPixelColor(SEGMENT.start + SEGLEN-1-i, color2); } } } - prevpos = pos; + + SEGENV.aux0 = pos; return FRAMETIME; } @@ -2497,12 +2488,9 @@ uint16_t WS2812FX::mode_popcorn(void) { uint32_t popcornColor = SEGCOLOR(0); uint32_t bgColor = SEGCOLOR(1); if(popcornColor == bgColor) popcornColor = color_wheel(random8()); - + static kernel popcorn[MAX_NUM_POPCORN]; - static float coeff = 0.0f; - if(coeff == 0.0f) { // calculate the velocity coeff once (the secret sauce) - coeff = pow((float)SEGLEN, 0.5223324f) * 0.3944296f; - } + float coeff = pow((float)SEGLEN, 0.5223324f) * 0.3944296f; fill(SEGCOLOR(1)); @@ -2513,6 +2501,7 @@ uint16_t WS2812FX::mode_popcorn(void) { if(isActive) { // if kernel is active, update its position popcorn[i].position += popcorn[i].velocity; popcorn[i].velocity -= (GRAVITY * ((255-SEGMENT.speed)*8/256 + 1)); + ledIndex = SEGMENT.start + popcorn[i].position; if(ledIndex >= SEGMENT.start && ledIndex <= SEGMENT.stop) setPixelColor(ledIndex, popcorn[i].color); } else { // if kernel is inactive, randomly pop it @@ -2520,6 +2509,7 @@ uint16_t WS2812FX::mode_popcorn(void) { popcorn[i].position = 0.0f; popcorn[i].velocity = coeff * (random(66, 100) / 100.0f); popcorn[i].color = popcornColor; + ledIndex = SEGMENT.start; setPixelColor(ledIndex, popcorn[i].color); } From e4ad0d3b59687c9cd879c4f0967b57d901d08d14 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sun, 5 Jan 2020 21:26:14 +0100 Subject: [PATCH 4/4] Improved bouncing balls --- wled00/FX.cpp | 199 +++++++++++++++++++++++----------------------- wled00/FX.h | 4 +- wled00/wled00.ino | 2 +- 3 files changed, 101 insertions(+), 104 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 4549a0ed0..72d540a92 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2401,107 +2401,94 @@ 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_BouncingBalls(void) { +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 - int balls = int(((SEGMENT.intensity * 6.2) / 255) + 1); - - const int maxBallCount = 7; + uint8_t numBalls = int(((SEGMENT.intensity * (maxNumBalls - 0.8f)) / 255) + 1); - float Gravity = -9.81; // standard value of gravity - int Position[maxBallCount]; // current position of the ball normalized to segment size - float TimeSinceLastBounce[maxBallCount] = {0}; // time difference for physics calculations - int StartHeight = 1; // height in metres (strip length) - float Dampening[maxBallCount] = {0}; // Coefficient of Restitution (bounce damping) - float ImpactVelocityStart = sqrt( -2 * Gravity * StartHeight); + float gravity = -9.81; // standard value of gravity + float impactVelocityStart = sqrt( -2 * gravity); - static float ImpactVelocity[maxBallCount] = {ImpactVelocityStart}; - static long ClockTimeSinceLastBounce[maxBallCount] = {millis()}; - static float Height[maxBallCount]; - - //set up variable damping for better effect using multiple balls - for (int i = 0 ; i < maxBallCount ; i++) { - Dampening[i] = 0.90 - float(i)/pow(maxBallCount,2); + unsigned long time = millis(); + + if (SEGENV.call == 0) { + for (uint8_t i = 0; i < maxNumBalls; i++) balls[i].lastBounceTime = time; } - for (int i = 0 ; i < balls ; i++) { - TimeSinceLastBounce[i] = (millis() - ClockTimeSinceLastBounce[i])/((255-SEGMENT.speed)*8/256 +1); - Height[i] = 0.5 * Gravity * pow( TimeSinceLastBounce[i]/1000 , 2.0 ) + ImpactVelocity[i] * TimeSinceLastBounce[i]/1000; + 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 ( Height[i] < 0 ) { - Height[i] = 0; - ImpactVelocity[i] = Dampening[i] * ImpactVelocity[i]; - ClockTimeSinceLastBounce[i] = millis(); + 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 ( ImpactVelocity[i] < 0.01 ) { - ImpactVelocity[i] = ImpactVelocityStart; + if (balls[i].impactVelocity < 0.015) { + balls[i].impactVelocity = impactVelocityStart; } } - Position[i] = round( Height[i] * (SEGLEN - 1) / StartHeight); - } - - fill(BLACK); - - for (int i = 0 ; i < balls ; i++) { - uint32_t color = SEGCOLOR(i % NUM_COLORS); - if (!color) { - color = color_wheel(random8()); - } - setPixelColor(SEGMENT.start+Position[i],color); + 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); - int prevpos = SEGENV.aux0; uint32_t color1 = color_from_palette(pos, true, false, 0); if (rainbow) { - color1 = color_wheel((pos % 8) * 32); - } - - if( pos < prevpos ) { - for (uint16_t i = pos; i < prevpos; i++) { - setPixelColor(i, color1); - } - } else { - for (uint16_t i = prevpos; i < pos; i++) { - setPixelColor(SEGMENT.start + i, color1); - } + color1 = color_wheel((pos & 0x07) * 32); } + setPixelColor(SEGMENT.start + pos, color1); if (dual) { uint32_t color2 = SEGCOLOR(2); - if (color2 == 0) { - color2 = color_from_palette(pos, true, false, 0); - } - if (rainbow) { - color2 = color_wheel((pos % 8) * 32); - } - if ( pos < prevpos ) { - for (uint16_t i = pos; i < prevpos; i++) { - setPixelColor(SEGMENT.start + SEGLEN-1-i, color2); - } - } else { - for (uint16_t i = prevpos; i < pos; i++) { - setPixelColor(SEGMENT.start + SEGLEN-1-i, color2); - } - } + if (!color2) color2 = color_from_palette(pos, true, false, 0); + if (rainbow) color2 = color1; //rainbow + + setPixelColor(SEGMENT.start + SEGLEN-1-pos, color2); } - SEGENV.aux0 = pos; return FRAMETIME; } @@ -2532,46 +2519,64 @@ 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 */ -typedef struct Kernel { - float position; - float velocity; - int32_t color; -} kernel; - -#define MAX_NUM_POPCORN 12 -#define GRAVITY 0.06 - uint16_t WS2812FX::mode_popcorn(void) { - uint32_t popcornColor = SEGCOLOR(0); - uint32_t bgColor = SEGCOLOR(1); - if(popcornColor == bgColor) popcornColor = color_wheel(random8()); + //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); - static kernel popcorn[MAX_NUM_POPCORN]; - float coeff = pow((float)SEGLEN, 0.5223324f) * 0.3944296f; + float gravity = -0.0001 - (SEGMENT.speed/200000.0); // m/s/s + gravity *= SEGLEN; - fill(SEGCOLOR(1)); + bool hasCol2 = SEGCOLOR(2); + fill(hasCol2 ? BLACK : SEGCOLOR(1)); - uint16_t ledIndex; - for(int8_t i=0; i < SEGMENT.intensity*MAX_NUM_POPCORN/255; i++) { - bool isActive = popcorn[i].position >= 0.0f; + 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].position += popcorn[i].velocity; - popcorn[i].velocity -= (GRAVITY * ((255-SEGMENT.speed)*8/256 + 1)); + 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); - ledIndex = SEGMENT.start + popcorn[i].position; - if(ledIndex >= SEGMENT.start && ledIndex <= SEGMENT.stop) setPixelColor(ledIndex, popcorn[i].color); + 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].position = 0.0f; - popcorn[i].velocity = coeff * (random(66, 100) / 100.0f); - popcorn[i].color = popcornColor; + popcorn[i].pos = 0.01f; - ledIndex = SEGMENT.start; - setPixelColor(ledIndex, popcorn[i].color); + 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; + } } } } @@ -2756,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 fbad203f9..fdbbfb554 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -360,7 +360,7 @@ 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_BouncingBalls; + _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; @@ -543,7 +543,7 @@ class WS2812FX { mode_candle(void), mode_starburst(void), mode_exploding_fireworks(void), - mode_BouncingBalls(void), + mode_bouncing_balls(void), mode_sinelon(void), mode_sinelon_dual(void), mode_sinelon_rainbow(void), 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";