p5.js Brique 6 : créer un fichier avec des données collectées

p5.js Brique 6 : créer un fichier avec des données collectées

Nous voyons ici, dans ce sixème article de la série , comment créer un fichier et proposer à l’utilisateur de l’enregistrer. Nous recueillons des données (sur la base de la brique 5) et nous créons deux fichier texte au clic de la souris dans le canevas.

Pour voir le code complet du sketch : c’est ici sur GitHub ou ici sur l’éditeur p5.js en ligne. On peut également voir l’exécution ici sur une page GitHub. Attention tout le reste du code est expliqué dans la “brique” n°5.

La partie qui permet la création et l’enregistrement des fichiers se trouve dans la fonction mousePressed() :

function mousePressed() {
  if (mouseX > 0 &amp;&amp; mouseX < width &amp;&amp; mouseY > 0 &amp;&amp; mouseY < height) {
    saveStrings(croppedUrl, 'croppedURLs.txt');
    saveStrings(imgUrl, 'fullURLs.txt');
  }
}

Dans ce script, un clic dans le canevas génère :

  • un fichier avec toutes les chaines de caractères de l’array (tableau) croppedUrl. Ce fichier est nommé croppedURLs.txt.
  • un fichier avec toutes les chaines de caractères de l’array (tableau) imgUrl. Ce fichier est nommé fullURLs.txt.

On utilise ici la fonction saveStrings() de p5.js. J’aurais également pu utiliser la fonction save(), de la même manière exactement. Dans d’autres circonstances on pourra s’intéresser aux fonctions : saveJSON(), saveTable(), voire saveCanvas() ou saveFrames().

Et maintenant ?

Et voilà nous avons une cinquième brique ! Les autres briques sont et seront publiées dans cette même série, .

p5.js Brique 5 : des images par une API ou par l’utilisateur

p5.js Brique 5 : des images par une API ou par l’utilisateur

Nous voyons ici, dans ce cinquième article de la série , comment constituer un array (tableau) d’images que nous pourrons manipuler. Ces images sont chargées soit par l’action d’un utilisateur (glissé – déposé d’images sur le canevas), soit par le chargement d’images aléatoires via des informations JSON de l’API d’unsplash.

Je présente deux solutions ici de chargement de fichiers. La première permet de glisser des fichiers images sur le canevas. La deuxième va chercher des images aléatoires sur Unsplash. Dans les deux cas on constitue un array (tableau) d’images.

Constituer un array (tableau) d’images

Un tableau d’image est un tableau classique, sauf que son contenu est formé d’images. Dans le script que vous pouvez voir en action ici, on constitue un array de nbImg images. Cet array est constitué dans la fonction preload, en mode asynchrone. Une fois que toutes les images sont chargées, et seulement à ce moment, la fonction setup() se lance, crée le canevas puis redimensionne les images. Et enfin la fonction draw() affiche l’une des images, prise au hasard. Si l’utilisateur appuie sur la touche a (ou A), une autre image s’affiche.

/****************************************************************
https://editor.p5js.org/Anne-Laure/sketches/cdNa-nKUH 
charger un array d'images 
****************************************************************/

var img = [];
var nbImg = 12 ;

function preload() {

  // noter que les dimensions sont un tout petit peu différentes pour avoir des url différentes
  // cf https://github.com/unsplash/unsplash-source-js/issues/9
  for( let i = 0 ; i < nbImg ; i++ ) {
      img[i] = loadImage( 'https://source.unsplash.com/random/1024x6' + nf(i,2,0) );
  }
}

function setup() {
  
	createCanvas(windowWidth, windowHeight);
    for( let i = 0 ; i < nbImg ; i++ ) {
		let newW = min(img[i].width, 0.9*width) ;
		img[i].resize( newW, 0);
    }
}

function draw() {
  
	background(240) ;
    let num = int(random(img.length));
	image( img[num], 10, 10);
	text("taper a ou A pour changer d'image", 10, img[num].height + 30 ) ;
	text("taper s ou S pour sauvegarder le canevas", 10, img[num].height + 60 ) ;
    noLoop() ;
}


// Export when key is pressed
function keyReleased() {
  if (key == 'a' || key == 'A') {
	redraw();
  }
  if (key == 's' || key == 'S') {
	save();
  }
}

Par glissé-déposé de fichiers image sur le canevas

J’explique ce que je comprends de la solution de Takawo, un codeur japonais. J’ai présenté rapidement cette solution dans mon article sur la brique précédente : “p5.js Brique 4 : charger des images en mode asynchrone“. Ici je vais expliquer un peu plus ce que je comprends du code du sketch.

 Le travail de Takawo a été mis en ligne sur OpenProcessing ici. On peut également le voir sur mon dépôt GitHub (et ici il s’éxécute). Ce sketch reçoit (en une ou plusieurs fois) des fichiers par simple glisser-poser sur le canevas puis les assemble. Si on superpose des images avec des transparences comme ici, il y a un effet intéressant. Ca donne par exemple cette image :

Ce script utilise les fonctionnalités p5.js de la fonction drop(). C’est une fonction qui sert à définir ce qu’il faut faire lorsq’un fichier est déposé sur le canevas et chargé. Le script exécute les actions suivantes :

  1. Dans setup() : donner au canevas le nom « c ». Appliquer à c la fonction drop() de telle sorte que la fonction gotFile, s’exécute à chaque fois qu’un fichier est déposé sur le canevas c. Dans setup() on déclenche aussi la fonction init().
  2. La fonction gotFile() vérifie, pour chaque fichier, que c’est une image. Si oui, elle ajoute cette image dans l’array imgs et elle exécute la fonction drawImage() sur cette image.
  3. La fonction init() est exécutée au setup et à chaque fois que quelqu’un appuie sur la touche r ou R. init() initialise un tableau (array) nommé imgs et demande que la fonction draw() soit exécutée de nouveau. C’est une sorte de remise à zéro.
  4. la fonction drawImage() assure la mise en scène de toutes les images les unes avec les autres. Je n’en ai pas regardé le fonctionnement mais j’aime bien le résultat.
  5. La fonction draw() est exécuté au démarrage et à chaque fois que init() est déclenché. Le canevas est vidé, son fond est gris et un texte indique que l’on peut y placer plusieurs fichiers d’images.
  6. mousePressed() redessine en appelant drawImage()  sur les fichiers existants. Comme il y a une partie aléatoire dans drawImage(), le canevas change.
  7. Et enfin keyPressed() définit ce qui est fait lorsque les touches s ou S d’une part, r ou R d’autre part sont pressées.

C’est un beau script, avec une grande simplicité. Je suis admirative.

Chargement via du JSON et l’API d’Unsplash

Le script est visible sur https://editor.p5js.org/Anne-Laure/sketches/3981uPnuE.

Dans ce script il y a une différence essentielle avec le premier script ci-dessus, dans lequel on constituait un array d’images : on constitue d’abord un array d’URL et ensuite on charge les images correspondantes. Et pour constituer l’Array d’url, il faut récupérer des données au format JSON et les lire (étape 1). Une fois qu’on a lu les données (et obligatoirement uniquement lorsque c’est fait), on doit charger les images correspondant aux url trouvées (étape 2). Une fois qu’on a chargé les images (et obligatoirement uniquement lorsque c’est fait), on doit afficher les images et le texte des url (étape 3).

Ca n’est pas très compliqué dans l’absolu sauf qu’il y a deux fois “(et obligatoirement uniquement lorsque c’est fait)” entre deux étapes successives. Et c’est là que je me suis bien arrachée les cheveux pour obtenir le résultat qui suit (très moche je suis d’accord mais j’en suis hyper fière cependant) :

p5.js : charger des images et des données JSON en mode asynchrone

Pour voir le code complet du sketch : c’est ici sur GitHub ou ici sur l’éditeur p5.js en ligne. On peut également voir l’exécution ici sur une page GitHub.

Récupérer des données au format JSON (étape 1)

Je précise que j’ai mis plusieurs jours à comprendre des explications qui paraissaient certainement claires pour leurs auteurs. Alors je n’ai pas la prétention d’être limpide. Je vous recommande de vous munir de patience et de temps !

Ici je veux récupérer des images aléatoires en provenance du site Unsplash.com. Unsplash.com dispose d’une API (Interface de programmation ou Application Programming Interface) . Une API c’est un moyen de faire communiquer des programmes entre eux. Unsplash a créé une documentation sur son API mais ce n’est pas simple au départ à comprendre. Dans le script présenté j’utilise seulement la possibilité de charger un nombre nbImg d’images choisies aléatoirement. D’autres options existent, telles que la sélection de photos contenant une couleur donnée ou correspondant à des mots clés précisés.

Utilisation de loadJSON

Dans la fonction preload() ci-dessous on utilise loadJSON pour charger en mode asynchrone des données au format JSON. la variable client_id contient une valeur que j’ai trouvé dans un exemple d’url sur internet. Chacun peut s’inscrire gratuitement sur Unsplash.com et obtenir cet identifiant client. On notera que la fonction loadJSON utilise une fonction callback nommée processJson().

function preload() {
	let url = 'https://api.unsplash.com/photos/random/?client_id=';
	url = url + client_id + '&amp;count=' + nbImg ;
	print("url",url) ;
	loadJSON(url, processJson ); 
}

La structure du JSON retourné

Pour savoir à quoi ressemble le format JSON, le plus simple est de taper une url construite selon les règles de l’API concernée et de regarder ce qui revient dans le navigateur. Ensuite on sélectionne ce qui est revenu (du texte pas très facile à lire), on le copie avec CTRL C et on va le coller dans un formatteur de JSON tel que celui-ci. Et là miracle le texte illisible se structure et on commence à le comprendre :

du JSON formatté pour être lisible par un humain

Ici je vois que les données JSON qui m’intéressent sont dans la structure sous “urls” puis “full”.

Quelques ressources sur le format JSON et son utilisation en p5.js

Le traitement du JSON retourné

avec processJson() j’ai mis ce qu’il fallait pour lire les informations qui m’intéressent. data est le nom arbitraire que j’ai donné aux données qui sont retournées par la requête initiale. Dans data on cherche à récupérer, pour chacun des nbImg éléments, le sous élément “full” de “urls”. Ce sous élément est contenu dans data[i].urls.full. Ensuite croppedUrl[i] est construit selon les règles de l’API Unsplash pour obtenir une image découpée de hauteur 600 et largeur 1500.

Charger les images (étape 2)

La fonctionprocessJson() s’éxécute de manière asynchrone, uniquement lorsque la fonction loadJSON a récupéré toutes les données JSON. Donc lorsque processJson() démarre, on est certains d’avoir des url définies. loadImage() peut se dérouler correctement.

function processJson( data ) {
	for (let i = 0; i < nbImg; i++) {
		imgUrl[i] = data[i].urls.full ;
		// découpe l'image de manière aléatoire pour qu'elle fasse 1500 de large
		croppedUrl[i] = imgUrl[i] + '&amp;crop=entropy&amp;h=600&amp;w=1500&amp;fit=crop' ;
		img[i] = loadImage( croppedUrl[i] );
	}
}

Et lorsque la callback fonction processJson( data ) est totalement terminée (les images sont toutes chargées), alors seulement elle retourne à son origine loadJSON() et lui “dit” que ça y est on peut continuer. La fonction preload() peut enfin se terminer et la fonction setup() démarre.

afficher les images et le texte des url (étape 3)

il n’y a rien de spécial dans le code. La fonction setup() retaille toutes les images à la bonne taille. La fonction draw() les affiche et positionne le texte des url sur le canevas.

function setup() {
	createCanvas(windowWidth, windowHeight);
	for (let i = 0; i < nbImg; i++) { 
		let newW = width / 4 -( 10 * 4 ) ;
		img[i].resize( newW, 0) ;
	}
	noLoop();
}

function draw() {
	background(200);
	let pos = createVector(10,10) ;
	for (let i = 0; i < nbImg; i++) {  
		image(img[i], pos.x, pos.y ) ;
		  
		pos.x = pos.x + img[i].width + 10 ;
		if ( (pos.x + img[i].width) > width ) {
		   pos.x = 0 ;
		   pos.y = pos.y + img[i].height + 10 ;
		}  
	}

	pos.y = pos.y + 20 ;
	text( "full URLs", 20, pos.y  ) ; 
	for (let i = 0; i < nbImg; i++) {  
		pos.x = 20 ;
		pos.y = pos.y + 15 ;
		text( imgUrl[i], pos.x, pos.y ) ;
	}  

	pos.y = pos.y +20 ;
	text( "cropped URLs", 20, pos.y  ) ; 
	for (let i = 0; i < nbImg; i++) {  
		pos.x = 20 ;
		pos.y = pos.y + 15 ;
		text( croppedUrl[i], pos.x, pos.y ) ;
	} 
}

Et maintenant ?

Et voilà nous avons une cinquième brique ! Les autres briques sont et seront publiées dans cette même série, .

p5.js Brique 4 : charger des images en mode asynchrone

p5.js Brique 4 : charger des images en mode asynchrone

Dans cet article, d’une série , nous allons voir les différentes options qui s’offrent à nous pour charger des images et les afficher :

  • charger une image d’un répertoire du script ;
  • charger une image via son url ;
  • demander à l’utilisateur de déposer des images à modifier

Les difficultés liées à l’utilisation d’images

Difficulté 1 : avoir complétement chargé l’image avant d’agir dessus

Si on charge l’image dans le setup, l’ordre est donné de charger et notre script continue l’exécution du code. Si l’image est un peu longue à charger, notre code cherche à l’utiliser avant même qu’elle soit chargée. Il faut donc empêcher javascript d’agir sur l’image avant qu’elle ne soit entièrement chargée. On va donc travailler de manière “Asynchrone”. On va donc utiliser la fonction preload() lorsque c’est possible ou une fonction “callback” sinon. C’est ce que l’on va voir dans cet article.

Difficulté 2 : erreur Cross-origin resource sharing (CORS)

On peut exécuter un script javascript sur son navigateur. Mais dès lors qu’on cherche à lui faire utiliser des fichiers associés, on tombe sur une erreur Cross-origin resource sharing (CORS).

C’est un mécanisme de sécurité, qui évite d’exécuter sur un site un fichier en provenance d’un autre site. Malheureusement, ça nous complique sérieusement la vie. Pour l’éviter, il faut obligatoirement utiliser serveur web :

Pour ce qui suit, je considère que nous utilisons un serveur, local ou non.

Charger avec preload()

La fonction preload() permet de charger une image et de ne pas l’utiliser tant qu’elle n’est pas complètement chargée. On peut l’utiliser pour une ou plusieurs images, présentes dans le répertoire du script ou via une url.

Ce script d’exemple p5.js montre comment on peut charger une image dans la fonction setup(). Cependant, si l’image est longue à charger, on peut avoir des dysfonctionnements,
Cet autre exemple, de pointillisme, illustrent le fonctionnement de la fonction preload(). L’image est chargée d’abord puis utilisée dans les fonctions setup() et/ou draw().

function preload() {
  img = loadImage('assets/moonwalk.jpg');
}

On peut également charger une image identifiée par une url :

img = loadImage('https://source.unsplash.com/random/720x400');

Autres solutions asynchrones

Dans certains cas on doit réaliser des actions sur ce qui a été chargé avant toute autre manipulation. Pour préserver l’asynchronicité, on utilisera des fonctions “call back”.

La fonction loadImage peut contenir des fonctions callback qui se déclenche lorsque loadImage a réussi (une autre fonction peut se déclencher en cas d’échec. C’est ce qui se passe dans cet exemple :

var tree;

function setup() {
  createCanvas(400, 400);
  //Loop is off so it would load faster
  noLoop()
}

function draw() {
  background(128);
 //when the image has finished loading, call our imageLoaded function (defined below)
  tree = loadImage("birch.png", imageLoaded)
  }

function imageLoaded(){ //this function could be called whatever we want
	  image(tree,200,200,100,200);
}

Dans le code ci-dessus, on réalise le chargement de l’image dans la fonction draw mais on ne l’affiche que lorsqu’elle est chargée car la fonction imageLoaded() est une fonction callback, qui ne s’éxécutera qu’au « succcès » de la fonction loadImage() qui l’a appelée.

Dans cette option (contenu du sketch), que j’ai mis énormément de temps à faire fonctionner, il y a une succession d’actions :

  1. la fonction preload() exécute fetch sur une url puis (noter le .then{} qui suit le fetch), stocke l’url fournie en réponse dans la variable imgURL avant de charger l’image correspondant à cette url. Le chargement de l’image déclenche l’exécution (callback) de la fonction waitForElement().
  2. la fonction waitForElement( variable ) transfère en tant que variable la réponse de loadImage. Tant que la variable est indéfinie, la fonction s’éxécute elle même (elle est construite de façon récursive) et recommence à s’exécuter toutes les 250 millisecondes. Une fois que variable est définie (l’image est chargée), drawLoop est passé à false.
  3. la fonction draw s’éxécute en permanence mais ne fait rien lorsque la variable drawLoop est true. Ce n’est donc que quand l’image est chargée, elle même chargée lorsqu’on a son url, que l’image est affichée et l’url aussi sur le canevas.

On peut aussi voir le code et l’exécution de ce sketch sur https://editor.p5js.org/Anne-Laure/sketches/w_cCQUJyQ.

Nota : sur l’utilisation de fetch() avec then en javascript, on pourra utilement se référer à cet article sur Medium, ou cette introduction de google ou encore ici sur javascript.info.

/****************************************************************
https://editor.p5js.org/Anne-Laure/sketches/w_cCQUJyQ

Charger 1 image aléatoire sur unsplash et afficher son url

construite à partir de https://editor.p5js.org/Anne-Laure/sketches/JsVMS3ENO 
****************************************************************/

var imgURL ;
var drawLoop = true ;

function preload() {
  
  fetch(`https://source.unsplash.com/random/512x302`).then((response)=> {  
    imgURL = response.url ;
    print("---", imgURL) ;
    // charger l'image 
    img = loadImage( imgURL, waitForElement );
    return imgURL ;
    
  })   
}

function setup() {
  
  createCanvas(windowWidth, windowHeight);

    // voir aussi https://p5js.org/reference/#/p5/loadJSON 
    print("aaa", imgURL) ;

}

function draw() {
    
  if ( !drawLoop ) { 
    
      background(220) ;
      image(img, 10,10) ;
      text( imgURL, 10, img.height + 30 ) ;
      
  } 
}

function waitForElement( variable ){
  // voir https://stackoverflow.com/questions/7307983/while-variable-is-not-defined-wait
    if(typeof variable !== "undefined"){
        //variable exists, do what you want
        print("zzz", variable) ;
        drawLoop = false ;
    }
    else{
        setTimeout(waitForElement, 250);
    }
}

Dans la console, on peut se faire une idée de ce qu’il se passe . La première ligne “aaa undefined” correspond à un print() dans setup(). On voit donc que cette ligne est exécutée alors que preload() n’a pas terminé. fetch() n’est pas une fonction à utiliser en principe dans preload, c’est pour ça. Mais je n’ai pas trouvé d’autre solution. La deuxième ligne “—” est exécutée dans preload et indique la valeur de imgURL sur la ligne suivante. Enfin à partir de “zzz” on voit le print() demandé lorsque l’image est totalement chargée. On voit ainsi quelles informations sont renvoyées une fois que l’image est chargée. Et par exemple print( variable.width ) afficherait 512.

chargement par l’utilisateur

C’est l’utilisateur qui va déposer les images à modifier. C’est la solution proposée par takawo dans ce script :

Takawo a trouvé une solution pour que ce soit l’utilisateur qui charge les fichiers à utiliser. Et il parvient à les traiter de manière asynchrone : tant que les fichiers n’ont pas tous été chargés et traités, rien ne se passe. Le code est visible ici, sur mon dépôt GitHub.

Et maintenant ?

Et voilà nous avons une quatrième brique ! Les autres briques sont et seront publiées dans cette même série, .

Nous verrons dans une prochaine brique comment charger plusieurs images à la fois. Une autre brique consistera à charger des images aléatoires à partir d’une API. Nous utiliserons l’API du site d’images unsplash. On récupérera des données au format JSON et on chargera les url obtenues pour avoir des images manipulables.

p5.js Brique 3 : “GRAPHICS”, contour et vertex

p5.js Brique 3 : “GRAPHICS”, contour et vertex

Avec les briques 1 et 2 – de cette série – nous avons exploré la création de canevas “responsive” puis l’utilisation de “GRAPHICS” pour dessiner des rectangles sur des graphics, ou canevas virtuels. Cette fois ci nous allons dessiner des polygones avec des vertex à la place des rectangles. Un des polygones sera creux pour voir comment ça se passe. Nous obtiendrons ainsi quelque chose comme ça :

Des polygones, dont un creux, qui s’ajustent automatiquement à la taille de l’écran

J’ai fait ce sketch sur openProcessing.org, en p5.js :

Le script est intéressant pour son utilisation de beginShape et beginContour.

la fonction ajustPolygon(index, ep, w, h) crée les polygones (quadrilatères) 1 à 4 en calculant les coordonnées des extrémités (vertex) des 4 segments qui les constituent. Le polygone d’index 4, celui qui est représenté en bas à gauche, est “percé” d’un polygone à trois segments (un triangle). Ici les vertex sont donnés dans le sens des aiguille d’une montre si l’on veut juste indiquer un contour sans “percer” mais dans le sens inverse si l’on veut évider ou “percer”.

La fonction ajustImage(txt, ratio) a déjà été vue dans la “brique” 1 (un canevas “responsive”) . L’utilisation de Graphics, une sorte de calque, a été abordé dans la “brique” 2 dédiée au graphics. .

L’ensemble des fichiers composant ce sketch est accessible sur mon depôt GitHub, dans le répertoire vertex ajustes – 2020 05 10. On peut aussi exécuter le code directement sur GitHub via cette url.

Et maintenant ?

Et voilà nous avons une troisième brique ! Les autres briques sont et seront publiées dans cette même série, .

Mon prochain objectif est de découper des images grâce à des graphics. Pour celà, il va me falloir maîtriser trois nouvelles briques au moins : charger des images prédéfinie, récupérer des images par une API ou via l’utilisateur, appliquer un masque de découpe à une image.

p5.js Brique 2 : utilisation des “GRAPHICS”

p5.js Brique 2 : utilisation des “GRAPHICS”

Lorsqu’on conçoit des éléments visuels animés il peut être intéressant de superposer des éléments graphiques animés individuellement. Les GRAPHICS sont des canavas virtuels, ils peuvent être affichés à l’écran ou pas. Ils peuvent apparaître comme un élément qui est devant un autre, ou faire l’objet d’une transformation (rotation, translation, ….) sans que le reste du canevas soit altéré. Ils fonctionnent un peu comme des calques. des feuilles de papier virtuelles. On peut avoir plusieurs “graphics” ou calques dans le même projet.

Dans cette brique j’explore la création de 6 objets GRAPHIC, tous placés sur un canevas blanc qui fait la taille de l’écran :

  • graf, un GRAPHIC gris (dont les dimensions correspondent à ce qui a été défini dans l’article “p5.js Brique 1 : un canevas “responsive“) ;
  • polyG[0], un GRAPHIC blanc de la même taille que précédemment à la hauteur près ;
  • polyG[1] à polyG[4] des GRAPHIC qui sont des polygones rectangles, de 4 couleurs différentes.

L’ensemble des fichiers composant ce sketch est accessible sur mon depôt GitHub, dans le répertoire Polygones_ajustes_2020_05_10_08_12_27. On peut aussi exécuter le code directement sur GitHub via cette url.

Le script crée les 6 GRAPHIC, place les différents éléments les uns sur les autres. ce qui donne ceci :

Des GRAPHICS p5js dans des GRAPHICS
Des GRAPHIC les uns sur les autres

Cette brique est visible sur https://editor.p5js.org/Anne-Laure/sketches/I4K–pRHH.

Le script complet, qui réutilise la fonction ajustImage(txt, ratio) définie dans dans l’article “p5.js Brique 1 : un canevas “responsive” n’a pas été modifiée. Elle sert à calculer la taille du premier rectangle gris, le GRAPHIC graf.

/****************************************************************
https://editor.p5js.org/Anne-Laure/sketches/I4K--pRHH
Dessiner des polygones à l'intérieur d'un rectangle qui s'ajustent à la taille du rectangle
Suite de https://editor.p5js.org/Anne-Laure/sketches/isSlIPoVn
****************************************************************/

// les caractéristiques du rectangle de base
var grafSize;
var grafPos;
var ratio = 16 / 9; // ratio du rectangle w = ratio * h 

var graf  ;    // le GRAPHICS qui va porter tout
var offsetY = 50 ;  // l'espace en hauteur dédié à autre chose

var drawLoop = true ; // arrêter la boucle draw

var polyG = [] ; // le rectangle et les 4 polygones

function setup() {

  createCanvas(windowWidth, windowHeight);

  grafSize = createVector(0, 0);
  grafPos = createVector(0, 0);

}

function draw() {

  if( drawLoop) {
    
    
    background( 255) ;

    /*** initialiser les GRAPHICS, calculer les tailles  ***/
    grafSize = ajustImage("sz", ratio);
    
    graf = createGraphics(grafSize.x, grafSize.y + offsetY );
    
    for (let i = 0; i <5; i++) {
      // renderer les formes
      polyG[i] = createGraphics(grafSize.x, grafSize.y );
    }

    grafPos = ajustImage("ps", ratio);

    /*** créer les GRAPHICS  ***/    
    // graf est le renderer principal, celui qui sera sauvegardé
    graf.background( 220 ) ;
    
    /* créer les formes polyG[i]  */
    
    // i = 0 un simple rectangle blanc
    polyG[0].fill( 255 ) ;
    polyG[0].rect(0,0,graf.width, graf.height) ;
    
    // i = 1 à 4 des polygones plus compliqués
    let ep = grafSize.x/20 ; // espace autour des rectangles
    let w = grafSize.x/2 - 3 * ep/2 ;
    let h = grafSize.y/2 - 3 * ep/2;

    polyG[1].fill( 255, 0, 0 ) ;
    polyG[1].rect(ep,ep,w, h) ;   
    polyG[2].fill( 0, 255, 0 ) ;
    polyG[2].rect(w+2*ep,ep,w, h) ;       
    polyG[3].fill( 0, 0, 255 ) ;
    polyG[3].rect(w+2*ep,h+2*ep,w,h) ;   
    polyG[4].fill( 155, 55, 0 ) ;
    polyG[4].rect(ep,h+2*ep,w, h) ;   

    for (let i = 0; i < 5; i++) {
      // afficher les polygones dans graf
      graf.image( polyG[i], 0, 0) ;
    }
    
    // graf.stroke(0) ;
    graf.fill(0) ;
    graf.textSize(offsetY/3) ;
    graf.text("Et voilà !", 10, grafSize.y + 25 ) ;
    graf.ellipse(grafSize.x/8, grafSize.y/4, 20, 20 ) ;
    image( graf, grafPos.x, grafPos.y  ) ;    
    drawLoop = false ;
    
  }

}

function ajustImage(txt, ratio) {

  // RAPPEL ratio = 3/4 ; // ratio du rectangle que je veux dessiner w= 3 --> h = 4
  let coeff = 0.95; // on veut que l'image ne représente que 95% du plus petit côté du canevas
  let OffH = height - offsetY ; // laisser place pour texte)
  let CanR = width / OffH;
  print(txt, " Canevas W/H ", CanR);

  let maxSize = createVector(0, 0);
  let gPos = createVector(0, 0);

  if (CanR >= 1) {
    // la largeur est supérieure à la hauteur. C'est la hauteur qui nous limite
    if ( OffH * coeff * ratio >= width * coeff ) {
        
      // il faut que la hauteur soit réduite malgré tout
      maxSize.y = width * coeff / ratio ;
    } else {
      // il faut que la largeur ne dépasse pas la largeur moins la bordure prévue
      maxSize.y = OffH * coeff ; 
    }   
    maxSize.x = maxSize.y * ratio ;
    
  } else {
    // la hauteur est supérieure à la largeur. C'est la largeur qui nous limite
    /* maxSize.x est le plus petit de 
        - la largeur du canevas * le coefficient d'occupation (pour avoir une bordure)
        - la hauteur du dessin qui doit quand même respecter le ratio initial sans dépasser la hauteur du canevas
    */
    if ( width * coeff / ratio >= OffH * coeff ) {
        
      // il faut que la largeur soit réduite malgré tout
      maxSize.x = OffH * coeff * ratio ;
    } else {
      // il faut que la largeur ne dépasse pas la largeur moins la bordure prévue
      maxSize.x = width * coeff ; 
    }
    
    maxSize.y = maxSize.x / ratio ;
  }

  maxSize.x = int(maxSize.x);
  maxSize.y = int(maxSize.y);
  gPos.x = (width - maxSize.x) / 2;
  gPos.y = (OffH - maxSize.y) / 2;


  if (txt == "sz") {
    print(txt, "l canvas, l size, h canvas, h size ", width, maxSize.x, OffH, maxSize.y);
    return maxSize;
  } else if (txt == "ps") {
    print(txt, "x, y totx toty", gPos.x, gPos.y, maxSize.x + 2 * gPos.x, maxSize.y + 2 * gPos.y);
    return gPos;
  }
}

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  // redessiner avec draw()
  drawLoop = true ;
}

Et maintenant ?

Et voilà nous avons une deuxième brique ! Les autres briques sont et seront publiées dans cette même série, . La prochaine étape sera de faire des polygones plus compliqués avec certains GRAPHIC. A suivre !