diff --git a/CHANGELOG.md b/CHANGELOG.md index c570ac1f7..f591fc2b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -173,7 +173,7 @@ - v0.15.0-b2 - WS2805 support (RGB + WW + CW, 600kbps) - Unified PSRAM use -- NeoPixelBus v2.7.9 +- NeoPixelBus v2.7.9 (for future WS2805 support) - Ubiquitous PSRAM mode for all variants of ESP32 - SSD1309_64 I2C Support for FLD Usermod (#3836 by @THATDONFC) - Palette cycling fix (add support for `{"seg":[{"pal":"X~Y~"}]}` or `{"seg":[{"pal":"X~Yr"}]}`) diff --git a/usermods/stairway_wipe_basic/wled06_usermod.ino b/usermods/stairway_wipe_basic/wled06_usermod.ino deleted file mode 100644 index dc2159ee9..000000000 --- a/usermods/stairway_wipe_basic/wled06_usermod.ino +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This file allows you to add own functionality to WLED more easily - * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality - * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled_eeprom.h) - * bytes 2400+ are currently ununsed, but might be used for future wled features - */ - -//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t) - -byte wipeState = 0; //0: inactive 1: wiping 2: solid -unsigned long timeStaticStart = 0; -uint16_t previousUserVar0 = 0; - -//comment this out if you want the turn off effect to be just fading out instead of reverse wipe -#define STAIRCASE_WIPE_OFF - -//gets called once at boot. Do all initialization that doesn't depend on network here -void userSetup() -{ - //setup PIR sensor here, if needed -} - -//gets called every time WiFi is (re-)connected. Initialize own network interfaces here -void userConnected() -{ - -} - -//loop. You can use "if (WLED_CONNECTED)" to check for successful connection -void userLoop() -{ - //userVar0 (U0 in HTTP API): - //has to be set to 1 if movement is detected on the PIR that is the same side of the staircase as the ESP8266 - //has to be set to 2 if movement is detected on the PIR that is the opposite side - //can be set to 0 if no movement is detected. Otherwise LEDs will turn off after a configurable timeout (userVar1 seconds) - - if (userVar0 > 0) - { - if ((previousUserVar0 == 1 && userVar0 == 2) || (previousUserVar0 == 2 && userVar0 == 1)) wipeState = 3; //turn off if other PIR triggered - previousUserVar0 = userVar0; - - if (wipeState == 0) { - startWipe(); - wipeState = 1; - } else if (wipeState == 1) { //wiping - uint32_t cycleTime = 360 + (255 - effectSpeed)*75; //this is how long one wipe takes (minus 25 ms to make sure we switch in time) - if (millis() + strip.timebase > (cycleTime - 25)) { //wipe complete - effectCurrent = FX_MODE_STATIC; - timeStaticStart = millis(); - colorUpdated(CALL_MODE_NOTIFICATION); - wipeState = 2; - } - } else if (wipeState == 2) { //static - if (userVar1 > 0) //if U1 is not set, the light will stay on until second PIR or external command is triggered - { - if (millis() - timeStaticStart > userVar1*1000) wipeState = 3; - } - } else if (wipeState == 3) { //switch to wipe off - #ifdef STAIRCASE_WIPE_OFF - effectCurrent = FX_MODE_COLOR_WIPE; - strip.timebase = 360 + (255 - effectSpeed)*75 - millis(); //make sure wipe starts fully lit - colorUpdated(CALL_MODE_NOTIFICATION); - wipeState = 4; - #else - turnOff(); - #endif - } else { //wiping off - if (millis() + strip.timebase > (725 + (255 - effectSpeed)*150)) turnOff(); //wipe complete - } - } else { - wipeState = 0; //reset for next time - if (previousUserVar0) { - #ifdef STAIRCASE_WIPE_OFF - userVar0 = previousUserVar0; - wipeState = 3; - #else - turnOff(); - #endif - } - previousUserVar0 = 0; - } -} - -void startWipe() -{ - bri = briLast; //turn on - transitionDelayTemp = 0; //no transition - effectCurrent = FX_MODE_COLOR_WIPE; - strip.resetTimebase(); //make sure wipe starts from beginning - - //set wipe direction - Segment& seg = strip.getSegment(0); - bool doReverse = (userVar0 == 2); - seg.setOption(1, doReverse); - - colorUpdated(CALL_MODE_NOTIFICATION); -} - -void turnOff() -{ - #ifdef STAIRCASE_WIPE_OFF - transitionDelayTemp = 0; //turn off immediately after wipe completed - #else - transitionDelayTemp = 4000; //fade out slowly - #endif - bri = 0; - stateUpdated(CALL_MODE_NOTIFICATION); - wipeState = 0; - userVar0 = 0; - previousUserVar0 = 0; -} diff --git a/wled00/FX.h b/wled00/FX.h index f54b3ba5c..c877c17f2 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -325,6 +325,30 @@ extern byte realtimeMode; // used in getMappedPixelIndex() #define MODE_COUNT 187 + +#define BLEND_STYLE_FADE 0x00 // universal +#define BLEND_STYLE_FAIRY_DUST 0x01 // universal +#define BLEND_STYLE_SWIPE_RIGHT 0x02 // 1D or 2D +#define BLEND_STYLE_SWIPE_LEFT 0x03 // 1D or 2D +#define BLEND_STYLE_PINCH_OUT 0x04 // 1D or 2D +#define BLEND_STYLE_INSIDE_OUT 0x05 // 1D or 2D +#define BLEND_STYLE_SWIPE_UP 0x06 // 2D +#define BLEND_STYLE_SWIPE_DOWN 0x07 // 2D +#define BLEND_STYLE_OPEN_H 0x08 // 2D +#define BLEND_STYLE_OPEN_V 0x09 // 2D +// as there are many push variants to optimise if statements they are groupped together +#define BLEND_STYLE_PUSH_RIGHT 0x10 // 1D or 2D (& 0b00010000) +#define BLEND_STYLE_PUSH_LEFT 0x11 // 1D or 2D (& 0b00010000) +#define BLEND_STYLE_PUSH_UP 0x12 // 2D (& 0b00010000) +#define BLEND_STYLE_PUSH_DOWN 0x13 // 2D (& 0b00010000) +#define BLEND_STYLE_PUSH_TL 0x14 // 2D (& 0b00010000) +#define BLEND_STYLE_PUSH_TR 0x15 // 2D (& 0b00010000) +#define BLEND_STYLE_PUSH_BR 0x16 // 2D (& 0b00010000) +#define BLEND_STYLE_PUSH_BL 0x17 // 2D (& 0b00010000) +#define BLEND_STYLE_PUSH_MASK 0x10 +#define BLEND_STYLE_COUNT 18 + + typedef enum mapping1D2D { M12_Pixels = 0, M12_pBar = 1, @@ -333,7 +357,7 @@ typedef enum mapping1D2D { M12_sPinwheel = 4 } mapping1D2D_t; -// segment, 80 bytes +// segment, 68 bytes typedef struct Segment { public: uint16_t start; // start index / start X coordinate 2D (left) @@ -436,6 +460,9 @@ typedef struct Segment { static uint16_t _transitionprogress; // current transition progress 0 - 0xFFFF #ifndef WLED_DISABLE_MODE_BLEND static bool _modeBlend; // mode/effect blending semaphore + // clipping + static uint16_t _clipStart, _clipStop; + static uint8_t _clipStartY, _clipStopY; #endif // transition data, valid only if transitional==true, holds values during transition (72 bytes) @@ -446,6 +473,7 @@ typedef struct Segment { #else uint32_t _colorT[NUM_COLORS]; #endif + uint8_t _palTid; // previous palette uint8_t _briT; // temporary brightness uint8_t _cctT; // temporary CCT CRGBPalette16 _palT; // temporary palette @@ -607,6 +635,10 @@ typedef struct Segment { inline void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) const { setPixelColor(i, RGBW32(r,g,b,w), aa); } inline void setPixelColor(float i, CRGB c, bool aa = true) const { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); } #endif + #ifndef WLED_DISABLE_MODE_BLEND + static inline void setClippingRect(int startX, int stopX, int startY = 0, int stopY = 1) { _clipStart = startX; _clipStop = stopX; _clipStartY = startY; _clipStopY = stopY; }; + #endif + bool isPixelClipped(int i) const; [[gnu::hot]] uint32_t getPixelColor(int i) const; // 1D support functions (some implement 2D as well) void blur(uint8_t, bool smear = false); @@ -653,6 +685,7 @@ typedef struct Segment { inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) const { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); } inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) const { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), aa); } #endif + [[gnu::hot]] bool isPixelXYClipped(int x, int y) const; [[gnu::hot]] uint32_t getPixelColorXY(int x, int y) const; // 2D support functions inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend) { setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, blend)); } @@ -690,6 +723,7 @@ typedef struct Segment { inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColor(x, RGBW32(r,g,b,w), aa); } inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0), aa); } #endif + inline bool isPixelXYClipped(int x, int y) { return isPixelClipped(x); } inline uint32_t getPixelColorXY(int x, int y) { return getPixelColor(x); } inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t c, uint8_t blend) { blendPixelColor(x, c, blend); } inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColor(x, RGBW32(c.r,c.g,c.b,0), blend); } @@ -734,9 +768,7 @@ class WS2812FX { // 96 bytes public: WS2812FX() : - paletteFade(0), paletteBlend(0), - cctBlending(0), now(millis()), timebase(0), isMatrix(false), @@ -824,7 +856,6 @@ class WS2812FX { // 96 bytes inline void resume() { _suspend = false; } // will resume strip.service() execution bool - paletteFade, checkSegmentAlignment() const, hasRGBWBus() const, hasCCTBus() const, @@ -839,7 +870,6 @@ class WS2812FX { // 96 bytes uint8_t paletteBlend, - cctBlending, getActiveSegmentsNum() const, getFirstSelectedSegId() const, getLastActiveSegmentId() const, diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 0d38578a3..67624bac3 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -160,7 +160,7 @@ void IRAM_ATTR_YN Segment::_setPixelColorXY_raw(const int& x, const int& y, uint const int baseY = startY + y; #ifndef WLED_DISABLE_MODE_BLEND // if blending modes, blend with underlying pixel - if (_modeBlend) col = color_blend16(strip.getPixelColorXY(baseX, baseY), col, 0xFFFFU - progress()); + if (_modeBlend && blendingStyle == BLEND_STYLE_FADE) col = color_blend16(strip.getPixelColorXY(baseX, baseY), col, 0xFFFFU - progress()); #endif strip.setPixelColorXY(baseX, baseY, col); @@ -179,14 +179,57 @@ void IRAM_ATTR_YN Segment::_setPixelColorXY_raw(const int& x, const int& y, uint } } +// pixel is clipped if it falls outside clipping range (_modeBlend==true) or is inside clipping range (_modeBlend==false) +// if clipping start > stop the clipping range is inverted +// _modeBlend==true -> old effect during transition +// _modeBlend==false -> new effect during transition +bool IRAM_ATTR_YN Segment::isPixelXYClipped(int x, int y) const { +#ifndef WLED_DISABLE_MODE_BLEND + if (_clipStart != _clipStop && blendingStyle != BLEND_STYLE_FADE) { + const bool invertX = _clipStart > _clipStop; + const bool invertY = _clipStartY > _clipStopY; + const int startX = invertX ? _clipStop : _clipStart; + const int stopX = invertX ? _clipStart : _clipStop; + const int startY = invertY ? _clipStopY : _clipStartY; + const int stopY = invertY ? _clipStartY : _clipStopY; + if (blendingStyle == BLEND_STYLE_FAIRY_DUST) { + const unsigned width = stopX - startX; // assumes full segment width (faster than virtualWidth()) + const unsigned len = width * (stopY - startY); // assumes full segment height (faster than virtualHeight()) + if (len < 2) return false; + const unsigned shuffled = hashInt(x + y * width) % len; + const unsigned pos = (shuffled * 0xFFFFU) / len; + return progress() > pos; + } + bool xInside = (x >= startX && x < stopX); if (invertX) xInside = !xInside; + bool yInside = (y >= startY && y < stopY); if (invertY) yInside = !yInside; + const bool clip = (invertX && invertY) ? !_modeBlend : _modeBlend; + if (xInside && yInside) return clip; // covers window & corners (inverted) + return !clip; + } +#endif + return false; +} + void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) const { if (!isActive()) return; // not active const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive) const int vH = vHeight(); // segment height in logical pixels (is always >= 1) - // negative values of x & y cast into unsigend will become very large values and will therefore be greater than vW/vH - if (unsigned(x) >= unsigned(vW) || unsigned(y) >= unsigned(vH)) return; // if pixel would fall out of virtual segment just exit + +#ifndef WLED_DISABLE_MODE_BLEND + unsigned prog = 0xFFFF - progress(); + if (!prog && !_modeBlend && (blendingStyle & BLEND_STYLE_PUSH_MASK)) { + unsigned dX = (blendingStyle == BLEND_STYLE_PUSH_UP || blendingStyle == BLEND_STYLE_PUSH_DOWN) ? 0 : prog * vW / 0xFFFF; + unsigned dY = (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_RIGHT) ? 0 : prog * vH / 0xFFFF; + if (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_TL || blendingStyle == BLEND_STYLE_PUSH_BL) x += dX; + else x -= dX; + if (blendingStyle == BLEND_STYLE_PUSH_DOWN || blendingStyle == BLEND_STYLE_PUSH_TL || blendingStyle == BLEND_STYLE_PUSH_TR) y -= dY; + else y += dY; + } +#endif + + if (x >= vW || y >= vH || x < 0 || y < 0 || isPixelXYClipped(x,y)) return; // if pixel would fall out of virtual segment just exit // if color is unscaled if (!_colorScaled) col = color_fade(col, _segBri); @@ -259,9 +302,24 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa) const // returns RGBW values of pixel uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const { if (!isActive()) return 0; // not active + const int vW = vWidth(); const int vH = vHeight(); - if (unsigned(x) >= unsigned(vW) || unsigned(y) >= unsigned(vH)) return 0; // if pixel would fall out of virtual segment just exit + +#ifndef WLED_DISABLE_MODE_BLEND + unsigned prog = 0xFFFF - progress(); + if (!prog && !_modeBlend && (blendingStyle & BLEND_STYLE_PUSH_MASK)) { + unsigned dX = (blendingStyle == BLEND_STYLE_PUSH_UP || blendingStyle == BLEND_STYLE_PUSH_DOWN) ? 0 : prog * vW / 0xFFFF; + unsigned dY = (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_RIGHT) ? 0 : prog * vH / 0xFFFF; + if (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_TL || blendingStyle == BLEND_STYLE_PUSH_BL) x -= dX; + else x += dX; + if (blendingStyle == BLEND_STYLE_PUSH_DOWN || blendingStyle == BLEND_STYLE_PUSH_TL || blendingStyle == BLEND_STYLE_PUSH_TR) y -= dY; + else y += dY; + } +#endif + + if (x >= vW || y >= vH || x<0 || y<0 || isPixelXYClipped(x,y)) return 0; // if pixel would fall out of virtual segment just exit + if (reverse ) x = vW - x - 1; if (reverse_y) y = vH - y - 1; if (transpose) { std::swap(x,y); } // swap X & Y if segment transposed diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 20d99519d..17af4e24b 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -84,6 +84,10 @@ uint16_t Segment::_transitionprogress = 0xFFFF; #ifndef WLED_DISABLE_MODE_BLEND bool Segment::_modeBlend = false; +uint16_t Segment::_clipStart = 0; +uint16_t Segment::_clipStop = 0; +uint8_t Segment::_clipStartY = 0; +uint8_t Segment::_clipStopY = 1; #endif // copy constructor @@ -258,24 +262,21 @@ void Segment::startTransition(uint16_t dur) { //DEBUG_PRINTF_P(PSTR("-- Started transition: %p (%p)\n"), this, _t); loadPalette(_t->_palT, palette); + _t->_palTid = palette; _t->_briT = on ? opacity : 0; _t->_cctT = cct; #ifndef WLED_DISABLE_MODE_BLEND - if (modeBlending) { - swapSegenv(_t->_segT); - _t->_modeT = mode; - _t->_segT._dataLenT = 0; - _t->_segT._dataT = nullptr; - if (_dataLen > 0 && data) { - _t->_segT._dataT = (byte *)malloc(_dataLen); - if (_t->_segT._dataT) { - //DEBUG_PRINTF_P(PSTR("-- Allocated duplicate data (%d) for %p: %p\n"), _dataLen, this, _t->_segT._dataT); - memcpy(_t->_segT._dataT, data, _dataLen); - _t->_segT._dataLenT = _dataLen; - } + swapSegenv(_t->_segT); + _t->_modeT = mode; + _t->_segT._dataLenT = 0; + _t->_segT._dataT = nullptr; + if (_dataLen > 0 && data) { + _t->_segT._dataT = (byte *)malloc(_dataLen); + if (_t->_segT._dataT) { + //DEBUG_PRINTF_P(PSTR("-- Allocated duplicate data (%d) for %p: %p\n"), _dataLen, this, _t->_segT._dataT); + memcpy(_t->_segT._dataT, data, _dataLen); + _t->_segT._dataLenT = _dataLen; } - } else { - for (size_t i=0; i_segT._colorT[i] = colors[i]; } #else for (size_t i=0; i_colorT[i] = colors[i]; @@ -381,28 +382,52 @@ void Segment::restoreSegenv(const tmpsegd_t &tmpSeg) { uint8_t Segment::currentBri(bool useCct) const { unsigned prog = isInTransition() ? progress() : 0xFFFFU; - if (prog < 0xFFFFU) { // progress() < 0xFFFF implies that _t is a valid pointer - unsigned curBri = (useCct ? cct : (on ? opacity : 0)) * prog; - curBri += (useCct ? _t->_cctT : _t->_briT) * (0xFFFFU - prog); + uint32_t curBri = useCct ? cct : (on ? opacity : 0); + if (prog < 0xFFFFU) { +#ifndef WLED_DISABLE_MODE_BLEND + uint8_t tmpBri = useCct ? _t->_cctT : (_t->_segT._optionsT & 0x0004 ? _t->_briT : 0); + // _modeBlend==true -> old effect + if (blendingStyle != BLEND_STYLE_FADE) return _modeBlend ? tmpBri : curBri; // not fade/blend transition, each effect uses its brightness +#else + uint8_t tmpBri = useCct ? _t->_cctT : _t->_briT; +#endif + curBri *= prog; + curBri += tmpBri * (0xFFFFU - prog); return curBri / 0xFFFFU; } - return (useCct ? cct : (on ? opacity : 0)); + return curBri; } uint8_t Segment::currentMode() const { #ifndef WLED_DISABLE_MODE_BLEND unsigned prog = isInTransition() ? progress() : 0xFFFFU; - if (modeBlending && prog < 0xFFFFU) return _t->_modeT; // progress() < 0xFFFF implies that _t is a valid pointer -#endif + if (prog == 0xFFFFU) return mode; + if (blendingStyle != BLEND_STYLE_FADE) { + // workaround for on/off transition to respect blending style + uint8_t modeT = (bri != briT) && bri ? FX_MODE_STATIC : _t->_modeT; // On/Off transition active (bri!=briT) and final bri>0 : old mode is STATIC + uint8_t modeS = (bri != briT) && !bri ? FX_MODE_STATIC : mode; // On/Off transition active (bri!=briT) and final bri==0 : new mode is STATIC + return _modeBlend ? modeT : modeS; // _modeBlend==true -> old effect + } + return _modeBlend ? _t->_modeT : mode; // _modeBlend==true -> old effect +#else return mode; +#endif } uint32_t Segment::currentColor(uint8_t slot) const { if (slot >= NUM_COLORS) slot = 0; + unsigned prog = progress(); + if (prog == 0xFFFFU) return colors[slot]; #ifndef WLED_DISABLE_MODE_BLEND - return isInTransition() ? color_blend16(_t->_segT._colorT[slot], colors[slot], progress()) : colors[slot]; + if (blendingStyle != BLEND_STYLE_FADE) { + // workaround for on/off transition to respect blending style + uint32_t colT = (bri != briT) && bri ? BLACK : _t->_segT._colorT[slot]; // On/Off transition active (bri!=briT) and final bri>0 : old color is BLACK + uint32_t colS = (bri != briT) && !bri ? BLACK : colors[slot]; // On/Off transition active (bri!=briT) and final bri==0 : new color is BLACK + return _modeBlend ? colT : colS; // _modeBlend==true -> old effect + } + return color_blend16(_t->_segT._colorT[slot], colors[slot], prog); #else - return isInTransition() ? color_blend16(_t->_colorT[slot], colors[slot], progress()) : colors[slot]; + return color_blend16(_t->_colorT[slot], colors[slot], prog); #endif } @@ -424,32 +449,37 @@ void Segment::beginDraw() { } // load palette into _currentPalette loadPalette(_currentPalette, palette); - if (strip.paletteFade && prog < 0xFFFFU) { - // blend palettes - // there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time) - // minimum blend time is 100ms maximum is 65535ms - unsigned noOfBlends = ((255U * prog) / 0xFFFFU) - _t->_prevPaletteBlends; - for (unsigned i = 0; i < noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, _currentPalette, 48); - _currentPalette = _t->_palT; // copy transitioning/temporary palette + if (prog < 0xFFFFU) { +#ifndef WLED_DISABLE_MODE_BLEND + if (blendingStyle > BLEND_STYLE_FADE) { + //if (_modeBlend) loadPalette(_currentPalette, _t->_palTid); // not fade/blend transition, each effect uses its palette + if (_modeBlend) _currentPalette = _t->_palT; // not fade/blend transition, each effect uses its palette + } else +#endif + { + // blend palettes + // there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time) + // minimum blend time is 100ms maximum is 65535ms + unsigned noOfBlends = ((255U * prog) / 0xFFFFU) - _t->_prevPaletteBlends; + for (unsigned i = 0; i < noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, _currentPalette, 48); + _currentPalette = _t->_palT; // copy transitioning/temporary palette + } } } // relies on WS2812FX::service() to call it for each frame void Segment::handleRandomPalette() { // is it time to generate a new palette? - if ((uint16_t)((uint16_t)(millis() / 1000U) - _lastPaletteChange) > randomPaletteChangeTime){ - _newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette(); - _lastPaletteChange = (uint16_t)(millis() / 1000U); - _lastPaletteBlend = (uint16_t)((uint16_t)millis() - 512); // starts blending immediately + if ((uint16_t)(millis()/1000U) - _lastPaletteChange > randomPaletteChangeTime) { + _newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette(); + _lastPaletteChange = (uint16_t)(millis()/1000U); + _lastPaletteBlend = (uint16_t)(millis())-512; // starts blending immediately } - // if palette transitions is enabled, blend it according to Transition Time (if longer than minimum given by service calls) - if (strip.paletteFade) { - // assumes that 128 updates are sufficient to blend a palette, so shift by 7 (can be more, can be less) - // in reality there need to be 255 blends to fully blend two entirely different palettes - if ((uint16_t)((uint16_t)millis() - _lastPaletteBlend) < strip.getTransition() >> 7) return; // not yet time to fade, delay the update - _lastPaletteBlend = (uint16_t)millis(); - } + // assumes that 128 updates are sufficient to blend a palette, so shift by 7 (can be more, can be less) + // in reality there need to be 255 blends to fully blend two entirely different palettes + if ((uint16_t)millis() - _lastPaletteBlend < strip.getTransition() >> 7) return; // not yet time to fade, delay the update + _lastPaletteBlend = (uint16_t)millis(); nblendPaletteTowardPalette(_randomPalette, _newRandomPalette, 48); } @@ -524,7 +554,8 @@ Segment &Segment::setColor(uint8_t slot, uint32_t c) { if (slot == 0 && c == BLACK) return *this; // on/off segment cannot have primary color black if (slot == 1 && c != BLACK) return *this; // on/off segment cannot have secondary color non black } - if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change + //DEBUG_PRINTF_P(PSTR("- Starting color transition: %d [0x%X]\n"), slot, c); + startTransition(strip.getTransition()); // start transition prior to change colors[slot] = c; stateChanged = true; // send UDP/WS broadcast return *this; @@ -537,7 +568,7 @@ Segment &Segment::setCCT(uint16_t k) { k = (k - 1900) >> 5; } if (cct != k) { - //DEBUGFX_PRINTF_P(PSTR("- Starting CCT transition: %d\n"), k); + //DEBUG_PRINTF_P(PSTR("- Starting CCT transition: %d\n"), k); startTransition(strip.getTransition()); // start transition prior to change cct = k; stateChanged = true; // send UDP/WS broadcast @@ -547,7 +578,7 @@ Segment &Segment::setCCT(uint16_t k) { Segment &Segment::setOpacity(uint8_t o) { if (opacity != o) { - //DEBUGFX_PRINTF_P(PSTR("- Starting opacity transition: %d\n"), o); + //DEBUG_PRINTF_P(PSTR("- Starting opacity transition: %d\n"), o); startTransition(strip.getTransition()); // start transition prior to change opacity = o; stateChanged = true; // send UDP/WS broadcast @@ -557,7 +588,7 @@ Segment &Segment::setOpacity(uint8_t o) { Segment &Segment::setOption(uint8_t n, bool val) { bool prevOn = on; - if (fadeTransition && n == SEG_OPTION_ON && val != prevOn) startTransition(strip.getTransition()); // start transition prior to change + if (n == SEG_OPTION_ON && val != prevOn) startTransition(strip.getTransition()); // start transition prior to change if (val) options |= 0x01 << n; else options &= ~(0x01 << n); if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET)) stateChanged = true; // send UDP/WS broadcast @@ -571,7 +602,8 @@ Segment &Segment::setMode(uint8_t fx, bool loadDefaults) { // if we have a valid mode & is not reserved if (fx != mode) { #ifndef WLED_DISABLE_MODE_BLEND - if (modeBlending) startTransition(strip.getTransition()); // set effect transitions + //DEBUG_PRINTF_P(PSTR("- Starting effect transition: %d\n"), fx); + startTransition(strip.getTransition()); // set effect transitions #endif mode = fx; int sOpt; @@ -606,7 +638,8 @@ Segment &Segment::setPalette(uint8_t pal) { if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0; // built in palettes if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; // custom palettes if (pal != palette) { - if (strip.paletteFade) startTransition(strip.getTransition()); + //DEBUG_PRINTF_P(PSTR("- Starting palette transition: %d\n"), pal); + startTransition(strip.getTransition()); palette = pal; stateChanged = true; // send UDP/WS broadcast } @@ -697,6 +730,33 @@ uint16_t Segment::virtualLength() const { return vLength; } +// pixel is clipped if it falls outside clipping range (_modeBlend==true) or is inside clipping range (_modeBlend==false) +// if clipping start > stop the clipping range is inverted +// _modeBlend==true -> old effect during transition +// _modeBlend==false -> new effect during transition +bool IRAM_ATTR_YN Segment::isPixelClipped(int i) const { +#ifndef WLED_DISABLE_MODE_BLEND + if (_clipStart != _clipStop && blendingStyle > BLEND_STYLE_FADE) { + bool invert = _clipStart > _clipStop; // ineverted start & stop + int start = invert ? _clipStop : _clipStart; + int stop = invert ? _clipStart : _clipStop; + if (blendingStyle == BLEND_STYLE_FAIRY_DUST) { + unsigned len = stop - start; + if (len < 2) return false; + unsigned shuffled = hashInt(i) % len; + unsigned pos = (shuffled * 0xFFFFU) / len; + return (progress() <= pos) ^ _modeBlend; + } + const bool iInside = (i >= start && i < stop); + //if (!invert && iInside) return _modeBlend; + //if ( invert && !iInside) return _modeBlend; + //return !_modeBlend; + return !iInside ^ invert ^ _modeBlend; // thanks @willmmiles (https://github.com/Aircoookie/WLED/pull/3877#discussion_r1554633876) + } +#endif + return false; +} + void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) const { if (!isActive() || i < 0) return; // not active or invalid index @@ -829,6 +889,18 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) const } #endif +#ifndef WLED_DISABLE_MODE_BLEND + // if we blend using "push" style we need to "shift" new mode to left or right + if (isInTransition() && !_modeBlend && (blendingStyle == BLEND_STYLE_PUSH_RIGHT || blendingStyle == BLEND_STYLE_PUSH_LEFT)) { + unsigned prog = 0xFFFF - progress(); + unsigned dI = prog * vL / 0xFFFF; + if (blendingStyle == BLEND_STYLE_PUSH_RIGHT) i -= dI; + else i += dI; + } +#endif + + if (i >= vL || i < 0 || isPixelClipped(i)) return; // handle clipping on 1D + unsigned len = length(); // if color is unscaled if (!_colorScaled) col = color_fade(col, _segBri); @@ -854,14 +926,16 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) const indexMir += offset; // offset/phase if (indexMir >= stop) indexMir -= len; // wrap #ifndef WLED_DISABLE_MODE_BLEND - if (_modeBlend) tmpCol = color_blend16(strip.getPixelColor(indexMir), col, uint16_t(0xFFFFU - progress())); + // _modeBlend==true -> old effect + if (_modeBlend && blendingStyle == BLEND_STYLE_FADE) tmpCol = color_blend16(strip.getPixelColor(indexMir), col, 0xFFFFU - progress()); #endif strip.setPixelColor(indexMir, tmpCol); } indexSet += offset; // offset/phase if (indexSet >= stop) indexSet -= len; // wrap #ifndef WLED_DISABLE_MODE_BLEND - if (_modeBlend) tmpCol = color_blend16(strip.getPixelColor(indexSet), col, uint16_t(0xFFFFU - progress())); + // _modeBlend==true -> old effect + if (_modeBlend && blendingStyle == BLEND_STYLE_FADE) tmpCol = color_blend16(strip.getPixelColor(indexSet), col, 0xFFFFU - progress()); #endif strip.setPixelColor(indexSet, tmpCol); } @@ -907,6 +981,9 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const { if (!isActive()) return 0; // not active + int vL = vLength(); + if (i >= vL || i < 0) return 0; + #ifndef WLED_DISABLE_2D if (is2D()) { const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive) @@ -963,7 +1040,18 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const } #endif - if (reverse) i = vLength() - i - 1; +#ifndef WLED_DISABLE_MODE_BLEND + if (isInTransition() && !_modeBlend && (blendingStyle == BLEND_STYLE_PUSH_RIGHT || blendingStyle == BLEND_STYLE_PUSH_LEFT)) { + unsigned prog = 0xFFFF - progress(); + unsigned dI = prog * vL / 0xFFFF; + if (blendingStyle == BLEND_STYLE_PUSH_RIGHT) i -= dI; + else i += dI; + } +#endif + + if (i >= vL || i < 0 || isPixelClipped(i)) return 0; // handle clipping on 1D + + if (reverse) i = vL - i - 1; i *= groupLength(); i += start; // offset/phase @@ -1196,7 +1284,7 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_ if (mapping && vL > 1) paletteIndex = (i*255)/(vL -1); // paletteBlend: 0 - wrap when moving, 1 - always wrap, 2 - never wrap, 3 - none (undefined) if (!wrap && strip.paletteBlend != 3) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end" - CRGBW palcol = ColorFromPalette(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global + CRGBW palcol = ColorFromPaletteWLED(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global palcol.w = W(color); return palcol.color32; @@ -1373,21 +1461,78 @@ void WS2812FX::service() { // The blending will largely depend on the effect behaviour since actual output (LEDs) may be // overwritten by later effect. To enable seamless blending for every effect, additional LED buffer // would need to be allocated for each effect and then blended together for each pixel. - [[maybe_unused]] uint8_t tmpMode = seg.currentMode(); // this will return old mode while in transition seg.beginDraw(); // set up parameters for get/setPixelColor() - frameDelay = (*_mode[seg.mode])(); // run new/current mode #ifndef WLED_DISABLE_MODE_BLEND - if (modeBlending && seg.mode != tmpMode) { + Segment::setClippingRect(0, 0); // disable clipping (just in case) + if (seg.isInTransition()) { + // set clipping rectangle + // new mode is run inside clipping area and old mode outside clipping area + unsigned p = seg.progress(); + unsigned w = seg.is2D() ? Segment::vWidth() : Segment::vLength(); + unsigned h = Segment::vHeight(); + unsigned dw = p * w / 0xFFFFU + 1; + unsigned dh = p * h / 0xFFFFU + 1; + unsigned orgBS = blendingStyle; + if (w*h == 1) blendingStyle = BLEND_STYLE_FADE; // disable belending for single pixel segments (use fade instead) + switch (blendingStyle) { + case BLEND_STYLE_FAIRY_DUST: // fairy dust (must set entire segment, see isPixelXYClipped()) + Segment::setClippingRect(0, w, 0, h); + break; + case BLEND_STYLE_SWIPE_RIGHT: // left-to-right + case BLEND_STYLE_PUSH_RIGHT: // left-to-right + Segment::setClippingRect(0, dw, 0, h); + break; + case BLEND_STYLE_SWIPE_LEFT: // right-to-left + case BLEND_STYLE_PUSH_LEFT: // right-to-left + Segment::setClippingRect(w - dw, w, 0, h); + break; + case BLEND_STYLE_PINCH_OUT: // corners + Segment::setClippingRect((w + dw)/2, (w - dw)/2, (h + dh)/2, (h - dh)/2); // inverted!! + break; + case BLEND_STYLE_INSIDE_OUT: // outward + Segment::setClippingRect((w - dw)/2, (w + dw)/2, (h - dh)/2, (h + dh)/2); + break; + case BLEND_STYLE_SWIPE_DOWN: // top-to-bottom (2D) + case BLEND_STYLE_PUSH_DOWN: // top-to-bottom (2D) + Segment::setClippingRect(0, w, 0, dh); + break; + case BLEND_STYLE_SWIPE_UP: // bottom-to-top (2D) + case BLEND_STYLE_PUSH_UP: // bottom-to-top (2D) + Segment::setClippingRect(0, w, h - dh, h); + break; + case BLEND_STYLE_OPEN_H: // horizontal-outward (2D) same look as INSIDE_OUT on 1D + Segment::setClippingRect((w - dw)/2, (w + dw)/2, 0, h); + break; + case BLEND_STYLE_OPEN_V: // vertical-outward (2D) + Segment::setClippingRect(0, w, (h - dh)/2, (h + dh)/2); + break; + case BLEND_STYLE_PUSH_TL: // TL-to-BR (2D) + Segment::setClippingRect(0, dw, 0, dh); + break; + case BLEND_STYLE_PUSH_TR: // TR-to-BL (2D) + Segment::setClippingRect(w - dw, w, 0, dh); + break; + case BLEND_STYLE_PUSH_BR: // BR-to-TL (2D) + Segment::setClippingRect(w - dw, w, h - dh, h); + break; + case BLEND_STYLE_PUSH_BL: // BL-to-TR (2D) + Segment::setClippingRect(0, dw, h - dh, h); + break; + } + frameDelay = (*_mode[seg.currentMode()])(); // run new/current mode + // now run old/previous mode Segment::tmpsegd_t _tmpSegData; Segment::modeBlend(true); // set semaphore seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state) seg.beginDraw(); // set up parameters for get/setPixelColor() - unsigned d2 = (*_mode[tmpMode])(); // run old mode + frameDelay = min(frameDelay, (unsigned)(*_mode[seg.currentMode()])()); // run old mode + seg.call++; // increment old mode run counter seg.restoreSegenv(_tmpSegData); // restore mode state (will also update transitional state) - frameDelay = min(frameDelay,d2); // use shortest delay Segment::modeBlend(false); // unset semaphore - } + blendingStyle = orgBS; // restore blending style if it was modified for single pixel segment + } else #endif + frameDelay = (*_mode[seg.mode])(); // run effect mode (not in transition) seg.call++; if (seg.isInTransition() && frameDelay > FRAMETIME) frameDelay = FRAMETIME; // force faster updates during transition BusManager::setSegmentCCT(oldCCT); // restore old CCT for ABL adjustments @@ -1397,6 +1542,7 @@ void WS2812FX::service() { } _segment_index++; } + Segment::setClippingRect(0, 0); // disable clipping for overlays _isServicing = false; _triggered = false; diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 443f16c73..00cfc60d7 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -114,8 +114,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(strip.correctWB, hw_led["cct"]); CJSON(strip.cctFromRgb, hw_led[F("cr")]); CJSON(cctICused, hw_led[F("ic")]); - CJSON(strip.cctBlending, hw_led[F("cb")]); - Bus::setCCTBlend(strip.cctBlending); + uint8_t cctBlending = hw_led[F("cb")] | Bus::getCCTBlend(); + Bus::setCCTBlend(cctBlending); strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS CJSON(useGlobalLedBuffer, hw_led[F("ld")]); @@ -444,12 +444,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal); // fill look-up table JsonObject light_tr = light["tr"]; - CJSON(fadeTransition, light_tr["mode"]); - CJSON(modeBlending, light_tr["fx"]); int tdd = light_tr["dur"] | -1; if (tdd >= 0) transitionDelay = transitionDelayDefault = tdd * 100; - strip.setTransition(fadeTransition ? transitionDelayDefault : 0); - CJSON(strip.paletteFade, light_tr["pal"]); + strip.setTransition(transitionDelayDefault); CJSON(randomPaletteChangeTime, light_tr[F("rpc")]); CJSON(useHarmonicRandomPalette, light_tr[F("hrp")]); @@ -827,7 +824,7 @@ void serializeConfig() { hw_led["cct"] = strip.correctWB; hw_led[F("cr")] = strip.cctFromRgb; hw_led[F("ic")] = cctICused; - hw_led[F("cb")] = strip.cctBlending; + hw_led[F("cb")] = Bus::getCCTBlend(); hw_led["fps"] = strip.getTargetFps(); hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override hw_led[F("ld")] = useGlobalLedBuffer; @@ -945,10 +942,7 @@ void serializeConfig() { light_gc["val"] = gammaCorrectVal; JsonObject light_tr = light.createNestedObject("tr"); - light_tr["mode"] = fadeTransition; - light_tr["fx"] = modeBlending; light_tr["dur"] = transitionDelayDefault / 100; - light_tr["pal"] = strip.paletteFade; light_tr[F("rpc")] = randomPaletteChangeTime; light_tr[F("hrp")] = useHarmonicRandomPalette; diff --git a/wled00/data/index.htm b/wled00/data/index.htm index 8adec791f..6027eeb51 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -266,7 +266,29 @@
-

Transition:  s

+

Transition:  s

+

Blend: + +

diff --git a/wled00/data/index.js b/wled00/data/index.js index 1482c27b5..dc94cf426 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -677,8 +677,10 @@ function parseInfo(i) { isM = mw>0 && mh>0; if (!isM) { gId("filter2D").classList.add('hide'); + gId('bs').querySelectorAll('option[data-type="2D"]').forEach((o,i)=>{o.style.display='none';}); } else { gId("filter2D").classList.remove('hide'); + gId('bs').querySelectorAll('option[data-type="2D"]').forEach((o,i)=>{o.style.display='';}); } // if (i.noaudio) { // gId("filterVol").classList.add("hide"); @@ -1437,6 +1439,9 @@ function readState(s,command=false) tr = s.transition; gId('tt').value = tr/10; + gId('bs').value = s.bs || 0; + if (tr===0) gId('bsp').classList.add('hide') + else gId('bsp').classList.remove('hide') populateSegments(s); var selc=0; @@ -1698,6 +1703,7 @@ function requestJson(command=null) var tn = parseInt(t.value*10); if (tn != tr) command.transition = tn; } + //command.bs = parseInt(gId('bs').value); req = JSON.stringify(command); if (req.length > 1340) useWs = false; // do not send very long requests over websocket if (req.length > 500 && lastinfo && lastinfo.arch == "esp8266") useWs = false; // esp8266 can only handle 500 bytes diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 956cf7d90..02ebb6ed0 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -832,12 +832,7 @@ Swap:

Brightness factor: %

Transitions

- Enable transitions:
- - Effect blending:
- Default transition time: ms
- Palette transitions:
-
+ Default transition time: ms
Random Cycle Palette Time: s
Use harmonic Random Cycle Palette:

Timed light

diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index c8b1f05ab..d46fd642d 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -493,6 +493,7 @@ um_data_t* simulateSound(uint8_t simulationId); void enumerateLedmaps(); [[gnu::hot]] uint8_t get_random_wheel_index(uint8_t pos); [[gnu::hot, gnu::pure]] float mapf(float x, float in_min, float in_max, float out_min, float out_max); +uint32_t hashInt(uint32_t s); // fast (true) random numbers using hardware RNG, all functions return values in the range lowerlimit to upperlimit-1 // note: for true random numbers with high entropy, do not call faster than every 200ns (5MHz) diff --git a/wled00/json.cpp b/wled00/json.cpp index c63da0854..5fae9544e 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -332,15 +332,20 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) tr = root[F("transition")] | -1; if (tr >= 0) { transitionDelay = tr * 100; - if (fadeTransition) strip.setTransition(transitionDelay); + strip.setTransition(transitionDelay); } } +#ifndef WLED_DISABLE_MODE_BLEND + blendingStyle = root[F("bs")] | blendingStyle; + blendingStyle = constrain(blendingStyle, 0, BLEND_STYLE_COUNT-1); +#endif + // temporary transition (applies only once) tr = root[F("tt")] | -1; if (tr >= 0) { jsonTransitionOnce = true; - if (fadeTransition) strip.setTransition(tr * 100); + strip.setTransition(tr * 100); } tr = root[F("tb")] | -1; @@ -568,6 +573,9 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme root["on"] = (bri > 0); root["bri"] = briLast; root[F("transition")] = transitionDelay/100; //in 100ms +#ifndef WLED_DISABLE_MODE_BLEND + root[F("bs")] = blendingStyle; +#endif } if (!forPreset) { @@ -761,7 +769,7 @@ void serializeInfo(JsonObject root) root[F("freeheap")] = ESP.getFreeHeap(); #if defined(ARDUINO_ARCH_ESP32) - if (psramSafe && psramFound()) root[F("psram")] = ESP.getFreePsram(); + if (psramFound()) root[F("psram")] = ESP.getFreePsram(); #endif root[F("uptime")] = millis()/1000 + rolloverMillis*4294967; diff --git a/wled00/led.cpp b/wled00/led.cpp index 8b34bbf0c..9731da655 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -71,7 +71,7 @@ byte scaledBri(byte in) } -//applies global brightness +//applies global temporary brightness (briT) to strip void applyBri() { if (!(realtimeMode && arlsForceMaxBri)) { //DEBUG_PRINTF_P(PSTR("Applying strip brightness: %d (%d,%d)\n"), (int)briT, (int)bri, (int)briOld); @@ -85,7 +85,7 @@ void applyFinalBri() { briOld = bri; briT = bri; applyBri(); - strip.trigger(); + strip.trigger(); // force one last update } @@ -129,29 +129,23 @@ void stateUpdated(byte callMode) { // notify usermods of state change UsermodManager::onStateChange(callMode); - if (fadeTransition) { - if (strip.getTransition() == 0) { - jsonTransitionOnce = false; - transitionActive = false; - applyFinalBri(); - strip.trigger(); - return; - } - - if (transitionActive) { - briOld = briT; - } else - strip.setTransitionMode(true); // force all segments to transition mode - transitionActive = true; - transitionStartTime = millis(); - } else { + if (strip.getTransition() == 0) { + jsonTransitionOnce = false; + transitionActive = false; applyFinalBri(); + return; } + + if (transitionActive) { + briOld = briT; + } else + strip.setTransitionMode(true); // force all segments to transition mode + transitionActive = true; + transitionStartTime = millis(); } -void updateInterfaces(uint8_t callMode) -{ +void updateInterfaces(uint8_t callMode) { if (!interfaceUpdateCallMode || millis() - lastInterfaceUpdate < INTERFACE_UPDATE_COOLDOWN) return; sendDataWs(); @@ -172,8 +166,7 @@ void updateInterfaces(uint8_t callMode) } -void handleTransitions() -{ +void handleTransitions() { //handle still pending interface update updateInterfaces(interfaceUpdateCallMode); @@ -204,8 +197,7 @@ void colorUpdated(byte callMode) { } -void handleNightlight() -{ +void handleNightlight() { unsigned long now = millis(); if (now < 100 && lastNlUpdate > 0) lastNlUpdate = 0; // take care of millis() rollover if (now - lastNlUpdate < 100) return; // allow only 10 NL updates per second @@ -285,7 +277,6 @@ void handleNightlight() } //utility for FastLED to use our custom timer -uint32_t get_millisecond_timer() -{ +uint32_t get_millisecond_timer() { return strip.now; } diff --git a/wled00/playlist.cpp b/wled00/playlist.cpp index 7f11e2bf0..1908334d7 100644 --- a/wled00/playlist.cpp +++ b/wled00/playlist.cpp @@ -146,7 +146,7 @@ if (millis() - presetCycledTime > (100 * playlistEntryDur) || doAdvancePlaylist) } jsonTransitionOnce = true; - strip.setTransition(fadeTransition ? playlistEntries[playlistIndex].tr * 100 : 0); + strip.setTransition(playlistEntries[playlistIndex].tr * 100); playlistEntryDur = playlistEntries[playlistIndex].dur; applyPresetFromPlaylist(playlistEntries[playlistIndex].preset); doAdvancePlaylist = false; diff --git a/wled00/set.cpp b/wled00/set.cpp index 27ac6b805..88249d3c4 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -134,8 +134,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) strip.correctWB = request->hasArg(F("CCT")); strip.cctFromRgb = request->hasArg(F("CR")); cctICused = request->hasArg(F("IC")); - strip.cctBlending = request->arg(F("CB")).toInt(); - Bus::setCCTBlend(strip.cctBlending); + uint8_t cctBlending = request->arg(F("CB")).toInt(); + Bus::setCCTBlend(cctBlending); Bus::setGlobalAWMode(request->arg(F("AW")).toInt()); strip.setTargetFps(request->arg(F("FR")).toInt()); useGlobalLedBuffer = request->hasArg(F("LD")); @@ -326,11 +326,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) } NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal); // fill look-up table - fadeTransition = request->hasArg(F("TF")); - modeBlending = request->hasArg(F("EB")); t = request->arg(F("TD")).toInt(); if (t >= 0) transitionDelayDefault = t; - strip.paletteFade = request->hasArg(F("PF")); t = request->arg(F("TP")).toInt(); randomPaletteChangeTime = MIN(255,MAX(1,t)); useHarmonicRandomPalette = request->hasArg(F("TH")); @@ -1148,7 +1145,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) pos = req.indexOf(F("TT=")); if (pos > 0) transitionDelay = getNumVal(&req, pos); - if (fadeTransition) strip.setTransition(transitionDelay); + strip.setTransition(transitionDelay); //set time (unix timestamp) pos = req.indexOf(F("ST=")); diff --git a/wled00/udp.cpp b/wled00/udp.cpp index b7be591e7..4395b285d 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -225,10 +225,8 @@ static void parseNotifyPacket(const uint8_t *udpIn) { // set transition time before making any segment changes if (version > 3) { - if (fadeTransition) { - jsonTransitionOnce = true; - strip.setTransition(((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00)); - } + jsonTransitionOnce = true; + strip.setTransition(((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00)); } //apply colors from notification to main segment, only if not syncing full segments diff --git a/wled00/util.cpp b/wled00/util.cpp index ed38ae180..4f2de0a2a 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -595,6 +595,13 @@ float mapf(float x, float in_min, float in_max, float out_min, float out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } +uint32_t hashInt(uint32_t s) { + // borrowed from https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key + s = ((s >> 16) ^ s) * 0x45d9f3b; + s = ((s >> 16) ^ s) * 0x45d9f3b; + return (s >> 16) ^ s; +} + // 32 bit random number generator, inlining uses more code, use hw_random16() if speed is critical (see fcn_declare.h) uint32_t hw_random(uint32_t upperlimit) { uint32_t rnd = hw_random(); @@ -608,4 +615,4 @@ int32_t hw_random(int32_t lowerlimit, int32_t upperlimit) { } uint32_t diff = upperlimit - lowerlimit; return hw_random(diff) + lowerlimit; -} \ No newline at end of file +} diff --git a/wled00/wled.h b/wled00/wled.h index b7f1ae710..371546613 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -588,8 +588,7 @@ WLED_GLOBAL bool wasConnected _INIT(false); WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same // transitions -WLED_GLOBAL bool fadeTransition _INIT(true); // enable crossfading brightness/color -WLED_GLOBAL bool modeBlending _INIT(true); // enable effect blending +WLED_GLOBAL uint8_t blendingStyle _INIT(0); // effect blending/transitionig style WLED_GLOBAL bool transitionActive _INIT(false); WLED_GLOBAL uint16_t transitionDelay _INIT(750); // global transition duration WLED_GLOBAL uint16_t transitionDelayDefault _INIT(750); // default transition time (stored in cfg.json) diff --git a/wled00/wled_eeprom.cpp b/wled00/wled_eeprom.cpp index 8582b49df..e82493d11 100644 --- a/wled00/wled_eeprom.cpp +++ b/wled00/wled_eeprom.cpp @@ -224,7 +224,7 @@ void loadSettingsFromEEPROM() if (lastEEPROMversion > 7) { - strip.paletteFade = EEPROM.read(374); + //strip.paletteFade = EEPROM.read(374); strip.paletteBlend = EEPROM.read(382); for (int i = 0; i < 8; ++i) diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 957ccebda..78d2d7d56 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -285,7 +285,7 @@ void getSettingsJS(byte subPage, Print& settingsScript) printSetFormCheckbox(settingsScript,PSTR("CCT"),strip.correctWB); printSetFormCheckbox(settingsScript,PSTR("IC"),cctICused); printSetFormCheckbox(settingsScript,PSTR("CR"),strip.cctFromRgb); - printSetFormValue(settingsScript,PSTR("CB"),strip.cctBlending); + printSetFormValue(settingsScript,PSTR("CB"),Bus::getCCTBlend()); printSetFormValue(settingsScript,PSTR("FR"),strip.getTargetFps()); printSetFormValue(settingsScript,PSTR("AW"),Bus::getGlobalAWMode()); printSetFormCheckbox(settingsScript,PSTR("LD"),useGlobalLedBuffer); @@ -369,10 +369,7 @@ void getSettingsJS(byte subPage, Print& settingsScript) printSetFormCheckbox(settingsScript,PSTR("GB"),gammaCorrectBri); printSetFormCheckbox(settingsScript,PSTR("GC"),gammaCorrectCol); dtostrf(gammaCorrectVal,3,1,nS); printSetFormValue(settingsScript,PSTR("GV"),nS); - printSetFormCheckbox(settingsScript,PSTR("TF"),fadeTransition); - printSetFormCheckbox(settingsScript,PSTR("EB"),modeBlending); printSetFormValue(settingsScript,PSTR("TD"),transitionDelayDefault); - printSetFormCheckbox(settingsScript,PSTR("PF"),strip.paletteFade); printSetFormValue(settingsScript,PSTR("TP"),randomPaletteChangeTime); printSetFormCheckbox(settingsScript,PSTR("TH"),useHarmonicRandomPalette); printSetFormValue(settingsScript,PSTR("BF"),briMultiplier);