From e2f23542f5eac0d7c1e11e2d86be67500dd893d7 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Wed, 10 Jul 2024 08:25:40 +0100 Subject: [PATCH 01/27] Add @TroyHacks 3D GEQ --- wled00/FX.cpp | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++ wled00/FX.h | 3 +- 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 416884cf37..dcd40196e3 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8355,6 +8355,129 @@ uint16_t mode_2Dwavingcell() { } static const char _data_FX_MODE_2DWAVINGCELL[] PROGMEM = "Waving Cell@!,,Amplitude 1,Amplitude 2,Amplitude 3;;!;2"; +uint16_t mode_3DGEQ(void) { + + static uint16_t projector; + static uint16_t projector_dir = 1; + + // delay(1000); + + if (SEGENV.call == 0) { + projector = 0; + SEGMENT.setUpLeds(); // WLEDMM use lossless getPixelColor() + SEGMENT.fill(BLACK); + } else { + projector += projector_dir; + if (projector == SEGMENT.virtualWidth()) projector_dir = -1; + if (projector == 0) projector_dir = 1; + } + + uint_fast8_t split = map(projector,0,SEGMENT.virtualWidth(),0,15); + + if (SEGMENT.speed > 250) { + SEGMENT.fill(BLACK); + } else { + SEGMENT.fadeToBlackBy(SEGMENT.speed); + } + + const int NUM_BANDS = 16; // map(SEGMENT.custom1, 0, 255, 1, 16); + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; + + uint8_t heights[16] = { 0 }; + + for (int i=0; i<16; i++) { + heights[i] = map(fftResult[i],0,255,0,rows-10); + } + + for (int i=0; i<=split; i++) { // paint right vertical faces and top + + uint16_t colorIndex = map(cols/16*i, 0, cols-1, 0, 255); + uint32_t ledColor = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0); + + int linex = i*(cols/16); + + if (heights[i] > 1) { + + for (int y = 0; y <= heights[i]; y++) { + SEGMENT.drawLine(linex+(cols/16)-1,rows-y-1,projector,0,color_fade(ledColor,32,true)); + } + + for (int x=linex; x<=linex+(cols/16)-1;x++) { + SEGMENT.drawLine(x, rows-heights[i]-2,projector,0,color_fade(ledColor,128,true)); // top perspective + } + + } + + } + + for (int i=15; i>split; i--) { // paint left vertical faces and top + + uint16_t colorIndex = map(cols/16*i, 0, cols-1, 0, 255); + uint32_t ledColor = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0); + + int linex = i*(cols/16); + + if (heights[i] > 1) { + + for (int y = 0; y <= heights[i]; y++) { + SEGMENT.drawLine(linex ,rows-y-1,projector,0,color_fade(ledColor,32,true)); + } + + for (int x=linex; x<=linex+(cols/16)-1;x++) { + SEGMENT.drawLine(x, rows-heights[i]-2,projector,0,color_fade(ledColor,128,true)); // top perspective + } + + } + + } + + for (int i=0; i<16; i++) { + + uint16_t colorIndex = map(cols/16*i, 0, cols-1, 0, 255); + uint32_t ledColor = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0); + + int linex = i*(cols/16); + + if (heights[i] > 1) { + + // Full bright fronts, fills all front face. + // for (int x=linex; x Date: Wed, 10 Jul 2024 08:26:07 +0100 Subject: [PATCH 02/27] Add @TroyHacks 3D GEQ --- wled00/FX.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index dcd40196e3..70fa797d82 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8357,6 +8357,8 @@ static const char _data_FX_MODE_2DWAVINGCELL[] PROGMEM = "Waving Cell@!,,Amplitu uint16_t mode_3DGEQ(void) { + // Author: @TroyHacks + static uint16_t projector; static uint16_t projector_dir = 1; From 2d7e416879bdc3a481596cd479d98727872ddc59 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Wed, 10 Jul 2024 08:33:42 +0100 Subject: [PATCH 03/27] ; --- wled00/FX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 70fa797d82..70163a1eb5 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8478,7 +8478,7 @@ uint16_t mode_3DGEQ(void) { return FRAMETIME; } -static const char _data_FX_MODE_3DGEQ[] PROGMEM = "3D GEQ" // TODO set Audio, 2D and controls etc +static const char _data_FX_MODE_3DGEQ[] PROGMEM = "3D GEQ"; // TODO set Audio, 2D and controls etc #endif // WLED_DISABLE_2D From 45f288a2f2aa942543ee1a411fe5c629cdd64f59 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Wed, 10 Jul 2024 08:59:05 +0100 Subject: [PATCH 04/27] 3D GEQ - make front brightness a control --- wled00/FX.cpp | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 70163a1eb5..02d9efcf39 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8358,7 +8358,7 @@ static const char _data_FX_MODE_2DWAVINGCELL[] PROGMEM = "Waving Cell@!,,Amplitu uint16_t mode_3DGEQ(void) { // Author: @TroyHacks - + static uint16_t projector; static uint16_t projector_dir = 1; @@ -8441,6 +8441,7 @@ uint16_t mode_3DGEQ(void) { } + uint8_t frontBrightness = SEGMENT.custom1; for (int i=0; i<16; i++) { uint16_t colorIndex = map(cols/16*i, 0, cols-1, 0, 255); @@ -8450,35 +8451,41 @@ uint16_t mode_3DGEQ(void) { if (heights[i] > 1) { - // Full bright fronts, fills all front face. - // for (int x=linex; x 250) { + // Full bright fronts, fills all front face. + for (int x=linex; x= 50) { + // Faded fronts, assumes border added later. + for (int x=linex+1; x= 50) { // TODO: other values too? not sure exactly when we want the border // Border - // SEGMENT.drawLine(linex, rows-1,linex,rows-heights[i]-1,ledColor); // left side line - // SEGMENT.drawLine(linex+(cols/16)-1,rows-1,linex+(cols/16)-1,rows-heights[i]-1,ledColor); // right side line - // SEGMENT.drawLine(linex, rows-heights[i]-2,linex+(cols/16)-1,rows-heights[i]-2,ledColor); // top line - // SEGMENT.drawLine(linex, rows-1,linex+(cols/16)-1,rows-1,ledColor); // bottom line - + SEGMENT.drawLine(linex, rows-1,linex,rows-heights[i]-1,ledColor); // left side line + SEGMENT.drawLine(linex+(cols/16)-1,rows-1,linex+(cols/16)-1,rows-heights[i]-1,ledColor); // right side line + SEGMENT.drawLine(linex, rows-heights[i]-2,linex+(cols/16)-1,rows-heights[i]-2,ledColor); // top line + SEGMENT.drawLine(linex, rows-1,linex+(cols/16)-1,rows-1,ledColor); // bottom line } } + } + return FRAMETIME; } -static const char _data_FX_MODE_3DGEQ[] PROGMEM = "3D GEQ"; // TODO set Audio, 2D and controls etc +static const char _data_FX_MODE_3DGEQ[] PROGMEM = "3D GEQ☾@!,,Fill Front,,;;!;2"; // TODO set Audio, 2D and controls etc #endif // WLED_DISABLE_2D From f6cd28cea2ed291560d1e8b44b14e6d354d2eb0d Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Wed, 10 Jul 2024 09:20:58 +0100 Subject: [PATCH 05/27] 3D GEQ - customisable number of bands --- wled00/FX.cpp | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 02d9efcf39..6cda5cf98e 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8382,7 +8382,7 @@ uint16_t mode_3DGEQ(void) { SEGMENT.fadeToBlackBy(SEGMENT.speed); } - const int NUM_BANDS = 16; // map(SEGMENT.custom1, 0, 255, 1, 16); + const int NUM_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16); const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -8393,26 +8393,26 @@ uint16_t mode_3DGEQ(void) { } uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; - uint8_t heights[16] = { 0 }; + uint8_t heights[NUM_BANDS] = { 0 }; - for (int i=0; i<16; i++) { - heights[i] = map(fftResult[i],0,255,0,rows-10); + for (int i=0; i 1) { for (int y = 0; y <= heights[i]; y++) { - SEGMENT.drawLine(linex+(cols/16)-1,rows-y-1,projector,0,color_fade(ledColor,32,true)); + SEGMENT.drawLine(linex+(cols/NUM_BANDS)-1,rows-y-1,projector,0,color_fade(ledColor,32,true)); } - for (int x=linex; x<=linex+(cols/16)-1;x++) { + for (int x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { SEGMENT.drawLine(x, rows-heights[i]-2,projector,0,color_fade(ledColor,128,true)); // top perspective } @@ -8422,10 +8422,10 @@ uint16_t mode_3DGEQ(void) { for (int i=15; i>split; i--) { // paint left vertical faces and top - uint16_t colorIndex = map(cols/16*i, 0, cols-1, 0, 255); + uint16_t colorIndex = map(cols/NUM_BANDS*i, 0, cols-1, 0, 255); uint32_t ledColor = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0); - int linex = i*(cols/16); + int linex = i*(cols/NUM_BANDS); if (heights[i] > 1) { @@ -8433,7 +8433,7 @@ uint16_t mode_3DGEQ(void) { SEGMENT.drawLine(linex ,rows-y-1,projector,0,color_fade(ledColor,32,true)); } - for (int x=linex; x<=linex+(cols/16)-1;x++) { + for (int x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { SEGMENT.drawLine(x, rows-heights[i]-2,projector,0,color_fade(ledColor,128,true)); // top perspective } @@ -8441,31 +8441,31 @@ uint16_t mode_3DGEQ(void) { } - uint8_t frontBrightness = SEGMENT.custom1; - for (int i=0; i<16; i++) { + uint8_t frontBrightness = SEGMENT.custom2; + for (int i=0; i 1) { if(frontBrightness > 250) { // Full bright fronts, fills all front face. - for (int x=linex; x= 50) { // Faded fronts, assumes border added later. - for (int x=linex+1; x= 50) { // TODO: other values too? not sure exactly when we want the border // Border SEGMENT.drawLine(linex, rows-1,linex,rows-heights[i]-1,ledColor); // left side line - SEGMENT.drawLine(linex+(cols/16)-1,rows-1,linex+(cols/16)-1,rows-heights[i]-1,ledColor); // right side line - SEGMENT.drawLine(linex, rows-heights[i]-2,linex+(cols/16)-1,rows-heights[i]-2,ledColor); // top line - SEGMENT.drawLine(linex, rows-1,linex+(cols/16)-1,rows-1,ledColor); // bottom line + SEGMENT.drawLine(linex+(cols/NUM_BANDS)-1,rows-1,linex+(cols/NUM_BANDS)-1,rows-heights[i]-1,ledColor); // right side line + SEGMENT.drawLine(linex, rows-heights[i]-2,linex+(cols/NUM_BANDS)-1,rows-heights[i]-2,ledColor); // top line + SEGMENT.drawLine(linex, rows-1,linex+(cols/NUM_BANDS)-1,rows-1,ledColor); // bottom line } } @@ -8485,7 +8485,7 @@ uint16_t mode_3DGEQ(void) { return FRAMETIME; } -static const char _data_FX_MODE_3DGEQ[] PROGMEM = "3D GEQ☾@!,,Fill Front,,;;!;2"; // TODO set Audio, 2D and controls etc +static const char _data_FX_MODE_3DGEQ[] PROGMEM = "3D GEQ☾@!,,Bands,Fill Front,;;!;2"; // TODO set Audio, 2D and controls etc #endif // WLED_DISABLE_2D From cb3d06bc894ca4c61aa7b8bbecb44d2684726859 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Wed, 10 Jul 2024 09:24:26 +0100 Subject: [PATCH 06/27] 3D GEQ - variable front brightness --- wled00/FX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 6cda5cf98e..d157d5ce54 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8460,7 +8460,7 @@ uint16_t mode_3DGEQ(void) { else if(frontBrightness >= 50) { // Faded fronts, assumes border added later. for (int x=linex+1; x Date: Wed, 10 Jul 2024 09:40:40 +0100 Subject: [PATCH 07/27] 3D GEQ - customisable number of bands --- wled00/FX.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index d157d5ce54..144499c57a 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8374,8 +8374,6 @@ uint16_t mode_3DGEQ(void) { if (projector == 0) projector_dir = 1; } - uint_fast8_t split = map(projector,0,SEGMENT.virtualWidth(),0,15); - if (SEGMENT.speed > 250) { SEGMENT.fill(BLACK); } else { @@ -8385,6 +8383,7 @@ uint16_t mode_3DGEQ(void) { const int NUM_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16); const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); + const uint_fast8_t split = map(projector,0,SEGMENT.virtualWidth(),0,(NUM_BANDS - 1)); um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { @@ -8420,7 +8419,7 @@ uint16_t mode_3DGEQ(void) { } - for (int i=15; i>split; i--) { // paint left vertical faces and top + for (int i=(NUM_BANDS - 1); i>split; i--) { // paint left vertical faces and top uint16_t colorIndex = map(cols/NUM_BANDS*i, 0, cols-1, 0, 255); uint32_t ledColor = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0); From bda4bbeeecabce2cc23fd96a98bba00ae95f2358 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Wed, 10 Jul 2024 23:00:42 +0100 Subject: [PATCH 08/27] 3D GEQ - fade speed --- wled00/FX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 144499c57a..e61a466b16 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8484,7 +8484,7 @@ uint16_t mode_3DGEQ(void) { return FRAMETIME; } -static const char _data_FX_MODE_3DGEQ[] PROGMEM = "3D GEQ☾@!,,Bands,Fill Front,;;!;2"; // TODO set Audio, 2D and controls etc +static const char _data_FX_MODE_3DGEQ[] PROGMEM = "3D GEQ☾@!Fade Speed,,Bands,Fill Front,;;!;2"; // TODO set Audio, 2D and controls etc #endif // WLED_DISABLE_2D From 8f4380b075a3ba054d15b0a82330843a6ad436cf Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Wed, 10 Jul 2024 23:51:53 +0100 Subject: [PATCH 09/27] Rename 3D GEQ to GEQ Laser --- wled00/FX.cpp | 6 +++--- wled00/FX.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index e61a466b16..6ae0672b8b 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8355,7 +8355,7 @@ uint16_t mode_2Dwavingcell() { } static const char _data_FX_MODE_2DWAVINGCELL[] PROGMEM = "Waving Cell@!,,Amplitude 1,Amplitude 2,Amplitude 3;;!;2"; -uint16_t mode_3DGEQ(void) { +uint16_t mode_GEQLASER(void) { // Author: @TroyHacks @@ -8484,7 +8484,7 @@ uint16_t mode_3DGEQ(void) { return FRAMETIME; } -static const char _data_FX_MODE_3DGEQ[] PROGMEM = "3D GEQ☾@!Fade Speed,,Bands,Fill Front,;;!;2"; // TODO set Audio, 2D and controls etc +static const char _data_FX_MODE_GEQLASER[] PROGMEM = "GEQ Laser ☾@Fade Speed,,Bands,Fill Front,;;!;2f"; #endif // WLED_DISABLE_2D @@ -8733,7 +8733,7 @@ void WS2812FX::setupEffectData() { addEffect(FX_MODE_2DAKEMI, &mode_2DAkemi, _data_FX_MODE_2DAKEMI); // audio - addEffect(FX_MODE_3DGEQ, &mode_3DGEQ, _data_FX_MODE_3DGEQ); // audio + addEffect(FX_MODE_GEQLASER, &mode_GEQLASER, _data_FX_MODE_GEQLASER); // audio #endif // WLED_DISABLE_2D diff --git a/wled00/FX.h b/wled00/FX.h index 10c4c7d0fe..2143d98407 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -344,7 +344,7 @@ bool strip_uses_global_leds(void); // WLEDMM implemented in FX_fcn. #define FX_MODE_STARBURST_AR 192 // WLED-SR audioreactive fireworks starburst // #define FX_MODE_PALETTE_AR 193 // WLED-SR audioreactive palette #define FX_MODE_FIREWORKS_AR 194 // WLED-SR audioreactive fireworks -#define FX_MODE_3DGEQ 195 // WLED-MM 3D GEQ +#define FX_MODE_GEQLASER 195 // WLED-MM GEQ Laser #define MODE_COUNT 196 From aed861d063c2a4f66d42788fad811270b2bdce03 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Wed, 10 Jul 2024 23:58:35 +0100 Subject: [PATCH 10/27] GEQ Laser use SEGMENT.data --- wled00/FX.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 6ae0672b8b..68b848cdad 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8359,19 +8359,21 @@ uint16_t mode_GEQLASER(void) { // Author: @TroyHacks - static uint16_t projector; - static uint16_t projector_dir = 1; + const size_t dataSize = sizeof(uint16_t); + if (!SEGENV.allocateData(dataSize * 2)) return mode_static(); //allocation failed - // delay(1000); + uint16_t *projector = reinterpret_cast(SEGENV.data); + uint16_t *projector_dir = reinterpret_cast(SEGENV.data + dataSize); if (SEGENV.call == 0) { - projector = 0; + *projector = 0; + *projector_dir = 0; SEGMENT.setUpLeds(); // WLEDMM use lossless getPixelColor() SEGMENT.fill(BLACK); } else { - projector += projector_dir; - if (projector == SEGMENT.virtualWidth()) projector_dir = -1; - if (projector == 0) projector_dir = 1; + *projector += *projector_dir; + if (*projector == SEGMENT.virtualWidth()) *projector_dir = -1; + if (*projector == 0) *projector_dir = 1; } if (SEGMENT.speed > 250) { @@ -8383,7 +8385,7 @@ uint16_t mode_GEQLASER(void) { const int NUM_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16); const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - const uint_fast8_t split = map(projector,0,SEGMENT.virtualWidth(),0,(NUM_BANDS - 1)); + const uint_fast8_t split = map(*projector,0,SEGMENT.virtualWidth(),0,(NUM_BANDS - 1)); um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { @@ -8395,7 +8397,7 @@ uint16_t mode_GEQLASER(void) { uint8_t heights[NUM_BANDS] = { 0 }; for (int i=0; i 1) { for (int y = 0; y <= heights[i]; y++) { - SEGMENT.drawLine(linex+(cols/NUM_BANDS)-1,rows-y-1,projector,0,color_fade(ledColor,32,true)); + SEGMENT.drawLine(linex+(cols/NUM_BANDS)-1,rows-y-1,*projector,0,color_fade(ledColor,32,true)); } for (int x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { - SEGMENT.drawLine(x, rows-heights[i]-2,projector,0,color_fade(ledColor,128,true)); // top perspective + SEGMENT.drawLine(x, rows-heights[i]-2,*projector,0,color_fade(ledColor,128,true)); // top perspective } } @@ -8429,11 +8431,11 @@ uint16_t mode_GEQLASER(void) { if (heights[i] > 1) { for (int y = 0; y <= heights[i]; y++) { - SEGMENT.drawLine(linex ,rows-y-1,projector,0,color_fade(ledColor,32,true)); + SEGMENT.drawLine(linex ,rows-y-1,*projector,0,color_fade(ledColor,32,true)); } for (int x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { - SEGMENT.drawLine(x, rows-heights[i]-2,projector,0,color_fade(ledColor,128,true)); // top perspective + SEGMENT.drawLine(x, rows-heights[i]-2,*projector,0,color_fade(ledColor,128,true)); // top perspective } } From ce8f01bf902b42c112157c4e5d57d885812f32e2 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Fri, 12 Jul 2024 21:48:09 -0400 Subject: [PATCH 11/27] Bugfixed GEQ 3D Bug fixes after lots of testing, better settings, etc. Big thanks to @netmindz for restructuring the code. Add rough distance stop to Segment::drawLine() --- wled00/FX.cpp | 106 +++++++++++++++++++++++++------------------- wled00/FX.h | 4 +- wled00/FX_2Dfcn.cpp | 4 +- 3 files changed, 65 insertions(+), 49 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 68b848cdad..f1339bd1d3 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8371,21 +8371,18 @@ uint16_t mode_GEQLASER(void) { SEGMENT.setUpLeds(); // WLEDMM use lossless getPixelColor() SEGMENT.fill(BLACK); } else { - *projector += *projector_dir; + if (SEGENV.call % map(SEGMENT.speed,0,255,10,1) == 0) *projector += *projector_dir; if (*projector == SEGMENT.virtualWidth()) *projector_dir = -1; if (*projector == 0) *projector_dir = 1; } - if (SEGMENT.speed > 250) { - SEGMENT.fill(BLACK); - } else { - SEGMENT.fadeToBlackBy(SEGMENT.speed); - } + SEGMENT.fill(BLACK); - const int NUM_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16); + const int NUM_BANDS = map(SEGMENT.custom3, 0, 31, 1, 16); // custom3 is 0..31 const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - const uint_fast8_t split = map(*projector,0,SEGMENT.virtualWidth(),0,(NUM_BANDS - 1)); + uint32_t ledColorTemp; + uint_fast8_t split = map(*projector,0,SEGMENT.virtualWidth(),0,(NUM_BANDS - 1)); um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { @@ -8397,10 +8394,14 @@ uint16_t mode_GEQLASER(void) { uint8_t heights[NUM_BANDS] = { 0 }; for (int i=0; i 1) { + ledColorTemp = color_fade(ledColor,32,true); + for (int y = 0; y <= heights[i]; y++) { - SEGMENT.drawLine(linex+(cols/NUM_BANDS)-1,rows-y-1,*projector,0,color_fade(ledColor,32,true)); + SEGMENT.drawLine(linex+(cols/NUM_BANDS)-1,rows-y-1,*projector,horizon,ledColorTemp,distance); // right side perspective } - for (int x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { - SEGMENT.drawLine(x, rows-heights[i]-2,*projector,0,color_fade(ledColor,128,true)); // top perspective + ledColorTemp = color_fade(ledColor,128,true); + + if (heights[i] < rows-horizon && (*projector <=linex || *projector >= linex+(cols/NUM_BANDS)-1)) { // draw if above horizon AND not directly under projector (special case later) + + for (uint_fast8_t x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { + SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,distance); // top perspective + } + } } } - for (int i=(NUM_BANDS - 1); i>split; i--) { // paint left vertical faces and top + for (int i=(NUM_BANDS - 1); i>split; i--) { // paint left vertical faces and top - RIGHT to LEFT uint16_t colorIndex = map(cols/NUM_BANDS*i, 0, cols-1, 0, 255); uint32_t ledColor = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0); @@ -8430,19 +8439,26 @@ uint16_t mode_GEQLASER(void) { if (heights[i] > 1) { - for (int y = 0; y <= heights[i]; y++) { - SEGMENT.drawLine(linex ,rows-y-1,*projector,0,color_fade(ledColor,32,true)); - } + ledColorTemp = color_fade(ledColor,32,true); - for (int x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { - SEGMENT.drawLine(x, rows-heights[i]-2,*projector,0,color_fade(ledColor,128,true)); // top perspective + for (uint_fast8_t y = 0; y <= heights[i]; y++) { + SEGMENT.drawLine(linex,rows-y-1,*projector,horizon,ledColorTemp,distance); // left side perspective } + ledColorTemp = color_fade(ledColor,128,true); + + if (heights[i] < rows-horizon && (*projector <=linex || *projector >= linex+(cols/NUM_BANDS)-1)) { // draw if above horizon AND not directly under projector (special case later) + + for (uint_fast8_t x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { + SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,distance); // top perspective + } + + } + } } - uint8_t frontBrightness = SEGMENT.custom2; for (int i=0; i 1) { + if (*projector >=linex && *projector <= linex+(cols/NUM_BANDS)-1) { // special case when top perspective is directly under the projector + + if (heights[i] > 1 && heights[i] < rows-horizon) { + + ledColorTemp = color_fade(ledColor,128,true); - if(frontBrightness > 250) { - // Full bright fronts, fills all front face. - for (int x=linex; x= 50) { - // Faded fronts, assumes border added later. - for (int x=linex+1; x 1) { + + ledColorTemp = color_fade(ledColor,SEGMENT.intensity,true); + + for (uint_fast8_t x=linex; x rows-horizon) { + + if (SEGMENT.intensity == 0) ledColorTemp = color_fade(ledColor,32,true); // match side fill if we're in blackout mode + + SEGMENT.drawLine(linex,rows-heights[i]-1,linex+(cols/NUM_BANDS)-1,rows-heights[i]-1,ledColorTemp); // top line to simulate hidden top fill + } - if(frontBrightness >= 50) { // TODO: other values too? not sure exactly when we want the border - // Border - SEGMENT.drawLine(linex, rows-1,linex,rows-heights[i]-1,ledColor); // left side line - SEGMENT.drawLine(linex+(cols/NUM_BANDS)-1,rows-1,linex+(cols/NUM_BANDS)-1,rows-heights[i]-1,ledColor); // right side line - SEGMENT.drawLine(linex, rows-heights[i]-2,linex+(cols/NUM_BANDS)-1,rows-heights[i]-2,ledColor); // top line - SEGMENT.drawLine(linex, rows-1,linex+(cols/NUM_BANDS)-1,rows-1,ledColor); // bottom line } } - } - return FRAMETIME; } -static const char _data_FX_MODE_GEQLASER[] PROGMEM = "GEQ Laser ☾@Fade Speed,,Bands,Fill Front,;;!;2f"; - +static const char _data_FX_MODE_GEQLASER[] PROGMEM = "GEQ 3D ☾@Speed,Front Fill,Horizon,Distance,Num Bands,,,;!,,Peaks;!;2f;sx=255,ix=255,c1=255,c2=255,c3=255,pal=11"; #endif // WLED_DISABLE_2D diff --git a/wled00/FX.h b/wled00/FX.h index 2143d98407..f6a5fd315e 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -687,8 +687,8 @@ typedef struct Segment { void move(uint8_t dir, uint8_t delta, bool wrap = false); void draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c); void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c); - void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c); - void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0)); } // automatic inline + void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, uint16_t d = UINT16_MAX); + void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, uint16_t d = UINT16_MAX) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), d); } // automatic inline void drawArc(uint16_t x0, uint16_t y0, uint16_t radius, uint32_t color, uint32_t fillColor = 0); void drawArc(uint16_t x0, uint16_t y0, uint16_t radius, CRGB color, CRGB fillColor = BLACK) { drawArc(x0, y0, radius, RGBW32(color.r,color.g,color.b,0), RGBW32(fillColor.r,fillColor.g,fillColor.b,0)); } // automatic inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0); diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 1d217ff071..83599c9702 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -582,7 +582,7 @@ void Segment::nscale8(uint8_t scale) { //WLEDMM: use fast types } //line function -void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) { +void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, uint16_t distance) { if (!isActive()) return; // not active const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); @@ -590,7 +590,7 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3 const int16_t dx = abs(x1-x0), sx = x0dy ? dx : -dy)/2, e2; - for (;;) { + for (uint_fast16_t d=0; d Date: Fri, 12 Jul 2024 22:12:59 -0400 Subject: [PATCH 12/27] Seems someone improved drawline() - fixed for this --- wled00/FX.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 82fd22251d..ff62e77555 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8413,7 +8413,7 @@ uint16_t mode_GEQLASER(void) { ledColorTemp = color_fade(ledColor,32,true); for (int y = 0; y <= heights[i]; y++) { - SEGMENT.drawLine(linex+(cols/NUM_BANDS)-1,rows-y-1,*projector,horizon,ledColorTemp,distance); // right side perspective + SEGMENT.drawLine(linex+(cols/NUM_BANDS)-1,rows-y-1,*projector,horizon,ledColorTemp,true,distance); // right side perspective } ledColorTemp = color_fade(ledColor,128,true); @@ -8421,7 +8421,7 @@ uint16_t mode_GEQLASER(void) { if (heights[i] < rows-horizon && (*projector <=linex || *projector >= linex+(cols/NUM_BANDS)-1)) { // draw if above horizon AND not directly under projector (special case later) for (uint_fast8_t x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { - SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,distance); // top perspective + SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,true,distance); // top perspective } } @@ -8442,7 +8442,7 @@ uint16_t mode_GEQLASER(void) { ledColorTemp = color_fade(ledColor,32,true); for (uint_fast8_t y = 0; y <= heights[i]; y++) { - SEGMENT.drawLine(linex,rows-y-1,*projector,horizon,ledColorTemp,distance); // left side perspective + SEGMENT.drawLine(linex,rows-y-1,*projector,horizon,ledColorTemp,true,distance); // left side perspective } ledColorTemp = color_fade(ledColor,128,true); @@ -8450,7 +8450,7 @@ uint16_t mode_GEQLASER(void) { if (heights[i] < rows-horizon && (*projector <=linex || *projector >= linex+(cols/NUM_BANDS)-1)) { // draw if above horizon AND not directly under projector (special case later) for (uint_fast8_t x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { - SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,distance); // top perspective + SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,true,distance); // top perspective } } @@ -8473,7 +8473,7 @@ uint16_t mode_GEQLASER(void) { ledColorTemp = color_fade(ledColor,128,true); for (uint_fast8_t x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { - SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,distance); // top perspective + SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,true,distance); // top perspective } } From 643f82daf061af4b0021082319c66d670e8330b9 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Fri, 12 Jul 2024 22:32:14 -0400 Subject: [PATCH 13/27] Don't use new soft drawline() option --- wled00/FX.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index ff62e77555..72ec8310e9 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8413,7 +8413,7 @@ uint16_t mode_GEQLASER(void) { ledColorTemp = color_fade(ledColor,32,true); for (int y = 0; y <= heights[i]; y++) { - SEGMENT.drawLine(linex+(cols/NUM_BANDS)-1,rows-y-1,*projector,horizon,ledColorTemp,true,distance); // right side perspective + SEGMENT.drawLine(linex+(cols/NUM_BANDS)-1,rows-y-1,*projector,horizon,ledColorTemp,false,distance); // right side perspective } ledColorTemp = color_fade(ledColor,128,true); @@ -8421,7 +8421,7 @@ uint16_t mode_GEQLASER(void) { if (heights[i] < rows-horizon && (*projector <=linex || *projector >= linex+(cols/NUM_BANDS)-1)) { // draw if above horizon AND not directly under projector (special case later) for (uint_fast8_t x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { - SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,true,distance); // top perspective + SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,false,distance); // top perspective } } @@ -8442,7 +8442,7 @@ uint16_t mode_GEQLASER(void) { ledColorTemp = color_fade(ledColor,32,true); for (uint_fast8_t y = 0; y <= heights[i]; y++) { - SEGMENT.drawLine(linex,rows-y-1,*projector,horizon,ledColorTemp,true,distance); // left side perspective + SEGMENT.drawLine(linex,rows-y-1,*projector,horizon,ledColorTemp,false,distance); // left side perspective } ledColorTemp = color_fade(ledColor,128,true); @@ -8450,7 +8450,7 @@ uint16_t mode_GEQLASER(void) { if (heights[i] < rows-horizon && (*projector <=linex || *projector >= linex+(cols/NUM_BANDS)-1)) { // draw if above horizon AND not directly under projector (special case later) for (uint_fast8_t x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { - SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,true,distance); // top perspective + SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,false,distance); // top perspective } } @@ -8473,7 +8473,7 @@ uint16_t mode_GEQLASER(void) { ledColorTemp = color_fade(ledColor,128,true); for (uint_fast8_t x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { - SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,true,distance); // top perspective + SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,false,distance); // top perspective } } From 75f0c7cb982424f03a02436ab6b67ffa0fc5d670 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Fri, 12 Jul 2024 22:34:37 -0400 Subject: [PATCH 14/27] Oopsy --- wled00/FX.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX.h b/wled00/FX.h index 97f2db27db..d838cd0f2e 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -695,7 +695,7 @@ typedef struct Segment { void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false); inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { fillCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); } void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false, uint16_t distance = UINT16_MAX); - inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false, uint16_t distance = UINT16_MAX)) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), soft, distance); } // automatic inline + inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false, uint16_t distance = UINT16_MAX) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), soft, distance); } // automatic inline void drawArc(uint16_t x0, uint16_t y0, uint16_t radius, uint32_t color, uint32_t fillColor = 0); inline void drawArc(uint16_t x0, uint16_t y0, uint16_t radius, CRGB color, CRGB fillColor = BLACK) { drawArc(x0, y0, radius, RGBW32(color.r,color.g,color.b,0), RGBW32(fillColor.r,fillColor.g,fillColor.b,0)); } // automatic inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0); From dddd1574ec7a63694d81c923f2df419a87909adb Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 14 Jul 2024 14:11:11 +0200 Subject: [PATCH 15/27] (experimental) setPixelColorXY_fast speedup by setPixelColorXY_fast * adding a _fast_ variant of SetPixelColorXY, that does not perform any error checking. * drawLine uses the fast setPixelColor variant (bresenham algo only) * for TESTING: segment option "reverse" switches back to the "original slow" code. surely needs some more optimization and improvements. First test on GEQ 3D shows 10%-30% speedup --- wled00/FX.h | 4 +++ wled00/FX_2Dfcn.cpp | 85 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/wled00/FX.h b/wled00/FX.h index d838cd0f2e..0d7be0bf81 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -666,6 +666,9 @@ typedef struct Segment { if (height == 0) return (x%width); // softhack007 avoid div/0 return (x%width) + (y%height) * width; } + + //void setPixelColorXY_fast(int x, int y,uint32_t c); // set relative pixel within segment with color - wrapper for _fast + void setPixelColorXY_fast(int x, int y,uint32_t c, uint32_t scaled_col, int cols, int rows); // set relative pixel within segment with color - faster, but no error checking!!! void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); } inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } @@ -981,6 +984,7 @@ class WS2812FX { // 96 bytes void setUpMatrix(), + setPixelColorXY_fast(int x, int y, uint32_t c), setPixelColorXY(int x, int y, uint32_t c); // outsmart the compiler :) by correctly overloading diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 9381030145..f3bd6ce173 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -176,6 +176,15 @@ void WS2812FX::setUpMatrix() { #endif } +// absolute matrix version of setPixelColor(), without error checking +void IRAM_ATTR WS2812FX::setPixelColorXY_fast(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally +{ + uint_fast16_t index = y * Segment::maxWidth + x; + if (index < customMappingSize) index = customMappingTable[index]; + if (index >= _length) return; + busses.setPixelColor(index, col); +} + // absolute matrix version of setPixelColor() void IRAM_ATTR_YN WS2812FX::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally { @@ -211,6 +220,67 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { // XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element) // WLEDMM Segment::XY()is declared inline, see FX.h + +// Simplified version of Segment::setPixelColorXY - without error checking. Does not support grouping or spacing +// * expects scaled color (final brightness) as additional input parameter, plus segment virtualWidth() and virtualHeight() +void IRAM_ATTR Segment::setPixelColorXY_fast(int x, int y, uint32_t col, uint32_t scaled_col, int cols, int rows) //WLEDMM +{ + // if (Segment::maxHeight==1) return; // not a matrix set-up + // const int_fast16_t cols = virtualWidth(); // WLEDMM optimization + // const int_fast16_t rows = virtualHeight(); + // if (x<0 || y<0 || x >= cols || y >= rows) return; // if pixel would fall out of virtual segment just exit + unsigned i = UINT_MAX; + bool sameColor = false; + if (ledsrgb) { // WLEDMM small optimization + //i = XY(x,y); + //i = (x%cols) + (y%rows) * cols; // avoid error checking done in XY() + i = x + y*cols; // avoid error checking done by XY() - be optimistic about ranges of x and y + CRGB fastled_col = CRGB(col); + if (ledsrgb[i] == fastled_col) sameColor = true; + else ledsrgb[i] = fastled_col; + } +#if 0 + // we are NOT doing brightness here - must be done by the calling function! + //uint32_t scaled_col = col; + uint8_t _bri_t = currentBri(on ? opacity : 0); + if (!_bri_t && !transitional) return; + if (_bri_t < 255) scaled_col = color_fade(col, _bri_t); + else scaled_col = col; +#endif + +#if 0 // this is still a dangerous optimization + if ((i < UINT_MAX) && sameColor && (call > 0) && (!transitional) && (ledsrgb[i] == CRGB(col)) && (_globalLeds == nullptr)) return; // WLEDMM looks like nothing to do (but we don't trust globalleds) +#endif + + // handle reverse and transpose + if (reverse ) x = cols - x - 1; + if (reverse_y) y = rows - y - 1; + if (transpose) std::swap(x,y); // swap X & Y if segment transposed + + // set the requested pixel + strip.setPixelColorXY_fast(start + x, startY + y, scaled_col); + bool simpleSegment = !mirror && !mirror_y; + //if (simpleSegment) return; // WLEDMM shortcut when no mirroring needed + + // handle mirroring + const int_fast16_t wid_ = stop - start; + const int_fast16_t hei_ = stopY - startY; + //if (x >= wid_ || y >= hei_) return; // if pixel would fall out of segment just exit - should never happen, because width() >= virtualWidth() + if (mirror) { //set the corresponding horizontally mirrored pixel + if (transpose) strip.setPixelColorXY_fast(start + x, startY + hei_ - y - 1, scaled_col); + else strip.setPixelColorXY_fast(start + wid_ - x - 1, startY + y, scaled_col); + } + if (mirror_y) { //set the corresponding vertically mirrored pixel + if (transpose) strip.setPixelColorXY_fast(start + wid_ - x - 1, startY + y, scaled_col); + else strip.setPixelColorXY_fast(start + x, startY + hei_ - y - 1, scaled_col); + } + if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel + strip.setPixelColorXY_fast(wid_ - x - 1, hei_ - y - 1, scaled_col); + } +} + + +// normal Segment::setPixelColorXY with error checking, and support for grouping / spacing void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally { if (Segment::maxHeight==1) return; // not a matrix set-up @@ -608,6 +678,7 @@ void Segment::nscale8(uint8_t scale) { //WLEDMM: use fast types //line function void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft, uint16_t distance) { if (!isActive()) return; // not active + // if (Segment::maxHeight==1) return; // not a matrix set-up const int cols = virtualWidth(); const int rows = virtualHeight(); if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return; @@ -621,6 +692,16 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3 return; } + // WLEDMM shortcut when no grouping/spacing used + bool simpleSegment = !reverse && (grouping == 1) && (spacing == 0); // !reverse is just for back-to-back testing against "slow" functions + uint32_t scaled_col = c; + if (simpleSegment) { + // segment brightness must be pre-calculated for the "fast" setPixelColorXY variant! + uint8_t _bri_t = currentBri(on ? opacity : 0); + if (!_bri_t && !transitional) return; + if (_bri_t < 255) scaled_col = color_fade(c, _bri_t); + } + if (soft) { // Xiaolin Wu’s algorithm const bool steep = dy > dx; @@ -651,7 +732,9 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3 // Bresenham's algorithm int err = (dx>dy ? dx : -dy)/2; // error direction for (uint_fast16_t d=0; d= cols || y0 >= rows) break; // WLEDMM we hit the edge - should never happen + if (simpleSegment) setPixelColorXY_fast(x0, y0, c, scaled_col, cols, rows); + else setPixelColorXY(x0, y0, c); if (x0==x1 && y0==y1) break; int e2 = err; if (e2 >-dx) { err -= dy; x0 += sx; } From 6aaf7a908dd4703d04dc5b48f67cff11bf9b729c Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 14 Jul 2024 23:49:37 +0200 Subject: [PATCH 16/27] more sPC optimizations * made setPixelColorXY_fast private * optimized Segment::fill() to use setPixelColorXY_fast * "bar" 1D expand optimization --- wled00/FX.h | 2 +- wled00/FX_2Dfcn.cpp | 21 +++++++++++---------- wled00/FX_fcn.cpp | 25 ++++++++++++++++++++----- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 0d7be0bf81..fbc8473785 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -432,6 +432,7 @@ typedef struct Segment { }; size_t _dataLen; // WLEDMM uint16_t is too small static size_t _usedSegmentData; // WLEDMM uint16_t is too small + void setPixelColorXY_fast(int x, int y,uint32_t c, uint32_t scaled_col, int cols, int rows); // set relative pixel within segment with color - faster, but no error checking!!! // perhaps this should be per segment, not static static CRGBPalette16 _currentPalette; // palette used for current effect (includes transition, used in color_from_palette()) @@ -668,7 +669,6 @@ typedef struct Segment { } //void setPixelColorXY_fast(int x, int y,uint32_t c); // set relative pixel within segment with color - wrapper for _fast - void setPixelColorXY_fast(int x, int y,uint32_t c, uint32_t scaled_col, int cols, int rows); // set relative pixel within segment with color - faster, but no error checking!!! void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); } inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index f3bd6ce173..95786b8dd9 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -260,7 +260,7 @@ void IRAM_ATTR Segment::setPixelColorXY_fast(int x, int y, uint32_t col, uint32_ // set the requested pixel strip.setPixelColorXY_fast(start + x, startY + y, scaled_col); bool simpleSegment = !mirror && !mirror_y; - //if (simpleSegment) return; // WLEDMM shortcut when no mirroring needed + if (simpleSegment) return; // WLEDMM shortcut when no mirroring needed // handle mirroring const int_fast16_t wid_ = stop - start; @@ -683,15 +683,6 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3 const int rows = virtualHeight(); if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return; - const int dx = abs(x1-x0), sx = x0 dx; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 34e59e40f2..fae5199e54 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -920,7 +920,8 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATT case M12_pBar: // expand 1D effect vertically or have it play on virtual strips if (vStrip>0) setPixelColorXY(vStrip - 1, vH - i - 1, col); - else for (int x = 0; x < vW; x++) setPixelColorXY(x, vH - i - 1, col); + //else for (int x = 0; x < vW; x++) setPixelColorXY(x, vH - i - 1, col); + else drawLine(0, vH - i - 1, vW-1, vH - i - 1, col, false); // WLEDMM draw line instead of plotting each pixel break; case M12_pArc: // expand in circular fashion from center @@ -1315,15 +1316,29 @@ void Segment::refreshLightCapabilities() { } /* - * Fills segment with color + * Fills segment with color - WLEDMM using faster sPC if possible */ void Segment::fill(uint32_t c) { if (!isActive()) return; // not active const uint_fast16_t cols = is2D() ? virtualWidth() : virtualLength(); // WLEDMM use fast int types const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D - for(uint_fast16_t y = 0; y < rows; y++) for (uint_fast16_t x = 0; x < cols; x++) { - if (is2D()) setPixelColorXY((uint16_t)x, (uint16_t)y, c); - else setPixelColor((uint16_t)x, c); + + if (is2D()) { + // pre-calculate scaled color + uint32_t scaled_col = c; + bool simpleSegment = !reverse && (grouping == 1) && (spacing == 0); // !reverse is just for back-to-back testing against "slow" functions + if (simpleSegment) { + uint8_t _bri_t = currentBri(on ? opacity : 0); + if (!_bri_t && !transitional) return; + if (_bri_t < 255) scaled_col = color_fade(c, _bri_t); + } + // fill 2D segment + for(int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { + if (simpleSegment) setPixelColorXY_fast(x, y, c, scaled_col, cols, rows); + else setPixelColorXY(x, y, c); + } + } else { // fill 1D strip + for (int x = 0; x < cols; x++) setPixelColor(x, c); } } From 3857862e0384c2f4e71a3cbd177b4c644a6bacc3 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Sun, 14 Jul 2024 22:32:42 -0400 Subject: [PATCH 17/27] Optimized side-wall drawing Only draw what will be visible. This is a pretty solid speed increase when the adjacent bar is close in height, or if it's taller and would occlude it anyway, it'll be skipped. --- wled00/FX.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 72ec8310e9..be19f00e8e 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8412,7 +8412,7 @@ uint16_t mode_GEQLASER(void) { ledColorTemp = color_fade(ledColor,32,true); - for (int y = 0; y <= heights[i]; y++) { + for (int y = heights[i+1]; y <= heights[i]; y++) { // don't bother drawing what we'll hide anyway SEGMENT.drawLine(linex+(cols/NUM_BANDS)-1,rows-y-1,*projector,horizon,ledColorTemp,false,distance); // right side perspective } @@ -8441,7 +8441,7 @@ uint16_t mode_GEQLASER(void) { ledColorTemp = color_fade(ledColor,32,true); - for (uint_fast8_t y = 0; y <= heights[i]; y++) { + for (uint_fast8_t y = heights[i-1]; y <= heights[i]; y++) { // don't bother drawing what we'll hide anyway SEGMENT.drawLine(linex,rows-y-1,*projector,horizon,ledColorTemp,false,distance); // left side perspective } From 4f6eb8b1618a8f1f929eeaab76d291c8dd4490d4 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 15 Jul 2024 13:24:18 +0200 Subject: [PATCH 18/27] prevent array bounds violation + don't use dynamic array for heights[] - dynamic arrays are using malloc() -> heap frag. --- wled00/FX.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 599dea0d09..5b367e84ba 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8420,7 +8420,7 @@ uint16_t mode_GEQLASER(void) { } uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; - uint8_t heights[NUM_BANDS] = { 0 }; + uint8_t heights[NUM_GEQ_CHANNELS] = { 0 }; for (int i=0; i0) ? heights[i-1] : 0; y <= heights[i]; y++) { // don't bother drawing what we'll hide anyway SEGMENT.drawLine(linex,rows-y-1,*projector,horizon,ledColorTemp,false,distance); // left side perspective } From 5401328b6e69182cdbf879c4dd81d66e8bdc6d00 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 15 Jul 2024 14:05:19 +0200 Subject: [PATCH 19/27] loop optimizations * made cols/rows integer, to avoid that "cols-x" wraps around * pre-compute some expressions that don't depend on loop counters --> wins us 1-4 fps on my 72x72 test setup --- wled00/FX.cpp | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 5b367e84ba..8d523ffc77 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8408,8 +8408,8 @@ uint16_t mode_GEQLASER(void) { SEGMENT.fill(BLACK); const int NUM_BANDS = map(SEGMENT.custom3, 0, 31, 1, 16); // custom3 is 0..31 - const uint16_t cols = SEGMENT.virtualWidth(); - const uint16_t rows = SEGMENT.virtualHeight(); + const int cols = SEGMENT.virtualWidth(); + const int rows = SEGMENT.virtualHeight(); uint32_t ledColorTemp; uint_fast8_t split = map(*projector,0,SEGMENT.virtualWidth(),0,(NUM_BANDS - 1)); @@ -8421,9 +8421,8 @@ uint16_t mode_GEQLASER(void) { uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; uint8_t heights[NUM_GEQ_CHANNELS] = { 0 }; - for (int i=0; i 1) { ledColorTemp = color_fade(ledColor,32,true); + int pPos = linex+(cols/NUM_BANDS)-1; for (int y = (i= linex+(cols/NUM_BANDS)-1)) { // draw if above horizon AND not directly under projector (special case later) + if (heights[i] < rows-horizon && (*projector <=linex || *projector >= pPos)) { // draw if above horizon AND not directly under projector (special case later) - for (uint_fast8_t x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { + for (uint_fast8_t x=linex; x<=pPos;x++) { SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,false,distance); // top perspective } @@ -8465,6 +8465,7 @@ uint16_t mode_GEQLASER(void) { uint32_t ledColor = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0); int linex = i*(cols/NUM_BANDS); + int pPos = linex+(cols/NUM_BANDS)-1; if (heights[i] > 1) { @@ -8476,9 +8477,9 @@ uint16_t mode_GEQLASER(void) { ledColorTemp = color_fade(ledColor,128,true); - if (heights[i] < rows-horizon && (*projector <=linex || *projector >= linex+(cols/NUM_BANDS)-1)) { // draw if above horizon AND not directly under projector (special case later) + if (heights[i] < rows-horizon && (*projector <=linex || *projector >= pPos)) { // draw if above horizon AND not directly under projector (special case later) - for (uint_fast8_t x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { + for (uint_fast8_t x=linex; x<=pPos;x++) { SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,false,distance); // top perspective } @@ -8494,14 +8495,16 @@ uint16_t mode_GEQLASER(void) { uint32_t ledColor = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0); int linex = i*(cols/NUM_BANDS); + int pPos = linex+(cols/NUM_BANDS)-1; + int pPos1 = linex+(cols/NUM_BANDS); - if (*projector >=linex && *projector <= linex+(cols/NUM_BANDS)-1) { // special case when top perspective is directly under the projector + if (*projector >=linex && *projector <= pPos) { // special case when top perspective is directly under the projector if (heights[i] > 1 && heights[i] < rows-horizon) { ledColorTemp = color_fade(ledColor,128,true); - for (uint_fast8_t x=linex; x<=linex+(cols/NUM_BANDS)-1;x++) { + for (uint_fast8_t x=linex; x<=pPos;x++) { SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,false,distance); // top perspective } @@ -8513,7 +8516,7 @@ uint16_t mode_GEQLASER(void) { ledColorTemp = color_fade(ledColor,SEGMENT.intensity,true); - for (uint_fast8_t x=linex; x