Jump to: navigation, search

Course:Graphisme programmé 2017

Graprog17-cover.png

Description

Ce cours vise à découvrir les algorithmes sous-jacents aux motifs ainsi qu'à la maitrise de concepts en programmation java.

Cette vidéo a été faite sur base d'une ancienne version du projet. Elle montre comment encoder les équations dans les champs textes.

Processing

Code source: download, licence MIT.

Dépôt Github.

Ce sketch utilise la librairie controlP5, installable via le gestionnaire d'extensions de l'IDE.

Version locale du code ici, au cas où...

Prise en main

Une fois le sketch ouvert, lancer. Une barre d'outil est disponible en haut de la fenêtre.

Champs

  • size - slider - permet de régler la taille du motif;
  • translatex - textfield - permet d'appliquer une forme à la répétion du motif sur l'axe x;
  • translatey - textfield - permet d'appliquer une forme à la répétion du motif sur l'axe y;
  • rotation - textfield - permet d'appliquer une forme sur chaque impression du motif;
  • apply - button - lance la génération d'un nouveau motif;
  • save - button - sauve le motif généré;
  • FGCOLOR - color picker - applique un filtre coloré sur le motif;
  • BGCOLOR - color picker - couleur d'arrière-plan.


Les variables disponibles dans les champs textes sont les suivants:

  • x - int - numéro de la colonne;
  • y - int - numéro de la ligne;
  • s - float - taille du motif.

Démarrage

Les chapitres ci-dessous expliquent l'utilisation de l'application par le développement d'un exemple. Les équations ci-dessous vont utiliser massivement le modulo[1].

Les champs contiennent les valeurs par défaut pour remplir la fenêtre, càd:

  • x*s horizontalement: on avance de la taille du motif chaque colonne;
  • y*s verticalement: on avance de la taille du motif chaque ligne;
  • 0 rotation: pas de rotation.


Le motif étant une croix noire au centre de laquelle est placé un carré blanc sur pointe, le résultat est similaire à du carrelage.

Motifs 650.png

Décalage vertical

Pour décaler le motif de la moitié de sa hauteur à chaque colonne, on adapte translationy (verticalement donc). À chaque fois que le numéro de la colonne est impair, on ajoute la moitié de la hauteur à la poistion du motif.

Ce qui donne, algorithmiquement:

(y*s) + ( (x%2) * s / 2 )

Pour la première colonne, ((x%2) * s / 2 )x = 0, ce qui donne 0 * s / 2 donc 0. La position verticale est égale à y*s.

Dans la deuxième colonne, x = 1. De là, la position verticale du motif devient y*s + s/2. La deuxième rangée, la quatrième, etc. sont donc décalées de la moitié de la hauteur du motif.

Motifs 649.png

Rotation

Nous allons maintenant faire pivoter d'un quart de tour chaque colonne. Cette opération se passe dans le champ rotation.

Le motif étant identique si il est pivoté à 90°, il suffit donc de le faire pivoter de 45° toutes les colonnes impaires.

Ce qui donne, algorithmiquement:

(x%2) * 45
  • première colonne: 0 * 45, donc ;
  • deuxième colonne: 1 * 45, donc 45°;
  • troisième colonne: 0 * 45, donc ;
  • etc.


Motifs 648.png

Raccords

Il faudrait maintenant que les motifs soit raccordés les uns aux autres. Pour ce faire, il faut reduire l'espace entre les colonnes et les lignes de manière régulière.

Nous allons donc modifier le champ translationx et translationy.

Dans le champ translationx, nous allons retirer de manière contsante une fraction de la taille du motif.

( x*s ) - ( x *s * 0.55 )

A chaque colonne, nous n'avançons donc plus que de 45% de la taille du motif. Le chiffre 0.55 a été choisi empiriquement pour que la jonction entre deux colonnes soit invisible.

Pour le champ translationy, le calcul est un peu plus complexe. Le rapprochement horizontal a rapproché les bords des motifs. Il ne faut donc plus retirer 55% de la hauteur mais seulement 5%., tout en conservant l'alternance.

Ce qui donne, algorithmiquement:

 (y*s) + ( (x%2) * s / 2 ) + ( y* s * 0.05 )

Motifs 647.png

Couleurs

Pour finir, la réaction du motif peut être testée sur différents fonds de couleur en jouant avec le color picker.

Motifs 651.png

Programmation

Ce sketch utilise du rendu sur texture[2] pour le motif et le rendu final.

De plus, le sketch utilise 3 notions s'appuyant sur du code natif Java[3], c'est-à-dire qui ne sont pas disponibles dans le framework processing:

  • multi-threading;
  • interprétation de javascript;
  • ouverture d'une fenêtre de sauvegarde.

Rendu sur texture

Le sketch utilise deux PGraphics, voir Motifs ligne 8 et 9:

PGraphics motif;
PGraphics render;

Le PGraphics motif, équivalent à un tampon qui sera répété sur toute la fenêtre, est créé par la méthode ligne 64:

void createMotif()

Lors du lancement du rendu, si l'utilisateur a modifé la taille du motif (via le slider size), un nouveau motif est généré. Voir méthode apply(), lignes 108 à 112:

 if ( size != motifsize ) {
   motifsize = size;
   msh = motifsize * 0.5;
   createMotif();
 }

Il est donc important d'utiliser des tailles relatives dans la création du motif, pour conserver son intégrité quelque soit la taille demandée. Par exemple, utiliser motifsize / 4 plutôt que 10 pour fixer la taille d'un carré.

Note: l'épaisseur des traits, strokeWeight(), pourrait aussi être définie en relatif.

Le PGraphics render, qui contiendra le résultat des équations, est lui créé une fois pour toute à la ligne 52 de Motifs:

render = createGraphics( width, height );
render.beginDraw();
render.background( 0,0 );
render.endDraw();

Le fond des deux textures est transparent, ce qui permettra plus tard de modifier la couleur du fond de la fenêtre sans regénérer la texture.

L'utilisation d'une texture de rendu est essentielle pour le mutli-threading. Durant le dessin dans cette texture (voir Rendering, lignes 89 à 113), il est impossible de l'afficher. Pour afficher une texture à l'ećran, il faut en effet la refermer, ce qui reviendrait à interrompre le rendu à chaque draw(). Bien que ce soit possible, la programmation d'un tel mécanisme est relativement complexe et n'a pas été implémenté dans cet exemple.

Note: il est possible de modifier la taille de la texture render sans altérer le fonctionnement de l'application.

Multi-tâche

Le multi-threading (ou multi-tâche) est une architecture logicielle permettant d'effectuer plusieurs tâches simultanément.

La boucle d'animation de processing, càd le processus qui appelle la méthode draw() à intervales réguliers, est un thread[4].

Dans le code de ce sketch, le tab Rendering contient le code source d'une classe héritant de l'objet Thread, disponible en java natif. Aucun import n'est nécessaire puisque processing utilise cet objet.

Cet objet sert à faire le rendu de l'image sans bloquer le thread principal de dessin. Il devient donc possible d'afficher une barre de chargement, représentant l'avancée du rendu.

Motifs 654.png

Le thread de rendu est créé dans la méthode apply(), à la ligne 115

rthread = new RenderThread();

et est démarré à la ligne 123.

rthread.start();

Quand le thread de rendu a terminé, détruit à la ligne 176:

if ( rthread.isFinished() )
    rthread = null;

Script engine

Permettre la modification d'équations au runtime[5] pose un problème lorsqu'on utilise un language compilé[6]. En effet, la modification du calcul appliqué à chaque cellule nécéssite l'arrêt du programme, la modification du code, sa recompilation et le re-lancement de l'exécutable. Cette approche est lourde quand il s'agit de tester différentes équations.

Java met à disposition un interpréteur de Javascript[7]

Cet interpréteur n'est pas chargé dans processing par défaut, il faut donc importer explicitement les objets dans le sketch. Voir ligne 1 et 2:

import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;

Le contenu dess trois champs texte du panneau de configuration est emballé dans des déclarations de fonctions javascript lors du démarrage du thread de rendu, voir Rendering, ligne 120 à 122:

   String func = "function renderTX(x,y,s) { return " + translatex + "; }";
   func += "function renderTY(x,y,s) { return " + translatey + "; }";
   func += "function renderR(x,y,s) { return " + rotation + "; }";

Une fois la chaîne constituée, elle est évaluée par l'interpréteur de script:

try {
     jsEngine.eval(func);
} catch ( Exception e ) { }

Le jsEngine est instancié plus haut, voir Rendering, ligne 31 et 32:

   jsMgr = new ScriptEngineManager();
   jsEngine = jsMgr.getEngineByName("JavaScript");

Une fois les fonctions javascript chargées dans le moteur, nous pouvons les utiliser à chaque répétition du motif, voir ligne 100, 101 et 104.

px = jsTranslateX( x, y, motifsize );

La méthode jsTranslateX, par exemple, se charge de passer les variables java au javascript et de décoder le retour, voir ligne 130 et 132:

String func = "renderTX( " + x + ", " + y + ", " + s + " );";
float v = Float.parseFloat( jsEngine.eval(func).toString() );

File chooser

Java étant un language cross-platform[8], il contient tous les utilitaires nécessaires à la création et destruction de fichiers sur le disque dur.

Processing s'appuie sur cette faculté pour fournir les méthodes de sauvegarde et de lecture des fichiers, loadImage() par exemple.

Dans ce sketch, la volonté est de proposer une véritable fenêtre de sauvegarde, permettant de choisir n'importe quel dossier sur le disque dur.

Pour ce faire, il est nécessaire d'importer la bibliothèque swing, dans laquelle l'object JFileChooser remplit cette fonction, voir Motifs ligne 3:

import javax.swing.JFileChooser;

La méthode save(), ligne 129, crée un objet JFileChooser. Lors de sa création, il met le programme en pause et affiche un explorateur de fichier:

Java-swing-jfilechooser.png

Une fois le bouton save ou cancel relâché, l'application reprend le cours normal de son exécution.

Il reste alors à vérifier l'adresse saisie par l'utilisateur et à lui ajouter la bonne extension si elle n'est pas preśente.

Attention: il n'y a pas de test sur l'existence d'un fichier du même nom, ce qui impilque que si un fichier portant le même nom existe déjà sur le disque dur, il sera écrasé.

Bonus

Le fond de la bannière de cette page a été créé dans Gimp avec trois images générées par l'application. Pour réactiver le motif utilisé, décommenter les lignes 85 à 97 dans Motifs:

 motif = createGraphics( motifsize, motifsize );
 motif.beginDraw();
 motif.background( 0, 0 );
 motif.noFill();
 motif.strokeWeight( 10 );
 motif.stroke( 127 );
 motif.ellipse( 0,0, motifsize * 2, motifsize * 2 ); 
 motif.strokeWeight( 2 );
 motif.stroke( 255 );
 motif.ellipse( 0,0, motifsize * 2, motifsize * 2 );  
 motif.endDraw();

Les trois exports utilisés:

Pattern-circle-002.png Pattern-circle-003.png Pattern-circle-007.png

Les deux premières images sont simplement superposées, la troisième image a été décalée.

Autres essais:

Pattern-circle-001.png Pattern-circle-004.png Pattern-circle-005.png Pattern-circle-006.png

Références

  1. Le modulo retourne le reste d'une division, voir wikipedia pour plus d'informations
  2. Rendu sur texture: technique consistant à créer des images dans la carte graphique qui ne sont pas nécessairement utilisées directement à l'affichage, voir wikipedia.
  3. Java: language de programmation orienté object développé par Sun Microsystem. Plus d'info sur wikipedia.
  4. Thread: se dit d'un bloc de code pouvant être effectué séparemment du reste du code d'une application. Voir Thread (computing) et Java - Multithreading
  5. Runtime: indique que le programme est compilé est est en cours d'exécution, voir Run time (program lifecycle phase)
  6. Language compilé: par opposition aux languages de scripts, voir Compiled language
  7. A propos du moteur javascript intégré dans le JRE: Oracle Nashorn.
  8. Cross-platform: se dit d'une technologie portable d'un operating system à l'autre, sans modification du code source, voir wikipedia

Resources


--Frankiezafe (talk) 19:32, 2 January 2017 (CET)

Merci à Greg Berger pour les corrections.