LOCODUINO

Forum de discussion
Dépôt GIT Locoduino
Flux RSS

jeudi 14 décembre 2017

12 visiteurs en ce moment

Processing pour nos trains

. Par : Pierre59

On a pas mal parlé de Processing dans le forum et dans quelques articles pour l’écriture de TCOs. On va présenter ici Processing en insistant sur les analogies et les différences entre Processing et Arduino, et en mettant l’accent essentiellement sur ce qui peut servir pour nos trains et tout particulièrement pour des TCOs.

Processing est très semblable à l’Arduino, c’est normal car Arduino s’est fortement inspiré de Processing. Les deux sont en fait très différents vis à vis de leur utilisation, mais très complémentaires (surtout pour nous car ils communiquent entre eux très facilement (par la ligne USB/série). Mais Arduino et Processing peuvent aussi communiquer assez facilement par Ethernet ou WiFi.

Arduino est destiné à faciliter la programmation d’applications sur micro-contrôleurs. Processing est destiné à faciliter la programmation d’applications d’arts visuels numériques (dessins, animations, manipulations d’images … ) en 2D et en 3D.

Arduino nécessite d’avoir un ordinateur et un module Arduino, Processing nécessite juste un ordinateur. Dans les deux cas l’ordinateur peut être sous Windows, Mac   OS ou Linux.

Il y a une différence essentielle entre Processing et Arduino : Arduino est basé sur C/C++, tandis que Processing est basé sur Java. Comme Java et C/C++ sont très proches au niveau syntaxe, il n’y a pratiquement aucune différence sur les programmes simples, mais des différences apparaissent vite sur des programmes plus élaborés comme ceux manipulant des objets. Ces différences sont surtout syntaxiques et non conceptuelles (la programmation objet reste la même). On reparlera de ces différences par la suite.

Les distributions d’Arduino et de Processing, trouvées sur l’internet, sont essentiellement constituées d’un IDE (Environnement de Développement Intégré en français), permettant la saisie, les vérifications et l’exécution de programmes, ainsi que de bibliothèques. Ces distributions intègrent un compilateur adapté (C/C++ pour Arduino, Java pour Processing). Les deux IDE sont écris en Java.

On trouvera tout ce qu’il faut savoir sur l’installation, la mise en route et l’utilisation de Processing dans l’article Démarrer en Processing (1).

L’IDE Arduino s’exécute sur un ordinateur, mais le programme qui en résulte s’exécute sur un micro-contrôleur (Arduino). L’IDE Processing s’exécute sur un ordinateur et le programme qui en résulte s’exécute aussi sur un ordinateur. Les deux IDE sont très semblables, mais l’IDE Processing est beaucoup plus élaboré, les erreurs de syntaxe sont signalées au fil de la frappe du programme (à partir de Processing3), les erreurs d’exécution sont bien repérées et commentées et il existe même un débogeur.

Voici le "programme minimum" sur les deux IDE, la filiation entre Processing et Arduino est nette :

Langage

Processing et Arduino ne sont pas des langages, mais juste une sur-couche au dessus de Java ou de C/C++. Cette sur-couche comporte :

  • des constantes usuelles prédéfinies, exemples :
constantes
Processing Arduino
HALF_PI INPUT_PULLUP
QUARTER_PI HIGH
  • des fonctions ou méthodes prédéfinies, exemples :
fonctions
Processing Arduino
triangle() digitalWrite()
color() delay()
mouseClicked() analogRead()
  • des objets prédéfinis, exemples :
objets
Processing Arduino
PShape Serial
PImage String
  • des bibliothèques

Similitudes

Comme Java s’est fortement inspiré de C, beaucoup de choses ont la même syntaxe en Processing et en Arduino, c’est le cas :

  • des types : boolean, int, char, byte, float, long, double. (les entiers sont obligatoirement signés en Java et le type "boolean" est une vrai type et pas un entier renommé)
  • des opérateurs : + - >= && ++ *= …
  • des structures de contrôle : if for while …
  • des fonctions

Différences

Les tableaux sont traités légèrement différemment, essentiellement au niveau des déclarations. En Java les tableaux sont une sorte d’objet, ce qui implique l’utilisation d’un new pour les créer, l’utilisation est la même, exemples :

tableaux declaration
Processing Arduino
int[] t1=new int[10] ; int t1[10] ;
int* t1=new int[10] ;
float[][] t2=new float[3][3] ; float t2[3][3] ;
int[] t3=new int[n] ; int* t3=new int[n] ;

Ce changement de syntaxe est important, la taille d’un tableau C++ est une constante (la taille est définie à l’écriture du programme), mais on peut aussi utiliser des tableaux dynamiques (la taille est définie à l’exécution du programme) avec l’opérateur new. La taille d’un tableau Java est une constante ou une variable et, cerise sur le gâteau, cette taille est accessible avec l’attribut length, exemple :

  1. int n=5;
  2. int[] t=new int[n];
  3. int l=t.length; // la taille du tableau

La déclaration d’un tableau dynamique à deux dimensions en C++ est trop compliquée pour être décrite ici.

En Java les chaines de caractères sont exclusivement des objets, la classe associée est String (cette classe est apparue aussi dans les versions récentes de l’Arduino). Qui dit objet dit méthodes, la classe String comporte effectivement quelques méthodes (voir la référence Processing).
On peut obtenir la taille d’une chaîne avec la méthode length(), il y a aussi un opérateur de concaténation noté + (attention sur les tableaux length est une variable donc pas de parenthèses, sur les chaînes c’est une méthode donc parenthèses), exemple :

  1. String s="d";
  2. s="abc"+s+"ef"; // s = "abcdef"
  3. int l=s.length(); // la taille de la chaîne

Au niveau de l’affichage on retrouve les fonctions/méthodes print() et println(), dans Arduino ces deux méthodes viennent de la classe Serial , tandis qu’avec Processing ce sont des fonctions. Les affichages se font dans la "console" pour Processing et dans le "moniteur série" pour Arduino. En Processing on peut utiliser l’opérateur de concaténation (+) pour simplifier certaines écritures, exemple :

affichage
Processing Arduino
print(x) ; Serial.print(x) ;
println() ; Serial.println() ;
println(a+b) ; Serial.println(a+b) ;
println("x : "+x+" y : "+y) ; Serial.print("x : ") ; Serial.print(x) ;
Serial.print(" y : ") ; Serial.println(y) ;

Restrictions

Java se veut un langage sûr, ce qui implique que toutes les sources fréquentes d’erreurs on étés éliminées par rapport à C, il n’y a pas de goto , surtout pas de pointeurs explicites avec leurs manipulations et rien qui puisse changer un type pour faire du "bricolage".
Java est très typé, il fait beaucoup de contrôles à la compilation et tout ce qui n’a pu être vérifié à la compilation est vérifié à l’exécution, par exemple les indices des tableaux ou les "pointeurs" nuls (pour les objets).

C/C++ a plusieurs façons de passer des paramètres à une fonction/méthode : par valeur, par référence ou par pointeur. Java n’a que des passages par valeur, ce qui implique qu’aucun résultat ne peut être transmis par les paramètres (sauf pour les objets).

Processing

On va maintenant se recentrer sur Processing, sur son fonctionnement et sur les outils disponibles en se limitant à ce qui peut servir pour le train et tout particulièrement pour dessiner des TCOs.

Fonctionnement

Les structures de base d’un programme Processing ou Arduino sont très semblables. On retrouve la fonction setup() prévue pour les initialisations et qui n’est exécutée qu’une fois au début. Avec Arduino on a une fonction loop() qui est exécutée indéfiniment, avec Processing on a une fonction équivalente draw() qui est aussi exécutée indéfiniment et qui est prévue pour dessiner le "dessin" qui y est décrit dans une fenêtre. Si on change tout ou partie du "dessin" (couleur, emplacement, taille, orientation …) ces modifications sont répercutées quasi immédiatement, c’est comme cela que l’on fait des "animations". Pour nos trains on ne fait pas vraiment d’animation, mais pour un TCO il y a régulièrement des changements à répercuter : changement de position d’une aiguille, changement de couleur d’un signal ou d’une zone, avancement des trains virtuels, … .

Voici le squelette d’une application Processing pour TCO :

  1. void setup() {
  2. // initialisations
  3. }
  4.  
  5. void draw() {
  6. // décrire le dessin en faisant éventuellement les modification nécessaires
  7. }

Outils

Pour faire des "dessins" on a besoin d’outils. La fenêtre créée et ouverte automatiquement à l’exécution par Processing est un espace graphique dans lequel on peut dessiner :

  • des dessins prédéfinis : points, lignes, arcs, rectangles, triangles, ellipses, quadrilatères, … en utilisant les fonctions prédéfinies éponymes (en anglais) ;
  • des "formes" diverses ("shapes" en anglais) par enchaînement de lignes et de courbes de Béziers cubiques et quadratiques en utilisant la classe PShape et ses méthodes ;

Dans les deux cas on peut avoir un contour dessiné ou pas, avec choix de la couleur, de l’épaisseur, etc., en utilisant les méthodes prédéfinies stroke(), noStroke(), … On peut aussi avoir un remplissage ou pas, avec choix de la couleur, en utilisant les méthodes prédéfinies fill() et noFill(). Ces méthodes s’appliquent de façon permanente à tout l’espace graphique, il faut donc faire les changements avant de dessiner autre chose qui n’aurait pas les mêmes besoins. La classe PShape a aussi les mêmes méthodes pour les formes.

On peut aussi faire des "transformations" : translations, rotations, zooms, … soit sur tout l’espace graphique soit sur une "forme" particulière.

On peut aussi traiter des événements issus de la souris (clic, déplacement, …) ou du clavier (frappe d’une touche, …)

Pour plus de détails sur les outils consulter le site Processing : manuel de référence.

Exemples

On va écrire en Processing une "brique" de TCO, c’est à dire un "pavé" contenant un bout de rail droit, puis on transformera ce rail droit en aiguillage et pour terminer on l’animera (mouvement de l’aiguillage). Un TCO réel se fait en juxtaposant de tels pavés, mais avec une plus grande variété de pavés.

Tous les pavés sont construits sur une grille virtuelle de 10x10 points, l’épaisseur de la voie étant de 3 (1/3 de pavé) :

Les pavés sont tout petits et un "zoom" sera nécessaire.

Généralement en informatique graphique les éléments sont repérés par le coin supérieur gauche du rectangle englobant, l’axe des abscisses (axe des x) va de gauche à droite (comme en maths), l’axe des ordonnées (axe des y) va de haut en bas ( ! inverse des maths) :

Les quatre points noirs de la grille précédente (qui forment les quatre coins du rectangle représentant le bout de rail) ont donc comme coordonnées (x,y) : 0,3 9,3 9,6 0,6 .

Et ce bout de rail s’inscrit dans un pavé carré ayant comme coordonnées : 0,0 9,0 9,9 0,9 .

Le pavé droit

On commence par un bout de rail droit, c’est en fait un rectangle horizontal dessiné dans le pavé, les coordonnées de son coin supérieur gauche dans le pavé sont (0,3) zéro en abscisse (x) et trois en ordonnée (y), sa largeur est celle d’un pavé (9) et sa hauteur 1/3 de pavé donc 3. Pour que le pavé soit bien visible on va lui appliquer un taux de zoom de 5. Cela donne le programme :

  1. int GRIS=#7F7F7F,NOIR=#000000; // des couleurs
  2. int zoom=5; // le taux de zoom
  3.  
  4. void setup() {
  5. println("l : "+width+" h : "+height); // largeur/hauteur de la fenetre
  6. }
  7.  
  8. void draw() {
  9. scale(zoom); // zoom x5
  10. noStroke(); // pas de bordures pour le rectangle
  11. fill(NOIR); // remplissage du rectangle en noir
  12. rect(0,3,9,3); // dessin du rectangle (x origine, y origine, largeur, hauteur)
  13. }

Il y a d’abord des constantes globales : des couleurs et le taux de zoom. Puis la fonction setup() dans lequel on affiche la taille de la partie graphique de la fenêtre, c’est dans cette fenêtre que l’on va dessiner.

La fonction draw() est chargée de dessiner notre pavé : dans l’espace graphique de la fenêtre.

  • on applique à l’espace graphique un zoom (de 5) pour que cela soit bien visible
  • on ne veut pas de bordure à notre futur rectangle
  • on veut que notre futur rectangle soit rempli avec la couleur noire
  • on dessine le rectangle du bout de rail en (0,3) avec un largeur de 9 et une hauteur de 3

L’exécution donne :

C’est pas très joli, on va centrer notre pavé. On pourrait jouer sur les coordonnées du rectangle (0,3) mais on va le faire ici en utilisant une transformation la "translation". Comme c’est le coin en haut à gauche du pavé qui est la base du dessin alors que l’on veut que cela soit le centre du pavé qui soit au centre de la fenêtre, cela nécessite des petits calculs avec la taille de la fenêtre et la taille du futur pavé pour que le centre du pavé soit au centre de la fenêtre, en n’oubliant pas que le pavé sera zoomé. Cela donne la translation suivante qui est ajoutée comme première ligne de la méthode draw() :

  1. translate((width-9*zoom)/2,(height-9*zoom)/2); // centrage dans la fenetre

width et height sont les dimensions largeur/hauteur de l’espace graphique de la fenêtre.

Pour bien visualiser les limites du pavé on va les dessiner. Ces limites sont un carré, on va donc dessiner un carré transparent non rempli (en fait un rectangle) avec un pourtour de 1 pixel. Comme on veut garder un pourtour de 1 pixel il faut dessiner avant le zoom en tenant compte de ses conséquences. Voici les trois instructions à ajouter juste après la translation dans la méthode draw() :

  1. stroke(NOIR); // pourtour noir
  2. noFill(); // pas de remplissage
  3. rect(0,0,9*zoom,9*zoom); // dessin du pourtour du carre (epaisseur 1 pixel)

Le programme complet :

  1. int GRIS=#7F7F7F,NOIR=#000000; // des couleurs
  2. int zoom=5; // le taux de zoom
  3.  
  4. void setup() {
  5. println("l : "+width+" h : "+height); // largeur/hauteur de la fenetre
  6. }
  7.  
  8. void draw() {
  9. translate((width-9*zoom)/2,(height-9*zoom)/2); // centrage dans la fenetre
  10.  
  11. stroke(NOIR); // pourtour noir
  12. noFill(); // pas de remplissage
  13. rect(0,0,9*zoom,9*zoom); // dessin du pourtour du carre (epaisseur 1 pixel)
  14.  
  15. scale(zoom); // zoom x5
  16. noStroke(); // pas de bordures pour le rectangle
  17. fill(NOIR); // remplissage du rectangle en gris
  18. rect(0,3,9,3); // dessin du rectangle
  19. }

Et le résultat d’exécution :

Aiguillage

On va passer à l’aiguillage, un aiguillage a une partie droite (voie directe) et une partie courbe (voie déviée), on a déjà une partie droite, il ne manque que la partie courbe. Pour cette partie courbe il n’y a aucune figure géométrique classique, on va devoir créer une "forme" spécifique, cette forme spécifique sera définie par son "contour" que l’on pourra remplir (ou pas). Le contour se compose de deux droites et de deux arcs de cercles. La fonction prédéfinie arc() de Processing ne convient pas, il faut donc avoir recours aux courbes de Bezier (quadratiques ou cubiques) pour avoir des courbes pour notre contour. Les courbes de Bezier quadratiques ont un point de contrôle, les cubiques deux points de contrôle. On utilisera ici deux courbes de Bezier quadratiques (sans plus de détails concernant les courbes de Bezier). Le dessin ci-dessous montre les coordonnées des points sur la grille, ainsi que les deux points de contrôle en rouge (points d’intersection des tangentes aux deux extrémités) :

Pour décrire un contour en Processing il faut un objet PShape avec lequel on décrit le contour point par point avec les méthodes :

  • vertex() qui définit un nouveau point relié au précédent par un segment de droite (les paramètres sont les coordonnées du point d’arrivée)
  • quadraticVertex() qui définit un nouveau point relié au précédent par une courbe de Bezier quadratique (les paramètres sont les coordonnées du point de contrôle et les coordonnées du point d’arrivée)

Cette description n’est faite qu’une fois, elle est donc mise dans le setup(). Le dessin de la partie courbe de l’aiguille se fera en remplissant le contour (en gris), ce dessin se fera dans la fonction draw() après le dessin de la partie droite par appel de la méthode shape(). Voila le programme complet :

  1. int GRIS=#7F7F7F,NOIR=#000000; // des couleurs
  2. int zoom=5; // le taux de zoom
  3.  
  4. PShape arc; // la partie deviee de l'aiguille
  5.  
  6. void setup() {
  7. // size(100,100,P2D); // necessaire pour certaines versions de Processing
  8. println("l : "+width+" h : "+height); // largeur/hauteur de la fenetre
  9.  
  10. arc=createShape(); // creation de la forme
  11. arc.beginShape(); // debut du contour de la forme
  12. arc.noStroke(); // pas de dessin du contour
  13. arc.vertex(0,3); // point de depart
  14. arc.quadraticVertex(4,3, 8,-1); // courbe Bezier quadratique
  15. arc.vertex(10,1); // ligne droite
  16. arc.quadraticVertex(5,6, 0,6); // courbe Bezier quadratique
  17. arc.endShape(CLOSE); // fermeture du contour de la forme (ligne droite)
  18. }
  19.  
  20. void draw() {
  21. translate((width-9*zoom)/2,(height-9*zoom)/2); // centrage dans la fenetre
  22.  
  23. stroke(NOIR); // pourtour noir
  24. noFill(); // pas de remplissage
  25. rect(0,0,9*zoom,9*zoom); // dessin du pourtour du carre (epaisseur 1 pixel)
  26.  
  27. scale(zoom); // zoom x5
  28.  
  29. noStroke(); // pas de bordures pour le rectangle
  30. fill(NOIR); // remplissage du rectangle en noir
  31. rect(0,3,9,3); // dessin du rectangle
  32.  
  33. arc.setFill(GRIS); // remplissage de la forme en gris
  34. shape(arc,0,0); // dessin de la forme (forme, x origine, y origine)
  35. }

Ce qui donne :

Sur certaines versions de Processing, le résultat est légèrement différent comme le montre la partie droite de l’image ci-dessous.

Pour résoudre ce problème, il suffit de rajouter en première ligne du setup, l’instruction :
size(100,100,P2D);
P2D pour Processing 2D de même qu’il existe P3D. Cette instruction est mise en commentaire en première ligne du setup dans le programme proposé ; au besoin, il suffit de décommenter la ligne.

L’animation

Pour terminer on va faire une petite "animation", la manœuvre de l’aiguillage en cliquant dessus. Pour cela il faut prendre en compte les événements de souris et tout particulièrement les événements de type "clic", cela se fait avec la fonction prédéfinie mouseClicked() qui est appelée automatiquement par Processing pour chaque clic dans la fenêtre.

On va ajouter à notre programme une variable booléenne globale bsc (bascule) qui sera inversée à chaque clic, cette inversion se faisant dans la fonction mouseClicked() qui gère l’événement :

  1. void mouseClicked() { bsc=!bsc; } // evenement clic (inversion de la bascule)

Il ne reste plus qu’a prendre en compte l’état de la bascule, pour jouer sur l’ordre des dessins (de la partie droite et de la partie courbe) ainsi que sur leur couleur (noir ou gris). Voila le programme complet :

  1. int GRIS=#7F7F7F,NOIR=#000000; // des couleurs
  2. int zoom=5; // le taux de zoom
  3.  
  4. PShape arc; // la partie deviee de l'aiguille
  5.  
  6. void setup() {
  7. // size(100,100,P2D); // necessaire pour certaines versions de Processing
  8. println("l : "+width+" h : "+height); // largeur/hauteur de la fenetre
  9.  
  10. arc=createShape(); // creation de la forme
  11. arc.beginShape(); // debut du contour de la forme
  12. arc.noStroke(); // pas de dessin du contour
  13. arc.vertex(0,3); // point de depart
  14. arc.quadraticVertex(4,3, 8,-1); // courbe Bezier quadratique
  15. arc.vertex(10,1); // ligne droite
  16. arc.quadraticVertex(5,6, 0,6); // courbe Bezier quadratique
  17. arc.endShape(CLOSE); // fermeture du contour de la forme (ligne droite)
  18. }
  19.  
  20. void draw() {
  21. translate((width-9*zoom)/2,(height-9*zoom)/2); // centrage dans la fenetre
  22.  
  23. stroke(NOIR); // pourtour noir
  24. noFill(); // pas de remplissage
  25. rect(0,0,9*zoom,9*zoom); // dessin du pourtour du carre (epaisseur 1 pixel)
  26.  
  27. scale(zoom); // zoom x5
  28. noStroke(); // pas de bordures pour les formes
  29. if (bsc) { arc.setFill(NOIR); shape(arc,0,0); fill(GRIS); rect(0,3,9,3); }
  30. // arc puis rect
  31. else { fill(NOIR); rect(0,3,9,3); arc.setFill(GRIS); shape(arc,0,0); }
  32. // rect puis arc
  33. }
  34.  
  35. boolean bsc=true; // sens de l'aiguille (bascule)
  36.  
  37. void mouseClicked() { bsc=!bsc; } // evenement clic (inversion de la bascule)

Voila ce que cela donne à l’exécution, quand on clique :

En réalité, juste pour les besoins de cet article, cette image est un GIF animé et elle n’est pas sensible aux clics.

La manœuvre de l’aiguillage se fait quelque soit l’endroit où l’on clique dans la fenêtre, on pourrait facilement la restreindre à un clic dans le pavé en utilisant les coordonnées du clic (avec les variables prédéfinies mouseX et mouseY).

Les deux dessins que l’on a créés précédemment (partie droite et partie courbe) plus le dessin d’une partie biaise peuvent facilement êtres utilisés pour produire tous les pavés dont on a besoin pour un TCO, juste en appliquant des transformations (symétries et rotations) à ces trois dessins. D’autres choix de dessins de base peuvent êtres faits.

Voici un exemple de palette possible :

La septième icône est une TJD alors que la huitième est une TJS.

Toutes les variantes dont on aura besoin pour un TCO peuvent êtres obtenues, à partir des éléments de la palette, par des rotations et/ou des symétries que l’on peut réaliser avec des clics en jouant (par exemple) sur des clics gauches et des clics droits (pour effectuer des rotations ou des symétries).

Voici un exemple de TCO, réalisé pour le Locodrome (pour plus d’informations à ce sujet voir l’article Un gestionnaire en C++ pour votre réseau (1) et les suivants), en suivant ces principes (des dessins ont étés rajoutés : courbes, textes, signaux, boutons, … ) :

Objets

Dans tout ce que l’on a écrit jusqu’ici on ne se rend pas trop compte que Processing c’est en fait du Java, cela pourrait être tout aussi bien du C/C++. En passant aux objets, des différences, essentiellement de syntaxe (écriture des programmes) et un peu de sémantique (signification des programmes), vont arriver.

La lecture de cette partie peut être omise si on ne fait pas d’objets. Sinon les principes de la programmation objet restent les mêmes avec les deux langages (voir la série d’articles Le monde des objets (1)). On va insister ici sur les différences (syntaxiques et sémantiques) entre les deux langages.

classes

Commençons par la forme générales des classes :

classes
Java C++
class A  {        
 int n; // variable
 A() { … } // constructeur
 char m() { … } // méthode
}
class A {
 int n; // variable
 A() { … } // constructeur
 char m() { … } // méthode
};

Pas de différences à part le " ;" en fin de classe pour C++.

Petite différence pour les classes abstraites (classes non instanciables, on ne peut pas créer d’objets directement avec elles), en Java on met le mot clé abstract devant la classe tandis que pour C++ il doit y avoir, au moins, une méthode "virtuelle pure".

variables

Pas de différences sur l’écriture des variables.

protections

Pour la protection des variables, constructeurs et méthodes on retrouve les trois mots clés public, protected et private (on peut aussi ne rien mettre) dans les deux langages. La protection en Java se fait élément par élément, tandis qu’en C++ elle se fait par paquet :

protections
Java C++
class A  {        

 private int i;
 private char c;

 public float x;
 public double y;
}
class A {          
 private:
   int i;
   char c;
 public:
   float x;
   double y;
};

constructeurs

Pas de différence sur l’écriture des constructeurs (sauf en cas d’héritage, voir plus loin).

méthodes

La grosse différence dans l’écriture des méthodes est que celles de C++ sont écrites en deux parties, l’en tête dans la classe et le reste en dehors, des méthodes peuvent êtres complètement dans la classe mais dans ce cas elles sont réputées "inline". En Java les méthodes sont complètement dans la classe. Exemple :

méthodes
Java C++
class A  {        
 char m() { … } // méthode
}  

// rien
class A {
 char m();  // en tête de méthode
};  

A :: char m() { … } // méthode

Autre différence qui viendra avec l’héritage, en Java toutes les méthodes sont réputées "virtuelles", tandis qu’en C++ il faut utiliser le mot clé virtual pour obtenir le même effet.

héritage

Contrairement à C/C++ Java n’a pas d’héritage multiple (qui peut être une source d’erreurs inextricables) mais un "ersatz" en remplacement : les interfaces. Cette différence conduit à une syntaxe différente pour tout ce qui a rapport à l’héritage, essentiellement au niveau de la déclaration d’héritage et au niveau des constructeurs, exemples (pour simplifier les protections ont été omises) :

héritage
Java C++
class A  {        
 int n;
 A(int a) { n=a; }
 …
}

class B extends A { // hérite de A
 int m;
 B(int a,int b) { super(a); m=b; }
 …
}
class A {
 int n;
 A(int a) { n=a; }
 ...
};

class B : A { // hérite de A
 int m;
 B(int a,int b) : A(a) { m=b; }
 …
};

Noter l’écriture super en Java, ici super() est utilisé comme une méthode pour appeler le constructeur hérité (cette notation est aussi utilisable dans une méthode pour appeler la même méthode héritée void m() { … super.m(); … }).

manipulation des objets

  • C++ permet de manipuler les objets de plusieurs façons : comme des variables normales, par références ou par pointeurs, on se limitera ici à la manipulation par pointeurs qui permet de profiter pleinement des mécanismes issus de l’héritage, pour plus d’informations à ce sujet voir l’article Un gestionnaire en C++ pour votre réseau (1) et les suivants. Dans le cas de pointeurs sur les objets, l’opérateur permettant d’accéder aux composants d’un objet est ->.
  • Java n’a pas de pointeurs explicites, mais Java utilise systématiquement des pointeurs pour manipuler les objets, ces pointeurs sont implicites et ne sont pas accessibles à l’utilisateur (il n’y a pas d’opérateur ->) et l’accès aux composants se fait avec l’opérateur . (qui existe aussi en C++).

Le pointeur sur rien (pointeur nul) est souvent utilisé, il s’écrit null en Java mais NULL en C++ (on utilise aussi la notation this pour avoir un pointeur sur l’objet dans lequel on est).

Quelques manipulations d’objets :

manipulation d’objets
Java C++
class A  {        
 int n;
 void m() { … }
}  

A a=new A(); // objet

a.n++; // variable
a.m(); // méthode
if (a==null) … // test
class A {
 int n;
 void m() { … }
};

A* a=new A(); // objet

a->n++; // variable
a->m(); // méthode
if (a==NULL) … // test

autres

Il y a beaucoup d’autres différences, tant syntaxiques que sémantiques, entre Java et C++, on s’est limité ici aux choses simples servant pour le train.

Il faut signaler la souplesse de Java dans l’ordre des déclarations (surtout au niveau des objets), alors qu’en C++ il est assez difficile d’organiser des classes qui s’utilisent mutuellement, comme dans le gestionnaire du Locodrome présenté dans l’article Un gestionnaire en C++ pour votre réseau (4) (sketch Arduino à télécharger).

Un gros exemple d’un algorithme de recherche récursive d’itinéraires écrit en Java et en C++ peut être trouvé sur le forum : itinéraires (réponse #64).

Certains (des mauvaises langues) disent que Java est verbeux, sur l’exemple précédent vous pouvez constater que C++ a 50% de lignes en plus !

Bilan

Le but de cet article était de présenter Processing/Java, cela s’est fait en faisant un parallèle avec Arduino/C++, on s’est focalisé sur ce qui touche au graphisme 2D et tout particulièrement à l’écriture de TCOs, ainsi que sur la programmation objet de base.

Dans des articles à venir, on parlera de la communication (par la ligne USB/série ou autre) entre Processing et Arduino. On parlera aussi de la construction pas à pas de TCOs simples en Processing, bien évidemment en utilisant la programmation objet, ainsi que de leurs interfaces avec des "gestionnaires de réseau" sur Arduino.

Pour aller plus loin avec Processing et/ou Java, on peut consulter les documentations de ces langages.

2 Messages

Réagissez à « Processing pour nos trains »

Qui êtes-vous ?
Votre message

Pour créer des paragraphes, laissez simplement des lignes vides.

Lien hypertexte

(Si votre message se réfère à un article publié sur le Web, ou à une page fournissant plus d’informations, vous pouvez indiquer ci-après le titre de la page et son adresse.)

Rubrique « Programmation »

Comment gérer le temps dans un programme ?

La programmation, qu’est ce que c’est

Types, constantes et variables

Installation de l’IDE Arduino

Répéter des instructions : les boucles

Les interruptions (1)

Instructions conditionnelles : le if … else

Instructions conditionnelles : le switch … case

Comment gérer l’aléatoire ?

Calculer avec l’Arduino (1)

Calculer avec l’Arduino (2)

Les structures

Systèmes de numération

Les fonctions

Trois façons de déclarer des constantes

Transcription d’un programme simple en programmation objet

Ces tableaux qui peuvent nous simplifier le développement Arduino

Les chaînes de caractères

Trucs, astuces et choses à ne pas faire !

Processing pour nos trains

Arduino : toute première fois !

Démarrer en Processing (1)

Le monde des objets (1)

Le monde des objets (2)

Le monde des objets (3)

Le monde des objets (4)

Les pointeurs (1)

Les pointeurs (2)

Les Timers (I)

Les Timers (II)

Les Timers (III)

Les Timers (IV)

Les Timers (V)

Bien utiliser l’IDE d’Arduino (1)

Bien utiliser l’IDE d’Arduino (2)

Piloter son Arduino avec son navigateur web et Node.js (1)

Piloter son Arduino avec son navigateur web et Node.js (2)

Piloter son Arduino avec son navigateur web et Node.js (3)

Les derniers articles

Ces tableaux qui peuvent nous simplifier le développement Arduino


bobyAndCo

Processing pour nos trains


Pierre59

Piloter son Arduino avec son navigateur web et Node.js (3)


bobyAndCo

Piloter son Arduino avec son navigateur web et Node.js (2)


bobyAndCo

Démarrer en Processing (1)


DDEFF

Piloter son Arduino avec son navigateur web et Node.js (1)


bobyAndCo

Arduino : toute première fois !


Christian

Bien utiliser l’IDE d’Arduino (2)


Thierry

Bien utiliser l’IDE d’Arduino (1)


Christian, Dominique, Jean-Luc, Thierry

Les Timers (V)


Christian

Les articles les plus lus

Les interruptions (1)

Les Timers (I)

Calculer avec l’Arduino (1)

Répéter des instructions : les boucles

Piloter son Arduino avec son navigateur web et Node.js (1)

Les chaînes de caractères

Comment gérer le temps dans un programme ?

Les Timers (II)

Instructions conditionnelles : le if … else

Installation de l’IDE Arduino