Advertisement
Guest User

Untitled

a guest
Mar 7th, 2025
285
0
232 days
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 17.96 KB | Source Code | 0 0
  1. // Include the ESP32 HUB75 Matrix Panel library and math library for trigonometric functions.
  2. #include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
  3. #include <math.h>
  4.  
  5. // ===== Panel Configuration =====
  6. // Define the resolution of each panel and the overall matrix dimensions.
  7. const int panelResX = 64;                // Horizontal resolution of one panel.
  8. const int panelResY = 64;                // Vertical resolution of one panel.
  9. const int panel_chain = 2;               // Number of panels chained together horizontally.
  10. const int WIDTH  = panelResX * panel_chain;  // Total width: 64 * 2 = 128 pixels.
  11. const int HEIGHT = panelResY;                // Total height: 64 pixels.
  12. const int CENTER_X = WIDTH / 2;          // Center X coordinate of the display.
  13. const int CENTER_Y = HEIGHT / 2;         // Center Y coordinate of the display.
  14.  
  15. // Global pointer to the matrix object used for drawing.
  16. MatrixPanel_I2S_DMA *matrix = nullptr;
  17.  
  18. // ===== Two Swirls (Galaxies) =====
  19. // Define parameters for the swirling galaxies animation.
  20. #define NUM_STARS  100       // Number of stars (points) per swirl.
  21. #define NUM_ARMS   3         // Number of spiral arms in each swirl.
  22. #define MAX_RADIUS 40.0f     // Maximum radius for star positions from the swirl center.
  23. #define THICKNESS  6.0f      // Thickness (depth variance) of the swirl.
  24.  
  25. // Structure representing a single star in a swirl.
  26. struct SwirlStar {
  27.   float baseX, baseY, baseZ;  // The 3D position of the star in the swirl's local space.
  28.   uint8_t baseHue;           // Base hue value for the star's color.
  29. };
  30.  
  31. // Declare two arrays of swirl stars, one for each galaxy effect.
  32. SwirlStar swirlA[NUM_STARS];
  33. SwirlStar swirlB[NUM_STARS];
  34.  
  35. // Define initial positions for the centers of the two swirls.
  36. float swirlACenterX_init = -30.0f;
  37. float swirlACenterY_init = 0.0f;
  38. float swirlBCenterX_init = +30.0f;
  39. float swirlBCenterY_init = 0.0f;
  40.  
  41. // Variables to hold the current positions of the swirl centers (they will orbit).
  42. float swirlACenterX, swirlACenterY;
  43. float swirlBCenterX, swirlBCenterY;
  44.  
  45. // Variables for rotation angles for each swirl around the X, Y, and Z axes.
  46. float rotAx = 0.0f, rotAy = 0.0f, rotAz = 0.0f;
  47. float rotBx = 0.0f, rotBy = 0.0f, rotBz = 0.0f;
  48.  
  49. // Define rotational speeds for each axis for both swirls.
  50. float speedAx = 0.01f, speedAy = 0.015f, speedAz = 0.008f;
  51. float speedBx = 0.012f, speedBy = 0.013f, speedBz = 0.01f;
  52.  
  53. // ===== Warp Effect =====
  54. // Maximum angle for the warp tilt effect and its periodicity.
  55. const float WARP_MAX_ANGLE = M_PI / 9;   // Maximum tilt in radians.
  56. const uint32_t WARP_PERIOD_MS = 10000;     // Time period (in ms) for one warp cycle.
  57.  
  58. // ===== Color Cycle =====
  59. // Duration for a complete hue cycle (for color animation).
  60. const uint32_t COLOR_CYCLE_MS = 8000;
  61.  
  62. // ===== Fade Factor for Trails =====
  63. // This factor is used to gradually fade the pixels for trailing effects.
  64. const uint8_t FADE_FACTOR = 230;
  65.  
  66. // ===== Swirl Orbit Variables =====
  67. // Variables to control the orbital motion of the swirl centers around each other.
  68. float swirlOrbitRadius = 20.0f;   // Orbit radius.
  69. float swirlOrbitAngle = 0.0f;     // Current angle in the orbit.
  70. float swirlOrbitSpeed = 0.003f;   // Speed of orbital motion.
  71.  
  72. // ---------- HSV to RGB Utility ----------
  73. // This function converts a color from HSV (Hue, Saturation, Value) to RGB format.
  74. // The parameters h, s, v represent the input hue, saturation, and brightness.
  75. // The output parameters r, g, b are set to the corresponding red, green, and blue values.
  76. void hsvToRgb(uint8_t h, uint8_t s, uint8_t v,
  77.               uint8_t &r, uint8_t &g, uint8_t &b) {
  78.   uint8_t region = h / 43;  // There are 6 regions in the hue circle (256/6 ≈ 43).
  79.   uint8_t remainder = (h - region * 43) * 6;  // Position within the region.
  80.   // Compute intermediary values for conversion.
  81.   uint8_t p = (v * (255 - s)) / 255;
  82.   uint8_t q = (v * (255 - ((s * remainder) / 255))) / 255;
  83.   uint8_t t = (v * (255 - ((s * (255 - remainder)) / 255))) / 255;
  84.   // Choose the correct RGB combination based on the hue region.
  85.   switch (region) {
  86.     case 0: r = v; g = t; b = p; break;
  87.     case 1: r = q; g = v; b = p; break;
  88.     case 2: r = p; g = v; b = t; break;
  89.     case 3: r = p; g = q; b = v; break;
  90.     case 4: r = t; g = p; b = v; break;
  91.     default: r = v; g = p; b = q; break;
  92.   }
  93. }
  94.  
  95. // ========== Fade Buffer ==========
  96. // A 3D array that holds the current color values for each pixel on the panel.
  97. // The dimensions are [HEIGHT][WIDTH][3] representing Y coordinate, X coordinate, and RGB channels.
  98. static uint8_t frameBuf[HEIGHT][WIDTH][3];
  99.  
  100. // ========== Explosion State ==========
  101. // Variables used to manage the explosion animation when the two swirls collide.
  102. bool inExplosion = false;       // Flag indicating if an explosion is active.
  103. uint32_t explosionStartTime = 0; // Timestamp (ms) when the explosion started.
  104. const uint32_t EXPLOSION_DURATION = 2000; // Duration of the explosion effect in milliseconds.
  105. float collisionX = 0, collisionY = 0;     // Coordinates of the collision (explosion center).
  106.  
  107. // ========== Spiral Generation ==========
  108. // This function generates a spiral pattern of stars for a given swirl.
  109. // It randomly distributes stars along several spiral arms, with some noise added.
  110. void generateSpiral(SwirlStar *swirl) {
  111.   for (int i = 0; i < NUM_STARS; i++) {
  112.     // Randomly choose an arm for the star.
  113.     int arm = random(NUM_ARMS);
  114.     // Generate a radius with a square root to achieve a non-linear distribution.
  115.     float r = sqrtf((float)random(0, 10001) / 10000.0f) * MAX_RADIUS;
  116.     // Calculate the base angle for the chosen arm.
  117.     float baseArmAngle = ((float)arm / NUM_ARMS) * 2.0f * M_PI;
  118.     // Add a twist factor proportional to the radius.
  119.     float twist = r * 0.8f;
  120.     // Add some noise for natural variation.
  121.     float noise = (((float)random(-100, 101)) / 1000.0f) * M_PI;
  122.     // Final angle position for the star.
  123.     float angle = baseArmAngle + twist + noise;
  124.     // Convert polar coordinates to Cartesian.
  125.     float x = r * cosf(angle);
  126.     float y = r * sinf(angle);
  127.     // Add a slight variation in the Z-axis to give a 3D effect.
  128.     float z = (((float)random(-100, 101)) / 100.0f) * (THICKNESS / 2.0f);
  129.     // Store the calculated base positions and a random hue.
  130.     swirl[i].baseX = x;
  131.     swirl[i].baseY = y;
  132.     swirl[i].baseZ = z;
  133.     swirl[i].baseHue = (uint8_t)random(0, 256);
  134.   }
  135. }
  136.  
  137. // ===== Setup Function =====
  138. // This function initializes the hardware panel, clears buffers, and generates the initial swirls.
  139. void setup() {
  140.   // Configure the panel using the HUB75 I2S configuration structure.
  141.   HUB75_I2S_CFG mxconfig(panelResX, panelResY, panel_chain);
  142.   mxconfig.double_buff = true;   // Enable double buffering for smoother updates.
  143.   mxconfig.gpio.e = 18;          // Set the GPIO pin for the enable signal.
  144.   mxconfig.clkphase = false;     // Clock phase configuration.
  145.  
  146.   // Instantiate and initialize the matrix panel.
  147.   matrix = new MatrixPanel_I2S_DMA(mxconfig);
  148.   matrix->begin();
  149.   matrix->setBrightness8(200);  // Set the brightness level.
  150.   matrix->fillScreen(matrix->color565(0, 0, 0));  // Clear the screen to black.
  151.  
  152.   // Clear the fade buffer (initialize every pixel to black).
  153.   for (int y = 0; y < HEIGHT; y++) {
  154.     for (int x = 0; x < WIDTH; x++) {
  155.       frameBuf[y][x][0] = 0;
  156.       frameBuf[y][x][1] = 0;
  157.       frameBuf[y][x][2] = 0;
  158.     }
  159.   }
  160.  
  161.   // Seed the random number generator with the current time (in ms).
  162.   randomSeed(millis());
  163.   // Generate the initial spiral patterns for both swirls.
  164.   generateSpiral(swirlA);
  165.   generateSpiral(swirlB);
  166.  
  167.   // Set the initial positions for the swirl centers.
  168.   swirlACenterX = swirlACenterX_init;
  169.   swirlACenterY = swirlACenterY_init;
  170.   swirlBCenterX = swirlBCenterX_init;
  171.   swirlBCenterY = swirlBCenterY_init;
  172. }
  173.  
  174. // ===== Draw Swirl Function =====
  175. // This function draws a swirl (galaxy) onto the panel.
  176. // It applies rotations, perspective, and color adjustments for each star.
  177. void drawSwirl(SwirlStar *swirl, float centerX, float centerY,
  178.                float rotX, float rotY, float rotZ,
  179.                float tiltAngle, uint8_t hueOffset)
  180. {
  181.   // Process each star in the swirl.
  182.   for (int i = 0; i < NUM_STARS; i++) {
  183.     // Get the star's base position.
  184.     float x = swirl[i].baseX;
  185.     float y = swirl[i].baseY;
  186.     float z = swirl[i].baseZ;
  187.  
  188.     // ---- 3D Rotations ----
  189.     // Rotate around the X-axis.
  190.     float y1 = y * cosf(rotX) - z * sinf(rotX);
  191.     float z1 = y * sinf(rotX) + z * cosf(rotX);
  192.     // Rotate around the Y-axis.
  193.     float x2 = x * cosf(rotY) + z1 * sinf(rotY);
  194.     float z2 = -x * sinf(rotY) + z1 * cosf(rotY);
  195.     // Rotate around the Z-axis.
  196.     float x3 = x2 * cosf(rotZ) - y1 * sinf(rotZ);
  197.     float y3 = x2 * sinf(rotZ) + y1 * cosf(rotZ);
  198.     float z3 = z2;
  199.  
  200.     // ---- Warp (Tilt) Effect ----
  201.     // Apply an additional tilt effect to simulate a warp.
  202.     float yw = y3 * cosf(tiltAngle) - z3 * sinf(tiltAngle);
  203.     float zw = y3 * sinf(tiltAngle) + z3 * cosf(tiltAngle);
  204.  
  205.     // ---- Translation and Perspective ----
  206.     // Translate the star's position by adding the swirl's center coordinates.
  207.     float finalX = x3 + centerX;
  208.     float finalY = yw + centerY;
  209.     float finalZ = zw;
  210.    
  211.     // Apply a simple perspective projection:
  212.     // "cameraDist" simulates the distance from the viewer to the screen.
  213.     float cameraDist = 100.0f;
  214.     float depth = cameraDist - finalZ;
  215.     if (depth < 1.0f) continue; // Skip if the star is too close.
  216.     float scale = cameraDist / depth;
  217.     float projX = finalX * scale;
  218.     float projY = finalY * scale;
  219.  
  220.     // Convert the projected coordinates into screen coordinates.
  221.     int screenX = (int)(CENTER_X + projX);
  222.     int screenY = (int)(CENTER_Y - projY);
  223.     // Skip drawing if the coordinates fall outside the display.
  224.     if (screenX < 0 || screenX >= WIDTH || screenY < 0 || screenY >= HEIGHT)
  225.       continue;
  226.  
  227.     // ---- Brightness Calculation ----
  228.     // Calculate the base radius (distance from swirl center) to modulate brightness.
  229.     float baseRadius = sqrtf(x*x + y*y);
  230.     uint8_t brightness = (uint8_t)fmin(255.0f * (baseRadius / MAX_RADIUS), 255.0f);
  231.  
  232.     // ---- Color Adjustment ----
  233.     // Adjust the hue by the provided offset.
  234.     uint8_t hue = swirl[i].baseHue + hueOffset;
  235.     uint8_t rr, gg, bb;
  236.     hsvToRgb(hue, 255, brightness, rr, gg, bb);
  237.  
  238.     // Draw the pixel on the matrix using the computed RGB color.
  239.     matrix->drawPixel(screenX, screenY, matrix->color565(rr, gg, bb));
  240.     // Update the fade buffer so future frames can apply a fading trail effect.
  241.     frameBuf[screenY][screenX][0] = rr;
  242.     frameBuf[screenY][screenX][1] = gg;
  243.     frameBuf[screenY][screenX][2] = bb;
  244.   }
  245. }
  246.  
  247. // ===== Explosion Drawing =====
  248. // This function draws an explosion effect when the two swirls collide.
  249. // It expands a radial blast and modifies pixel colors based on the explosion progress.
  250. void drawExplosion(uint32_t now, float cx, float cy) {
  251.   // Calculate the explosion progress (0.0 at start, 1.0 at completion).
  252.   float elapsed = (now - explosionStartTime);
  253.   float progress = elapsed / (float)EXPLOSION_DURATION;
  254.   if (progress > 1.0f) progress = 1.0f;
  255.  
  256.   // Compute the maximum radius of the blast (using 75% of the screen's diagonal length).
  257.   float maxR = sqrtf((float)(WIDTH * WIDTH + HEIGHT * HEIGHT)) * 0.75f;
  258.   float blastRadius = maxR * progress;
  259.  
  260.   // Set explosion color: starts as bright white with a red tint, dims as the explosion expands.
  261.   int baseVal = (int)(255 * (1.0f - progress));
  262.   if (baseVal < 0) baseVal = 0;
  263.   uint8_t rr = 255, gg = (uint8_t)baseVal, bb = (uint8_t)baseVal / 2;
  264.  
  265.   // Convert the explosion center to screen coordinates.
  266.   // For simplicity, the swirl center coordinates are directly treated as screen coordinates.
  267.   int scx = (int)(CENTER_X + cx);
  268.   int scy = (int)(CENTER_Y - cy);
  269.  
  270.   // Loop through every pixel on the screen.
  271.   for (int y = 0; y < HEIGHT; y++) {
  272.     for (int x = 0; x < WIDTH; x++) {
  273.       int dx = x - scx;
  274.       int dy = y - scy;
  275.       // Calculate the distance from the explosion center.
  276.       float dist = sqrtf(dx * dx + dy * dy);
  277.       // If the pixel is within the blast radius, modify its color.
  278.       if (dist <= blastRadius) {
  279.         // Determine how close the pixel is to the center (for gradient effect).
  280.         float f = 1.0f - (dist / blastRadius);
  281.         int cval = (int)(baseVal + (255 - baseVal) * f);
  282.         if (cval > 255) cval = 255;
  283.         // Overwrite the pixel color for dramatic effect.
  284.         uint8_t newR = (uint8_t)cval;
  285.         uint8_t newG = (uint8_t)(cval / 4);
  286.         uint8_t newB = (uint8_t)(cval / 6);
  287.         // Update the fade buffer.
  288.         frameBuf[y][x][0] = newR;
  289.         frameBuf[y][x][1] = newG;
  290.         frameBuf[y][x][2] = newB;
  291.         // Draw the updated pixel on the matrix.
  292.         matrix->drawPixel(x, y, matrix->color565(newR, newG, newB));
  293.       }
  294.     }
  295.   }
  296. }
  297.  
  298. // ===== Reset the Scene =====
  299. // This function resets the entire animation scene after an explosion has finished.
  300. // It regenerates the swirls, resets rotation angles and positions, and clears the fade buffer.
  301. void resetScene() {
  302.   // Regenerate the star positions for both swirls.
  303.   generateSpiral(swirlA);
  304.   generateSpiral(swirlB);
  305.  
  306.   // Reset all rotational angles.
  307.   rotAx = rotAy = rotAz = 0.0f;
  308.   rotBx = rotBy = rotBz = 0.0f;
  309.   swirlOrbitAngle = 0.0f;
  310.  
  311.   // Reset the swirl centers to their initial positions.
  312.   swirlACenterX = swirlACenterX_init;
  313.   swirlACenterY = swirlACenterY_init;
  314.   swirlBCenterX = swirlBCenterX_init;
  315.   swirlBCenterY = swirlBCenterY_init;
  316.  
  317.   // Clear the fade buffer and the display by setting every pixel to black.
  318.   for (int y = 0; y < HEIGHT; y++){
  319.     for (int x = 0; x < WIDTH; x++){
  320.       frameBuf[y][x][0] = 0;
  321.       frameBuf[y][x][1] = 0;
  322.       frameBuf[y][x][2] = 0;
  323.       matrix->drawPixel(x, y, 0); // Draw black.
  324.     }
  325.   }
  326.   // Swap the double buffer to update the display.
  327.   matrix->flipDMABuffer();
  328. }
  329.  
  330. // ===== Main Loop =====
  331. // This is the main animation loop, called repeatedly. It handles fading, drawing the swirls,
  332. // updating positions and rotations, handling collisions, and managing the explosion effect.
  333. void loop() {
  334.   // Get the current time in milliseconds.
  335.   uint32_t now = millis();
  336.  
  337.   // ----- 1) Fade the Old Frame -----
  338.   // Loop through every pixel to gradually fade the previous frame,
  339.   // creating a trailing effect.
  340.   for (int y = 0; y < HEIGHT; y++) {
  341.     for (int x = 0; x < WIDTH; x++) {
  342.       uint8_t r = frameBuf[y][x][0];
  343.       uint8_t g = frameBuf[y][x][1];
  344.       uint8_t b = frameBuf[y][x][2];
  345.       // Multiply each color channel by the fade factor and shift right (divide by 256).
  346.       r = (r * FADE_FACTOR) >> 8;
  347.       g = (g * FADE_FACTOR) >> 8;
  348.       b = (b * FADE_FACTOR) >> 8;
  349.       // Update the fade buffer and redraw the pixel.
  350.       frameBuf[y][x][0] = r;
  351.       frameBuf[y][x][1] = g;
  352.       frameBuf[y][x][2] = b;
  353.       matrix->drawPixel(x, y, matrix->color565(r, g, b));
  354.     }
  355.   }
  356.  
  357.   // ----- 2) Explosion Handling -----
  358.   // If an explosion is currently in progress, draw the explosion effect.
  359.   if (inExplosion) {
  360.     drawExplosion(now, collisionX, collisionY);
  361.  
  362.     // Once the explosion duration is exceeded, reset the scene.
  363.     if (now - explosionStartTime > EXPLOSION_DURATION) {
  364.       inExplosion = false;
  365.       resetScene();
  366.     }
  367.  
  368.     // Swap buffers and yield control before processing the next frame.
  369.     matrix->flipDMABuffer();
  370.     yield();
  371.     return;
  372.   }
  373.  
  374.   // ----- 3) Normal Swirl Animation -----
  375.   // Compute the warp tilt angle based on a sine wave that oscillates over WARP_PERIOD_MS.
  376.   float warpPhase = 2.0f * M_PI * (float)((now % WARP_PERIOD_MS) / (double)WARP_PERIOD_MS);
  377.   float tiltAngle = WARP_MAX_ANGLE * sinf(warpPhase);
  378.  
  379.   // Determine the hue offset for the color cycle.
  380.   uint8_t timeHueOffset = (now % COLOR_CYCLE_MS) * 256 / COLOR_CYCLE_MS;
  381.  
  382.   // ----- Update Swirl Orbit -----
  383.   // Increment the orbit angle and use sine/cosine to calculate the new positions.
  384.   swirlOrbitAngle += swirlOrbitSpeed;
  385.   if (swirlOrbitAngle > 2 * M_PI) swirlOrbitAngle -= 2 * M_PI;
  386.   float cosO = cosf(swirlOrbitAngle);
  387.   float sinO = sinf(swirlOrbitAngle);
  388.   // Update both swirls' centers to orbit in opposite directions.
  389.   swirlACenterX = swirlACenterX_init + swirlOrbitRadius * cosO;
  390.   swirlACenterY = swirlACenterY_init + swirlOrbitRadius * sinO;
  391.   swirlBCenterX = swirlBCenterX_init - swirlOrbitRadius * cosO;
  392.   swirlBCenterY = swirlBCenterY_init - swirlOrbitRadius * sinO;
  393.  
  394.   // ----- Update Rotation Angles -----
  395.   // Increment rotation angles for each swirl by their respective speeds.
  396.   rotAx += speedAx; if (rotAx > 2 * M_PI) rotAx -= 2 * M_PI;
  397.   rotAy += speedAy; if (rotAy > 2 * M_PI) rotAy -= 2 * M_PI;
  398.   rotAz += speedAz; if (rotAz > 2 * M_PI) rotAz -= 2 * M_PI;
  399.   rotBx += speedBx; if (rotBx > 2 * M_PI) rotBx -= 2 * M_PI;
  400.   rotBy += speedBy; if (rotBy > 2 * M_PI) rotBy -= 2 * M_PI;
  401.   rotBz += speedBz; if (rotBz > 2 * M_PI) rotBz -= 2 * M_PI;
  402.  
  403.   // ----- Draw the Two Swirls -----
  404.   // Draw swirl A with its current center and rotation parameters.
  405.   drawSwirl(swirlA, swirlACenterX, swirlACenterY, rotAx, rotAy, rotAz, tiltAngle, timeHueOffset);
  406.   // Draw swirl B similarly.
  407.   drawSwirl(swirlB, swirlBCenterX, swirlBCenterY, rotBx, rotBy, rotBz, tiltAngle, timeHueOffset);
  408.  
  409.   // ----- 4) Collision Detection -----
  410.   // Check the distance between the two swirl centers.
  411.   float dx = swirlACenterX - swirlBCenterX;
  412.   float dy = swirlACenterY - swirlBCenterY;
  413.   float distSq = dx * dx + dy * dy;
  414.   // If the swirls are very close, trigger an explosion.
  415.   if (distSq < 8.0f * 8.0f) {
  416.     inExplosion = true;
  417.     explosionStartTime = now;
  418.     // Store the collision center (average of the two centers) for the explosion effect.
  419.     collisionX = (swirlACenterX + swirlBCenterX) * 0.5f;
  420.     collisionY = (swirlACenterY + swirlBCenterY) * 0.5f;
  421.   }
  422.  
  423.   // Swap the double buffer to display the newly drawn frame.
  424.   matrix->flipDMABuffer();
  425.   yield();  // Yield to allow background tasks to run.
  426. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement