Les courbes de Rose

Les courbes de Rose

Introduction

Je pourrais commencer en citant le grand Omar Sharif qui disait (je crois) "Les courbes, c'est ma grande passion". En effet, après un article précédent sur les courbes de Lissajous, on continue notre périple dans le monde merveilleux des trucs pas droit avec les courbes de Rose. Dans ce nouveau cas, le déplacement du point de dépend plus d'une double composante sinusoïdale mais suit les propriétés suivantes :

x=cos(kθ) cos(θ)
y=cos(kθ) sin(θ)

Les variations se font donc toujours en fonction d'un angle mais aussi d'un facteur k. Pour représenter en deux dimensions l'impact de ce facteur nous allons créer différentes valeurs en écrivant k=n/d et nous ferons varier les valeurs respectives de n et d entre 1 et 6. (Évidemment tout ceci est une fois de plus paramétrable). En premier lieu voici le résultat que l'on obtient.

Le résultat


Le code

On commence avec la fonction qui permettra de placer le point à chaque itération.
coord=function(x,y,theta,k=1,r=.4){
  return(c(x+r*cos(k*theta)*cos(theta),y+r*cos(k*theta)*sin(theta))) 
}
On poursuit en initialisant les valeur de N et D ainsi que les différentes valeurs de thêta et les arrays qui nous permettront de stocker a chaque itération la position des points.
S=seq(0,12*pi,by=pi/30)
N=D=1:6
COL=colorRampPalette(c("lightblue","orange"))(max(c(length(N),length(D))))
XX=YY=array(0,dim=c(length(N),length(D),length(S)))

Enfin, la partie centrale du code (qui ressemble fortement au code pour les courbes de Lissajous). Il y a principalement des différences d'ordre esthétique puisque le plus gros des différences de résultats vient de la fonction de calcul des coordonnées des points.
for(theta in S){
  n=match(theta,S)
  # Plot
  par(mar=c(0,0,0,0), bg="grey40");
  plot(0, type="n", xlim=c(.5,length(N)+.5), ylim=c(.5,length(D)+.5))
  for(i in 1:length(N)){
 for(j in 1:length(D)){
   # On calcule la position du point i j pour l'iteration s et on stocke
   yy = coord(i,length(D)-j+1,theta,k=N[i]/D[j])[2]
   xx = coord(i,length(D)-j+1,theta,k=N[i]/D[j])[1]
   XX[i,j,n]=xx
   YY[i,j,n]=yy
   # On affiche les points deja traces
   lines(XX[i,j,1:n],YY[i,j,1:n], col= meancol(COL[i],COL[j]), lwd=3)
          # On plot les elts
   points(x=XX[i,j,n],y=YY[i,j,n],col="white", pch=16, cex=2)
 }
  }
mtext(text=D,side=2,at=rev(1:length(D)), col="white", line=-1, font=2, las=1)
mtext(text=N,side=3,at=1:length(N), col="white", line=-1, font=2, las=1)
text(.5,length(D)+.5,labels="d/n", font=2, col="white")
}





Les courbes de Lissajous

Les courbes de Lissajous

Présentation

J'avoue que là je me suis honteusement inspiré d'un gif trouvé sur internet mais je le trouvais tellement beau que je me suis dit qu'il fallait absolument que je le refasse. (Attention, ce gif va consommer tout votre data)



Je ne connaissais pas ces éléments mais il s'agit apparemment de courbes de Lissajous. Une construction mathématique se basant sur la trajectoire d'un point dont les composantes rectangulaires ont un mouvement sinusoïdal.

Le code

Comme je suis sympa (et que j'ai enfin trouvé comment intégrer du code dans le blog) je vais vous partager ma création/copie, réalisée en R. C'est finalement assez simple puisque chacune des trajectoires ne dépend que du mouvement indiqué par la position des points sur les cercles extérieurs. Il suffit alors de coder la trajectoire de manière générique pour ne pas avoir à faire 49 morceaux de codes différents. On commence par deux petites fonctions, la première qui permet de créer un dégradé dans les couleurs (en bricolant le rgb) et la seconde qui permet de calculer les coordonnées d'un point en fonction d'un angle.
meancol=function(col1,col2){
  col1=gsub("#","",col1)
  col2=gsub("#","",col2)
  res=c()
  for(i in seq(1,nchar(col1)-1,by=2)){
    c1=substring(col1,i,i+1)
    c2=substring(col2,i,i+1)
    s = round( (strtoi(sprintf("0x%s",tolower(c1))) + strtoi(sprintf("0x%s",tolower(c2))))/2)
    res=c(res,toupper(as.hexmode(s)))
  }
  res[nchar(res)==1]=sprintf("%s0",res[nchar(res)==1])
  return(sprintf("#%s",paste(res,sep="",collapse="")))
}

coord=function(x,y,s,k=1,r=.4){  return(c(x+r*cos(s/k),y+r*sin(s/k))) }

Et on enchaine avec le code de la fonction principale, la première étape consiste à initialiser les objets qui nous seront utiles par la suite. On crée les points à parcourir (de 0 à 14 pi par pas de pi/8). C'est cet angle qui décidera de la position de chaque point et donc de l'apparence de chaque image. Ensuite, on crée les 7 vitesses de parcours de cercles (Par défaut de 1 à 7 mais c'est paramétrable). Dans notre cas, quand le point dans le premier cercle fait un tour complet, le point du deuxième cercle en fait deux, celui du troisième cercle en fait trois, etc...

S=seq(0,14*pi,by=pi/8)
X=Y=7/(1:7)
COL=colorRampPalette(c("lightblue","orange"))(length(X));
XX=YY=array(0,dim=c(length(X),length(Y),length(S)));  


Et le gros du code, une boucle qui va remplir les 8 x 8 éléments de notre dessin. Si on est sur la première colonne ou la première ligne, on fait juste un cercle et on place un point en fonction de la valeur de l'angle de l’itération en cours.
Pour les autres éléments (qui ne sont pas forcement des cercles) on trace le parcours du point (que l'on stocke à chaque itération dans les arrays à 3 dimensions XX et YY pour pouvoir l'afficher à l’étape suivante)

for(s in S){
  k=match(s,S)
  # Plot
  par(mar=c(0,0,0,0), bg="grey40");
  plot(0, type="n", xlim=c(-.5,length(X)+.5), ylim=c(-.5,length(Y)+.5))
 
  # Iterations
  for(i in 0:length(X)){
    for(j in 0:length(Y)){
      if(i*j==0){
        # Cercle
        if(i+j>0){
          points(x=i,y=length(Y)-j, cex=14, col=COL[i+j], lwd=4)
          if(i>0) cc=coord(i,length(Y)-j,s,X[i]) else cc=coord(i,length(Y)-j,s,Y[j])
          points(x=cc[1],y=cc[2],  col="white", pch=16, cex=2)
        }
    
      }else{        
        # On calcule la position du point i j pour l'iteration s et on stocke
          yy = coord(i,length(Y)-j,s,Y[j])[2]
          xx = coord(i,length(Y)-j,s,X[i])[1]
          XX[i,j,k]=xx
          YY[i,j,k]=yy
        # On affiche les points deja traces
          lines(XX[i,j,1:k],YY[i,j,1:k], col= meancol(COL[i],COL[j]), lwd=4)
         # On plot les elts
          points(x=XX[i,j,k],y=YY[i,j,k],col="white", pch=16, cex=2)
      }
       
    }
  # On fait les abline
 for(n in 1:length(X)){
  abline(v=XX[n,1,k], lty=2 ,col="grey60")
  abline(h=YY[1,n,k], lty=2 ,col="grey60")
 }
  }
  # Sys.sleep(2)
}

J'avoue par contre que le code n'est pas nickel, un des souci est le tracé du cercle dans la première ligne et la première colonne. J'ai utilisé la fonction points avec pch=16 mais ce n'est pas la bonne méthode car la taille du cercle n'est pas définie de manière dynamique (normalement on souhaite qu'il fasse un rayon de 1)
Cette fonction va donc créer un graphique pour chaque itération, libre à vous ensuite de stocker ces résultats (dans un gif par exemple). Dans l'idéal il faut avancer par pas assez petit (pi/8 c'est le minimum, on constate d'ailleurs que le cercle en bas à droite n'est d'ailleurs pas très circulaire). Il semble aussi nécessaire d'avoir un parcours allant de 0 à 14 pi pour pouvoir observer les différents parcours dans leur intégralité. On a donc au moins 104 graphiques à réaliser.

Carte animée des autoroutes françaises


Carte des autoroutes


Un petite carte animée des différentes autoroutes françaises (uniquement les tronçons supérieurs à 50 km) et représentées dans l'ordre de leur numérotation. J'aurais aimé représenter les tracés en fonction de la chronologie, mais ça devient rapidement compliqué puisque certaines autoroutes ont été ouvertes à différentes dates selon les tronçons. Il est a noter que certaines autoroute n'existent pas (la A17 ou la A18 et pleins d'autres encore. La raison principale venant de projets qui furent abandonnés ou alors fusionnés. De même les autoroutes avec des chiffres élevés sont souvent des connections avec des autoroutes déjà existantes. Par exemple la A630 est connectée à la A63.

On obtient alors le résultat suivant :




 Et voici la version statique :

 Et enfin la version complète avec tous les tronçons (c'est un peu plus long)

Et pour finir la version statique avec tous les tronçons