Kaleidoscope I

Le kaléidoscope est un instrument optique réfléchissant à l’infini et en couleurs la lumière extérieure. Le nom de ce jouet vient du grec kalos, « beau », eidos « image », et skopein « regarder ».  (wikipedia)

 

On va ici utiliser Processing pour générer ca :

 

Vous pouvez retrouver l’ensemble du code sur le gitHub generartiv : https://github.com/generartiv/kaleidoscope1

Pour ce faire on va avoir besoin de plusieurs éléments qu’on va declarer avant d’arriver au Setup :

 

Les déclarations globales

PImage imageDepart;

PShape triangle;
PShape triangleMiroir;

float tailleEcran;
float tailleTexture;
float xCentreSymetrieEcran;
float yCentreSymetrieEcran;
float xCentreSymetrie = 0;
float yCentreSymetrie = 0;

float angleDepart = 0;
float ajoutAngleTexture = 0;

Tout d’abord une image de départ, c’est des parties de celle-ci qu’on va dupliquer sur l’ensemble de la création. On chargera son contenu dans le Setup.

Les deux PShape « triangle » et « triangleMiroir » contiendront l’image copiée. Pour finir avec une image sans rupture,  la création sera entièrement pavée d’hexagones, chacun des hexagones étant formé de six triangles isocèles, formé en fait de deux triangles rectangles, tantôt texturé avec un bout de l’image d’origine, tantôt avec un bout de l’image d’origine en miroir. Ainsi les trois cotés de chaque triangle isocèle coïncideront avec les même pixels.

 

TailleEcran et tailleTexture sont respectivement la taille du coté du triangle et la taille de la copie de l’original. On aura aussi besoin de la position où on affichera la point du triangle / le centre de l’hexagone, et a quelle position sur l’image de départ celui ci correspond.

« angleDepart » correspond a l’angle d’affichage du triangle. Pour le rendu que vous voyez sur la video, il ne sert a rien. Il est toutefois possible de faire faire des rotations aux hexagones en incrementant celui-ci.

« ajoutAngleTexture  » c’est l’angle qui sert a calculer la position de la texture qu’on va récupérer. C’est lui qu’on va incrémenter pour l’effet de pivotement de l’image dans chaque triangle.

 

Le Setup

void setup() {
  size(1280, 720, P2D);
  imageDepart = loadImage("data/image.jpg");
}

Pas grand chose de particulier ici… Le size (en utilisant le rendrer P2D pour avoir la possibilié de texturer les triangles) et on charge l’image de départ….

Draw



void draw() {

  tailleEcran = width/13;
  tailleTexture =  cos(millis() * 0.0001389)*61 + 142;


  background(0);
 

On aurait pu donner la taille du triangle affiché au moment de sa definition, mais ce faisant on aurait perdu la possibilité d’utiliser la fonction « tweak » de processing… dommage. Avec ce reglage (width/13) on obtient 13 hexagones pour recouvrir l’ecran.

On calcule la taille de la texture :

cos() renvoi un float de -1 a 1, millis() le nombre de millisecondes depuis le lancement du script. En multipliant millis() par un nombre <1 on réduit la vitesse de grossissement / rétractation de la texture, pour avoir une taille appropriée, on multplie le cos() et on ajoute 142 pixels…

ajoutAngleTexture +=0.00303;
  if (ajoutAngleTexture>TWO_PI) {
    ajoutAngleTexture-=TWO_PI;
  }

xCentreSymetrie = cos(millis() * 0.000015) * imageDepart.width/4 + imageDepart.width/2;
yCentreSymetrie = sin(millis() * 0.000013) * imageDepart.height/4 + imageDepart.height/2;

on incremente l’angle a partir duquel on va recuperer la texture. Si on a fait un tour, on peux retirer un tour (TWO_PI).

on calcule la position du sommet du triangle de la texture… Avec le même principe du cos() et millis() on va se deplacer en cercle sur l’image de depart.

int nbRepet = 3;

  float angleRepet = TWO_PI / nbRepet;

  float angleFin = angleDepart + (angleRepet/2);
  float xPoint1 = cos(angleDepart)*tailleEcran;
  float yPoint1 = sin(angleDepart)*tailleEcran;
  float xPoint2 = cos(angleFin)*tailleEcran;
  float yPoint2 = sin(angleFin)*tailleEcran;

  float xPoint1Texture = cos(angleDepart + ajoutAngleTexture)*tailleTexture + xCentreSymetrie;
  float yPoint1Texture = sin(angleDepart + ajoutAngleTexture)*tailleTexture + yCentreSymetrie;
  float xPoint2Texture = cos(angleFin + ajoutAngleTexture)*tailleTexture + xCentreSymetrie;
  float yPoint2Texture = sin(angleFin + ajoutAngleTexture)*tailleTexture + yCentreSymetrie;

Ici commence les calculs un peu chauds… Pour ceux qui n’ont pas retenu leurs cours de trigonométrie du lycée, ça risque de faire un peu mal a la tête…

On va avoir trois répétitions de chaque triangle (+ 3 répétions des miroirs bien sur…) pour calculer la position des points du triangle a recopier, on va se servir des sinus et cosinus: Le cosinus de l’angle multiplié par la distance nous donne l’abscisse du point, le sinus l’ordonnée. On y ajoute les coordonnées du centre de symetrie et le tour est joué!

 

triangle = createShape();
  triangle.beginShape();
  triangle.noStroke();
  triangle.noFill();
  triangle.texture(imageDepart);
  triangle.vertex(0, 0, xCentreSymetrie, yCentreSymetrie);
  triangle.vertex(xPoint1, yPoint1, xPoint1Texture, yPoint1Texture);
  triangle.vertex(xPoint2, yPoint2, xPoint2Texture, yPoint2Texture);
  triangle.endShape(CLOSE);

  triangleMiroir = createShape();
  triangleMiroir.beginShape();
  triangleMiroir.noStroke();
  triangleMiroir.noFill();
  triangleMiroir.texture(imageDepart);
  triangleMiroir.vertex(0, 0, xCentreSymetrie, yCentreSymetrie);
  triangleMiroir.vertex(xPoint1, yPoint1, xPoint2Texture, yPoint2Texture);
  triangleMiroir.vertex(xPoint2, yPoint2, xPoint1Texture, yPoint1Texture  );
  triangleMiroir.endShape(CLOSE);

A ce stade, on cré les triangles qu’on texture avec l’image d’origine, aux coordonnées que l’on vient de calculer:

beginShape() commence la création,

texture() indique qu’on veut texturer notre forme avec l’image,

vertex() cré un point, c’est aussi là qu’on indique la position correspondante de la texture.

endShape cloture la création de la forme.

float rang = 0;
  for (float yCentreSymetrieEcran=0; yCentreSymetrieEcran<height + tailleEcran*2; yCentreSymetrieEcran+=tailleEcran * 0.8659) {
    rang++;
    float xDepart = 0;
    if (rang%2==0) {
      xDepart = tailleEcran*1.5;
    }
    for (float xCentreSymetrieEcran=0; xCentreSymetrieEcran<width+tailleEcran*2; xCentreSymetrieEcran+=tailleEcran*3) {



      pushMatrix();
      translate(xCentreSymetrieEcran, yCentreSymetrieEcran);
      translate(xDepart, 0);
      noFill();
      noStroke();

      for (int n=0; n<nbRepet; n++) {
        shape(triangle, 0, 0);
        rotate(angleRepet/2);
        shape(triangleMiroir, 0, 0);
        rotate(angleRepet/2);
      }
      popMatrix();
    }
  }

Il ne nous reste plus qu’a afficher les hexagones:

Pour pouvoir les afficher en quinconce, on va incrementer « rang », si on est sur une ligne paire ( rang modulo 2 = 0), alors on va decaler l’affichage vers la droite…

 

pour chaque ligne, chaque colonne, on affiche un hexagone:

pushMatrix() nous permetra de retrouver notre position après l’affichage,

on translate() a la position,

pour chaque repetion du triangle,

shape() affiche le triangle,

on pivote de la motié de l’angle de repetition,

on affiche le triangle miroir

on pivote a nouveau…

quand on a finit l’hexagone, popMatrix nous ramene a notre point d’origine…

Et le tour est joué!!

Le code complet:

PImage imageDepart;

PShape triangle;
PShape triangleMiroir;

float tailleEcran;
float tailleTexture;
float xCentreSymetrieEcran;
float yCentreSymetrieEcran;
float xCentreSymetrie = 0;
float yCentreSymetrie = 0;

float angleDepart = 0;
float ajoutAngleTexture = 0;



void setup() {
  size(1280, 720, P2D);
  imageDepart = loadImage("data/image.jpg");
}


void draw() {

   tailleEcran = width/13;
   tailleTexture =  cos(millis() * 0.0001389)*61 + 142;



  background(0);
  ajoutAngleTexture +=0.00303;
  if (ajoutAngleTexture>TWO_PI) {
    ajoutAngleTexture-=TWO_PI;
  }

  xCentreSymetrie = cos(millis() * 0.000015) * imageDepart.width/4 + imageDepart.width/2;
  yCentreSymetrie = sin(millis() * 0.000013) * imageDepart.height/4 + imageDepart.height/2;

  int nbRepet = 3;

  float angleRepet = TWO_PI / nbRepet;

  float angleFin = angleDepart + (angleRepet/2);
  float xPoint1 = cos(angleDepart)*tailleEcran;
  float yPoint1 = sin(angleDepart)*tailleEcran;
  float xPoint2 = cos(angleFin)*tailleEcran;
  float yPoint2 = sin(angleFin)*tailleEcran;

  float xPoint1Texture = cos(angleDepart + ajoutAngleTexture)*tailleTexture + xCentreSymetrie;
  float yPoint1Texture = sin(angleDepart + ajoutAngleTexture)*tailleTexture + yCentreSymetrie;
  float xPoint2Texture = cos(angleFin + ajoutAngleTexture)*tailleTexture + xCentreSymetrie;
  float yPoint2Texture = sin(angleFin + ajoutAngleTexture)*tailleTexture + yCentreSymetrie;


  triangle = createShape();
  triangle.beginShape();
  triangle.noStroke();
  triangle.noFill();
  triangle.texture(imageDepart);
  triangle.vertex(0, 0, xCentreSymetrie, yCentreSymetrie);
  triangle.vertex(xPoint1, yPoint1, xPoint1Texture, yPoint1Texture);
  triangle.vertex(xPoint2, yPoint2, xPoint2Texture, yPoint2Texture);
  triangle.endShape(CLOSE);

  triangleMiroir = createShape();
  triangleMiroir.beginShape();
  triangleMiroir.noStroke();
  triangleMiroir.noFill();
  triangleMiroir.texture(imageDepart);
  triangleMiroir.vertex(0, 0, xCentreSymetrie, yCentreSymetrie);
  triangleMiroir.vertex(xPoint1, yPoint1, xPoint2Texture, yPoint2Texture);
  triangleMiroir.vertex(xPoint2, yPoint2, xPoint1Texture, yPoint1Texture  );
  triangleMiroir.endShape(CLOSE);

  float rang = 0;
  for (float yCentreSymetrieEcran=0; yCentreSymetrieEcran<height + tailleEcran*2; yCentreSymetrieEcran+=tailleEcran * 0.8659) {
    rang++;
    float xDepart = 0;
    if (rang%2==0) {
      xDepart = tailleEcran*1.5;
    }
    for (float xCentreSymetrieEcran=0; xCentreSymetrieEcran<width+tailleEcran*2; xCentreSymetrieEcran+=tailleEcran*3) {



      pushMatrix();
      translate(xCentreSymetrieEcran, yCentreSymetrieEcran);
      translate(xDepart, 0);
      noFill();
      noStroke();

      for (int n=0; n<nbRepet; n++) {
        shape(triangle, 0, 0);
        rotate(angleRepet/2);
        shape(triangleMiroir, 0, 0);
        rotate(angleRepet/2);
      }
      popMatrix();
    }
  }
  
  //saveFrame("output/frame-#####.jpg");
}