Add Aurora Borealis effect (#1589)

* Added aurora effect

* Cosmetic changes prior to PR to match some guidelines

* Update FX.cpp

Moved mode_aurora function to end of FX.cpp

* Cosmetic changes prior to PR

- Changed new palette name to have a whitespace
- Undid changes to platformio.ini

* Removed commented out test palette

* Improved memory handling

Use SEGENV to allocate and manage dynamic memory needs of effect.
Only allocate as much memory as needed for current amount of waves set.

* Improvements to Aurora effect

- Smoother on low speed
- CRGB is trivially copiable
- Replaced Red & Blue mode
- Simplified logic and mem use a tiny bit
- Aurora2 palette a bit less yellowish

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
This commit is contained in:
Mazen 2021-01-04 11:11:36 +01:00 committed by GitHub
parent f5ed710c0b
commit af61962314
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 166 additions and 17 deletions

View File

@ -30,7 +30,6 @@
#define IBN 5100 #define IBN 5100
#define PALETTE_SOLID_WRAP (paletteBlend == 1 || paletteBlend == 3) #define PALETTE_SOLID_WRAP (paletteBlend == 1 || paletteBlend == 3)
/* /*
* No blinking. Just plain old static light. * No blinking. Just plain old static light.
*/ */
@ -1003,14 +1002,6 @@ uint16_t WS2812FX::mode_running_color(void) {
} }
/*
* Alternating red/blue pixels running.
*/
uint16_t WS2812FX::mode_running_red_blue(void) {
return running(RED, BLUE);
}
/* /*
* Alternating red/green pixels running. * Alternating red/green pixels running.
*/ */
@ -3880,3 +3871,153 @@ uint16_t WS2812FX::mode_tv_simulator(void) {
return FRAMETIME; return FRAMETIME;
#endif #endif
} }
/*
Aurora effect
*/
//CONFIG
#define BACKLIGHT 5
#define W_MAX_COUNT 20 //Number of simultaneous waves
#define W_MAX_SPEED 6 //Higher number, higher speed
#define W_WIDTH_FACTOR 6 //Higher number, smaller waves
class AuroraWave {
private:
uint16_t ttl;
CRGB basecolor;
float basealpha;
uint16_t age;
uint16_t width;
float center;
bool goingleft;
float speed_factor;
bool alive = true;
public:
void init(uint32_t segment_length, CRGB color) {
ttl = random(500, 1501);
basecolor = color;
basealpha = random(60, 101) / (float)100;
age = 0;
width = random(segment_length / 20, segment_length / W_WIDTH_FACTOR); //half of width to make math easier
if (!width) width = 1;
center = random(101) / (float)100 * segment_length;
goingleft = random(0, 2) == 0;
speed_factor = (random(10, 31) / (float)100 * W_MAX_SPEED / 255);
alive = true;
}
CRGB getColorForLED(int ledIndex) {
if(ledIndex < center - width || ledIndex > center + width) return 0; //Position out of range of this wave
CRGB rgb;
//Offset of this led from center of wave
//The further away from the center, the dimmer the LED
float offset = ledIndex - center;
if (offset < 0) offset = -offset;
float offsetFactor = offset / width;
//The age of the wave determines it brightness.
//At half its maximum age it will be the brightest.
float ageFactor = 0.1;
if((float)age / ttl < 0.5) {
ageFactor = (float)age / (ttl / 2);
} else {
ageFactor = (float)(ttl - age) / ((float)ttl * 0.5);
}
//Calculate color based on above factors and basealpha value
float factor = (1 - offsetFactor) * ageFactor * basealpha;
rgb.r = basecolor.r * factor;
rgb.g = basecolor.g * factor;
rgb.b = basecolor.b * factor;
return rgb;
};
//Change position and age of wave
//Determine if its sill "alive"
void update(uint32_t segment_length, uint32_t speed) {
if(goingleft) {
center -= speed_factor * speed;
} else {
center += speed_factor * speed;
}
age++;
if(age > ttl) {
alive = false;
} else {
if(goingleft) {
if(center + width < 0) {
alive = false;
}
} else {
if(center - width > segment_length) {
alive = false;
}
}
}
};
bool stillAlive() {
return alive;
};
};
uint16_t WS2812FX::mode_aurora(void) {
//aux1 = Wavecount
//aux2 = Intensity in last loop
AuroraWave* waves;
if(SEGENV.aux0 != SEGMENT.intensity || SEGENV.call == 0) {
//Intensity slider changed or first call
SEGENV.aux1 = ((float)SEGMENT.intensity / 255) * W_MAX_COUNT;
SEGENV.aux0 = SEGMENT.intensity;
if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) {
return mode_static(); //allocation failed
}
waves = reinterpret_cast<AuroraWave*>(SEGENV.data);
for(int i = 0; i < SEGENV.aux1; i++) {
waves[i].init(SEGLEN, col_to_crgb(color_from_palette(random8(), false, false, random(0, 3))));
}
} else {
waves = reinterpret_cast<AuroraWave*>(SEGENV.data);
}
for(int i = 0; i < SEGENV.aux1; i++) {
//Update values of wave
waves[i].update(SEGLEN, SEGMENT.speed);
if(!(waves[i].stillAlive())) {
//If a wave dies, reinitialize it starts over.
waves[i].init(SEGLEN, col_to_crgb(color_from_palette(random8(), false, false, random(0, 3))));
}
}
//Loop through LEDs to determine color
for(int i = 0; i < SEGLEN; i++) {
CRGB mixedRgb = CRGB(BACKLIGHT, BACKLIGHT, BACKLIGHT);
//For each LED we must check each wave if it is "active" at this position.
//If there are multiple waves active on a LED we multiply their values.
for(int j = 0; j < SEGENV.aux1; j++) {
CRGB rgb = waves[j].getColorForLED(i);
if(rgb != CRGB(0)) {
mixedRgb += rgb;
}
}
setPixelColor(i, mixedRgb[0], mixedRgb[1], mixedRgb[2], BACKLIGHT);
}
return FRAMETIME;
}

View File

@ -119,7 +119,6 @@
#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 118 #define MODE_COUNT 118
#define FX_MODE_STATIC 0 #define FX_MODE_STATIC 0
@ -160,7 +159,7 @@
#define FX_MODE_TRAFFIC_LIGHT 35 #define FX_MODE_TRAFFIC_LIGHT 35
#define FX_MODE_COLOR_SWEEP_RANDOM 36 #define FX_MODE_COLOR_SWEEP_RANDOM 36
#define FX_MODE_RUNNING_COLOR 37 #define FX_MODE_RUNNING_COLOR 37
#define FX_MODE_RUNNING_RED_BLUE 38 #define FX_MODE_AURORA 38
#define FX_MODE_RUNNING_RANDOM 39 #define FX_MODE_RUNNING_RANDOM 39
#define FX_MODE_LARSON_SCANNER 40 #define FX_MODE_LARSON_SCANNER 40
#define FX_MODE_COMET 41 #define FX_MODE_COMET 41
@ -389,7 +388,7 @@ class WS2812FX {
_mode[FX_MODE_TRAFFIC_LIGHT] = &WS2812FX::mode_traffic_light; _mode[FX_MODE_TRAFFIC_LIGHT] = &WS2812FX::mode_traffic_light;
_mode[FX_MODE_COLOR_SWEEP_RANDOM] = &WS2812FX::mode_color_sweep_random; _mode[FX_MODE_COLOR_SWEEP_RANDOM] = &WS2812FX::mode_color_sweep_random;
_mode[FX_MODE_RUNNING_COLOR] = &WS2812FX::mode_running_color; _mode[FX_MODE_RUNNING_COLOR] = &WS2812FX::mode_running_color;
_mode[FX_MODE_RUNNING_RED_BLUE] = &WS2812FX::mode_running_red_blue; _mode[FX_MODE_AURORA] = &WS2812FX::mode_aurora;
_mode[FX_MODE_RUNNING_RANDOM] = &WS2812FX::mode_running_random; _mode[FX_MODE_RUNNING_RANDOM] = &WS2812FX::mode_running_random;
_mode[FX_MODE_LARSON_SCANNER] = &WS2812FX::mode_larson_scanner; _mode[FX_MODE_LARSON_SCANNER] = &WS2812FX::mode_larson_scanner;
_mode[FX_MODE_COMET] = &WS2812FX::mode_comet; _mode[FX_MODE_COMET] = &WS2812FX::mode_comet;
@ -603,7 +602,7 @@ class WS2812FX {
mode_colorful(void), mode_colorful(void),
mode_traffic_light(void), mode_traffic_light(void),
mode_running_color(void), mode_running_color(void),
mode_running_red_blue(void), mode_aurora(void),
mode_running_random(void), mode_running_random(void),
mode_larson_scanner(void), mode_larson_scanner(void),
mode_comet(void), mode_comet(void),
@ -764,7 +763,7 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Solid","Blink","Breathe","Wipe","Wipe Random","Random Colors","Sweep","Dynamic","Colorloop","Rainbow", "Solid","Blink","Breathe","Wipe","Wipe Random","Random Colors","Sweep","Dynamic","Colorloop","Rainbow",
"Scan","Scan Dual","Fade","Theater","Theater Rainbow","Running","Saw","Twinkle","Dissolve","Dissolve Rnd", "Scan","Scan Dual","Fade","Theater","Theater Rainbow","Running","Saw","Twinkle","Dissolve","Dissolve Rnd",
"Sparkle","Sparkle Dark","Sparkle+","Strobe","Strobe Rainbow","Strobe Mega","Blink Rainbow","Android","Chase","Chase Random", "Sparkle","Sparkle Dark","Sparkle+","Strobe","Strobe Rainbow","Strobe Mega","Blink Rainbow","Android","Chase","Chase Random",
"Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Running 2","Red & Blue","Stream", "Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Running 2","Aurora","Stream",
"Scanner","Lighthouse","Fireworks","Rain","Merry Christmas","Fire Flicker","Gradient","Loading","Police","Police All", "Scanner","Lighthouse","Fireworks","Rain","Merry Christmas","Fire Flicker","Gradient","Loading","Police","Police All",
"Two Dots","Two Areas","Circus","Halloween","Tri Chase","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet", "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", "Scanner Dual","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise",
@ -782,7 +781,7 @@ const char JSON_palette_names[] PROGMEM = R"=====([
"Pastel","Sunset 2","Beech","Vintage","Departure","Landscape","Beach","Sherbet","Hult","Hult 64", "Pastel","Sunset 2","Beech","Vintage","Departure","Landscape","Beach","Sherbet","Hult","Hult 64",
"Drywet","Jul","Grintage","Rewhi","Tertiary","Fire","Icefire","Cyane","Light Pink","Autumn", "Drywet","Jul","Grintage","Rewhi","Tertiary","Fire","Icefire","Cyane","Light Pink","Autumn",
"Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura", "Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura",
"Aurora","Atlantica","C9 2","C9 New","Temperature" "Aurora","Atlantica","C9 2","C9 New","Temperature","Aurora 2"
])====="; ])=====";
#endif #endif

View File

@ -13,7 +13,7 @@
#ifndef PalettesWLED_h #ifndef PalettesWLED_h
#define PalettesWLED_h #define PalettesWLED_h
#define GRADIENT_PALETTE_COUNT 42 #define GRADIENT_PALETTE_COUNT 43
const byte ib_jul01_gp[] PROGMEM = { const byte ib_jul01_gp[] PROGMEM = {
0, 194, 1, 1, 0, 194, 1, 1,
@ -631,6 +631,14 @@ const byte temperature_gp[] PROGMEM = {
240, 80, 3, 3, 240, 80, 3, 3,
255, 80, 3, 3}; 255, 80, 3, 3};
const byte Aurora2[] PROGMEM = {
0, 17, 177, 13, //Greenish
64, 121, 242, 5, //Greenish
128, 25, 173, 121, //Turquoise
192, 250, 77, 127, //Pink
255, 171, 101, 221 //Purple
};
// Single array of defined cpt-city color palettes. // Single array of defined cpt-city color palettes.
// This will let us programmatically choose one based on // This will let us programmatically choose one based on
// a number, rather than having to activate each explicitly // a number, rather than having to activate each explicitly
@ -677,7 +685,8 @@ const byte* const gGradientPalettes[] PROGMEM = {
Atlantica_gp, //51-38 Atlantica Atlantica_gp, //51-38 Atlantica
C9_2_gp, //52-39 C9 2 C9_2_gp, //52-39 C9 2
C9_new_gp, //53-40 C9 New C9_new_gp, //53-40 C9 New
temperature_gp //54-41 Temperature temperature_gp, //54-41 Temperature
Aurora2 //55-42 Aurora 2
}; };
#endif #endif