mirror of
https://github.com/wled/WLED.git
synced 2025-07-27 20:56:40 +00:00
Improved bouncing balls
This commit is contained in:
parent
4af7ccd84c
commit
e4ad0d3b59
199
wled00/FX.cpp
199
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
|
* 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<Ball*>(SEGENV.data);
|
||||||
|
|
||||||
// number of balls based on intensity setting to max of 7 (cycles colors)
|
// number of balls based on intensity setting to max of 7 (cycles colors)
|
||||||
// non-chosen color is a random color
|
// non-chosen color is a random color
|
||||||
int balls = int(((SEGMENT.intensity * 6.2) / 255) + 1);
|
uint8_t numBalls = int(((SEGMENT.intensity * (maxNumBalls - 0.8f)) / 255) + 1);
|
||||||
|
|
||||||
const int maxBallCount = 7;
|
|
||||||
|
|
||||||
float Gravity = -9.81; // standard value of gravity
|
float gravity = -9.81; // standard value of gravity
|
||||||
int Position[maxBallCount]; // current position of the ball normalized to segment size
|
float impactVelocityStart = sqrt( -2 * gravity);
|
||||||
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);
|
|
||||||
|
|
||||||
static float ImpactVelocity[maxBallCount] = {ImpactVelocityStart};
|
unsigned long time = millis();
|
||||||
static long ClockTimeSinceLastBounce[maxBallCount] = {millis()};
|
|
||||||
static float Height[maxBallCount];
|
if (SEGENV.call == 0) {
|
||||||
|
for (uint8_t i = 0; i < maxNumBalls; i++) balls[i].lastBounceTime = time;
|
||||||
//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++) {
|
bool hasCol2 = SEGCOLOR(2);
|
||||||
TimeSinceLastBounce[i] = (millis() - ClockTimeSinceLastBounce[i])/((255-SEGMENT.speed)*8/256 +1);
|
fill(hasCol2 ? BLACK : SEGCOLOR(1));
|
||||||
Height[i] = 0.5 * Gravity * pow( TimeSinceLastBounce[i]/1000 , 2.0 ) + ImpactVelocity[i] * TimeSinceLastBounce[i]/1000;
|
|
||||||
|
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 ) {
|
if (balls[i].height < 0) { //start bounce
|
||||||
Height[i] = 0;
|
balls[i].height = 0;
|
||||||
ImpactVelocity[i] = Dampening[i] * ImpactVelocity[i];
|
//damping for better effect using multiple balls
|
||||||
ClockTimeSinceLastBounce[i] = millis();
|
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 ) {
|
if (balls[i].impactVelocity < 0.015) {
|
||||||
ImpactVelocity[i] = ImpactVelocityStart;
|
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;
|
return FRAMETIME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sinelon stolen from FASTLED examples
|
* Sinelon stolen from FASTLED examples
|
||||||
*/
|
*/
|
||||||
uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow=false) {
|
uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow=false) {
|
||||||
|
|
||||||
fade_out(SEGMENT.intensity);
|
fade_out(SEGMENT.intensity);
|
||||||
int pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1);
|
int pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1);
|
||||||
int prevpos = SEGENV.aux0;
|
|
||||||
|
|
||||||
uint32_t color1 = color_from_palette(pos, true, false, 0);
|
uint32_t color1 = color_from_palette(pos, true, false, 0);
|
||||||
if (rainbow) {
|
if (rainbow) {
|
||||||
color1 = color_wheel((pos % 8) * 32);
|
color1 = color_wheel((pos & 0x07) * 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
setPixelColor(SEGMENT.start + pos, color1);
|
||||||
|
|
||||||
if (dual) {
|
if (dual) {
|
||||||
uint32_t color2 = SEGCOLOR(2);
|
uint32_t color2 = SEGCOLOR(2);
|
||||||
|
|
||||||
if (color2 == 0) {
|
if (!color2) color2 = color_from_palette(pos, true, false, 0);
|
||||||
color2 = color_from_palette(pos, true, false, 0);
|
if (rainbow) color2 = color1; //rainbow
|
||||||
}
|
|
||||||
if (rainbow) {
|
setPixelColor(SEGMENT.start + SEGLEN-1-pos, color2);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SEGENV.aux0 = pos;
|
|
||||||
return FRAMETIME;
|
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
|
* 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) {
|
uint16_t WS2812FX::mode_popcorn(void) {
|
||||||
uint32_t popcornColor = SEGCOLOR(0);
|
//allocate segment data
|
||||||
uint32_t bgColor = SEGCOLOR(1);
|
uint16_t maxNumPopcorn = 24;
|
||||||
if(popcornColor == bgColor) popcornColor = color_wheel(random8());
|
uint16_t dataSize = sizeof(spark) * maxNumPopcorn;
|
||||||
|
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||||
|
|
||||||
|
Spark* popcorn = reinterpret_cast<Spark*>(SEGENV.data);
|
||||||
|
|
||||||
static kernel popcorn[MAX_NUM_POPCORN];
|
float gravity = -0.0001 - (SEGMENT.speed/200000.0); // m/s/s
|
||||||
float coeff = pow((float)SEGLEN, 0.5223324f) * 0.3944296f;
|
gravity *= SEGLEN;
|
||||||
|
|
||||||
fill(SEGCOLOR(1));
|
bool hasCol2 = SEGCOLOR(2);
|
||||||
|
fill(hasCol2 ? BLACK : SEGCOLOR(1));
|
||||||
|
|
||||||
uint16_t ledIndex;
|
uint8_t numPopcorn = SEGMENT.intensity*maxNumPopcorn/255;
|
||||||
for(int8_t i=0; i < SEGMENT.intensity*MAX_NUM_POPCORN/255; i++) {
|
if (numPopcorn == 0) numPopcorn = 1;
|
||||||
bool isActive = popcorn[i].position >= 0.0f;
|
|
||||||
|
for(uint8_t i = 0; i < numPopcorn; i++) {
|
||||||
|
bool isActive = popcorn[i].pos >= 0.0f;
|
||||||
|
|
||||||
if(isActive) { // if kernel is active, update its position
|
if(isActive) { // if kernel is active, update its position
|
||||||
popcorn[i].position += popcorn[i].velocity;
|
popcorn[i].pos += popcorn[i].vel;
|
||||||
popcorn[i].velocity -= (GRAVITY * ((255-SEGMENT.speed)*8/256 + 1));
|
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;
|
uint16_t ledIndex = SEGMENT.start + popcorn[i].pos;
|
||||||
if(ledIndex >= SEGMENT.start && ledIndex <= SEGMENT.stop) setPixelColor(ledIndex, popcorn[i].color);
|
if(ledIndex >= SEGMENT.start && ledIndex < SEGMENT.stop) setPixelColor(ledIndex, col);
|
||||||
} else { // if kernel is inactive, randomly pop it
|
} else { // if kernel is inactive, randomly pop it
|
||||||
if(random8() < 2) { // POP!!!
|
if(random8() < 2) { // POP!!!
|
||||||
popcorn[i].position = 0.0f;
|
popcorn[i].pos = 0.01f;
|
||||||
popcorn[i].velocity = coeff * (random(66, 100) / 100.0f);
|
|
||||||
popcorn[i].color = popcornColor;
|
|
||||||
|
|
||||||
ledIndex = SEGMENT.start;
|
uint16_t peakHeight = 128 + random8(128); //0-255
|
||||||
setPixelColor(ledIndex, popcorn[i].color);
|
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/
|
* 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
|
||||||
|
@ -360,7 +360,7 @@ 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_BouncingBalls;
|
_mode[FX_MODE_BOUNCINGBALLS] = &WS2812FX::mode_bouncing_balls;
|
||||||
_mode[FX_MODE_SINELON] = &WS2812FX::mode_sinelon;
|
_mode[FX_MODE_SINELON] = &WS2812FX::mode_sinelon;
|
||||||
_mode[FX_MODE_SINELON_DUAL] = &WS2812FX::mode_sinelon_dual;
|
_mode[FX_MODE_SINELON_DUAL] = &WS2812FX::mode_sinelon_dual;
|
||||||
_mode[FX_MODE_SINELON_RAINBOW] = &WS2812FX::mode_sinelon_rainbow;
|
_mode[FX_MODE_SINELON_RAINBOW] = &WS2812FX::mode_sinelon_rainbow;
|
||||||
@ -543,7 +543,7 @@ class WS2812FX {
|
|||||||
mode_candle(void),
|
mode_candle(void),
|
||||||
mode_starburst(void),
|
mode_starburst(void),
|
||||||
mode_exploding_fireworks(void),
|
mode_exploding_fireworks(void),
|
||||||
mode_BouncingBalls(void),
|
mode_bouncing_balls(void),
|
||||||
mode_sinelon(void),
|
mode_sinelon(void),
|
||||||
mode_sinelon_dual(void),
|
mode_sinelon_dual(void),
|
||||||
mode_sinelon_rainbow(void),
|
mode_sinelon_rainbow(void),
|
||||||
|
@ -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";
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user