Archivi tag: beginShape()

Grafico a linee

Partiamo sempre dall’esempio su cui stiamo lavorando da un po’ di tempo a questa parte e creiamo un grafico a linee in Processing.

L’unica modifica al codice che dobbiamo fare è quella relativa alla grafica: lasciamo, dunque, inalterata la prima parte, quella relativa alla lettura dei dati dal file .csv e l’inserimento degli stessi in un array e passiamo direttamente alla grafica.

Ripensare la posizione della x

La prima differenza rispetto al grafico a barre sarà determinare correttamente la posizione x del valore estratto dall’array. Se, infatti, il ciascuna barra del grafico aveva una sua larghezza, ora dobbiamo rappresentare solo un punto.

Per risolvere il problema, pur mantenendo il risultato finale che avevamo realizzato nell’ultimo post, creo una nuova variabile chiamata x1 che mi servirà come nuovo riferimento e aggiungo un nuovo ciclo for solo per disegnare i punti di riferimento nel grafico.


int x1 = 68;
  
for(int i = 0; i < tempMin.length; i++) {
  ellipse(x1, height - 50 + tempMin[i] * - 20, 10, 10);
  ellipse(x1, height - 50 + tempMax[i] * - 20, 10, 10);
  x1 += 40;
}

Ecco il risultato:

Grafico a linee in Processing

Creiamo il grafico a linee

Creare un grafico a linee potrebbe essere più complesso di quanto ipotizzato: dovremmo, infatti, creare manualmente una linea per congiungere tra loro i punti disegnati in precedenza.

Per fortuna ci vengono in aiuto alcune funzione di Processing: beginShape()vertex() ed endShape().

Con beginShape() diciamo al programma di iniziare a creare una forma, aggiungiamo tutti i vertici che la compongono con vertex() e, una volta richiamata la funzione endShape(), Processing si preoccuperà di collegare insieme tutti i punti.

Come al solito, consiglio di verificare sul reference i parametri opzionali che possiamo usare per ciascuna funzione. Per questo esempio il comportamento di default è quello che fa al caso nostro ma in futuro potrebbero tornarvi utili anche le altre possibilità.

Dovendo creare due grafici a barre, dovremo utilizzare due cicli for:


int x1 = 68;

beginShape();
for (int i = 0; i < tempMin.length; i++) {
  vertex(x1, height - 50 + tempMin[i] * - 20);
  ellipse(x1, height - 50 + tempMin[i] * - 20, 10, 10);
  x1 += 40;
}
endShape();

x1 = 68;
beginShape();
for (int i = 0; i < tempMax.length; i++) {
  vertex(x1, height - 50 + tempMax[i] * - 20);
  ellipse(x1, height - 50 + tempMax[i] * - 20, 10, 10);
  x1 += 40;
}
endShape();

Grafico a linee beginShape, vertex, endShape

A questo punto possiamo eliminare i grafici a barre che avevamo tenuto come riferimento e andare a sistemare i colori per ottenere un risultato finale soddisfacente:

Grafico a linee finale in Processing

Codice completo

Guardando il codice vi sembrerà un po’ verboso, in particolare per quanto riguarda le assegnazioni dei colori. Facendo qualche esperimento vi renderete conto di quanto sia importante inserire le istruzioni giuste per evitare problemi di visualizzazione.


/*
 * Grafico a linee
 * Federico Pepe, 20.05.2018
 * http://blog.federicopepe.com/processing
 */

Table csv;

float tempMin[], tempMax[];

void setup() {
  size(500, 500);
  background(255);
  noStroke();
  csv = loadTable("data.csv", "header");

  println("Numero righe: " + csv.getRowCount());
  println("Numero colonne: " + csv.getColumnCount());

  tempMin = new float[0];
  tempMax = new float[0];

  for (int i = 1; i < csv.getColumnCount(); i++) {
    tempMin = append(tempMin, csv.getFloat(0, i));
    tempMax = append(tempMax, csv.getFloat(3, i));
  }

  printArray(tempMin);

  println("Il valore minimo è: " + min(tempMin));
  println("Il valore massimo è: " + max(tempMin));

  noLoop();

  // Scritte relative all'anno
  int x = 50;
  int year = 2008;
  
  for (int i = 0; i < tempMin.length; i++) {
    fill(0, 127);
    text(year, x + 2, height - 30);
    x += 40;
    year++;
  }
  
  // Temperature minime
  int x1 = 68;
  beginShape();
  for (int i = 0; i < tempMin.length; i++) {
    fill(100, 190, 255);
    vertex(x1, height - 50 + tempMin[i] * - 20);
    noStroke();
    ellipse(x1, height - 50 + tempMin[i] * - 20, 10, 10);
    x1 += 40;
    stroke(100, 190, 255);
    noFill();
  }
  endShape();
  
  // Temperature massime
  x1 = 68;
  beginShape();
  for (int i = 0; i < tempMax.length; i++) {
    fill(255, 100, 100);
    vertex(x1, height - 50 + tempMax[i] * - 20);
    noStroke();
    ellipse(x1, height - 50 + tempMax[i] * - 20, 10, 10);
    x1 += 40;
    stroke(255, 100, 100);
    noFill();
  }
  endShape();

  // Griglia di riferimento
  textAlign(RIGHT, CENTER);
  for (int j = 0; j <= 20; j++) {
    if (j % 5 == 0) {
      fill(0, 127);
      text(j + "°", 25, height - 52 + (j * - 20));
      stroke(0, 30);
    } else {
      stroke(0, 15);
    }
    line(30, height - 50 + (j * - 20), 470, height - 50 + (j * - 20));
  }
}

void draw() {
}

Altri esempi con la funzione noise()

Nell’ultimo post abbiamo analizzato la differenza tra la funzione random()noise(). La prima, vi ricordo, restituisce dei valori assolutamente casuali che dipendono dai parametri che passiamo alla funzione; la seconda, invece, ci permette di avere una sorta di casualità controllata.

I valori che vengono restituiti dalla funzione noise() hanno due caratteristiche fondamentali: sono correlati tra loro e hanno sempre un valore compreso tra 0 e 1.

Maggiore sarà la vicinanza tra i parametri che passiamo alla funzione, maggiore sarà, ovviamente, la correlazione tra i valori restituiti in output. È possibile verificare quanto ho appena detto utilizzando un paio di righe di codice:

println(noise(100));
println(noise(100.01));

Questo il risultato visualizzato in console:

0.3187103
0.318551

I risultati sono davvero molto vicini tra loro. Proviamo ad aumentare la distanza tra i due valori:

println(noise(100));
println(noise(101));

Ora la correlazione tra i due output è meno evidente:

0.56860673
0.48344862

Aumentando ulterioremente la distanza tra i parametri di input, avremo risultati in output sempre più simili a quelli ottenuti dalla funzione random():

println("Noise:");
println(noise(100));
println(noise(500));
println("Random:");
println(random(1));
println(random(1));

Può generare risultati simili a questo:

Noise:
0.16914257
0.68422705
Random:
0.48342746
0.89731866

Creare un grafico di valori restituiti da noise()

Il primo esempio che voglio realizzare oggi è un grafico che mi rappresenti, per valori di x crescenti, un valore y restituito da noise().

Ecco il codice:

/*
 * Noise examples 1
 * by Federico Pepe
*/

float xoff = 0;
float increment = 0.02;

void setup() {
  size(500, 500);
  background(255);
  noFill();
  noLoop();
}

void draw() {
  beginShape();
  for(int x = 0; x <= width; x++) {
    stroke(0);
    vertex(x, noise(xoff)*height);
    xoff += increment;
  }
  endShape();
}

Analizziamo velocemente il codice riportato qui sopra: per prima cosa creo le variabile di tipo float xoff e increment, dopodiché all'interno di setup() imposto la grandezza della finestra, il colore di sfondo, imposto che non ci sia nessun colore di riempimento, con noFill(), e che il programma non vada in loo  una volta avviato, con noLoop().

In draw() utilizzo una funzione che non abbiamo ancora visto: beginShape(). Questa funzione dice a Processing che vogliamo disegnare una forma complessa che avrà un'insieme di vertici – aggiunti con la funzione vertex() – che dovranno essere uniti in ordine gli uni agli altri finché non chiuderemo la forma con endShape().

Perché ho deciso di usare questa funzione? Perché così, estraendo per valori di x crescenti dei valori y generati dalla funzione noise(), tutti i punti saranno già collegati tra loro e genereranno un grafico come quello rappresentato qui sotto:

Grafico della funzione noise

Animiamo il grafico

/*
 * Noise example 2
 * by Federico Pepe
*/

float xoff;
float increment = 0.015;
float startPoint;

void setup() {
  size(500, 500);
  background(255);
  noFill();
}

void draw() {
  background(255);
  xoff = startPoint;
  beginShape();
  for(int x = 0; x <= width; x++) {
    stroke(0);
    vertex(x, noise(xoff)*height);
    xoff += increment;
  }
  endShape();
  startPoint += increment;
}

Con una veloce modifica al codice è possibile animare il grafico: per prima cosa eliminiamo il noLoop() e aggiungiamo una variabile per stabilire di volta in volta quale sarà il nostro punto di inizio: startPoint. Ovviamente, al termine del loop, dobbiamo incrementare quest'ultima variabile in modo da avere lo scorrimento orizzontale.

Ecco il risultato (in formato gif):

Grafico noise animato