@@ -2310,7 +2310,7 @@ uint16_t mode_meteor() {
23102310 {
23112311 byte meteorTrailDecay = 128 + random8 (127 );
23122312 trail[i] = scale8 (trail[i], meteorTrailDecay);
2313- SEGMENT.setPixelColor (i, SEGMENT.color_from_palette (i, true , false , 0 , trail[i]));
2313+ SEGMENT.setPixelColor (i, color_blend ( SEGCOLOR ( 1 ), SEGMENT.color_from_palette (i, true , false , 0 ) , trail[i]));
23142314 }
23152315 }
23162316
@@ -2321,12 +2321,12 @@ uint16_t mode_meteor() {
23212321 index -= SEGLEN;
23222322 }
23232323 trail[index] = 240 ;
2324- SEGMENT.setPixelColor (index, SEGMENT.color_from_palette (index, true , false , 0 , 255 ));
2324+ SEGMENT.setPixelColor (index, SEGMENT.color_from_palette (index, true , false , 0 ));
23252325 }
23262326
23272327 return FRAMETIME;
23282328}
2329- static const char _data_FX_MODE_METEOR[] PROGMEM = " Meteor@!,Trail length;!;! " ;
2329+ static const char _data_FX_MODE_METEOR[] PROGMEM = " Meteor@!,Trail length;!,!;!;1 " ;
23302330
23312331
23322332// smooth meteor effect
@@ -2349,7 +2349,7 @@ uint16_t mode_meteor_smooth() {
23492349 trail[i] += change;
23502350 if (trail[i] > 245 ) trail[i] = 0 ;
23512351 if (trail[i] > 240 ) trail[i] = 240 ;
2352- SEGMENT.setPixelColor (i, SEGMENT.color_from_palette (i, true , false , 0 , trail[i]));
2352+ SEGMENT.setPixelColor (i, color_blend ( SEGCOLOR ( 1 ), SEGMENT.color_from_palette (i, true , false , 0 ) , trail[i]));
23532353 }
23542354 }
23552355
@@ -2360,13 +2360,13 @@ uint16_t mode_meteor_smooth() {
23602360 index -= SEGLEN;
23612361 }
23622362 trail[index] = 240 ;
2363- SEGMENT.setPixelColor (index, SEGMENT.color_from_palette (index, true , false , 0 , 255 ));
2363+ SEGMENT.setPixelColor (index, SEGMENT.color_from_palette (index, true , false , 0 ));
23642364 }
23652365
23662366 SEGENV.step += SEGMENT.speed +1 ;
23672367 return FRAMETIME;
23682368}
2369- static const char _data_FX_MODE_METEOR_SMOOTH[] PROGMEM = " Meteor Smooth@!,Trail length;!;! " ;
2369+ static const char _data_FX_MODE_METEOR_SMOOTH[] PROGMEM = " Meteor Smooth@!,Trail length;!,!;!;1 " ;
23702370
23712371
23722372// Railway Crossing / Christmas Fairy lights
@@ -2441,9 +2441,10 @@ uint16_t ripple_base()
24412441
24422442 #ifndef WLED_DISABLE_2D
24432443 if (SEGMENT.is2D ()) {
2444+ propI /= 2 ;
24442445 uint16_t cx = rippleorigin >> 8 ;
24452446 uint16_t cy = rippleorigin & 0xFF ;
2446- uint8_t mag = scale8 (cubicwave8 ((propF>>2 )), amp);
2447+ uint8_t mag = scale8 (sin8 ((propF>>2 )), amp);
24472448 if (propI > 0 ) SEGMENT.draw_circle (cx, cy, propI, color_blend (SEGMENT.getPixelColorXY (cx + propI, cy), col, mag));
24482449 } else
24492450 #endif
@@ -2461,7 +2462,7 @@ uint16_t ripple_base()
24612462 ripplestate += rippledecay;
24622463 ripples[i].state = (ripplestate > 254 ) ? 0 : ripplestate;
24632464 } else {// randomly create new wave
2464- if (random16 (IBN + 10000 ) <= SEGMENT.intensity ) {
2465+ if (random16 (IBN + 10000 ) <= ( SEGMENT.intensity >> (SEGMENT. is2D ()* 3 )) ) {
24652466 ripples[i].state = 1 ;
24662467 ripples[i].pos = SEGMENT.is2D () ? ((random8 (SEGENV.virtualWidth ())<<8 ) | (random8 (SEGENV.virtualHeight ()))) : random16 (SEGLEN);
24672468 ripples[i].color = random8 (); // color
@@ -2477,6 +2478,7 @@ uint16_t ripple_base()
24772478uint16_t mode_ripple (void ) {
24782479 if (SEGLEN == 1 ) return mode_static ();
24792480 if (!SEGMENT.check2 ) SEGMENT.fill (SEGCOLOR (1 ));
2481+ else SEGMENT.fade_out (250 );
24802482 return ripple_base ();
24812483}
24822484static const char _data_FX_MODE_RIPPLE[] PROGMEM = " Ripple@!,Wave #,,,,,Overlay;,!;!;12" ;
@@ -2883,6 +2885,103 @@ uint16_t mode_bouncing_balls(void) {
28832885static const char _data_FX_MODE_BOUNCINGBALLS[] PROGMEM = " Bouncing Balls@Gravity,# of balls,,,,,Overlay;!,!,!;!;1.5d;m12=1" ; // bar WLEDMM 1.5d
28842886
28852887
2888+ /*
2889+ * bouncing balls on a track track Effect modified from Aircoookie's bouncing balls
2890+ * Courtesy of pjhatch (https://github.com/pjhatch)
2891+ * https://github.com/Aircoookie/WLED/pull/1039
2892+ */
2893+ // modified for balltrack mode
2894+ typedef struct RollingBall {
2895+ unsigned long lastBounceUpdate;
2896+ float mass; // could fix this to be = 1. if memory is an issue
2897+ float velocity;
2898+ float height;
2899+ } rball_t ;
2900+
2901+ static uint16_t rolling_balls (void ) {
2902+ // allocate segment data
2903+ const uint16_t maxNumBalls = 16 ; // 255/16 + 1
2904+ uint16_t dataSize = sizeof (rball_t ) * maxNumBalls;
2905+ if (!SEGENV.allocateData (dataSize)) return mode_static (); // allocation failed
2906+
2907+ rball_t *balls = reinterpret_cast <rball_t *>(SEGENV.data );
2908+
2909+ // number of balls based on intensity setting to max of 16 (cycles colors)
2910+ // non-chosen color is a random color
2911+ uint8_t numBalls = SEGMENT.intensity /16 + 1 ;
2912+
2913+ if (SEGENV.call == 0 ) {
2914+ for (int i = 0 ; i < maxNumBalls; i++) {
2915+ balls[i].lastBounceUpdate = strip.now ;
2916+ balls[i].velocity = 20 .0f * float (random16 (1000 , 10000 ))/10000 .0f ; // number from 1 to 10
2917+ if (random8 ()<128 ) balls[i].velocity = -balls[i].velocity ; // 50% chance of reverse direction
2918+ balls[i].height = (float (random16 (0 , 10000 )) / 10000 .0f ); // from 0. to 1.
2919+ balls[i].mass = (float (random16 (1000 , 10000 )) / 10000 .0f ); // from .1 to 1.
2920+ }
2921+ }
2922+
2923+ float cfac = float (scale8 (8 , 255 -SEGMENT.speed ) +1 )*20000 .0f ; // this uses the Aircoookie conversion factor for scaling time using speed slider
2924+
2925+ bool hasCol2 = SEGCOLOR (2 );
2926+ if (!SEGMENT.check2 ) SEGMENT.fill (hasCol2 ? BLACK : SEGCOLOR (1 ));
2927+
2928+ for (int i = 0 ; i < numBalls; i++) {
2929+ float timeSinceLastUpdate = float ((strip.now - balls[i].lastBounceUpdate ))/cfac;
2930+ float thisHeight = balls[i].height + balls[i].velocity * timeSinceLastUpdate; // this method keeps higher resolution
2931+ // test if intensity level was increased and some balls are way off the track then put them back
2932+ if (thisHeight < -0 .5f || thisHeight > 1 .5f ){
2933+ thisHeight = balls[i].height = (float (random16 (0 , 10000 )) / 10000 .0f ); // from 0. to 1.
2934+ balls[i].lastBounceUpdate = strip.now ;
2935+ }
2936+ // check if reached ends of the strip
2937+ if ((thisHeight <= 0 .0f && balls[i].velocity < 0 .0f ) || (thisHeight >= 1 .0f && balls[i].velocity > 0 .0f )) {
2938+ balls[i].velocity = -balls[i].velocity ; // reverse velocity
2939+ balls[i].lastBounceUpdate = strip.now ;
2940+ balls[i].height = thisHeight;
2941+ }
2942+ // check for collisions
2943+ if (SEGMENT.check1 ) {
2944+ for (int j = i+1 ; j < numBalls; j++) {
2945+ if (balls[j].velocity != balls[i].velocity ) {
2946+ // tcollided + balls[j].lastBounceUpdate is acutal time of collision (this keeps precision with long to float conversions)
2947+ float tcollided = (cfac*(balls[i].height - balls[j].height ) +
2948+ balls[i].velocity *float (balls[j].lastBounceUpdate - balls[i].lastBounceUpdate ))/(balls[j].velocity - balls[i].velocity );
2949+
2950+ if ((tcollided > 2 .0f ) && (tcollided < float (strip.now - balls[j].lastBounceUpdate ))) { // 2ms minimum to avoid duplicate bounces
2951+ balls[i].height = balls[i].height + balls[i].velocity *(tcollided + float (balls[j].lastBounceUpdate - balls[i].lastBounceUpdate ))/cfac;
2952+ balls[j].height = balls[i].height ;
2953+ balls[i].lastBounceUpdate = (unsigned long )(tcollided + 0 .5f ) + balls[j].lastBounceUpdate ;
2954+ balls[j].lastBounceUpdate = balls[i].lastBounceUpdate ;
2955+ float vtmp = balls[i].velocity ;
2956+ balls[i].velocity = ((balls[i].mass - balls[j].mass )*vtmp + 2 .0f *balls[j].mass *balls[j].velocity )/(balls[i].mass + balls[j].mass );
2957+ balls[j].velocity = ((balls[j].mass - balls[i].mass )*balls[j].velocity + 2 .0f *balls[i].mass *vtmp) /(balls[i].mass + balls[j].mass );
2958+ thisHeight = balls[i].height + balls[i].velocity *(strip.now - balls[i].lastBounceUpdate )/cfac;
2959+ }
2960+ }
2961+ }
2962+ }
2963+
2964+ uint32_t color = SEGCOLOR (0 );
2965+ if (SEGMENT.palette ) {
2966+ // color = SEGMENT.color_wheel(i*(256/MAX(numBalls, 8)));
2967+ color = SEGMENT.color_from_palette (i*255 /numBalls, false , PALETTE_SOLID_WRAP, 0 );
2968+ } else if (hasCol2) {
2969+ color = SEGCOLOR (i % NUM_COLORS);
2970+ }
2971+
2972+ if (thisHeight < 0 .0f ) thisHeight = 0 .0f ;
2973+ if (thisHeight > 1 .0f ) thisHeight = 1 .0f ;
2974+ uint16_t pos = round (thisHeight * (SEGLEN - 1 ));
2975+ SEGMENT.setPixelColor (pos, color);
2976+ balls[i].lastBounceUpdate = strip.now ;
2977+ balls[i].height = thisHeight;
2978+ }
2979+
2980+ return FRAMETIME;
2981+ }
2982+ static const char _data_FX_MODE_ROLLINGBALLS[] PROGMEM = " Rolling Balls@!,# of balls,,,,Collisions,Overlay;!,!,!;!;1;m12=1" ; // bar
2983+
2984+
28862985/*
28872986* Sinelon stolen from FASTLED examples
28882987*/
@@ -5165,7 +5264,7 @@ static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous ☾@X frequen
51655264// /////////////////////
51665265// 2D Matrix //
51675266// /////////////////////
5168- uint16_t mode_2Dmatrix (void ) { // Matrix2D. By Jeremy Williams. Adapted by Andrew Tuline & improved by merkisoft and ewowi.
5267+ uint16_t mode_2Dmatrix (void ) { // Matrix2D. By Jeremy Williams. Adapted by Andrew Tuline & improved by merkisoft and ewowi, and softhack007 .
51695268 if (!strip.isMatrix ) return mode_static (); // not a 2D set-up
51705269
51715270 const uint16_t cols = SEGMENT.virtualWidth ();
@@ -5174,6 +5273,8 @@ uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams.
51745273 if (SEGENV.call == 0 ) {
51755274 SEGMENT.setUpLeds ();
51765275 SEGMENT.fill (BLACK);
5276+ SEGENV.aux0 = SEGENV.aux1 = UINT16_MAX;
5277+ SEGENV.step = 0 ;
51775278 }
51785279
51795280 uint8_t fade = map (SEGMENT.custom1 , 0 , 255 , 50 , 250 ); // equals trail size
@@ -5191,10 +5292,23 @@ uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams.
51915292
51925293 if (strip.now - SEGENV.step >= speed) {
51935294 SEGENV.step = strip.now ;
5295+ // find out what color value is returned by gPC for a "falling code" example pixel
5296+ // the color values returned may differ from the previously set values, due to
5297+ // - auto brightness limiter (dimming)
5298+ // - lossy color buffer (when not using global buffer)
5299+ // - color balance correction
5300+ // - segment opacity
5301+ CRGB oldSpawnColor = spawnColor;
5302+ if ((SEGENV.aux0 < cols) && (SEGENV.aux1 < rows)) { // we have a hint from last run
5303+ oldSpawnColor = SEGMENT.getPixelColorXY (SEGENV.aux0 , SEGENV.aux1 ); // find color of previous spawns
5304+ SEGENV.aux1 ++; // our sample pixel will be one row down the next time
5305+ }
5306+
5307+ // move pixels one row down. Falling codes keep color and add trail pixels; all others pixels are faded
51945308 for (int row=rows-1 ; row>=0 ; row--) {
51955309 for (int col=0 ; col<cols; col++) {
51965310 CRGB pix = SEGMENT.getPixelColorXY (col, row);
5197- if (pix == spawnColor ) {
5311+ if (pix == oldSpawnColor ) { // this comparison may still fail due to overlays changing pixels, or due to gaps (2d-gaps.json)
51985312 SEGMENT.setPixelColorXY (col, row, trailColor); // create trail
51995313 if (row < rows-1 ) SEGMENT.setPixelColorXY (col, row+1 , spawnColor);
52005314 } else {
@@ -5205,18 +5319,15 @@ uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams.
52055319 }
52065320
52075321 // check for empty screen to ensure code spawn
5208- bool emptyScreen = true ;
5209- for (int x=0 ; x<cols; x++) for (int y=0 ; y<rows; y++) {
5210- if (SEGMENT.getPixelColorXY (x,y)) {
5211- emptyScreen = false ;
5212- break ;
5213- }
5214- }
5322+ bool emptyScreen = (SEGENV.aux1 >= rows); // empty screen means that the last falling code has moved out of screen area
52155323
52165324 // spawn new falling code
5217- if (random8 () < SEGMENT.intensity || emptyScreen) {
5325+ if (random8 () <= SEGMENT.intensity || emptyScreen) {
52185326 uint8_t spawnX = random8 (cols);
52195327 SEGMENT.setPixelColorXY (spawnX, 0 , spawnColor);
5328+ // update hint for next run
5329+ SEGENV.aux0 = spawnX;
5330+ SEGENV.aux1 = 0 ;
52205331 }
52215332 } // if millis
52225333
@@ -7986,6 +8097,7 @@ void WS2812FX::setupEffectData() {
79868097 addEffect (FX_MODE_FIRE_FLICKER, &mode_fire_flicker, _data_FX_MODE_FIRE_FLICKER);
79878098 addEffect (FX_MODE_GRADIENT, &mode_gradient, _data_FX_MODE_GRADIENT);
79888099 addEffect (FX_MODE_LOADING, &mode_loading, _data_FX_MODE_LOADING);
8100+ addEffect (FX_MODE_ROLLINGBALLS, &rolling_balls, _data_FX_MODE_ROLLINGBALLS);
79898101
79908102 addEffect (FX_MODE_FAIRY, &mode_fairy, _data_FX_MODE_FAIRY);
79918103 addEffect (FX_MODE_TWO_DOTS, &mode_two_dots, _data_FX_MODE_TWO_DOTS);
0 commit comments