par Anne-Laure DELPECH | 27 Mai 2018 | Création numérique
En cours d’art numérique (http://esbac.samedi.free.fr/), j’ai appris à utiliser les motifs et les masques en dessin vectoriel avec Inkscape. Je décris le fonctionnement ici (pour ne pas oublier, et parce que ça peut servir).
faire un masque ou une découpe avec inkscape
L’objet qui sert de forme de découpe est au dessus de celui qui doit être découpé (Objet > mettre au premier plan).
Le masque revient à modifier l’opacité de la forme de découpe tandis que la découpe rend totalement transparente la forme. Ici on fait soit un masque soit une découpe avec la forme de l’étoile au dessus :

Découpe ou masque avec Inkscape
Pour les réaliser :
- Il est préférable que les deux objets fassent la même taille
- L’objet mis au premier plan sert de masque ou découpe à celui qui est plus en arrière.
- Sélectionner les deux objets
- puis choisir objet > masque > définir. ou objet > découpe > définir
créer un motif et en remplir un objet
Pour créer le rectangle coloré qui a été découpé par l’étoile, j’ai rempli le rectangle avec un « motif « . Pour ce faire :
- faire un gribouillis ou autre.
- Sélectionner l’ensemble des objets à transformer en motif (ici un tortillon orange sur un fond bleu).
- dans le menu Objet, choisir motif puis objet en motif

Créer un motif avec Inkscape
4. Maintenant, si je sélectionne par exemple un rectangle à fond vert que j’avais préalablement créé, je vais dans remplissage et contour et dans l’onglet fond, si je clique sur le bouton « motif » mon rectangle devient rempli du motif défini.
5. Si je double clique sur le fond ainsi créé, je vois apparaître trois éléments à gauche de mon motif : une croix, un cercle et un carré

Avec le cercle je modifie l’orientation du motif tandis que le carré sert à modifier sa taille. J’obtiens par exemple l’un de ces trois remplissages :

Des motifs plutôt que des découpes ou des masques !
Evidemment, on voit maintenant que j’aurais pu tout simplement remplir mon étoile avec un motif. J’aurais obtenu le même résultat.
Et maintenant ?
Je suis très contente de connaître ces notions de motif et de découpe ou masque. Je vais voir comment les appliquer aussi dans des logiciels comme Gimp et même dans Processing.
par Anne-Laure DELPECH | 27 Mai 2018 | Création numérique
Cet article décrit comment on peut tracer un cercle avec Processing sans utiliser ellipse(). C’est utile si l’on veut tracer un cercle vide (transparent) à l’intérieur d’un objet, rectangle (pour faire un cache comme un microscope) ou cercle (un doughnut!).
Les notions de PShape, vertex, Contour
Pour comprendre les bases, il faut se référer à l’aide de Processing, et en particulier le tutoriel en anglais sur PShape qui utilise PShape, des vertex, ainsi que beginContour() et endContour() pour « percer » des trous dans des formes.
Le principe général
Au départ, lorsque je me suis intéressée au tracé de cercle avec des vertices, c’était pour mettre un cache en forme de microscope sur un travail collectif utilisant Processing. Je voulais faire un rectangle qui fasse la taille de l’écran, et à l’intérieur un cercle transparent de diamètre 90% de la hauteur de l’écran. L’écran était variable et je ne voulais donc pas créer une forme fixe.
C’est ainsi que j’ai compris comment faire un « trou » dans un objet :
- commencer la forme, avec beginShape() ;
- dessiner un rectangle en le traçant dans le sens des aiguilles d’une montre : aller d’un sommet à l’autre dans ce sens ;
- Démarrer un « contour » (un trou en fait) avec beginContour();
- tracer un cercle en le traçant dans le sens inverse des aiguilles d’une montre ;
- Arrêter le « contour » avec endContour();
- finir la forme avec endShape(CLOSE);
L’exemple que j’ai créé pour comprendre
Dans l’exemple qui suit, la fonction drawCircle() trace un cercle :
- un cercle de centre Vcentre (un vecteur qui contient les coordonnées de ce point)
- un cercle de rayon r
- un cercle qui ressemble à un kaléidoscope si sides a une valeur faible, à un vrai cercle sinon
- un cercle qui est tracé dans le sens des aiguilles d’une montre (cas « false » ou non (cas « true »)

Processing : vertices, PShape, Contour…
Le fonctionnement
dans le setup(), on définit principalement les couleurs des différents éléments. colBkgnd utilisé pour le fond de l’écran est défini comme blanc #FFFFFF.
Dans draw() – on aurait tout pu mettre dans setup si on avait voulu – , on dessine le contenu de 4 quadrants différents.
Dans les 4 quadrants on trace un cercle en utilisant la fonction drawCircle(PVector Centre, int sides, float r, boolean aclockw), définie à la fin du sketch.
Dans le premier quadrant (Nord Ouest), on trace un rectangle classique – avec rect() – puis on y trace un cercle qui est un PShape. Le cercle n’est pas vide. C’est un cercle plein. Le rectangle prend une couleur semi-transparente qui est diluée par le blanc du fond de l’écran. Le cercle est plus foncé, de la couleur de remplissage du rectangle mais sans la transparence pour une raison incompréhensible (et qui serait un bug de processing).
Dans le deuxième quadrant (Sud Ouest), on trace un rectangle puis un cercle selon le principe général décrit plus haut. On a bien un rectangle percé d’un cercle (on voit le fond blanc qui est le background.
Dans le troisième quadrant (Nord Est), on trace un premier cercle dans le sens des aiguilles d’une montre puis un deuxième cercle plus petit dans le sens inverse des aiguilles d’une montre. On obtient un doughnut (un cercle évidé) bleu sur le fond blanc.
Dans le quatrième quadrant (Sud Est), on trace un rectangle puis un cercle, comme dans le deuxième quadrant. Ensuite, une fois que l’on a refermé la forme avec endShape(), on trace une nouvelle forme avec un cercle plus petit. On obtient un rectangle évidé d’une forme de doughnut.
La fonction drawCircle( PVector Centre, int sides, float r, boolean aclockw )
Elle exécute un cercle comme on le ferait à la main si on disposait de l’équation d’un cercle, en traçant une succession de points de coordonnées x,y conformes à l’équation. Le tracé se fait dans le sens des aiguilles d’une montre si aclockw est vraie, dans le sens contraire sinon.
On trace autant de points que de « sides » : notre cercle est vraiment rond si on a défini une valeur élevée pour sides (plus de 30) et plus proche d’un quadrilatère ou d’un kaéliodoscope si l’on définit sides à une valeur faible.
Le code complet
Le code complet est disponible dans ce fichier texte téléchargeable : tracer cercles et autres PShapes
Le code complet est :
/*
* tracer des cercles et autres objets avec des vertices
*
*/
PVector VCentre ; // centre du microscope
float r ; // Rayon du microscope
int sides ; // forme du cercle (sides faible = kaleidoscope plutôt)
color colBkgnd, colQ1, colQ2, colQ3, colQ4 ;
void setup() {
size(600, 400) ;
colorMode(HSB, 360, 100, 100, 100);
VCentre = new PVector( 0, 0) ;
r = 70 ;
sides = 30 ; // avec 30 on a un cercle très bien déjà
colBkgnd = #FFFFFF ;
colQ1 = color( random(100, 250) , random(50,100), random(60,90), random(60,80) ) ;
colQ2 = color( random(100, 250) , random(50,100), random(60,90), random(60,80) ) ;
colQ3 = color( random(100, 250) , random(50,100), random(60,90), random(60,80) ) ;
colQ4 = color( random(100, 250) , random(50,100), random(60,90), random(60,80) ) ;
}
void draw() {
background( colBkgnd ) ;
// Quadrant 1 (Nord Ouest) on trace un cercle dans un rect()
VCentre = new PVector( width/4,height/4) ;
stroke(#FFFFFF) ;
strokeWeight(2);
fill( colQ1 ) ;
rect(0,0,width/2,height/2) ;
beginShape();
drawCircle( VCentre, sides, r, true ) ; // true draws anticlockwise, false clockwise
endShape(CLOSE);
// Quadrant 2 (Sud Ouest) on trace un trou circulaire dans un rectangle
noStroke() ;
fill( colQ2 );
beginShape();
vertex( 0, height/2 );
vertex( width/2, height/2 );
vertex( width/2, height );
vertex( 0, height );
VCentre = new PVector( width/4,3*height/4) ;
stroke(#FFFFFF) ;
strokeWeight(2);
beginContour();
// http://doc.gold.ac.uk/CreativeComputing/creativecomputation/?page_id=1142
// must be counterclockwise for a contour !!!!
drawCircle( VCentre, sides, r, true ) ; // true draws anticlockwise, false clockwise
endContour();
endShape(CLOSE);
// Quadrant 3 (Nord Est) on trace un doughnut
noStroke() ;
fill( colQ3 );
beginShape();
VCentre = new PVector( 3*width/4,height/4) ;
stroke(colQ3) ;
strokeWeight(2);
// on trace un cercle par dessus le rectangle
drawCircle( VCentre, sides, 1.2*r, false ) ;
beginContour();
// http://doc.gold.ac.uk/CreativeComputing/creativecomputation/?page_id=1142
// must be counterclockwise for a contour !!!!
drawCircle( VCentre, sides, 0.8*r, true ) ; // true draws anticlockwise, false clockwise
endContour();
endShape(CLOSE);
// Quadrant 4 (Sud Est) on trace un doughnut dans un rectangle
noStroke() ;
fill( colQ4 );
beginShape();
vertex( width/2, height/2 );
vertex( width, height/2 );
vertex( width, height );
vertex( width/2, height );
VCentre = new PVector( 3*width/4,3*height/4) ;
stroke(colQ4) ;
strokeWeight(2);
beginContour();
drawCircle( VCentre, sides, 1.2*r, true ) ;
endContour();
endShape(CLOSE);
beginShape() ;
fill( colQ4 );
drawCircle( VCentre, sides, 0.8*r, false ) ; // true draws anticlockwise, false clockwise
endShape(CLOSE);
}
void drawCircle(PVector Centre, int sides, float r, boolean aclockw) {
// http://vormplus.be/blog/article/drawing-a-cylinder-with-processing
float angle = 360 / sides;
if ( aclockw ) {
for (int i = sides; i >0 ; i--) {
float x = Centre.x + cos( radians( i * angle ) ) * r;
float y = Centre.y + sin( radians( i * angle ) ) * r;
vertex( x, y );
// println( i, " - ", i * angle ) ;
}
} else if ( !aclockw ) {
for ( int i = 0 ; i < sides ; i++) {
float x = Centre.x + cos( radians( i * angle ) ) * r;
float y = Centre.y + sin( radians( i * angle ) ) * r;
vertex( x, y );
// println( i, " - ", i * angle ) ;
}
}
}
Et maintenant ?
On sait tracer des cercles, faire des trous dans des objets. On peut explorer d’autres sujets !
par Anne-Laure DELPECH | 19 Mai 2018 | Création numérique
Je veux pouvoir charger des images (png ou jpg en principe) et pouvoir les positionner et les dimensionner comme je le veux. Je veux aussi pouvoir les teinter comme je l’entend.
J’explore donc ici les moyens de :
- utiliser la classe PImage ;
- charger une image avec loadImage() et éventuellement la redimensionner ;
- attraper une portion d’une image, avec get() ;
- utiliser des filtres, par exemple pour mettre en noir et blanc ou en niveau de gris ;
- Rendre l’image transparente, avec ce qui est opaque soit blanc, soit en niveaux de gris clair ;
- donner une couleur à cette image transparente ;
- utiliser les images comme fond (background) ou les positionner à divers endroits.
charger une image et l’afficher comme fond d’écran
Pour qu’une image puisse être utilisée comme fond d’écran, elle doit impérativement être exactement à la taille de cet écran.
Dans le sketch ci-dessous, je réalise les opérations suivantes :
Dans setup()
- définir la taille de l’écran, ici 720 x 540 px ;
- définir un mode couleur HSB (voir l’article Processing : exploration des couleurs HSB et de l’ordre des dessins)
- Charger une image qui s’appelle test-fond-AL-2017-12.png et se trouve sur mon ordinateur dans le répertoire « C:/Processing Scripts/Patrice 2018 04/img »
- redimensionner l’image pour qu’elle fasse la largeur de l’écran et la hauteur de l’écran
Dans draw(), qui s’éxécute en boucle (noter que j’aurais pu tout mettre dans setup) :
- Définir une couleur de remplissage en mode HSB ;
- définir différentes variables aléatoires
- tous les 50 counter (sinon ça défile trop vite)
- redéfinir que le fond de l’écran est constitué par l’image ;
- tracer un rectangle sur ce fond, de position et dimension aléatoire
- par dessus ce rectangle, dessiner l’image, redimensionnée pour que le rectangle soit comme un cadre.
/* tests_images ---------------------
* V0-A
*/
String repertoire = "c:/REPERTOIRE/img" ; // CHANGER pour le bon répertoire !!!!
String fileFond= "test-fond-AL-2017-12.png" ;
PImage imgbkg ;
float xpos, ypos, w, h ;
color c ;
int counter = 0 ;
void setup() {
size(720, 540, JAVA2D); // 16/9 : 960x540 et 4/3 720*540
colorMode(HSB, 360, 100, 100, 100);
// voir fichier exemple processing /images/BackgroundImage
imgbkg = loadImage( repertoire + "/" + fileFond );
imgbkg.resize( width, height) ;
}
void draw() {
c = color (random(360), 70, 70, 100) ;
fill(c) ;
w = random( 20, width / 2 ) ;
xpos = random ( 10, width - w - 10 ) ;
h = random( 20, height / 2 ) ;
ypos = random (10, height - h - 10 ) ;
if (counter % 50 == 0 ) {
background( imgbkg ) ;
rect( xpos, ypos, w + 10, h + 10 ) ;
image( imgbkg, xpos + 5, ypos + 5, w, h ) ;
}
counter ++ ;
}
Et voici ce que celà donne (ici en gif) : l’image est affichée comme fond et aussi dans un encart. Dans l’encart, elle est retaillée à la taille du rectangle.

Afficher seulement une partie de l’image avec get()
get() permet de récupérer des pixels d’une l’image (cf l’aide de Processing.org) .
On peut récupérer tous les pixels, donc toute l’image ou seulement une partie.
Dans le sketch qui suit, j’utilise newimg = img.get(50, 50, w, h ); pour créer une image newimg qui correspond à un rectangle positionné en (50,50), de largeur w et de hauteur l.
J’utilise aussi c = img.get(160, 190); pour lire la couleur d’un pixel spécifique (coordonnées 160,190) de l’image img.

Processing : utilisation de PImage et get()
Cette image est produite par ce sketch :
/* tests_images ---------------------
* V0-A
*/
String repertoire = "c:/REPERTOIRE/img" ; // CHANGER pour le bon répertoire !!!!
String fileFond= "test-fond-AL-2017-12.png" ;
PImage img, imgbkg, newimg ;
color c ;
int w, h ;
void setup() {
size(720, 540, JAVA2D); // 16/9 : 960x540 et 4/3 720*540
colorMode(HSB, 360, 100, 100, 100);
// l'image d'origine, sans modification
img = loadImage( repertoire + "/" + fileFond );
// une image identique mais simplement redimensionnée
imgbkg = img ;
imgbkg.resize( width, height) ;
// une portion de l'image d'origine
w = int( img.width * 0.3 ) ;
h = int( img.height * 0.8 ) ;
newimg = img.get(50, 50, w, h );
// cette couleur sera celle du pixel (160,190) de l'image img
c = img.get(160, 190);
}
void draw() {
noStroke() ;
fill(120,70,70,100) ;
background( imgbkg ) ;
rect(width/10-5, height/10-5, width/2+10, height/2+10);
image( img, width/10, height/10, width/2, height/2) ;
fill(c) ;
rect( width/10-5, height*3/4, 200, 55 ) ;
rect( width * 6/10 + 15, height/10 - 5, w + 10, h +10 );
image( newimg, width * 6/10 + 20, height/10 ) ;
}
background( imgbkg ) ; produit le fond correspondant à l’image (qui a été redimensionnée à la taille de l’écran dans le setup).
image( img, width/10, height/10, width/2, height/2) ; produit l’image encadrée de vert, en haut de la moitié gauche de l’écran.
fill(c) ; et rect( width/10-5, height*3/4, 200, 55 ) ; produisent le rectangle de couleur rose. La couleur rose a été extraite du pixel de coordonnées (160,190) de l’image par la commande c = img.get(160, 190); dans setup().
Enfin image( newimg, width * 6/10 + 20, height/10 ) ; produit l’image à droite, c’est une portion de l’image d’origine. Elle a été obtenue par la commande newimg = img.get(50, 50, w, h ); dans setup().
tint, filter, pixel
Pour ce qui suit, je me suis aidée des informations du site processing.org et de deux pages d’un tutoriel, en anglais, sur la gestion des images avec Processing : cette page sur la teinte des images et cette page sur les filtres de transformation. Quant à l’image d’une oeuvre de Kandinsky, elle provient aussi de ces tutoriels. On peut la télécharger ici.
explication des différentes images obtenues
Le sketch qui suit illustre différentes possibilités de traitement d’une même image. Toutes ces possibilités sont visualisées sur un même écran de Processing :

Processing : utilisation de PImage et filter()

J’appelle A à D les lignes, 1 à 5 les colonnes et les différentes images correspondent à :
- A1 l’image telle quelle, redimensionnée ;
- A2 l’image avec une transparence ;
- A3 un rectangle de couleur mauve qui est utilisée ensuite pour teinter des images ;
- B1 l’image source passée en noir et blanc avec le filtre filter(THRESHOLD, 0.5)
- B2 l’image précédente inversée avec le filtre filter(INVERT)
- B3 l’image inversée teintée avec avec le filtre tint(196,85,189, 255); (un mauve) puis tint(255, 255); qui remet la teinte à neutre (pas de teinte, pas de transparence)
- B4 l’image inversée passée dans une « moulinette » qui transforme tous les pixels noirs en pixels noirs et transparents. On a alors uniquement les pixels blancs qui sont visibles lors de l’affichage
- B5 l’image inversée et transparente de B4 est tintée en mauve.
- Les différentes colonnes de la ligne C sont les mêmes qu’en ligne B sauf qu’au départ on met l’image en niveau de gris avec le filtre filter(GRAY) . La « moulinette » pour transformer en image transparente est différente.
- En ligne D, on observe deux images transparentes. La première en D1 est le résultat d’une fonction créée dans le sketch, pour les images en noir et blanc. La deuxième, en D3 est une autre fonction qui passe en niveaux de gris. Ces deux fonctions font la même chose que ce qui est réalisé de manière décomposée en lignes B et C.
Le code complet pour obtenir ce résultat est le suivant (attention ne pas oublier de placer l’image kandinsky dans le répertoire data du script) :
/* tests_images ---------------------
* Source http://www.cs.sfu.ca/CourseCentral/166/tjd/using_images.html
* et http://www.cs.sfu.ca/CourseCentral/166/tjd/image_tint.html
* kandinsky.jpg utilisée http://www.cs.sfu.ca/CourseCentral/166/tjd/_downloads/kandinsky.jpg
*
* résultat = image img-img-filter-3.png
*/
PImage img ;
PImage imgBw, imgBwInvert, imgBwTransp ;
PImage imgGrey, imgGreyInvert, imgGreyTransp ;
PImage testBW, testGrey ;
int w, h ;
int xpos, ypos, interval ;
void setup() {
// load the image file from the "data" folder
img = loadImage("kandinsky.jpg");
// set the window to be the same dimensions as the image
size(1098, 757);
// l'image devient noir et blanc
imgBw = img.get() ; // a copy of original
imgBw.filter(THRESHOLD, 0.5);
// L'image noir et blanc est inversée
imgBwInvert = img.get() ; // a copy of original
imgBwInvert.filter(THRESHOLD, 0.5);
imgBwInvert.filter(INVERT) ;
// créer une image transparente à partir de imgBwInvert
/*
* https://forum.processing.org/one/topic/turn-white-pixels-transparent.html
*/
int x, y , i ; // pour lire les pixels
imgBwTransp = createImage( imgBwInvert.width, imgBwInvert.height, ARGB );
for( x = 0; x < imgBwInvert.width; x++ ){
for( y = 0; y < imgBwInvert.height; y++ ){
i = ( ( y * imgBwInvert.width ) + x );
if( imgBwInvert.pixels[i] == color( 0, 0, 0 ) ){
// pixel noir devient transparent
imgBwTransp.pixels[i] = color( 0, 0, 0, 0 );
}
else {
// autre couleur reste en l'état
imgBwTransp.pixels[i] = imgBwInvert.pixels[i];
}
}
}
// l'image devient en grey scale
imgGrey = img.get() ; // a copy of original
imgGrey.filter(GRAY);
// L'image grise est inversée
imgGreyInvert = img.get() ; // a copy of original
imgGreyInvert.filter(GRAY);
imgGreyInvert.filter(INVERT) ;
// créer une image transparente à partir de imgGreyInvert
/*
* https://forum.processing.org/one/topic/turn-white-pixels-transparent.html
* avec des ajustements pour conserver les niveaux de gris
*/
imgGreyTransp = createImage( imgGreyInvert.width, imgGreyInvert.height, ARGB );
float red , green , blue, alpha ;
boolean lightGrey = false ;
for( x = 0; x < imgGreyInvert.width; x++ ){
for( y = 0; y < imgGreyInvert.height; y++ ){
i = ( ( y * imgGreyInvert.width ) + x );
red = red(imgGreyInvert.pixels[i]) ;
green = green(imgGreyInvert.pixels[i]) ;
blue = blue(imgGreyInvert.pixels[i]) ;
alpha = alpha(imgGreyInvert.pixels[i]) ;
if ( red < 150 ) {
if ( ( red == green ) && ( red == blue ) ) {
lightGrey = true ;
}
} else if (red >= 150) {
// it's either not grey or it's dark grey
lightGrey = false ;
}
if( !lightGrey ){
// dark grey turned to same color without transparency
imgGreyTransp.pixels[i] = color( red, green, blue, 255 );
}
else {
// la couleur est transformée en noire transparent
imgGreyTransp.pixels[i] = color( 0, 0, 0, 0 );
}
}
}
// utiliser la fonction pour rendre transparente et blanche une image
testBW = img.get() ; // a copy of original
testBW = fnbwTransp(testBW) ;
// utiliser la fonction pour rendre transparente et en niveau de gris clair une image
testGrey = img.get() ; // a copy of original
testGrey = fnGreyTransp(testGrey) ;
// pour positionner et dimensionner les images
w = width / 6 ;
h = height / 5 ;
interval = 20 ;
xpos = interval ;
ypos = interval ;
}
void draw() {
background(img) ;
// ----------------------------------- LIGNE 1 -----
ypos = interval ;
// l'image d'origine en petite taille
xpos = interval ;
image(img, xpos, ypos, w, h);
// ne change pas la couleur mais met de la transparence
tint(255, 126);
xpos = 2 * interval + w ;
image(img, xpos, ypos, w, h);
// remet la couleur et la transparence normale
tint(255, 255);
// pour avoir une idée de la couleur de tint
fill (196,85,189, 255); // un mauve
xpos = 3 * interval + 2 * w ;
rect ( xpos, ypos, w, h );
xpos = 4 * interval + 3 * w ;
// ----------------------------------- LIGNE 2 -----
ypos = 2 * interval + h ;
// les images noir et blanc
xpos = interval ;
image(imgBw, xpos, ypos, w, h);
xpos = 2 * interval + w ;
image(imgBwInvert, xpos, ypos, w, h);
// colorer l'image
tint(196,85,189, 255); // un mauve
xpos = 3 * interval + 2 * w ;
image(imgBwInvert, xpos, ypos, w, h);
// remet la couleur et la transparence normale
tint(255, 255);
xpos = 4 * interval + 3 * w ;
image(imgBwTransp, xpos, ypos, w, h);
// colorer l'image transparente
tint(196,85,189, 255); // un mauve
xpos = 5 * interval + 4 * w ;
image(imgBwTransp, xpos, ypos, w, h);
// remet la couleur et la transparence normale
tint(255, 255);
// ----------------------------------- LIGNE 3 -----
// les images grises
xpos = interval ;
ypos = 3 * interval + 2 * h ;
image(imgGrey, xpos, ypos, w, h);
xpos = 2 * interval + w ;
image(imgGreyInvert, xpos, ypos, w, h);
// ne change pas la couleur mais met de la transparence
tint(255, 126);
xpos = 3 * interval + 2 * w ;
// colorer l'image
tint(196,85,189, 255); // un mauve
image(imgGreyInvert, xpos, ypos, w, h);
// remet la couleur et la transparence normale
tint(255, 255);
xpos = 4 * interval + 3 * w ;
image(imgGreyTransp, xpos, ypos, w, h);
// colorer l'image transparente
tint(196,85,189, 255); // un mauve
xpos = 5 * interval + 4 * w ;
image(imgGreyTransp, xpos, ypos, w, h);
// remet la couleur et la transparence normale
tint(255, 255);
// ----------------------------------- LIGNE 4 -----
ypos = 4 * interval + 3 * h ;
// via les fonctions
xpos = interval ;
image(testBW, xpos, ypos, w, h);
xpos = 3 * interval + 2 * w ;
image(testGrey, xpos, ypos, w, h);
}
PImage fnbwTransp( PImage img ) {
PImage result ;
img.filter(THRESHOLD, 0.5); // now black & white
img.filter(INVERT) ; // black becomes white and white becomes black
// source https://forum.processing.org/one/topic/turn-white-pixels-transparent.html
int x, y , i ; // pour lire les pixels
// must be ARGB so that transparency may be added
result = createImage( img.width, img.height, ARGB );
for( x = 0; x < img.width; x++ ){
for( y = 0; y < img.height; y++ ){
i = ( ( y * img.width ) + x );
if( img.pixels[i] == color( 0, 0, 0 ) ){
// pixel noir devient transparent
result.pixels[i] = color( 0, 0, 0, 0 );
}
else {
// autre couleur reste en l'état
result.pixels[i] = img.pixels[i];
}
}
}
return result ; // une image blanche avec tout le reste transparent
}
/*
* fonction pour retourner une image avec niveaux de gris clair transparente
* on peut ensuite la teinter comme on veut
*/
PImage fnGreyTransp( PImage img ) {
PImage result = createImage( img.width, img.height, ARGB );
img.filter(GRAY); // now grey scale (niveau de gris)
img.filter(INVERT) ; // black becomes white and white becomes black
// source https://forum.processing.org/one/topic/turn-white-pixels-transparent.html
// avec des ajustements pour conserver les niveaux de gris
int x, y , i ; // pour lire les pixels
float red , green , blue, alpha ;
boolean lightGrey = false ;
for( x = 0; x < img.width; x++ ){
for( y = 0; y < img.height; y++ ){
i = ( ( y * img.width ) + x );
red = red(img.pixels[i]) ;
green = green(img.pixels[i]) ;
blue = blue(img.pixels[i]) ;
// alpha = alpha(img.pixels[i]) ;
if ( red < 150 ) {
if ( ( red == green ) && ( red == blue ) ) {
lightGrey = true ;
}
} else if (red >= 150) {
// it's either not grey or it's dark grey
lightGrey = false ;
}
if( !lightGrey ){
// dark grey turned to same color without transparency
result.pixels[i] = color( red, green, blue, 255 );
} else {
// la couleur est transformée en noire transparent
result.pixels[i] = color( 0, 0, 0, 0 );
}
}
}
return result ; // une image blanche avec tout le reste transparent
}
Aparté théorique sur la notion de « niveau de gris »
Lorsqu’on parle de niveau de gris, on a une couleur qui a deux caractéristiques :
- les valeurs de R, G et B sont identiques ;
- le niveau correspond au ratio entre la valeur de R (ou G ou B) et 256 : R = 126 correspond à un niveau de gris de 50%.
Pour une couleur (obtenue par exemple avec c = img.get(160, 190); comme précédemment), on peut savoir quelle sont les valeurs de rouge, vert et bleu, et même transparence, avec les fonctions respectivement red(), green(c), blue(c), alpha(c).
Ce sont ces caractéristiques qu’on utilise pour rendre une image teintable, avec nuances.
Rendre une image teintable et transparente
C’est réalisé avec la fonction fnbwTransp. Cette fonction est réalisée sur une image PImage img et renvoie une PImage puisqu’elle est définie comme PImage fnbwTransp( PImage img ) {} .
La « moulinette » qui transforme les pixels noirs en pixels transparents est inspirée d’une discussion sur le forum processing. L’idée générale en est la suivante :
- on crée une image vide appelée result, de dimensions celles de img (notre source) et au format ARGB, donc avec prise en compte de la transparence Alpha. Cette image est créée avec result = createImage( img.width, img.height, ARGB );
- on balaie l’image source, img, pixel par pixel.
- Si la couleur du pixel est noir – color( 0, 0, 0 ) – alors la couleur du pixel correspondant dans l’image result est color( 0, 0, 0, 0 ) – noir aussi mais transparent.
- Si la couleur du pixel n’est pas le noir, alors la couleur du du pixel correspondant dans l’image result est la couleur du pixel d’origine de img.
La fonction retourne l’image résultante, une image dont tous les pixels noirs sont devenus transparents.
Rendre une image teintable, avec nuances, et transprente
On utilise là la fonction fnGreyTransp. Elle est semblable à fnbwTransp mais elle gère des niveaux d’intensité de la couleur. On utilise le même principe que dans fnbwTransp mais la différence est dans les décisions lors du balayage des pixels d’img :
- si le niveau de rouge est inférieur à 150 (on a un gris variant de très clair à assez clair)
- si rouge = vert = bleu, alors on est en niveau de gris et lightGrey = true ;
- si le niveau de rouge est > 150 ou bien rouge n’est pas identique à vert et bleu, alors lightGrey = false ;
Et ensuite on a deux possibilités :
- si lightGrey = true , alors le pixel correspondant de result est noir et totalement transparent (valeur 0 de transparence)
- si lightGrey = false , alors on donne au pixel correspondant de result l’exacte valeur de la couleur du pixel de img, avec une transparence nulle (valeur 255 de transparence).
Et maintenant !?
J’aimerais bien faire la même chose en p5.js pour avoir des sketch plus facilement présentables en ligne.
Mais avant, je vais probablement explorer la gestion des couches avec PGraphics, et aussi des masques, avec mask().
par Anne-Laure DELPECH | 18 Mar 2018 | Création numérique
Pour un projet je souhaitais pouvoir interagir avec un script Processing en utilisant le clavier ou les boutons de la souris. Et je voulais aussi pouvoir sauvegarder des « copies d’écran » en format image (png, tif, jpg) ou vectoriel (pdf). Enfin je voulais pouvoir changer l’écran temporairement pour afficher un « générique ».
J’ai réalisé un script de test qui montre comment réaliser des interactions (en Processing ou en Javascript avec p5.js). Je le met en ligne pour conserver la mémoire de ces essais. Et aussi pour le cas où ça puisse être utile à quelqu’un d’autre.
Le script de test
La version javascript (p5.js)
En javascript, il est visible sur CodePen :
See the Pen p5.js keyboard interactions and save by Delpech (@aldelpech) on CodePen.
En Processing 3.0
Le script peut être téléchargé ici : interactions_1.pde (c’est un fichier zip, à décompresser)
Il fonctionne très bien et réalise les opérations suivantes :
Avec la souris :
- on trace des petits cercles colorés là où est la souris (avec Mouse X et Mouse Y)
- Lorsqu’on clique sur le bouton gauche de la souris, la saturation de la couleur est modifiée
Avec des touches du clavier
- z ou Z : on efface l’écran (ou canevas) ;
- g ou G : on affiche un nouvel écran jusqu’à ce que l’on appuie de nouveau sur g ou G
- r ou R : démarre puis arrête et sauvegarde l’enregistrement du dessin en mode vectoriel (pdf) ;
- p ou P : enregistre une seule itération de « draw » (1 frame) au format pdf ;
- w ou W : sauvegarder l’ecran en png ;
- 1 : modifie la teinte (entre 0 et 120)
- 2 : modifie la teinte (entre 120 et 240)
- 3 : modifie la teinte (entre 240 et 360)
Voici deux exemples d’images obtenues en tapant w ou W :

La conversion de Processing à p5.js
Je l’ai d’abord réalisé en langage Processing 3 avant de le convertir en p5.js avec cet excellent site de conversion. J’ai tout de même du faire des modifications manuelles, et en particulier :
- Supprimer les lignes :
- import processing.pdf.*;
- import java.util.Calendar;
- textFont(createFont(« Arial »,30));
- La fonction de création d’un timestamp devient
ont à supprimer dans la version javascript
La fonction de création d’un timestamp devient
function timestamp() {
var now = new Date();
return now.toISOString();
}
- Et pour la génération de pdf, elle ne fonctionne pas actuellement dans la version javascript. Si on veut vraiment que celà fonctionne, on peut essayer cet exemple sur github. Mais ca a l’air compliqué. D’ailleurs les auteurs du livre « generative design » ne mettent plus cette option dans les versions p5.js du code de la deuxième édition (qui sera disponible en anglais en octobre 2018 🙂 ).
Les interactions souris ou clavier : explications
tracer des petits cercles colorés à la position de la souris
Comme le script est très simple, je peux l’afficher en ligne et il fonctionne.
int r ; // radius
float hue ; // HUE of shape
float compHue; // complementary hue
float sat ; // saturation of shape's color
void setup() {
size(600, 400) ;
colorMode(HSB, 360, 100, 100, 100);
r = 40 ;
hue = random(360) ;
compHue = 0 ;
sat = 50 ;
background(0,0,99,100) ; // white
}
void draw() {
smooth();
strokeWeight(6);
compHue = (hue + 180) % 360 ; // modulo 360 to turn in a circle !
stroke( compHue, sat, 100, 70 );
fill( hue, sat, 100, 70 );
ellipse( mouseX, mouseY, r, r );
}
Le contenu du script est très simple. La seule difficulté se trouve en ligne 24 : compHue = (hue + 180) % 360 ; permet de « tourner dans un cercle » pour que la teinte complémentaire compHue ne dépasse jamais 360 et soit toujours à 180 degré de la teinte hue .
Pour comprendre les interactions clavier et souris, j’ai regardé comment fonctionne le script P_2_0_03.pde des auteurs du livre Generative Design. Le code est en ligne ici.
Modifier la saturation de la couleur lorsqu’on clique sur le bouton gauche de la souris
Je n’ai pas de solution simple pour afficher le code et son fonctionnement au fur et à mesure. Pour suivre les explications qui suivent, il faut lire le contenu du script : interactions_1.pde (c’est un fichier zip, à décompresser).
Ca se fait en ajoutant une fonction void mouseReleased() dans laquelle on définit la valeur de la variable sat .
Faire des choses différentes selon les touches de clavier utilisées
On peut le faire en utilisant switch et case (c’est propre) :
void keyReleased() {
switch(key){
case '1':
// DO SOMETHING
break;
case 'w':
// DO SOMETHING
break;
}
}
ou en utilisant des if
void keyReleased() {
if (key == DELETE || key == BACKSPACE) // DO SOMETHING ;
if (key=='s' || key=='S') // DO SOMETHING;
}
Avec des touches du clavier
- z ou Z : on efface l’écran (ou canevas) ;
- g ou G : on affiche un nouvel écran jusqu’à ce que l’on appuie de nouveau sur g ou G
- r ou R : démarre puis arrête et sauvegarde l’enregistrement du dessin en mode vectoriel (pdf) ;
- p ou P : enregistre une seule itération de « draw » (1 frame) au format pdf ;
- w ou W : sauvegarder l’ecran en png ;
- 1 : modifie la teinte (entre 0 et 120)
- 2 : modifie la teinte (entre 120 et 240)
- 3 : modifie la teinte (entre 240 et 360)
Les sauvegardes d’image : explications
Je suis partie des scripts suivants :
Je n’ai pas tout compris. En particulier j’ai longtemps essayé de créer des images plus grandes que le canevas, sans grand succès malgré deux sources très intéressantes (une question sur stackoverflow et l’idée 16 de cet article sur 25 solutions miracles pour Processing, en anglais) .
Pour réaliser des pdf avec processing
On doit utiliser la bibliothèque pdf, que l’on déclare par import processing.pdf.*; .
Ensuite l’enregistrement du pdf se fait en indiquant :
beginRecord(PDF, "framePDF/" + timestamp() + ".pdf");
Cette ligne indique qu’il faut commencer à enregistrer en mode vectoriel tout ce qui se passe (tout ce qui avait été réalisé avant n’apparaît pas dans le pdf). Et le nom du fichier sera timestamp() + « .pdf, enregistré dans le répertoire « framePDF/ », qui sera créé si nécessaire. timestamp() est une fonction tout à la fin de mon script : interactions_1.pde , qui nécessite la bibliothèque java.util.Calendar.
L’enregistrement du pdf s’arrête, et le fichier est enregistré, lorsque le script exécute la commande :
endRecord();
Pour les images, c’est encore plus simple. L’instruction suivante sauvegarde une « copie » du canevas dans un fichier timestamp_##.png du répertoire Wsaved.
save("Wsaved/" + timestamp() + "_##.png");
Le changement d’écran (générique par exemple) : explications
Enfin je voulais pouvoir changer l’écran temporairement pour afficher un « générique ».
J’ai utilisé presque tel quel ce que proposent les idées idées 14 & 15 de cet article, en anglais, sur 25 solutions miracles pour Processing.
Je trouve que c’est une solution élégante.
Et maintenant ?
Petit à petit j’ai constitué toutes les briques d’un travail que je veux réaliser avec une collègue. Il ne me reste plus qu’à les assembler. C’est chouette !
par Anne-Laure DELPECH | 28 Fév 2018 | Création numérique
Je cherche à mieux comprendre comment gérer les superpositions d’objets et de couleurs. Je vais tenter de comprendre progressivement et de noter au fur et à mesure ce que je comprends.
La boucle « draw »
Essayons d’abord un sketch simple pour explorer les notions de transparence de couleur, et de boucle ‘draw’.
J’ai préparé un sketch qui illustre les impacts des choix d’ordre d’affichage et de transparence.
Couleurs en HSB
Jusqu’à présent j’ai toujours utilisé des couleurs en RGB ou Hexadécimal (cf article ancien : choisir les bonnes couleurs pour un site (hexa, rgb) ) et j’ai toujours ignoré le mode HSB. Pourtant ce mode permettrait d’avoir plus de subtilité pour les choix de couleur automatique, comme dans Processing.
Pour définir que mon script Processing utilise HSB, j’indique colorMode(HSB, 360, 100,100,100) ; dans le setup :
- Hue : 0 ou 360 est rouge, 60 est jaune, 120 est vert, 180 est cyan, 240 est bleu, 300 est mauve ;
- Saturation : 0 gris, 50 clair, 100 saturé ;
- Brightness : 0 noir, 50 foncé, 100 plus clair – Quelle que soit le Hue, un brightness faible donne une couleur noire ;
- Alpha transparency 0 = transparent, 100 = opaque
Les couleurs sont représentées sur un cone, expliqué très bien sur ce site en anglais : HSB: hue, saturation, and brightness, de Tom Jewett. Sur cette page, en anglais, il y a des exemples très clairs et on comprend bien ce que sont les couleurs RGB, HSL et HSV. On pourra aussi lire cet article, toujours en anglais : The HSB Color System: A Practitioner’s Primer.
Le sketch qui suit utilise les couleurs suivantes (dans leur ordre d’apparition dans le code) :
- Canevas de taille 640×360 pixel et de fond blanc : background(360) ; dans setup()
- Grand Rectangle bleu violet opaque : fill(271, 100, 89, 100);
- Arc de cercle cyan avec transparence : fill(180, 100, 100,20);
- Cercle tracé par la souris rouge sang, opaque visible dans le coin en haut à gauche au démarrage du script : fill(353, 95, 73, 100);
- Triangle jaune doré avec transparence : fill(43, 94, 72, 10);
- Petits ronds de couleurs avec Hue variant de 0 à 360 par pas de 60 en haut (avec la boucle
for (int i = 0; i<=360; i+= 60) {
fill(i, 100, 100, 100);
ellipse( 150+i, 10 , 20, 20);
}
On obtient ainsi l’écran suivant au démarrage du script :

L’impact de l’ordre des dessins dans Setup() et draw()
Voici le code en Processing
void setup() {
size(640, 360, P2D);
colorMode(HSB, 360, 100,100,100) ;
background(360) ; // white
}
void draw() {
// Rectangle opaque (sans transparence) blue violet
fill(271, 100, 89, 100);
rect(40, 50, 150, 200);
// Arc de cercle (avec transparence) cyan (bleu clair)
fill(180, 100, 100,20);
arc(479, 300, 280, 280, PI, TWO_PI);
// Cercle tracé par la souris rouge sang opaque
fill(353, 95, 73, 100);
noStroke();
ellipse(mouseX, mouseY, 20, 20);
// triangle avec transparence jaune doré
fill(43, 94, 72, 10);
triangle(100, 25, 150, 340, 450, 260);
for (int i = 0; i<=360; i+= 60) {
fill(i, 100, 100, 100);
ellipse( 150+i, 10 , 20, 20);
}
}
Je l’ai converti en Javascript avec ce site puis mis, sans aucune modification, dans Codepen :
See the Pen Exploration des couleurs et layers Processing – 1 by Delpech (@aldelpech) on CodePen.
On obtient des écrans comme celui-ci par exemple lorsqu’on manipule la souris dedans :

Ce que je comprends à ce stade :
- Le Canevas de fond blanc est défini dans le setup() et n’est donc plus jamais redessiné. C’est pourquoi on voit la trace de la souris dessus. Si background() était défini au tout début de la fonction draw(), on ne verrait pas la trace du passage de la souris.
- Tous les objets qui sont dessinés dans draw() effacent les traces de la souris lorsqu’ils sont dessinés. C’est pourquoi les traces ne sont visibles que dans le canevas sans objets.
- Le grand Rectangle bleu violet opaque est dessiné en premier. Il apparaît donc sous tous les autres objets.
- L’Arc de cercle cyan avec transparence est dessiné ensuite. Il a beau être transparent, il n’y a pas de trace de souris en dessous puisque la souris est dessinée après.
- Lorsqu’on bouge la souris, un cercle rouge sang est. Il est visible par dessus les objets déjà présents. Attention on le voit dans le triangle jaune car il est transparent. S’il ne l’était pas le cercle de la souris ne serait pas visible car il est dessiné après.
- Triangle jaune doré avec transparence : on observe en dessous les traces de la souris et il laisse voir également le cercle de la souris à l’instant de la capture d’écran car il est dessiné par dessus l’arc de cercle et sous le triangle.
Et maintenant ?
Oui, j’ai déjà un peu mal à la tête !
Dans le prochain article, ce sera encore plus compliqué car nous explorerons les calques. Maintenant que nous avons vu l’impact de la transparence, nous n’utiliserons que des couleurs sans transparence pour ne pas brouiller le message !
par Anne-Laure DELPECH | 11 Fév 2018 | Création numérique
Je prépare un travail avec Processing qui va nécessiter de faire bouger des formes assez nombreuses dans le canevas. Ces formes peuvent être générées par le sketch ou être des fichiers svg (ou png) associés au sketch. J’explore donc les moyens de les générer puis de les faire bouger. Cet article reprend mes étapes successives d’apprentissage.
Etape 1 : explorer la notion de classe d’objet
D’abord tester la notion de classe et créer manuellement des objets
J’ai utilisé le script proposé dans l’exercice 2 du Chapitre 8 sur les objets de Learning Processing de Daniel Shiffman.
Ce que j’en retiens :
Une classe d’objet est utilisée lorsqu’on programme en mode orienté objet. La classe contient toujours les éléments suivants :
// des variables
// un constructor
// des fonctions, dont celle qui affichera l’objet (void display() mais pas obligatoirement ce nom)
La convention est toujours de faire commencer le nom d’une classe par une lettre majuscule, comme ici Car.
Ensuite, utiliser un array pour créer et animer un grand nombre d’objets à partir d’une classe
J’ai testé le code proposé dans l’exercice 9 du Chapitre 9 sur les « arrays » (tableaux) de Learning Processing de Daniel Shiffman.
Avec une seule classe on peut créer un nombre infini d’objets semblables, la limite étant la capacité de l’ordinateur à générer et animer ces objets.
Etape 2 : explorer le mouvement « autonome » d’objets divers
D’abord créer des objets divers et les faire bouger de manière « autonome »
Je me suis inspirée des essais de l’étape 1 et de l’exercice 2 du Chapitre 10 sur les algorithmes de Learning Processing de Daniel Shiffman.
Le script avec les classes dans des fichiers distincts en format zippé : alObjets3_Knowledge alObjets3 (zip)
.
Dans ce script, je crée « manuellement » un objet « myCar1 » et un objet « myVelo1 » et les objets balls[0] à balls[MaxBalls] sont générés par un array.
On notera qu’ici les paramètres de chaque objet sont définis dans les classes et pas dans le programme principal (voir plus bas).
Ensuite, tester la gestion des interactions entre certains objets
Je me suis inspirée de l’exercice 3 du Chapitre 10 sur les algorithmes de Learning Processing de Daniel Shiffman.
Dans ce sketch, les balles s’arrêtent lorsqu’elles approchent un « vélo » du fait d’une fonction intersect(Velo b) définie dans la classe Ball. Dans le programme principal, fonction void draw(), les balles ne bougent que si la distance au vélo est suffisante par le biais de :
if ( !balls[i].intersect(myVelo1) && !balls[i].intersect(myVelo2) ) {
balls[i].move();
}
Le script avec les classes dans des fichiers distincts en format zippé : alObjets3 (zip)
.
Etape 3 : Explorer une classe pour le chronométrage et la gestion du temps
J’ai lu quelque part que le système utilisé pour l’étape 2 peut consommer énormément de ressources si on crée beaucoup d’objet. J’ai donc souhaité chronométrer le processus. Pour celà j’ai utiliser une classe « Timer » inspirée encore une fois du livre Learning Processing de Daniel Shiffman : exemple 4 et exemple 5 (timer OOP ou Programmation Orientée Objet) du chapitre 10.
Ca donne ce programme : alObjets5 (zip). Il est identique au précédent sauf que la console affiche le temps écoulé en fonction d’un compteur.
Etape 4 : utilisation de PShape
Dans ce tutoriel de Processing.org sur PShape, Daniel Shiffman explique que PShape est une solution pour éviter de saturer les capacités de l’ordinateur lorsqu’on veut créer un grand nombre de formes. J’ai donc décidé d’explorer PShape.
Je me suis inspirée du tutoriel et de Je me suis inspirée de File → Examples → Topics → Create Shapes → PolygonPShapeOOP que l’on trouve dans les exemples de Processing.
Ce script (alObjets6 (zip)) crée des étoiles comme indiqué dans le tutoriel ci-dessus et les déplace sur l’écran. J’ai ensuite créé des balles sur le même principe qui se déplacent comme dans alObjets5 de l’étape 3. Il y a aussi un chronomètre qui mesure le temps toutes les 100 boucles de draw().
Je ne peux pas afficher le résultat ici car l’extension que j’utilise ne gère pas correctement createShape et ça provoque une erreur dans la page. J’ai créé une version JavaScript (cf étape 8 pour la visualisation).
Syntaxe pour les objets prédéfinis et les objets à construire
La construction des objets PShape n’utilise pas exactement la même syntaxe lorsque l’objet est prédéfini (ici ellipse) ou pas (ici pour l’étoile) :
Dans la classe Star
createShape()
// code de construction de la forme, morceau par morceau
beginShape()
s.fill(105, 204);
s.noStroke();
endShape(CLOSE)
et dans la classe Ball
createShape(ELLIPSE, x, y, r*2, r*2);
b.setStroke(false) ;
b.setFill( c );
PShape manipule l’origine (0,0) du canevas
L’utilisation de PShape manipule l’origine (0,0) du canevas et il faut faire bien attention au moment du setup. J’ai mis beaucoup de temps à comprendre pourquoi mon canevas paraissait décalé. Puis en relisant pour la énième fois le tutoriel sur PShape, j’ai vu cette petite phrase : « when using PShape, what we’re really doing is configuring the geometry relative to the origin (0,0) » et j’ai enfin compris le sens des exemples qui suivaient !
Dans la fonction setup() on doit construire la forme à l’origine du canevas (0,0). Ainsi si je crée un rectangle avec PShape, je vais utiliser le code suivant (positionnement du rectangle dans la fonction setup à 0,0 :
PShape rectangle;
void setup() {
size(640,360,P2D);
rectangle = createShape(RECT, 0, 0, 100, 50);
}
void draw() {
background(51);
translate(mouseX,mouseY);
shape(rectangle);
}
Par contre, si je décide de positionner les objets par leur centre (avec shapeMode(CENTER); ) c’est plus compliqué, je n’ai pas compris comment faire.
La façon d’afficher les objets est différente
Maintenant la fonction display() utilise des nouvelles fonctions :
void display() {
pushMatrix();
translate(x, y);
shape(b);
popMatrix();
}
pushMatrix() et popMatrix() assurent le changement d’origine du canevas et son retour aux coordonnées initiales.
Etape 5 : manipulation de PShape par des variables définies dans le programme principal
La version 7 de alObjets ne crée plus que des balles (pas les étoiles) et les variables sont définies à l’intérieur de la classe, ce qui limite un peu les choses.
Télécharger la version 7 : alObjets7 (zip)
Dans cette version 8, je m’inspire du cours sur la Programmation Orientée Objet de Margaret Noble, une artiste et professeur d’art. Elle explique très clairement le fonctionnement des classes puis vers la fin, elle expose comment on peut transférer des variables du programme principal vers les classes en utilisant des variables _var au lieu de var .
Télécharger la version 8 : alObjets8 (zip)
Je ne peux pas afficher le résultat ici car l’extension que j’utilise ne gère pas correctement createShape et ça provoque une erreur dans la page.
J’ai converti un mix du code de alObjets6.pde et alObjets8.pde en Javascript (librairie p5.js). Si vous voulez voir le contenu de ce fichier, vous pouvez le télécharger : alObjets6_convertiJS (en p5.js)
En voici le résultat dans CodePen
See the Pen Moving stars (p5.js) by Delpech (@aldelpech) on CodePen.
Et maintenant ?
J’ai compris comment génerer et contrôler des formes via des classes et PShape.
Maintenant, je veux faire la même chose sur des images svg. En attendant, je vais aussi voir comment convertir un script processing en javascript pour pouvoir en présenter une version animée en utilisant codepen.
par Anne-Laure DELPECH | 11 Jan 2018 | Archives
Dans deux articles précédents de cette série Un Raspberry Pi serveur de musique, nous avons installé un serveur miniDLNA et un lecteur de musique (GMediaRender) sur un Raspberry Pi 3. Maintenant je veux pouvoir écouter la musique de ce Pi sur un téléphone mobile, une tablette ou un PC.
Clients, renderers, serveurs ????
J’ai installé sur le Pi un serveur DLNA qui permet de lister mes musiques et de les proposer à d’autres appareils. Le serveur ne joue pas la musique. Et le serveur ne peut pas décider quelle musique servir.
Pour écouter la musique de ce Pi où que je sois dans le réseau local, il me faut donc deux choses essentielles :
- un « client» qui va contrôler le serveur et lui dire quelle musique choisir, composer des listes de lecture et dire où les restituer. Ce client peut être un PC, un appareil mobile (android, iOS,…) ou un appareil électronique compatible (une télévision par exemple) ;
- un « renderer» qui va jouer la musique définie par un client. Souvent le client est également renderer, mais ce n’est pas toujours le cas.
Mes choix de clients et renderers
J’ai installé des clients qui font également « renderer » sur Android ou Windows 10. Sur le Pi, j’ai installé un « renderer » seulement pour qu’il joue de la musique dans un amplificateur situé à proximité. Sur iOS, je ferai des essais plus tard.
Je ne suis pas certaine d’avoir fait les bons choix. J’avais aussi comme critère que les applications soient compatibles DLNA, UPnP et « Open Home » car je veux installer un serveur UPnP sur le Pi (ce sera l’objet du prochain article de cette série). J’ai fait quelques essais et ce qui m’a semblé bien, c’est le choix suivant :
- Sur Android : BubbleUPnP. Je l’installe et immédiatement Il voit ‘al music’, le « friendly name » du serveur DLNA

- Sur Windows 10, Windows Media Player. Je l’ai essayé en dernier et il fonctionne parfaitement. Il repère aussi ‘al music’ sans aucun réglage. J’ai donc rangé mes a priori et décidé de l’utiliser ! Sur Windows, VLC fonctionne avec le serveur miniDLNA mais c’est un peu plus compliqué et surtout ça paraît quasi impossible avec UPnP.
- Sur Linux (Debian), pour le Pi, j’ai installé gMediaRender, dans une version compatible DLNA, UPnP et « Open Home». J’explique tout dans l’article XXX.
- Dès que j’aurai la visite d’un appareil iOS, je proposerai d’essayer Linn Kazoo (Windows, Mac, android, iPad) ou LUMIN (iPad)
- J’ai également très envie d’un appareil Chromecast audio qui deviendrait un renderer (une fois que j’aurai installé un serveur UPnP sur le Pi) pour jouer ma musique numérisée sur ma chaine hifi très ancienne.
Nota : pour une utilisation optimale, il faut acheter la licence de BubbleUPnP UPnP/DLNA (montant 3.99 €). Avant de le faire, je vais essayer d’autres solutions Android pour décider laquelle est la plus conviviale :
Je mettrai à jour cet article lorsque j’aurai tranché.
Et maintenant ?
Il faut que j’installe mon serveur UPnP sur le Pi pour avoir une vision globale. Ce sera l’objet du prochain article de cette série, Un Raspberry Pi serveur de musique.
par Anne-Laure DELPECH | 11 Jan 2018 | Archives
Dans cet article de la série, Un Raspberry Pi serveur de musique, j’explique simplement comment installer une version compatible DLNA, UPnP et « Open Home » de GMediaRender, fondée sur gmrender-resurrect. Pour en savoir plus sur le fonctionnement d’un serveur DLNA, d’un serveur UPnP et du protocole « Open Home », il vous faudra lire les autres articles de cette série 😯 !
Installer gstreamer 1.0 et gmrender-resurrect UPnP renderer
Selon « Playing music on a Raspberry Pi using UPnP and DLNA (v3) » avec des modifications pour tenir compte que j’ai une installation plus récente, avec en particulier Raspbian Stretch et pas Wheezy.
Pour installer gstreamer 1.0, on copie tout ce qui suit (les deux lignes) et on le colle dans le terminal de notre Pi :
wget -O - http://www.chiark.greenend.org.uk/~christi/debian/christi@coraline.org.gpg.key \
| sudo apt-key add -
On doit avoir un message « OK ».
Ensuite on crée un nouveau fichier /etc/apt/sources.list.d/upnprender.list pour y ajouter l’adresse du dépôt de Christi Scarborough pour gstreamer 1.0 :
cp /etc/apt/sources.list.d/raspi.list /etc/apt/sources.list.d/upnprender.list
Pour comprendre (un peu) ce qu’est une sources list, voir cet article, en français.
Dans le nouveau fichier on supprime le contenu de raspi.list et on ajoute la ligne suivante. Les instructions originelles disent de terminer par wheezy main. Ce serait juste si on utilisait encore la distribution Wheezy de Raspbian. Mais depuis 2013 elle a été remplacée par Jessie puis Stretch. Comme j’utilise Stretch, je termine par stretch main
deb http://www.chiark.greenend.org.uk/~christi/debian/ stretch main
Puis on met à jour la liste des paquets avant d’installer gmrender-resurrect
apt-get update
Il y a une erreur liée au fait qu’il n’y a pas de version stretch dans le dépôt ‘http://www.chiark.greenend.org.uk/~christi/debian. Mais l’erreur ne paraît pas majeure…
Je poursuis l’installation de gmrender-resurrect en copiant collant l’ensemble des 4 lignes sur une seule ligne du terminal du Pi :
apt-get install libupnp-dev libgstreamer1.0-dev \
gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \
gstreamer1.0-alsa
Installation de gmediarender
Installer git avec
apt-get install git
On exécute ensuite les commandes suivantes, les unes après les autres :
cd
git clone https://github.com/hzeller/gmrender-resurrect.git
cd gmrender-resurrect
apt-get install autoconf
./autogen.sh
./configure
make install
Ca place gmediarender dans /usr/local/bin et des icones dans /usr/local/share/gmediarender
Et maintenant on peut ponctuellement lancer le renderer, en lui donnant le nom qu’on veut (l’option –f dit quel nom on veut donner)
./src/gmediarender -f "Pi Bureau"
Ca fonctionne correctement. On fait CTRL C pour en sortir.
Faire fonctionner gmediarender comme un service
Pour que gmediarender démarre comme un service, il faut copier le contenu de scripts /root/gmrender-resurrect/init.d/gmediarenderer dans /etc/init.d (ou home/votre-nom-d’utilisateur/gmrender-resurrect/init.d/gmediarenderer si vous n’êtes pas connecté en root au Pi)
cp scripts/init.d/gmediarenderer /etc/init.d
On édite /etc/init.d/gmediarender pour définir le nom du « renderer » et corriger si nécessaire l’utilisateur :
DAEMON_USER="al:audio"
UPNP_DEVICE_NAME="Pi Bureau"
Puis on utilise update-rc.d pour que ces informations soient placées correctement dans le Pi.
update-rc.d gmediarenderer defaults
On redémarre le Pi (reboot ou shutdown -r now ). gmediarender démarre. On peut le vérifier avec
ps aux|grep gmedia
Cette commande renvoit
552 11.9 6.9 317764 66444 ? Ssl 10:01 0:02 /usr/local/bin/gmediarender -f Pi Bureau -d -u 7aff11df49801835fb41c5ea6c54bfa0 --gstout-audiosink=alsasink --gstout-audiodevice=sysdefault --gstout-initial-volume-db=-10
root 657 0.0 0.0 4372 560 pts/0 S+ 10:02 0:00 grep --color=auto gmedia
Ca fonctionne et le “renderer” s’appelle Pi Bureau.
Sur mon ordinateur Windows, je peux maintenant destiner de la musique à « Pi Bureau » en cliquant à droite sur une liste de lecture ou un morceau dans Windows Media Player.

Et maintenant ?
Dans les prochains articles de cette série Un Raspberry Pi serveur de musique, j’expliquerai quels « clients » j’ai choisi pour faire fonctionner mon serveur DLNA et jouer de la musique du serveur. J’ajouterai également un serveur BubbleUPnP au Pi afin de disposer d’un partage des listes de lecture et d’un fonctionnement aux normes « Open Home ».
par Anne-Laure DELPECH | 11 Jan 2018 | Archives
Dans le premier article de cette série, Un Raspberry Pi serveur de musique, j’ai expliqué comment créer un serveur utilisant MPD. Mais je n’étais pas vraiment satisfaite. Depuis j’ai acheté un Raspberry Pi 3 et je me suis dit qu’il était temps de passer à un niveau supérieur.
Dans cet article, j’explique comment créer un serveur de musique miniDLNA qui va gérer toute ma musique numérique et la rendre accessible via des appareils Android (ou Apple). Dans un prochain article de cette même série, Un Raspberry Pi serveur de musique, j’expliquerai comment y ajouter un serveur UPnP.
De quoi j’ai besoin ?
- Un raspberry Pi 3 ;
- Un cable ethernet (on peut opter pour le wifi mais ce n’est pas ce que j’ai choisi) ;
- un disque dur USB ;
- Une alimentation USB pour le Raspberry Pi et le disque dur.
Préparer le Raspberry Pi (Raspbian Stretch, IP fixe)
J’ai installé Raspbian Stretch Lite (sans la partie graphique) en suivant les instructions de mon article Mise en service d’un Raspberry Pi.
Le « hostname » de mon Pi est : AlMusicServer. C’est le nom que j’utilise pour me connecter.
J’ai réglé ce Pi sur l’adresse IP fixe (connexion ethernet) 198.168.1.102 selon les instructions de mon article Raspberry Pi : connexion internet en wifi et/ou IP fixe.
Régler la sortie audio
Pour le réglage et les tests, je me suis inspirée des articles suivants :
ALSA est le système Linux de gestion du son. Le paquet alsa-utils est pré-installé dans les distributions Raspbian et contient des commandes utiles pour tester et régler le son de notre Raspberry Pi.
J’ai connecté mon Pi via le connecteur Jack 3.5mm. Ce n’est pas la meilleure solution pour une bonne qualité audio. On a intérêt à utiliser au moins une carte son USB, voire un DAC Hifi. Je verrai ça ultérieurement.
Dans le terminal, s’assurer que le mode audio renvoie bien au connecteur jack (et pas HDMI) avec
amixer cset numid=3 1
Pour tester le son
!!! Attention à bien enfoncer le connecteur Jack, sinon il y a du bruit dans les HP et rien ne fonctionne !!!
speaker-test
Ca fait un bruit (léger) dans les haut parleurs. Il faut régler le volume fort pour l’entendre.
aplay /usr/share/sounds/alsa/Noise.wav
Fait aussi du bruit, qu’on entend que si le volume est très fort
Pour voir les périphériques audios et leurs pilotes
aplay –l liste tous les périphériques audio présents
lsmod permet de voir quel pilote est associé au(x) périphérique(s) audio.
Pour régler le son
amixer controls renvoie
numid=3,iface=MIXER,name='PCM Playback Route'
numid=2,iface=MIXER,name='PCM Playback Switch'
numid=1,iface=MIXER,name='PCM Playback Volume'
numid=5,iface=PCM,name='IEC958 Playback Con Mask'
numid=4,iface=PCM,name='IEC958 Playback Default'
Playback switch règle le son sur “ON” ou « OFF ». Réglé sur On, le son sort des haut parleur au volume défini pat numid=1 (playback volume). Réglé sur Off, les haut pareleurs sont muets quel que soit le réglage de volume.
amixer cget numid=1 doit donc nous indiquer quel est le réglage du volume. Ca me dit :
; type=INTEGER,access=rw---R--,values=1,min=-10239,max=400,step=0
: values=-2000
| dBscale-min=-102.39dB,step=0.01dB,mute=1
Pour régler le son au maximum, je tape (noter qu’au lieu de cget j’utilise cset) :
amixer cset numid=1 100%
Maintenant aplay /usr/share/sounds/alsa/Noise.wav émet maintenant un son que j’entends mieux.
Et pour régler le volume sur 100% et la sortie sur ON et qu’elle soit utilisée à chaque redémarrage du Pi
amixer cset numid=1 100%
amixer cset numid=2 on
alsactl store
Mon son fonctionne.
Si j’utilisais une carte son USB (on en trouve à des prix dérisoires), il faudrait que j’identifie son microprocesseur selon https://learn.adafruit.com/usb-audio-cards-with-a-raspberry-pi/figure-out-your-chipset. Ensuite selon le microprocesseur utilisé, je suivrais les instructions d’Adafruit dans le tutoriel USB Audio Cards with a Raspberry Pi.
Ajouter un disque dur USB
Connecter un disque dur USB puis suivre les instructions de mon article Monter un disque dur externe partagé sur un Raspberry Pi.
Ma musique est dans /media/iomega/MULTIMEDIA/musique.
Installer un serveur miniDLNA
DLNA ?
Cet article, en français, explique plutôt bien ce qu’est le DLNA. Pour résumer, je dirai juste Le DLNA (Digital Living Network Alliance) a été créé par Sony et d’autres entreprises pour définir des pratiques permettant le partage de médias digitaux entre appareils électroniques. En 2015, selon Wikipedia, plus de 200 entreprises participaient à cette alliance.
Installation de mini DLNA sur notre Raspberry Pi 3
J’ai suivi les instructions de Raspberry Pi Media Server – MiniDLNA., avec une correction pour que la base de données se mette à jour correctement lorsque je modifie les fichiers de musique (source : ce sujet sur le forum Raspberrypi.org)
apt-get install minidlna
Copier le fichier de configuration pour le conserver avant modification
cp /etc/minidlna.conf /etc/minidlna.OLD
puis modifier /etc/minidlna.conf
Ma musique est dans /media/iomega/MULTIMEDIA/musique suite à l’installation du disque dur. Il ne faut pas mettre directement ce répertoire dans la configuration de miniDLNA car dans ce cas inotify ne fonctionne pas correctement.
On doit laisser un répertoire local (sur la carte SD) et on crée un lien vers la véritable localisation des fichiers de musique ensuite… Comme ça inotify fonctionne, c’est à dire qu’il surveille en permanence si des fichiers sont modifiés et met alors la base de données à jour (si les modifications sont réalisées pendant que miniDLNA fonctionne).
Je modifie donc les lignes suivantes (pour la ligne inotify=yes, j’ai simplement décommenté la ligne) :
media_dir=A,/var/lib/minidlna/music
db_dir=/media/iomega/MULTIMEDIA/
friendly_name= AL Music
inotify=yes
Dans le terminal du Pi, je définis un lien entre /var/lib/minidlna/music et /media/iomega/MULTIMEDIA/musique :
ln -s /media/iomega/MULTIMEDIA/musique /var/lib/minidlna/music
On peut verifier que ça fonctionne en cliquant sur /var/lib/minidlna/music dans filezilla. On est enmené automatiquement sur /media/iomega/MULTIMEDIA/musique
Je démarre le service avec la commande
service minidlna start
Le disque tourne pendant un bout de temps.
Le log est dans /var/log/minidlna.log . Pour savoir en temps réel ce qui se passe, je peux utiliser
service minidlna status
Dans l’explorateur Windows, un clic sur “Réseau » affiche AL Music. Si je clique dessus, j’accède au lien http://192.168.1.102:8200/.
Si j’ai modifié les fichiers pendant l’arrêt de miniDLNA, je redémarre en forcant la reconstruction de la base de données :
service minidlna force-reload
Gestion des utilisateurs
C’est assez bizarre que je ne doive pas définir d’utilisateur et de groupe pour miniDLNA. Mais comme tout semble fonctionner… Si jamais on a un problème, il faudra peut-être s’inspirer de cet article de la documentation Ubuntu.
Lire des radios internet avec MiniDLNA
Dans ce forum, la solution de phile est de créer une playlist contenant les url des radios souhaitées et le sauvegarder en mode .txt avant de le renommer avec une extension .m3u. L’auteur met même une playlist exemple en téléchargement sur dropbox ici.
Ensuite on place le fichier dans le répertoire musique du Pi, /media/iomega/MULTIMEDIA/musique/.
Avec Windows Media Player, si je clique sur « Playlists » dans la bibliothèque DLNA « AL Music », je vois toutes mes listes, y compris la liste que je viens de placer. Mais je ne peux pas la lire. PAr contre, si je lis une radio internet (CTRL U dans Windows Media Player puis lien vers un flux internet, par exemple http://chai5she.cdn.dvmr.fr/bfmbusiness pour BFM), je peux la mettre dans la play list créée et elle apparaît bien dans le contenu de mon Pi (/media/iomega/MULTIMEDIA/musiqueRadio – 2015 12 04 – HCFR.m3u.
C’est sans doute lié au problème d’utilisateur indiqué plus haut… A suivre !
Et maintenant ?
Dans un prochain article de cette même série, Un Raspberry Pi serveur de musique, j’expliquerai quelles applications on peut utiliser pour accéder au serveur à partir d’un PC ou d’un appareil mobile présent sur le réseau local. Un autre article expliquera comment ajouter un serveur UPnP à notre Raspberry Pi pour rendre l’utilisation vraiment confortable, voire même accéder à notre musique via internet.
par Anne-Laure DELPECH | 10 Jan 2018 | Archives
Lorsqu’on dispose des contenus en colonnes, on utilise flexbox pour avoir des colonnes de hauteur égale. Sous Divi, on fait la même chose.
Pour l’instant je n’ai pas eu l’occasion d’essayer. Je préfère conserver cet article en mémoire pour une prochaine fois : How to Set Equal Column Heights using Flexbox (article de Divi Space, en anglais).
Et je n’oublie pas l’excellent tutoriel, en anglais également, de CSS-tricks : « A Complete Guide to Flexbox« .
Je mettrai mon article à jour lorsque je réaliserai cette opération avec un thème Divi.
Commentaires récents