diff options
| author | mrcodetastic <12006953+mrcodetastic@users.noreply.github.com> | 2025-02-23 17:34:12 +0000 |
|---|---|---|
| committer | mrcodetastic <12006953+mrcodetastic@users.noreply.github.com> | 2025-02-23 17:34:12 +0000 |
| commit | 9add343a419f46272f2a440ae2f599c2acfdb661 (patch) | |
| tree | 516a8c6167312e2fb166bf53383dfd00aa719946 /src | |
| parent | 9ae0e0c2004d989a4fdecbea6cbf9320a3905b2d (diff) | |
#759
Added new FOUR_SCAN_40_80PX_HFARCAN option for VirtualMatrixPanel_T
Diffstat (limited to 'src')
| -rw-r--r-- | src/ESP32-HUB75-VirtualMatrixPanel_T.hpp | 705 |
1 files changed, 379 insertions, 326 deletions
diff --git a/src/ESP32-HUB75-VirtualMatrixPanel_T.hpp b/src/ESP32-HUB75-VirtualMatrixPanel_T.hpp index 089bc06..9a7e37d 100644 --- a/src/ESP32-HUB75-VirtualMatrixPanel_T.hpp +++ b/src/ESP32-HUB75-VirtualMatrixPanel_T.hpp @@ -4,27 +4,27 @@ * * This header defines the VirtualMatrixPanel_T template class which maps virtual pixel * coordinates to physical LED coordinates. It supports compile‐time configuration for: - * - Panel chain type (PANEL_CHAIN_TYPE) - * - Scan type mapping (via a class, default is STANDARD_TWO_SCAN) - * - A compile‐time scale factor (each virtual pixel is drawn as a block) + * - Panel chain type (PANEL_CHAIN_TYPE) + * - Scan type mapping (via a class, default is STANDARD_TWO_SCAN) + * - A compile‐time scale factor (each virtual pixel is drawn as a block) * * Runtime rotation is supported via setRotation(). Depending on the build options, * the class conditionally inherits from Adafruit_GFX, GFX_Lite, or stands alone. * * This class is used to accomplish two objectives: - * 1) Create a much larger display out of a number of physical LED panels - * chained in a various pattern. + * 1) Create a much larger display out of a number of physical LED panels + * chained in a various pattern. * - * 2) Provide a way to deal with weird individual physical panels that - * do not have a simple linear X, Y pixel mapping. - * i.e. Their DMA pixel mapping differs to the real world. - * i.e. Weird four-scan outdoor panels etc. + * 2) Provide a way to deal with weird individual physical panels that + * do not have a simple linear X, Y pixel mapping. + * i.e. Their DMA pixel mapping differs to the real world. + * i.e. Weird four-scan outdoor panels etc. * - * @tparam ChainType Compile‐time panel chain configuration. - * @tparam ScanType Policy type implementing a static apply() function for mapping. - * Default is ScanTypeMapping<STANDARD_TWO_SCAN>. - * @tparam ScaleFactor Compile‐time zoom factor (each virtual pixel becomes a - * ScaleFactor x ScaleFactor block). + * @tparam ChainType Compile‐time panel chain configuration. + * @tparam ScanType Policy type implementing a static apply() function for mapping. + * Default is ScanTypeMapping<STANDARD_TWO_SCAN>. + * @tparam ScaleFactor Compile‐time zoom factor (each virtual pixel becomes a + * ScaleFactor x ScaleFactor block). * * @note The enum PANEL_SCAN_TYPE replaces the former PANEL_SCAN_RATE. */ @@ -50,11 +50,11 @@ * @brief Structure holding virtual/physical coordinate mapping. */ struct VirtualCoords { - int16_t x; - int16_t y; - int16_t virt_row; // chain of panels row (optional) - int16_t virt_col; // chain of panels col (optional) - VirtualCoords() : x(0), y(0), virt_row(0), virt_col(0) {} + int16_t x; + int16_t y; + int16_t virt_row; // chain of panels row (optional) + int16_t virt_col; // chain of panels col (optional) + VirtualCoords() : x(0), y(0), virt_row(0), virt_col(0) {} }; /** @@ -63,11 +63,12 @@ struct VirtualCoords { * Defines the different scanning modes. */ enum PANEL_SCAN_TYPE { - STANDARD_TWO_SCAN, - FOUR_SCAN_32PX_HIGH, ///< Four-scan mode, 32-pixel high panels. - FOUR_SCAN_16PX_HIGH, ///< Four-scan mode, 16-pixel high panels. - FOUR_SCAN_64PX_HIGH, ///< Four-scan mode, 64-pixel high panels. - FOUR_SCAN_40PX_HIGH ///< Four-scan mode, 40-pixel high panels. + STANDARD_TWO_SCAN, + FOUR_SCAN_16PX_HIGH, ///< Four-scan mode, 16-pixel high panels. + FOUR_SCAN_32PX_HIGH, ///< Four-scan mode, 32-pixel high panels. + FOUR_SCAN_40PX_HIGH, ///< Four-scan mode, 40-pixel high panels. + FOUR_SCAN_40_80PX_HFARCAN, ///< Four-scan mode, 40-pixel high, 80px wide panel. Weird mapping: https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA/issues/759 + FOUR_SCAN_64PX_HIGH, ///< Four-scan mode, 64-pixel high panels. }; /** @@ -76,15 +77,15 @@ enum PANEL_SCAN_TYPE { * Defines the physical chain configuration for multiple panels. */ enum PANEL_CHAIN_TYPE { - CHAIN_NONE, ///< No chaining. - CHAIN_TOP_LEFT_DOWN, ///< Chain starting top-left, going down. - CHAIN_TOP_RIGHT_DOWN, ///< Chain starting top-right, going down. - CHAIN_BOTTOM_LEFT_UP, ///< Chain starting bottom-left, going up. - CHAIN_BOTTOM_RIGHT_UP, ///< Chain starting bottom-right, going up. - CHAIN_TOP_LEFT_DOWN_ZZ, ///< Zigzag chain starting top-left. - CHAIN_TOP_RIGHT_DOWN_ZZ, ///< Zigzag chain starting top-right. - CHAIN_BOTTOM_RIGHT_UP_ZZ, ///< Zigzag chain starting bottom-right. - CHAIN_BOTTOM_LEFT_UP_ZZ ///< Zigzag chain starting bottom-left. + CHAIN_NONE, ///< No chaining. + CHAIN_TOP_LEFT_DOWN, ///< Chain starting top-left, going down. + CHAIN_TOP_RIGHT_DOWN, ///< Chain starting top-right, going down. + CHAIN_BOTTOM_LEFT_UP, ///< Chain starting bottom-left, going up. + CHAIN_BOTTOM_RIGHT_UP, ///< Chain starting bottom-right, going up. + CHAIN_TOP_LEFT_DOWN_ZZ, ///< Zigzag chain starting top-left. + CHAIN_TOP_RIGHT_DOWN_ZZ, ///< Zigzag chain starting top-right. + CHAIN_BOTTOM_RIGHT_UP_ZZ, ///< Zigzag chain starting bottom-right. + CHAIN_BOTTOM_LEFT_UP_ZZ ///< Zigzag chain starting bottom-left. }; // ---------------------------------------------------------------------- @@ -100,393 +101,445 @@ enum PANEL_CHAIN_TYPE { */ template <PANEL_SCAN_TYPE ScanType> struct ScanTypeMapping { - static constexpr VirtualCoords apply(VirtualCoords coords, int virt_y, int panel_pixel_base) + static constexpr VirtualCoords apply(VirtualCoords coords, int virt_y, int panel_pixel_base) { - log_v("ScanTypeMapping: coords.x: %d, coords.y: %d, virt_y: %d, pixel_base: %d", coords.x, coords.y, virt_y, panel_pixel_base); + log_v("ScanTypeMapping: coords.x: %d, coords.y: %d, virt_y: %d, pixel_base: %d", coords.x, coords.y, virt_y, panel_pixel_base); - // FOUR_SCAN_16PX_HIGH - if constexpr (ScanType == FOUR_SCAN_16PX_HIGH) - { - if ((coords.y & 4) == 0) { - coords.x += (((coords.x / panel_pixel_base) + 1) * panel_pixel_base); + // FOUR_SCAN_16PX_HIGH + if constexpr (ScanType == FOUR_SCAN_16PX_HIGH) + { + if ((coords.y & 4) == 0) { + coords.x += (((coords.x / panel_pixel_base) + 1) * panel_pixel_base); } else { - coords.x += ((coords.x / panel_pixel_base) * panel_pixel_base); + coords.x += ((coords.x / panel_pixel_base) * panel_pixel_base); } - coords.y = (coords.y >> 3) * 4 + (coords.y & 0b00000011); - } - // FOUR_SCAN_40PX_HIGH - else if constexpr (ScanType == FOUR_SCAN_40PX_HIGH) - { - - if (((coords.y) / 10) % 2 == 0) { - coords.x += (((coords.x / panel_pixel_base) + 1) * panel_pixel_base); + coords.y = (coords.y >> 3) * 4 + (coords.y & 0b00000011); + } + // FOUR_SCAN_40PX_HIGH + else if constexpr (ScanType == FOUR_SCAN_40PX_HIGH) + { + + if (((coords.y) / 10) % 2 == 0) { + coords.x += (((coords.x / panel_pixel_base) + 1) * panel_pixel_base); } else { - coords.x += ((coords.x / panel_pixel_base) * panel_pixel_base); + coords.x += ((coords.x / panel_pixel_base) * panel_pixel_base); } - coords.y = (coords.y / 20) * 10 + (coords.y % 10); - } - // FOUR_SCAN_64PX_HIGH || FOUR_SCAN_32PX_HIGH - else if constexpr (ScanType == FOUR_SCAN_64PX_HIGH || ScanType == FOUR_SCAN_32PX_HIGH) + coords.y = (coords.y / 20) * 10 + (coords.y % 10); + } + else if constexpr (ScanType == FOUR_SCAN_40_80PX_HFARCAN) { - int adjusted_y = virt_y; - if constexpr (ScanType == FOUR_SCAN_64PX_HIGH) + + /* + * Weird mapping: https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA/issues/759 + * Panel is annoyingly weird. When sent DMA signal as if it's a + * 160px wide and 20px high. Then the DMA signal pixel (0,0) is + * shown on row 10. + * Then pixel 15 (i.e. 16 in real world), is at the physical of + * 0,0 (i.e. top left pixel), which then goes for 32 pixel. + * Then the next 32 pixel are at row 10 again. And so on and so forth. + */ + + int panel_local_x = coords.x % 80; // compensate for chain of these panels + int logical_dma_y = (coords.y % 10) + 10 * ((coords.y / 20) % 2); // not impacted by chaining + + int logical_dma_x = 0; + int logical_x_option = (coords.y/10)%2; + + // Option 0 - phyiscal lines 0-9, 20-29 + if (logical_x_option == 0) { + + if (panel_local_x < 32) { + logical_dma_x += 16; + } + else if (panel_local_x < 64) { + logical_dma_x += 48; + } + else if (panel_local_x < 80) { + logical_dma_x += 80; + } + + } else { + + // Option 1 + if (panel_local_x < 16) { + } + else if (panel_local_x < 48) { + logical_dma_x += 32; + } + else if (panel_local_x < 80) { + logical_dma_x += 64; + } + } + + // What logical row are we addressing? + logical_dma_x += panel_local_x; + + coords.x = logical_dma_x; + coords.y = logical_dma_y; + + } + // FOUR_SCAN_64PX_HIGH || FOUR_SCAN_32PX_HIGH + else if constexpr (ScanType == FOUR_SCAN_64PX_HIGH || ScanType == FOUR_SCAN_32PX_HIGH) + { + int adjusted_y = virt_y; + if constexpr (ScanType == FOUR_SCAN_64PX_HIGH) { - // As in the original code (with extra remapping for 64px high panels) - if ((virt_y & 8) != ((virt_y & 16) >> 1)) - adjusted_y = (((virt_y & 0b11000) ^ 0b11000) + (virt_y & 0b11100111)); - } + // As in the original code (with extra remapping for 64px high panels) + if ((virt_y & 8) != ((virt_y & 16) >> 1)) + adjusted_y = (((virt_y & 0b11000) ^ 0b11000) + (virt_y & 0b11100111)); + } - if ((coords.y & 8) == 0) { - coords.x += (((coords.x / panel_pixel_base) + 1) * panel_pixel_base); + if ((coords.y & 8) == 0) { + coords.x += (((coords.x / panel_pixel_base) + 1) * panel_pixel_base); } else { - coords.x += ((coords.x / panel_pixel_base) * panel_pixel_base); + coords.x += ((coords.x / panel_pixel_base) * panel_pixel_base); } - coords.y = (adjusted_y >> 4) * 8 + (adjusted_y & 0b00000111); - } + coords.y = (adjusted_y >> 4) * 8 + (adjusted_y & 0b00000111); + } - // For STANDARD_TWO_SCAN / NORMAL_ONE_SIXTEEN no remapping is done. - return coords; - } + // For STANDARD_TWO_SCAN / NORMAL_ONE_SIXTEEN no remapping is done. + return coords; + } }; // ---------------------------------------------------------------------- // VirtualMatrixPanel_T Declaration // // Template parameters: -// - ChainScanType: compile–time panel chain configuration. -// - ScanTypeMapping: a policy type implementing a static "apply" function -// (default is ScanTypeMapping<STANDARD_TWO_SCAN>). -// - ScaleFactor: a compile–time zoom factor (must be >= 1). +// - ChainScanType: compile–time panel chain configuration. +// - ScanTypeMapping: a policy type implementing a static "apply" function +// (default is ScanTypeMapping<STANDARD_TWO_SCAN>). +// - ScaleFactor: a compile–time zoom factor (must be >= 1). #ifdef USE_GFX_LITE template <PANEL_CHAIN_TYPE ChainScanType, - class ScanTypeMapping = ScanTypeMapping<STANDARD_TWO_SCAN>, - int ScaleFactor = 1> + class ScanTypeMapping = ScanTypeMapping<STANDARD_TWO_SCAN>, + int ScaleFactor = 1> class VirtualMatrixPanel_T : public GFX { public: #elif !defined(NO_GFX) template <PANEL_CHAIN_TYPE ChainScanType, - class ScanTypeMapping = ScanTypeMapping<STANDARD_TWO_SCAN>, - int ScaleFactor = 1> + class ScanTypeMapping = ScanTypeMapping<STANDARD_TWO_SCAN>, + int ScaleFactor = 1> class VirtualMatrixPanel_T : public Adafruit_GFX { public: #else template <PANEL_CHAIN_TYPE ChainScanType, - class ScanTypeMapping = ScanTypeMapping<STANDARD_TWO_SCAN>, - int ScaleFactor = 1> + class ScanTypeMapping = ScanTypeMapping<STANDARD_TWO_SCAN>, + int ScaleFactor = 1> class VirtualMatrixPanel_T { public: #endif - // Constructor: pass the underlying MatrixPanel_I2S_DMA display, - // virtual module dimensions, and physical panel resolution. - // (Chain type is chosen at compile time.) - VirtualMatrixPanel_T(uint8_t _vmodule_rows, - uint8_t _vmodule_cols, - uint8_t _panel_res_x, - uint8_t _panel_res_y) + // Constructor: pass the underlying MatrixPanel_I2S_DMA display, + // virtual module dimensions, and physical panel resolution. + // (Chain type is chosen at compile time.) + VirtualMatrixPanel_T(uint8_t _vmodule_rows, + uint8_t _vmodule_cols, + uint8_t _panel_res_x, + uint8_t _panel_res_y) #ifdef USE_GFX_LITE - : GFX(_vmodule_cols * _panel_res_x, _vmodule_rows * _panel_res_y), + : GFX(_vmodule_cols * _panel_res_x, _vmodule_rows * _panel_res_y), #elif !defined(NO_GFX) - : Adafruit_GFX(_vmodule_cols * _panel_res_x, _vmodule_rows * _panel_res_y), + : Adafruit_GFX(_vmodule_cols * _panel_res_x, _vmodule_rows * _panel_res_y), #endif - panel_res_x(_panel_res_x), - panel_res_y(_panel_res_y), - panel_pixel_base(_panel_res_x), // default pixel base is panel_res_x - vmodule_rows(_vmodule_rows), - vmodule_cols(_vmodule_cols), - virtual_res_x(_vmodule_cols * _panel_res_x), - virtual_res_y(_vmodule_rows * _panel_res_y), - dma_res_x(_panel_res_x * _vmodule_rows * _vmodule_cols - 1), - _virtual_res_x(virtual_res_x), - _virtual_res_y(virtual_res_y), - _rotate(0) - { - // Initialize with an invalid coordinate. - coords.x = coords.y = -1; - } + panel_res_x(_panel_res_x), + panel_res_y(_panel_res_y), + panel_pixel_base(_panel_res_x), // default pixel base is panel_res_x + vmodule_rows(_vmodule_rows), + vmodule_cols(_vmodule_cols), + virtual_res_x(_vmodule_cols * _panel_res_x), + virtual_res_y(_vmodule_rows * _panel_res_y), + dma_res_x(_panel_res_x * _vmodule_rows * _vmodule_cols - 1), + _virtual_res_x(virtual_res_x), + _virtual_res_y(virtual_res_y), + _rotate(0) + { + // Initialize with an invalid coordinate. + coords.x = coords.y = -1; + } - // ------------------------------------------------------------------ - // Drawing methods - inline void drawPixel(int16_t x, int16_t y, uint16_t color) { - if constexpr (ScaleFactor > 1) + // ------------------------------------------------------------------ + // Drawing methods + inline void drawPixel(int16_t x, int16_t y, uint16_t color) { + if constexpr (ScaleFactor > 1) { - for (int dx = 0; dx < ScaleFactor; dx++) { - for (int dy = 0; dy < ScaleFactor; dy++) { - //irtualCoords v = getCoords(x * ScaleFactor + dx, y * ScaleFactor + dy); + for (int dx = 0; dx < ScaleFactor; dx++) { + for (int dy = 0; dy < ScaleFactor; dy++) { + //irtualCoords v = getCoords(x * ScaleFactor + dx, y * ScaleFactor + dy); // display->drawPixel(v.x, v.y, color); - calcCoords(x * ScaleFactor + dx, y * ScaleFactor + dy); + calcPhysicalToElectricalCoords(x * ScaleFactor + dx, y * ScaleFactor + dy); display->drawPixel(coords.x, coords.y, color); - } - } - } else { - //VirtualCoords v = getCoords(x, y); - //display->drawPixel(v.x, v.y, color); + } + } + } else { + //VirtualCoords v = getCoords(x, y); + //display->drawPixel(v.x, v.y, color); - calcCoords(x , y); + calcPhysicalToElectricalCoords(x , y); display->drawPixel(coords.x, coords.y, color); - } + } - log_v("x: %d, y: %d -> coords.x: %d, coords.y: %d", x, y, coords.x, coords.y); - } + log_v("x: %d, y: %d -> coords.x: %d, coords.y: %d", x, y, coords.x, coords.y); + } - inline void fillScreen(uint16_t color) { - display->fillScreen(color); - } + inline void fillScreen(uint16_t color) { + display->fillScreen(color); + } - inline void fillScreenRGB888(uint8_t r, uint8_t g, uint8_t b) { - display->fillScreenRGB888(r, g, b); - } + inline void fillScreenRGB888(uint8_t r, uint8_t g, uint8_t b) { + display->fillScreenRGB888(r, g, b); + } - inline void drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b) { - //VirtualCoords v = getCoords(x, y); - //display->drawPixelRGB888(v.x, v.y, r, g, b); + inline void drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b) { + //VirtualCoords v = getCoords(x, y); + //display->drawPixelRGB888(v.x, v.y, r, g, b); - calcCoords(x , y); + calcPhysicalToElectricalCoords(x , y); display->drawPixelRGB888(coords.x, coords.y, r, g, b); - } + } #ifdef USE_GFX_LITE - inline void drawPixel(int16_t x, int16_t y, CRGB color) { - //VirtualCoords v = getCoords(x, y); - //display->drawPixel(v.x, v.y, color); + inline void drawPixel(int16_t x, int16_t y, CRGB color) { + //VirtualCoords v = getCoords(x, y); + //display->drawPixel(v.x, v.y, color); - calcCoords(x , y); + calcPhysicalToElectricalCoords(x , y); display->drawPixel(coords.x, coords.y, color); - } + } - inline void fillScreen(CRGB color) { - display->fillScreen(color); - } + inline void fillScreen(CRGB color) { + display->fillScreen(color); + } #endif #ifndef NO_GFX - inline void drawDisplayTest() { + inline void drawDisplayTest() { - // Call ourself as we need to re-map pixels if we're using our own ScanTypeMapping - // Note: Will mean this display test will be impacted by chaining approach etc. - // this->setFont(&FreeSansBold12pt7b); - this->setTextColor(display->color565(255, 255, 0)); - // this->setTextSize(1); - for (int col = 0; col < vmodule_cols; col++) { - for (int row = 0; row < vmodule_rows; row++) { + // Call ourself as we need to re-map pixels if we're using our own ScanTypeMapping + // Note: Will mean this display test will be impacted by chaining approach etc. + // this->setFont(&FreeSansBold12pt7b); + this->setTextColor(display->color565(255, 255, 0)); + // this->setTextSize(1); + for (int col = 0; col < vmodule_cols; col++) { + for (int row = 0; row < vmodule_rows; row++) { - int start_x = col * panel_res_x; - int start_y = row * panel_res_y; + int start_x = col * panel_res_x; + int start_y = row * panel_res_y; - int panel_id = col + (row * vmodule_cols) + 1; - //int top_left_x = panel * panel_res_x; - this->drawRect(start_x, start_y, panel_res_x, panel_res_y, this->color565(0, 255, 0)); - this->setCursor(start_x + panel_res_x/2 - 2, start_y + panel_res_y/2 - 4); - this->print(panel_id); + int panel_id = col + (row * vmodule_cols) + 1; + //int top_left_x = panel * panel_res_x; + this->drawRect(start_x, start_y, panel_res_x, panel_res_y, this->color565(0, 255, 0)); + this->setCursor(start_x + panel_res_x/2 - 2, start_y + panel_res_y/2 - 4); + this->print(panel_id); - log_d("drawDisplayTest() Panel: %d, start_x: %d, start_y: %d", panel_id, start_x, start_y); - } - } - - } + log_d("drawDisplayTest() Panel: %d, start_x: %d, start_y: %d", panel_id, start_x, start_y); + } + } + + } - inline void drawDisplayTestDMA() - { - // Write to the underlying panels only via the dma_display instance. - // This only works on standard panels with a linear mapping (i.e. two-scan). - this->display->setTextColor(this->display->color565(255, 255, 0)); - this->display->setTextSize(1); - - for (int panel = 0; panel < vmodule_cols * vmodule_rows; panel++) - { - int top_left_x = panel * panel_res_x; - this->display->drawRect(top_left_x, 0, panel_res_x, panel_res_y, this->display->color565(0, 255, 0)); - this->display->setCursor((panel * panel_res_x) + 6, panel_res_y - 12); - this->display->print((vmodule_cols * vmodule_rows) - panel); - } - } + inline void drawDisplayTestDMA() + { + // Write to the underlying panels only via the dma_display instance. + // This only works on standard panels with a linear mapping (i.e. two-scan). + this->display->setTextColor(this->display->color565(255, 255, 0)); + this->display->setTextSize(1); + + for (int panel = 0; panel < vmodule_cols * vmodule_rows; panel++) + { + int top_left_x = panel * panel_res_x; + this->display->drawRect(top_left_x, 0, panel_res_x, panel_res_y, this->display->color565(0, 255, 0)); + this->display->setCursor((panel * panel_res_x) + 6, panel_res_y - 12); + this->display->print((vmodule_cols * vmodule_rows) - panel); + } + } #endif - inline void clearScreen() { display->clearScreen(); } + inline void clearScreen() { display->clearScreen(); } - inline uint16_t color444(uint8_t r, uint8_t g, uint8_t b) { return display->color444(r, g, b); } - inline uint16_t color565(uint8_t r, uint8_t g, uint8_t b) { return display->color565(r, g, b); } + inline uint16_t color444(uint8_t r, uint8_t g, uint8_t b) { return display->color444(r, g, b); } + inline uint16_t color565(uint8_t r, uint8_t g, uint8_t b) { return display->color565(r, g, b); } - inline void flipDMABuffer() { display->flipDMABuffer(); } + inline void flipDMABuffer() { display->flipDMABuffer(); } - // ------------------------------------------------------------------ - // Rotation (runtime) - inline void setRotation(uint8_t rotate) { - if (rotate < 4) - _rotate = rotate; + // ------------------------------------------------------------------ + // Rotation (runtime) + inline void setRotation(uint8_t rotate) { + if (rotate < 4) + _rotate = rotate; #ifdef NO_GFX - // When NO_GFX is defined, update _virtual_res_x/_virtual_res_y as needed. + // When NO_GFX is defined, update _virtual_res_x/_virtual_res_y as needed. #else - uint8_t rotation = (rotate & 3); - switch (rotation) { - case 0: - case 2: - _virtual_res_x = virtual_res_x; - _virtual_res_y = virtual_res_y; - _width = virtual_res_x; - _height = virtual_res_y; - break; - case 1: - case 3: - _virtual_res_x = virtual_res_y; - _virtual_res_y = virtual_res_x; - _width = virtual_res_y; - _height = virtual_res_x; - break; - } + uint8_t rotation = (rotate & 3); + switch (rotation) { + case 0: + case 2: + _virtual_res_x = virtual_res_x; + _virtual_res_y = virtual_res_y; + _width = virtual_res_x; + _height = virtual_res_y; + break; + case 1: + case 3: + _virtual_res_x = virtual_res_y; + _virtual_res_y = virtual_res_x; + _width = virtual_res_y; + _height = virtual_res_x; + break; + } #endif - } + } - // ------------------------------------------------------------------ - // Panel scan–type configuration (runtime adjustment of pixel base) - inline void setPixelBase(uint8_t pixel_base) { - panel_pixel_base = pixel_base; - } + // ------------------------------------------------------------------ + // Panel scan–type configuration (runtime adjustment of pixel base) + inline void setPixelBase(uint8_t pixel_base) { + panel_pixel_base = pixel_base; + } - // ------------------------------------------------------------------ - // calcCoords() maps a virtual (x,y) coordinate to a physical coordinate. - //VirtualCoords getCoords(int16_t virt_x, int16_t virt_y) { - void calcCoords(int16_t virt_x, int16_t virt_y) { + // ------------------------------------------------------------------ + // calcPhysicalToElectricalCoords() maps a virtual (x,y) coordinate to a physical coordinate. + // VirtualCoords getCoords(int16_t virt_x, int16_t virt_y) { + void calcPhysicalToElectricalCoords(int16_t virt_x, int16_t virt_y) { #ifdef NO_GFX - if (virt_x < 0 || virt_x >= _virtual_res_x || virt_y < 0 || virt_y >= _virtual_res_y) { + if (virt_x < 0 || virt_x >= _virtual_res_x || virt_y < 0 || virt_y >= _virtual_res_y) { #else - if (virt_x < 0 || virt_x >= _width || virt_y < 0 || virt_y >= _height) { + if (virt_x < 0 || virt_x >= _width || virt_y < 0 || virt_y >= _height) { #endif - coords.x = coords.y = -1; + coords.x = coords.y = -1; return; - //return coords; - } + //return coords; + } - //log_d("calcCoords pre-chain: virt_x: %d, virt_y: %d", virt_x, virt_y); + //log_d("calcCoords pre-chain: virt_x: %d, virt_y: %d", virt_x, virt_y); - // --- Runtime rotation --- - switch (_rotate) { - case 1: { - int16_t temp = virt_x; - virt_x = virt_y; - virt_y = virtual_res_y - 1 - temp; - break; - } - case 2: { - virt_x = virtual_res_x - 1 - virt_x; - virt_y = virtual_res_y - 1 - virt_y; - break; - } - case 3: { - int16_t temp = virt_x; - virt_x = virtual_res_x - 1 - virt_y; - virt_y = temp; - break; - } - default: - break; - } + // --- Runtime rotation --- + switch (_rotate) { + case 1: { + int16_t temp = virt_x; + virt_x = virt_y; + virt_y = virtual_res_y - 1 - temp; + break; + } + case 2: { + virt_x = virtual_res_x - 1 - virt_x; + virt_y = virtual_res_y - 1 - virt_y; + break; + } + case 3: { + int16_t temp = virt_x; + virt_x = virtual_res_x - 1 - virt_y; + virt_y = temp; + break; + } + default: + break; + } - // --- Chain mapping --- - int row = virt_y / panel_res_y; // 0-indexed row in the virtual module - if constexpr (ChainScanType == CHAIN_TOP_RIGHT_DOWN) { - if ((row & 1) == 1) { - coords.x = dma_res_x - virt_x - (row * virtual_res_x); - coords.y = panel_res_y - 1 - (virt_y % panel_res_y); - } else { - coords.x = ((vmodule_rows - (row + 1)) * virtual_res_x) + virt_x; - coords.y = (virt_y % panel_res_y); - } - } - else if constexpr (ChainScanType == CHAIN_TOP_RIGHT_DOWN_ZZ) { - coords.x = ((vmodule_rows - (row + 1)) * virtual_res_x) + virt_x; - coords.y = (virt_y % panel_res_y); - } - else if constexpr (ChainScanType == CHAIN_TOP_LEFT_DOWN) { - if ((row & 1) == 0) { - coords.x = dma_res_x - virt_x - (row * virtual_res_x); - coords.y = panel_res_y - 1 - (virt_y % panel_res_y); - } else { - coords.x = ((vmodule_rows - (row + 1)) * virtual_res_x) + virt_x; - coords.y = (virt_y % panel_res_y); - } - } - else if constexpr (ChainScanType == CHAIN_TOP_LEFT_DOWN_ZZ) { - coords.x = ((vmodule_rows - (row + 1)) * virtual_res_x) + virt_x; - coords.y = (virt_y % panel_res_y); - } - else if constexpr (ChainScanType == CHAIN_BOTTOM_LEFT_UP) { - row = vmodule_rows - row - 1; - if ((row & 1) == 1) { - coords.x = ((vmodule_rows - (row + 1)) * virtual_res_x) + virt_x; - coords.y = (virt_y % panel_res_y); - } else { - coords.x = dma_res_x - (row * virtual_res_x) - virt_x; - coords.y = panel_res_y - 1 - (virt_y % panel_res_y); - } - } - else if constexpr (ChainScanType == CHAIN_BOTTOM_LEFT_UP_ZZ) { - row = vmodule_rows - row - 1; - coords.x = ((vmodule_rows - (row + 1)) * virtual_res_x) + virt_x; - coords.y = (virt_y % panel_res_y); - } - else if constexpr (ChainScanType == CHAIN_BOTTOM_RIGHT_UP) { - row = vmodule_rows - row - 1; - if ((row & 1) == 0) { - coords.x = ((vmodule_rows - (row + 1)) * virtual_res_x) + virt_x; - coords.y = (virt_y % panel_res_y); - } else { - coords.x = dma_res_x - (row * virtual_res_x) - virt_x; - coords.y = panel_res_y - 1 - (virt_y % panel_res_y); - } - } - else if constexpr (ChainScanType == CHAIN_BOTTOM_RIGHT_UP_ZZ) { - row = vmodule_rows - row - 1; - coords.x = ((vmodule_rows - (row + 1)) * virtual_res_x) + virt_x; - coords.y = (virt_y % panel_res_y); - } - else { // CHAIN_NONE (default) - coords.x = virt_x; - coords.y = virt_y; - } + // --- Chain mapping --- + int row = virt_y / panel_res_y; // 0-indexed row in the virtual module + if constexpr (ChainScanType == CHAIN_TOP_RIGHT_DOWN) { + if ((row & 1) == 1) { + coords.x = dma_res_x - virt_x - (row * virtual_res_x); + coords.y = panel_res_y - 1 - (virt_y % panel_res_y); + } else { + coords.x = ((vmodule_rows - (row + 1)) * virtual_res_x) + virt_x; + coords.y = (virt_y % panel_res_y); + } + } + else if constexpr (ChainScanType == CHAIN_TOP_RIGHT_DOWN_ZZ) { + coords.x = ((vmodule_rows - (row + 1)) * virtual_res_x) + virt_x; + coords.y = (virt_y % panel_res_y); + } + else if constexpr (ChainScanType == CHAIN_TOP_LEFT_DOWN) { + if ((row & 1) == 0) { + coords.x = dma_res_x - virt_x - (row * virtual_res_x); + coords.y = panel_res_y - 1 - (virt_y % panel_res_y); + } else { + coords.x = ((vmodule_rows - (row + 1)) * virtual_res_x) + virt_x; + coords.y = (virt_y % panel_res_y); + } + } + else if constexpr (ChainScanType == CHAIN_TOP_LEFT_DOWN_ZZ) { + coords.x = ((vmodule_rows - (row + 1)) * virtual_res_x) + virt_x; + coords.y = (virt_y % panel_res_y); + } + else if constexpr (ChainScanType == CHAIN_BOTTOM_LEFT_UP) { + row = vmodule_rows - row - 1; + if ((row & 1) == 1) { + coords.x = ((vmodule_rows - (row + 1)) * virtual_res_x) + virt_x; + coords.y = (virt_y % panel_res_y); + } else { + coords.x = dma_res_x - (row * virtual_res_x) - virt_x; + coords.y = panel_res_y - 1 - (virt_y % panel_res_y); + } + } + else if constexpr (ChainScanType == CHAIN_BOTTOM_LEFT_UP_ZZ) { + row = vmodule_rows - row - 1; + coords.x = ((vmodule_rows - (row + 1)) * virtual_res_x) + virt_x; + coords.y = (virt_y % panel_res_y); + } + else if constexpr (ChainScanType == CHAIN_BOTTOM_RIGHT_UP) { + row = vmodule_rows - row - 1; + if ((row & 1) == 0) { + coords.x = ((vmodule_rows - (row + 1)) * virtual_res_x) + virt_x; + coords.y = (virt_y % panel_res_y); + } else { + coords.x = dma_res_x - (row * virtual_res_x) - virt_x; + coords.y = panel_res_y - 1 - (virt_y % panel_res_y); + } + } + else if constexpr (ChainScanType == CHAIN_BOTTOM_RIGHT_UP_ZZ) { + row = vmodule_rows - row - 1; + coords.x = ((vmodule_rows - (row + 1)) * virtual_res_x) + virt_x; + coords.y = (virt_y % panel_res_y); + } + else { // CHAIN_NONE (default) + coords.x = virt_x; + coords.y = virt_y; + } - //log_d("calcCoords post-chain: virt_x: %d, virt_y: %d", virt_x, virt_y); + //log_d("calcCoords post-chain: virt_x: %d, virt_y: %d", virt_x, virt_y); - // --- Apply physical LED panel scan–type mapping / fix --- - coords = ScanTypeMapping::apply(coords, virt_y, panel_pixel_base); + // --- Apply physical LED panel scan–type mapping / fix --- + coords = ScanTypeMapping::apply(coords, virt_y, panel_pixel_base); - } + } #ifdef NO_GFX - inline uint16_t width() const { return _virtual_res_x; } - inline uint16_t height() const { return _virtual_res_y; } + inline uint16_t width() const { return _virtual_res_x; } + inline uint16_t height() const { return _virtual_res_y; } #endif - // ------------------------------------------------------------------ - // Data members (public for compatibility) - VirtualCoords coords; + // ------------------------------------------------------------------ + // Data members (public for compatibility) + VirtualCoords coords; - uint8_t panel_res_x; // physical panel resolution X - uint8_t panel_res_y; // physical panel resolution Y - uint8_t panel_pixel_base; // used for scan–type mapping + uint8_t panel_res_x; // physical panel resolution X + uint8_t panel_res_y; // physical panel resolution Y + uint8_t panel_pixel_base; // used for scan–type mapping - inline void setDisplay(MatrixPanel_I2S_DMA &disp) { - display = &disp; - } + inline void setDisplay(MatrixPanel_I2S_DMA &disp) { + display = &disp; + } private: - MatrixPanel_I2S_DMA *display; - // Note: panel_chain_type is now fixed via the compile–time template parameter 'ChainScanType'. - uint16_t virtual_res_x; // virtual display width (combination of panels) - uint16_t virtual_res_y; // virtual display height (combination of panels) - uint16_t _virtual_res_x; // width adjusted by current rotation - uint16_t _virtual_res_y; // height adjusted by current rotation - uint8_t vmodule_rows; // virtual module rows - uint8_t vmodule_cols; // virtual module columns - uint16_t dma_res_x; // width as seen by the DMA engine + MatrixPanel_I2S_DMA *display; + // Note: panel_chain_type is now fixed via the compile–time template parameter 'ChainScanType'. + uint16_t virtual_res_x; // virtual display width (combination of panels) + uint16_t virtual_res_y; // virtual display height (combination of panels) + uint16_t _virtual_res_x; // width adjusted by current rotation + uint16_t _virtual_res_y; // height adjusted by current rotation + uint8_t vmodule_rows; // virtual module rows + uint8_t vmodule_cols; // virtual module columns + uint16_t dma_res_x; // width as seen by the DMA engine - int _rotate; // runtime rotation (0 to 3) + int _rotate; // runtime rotation (0 to 3) }; -#endif // VIRTUAL_MATRIX_PANEL_TEMPLATE_H +#endif // VIRTUAL_MATRIX_PANEL_TEMPLATE_H |
