atuline

Mandelbrot Set for Arduino FastLED

Jan 18th, 2021
785
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.  * Minimal Mandelbrot for FastLED on Arduino
  3.  *
  4.  * By: Andrew Tuline
  5.  *
  6.  * Date: January 2021
  7.  *
  8.  * A static Mandelbrot display for a 16x16 (resizable) FastLED array.
  9.  *
  10.  * Based on an example in the Processing language at:
  11.  *
  12.  * https://processing.org/examples/mandelbrot.html
  13.  *
  14.  * An online interactive Mandelbrot set in order to help determine valid min and max values:
  15.  *
  16.  * https://mandel.gart.nz/
  17.  *
  18.  * Although this will run on an Arduino UNO, it does so at about 7 frames per second, whereas, you can get about 100 on an ESP32.
  19.  *
  20.  *
  21.  * How to calculate a Mandelbrot set:
  22.  *
  23.  * Either a series of calculations stays within a maximum value (and is in the Mandelbrot set), or they approach infinity.
  24.  * We start with a grid of x,y coordinates.
  25.  * We need to determine which x,y grid co-ordinates are inside and outside of the Mandelbrot set.
  26.  * The y axis uses complex numbers.
  27.  * A complex number includes 'i', where i^2 = -1.
  28.  * An example complex number is 3i, so (3i)^2 = -9.
  29.  * The Mandelbrot formula is z = z^2 + c.
  30.  * Where 'c' is the current x,y or (real, imaginary) location.
  31.  * The initial value of 'z' is 0.
  32.  * It's an iterative function, meaning you run it again and again. . .
  33.  * After some iterations, if abs(z) > a maximum value, then stop.
  34.  * You can pre-define the maximum number of iterations.
  35.  *
  36.  * z = z^2 + c
  37.  *
  38.  * Since we have a point 'c' in a real and complex plane, an example of that point is (2,3i).
  39.  * The initial value of z is 0.
  40.  * The next value of z will be 'c' or (2,3i).
  41.  * You can use the variables x and y for those values.
  42.  * For the next iteration, for z^2, you need to calculate:
  43.  *
  44.  * (2,3i) * (2,3i)
  45.  *
  46.  * Using the foil multiplocation method (first, outer, inner, last), you get:
  47.  *
  48.  * = 2*2 + 2*3i + 2*3i + 3i*3i    Where 3i*3i is the same as (3i)^2, which is 9*(-1) or -9.
  49.  * = 4 + 6i + 6i + 9*i^2
  50.  * = 4 + 12i -9                   Where i^2 = -1 and 3*3 = 9 so result is -9.
  51.  * = -5 + 12i                     This is the result of z^2.
  52.  *
  53.  * Finishing off, z = z^2 + c
  54.  *
  55.  * z = (-5 + 12i) + 2 + 3i
  56.  * z = -3 + 15i
  57.  *
  58.  *
  59.  * To get the maximum value of that point, we calculate its length:
  60.  *
  61.  * |z| = sqrt(x^2 + y^2)
  62.  * or z^2 = x^2 + y^2         In order to avoid the square root calculation, just make maximum value larger.
  63.  *      = -3*-3 + 15i*15i     Remember, i*i = -1
  64.  *      = 9 - 225
  65.  *      = -216                The absolute value of this is out of any reasonable range, so stop immediately.
  66.  *      
  67.  * Count the number of times you get to the maximum value, and use that for your colour.
  68.  * If you never make it, the colour will be black and that point is INSIDE the Mandelbrot set.
  69.  *  
  70.  * Review the source code below to see how these are easily calculated.
  71.  *
  72.  */
  73.  
  74.  
  75. #include <FastLED.h>
  76.  
  77. #define LED_PIN  2
  78. #define LED_TYPE WS2812
  79. #define COLOR_ORDER GRB
  80. #define matrixSerpentineLayout true
  81.  
  82. const uint8_t matrixWidth = 16;
  83. const uint8_t matrixHeight = 16;
  84.  
  85. #define NUM_LEDS matrixWidth * matrixHeight
  86.  
  87. CRGB leds[NUM_LEDS];
  88. // Palette definitions
  89. CRGBPalette16 currentPalette;
  90. CRGBPalette16 targetPalette;
  91.  
  92. // These initial values show the whole thing on the matrix.
  93. float reAl = -.5;               // Relatively close to https://mandel.gart.nz/#/
  94. float imAg = 0.1;
  95. float zoOm = 550.;
  96.  
  97. // Calculated start/stop coordinates.
  98. float xmin, ymin, xmax, ymax;   // Our window.
  99. float dx;                       // Delta x is mapped to the matrix size.
  100. float dy;                       // Delta y is mapped to the matrix size.
  101.  
  102. int maxIterations = 255;        // How many iterations per pixel before we give up. Make it 8 bits to match our range of colours.
  103. float maxCalc = 16.0;           // How big is each calculation allowed to be before we give up.
  104.  
  105.  
  106.  
  107. void setup() {
  108.   delay(1000);
  109.   Serial.begin(115200);
  110.   FastLED.addLeds<LED_TYPE,LED_PIN,COLOR_ORDER>(leds, NUM_LEDS);
  111.   FastLED.setBrightness(32);
  112. } // setup()
  113.  
  114.  
  115.  
  116. void loop() {
  117.  
  118.   resize();                               // Define the window to display.
  119.   mandel();                               // Calculate and display that window.
  120.  
  121.   FastLED.show();                         // FastLED now displays the results.
  122.  
  123. } // loop()
  124.  
  125.  
  126.  
  127. void resize() {                           // Resize the minimum and maximum values on the fly to display.
  128.  
  129. //  reAl = -1.304605388;                    // Try another location.
  130. //  imAg = 0.;
  131. //  zoOm = 92682.;
  132.  
  133. //  zoOm = 5000 * abs(sin((float)millis()/2000.))+400;   // Animate the zoom.
  134.  
  135.   const float zoomAdj = .0016;             // Adjust zoom factor so that it's compatible with https://mandel.gart.nz/#/
  136.   const float xadj = 0.;                   // Same for x and y values.
  137.   const float yadj = .001;
  138.  
  139.   xmin = reAl - 1./zoOm/zoomAdj+xadj;     // Scale the windows in accordance with the zoom factor.
  140.   xmax = reAl + 1./zoOm/zoomAdj+xadj;
  141.   ymin = imAg - 1./zoOm/zoomAdj+yadj;
  142.   ymax = imAg + 1./zoOm/zoomAdj+yadj;
  143.  
  144.   dx = (xmax - xmin) / (matrixWidth);     // Scale the delta x and y values to our matrix size.
  145.   dy = (ymax - ymin) / (matrixHeight);
  146.  
  147. } // resize()
  148.  
  149.  
  150.  
  151. void mandel() {                             // Calculate and display the Mandelbrot set for the current window.
  152.   // Start y
  153.   float y = ymin;
  154.   for (int j = 0; j < matrixHeight; j++) {
  155.    
  156.     // Start x
  157.     float x = xmin;
  158.     for (int i = 0; i < matrixWidth; i++) {
  159.  
  160.       // Now we test, as we iterate z = z^2 + c does z tend towards infinity?
  161.       float a = x;
  162.       float b = y;
  163.       int iter = 0;
  164.  
  165.       while (iter < maxIterations) {    // Here we determine whether or not we're out of bounds.
  166.         float aa = a * a;
  167.         float bb = b * b;
  168.         float len = aa + bb;
  169.         if (len > maxCalc) {            // |z| = sqrt(a^2+b^2) OR z^2 = a^2+b^2 to save on having to perform a square root.
  170.           break;  // Bail
  171.         }
  172.        
  173.        // This operation corresponds to z -> z^2+c where z=a+ib c=(x,y). Remember to use 'foil'.      
  174.         b = 2*a*b + y;
  175.         a = aa - bb + x;
  176.         iter++;
  177.       } // while
  178.  
  179.       // We color each pixel based on how long it takes to get to infinity, or black if it never gets there.
  180.       if (iter == maxIterations) {
  181.         leds[XY(i,j)] = CRGB::Black;            // Calculation kept on going, so it was within the set.
  182.       } else {
  183.         leds[XY(i,j)] = CHSV(iter,255,255);   // Near the edge of the set.
  184.       }
  185.       x += dx;
  186.     }
  187.     y += dy;
  188.   }
  189.  
  190. } // mandel()
  191.  
  192.  
  193.  
  194. // Use this for your matrix. Could be serpentine or not.
  195. uint16_t XY( uint8_t x, uint8_t y) {
  196.  
  197.   uint16_t i;
  198.  
  199.   if( matrixSerpentineLayout == false) {
  200.     i = (y * matrixWidth) + x;
  201.   }
  202.  
  203.   if( matrixSerpentineLayout == true) {
  204.     if( y & 0x01) {
  205.       uint8_t reverseX = (matrixWidth - 1) - x;
  206.       i = (y * matrixWidth) + reverseX;
  207.     } else {
  208.       i = (y * matrixWidth) + x;
  209.     }
  210.   }
  211.   return i;
  212.  
  213. } // XY()
RAW Paste Data