From b4d65258996632a8abb810f813b8fdbdaae11981 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Fri, 20 May 2022 19:35:22 +0200 Subject: [PATCH] Added new 2D effects: - 2D Floating Blobs - 2D Ghost Rider Crash fix for 2D Game of Life. Added support function fill_circle() & wu_pixel(). Removed obsolete debug strings. --- wled00/FX.cpp | 197 +++++++++++++++++++++++++++++++++++++++++++- wled00/FX.h | 16 +++- wled00/FX_2Dfcn.cpp | 29 ++++++- wled00/FX_fcn.cpp | 4 +- wled00/mqtt.cpp | 1 - wled00/presets.cpp | 2 - wled00/util.cpp | 3 + wled00/ws.cpp | 4 +- 8 files changed, 242 insertions(+), 14 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index bccce5f4..5bd29cb5 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -4562,11 +4562,11 @@ uint16_t WS2812FX::mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired if (!SEGENV.allocateData(dataSize*2)) return mode_static(); //allocation failed CRGB *leds = reinterpret_cast(SEGENV.data); - CRGB *prevLeds = reinterpret_cast(SEGENV.data) + dataSize; + CRGB *prevLeds = reinterpret_cast(SEGENV.data + dataSize); CRGB backgroundColor = SEGCOLOR(1); - if (SEGENV.call == 0 || now - resetMillis > 20000) { + if (SEGENV.call == 0 || now - resetMillis > 5000) { resetMillis = now; random16_set_seed(now); //seed the random generator @@ -4632,7 +4632,8 @@ uint16_t WS2812FX::mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired } //x,y // calculate CRC16 of leds[] - uint16_t crc = crc16((const unsigned char*)leds, dataSize); + uint16_t crc = crc16((const unsigned char*)leds, dataSize-1); + // check if we had same CRC and reset if needed if (!(crc == SEGENV.aux0 || crc == SEGENV.aux1)) resetMillis = now; //if no repetition avoid reset // remeber last two @@ -5533,3 +5534,193 @@ uint16_t WS2812FX::mode_2Dcrazybees(void) { setPixels(leds); return FRAMETIME; } + +///////////////////////// +// 2D Ghost Rider // +///////////////////////// +//// Ghost Rider by stepko (c)2021 [https://editor.soulmatelights.com/gallery/716-ghost-rider], adapted by Blaz Kristan +#define LIGHTERS_AM 64 // max lighters (adequate for 32x32 matrix) +uint16_t WS2812FX::mode_2Dghostrider(void) { + if (!isMatrix) return mode_static(); // not a 2D set-up + + uint16_t width = SEGMENT.virtualWidth(); + uint16_t height = SEGMENT.virtualHeight(); + uint16_t dataSize = sizeof(CRGB) * width * height; + + typedef struct Lighter { + int gPosX; + int gPosY; + uint16_t gAngle; + int8_t angleSpeed; + uint16_t lightersPosX[LIGHTERS_AM]; + uint16_t lightersPosY[LIGHTERS_AM]; + uint16_t Angle[LIGHTERS_AM]; + uint16_t time[LIGHTERS_AM]; + bool reg[LIGHTERS_AM]; + int8_t Vspeed; + } lighter_t; + + if (!SEGENV.allocateData(dataSize + sizeof(lighter_t))) return mode_static(); //allocation failed + CRGB *leds = reinterpret_cast(SEGENV.data); + lighter_t *lighter = reinterpret_cast(SEGENV.data + dataSize); + + if (SEGENV.call == 0) { + fill_solid(leds, CRGB::Black); + randomSeed(now); + lighter->angleSpeed = random(-10, 10); + lighter->Vspeed = 5; + lighter->gPosX = (width/2) * 10; + lighter->gPosY = (height/2) * 10; + for (byte i = 0; i < LIGHTERS_AM; i++) { + lighter->lightersPosX[i] = lighter->gPosX; + lighter->lightersPosY[i] = lighter->gPosY + i; + lighter->time[i] = i * 2; + } + } + + fadeToBlackBy(leds, SEGMENT.speed>>2); + + CRGB color = CRGB::White; + wu_pixel(leds, lighter->gPosX * 256 / 10, lighter->gPosY * 256 / 10, color); + + lighter->gPosX += lighter->Vspeed * sin_t(radians(lighter->gAngle)); + lighter->gPosY += lighter->Vspeed * cos_t(radians(lighter->gAngle)); + lighter->gAngle += lighter->angleSpeed; + if (lighter->gPosX < 0) lighter->gPosX = (width - 1) * 10; + if (lighter->gPosX > (width - 1) * 10) lighter->gPosX = 0; + if (lighter->gPosY < 0) lighter->gPosY = (height - 1) * 10; + if (lighter->gPosY > (height - 1) * 10) lighter->gPosY = 0; + for (byte i = 0; i < LIGHTERS_AM; i++) { + lighter->time[i] += random8(5, 20); + if (lighter->time[i] >= 255 || + (lighter->lightersPosX[i] <= 0) || + (lighter->lightersPosX[i] >= (width - 1) * 10) || + (lighter->lightersPosY[i] <= 0) || + (lighter->lightersPosY[i] >= (height - 1) * 10)) { + lighter->reg[i] = true; + } + if (lighter->reg[i]) { + lighter->lightersPosY[i] = lighter->gPosY; + lighter->lightersPosX[i] = lighter->gPosX; + lighter->Angle[i] = lighter->gAngle + random(-10, 10); + lighter->time[i] = 0; + lighter->reg[i] = false; + } else { + lighter->lightersPosX[i] += -7 * sin_t(radians(lighter->Angle[i])); + lighter->lightersPosY[i] += -7 * cos_t(radians(lighter->Angle[i])); + } + wu_pixel(leds, lighter->lightersPosX[i] * 256 / 10, lighter->lightersPosY[i] * 256 / 10, ColorFromPalette(currentPalette, (256 - lighter->time[i]))); + } + blur2d(leds, SEGMENT.intensity>>3); + + setPixels(leds); + return FRAMETIME; +} + +//////////////////////////// +// 2D Floating Blobs // +//////////////////////////// +//// Floating Blobs by stepko (c)2021 [https://editor.soulmatelights.com/gallery/573-blobs], adapted by Blaz Kristan +#define MAX_BLOBS 8 +uint16_t WS2812FX::mode_2Dfloatingblobs(void) { + if (!isMatrix) return mode_static(); // not a 2D set-up + + uint16_t width = SEGMENT.virtualWidth(); + uint16_t height = SEGMENT.virtualHeight(); + uint16_t dataSize = sizeof(CRGB) * width * height; + + typedef struct Blob { + float x[MAX_BLOBS], y[MAX_BLOBS]; + float sX[MAX_BLOBS], sY[MAX_BLOBS]; // speed + float r[MAX_BLOBS]; + bool grow[MAX_BLOBS]; + byte color[MAX_BLOBS]; + } blob_t; + + uint8_t Amount = (SEGMENT.intensity>>5) + 1; // NOTE: be sure to update MAX_BLOBS if you change this + + if (!SEGENV.allocateData(dataSize + sizeof(blob_t))) return mode_static(); //allocation failed + CRGB *leds = reinterpret_cast(SEGENV.data); + blob_t *blob = reinterpret_cast(SEGENV.data + dataSize); + + if (SEGENV.call == 0) { + fill_solid(leds, CRGB::Black); + DEBUG_PRINT(F("sizeof blob ")); + DEBUG_PRINTLN(sizeof(blob_t)); + DEBUG_PRINTLN(F("Init begin.")); + for (byte i = 0; i < MAX_BLOBS; i++) { + blob->r[i] = random8(1, width/4); + blob->sX[i] = (float) random8(5, 11) / (float)(257 - SEGMENT.speed) / 4.0; // speed x + blob->sY[i] = (float) random8(5, 11) / (float)(257 - SEGMENT.speed) / 4.0; // speed y + blob->x[i] = random8(0, width-1); + blob->y[i] = random8(0, height-1); + blob->color[i] = random8(); + blob->grow[i] = (blob->r[i] < 1.); + if (blob->sX[i] == 0) blob->sX[i] = 1; + if (blob->sY[i] == 0) blob->sY[i] = 1; + } + DEBUG_PRINTLN(F("Init done.")); + } + + fadeToBlackBy(leds, 20); + + // Bounce balls around + for (byte i = 0; i < Amount; i++) { + // change radius if needed + if (blob->grow[i]) { + // enlarge radius until it is >= 4 + blob->r[i] += (fabs(blob->sX[i]) > fabs(blob->sY[i]) ? fabs(blob->sX[i]) : fabs(blob->sY[i])) * 0.05; + if (blob->r[i] >= MIN(width/4,4.)) { + blob->grow[i] = false; + } + } else { + // reduce radius until it is < 1 + blob->r[i] -= (fabs(blob->sX[i]) > fabs(blob->sY[i]) ? fabs(blob->sX[i]) : fabs(blob->sY[i])) * 0.05; + if (blob->r[i] < 1.) { + blob->grow[i] = true; + blob->color[i] = random(0, 255); + } + } + if (blob->r[i] > 1.) + fill_circle(leds, blob->y[i], blob->x[i], blob->r[i], ColorFromPalette(currentPalette, blob->color[i])); + else + leds[XY(blob->y[i], blob->x[i])] += ColorFromPalette(currentPalette, blob->color[i]); + //---------------------- + if (blob->x[i] + blob->r[i] >= width - 1) + blob->x[i] += (blob->sX[i] * ((width - 1 - blob->x[i]) / blob->r[i] + 0.005)); + else if (blob->x[i] - blob->r[i] <= 0) + blob->x[i] += (blob->sX[i] * (blob->x[i] / blob->r[i] + 0.005)); + else + blob->x[i] += blob->sX[i]; + //----------------------- + if (blob->y[i] + blob->r[i] >= height - 1) + blob->y[i] += (blob->sY[i] * ((height - 1 - blob->y[i]) / blob->r[i] + 0.005)); + else if (blob->y[i] - blob->r[i] <= 0) + blob->y[i] += (blob->sY[i] * (blob->y[i] / blob->r[i] + 0.005)); + else + blob->y[i] += blob->sY[i]; + //------------------------ + if (blob->x[i] < 0.01) { + blob->sX[i] = (float) random8(5, 11) / (257 - SEGMENT.speed) / 4.0; + blob->x[i] = 0.01; + } else if (blob->x[i] > width - 1.01) { + blob->sX[i] = (float) random8(5, 11) / (257 - SEGMENT.speed) / 4.0; + blob->sX[i] = -blob->sX[i]; + blob->x[i] = width - 1.01; + } + //---------------------- + if (blob->y[i] < 0.01) { + blob->sY[i] = (float) random8(5, 11) / (257 - SEGMENT.speed) / 4.0; + blob->y[i] = 0.01; + } else if (blob->y[i] > height - 1.01) { + blob->sY[i] = (float) random8(5, 11) / (257 - SEGMENT.speed) / 4.0; + blob->sY[i] = -blob->sY[i]; + blob->y[i] = height - 1.01; + } + } + blur2d(leds, 16); + + setPixels(leds); + return FRAMETIME; +} +#undef MAX_BLOBS diff --git a/wled00/FX.h b/wled00/FX.h index 1f0e5e81..864b0d24 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -263,8 +263,10 @@ #define FX_MODE_WAVERLY 140 #define FX_MODE_SPACESHIPS 141 #define FX_MODE_CRAZYBEES 142 +#define FX_MODE_GHOST_RIDER 143 +#define FX_MODE_BLOBS 144 -#define MODE_COUNT 143 +#define MODE_COUNT 145 class WS2812FX { @@ -654,6 +656,8 @@ class WS2812FX { _mode[FX_MODE_AKEMI] = &WS2812FX::mode_2DAkemi; _mode[FX_MODE_SPACESHIPS] = &WS2812FX::mode_2Dspaceships; _mode[FX_MODE_CRAZYBEES] = &WS2812FX::mode_2Dcrazybees; + _mode[FX_MODE_GHOST_RIDER] = &WS2812FX::mode_2Dghostrider; + _mode[FX_MODE_BLOBS] = &WS2812FX::mode_2Dfloatingblobs; _brightness = DEFAULT_BRIGHTNESS; currentPalette = CRGBPalette16(CRGB::Black); @@ -916,11 +920,13 @@ class WS2812FX { blurCol(uint16_t col, fract8 blur_amount, CRGB* leds=nullptr), moveX(CRGB *leds, int8_t delta), moveY(CRGB *leds, int8_t delta), - fill_solid(CRGB* leds, const struct CRGB& color), + fill_solid(CRGB* leds, CRGB c), + fill_circle(CRGB* leds, uint16_t cx, uint16_t cy, uint8_t radius, CRGB c), fadeToBlackBy(CRGB* leds, uint8_t fadeBy), nscale8(CRGB* leds, uint8_t scale), setPixels(CRGB* leds), - drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, CRGB *leds = nullptr); + drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, CRGB *leds = nullptr), + wu_pixel(CRGB *leds, uint32_t x, uint32_t y, CRGB c); inline void setPixelColorXY(uint16_t x, uint16_t y, uint32_t c) { setPixelColorXY(x, y, byte(c>>16), byte(c>>8), byte(c), byte(c>>24)); } inline void setPixelColorXY(uint16_t x, uint16_t y, CRGB c) { setPixelColorXY(x, y, c.red, c.green, c.blue); } @@ -959,7 +965,9 @@ class WS2812FX { mode_2DWaverly(void), mode_2DAkemi(void), mode_2Dspaceships(void), - mode_2Dcrazybees(void); + mode_2Dcrazybees(void), + mode_2Dghostrider(void), + mode_2Dfloatingblobs(void); // end 2D support diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 7e7803b2..3fd348dd 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -305,7 +305,7 @@ void WS2812FX::moveY(CRGB *leds, int8_t delta) { //ewowi20210628: new functions moved from colorutils: add segment awareness -void WS2812FX::fill_solid(CRGB* leds, const struct CRGB& color) { +void WS2812FX::fill_solid(CRGB* leds, CRGB color) { uint16_t w = SEGMENT.virtualWidth(); uint16_t h = SEGMENT.virtualHeight(); for(uint16_t y = 0; y < h; y++) for (uint16_t x = 0; x < w; x++) { @@ -314,6 +314,16 @@ void WS2812FX::fill_solid(CRGB* leds, const struct CRGB& color) { } } +// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs +void WS2812FX::fill_circle(CRGB* leds, uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { + for (int16_t y = -radius; y <= radius; y++) { + for (int16_t x = -radius; x <= radius; x++) { + if (x * x + y * y <= radius * radius) + leds[XY(cx + x, cy + y)] += col; + } + } +} + void WS2812FX::fadeToBlackBy(CRGB* leds, uint8_t fadeBy) { nscale8(leds, 255 - fadeBy); } @@ -347,3 +357,20 @@ void WS2812FX::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB if (e2 < dy) { err += dx; y0 += sy; } } } + +#define WU_WEIGHT(a,b) ((uint8_t) (((a)*(b)+(a)+(b))>>8)) +void WS2812FX::wu_pixel(CRGB *leds, uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel procedure by reddit u/sutaburosu + // extract the fractional parts and derive their inverses + uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy; + // calculate the intensities for each affected pixel + uint8_t wu[4] = {WU_WEIGHT(ix, iy), WU_WEIGHT(xx, iy), + WU_WEIGHT(ix, yy), WU_WEIGHT(xx, yy)}; + // multiply the intensities by the colour, and saturating-add them to the pixels + for (uint8_t i = 0; i < 4; i++) { + uint16_t xy = XY((x >> 8) + (i & 1), (y >> 8) + ((i >> 1) & 1)); + leds[xy].r = qadd8(leds[xy].r, c.r * wu[i] >> 8); + leds[xy].g = qadd8(leds[xy].g, c.g * wu[i] >> 8); + leds[xy].b = qadd8(leds[xy].b, c.b * wu[i] >> 8); + } +} +#undef WU_WEIGHT diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 78f316a9..1fc07974 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1456,7 +1456,9 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "2D Tartan@X scale,Y scale;;!", "2D Waverly@Fade rate,Sensitivity;;!", "2D Spaceships@Fade rate,Blur;!,!,!;!", -"2D Crazy Bees@Fade rate,Blur;;" +"2D Crazy Bees@Fade rate,Blur;;", +"2D Ghost Rider@Fade rate,Blur;;", +"2D Blobs@!,# blobs;!,!,!;!" ])====="; const char JSON_palette_names[] PROGMEM = R"=====([ diff --git a/wled00/mqtt.cpp b/wled00/mqtt.cpp index 9efcfed6..e53a14e7 100644 --- a/wled00/mqtt.cpp +++ b/wled00/mqtt.cpp @@ -90,7 +90,6 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties colorFromDecOrHexString(col, (char*)payloadStr); colorUpdated(CALL_MODE_DIRECT_CHANGE); } else if (strcmp_P(topic, PSTR("/api")) == 0) { - DEBUG_PRINTLN(F("MQTT JSON buffer requested.")); if (!requestJSONBufferLock(15)) { delete[] payloadStr; return; } if (payload[0] == '{') { //JSON API deserializeJson(doc, payloadStr); diff --git a/wled00/presets.cpp b/wled00/presets.cpp index a8db3bb0..ca3902f3 100644 --- a/wled00/presets.cpp +++ b/wled00/presets.cpp @@ -58,7 +58,6 @@ void handlePresets(bool force) if (force) return; // something went wrong with force option (most likely WS request), quit and wait for async load // allocate buffer - DEBUG_PRINTLN(F("Apply preset JSON buffer requested.")); if (!requestJSONBufferLock(9)) return; // will also assign fileDoc #ifdef ARDUINO_ARCH_ESP32 @@ -117,7 +116,6 @@ void savePreset(byte index, const char* pname, JsonObject saveobj) if (!fileDoc) { // called from handleSet() HTTP API - DEBUG_PRINTLN(F("Save preset JSON buffer requested.")); if (!requestJSONBufferLock(10)) return; sObj = fileDoc->to(); bufferAllocated = true; diff --git a/wled00/util.cpp b/wled00/util.cpp index a4555f3d..8b4da755 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -211,6 +211,9 @@ bool requestJSONBufferLock(uint8_t module) } jsonBufferLock = module ? module : 255; + DEBUG_PRINT(F("JSON buffer locked. (")); + DEBUG_PRINT(jsonBufferLock); + DEBUG_PRINTLN(")"); fileDoc = &doc; // used for applying presets (presets.cpp) doc.clear(); return true; diff --git a/wled00/ws.cpp b/wled00/ws.cpp index 454e4ceb..3b9951e9 100644 --- a/wled00/ws.cpp +++ b/wled00/ws.cpp @@ -36,7 +36,6 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp } bool verboseResponse = false; - DEBUG_PRINTLN(F("WS JSON receive buffer requested.")); if (!requestJSONBufferLock(11)) return; DeserializationError error = deserializeJson(doc, data, len); @@ -97,7 +96,8 @@ void sendDataWs(AsyncWebSocketClient * client) if (!ws.count()) return; AsyncWebSocketMessageBuffer * buffer; - DEBUG_PRINTLN(F("WS JSON send buffer requested.")); + while (strip.isUpdating()) yield(); + if (!requestJSONBufferLock(12)) return; JsonObject state = doc.createNestedObject("state");