Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e2f2354
Add @TroyHacks 3D GEQ
netmindz Jul 10, 2024
e452d4d
Add @TroyHacks 3D GEQ
netmindz Jul 10, 2024
2d7e416
;
netmindz Jul 10, 2024
45f288a
3D GEQ - make front brightness a control
netmindz Jul 10, 2024
f6cd28c
3D GEQ - customisable number of bands
netmindz Jul 10, 2024
cb3d06b
3D GEQ - variable front brightness
netmindz Jul 10, 2024
3fd8078
3D GEQ - customisable number of bands
netmindz Jul 10, 2024
bda4bbe
3D GEQ - fade speed
netmindz Jul 10, 2024
8f4380b
Rename 3D GEQ to GEQ Laser
netmindz Jul 10, 2024
aed861d
GEQ Laser use SEGMENT.data
netmindz Jul 10, 2024
ce8f01b
Bugfixed GEQ 3D
troyhacks Jul 13, 2024
52130e1
Merge branch 'mdev' into 3DGEQ
troyhacks Jul 13, 2024
4c8d500
Seems someone improved drawline() - fixed for this
troyhacks Jul 13, 2024
643f82d
Don't use new soft drawline() option
troyhacks Jul 13, 2024
75f0c7c
Oopsy
troyhacks Jul 13, 2024
dddd157
(experimental) setPixelColorXY_fast speedup by setPixelColorXY_fast
softhack007 Jul 14, 2024
6aaf7a9
more sPC optimizations
softhack007 Jul 14, 2024
3857862
Optimized side-wall drawing
troyhacks Jul 15, 2024
6d16151
Merge branch 'mdev' into 3DGEQ
softhack007 Jul 15, 2024
4f6eb8b
prevent array bounds violation
softhack007 Jul 15, 2024
5401328
loop optimizations
softhack007 Jul 15, 2024
54f9d4c
Borders checkbox for @netmindz
troyhacks Jul 15, 2024
98c15d7
Borders checkbox
troyhacks Jul 15, 2024
9610408
speedup for pinwheel mapping
softhack007 Jul 15, 2024
6565e71
GEQ speedup
softhack007 Jul 15, 2024
e3d27de
better handling of perspective
softhack007 Jul 16, 2024
96584c6
better rounding
softhack007 Jul 16, 2024
e3b4a08
soft light option
softhack007 Jul 16, 2024
1f14673
workaround for "cannot read properties of undefined. reading 'split'
softhack007 Jul 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 169 additions & 2 deletions wled00/FX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7879,7 +7879,7 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.

uint16_t lastBandHeight = 0; // WLEDMM: for smoothing out bars

//WLEDMM: evenly ditribut bands
//WLEDMM: evenly ditribute bands
float bandwidth = (float)cols / NUM_BANDS;
float remaining = bandwidth;
uint8_t band = 0;
Expand Down Expand Up @@ -7914,13 +7914,17 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
if (barHeight > previousBarHeight[x]) previousBarHeight[x] = barHeight; //drive the peak up

uint32_t ledColor = BLACK;
if ((! SEGMENT.check1) && (barHeight > 0)) { // use faster drawLine when single-color bars are needed
ledColor = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0);
SEGMENT.drawLine(int(x), max(0,int(rows)-barHeight-1), int(x), int(rows-1), ledColor, false); // max(0, ...) to prevent negative Y
} else {
for (int y=0; y < barHeight; y++) {
if (SEGMENT.check1) //color_vertical / color bars toggle
colorIndex = map(y, 0, rows-1, 0, 255);

ledColor = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0);
SEGMENT.setPixelColorXY(x, rows-1 - y, ledColor);
}
} }
if ((SEGMENT.intensity < 255) && (previousBarHeight[x] > 0) && (previousBarHeight[x] < rows)) // WLEDMM avoid "overshooting" into other segments
SEGMENT.setPixelColorXY(x, rows - previousBarHeight[x], (SEGCOLOR(2) != BLACK) ? SEGCOLOR(2) : ledColor);

Expand Down Expand Up @@ -8384,6 +8388,166 @@ uint16_t mode_2Dwavingcell() {
}
static const char _data_FX_MODE_2DWAVINGCELL[] PROGMEM = "Waving Cell@!,,Amplitude 1,Amplitude 2,Amplitude 3;;!;2";

uint16_t mode_GEQLASER(void) {

// Author: @TroyHacks

const size_t dataSize = sizeof(uint16_t);
if (!SEGENV.allocateData(dataSize * 2)) return mode_static(); //allocation failed

uint16_t *projector = reinterpret_cast<uint16_t*>(SEGENV.data);
uint16_t *projector_dir = reinterpret_cast<uint16_t*>(SEGENV.data + dataSize);

if (SEGENV.call == 0) {
*projector = 0;
*projector_dir = 0;
SEGMENT.setUpLeds(); // WLEDMM use lossless getPixelColor()
SEGMENT.fill(BLACK);
} else {
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;
}

SEGMENT.fill(BLACK);

const int NUM_BANDS = map(SEGMENT.custom3, 0, 31, 1, 16); // custom3 is 0..31
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));

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[NUM_GEQ_CHANNELS] = { 0 };
for (int i=0; i<NUM_BANDS; i++) {
heights[i] = map8(fftResult[i],0,rows*0.85); // cache fftResult[] as data might be updated in parallel by the audioreactive core
}

uint16_t horizon = map(SEGMENT.custom1,0,255,rows-1,0);
uint8_t depth = SEGMENT.custom2; // depth of perspective. 255 = infinite ("laser")

for (int i=0; i<=split; i++) { // paint right vertical faces and top - LEFT to RIGHT

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/NUM_BANDS);

if (heights[i] > 1) {

ledColorTemp = color_fade(ledColor,32,true);
int pPos = linex+(cols/NUM_BANDS)-1;

for (int y = (i<NUM_BANDS-1) ? heights[i+1] : 0; y <= heights[i]; y++) { // don't bother drawing what we'll hide anyway
SEGMENT.drawLine(pPos,rows-y-1,*projector,horizon,ledColorTemp,false,depth); // right side perspective
}

ledColorTemp = color_fade(ledColor,128,true);

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<=pPos;x++) {
bool doSoft = SEGMENT.check2 && ((x==linex) || (x==pPos)); // only first and last line need AA
SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,doSoft,depth); // top perspective
}

}

}

}

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);

int linex = i*(cols/NUM_BANDS);
int pPos = linex+(cols/NUM_BANDS)-1;

if (heights[i] > 1) {

ledColorTemp = color_fade(ledColor,32,true);

for (uint_fast8_t y = (i>0) ? 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,depth); // left side perspective
}

ledColorTemp = color_fade(ledColor,128,true);

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<=pPos;x++) {
bool doSoft = SEGMENT.check2 && ((x==linex) || (x==pPos)); // only first and last line need AA
SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,doSoft,depth); // top perspective
}

}

}

}

for (int i=0; i<NUM_BANDS; i++) {

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/NUM_BANDS);
int pPos = linex+(cols/NUM_BANDS)-1;
int pPos1 = linex+(cols/NUM_BANDS);

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<=pPos;x++) {
SEGMENT.drawLine(x,rows-heights[i]-2,*projector,horizon,ledColorTemp,false,depth); // top perspective
}

}

}

if (heights[i] > 1) {

ledColorTemp = color_fade(ledColor,SEGMENT.intensity,true);

for (uint_fast8_t x=linex; x<pPos1;x++) {
SEGMENT.drawLine(x,rows-1,x,rows-heights[i]-1,ledColorTemp); // front fill
}

if (!SEGMENT.check1 && heights[i] > 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 (SEGMENT.check1) {
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 3D ☾@Speed,Front Fill,Horizon,Depth,Num Bands,Borders,Soft,;!,,Peaks;!;2f;sx=255,ix=255,c1=255,c2=255,c3=255,pal=11";

#endif // WLED_DISABLE_2D

Expand Down Expand Up @@ -8630,6 +8794,9 @@ void WS2812FX::setupEffectData() {
addEffect(FX_MODE_2DWAVINGCELL, &mode_2Dwavingcell, _data_FX_MODE_2DWAVINGCELL);

addEffect(FX_MODE_2DAKEMI, &mode_2DAkemi, _data_FX_MODE_2DAKEMI); // audio

addEffect(FX_MODE_GEQLASER, &mode_GEQLASER, _data_FX_MODE_GEQLASER); // audio

#endif // WLED_DISABLE_2D

}
11 changes: 8 additions & 3 deletions wled00/FX.h
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,9 @@ 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_GEQLASER 195 // WLED-MM GEQ Laser

#define MODE_COUNT 195
#define MODE_COUNT 196

typedef enum mapping1D2D {
M12_Pixels = 0,
Expand Down Expand Up @@ -431,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())
Expand Down Expand Up @@ -665,6 +667,8 @@ 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(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)); }
Expand Down Expand Up @@ -693,8 +697,8 @@ typedef struct Segment {
inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { drawCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); }
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);
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), soft); } // automatic inline
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false, uint8_t depth = UINT8_MAX);
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false, uint8_t depth = UINT8_MAX) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), soft, depth); } // 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);
Expand Down Expand Up @@ -980,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
Expand Down
104 changes: 101 additions & 3 deletions wled00/FX_2Dfcn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -606,18 +676,44 @@ 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) {
void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft, uint8_t depth) {
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;

// 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);
}

// WLEDMM shorten line according to depth
if (depth < UINT8_MAX) {
if (depth == 0) return; // nothing to paint
if (depth<2) {x1 = x0; y1=y0; } // single pixel
else { // shorten line
x0 *=2; y0 *=2; // we do everything "*2" for better rounding
int dx1 = ((int(2*x1) - int(x0)) * int(depth)) / 255; // X distance, scaled down by depth
int dy1 = ((int(2*y1) - int(y0)) * int(depth)) / 255; // Y distance, scaled down by depth
x1 = (x0 + dx1 +1) / 2;
y1 = (y0 + dy1 +1) / 2;
x0 /=2; y0 /=2;
}
}

const int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1; // x distance & step
const int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1; // y distance & step

// single pixel (line length == 0)
if (dx+dy == 0) {
setPixelColorXY(x0, y0, c);
if (simpleSegment) setPixelColorXY_fast(x0, y0, c, scaled_col, cols, rows);
else setPixelColorXY(x0, y0, c);
return;
}

Expand Down Expand Up @@ -651,7 +747,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 (;;) {
setPixelColorXY(x0, y0, c);
// if (x0 >= 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; }
Expand Down
Loading