Jeu du chaos en javascript

Autosimilarité fractale dans un polygone


Description & résultat / affichage

Le jeu du chaos est une méthode algorithmique qui permet de constuire relativement facilement l'attracteur d'un IFS (Système de Fonctions Itérées) qui a généralement des propriétés fractales.
Ce jeu (ou plutôt cette méthode) a été introduit par Barnsley dans les années 80. Originellement il se situe dans un polygone et se construit de la fa¸on suivante. On note N le nombre de sommets du polygone et k un nombre compris entre 0 et 1: paramètre modifiable interactivement ci-dessous).
  • On part d'un point M quelconque (ici le point de coordonnées (1,0))
  • on tire un nombre entier i, au hasard entre 1 et N
  • on calcule les coordonnées du nouveau point M situé à un rapport k entre le i-ème sommet et M précédent
  • On réitère l'étape précédente
Dans le programme ci-dessous, le nombre total de points, le nombre N de sommets du polygone et le rapport k sont des paramètres interactifs et on peut, si on le souhaite, affecter à chaque sommet un rapport différent.



Formulaire html

Tout d'abord la partie html avec form et canvas pour la représentation graphique:
<form id="globalform" onchange="Drawh();return false;" onsubmit="Drawh();return false;">
<label>Nombre de points:</label>
<input type="number" id="Np" min="0" value="50000" max="500000" step="10">
<br>
<label>Nombre de sommets:</label>
<input type="number" id="Ns" min="3" value="6" max="30" step="1" onchange="Chbx();return false;">
<br>
<label for="chbx">Rapports identiques pour tous les sommets</label>
<input type="checkbox" id="chbx" checked onchange="Chbx();return false;">
<div id="RapportFields"></div>
</form>
<canvas id="cnv" width="400" height="400"></canvas>


L'input "checkbox" permet de séparer, ou non, chacun des sommets. Lorsque sélectionné, un seul rapport est appliqué à tous les sommets, sinon, on sépare chacun des sommets et on créé autant d'input que de sommets.
La création dynamique d'input est traitée .

Programme javascript

<script>
// Références du canvas et ses dimensions
cnvj = document.getElementById("cnv");
ctx = cnvj.getContext("2d");
Width=document.getElementById("cnv").width;
Height=document.getElementById("cnv").height;

// Nouveau système de coordonnées, 
// voir: 
xmin=-1;xmax=1;
ymin=-1;ymax=1;
function cnv2x(X) {return X*(xmax-xmin)/Width+xmin;}
function cnv2y(Y) {return (Height-Y)*(ymax-ymin)/Height+ymin;}
function x2cnv(x) {return Width*(x-xmin)/(xmax-xmin);}
function y2cnv(y) {return Height-Height*(y-ymin)/(ymax-ymin);}

// fonction barycentre: 
// retourne les coordonnées du point situé au rapport k 
// entre les points A et B
function f(A,B,k) {
    //retourne C tel que Vecteur(AC)=k*Vecteur(AB)
    xC=(1-k)*A[0]+k*B[0]
    yC=(1-k)*A[1]+k*B[1]
    return [xC,yC];}


// Fonction "check Box" qui créé, si besoin 
// dynamiquement autant d'input que de sommets 
// voir cette page
function Chbx() {
    Fields=document.getElementById("RapportFields");
    if (!(Fields.hasChildNodes())) {k=0.6;}
    else {
     if (document.getElementById("k")) {k=document.getElementById("k").value;}
     else {k=document.getElementById("k0").value;
}
       while (Fields.hasChildNodes()) {Fields.removeChild(Fields.lastChild);}
    }

  if (document.getElementById("chbx").checked) {
       Fields.appendChild(document.createTextNode(" Rapport k"));
       var input = document.createElement("input");
       input.type = "number";
       input.name = "member";
       input.min=0;input.max=1;input.step=0.01;
       input.style="width:4em;"
       input.id = "k";input.value=k;
       Fields.appendChild(input);
  }  else {
     Ns=document.getElementById("Ns").value;
    for (i=0;i<Ns;i++) {
       // Texte précédent le nouvel input
       Fields.appendChild(document.createTextNode("Sommet S"+(i+1)+" - Rapport k" + (i+1)));
       // Nouvel input crée:
       var input = document.createElement("input");
       //... et ses attributs 
       input.type = "number";
       input.name = "member" + i;
       input.min=0;input.max=1;input.step=0.01;
       input.style = "width:4em;"
       input.id = "k" + i;
       Fields.appendChild(input);
       document.getElementById("k"+i).value=k;
       // On va enfin à la ligne:
       Fields.appendChild(document.createElement("br"));
    }
  }
}

// La fonction graphique, enfin...
function Drawh() {
 Np=document.getElementById("Np").value;
 Ns=document.getElementById("Ns").value;

    var k=new Array;// pour simplifier la suite, même s'il n'y a qu'un seul rapport k, 
                    // on considère k comme un tableau à un seul élément
    for (i=0;i<Ns;i++) {
       if (!(document.getElementById("chbx").checked)) {
          k[i]=document.getElementById("k"+i).value;}
       else {k[i]=document.getElementById("k").value;}
    }
 ctx.clearRect(0,0,Width,Height);// On efface tout le canvas

  // On dessine le polygone régulier
  // d'angle au centre α=i2π/Ns
  // Les coordonnées des sommets sont donc (cos(αi),sin(αi))
  // et on relie ces points: lineTo
  alpha=2*Math.PI/Ns;
  var S=new Array;
  S[0]=[1,0];
  ctx.fillStyle="blue";
  ctx.beginPath();
     X=x2cnv(1);Y=y2cnv(0);
     ctx.moveTo(X,Y);
  for (i=1;i<Ns;i++) {
     x=Math.cos(i*alpha);y=Math.sin(i*alpha);
     S[i]=[x,y];
     ctx.lineTo(x2cnv(x),y2cnv(y));
  }
  ctx.lineTo(x2cnv(1),y2cnv(0));
  ctx.stroke();

  // Jeu du chaos à proprement parler:
  M=[0,0];// Initialisation, point M de départ
  for (i=1;i<Np;i++) {
    p=Math.floor(Ns*Math.random());// Tirage d'un sommet au hasard
    M=f(M,S[p],k[p]);              // Nouveau point M, entre M et Sp, avec rapport kp
    X=x2cnv(M[0]);Y=y2cnv(M[1]);   // Retour aux coordonnées du canvas
    ctx.fillRect(X,Y,1,1);         // On dessine le point 
  }
}

// Initialisation: on exécute tout de suite 
// chaque fonction et on dessine le graphique
Chbx();
Drawh();
</script>



Voir aussi:

LongPage: h2: 1 - h3: 3