La source est un billet d'Etienne Ghys sur le site image des maths.
Cette animation a été codée juste après avoir lu l'article, il y a bien longtemps. Comme il s'agissait de surfaces réglées recollées entre elles, la tentation était forte de coder quelque chose dans le genre des autres animations de surfaces réglées. Une surface réglée est une surface qui est entièrement recouverte par une famille de droites. Comme dessiner des droites en javascript est facile, on peut relativement facilement dessiner de telles surfaces avec un code très simple, n'utilisant que les "canvas" en html5 et en dessinant des segments.
Il fallait donc trouver une façon de dessiner ces formes avec javascript.
Comme l'objectif était d'avoir un code qui permette d'augmenter facilement le nombre de traits (ou de le réduire, si on observe un ralentissement trop grand), le choix a été fait d'interpoler la forme du bord de la tuile avec des fonctions, c'était l'idée la plus simple. Après capture d'écran, zoom, et quelques tatonnements sur les coordonnées des points et des vecteurs, on obtient deux fonctions (définies par morceaux sur le segment [0,10] dans notre exemple), qui représentent le «haut» de la tuile, et le «bas» de la tuile :
function haut(x){
var r=0;
if(0<=x && x<= 1.5) r=x;
if(1.5<x && x<=2.5) r=1+0.5*(x-2.5)*(x-2.5);
if(2.5<x && x<=6.5) r=1+0.23*(x-2.5)*(x-2.5)-0.025*(x-2.5)*(x-2.5)*(x-2.5);//
if(6.5<x && x<=10) r=3*Math.sqrt((20-2*x)/7);
return r;
}
function bas(x){
var r=0;
if(0<= x && x<= 1.5) r=-x;
if(1.5< x && x<=2.5) r=-1-0.5*(x-2.5)*(x-2.5);
if(2.5<x && x<=6.5) r=-1;
if(6.5<x && x<=10) r=-Math.sqrt(1-((x-6.5)*(x-6.5))/(3.5*3.5));
return r;
}
C'est très moche, oui, mais c'était la première idée qui est venue et elle permet d'augmenter le nombre de traits à volonté. Illustration avec 20, 40 puis 60 traits:
Pour dessiner une face, il suffit de dessiner quatre tuiles en les faisant tourner d'un quart de tour à chaque fois, ce qui donne:
Toujours à comparer avec
// le tableau G contient 24 matrices de rotation
let G=new Array();
G[0]=matriceRotation([1,0,0],PI/4-0.55);
for(let i=1;i<4;i++)
G.push(produitMatriciel(matriceRotation([1,0,0],PI/2),G[i-1]));
for(let i=0;i<4;i++)
G.push(produitMatriciel(matriceRotation([0,0,1],PI/2),G[i]));
for(let i=0;i<4;i++)
G.push(produitMatriciel(matriceRotation([0,1,0],PI/2),G[i]));
for(let i=0;i<4;i++)
G.push(produitMatriciel(matriceRotation([0,1,0],PI),G[i]));
for(let i=0;i<4;i++)
G.push(produitMatriciel(matriceRotation([0,0,1],-PI/2),G[i]));
for(let i=0;i<4;i++)
G.push(produitMatriciel(matriceRotation([0,1,0],-PI/2),G[i]));
(Explication : on commence par les quatre rotations d'une face, puis on rajoute les rotations correspondant aux 5 autres faces, en prenant les quatres rotations de la première face et en les multipliant par des rotations d'un quart de tour, ou, pour la face opposée, par une rotation d'un demi-tour. Chacune des six boucles for correspond aux rotations d'une des six faces.)
Ce code utilise une fonction de multiplication de matrices, et une fonction matriceRotation(vecteur,angle)
qui retourne une matrice orthogonale de rotation, dont le code est standard (voir cours de L2 sur les espaces euclidiens). On obtient les 24 matrices de rotations qui sont celles qu el'on doit appliquer à une tuile pour obtenir les 24 autres dans l'espace, de sorte qu'elles forent bien un "cube". Si la première tuile est convenablement courbée dans l'espace, on obtient quelque chose qui ressemble à une sphère, plutôt qu'à un cube.
[1.5,0.1,1]
), et on redessine tous les traits.
Il manque plein de détails mais ces courtes explications devraient rendre la lecture du code assez facile, ou en tout cas faisable.
Le code n'est ni très beau ni très optimisé, et je ne connaissais pas requestAnimationFrame
à l'époque, par exemple, donc tout est fait avec un setTimeout
toutes les 40ms, ce qui est une mauvaise pratique...Il y a plein d'autres défauts (syntaxe javascript pre-ES6 en général) mais il y a peu de chances que tout ceci soit mis à jour.