From aed04adfcda1838bf85c629a8c3b560919b3a327 Mon Sep 17 00:00:00 2001
From: mrcodetastic <12006953+mrcodetastic@users.noreply.github.com>
Date: Wed, 19 Mar 2025 22:20:23 +0000
Subject: [PATCH] Enhance VirtualMatrixPanel_T example

---
 .../VirtualMatrixPanel/VirtualMatrixPanel.ino | 197 ++++++++++++------
 src/ESP32-HUB75-VirtualMatrixPanel_T.hpp      |   4 +-
 2 files changed, 141 insertions(+), 60 deletions(-)

diff --git a/examples/VirtualMatrixPanel/VirtualMatrixPanel.ino b/examples/VirtualMatrixPanel/VirtualMatrixPanel.ino
index 787df1f..6633b28 100644
--- a/examples/VirtualMatrixPanel/VirtualMatrixPanel.ino
+++ b/examples/VirtualMatrixPanel/VirtualMatrixPanel.ino
@@ -12,7 +12,7 @@
  * 
  *   1) and 2) can be combined and utilsied together.
  * 
- *   There are THREE examples contained within this library. What example gets built depends
+ *   There are FOUR examples contained within this library. What example gets built depends
  *   on the value of the "#define EXAMPLE_NUMBER X" value. Where X = Example number.
  *   
  *   Example 1:  STANDARD 1/2 Scan (i.e. 1/16, 1/32) LED matrix panels, 64x32 pixels each,
@@ -22,6 +22,9 @@
  *               in a grid of 2x2 panels, chained in a Serpentine manner. 
  * 
  *   Example 3:  A single non-standard 1/4 Scan (i.e. Four-Scan 1/8) outdoor LED matrix panel, 64x32 pixels.
+ *
+ *   Example 4:  Having your own panel pixel mapping logic of use only to a specific panel that isn't supported.
+ *               In this case we re-use this to map an individual pixel in a weird way.
  */
 
  #include <Arduino.h>
@@ -31,6 +34,7 @@
  #define EXAMPLE_NUMBER 1
  //#define EXAMPLE_NUMBER 2
  //#define EXAMPLE_NUMBER 3
+ //#define EXAMPLE_NUMBER 4 // Custom Special Effects example!
  
  /**
   * Configuration of the LED matrix panels number and individual pixel resolution.
@@ -64,80 +68,124 @@
   * Mandatory declaration of the dma_display. DO NOT CHANGE
   **/
  MatrixPanel_I2S_DMA *dma_display = nullptr;
+
+
+ // ------------------------------------------------------------------------------------------------------------
  
  
  /**
   * Template instantiation for the VirtualMatrixPanel_T class, depending on use-case.
   **/
  #if EXAMPLE_NUMBER == 1
- // --- Example 1: STANDARD 1/2 Scan ---
- 
- // Declare a pointer to the specific instantiation:
- VirtualMatrixPanel_T<PANEL_CHAIN_TYPE>* virtualDisp = nullptr;
- 
+
+  // --- Example 1: STANDARD 1/2 Scan ---
+  
+  // Declare a pointer to the specific instantiation:
+  VirtualMatrixPanel_T<PANEL_CHAIN_TYPE>* virtualDisp = nullptr;
+  
  #endif
  
- 
+  
  #if EXAMPLE_NUMBER == 2 
- // --- Example 2: Non-Standard 1/4 Scan (Four-Scan 1/8) ---
- 
- // Use an existing library user-contributed Scan Type pixel mapping
- using MyScanTypeMapping = ScanTypeMapping<PANEL_SCAN_TYPE>;
- 
- // Create a pointer to the specific instantiation of the VirtualMatrixPanel_T class
- VirtualMatrixPanel_T<PANEL_CHAIN_TYPE, MyScanTypeMapping>* virtualDisp = nullptr;
+
+  // --- Example 2: Non-Standard 1/4 Scan (Four-Scan 1/8) ---
  
+  // Use an existing library user-contributed Scan Type pixel mapping
+  using MyScanTypeMapping = ScanTypeMapping<PANEL_SCAN_TYPE>;
+  
+  // Create a pointer to the specific instantiation of the VirtualMatrixPanel_T class
+  VirtualMatrixPanel_T<PANEL_CHAIN_TYPE, MyScanTypeMapping>* virtualDisp = nullptr;
+  
  #endif
  
  #if EXAMPLE_NUMBER == 3
+
  // --- Example 3: Single non-standard 1/4 Scan (Four-Scan 1/8) ---
  
- // Use an existing library user-contributed Scan Type pixel mapping
- using MyScanTypeMapping = ScanTypeMapping<PANEL_SCAN_TYPE>;
- 
- // Create a pointer to the specific instantiation of the VirtualMatrixPanel_T class
- VirtualMatrixPanel_T<CHAIN_NONE, MyScanTypeMapping>* virtualDisp = nullptr;
- 
+  // Use an existing library user-contributed Scan Type pixel mapping
+  using MyScanTypeMapping = ScanTypeMapping<PANEL_SCAN_TYPE>;
+  
+  // Create a pointer to the specific instantiation of the VirtualMatrixPanel_T class
+  VirtualMatrixPanel_T<CHAIN_NONE, MyScanTypeMapping>* virtualDisp = nullptr;
+  
  #endif
  
  
- 
  // Bonus non-existnat example. Create your own per-panel custom pixel mapping!
  #if EXAMPLE_NUMBER == 4
- 
- // --- Custom Scan–Type Pixel Mapping ---
- // This policy adds a fixed offset to the coordinates.
- struct CustomScanTypeMapping {
-   static constexpr VirtualCoords apply(VirtualCoords coords, int virt_y, int panel_pixel_base) {
-       // For demonstration, add a fixed offset of +5 to x and +3 to y.
-       coords.x += 5;
-       coords.y += 3;
-       return coords;
-   }
- };
+  
+  // --- Custom Scan–Type Pixel Mapping ---
+  // This is not what you would use this for, but in any case it 
+  // makes a flipped mirror image
+  struct CustomMirrorScanTypeMapping {
+
+    static VirtualCoords apply(VirtualCoords coords, int vy, int pb) {
+
+      // coords are the input coords for adjusting
+
+      int width  = PANEL_RES_X;
+      int height = PANEL_RES_Y;	
+
+      // Flip / Mirror x 
+      coords.x = PANEL_RES_X - coords.x - 1;   
+    //  coords.y = PANEL_RES_Y - coords.y - 1;   
+      
+      return coords;
+
+    }
+
+  };
+
+    // Create a pointer to the specific instantiation of the VirtualMatrixPanel_T class
+  VirtualMatrixPanel_T<CHAIN_NONE, CustomMirrorScanTypeMapping>* virtualDisp = nullptr;
+  
  #endif 
  
+// ------------------------------------------------------------------------------------------------------------
+
  void setup() 
  {
    Serial.begin(115200);
    delay(2000);
-     
-   #if EXAMPLE_NUMBER == 3
-     /**
-      * HACK ALERT!
-      * For 1/4 scan panels (namely outdoor panels), electrically the pixels are connected in a chain that is
-      * twice the physical panel's pixel width, and half the pixel height. As such, we need to configure
-      * the underlying DMA library to match the same. Then we use the VirtualMatrixPanel_T class to map the
-      * physical pixels to the virtual pixels.
-     */
-   HUB75_I2S_CFG mxconfig(
-     PANEL_RES_X*2,              // DO NOT CHANGE THIS
-     PANEL_RES_Y/2,              // DO NOT CHANGE THIS
-     1 // A Single panel
-   );
- 
-   #elif EXAMPLE_NUMBER == 2 
+
+/*
+
+    #define RL1 18
+    #define GL1 17
+    #define BL1 16
+    #define RL2 15
+    #define GL2 7
+    #define BL2 6
+    #define CH_A 4
+    #define CH_B 10
+    #define CH_C 14
+    #define CH_D 21
+    #define CH_E 5 // assign to any available pin if using two panels or 64x64 panels with 1/32 scan
+    #define CLK 47
+    #define LAT 48
+    #define OE  38
+
+  // HUB75_I2S_CFG::i2s_pins _pins={RL1, GL1, BL1, RL2, GL2, BL2, CH_A, CH_B, CH_C, CH_D, CH_E, LAT, OE, CLK};
+
+*/  
+
+   #if EXAMPLE_NUMBER == 1
+     // A grid of normal (i.e. supported out of the box) 1/16, 1/32 (two scan) panels
+
+     // Standard panel type natively supported by this library (Example 1, 4)
+     HUB75_I2S_CFG mxconfig(
+       PANEL_RES_X,   
+       PANEL_RES_Y,  
+       PANEL_CHAIN_LEN
+       //, _pins
+     );
+
+   #endif
  
+   #if EXAMPLE_NUMBER == 2 
+   // A grid of 1/4 scan panels. This panel type is not supported 'out of the box' and require specific  
+   // ScanTypeMapping (pixel mapping) within the panel itself.
+
      /**
       * HACK ALERT!
       * For 1/4 scan panels (namely outdoor panels), electrically the pixels are connected in a chain that is
@@ -149,15 +197,41 @@
        PANEL_RES_X*2,              // DO NOT CHANGE THIS
        PANEL_RES_Y/2,              // DO NOT CHANGE THIS
        PANEL_CHAIN_LEN
+       //, _pins       
      );
  
-   #else
- 
-     // Standard panel type natively supported by this library (Example 1)
+   #endif
+
+   #if EXAMPLE_NUMBER == 3
+   // A single 1/4 scan panel. This panel type is not supported 'out of the box' and require specific  
+   // ScanTypeMapping (pixel mapping) within the panel itself.
+
+     /**
+      * HACK ALERT!
+      * For 1/4 scan panels (namely outdoor panels), electrically the pixels are connected in a chain that is
+      * twice the physical panel's pixel width, and half the pixel height. As such, we need to configure
+      * the underlying DMA library to match the same. Then we use the VirtualMatrixPanel_T class to map the
+      * physical pixels to the virtual pixels.
+     */
+   HUB75_I2S_CFG mxconfig(
+     PANEL_RES_X*2,              // DO NOT CHANGE THIS
+     PANEL_RES_Y/2,              // DO NOT CHANGE THIS
+     1 // A Single panel
+       //, _pins     
+   );
+   #endif
+
+   #if EXAMPLE_NUMBER == 4
+   // A single normal scan panel, but we're using a custom CustomScanTypeMapping in the 'wrong' 
+   // way to demonstrate how it can be used to create custom physical LED Matrix panel mapping.
+
+   HUB75_I2S_CFG::i2s_pins _pins={RL1, GL1, BL1, RL2, GL2, BL2, CH_A, CH_B, CH_C, CH_D, CH_E, LAT, OE, CLK};
+     // Standard panel type natively supported by this library (Example 1, 4)
      HUB75_I2S_CFG mxconfig(
        PANEL_RES_X,   
        PANEL_RES_Y,  
-       PANEL_CHAIN_LEN    
+       1          
+     //   , _pins
      );
  
    #endif
@@ -185,11 +259,13 @@
      virtualDisp = new VirtualMatrixPanel_T<PANEL_CHAIN_TYPE, MyScanTypeMapping>(VDISP_NUM_ROWS, VDISP_NUM_COLS, PANEL_RES_X, PANEL_RES_Y);
    #elif EXAMPLE_NUMBER == 3   
      virtualDisp = new VirtualMatrixPanel_T<CHAIN_NONE, MyScanTypeMapping>(1, 1, PANEL_RES_X, PANEL_RES_Y); // Single 1/4 scan panel
+   #elif EXAMPLE_NUMBER == 4   
+     virtualDisp = new VirtualMatrixPanel_T<CHAIN_NONE, CustomMirrorScanTypeMapping>(1, 1, PANEL_RES_X, PANEL_RES_Y); // Single 1/4 scan panel	 
    #endif  
  
    // Pass a reference to the DMA display to the VirtualMatrixPanel_T class
    virtualDisp->setDisplay(*dma_display);
- 
+  
    for (int y = 0; y < virtualDisp->height(); y++) {
      for (int x = 0; x < virtualDisp->width(); x++) {
  
@@ -199,20 +275,25 @@
        if (x == (virtualDisp->width()-1)) color = virtualDisp->color565(0, 0, 255); // b
  
        virtualDisp->drawPixel(x, y, color);
-       delay(2);
+       delay(1);
      }
    }
+
+   virtualDisp->drawLine(virtualDisp->width() - 1, virtualDisp->height() - 1, 0, 0, virtualDisp->color565(255, 255, 255));
    
+   virtualDisp->print("Virtual Matrix Panel");
+
    delay(3000);
    virtualDisp->clearScreen();
-   virtualDisp->drawDisplayTest(); // re draw text numbering on each screen to check connectivity
-   
+   virtualDisp->drawDisplayTest(); // re draw text numbering on each screen to check connectivity   
+
  }
+
+ // ------------------------------------------------------------------------------------------------------------
  
  
  void loop() {
  
    // Do nothing here.
-   delay (100);
- 
+
  }
\ No newline at end of file
diff --git a/src/ESP32-HUB75-VirtualMatrixPanel_T.hpp b/src/ESP32-HUB75-VirtualMatrixPanel_T.hpp
index 0cebc2b..765b966 100644
--- a/src/ESP32-HUB75-VirtualMatrixPanel_T.hpp
+++ b/src/ESP32-HUB75-VirtualMatrixPanel_T.hpp
@@ -103,7 +103,7 @@ template <PANEL_SCAN_TYPE ScanType>
 struct ScanTypeMapping {
 	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) 
@@ -301,7 +301,7 @@ public:
 				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);
 			}
 		}