• Home
  • CG
  • I miei Giochi
    • G-Ball
    • King Pong
    • L’Affitto
TIENITI AGGIORNATO

Programmare facile con Processing – l’Albero frattale

feb03
2011
5 Commenti Scritto da thiezar

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.

Scarica il file sorgente di questo tutorial

Categorie Processing, Programmazione - Tags albero, frattale, funzione ricorsiva, processing, programmazione, tutorial
VOTA L'ARTICOLO
Non mi piaceMi piace (No Ratings Yet)
Loading ... Loading ...
CONDIVIDI Twitter Facebook Delicious StumbleUpon E-mail
« Guadagnare creando giochi in Flash – Primi passi
» Creare un’immagine 3D anaglifica con Photoshop

5 Comments

  1. Francesco Vottari's Gravatar Francesco Vottari
    28 febbraio 2011 at 14:28 | Permalink

    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++.

  2. evolution's Gravatar evolution
    3 febbraio 2012 at 13:16 | Permalink

    Ottimo tutorial!

  3. thiezar's Gravatar thiezar
    3 febbraio 2012 at 17:42 | Permalink

    TI ringrazio. A breve ne posterò altri su Processing ;-)

  4. vincenzo's Gravatar vincenzo
    6 novembre 2012 at 16:01 | Permalink

    Come si puo un programma che riconosce immagini sferiche acquisite da web cam in processing??

  5. thiezar's Gravatar thiezar
    29 novembre 2012 at 15:38 | Permalink

    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.

Lascia un Commento Annulla risposta

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *

Immagine CAPTCHA
Cambia immagine

*

È possibile utilizzare questi tag ed attributi XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Benvenuto su Creactivity!

In questo blog troverai articoli e tutorial che nutriranno la tua anima creativa. Per qualsiasi informazione sentiti libero di contattarmi all'indirizzo thiezar87@gmail.com

Se i miei tutorial si sono rivelati utili o semplicemente se ti sono simpatico, che ne dici di offrirmi una birra utilizzando il pulsante qui sotto? :-)


Categorie

  • Altro (1)
  • Flash (1)
  • Illustrator (1)
  • Monetizzazione (2)
  • Photoshop (2)
  • Programmazione (3)
    • Giochi (1)
    • Processing (3)
  • Stereografia (2)

Calendario

giugno: 2013
L M M G V S D
« apr    
 12
3456789
10111213141516
17181920212223
24252627282930

Blogroll

  • L'Aleph Un interessante e attuale blog opinionista
  • Mondo Nerd Il titolo dice tutto
  • The Trap! Il blog personale di “zia” Silvia
  • Youcill Informatica News e consigli su telefonia ed elettronica

Articoli recenti

  • Programmare ad oggetti con Processing – Arkanoid
  • Guadagnare creando giochi in Flash – Sponsorizzazione
  • Disegnare una tazzina di caffè realistica con Illustrator
  • Convertire un’immagine 2D in una 3D anaglifica con Photoshop
  • Creare un’immagine 3D anaglifica con Photoshop

Ratings

  • Convertire un’immagine 2D in una 3D anaglifica con Photoshop (+1 rating, 1 votes)
  • Guadagnare creando giochi in Flash – Primi passi (+1 rating, 1 votes)
  • Programmare facile con Processing (+1 rating, 1 votes)
  • Disegnare una tazzina di caffè realistica con Illustrator (0 rating, 0 votes)
  • Creare un’immagine 3D anaglifica con Photoshop (0 rating, 0 votes)

EvoLve theme by Blogatize  •  Powered by WordPress Creactivity
coltiva la tua creatività digitale

Powered by AlterVista

Decrescente