Comment créer une application de dessin avec Processing

Vue de l'application de dessin programmée avec Processing

Processing (processing.org), à la fois logiciel et langage de programmation, permet de réaliser des applications graphiques et interactives sur la base du langage Java. Pour illustrer ces usages, voyons comment programmer une application de dessin qui permettra de sélectionner la couleur et l'épaisseur de trait et d'enregistrer le travail sous forme d'une image PNG ou JPEG selon le choix du programmeur (sous réserve que l'enregistrement comprend aussi des éléments d'interface : on enregistre tout ce qui s'affiche dans la fenêtre de notre programme).

Pour débuter, comme pour tout programme dans Processing, tout se passe autour du setup(), qui se joue une fois pour fixer les paramètres de l'application, et une boucle draw() qui tourne en continu pendant tout le temps que se joue le programme. Commençons donc par créer une fenêtre de 900 pixels de large et 700 pixels de hauteur. La page de dessin sera concrètement d'une largeur de 800 pixels et prendra toute la hauteur de la fenêtre. Le menu viendra, sur toute la hauteur, dans un rectangle de 100 pixels de largeur collé au bord.

 void setup() {
   size(900, 700);
   smooth();
   background(0);
   // définition de la surface de dessin
   noStroke();
   fill(255);
   rect(100, 0, width-100, height);
 }

 void draw() {
 }

🚩: Pour tester un programme à chaque étape, il suffit de cliquer sur le bouton lecture de Processing ().

Interagir avec la souris

Testons les grands principes d’interactivité dans Processing. Pour repérer la position du pointeur de la souris, ce qui nous permettra de définir où se situe la pointe du crayon dans la fenêtre, nous devons connaître ses coordonnées selon les axes X et Y. Nous allons utiliser mouseX et mouseY, ces paramètres déterminent en temps réel la position de la souris. Pour le visualiser, dessinons un cercle jaune de 5 pixels de rayon autour du pointeur.

 void draw() {
   fill(255, 255, 0);
   ellipse(mouseX, mouseY, 10, 10);
 }

Avec ce code, le programme dessine un cercle de 10 pixels de diamètre autour du pointeur de la souris à chaque fois que l'écran se rafraîchit.

Pour notre programme, il faudrait tracer une ligne continue pendant tout le temps où l'on maintient enfoncé le bouton de la souris. Pour ce faire, il nous faut repérer la position de la souris par rapport à son ancienne position et dessiner alors un trait entre les deux étapes. Nous pouvons appeler pmouseX et pmouseY pour cela. Le programme connaîtra alors les coordonnées X et Y précédentes.

 void draw() {
   noFill();
   stroke(255, 255, 0);
   strokeWeight(2);
   line(pmouseX, pmouseY, mouseX, mouseY);
 }

La position par défaut du pointeur est dans le coin haut gauche de la fenêtre, le dessin commence donc de cette position et relie le trait à la souris à chaque changement de position. Pour un programme de dessin, il nous faudra préciser que le dessin ne se fait que lorsque le bouton de la souris est enfoncé, dans le cadre de dessin blanc, pas sur le menu. Ces fonctionnalités se jouent en émettant une condition : mousePressed, et une autre condition : on dessine au delà de 100 pixels à partir de la gauche.

 void draw() {
   if (mousePressed && mouseX > 100 && pmouseX > 100) {
     noFill();
     stroke(255, 255, 0);
     strokeWeight(2);
     line(pmouseX, pmouseY, mouseX, mouseY);
   }
 }

Nous avons maintenant quasiment élaboré les principes de l'application. Le programme permet déjà de réaliser un dessin dans la fenêtre créée.

Pour obtenir un vrai programme de dessin, il faut avoir accès à certaines fonctionnalités qui permettent de paramétrer le crayon (épaisseur du trait, couleur) et d'agir à partir du menu (nouveau dessin, enregistrement). Occupons-nous maintenant du menu à gauche.

Le menu s'affiche à gauche, dans un cadre de couleur noire de 100 pixels sur la hauteur. Le menu sera dessiné dans la boucle draw() du programme pour pouvoir afficher les modifications de paramètres à chaque fois.

Épaisseur du crayon

L'épaisseur du crayon se déterminera à l'aide de deux flèches (des triangles blancs) encadrant le chiffre correspondant à l'épaisseur en pixels du trait (limitée entre 1 et 10). Créons une variable globale epaisseur (un entier) définie à 4 par défaut.

 int epaisseur;

 void setup() {
   ...
   epaisseur = 4;
   textAlign(CENTER);
   textSize(20);
   ...
 }

 void draw() {
   // dessin du menu
   noStroke();
   fill(0);
   rect(0, 0, 100, height);
   fill(255);
   triangle(50, 580, 80, 610, 20, 610);
   text(epaisseur, 50, 635);
   triangle(20, 650, 80, 650, 50, 680);
   // dessin des traits de crayon
   if (mousePressed && mouseX > 100 && pmouseX > 100) {
     noFill();
     stroke(255, 255, 0);
     strokeWeight(epaisseur);
     line(pmouseX, pmouseY, mouseX, mouseY);
   }
 }

L'interactivité du menu se passera dans une nouvelle boucle mousePressed() qui prend en charge chaque action de la souris. Il faut définir la zone correspondant au bouton d'action et l'action provoquée par un clic :

 void mousePressed() {
   // flèche vers le haut
   if(mouseX > 20 && mouseX < 80 && mouseY > 580 && mouseY < 610) epaisseur = epaisseur + 1;
   // flèche vers le bas
   if(mouseX > 20 && mouseX < 80 && mouseY > 650 && mouseY < 680) epaisseur = epaisseur - 1;
   // contraintes de l'épaisseur
   if(epaisseur < 1) epaisseur = 1;
   if(epaisseur > 10) epaisseur = 10;
 }

Sélecteur de couleurs

Les couleurs d'un programme Processing sont, par défaut, déterminées en mode RVB (rouge, vert, bleu en 256 niveaux de 0 à 255). Nous allons donc faire un sélecteur de couleurs avec 3 boutons à glisser pour faire varier la valeur de 0 à 255 du rouge, du vert et du bleu. Nous pouvons créer trois variables globales (définies ici à 100 pour un niveau de gris) au début du code :

 int rouge, vert, bleu;

 void setup() {
   ...
   rouge = 100;
   vert = 100;
   bleu = 100;
       …
 }

Pour afficher les sélecteurs de couleur, nous allons créer trois objets (étant donné que les éléments sont quasiment identiques). Nous créons une classe* « Selecteur » avec deux variables définissant le sélecteur.

* Pour la construction d'un objet, voir article Programmation Orientée Objets dans Processing.

Le sélecteur comprend une ligne servant de guide pour voir où on en est du niveau de couleur, le niveau de rouge, vert ou bleu s'affiche dans un rectangle qui sert de manette. La couleur de chaque rectangle est obtenue à partir des variables correspondantes combinées avec des valeurs nulles pour les deux autres nuances du mode RVB.

La création de l'objet peut se placer à la fin du code, ou même dans un nouvel onglet (Ctrl + Maj + N) :

 class Selecteur {
   int positionX, niveau;
   Selecteur(int tempX, int tempNiveau) {
     positionX = tempX;
     niveau = tempNiveau;
   }
   void display() {
     stroke(200);
     strokeWeight(2);
     line(positionX, 500, positionX, 240);
     if (positionX == 30) {
       fill(rouge, 0, 0);
       niveau = rouge;
     } else if (positionX == 50) {
       fill(0, vert, 0);
       niveau = vert;
     } else {
       fill(0, 0, bleu);
       niveau = bleu;
     }
     rectMode(CENTER);
     rect(positionX, 500 - niveau, 10, 10);
   }
 }

Pour afficher les sélecteurs, nous créons les instances de l'objet dans la partie setup() où l'on défini les paramètres, et on affiche les objets dans la boucle draw(). Au dessous des sélecteurs, nous affichons la couleur en cours. Nous ajoutons cette forme juste après la description de la partie interactive du tracé de crayon :

 void setup() {
   ...
   // création des boutons du sélecteur de couleur
   for (int i = 0; i < niveaux.length; i++) {
     niveaux[i] = new Selecteur(30+i*20, 50);
   }
 }
 void draw() {
       …
   // dessin des traits de crayon
   if (mousePressed && mouseX > 100 && pmouseX > 100) {
     noFill();
     stroke(rouge, vert, bleu);
     strokeWeight(epaisseur);
     line(pmouseX, pmouseY, mouseX, mouseY);
   }
   // affichage de la couleur en cours
   stroke(255);
   strokeWeight(2);
   fill(rouge, vert, bleu);
   rect(30, 520, 40, 40);
   line(20, 200, 80, 200);
   // affichage des sélecteurs de couleur
   for (int i = 0; i < niveaux.length; i++) {
     niveaux[i].display();
   }
 }

La partie interactive du sélecteur de couleurs ne sera pas positionnée dans la boucle mousePressed(), qui permet de sélectionner une action selon l'endroit où l'on clique, mais dans une boucle mouseDragged() qui offre l'avantage de suivre, tant que le bouton de la souris est enfoncé, la position du pointeur de la souris et d'agir de manière plus naturelle, comme avec une manette de réglage. Ajoutez et testez ce code pour agir avec les sélecteurs de couleurs :

 void mouseDragged() {
   if (mouseY > 244 && mouseY < 501) {
     if (mouseX > 24 && mouseX < 36) rouge = 500 - mouseY;
     if (mouseX > 44 && mouseX < 56) vert = 500 - mouseY;
     if (mouseX > 64 && mouseX < 76) bleu = 500 - mouseY;
   }
 }

Effacer et recommencer

Maintenant que nous avons une application fonctionnelle, nous pouvons dessiner. Mais le besoin se fera certainement ressentir de faire un nouveau dessin sans devoir fermer et rouvrir l'application à chaque fois. C'est ici qu'entrent en jeu des éléments d'interface : un bouton « nouveau document » et un autre bouton pour enregistrer.

Pour effacer le dessin en cours et en faire un nouveau, nous allons créer un bouton qui, lorsque l'on clique dessus, dessine un rectangle blanc pour recouvrir la surface de dessin et ouvrir ainsi un nouvel espace libre.

Dessinons une forme libre sous le sélecteur de couleurs. Dans la boucle draw(), traçons une forme (Shape) où chaque point de la figure géométrique est décrit par un vertex(coordonnéeX, coordonnéeY). Les deux lignes figurent le coin de la page replié.

 void draw() {
       …
   // forme du bouton "Nouveau"
   fill(255);
   beginShape();
   vertex(30, 40);
   vertex(60, 40);
   vertex(70, 50);
   vertex(70, 90);
   vertex(30, 90);
   endShape(CLOSE);
   stroke(50);
   line(60, 40, 60, 50);
   line(60, 50, 70, 50);
 }

Pour que ce bouton ait une fonction, ajoutons de l'interactivité dans la boucle mousePressed() :

 void mousePressed() {
       …
   // bouton "Nouveau" : nouvelle surface de dessin
   if(mouseX > 29 && mouseX < 71
   && mouseY > 39 && mouseY < 91) {
     fill(255);
     noStroke();
     rectMode(CORNER);
     rect(100, 0, width-100, height);
   }
 }

Nous avons défini la zone du bouton « Nouveau », et quand l'utilisateur clique dans cette zone, le programme dessine une nouvelle zone de dessin vierge.

Se souvenir des belles choses

On peut, en tant qu'utilisateur d'un programme de création graphique, avoir envie de sauvegarder ses créations. Pour offrir cette possibilité, créons notre bouton d'enregistrement sous la forme d'un disquette simplifiée, comme encore utilisé dans beaucoup d'applications.

Pour obtenir des coins arrondis sur notre disquette, indiquons le paramètre « ROUND » (arrondi) dans le descriptif de la jonction des lignes (strokeJoin). Le reste est du code de dessin classique :

 void draw() {
        …
   // forme du Bouton "Enregistrer"
   rectMode(CORNER);
   stroke(255);
   strokeJoin(ROUND);
   strokeWeight(5);
   rect(32, 120, 36, 40);
   fill(50);
   stroke(50);
   rect(40, 145, 20, 10);
   fill(100);
   noStroke();
   rect(42, 118, 16, 10);
   rect(45, 143, 10, 5);
 }

Une fois la disquette dessinée, ajoutons-lui la fonction dont nous avons besoin. Pour enregistrer la fenêtre de l'application en cours dans Processing, il existe une fonction simple : save(), qui prend pour paramètre le nom du fichier dans lequel nous souhaitons enregistrer l'image :

 void mousePressed() {
   
   // fonction d'enregistrement
   if(mouseX > 29 && mouseX < 71
   && mouseY > 119 && mouseY < 161) save("mesdessins/dessin.png");
 }


L'enregistrement se fera donc dans le dossier de l'application, à l'intérieur d'un sous-dossier « mes dessins » que le programme se chargera de créer s'il n'existe pas encore. Si le fichier existe déjà, la nouvelle image écrasera la précédente ; il faudra prévoir de renommer chaque nouveau dessin avant d'en commencer un autre. Vous pouvez maintenant exporter l'application (Fichier / Exporter ou Ctrl + Maj + E) pour diffuser votre programme sur différents ordinateurs.

Voici le code complet :

 int epaisseur;
 int rouge, vert, bleu;
 Selecteur niveaux[] = new Selecteur[3];

 void setup() {
   size(900, 700);
   smooth();
   background(0);
   epaisseur = 4;
   rouge = 100;
   vert = 100;
   bleu = 100;
   textAlign(CENTER);
   textSize(20);
   // définition de la surface de dessin
   noStroke();
   fill(255);
   rect(100, 0, width-100, height);
   // création des boutons du sélecteur de couleur
   for (int i = 0; i < niveaux.length; i++) {
     niveaux[i] = new Selecteur(30+i*20, 50);
   }
 } 

 void draw() {
   // dessin du menu
   rectMode(CORNER);
   noStroke();
   fill(0);
   rect(0, 0, 100, height);
   fill(255);
   triangle(50, 580, 80, 610, 20, 610);
   text(epaisseur, 50, 635);
   triangle(20, 650, 80, 650, 50, 680);
   // dessin des traits de crayon
   if (mousePressed && mouseX > 100 && pmouseX > 100) {
     noFill();
     stroke(rouge, vert, bleu);
     strokeWeight(epaisseur);
     line(pmouseX, pmouseY, mouseX, mouseY);
   }
   // affichage de la couleur en cours
   stroke(255);
   strokeWeight(2);
   fill(rouge, vert, bleu);
   rect(30, 520, 40, 40);
   line(20, 200, 80, 200);
   // affichage des sélecteurs de couleur
   for (int i = 0; i < niveaux.length; i++) {
     niveaux[i].display();
   }
   // forme du bouton "Nouveau"
   fill(255);
   beginShape();
   vertex(30, 40);
   vertex(60, 40);
   vertex(70, 50);
   vertex(70, 90);
   vertex(30, 90);
   endShape(CLOSE);
   stroke(50);
   line(60, 40, 60, 50);
   line(60, 50, 70, 50);
   // forme du Bouton "Enregistrer"
   rectMode(CORNER);
   stroke(255);
   strokeJoin(ROUND);
   strokeWeight(5);
   rect(32, 120, 36, 40);
   fill(50);
   stroke(50);
   rect(40, 145, 20, 10);
   fill(100);
   noStroke();
   rect(42, 118, 16, 10);
   rect(45, 143, 10, 5);
 }

   void mousePressed() {
   // flèche vers le haut
   if (mouseX > 20 && mouseX < 80 && mouseY > 580 && mouseY < 610) epaisseur = epaisseur + 1;
   // flèche vers le bas
   if (mouseX > 20 && mouseX < 80 && mouseY > 650 && mouseY < 680) epaisseur = epaisseur - 1;
   // contraintes de l'épaisseur
   if (epaisseur < 1) epaisseur = 1;
   if (epaisseur > 10) epaisseur = 10;
   // bouton "Nouveau" : nouvelle surface de dessin
   if(mouseX > 29 && mouseX < 71
   && mouseY > 39 && mouseY < 91) {
     fill(255);
     noStroke();
     rectMode(CORNER);
     rect(100, 0, width-100, height);
   }
   // fonction d'enregistrement
   if(mouseX > 29 && mouseX < 71
   && mouseY > 119 && mouseY < 161) save("mesdessins/dessin.png");
 }

 void mouseDragged() {
   if (mouseY > 244 && mouseY < 501) {
     if (mouseX > 24 && mouseX < 36) rouge = 500 - mouseY;
     if (mouseX > 44 && mouseX < 56) vert = 500 - mouseY;
     if (mouseX > 64 && mouseX < 76) bleu = 500 - mouseY;
   }
 }

 class Selecteur {
   int positionX, niveau;
   Selecteur(int tempX, int tempNiveau) {
     positionX = tempX;
     niveau = tempNiveau;
   }
   void display() {
     stroke(200);
     strokeWeight(2);
     line(positionX, 500, positionX, 240);
     if (positionX == 30) {
       fill(rouge, 0, 0);
       niveau = rouge;
     } else if (positionX == 50) {
       fill(0, vert, 0);
       niveau = vert;
     } else {
       fill(0, 0, bleu);
       niveau = bleu;
     }
     rectMode(CENTER);
     rect(positionX, 500 - niveau, 10, 10);
   }
 }


Commentaires

Posts les plus consultés de ce blog

Débuter avec les dégradés de couleurs dans Inkscape

Pour les débutants : 3 méthodes faciles pour créer un texte incurvé dans Inkscape

Pour les débutants : utiliser les guides dans le logiciel Inkscape pour préparer une animation