Le Brazuca, le ballon de la coupe du monde 2014, n'est pas un icosaèdre tronqué, c'est un recollement de surfaces réglées.

La source est un billet d'Etienne Ghys sur le site image des maths.

Making-of

Contexte

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.

Construction des «faces»

D'après l'article d'Étienne Ghys, le ballon est obtenu en recollant six «faces», comme un cube. La différence est que ces faces ne sont pas de carrés, mais des formes un peu plus complexes. Les six faces sont toutes identiques, les voici (illustration tirée de l'article) :

Il fallait donc trouver une façon de dessiner ces formes avec javascript.

Première utilisation des symétries

Noter que les faces ont une symétrie de rotation d'ordre quatre : il n'est donc nécessaire que de savoir dessiner une «ailette», (dans la suite, on eappellera ça une «tuile», et de dessiner les trois autres en appliquant des rotations successives d'un quart de tour.

Fonctions pour la forme des tuiles

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
(Les plus attentifs auront remarqué qu'il y a une différence, et que donc la face n'est pas exactement la même que le modèle original : c'est son symétrique. Ceci est dû au fait que les canvas html ont un système de coordonnées où les y vont «vers le bas»...)

Placement de toutes les tuiles/faces

Il suffit maintenant de savoir dessiner une tuile en trois dimensions, en la "pliant", puis de dessiner les 24 tuiles nécessaires, en prenant la première tuile et en appliquant chacune des 24 matrices de rotation suivantes:

// 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.

Animation

Le ballon est animé en tournant sur lui-même. À chaque nouvelle image, on applique à tous les points une rotation d'un centième de radian par rapport à un certain axe (ici [1.5,0.1,1]), et on redessine tous les traits.

Conclusion

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.