Sites


Processing

Les méthodes

Une méthode est un bloc qui contient une série d'instructions que l'on souhaite réutiliser. L'intérêt des méthodes réside dans la possibilité de réutiliser du code : nous aimerions écrire une seule fois une action tout en pouvant la répéter autant de fois que nécessaire. En englobant notre code dans une méthode, il devient possible d'appeler celle-ci à différents moments de notre programme.

Vous connaissez déjà plusieurs méthodes, mais uniquement en tant qu'utilisateur. Par exemple,  rect(), ellipse(), line() stroke() sont toutes des méthodes. En créant nos propres méthodes, nous nous rapprochons d'une certaine manière du rôle créatif des concepteurs de Processing : liberté nous est donnée d'inventer des formes ou des fonctionnalités qu'ils n'ont pas pu ou voulu écrire à l'origine.

Voici un exemple purement théorique de ce que la structuration d'un sketch à l'aide de méthodes pourrait éventuellement donner au final si nous devions dessiner un joli paysage.

background(255);

joliPaysage();

arbre(cypres, 0,300);
lune(400,100);
gazon(0,300,width,100); 

mouton(50,133);
mouton(213,98);
mouton(155,88);

L'objectif, c'est de regrouper des éléments complexes du programme dans des mots clés que vous pouvez appeler autant de fois que vous le voulez, tout en les mélangeant avec les mots clés Processing. Ce procédé appelé encapsulation vous éloigne apparemment de votre code, mais c'est pour vous en donner un nouvel accès simplifié en le rendant davantage lisible. Cela permet également d'éviter les répétitions inutiles. Un peu plus d'ordre, pour un peu moins de copier-coller.

dessinerMouton

Mots clés

Lorsque nous créons nos propres méthodes, il faut donner à chacune d'entre elles un nom. Une fois la méthode définie, on peut s'en servir dans le programme. Il suffit de l'appeler par son nom.

Processing nous fournit déjà plusieurs méthodes que nous pouvons remplacer par nos propres versions. Ce sera le cas des méthodes draw(), setup(), mousePressed()... que vous découvrirez dans d'autres chapitres. Nous pouvons également créer des méthodes sur mesure en leur donnant le nom de notre choix. Dans ce cas, il faut simplement éviter d'utiliser un nom qui est déjà pris.

Décomposer

Jusqu'ici, nous avons programmé dans Processing directement, en commençant à saisir du code depuis le haut du programme et en laissant celui-ci s'exécuter jusqu'en bas. Lorsque nous voulons construire nos propres méthodes, nous devons commencer à décomposer nos programmes en plusieurs parties séparées. Cela nous permettra par exemple d'indiquer les parties qui doivent s'exécuter tout de suite lorsque nous appuyons sur le bouton run de celles qui seront appelées par nos propres soins à l'intérieur du programme.

Par contre, en utilisant ce procédé de programmation, nous ne pourrons plus écrire des instructions directement dans Processing sans au préalable les avoir intégrées au sein d'une méthode ou d'une classe. Avec les méthodes, c'est tout ou rien.

void setup()

Processing nous offre une instruction ayant pour fonction de contenir le code de début de notre programme. Il s'agit de la méthode setup() :

void setup() {

}

C'est à l'intérieur des accolades de la méthode setup() que nous allons placer tout le code qui doit être exécuté au début de notre programme. Pour l'instant, ne cherchez pas à savoir à quoi sert le mot void, sachez juste qu'il faut l'écrire, suivi du mot setup, puis de parenthèses, et enfin les accolades.

La plupart du temps, nous nous servirons de la méthode setup() pour définir la taille de notre sketch. Cette taille ne peut être définie qu'une seule fois — ce qui tombe bien, car le démarrage n'a lieu qu'une seule fois dans le vie d'un programme.

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

Créer des méthodes sur mesure

Dans l'exemple qui suit, nous allons créer une méthode dessinerMouton() qui contient des instructions pour dessiner un mouton.

quatre_moutons

En fait, ce mouton est caché dans une boîte, alors on ne voit qu'une boîte ! On y dessine également des trous, afin que le mouton puisse respirer. Nous appelons plusieurs fois cette méthode pour dessiner plusieurs moutons.

Voici le code de ce dessin :

void setup() {

  size(600, 220);
  background(153,204,255);
  smooth();

  // l'appel à notre méthode de dessin d'un mouton
  dessinerMouton();
  translate(120, 60);
  dessinerMouton();
  translate(120, 60);
  dessinerMouton();
  translate(140, -60);
  dessinerMouton();
}


// la méthode pour dessiner le mouton

void dessinerMouton() {

  strokeWeight(3);
  strokeJoin(ROUND);
  stroke(0);
  fill(255);

  rect(20, 40, 80, 40);
  beginShape();
  vertex(20, 40);
  vertex(40, 20);
  vertex(120, 20);
  vertex(120, 40);
  endShape(CLOSE);

  beginShape();
  vertex(100, 40);
  vertex(120, 20);
  vertex(120, 60);
  vertex(100, 80);
  endShape(CLOSE);

  fill(0);
  ellipse(40, 60, 5, 5);
  ellipse(60, 60, 5, 5);
  ellipse(80, 60, 5, 5);
}

Le début de ce programme se décrit à l'intérieur de la méthode setup(). En effet, puisque nous utilisons une méthode pour dessiner notre mouton, le reste du programme doit également être placé quelque part dans une méthode. En début du programme, nous allons donc saisir :

 void setup() {
}

Ensuite, au sein des accolades de la méthode setup(), nous définissons la taille de notre sketch et sa couleur de fond.

  size(600, 220);
  background(153,204,255);

Vous vous êtes peut-être posé la question du rôle de smooth() dans notre programme. Optionnelle, cette ligne de code permet toutefois d'améliorer le rendu des lignes en lissant leur tracé : elles apparaissent ainsi plus jolies sur l'écran de l'ordinateur.

smooth();

Enfin, nous dessinons notre mouton, en faisant appel à une méthode que nous avons définie plus bas dans le programme.

  dessinerMouton();

Chaque fois que Processing tombe sur le mot dessinerMouton(), il vérifie si ce mot existe en tant que méthode quelque part dans le programme. Si cette méthode existe, il fait un détour par cette méthode et fait tout ce qui s'y trouve.

illustration_appel_methode.png

S'il ne trouve pas cette méthode — et qu'elle n'existe pas ailleurs dans la liste des fonctionnalités proposées directement par Processing —, votre programme s'arrêtera avec une erreur d'exécution.

Notez que vous pouvez écrire le mot clé dessinerMouton() autant de fois que vous voulez. Ici, dans ce programme, dessinerMouton() est écrit au final 4 fois :

  dessinerMouton();
  translate(120, 60);
  dessinerMouton();
  translate(120, 60);
  dessinerMouton();
  translate(140, -60);
  dessinerMouton();

Nous avons placé entre chaque appel à la méthode dessinerMouton(), une instruction translate(x,y). Cette instruction nous permet de ne pas dessiner quatre fois le même mouton au même endroit. Ce n'est pas le rôle de ce chapitre de vous expliquer les transformations comme translate() ; sachez néanmoins que translate() sert à déplacer le point d'origine où débutera le tracé d'un dessin.

void dessinerMouton() {
  
  /* ... */

}

Enfin nous arrivons à notre méthode dessinerMouton() proprement dite. C'est ici que nous dessinons les lignes et formes nécessaires pour obtenir le tracé de notre animal. Nous ne commenterons pas cette partie, puisqu'il s'agit uniquement d'instructions que vous trouverez davantage décrites dans le chapitre sur les formes.

Notez l'usage du mot-clé void devant le nom de notre méthode. Cela signifie qu'elle ne retourne rien. En faisant appel à elle, nous savons qu'elle n'a pas pour fonction de nous fournir des données.

La valeur de retour d'une méthode

Une méthode peut avoir une valeur de retour. Jusqu'ici, nous n'avons pas expérimenté cette particularité. Ni la méthode setup(), ni la méthode draw() ne retournent une valeur de retour. Le mot void a été placé devant chacune de ces deux méthodes pour bien préciser à Processing que rien ne doit être retourné lorsque l'on fera appel à elles.

L'emploi d'une méthode avec une valeur de retour suppose que nous cherchons à obtenir quelque chose d'elle en l'invoquant. Quand nous voulons savoir quelle heure est-il, nous demandons aux méthodes second(), minute(), ou hour() de nous donner en retour leurs valeurs sous forme d'un chiffre entier (int). Si ces méthodes ne nous donnaient rien (void) en retour, elles ne serviraient pas à grande chose.

Pour les méthodes qui doivent retourner une valeur à celle qui l'appelle, on indique un mot-clé avant pour indiquer le type de valeur qui doit être retourné. Une méthode dont le type est int nous retourne une valeur de type int correspondant à un nombre entier, une méthode dont le type est float nous retourne une valeur de type float (nombre à virgule), et ainsi de suite.

Voici un exemple de méthode qui nous donne le nombre secondes depuis 00:00:00 ce matin.

int secondesAujourdhui() {
  return hour() * 3600 + minute() * 60 + second();
}

void draw() {
  println( secondesAujourdhui() );
}

Même si vous ne connaissez pas la méthode draw(), amusez-vous néanmoins à exécuter  ce mini-programme et regardez les informations qui s'affichent dans la console située en bas de votre fenêtre d'édition de Processing. Vous verrez que la méthode draw() appelle en permanence la méthode secondesAujourdhui() et utilise le résultat de cette méthode pour nous afficher les secondes.

Les paramètres d'une méthode

Une méthode peut accepter des paramètres. La plupart du temps, on les appelle des arguments. Ces paramètres doivent avoir chacun un type et un nom, tout comme les variables.

Pour appeler une méthode, on écrit son nom, et on le fait suivre d'une parenthèse ouvrante et d'une autre fermante. Entre ces parenthèses, on place les paramètres de la méthode. Ce qu'on y met sera envoyé dans la méthode.

multiplier(2, 2);

Une fois arrivé dans le corps de la méthode, Processing peut accéder à leur valeur, comme il le fait avec les variables.

Ainsi dans l'exemple ci-après, lorsque cette méthode est appelée avec les argument 2 et 2, la valeur de a = 2, et celle de b = 2 également.  La valeur de retour de cette méthode sera donc 4 (2 fois 2 égale 4).

int mutliplier(int a, int b) {
  return a * b;
}

Notez que c'est la position des arguments qui détermine quelle valeur sera affectée à quel argument.

multiplier

Pour résumer notre exemple, on a créé une méthode qui retourne le résultat de la multiplication de ses deux arguments. Un commentaire précède la définition de la méthode (une bonne pratique de programmation pour se rappeler ultérieurement de la fonction d'un morceau de code).

/*
 * Retourne le résultat de la multiplication de ses
 * deux arguments.
 */
int multiplier(int a, int b) {
  return a * b;
}

void setup() {
  int resultat = multiplier(2, 2);
  print(resultat);
}

La console de Processing affichera :

multip_result.tiff

La portée des variables

Profitons de ce chapitre sur les méthodes et les variables pour vous mettre en garde contre une erreur classique qui peut survenir lorsque l'on utilise des variables et des méthodes dans un programme.

Les variables — que ce soit des objets ou des types fondamentaux de données — ne sont pas forcement accessible à l'ensemble de votre programme ! Tout dépend de l'endroit où elles ont été déclarées. Une variable déclarée à l'intérieur d'une méthode ne sera accessible que dans celle-ci :

void setup() {
  int x = 10;
}

void draw() {
  /* Le programme générera une erreur car la variable x
   * n'existe qu'à l'intérieur de la méthode setup()
   */
  x = x + 1;
}

En lançant l'exécution du programme ci-dessus, la console de Processing affichera le message d'erreur suivant :

var_error.tiff

Pour qu'une variable soit accessible à tout votre programme il faut la déclarer en en-tête comme ceci :

int x;

void setup() {
  x = 10;
}

void draw() {
  x = x + 1;
}

Il y a une erreur de communication avec le serveur Booktype. Nous ne savons pas actuellement où est le problème.

Vous devriez rafraîchir la page.