Affichage des articles dont le libellé est processing. Afficher tous les articles
Affichage des articles dont le libellé est processing. Afficher tous les articles

samedi 25 novembre 2017

Editeur des diagrammes Entité/Association en une soirée grace à Processing

La facilité de dessin et de gestion des évènement permet au développeur sous Processing de préparer rapidement des prototypes ou bien des outils dans un temps record. Cette capacité est très nécessaire lorsqu'il s'agit de prouver une idée ou bien pour préparer une démonstration rapide pour votre client.

Dans cet exemple, j'ai créé rapidement un éditeur des diagramme Entité/Association sous Processing dans environs 350 lignes de code. L'éditeur ne permet pas d'annuler, de déplacer de sélectionner les objets créés. Néanmoisn, il permet de :
  1. Créer des entités en faisant un clique gauche :
    1. L'entité est créée à l'endroit où le clique a eu lieu.
    2. L'éditeur vous demande le nom de l'entité qui ne doit pas être vide.
    3. Il vous demande, par la suite, une liste d'attributs, une à la fois. La création de la liste des attributs s'arrête lorsque la chaîne vide est entrée.
  2. Créer des associations en faisant un clique droit :
    1. L'association est créée à l'endroit où le clique a eu lieu.
    2. L'éditeur vous demande le nom de l'association qui ne doit pas être vide.
    3. Il vous demande, par la suite, une liste d'attributs, une à la fois. La création de la liste des attributs s'arrête lorsque la chaîne vide est entrée.
  3. Pour créer un lien, il faut dessiner une ligne d'un objet à un autre objet de différente nature (d'une entité vers une association ou bien d'une association vers une entité). Les autres liens seront refusés (l'unique vérification dans le système).
Au stade actuel, le système ne fait pas d'autres vérifications, c'est à dire, il est possible de créer deux entités avec le même nom, d'avoir deux attributs identiques et de mettre ce que vous voulez comme cardinalité. Mais, comme j'ai déjà mentionné, il s'agit d'une démo pour Processing plus qu'un éditeur.

Dans ce projet, vous pouvez voir comment différencier les bouttons de la souris en toute simplicit. Dans la méthode "mousePressed()", il est nécessaire de vérifier est ce qu'il s'agit d'un clique droit pour insérer une association ou bien d'un clique gaucheoù il faut véirifier est ce qu'il est appliqué à l'intérieur d'un objet ce qui veut dire qu'il s'agit d'un lien ou bien s'il est à l'extérieur de tout objet pour insérer une nouvelle entité.

void mousePressed(){
  if(mouseButton == LEFT){
    int entite = verifierEntites(mouseX, mouseY);
    if(entite != -1){
      creationLien = true;
      entiteSource = true;
      source = entite;
    }else{
      int association = verifierAssociations(mouseX, mouseY);
      if(association != -1){
        creationLien = true;
        entiteSource = false;
        source = association;
      }else{
        lireEntite(mouseX, mouseY);
      }
    }
  }else{
    lireAssociation(mouseX, mouseY);
  }
}

La vérification que le clique est à l'intérieur d'un objet se fait par rapport aux coordonnés et mesures de ce dernier. Ainsi dans la classe Objet, on trouve la méthode "estDedans()" :

  public boolean estDedans(int cx, int cy){
    return cx > x && cx < x + getLargeur() && cy > y && cy < y + getHauteur();
  }

La méthode qui est supposée faire toutes les vérifications concerant les noms des entitées, des associations et des attributs ne fait en réalité que lire ces valeurs sans aucune vérification; elle nécessite un effort supplémentaire :


void lireEntite(int mx, int my){
  String nom = JOptionPane.showInputDialog(null, "Donnez le nom de l'entité");
  if(nom != null && !nom.equals("")){
    Entite e = new Entite(mx, my);
    String propriete;
    int i = 0;
    do{
      propriete = JOptionPane.showInputDialog(null, "Donnez les propriétés (vide pour terminer)");
      if(propriete != null && !propriete.equals("")){
        e.addAttribut(propriete);
        i++;
      }
    }while(propriete != null && !propriete.equals("") && i < NOMBRE_MAX_ENTITES);
    e.setNom(nom);
    entites[nbrEntites] = e;
    nbrEntites++;
  }
}


Vous pouvez télécharger le code par ici.

Drag & Drop sous Processing en quelques lignes

L'interactivité sous Processing est très impressionnates. En quelque lignes, il est possible de récupérer et traiter les différents évènements prevenant de la souris ou bien du clavier. Cet exemple vise à donner un avant-goût de ces capacités.
Le Drag Drop consiste à lever un objet en cliquant dessus, le déplacer (le voir se déplacer) en gardant le clique et le déposer en relâchant le clique.

Sous Processing, nous pouvons exploiter les éléments suivants :
  •  Il est à tout moement possible de récupérer la position de la souris grâce à deux variables prédéfinies : museX, mouseY. Aucun code ou fonction ou Listener sont nécessaires.
  • Pour récupérer l'évènement du clique, il suffit d'implémenter la méthode "mousePressed()"; elle sera appelée si l'un des deux bouttons de la souris est enfoncé. mouseBoutton permet de connaître est ce qu'il s'agit d'un clique droit (RIGHT) u gauche (LEFT).
  • Pour récupérer l'évènement du relâche de la souris, il suffit d'implémenter la méthode "mouseReleased()".
Le seul élément que nous allons ajouter est une variable booléenne pour garder trace est ce que l'objet est entrain d'être déplacé ou pas. Le code suivant est le code de l'objet (nommé Activite, préparé dans un contexte précis) :

class Activite {
  
  int x, y;
  final int HEIGHT = 180;
  final int WIDTH = 180;
  
  public Activite(int x, int y){
    this.x = x;
    this.y = y;
  }
  
  void draw(){
    rect(x, y, HEIGHT, WIDTH);
  }
  
  void setX(int x){
    this.x= x;
  }
  
  void setY(int y){
    this.y = y;
  }
  
  int getX(){
    return x;
  }
  
  int getY(){
    return y;
  }
  
}

Ce code est presque un JavaBean, à l'exception de la méthode draw() qui déssine un rectangle.
Le code du Drag & Drop est :

Activite a;

int diffx, diffy;
boolean drawing = false;

void setup(){ 
  size(960, 480);
  a = new Activite(100, 100);
}

void draw(){
  background(240);
  
  if(drawing){
    a.setX(mouseX + diffx);
    a.setY(mouseY + diffy);
  }
  
  a.draw();
}

void mousePressed(){
  diffx = a.getX() - mouseX;
  diffy = a.getY() - mouseY;
  drawing = true;
}

void mouseReleased(){
  drawing = false;
}

Les deux variables diffx et diffy c'est pour garder la distance entre la position de la souris et la position de l'objet au départ de l'action.

Une version abrégée de ce code peut montrer qu'il est possible d'implémenter le tout avec toutes les données et les fonctions en moins de 30 lignes en gardant notre code claire at aéré:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int x, y, h, w;
int diffx, diffy;
boolean drawing = false;

void setup(){ 
  size(960, 480);
  x = 100; y = 100;
  h = 100; w = 250;
}

void draw(){
  background(240);
  if(drawing){
    x = mouseX + diffx;
    y = mouseY + diffy;
  }  
  rect(x, y, w, h);
}

void mousePressed(){
  diffx = x - mouseX;
  diffy = y - mouseY;
  drawing = true;
}

void mouseReleased(){
  drawing = false;
}

Chute libre dans le vide sous Processing

Simuler une phénomène naturel en utilisantl'animation est un moyen excellent pour l'enseigner et pour l'expliquer. Cette approche permet, aussi, de modifier les différents paramètres et voir les différents scénarios possibles avec un coût (pratiquement) nul.
Dans cet exemple, nous allons simuler une chute libre dans le vide. Pour rendre la simualtion un peu plus réel, nous allons ajouter un effet de rebond par définir un facteur de préservation de l'énergie au moement de l'impact au sol.
L'objet en question est une boule. Elle sera définie comme un objet séparé pour donner la possibilité de comparer plsuieurs chutes avec des paramètres différents.
Le problème peut être modélisé suivant deux approches :
  • L'utilisation des équations en fontion du temps "t", la position et la vitesse de l'objet seront calculées en fontion de l'instant "t" de l'animation.
  • En utilisant des suites pour la vitesse te la position. Les paramètres de l'instant "t" seront obtenus en fontion des paramètres de l'instant "t - 1".
Dans cet exemple, nousa allons suivre cette deuxième approche. Le passage d'un instant à l'instant suivant est implémenté sous la forme d'une fonction que nous appelerons chaque intervalle du temps (un autre paramètre que nous pouvons modifier à notre guise).

 


Le code principal est très simple. Il initialise l'environnement et déclare une boule. Chaque fois, il dessine la boule avec sa position, sa vitesse et son énergie.


Boule b;

void setup(){
  size(600, 400);
  b = new Boule(100, 0, 50, 10, 0.9f);
}

void draw(){
  background(255);
  b.draw();
  if(frameCount % 6 == 0)
    b.instantSuivant();
  fill(0);
  text("" + b.getVitesse(), 10, 30);
  text("" + b.getEnergie(), 10, 50);
  text("" + b.getY(), 10, 70);
};

Le code  de l'objet Boule permet de garder les différentes information de la boule, de simuler la chute et de déssiner la boule :


class Boule {
  
  public final static float G = 1f;
  
  float preservation;
  int x, y, diametre, vitesse, masse, energie;
  
  public Boule(int x, int y, int diametre, int masse, float preservation){
    this.x = x;
    this.y = y;
    this.diametre = diametre;
    this.masse = masse;
    this.preservation = preservation;
  }
  
  public void instantSuivant(){
    y += vitesse;
    energie = masse * vitesse * vitesse / 2;
    if(y >= height - diametre / 2){
      vitesse = (int)(-1 * preservation * Math.sqrt(2 * energie / masse));
    }else{
      vitesse += G;
    }
  }
  
  public void draw(){
    fill(255);
    stroke(0);
    ellipse(x, y, diametre, diametre);
  }
  
  public int getVitesse(){
    return vitesse;
  }
  
  public int getEnergie(){
    return energie;
  }
  
  public int getY(){
    return y;
  }
  
  public int getX(){
    return x;
  }
    
  
}

La fonction qui nous intéresse le plus est la fonction "instantSuivant()". Elle montre le changement appliqué sur la vitesse :


  public void instantSuivant(){
    y += vitesse;
    energie = masse * vitesse * vitesse / 2;
    if(y >= height - diametre / 2){
      vitesse = (int)(-1 * preservation * Math.sqrt(2 * energie / masse));
    }else{
      vitesse += G;
    }
  }

  • Si l'objet est en chute, la vitesse est influencée par la gravité seulement.
  • Si la boule touche le sol, la gravité n'est plus appliquée mais c'est le rebond qui repousse la boule vers le haut.
 
L'état final de l'animation est la boule immobile au fon d de l'écran (le sol).
Nous pouvons facilement ajouter une autre boule pour comparer les deux chutes :


Boule b1, b2;

void setup(){
  size(600, 400);
  b1 = new Boule(100, 0, 50, 10, 0.9f);
  b2 = new Boule(200, 0, 80, 10, 0.5f);
}

void draw(){
  background(255);
  b1.draw();
  b2.draw();
  if(frameCount % 6 == 0){
    b1.instantSuivant();
    b2.instantSuivant();
  }
  fill(0);
  text("b1", 10, 10);
  text("" + b1.getVitesse(), 10, 30);
  text("" + b1.getEnergie(), 10, 50);
  text("" + b1.getY(), 10, 70);
  text("b2", 10, 90);
  text("" + b2.getVitesse(), 10, 110);
  text("" + b2.getEnergie(), 10, 130);
  text("" + b2.getY(), 10, 150);
};


mercredi 8 novembre 2017

Bases de l'animation sous Processing (partie 2)


Dans la première partie, nous avons préciser les éléments les plus basiques pour pouvoir dessiner sous Processing. Animer n'est pas très différents du dessin, surtout si nous avons pas mal de notions déjà implémentées.
Animer c'est dessiner plusieurs images légèrement différentes d'une  manière successive pour créer l'illusion qu'il s'agit d'un objet en animation. La fréquence de l'animation et le degré de changement entre les images jouent un rôle important dans la qualité de l'animation.
D'un côté, si la fréquence de dessin est top basse, l'animation est perdue parce qu'elle devient un ensemble d'états (de l'objet en question) clairement distincts. Si elle est trop élevé, l'animation est perdues parce que l'utilisateur ne peut voir que le premier et le dernier états; il aura la sensation que l'objet en question a été téléporté de la première position vers la deuxième position.
De l'autre côté, les différences entre les différentes images doivent être non null, autrement il n'y a plus d'animation mais des objets immobiles. Comme elles ne doivent pas être trop grandes, sinon, on risque d'avoir le même effet de téléportation sensé avec une grande fréquence de dessin ( de mise à jour).
De toute ces notions, Processing a essayé d'implémenter une bonne partie. Si les objets de l'animation et la différence entre les différentes images (frames) sont la responsabilité de l'utilisateur (chaque utilisateur peut créer ce qu'il veut), Processing a pu apporté sa valeur ajoutée concernant le principe de l'animation et de la fréquence de l'animation.
Un script de base d'animation sous Processing doit définir les deux fonctions : setup() et draw().
setup()  : est, comme son nom l'indique, une fonction d'initialisation qui sera appelée une seule fois avant toutes les autres fonctions. Cette fonction est généralement utilisée pour initialiser les variables manipulées et les définir les propriétés générale de l'animation telles que l'arrière plan et la taille de la fenêtre.
draw() : est la fonction qui permet de dessiner le contenu de la fenêtre. Cette fonction est appelée 60 fois par seconde. Cette philosophie permet d'éviter au utilisateur de gérer deux choses :
  1. La boucle de dessin,
  2. Le rythme de dessin.
Prenons un exemple :


int i, j;

void setup(){
  size(600, 600);
  i = 0;
  j = 0;
}

void draw(){
  i++;
  j++;
  ellipse(i, j, 5, 5); // un cercle de 5 pixels
}



Ce script nous permet de dessiner une ligne qui grandit peu à peu en diagonale de la fenêtre. La question ici est : comment avons nous obtenu une ligne malgré que le script fait appel à la fonction "ellipse()" qui dessine un seul point ?
La réponse réside dans le fait que Processing ne vide pas la fenêtre entre deux appels à la fonction "draw()"; il y ajoute les nouvelles formes sans effacer les anciennes.
La méthode la plus simple pour effacer le contenu de la fenêtre est de re-dessiner l'arrière plan à chaque appel de la fonction "draw()". Ainsi, le script peut être modifié pour avoir un point qui se déplace en diagonale de la fenêtre au lieu d'avoir une ligne qui grandit.


int i, j;

void setup(){
  size(600, 600);
  i = 0;
  j = 0;
}

void draw(){
  background(255);
  i++;
  j++;
  ellipse(i, j, 5, 5);
}

A la fin, le point en déplacement finit par sortir de la fenêtre.


Le règles de dessin reste les mêmes que les règles présentées dans la première partie de cet article.

Note : l'utilisation de la méthode "ellipse()" qui dessine, à la base, la forme ovale nous permet de dessiner une un grand point. La méthode "point()" permet de dessiner un seul point mais il ne sera pas très visible sur les captures.

Bases de l'animation sous Processing (partie 1)

Processing facilite la création des animation. Il implémente les notions de base de toute animation. Dans cet article (sur deux parties), je vais brièvement présenter la différence entre un script de dessin et un script d'animation sous Processing.
Pour dessiner une forme, aucune condition préalable est nécessaire sauf la taille de la fenêtre. Cette taille peut être définie par la fonction "size". Elle respecte la norme en prenant le premier paramètre comme la largeur de la fenêtre et le deuxième paramètre comme sa hauteur. Par exemple :


size(600, 400);

Nous donne la fenêtre suivante :


Et puisque Processing cache la complexité liée à la programmation Orientée Objet en Java, la ligne de code précédente est tout ce que nous allons mettre dans le script.


Pour dessiner une forme donnée, il faut appeler la fonction adéquate. Une ligne est dessinée grâce à la fonction "line()" et une rectangle est dessiné en appelant la méthode "rect()". Chaque fonction nécessite des paramètres selon la forme que nous voulons dessiner. Par exemple, le code suivant :

size(600, 400);
rect(100, 100, 400, 200);

Nous donne la forme suivante :


Nous pouvons appeler plusieurs fonctions de dessin comme toutes les autres fonctions. Par exemple, le code suivant :

size(600, 400);
rect(100, 100, 400, 200);
line(100, 100, 500, 300);
line(100, 300, 500, 100);

Nous donne la forme suivante :


Il est possible de modifier l'ensemble des paramètres des dessin par appeler les fonctions adéquates avant d'appeler la fonction de dessin. Parmi ces paramètres, nous pouvons préciser la présence ou bien l'absence d'une ligne qui délimite la forme, la couleur de cette ligne et la couleur de remplissage. Par exemple, dans ce code :


size(600, 400);
noStroke();
fill(0, 200, 130);
rect(100, 100, 400, 200);

Nous avons demandé à Processing de ne pas dessiner une ligne autour de notre rectangle comme nous avons demandé que le remplissage aura la couleur 0 pour le rouge, 200 pour le vert et 130 pour bleu (un codage de 0 à 255 pour chaque couleur, d'autres modes existent sous Processing). Le résultat est :


Cela est concernant le dessin, comment créer des animations ? Voir partie 2.

Le trésor caché

L'évolution rapide des différentes technologies liées à la programmation et au développement complique de lus en plus la tâche du développeur. Être un développeur d'une technologie particulière veut dire suivre ses différentes avancées et faire la mise à jour de ses connaissances pour pouvoir exploiter et bénéficier des nouvelles versions et des nouvelles frameworks. Cette tâche devient très difficile vu le rythme suivant lequel les nouvelles propositions sont déployées. Ainsi, le développeur se trouve face à deux solutions.
La première solution est la spécialisation dans un ensemble donné de technologies. A mon avis, c'est la solution suivie par la plus part des développeurs. Chaque développeur choisit la technologie qu'il trouve meilleure pour lui (comme préférence personnelle) ou bien pour son travail (exigence professionnelle). Après plusieurs années d'expérience, il sera un expert dans ces technologies. La plus part des auteurs des livres font partie de cette catégorie.
La deuxième solution est l'apprentissage limité selon le besoin. Dans le cas où le développeur se trouve obligé de travailler sur plusieurs projets avec des technologies différentes, il peut apprendre une partie ou une API d'un langage ou d'une plate-forme donné suffisamment pour pouvoir satisfaire son engagement professionnel mais pas pour mériter le titre d'expert ou pour occuper le poste de consultant dans cette technologie. Je ferai beaucoup moins confiance dans un livre si je trouve que son auteur soit de cette catégorie.
Dans les deux cas, il restera probablement des trésors cachés. Et par trésor, je veux dire un outil, un langage, une API ou une extension que vous ne connaissez même pas l'extasient ou que vous n'utilisez pas son potentiel d'une manière correcte.
C'est mon cas avec le langage Processing. Processing : "(autrefois typographié Proce55ing) est une bibliothèque Java et un environnement de développement libre (sous licence GNU GPL), créé par Benjamin Fry et Casey Reas, deux artistes américains. Processing est le prolongement « multimédia » de Design by numbers, l'environnement de programmation graphique développé par John Maeda au Media Lab du Massachusetts Institute of Technology" (Wikipedia, simplement). Processing cache une grande partie de la complexité relative à la programmation Orientée Objet que nous trouvons dans Java. Il permet aussi de créer facilement des graphiques et des animations en implantant une grande partie des notions relatives à l'animation; la chose qui est complètement absente dans Java.
Dans mon cas, j'ai toujours utilisé les Applets pour implanter des animations de base à des fin pédagogiques et d'enseignement. Avec l'abondant des Applets, j'ai cherché une solution alternative et j'ai tombé sur Processing. Ma première impression était un regret profond de toutes les années que j'ai perdues en utilisant les Applets au moment où je pouvait faire appel à Processing. En effet, pour dessiner un carré, il vous suffit 2 lignes de code :

size(600, 400);
rect(100, 100, 400, 200);



Processing est le plus grand trésor sur lequel j'ai tombé cette année. Avec les possibilités qu'il offre (quelques exemples), il fera, probablement, partie de mon quotidien durant les prochains jours où  je tenterai de l'explorer et de l'exploiter.
Note Importante : Apprendre une technologie ou un langage ne se résume pas dans l'apprentissage du syntaxe de base ou bien de l'API de base seulement. Le développeur doit avoir des connaissances plus approfondie et doit pouvoir faire face aux problèmes et erreurs souvent rencontrés ou même non-documentées.