Pavages de Penrose

De Wiki du LAMA (UMR 5127)
Aller à la navigation Aller à la recherche
exemple de pavage de Penrose

Bien qu'étant utilisés depuis des siècles, ce n'est que tout récemment que l’engouement pour leurs études a eu lieu (durant la seconde moitié du 20eme siècle).


Nous devons ces études à plusieurs grands noms des mathématiques, tels que wang, robinson ou encore Penrose. C'est aux travaux de l'un de ces mathématitiens que nous nous intéresserons dans les lignes qui suivent.



Introduction à la notion de pavage

La notion de pavage et ses propriétés les plus classiques sont fondamentales.

Définition(s) d'un pavage

Un pavage est formé d'un ensemble fini de pièces (compacts) dont l'on peut disposer en quantité infinie, afin de paver un plan selon plusieurs règles :


-Il ne doit pas y avoir d’interstice


-Il ne doit pas y avoir de chevauchement entre les différents compacts


-Le pavage doit paver un plan (donc s'étendre à l'infini)


Mosaique marocaine.jpg


On peut pour cela utiliser une caractérisation un peu plus mathématique. Car, bien que les pavages de penrose soient en 2D, il serait tout à fait possible de réaliser un pavage en 3D.


Cette caractérisation est la suivante :


Soit E un espace topologique. On appelle pavage tout ensemble p de compacts de E tel que


(i) U(p∈P) P = E , (recouvrement de l'espace)


(ii) pour tout p,q∈ P, °p ∩ °q= ∅ (pas de chevauchement)


(iii) pour tout K ⊂ E compact, {p ∈ P | P ∩ K != ∅} est fini (recouvrement local)


Néanmoins cette définition est incomplète puisqu'elle ne précise pas qu'il doit y avoir un nombre fini de type de compact.



Notion de périodicité et d'apériodicité

Un pavage est dit périodique lorsque l'on retrouve un motif avec lequel on peut, par translation ou rotation , paver le plan.


Birds.jpg


Un pavage est dit aperiodique lorsque l'on ne peut pas reconnaitre de motif récurent par translation ou rotation sur l’entièreté du plan.


Robi.gif


On a souvent pensé, comme le montre la conjecture de Wang, qu'avec un jeu de tuile permettant de réaliser un pavage apériodique, on pouvait obligatoirement en réaliser un périodique avec le même ensemble de compacts.


(image tuiles de wang) Or, c'est dans les années 1950 que le pavage de Robinson, composé de 6 tuiles (donc converties en tuiles de wang, 24), fût démontré comme apériodique et permis donc de réfuter cette idée (et la conjecture de wang).


Robinson.png

Les pavages de Penrose

Ces types de pavages sont une découverte de Roger Penrose, et ils ont permis de populariser la notion de pavage et de la rendre "plus grand public"

Roger Penrose

Roger Penrose, né en 1931 en Angleterre est le mathématicien connu pour avoir découvert en 1974 un pavage aperiodique, possédant uniquement 2 types de pièces, nommé par la suite pavage de Penrose.


photo de Roger Penrose

Les principes d'un pavage de Penrose Kite/Darts

Principe

Ce type de pavage de Penrose est composé de deux types de pièces : les kites et les darts .


LEs deux pièces du pavage


Le principe essentiel de ce type de pavage de Penrose étant que les deux pièces ne peuvent être assemblées afin de former un losange.

Motifs remarquables

On peut retrouver plusieurs motifs remarquables dans ce type de pavage de Penrose, bien que n'étant pas réguliers ou prévisibles dans leur apparition (puisque le pavage est apériodique).


motif sun
motif star


Les principes d'un pavage de Penrose avec losanges

Ce type de pavage est composé encore une fois de deux pièces mais ici ces deux pièces sont des losanges comportant des angles différents.

les deux types de losange


Il ne va pourtant dans ce wiki être traité que le cas des kites et des darts, et ce pour une raison simple :

les deux types de pièces sont fortement liées.

En effet, en découpant les losanges, on peut retrouver des kites et des darts, on peut donc plutôt aisément passer d'un pavage à un autre à l'aide d'un simple découpage.


Decoupe losange.png

Les méthodes de construction d'un pavage de Penrose

La méthode dite "intuitive"

Cette méthode consiste à placer les pièces "bloquées" (qui ne peuvent aller que là où on cherche à les mettre), puis continuer, et si blocage il y a on enlève l'étape précédente et on tente l'autre possibilité.


Comme on peut aisément le constater,cette méthode était très complexe et très lourde à coder, bien qu'étant fonctionnelle.


La méthode par inflation/déflation

Principe

La méthode par inflation/déflation est une méthode où l'on partage les différentes pièces afin de reformer de nouvelles pièces, plus petites.


Prog0.png


Prog1.png


Ainsi,si l'on considère le coté d'une pièce comme étant de taille fixe, on peut constater l'augmentation du nombre de pièces qui forment le pavage, et avec un nombre de division qui tend vers l'infini, on peut montrer que le pavage de penrose pave le plan.

Constuction géométrique

Pour appliquer la méthode par inflation déflation, on divise chaque demi-kite en un kite et un demi dart, et chaque demi-dart en un demi-kite et un demi dart.


Avec cette méthode ci, on peut aisément construire un algorithme qui fonctionne de maniere récursive et qui génère un pavage de Penrose.


Réalisation du code

Le programme permet de générer un pavage de penrose, de la profondeur désirée (nombre de récursivité), il indique le nombre de pièces total ainsi que le niveau de récursivité dans lequel on se trouve.


Le code a été réalisé sur processing, un logiciel libre, le programme est codé en java :

// quelques constantes utiles
float PI=3.14159265; // merci archimède !!
float PIsur5=PI/5; // l'angle générateur de base
float epsilon = 1E-5; // defini les points proches

// Les tailles écran
int ecranX=800;
int ecranY=800;
int marge =100;
// Les couleurs
color couleurKite = color(255, 204, 0); // jaune
color couleurDart = color(0, 255, 0); // vert 
color couleurTrait = color(255, 255, 255); // blanc


int profondeur = 10; //profondeur
int attente = 1000;//750;// en ms

// Les variables globalse
int nbForme=0;

// definition des classes graphiques
// ************************************
// un point en memoire

class pointMem
{
  private float x;
  private float y;

  // Le createur
  public pointMem(float Xd, float Yd) 
  {
    x=Xd; // putX(Xd);
    y=Yd; // putY(Yd);
  }
  // Le createur par defaut
  public pointMem() 
  {
    x=0; 
    y=0;
  }

  // Les méthodes de manipulation des attributs
  public float getX() {
    return x;
  }
  public float getY() {
    return y;
  }
  public void putX(float Xp)
  {
    x=Xp;
  }
  public void putY(float Yp)
  {
    y=Yp;
  }
  // manipulations géométriques
  // translation
  public void translate(pointMem P)
  {
    putX(getX()+P.getX());
    putY(getY()+P.getY());
  }
  // rotation autour d'un point P et d'angle A
  public void rotation( pointMem P, float A)
  { // on ramene au centre
    putX(getX()-P.getX());
    putY(getY()-P.getY());
    // on tourne
    float C=cos(A);
    float S=sin(A);
    putX(getX()*C-P.getY()*S);
    putY(getX()*S+P.getY()*C);
    // on remet au bon endroit
    putX(getX()+P.getX());
    putY(getY()+P.getY());
  }
  // Homothetie de rapport k de centre P
  public void homothetie(pointMem P, float K)

  { // on ramene au centre
    putX(getX()-P.getX());
    putY(getY()-P.getY());
    // on amplifie

    putX(getX()*K);
    putY(getX()*K);

    // on remet au bon endroit
    putX(getX()+P.getX());
    putY(getY()+P.getY());
  }

  // deux points proches ??
  boolean egal(pointMem P1, pointMem P2)
  {
    return 
      (abs(P1.getX()-P2.getX())<epsilon) &&
      (abs(P1.getY()-P2.getY())<epsilon);
  }

  // procedures graphiques
  public void draw()
  {
    point(getX(), getY()); // on invoque les procedures de la bibliotheque
  }
  public void draw(color c)
  {
    stroke(c); // bascule la couleur
    draw();
  }
};

// ****************************************************
// exemple de création de points aux coordonnées 0,0
// pour le kite de base
pointMem P1 = new pointMem(0, 0); // reserver un espace memoire de taille pointMem lui affecter le nom de P1 et le remplir de valeurs nulles
pointMem P2 = new pointMem(0, 0);
pointMem P3 = new pointMem(0, 0);
pointMem P4 = new pointMem(0, 0);

// Procedures et fonctions utiles
// Calcul de la norme d'un vercteur
float norme(pointMem P1, pointMem P2) // calcul de la norme du vecteur P1 P2
{
  return sqrt(sq(P1.getX()-P2.getX())+sq(P1.getY()-P2.getY()));
}

// trace une ligne
void ligne(pointMem P1, pointMem P2)
{
  line(P1.getX(), P1.getY(), P2.getX(), P2.getY());
}

// traitement des demi cerf volants

void demiKite(pointMem P1, pointMem P2, pointMem P3, int n)
{
  if (n<=0)
  { // on trace et on "dégage"
    // on colorie
    fill(couleurKite); // couleur remplissage des formes
    stroke(couleurKite);  
    beginShape();
    vertex(P1.getX(), P1.getY());
    vertex(P2.getX(), P2.getY());
    vertex(P3.getX(), P3.getY());
    endShape(CLOSE);
    noFill();
    stroke(couleurTrait);
    ligne (P1, P2);
    ligne (P2, P3);
    nbForme++;
    return;
  }
  // dans le cas contraire, on traite
  // on redecoupe puis on relance !!

  // Creation de points intermediares P4 et P5
  pointMem P4 = new pointMem(0, 0);
  pointMem P5 = new pointMem(0, 0);
  // on les calcul
  // calcul des coté opposé aux points P1 et P2
  float l1=norme(P2, P3);
  float l2=norme(P1, P3);
  // calcul de la nouvelle longueur de decoupage sur le coté P1 P3
  float a= l2/(1+2*cos(PIsur5));

  // vecteur (P1 P4) = a/l1 vecteur (P1 P3) 
  P4.putX(P1.getX()+(P3.getX()-P1.getX())*a/l1);
  P4.putY(P1.getY()+(P3.getY()-P1.getY())*a/l1);

  // vecteur (P3 P5) = a/l2 vecteur (P3 P2) 
  P5.putX(P3.getX()+(P2.getX()-P3.getX())*a/l2);
  P5.putY(P3.getY()+(P2.getY()-P3.getY())*a/l2);

  // On lance la génération suivante suivant le schéma de decoupage
  demiKite(P4, P1, P2, n-1);
  demiKite(P4, P5, P2, n-1);
  demiDart(P3, P4, P5, n-1);
}

// Traitement des demi flèches

void demiDart(pointMem P1, pointMem P2, pointMem P3, int n)
{
  if (n<=0)

  { // on trace et on "dégage"
    fill(couleurDart); // couleur de remplissage
    stroke(couleurDart);

    beginShape();
    vertex(P1.getX(), P1.getY());
    vertex(P2.getX(), P2.getY());
    vertex(P3.getX(), P3.getY());
    endShape(CLOSE);
    stroke(couleurTrait);
    noFill();
    ligne (P1, P2);
    ligne (P2, P3);
    nbForme++;
    return;
  }
  // dans le cas contraire 
  // on redecoupe puis on relance ::
  // Creation du point intermediares P4 
  pointMem P4 = new pointMem(0, 0);
  // on le calcul
  // calcul des cotés opposés au points P2 et P3
  float l2=norme(P1, P3);
  float l3=norme(P1, P2);

  // vecteur (P1 P4) = l2/l3 vecteur (P1 P2) 
  P4.putX(P1.getX()+(P2.getX()-P1.getX())*l2/l3);
  P4.putY(P1.getY()+(P2.getY()-P1.getY())*l2/l3);

  // On lance la génération suivante suivant le schéma de decoupage
  demiKite(P3, P4, P1, n-1);
  demiDart(P2, P3, P4, n-1);
}

// Les initialisations

void setup() 
{
  size(800, 800); // taille de l'écran 640 sur x 480 sur y

  // Creation du premier demiKite
  // a partir des points P1 P3
  P1.putX(ecranX-marge); 
  P1.putY(ecranY/2);

  P3.putX(marge);
  P3.putY(ecranY/2);
  // Calcul du 3ème point P3
  // Calcul de la longueur caractéristique du kite
  float l2=norme(P1, P3);
  // calcul de la nouvelle longueur de decoupage sur le coté P1 P3
  float a= l2/(1+2*cos(PIsur5));
  float h= (a/2)/tan(PIsur5/2);
  pointMem P5= new pointMem(0, 0); // point intermédiaire
  // vecteur (P1 P5) = a/2*l2 vecteur (P3 P2) 
  P5.putX(P1.getX()+(P3.getX()-P1.getX())*a/l2/2);
  P5.putY(P1.getY()+(P3.getY()-P1.getY())*a/l2/2);
  // vecteur P5 P2 perpendiculaire à P1 P2 avec longueur h
  P2.putX(P5.getX()-(P3.getY()-P1.getY())/l2*h);
  P2.putY(P5.getY()+(P3.getX()-P1.getX())/l2*h);
  // Calcul du 4ème point du kite P4 2ème point du demi kite supérieur
  // vecteur P5 P4 perpendiculaire à P1 P2 avec longueur h symétrique par rapport à P2
  P4.putX(P5.getX()+(P3.getY()-P1.getY())/l2*h);
  P4.putY(P5.getY()-(P3.getX()-P1.getX())/l2*h);
  background(0);
  stroke(255);
}

int n=0;
boolean sens = true;

void draw()  // la procedure cyclique
{
  background(0);
  demiKite(P1, P2, P3, n);
  demiKite(P1, P4, P3, n);
  
  // on imprime sur la console
  print("profondeur : "); 
  print(n);
  print(" nb de formes tracées : "); 
  println(nbForme / 2);
  
  // on imprime sur la partie graphique
  fill(255);
  String vals = String.format("%d", n);
  String S1=" profondeur : "+vals;
  vals=String.format("%d", nbForme/2);
  S1=S1+" nb de formes tracées : "+vals;
  text(S1, 10, 15); 
  nbForme=0; // reset du calcul
  
  // evolution
  if (sens) 
  {
    n=n+1;
    if (n>=profondeur) sens=false;
  } else
  {
    n=n-1;
    if (n<=0) sens =true;
  }
  // n=(n+1)%profondeur;

  delay(int(attente/(log(n+2))));
}

La méthode des grilles de Burjin

Cette méthode consiste en le tracé d'une série de droites, dont certaines parallèles. A l'aide des intersections de ces droites on peut retrouver un pavage de Robinson.

illustration grille de Burjin

Approfondissements possibles

Caractérisation mathématique

Il serait interessant de parvenir à compléter la caractérisation mathématique d'un pavage à l'aide d'un langage mathématique, afin d'ajouter la caractéristique du nombre fini de types de compats.

Burjin

Il faudrait pour ce faire :
- Comprendre de manière plus approfondie la méthode des grilles de Burjin.
- Etudier cette méthode d'une manière plus mathématique et plus formelle.
- Réaliser un programme qui génère un pavage de Penrose avec cette méthode.

Bibliographie

Cours de Vincent Pilaud (polytechnique, 2006)
Cours d’Alexandre F Ritter (Oxford masterclasses in geometry, 2014)
Penrose tiles to trapdoor ciphers (Martin Gardner)
Page wikipedia pavages de Penrose (https://fr.wikipedia.org/wiki/Pavage_de_Penrose)
Wiki réalisé par CORDIER PIERRE BES Brunelle, dans le cadre de l'enseignement VISI201 à l'Université Savoie Mont Blanc.
Remerciements à Pierre HYVERNAT.