diff --git a/wled00/FX.h b/wled00/FX.h index 9fd3a04d8a..ac25ec4795 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -409,7 +409,8 @@ typedef enum mapping1D2D { M12_pBar = 1, M12_pArc = 2, M12_pCorner = 3, - M12_sPinwheel = 4 + M12_sPinwheel = 4, + M12_Cube = 5 } mapping1D2D_t; class WS2812FX; @@ -546,6 +547,7 @@ class Segment { // transition functions void stopTransition(); // ends transition mode by destroying transition structure (does nothing if not in transition) void updateTransitionProgress() const; // sets transition progress (0-65535) based on time passed since transition start + void applyCubeWrapping(int &x, int &y) const; inline void handleTransition() { updateTransitionProgress(); if (isInTransition() && progress() == 0xFFFFU) stopTransition(); @@ -826,6 +828,7 @@ class WS2812FX { now(millis()), timebase(0), isMatrix(false), + isCube(false), #ifdef WLED_AUTOSEGMENTS autoSegments(true), #else @@ -1007,6 +1010,7 @@ class WS2812FX { bool autoSegments : 1; bool correctWB : 1; bool cctFromRgb : 1; + bool isCube : 1; }; Segment *_currentSegment; diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index a0cc3c4461..ff5f2300bf 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -185,9 +185,47 @@ bool Segment::isPixelXYClipped(int x, int y) const { return false; } +void Segment::applyCubeWrapping(int &x, int &y) const +{ + // AI: below section was generated by an AI + if (!strip.isCube || map1D2D != M12_Cube) return; + const uint16_t vH = vHeight(); + const uint16_t vW = vWidth(); + if (vH % 3 != 0 || vW != (vH / 3) * 4) return; + + int n = vH / 3; + int fx = (x >= 0) ? (x / n) : -1; + int fy = (y >= 0) ? (y / n) : -1; + int u = (x % n + n) % n; + int v = (y % n + n) % n; + + if (fy < 0 || fy > 2) { // Above Top or Below Bottom + x = 4 * n - 1 - u; + y = 2 * n - 1 - v; + } else if (fy == 0) { + if (fx == 0) { // Top-Left gap -> maps to Top face left edge + x = n + (n - 1 - v); y = u; + } else if (fx == 2) { // Top-Right gap -> maps to Top face right edge + x = n + v; y = n - 1 - u; + } else if (fx != 1) { // fx == 3 (Top-Back gap) -> maps to Top face top edge + x = 2 * n - 1 - u; y = n - 1 - v; + } + } else if (fy == 2) { + if (fx == 0) { // Bottom-Left gap -> maps to Bottom face left edge + x = n + v; y = 3 * n - 1 - u; + } else if (fx == 2) { // Bottom-Right gap -> maps to Bottom face right edge + x = 2 * n - 1 - v; y = 2 * n + u; + } else if (fx != 1) { // fx == 3 (Bottom-Back gap) -> maps to Bottom face bottom edge + x = 2 * n - 1 - u; y = 3 * n - 1 - v; + } + } + // AI: end +} + void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) const { if (!isActive()) return; // not active + if (strip.isCube && map1D2D == M12_Cube) applyCubeWrapping(x, y); if ((unsigned)x >= vWidth() || (unsigned)y >= vHeight()) return; // if pixel would fall out of virtual segment just exit setPixelColorXYRaw(x, y, col); } @@ -238,6 +276,7 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa) const // returns RGBW values of pixel uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const { if (!isActive()) return 0; // not active + if (strip.isCube && map1D2D == M12_Cube) applyCubeWrapping(x, y); if ((unsigned)x >= vWidth() || (unsigned)y >= vHeight()) return 0; // if pixel would fall out of virtual segment just exit return getPixelColorXYRaw(x,y); } diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 2e458e7da9..b72ef3dc63 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -186,6 +186,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { JsonObject matrix = hw_led[F("matrix")]; if (!matrix.isNull()) { strip.isMatrix = true; + CJSON(strip.isCube, matrix["isCube"]); unsigned numPanels = matrix[F("mpc")] | 1; numPanels = constrain(numPanels, 1, WLED_MAX_PANELS); strip.panel.clear(); @@ -954,6 +955,7 @@ void serializeConfig(JsonObject root) { // 2D Matrix Settings if (strip.isMatrix) { JsonObject matrix = hw_led.createNestedObject(F("matrix")); + matrix["isCube"] = strip.isCube; matrix[F("mpc")] = strip.panel.size(); JsonArray panels = matrix.createNestedArray(F("panels")); for (size_t i = 0; i < strip.panel.size(); i++) { diff --git a/wled00/data/index.js b/wled00/data/index.js index ee5126973c..cd4e4971df 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -796,6 +796,7 @@ function populateSegments(s) ``+ ``+ ``+ + ``+ ``+ ``; let blend = `
Blend mode
`+ diff --git a/wled00/data/settings_2D.htm b/wled00/data/settings_2D.htm index 76f84fa61b..91044beb2e 100644 --- a/wled00/data/settings_2D.htm +++ b/wled00/data/settings_2D.htm @@ -119,6 +119,29 @@ UI(); // Update the preview after generating panels } + function genCube() { + resetPanels(); + var pw = parseInt(Sf.PW.value); + var ph = parseInt(Sf.PH.value); + let count = Math.min(6, maxPanels); + Sf.MPC.value = count; + for (let i = 0; i < count; i++) addPanel(i); + + if (count > 0) { Sf.P0X.value = pw; Sf.P0Y.value = 0; } + if (count > 1) { Sf.P1X.value = 0; Sf.P1Y.value = ph; } + if (count > 2) { Sf.P2X.value = pw; Sf.P2Y.value = ph; } + if (count > 3) { Sf.P3X.value = 2*pw; Sf.P3Y.value = ph; } + if (count > 4) { Sf.P4X.value = 3*pw; Sf.P4Y.value = ph; } + if (count > 5) { Sf.P5X.value = pw; Sf.P5Y.value = 2*ph; } + + for (let i = 0; i < count; i++) { + Sf["P"+i+"W"].value = pw; + Sf["P"+i+"H"].value = ph; + } + Sf.ISCB.checked = true; + UI(); + } + function expand(o,i) { i.style.display = i.style.display!=="none" ? "none" : ""; @@ -260,7 +283,8 @@

2D setup

+
+ 3D Cube:
diff --git a/wled00/set.cpp b/wled00/set.cpp index fb516ac7d6..a5e59cb6e4 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -843,6 +843,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) if (subPage == SUBPAGE_2D) { strip.isMatrix = request->arg(F("SOMP")).toInt(); + strip.isCube = request->hasArg(F("ISCB")); strip.panel.clear(); if (strip.isMatrix) { unsigned panels = constrain(request->arg(F("MPC")).toInt(), 1, WLED_MAX_PANELS); diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 812ef8c207..08741d207e 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -718,6 +718,7 @@ void getSettingsJS(byte subPage, Print& settingsScript) if (subPage == SUBPAGE_2D) // 2D matrices { printSetFormValue(settingsScript,PSTR("SOMP"),strip.isMatrix); + printSetFormCheckbox(settingsScript,PSTR("ISCB"),strip.isCube); #ifndef WLED_DISABLE_2D settingsScript.printf_P(PSTR("maxPanels=%d;resetPanels();"),WLED_MAX_PANELS); if (strip.isMatrix) {