Outils pour utilisateurs

Outils du site


nsi:langages:processing:solutions:mandelbrot

Ensemble de Mandelbrot

Je donne ici une solution processing au TD Mandelbrot.

// Mandelbrot

final int ITERATIONS = 500;

// zoom quand on clic à l'écran
final double ZOOM = 1.5;

// colormap définie à la main
color[] colors = { 
    color(100, 14, 0),
    color(100, 70, 0),
    color(94, 100, 0),
    color(60, 120, 10),
    color(0, 100, 30),
    color(0, 100, 80),
    color(20, 80, 80),
    color(30, 0, 80),
    color(80, 0, 80),
    color(50, 0, 50),
    color(20, 0, 20),
    color(0, 0, 0)
};

Mandelbrot M = new Mandelbrot(-.5, 0, .01);

void setup(){
  size(1000, 800);
  fill(255);
  M.display();
}

void draw(){
  
}


class Mandelbrot {
  double centerx, centery, dot;
  Mandelbrot(double cx, double cy, double pixel_size){
    /* initialise un panneau centré sur cx, cy
       avec un pixel de taille pixel_size */
    centerx = cx;
    centery = cy;
    dot = pixel_size;
  }
  
  void zoom(int col, int line){
    /* zoom en laissant inchangé le pixel en lig et col */
    double x = centerx + (col - width/2)*dot;
    double y = centery - (line - height/2)*dot;
    dot = dot/ZOOM;
    centerx = x - (col - width/2)*dot;
    centery = y + (line - height/2)*dot;
    display();
  }
  
  void display() {
    /* calcule et trace l'ensemble de Mandelbrot */
    loadPixels();
    for (int line=0; line<height; line++){
      for (int col=0; col<width; col++) {
        double x = centerx + (col - width/2)*dot;
        double y = centery - (line - height/2)*dot;
        float score = getScore(x, y);
        pixels[line*width+col] = getColor(score);
      }
    }
    updatePixels();
  }
  
  float getScore(double cx, double cy){
    /* teste l'appartenance ce cx, cy à l'ensemble.
       renvoie un float entre 0 et 1, 0 pour appartenance complète à l'ensemble.
    */
    double zx = 0;
    double zy = 0;
    for (int i=0; i<ITERATIONS; i++){
      double nzx = zx*zx - zy*zy + cx;
      zy = 2*zx*zy + cy;
      zx = nzx;
      float m2 = (float) (zx*zx + zy*zy);
      if (m2 >= 4) {
        return 1 - max(0, i - log(m2)/log(4))/ITERATIONS;
      }
    }
    return 0;
  }
  
  color getColor(float rscore{
    /* score: facteur entre 0 et 1.
       renvoie la couleur correspondante dans la table colors */
    int n = colors.length;
    if (score == 1){
      return colors[n-1];
    }
    float v = score*(n-1);
    int i = floor(v);
    float f = v - i;
    return lerpColor(colors[i], colors[i+1], f);
  }
}

void mouseClicked() {
  M.zoom(mouseX, mouseY);
}

lissage entre les couleurs

La colormap est assez limitée. On veut passer linéairement d'une couleur à l'autre par un dégradé. Par exemple ici, il y a 12 images dans la colormap, colors[0] à colors[11]. pour un score de 0, on veut colors[0]. Pour un score de 1 on veut colors[11].

Donc en gros, on veut la couleur d'indice 11*score. Et si score = 0.4 ? Alors on voudrait colors[4.4] ? Eh bien on va prendre qui se situe à 40 % du dégradé allant de colors[4] jusqu'à colors[5].

La fonction lerpColor sert justement à calculer cette couleur intermédiaire.

meilleur lissage du score

Quand on calcule le score, on obtient toujours un résultat qui est en i/ITERATIONS. Donc on peut avoir des variations brusque selon que d'un point à un autre, on passe de i à i+1.

Mais quand un point sort du cercle de rayon 2, c'est à dire quand $|z_n|\geq 2$, , il peut sortir plus ou moins fort. On fait donc le calcul $\frac{\ln(|z_n|)}{\ln(2)}$, ou plutôt $\frac{\ln(|z_n|^2)}{\ln(4)}$, pour ajuster un peu le score. C'est un calcul empirique, cela donne un bon résultat. C'est une simple astuce pour obtenir un résultat plus joli, mais cela n'a pas grand sens mathématique.

nsi/langages/processing/solutions/mandelbrot.txt · Dernière modification : de goupillwiki