Générateur de fractale de Mandelbrot sur plateforme Arduino (calculs à virgule fixe)

par skywodd | | Langue : C++ | Licence : GPLv3

Description :

Générateur de fractale de Mandelbrot sur plateforme Arduino (calculs à virgule fixe).

Code source :

Voir le code source brut | Télécharger mandelbrot_fast.ino | Télécharger mandelbrot_fast.ino.zip

/*
 * Générateur de fractale de Mandelbrot sur plateforme Arduino (calculs à virgule fixe)
 */

/* Includes */
#include <stdint.h>           // Pour les types d'entier de taille fixe
#include <SPI.h>              // Pour la communication avec l'écran
#include <digitalWriteFast.h> // Pour éviter une erreur de compilation
#include <GraphicsLib.h>      // Pour les fonctions de dessin
#include <MI0283QT2.h>        // Pour l'écran lui-même

/* Utilitaire de calculs à virgule fixe */
#define decimal int16_t
#define decimal_2x int32_t
#define MID_FP_POINT (8)
#define floatToDecimal(f) (f * ((decimal_2x) 1 << MID_FP_POINT))
#define addDecimal(a, b) ((decimal) a + (decimal) b)
#define substractDecimal(a, b) ((decimal) a - (decimal) b)
#define multiplyDecimal(a, b) (((decimal_2x) a * b) >> MID_FP_POINT)
#define divideDecimal(a, b) (((decimal_2x) a << MID_FP_POINT) / b)

/* Defines */
const uint16_t MAX_ITERATION = 142; // Nombre de couleurs
const uint16_t SCREEN_WIDTH  = 320; // 
const uint16_t SCREEN_HEIGHT = 240; // Taille de l'écran

/**
 * Tableaux de couleurs (142 couleurs + noir, tiré d'un nuancier trouvé sur le web)
 */
const uint16_t COLOR_TABLE[143] = { // HTML color
  0xf7df, 0xff5a, 0x07ff, 0x7ffa, 0xf7ff, 0xf7bb, 0xff38, 0xff59, 0x001f, 0x895c, 
  0xa145, 0xddd0, 0x5cf4, 0x7fe0, 0xd343, 0xfbea, 0x64bd, 0xffdb, 0xd8a7, 0x7ff, 
  0x0011, 0x0451, 0xbc21, 0xad55, 0x0320, 0xbdad, 0x8811, 0x5345, 0xfc60, 0x9999, 
  0x8800, 0xecaf, 0x8df1, 0x49f1, 0x2a69, 0x067a, 0x901a, 0xf8b2, 0x05ff, 0x6b4d, 
  0x1c9f, 0xd48e, 0xb104, 0xffde, 0x2444, 0xf81f, 0xdefb, 0xffdf, 0xfea0, 0xdd24, 
  0x8410, 0x0400, 0xafe5, 0xf7fe, 0xfb56, 0xcaeb, 0x4810, 0xfffe, 0xf731, 0xe73f, 
  0xff9e, 0x7fe0, 0xffd9, 0xaedc, 0xf410, 0xe7ff, 0xffda, 0xd69a, 0x9772, 0xfdb8, 
  0xfd0f, 0x2595, 0x867f, 0x839f, 0x7453, 0xb63b, 0xfffc, 0x07e0, 0x3666, 0xff9c, 
  0xf81f, 0x8000, 0x6675, 0x0019, 0xbaba, 0x939b, 0x3d8e, 0x7b5d, 0x07d3, 0x4e99, 
  0xc0b0, 0x18ce, 0xf7ff, 0xff3c, 0xff36, 0xfef5, 0x0010, 0xffbc, 0x8400, 0x6c64, 
  0xfd20, 0xfa20, 0xdb9a, 0xef55, 0x9fd3, 0xaf7d, 0xdb92, 0xff7a, 0xfed7, 0xcc27, 
  0xfe19, 0xdd1b, 0xb71c, 0x8010, 0xf800, 0xbc71, 0x435c, 0x8a22, 0xfc0e, 0xf52c, 
  0x2c4a, 0xffbd, 0xa285, 0xc618, 0x867d, 0x6ad9, 0x7412, 0xffdf, 0x07ef, 0x4416, 
  0xd5b1, 0x0410, 0xddfb, 0xfb08, 0x471a, 0xec1d, 0xd112, 0xf6f6, 0xffff, 0xf7be, 
  0xffe0, 0x9e66, 0x0000
};

/* Déclaration de l'objet lcd pour l'affichage */
MI0283QT2 lcd;

/* Fonction setup */
void setup() {

  /* Initialise l'écran LCD */
  lcd.begin(2); // Initialisation de l'écran lcd
  lcd.led(30); // Backligth à 30%
  //lcd.fillScreen(RGB(0, 0, 0)); // Écran noir
  lcd.setOrientation(270);
}

/* Fonction loop() */
void loop() {

  /* Point de départ de la fenêtre de dessin dans le plan réel / complexe de mandelbrot */
  static decimal start_x = floatToDecimal(-0.75);
  static decimal start_y = floatToDecimal(0.0);
  static decimal zoom = floatToDecimal(0.8);

  /* Dessine la fractale */
  draw_mandelbrot(start_x, start_y, zoom);

  /* Effet de zoom très sommaire et pas du tout proportionné */
  zoom += floatToDecimal(0.1);
}

/** 
 * Dessine une fractale de Mandelbrot
 */
void draw_mandelbrot(const decimal start_x, const decimal start_y, const decimal zoom) {

  /* Commence l'affichage */
  lcd.setArea(0, 0, SCREEN_HEIGHT - 1, SCREEN_WIDTH - 1);
  lcd.drawStart();

  /* Pour chaque pixel en X */
  for(uint16_t x = 0; x < SCREEN_WIDTH; ++x) {
    // decimal p_r = 1.5 * (x - SCREEN_WIDTH / 2.0) / (0.5 * zoom * SCREEN_WIDTH) + start_x;
    decimal p_r = addDecimal(divideDecimal(multiplyDecimal(floatToDecimal(1.5), substractDecimal(x, divideDecimal(SCREEN_WIDTH, floatToDecimal(2.0)))), 
      multiplyDecimal(multiplyDecimal(floatToDecimal(0.5), zoom), SCREEN_WIDTH)), start_x);

    /* Pour chaque pixel en Y */
    for(uint16_t y = 0; y < SCREEN_HEIGHT; ++y) {
      // decimal p_i = (y - SCREEN_HEIGHT / 2.0) / (0.5 * zoom * SCREEN_HEIGHT) + start_y;
      decimal p_i = addDecimal(divideDecimal(substractDecimal(y, divideDecimal(SCREEN_HEIGHT, floatToDecimal(2.0))), 
        multiplyDecimal(multiplyDecimal(floatToDecimal(0.5), zoom), SCREEN_HEIGHT)), start_y);
      
      decimal new_r = floatToDecimal(0.0), new_i = floatToDecimal(0.0);
      decimal old_r = floatToDecimal(0.0), old_i = floatToDecimal(0.0);
      uint16_t i = 0;

      /* Magie noir mathématique (merci Wikipedia) */
      // while ((new_r * new_r + new_i * new_i) < 4.0 && i < MAX_ITERATION) {
      while (addDecimal(multiplyDecimal(new_r, new_r), multiplyDecimal(new_i, new_i)) < floatToDecimal(4.0) && i < MAX_ITERATION) {
        old_r = new_r;
        old_i = new_i;
        // new_r = old_r * old_r - old_i * old_i + p_r;
        new_r = addDecimal(substractDecimal(multiplyDecimal(old_r, old_r), multiplyDecimal(old_i, old_i)), p_r);
        // new_i = 2.0 * old_r * old_i + p_i;
        new_i = addDecimal(multiplyDecimal(multiplyDecimal(floatToDecimal(2.0), old_r), old_i), p_i);
        ++i;
      }

      /* Affiche le pixel */
      lcd.draw(COLOR_TABLE[i]);
    }
  }

  /* Termine l'affichage */
  lcd.drawStop();
}