Se non avete esperienza con Processing e in generale con i linguaggi di programmazione vi consiglio di leggere, se non l’avete già fatto, il tutorial introduttivo Programmare facile con Processing.
L’obiettivo di questo tutorial è programmare un oggetto geometrico molto particolare chiamato frattale. Dato che i frattali trovano molti riscontri in natura, il nostro modellerà matematicamente la struttura base di un albero e in generale di qualsiasi forma ramificata. Grazie all’intuitività di Processing raggiungeremo il nostro obiettivo, all’apparenza complesso, con una facilità davvero stupefacente.
Prima di iniziare a scrivere un po’ di codice è bene spendere qualche parola sulle funzioni ricorsive. Provate a pensare ad una scatola, dentro la quale ce n’è un’altra più piccola, dentro di questa un’altra ancora più piccola e così via. Le funzioni ricorsive si possono vedere proprio così, infatti hanno la particolarità di richiamare se stesse al loro interno.
Un albero si può pensare come un tronco che si divide in due rami più piccoli, che a loro volta si dividono in due rami più piccoli e così via. Bene, questa non è altro che una funzione ricorsiva :-)
Iniziamo, quindi, a scrivere la nostra funzione. Prima di tutto abbiamo bisogno di un paio di variabili, una per la lunghezza del tronco che poi diventerà man mano più piccola per costruire i rami, e l’altra per l’angolo (in radianti) tra i rami. Scriviamo
float a=HALF_PI/3; //angolo
float lunghezza_tronco=150;
dove HALF_PI indica Pi Greco mezzi e quindi HALF_PI/3 equivale a 30° sessagesimali. Settiamo finestra e colori nella funzione setup. Come abbiamo detto nel tutorial precedente le istruzioni della funzione setup vengono eseguite una sola volta all’avvio dell’applicazione.
[sourcecode language="processing"]
void setup(){
size(550,500); //dimensione della finestra
smooth(); //antialiasing
background(255); //sfondo bianco
stroke(0); //linee nere
}
[/sourcecode]
Per disegnare le linee che compongono il nostro albero introduciamo un metodo che si rivelerà molto utile. Finora abbiamo disegnato le nostre linee usando le coordinate dei punti che le compongono. Questo significa che se vogliamo disegnare, ad esempio una linea lunga 100 pixel e inclinata di 45° dovremmo usare un po’ di trigonometria per ricavare le coordinate di uno dei due punti.

Perchè allora non spostare e ruotare a nostro piacimento gli assi cartesiani in modo da facilitarci il lavoro?

Con Processing questo metodo è possibile grazie a due funzioni:
translate(x,y); sposta gli assi nelle coordinate x e y;
rotate(angolo); ruota gli assi di un certo angolo in senso orario espresso in radianti;
Ora nella funzione setup possiamo facilmente disegnare il tronco di partenza col le istruzioni
[sourcecode language="processing"]
void setup(){
size(550,500); //dimensione della finestra
smooth(); //antialiasing
background(255); //sfondo bianco
stroke(0); //linee nere
//disegno
translate(width/2,height); //si muove in basso al centro
line(0,0,0,-lunghezza_tronco); //disegna la linea di partenza
translate(0,-lunghezza_tronco); //si muove sulla punta della linea
}
[/sourcecode]
è il momento di scrivere la nostra funzione ricorsiva.
La chiameremo albero e prenderà come argomento la lunghezza del ramo che al suo interno verrà ridotta di 1/3. Questa funzione costruirà separatamente prima il ramo destro e poi il ramo sinistro e per ognuno dei due rami richiamerà se stessa fino a quando la lunghezza dei rami sarà troppo piccola (nel nostro caso inferiore a 2 pixel).
Scriviamo la funzione albero al di fuori della funzione setup.
[sourcecode language="processing"]
void albero(float l) {
l = l*2/3; //ogni ramo successivo è lungo 2/3 il precedente
if(l>2){ //condizione di uscita se la lunghezza è minore di 2 pixel
//costruzione braccio destro
rotate(a); //ruota di a in senso orario
line(0,0,0,-l); //disegna una linea
translate(0,-l); //si muove sulla punta della linea
albero(l); //richiama la funzione ricorsivamente
translate(0,l); //torna indietro alla base della linea
rotate(-a); //ruota di a in senso antiorario
//costruzione braccio sinistro
rotate(-a); //ruota di a in senso antiorario
line(0,0,0,-l); //disegna una linea
translate(0,-l); //si muove sulla punta della linea
albero(l); //richiama la funzione ricorsivamente
translate(0,l); //torna indietro alla base della linea
rotate(a); //ruota di a in senso orario
}
}
[/sourcecode]
Potete notare che dopo che la funzione è stata richiamata ricorsivamente lo spostamento e la rotazione degli assi vengono riportate alle loro condizioni precedenti.
Cercherò di spiegarvi il perché nel modo più semplice possibile. Avete presente l’esempio delle scatole che ho fatto all’inizio? Immaginate di costruire la prima scatola, non potete costruire il coperchio fin quando non avete costruito anche tutto le altre scatole all’interno. Infatti costruiremo prima tutte le scatole una dentro l’altra, poi costruiremo i coperchi a ritroso dalla più piccola alla più grande. Solo che man mano che abbiamo costruito le scatole abbiamo dimenticato le dimensioni di quelle precedenti quindi abbiamo bisogno che qualcuno ce le ricordi.
Ripristinare le condizioni degli assi dopo aver richiamato la funzione ha proprio la funzione di ricordarci a che punto eravamo prima di addentrarci nella ricorsione.
Per far funzionare il tutto dobbiamo chiamare la funzione albero per la prima volta all’interno della funzione setup passando come parametro la lunghezza del tronco
[sourcecode language="processing"]
void setup(){
size(550,500); //dimensione della finestra
smooth(); //antialiasing
background(255); //sfondo bianco
stroke(0); //linee nere
//disegno
translate(width/2,height); //si muove in basso al centro
line(0,0,0,-lunghezza_tronco); //disegna la linea di partenza
translate(0,-lunghezza_tronco); //si muove sulla punta della linea
albero(lunghezza_tronco);
}
[/sourcecode]
Premiamo RUN e godiamoci il risultato.

Non so voi ma io trovo affascinante quanto la freddezza della matematica si ritrovi nella bellezza della natura.
Dovete sapere che tutte le trasformazioni (traslazioni e rotazioni) che effettuiamo sugli assi cartesiani di riferimento sono gestiti da una matrice detta matrice di trasformazione. Gli stati della matrice di trasformazione possono essere salvati in un array Last In First Out (l’ultimo ad entrare è il primo ad uscire) detto anche pila. La particolarità di questa struttura dati è che quando si salvano dei dati questi finiscono sempre in cima alla pila e quando si vogliono recuperare si possono prendere solo i dati che si trovano in cima alla pila in quel momento. Le istruzioni che ci permettono di salvare e recuperare i i dati dalla pila della matrice di trasformazione sono
pushMatrix(); salva lo stato attuale della matrice di trasformazione in cima alla pila;
popMatrix(); recupera l’ultimo stato salvato della matrice di trasformazione.
Usiamo queste due istruzioni per migliorare il codice della funzione albero.
[sourcecode language="processing"]
void albero(float l) {
l = l*2/3; //ogni ramo successivo è lungo 2/3 il precedente
if(l>2){ //condizione di uscita se la lunghezza è minore di 2 pixel
//costruzione braccio destro
pushMatrix(); //salva lo stato attuale della matrice di trasformazione
rotate(a); //ruota di a in senso orario
line(0,0,0,-l); //disegna una linea
translate(0,-l); //si muove sulla punta della linea
albero(l); //richiama la funzione ricorsivamente
popMatrix(); //recupera l’ultimo stato salvato della matrice di trasformazione
//costruzione braccio sinistro
pushMatrix(); //salva lo stato attuale della matrice di trasformazione
rotate(-a); //ruota di a in senso antiorario
line(0,0,0,-l); //disegna una linea
translate(0,-l); //si muove sulla punta della linea
albero(l); //richiama la funzione ricorsivamente
popMatrix(); //recupera l’ultimo stato salvato della matrice di trasformazione
}
}
[/sourcecode]
Rendiamo più interessante il nostro albero variando lo spessore e il colore dei rami. L’istruzione che determina lo spessore di una linea è
strokeWeight(spessore); dove il parametro spessore è espresso in pixel.
Abbiamo quindi bisogno di due nuove variabili globali, una per lo spessore del tronco che sarà sempre più piccolo e una per il colore del tronco che sarà sempre più chiaro. Inoltre dobbiamo aggiungere due parametri alla funzione albero per lo spessore e il colore e trattare questi parametri all’interno della funzione così come abbiamo fatto con la lunghezza.
Ecco il codice completo:
[sourcecode language="processing"]
float a=HALF_PI/3; //angolo
float lunghezza_tronco=150;
float spessore_tronco=25;
int colore_tronco=0;
void setup(){
size(550,500); //dimensione della finestra
smooth(); //antialiasing
background(255); //sfondo bianco
stroke(colore_tronco); //colore linee
strokeWeight(spessore_tronco); //spessore linee
//disegno
translate(width/2,height); //si muove in basso al centro
line(0,0,0,-lunghezza_tronco); //disegna la linea di partenza
translate(0,-lunghezza_tronco); //si muove sulla punta della linea
albero(lunghezza_tronco,spessore_tronco,colore_tronco);
}
void albero(float l,float s,int c) {
l = l*2/3; //ogni ramo successivo è lungo 2/3 il precedente
s = s*2/3; //ogni ramo successivo è spesso 2/3 il precedente
c += 20; //ogni ramo successivo è più chiaro di 20 punti del precedente
if(l>2){ //condizione di uscita se la lunghezza è minore di 2 pixel
//costruzione braccio destro
pushMatrix(); //salva lo stato attuale della matrice di trasformazione
rotate(a); //ruota di a in senso orario
stroke(c);
strokeWeight(s);
line(0,0,0,-l); //disegna una linea
translate(0,-l); //si muove sulla punta della linea
albero(l,s,c); //richiama la funzione ricorsivamente
popMatrix(); //recupera l’ultimo stato salvato della matrice di trasformazione
//costruzione braccio sinistro
pushMatrix(); //salva lo stato attuale della matrice di trasformazione
rotate(-a); //ruota di a in senso antiorario
stroke(c);
strokeWeight(s);
line(0,0,0,-l); //disegna una linea
translate(0,-l); //si muove sulla punta della linea
albero(l,s,c); //richiama la funzione ricorsivamente
popMatrix(); //recupera l’ultimo stato salvato della matrice di trasformazione
}
}
[/sourcecode]
E questo è il risultato

Per completare il tutorial aggiungeremo un po’ di interattività. Faremo in modo di modificare l’angolo tra i rami con il mouse. Per fare questo abbiamo bisogno della funzione draw (ricordiamo che le istruzioni all’interno di questa funzione vengono eseguite ad ogni frame) all’interno della quale andremo a calcolare l’angolo a seconda della posizione verticale del mouse. Tale varierà da 0° quando il mouse si troverà in alto a 90° quando il mouse si troverà in basso. Naturalmente la costruzione del tronco così come funzione albero non dovranno più trovarsi nella funzione setup bensì proprio nella funzione draw in modo che l’albero possa essere ridisegnato ad ogni frame con l’angolo corrente.
Il codice non è molto diverso dal precedente:
[sourcecode language="processing"]
float a; //angolo
float lunghezza_tronco=150;
float spessore_tronco=25;
int colore_tronco=0;
void setup(){
size(550,500); //dimensione della finestra
smooth(); //antialiasing
}
void draw() {
frameRate(30);
background(255); //pulisce lo schermo
//calcola l’angolo in base alla posizione y del mouse
a = ((float)mouseY/ (float)height)*HALF_PI;
stroke(colore_tronco); //colore linee
strokeWeight(spessore_tronco); //spessore linee
//disegno
translate(width/2,height); //si muove in basso al centro
line(0,0,0,-lunghezza_tronco); //disegna la linea di partenza
translate(0,-lunghezza_tronco); //si muove sulla punta della linea
albero(lunghezza_tronco,spessore_tronco,colore_tronco);
}
void albero(float l,float s,int c) {
l = l*2/3; //ogni ramo successivo è lungo 2/3 il precedente
s = s*2/3; //ogni ramo successivo è spesso 2/3 il precedente
c += 20; //ogni ramo successivo è più chiaro di 20 punti del precedente
if(l>2){ //condizione di uscita se la lunghezza è minore di 2 pixel
//costruzione braccio destro
pushMatrix(); //salva lo stato attuale della matrice di trasformazione
rotate(a); //ruota di a in senso orario
stroke(c);
strokeWeight(s);
line(0,0,0,-l); //disegna una linea
translate(0,-l); //si muove sulla punta della linea
albero(l,s,c); //richiama la funzione ricorsivamente
popMatrix(); //recupera l’ultimo stato salvato della matrice di trasformazione
//costruzione braccio sinistro
pushMatrix(); //salva lo stato attuale della matrice di trasformazione
rotate(-a); //ruota di a in senso antiorario
stroke(c);
strokeWeight(s);
line(0,0,0,-l); //disegna una linea
translate(0,-l); //si muove sulla punta della linea
albero(l,s,c); //richiama la funzione ricorsivamente
popMatrix(); //recupera l’ultimo stato salvato della matrice di trasformazione
}
}
[/sourcecode]
Potete vedere chiaramente che l’istruzione a = ((float)mouseY/ (float)height)*HALF_PI; calcola l’angolo tra i rami in base alla coordinata y del mouse e all’altezza della finestra. Il float tra parentesi rende decimali i le due variabili intere mouseY e height perché non è possibile assegnare un valore intero ad una variabile decimale.
Non vi resta che premere RUN :-)
Questo tutorial è basato su un progetto di esempio di Processing che potete trovare andando su File -> Examples -> Topics -> Fractals and L-Systems -> Tree.


(No Ratings Yet)
Ciao Stefano! Scusa se rompo, volevo chiederti come si poteva creare un sistema client-server con processing. :)
In particolare come si può gestire il server in modo che il client possa essere un applicativo in Java o c++.
Ottimo tutorial!
TI ringrazio. A breve ne posterò altri su Processing ;-)
Come si puo un programma che riconosce immagini sferiche acquisite da web cam in processing??
Quello di cui hai bisogno è un modo di catturare immagini da webcam con Processing e un algoritmo efficiente che ti permetta di riconoscere delle forme in un’immagine.
Per il primo punto ti consiglio di guardare gli esempi di Processing (li puoi trovare nel menu File), in particolare alla sezione Libraries->video->Capture.
Il secondo punto è più complesso. L’algoritmo di cui hai bisogno fa uso della Trasformata di Hough di cui puoi trovare maggiori informazioni qui http://microservo.altervista.org/documenti/Relazione5/hough.htm.
Spero di esserti stato utile.