Textetutorial de programmation |
|
Bradford Paley, Text Arc |
Dans ce tutorial, nous aborderons les principaux méthodes pour travailler avec le texte dans Processing. Ensuite, nous développons un code pour afficher des données d'un tableau d'une manière graphique.
Valence de Ben Fry
Text Arc de W. Bradford Paley, 2002 — son site et un entretien vidéo
Appartment de Martin Wattenberg et Marek Walczak
Afficher du texte dans processing est très simple. On déclare une variable du type PFont, qui est un class propre à Processing.
Pfont font;
Ensuite, il faut créer une police dans le format propre au Processing (.vlw). Pour ce faire, on ouvre dans le Menu Tools/ Create Font...la fenêtre qui nous permet de choir une police et une taille donné. Il est préférable de créer la taille qu'on utilise ensuite. Si ensuite en regarde dans le dossier du sketch ( Ctrl k) on voit qu'il y a une fichier .vlw qui a été rajouté. La prochaine étape est de charger la police dans notre code avec la ligne font = loadFont("Arial-BoldMT-24.vlw"); Attention de faire cette étape toujours dans le void setup() sinon, Processing va charger à chaque boucle de lecture la police, ce qui ralenti énormément la lecture. |
Ensuite, nous définiront la taille qu'on utilisera en précisant la variable de notre police. Et enfin, nous affichons un texte à une coordonnée précise. Nous mettons le texte en question entre guillemets " ".
textFont(font, 24);
text("cool", 50, 50);
Pour rendre les chose un peu plus intéressant, on rajoute du mouvement et la couleur, et voici le code complète:
PFont font;
int x, incr;
void setup(){
font = loadFont("Arial-BoldMT-48.vlw");
textFont(font, 24);
}
void draw(){
background(0);
incr++;
x=(incr%140)-40;
fill(255, 0, 0);
text("cool", x, 50);
}
Il y a différent fonction lié à la typographie, voici les principaux
Un String est contrairement à ce qu'on imagine d'abord aussi un type de donnée qui peut contenir du texte. On peut aussi le définir comme chaîne de caractère, car il est composé de plusieurs signes. Un seul signe s'appelle char, qui est définit entre guillemets simple ('a', 'C') et un String en guillemets double ("cool")
pour déclarer un String, nous procédons comme d'habitude
String mot = "cool";
Nous pouvons utiliser la variable maintenant dans notre code précédent
text(mot, x, 50);
Différents mode nous permettons ensuite de manipuler un String, par exemple de concaténer (lat. concatenare = enchainer) deux String:
String mot = "cool";
String mot1 = "baba";
String mot2 = mot1+mot;
Ou bien rajouter une chiffre
String mot = "cool";
int chiffre = 2;
String mot1 = mot + chiffre;
Nous pouvons faire également des recherches dans les String:
Pour savoir si Babacool comporte un b on utilise le boucle for avec charAt:
for (int i = 0; i<mot2.length(); i++){
char a = mot1.charAt(i);
if (a == 'b') println ("il y a un b a la position "+i);
}
On peut faire ces même recherche d'un mot dans un String avec la méthode indexOf();
String phrase = "C'est cool, quoi";
println (phrase.indexOf("cool"));
indexOf() donne en retour la position du première lettre du mot recherché. Attention, chaque signe compte, aussi les espace et apostrophe... S'il n'est trouve rien, il donne -1 en retour. Donc pour savoir si un mot existe, on peut créer un condition du genre
if ( phrase.indexOf("cool") > 0) ...
Processing Reference: String.charAt()
Processing Reference: String.indexOf()
La méthode split() nous permet de couper un String en plusieurs petits partie. Nous rentrons un String et le signe qui indique l'endroit de couper le String initial, dans notre cas un espace. En retour, nous recevons un liste de String
String phrase = "C'est cool, quoi";
String [] mots = split(phrase, " ");
println(mots);// print: [0] "C'est" [1] "cool," [2] "quoi"
Il est relativement simple de charger un fichier externe, soit un
Pour le .txt on rajoute le ficher texte dans le dossier du sketch et avec loadStrings(##.txt) nous chargerons le fichier dans un array des Strings, car la méthode traite chaque ligne séparé. Pour
lyrics = loadStrings("roboter.txt");
pour ensuite l'afficher, il faut utiliser un boucle, le reste est connu. Voici le texte utilisé dans l'exemple suivant => (clic droit/ Enregistrer la cible sous...pour télécharger)
PFont font;
String[] lyrics;
void setup(){
size(200, 410);
font = loadFont("Arial-BoldMT-12.vlw");
textFont(font,12);
lyrics = loadStrings("roboter.txt");
background(0);
for (int i = 0; i<lyrics.length; i++){
String ligne = lyrics[i];
text(ligne, 5,i*10+15);
}
}
Pour récupérer les données d'un fichier en forme de tableau, le procédé est similaire que celui d'un texte. L'exemple suivant utilisera un fichier .tsv, les cellules sont donc séparé par des tabulations (TAB). Seulement, les données ne sont pas organisées en lignes, (rows) comme dans l'exemple précédent mais en deux dimensions, en lignes et colonnes (column) c'est à dire que nous avons une organisation des données en deux dimensions. Ceci complique un peu les choses, car nous allons travailler un array en 2 dimensions.
Nous allons récupérer d'abord les lignes par notre méthode connu et ensuite nous créons un array à deux dimensions, dont la premier indice a des emplacement égale au nombre des lignes.
String[][] data;
String[] rows = loadStrings(filename);
data = new String[rows.length][];
Ensuite nous devrons découpé les lignes en colonnes. actuellement chaque ligne est un seul String avec des tabs entre les les informations de chaque cellule. Pour découper le String aux tabs, nous utilisons la méthode split(). La méthode split nous retourne un array de String. Nous mettons ensuite cette array dans la référence du premier indice.
for (int i = 0; i<rows.length; i++){
String[] cells = split(rows[i], TAB);
// copy to the table array
data[rowCount] = cells;
rowCount++;
}
Nous allons créer encore deux méthodes pour récupérer les données des différents cellules, l'un pour le texte l'autre pour les chiffres. Étant donné que tous les données sont actuellement en String, nous devrons les transformer pour la deuxième méthode en int avec la méthode parseInt().Cette méthode n'est pas documenté sur le site de Processing. Pour connaître le nombre de ligne, nous allons créer un troisième méthode. C'est a cet effet que nous sert la ligne du code précédent ( rowCount++)
String getString(int rowIndex, int column) {
return data[rowIndex][column];
}
int getInt(int rowIndex, int column) {
return parseInt(data[rowIndex][column]);
}
int getRowCount() {
return rowCount;
}
Pour simplifier la vie, nous mettons tous ça dans un class, nommé Table, voici le code complet:
class Table {
String[][] data;
int rowCount;
Table(String filename) {
String[] rows = loadStrings(filename);
data = new String[rows.length][];
for (int i = 0; i<rows.length; i++){
String[] cells = split(rows[i], TAB);
// copy to the table array
data[rowCount] = cells;
rowCount++;
}
}
int getRowCount() {
return rowCount;
}
String getString(int rowIndex, int column) {
return data[rowIndex][column];
}
int getInt(int rowIndex, int column) {
return parseInt(data[rowIndex][column]);
}
}
Le projet est de prendre la liste des étudiants avec les noms et leurs date d'anniversaire et l'afficher les noms sur l'écran pour que la distance par rapport au centre correspond à la distance entre la date d'aujourd'hui et celui de l'anniversaire. Ce code n'est pas difficile en soi, mais il y a beaucoup des petits étapes à résoudre. Vous pouvez télécharger la liste ici=>
D'abord il faut connaître la distance entre la date d'aujourd'hui et la date d'anniversaire. Il n'y aucune methode, de ma connaissance, en Processing qui nous permet cela. On va contourner le problème en transformant les dates jour/mois en journée absolu de l'année. Il sera très compliqué si nous respectons le fait que les mois ont des nombres différents des jours (janvier 31, février 28 ou 29...). Simplifions les choses en disant que un mois à 30.5, cela nous fait une année de 366 jours. Cette précision nous suffit pour notre visualisation. Voilà la méthode:
float calculeAbsDate(float jour, float mois){
float absDate = (mois-1)*30.5 + jour;
return absDate;
}
Pour connaître maintenant la distance, c'est un simple soustraction. Les dates antérieur seront négative, pour éviter ça nous utilisons la méthode abs()
float calculeDist(float currentDate, float studentDate){
float distance = abs(currentDate-studentDate);
return distance;
}
Il faut encore connaître la distance maximum entre le jour actuel et la date d'anniversaire le plus éloigné, qui nous permet d'etablir un échelle sur l'écran. Pour ce faire, nous comparons dans un boucle chaque valeur avec une valeur initiale, si la valeur est plus grand que l'actuelle, nous allons le remplacer. Dans l'absolu (non pas dans notre cas), il est possible que la valeur soit négatif, si nous commençons avec 0, la valeur maximum restera toujours 0. Pour éviter ceci, nous partirons du plus petit float existant, ceci est un valeur fixe, marqué par MIN_FLOAT
Nous allons créer nous différents arrays et créerons un boucle pour récupérer les différents informations:
float dataMin = 0;//
float dataMax = MIN_FLOAT; //MIN_FLOAT est la valeur du plus grand float
float currentAbsDate = calculeAbsDate(day(), month());
float absDates [] = new float [table.getRowCount()]; // liste pour les dates d'aniversaires exprimés en jours (approximatif)
float calculeDists [] = new float [table.getRowCount()]; // liste pour distance en jour entre aujourd'hui et l'aniversaires
for (int i = 1; i< table.getRowCount(); i++){// premier row sont les categories, on commence donc avec 1
absDates[i]=calculeAbsDate(table.getInt(i, 2), table.getInt(i, 3));// calule la date d'anniversaire en nbr des jours
calculeDists [i] = calculeDist(currentAbsDate, absDates[i]);
// on cherche la plus grande valeur dans la liste
if (absDates[i] > dataMax) dataMax = absDates[i];
}
Maintenant, il faut mettre les valeurs à une échelle de l'écran. On fait cela avec la méthode map() qui est un formidable chose pour ce genre des conversions. Son premier argument prend la valeur à convertir, le deuxième et troisième les valeurs min et max de l'échelle d'origine (nos jours) et le quatrième et cinquième les valeurs finale (les pixels sur l'écran).
Pour que les noms ne se trouvent pas sur la même ligne, nous allons les afficher d'une manière circulaire. Nous allons créer alors un méthode qui définit les distances entre chaque nom en radians. Et puis il faut encore convertir la distance calculé et le radians en un point x et y, comme on l'a fait avec la montre. Et enfin on peut afficher les données.
float distProport[]=new float [table.getRowCount()];
for (int i = 1; i < table.getRowCount(); i++){
distProport[i] = map(calculeDists[i], dataMin, dataMax, 0, height/2); // distance proportionnel
float x = cos(calAngle(table.getRowCount())*i) * distProport[i];
float y = sin(calAngle(table.getRowCount())*i) * distProport[i];
text(table.getString(i, 1), x+width/2, y+height/2);
}
fill(#FF0000);
text(day()+" / "+month(),width/2, height/2);
}
float calAngle( int nbrRows){
return PI*2/(nbrRows-1);// on enleve la premiere ligne
}
Processing référence abs()
Processing référence map()