Archives par mot-clé : fleur

Retour sur les bases de trigonométrie

Il n’est pas rare de voir les étudiants l’air dépités à l’idée de devoir faire de la trigonométrie, et pourtant celle-ci revient très souvent sur le devant de la scène lorsque l’on commence à vouloir faire des attracteurs ou des mouvements circulaires.

La trigonométrie et ses fonctions sont des éléments indispensables quand on commence à vouloir faire des visuels plus évolués. Même si son nom parait horrible et semble nous renvoyer à nos cours de maths de 3ème nous verrons que processing va vite nous la faire re-aimer.

Ici nous nous attarderons sur le cercle trigonométrique qui nous permettra de voir comment on effectue le calcul d’un angle ou même comment positionner un point sur un cercle. En bref nous verrons nos vieux amis des cours de maths que sont sinus, cosinus et tangente.

Nous appliquerons cela à deux skecth. Le premier nous permettra de réaliser un attracteur/repulseur simple. Le seconde, plus appliquer au design génératif nous permettra de découvrir comment réaliser des visuel simple à l’aide de la trigonométrie.

Petit rappel de trigonométrie

La trigonométrie traite des relations entre distances et angles dans les triangles et notamment dans le triangle rectangle. C’est ce dernier qui va nous intéresser plus particulièrement.

Faisons un rapide bon dans le passé concernant ce triangle. Un triangle rectangle se caractérise par un angle droit (90°) et la somme de ses angles est égale à 180°. L’angle droit est donc son angle le plus grand. Enfin le côté opposé à cet angle s’appel l’hypoténuse et se caractérise par le fait qu’il est le côté le plus grand de ce triangle.

Capture d’écran 2013-03-17 à 18.24.47

Si on s’interesse à l’angle BAC et aux fonctions trigonométriques alors nous remarquons que :

  • sin(BAC) = a/c
  • cos(BAC) = b/c
  • tan(BAC) = a/b

Nous avons ici les fonctions auxquelles nous allons prêter attention dans processing.

Allons maintenant plus loin en traçant un cercle dont l’origine sera A et de rayon AB (notre hypothenuse). Partons du principe que notre hypothenuse AB a un valeur de 1. Nous obtenons alors un cercle trigonométrique.

Capture d’écran 2013-03-17 à 18.31.18

Si on observe ce cercle nous remarquons que les cordonnées du point B peuvent être définies de la sorte

  • x = cos(t)
  • y = sin(t)

Or nous savons ici que notre hypothenuse à un valeur de 1. Si nous voulons être correcte dans nos cordonnées, nous obtenons

  • x = cos(t)*1
  • y = sin(t)*1

Nous venons de voir la formule permettant de calculer les coordonnées d’un point sur un cercle. Nous avons ici l’une des formules que nous utiliserons le plus dans processing par la suite.

  • x = cos(angle)*rayon
  • y = sin(angle)*rayon

Nous venons de voir à quoi les fonctions trigonométriques Sinus et Cosinus pouvaient nous être utile, attardons nous maintenant sur la dernière, la Tangente.

La tangente est le rapport du sinus au cosinus, par définition :

  • tan(angle) = sin(angle)/cos(angle)

On appelle tangente de l’angle aigu , le nombre noté tan(angle) défini par BC/AC. Nous verrons par la suite comment nous pouvons nous servir de la tangente pour calculer un angle.

Capture d’écran 2013-03-17 à 18.24.47

Trigonométrie et processing

Nous venons de voir un résumer des règles de trigonométrie dont nous allons nous servir le plus. Retournons maintenant sur processing pour faire un rapide état des lieux des méthodes disponible. Si nous venons de revoir toute ces bases c’est bien parce que nous allons utiliser les angles afin de positionner des points sur un cercle. Là dessus processing est très bien fait puisqu’il va nous proposer différentes fonctions trigonométriques tel que :

float s = sin(a); //sinus d'un angle
float c = cos(a); //cosinus d'un angle
float t = tan(a); //tangente d'un angle

float as = asin(a); //inverse du sinus
float ac = acos(a); //inverse du cosinus
float at = atan(a); //inverse de la tangente

Une des particularités à savoir sur processing et qu’il effectue ses calculs d’angle en radians là où nous avons plutôt tendance utiliser des dégrés. Le radians est l’unité de mesure internationale d’un angle. Ainsi, si en degrés nous divisons un cercle en 360° en radians celui-ci se divise entre 0 et 2PI

cercle_trigonom_trique

Lorsque nous travaillons en degrés on pourrait se dire qu’il va nous falloir entrer dans des calculs fastidieux mais là aussi processing nous offre une fonction permettant de convertir un angle de degrés en radians

float a = radians(valeur en degré);

Nous avons maintenant tout ce qu’il nous faut pour tester la trigonométrie dans processing.

Attracteur et répulseur simple

Pour voir à quoi peut nous servir la trigonométrie prenons un exemple d’un projet d’un étudiant de 4ème année.
Ce dernier réalisait une installation interactive avec la wiimote. Cette dernière lui servait de pointeur et il souhaitait que régulièrement, les lettres présentent sur le sketch à des positions aléatoires, attaquent le pointeur. Essayons de reproduire cela le plus simplement possible.

Nous allons produire un sketch de 500*500 sur lequel nous placerons des balles à des positions aléatoire. Celles-ci attaqueront notre souris. C’est à dire qu’elles se déplaceront en direction de notre souris et une fois leurs déplacements effectués, retournerons à leurs places avant d’effectuer une autre attaque.

Cela peut sembler compliqué. Comment faire en sorte de rapprocher nos balles vers notre souris sachant que certaines seront plus haute, plus basse, plus à droite ou plus à gauche. Réduisons notre concept à une balle on se rend tout de suite compte qu’il nous suffit d’utiliser la trigonométrie. Nous avons une balle et une cible. Nous connaissons la distance entre ces deux élément et nous savons que la position d’un point sur un cercle est égale à :

  • x = cos(angle)*rayon
  • y = sin(angle)*rayon

Il nous suffit alors de calculer la position de notre balle sur un cercle dont le rayon est égale à sa distance la séparant de sa cible. Nous n’auront plus qu’à incrémenter ou décrémenter notre rayon pour attirer ou repousser notre balle

trigo2

Reproduisons cela à partir d’une balle que nous placerons de maniere aléatoire, nous définirons la position de l’attracteur au centre de notre skecth. La première chose que nous devons faire c’est de replacer cette balle sur un cercle dont le rayons sera égale à la distance entre la balle et sa cible. Pour cela nous allons utiliser la fonction dist() qui nous permettra d’obtenir le rayon de notre cercle. C’est ce rayon que nous incrémenterons puis decrémenterons pour attirer ou repousser notre balle.

d = dist(balle_x, balle_y, cible_x, cible_y);

Nous savons que les coordonnées x, y d’un point sur un cercle sont respectivement égales à cos(angle)*rayon et sin(angle)*rayon. Nous avons déjà le rayon, il ne nous manque donc plus que l’angle.

Si on regarde de plus pres notre schema, nous avons notre balle sur un cercle de centre cible mais nous avons aussi un triangle rectangle dont nous connaissons déjà l’hypothenuse c’est à dire notre rayon.

gabarit_blogeart_3

Il nous est alors facile d’obtenir les autres valeurs puisque nous avons les cordonnées de notre balle et de sa cible. Ainsi :

  • a = balle_x – cible_x
  • b = balle_y – cible_y

Nous savons aussi que la tangente d’un angle est égale au côté opposé / côté adjacent ou dans notre cas à b/a. Pour obtenir notre angle il nous faudra alors obtenir l’inverse de cette tangente. C’est là l’utilité de la fonction atan() de processing qui nous reverra l’inverse de la tangente et donc notre angle en radians

angle = atan2(balle_y - cible_y, balle_x - cible_x);

Nous pouvons donc définir les coordonnées de notre balle sur le cercle comme ceci

  • x = cos(angle)*rayon
  • y = sin(angle)*rayon

Il ne nous reste plus qu’à décrémenter puis incrémenter notre rayons. Nous obtenons alors ceci
(cliquez sur le skecth pour activer l’attracteur ou sur ‘a’ pour repositioner la balle)

//Cliquez pour lancer l'animation
float posX, posY, oX, oY, d, angle, r, v;
boolean etat = true;

void setup()
{
  size(500, 500);
  update();
}

void draw()
{
  background(255);
  //calcul de l'angle
  angle = atan2(posY - oY, posX - oX);
  //deplacement de la balle
  if (etat == true)
  {
    r=d; //si nous n'activons pas l'attracteur le rayon est égale à la distance entre nos éléments
  }
  if (etat == false)
  {
    r+=v; //sinon notre rayon s'incrémente de la vitesse (-1 donc négative)
  }

  if ( r<1 || r>d)
  {
    v = -v;//si nous atteignons les limite rayon ou 0 notre vitesse s'inverse. Nous attirons puis repoussons notre balle
  }

  //position de la balle
  posX = oX+cos(angle)*r;
  posY = oY+sin(angle)*r;

  //balle
  stroke(0);
  strokeWeight(5);
  point(posX, posY);
  strokeWeight(1); 
  stroke(255, 0, 0);
  line(posX, posY, oX, oY);
  ellipse(posX, posY, 15, 15);

  //cercle trigonométrique
  stroke(0);
  point(oY, oY);
  strokeWeight(1);
  noFill();
  ellipse(oX, oY, d*2, d*2);
  fill(255);
  ellipse(oX, oY, 10, 10);
}

void keyPressed()
{
  if (key == 'a')
  {
    update();
  }
}

void mousePressed()
{
  etat = !etat;  
}

//fonction initialisant notre balle
void update()
{
  oX = width/2;
  oY = width/2;
  posX = random(width);
  posY = random(height);
  d = dist(posX, posY, oX, oY);
  v = -1;
}

ixd_trigo_00

Nous avons une balle attirée par notre cible mais celle-ci est fixe et nous n’avons qu’une balle. Comment appliquer cela à plusieurs balles?

Pour cela rien de plus simple, il nous suffit d’utiliser les listes et de replacer chacune de nos balles sur un cercle dont le rayon sera la distance entre la balle et sa cible. Il nous faut utiliser les notions vu dans le cours précédent à savoir les liste et les boucle for (disponible ici).

Nous obtenons alors ceci (cliquez pour activer l’attracteur, appuyez sur ‘a’ pour repositioner les balles et sur ‘z’ pour retirer les tracés)

//Cliquez pour lancer l'animation

float oX;
float oY;
float nbParticles;
float[] d;
float[] angle;
float[] posX;
float[] posY;
float[] posX_2;
float[] posY_2;
float[] v;
float[] r;

boolean etat = true;
boolean dessin = true;

void setup()
{
  size(500, 500);
  //frameRate(2); 
  update();
}

void draw()
{

  background(255);
  oX = mouseX;
  oY = mouseY;
  for (int i = 0; i<nbParticles; i++)
  {
    angle[i] = atan2(posY[i] - oY, posX[i]  - oX);
    d[i] = dist(posX[i], posY[i], oX, oY);
    //deplacement de la lettre
    if (etat == true)
    {
      r[i]=d[i];
    }
    if (etat == false)
    {
      r[i]+=v[i];
    }

    if ( r[i]<1)
    {
      v[i] = -v[i];
    }
    if ( r[i]>d[i])
    {
      v[i]= -v[i];
      r[i]=d[i];
    }

    //position de la lettre 
    posX_2[i] = oX+cos(angle[i])*r[i];
    posY_2[i] = oY+sin(angle[i])*r[i];

    if(dessin == true)
    {
    float a = map(d[i], 0, width, 0, 50);  
     stroke(0, a);
     ellipse(oX, oY, d[i]*2, d[i]*2);
     //dessin de l'origine de point
    stroke(0);
    strokeWeight(5);
    point(posX[i], posY[i] );
    strokeWeight(1);
    line(posX[i], posY[i], oX, oY);
    stroke(255, 0, 0);
    strokeWeight(1);
    line(posX_2[i], posY_2[i], oX, oY);
    }

    //dessin de lettre
    stroke(255, 0, 0);
    strokeWeight(1);
    ellipse(posX_2[i], posY_2[i], 15, 15);

  }

  //ellipse 
  strokeWeight(1);
  stroke(255, 100);
  point(oY, oY);
  noFill();

}

void keyPressed()
{
  if (key == 'a')
  {
    update();
    etat = true;
  }
  if(key == 'z')
  {
    dessin = !dessin;
  }
}

void mousePressed()
{
  etat = !etat;

}

void update()
{

  nbParticles = 20;
  oX = width/2;
  oY = height/2;
  posX = new float[20];
  posY = new float[20];
  d = new float[20];
  v = new float[20];
  angle = new float[20];
  r = new float[20];
  posX_2 = new float[20];
  posY_2 = new float[20];
  for (int i = 0; i<nbParticles; i++)
  {
    posX[i] = random(width);
    posY[i] = random(height);
    d[i] = dist(posX[i], posY[i], oX, oY);
    v[i] = random(-4, -0.5);
  }
}

ixd_trigo_01

Design génératif et trigonométrie

Nous avons vu comment faire un attracteur/repulseur simple mais allons un peu plus loin dans l’utilisation de ces méthodes et appliquons cela à du design génératif. Il nous est facile à partir de là d’imaginer un visuel génératif type fleur.

Reprenons nos balles. Définissons leur position de départ sur un cercle de rayon 200 et toute équidistantes les une des autres.
Nous gardons notre principe d’attracteur et définissons une vitesse aléatoire pour chacune de nos balles. Au fur et à mesure celle-ci se déplaceront vers le centre de notre skecth. À chaque fois que l’une d’entre elles atteindra le centre nous clôturons le cycle et en déclencherons un nouveau. À chaque début de cycle nous définirons une plage de couleur pour notre fleur générative. Ajoutons un peu de piquant à notre sketch et définissant à chaque debut de cycle un sens de rotation pour notre fleur. Enfin définissons pour chaque cycle un temps de vie de 1000 frame avant de se re-initialiser

trigo3

Globalement nous allons utiliser le même code que notre attracteur à plusieurs balles mais nous ne dessinerons pas la même chose. Commençons par définir une fonction update() dans laquelle nous initialiserons toute nos valeurs.

float oX, oY, d; //cible et distance de la cible
float[] posX; //position des partciules
float[] posY; //position des partciules
color[] c; //couleurs des particules
float[] r; //rayon des particules sur le cercle trigonométrique
float[] angle; //angle des particules sur le cercle trigonométrique
float[] vr; //vitesse des particules
float colorRatio; //plage de couleur

boolean etat = false;//animation on/off
int nbParticules;//notre de point

float angleGlobal; //angle de rotation de notre skecth
float vAngle; //sens de la rotation

float count; //notre décompte. Toute les 500 frame nous effacerons notre fleur pour en dessiner une nouvelle.

void setup()
{
  size(500, 500);
  colorMode(HSB, 360, 100, 100, 100); // ici nous passons en mode colorimétrique Teinte Saturation Luminosité. Ce qui nous pemrettra de définir plus aisement nos plages de couleur
  update();  //fonction initialisant nos valeurs
  background(0);
}

void update()
{
  vAngle = random(-0.1, 0.1); //définition du sens de rotation du sketch 
  colorRatio = random(0, 300); //choix d'un plage de couleur entre 0 et 300° - notre plage sera entre cette valeur et cette valeur+60
  nbParticules = 50; 
  oX = width/2;
  oY = width/2;
  posX = new float[nbParticules];
  posY = new float[nbParticules];
  r = new float[nbParticules];
  angle = new float[nbParticules];
  vr = new float[nbParticules];  
  c = new color[nbParticules];

  for (int i=1; i<nbParticules; i++) { 
    angle[i] = ((2*PI)/nbParticules)*i; //Ici nous divisons le cercle 2PI par le nombre de particule multiplier par i pour obtenir l'angle de chaque particule et les placer à équidistance les une des autres
    vr[i] = random(-0.5, -0.1); 
    r[i] = 200; //ici nos particules ont un rayon de départ identique pour former notre cercle mais nous pouvons aussi le définir en random et obtenir un autre visuel random(150, 200);
    posX[i] = oX+cos(angle[i])*r[i]; //position x sur le cercle x = cos(angle)*rayon
    posY[i] = oY+sin(angle[i])*r[i]; //position y sur le cercle y = cos(angle)*rayon
    d = dist(posX[i], posY[i], oX, oY); //distance entre les particules et la cible
    c[i] = color(random(colorRatio, colorRatio+60),random(80, 100), random(80,100)); //définissions de la couleur
  }
}

Maintenant que nous avons notre cercle de départ il ne nous reste plus qu’à le dessiner de la manière suivante

void draw()
{

  pushMatrix();
  /*rotation de la fleur*/
  if (etat == false) //si nous activons la rotation de la fleur
  {

    angleGlobal += vAngle;
  }
  else if (etat == true) //sinon la fleur reste droite
  {
    angleGlobal = angleGlobal;
  }
  translate(oX, oY);
  rotate(radians(angleGlobal));

  /*calcul des nouvelles positions de notre fleur*/
  for (int i=1; i<nbParticules; i++) { 
    r[i] += vr[i]; // reduction du rayon

    if (r[i]>200 || r[i]<0) //limite de la fleur. Si une des particules atteint le centre alors nous re initinalisons un cycle à l'aide de la fonction update();
    {
      update();
    }
    //position des partciules
    posX[i] = cos(angle[i])*r[i];
    posY[i] = sin(angle[i])*r[i];
    stroke(c[i], 5);
    noFill();
   //dessin de la fleur
    if (i != nbParticules-1)
    {
      line(posX[i], posY[i], posX[i+1], posY[i+1]);
    }
    else
    {
      line(posX[i], posY[i], posX[1], posY[1]);
    }
  }
  popMatrix();
  //decompte
  count++;
  if(count >= 1000)
  {
    count = 0;
    background(0);
  }
}

void keyPressed()
{
  if (key == 'a')
  {
    etat = !etat;
  }
}

//interactivité
void mousePressed()
{
  background(0);
  update();
}

Nous obtenons alors la fleur suivante. À partir de là il nous est facile de rajouter des comportements à notre fleur ou de changer sa forme de départ, son sens de rotation, la manière dont elle se dessine… et tout ça sur une base de trigonométrie.
trigonometrie