Merge pull request #443 from fishbone-git/master

add bouncing balls effect
This commit is contained in:
Aircoookie 2020-01-05 21:38:24 +01:00 committed by GitHub
commit 2fe2b3c975
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 197 additions and 34 deletions

View File

@ -962,10 +962,6 @@ uint16_t WS2812FX::mode_halloween(void) {
* Random colored pixels running. * Random colored pixels running.
*/ */
uint16_t WS2812FX::mode_running_random(void) { 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--) { for(uint16_t i=SEGLEN-1; i > 0; i--) {
setPixelColor(SEGMENT.start + i, getPixelColor(SEGMENT.start + i - 1)); setPixelColor(SEGMENT.start + i, getPixelColor(SEGMENT.start + i - 1));
} }
@ -980,9 +976,7 @@ uint16_t WS2812FX::mode_running_random(void) {
{ {
SEGENV.step = 0; SEGENV.step = 0;
} }
return SPEED_FORMULA_L;
SEGENV.aux1 = it;
return FRAMETIME;
} }
@ -1098,10 +1092,6 @@ uint16_t WS2812FX::mode_rain()
* Fire flicker function * Fire flicker function
*/ */
uint16_t WS2812FX::mode_fire_flicker(void) { 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 w = (SEGCOLOR(0) >> 24) & 0xFF;
byte r = (SEGCOLOR(0) >> 16) & 0xFF; byte r = (SEGCOLOR(0) >> 16) & 0xFF;
byte g = (SEGCOLOR(0) >> 8) & 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)); setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, 255 - flicker));
} }
} }
return 20 + random((255 - SEGMENT.speed),(2 * (uint16_t)(255 - SEGMENT.speed)));
SEGENV.step = it;
return FRAMETIME;
} }
@ -1382,7 +1370,10 @@ uint16_t WS2812FX::mode_tricolor_fade(void)
setPixelColor(i, color); 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) 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); fade_out(SEGMENT.intensity);
static uint16_t comets[] = {UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX}; 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)
} }
} }
} }
return SPEED_FORMULA_L;
SEGENV.step = it;
return FRAMETIME;
} }
@ -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<Ball*>(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 //Rainbow with glitter, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6
uint16_t WS2812FX::mode_glitter() 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<Spark*>(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 //values close to 100 produce 5Hz flicker, which looks very candle-y
//Inspired by https://github.com/avanhanegem/ArduinoCandleEffectNeoPixel //Inspired by https://github.com/avanhanegem/ArduinoCandleEffectNeoPixel
//and https://cpldcpu.wordpress.com/2016/01/05/reverse-engineering-a-real-candle/ //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/ * 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) uint16_t WS2812FX::mode_exploding_fireworks(void)
{ {
//allocate segment data //allocate segment data

View File

@ -91,7 +91,7 @@
#define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) #define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE )
#define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED ) #define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED )
#define MODE_COUNT 91 #define MODE_COUNT 96
#define FX_MODE_STATIC 0 #define FX_MODE_STATIC 0
#define FX_MODE_BLINK 1 #define FX_MODE_BLINK 1
@ -184,6 +184,11 @@
#define FX_MODE_CANDLE 88 #define FX_MODE_CANDLE 88
#define FX_MODE_STARBURST 89 #define FX_MODE_STARBURST 89
#define FX_MODE_EXPLODING_FIREWORKS 90 #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 { class WS2812FX {
@ -355,6 +360,11 @@ class WS2812FX {
_mode[FX_MODE_CANDLE] = &WS2812FX::mode_candle; _mode[FX_MODE_CANDLE] = &WS2812FX::mode_candle;
_mode[FX_MODE_STARBURST] = &WS2812FX::mode_starburst; _mode[FX_MODE_STARBURST] = &WS2812FX::mode_starburst;
_mode[FX_MODE_EXPLODING_FIREWORKS] = &WS2812FX::mode_exploding_fireworks; _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; _brightness = DEFAULT_BRIGHTNESS;
currentPalette = CRGBPalette16(CRGB::Black); currentPalette = CRGBPalette16(CRGB::Black);
@ -532,7 +542,12 @@ class WS2812FX {
mode_glitter(void), mode_glitter(void),
mode_candle(void), mode_candle(void),
mode_starburst(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: private:
@ -573,6 +588,7 @@ class WS2812FX {
theater_chase(uint32_t, uint32_t, bool), theater_chase(uint32_t, uint32_t, bool),
running_base(bool), running_base(bool),
larson_scanner(bool), larson_scanner(bool),
sinelon_base(bool,bool),
dissolve(uint32_t), dissolve(uint32_t),
chase(uint32_t, uint32_t, uint32_t, bool), chase(uint32_t, uint32_t, uint32_t, bool),
gradient_base(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", "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", "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", "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"
])====="; ])=====";

View File

@ -98,7 +98,7 @@
//version code in format yymmddb (b = daily build) //version code in format yymmddb (b = daily build)
#define VERSION 2001031 #define VERSION 2001051
char versionString[] = "0.9.0-b2"; char versionString[] = "0.9.0-b2";