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..bc31b93c26 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -185,9 +185,77 @@ 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; + + // Horizontal wrapping first to ensure x is always in [0, 4n-1] + x = (x % vW + vW) % vW; + + int fx = x / n; + int fy = (y >= 0) ? (y / n) : -1; + int u = x % 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 + if (u >= v) { // Closer to T -> maps to L top edge + x = v; + y = n + n - 1 - u; + } else { // Closer to L -> maps to T left edge + x = n + (n - 1 - v); + y = u; + } + } else if (fx == 2) { // Top-Right gap + if (u + v < n) { // Closer to T -> maps to R top edge + x = 3 * n - 1 - v; + y = n + u; + } else { // Closer to R -> maps to T right edge + x = n + v; + y = n - 1 - u; + } + } else if (fx == 3) { // Top-Back gap -> maps to T top edge + x = 2 * n - 1 - u; + y = n - 1 - v; + } + } else if (fy == 2) { + if (fx == 0) { // Bottom-Left gap + if (u >= v) { // Closer to D -> maps to L bottom edge + x = v; + y = n + u; + } else { // Closer to L -> maps to D left edge + x = n + v; + y = 3 * n - 1 - u; + } + } else if (fx == 2) { // Bottom-Right gap + if (u + v < n) { // Closer to D -> maps to R bottom edge + x = 3 * n - 1 - v; + y = 2 * n - 1 - u; + } else { // Closer to R -> maps to D right edge + x = 2 * n - 1 - v; + y = 2 * n + u; + } + } else if (fx == 3) { // Bottom-Back gap -> maps to D 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 +306,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 = `