diff --git a/usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.h b/usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.h index 85a9a16054..c3e1ce17b2 100644 --- a/usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.h +++ b/usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.h @@ -95,9 +95,9 @@ class RotaryEncoderBrightnessColor : public Usermod } else { - fastled_col.red = col[0]; - fastled_col.green = col[1]; - fastled_col.blue = col[2]; + fastled_col.red = colPri[0]; + fastled_col.green = colPri[1]; + fastled_col.blue = colPri[2]; prim_hsv = rgb2hsv_approximate(fastled_col); new_val = (int16_t)prim_hsv.h + fadeAmount; if (new_val > 255) @@ -106,9 +106,9 @@ class RotaryEncoderBrightnessColor : public Usermod new_val += 255; // roll-over if smaller than 0 prim_hsv.h = (byte)new_val; hsv2rgb_rainbow(prim_hsv, fastled_col); - col[0] = fastled_col.red; - col[1] = fastled_col.green; - col[2] = fastled_col.blue; + colPri[0] = fastled_col.red; + colPri[1] = fastled_col.green; + colPri[2] = fastled_col.blue; } } else if (Enc_B == LOW) @@ -120,9 +120,9 @@ class RotaryEncoderBrightnessColor : public Usermod } else { - fastled_col.red = col[0]; - fastled_col.green = col[1]; - fastled_col.blue = col[2]; + fastled_col.red = colPri[0]; + fastled_col.green = colPri[1]; + fastled_col.blue = colPri[2]; prim_hsv = rgb2hsv_approximate(fastled_col); new_val = (int16_t)prim_hsv.h - fadeAmount; if (new_val > 255) @@ -131,9 +131,9 @@ class RotaryEncoderBrightnessColor : public Usermod new_val += 255; // roll-over if smaller than 0 prim_hsv.h = (byte)new_val; hsv2rgb_rainbow(prim_hsv, fastled_col); - col[0] = fastled_col.red; - col[1] = fastled_col.green; - col[2] = fastled_col.blue; + colPri[0] = fastled_col.red; + colPri[1] = fastled_col.green; + colPri[2] = fastled_col.blue; } } //call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification) diff --git a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h index 383c1193eb..9533fa21bf 100644 --- a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h +++ b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h @@ -518,7 +518,7 @@ void RotaryEncoderUIUsermod::setup() loopTime = millis(); - currentCCT = (approximateKelvinFromRGB(RGBW32(col[0], col[1], col[2], col[3])) - 1900) >> 5; + currentCCT = (approximateKelvinFromRGB(RGBW32(colPri[0], colPri[1], colPri[2], colPri[3])) - 1900) >> 5; if (!initDone) sortModesAndPalettes(); @@ -920,17 +920,17 @@ void RotaryEncoderUIUsermod::changeHue(bool increase){ display->updateRedrawTime(); #endif currentHue1 = max(min((increase ? currentHue1+fadeAmount : currentHue1-fadeAmount), 255), 0); - colorHStoRGB(currentHue1*256, currentSat1, col); + colorHStoRGB(currentHue1*256, currentSat1, colPri); stateChanged = true; if (applyToAll) { for (unsigned i=0; iupdateRedrawTime(); #endif currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0); - colorHStoRGB(currentHue1*256, currentSat1, col); + colorHStoRGB(currentHue1*256, currentSat1, colPri); if (applyToAll) { for (unsigned i=0; i= (int)vWidth() || y0 < 0 || y0 >= (int)vHeight()) continue; // drawing off-screen if (((bits>>(j+(8-w))) & 0x01)) { // bit set - setPixelColorXY(x0, y0, c); + setPixelColorXY(x0, y0, c.color32); } } _colorScaled = false; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 65cb2640e7..9acc36e7c8 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -270,7 +270,7 @@ void Segment::startTransition(uint16_t dur) { _t->_briT = on ? opacity : 0; _t->_cctT = cct; #ifndef WLED_DISABLE_MODE_BLEND - swapSegenv(_t->_segT); + swapSegenv(_t->_segT); // copy runtime data to temporary _t->_modeT = mode; _t->_segT._dataLenT = 0; _t->_segT._dataT = nullptr; @@ -282,6 +282,13 @@ void Segment::startTransition(uint16_t dur) { _t->_segT._dataLenT = _dataLen; } } + DEBUG_PRINTF_P(PSTR("-- pal: %d, bri: %d, C:[%08X,%08X,%08X], m: %d\n"), + (int)_t->_palTid, + (int)_t->_briT, + _t->_segT._colorT[0], + _t->_segT._colorT[1], + _t->_segT._colorT[2], + (int)_t->_modeT); #else for (size_t i=0; i_colorT[i] = colors[i]; #endif @@ -502,21 +509,17 @@ void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, ui #ifndef WLED_DISABLE_2D if (Segment::maxHeight>1) boundsUnchanged &= (startY == i1Y && stopY == i2Y); // 2D #endif + + if (stop && (spc > 0 || m12 != map1D2D)) clear(); +/* if (boundsUnchanged && (!grp || (grouping == grp && spacing == spc)) && (ofs == UINT16_MAX || ofs == offset) && (m12 == map1D2D) ) return; - +*/ stateChanged = true; // send UDP/WS broadcast - if (stop || spc != spacing || m12 != map1D2D) { - _vWidth = virtualWidth(); - _vHeight = virtualHeight(); - _vLength = virtualLength(); - _segBri = currentBri(); - fill(BLACK); // turn old segment range off or clears pixels if changing spacing (requires _vWidth/_vHeight/_vLength/_segBri) - } if (grp) { // prevent assignment of 0 grouping = grp; spacing = spc; @@ -527,10 +530,7 @@ void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, ui if (ofs < UINT16_MAX) offset = ofs; map1D2D = constrain(m12, 0, 7); - DEBUG_PRINT(F("setUp segment: ")); DEBUG_PRINT(i1); - DEBUG_PRINT(','); DEBUG_PRINT(i2); - DEBUG_PRINT(F(" -> ")); DEBUG_PRINT(i1Y); - DEBUG_PRINT(','); DEBUG_PRINTLN(i2Y); + DEBUG_PRINTF_P(PSTR("Segment geometry: %d,%d -> %d,%d\n"), (int)i1, (int)i2, (int)i1Y, (int)i2Y); markForReset(); if (boundsUnchanged) return; @@ -1156,6 +1156,26 @@ void Segment::refreshLightCapabilities() { _capabilities = capabilities; } +/* + * Fills segment with black + */ +void Segment::clear() { + if (!isActive()) return; // not active + unsigned oldVW = _vWidth; + unsigned oldVH = _vHeight; + unsigned oldVL = _vLength; + unsigned oldSB = _segBri; + _vWidth = virtualWidth(); + _vHeight = virtualHeight(); + _vLength = virtualLength(); + _segBri = currentBri(); + fill(BLACK); + _vWidth = oldVW; + _vHeight = oldVH; + _vLength = oldVL; + _segBri = oldSB; +} + /* * Fills segment with color */ @@ -1175,42 +1195,45 @@ void Segment::fill(uint32_t c) { /* * fade out function, higher rate = quicker fade + * fading is highly dependant on frame rate (higher frame rates, faster fading) + * each frame will fade at max 9% or as little as 0.8% */ void Segment::fade_out(uint8_t rate) { if (!isActive()) return; // not active const int cols = is2D() ? vWidth() : vLength(); const int rows = vHeight(); // will be 1 for 1D - rate = (255-rate) >> 1; - float mappedRate = 1.0f / (float(rate) + 1.1f); - - uint32_t color = colors[1]; // SEGCOLOR(1); // target color - int w2 = W(color); - int r2 = R(color); - int g2 = G(color); - int b2 = B(color); + rate = (256-rate) >> 1; + const int mappedRate = 256 / (rate + 1); for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { - color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x); + uint32_t color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x); if (color == colors[1]) continue; // already at target color - int w1 = W(color); - int r1 = R(color); - int g1 = G(color); - int b1 = B(color); - - int wdelta = (w2 - w1) * mappedRate; - int rdelta = (r2 - r1) * mappedRate; - int gdelta = (g2 - g1) * mappedRate; - int bdelta = (b2 - b1) * mappedRate; + for (int i = 0; i < 32; i += 8) { + uint8_t c2 = (colors[1]>>i); // get background channel + uint8_t c1 = (color>>i); // get foreground channel + // we can't use bitshift since we are using int + int delta = (c2 - c1) * mappedRate / 256; + // if fade isn't complete, make sure delta is at least 1 (fixes rounding issues) + if (delta == 0) delta += (c2 == c1) ? 0 : (c2 > c1) ? 1 : -1; + // stuff new value back into color + color &= ~(0xFF< w1) ? 1 : -1; - rdelta += (r2 == r1) ? 0 : (r2 > r1) ? 1 : -1; - gdelta += (g2 == g1) ? 0 : (g2 > g1) ? 1 : -1; - bdelta += (b2 == b1) ? 0 : (b2 > b1) ? 1 : -1; +// fades all pixels to secondary color +void Segment::fadeToSecondaryBy(uint8_t fadeBy) { + if (!isActive() || fadeBy == 0) return; // optimization - no scaling to apply + const int cols = is2D() ? vWidth() : vLength(); + const int rows = vHeight(); // will be 1 for 1D - if (is2D()) setPixelColorXY(x, y, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); - else setPixelColor(x, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); + for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { + if (is2D()) setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), colors[1], fadeBy)); + else setPixelColor(x, color_blend(getPixelColor(x), colors[1], fadeBy)); } } @@ -1288,12 +1311,12 @@ uint32_t Segment::color_wheel(uint8_t pos) const { * Gets a single color from the currently selected palette. * @param i Palette Index (if mapping is true, the full palette will be _virtualSegmentLength long, if false, 255). Will wrap around automatically. * @param mapping if true, LED position in segment is considered for color - * @param wrap FastLED palettes will usually wrap back to the start smoothly. Set false to get a hard edge + * @param moving FastLED palettes will usually wrap back to the start smoothly. Set to true if effect has moving palette and you want wrap. * @param mcol If the default palette 0 is selected, return the standard color 0, 1 or 2 instead. If >2, Party palette is used instead * @param pbri Value to scale the brightness of the returned color by. Default is 255. (no scaling) * @returns Single color from palette */ -uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) const { +uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool moving, uint8_t mcol, uint8_t pbri) const { uint32_t color = getCurrentColor(mcol < NUM_COLORS ? mcol : 0); // default palette or no RGB support on segment if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) { @@ -1303,9 +1326,15 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_ const int vL = vLength(); unsigned paletteIndex = i; 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 = ColorFromPaletteWLED(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global + // paletteBlend: 0 - wrap when moving, 1 - always wrap, 2 - never wrap, 3 - none (undefined/no interpolation of palette entries) + // ColorFromPalette interpolations are: NOBLEND, LINEARBLEND, LINEARBLEND_NOWRAP + TBlendType blend = NOBLEND; + switch (strip.paletteBlend) { // NOTE: paletteBlend should be global + case 0: blend = moving ? LINEARBLEND : LINEARBLEND_NOWRAP; break; + case 1: blend = LINEARBLEND; break; + case 2: blend = LINEARBLEND_NOWRAP; break; + } + CRGBW palcol = ColorFromPalette(_currentPalette, paletteIndex, pbri, blend); palcol.w = W(color); return palcol.color32; @@ -1487,7 +1516,7 @@ void WS2812FX::service() { _segment_index = 0; for (segment &seg : _segments) { - if (_suspend) return; // immediately stop processing segments if suspend requested during service() + if (_suspend) break; // immediately stop processing segments if suspend requested during service() // process transition (mode changes in the middle of transition) seg.handleTransition(); @@ -1518,6 +1547,11 @@ void WS2812FX::service() { #ifndef WLED_DISABLE_MODE_BLEND Segment::setClippingRect(0, 0); // disable clipping (just in case) if (seg.isInTransition()) { + // a hack to determine if effect has changed + uint8_t m = seg.currentMode(); + Segment::modeBlend(true); // set semaphore + bool sameEffect = (m == seg.currentMode()); + Segment::modeBlend(false); // clear semaphore // set clipping rectangle // new mode is run inside clipping area and old mode outside clipping area unsigned p = seg.progress(); @@ -1526,7 +1560,20 @@ void WS2812FX::service() { 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) + if (w*h == 1) blendingStyle = BLEND_STYLE_FADE; // disable style for single pixel segments (use fade instead) + else if (sameEffect && (blendingStyle & BLEND_STYLE_PUSH_MASK)) { + // when effect stays the same push will look awful, change it to swipe + switch (blendingStyle) { + case BLEND_STYLE_PUSH_BR: + case BLEND_STYLE_PUSH_TR: + case BLEND_STYLE_PUSH_RIGHT: blendingStyle = BLEND_STYLE_SWIPE_RIGHT; break; + case BLEND_STYLE_PUSH_BL: + case BLEND_STYLE_PUSH_TL: + case BLEND_STYLE_PUSH_LEFT: blendingStyle = BLEND_STYLE_SWIPE_LEFT; break; + case BLEND_STYLE_PUSH_DOWN: blendingStyle = BLEND_STYLE_SWIPE_DOWN; break; + case BLEND_STYLE_PUSH_UP: blendingStyle = BLEND_STYLE_SWIPE_UP; break; + } + } switch (blendingStyle) { case BLEND_STYLE_FAIRY_DUST: // fairy dust (must set entire segment, see isPixelXYClipped()) Segment::setClippingRect(0, w, 0, h); @@ -1572,7 +1619,7 @@ void WS2812FX::service() { Segment::setClippingRect(0, dw, h - dh, h); break; } - frameDelay = (*_mode[seg.currentMode()])(); // run new/current mode + frameDelay = (*_mode[m])(); // run new/current mode // now run old/previous mode Segment::tmpsegd_t _tmpSegData; Segment::modeBlend(true); // set semaphore @@ -1608,8 +1655,8 @@ void WS2812FX::service() { if (doShow) { yield(); Segment::handleRandomPalette(); // slowly transition random palette; move it into for loop when each segment has individual random palette - show(); _lastServiceShow = nowUp; // update timestamp, for precise FPS control + if (!_suspend) show(); } #ifdef WLED_DEBUG if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime); diff --git a/wled00/button.cpp b/wled00/button.cpp index 5144f09f29..cf8fabe42e 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -40,7 +40,7 @@ void longPressAction(uint8_t b) { if (!macroLongPress[b]) { switch (b) { - case 0: setRandomColor(col); colorUpdated(CALL_MODE_BUTTON); break; + case 0: setRandomColor(colPri); colorUpdated(CALL_MODE_BUTTON); break; case 1: if(buttonBriDirection) { if (bri == 255) break; // avoid unnecessary updates to brightness @@ -230,7 +230,7 @@ void handleAnalog(uint8_t b) effectPalette = constrain(effectPalette, 0, strip.getPaletteCount()-1); // map is allowed to "overshoot", so we need to contrain the result } else if (macroDoublePress[b] == 200) { // primary color, hue, full saturation - colorHStoRGB(aRead*256,255,col); + colorHStoRGB(aRead*256,255,colPri); } else { // otherwise use "double press" for segment selection Segment& seg = strip.getSegment(macroDoublePress[b]); diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 7bfd71c292..ba38d5a151 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -10,12 +10,13 @@ */ uint32_t color_blend(uint32_t color1, uint32_t color2, uint8_t blend) { // min / max blend checking is omitted: calls with 0 or 255 are rare, checking lowers overall performance - uint32_t rb1 = color1 & 0x00FF00FF; - uint32_t wg1 = (color1>>8) & 0x00FF00FF; - uint32_t rb2 = color2 & 0x00FF00FF; - uint32_t wg2 = (color2>>8) & 0x00FF00FF; - uint32_t rb3 = ((((rb1 << 8) | rb2) + (rb2 * blend) - (rb1 * blend)) >> 8) & 0x00FF00FF; - uint32_t wg3 = ((((wg1 << 8) | wg2) + (wg2 * blend) - (wg1 * blend))) & 0xFF00FF00; + const uint32_t TWO_CHANNEL_MASK = 0x00FF00FF; // mask for R and B channels or W and G if negated (poorman's SIMD; https://github.com/wled/WLED/pull/4568#discussion_r1986587221) + uint32_t rb1 = color1 & TWO_CHANNEL_MASK; // extract R & B channels from color1 + uint32_t wg1 = (color1 >> 8) & TWO_CHANNEL_MASK; // extract W & G channels from color1 (shifted for multiplication later) + uint32_t rb2 = color2 & TWO_CHANNEL_MASK; // extract R & B channels from color2 + uint32_t wg2 = (color2 >> 8) & TWO_CHANNEL_MASK; // extract W & G channels from color2 (shifted for multiplication later) + uint32_t rb3 = ((((rb1 << 8) | rb2) + (rb2 * blend) - (rb1 * blend)) >> 8) & TWO_CHANNEL_MASK; // blend red and blue + uint32_t wg3 = ((((wg1 << 8) | wg2) + (wg2 * blend) - (wg1 * blend))) & ~TWO_CHANNEL_MASK; // negated mask for white and green return rb3 | wg3; } @@ -28,8 +29,9 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool preserveCR) { if (c1 == BLACK) return c2; if (c2 == BLACK) return c1; - uint32_t rb = (c1 & 0x00FF00FF) + (c2 & 0x00FF00FF); // mask and add two colors at once - uint32_t wg = ((c1>>8) & 0x00FF00FF) + ((c2>>8) & 0x00FF00FF); + const uint32_t TWO_CHANNEL_MASK = 0x00FF00FF; // mask for R and B channels or W and G if negated + uint32_t rb = ( c1 & TWO_CHANNEL_MASK) + ( c2 & TWO_CHANNEL_MASK); // mask and add two colors at once + uint32_t wg = ((c1>>8) & TWO_CHANNEL_MASK) + ((c2>>8) & TWO_CHANNEL_MASK); uint32_t r = rb >> 16; // extract single color values uint32_t b = rb & 0xFFFF; uint32_t w = wg >> 16; @@ -44,10 +46,10 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool preserveCR) //max = b > max ? b : max; //max = w > max ? w : max; if (max > 255) { - uint32_t scale = (uint32_t(255)<<8) / max; // division of two 8bit (shifted) values does not work -> use bit shifts and multiplaction instead - rb = ((rb * scale) >> 8) & 0x00FF00FF; // - wg = (wg * scale) & 0xFF00FF00; - } else wg = wg << 8; //shift white and green back to correct position + const uint32_t scale = (uint32_t(255)<<8) / max; // division of two 8bit (shifted) values does not work -> use bit shifts and multiplaction instead + rb = ((rb * scale) >> 8) & TWO_CHANNEL_MASK; + wg = (wg * scale) & ~TWO_CHANNEL_MASK; + } else wg <<= 8; //shift white and green back to correct position return rb | wg; } else { r = r > 255 ? 255 : r; @@ -77,8 +79,9 @@ uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) addRemains |= B(c1) ? 0x00000001 : 0; addRemains |= W(c1) ? 0x01000000 : 0; } - uint32_t rb = (((c1 & 0x00FF00FF) * scale) >> 8) & 0x00FF00FF; // scale red and blue - uint32_t wg = (((c1 & 0xFF00FF00) >> 8) * scale) & 0xFF00FF00; // scale white and green + const uint32_t TWO_CHANNEL_MASK = 0x00FF00FF; + uint32_t rb = (((c1 & TWO_CHANNEL_MASK) * scale) >> 8) & TWO_CHANNEL_MASK; // scale red and blue + uint32_t wg = (((c1 >> 8) & TWO_CHANNEL_MASK) * scale) & ~TWO_CHANNEL_MASK; // scale white and green scaledcolor = (rb | wg) + addRemains; return scaledcolor; } @@ -95,7 +98,7 @@ uint32_t ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t unsigned red1 = entry->r; unsigned green1 = entry->g; unsigned blue1 = entry->b; - if(lo4 && blendType != NOBLEND) { + if (lo4 && blendType != NOBLEND) { if (hi4 == 15) entry = &(pal[0]); else ++entry; unsigned f2 = (lo4 << 4); @@ -105,6 +108,7 @@ uint32_t ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t blue1 = (blue1 * f1 + (unsigned)entry->b * f2) >> 8; } if (brightness < 255) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted + // actually color_fade(c1, brightness) uint32_t scale = brightness + 1; // adjust for rounding (bitshift) red1 = (red1 * scale) >> 8; // note: using color_fade() is 30% slower green1 = (green1 * scale) >> 8; diff --git a/wled00/data/index.js b/wled00/data/index.js index f3648d3b56..147abf3892 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -409,7 +409,9 @@ function pName(i) function isPlaylist(i) { - return pJson[i].playlist && pJson[i].playlist.ps; + if (isNumeric(i)) return pJson[i].playlist && pJson[i].playlist.ps; + if (isObj(i)) return i.playlist && i.playlist.ps; + return false; } function papiVal(i) @@ -775,8 +777,8 @@ function populateSegments(s) } let segp = `
`+ - ``+ - `
`+ + ``+ + `
`+ ``+ `
`+ `
`+ @@ -814,7 +816,7 @@ function populateSegments(s) cn += `
`+ ``+ `
`+ `&#x${inst.frz ? (li.live && li.liveseg==i?'e410':'e0e8') : 'e325'};`+ @@ -1666,13 +1668,17 @@ function setEffectParameters(idx) paOnOff[0] = paOnOff[0].substring(0,dPos); } if (paOnOff.length>0 && paOnOff[0] != "!") text = paOnOff[0]; + gId("adPal").classList.remove("hide"); + if (lastinfo.cpalcount>0) gId("rmPal").classList.remove("hide"); } else { // disable palette list text += ' not used'; palw.style.display = "none"; + gId("adPal").classList.add("hide"); + gId("rmPal").classList.add("hide"); // Close palette dialog if not available - if (gId("palw").lastElementChild.tagName == "DIALOG") { - gId("palw").lastElementChild.close(); + if (palw.lastElementChild.tagName == "DIALOG") { + palw.lastElementChild.close(); } } pall.innerHTML = icon + text; @@ -1887,7 +1893,7 @@ function makeSeg() function resetUtil(off=false) { gId('segutil').innerHTML = `
` - + '' + + '' + `
Add segment
` + '
' + `` @@ -1901,15 +1907,16 @@ function resetUtil(off=false) if (lSeg>2) d.querySelectorAll("#Segments .pop").forEach((e)=>{e.classList.remove("hide");}); } -function makePlSel(el, incPl=false) +function makePlSel(p, el) { var plSelContent = ""; delete pJson["0"]; // remove filler preset Object.entries(pJson).sort(cmpP).forEach((a)=>{ var n = a[1].n ? a[1].n : "Preset " + a[0]; + if (isPlaylist(a[1])) n += ' ▶'; // mark playlist if (cfg.comp.idsort) n = a[0] + ' ' + n; - if (!(!incPl && a[1].playlist && a[1].playlist.ps)) // skip playlists, sub-playlists not yet supported - plSelContent += ``; + // skip endless playlists and itself + if (!isPlaylist(a[1]) || (a[1].playlist.repeat > 0 && a[0]!=p)) plSelContent += ``; }); return plSelContent; } @@ -1934,21 +1941,23 @@ function refreshPlE(p) }); } -// p: preset ID, i: ps index +// p: preset ID, i: playlist item index function addPl(p,i) { - plJson[p].ps.splice(i+1,0,0); - plJson[p].dur.splice(i+1,0,plJson[p].dur[i]); - plJson[p].transition.splice(i+1,0,plJson[p].transition[i]); + const pl = plJson[p]; + pl.ps.splice(i+1,0,1); + pl.dur.splice(i+1,0,pl.dur[i]); + pl.transition.splice(i+1,0,pl.transition[i]); refreshPlE(p); } function delPl(p,i) { - if (plJson[p].ps.length < 2) return; - plJson[p].ps.splice(i,1); - plJson[p].dur.splice(i,1); - plJson[p].transition.splice(i,1); + const pl = plJson[p]; + if (pl.ps.length < 2) return; + pl.ps.splice(i,1); + pl.dur.splice(i,1); + pl.transition.splice(i,1); refreshPlE(p); } @@ -1965,6 +1974,13 @@ function pleDur(p,i,field) function pleTr(p,i,field) { + const du = gId(`pl${p}du${i}`); + const dv = parseFloat(du.value); + if (dv > 0) { + field.max = dv; + if (parseFloat(field.value) > dv) + field.value = du.value; + } if (field.validity.valid) plJson[p].transition[i] = Math.floor(field.value*10); } @@ -1984,6 +2000,17 @@ function plR(p) } } +function plM(p) +{ + const man = gId(`pl${p}manual`).checked; + plJson[p].dur.forEach((e,i)=>{ + const d = gId(`pl${p}du${i}`); + plJson[p].dur[i] = e = man ? 0 : 100; + d.value = e/10; // 10s default + d.readOnly = man; + }); +} + function makeP(i,pl) { var content = ""; @@ -1997,12 +2024,17 @@ function makeP(i,pl) r: false, end: 0 }; - var rep = plJson[i].repeat ? plJson[i].repeat : 0; + const rep = plJson[i].repeat ? plJson[i].repeat : 0; + const man = plJson[i].dur == 0; content = `
+
`; @@ -2086,25 +2118,26 @@ function makePUtil() function makePlEntry(p,i) { + const man = gId(`pl${p}manual`).checked; return `
- + - - + +
DurationDuration (0=inf.) Transition #${i+1}
ssss
@@ -2657,28 +2690,28 @@ function fromRgb() var g = gId('sliderG').value; var b = gId('sliderB').value; setPicker(`rgb(${r},${g},${b})`); - let cd = gId('csl').children; // color slots - cd[csel].dataset.r = r; - cd[csel].dataset.g = g; - cd[csel].dataset.b = b; - setCSL(cd[csel]); + let cd = gId('csl').children[csel]; // color slots + cd.dataset.r = r; + cd.dataset.g = g; + cd.dataset.b = b; + setCSL(cd); } function fromW() { let w = gId('sliderW'); - let cd = gId('csl').children; // color slots - cd[csel].dataset.w = w.value; - setCSL(cd[csel]); + let cd = gId('csl').children[csel]; // color slots + cd.dataset.w = w.value; + setCSL(cd); updateTrail(w); } // sr 0: from RGB sliders, 1: from picker, 2: from hex function setColor(sr) { - var cd = gId('csl').children; // color slots - let cdd = cd[csel].dataset; - let w = 0, r,g,b; + var cd = gId('csl').children[csel]; // color slots + let cdd = cd.dataset; + let w = parseInt(cdd.w), r = parseInt(cdd.r), g = parseInt(cdd.g), b = parseInt(cdd.b); if (sr == 1 && isRgbBlack(cdd)) cpick.color.setChannel('hsv', 'v', 100); if (sr != 2 && hasWhite) w = parseInt(gId('sliderW').value); var col = cpick.color.rgb; @@ -2686,7 +2719,7 @@ function setColor(sr) cdd.g = g = hasRGB ? col.g : w; cdd.b = b = hasRGB ? col.b : w; cdd.w = w; - setCSL(cd[csel]); + setCSL(cd); var obj = {"seg": {"col": [[],[],[]]}}; obj.seg.col[csel] = [r, g, b, w]; requestJson(obj); diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 79b41bcdfd..a45ed050ef 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -24,6 +24,7 @@ function is16b(t) { return !!(gT(t).c & 0x10); } // is digital 16 bit type function mustR(t) { return !!(gT(t).c & 0x20); } // Off refresh is mandatory function numPins(t){ return Math.max(gT(t).t.length, 1); } // type length determines number of GPIO pins + function chrID(x) { return String.fromCharCode((x<10?48:55)+x); } function S() { getLoc(); loadJS(getURL('/settings/s.js?p=2'), false, ()=>{ @@ -138,7 +139,7 @@ gId("ppldis").style.display = ppl ? 'inline' : 'none'; // set PPL minimum value and clear actual PPL limit if ABL is disabled d.Sf.querySelectorAll("#mLC input[name^=MA]").forEach((i,x)=>{ - var n = String.fromCharCode((x<10?48:55)+x); + var n = chrID(x); gId("PSU"+n).style.display = ppl ? "inline" : "none"; const t = parseInt(d.Sf["LT"+n].value); // LED type SELECT const c = parseInt(d.Sf["LC"+n].value); //get LED count @@ -169,7 +170,7 @@ // select appropriate LED current d.Sf.querySelectorAll("#mLC select[name^=LAsel]").forEach((sel,x)=>{ sel.value = 0; // set custom - var n = String.fromCharCode((x<10?48:55)+x); + var n = chrID(x); if (en) switch (parseInt(d.Sf["LA"+n].value)) { case 0: break; // disable ABL @@ -400,7 +401,7 @@ } function lastEnd(i) { if (i-- < 1) return 0; - var s = String.fromCharCode((i<10?48:55)+i); + var s = chrID(i); v = parseInt(d.getElementsByName("LS"+s)[0].value) + parseInt(d.getElementsByName("LC"+s)[0].value); var t = parseInt(d.getElementsByName("LT"+s)[0].value); if (isPWM(t)) v = 1; //PWM busses @@ -507,7 +508,7 @@ function addCOM(start=0,len=1,co=0) { var i = gEBCN("com_entry").length; if (i >= maxCO) return; - var s = String.fromCharCode((i<10?48:55)+i); + var s = chrID(i); var b = `

${i+1}: Start:   @@ -561,7 +562,7 @@ function addBtn(i,p,t) { var c = gId("btns").innerHTML; - var s = String.fromCharCode((i<10?48:55)+i); + var s = chrID(i); c += `Button ${i} GPIO: `; c += `