If(){design}else{art} Processing, le livre

13 juin 2019

Outil de diffusion d’images

Filed under: Non classé — jean-noël Lafargue @ 9 h 21 min

Le programme qui suit sert à diffuser au hasard les images contenues dans le dossier « data ». Les images affichées voient leur format s’adapter à l’écran lorsqu’elles excèdent ses dimensions.

String[] images;
IntList ordre;
int vitesse=1500; // une nouvelle image s'affiche toutes les 1,5 secondes (1500 millisecondes)
int next=0;
color fond=#000000;

void setup(){
  fullScreen();
  imageMode(CENTER);
  ordre=new IntList();
  images = listFileNames(dataPath("")); 
  background(fond);
}

void draw(){
  if(ordre.size()==0){
    for(int a=0;anext){
    affiche(images[ordre.get(0)]);
    ordre.remove(0);
    next=millis()+vitesse;
  }
}

String[] listFileNames(String dir) {
  File file = new File(dir);
  if (file.isDirectory()) {
    String names[] = file.list();
    String newNames[] = new String[0];
    for(String n:names){
      if(match(n, ".*\\.|jpg|jpeg|png|bmp|gif|JPG|JPEG|PNG|BMP|GIF")!=null){
        newNames = (String[]) append(newNames, n);
      }
    } 
    return newNames ;
  } else { 
    return null;
  }
}

void affiche(String im){
  background(fond);
  PImage fichier = loadImage(im);
  int largeur=fichier.width;
  int hauteur=fichier.height;
  if(largeur>width){
    float pc = float(width)/largeur;
    hauteur = round(pc*hauteur);
    largeur=width;
  }
  if(hauteur>height){
    float pc = float(height)/hauteur;
    largeur = round(pc*largeur);
    hauteur=height;
  }
  
  if(largeur>width||hauteur>height){
    largeur=width;hauteur=height;
  } 
  image(fichier, width/2, height/2, largeur, hauteur);
  
}

28 mars 2018

Déterminer si un point se trouve inscrit dans un triangle

Filed under: Non classé — jean-noël Lafargue @ 8 h 02 min

Pour déterminer si un point est inscrit dans un triangle, nous créons deux fonctions. L’une permet de déterminer le barycentre de trois points. La seconde fonction appellera la première trois fois pour calculer le barycentre des coordonnées du point recherché avec chaque paire de points des trois lignes du triangle :

float barycentre (PVector p1, PVector p2, PVector p3)
{
    return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);
}

boolean dansLeTriangle (PVector pt, PVector v1, PVector v2, PVector v3)
{
    boolean b1, b2, b3;

    b1 = barycentre(pt, v1, v2) < 0 ;
    b2 = barycentre(pt, v2, v3) < 0;
    b3 = barycentre(pt, v3, v1) < 0 ;

    return ((b1 == b2) && (b2 == b3));
}

Un exemple de mise en œuvre :

Dans le programme qui suit, on vérifie si la souris se trouve à l’intérieur d’un triangle ou non, le triangle est rempli en blanc si c’est le cas, en noir sinon.

PVector p1,p2,p3;

void setup() {
  size(500, 500);
  p1 = new PVector(100, 100);
  p2 = new PVector(400, 100);
  p3 = new PVector(250, 400);
}

void draw() { 
  background(255);
  if (dansLeTriangle(new PVector(mouseX, mouseY), p1, p2, p3)) { 
    stroke(0); 
    fill(255);
  } else { 
    noStroke();
    fill(0);
  } 
  triangle(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
} 

float barycentre (PVector p1, PVector p2, PVector p3)
{
  return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);
}

boolean dansLeTriangle (PVector pt, PVector v1, PVector v2, PVector v3)
{
  boolean b1, b2, b3;

  b1 = barycentre(pt, v1, v2) <   0;
  b2 = barycentre(pt, v2, v3) <   0;
  b3 = barycentre(pt, v3, v1) <   0;

  return ((b1 == b2)  &&  (b2 == b3));
}

28 mars 2017

Implémentation de l’algorithme de diffusion Floyd-Steinberg

Filed under: Non classé — jean-noël Lafargue @ 13 h 32 min

L’algorithme de diffusion Floyd-Steinberg, inventé en 1976, permet de tramer une image de manière intelligente. On peut lire son fonctionnement en détails sur Wikipédia.
L’image qui suit nous permet de comparer un détail d’image transformée en image 1bit (noir ou blanc) avec cet algorithme (gauche) et sans (droite).

Le programme qui suit est une implémentation de cet algorithme pour Processing. Il n’est pas forcément rapide mais il fonctionne bien, en niveaux de gris. Lorsque la variable colorsNumber est 2, il n’y a que deux couleurs qui seront employées : le blanc et le noir.


En entrant l’image en couleurs ci-dessus (« montagne.png »), on obtient l’image en noir et blanc qui suit et qui sera enregistrée sous le nom « floyd_steinberg.png ».

float palette[];
float mu1 = 7.0/16, mu2 = 3.0/16, mu3=5.0/16, mu4=1.0/16;


void setup() {
  size(600, 480);
  background(255);
  int colorsNumber = 2; // ici on décide le nombre de couleurs de la palette
  palette=getPalette(0,255,colorsNumber);  
}

void draw() {
  // l'image à charger s'appelle "montagne.jpg" et doit 
  // se trouver dans le dossier "data"
  PGraphics g= floydSteinberg(loadImage("montagne.jpg")); 
  image(g,0,0);
  g.save("floyd_steinberg.png");
  noLoop();
}

PGraphics floydSteinberg(PImage img) { 
  PGraphics g = createGraphics(img.width, img.height);
  g.beginDraw();
  g.background(0);
  float[][] pixelz;
  pixelz = new float[img.width][img.height];
  for (int y=0; y<img.height; y++) {
    for (int x=0; x<img.width; x++) {
      pixelz[x][y]= brightness(img.get(x, y));
    }
  }
  for (int y=0; y<img.height; y++) {
    for (int x=0; x<img.width; x++) {
      float ancien=pixelz[x][y];  
      float[] nouveau = couleur_la_plus_proche(ancien);

      pixelz[x][y] = nouveau[0];
      float error= nouveau[1];
      boolean xMax=(x==img.width-1);
      boolean xMin=(x==0);
      boolean yMax=(y==img.height-1);
      if (!xMax) pixelz[x+1][y] += mu1* error;
      if (!xMin && !yMax) pixelz[x-1][y+1] += mu2* error;
      if (!yMax)pixelz[x][y+1] += mu3* error;
      if (!xMax && !yMax)pixelz[x+1][y+1] += mu4* error;
    }
  }
  for (int y=0; y<img.height; y++) {
    for (int x=0; x<img.width; x++) {
      float b=pixelz[x][y];
      g.stroke(b);
      g.point(x, y);
    }
  }
  g.endDraw();
  return g;
}

float[] couleur_la_plus_proche(float b) {
  float[] paire = {0, 0};
  float min=256; 
  for (int i=0; i<palette.length; i++) {
    float c=palette[i];
    float dis = abs(b-c);
    if (dis<min) {
      min=dis;
      paire[0]=c;
    }
  } 
  paire[1]=b-paire[0];
  return paire;
}

float[] getPalette(float depart, float fin, int n){
  if(n<2)n=2;
  float[] toreturn = new float[0];
  float diff = (fin-depart)/(n-1);
  for(int a=0;a<n;a++){
    toreturn = (float[]) append (toreturn, a*diff);
  } 
  return toreturn;
}

La valeur de chaque pixel (ligne par ligne depuis le haut jusqu’en bas et de gauche à droite à l’intérieur de chaque ligne) est lue et comparé à sa valeur indexée la plus proche. Si la valeur du pixel est 120 et que la valeur la plus proche dans ma palette est 128, la marge (dite « erreur ») sera de -8. Nous allons alors ajouter :
7/16e de -8 à la valeur du pixel (x+1, y)
3/16e de -8 au pixel (x-1, y+1)
5/16e de -8 à (x,y+1)
1/16e de -8 à (x+1,y+1)

13 février 2016

L’enregistrement au format SVG

Filed under: Non classé — jean-noël Lafargue @ 16 h 07 min

Processing permet désormais l’enregistrement au format vectoriel SVG.
Le format SVG ne contient pas une matrice de points, comme les formats bitmap Jpeg, png, gif, bmp ou tiff, mais des commandes vectorielles. Pour cela, il serait impossible de se contenter d’une capture de l’espace d’affichage le font les commandes save() et saveFrame(). L’enregistrement au format SVG fonctionne en fait comme l’enregistrement au format PDF, qui est lui aussi un format de document vectoriel.

Voici un exemple qui commence par importer la librairie SVG, crée un écran au format 400×400 pixels, puis, à l’aide de la commande beginRecord(), démarre l’enregistrement au format SVG d’une image que nous choisissons de nommer « image.svg ». Ensuite, nous dessinons une ellipse de 100×100 pixels tous les 25 pixels, à l’aide d’une boucle.
Enfin, nous interrompons l’enregistrement avec endRecord().

import processing.svg.*;
size(400,400);
beginRecord(SVG, "image.svg");
  for(int a=50;a<350;a+=25){
     ellipse(a, a, 100,100);
  }
endRecord();

On le voit, l’image qui en résulte est bien une image vectorielle, qui peut être ouverte et éditée à l’aide d’un logiciel de dessin vectoriel tel que Adobe Illustrator ou que le logiciel libre Inkscape :

exemple_svg

Voici un second exemple, qui permet de remplacer la commande saveFrame() :

import processing.svg.*;

void setup(){
  size(500,500);
}

void draw(){
  beginRecord(SVG, "images/image"+nf(frameCount, 5)+".svg");
    ellipse(mouseX, mouseY, 100,100);
  endRecord();
}

À chaque rafraîchissement de l’écran (draw()), on enregistre ce qui se passe sur l’écran (en l’occurrence, le dessin d’une ellipse à la position de la souris) à l’intérieur d’un fichier.
nf(frameCount, 5) renvoie le numéro de frame, écrit avec cinq chiffre : 00001, 00002, 00003, etc. Les fichiers auront donc pour nom « image00001.svg », image00002.svg », « image00003.svg » et ainsi de suite.
Le nom du fichier est précédé de « images/ », ce qui est une astuce pour que toutes les images ne se retrouvent pas à l’intérieur du dossier du programme, mais à l’intérieur d’un de ses sous-dossiers, que nous choisissons de nommer « images ».

12 octobre 2014

Placer un point sur un cercle et sur une sphère

Filed under: Non classé — jean-noël Lafargue @ 22 h 18 min

Il est facile de placer un point sur un cercle, par exemple avec les lignes qui suivent :

void setup(){
  size(500,500);
  point(250+cos(PI)*100, 250+sin(PI)*100);
}

…qui consistent à placer un point à 100 pixels du centre de l’écran (250, 250) et à l’angle PI (donc la moitié d’un cercle).
La formule est :
– abscisse de départ + cosinus (angle) * rayon // pour l’abscisse (x)
– ordonnée de départ + sinus (angle) * rayon // pour l’ordonnée (y)

Dans l’exemple qui suit, nous dessinons un trait de 200 pixels, partant du centre de l’écran, et aboutissant successivement à 0°, 1°, 2°, 3°,… et jusqu’à 359°.

float angle=0;

void setup(){
  size(500,500);
}

void draw(){
  background(255);
  angle+=TWO_PI/360;
  translate(width/2, height/2);
  line(0, 0, cos(angle)*200, sin(angle)*200);
}

Pour placer un point sur une sphère, qui sera repéré par trois coordonnées (abscisse, ordonnée et cote, ou x, y, z), il faut deux angles, que nous nommerons phi et theta.
La formule à appliquer est alors

– abscisse de départ + (sin(phi)*cos(theta))*rayon // pour l’abscisse (x)
– ordonnée de départ + (sin(phi)*sin(theta))*rayon // pour l’ordonnée (y)
– cote de départ + (cos(phi))*rayon // pour la cote (z)

Un exemple d’utilisation avec le programme qui suit, qui affiche une sphère de 24 longitudes x 24 latitudes, tournant sur elle-même :

float rx=0, ry=0;

void setup() {
  size(500, 500, P3D);
  strokeWeight(4);
}

void draw() {
  background(255);
  ry+=0.01;
  rx+=0.031;
  float r=200;
  translate(width/2, height/2);
  rotateX(rx);
  rotateY(ry);
  for (float phi=0; phi<TWO_PI; phi+=TWO_PI/24) {
    for (float theta=0; theta<TWO_PI; theta+=TWO_PI/24) { 
      float x=(sin(phi)*cos(theta))*r;
      float  y=(sin(phi)*sin(theta))*r;
      float z=(cos(phi))*r; 
      point(x, y, z);
    }
  }
}

Voici une capture d’écran :

points

les variables rx et ry correspondent à l’angle de rotation qui est appliqué à l’environnement, à partir du point de référence (width/2, height/2), c’est à dire le centre de l’écran.

9 février 2011

Fichiers d’exemples à télécharger

Filed under: Non classé — Jean-Michel Géridan @ 22 h 37 min

Télécharger les fichiers d’exemples (7.8Mo).

2 chapitres en téléchargement

Filed under: Non classé — Jean-Michel Géridan @ 14 h 46 min

Les chapitres relatifs à l’interactivité souris et à la programmation objet ainsi que le sommaire détaillé de l’ouvrage « Processing, le code informatique comme outil de création » sont disponibles dans leur intégralité au format pdf aux adresses suivantes :

Chapitre 8: Interactivité Souris

Chapitre 16: Classes

Table des matières

5 janvier 2011

Bientôt

Filed under: Non classé — jean-noël Lafargue @ 15 h 02 min

Sortie le 4 février 2010 de Processing : Le code informatique comme outil de création, par Jean-Michel Géridan et Jean-Noël Lafargue, éd. Pearson.

Le présent site (en préparation) sera un complément au livre et permettra, s’il en est besoin, de fournir à ses lecteurs des rectificatifs

Powered by WordPress