kopia lustrzana https://github.com/Aircoookie/WLED
				
				
				
			WLED 2D matrix support.
- Added 2 sample effects to UI. - 2D segment details. - 1D effect upscaling to 2D. - 2D setPixelColorXY() and other utility functions - Arbitrary panel position & oritentation. - 2D settings with physical to logical mapping. - Bump version.pull/2737/head
							rodzic
							
								
									9f71a6ab18
								
							
						
					
					
						commit
						f0d36fd769
					
				| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
{
 | 
			
		||||
  "name": "wled",
 | 
			
		||||
  "version": "0.13.2-bl0",
 | 
			
		||||
  "version": "0.14.0-bl0",
 | 
			
		||||
  "lockfileVersion": 1,
 | 
			
		||||
  "requires": true,
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
{
 | 
			
		||||
  "name": "wled",
 | 
			
		||||
  "version": "0.13.2-bl0",
 | 
			
		||||
  "version": "0.14.0-bl0",
 | 
			
		||||
  "description": "Tools for WLED project",
 | 
			
		||||
  "main": "tools/cdata.js",
 | 
			
		||||
  "directories": {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -366,6 +366,18 @@ writeChunks(
 | 
			
		|||
            "</script><script src=\"/settings/s.js?p=8\"></script>"
 | 
			
		||||
          )
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      file: "settings_2D.htm",
 | 
			
		||||
      name: "PAGE_settings_2D",
 | 
			
		||||
      method: "gzip",
 | 
			
		||||
      filter: "html-minify",
 | 
			
		||||
      mangle: (str) =>
 | 
			
		||||
        str
 | 
			
		||||
          .replace(
 | 
			
		||||
            /function GetV().*\<\/script\>/gms,
 | 
			
		||||
            "</script><script src=\"/settings/s.js?p=10\"></script>"
 | 
			
		||||
          )
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      file: "settings_pin.htm",
 | 
			
		||||
      name: "PAGE_settings_pin",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2346,7 +2346,7 @@ uint16_t WS2812FX::mode_ripple_rainbow(void) {
 | 
			
		|||
// incandescent bulbs change color as they get dim down.
 | 
			
		||||
#define COOL_LIKE_INCANDESCENT 1
 | 
			
		||||
 | 
			
		||||
CRGB IRAM_ATTR WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
 | 
			
		||||
CRGB WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
 | 
			
		||||
{
 | 
			
		||||
  // Overall twinkle speed (changed)
 | 
			
		||||
  uint16_t ticks = ms / SEGENV.aux0;
 | 
			
		||||
| 
						 | 
				
			
			@ -4226,4 +4226,71 @@ uint16_t WS2812FX::mode_aurora(void) {
 | 
			
		|||
  }
 | 
			
		||||
  
 | 
			
		||||
  return FRAMETIME;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
//***************************  2D routines  ***********************************
 | 
			
		||||
 | 
			
		||||
// sample 2D routine
 | 
			
		||||
uint16_t WS2812FX::mode_2DBlackHole_A() {            // By: Stepko https://editor.soulmatelights.com/gallery/1012 , Modified by: Andrew Tuline
 | 
			
		||||
  uint16_t w = SEGMENT.virtualWidth();
 | 
			
		||||
  uint16_t h = SEGMENT.virtualHeight();
 | 
			
		||||
  uint16_t dataSize = sizeof(CRGB) * w * h;
 | 
			
		||||
 | 
			
		||||
  if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
 | 
			
		||||
  CRGB *leds = reinterpret_cast<CRGB*>(SEGENV.data);
 | 
			
		||||
 | 
			
		||||
  uint16_t x, y;
 | 
			
		||||
 | 
			
		||||
  // initialize on first call
 | 
			
		||||
  if (SEGENV.call == 0) {
 | 
			
		||||
    for (y = 0; y < h; y++) for (x = 0; x < w; x++) {
 | 
			
		||||
      leds[x + y * w] = CRGB::Black;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fadeToBlackBy(32, leds);  // create fading trails
 | 
			
		||||
  float t = (float)(millis())/128;
 | 
			
		||||
  for (byte i = 0; i < 8; i++) {
 | 
			
		||||
    x = beatsin8(SEGMENT.c1x>>3, 0, w - 1, 0, ((i % 2) ? 128 : 0) + t * i);
 | 
			
		||||
    y = beatsin8(10            , 0, h - 1, 0, ((i % 2) ? 192 : 64) + t * i);
 | 
			
		||||
    leds[x + y * w] += CHSV(i*32, 255, 255);
 | 
			
		||||
  }
 | 
			
		||||
  for (byte i = 0; i < 8; i++) {
 | 
			
		||||
    x = beatsin8(SEGMENT.c2x>>3, w/4, w - 1 - w/4, 0, ((i % 2) ? 128 : 0) + t * i);
 | 
			
		||||
    y = beatsin8(SEGMENT.c3x>>3, h/4, h - 1 - h/4, 0, ((i % 2) ? 192 : 64) + t * i);
 | 
			
		||||
    leds[x + y * w] += CHSV(i*32, 255, 255);
 | 
			
		||||
  }
 | 
			
		||||
  leds[w/2 * (1 + h)] = CHSV(0,0,255);
 | 
			
		||||
  blur2d(16, leds);
 | 
			
		||||
 | 
			
		||||
  for (y = 0; y < h; y++) for (x = 0; x < w; x++) {
 | 
			
		||||
    uint16_t o = x + y * w;
 | 
			
		||||
    setPixelColorXY(x, y, leds[o]);
 | 
			
		||||
  }
 | 
			
		||||
  return FRAMETIME;
 | 
			
		||||
} // mode_2DBlackHole()
 | 
			
		||||
 | 
			
		||||
// same as above not using leds[]
 | 
			
		||||
uint16_t WS2812FX::mode_2DBlackHole_B() {            // By: Stepko https://editor.soulmatelights.com/gallery/1012 , Modified by: Andrew Tuline
 | 
			
		||||
  uint16_t w = SEGMENT.virtualWidth();
 | 
			
		||||
  uint16_t h = SEGMENT.virtualHeight();
 | 
			
		||||
  uint16_t x, y;
 | 
			
		||||
 | 
			
		||||
  fade_out2D(32);  // create fading trails
 | 
			
		||||
  float t = (float)(millis())/128;
 | 
			
		||||
  for (byte i = 0; i < 8; i++) {
 | 
			
		||||
    x = beatsin8(SEGMENT.c1x>>3, 0, w - 1, 0, ((i % 2) ? 128 : 0) + t * i);
 | 
			
		||||
    y = beatsin8(10            , 0, h - 1, 0, ((i % 2) ? 192 : 64) + t * i);
 | 
			
		||||
    setPixelColorXY(x, y, col_to_crgb(getPixelColorXY(x,y)) + CHSV(i*32, 255, 255));
 | 
			
		||||
  }
 | 
			
		||||
  for (byte i = 0; i < 8; i++) {
 | 
			
		||||
    x = beatsin8(SEGMENT.c2x>>3, w/4, w - 1 - w/4, 0, ((i % 2) ? 128 : 0) + t * i);
 | 
			
		||||
    y = beatsin8(SEGMENT.c3x>>3, h/4, h - 1 - h/4, 0, ((i % 2) ? 192 : 64) + t * i);
 | 
			
		||||
    setPixelColorXY(x, y, col_to_crgb(getPixelColorXY(x,y)) + CHSV(i*32, 255, 255));
 | 
			
		||||
  }
 | 
			
		||||
  setPixelColorXY(w/2, h/2, WHITE);
 | 
			
		||||
  blur2d(16);
 | 
			
		||||
  return FRAMETIME;
 | 
			
		||||
} // mode_2DBlackHole()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										150
									
								
								wled00/FX.h
								
								
								
								
							
							
						
						
									
										150
									
								
								wled00/FX.h
								
								
								
								
							| 
						 | 
				
			
			@ -38,6 +38,9 @@
 | 
			
		|||
#define DEFAULT_SPEED      (uint8_t)128
 | 
			
		||||
#define DEFAULT_INTENSITY  (uint8_t)128
 | 
			
		||||
#define DEFAULT_COLOR      (uint32_t)0xFFAA00
 | 
			
		||||
#define DEFAULT_C1         (uint8_t)128
 | 
			
		||||
#define DEFAULT_C2         (uint8_t)128
 | 
			
		||||
#define DEFAULT_C3         (uint8_t)128
 | 
			
		||||
 | 
			
		||||
#ifndef MIN
 | 
			
		||||
#define MIN(a,b) ((a)<(b)?(a):(b))
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +112,9 @@
 | 
			
		|||
// bit    0: segment is selected
 | 
			
		||||
#define NO_OPTIONS   (uint8_t)0x00
 | 
			
		||||
#define TRANSITIONAL (uint8_t)0x80
 | 
			
		||||
#define TRANSPOSED   (uint8_t)0x40 // rotated 90deg & reversed
 | 
			
		||||
#define REVERSE_Y_2D (uint8_t)0x20
 | 
			
		||||
#define MIRROR_Y_2D  (uint8_t)0x10
 | 
			
		||||
#define MIRROR       (uint8_t)0x08
 | 
			
		||||
#define SEGMENT_ON   (uint8_t)0x04
 | 
			
		||||
#define REVERSE      (uint8_t)0x02
 | 
			
		||||
| 
						 | 
				
			
			@ -118,8 +124,11 @@
 | 
			
		|||
#define IS_SEGMENT_ON   ((SEGMENT.options & SEGMENT_ON  ) == SEGMENT_ON  )
 | 
			
		||||
#define IS_REVERSE      ((SEGMENT.options & REVERSE     ) == REVERSE     )
 | 
			
		||||
#define IS_SELECTED     ((SEGMENT.options & SELECTED    ) == SELECTED    )
 | 
			
		||||
#define IS_REVERSE_Y_2D ((SEGMENT.options & REVERSE_Y_2D) == REVERSE_Y_2D)
 | 
			
		||||
#define IS_MIRROR_Y_2D  ((SEGMENT.options & MIRROR_Y_2D ) == MIRROR_Y_2D )
 | 
			
		||||
#define IS_TRANSPOSED   ((SEGMENT.options & TRANSPOSED  ) == TRANSPOSED  )
 | 
			
		||||
 | 
			
		||||
#define MODE_COUNT  118
 | 
			
		||||
#define MODE_COUNT  120
 | 
			
		||||
 | 
			
		||||
#define FX_MODE_STATIC                   0
 | 
			
		||||
#define FX_MODE_BLINK                    1
 | 
			
		||||
| 
						 | 
				
			
			@ -239,6 +248,8 @@
 | 
			
		|||
#define FX_MODE_BLENDS                 115
 | 
			
		||||
#define FX_MODE_TV_SIMULATOR           116
 | 
			
		||||
#define FX_MODE_DYNAMIC_SMOOTH         117
 | 
			
		||||
#define FX_MODE_BLACK_HOLE_A           118
 | 
			
		||||
#define FX_MODE_BLACK_HOLE_B           119
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class WS2812FX {
 | 
			
		||||
| 
						 | 
				
			
			@ -249,24 +260,35 @@ class WS2812FX {
 | 
			
		|||
 | 
			
		||||
  static WS2812FX* instance;
 | 
			
		||||
  
 | 
			
		||||
  // segment parameters
 | 
			
		||||
  public:
 | 
			
		||||
    typedef struct Segment { // 34 (35 in memory) bytes
 | 
			
		||||
      uint16_t start;
 | 
			
		||||
      uint16_t stop; //segment invalid if stop == 0
 | 
			
		||||
 | 
			
		||||
  // segment parameters
 | 
			
		||||
    typedef struct Segment { // 35 (36 in memory) bytes
 | 
			
		||||
      uint16_t start; // start index / start X coordinate 2D (left)
 | 
			
		||||
      uint16_t stop;  // stop index / stop X coordinate 2D (right); segment is invalid if stop == 0
 | 
			
		||||
      uint16_t offset;
 | 
			
		||||
      uint8_t  speed;
 | 
			
		||||
      uint8_t  intensity;
 | 
			
		||||
      uint8_t  palette;
 | 
			
		||||
      uint8_t  mode;
 | 
			
		||||
      uint8_t  options; //bit pattern: msb first: transitional needspixelstate tbd tbd (paused) on reverse selected
 | 
			
		||||
      uint16_t options; //bit pattern: msb first: [transposed mirrorY reverseY] transitional (tbd) paused needspixelstate mirrored on reverse selected
 | 
			
		||||
      uint8_t  grouping, spacing;
 | 
			
		||||
      uint8_t  opacity;
 | 
			
		||||
      uint32_t colors[NUM_COLORS];
 | 
			
		||||
      uint8_t  cct; //0==1900K, 255==10091K
 | 
			
		||||
      uint8_t  _capabilities;
 | 
			
		||||
      uint8_t c1x, c2x, c3x; // custom FX parameters
 | 
			
		||||
      uint8_t  c1x, c2x, c3x; // custom FX parameters
 | 
			
		||||
      uint16_t startY;  // start Y coodrinate 2D (top)
 | 
			
		||||
      uint16_t stopY;   // stop Y coordinate 2D (bottom)
 | 
			
		||||
      char *name;
 | 
			
		||||
      inline bool     getOption(uint8_t n)   { return ((options >> n) & 0x01); }
 | 
			
		||||
      inline bool     isSelected()           { return getOption(0); }
 | 
			
		||||
      inline bool     isActive()             { return stop > start; }
 | 
			
		||||
      inline uint16_t width()                { return options & TRANSPOSED ? stopY - startY : stop - start; }
 | 
			
		||||
      inline uint16_t height()               { return options & TRANSPOSED ? stop - start : stopY - startY; }
 | 
			
		||||
      inline uint16_t length()               { return width(); }
 | 
			
		||||
      inline uint16_t groupLength()          { return grouping + spacing; }
 | 
			
		||||
      inline uint8_t  getLightCapabilities() { return _capabilities; }
 | 
			
		||||
      bool setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed
 | 
			
		||||
        if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false;
 | 
			
		||||
        if (c == colors[slot]) return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -291,8 +313,7 @@ class WS2812FX {
 | 
			
		|||
        ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0);
 | 
			
		||||
        opacity = o;
 | 
			
		||||
      }
 | 
			
		||||
      void setOption(uint8_t n, bool val, uint8_t segn = 255)
 | 
			
		||||
      {
 | 
			
		||||
      void setOption(uint8_t n, bool val, uint8_t segn = 255) {
 | 
			
		||||
        bool prevOn = false;
 | 
			
		||||
        if (n == SEG_OPTION_ON) {
 | 
			
		||||
          prevOn = getOption(SEG_OPTION_ON);
 | 
			
		||||
| 
						 | 
				
			
			@ -300,48 +321,31 @@ class WS2812FX {
 | 
			
		|||
            ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (val) {
 | 
			
		||||
          options |= 0x01 << n;
 | 
			
		||||
        } else
 | 
			
		||||
        {
 | 
			
		||||
          options &= ~(0x01 << n);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (val) options |=   0x01 << n;
 | 
			
		||||
        else     options &= ~(0x01 << n);
 | 
			
		||||
        if (n == SEG_OPTION_ON && val && !prevOn) { //fade on
 | 
			
		||||
          ColorTransition::startTransition(0, colors[0], instance->_transitionDur, segn, 0);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      bool getOption(uint8_t n)
 | 
			
		||||
      {
 | 
			
		||||
        return ((options >> n) & 0x01);
 | 
			
		||||
      uint16_t virtualWidth() {
 | 
			
		||||
        uint16_t groupLen = groupLength();
 | 
			
		||||
        uint16_t vWidth = (width() + groupLen - 1) / groupLen;
 | 
			
		||||
        if (options & MIRROR) vWidth = (vWidth + 1) /2;  // divide by 2 if mirror, leave at least a single LED
 | 
			
		||||
        return vWidth;
 | 
			
		||||
      }
 | 
			
		||||
      inline bool isSelected()
 | 
			
		||||
      {
 | 
			
		||||
        return getOption(0);
 | 
			
		||||
      uint16_t virtualHeight() {
 | 
			
		||||
        uint16_t groupLen = groupLength();
 | 
			
		||||
        uint16_t vHeight = (height() + groupLen - 1) / groupLen;
 | 
			
		||||
        if (options & MIRROR_Y_2D) vHeight = (vHeight + 1) /2;  // divide by 2 if mirror, leave at least a single LED
 | 
			
		||||
        return vHeight;
 | 
			
		||||
      }
 | 
			
		||||
      inline bool isActive()
 | 
			
		||||
      {
 | 
			
		||||
        return stop > start;
 | 
			
		||||
      }
 | 
			
		||||
      inline uint16_t length()
 | 
			
		||||
      {
 | 
			
		||||
        return stop - start;
 | 
			
		||||
      }
 | 
			
		||||
      inline uint16_t groupLength()
 | 
			
		||||
      {
 | 
			
		||||
        return grouping + spacing;
 | 
			
		||||
      }
 | 
			
		||||
      uint16_t virtualLength()
 | 
			
		||||
      {
 | 
			
		||||
      uint16_t virtualLength() {
 | 
			
		||||
        uint16_t groupLen = groupLength();
 | 
			
		||||
        uint16_t vLength = (length() + groupLen - 1) / groupLen;
 | 
			
		||||
        if (options & MIRROR)
 | 
			
		||||
          vLength = (vLength + 1) /2;  // divide by 2 if mirror, leave at least a single LED
 | 
			
		||||
        if (options & MIRROR) vLength = (vLength + 1) /2;  // divide by 2 if mirror, leave at least a single LED
 | 
			
		||||
        return vLength;
 | 
			
		||||
      }
 | 
			
		||||
      uint8_t differs(Segment& b);
 | 
			
		||||
      inline uint8_t getLightCapabilities() {return _capabilities;}
 | 
			
		||||
      void refreshLightCapabilities();
 | 
			
		||||
    } segment;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -610,6 +614,8 @@ class WS2812FX {
 | 
			
		|||
      _mode[FX_MODE_BLENDS]                  = &WS2812FX::mode_blends;
 | 
			
		||||
      _mode[FX_MODE_TV_SIMULATOR]            = &WS2812FX::mode_tv_simulator;
 | 
			
		||||
      _mode[FX_MODE_DYNAMIC_SMOOTH]          = &WS2812FX::mode_dynamic_smooth;
 | 
			
		||||
      _mode[FX_MODE_BLACK_HOLE_A]            = &WS2812FX::mode_2DBlackHole_A;
 | 
			
		||||
      _mode[FX_MODE_BLACK_HOLE_B]            = &WS2812FX::mode_2DBlackHole_B;
 | 
			
		||||
 | 
			
		||||
      _brightness = DEFAULT_BRIGHTNESS;
 | 
			
		||||
      currentPalette = CRGBPalette16(CRGB::Black);
 | 
			
		||||
| 
						 | 
				
			
			@ -637,7 +643,7 @@ class WS2812FX {
 | 
			
		|||
      setTransitionMode(bool t),
 | 
			
		||||
      calcGammaTable(float),
 | 
			
		||||
      trigger(void),
 | 
			
		||||
      setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0, uint16_t offset = UINT16_MAX),
 | 
			
		||||
      setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0, uint16_t offset = UINT16_MAX, uint16_t startY=0, uint16_t stopY=0),
 | 
			
		||||
      setMainSegmentId(uint8_t n),
 | 
			
		||||
      restartRuntime(),
 | 
			
		||||
      resetSegments(),
 | 
			
		||||
| 
						 | 
				
			
			@ -835,6 +841,62 @@ class WS2812FX {
 | 
			
		|||
      mode_tv_simulator(void),
 | 
			
		||||
      mode_dynamic_smooth(void);
 | 
			
		||||
 | 
			
		||||
// 2D support (panels)
 | 
			
		||||
    bool
 | 
			
		||||
      isMatrix = false;
 | 
			
		||||
 | 
			
		||||
    uint8_t
 | 
			
		||||
      hPanels = 1,
 | 
			
		||||
      vPanels = 1;
 | 
			
		||||
 | 
			
		||||
    uint16_t
 | 
			
		||||
      panelH = 8,
 | 
			
		||||
      panelW = 8,
 | 
			
		||||
      matrixWidth = DEFAULT_LED_COUNT,
 | 
			
		||||
      matrixHeight = 1;
 | 
			
		||||
 | 
			
		||||
    #define WLED_MAX_PANELS 64
 | 
			
		||||
    typedef struct panel_bitfield_t {
 | 
			
		||||
      unsigned char
 | 
			
		||||
        bottomStart : 1, // starts at bottom?
 | 
			
		||||
        rightStart  : 1, // starts on right?
 | 
			
		||||
        vertical    : 1, // is vertical?
 | 
			
		||||
        serpentine  : 1; // is serpentine?
 | 
			
		||||
    } Panel;
 | 
			
		||||
    Panel
 | 
			
		||||
      matrix = {0,0,0,0},
 | 
			
		||||
      panel[WLED_MAX_PANELS] = {{0,0,0,0}};
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
      setUpMatrix(),
 | 
			
		||||
      setPixelColorXY(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
 | 
			
		||||
      fill2D(uint32_t),
 | 
			
		||||
      blur1d(fract8 blur_amount, CRGB* leds=nullptr),
 | 
			
		||||
      blur2d(fract8 blur_amount, CRGB* leds=nullptr),
 | 
			
		||||
      blurRows(fract8 blur_amount, CRGB* leds=nullptr),
 | 
			
		||||
      blurColumns(fract8 blur_amount, CRGB* leds=nullptr),
 | 
			
		||||
      fill_solid(const struct CRGB& color, CRGB* leds=nullptr),
 | 
			
		||||
      fade_out2D(uint8_t r),
 | 
			
		||||
      fadeToBlackBy(uint8_t fadeBy, CRGB* leds=nullptr),
 | 
			
		||||
      nscale8(uint8_t scale, CRGB* leds=nullptr),
 | 
			
		||||
      setPixels(CRGB* leds);
 | 
			
		||||
 | 
			
		||||
    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); }
 | 
			
		||||
 | 
			
		||||
    uint16_t
 | 
			
		||||
      XY(uint16_t x, uint16_t y, uint8_t seg=255);
 | 
			
		||||
 | 
			
		||||
    uint32_t
 | 
			
		||||
      getPixelColorXY(uint16_t, uint16_t);
 | 
			
		||||
 | 
			
		||||
  // 2D modes
 | 
			
		||||
  uint16_t
 | 
			
		||||
    mode_2DBlackHole_A(),
 | 
			
		||||
    mode_2DBlackHole_B();
 | 
			
		||||
 | 
			
		||||
// end 2D support
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    uint32_t crgb_to_col(CRGB fastled);
 | 
			
		||||
    CRGB col_to_crgb(uint32_t);
 | 
			
		||||
| 
						 | 
				
			
			@ -905,9 +967,9 @@ class WS2812FX {
 | 
			
		|||
    uint8_t _segment_index_palette_last = 99;
 | 
			
		||||
    uint8_t _mainSegment;
 | 
			
		||||
 | 
			
		||||
    segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 24 bytes per element
 | 
			
		||||
      // start, stop, offset, speed, intensity, palette, mode, options, grouping, spacing, opacity (unused), color[], capabilities
 | 
			
		||||
      {0, 7, 0, DEFAULT_SPEED, 128, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}, 0}
 | 
			
		||||
    segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 27 bytes per element
 | 
			
		||||
      // start, stop, offset, speed, intensity, palette, mode, options, grouping, spacing, opacity (unused), color[], capabilities, custom 1, custom 2, custom 3
 | 
			
		||||
      {0, 7, 0, DEFAULT_SPEED, DEFAULT_INTENSITY, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}, 0, DEFAULT_C1, DEFAULT_C2, DEFAULT_C3, 0, 1}
 | 
			
		||||
    };
 | 
			
		||||
    segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 28 bytes per element
 | 
			
		||||
    friend class Segment_runtime;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,349 @@
 | 
			
		|||
/*
 | 
			
		||||
  FX_2Dfcn.cpp contains all 2D utility functions
 | 
			
		||||
  
 | 
			
		||||
  LICENSE
 | 
			
		||||
  The MIT License (MIT)
 | 
			
		||||
  Copyright (c) 2022  Blaz Kristan (https://blaz.at/home)
 | 
			
		||||
  Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
  of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
  in the Software without restriction, including without limitation the rights
 | 
			
		||||
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
  copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
  furnished to do so, subject to the following conditions:
 | 
			
		||||
  The above copyright notice and this permission notice shall be included in
 | 
			
		||||
  all copies or substantial portions of the Software.
 | 
			
		||||
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
  THE SOFTWARE.
 | 
			
		||||
 | 
			
		||||
  Parts of the code adapted from WLED Sound Reactive
 | 
			
		||||
*/
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#include "FX.h"
 | 
			
		||||
#include "palettes.h"
 | 
			
		||||
 | 
			
		||||
// setUpMatrix() - constructs ledmap array from matrix of panels with WxH pixels
 | 
			
		||||
// this converts physical (possibly irregular) LED arrangement into well defined
 | 
			
		||||
// array of logical pixels: fist entry corresponds to left-topmost logical pixel
 | 
			
		||||
// followed by horizontal pixels, when matrixWidth logical pixels are added they
 | 
			
		||||
// are followed by next row (down) of matrixWidth pixels (and so forth)
 | 
			
		||||
// note: matrix may be comprised of multiple panels each with different orientation
 | 
			
		||||
// but ledmap takes care of that. ledmap is constructed upon initialization
 | 
			
		||||
// so matrix should disable regular ledmap processing
 | 
			
		||||
void WS2812FX::setUpMatrix() {
 | 
			
		||||
  // erase old ledmap, just in case.
 | 
			
		||||
  if (customMappingTable != nullptr) delete[] customMappingTable;
 | 
			
		||||
  customMappingTable = nullptr;
 | 
			
		||||
  customMappingSize = 0;
 | 
			
		||||
 | 
			
		||||
  if (isMatrix) {
 | 
			
		||||
    matrixWidth  = hPanels * panelW;
 | 
			
		||||
    matrixHeight = vPanels * panelH;
 | 
			
		||||
 | 
			
		||||
    // safety check
 | 
			
		||||
    if (matrixWidth * matrixHeight > MAX_LEDS) {
 | 
			
		||||
      matrixWidth = getLengthTotal();
 | 
			
		||||
      matrixHeight = 1;
 | 
			
		||||
      isMatrix = false;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    customMappingSize  = matrixWidth * matrixHeight;
 | 
			
		||||
    customMappingTable = new uint16_t[customMappingSize];
 | 
			
		||||
 | 
			
		||||
    if (customMappingTable != nullptr) {
 | 
			
		||||
      uint16_t startL; // index in custom mapping array (logical strip)
 | 
			
		||||
      uint16_t startP; // position of 1st pixel of panel on (virtual) strip
 | 
			
		||||
      uint16_t x, y, offset;
 | 
			
		||||
      uint8_t h = matrix.vertical ? vPanels : hPanels;
 | 
			
		||||
      uint8_t v = matrix.vertical ? hPanels : vPanels;
 | 
			
		||||
 | 
			
		||||
      for (uint8_t j=0, p=0; j<v; j++) {
 | 
			
		||||
        for (uint8_t i=0; i<h; i++, p++) {
 | 
			
		||||
          y = (matrix.vertical ? matrix.rightStart : matrix.bottomStart) ? v - j - 1 : j;
 | 
			
		||||
          x = (matrix.vertical ? matrix.bottomStart : matrix.rightStart) ? h - i - 1 : i;
 | 
			
		||||
          x = matrix.serpentine && j%2 ? h - x - 1 : x;
 | 
			
		||||
 | 
			
		||||
          startL = (matrix.vertical ? y : x) * panelW + (matrix.vertical ? x : y) * matrixWidth * panelH; // logical index (top-left corner)
 | 
			
		||||
          startP = p * panelW * panelH; // physical index (top-left corner)
 | 
			
		||||
 | 
			
		||||
          for (uint16_t l=0; l<panelH; l++) {
 | 
			
		||||
            y = panel[h*j + i].bottomStart ? (panelH - l - 1) : l;
 | 
			
		||||
            for (uint16_t k=0; k<panelW; k++) {
 | 
			
		||||
              x = panel[h*j + i].rightStart ? (panelW - k - 1) : k;
 | 
			
		||||
              if (panel[h*j + i].vertical) {
 | 
			
		||||
                y = (panel[h*j + i].serpentine && x%2) ? (panelH - y - 1) : y;
 | 
			
		||||
                offset = y + x * panelH;
 | 
			
		||||
              } else {
 | 
			
		||||
                x = (panel[h*j + i].serpentine && y%2) ? (panelW - x - 1) : x;
 | 
			
		||||
                offset = x + y * panelW;
 | 
			
		||||
              }
 | 
			
		||||
              customMappingTable[startL + k + l * matrixWidth] = startP + offset;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      #ifdef WLED_DEBUG
 | 
			
		||||
      DEBUG_PRINT(F("Matrix ledmap:"));
 | 
			
		||||
      for (uint16_t i=0; i<customMappingSize; i++) {
 | 
			
		||||
        if (!(i%matrixWidth)) DEBUG_PRINTLN();
 | 
			
		||||
        DEBUG_PRINTF("%4d,", customMappingTable[i]);
 | 
			
		||||
      }
 | 
			
		||||
      DEBUG_PRINTLN();
 | 
			
		||||
      #endif
 | 
			
		||||
    } else {
 | 
			
		||||
      // memory allocation error
 | 
			
		||||
      matrixWidth = getLengthTotal();
 | 
			
		||||
      matrixHeight = 1;
 | 
			
		||||
      isMatrix = false;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  } else { 
 | 
			
		||||
    // not a matrix set up
 | 
			
		||||
    matrixWidth = getLengthTotal();
 | 
			
		||||
    matrixHeight = 1;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// XY(x,y,seg) - returns an index of segment pixel in a matrix layout
 | 
			
		||||
// index still needs to undergo ledmap processing to represent actual physical pixel
 | 
			
		||||
// matrix is always organized by matrixHeight number of matrixWidth pixels from top to bottom, left to right
 | 
			
		||||
// so: pixel at XY(5,6) in a 2D segment with [start=10, stop=19, startY=20, stopY=29 : 10x10 pixels]
 | 
			
		||||
// corresponds to pixel with logical index of 847 (0 based) if a 2D segment belongs to a 32x32 matrix.
 | 
			
		||||
// math: (matrixWidth * (startY + y)) + start + x => (32 * (20+6)) + 10 + 5 = 847
 | 
			
		||||
uint16_t IRAM_ATTR WS2812FX::XY(uint16_t x, uint16_t y, uint8_t seg) {
 | 
			
		||||
  if (seg == 255) seg = _segment_index;
 | 
			
		||||
  x %= _segments[seg].width();  // just in case constrain x (wrap around)
 | 
			
		||||
  y %= _segments[seg].height(); // just in case constrain y (wrap around)
 | 
			
		||||
  return ((_segments[seg].startY + y) * matrixWidth) + _segments[seg].start + x;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IRAM_ATTR WS2812FX::setPixelColorXY(uint16_t x, uint16_t y, byte r, byte g, byte b, byte w)
 | 
			
		||||
{
 | 
			
		||||
  if (!isMatrix) return; // not a matrix set-up
 | 
			
		||||
  uint8_t segIdx = SEGLEN ? _segment_index : _mainSegment;
 | 
			
		||||
  if (SEGLEN && _bri_t < 255) {  
 | 
			
		||||
    r = scale8(r, _bri_t);
 | 
			
		||||
    g = scale8(g, _bri_t);
 | 
			
		||||
    b = scale8(b, _bri_t);
 | 
			
		||||
    w = scale8(w, _bri_t);
 | 
			
		||||
  }
 | 
			
		||||
  uint32_t col = RGBW32(r, g, b, w);
 | 
			
		||||
 | 
			
		||||
  uint16_t width  = _segments[segIdx].virtualWidth();   // segment width in logical pixels
 | 
			
		||||
  uint16_t height = _segments[segIdx].virtualHeight();  // segment height in logical pixels
 | 
			
		||||
  if (_segments[segIdx].options & MIRROR     ) width  = (width  + 1) /2;  // divide by 2 if mirror, leave at least a single LED
 | 
			
		||||
  if (_segments[segIdx].options & MIRROR_Y_2D) height = (height + 1) /2;  // divide by 2 if mirror, leave at least a single LED
 | 
			
		||||
  if (_segments[segIdx].options & TRANSPOSED ) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed
 | 
			
		||||
 | 
			
		||||
  x *= _segments[segIdx].groupLength();
 | 
			
		||||
  y *= _segments[segIdx].groupLength();
 | 
			
		||||
  if (x >= width || y >= height) return;  // if pixel would fall out of segment just exit
 | 
			
		||||
 | 
			
		||||
  for (uint8_t j = 0; j < _segments[segIdx].grouping; j++) {        // groupping vertically
 | 
			
		||||
    for (uint8_t g = 0; g < _segments[segIdx].grouping; g++) {      // groupping horizontally
 | 
			
		||||
      uint16_t index, xX = (x+g), yY = (y+j);
 | 
			
		||||
      if (xX >= width || yY >= height) continue; // we have reached one dimension's end
 | 
			
		||||
 | 
			
		||||
      if (_segments[segIdx].options & REVERSE     ) xX = width  - xX - 1;
 | 
			
		||||
      if (_segments[segIdx].options & REVERSE_Y_2D) yY = height - yY - 1;
 | 
			
		||||
 | 
			
		||||
      index = XY(xX, yY, segIdx);
 | 
			
		||||
      if (index < customMappingSize) index = customMappingTable[index];
 | 
			
		||||
      busses.setPixelColor(index, col);
 | 
			
		||||
 | 
			
		||||
      if (_segments[segIdx].options & MIRROR) { //set the corresponding horizontally mirrored pixel
 | 
			
		||||
        index = XY(_segments[segIdx].stop - xX - 1, yY, segIdx);
 | 
			
		||||
        if (index < customMappingSize) index = customMappingTable[index];
 | 
			
		||||
        busses.setPixelColor(index, col);
 | 
			
		||||
      }
 | 
			
		||||
      if (_segments[segIdx].options & MIRROR_Y_2D) { //set the corresponding vertically mirrored pixel
 | 
			
		||||
        index = XY(xX, _segments[segIdx].stopY - yY - 1, segIdx);
 | 
			
		||||
        if (index < customMappingSize) index = customMappingTable[index];
 | 
			
		||||
        busses.setPixelColor(index, col);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y)
 | 
			
		||||
{
 | 
			
		||||
  uint8_t segIdx  = _segment_index;
 | 
			
		||||
  uint16_t width  = _segments[segIdx].virtualWidth();   // segment width in logical pixels
 | 
			
		||||
  uint16_t height = _segments[segIdx].virtualHeight();  // segment height in logical pixels
 | 
			
		||||
  if (_segments[segIdx].options & MIRROR     ) width  = (width  + 1) /2;  // divide by 2 if mirror, leave at least a single LED
 | 
			
		||||
  if (_segments[segIdx].options & MIRROR_Y_2D) height = (height + 1) /2;  // divide by 2 if mirror, leave at least a single LED
 | 
			
		||||
  if (_segments[segIdx].options & TRANSPOSED ) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed
 | 
			
		||||
 | 
			
		||||
  x *= _segments[segIdx].groupLength();
 | 
			
		||||
  y *= _segments[segIdx].groupLength();
 | 
			
		||||
  if (x >= width || y >= height) return 0;
 | 
			
		||||
 | 
			
		||||
  if (_segments[segIdx].options & REVERSE     ) x = width  - x - 1;
 | 
			
		||||
  if (_segments[segIdx].options & REVERSE_Y_2D) y = height - y - 1;
 | 
			
		||||
 | 
			
		||||
  uint16_t index = XY(x, y, segIdx);
 | 
			
		||||
  if (index < customMappingSize) index = customMappingTable[index];
 | 
			
		||||
 | 
			
		||||
  return busses.getPixelColor(index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// blurRows: perform a blur1d on every row of a rectangular matrix
 | 
			
		||||
void WS2812FX::blurRows(fract8 blur_amount, CRGB* leds)
 | 
			
		||||
{
 | 
			
		||||
  uint16_t width  = SEGMENT.virtualWidth();
 | 
			
		||||
  uint16_t height = SEGMENT.virtualHeight();
 | 
			
		||||
  uint8_t keep = 255 - blur_amount;
 | 
			
		||||
  uint8_t seep = blur_amount >> 1;
 | 
			
		||||
  CRGB carryover = CRGB::Black;
 | 
			
		||||
  for (uint16_t y = 0; y < height; y++) for (uint16_t x = 0; x < width; x++) {
 | 
			
		||||
      CRGB cur = leds ? leds[x + y * width] : col_to_crgb(getPixelColorXY(x,y));
 | 
			
		||||
      CRGB part = cur;
 | 
			
		||||
      part.nscale8(seep);
 | 
			
		||||
      cur.nscale8(keep);
 | 
			
		||||
      cur += carryover;
 | 
			
		||||
      if (x) {
 | 
			
		||||
        if (leds) leds[(x-1) + y * width] += part;
 | 
			
		||||
        else setPixelColorXY(x-1, y, col_to_crgb(getPixelColorXY(x-1, y)) + part);
 | 
			
		||||
      }
 | 
			
		||||
      if (leds) leds[x + y * width] = cur;
 | 
			
		||||
      else setPixelColorXY(x, y, cur);
 | 
			
		||||
      carryover = part;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// blurColumns: perform a blur1d on each column of a rectangular matrix
 | 
			
		||||
void WS2812FX::blurColumns(fract8 blur_amount, CRGB* leds)
 | 
			
		||||
{
 | 
			
		||||
  uint16_t width  = SEGMENT.virtualWidth();
 | 
			
		||||
  uint16_t height = SEGMENT.virtualHeight();
 | 
			
		||||
  // blur columns
 | 
			
		||||
  uint8_t keep = 255 - blur_amount;
 | 
			
		||||
  uint8_t seep = blur_amount >> 1;
 | 
			
		||||
  for ( uint16_t x = 0; x < width; x++) {
 | 
			
		||||
    CRGB carryover = CRGB::Black;
 | 
			
		||||
    for ( uint16_t y = 0; y < height; y++) {
 | 
			
		||||
      CRGB cur = leds ? leds[x + y * width] : col_to_crgb(getPixelColorXY(x,y));
 | 
			
		||||
      CRGB part = cur;
 | 
			
		||||
      part.nscale8(seep);
 | 
			
		||||
      cur.nscale8(keep);
 | 
			
		||||
      cur += carryover;
 | 
			
		||||
      if (y) {
 | 
			
		||||
        if (leds) leds[x + (y-1) * width] += part;
 | 
			
		||||
        else setPixelColorXY(x, y-1, col_to_crgb(getPixelColorXY(x,y-1)) + part);
 | 
			
		||||
      }
 | 
			
		||||
      if (leds) leds[x + y * width] = cur;
 | 
			
		||||
      else setPixelColorXY(x, y, cur);
 | 
			
		||||
      carryover = part;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors.
 | 
			
		||||
// blur2d: two-dimensional blur filter. Spreads light to 8 XY neighbors.
 | 
			
		||||
//
 | 
			
		||||
//           0 = no spread at all
 | 
			
		||||
//          64 = moderate spreading
 | 
			
		||||
//         172 = maximum smooth, even spreading
 | 
			
		||||
//
 | 
			
		||||
//         173..255 = wider spreading, but increasing flicker
 | 
			
		||||
//
 | 
			
		||||
//         Total light is NOT entirely conserved, so many repeated
 | 
			
		||||
//         calls to 'blur' will also result in the light fading,
 | 
			
		||||
//         eventually all the way to black; this is by design so that
 | 
			
		||||
//         it can be used to (slowly) clear the LEDs to black.
 | 
			
		||||
 | 
			
		||||
void WS2812FX::blur1d(fract8 blur_amount, CRGB* leds)
 | 
			
		||||
{
 | 
			
		||||
  blurRows(blur_amount, leds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WS2812FX::blur2d(fract8 blur_amount, CRGB* leds)
 | 
			
		||||
{
 | 
			
		||||
  blurRows(blur_amount, leds);
 | 
			
		||||
  blurColumns(blur_amount, leds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//ewowi20210628: new functions moved from colorutils: add segment awareness
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Fills segment with color
 | 
			
		||||
 */
 | 
			
		||||
void WS2812FX::fill2D(uint32_t c) {
 | 
			
		||||
  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++) {
 | 
			
		||||
    setPixelColorXY(x, y, c);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WS2812FX::fill_solid(const struct CRGB& color, CRGB* leds) {
 | 
			
		||||
  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++) {
 | 
			
		||||
    if (leds) leds[x + y * w] = color;
 | 
			
		||||
    else setPixelColorXY(x, y, color);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * fade out function, higher rate = quicker fade
 | 
			
		||||
 * TODO: may be better to use approach of nscale8()
 | 
			
		||||
 */
 | 
			
		||||
void WS2812FX::fade_out2D(uint8_t rate) {
 | 
			
		||||
  uint16_t w  = SEGMENT.virtualWidth();
 | 
			
		||||
  uint16_t h = SEGMENT.virtualHeight();
 | 
			
		||||
  rate = (255-rate) >> 1;
 | 
			
		||||
  float mappedRate = float(rate) +1.1;
 | 
			
		||||
 | 
			
		||||
  uint32_t color = SEGCOLOR(1); // target color
 | 
			
		||||
  int w2 = W(color);
 | 
			
		||||
  int r2 = R(color);
 | 
			
		||||
  int g2 = G(color);
 | 
			
		||||
  int b2 = B(color);
 | 
			
		||||
 | 
			
		||||
  for(uint16_t y = 0; y < h; y++) for (uint16_t x = 0; x < w; x++) {
 | 
			
		||||
    color = getPixelColorXY(x, y);
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    // if fade isn't complete, make sure delta is at least 1 (fixes rounding issues)
 | 
			
		||||
    wdelta += (w2 == w1) ? 0 : (w2 > 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;
 | 
			
		||||
 | 
			
		||||
    setPixelColorXY(x, y, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WS2812FX::fadeToBlackBy(uint8_t fadeBy, CRGB* leds) {
 | 
			
		||||
  nscale8(255 - fadeBy, leds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WS2812FX::nscale8(uint8_t scale, CRGB* leds) {
 | 
			
		||||
  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++) {
 | 
			
		||||
    if (leds) leds[x + y * w].nscale8(scale);
 | 
			
		||||
    else setPixelColorXY(x, y, col_to_crgb(getPixelColorXY(x, y)).nscale8(scale));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WS2812FX::setPixels(CRGB* leds) {
 | 
			
		||||
  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++) setPixelColorXY(x, y, leds[x + y*w]);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -184,23 +184,27 @@ void WS2812FX::service() {
 | 
			
		|||
 | 
			
		||||
void IRAM_ATTR WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
 | 
			
		||||
{
 | 
			
		||||
  uint8_t segIdx;
 | 
			
		||||
  uint8_t segIdx = SEGLEN ? _segment_index : _mainSegment;
 | 
			
		||||
  if (isMatrix) {
 | 
			
		||||
    // map linear pixel into 2D segment area (even for 1D segments, expanding vertically)
 | 
			
		||||
    uint16_t h = _segments[segIdx].height();  // segment height in logical pixels
 | 
			
		||||
    uint8_t  l = _segments[segIdx].groupLength();
 | 
			
		||||
    for (uint16_t y = 0; y < h; y += l) { // expand 1D effect vertically
 | 
			
		||||
      setPixelColorXY(i, y, r, g, b, w);
 | 
			
		||||
    }
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (SEGLEN) { // SEGLEN!=0 -> from segment/FX
 | 
			
		||||
  if (SEGLEN || (realtimeMode && useMainSegmentOnly)) {
 | 
			
		||||
    //color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments)
 | 
			
		||||
    if (_bri_t < 255) {  
 | 
			
		||||
    if (SEGLEN && _bri_t < 255) {  // SEGLEN!=0 -> from segment/FX
 | 
			
		||||
      r = scale8(r, _bri_t);
 | 
			
		||||
      g = scale8(g, _bri_t);
 | 
			
		||||
      b = scale8(b, _bri_t);
 | 
			
		||||
      w = scale8(w, _bri_t);
 | 
			
		||||
    }
 | 
			
		||||
    segIdx = _segment_index;
 | 
			
		||||
  } else // from live/realtime
 | 
			
		||||
    segIdx = _mainSegment;
 | 
			
		||||
 | 
			
		||||
  if (SEGLEN || (realtimeMode && useMainSegmentOnly)) {
 | 
			
		||||
    uint32_t col = RGBW32(r, g, b, w);
 | 
			
		||||
    uint16_t len = _segments[segIdx].length();
 | 
			
		||||
    uint16_t len = _segments[segIdx].length();  // length of segment in number of pixels
 | 
			
		||||
 | 
			
		||||
    // get physical pixel address (taking into account start, grouping, spacing [and offset])
 | 
			
		||||
    i = i * _segments[segIdx].groupLength();
 | 
			
		||||
| 
						 | 
				
			
			@ -211,7 +215,7 @@ void IRAM_ATTR WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte
 | 
			
		|||
        i = (len - 1) - i;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    i += _segments[segIdx].start;
 | 
			
		||||
    i += _segments[segIdx].start; // starting pixel in a group
 | 
			
		||||
 | 
			
		||||
    // set all the pixels in the group
 | 
			
		||||
    for (uint16_t j = 0; j < _segments[segIdx].grouping; j++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -221,15 +225,13 @@ void IRAM_ATTR WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte
 | 
			
		|||
        if (_segments[segIdx].options & MIRROR) { //set the corresponding mirrored pixel
 | 
			
		||||
          uint16_t indexMir = _segments[segIdx].stop - indexSet + _segments[segIdx].start - 1;          
 | 
			
		||||
          indexMir += _segments[segIdx].offset; // offset/phase
 | 
			
		||||
 | 
			
		||||
          if (indexMir >= _segments[segIdx].stop) indexMir -= len;
 | 
			
		||||
          if (indexMir >= _segments[segIdx].stop) indexMir -= len; // wrap
 | 
			
		||||
          if (indexMir < customMappingSize) indexMir = customMappingTable[indexMir];
 | 
			
		||||
 | 
			
		||||
          busses.setPixelColor(indexMir, col);
 | 
			
		||||
        }
 | 
			
		||||
        indexSet += _segments[segIdx].offset; // offset/phase
 | 
			
		||||
 | 
			
		||||
        if (indexSet >= _segments[segIdx].stop) indexSet -= len;
 | 
			
		||||
        if (indexSet >= _segments[segIdx].stop) indexSet -= len; // wrap
 | 
			
		||||
        if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet];
 | 
			
		||||
 | 
			
		||||
        busses.setPixelColor(indexSet, col);
 | 
			
		||||
| 
						 | 
				
			
			@ -496,6 +498,8 @@ uint8_t WS2812FX::getActiveSegmentsNum(void) {
 | 
			
		|||
 | 
			
		||||
uint32_t WS2812FX::getPixelColor(uint16_t i)
 | 
			
		||||
{
 | 
			
		||||
  if (isMatrix) return getPixelColorXY(i, 0);
 | 
			
		||||
 | 
			
		||||
  // get physical pixel
 | 
			
		||||
  i = i * SEGMENT.groupLength();;
 | 
			
		||||
  if (IS_REVERSE) {
 | 
			
		||||
| 
						 | 
				
			
			@ -566,14 +570,13 @@ uint8_t WS2812FX::Segment::differs(Segment& b) {
 | 
			
		|||
  if (c1x != b.c1x)             d |= SEG_DIFFERS_FX;
 | 
			
		||||
  if (c2x != b.c2x)             d |= SEG_DIFFERS_FX;
 | 
			
		||||
  if (c3x != b.c3x)             d |= SEG_DIFFERS_FX;
 | 
			
		||||
  if (startY != b.startY)       d |= SEG_DIFFERS_BOUNDS;
 | 
			
		||||
  if (stopY != b.stopY)         d |= SEG_DIFFERS_BOUNDS;
 | 
			
		||||
 | 
			
		||||
  if ((options & 0b00101110) != (b.options & 0b00101110)) d |= SEG_DIFFERS_OPT;
 | 
			
		||||
  if ((options & 0x01) != (b.options & 0x01)) d |= SEG_DIFFERS_SEL;
 | 
			
		||||
  if ((options & 0x01) != (b.options & 0x01))             d |= SEG_DIFFERS_SEL;
 | 
			
		||||
  
 | 
			
		||||
  for (uint8_t i = 0; i < NUM_COLORS; i++)
 | 
			
		||||
  {
 | 
			
		||||
    if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
 | 
			
		||||
  }
 | 
			
		||||
  for (uint8_t i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
 | 
			
		||||
 | 
			
		||||
  return d;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -640,12 +643,15 @@ bool WS2812FX::hasCCTBus(void) {
 | 
			
		|||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset) {
 | 
			
		||||
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset, uint16_t startY, uint16_t stopY) {
 | 
			
		||||
  if (n >= MAX_NUM_SEGMENTS) return;
 | 
			
		||||
  Segment& seg = _segments[n];
 | 
			
		||||
 | 
			
		||||
  //return if neither bounds nor grouping have changed
 | 
			
		||||
  bool boundsUnchanged = (seg.start == i1 && seg.stop == i2);
 | 
			
		||||
  if (isMatrix) {
 | 
			
		||||
    boundsUnchanged &= (seg.startY == startY && seg.stopY == stopY);
 | 
			
		||||
  }
 | 
			
		||||
  if (boundsUnchanged
 | 
			
		||||
			&& (!grouping || (seg.grouping == grouping && seg.spacing == spacing))
 | 
			
		||||
			&& (offset == UINT16_MAX || offset == seg.offset)) return;
 | 
			
		||||
| 
						 | 
				
			
			@ -662,9 +668,17 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
 | 
			
		|||
    if (n == _mainSegment) setMainSegmentId(0);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (i1 < _length) seg.start = i1;
 | 
			
		||||
  seg.stop = i2;
 | 
			
		||||
  if (i2 > _length) seg.stop = _length;
 | 
			
		||||
  if (isMatrix) {
 | 
			
		||||
    if (i1 < matrixWidth) seg.start = i1;
 | 
			
		||||
    seg.stop = i2 > matrixWidth ? matrixWidth : i2;
 | 
			
		||||
    if (startY < matrixHeight) seg.startY = startY;
 | 
			
		||||
    seg.stopY = stopY > matrixHeight ? matrixHeight : MAX(1,stopY);
 | 
			
		||||
  } else {
 | 
			
		||||
    if (i1 < _length) seg.start = i1;
 | 
			
		||||
    seg.stop = i2 > _length ? _length : i2;
 | 
			
		||||
    seg.startY = 0;
 | 
			
		||||
    seg.stopY  = 1;
 | 
			
		||||
  }
 | 
			
		||||
  if (grouping) {
 | 
			
		||||
    seg.grouping = grouping;
 | 
			
		||||
    seg.spacing = spacing;
 | 
			
		||||
| 
						 | 
				
			
			@ -1157,6 +1171,8 @@ uint32_t IRAM_ATTR WS2812FX::color_from_palette(uint16_t i, bool mapping, bool w
 | 
			
		|||
 | 
			
		||||
//load custom mapping table from JSON file (called from finalizeInit() or deserializeState())
 | 
			
		||||
void WS2812FX::deserializeMap(uint8_t n) {
 | 
			
		||||
  if (isMatrix) return; // 2D support creates its own ledmap
 | 
			
		||||
 | 
			
		||||
  char fileName[32];
 | 
			
		||||
  strcpy_P(fileName, PSTR("/ledmap"));
 | 
			
		||||
  if (n) sprintf(fileName +7, "%d", n);
 | 
			
		||||
| 
						 | 
				
			
			@ -1399,7 +1415,9 @@ const char JSON_mode_names[] PROGMEM = R"=====([
 | 
			
		|||
"Candy Cane@!,Width;;",
 | 
			
		||||
"Blends@Shift speed,Blend speed;1,2,3,!",
 | 
			
		||||
"TV Simulator",
 | 
			
		||||
"Dynamic Smooth"
 | 
			
		||||
"Dynamic Smooth",
 | 
			
		||||
"2D Black Hole A@!,!,C1,C2,C3;!;!",
 | 
			
		||||
"2D Black Hole B@!,!,C1,C2,C3;!;!"
 | 
			
		||||
])=====";
 | 
			
		||||
 | 
			
		||||
const char JSON_palette_names[] PROGMEM = R"=====([
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -91,6 +91,41 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
 | 
			
		|||
  Bus::setCCTBlend(strip.cctBlending);
 | 
			
		||||
  strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
 | 
			
		||||
 | 
			
		||||
  // 2D Matrix Settings
 | 
			
		||||
  JsonObject matrix = hw_led[F("matrix")];
 | 
			
		||||
  if (!matrix.isNull()) {
 | 
			
		||||
    strip.isMatrix = true;
 | 
			
		||||
    CJSON(strip.panelH,  matrix[F("ph")]);
 | 
			
		||||
    CJSON(strip.panelW,  matrix[F("pw")]);
 | 
			
		||||
    CJSON(strip.hPanels, matrix[F("mph")]);
 | 
			
		||||
    CJSON(strip.vPanels, matrix[F("mpv")]);
 | 
			
		||||
    CJSON(strip.matrix.bottomStart, matrix[F("pb")]);
 | 
			
		||||
    CJSON(strip.matrix.rightStart,  matrix[F("pr")]);
 | 
			
		||||
    CJSON(strip.matrix.vertical,    matrix[F("pv")]);
 | 
			
		||||
    CJSON(strip.matrix.serpentine,  matrix[F("ps")]);
 | 
			
		||||
 | 
			
		||||
    JsonArray panels = matrix[F("panels")];
 | 
			
		||||
    uint8_t s = 0;
 | 
			
		||||
    if (!panels.isNull()) {
 | 
			
		||||
      for (JsonObject pnl : panels) {
 | 
			
		||||
        CJSON(strip.panel[s].bottomStart, pnl["b"]);
 | 
			
		||||
        CJSON(strip.panel[s].rightStart, pnl["r"]);
 | 
			
		||||
        CJSON(strip.panel[s].vertical, pnl["v"]);
 | 
			
		||||
        CJSON(strip.panel[s].serpentine, pnl["s"]);
 | 
			
		||||
        if (++s >= WLED_MAX_PANELS) break; // max panels reached
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    // clear remaining panels
 | 
			
		||||
    for (; s<WLED_MAX_PANELS; s++) {
 | 
			
		||||
      strip.panel[s].bottomStart = 0;
 | 
			
		||||
      strip.panel[s].rightStart = 0;
 | 
			
		||||
      strip.panel[s].vertical = 0;
 | 
			
		||||
      strip.panel[s].serpentine = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    strip.setUpMatrix();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  JsonArray ins = hw_led["ins"];
 | 
			
		||||
  
 | 
			
		||||
  if (fromFS || !ins.isNull()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -583,6 +618,28 @@ void serializeConfig() {
 | 
			
		|||
  hw_led["fps"] = strip.getTargetFps();
 | 
			
		||||
  hw_led[F("rgbwm")] = Bus::getAutoWhiteMode();    // global override
 | 
			
		||||
 | 
			
		||||
  // 2D Matrix Settings
 | 
			
		||||
  if (strip.isMatrix) {
 | 
			
		||||
    JsonObject matrix = hw_led.createNestedObject(F("matrix"));
 | 
			
		||||
    matrix[F("ph")] = strip.panelH;
 | 
			
		||||
    matrix[F("pw")] = strip.panelW;
 | 
			
		||||
    matrix[F("mph")] = strip.hPanels;
 | 
			
		||||
    matrix[F("mpv")] = strip.vPanels;
 | 
			
		||||
    matrix[F("pb")] = strip.matrix.bottomStart;
 | 
			
		||||
    matrix[F("pr")] = strip.matrix.rightStart;
 | 
			
		||||
    matrix[F("pv")] = strip.matrix.vertical;
 | 
			
		||||
    matrix[F("ps")] = strip.matrix.serpentine;
 | 
			
		||||
 | 
			
		||||
    JsonArray panels = matrix.createNestedArray(F("panels"));
 | 
			
		||||
    for (uint8_t i=0; i<strip.hPanels*strip.vPanels; i++) {
 | 
			
		||||
      JsonObject pnl = panels.createNestedObject();
 | 
			
		||||
      pnl["b"] = strip.panel[i].bottomStart;
 | 
			
		||||
      pnl["r"] = strip.panel[i].rightStart;
 | 
			
		||||
      pnl["v"] = strip.panel[i].vertical;
 | 
			
		||||
      pnl["s"] = strip.panel[i].serpentine;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  JsonArray hw_led_ins = hw_led.createNestedArray("ins");
 | 
			
		||||
 | 
			
		||||
  for (uint8_t s = 0; s < busses.getNumBusses(); s++) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -223,6 +223,9 @@
 | 
			
		|||
#define SEG_OPTION_NONUNITY       4            //Indicates that the effect does not use FRAMETIME or needs getPixelColor
 | 
			
		||||
#define SEG_OPTION_FREEZE         5            //Segment contents will not be refreshed
 | 
			
		||||
#define SEG_OPTION_TRANSITIONAL   7
 | 
			
		||||
#define SEG_OPTION_REVERSED_Y     8
 | 
			
		||||
#define SEG_OPTION_MIRROR_Y       9
 | 
			
		||||
#define SEG_OPTION_TRANSPOSE     10
 | 
			
		||||
 | 
			
		||||
//Segment differs return byte
 | 
			
		||||
#define SEG_DIFFERS_BRI        0x01
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -972,6 +972,11 @@ textarea {
 | 
			
		|||
	margin-top: 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TD .revchkl {
 | 
			
		||||
	padding: 0 0 0 32px;
 | 
			
		||||
	margin-top: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.check input, .radio input {
 | 
			
		||||
	position: absolute;
 | 
			
		||||
	opacity: 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1041,6 +1046,10 @@ textarea {
 | 
			
		|||
	background: var(--c-f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TD .checkmark, TD .radiomark {
 | 
			
		||||
	top: -6px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.h {
 | 
			
		||||
	font-size: 13px;
 | 
			
		||||
	text-align: center;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,7 @@ var pJson = {}, eJson = {}, lJson = {};
 | 
			
		|||
var pN = "", pI = 0, pNum = 0;
 | 
			
		||||
var pmt = 1, pmtLS = 0, pmtLast = 0;
 | 
			
		||||
var lastinfo = {};
 | 
			
		||||
var isM = false, mw = 0, mh=0;
 | 
			
		||||
var ws, cpick, ranges;
 | 
			
		||||
var cfg = {
 | 
			
		||||
	theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}},
 | 
			
		||||
| 
						 | 
				
			
			@ -599,6 +600,10 @@ function parseInfo(i) {
 | 
			
		|||
	syncTglRecv = i.str;
 | 
			
		||||
	maxSeg      = i.leds.maxseg;
 | 
			
		||||
	pmt         = i.fs.pmt;
 | 
			
		||||
	// do we have a matrix set-up
 | 
			
		||||
	mw = i.leds.matrix ? i.leds.matrix.w : 0;
 | 
			
		||||
	mh = i.leds.matrix ? i.leds.matrix.h : 0;
 | 
			
		||||
	isM = mw>0 && mh>0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function populateInfo(i)
 | 
			
		||||
| 
						 | 
				
			
			@ -665,6 +670,13 @@ function populateSegments(s)
 | 
			
		|||
			<div class="sliderdisplay"></div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>`;
 | 
			
		||||
		let rvXck = `<label class="check revchkl">Reverse ${isM?'':'direction'}<input type="checkbox" id="seg${i}rev" onchange="setRev(${i})" ${inst.rev?"checked":""}><span class="checkmark schk"></span></label>`;
 | 
			
		||||
		let miXck = `<label class="check revchkl">Mirror<input type="checkbox" id="seg${i}mi" onchange="setMi(${i})" ${inst.mi?"checked":""}><span class="checkmark schk"></span></label>`;
 | 
			
		||||
		let rvYck = "", miYck ="";
 | 
			
		||||
		if (isM) {
 | 
			
		||||
			rvYck = `<label class="check revchkl">Reverse<input type="checkbox" id="seg${i}rY" onchange="setRevY(${i})" ${inst.rY?"checked":""}><span class="checkmark schk"></span></label>`;
 | 
			
		||||
			miYck = `<label class="check revchkl">Mirror<input type="checkbox" id="seg${i}mY" onchange="setMiY(${i})" ${inst.mY?"checked":""}><span class="checkmark schk"></span></label>`;
 | 
			
		||||
		}
 | 
			
		||||
		cn += `<div class="seg lstI ${i==s.mainseg ? 'selected' : ''} ${exp ? "expanded":""}" id="seg${i}">
 | 
			
		||||
	<label class="check schkl">
 | 
			
		||||
		<input type="checkbox" id="seg${i}sel" onchange="selSeg(${i})" ${inst.sel ? "checked":""}>
 | 
			
		||||
| 
						 | 
				
			
			@ -681,19 +693,25 @@ function populateSegments(s)
 | 
			
		|||
		<input type="text" class="ptxt noslide" id="seg${i}t" autocomplete="off" maxlength=32 value="${inst.n?inst.n:""}" placeholder="Enter name..."/>
 | 
			
		||||
		<table class="infot segt">
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td>Start LED</td>
 | 
			
		||||
			<td>${cfg.comp.seglen?"LED count":"Stop LED"}</td>
 | 
			
		||||
			<td>Offset</td>
 | 
			
		||||
			<td>${isM?'Start X':'Start LED'}</td>
 | 
			
		||||
			<td>${isM?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}</td>
 | 
			
		||||
			<td>${isM?'':'Offset'}</td>
 | 
			
		||||
		</tr>
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td><input class="noslide segn" id="seg${i}s" type="number" min="0" max="${ledCount-1}" value="${inst.start}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
 | 
			
		||||
			<td><input class="noslide segn" id="seg${i}e" type="number" min="0" max="${ledCount-(cfg.comp.seglen?inst.start:0)}" value="${inst.stop-(cfg.comp.seglen?inst.start:0)}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
 | 
			
		||||
			<td><input class="noslide segn" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td>
 | 
			
		||||
			<td><input class="noslide segn" id="seg${i}s" type="number" min="0" max="${(isM?mw:ledCount)-1}" value="${inst.start}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
 | 
			
		||||
			<td><input class="noslide segn" id="seg${i}e" type="number" min="0" max="${(isM?mw:ledCount)-(cfg.comp.seglen?inst.start:0)}" value="${inst.stop-(cfg.comp.seglen?inst.start:0)}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
 | 
			
		||||
			<td style="text-align:revert;">${isM?miXck+'<br>'+rvXck:''}<input class="noslide segn ${isM?'hide':''}" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td>
 | 
			
		||||
		</tr>
 | 
			
		||||
		${isM ? '<tr><td>Start Y</td><td>'+(cfg.comp.seglen?'Height':'Stop Y')+'</td><td></td></tr>'+
 | 
			
		||||
		'<tr>'+
 | 
			
		||||
			'<td><input class="noslide segn" id="seg'+i+'sY" type="number" min="0" max="'+(mh-1)+'" value="'+inst.startY+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+
 | 
			
		||||
			'<td><input class="noslide segn" id="seg'+i+'eY" type="number" min="0" max="'+(mh-(cfg.comp.seglen?inst.startY:0))+'" value="'+(inst.stopY-(cfg.comp.seglen?inst.start:0))+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+
 | 
			
		||||
			'<td style="text-align:revert;">'+miYck+'<br>'+rvYck+'</td>'+
 | 
			
		||||
		'</tr>':''}
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td>Grouping</td>
 | 
			
		||||
			<td>Spacing</td>
 | 
			
		||||
			<td>Apply</td>
 | 
			
		||||
			<td><!--Apply--></td>
 | 
			
		||||
		</tr>
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td><input class="noslide segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
 | 
			
		||||
| 
						 | 
				
			
			@ -702,14 +720,10 @@ function populateSegments(s)
 | 
			
		|||
		</tr>
 | 
			
		||||
		</table>
 | 
			
		||||
		<div class="h bp" id="seg${i}len"></div>
 | 
			
		||||
		${!isM?rvXck:''}
 | 
			
		||||
		<label class="check revchkl">
 | 
			
		||||
			Reverse direction
 | 
			
		||||
			<input type="checkbox" id="seg${i}rev" onchange="setRev(${i})" ${inst.rev ? "checked":""}>
 | 
			
		||||
			<span class="checkmark schk"></span>
 | 
			
		||||
		</label>
 | 
			
		||||
		<label class="check revchkl">
 | 
			
		||||
			Mirror effect
 | 
			
		||||
			<input type="checkbox" id="seg${i}mi" onchange="setMi(${i})" ${inst.mi ? "checked":""}>
 | 
			
		||||
			${isM?'Transpose':'Mirror effect'}
 | 
			
		||||
			<input type="checkbox" id="seg${i}${isM?'tp':'mi'}" onchange="${(isM?'setTp(':'setMi(')+i})" ${isM?(inst.tp?"checked":""):(inst.mi?"checked":"")}>
 | 
			
		||||
			<span class="checkmark schk"></span>
 | 
			
		||||
		</label>
 | 
			
		||||
		<div class="del">
 | 
			
		||||
| 
						 | 
				
			
			@ -736,7 +750,7 @@ function populateSegments(s)
 | 
			
		|||
		if (!gId(`seg${i}sel`).checked) gId(`selall`).checked = false;
 | 
			
		||||
	}
 | 
			
		||||
	if (segCount < 2) gId(`segd${lSeg}`).style.display = "none";
 | 
			
		||||
	if (!noNewSegs && (cfg.comp.seglen?parseInt(gId(`seg${lSeg}s`).value):0)+parseInt(gId(`seg${lSeg}e`).value)<ledCount) gId(`segr${lSeg}`).style.display = "inline";
 | 
			
		||||
	if (!isM && !noNewSegs && (cfg.comp.seglen?parseInt(gId(`seg${lSeg}s`).value):0)+parseInt(gId(`seg${lSeg}e`).value)<ledCount) gId(`segr${lSeg}`).style.display = "inline";
 | 
			
		||||
	gId('rsbtn').style.display = (segCount > 1) ? "inline":"none";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -966,6 +980,11 @@ function updateLen(s)
 | 
			
		|||
	var start = parseInt(gId(`seg${s}s`).value);
 | 
			
		||||
	var stop = parseInt(gId(`seg${s}e`).value);
 | 
			
		||||
	var len = stop - (cfg.comp.seglen?0:start);
 | 
			
		||||
	if (isM) {
 | 
			
		||||
		start = parseInt(gId(`seg${s}sY`).value);
 | 
			
		||||
		stop = parseInt(gId(`seg${s}eY`).value);
 | 
			
		||||
		len *= (stop-(cfg.comp.seglen?0:start));
 | 
			
		||||
	}
 | 
			
		||||
	var out = "(delete)";
 | 
			
		||||
	if (len > 1) {
 | 
			
		||||
		out = `${len} LEDs`;
 | 
			
		||||
| 
						 | 
				
			
			@ -1280,7 +1299,7 @@ function setSliderAndColorControl(idx, applyDef=false)
 | 
			
		|||
				var v = Math.max(0,Math.min(255,parseInt(slOnOff[i].substr(dPos+1))));
 | 
			
		||||
				if      (i==0) { if (applyDef) gId("sliderSpeed").value     = v; obj.seg.sx = v; }
 | 
			
		||||
				else if (i==1) { if (applyDef) gId("sliderIntensity").value = v; obj.seg.ix = v; }
 | 
			
		||||
				else           { if (applyDef) gId("sliderC"+(i-1)).value   = v; obj.seg["C"+(i-1)] = v}
 | 
			
		||||
				else           { if (applyDef) gId("sliderC"+(i-1)).value   = v; obj.seg["c"+(i-1)+"x"] = v}
 | 
			
		||||
				slOnOff[i] = slOnOff[i].substring(0,dPos);
 | 
			
		||||
			}
 | 
			
		||||
			if (slOnOff.length>i && slOnOff[i]!="!") label.innerHTML = slOnOff[i];
 | 
			
		||||
| 
						 | 
				
			
			@ -1501,6 +1520,7 @@ function makeSeg()
 | 
			
		|||
{
 | 
			
		||||
	var ns = 0;
 | 
			
		||||
	var lu = lowestUnused;
 | 
			
		||||
	let li = lastinfo;
 | 
			
		||||
	if (lu > 0) {
 | 
			
		||||
		var pend = parseInt(gId(`seg${lu -1}e`).value,10) + (cfg.comp.seglen?parseInt(gId(`seg${lu -1}s`).value,10):0);
 | 
			
		||||
		if (pend < ledCount) ns = pend;
 | 
			
		||||
| 
						 | 
				
			
			@ -1515,14 +1535,19 @@ function makeSeg()
 | 
			
		|||
		<input type="text" class="noslide" id="seg${lu}t" autocomplete="off" maxlength=32 value="" placeholder="New segment ${lu}"/>
 | 
			
		||||
		<table class="segt">
 | 
			
		||||
			<tr>
 | 
			
		||||
				<td width="38%">Start LED</td>
 | 
			
		||||
				<td width="38%">${cfg.comp.seglen?"LED count":"Stop LED"}</td>
 | 
			
		||||
				<td width="38%">${isM?'Start X':'Start LED'}</td>
 | 
			
		||||
				<td width="38%">${isM?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}</td>
 | 
			
		||||
			</tr>
 | 
			
		||||
			<tr>
 | 
			
		||||
				<td><input class="noslide segn" id="seg${lu}s" type="number" min="0" max="${ledCount-1}" value="${ns}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
 | 
			
		||||
				<td><input class="noslide segn" id="seg${lu}e" type="number" min="0" max="${ct}" value="${ct}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
 | 
			
		||||
				<td><button class="btn btn-xs" onclick="setSeg(${lu});resetUtil();"><i class="icons bth-icon" id="segc${lu}"></i></button></td>
 | 
			
		||||
			</tr>
 | 
			
		||||
			${isM ? '<tr><td>Start Y</td><td>'+(cfg.comp.seglen?'Height':'Stop Y')+'</td></tr>'+
 | 
			
		||||
			'<tr>'+
 | 
			
		||||
				'<td><input class="noslide segn" id="seg'+lu+'sY" type="number" min="0" max="'+(mh-1)+'" value="'+0+'" oninput="updateLen('+lu+')" onkeydown="segEnter('+lu+')"></td>'+
 | 
			
		||||
				'<td><input class="noslide segn" id="seg'+lu+'eY" type="number" min="0" max="'+mh+'" value="'+mh+'" oninput="updateLen('+lu+')" onkeydown="segEnter('+lu+')"></td>'+
 | 
			
		||||
			'</tr>':''}
 | 
			
		||||
		</table>
 | 
			
		||||
		<div class="h" id="seg${lu}len">${ledCount - ns} LEDs</div>
 | 
			
		||||
		<div class="c"><button class="btn btn-p" onclick="resetUtil()">Cancel</button></div>
 | 
			
		||||
| 
						 | 
				
			
			@ -1813,6 +1838,7 @@ function selSeg(s)
 | 
			
		|||
 | 
			
		||||
function rptSeg(s)
 | 
			
		||||
{
 | 
			
		||||
	//TODO: 2D support
 | 
			
		||||
	var name = gId(`seg${s}t`).value;
 | 
			
		||||
	var start = parseInt(gId(`seg${s}s`).value);
 | 
			
		||||
	var stop = parseInt(gId(`seg${s}e`).value);
 | 
			
		||||
| 
						 | 
				
			
			@ -1842,6 +1868,12 @@ function setSeg(s)
 | 
			
		|||
	var stop = parseInt(gId(`seg${s}e`).value);
 | 
			
		||||
	if ((cfg.comp.seglen && stop == 0) || (!cfg.comp.seglen && stop <= start)) {delSeg(s); return;}
 | 
			
		||||
	var obj = {"seg": {"id": s, "n": name, "start": start, "stop": (cfg.comp.seglen?start:0)+stop}};
 | 
			
		||||
	if (isM) {
 | 
			
		||||
		var startY = parseInt(gId(`seg${s}sY`).value);
 | 
			
		||||
		var stopY = parseInt(gId(`seg${s}eY`).value);
 | 
			
		||||
		obj.seg.startY = startY;
 | 
			
		||||
		obj.seg.stopY = (cfg.comp.seglen?startY:0)+stopY;
 | 
			
		||||
	}
 | 
			
		||||
	if (gId(`seg${s}grp`)) {
 | 
			
		||||
		var grp = parseInt(gId(`seg${s}grp`).value);
 | 
			
		||||
		var spc = parseInt(gId(`seg${s}spc`).value);
 | 
			
		||||
| 
						 | 
				
			
			@ -1871,6 +1903,13 @@ function setRev(s)
 | 
			
		|||
	requestJson(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function setRevY(s)
 | 
			
		||||
{
 | 
			
		||||
	var rev = gId(`seg${s}rY`).checked;
 | 
			
		||||
	var obj = {"seg": {"id": s, "rY": rev}};
 | 
			
		||||
	requestJson(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function setMi(s)
 | 
			
		||||
{
 | 
			
		||||
	var mi = gId(`seg${s}mi`).checked;
 | 
			
		||||
| 
						 | 
				
			
			@ -1878,6 +1917,20 @@ function setMi(s)
 | 
			
		|||
	requestJson(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function setMiY(s)
 | 
			
		||||
{
 | 
			
		||||
	var mi = gId(`seg${s}mY`).checked;
 | 
			
		||||
	var obj = {"seg": {"id": s, "mY": mi}};
 | 
			
		||||
	requestJson(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function setTp(s)
 | 
			
		||||
{
 | 
			
		||||
	var tp = gId(`seg${s}tp`).checked;
 | 
			
		||||
	var obj = {"seg": {"id": s, "tp": tp}};
 | 
			
		||||
	requestJson(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function setSegPwr(s)
 | 
			
		||||
{
 | 
			
		||||
	var pwr = gId(`seg${s}pwr`).classList.contains("act");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,31 +18,33 @@
 | 
			
		|||
			margin: 0;
 | 
			
		||||
		}
 | 
			
		||||
		html {
 | 
			
		||||
			--h: 10.2vh;
 | 
			
		||||
			--h: 9vh;
 | 
			
		||||
		}
 | 
			
		||||
		button {
 | 
			
		||||
			background: #333;
 | 
			
		||||
			color: #fff;
 | 
			
		||||
			font-family: Verdana, Helvetica, sans-serif;
 | 
			
		||||
			display: inline-block;
 | 
			
		||||
			display: block;
 | 
			
		||||
			border: 1px solid #333;
 | 
			
		||||
			border-radius: var(--h);
 | 
			
		||||
			font-size: 6vmin;
 | 
			
		||||
			height: var(--h);
 | 
			
		||||
			width: calc(100% - 40px);
 | 
			
		||||
			margin-top: 2vh;
 | 
			
		||||
			cursor: pointer;
 | 
			
		||||
		}
 | 
			
		||||
	</style>
 | 
			
		||||
</head>
 | 
			
		||||
<body onload="S()">
 | 
			
		||||
<form action="/"><button type=submit id="b">Back</button></form>
 | 
			
		||||
<form action="/settings/wifi"><button type="submit">WiFi Setup</button></form>
 | 
			
		||||
<form action="/settings/leds"><button type="submit">LED Preferences</button></form>
 | 
			
		||||
<form action="/settings/ui"><button type="submit">User Interface</button></form>
 | 
			
		||||
<form action="/settings/dmx"><button id="dmxbtn" style="display: none;" type="submit">DMX Output</button></form>
 | 
			
		||||
<form action="/settings/sync"><button type="submit">Sync Interfaces</button></form>
 | 
			
		||||
<form action="/settings/time"><button type="submit">Time & Macros</button></form>
 | 
			
		||||
<form action="/settings/um"><button type="submit">Usermods</button></form>
 | 
			
		||||
<form action="/settings/sec"><button type="submit">Security & Updates</button></form>
 | 
			
		||||
<button type=submit id="b" onclick="window.location='/'">Back</button>
 | 
			
		||||
<button type="submit" onclick="window.location='./settings/wifi'">WiFi Setup</button>
 | 
			
		||||
<button type="submit" onclick="window.location='./settings/leds'">LED Preferences</button>
 | 
			
		||||
<button type="submit" onclick="window.location='./settings/2D'">2D Configuration</button>
 | 
			
		||||
<button type="submit" onclick="window.location='./settings/ui'">User Interface</button>
 | 
			
		||||
<button id="dmxbtn" style="display: none;" type="submit" onclick="window.location='./settings/dmx'">DMX Output</button>
 | 
			
		||||
<button type="submit" onclick="window.location='./settings/sync'">Sync Interfaces</button>
 | 
			
		||||
<button type="submit" onclick="window.location='./settings/time'">Time & Macros</button>
 | 
			
		||||
<button type="submit" onclick="window.location='./settings/um'">Usermods</button>
 | 
			
		||||
<button type="submit" onclick="window.location='./settings/sec'">Security & Updates</button>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,126 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
	<meta charset="utf-8">
 | 
			
		||||
	<meta name="viewport" content="width=500">
 | 
			
		||||
	<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
 | 
			
		||||
	<title>2D Set-up</title>
 | 
			
		||||
	<script>
 | 
			
		||||
	var d=document;
 | 
			
		||||
	function H(){window.open("https://kno.wled.ge/features/2D");}
 | 
			
		||||
	function B(){window.open("/settings","_self");}
 | 
			
		||||
	function gId(n){return d.getElementById(n);}
 | 
			
		||||
 | 
			
		||||
	var maxPanels=64;
 | 
			
		||||
	function UI(change=false)
 | 
			
		||||
	{
 | 
			
		||||
		if (gId("somp").value === "0") {
 | 
			
		||||
			gId("mpdiv").style.display = "none";
 | 
			
		||||
			resetPanels();
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		gId("mpdiv").style.display = "block";
 | 
			
		||||
		maxPanels = parseInt(d.Sf.MPH.value) * parseInt(d.Sf.MPV.value);
 | 
			
		||||
 | 
			
		||||
		let i = gId("panels").children.length;
 | 
			
		||||
		if (i<maxPanels) for (let j=i; j<maxPanels; j++) addPanel(j);
 | 
			
		||||
		if (i>maxPanels) for (let j=i; j>maxPanels; j--) remPanel();
 | 
			
		||||
		//btnPanel(gId("panels").children.length);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function addPanels() {
 | 
			
		||||
		let h = parseInt(d.Sf.MPH.value);
 | 
			
		||||
		let v = parseInt(d.Sf.MPV.value);
 | 
			
		||||
		for (let i=0; i<h*v; i++) addPanel(i);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function addPanel(i=0) {
 | 
			
		||||
		let p = gId("panels");
 | 
			
		||||
		if (p.children.length >= maxPanels) return;
 | 
			
		||||
		let b = `<div id="pnl${i}">${i===0?"":'<hr style="width:260px">'}Panel ${i}<br>1<sup>st</sup> LED: <select name="P${i}B">
 | 
			
		||||
	<option value="0">Top</option>
 | 
			
		||||
	<option value="1">Bottom</option>
 | 
			
		||||
</select><select name="P${i}R">
 | 
			
		||||
	<option value="0">Left</option>
 | 
			
		||||
	<option value="1">Right</option>
 | 
			
		||||
</select><br>
 | 
			
		||||
Orientation: <select name="P${i}V">
 | 
			
		||||
	<option value="0">Horizontal</option>
 | 
			
		||||
	<option value="1">Vertical</option>
 | 
			
		||||
</select><br>
 | 
			
		||||
Serpentine: <input type="checkbox" name="P${i}S"></div>`;
 | 
			
		||||
		p.insertAdjacentHTML("beforeend", b);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    function remPanel() {
 | 
			
		||||
		let p = gId("panels").children;
 | 
			
		||||
      	var i = p.length;
 | 
			
		||||
      	if (i <= 1) return;
 | 
			
		||||
      	p[i-1].remove();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	function resetPanels() {
 | 
			
		||||
		d.Sf.MPH.value = 1;
 | 
			
		||||
		d.Sf.MPV.value = 1;
 | 
			
		||||
		for (let e of gId("panels").children) e.remove();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function btnPanel(i) {
 | 
			
		||||
		gId("pnl_add").style.display = (i<maxPanels) ? "inline":"none";
 | 
			
		||||
		gId("pnl_rem").style.display = (i>1) ? "inline":"none";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function S() {GetV();UI()}
 | 
			
		||||
	//values injected by server while sending HTML
 | 
			
		||||
	function GetV() {}
 | 
			
		||||
	</script>
 | 
			
		||||
	<style>@import url("style.css");</style>
 | 
			
		||||
</head>
 | 
			
		||||
<body onload="S()">
 | 
			
		||||
	<form id="form_s" name="Sf" method="post">
 | 
			
		||||
	<div class="toprow">
 | 
			
		||||
		<div class="helpB"><button type="button" onclick="H()">?</button></div>
 | 
			
		||||
		<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
 | 
			
		||||
	</div>
 | 
			
		||||
	<h2>2D setup</h2>
 | 
			
		||||
    Strip or panel:
 | 
			
		||||
	<select id="somp" name="SOMP" onchange="resetPanels();addPanels();UI();" >
 | 
			
		||||
		<option value="0">1D Strip</option>
 | 
			
		||||
		<option value="1">2D Matrix</option>
 | 
			
		||||
	</select><br>
 | 
			
		||||
	<div id="mpdiv" style="display:none;">
 | 
			
		||||
		<h3>2D Matrix</h3>
 | 
			
		||||
		Panel dimensions (WxH): <input name="PW" type="number" min="1" max="128" value="8"> x <input name="PH" type="number" min="1" max="128" value="8"><br>
 | 
			
		||||
		Horizontal panels: <input name="MPH" type="number" min="1" max="8" value="1" oninput="UI()">
 | 
			
		||||
		Vertical panels: <input name="MPV" type="number" min="1" max="8" value="1" oninput="UI()"><br>
 | 
			
		||||
		1<sup>st</sup> panel: <select name="PB">
 | 
			
		||||
			<option value="0">Top</option>
 | 
			
		||||
			<option value="1">Bottom</option>
 | 
			
		||||
		</select><select name="PR">
 | 
			
		||||
			<option value="0">Left</option>
 | 
			
		||||
			<option value="1">Right</option>
 | 
			
		||||
		</select><br>
 | 
			
		||||
		Orientation: <select name="PV">
 | 
			
		||||
			<option value="0">Horizontal</option>
 | 
			
		||||
			<option value="1">Vertical</option>
 | 
			
		||||
		</select><br>
 | 
			
		||||
		Serpentine: <input type="checkbox" name="PS">
 | 
			
		||||
		<hr style="width:260px">
 | 
			
		||||
		<i>A matrix is made of 1 or more physical led panels of the same dimensions.<br>
 | 
			
		||||
		Panels should be arranged from top-left to bottom-right order, starting with lower panel number on the left (or top if transposed).<br>
 | 
			
		||||
		Each panel can have different orientation and/or starting point and/or layout.</i><br>
 | 
			
		||||
		<hr style="width:260px">
 | 
			
		||||
		<h3>2D Panel layout</h3>
 | 
			
		||||
		<div id="panels">
 | 
			
		||||
		</div>
 | 
			
		||||
		<!--
 | 
			
		||||
		<button type="button" class="xs" id="pnl_add" onclick="addPanel()">+</button>
 | 
			
		||||
		<button type="button" class="xs" id="pnl_rem" onclick="remPanel()">-</button>
 | 
			
		||||
		-->
 | 
			
		||||
	</div>
 | 
			
		||||
	<hr>
 | 
			
		||||
	<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
 | 
			
		||||
	</form>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -64,50 +64,52 @@ const uint8_t PAGE_settingsCss[] PROGMEM = {
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
// Autogenerated from wled00/data/settings.htm, do not edit!!
 | 
			
		||||
const uint16_t PAGE_settings_length = 669;
 | 
			
		||||
const uint16_t PAGE_settings_length = 694;
 | 
			
		||||
const uint8_t PAGE_settings[] PROGMEM = {
 | 
			
		||||
  0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x8d, 0x94, 0xdb, 0x6e, 0xdb, 0x30,
 | 
			
		||||
  0x0c, 0x86, 0xef, 0xfd, 0x14, 0x9a, 0x8b, 0x0d, 0x09, 0x10, 0x37, 0x6d, 0x3a, 0x0c, 0x83, 0x0f,
 | 
			
		||||
  0x19, 0xd0, 0xb5, 0xdd, 0x0a, 0xac, 0x68, 0x81, 0x9e, 0xb6, 0x4b, 0x59, 0xa2, 0x13, 0xae, 0x96,
 | 
			
		||||
  0x64, 0x48, 0x74, 0x1a, 0x2f, 0xc8, 0xbb, 0x4f, 0x76, 0xb2, 0xb4, 0x6b, 0x17, 0x2c, 0x37, 0x06,
 | 
			
		||||
  0x48, 0x51, 0x3f, 0xc9, 0x8f, 0x94, 0xd3, 0x37, 0x27, 0x97, 0x9f, 0x6f, 0x7e, 0x5c, 0x9d, 0xb2,
 | 
			
		||||
  0x29, 0xa9, 0x72, 0x9c, 0xb6, 0x5f, 0x56, 0x72, 0x3d, 0xc9, 0x42, 0xd0, 0xa1, 0xb7, 0x81, 0xcb,
 | 
			
		||||
  0x71, 0xaa, 0x80, 0x38, 0x13, 0x53, 0x6e, 0x1d, 0x50, 0x16, 0xde, 0xde, 0x9c, 0x45, 0x1f, 0xc3,
 | 
			
		||||
  0xb5, 0x37, 0x10, 0x46, 0x13, 0x68, 0xef, 0x7e, 0x44, 0x49, 0xd3, 0x4c, 0xc2, 0x0c, 0x05, 0x44,
 | 
			
		||||
  0x9d, 0x31, 0x40, 0x8d, 0x84, 0xbc, 0x8c, 0x9c, 0xe0, 0x25, 0x64, 0x87, 0x03, 0xc5, 0xe7, 0xa8,
 | 
			
		||||
  0x6a, 0xb5, 0xb1, 0x6b, 0x07, 0xb6, 0x33, 0x78, 0xee, 0x6d, 0x6d, 0x42, 0x16, 0x68, 0xae, 0x20,
 | 
			
		||||
  0x0b, 0x67, 0x08, 0x8f, 0x95, 0xb1, 0xe4, 0xb3, 0x10, 0x52, 0x09, 0xe3, 0xfb, 0x6f, 0xa7, 0x27,
 | 
			
		||||
  0xec, 0x1a, 0x88, 0x50, 0x4f, 0x5c, 0x3a, 0x5c, 0x39, 0x53, 0x27, 0x2c, 0x56, 0x34, 0x0e, 0x66,
 | 
			
		||||
  0xdc, 0x32, 0x99, 0x49, 0x23, 0x6a, 0xe5, 0x4b, 0x49, 0x8a, 0x5a, 0x0b, 0x42, 0xa3, 0xd9, 0xe4,
 | 
			
		||||
  0x5c, 0xf6, 0x74, 0x7f, 0x61, 0x81, 0x6a, 0xab, 0x99, 0xdc, 0x9f, 0x00, 0x9d, 0x96, 0xd0, 0xc6,
 | 
			
		||||
  0x1c, 0x37, 0xdd, 0xd1, 0x72, 0x13, 0x7a, 0xdd, 0xeb, 0x2f, 0xbe, 0x00, 0xdd, 0xf5, 0xfa, 0xcb,
 | 
			
		||||
  0x20, 0x1d, 0xae, 0x85, 0xd7, 0x09, 0x98, 0xb3, 0x22, 0x0b, 0x87, 0x6e, 0x9d, 0x7e, 0xe8, 0xf6,
 | 
			
		||||
  0x7f, 0xba, 0x4f, 0x55, 0x76, 0xe0, 0xab, 0x7b, 0x8a, 0xa4, 0xc6, 0x57, 0x14, 0xe4, 0x46, 0x36,
 | 
			
		||||
  0x0b, 0x82, 0x39, 0x45, 0xbc, 0xc4, 0x89, 0x8e, 0x85, 0xcf, 0x05, 0x36, 0xc9, 0xb9, 0x78, 0x98,
 | 
			
		||||
  0x58, 0x53, 0x6b, 0x19, 0xef, 0x8d, 0x46, 0xa3, 0x64, 0x0a, 0x38, 0x99, 0x52, 0x7c, 0x78, 0x70,
 | 
			
		||||
  0x50, 0xcd, 0x13, 0xc5, 0xed, 0x04, 0x75, 0x7c, 0xb0, 0x6c, 0xe1, 0x2f, 0xa2, 0x68, 0xea, 0xfd,
 | 
			
		||||
  0xfb, 0xa3, 0xd9, 0x74, 0x99, 0xd7, 0x44, 0x46, 0x2f, 0x9e, 0x5f, 0x3e, 0x3a, 0x3a, 0x4a, 0x84,
 | 
			
		||||
  0x29, 0x8d, 0x8d, 0xf7, 0x8a, 0xa2, 0x48, 0x0a, 0x0f, 0x3f, 0x2a, 0xb8, 0xc2, 0xb2, 0x89, 0xef,
 | 
			
		||||
  0xc0, 0x4a, 0xae, 0xf9, 0xe0, 0x2b, 0x94, 0x33, 0x20, 0x14, 0x7c, 0xe0, 0xb8, 0x76, 0x91, 0x47,
 | 
			
		||||
  0x8c, 0x45, 0x22, 0xd1, 0x55, 0x25, 0x6f, 0x62, 0xd4, 0x25, 0x6a, 0x88, 0xf2, 0xd2, 0x88, 0x87,
 | 
			
		||||
  0x24, 0x37, 0x56, 0x82, 0x8d, 0x0f, 0xab, 0x39, 0x73, 0xa6, 0x44, 0xc9, 0x3a, 0xf9, 0x95, 0x37,
 | 
			
		||||
  0xb2, 0x5c, 0x62, 0xed, 0x62, 0x8f, 0xb6, 0xe7, 0x4b, 0xea, 0xaf, 0x52, 0x39, 0xfc, 0x05, 0xf1,
 | 
			
		||||
  0x87, 0x99, 0x42, 0xfd, 0xa7, 0x85, 0xcd, 0x79, 0x37, 0xf1, 0xd8, 0xcf, 0x52, 0xf4, 0x7c, 0x5b,
 | 
			
		||||
  0x6f, 0x59, 0xc4, 0xde, 0xfb, 0xe6, 0xfa, 0xeb, 0xee, 0x22, 0x32, 0x55, 0xdc, 0xf6, 0xd4, 0xc2,
 | 
			
		||||
  0xed, 0x50, 0xa5, 0xc3, 0xd5, 0x6e, 0xb5, 0xc4, 0x98, 0xd1, 0xa5, 0xe1, 0x32, 0x0b, 0xfd, 0x14,
 | 
			
		||||
  0x3c, 0xd4, 0xc2, 0x58, 0xc5, 0x78, 0x37, 0x17, 0x8f, 0xdd, 0x3b, 0x56, 0x20, 0x18, 0x35, 0x95,
 | 
			
		||||
  0xdf, 0x0d, 0x57, 0xe7, 0x0a, 0x29, 0x64, 0xe8, 0xe3, 0xf3, 0x70, 0x1c, 0x1c, 0x7b, 0x3c, 0xe9,
 | 
			
		||||
  0x70, 0x15, 0xe2, 0x55, 0xdb, 0xcb, 0x2f, 0x25, 0x36, 0x93, 0x7b, 0xc4, 0x02, 0xb7, 0xe8, 0x8d,
 | 
			
		||||
  0x83, 0x7b, 0x3c, 0xc3, 0x76, 0xc7, 0xea, 0x6a, 0x57, 0xb9, 0x12, 0xa4, 0xdb, 0x2a, 0xd7, 0x6e,
 | 
			
		||||
  0xec, 0x95, 0x85, 0x02, 0x2c, 0x68, 0x01, 0x6e, 0x57, 0xcd, 0xfa, 0x59, 0x81, 0xc1, 0xdf, 0x92,
 | 
			
		||||
  0xb7, 0x7e, 0x96, 0xec, 0xbc, 0x5d, 0xa8, 0x82, 0x0b, 0xd8, 0x55, 0x50, 0xaa, 0xf9, 0x46, 0x31,
 | 
			
		||||
  0xe8, 0xa8, 0x79, 0x4f, 0x4e, 0x3a, 0x64, 0xdd, 0x20, 0xbc, 0xb9, 0xde, 0x0d, 0x6d, 0x34, 0x84,
 | 
			
		||||
  0x2f, 0xba, 0x38, 0xb9, 0xf8, 0xce, 0x2e, 0x6b, 0xaa, 0x6a, 0xfa, 0x77, 0xba, 0xe0, 0x75, 0x3e,
 | 
			
		||||
  0xd7, 0x68, 0xb1, 0x0d, 0xca, 0xb5, 0x3f, 0x7b, 0xea, 0xe0, 0x15, 0x93, 0x60, 0x5b, 0x0f, 0x84,
 | 
			
		||||
  0x0a, 0xb6, 0x69, 0xde, 0xf8, 0x33, 0xf6, 0x8e, 0x5d, 0x70, 0x61, 0xcd, 0x93, 0x62, 0xf0, 0x3f,
 | 
			
		||||
  0xcc, 0x6a, 0x9b, 0x5e, 0x4b, 0x59, 0x19, 0xb9, 0xbb, 0x94, 0x83, 0xed, 0xfd, 0x82, 0xa8, 0x2d,
 | 
			
		||||
  0x52, 0xe3, 0xeb, 0xbb, 0xad, 0x24, 0x27, 0x70, 0xc1, 0x2b, 0x8e, 0xc3, 0xf6, 0x05, 0xb4, 0xcf,
 | 
			
		||||
  0xa1, 0xfd, 0x01, 0xff, 0x06, 0x3e, 0x6a, 0x2c, 0x53, 0x90, 0x05, 0x00, 0x00
 | 
			
		||||
  0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xad, 0x55, 0x6d, 0x6f, 0xd3, 0x30,
 | 
			
		||||
  0x10, 0xfe, 0xde, 0x5f, 0x61, 0x32, 0xc1, 0x5a, 0xa9, 0x69, 0xbb, 0x0e, 0x21, 0xc8, 0x4b, 0x91,
 | 
			
		||||
  0xb6, 0x6e, 0x30, 0x89, 0x69, 0x93, 0xf6, 0x06, 0x1f, 0x1d, 0xfb, 0x92, 0x1c, 0x4b, 0xec, 0xc8,
 | 
			
		||||
  0x76, 0xfa, 0x42, 0xd5, 0xff, 0xce, 0x25, 0x2d, 0xdb, 0x10, 0x13, 0x4c, 0xa8, 0x5f, 0xe2, 0xdc,
 | 
			
		||||
  0xd9, 0x79, 0xee, 0xb9, 0xe7, 0x39, 0x2b, 0xd1, 0xab, 0xe9, 0xc5, 0xf1, 0xf5, 0xb7, 0xcb, 0x13,
 | 
			
		||||
  0x96, 0xbb, 0xb2, 0x98, 0x44, 0xcd, 0x93, 0x15, 0x5c, 0x65, 0xb1, 0x07, 0xca, 0xa3, 0x18, 0xb8,
 | 
			
		||||
  0x9c, 0x44, 0x25, 0x38, 0xce, 0x44, 0xce, 0x8d, 0x05, 0x17, 0x7b, 0x37, 0xd7, 0xa7, 0xfe, 0x7b,
 | 
			
		||||
  0x6f, 0x9b, 0xed, 0x08, 0xad, 0x1c, 0x28, 0x4a, 0xcf, 0x51, 0xba, 0x3c, 0x96, 0x30, 0x43, 0x01,
 | 
			
		||||
  0x7e, 0x1b, 0xf4, 0x51, 0xa1, 0x43, 0x5e, 0xf8, 0x56, 0xf0, 0x02, 0xe2, 0x83, 0x7e, 0xc9, 0x17,
 | 
			
		||||
  0x58, 0xd6, 0xe5, 0x43, 0x5c, 0x5b, 0x30, 0x6d, 0xc0, 0x13, 0x8a, 0x95, 0xf6, 0x58, 0x47, 0xf1,
 | 
			
		||||
  0x12, 0x62, 0x6f, 0x86, 0x30, 0xaf, 0xb4, 0x71, 0x54, 0xc5, 0xa1, 0x2b, 0x60, 0x72, 0xf7, 0xe5,
 | 
			
		||||
  0x64, 0xca, 0xae, 0xc0, 0x39, 0x54, 0x99, 0x8d, 0x86, 0x9b, 0x64, 0x64, 0x85, 0xc1, 0xca, 0x4d,
 | 
			
		||||
  0x3a, 0x33, 0x6e, 0x98, 0x8c, 0xa5, 0x16, 0x75, 0x49, 0x54, 0xc2, 0xb4, 0x56, 0xc2, 0xa1, 0x56,
 | 
			
		||||
  0x2c, 0x3b, 0x93, 0x5d, 0xd5, 0x5b, 0x19, 0x70, 0xb5, 0x51, 0x4c, 0x0e, 0x32, 0x70, 0x27, 0x05,
 | 
			
		||||
  0x34, 0x67, 0x8e, 0x96, 0xed, 0xd6, 0xfa, 0xe1, 0xe8, 0x55, 0xb7, 0xb7, 0xfa, 0x04, 0xee, 0xb6,
 | 
			
		||||
  0xdb, 0x5b, 0x77, 0xa2, 0xe1, 0x16, 0x78, 0x5b, 0x80, 0x59, 0x23, 0x62, 0x6f, 0x68, 0xb7, 0xe5,
 | 
			
		||||
  0x87, 0x76, 0xf0, 0xdd, 0x7e, 0xac, 0xe2, 0x11, 0xb1, 0x7b, 0x3c, 0xe9, 0x96, 0xc4, 0xa8, 0x93,
 | 
			
		||||
  0x68, 0xb9, 0x5c, 0x39, 0x58, 0x38, 0x9f, 0x17, 0x98, 0xa9, 0x40, 0x50, 0x2d, 0x30, 0x61, 0xc2,
 | 
			
		||||
  0xc5, 0x7d, 0x66, 0x74, 0xad, 0x64, 0xb0, 0x37, 0x1e, 0x8f, 0xc3, 0x1c, 0x30, 0xcb, 0x5d, 0x70,
 | 
			
		||||
  0x30, 0x1a, 0x55, 0x8b, 0xb0, 0xe4, 0x26, 0x43, 0x15, 0x8c, 0xd6, 0x8d, 0xf8, 0x2b, 0xdf, 0xcf,
 | 
			
		||||
  0x83, 0x0f, 0xb3, 0x7c, 0x9d, 0xd4, 0xce, 0x69, 0xb5, 0x7a, 0xfa, 0xe5, 0xe1, 0xe1, 0x61, 0x28,
 | 
			
		||||
  0x74, 0xa1, 0x4d, 0xb0, 0x97, 0xa6, 0x69, 0x98, 0x92, 0xf2, 0x7e, 0xca, 0x4b, 0x2c, 0x96, 0xc1,
 | 
			
		||||
  0x2d, 0x18, 0xc9, 0x15, 0xef, 0x7f, 0x86, 0x62, 0x06, 0x0e, 0x05, 0xef, 0x5b, 0xae, 0xac, 0x4f,
 | 
			
		||||
  0xfa, 0x62, 0x1a, 0x4a, 0xb4, 0x55, 0xc1, 0x97, 0x41, 0x52, 0x68, 0x71, 0x1f, 0x26, 0xda, 0x48,
 | 
			
		||||
  0x30, 0xc1, 0x41, 0xb5, 0x60, 0x56, 0x17, 0x28, 0x59, 0x8b, 0xbb, 0xc9, 0xfa, 0x86, 0x4b, 0xac,
 | 
			
		||||
  0x6d, 0x40, 0x82, 0x76, 0x89, 0x48, 0x6f, 0x53, 0xc3, 0xe2, 0x0f, 0x08, 0xde, 0xcd, 0x4a, 0x54,
 | 
			
		||||
  0xbf, 0x88, 0x3f, 0xec, 0xb7, 0x3e, 0x07, 0xe4, 0xa0, 0xe8, 0x52, 0x33, 0xaf, 0x99, 0xcf, 0xde,
 | 
			
		||||
  0x52, 0x4b, 0xbd, 0x6d, 0x4f, 0xbe, 0xd3, 0x55, 0x30, 0x9e, 0xe5, 0xa1, 0xa8, 0x8d, 0x25, 0xda,
 | 
			
		||||
  0x95, 0xc6, 0x46, 0x8e, 0x56, 0xe1, 0x56, 0xaf, 0x68, 0xb8, 0x19, 0xb0, 0x46, 0x36, 0xa6, 0x55,
 | 
			
		||||
  0xa1, 0xb9, 0x8c, 0x3d, 0xb2, 0x82, 0x94, 0xdd, 0xf4, 0xcf, 0xdc, 0xb2, 0xa2, 0x79, 0xb0, 0x75,
 | 
			
		||||
  0x52, 0xa2, 0xf3, 0x18, 0xd2, 0x76, 0x42, 0x53, 0xa2, 0x95, 0x28, 0x50, 0xdc, 0xc7, 0xfb, 0x73,
 | 
			
		||||
  0x54, 0x52, 0xcf, 0x07, 0xd4, 0x18, 0x6f, 0x6c, 0x24, 0x97, 0xbc, 0xfd, 0xc9, 0x11, 0x69, 0x16,
 | 
			
		||||
  0x0d, 0x37, 0x00, 0x13, 0xf6, 0x3c, 0xd2, 0x5f, 0x20, 0x06, 0x8f, 0x4e, 0xcf, 0x31, 0x45, 0x02,
 | 
			
		||||
  0xbc, 0xc3, 0x53, 0x6c, 0xa6, 0xaf, 0xae, 0xfe, 0x84, 0xed, 0xfc, 0x8e, 0xfb, 0x22, 0xd8, 0x02,
 | 
			
		||||
  0xa4, 0x25, 0xd8, 0x66, 0xa6, 0x2f, 0x0d, 0xa4, 0x60, 0x40, 0x09, 0xb0, 0x9d, 0x7f, 0x71, 0x7e,
 | 
			
		||||
  0x11, 0xf6, 0x78, 0x4a, 0xc8, 0x9d, 0xf1, 0x94, 0x1d, 0x6b, 0x95, 0x62, 0x56, 0x9b, 0xf6, 0xc8,
 | 
			
		||||
  0x6e, 0xe4, 0xa8, 0x1b, 0x31, 0x6e, 0x68, 0xa8, 0xd8, 0x59, 0xe3, 0x63, 0xca, 0x05, 0x3c, 0x23,
 | 
			
		||||
  0x48, 0x63, 0x92, 0x2c, 0x17, 0x89, 0x53, 0x1e, 0x6b, 0x6d, 0xa6, 0x70, 0x3b, 0x81, 0x4a, 0x2b,
 | 
			
		||||
  0xf0, 0xfe, 0xab, 0x34, 0x01, 0x52, 0xed, 0xe9, 0xf9, 0x57, 0x76, 0x51, 0xbb, 0xaa, 0x76, 0xbb,
 | 
			
		||||
  0x31, 0xc2, 0x2e, 0x95, 0x20, 0xd8, 0x2b, 0x5a, 0x1e, 0x5b, 0xda, 0x91, 0x11, 0x0e, 0x4b, 0x68,
 | 
			
		||||
  0xac, 0xb8, 0xa6, 0x95, 0xbd, 0x61, 0xe7, 0x5c, 0x18, 0x6d, 0x77, 0xe4, 0x43, 0xb9, 0xf5, 0xa1,
 | 
			
		||||
  0xd4, 0xd2, 0xee, 0x48, 0x09, 0x68, 0x85, 0x00, 0xba, 0xa9, 0xe8, 0x96, 0xc4, 0xf7, 0xa6, 0x92,
 | 
			
		||||
  0xdc, 0x3d, 0xd5, 0x82, 0x5e, 0xe8, 0x96, 0x36, 0x57, 0xb6, 0xf9, 0x53, 0xfc, 0x04, 0xe7, 0xc3,
 | 
			
		||||
  0x64, 0x44, 0x39, 0x06, 0x00, 0x00
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1548,6 +1550,103 @@ const uint8_t PAGE_settings_um[] PROGMEM = {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Autogenerated from wled00/data/settings_2D.htm, do not edit!!
 | 
			
		||||
const uint16_t PAGE_settings_2D_length = 1453;
 | 
			
		||||
const uint8_t PAGE_settings_2D[] PROGMEM = {
 | 
			
		||||
  0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x8d, 0x57, 0x6d, 0x6f, 0xdb, 0x36,
 | 
			
		||||
  0x10, 0xfe, 0xae, 0x5f, 0xc1, 0x12, 0x05, 0x6a, 0xb5, 0xb6, 0x15, 0x67, 0x5b, 0x51, 0xc4, 0x92,
 | 
			
		||||
  0xb2, 0x7a, 0x09, 0xe6, 0x02, 0x0d, 0x1a, 0xd4, 0x5d, 0x8a, 0x61, 0x1d, 0x5a, 0x59, 0x3a, 0x47,
 | 
			
		||||
  0x6c, 0x28, 0x52, 0x20, 0x29, 0x3b, 0x99, 0x9b, 0xff, 0xbe, 0x23, 0x25, 0x5b, 0xb6, 0x63, 0xa7,
 | 
			
		||||
  0xf9, 0x12, 0x99, 0x2f, 0xf7, 0x7e, 0xf7, 0x3c, 0x4c, 0xf8, 0xec, 0xec, 0xc3, 0x1f, 0x9f, 0xfe,
 | 
			
		||||
  0xbe, 0x3c, 0x27, 0xb9, 0x29, 0x78, 0x1c, 0xda, 0xbf, 0x84, 0x27, 0xe2, 0x3a, 0xa2, 0x20, 0x28,
 | 
			
		||||
  0xae, 0x21, 0xc9, 0xe2, 0xb0, 0x00, 0x93, 0x90, 0x34, 0x4f, 0x94, 0x06, 0x13, 0xd1, 0xca, 0xcc,
 | 
			
		||||
  0x7a, 0x6f, 0x68, 0xb3, 0xeb, 0x89, 0xa4, 0x80, 0x88, 0xce, 0x19, 0x2c, 0x4a, 0xa9, 0x0c, 0x25,
 | 
			
		||||
  0xa9, 0x14, 0x06, 0x04, 0x5e, 0x5b, 0xb0, 0xcc, 0xe4, 0xd1, 0x6f, 0x47, 0x47, 0xeb, 0xab, 0x3b,
 | 
			
		||||
  0x47, 0x19, 0xcc, 0x59, 0x0a, 0x3d, 0xb7, 0xe8, 0x32, 0xc1, 0x0c, 0x4b, 0x78, 0x4f, 0xa7, 0x09,
 | 
			
		||||
  0x87, 0x68, 0xd0, 0x2d, 0x92, 0x5b, 0x56, 0x54, 0xc5, 0x7a, 0x5d, 0x69, 0x50, 0x6e, 0x91, 0x4c,
 | 
			
		||||
  0x71, 0x2d, 0x24, 0x7d, 0x60, 0x39, 0x0e, 0x0d, 0x33, 0x1c, 0xe2, 0xe3, 0x33, 0x32, 0x01, 0xd3,
 | 
			
		||||
  0xab, 0xca, 0x30, 0xa8, 0x37, 0x42, 0x9d, 0x2a, 0x56, 0x9a, 0xd8, 0x9b, 0x27, 0x8a, 0x64, 0x51,
 | 
			
		||||
  0x26, 0xd3, 0xaa, 0x40, 0x37, 0x86, 0xb3, 0x4a, 0xa4, 0x86, 0x49, 0x41, 0xc6, 0x1d, 0x7f, 0xb9,
 | 
			
		||||
  0x60, 0x22, 0x93, 0x8b, 0xbe, 0x2c, 0x41, 0x74, 0x68, 0x6e, 0x4c, 0xa9, 0x4f, 0x82, 0xe0, 0x46,
 | 
			
		||||
  0xc8, 0xfe, 0x82, 0x43, 0xd6, 0xbf, 0x86, 0x60, 0x06, 0x89, 0xa9, 0x14, 0xe8, 0xe0, 0xf8, 0x8c,
 | 
			
		||||
  0xfa, 0xf7, 0x6b, 0xd9, 0xd1, 0xae, 0x6c, 0x80, 0x39, 0x32, 0x4c, 0x5c, 0x6b, 0xda, 0xa5, 0x5f,
 | 
			
		||||
  0x35, 0xf0, 0xd9, 0xe6, 0xed, 0xeb, 0x77, 0x59, 0x07, 0xfc, 0xa5, 0x02, 0x54, 0x25, 0x88, 0xd5,
 | 
			
		||||
  0x6b, 0xce, 0x39, 0x58, 0x6f, 0x46, 0x77, 0xee, 0xe8, 0xde, 0xfa, 0x88, 0xb1, 0x5f, 0x26, 0x02,
 | 
			
		||||
  0xb8, 0x8e, 0x5e, 0xff, 0xda, 0x7a, 0xf9, 0xd7, 0xbb, 0x0e, 0x44, 0xcf, 0x06, 0xfe, 0x92, 0xcd,
 | 
			
		||||
  0x3a, 0xf4, 0x88, 0x46, 0x51, 0x64, 0xb5, 0x51, 0x2d, 0x8b, 0x92, 0xfa, 0xfd, 0x79, 0xc2, 0x2b,
 | 
			
		||||
  0xf0, 0x1b, 0xc5, 0xee, 0xa0, 0x28, 0x33, 0x36, 0xc7, 0x13, 0x6d, 0xee, 0x38, 0xf4, 0x33, 0xa6,
 | 
			
		||||
  0x4b, 0x9e, 0xdc, 0x45, 0x54, 0x48, 0x01, 0xb4, 0x3b, 0x97, 0x2c, 0x23, 0x18, 0x0d, 0x98, 0xda,
 | 
			
		||||
  0x52, 0xc7, 0x1f, 0x3e, 0x26, 0x34, 0xe5, 0x32, 0xbd, 0xa1, 0xdd, 0xd6, 0xb1, 0xd2, 0xf6, 0xc2,
 | 
			
		||||
  0x3b, 0x61, 0x3a, 0x59, 0x7f, 0x32, 0xeb, 0x5f, 0x5c, 0x8e, 0x1b, 0x07, 0x5e, 0xee, 0x1e, 0x5c,
 | 
			
		||||
  0x35, 0x07, 0x43, 0x0e, 0x86, 0x88, 0xda, 0xe5, 0xd2, 0xe9, 0x40, 0x2b, 0x69, 0xce, 0x78, 0xa6,
 | 
			
		||||
  0x40, 0xf4, 0x39, 0x88, 0x6b, 0x93, 0x0f, 0x31, 0x32, 0x11, 0xae, 0x8d, 0xf8, 0x33, 0xa9, 0x3a,
 | 
			
		||||
  0x56, 0x0c, 0xeb, 0x3d, 0x84, 0x76, 0x7f, 0x08, 0xaf, 0x5e, 0xf9, 0x49, 0x96, 0xb9, 0x15, 0xe6,
 | 
			
		||||
  0xcc, 0x89, 0xc5, 0x07, 0xc4, 0xe2, 0x0d, 0xb1, 0x5e, 0x0f, 0x13, 0x54, 0xd4, 0x62, 0x1b, 0x45,
 | 
			
		||||
  0x59, 0xa9, 0xc2, 0x24, 0x2c, 0x6b, 0xb9, 0x43, 0xd1, 0x75, 0x45, 0x74, 0x30, 0xbe, 0x95, 0x55,
 | 
			
		||||
  0x13, 0x1d, 0x0d, 0x4d, 0x08, 0x2f, 0xc5, 0xd0, 0x6c, 0xba, 0x69, 0xf6, 0x18, 0xc4, 0x7a, 0x1e,
 | 
			
		||||
  0xd5, 0x26, 0x77, 0x12, 0xe3, 0x22, 0xda, 0xcd, 0x4e, 0x1c, 0xb5, 0x21, 0xd6, 0x75, 0x1e, 0xd6,
 | 
			
		||||
  0xf6, 0xbe, 0x85, 0x58, 0x33, 0xc2, 0xb2, 0x88, 0x96, 0x82, 0x3f, 0x5f, 0xc2, 0x3d, 0x8d, 0x9f,
 | 
			
		||||
  0x2f, 0x8f, 0xb0, 0x3f, 0xe0, 0x94, 0xd2, 0x93, 0x17, 0x61, 0xae, 0x88, 0x2b, 0x67, 0x33, 0x77,
 | 
			
		||||
  0x27, 0xc7, 0xaf, 0x8f, 0xca, 0x5b, 0x1a, 0xbf, 0xb8, 0x77, 0xca, 0x88, 0x95, 0x08, 0xa7, 0x2a,
 | 
			
		||||
  0x1e, 0x84, 0xba, 0x2a, 0x63, 0x6d, 0xc2, 0xc0, 0x7e, 0xc9, 0xfb, 0xf3, 0xb3, 0x13, 0x12, 0x62,
 | 
			
		||||
  0x03, 0x43, 0x8a, 0xfe, 0xb9, 0x51, 0xbb, 0xb4, 0x57, 0x47, 0x34, 0xfe, 0x22, 0xbe, 0x98, 0x50,
 | 
			
		||||
  0x96, 0x2e, 0x16, 0x17, 0x7d, 0x84, 0xfd, 0x18, 0x7f, 0x92, 0x38, 0x71, 0xf5, 0xee, 0xbe, 0x1b,
 | 
			
		||||
  0x03, 0x1a, 0x8f, 0xa4, 0x31, 0xb2, 0xd8, 0xb8, 0x84, 0xa6, 0x9c, 0xfe, 0x78, 0x8f, 0x9d, 0x8f,
 | 
			
		||||
  0x87, 0xec, 0xbc, 0x87, 0x99, 0xf9, 0x89, 0xa1, 0x8f, 0xec, 0x3a, 0x37, 0x7b, 0xed, 0x60, 0x9c,
 | 
			
		||||
  0x5f, 0xc4, 0x07, 0xc5, 0x70, 0xe2, 0x12, 0x7b, 0xb8, 0x37, 0xc4, 0xab, 0x43, 0xa6, 0xc7, 0x52,
 | 
			
		||||
  0xb1, 0xff, 0x10, 0xc4, 0x12, 0xfe, 0x13, 0x07, 0xae, 0x40, 0x19, 0x96, 0x6e, 0x5d, 0xdb, 0xf1,
 | 
			
		||||
  0x61, 0x02, 0x0a, 0xe1, 0x02, 0x81, 0x02, 0xd0, 0x05, 0x26, 0xca, 0x0a, 0x2b, 0x79, 0x57, 0xa2,
 | 
			
		||||
  0x74, 0x9a, 0x43, 0x7a, 0x33, 0x95, 0xb7, 0x74, 0xd3, 0xa3, 0x09, 0xa2, 0x5b, 0x80, 0x55, 0x8e,
 | 
			
		||||
  0xbf, 0x0d, 0x45, 0x9f, 0x09, 0xc4, 0x42, 0xf3, 0x36, 0xfb, 0x9e, 0xa4, 0xa8, 0x61, 0xfc, 0xe9,
 | 
			
		||||
  0xe2, 0x7d, 0x87, 0x4e, 0x01, 0xfb, 0x0f, 0x40, 0x64, 0xb4, 0xbb, 0xd9, 0x69, 0x6d, 0xbb, 0x37,
 | 
			
		||||
  0x9d, 0xbd, 0x7f, 0xfe, 0x86, 0x16, 0x75, 0x44, 0x04, 0xab, 0x39, 0x14, 0x61, 0x34, 0xf8, 0xf1,
 | 
			
		||||
  0x03, 0xfe, 0x11, 0xbd, 0xc1, 0xbf, 0x7d, 0xd4, 0x21, 0xe7, 0xd0, 0xd9, 0xd2, 0xba, 0x81, 0x1b,
 | 
			
		||||
  0xcb, 0xed, 0x19, 0x41, 0xa8, 0xde, 0x9e, 0x8c, 0x68, 0xb0, 0x1e, 0x0d, 0x20, 0x72, 0x46, 0xf6,
 | 
			
		||||
  0x7b, 0xe0, 0xc3, 0x1e, 0x3b, 0x53, 0x23, 0x56, 0x33, 0xbe, 0xac, 0xc5, 0x04, 0xff, 0x8a, 0xc3,
 | 
			
		||||
  0xf3, 0x00, 0x9f, 0x36, 0xc0, 0xe1, 0x94, 0x32, 0xc1, 0x31, 0xab, 0xf4, 0xa4, 0x41, 0xba, 0xb5,
 | 
			
		||||
  0x20, 0xea, 0x7f, 0x28, 0x18, 0x0f, 0x76, 0x05, 0x5a, 0xf3, 0x13, 0x0c, 0xee, 0x4f, 0x30, 0x57,
 | 
			
		||||
  0x1d, 0xbf, 0x8b, 0xd8, 0xeb, 0xdf, 0x7b, 0x58, 0xc2, 0x9a, 0x49, 0x1a, 0x46, 0x21, 0x5a, 0xa5,
 | 
			
		||||
  0x51, 0x0b, 0xf8, 0x81, 0xee, 0x7f, 0xd7, 0xa7, 0x65, 0x34, 0xb0, 0x8c, 0xd7, 0x5e, 0xb5, 0x06,
 | 
			
		||||
  0x63, 0xef, 0x77, 0x56, 0x58, 0x96, 0x22, 0x95, 0xe2, 0x88, 0xd9, 0xce, 0x89, 0x54, 0xdb, 0x51,
 | 
			
		||||
  0xc7, 0x9b, 0xee, 0x46, 0x18, 0xd4, 0x5c, 0x3b, 0x95, 0xd9, 0x1d, 0x91, 0x82, 0xcb, 0x04, 0x07,
 | 
			
		||||
  0x1a, 0x5d, 0x40, 0x5d, 0x98, 0xbf, 0xc2, 0xcd, 0xb7, 0xfd, 0xf1, 0x55, 0xaf, 0xa9, 0x6f, 0x32,
 | 
			
		||||
  0xa3, 0x04, 0x99, 0x35, 0x97, 0x76, 0xf4, 0xa5, 0xb6, 0x14, 0x68, 0x91, 0x20, 0xe5, 0x89, 0xd6,
 | 
			
		||||
  0x11, 0x35, 0xb2, 0x54, 0x72, 0xb1, 0xbd, 0x97, 0x03, 0x2f, 0x71, 0x80, 0xc3, 0x69, 0x85, 0x93,
 | 
			
		||||
  0x28, 0x88, 0x57, 0xb7, 0x5c, 0xbd, 0xa2, 0x68, 0x35, 0xe5, 0x2c, 0xbd, 0x89, 0xe8, 0xd8, 0x9a,
 | 
			
		||||
  0x3d, 0x0d, 0x83, 0xfa, 0xa0, 0x69, 0xbd, 0x95, 0xd0, 0x01, 0x99, 0x91, 0x95, 0xf1, 0x46, 0x49,
 | 
			
		||||
  0x7a, 0xd3, 0xca, 0x6d, 0x49, 0xe8, 0x6a, 0x5a, 0x30, 0xf4, 0x71, 0x92, 0xcc, 0xa1, 0xbd, 0x92,
 | 
			
		||||
  0xab, 0x95, 0xfa, 0xfc, 0xd8, 0x52, 0x37, 0x26, 0xd3, 0x32, 0x37, 0x2e, 0xbc, 0x89, 0xc1, 0x0c,
 | 
			
		||||
  0x12, 0xa9, 0x88, 0xeb, 0x96, 0x76, 0x52, 0x6d, 0x26, 0x1c, 0xeb, 0x35, 0x13, 0x32, 0xf9, 0x70,
 | 
			
		||||
  0x71, 0x89, 0x39, 0x41, 0x4f, 0x72, 0x7c, 0xb8, 0xe0, 0xc6, 0x56, 0x7f, 0x76, 0x37, 0xe0, 0xdd,
 | 
			
		||||
  0xd5, 0x11, 0xc3, 0x7f, 0x30, 0xd8, 0x03, 0x7c, 0x33, 0x58, 0x6b, 0xeb, 0x79, 0xf5, 0x1e, 0xce,
 | 
			
		||||
  0x34, 0x3a, 0x77, 0x91, 0xe0, 0xa5, 0xdb, 0xf5, 0xa5, 0xad, 0x91, 0x5e, 0x83, 0x70, 0x4d, 0xa1,
 | 
			
		||||
  0xc4, 0x6b, 0x30, 0xb7, 0x69, 0xb5, 0x13, 0xd7, 0x5c, 0x18, 0xe5, 0x2f, 0x9b, 0x8a, 0x70, 0x55,
 | 
			
		||||
  0x83, 0x70, 0xc6, 0xf0, 0x11, 0xa0, 0x51, 0xa9, 0x26, 0x9d, 0xcf, 0xb7, 0x63, 0x7f, 0x0d, 0x0a,
 | 
			
		||||
  0x0d, 0x08, 0x7c, 0xa6, 0xab, 0x62, 0x89, 0xaa, 0x98, 0x82, 0xc2, 0xc2, 0x33, 0x61, 0xdd, 0xb2,
 | 
			
		||||
  0x8f, 0x05, 0xfc, 0x1e, 0xbf, 0xa1, 0x2b, 0x57, 0xf1, 0x69, 0x46, 0x6e, 0x77, 0xc4, 0xc7, 0x94,
 | 
			
		||||
  0x6c, 0x4b, 0x7b, 0x8f, 0x89, 0xdb, 0x70, 0x5a, 0xa8, 0xab, 0xd3, 0xaf, 0x77, 0x3c, 0xba, 0xb0,
 | 
			
		||||
  0x3a, 0x1f, 0x73, 0xa9, 0xd5, 0x38, 0xb0, 0x5d, 0xe2, 0x64, 0x23, 0x5a, 0x17, 0x80, 0xac, 0xf0,
 | 
			
		||||
  0x71, 0x57, 0xb7, 0xb7, 0x52, 0x7e, 0xb5, 0xeb, 0xf0, 0x93, 0x75, 0x6f, 0x52, 0x99, 0xd7, 0x70,
 | 
			
		||||
  0xd9, 0x4e, 0x03, 0x35, 0x49, 0x19, 0xed, 0xeb, 0x84, 0x4d, 0x16, 0x7b, 0xd8, 0x03, 0xde, 0x0e,
 | 
			
		||||
  0x85, 0x1d, 0x22, 0xb0, 0x8f, 0xfb, 0x54, 0x6f, 0x11, 0x57, 0x73, 0xec, 0x1d, 0x64, 0xad, 0xad,
 | 
			
		||||
  0xe6, 0x7a, 0x8c, 0xb1, 0xae, 0x5a, 0x5b, 0xde, 0xa3, 0x54, 0xf5, 0x04, 0x9a, 0xda, 0x32, 0xea,
 | 
			
		||||
  0x3d, 0x9d, 0xa3, 0x2c, 0x3f, 0x1d, 0x78, 0x66, 0x84, 0x2c, 0xf6, 0xde, 0x62, 0xd9, 0x6c, 0xc3,
 | 
			
		||||
  0x13, 0xa6, 0xf1, 0x57, 0xe6, 0x78, 0x60, 0x60, 0x27, 0xbb, 0x40, 0xd2, 0x22, 0x65, 0x7e, 0xa7,
 | 
			
		||||
  0x5d, 0x33, 0xe0, 0x4b, 0xbb, 0x69, 0x08, 0x7b, 0xc1, 0xe4, 0x40, 0x34, 0xaa, 0xdf, 0x98, 0x8d,
 | 
			
		||||
  0xbe, 0x73, 0xab, 0x9e, 0x66, 0xa2, 0x73, 0x59, 0xf1, 0x8c, 0x4c, 0x81, 0x24, 0x4a, 0xd9, 0xc1,
 | 
			
		||||
  0xcf, 0xc8, 0x4c, 0xc9, 0x82, 0x20, 0xee, 0xf5, 0x38, 0xa6, 0x1a, 0x7f, 0x90, 0xa9, 0x2b, 0x57,
 | 
			
		||||
  0x4f, 0xd9, 0xcc, 0xa2, 0xbd, 0x0c, 0x54, 0x17, 0x9d, 0x4c, 0x94, 0xc5, 0x6b, 0xb2, 0x60, 0x26,
 | 
			
		||||
  0x27, 0x5c, 0x2e, 0xa0, 0x41, 0x18, 0x52, 0xf7, 0x1a, 0xb1, 0x88, 0x85, 0xb6, 0x9d, 0x8e, 0x0e,
 | 
			
		||||
  0x3a, 0x89, 0x0a, 0x09, 0x43, 0x7f, 0xd0, 0x88, 0x46, 0x98, 0x85, 0xcc, 0xef, 0x7b, 0xce, 0x91,
 | 
			
		||||
  0xf3, 0x24, 0xcd, 0x1b, 0xd1, 0x34, 0x11, 0x24, 0x47, 0x60, 0x43, 0x67, 0x67, 0x33, 0x40, 0x42,
 | 
			
		||||
  0xb3, 0xe6, 0xd6, 0x35, 0x23, 0x89, 0xc8, 0x02, 0xa9, 0x5a, 0xd3, 0xa5, 0x64, 0x78, 0xa3, 0xd9,
 | 
			
		||||
  0x45, 0x74, 0x90, 0x95, 0x41, 0x9d, 0x01, 0xab, 0x91, 0xe4, 0x50, 0x26, 0x6b, 0xec, 0xa8, 0x01,
 | 
			
		||||
  0xa3, 0x16, 0x72, 0x08, 0xd2, 0x3e, 0xff, 0x6a, 0x56, 0x6d, 0x00, 0xd5, 0x5b, 0xe1, 0xaa, 0x7a,
 | 
			
		||||
  0x12, 0x74, 0xef, 0x45, 0x6e, 0xef, 0x31, 0xe8, 0x0e, 0x2c, 0x1d, 0xe1, 0xc7, 0x32, 0x96, 0xa5,
 | 
			
		||||
  0x2f, 0xfb, 0x0f, 0xe4, 0xff, 0x77, 0x44, 0x75, 0x1f, 0x50, 0x0e, 0x00, 0x00
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Autogenerated from wled00/data/settings_pin.htm, do not edit!!
 | 
			
		||||
const uint16_t PAGE_settings_pin_length = 471;
 | 
			
		||||
const uint8_t PAGE_settings_pin[] PROGMEM = {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										3463
									
								
								wled00/html_ui.h
								
								
								
								
							
							
						
						
									
										3463
									
								
								wled00/html_ui.h
								
								
								
								
							
										
											
												Plik diff jest za duży
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -20,6 +20,9 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
 | 
			
		|||
    uint16_t len = elem["len"];
 | 
			
		||||
    stop = (len > 0) ? start + len : seg.stop;
 | 
			
		||||
  }
 | 
			
		||||
  // 2D segments
 | 
			
		||||
  uint16_t startY = elem["startY"] | seg.startY;
 | 
			
		||||
  uint16_t stopY = elem["stopY"] | seg.stopY;
 | 
			
		||||
 | 
			
		||||
  //repeat, multiplies segment until all LEDs are used, or max segments reached
 | 
			
		||||
  bool repeat = elem["rpt"] | false;
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +34,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
 | 
			
		|||
    for (byte i=id+1; i<strip.getMaxSegments(); i++) {
 | 
			
		||||
      start = start + len;
 | 
			
		||||
      if (start >= strip.getLengthTotal()) break;
 | 
			
		||||
      //TODO: add support for 2D
 | 
			
		||||
      elem["start"] = start;
 | 
			
		||||
      elem["stop"]  = start + len;
 | 
			
		||||
      elem["rev"]   = !elem["rev"]; // alternate reverse on even/odd segments
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +82,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
 | 
			
		|||
    of = offsetAbs;
 | 
			
		||||
  }
 | 
			
		||||
  if (stop > start && of > len -1) of = len -1;
 | 
			
		||||
  strip.setSegment(id, start, stop, grp, spc, of);
 | 
			
		||||
  strip.setSegment(id, start, stop, grp, spc, of, startY, stopY);
 | 
			
		||||
 | 
			
		||||
  byte segbri = 0;
 | 
			
		||||
  if (getVal(elem["bri"], &segbri)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -146,6 +150,10 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
 | 
			
		|||
  seg.setOption(SEG_OPTION_SELECTED, elem[F("sel")] | seg.getOption(SEG_OPTION_SELECTED));
 | 
			
		||||
  seg.setOption(SEG_OPTION_REVERSED, elem["rev"]    | seg.getOption(SEG_OPTION_REVERSED));
 | 
			
		||||
  seg.setOption(SEG_OPTION_MIRROR  , elem[F("mi")]  | seg.getOption(SEG_OPTION_MIRROR  ));
 | 
			
		||||
  // 2D options
 | 
			
		||||
  seg.setOption(SEG_OPTION_REVERSED_Y, elem[F("rY")] | seg.getOption(SEG_OPTION_REVERSED_Y));
 | 
			
		||||
  seg.setOption(SEG_OPTION_MIRROR_Y  , elem[F("mY")] | seg.getOption(SEG_OPTION_MIRROR_Y  ));
 | 
			
		||||
  seg.setOption(SEG_OPTION_TRANSPOSE , elem[F("tp")] | seg.getOption(SEG_OPTION_TRANSPOSE ));
 | 
			
		||||
 | 
			
		||||
  byte fx = seg.mode;
 | 
			
		||||
  if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value)
 | 
			
		||||
| 
						 | 
				
			
			@ -189,7 +197,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
 | 
			
		|||
          set = 2;
 | 
			
		||||
        }
 | 
			
		||||
      } else { //color
 | 
			
		||||
        int rgbw[] = {0,0,0,0};
 | 
			
		||||
        uint8_t rgbw[] = {0,0,0,0};
 | 
			
		||||
        JsonArray icol = iarr[i];
 | 
			
		||||
        if (!icol.isNull()) { //array, e.g. [255,0,0]
 | 
			
		||||
          byte sz = icol.size();
 | 
			
		||||
| 
						 | 
				
			
			@ -386,6 +394,10 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
 | 
			
		|||
  if (segmentBounds) {
 | 
			
		||||
    root["start"] = seg.start;
 | 
			
		||||
    root["stop"] = seg.stop;
 | 
			
		||||
    if (strip.isMatrix) {
 | 
			
		||||
      root[F("startY")] = seg.startY;
 | 
			
		||||
      root[F("stopY")]  = seg.stopY;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (!forPreset) root["len"] = seg.stop - seg.start;
 | 
			
		||||
  root["grp"] = seg.grouping;
 | 
			
		||||
| 
						 | 
				
			
			@ -427,6 +439,11 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
 | 
			
		|||
  root[F("sel")] = seg.isSelected();
 | 
			
		||||
  root["rev"]    = seg.getOption(SEG_OPTION_REVERSED);
 | 
			
		||||
  root[F("mi")]  = seg.getOption(SEG_OPTION_MIRROR);
 | 
			
		||||
  if (strip.isMatrix) {
 | 
			
		||||
    root[F("rY")] = seg.getOption(SEG_OPTION_REVERSED_Y);
 | 
			
		||||
    root[F("mY")] = seg.getOption(SEG_OPTION_MIRROR_Y);
 | 
			
		||||
    root[F("tp")] = seg.getOption(SEG_OPTION_TRANSPOSE);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segmentBounds)
 | 
			
		||||
| 
						 | 
				
			
			@ -494,7 +511,13 @@ void serializeInfo(JsonObject root)
 | 
			
		|||
  leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0;
 | 
			
		||||
  leds[F("maxseg")] = strip.getMaxSegments();
 | 
			
		||||
  //leds[F("seglock")] = false; //might be used in the future to prevent modifications to segment config
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  if (strip.isMatrix) {
 | 
			
		||||
    JsonObject matrix = leds.createNestedObject("matrix");
 | 
			
		||||
    matrix["w"] = strip.matrixWidth;
 | 
			
		||||
    matrix["h"] = strip.matrixHeight;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t totalLC = 0;
 | 
			
		||||
  JsonArray lcarr = leds.createNestedArray(F("seglc"));
 | 
			
		||||
  uint8_t nSegs = strip.getLastActiveSegmentId();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,8 +15,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
 | 
			
		|||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec 7: DMX 8: usermods
 | 
			
		||||
  if (subPage <1 || subPage >8 || !correctPIN) return;
 | 
			
		||||
  //0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec 7: DMX 8: usermods 9: N/A 10: 2D
 | 
			
		||||
  if (subPage <1 || subPage >10 || !correctPIN) return;
 | 
			
		||||
 | 
			
		||||
  //WIFI SETTINGS
 | 
			
		||||
  if (subPage == 1)
 | 
			
		||||
| 
						 | 
				
			
			@ -547,6 +547,30 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
 | 
			
		|||
    releaseJSONBufferLock();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //2D panels
 | 
			
		||||
  if (subPage == 10)
 | 
			
		||||
  {
 | 
			
		||||
    strip.isMatrix = request->arg(F("SOMP")).toInt();
 | 
			
		||||
    strip.panelH   = MAX(1,MIN(128,request->arg(F("PH")).toInt()));
 | 
			
		||||
    strip.panelW   = MAX(1,MIN(128,request->arg(F("PW")).toInt()));
 | 
			
		||||
    strip.hPanels  = MAX(1,MIN(8,request->arg(F("MPH")).toInt()));
 | 
			
		||||
    strip.vPanels  = MAX(1,MIN(8,request->arg(F("MPV")).toInt()));
 | 
			
		||||
    strip.matrix.bottomStart = request->arg(F("PB")).toInt();
 | 
			
		||||
    strip.matrix.rightStart  = request->arg(F("PR")).toInt();
 | 
			
		||||
    strip.matrix.vertical    = request->arg(F("PV")).toInt();
 | 
			
		||||
    strip.matrix.serpentine  = request->hasArg(F("PS"));
 | 
			
		||||
    for (uint8_t i=0; i<WLED_MAX_PANELS; i++) {
 | 
			
		||||
      char pO[8]; sprintf_P(pO, PSTR("P%d"), i);
 | 
			
		||||
      uint8_t l = strlen(pO); pO[l+1] = 0;
 | 
			
		||||
      pO[l] = 'B'; if (!request->hasArg(pO)) break;
 | 
			
		||||
      pO[l] = 'B'; strip.panel[i].bottomStart = request->arg(pO).toInt();
 | 
			
		||||
      pO[l] = 'R'; strip.panel[i].rightStart  = request->arg(pO).toInt();
 | 
			
		||||
      pO[l] = 'V'; strip.panel[i].vertical    = request->arg(pO).toInt();
 | 
			
		||||
      pO[l] = 'S'; strip.panel[i].serpentine  = request->hasArg(pO);
 | 
			
		||||
    }
 | 
			
		||||
    strip.setUpMatrix(); // will check limits
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  lastEditTime = millis();
 | 
			
		||||
  if (subPage != 2 && !doReboot) serializeConfig(); //do not save if factory reset or LED settings (which are saved after LED re-init)
 | 
			
		||||
  if (subPage == 4) alexaInit();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -476,7 +476,7 @@ void serveSettingsJS(AsyncWebServerRequest* request)
 | 
			
		|||
  char buf[SETTINGS_STACK_BUF_SIZE+37];
 | 
			
		||||
  buf[0] = 0;
 | 
			
		||||
  byte subPage = request->arg(F("p")).toInt();
 | 
			
		||||
  if (subPage > 9) {
 | 
			
		||||
  if (subPage > 10) {
 | 
			
		||||
    strcpy_P(buf, PSTR("alert('Settings for this request are not implemented.');"));
 | 
			
		||||
    request->send(501, "application/javascript", buf);
 | 
			
		||||
    return;
 | 
			
		||||
| 
						 | 
				
			
			@ -510,6 +510,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
 | 
			
		|||
    else if (url.indexOf("sec")  > 0) subPage = 6;
 | 
			
		||||
    else if (url.indexOf("dmx")  > 0) subPage = 7;
 | 
			
		||||
    else if (url.indexOf("um")   > 0) subPage = 8;
 | 
			
		||||
    else if (url.indexOf("2D")   > 0) subPage = 10;
 | 
			
		||||
    else if (url.indexOf("lock") > 0) subPage = 251;
 | 
			
		||||
  }
 | 
			
		||||
  else if (url.indexOf("/update") >= 0) subPage = 9; // update page, for PIN check
 | 
			
		||||
| 
						 | 
				
			
			@ -542,6 +543,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
 | 
			
		|||
      case 6: strcpy_P(s, PSTR("Security")); if (doReboot) strcpy_P(s2, PSTR("Rebooting, please wait ~10 seconds...")); break;
 | 
			
		||||
      case 7: strcpy_P(s, PSTR("DMX")); break;
 | 
			
		||||
      case 8: strcpy_P(s, PSTR("Usermods")); break;
 | 
			
		||||
      case 10: strcpy_P(s, PSTR("2D")); break;
 | 
			
		||||
      case 252: strcpy_P(s, correctPIN ? PSTR("PIN accepted") : PSTR("PIN rejected")); break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -572,6 +574,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
 | 
			
		|||
    case 7:   response = request->beginResponse_P(200, "text/html", PAGE_settings_dmx,  PAGE_settings_dmx_length);  break;
 | 
			
		||||
    case 8:   response = request->beginResponse_P(200, "text/html", PAGE_settings_um,   PAGE_settings_um_length);   break;
 | 
			
		||||
    case 9:   response = request->beginResponse_P(200, "text/html", PAGE_update,        PAGE_update_length);        break;
 | 
			
		||||
    case 10:  response = request->beginResponse_P(200, "text/html", PAGE_settings_2D,   PAGE_settings_2D_length);   break;
 | 
			
		||||
    case 251: {
 | 
			
		||||
      correctPIN = !strlen(settingsPIN); // lock if a pin is set
 | 
			
		||||
      createEditHandler(correctPIN);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -177,7 +177,7 @@ void getSettingsJS(byte subPage, char* dest)
 | 
			
		|||
  obuf = dest;
 | 
			
		||||
  olen = 0;
 | 
			
		||||
 | 
			
		||||
  if (subPage <0 || subPage >9) return;
 | 
			
		||||
  if (subPage <0 || subPage >10) return;
 | 
			
		||||
 | 
			
		||||
  if (subPage == 0)
 | 
			
		||||
  {
 | 
			
		||||
| 
						 | 
				
			
			@ -631,4 +631,33 @@ void getSettingsJS(byte subPage, char* dest)
 | 
			
		|||
    oappendi(VERSION);
 | 
			
		||||
    oappend(SET_F(")\";"));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (subPage == 10) // 2D matrices
 | 
			
		||||
  {
 | 
			
		||||
    sappend('v',SET_F("SOMP"),strip.isMatrix);
 | 
			
		||||
    oappend(SET_F("resetPanels();"));
 | 
			
		||||
    if (strip.isMatrix) {
 | 
			
		||||
      sappend('v',SET_F("PH"),strip.panelH);
 | 
			
		||||
      sappend('v',SET_F("PW"),strip.panelW);
 | 
			
		||||
      sappend('v',SET_F("MPH"),strip.hPanels);
 | 
			
		||||
      sappend('v',SET_F("MPV"),strip.vPanels);
 | 
			
		||||
      sappend('v',SET_F("PB"),strip.matrix.bottomStart);
 | 
			
		||||
      sappend('v',SET_F("PR"),strip.matrix.rightStart);
 | 
			
		||||
      sappend('v',SET_F("PV"),strip.matrix.vertical);
 | 
			
		||||
      sappend('c',SET_F("PS"),strip.matrix.serpentine);
 | 
			
		||||
      // panels
 | 
			
		||||
      for (uint8_t i=0; i<strip.hPanels*strip.vPanels; i++) {
 | 
			
		||||
        char n[5];
 | 
			
		||||
        oappend(SET_F("addPanel("));
 | 
			
		||||
        oappend(itoa(i,n,10));
 | 
			
		||||
        oappend(SET_F(");"));
 | 
			
		||||
        char pO[8]; sprintf_P(pO, PSTR("P%d"), i);
 | 
			
		||||
        uint8_t l = strlen(pO); pO[l+1] = 0;
 | 
			
		||||
        pO[l] = 'B'; sappend('v',pO,strip.panel[i].bottomStart);
 | 
			
		||||
        pO[l] = 'R'; sappend('v',pO,strip.panel[i].rightStart);
 | 
			
		||||
        pO[l] = 'V'; sappend('v',pO,strip.panel[i].vertical);
 | 
			
		||||
        pO[l] = 'S'; sappend('c',pO,strip.panel[i].serpentine);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Ładowanie…
	
		Reference in New Issue