Sunteți pe pagina 1din 17

Colegiul Naţional de Informatică “Grigore Moisil” Braşov

FRACTALI ÎN VISUAL C++

– Lucrare pentru atestarea competenţelor profesionale –

Iulia Comşa
Clasa a XII-a C
Profesor îndrumător: Laura Niţulescu

2008
1. Motivarea alegerii temei. Utilitatea aplicaţiei.

„De ce este geometria considerată „rece” şi „seacă”? Unul dintre motive constă în
incapacitatea ei de a descrie forma unui nor, a unui munte, a unui ţărm sau a unui copac.
Copacii nu sunt sfere, munţii nu sunt conuri, ţărmurile nu sunt cercuri, coroanele arborilor
nu sunt netede şi nici fulgerele nu călătoresc în linie dreaptă..”

Astfel îşi începea Benoît Mandelbrot lucrarea „Geometria Fractală a Naturii” (1975),
subliniind necesitatea unei noi abordări matematice a studierii formelor şi mişcărilor din
natură şi din univers. Scopul lui era acela de a pune bazele unei teorii care să studieze
modelele neregulate şi fragmentate. Pentru aceasta, a definit o „familie de forme” numite
„fractali”.

Termenul ”fractal” provine din limba latină, „fractus” însemnând „spart”, „fracturat”.
Caracteristicile fractalilor sunt, în general, următoarele:
 structura fină la orice scară;
 regăsirea, la scară mai mică, a modelului privit de la scară mare (autosimilitudine);
 definiţia simplă, recursivă, ce dă naştere la forme prea complexe pentru a putea fi
descrise în detaliu cu ajutorul geometriei euclidiene.

Mandelbrot a folosit geometria fractală atât în topologie, descriind suprefeţe neregulate (de
exemplu, coasta de ţărm a Marii Britanii), cât şi în astronomie, prezentând dispoziţia roiurilor
de stele în univers. Fractalii cu cea mai mare aplicabilitate folosiţi de el se bazează pe hazard,
regularităţile şi iregularităţile lor fiind statistice.

Cu toate că doar cei iniţiaţi în detalii matematice pot lucra în mod practic cu fractali,
frumuseţea, simetria şi perfecţiunea matematică a acestora este fascinantă pentru publicul
larg. Am ales fractalii ca temă pentru lucrarea de atestat în primul rând pentru a înţelege baza
matematică a frumuseţii celebrei imagini a fractalului Mandelbrot şi a altor fractali, dar şi
pentru a mă obişnui cu generarea graficii în Visual C++, partea vizuală fiind foarte
importantă – uneori chiar definitorie pentru o aplicaţie – în programare. De asemenea, este o
ocazie de a populariza fractalii, ca pe manifestari ale artei matematice.
Aplicaţia construieşte fractali generaţi prin metoda Divide-et-Impera şi mulţimi de tip
Mandelbrot şi Julia, dând utilizatorului posibilitatea de a seta anumiţi paramentri ai funcţiilor
de calcul al poziţiei/culorii punctelor. Având în vedere numărul infinit de fractali ce pot fi
creaţi şi complexitatea acestora, acest program are ca scop ilustrarea tipurilor de bază de
fractali şi deschiderea orizontului utilizatorului pentru fractali mai complecşi.

2. Structura aplicaţiei. Organizarea conţinutului informaţional

Am încercat realizarea unei interfeţe cât mai plăcute şi uşor de folosit din punctul de vedere
al utilizatorului şi sper că am reuşit acest lucru.

Aplicaţia este centrată pe meniul principal, ce constă într-un form ce conţine informaţii
despre autor şi două butoane, însoţite de etichete ce enumeră tipurile de fractali spre care fac
legatura butoanele. Acestea deschid câte un form ce conţine câte o listă derulantă
(comboBox) ce permite selecţia tipului de fractal, un buton pentru desenarea fractalului şi o
casetă pentru imagine, în care va fi reprezentat fractalul la apăsarea butonului „Desenare”.

a) Fractalii generaţi prin metoda Divide-et-Impera au la bază un model simplu, pe baza


căruia se adaugă (în cazul arborilor şi al triunghiului lui Sierpinski) sau se înlocuiesc (în cazul
fractalilor Koch) segmente, după o regulă bine definită, aşa cum se poate observa în figurile
1.1 – 1.4.
Figura 1.1: fractalul arbore. Raportul dintre lungimea ramurii-mamă şi ramurii-fiică este ales din
motive de încadrare cât mai bună în caseta imaginii.

Figura 1.2: fractalul de tip Koch triunghiular. Aşezarea a trei braţe în formă de triunghi duce la
celebra formă de fulg de nea.

Figura 1.3: fractalul de tip Koch pătratic. Poate fi folosită în „umplerea” spaţiilor.

Figura 1.4: fractalul de tip Sierpinski. Fiecare triunghi mai mic este copia triunghiului iniţial la
scara mai mică.
O casetă de text îi permite utilizatorului să introducă numărul de iteraţii („încreţituri”, )
folosite în construirea fractalului. Acest număr va defini „adâncimea” fractalului, fiind de
asemenea responsabil de timpul în care acesta va fi desenat.

Un număr mare de iteraţii duce la un timp foarte mare de desenare şi la o îmbunătăţire


nesemnificativă a „rezoluţiei” fractalului, având în vedere lungimile foarte mici (aproape
insesizabile) din care va fi format acesta. De aceea, în funcţie de raportul dintre îmbunătăţirea
imaginii şi timpul necesar desenării, am stabilit o limită a „încreţiturii” pentru fiecare tip de
fractal:
 arbori: n=10 (aproximativ 65 secunde)
 Koch (triunghi): n=5 (aproximativ 102 secunde)
 Koch (pătrat): n=4 (aproximativ 66 secunde)
 Sierpinski: n=6 (aproximativ 35 secunde)

La trecerea de aceste limite, utilizatorul este avertizat printr-un mesaj şi i se dă ocazia să


revoce desenarea, dar, în special dacă este suficient de răbdător, el poate alege să continue.

b) Seturile Julia şi Mandelbrot sunt reprezentări ale punctelor dintr-un planul numerelor
complexe cu proprietatea că şirul xn+1= xn2+C este mărginit.

În cazul setului Mandelbrot, primul


termen al şirului (x0) este egal cu 0,
iar C ia coordonatele fiecărui punct
din planul considerat, studiindu-se,
pentru fiecare caz, dacă xn tinde sau
nu spre infinit.

Se poate demonstra că, dacă


modulul lui xn devine mai mare
decât 2, şirul tinde spre infinit.
Seturile Julia pe care le-am construit au aceeaşi formulă de recurenţă ca setul Mandelbrot,
dar, spre deosebire de acesta, punctul C este fixat, iar primul termen (xn) ia coordonatele
fiecărui punct din plan.

La selectarea setului Julia din


lista derulantă, pe form apar
două casete numerice
(numericUpDown) cu
opţiunea de incrementare cu
0.01, prin care utilizatorul
poate alege coordonatele
constantei C. Valorile implicite
pentru aceste casete sunt -0.8
şi 0.156, ce corespund
punctului C=-0.8+0.156i.
Fractalul generat de aceaste
coordonate este cel alăturat. Am ales aceste coordonate deoarece definesc, după părerea mea,
unul dintre cele mai frumoase seturi Julia. Pentru revenirea la acestea, utilizatorul are la
dispoziţie un buton ce completează automat casetele numerice.

Pentru fractalii de tip iterativ, este posibilă mărirea unei porţiuni din imagine. Selecţia
porţiunii dorite se face cu mouse-ul. Raportul dintre lungimea şi lăţimea ariei selectate va fi
constrâns la raportul dintre lungimea şi lăţimea casetei imaginii, pentru a evita „tăierea”
ulterioară a unei porţiuni din selecţie e ce nu va „încăpea” în casetă. La eliberarea mouse-ului,
în cazul în care selecţia are o arie mai mare decât 0, utilizatorul este întrebat, printr-un mesaj,
dacă doreşte să mărească zona selectată. În cazul unui răspuns pozitiv, aria selectată este
desenată în caseta de imagine. În noul desen, utilizatorul poate mări din nou orice porţiune
dorită, respectând raportul dintre lungime şi lăţime. Pentru a desena din nou fractalul iniţial,
se va apăsa pe butonul „Desenare”.

Dorind să aleg o paletă de culori cât mai frumoasă şi experimentând în acest sens, am găsit o
serie de seturi cromatice ce pot fi utilizate în desenarea fractalilor Mandelbrot şi Julia.
Neputând să aleg una singură, deoacere fiecare era frumoasă în felul ei, am adăugat un buton
ce pemite schimbarea culorii. Seturile cromatice se schimbă ciclic.

3. Detalii tehnice de implementare

La apăsarea unuia dintre cele două butoane de pe formul principal, se creează o nouă
instanţă a formului corespunzător (am numit cele două formuri „sierpinsky.h” şi
„mandelbrot.h”, după numele unuia dintre fractali):

private: System::Void button1_Click(System::Object * sender,


System::EventArgs * e)
{
sierpinsky *divimp=new sierpinsky;
divimp->ShowDialog();
}

private: System::Void button2_Click(System::Object * sender,


System::EventArgs * e)
{
mandelbrot *mandel=new mandelbrot;
mandel->ShowDialog();
}

“ShowDialog()” asigură dezactivarea meniului principal atâta timp cât formul al doilea este
deschis, prevenind astfel deschiderea mai multor ferestre de acelaşi tip.

a) Fractalii de tip Divide-et-Impera sunt construiţi, după cum sugerează numele, prin
„spargerea” componentei principale în mai multe părţi şi aplicarea „spargerii” asupra
componentelor mai „mici” rezultate, până când se ajunge la cazul (componenta) de bază. La
aceasta se ajunge efectuând un număr de apeluri ale funcţiei recursive egal cu numărul
introdus în caseta de text – în cazul în care utilizatorul a introdus un număr natural. În caz
contrar, este afişat un mesaj:

int n;
try
{
n=Convert::ToInt32(textBox1->Text);
}
catch(...)
{
MessageBox::Show("Valoare imposibila.\nVa rugam reveniti cu
numere naturale.", "Mai incercati!");
return;
}

În funcţie de indexul selecţiei din lista derulantă (this->comboBox1->SelectedIndex),


este apelată una dintre funcţiile recursive de desenare a fractalilor, având ca parametri
numărul de încreţituri introdus de utilizator, coordonatele pe axele Ox şi Oy a punctelor de
început pentru desenare, lungimea liniei folosite şi unghiul acesteia).

De exemplu, funcţia de desenare a fractalului arbore este următoarea:

void arbore(double x0,double y0,double l,double u,int n)


{
if(n>=0)
{
double x1,y1;
x1=x0+l*Math::Cos(u);
y1=y0-l*Math::Sin(u);

Graphics *g;
g=Graphics::FromImage(b);
g->DrawLine(new Pen(Color::Blue,2),
Point(x0,y0),Point(x1,y1));
g->Dispose();
pictureBox1->Refresh();
arbore(x1,y1,l*4/7,u-Math::PI/4,n-1);
arbore(x1,y1,l*4/7,u+Math::PI/4,n-1);
}
}

În variabilele x1 şi y1 sunt calculate coordonatele punctului de sfârşit al liniei ce trebuie


reprezentată, în funcţie de lungimea acesteia şi de unghiul sub care va fi desenată. Pointerul
*g primeşte adresa imaginii din casetă, căreia îi adaugă o linie prin comanda g->
DrawLine(new Pen(Color::Blue,2),Point(x0,y0),Point(x1,y1)) (parametrii sunt
culoarea şi grosimea liniei, coordonatele punctul de început, coordonatele punctului de
sfârşit). Este apoi apelată de două ori funcţia de desenare, cu paramentrii modificaţi: punctele
de început ale următoarei linii vor avea coordonatele punctului de sfârşit al ultimei linii
desenate, lungimea va fi micşorată prin raportul 4/7, numărul nivelului va fi cu o unitate mai
mic, iar unghiul va fi modificat cu 45o (o dată în plus, a doua oară în minus).
Condiţia de efectuare a instrucţiunilor din funcţie este ca numărul de niveluri ale fractalului
să fie mai mare decât 0.

Ca şi în cazul arborelui, fractalul Sierpinski este construit prin adăugarea de linii la imaginea
generată până în acel moment, până când nivelul devine mai mic decât n. Se porneşte de la
un triunghi în care au fost desenate liniile mijlocii şi se desenează liniile mijlocii ale
triunghiurilor mai mici marginale formate:

void triunghi(int n,double x1,double y1,double l)


{

if(n>=0)
{
Graphics *g;
g=Graphics::FromImage(b);

double x2=x1+l/2;
double y2=y1-l*Math::Sqrt(3)/2;
double x3=x1-l/2;
double y3=y2;

g->DrawLine(new Pen(Color::DarkTurquoise,2),
Point(x1,y1),Point(x2,y2));
g->DrawLine(new Pen(Color::DarkTurquoise,2),
Point(x2,y2),Point(x3,y3));
g->DrawLine(new Pen(Color::DarkTurquoise,2),
Point(x3,y3),Point(x1,y1));
g->Dispose();
pictureBox1->Refresh();

triunghi(n-1,x1,y2,l/2);
triunghi(n-1,x2,y1,l/2);
triunghi(n-1,x3,y1,l/2);
}
}

În cazul fractalilor de tip Koch, liniile nu se adaugă, ci se înlocuiesc. Pentru calcularea


lungimii, poziţiei şi unghiului fiecărui segment al liniei, funcţia este apelată de n ori (de aceea
timpul necesar desenării este foarte lung), segmentul fiind desenat atunci când nivelul devine
0.

La fractalul de tip Koch triunghiular, coordonatele care ne interesează se află la prima treime,
a doua treime şi jumătatea segmentului. La fiecare dintre acestea, unghiul de desenare se
modifică cu 60o, -120o, respectiv 60o. Iată funcţia:
void koch(int n,double x1,double y1,double l,double u)
{
if(n==0)
{
double x2,y2;
x2=x1+l*Math::Cos(u);
y2=y1-l*Math::Sin(u);

Graphics *g;
g=Graphics::FromImage(b);
g->DrawLine(new Pen(Color::Color::DarkBlue,2),
Point(x1,y1),Point (x2,y2));
g->Dispose();
pictureBox1->Refresh();

else
{
double x2,y2,x3,y3,x4,y4;

x2=x1+(l/3)*Math::Cos(u);
y2=y1-(l/3)*Math::Sin(u);

x4=x1+(2*l/3)*Math::Cos(u);
y4=y1-(2*l/3)*Math::Sin(u);

x3=x2+(l/3)*Math::Cos(u+Math::PI/3);
y3=y2-(l/3)*Math::Sin(u+Math::PI/3);

koch(n-1,x1,y1,l/3,u);
koch(n-1,x2,y2,l/3,u+Math::PI/3);
koch(n-1,x3,y3,l/3,u-Math::PI/3);
koch(n-1,x4,y4,l/3,u);
}
}

Fractalul de tip Koch pătratic este similar cu fractalul Koch triunghiular, cu excepţia faptului
că ne interesează punctele ce marchează un sfert din lungimea segmentului, iar unghiurile se
schimbă cu ±90o:

void koch_q(int n,double x0,double y0,double l,double u)


{
double x1=x0+l*Math::Cos(u);
double y1=y0-l*Math::Sin(u);
if(n==0)
{
Graphics *g;
g=Graphics::FromImage(b);
g->DrawLine(new Pen(Color::DarkRed,2),
Point(x0,y0),Point(x1,y1));
g->Dispose();
pictureBox1->Refresh();
}
else
{
l=l/4;
koch_q(n-1,x0,y0,l,u);
double x2,y2;

x2=x0+l*Math::Cos(u);
y2=y0-l*Math::Sin(u);
u+=Math::PI/2;
koch_q(n-1,x2,y2,l,u);

x2+=l*Math::Cos(u);
y2-=l*Math::Sin(u);
u-=Math::PI/2;
koch_q(n-1,x2,y2,l,u);

x2+=l*Math::Cos(u);
y2-=l*Math::Sin(u);
u-=Math::PI/2;
koch_q(n-1,x2,y2,l*2,u);

x2+=l*2*Math::Cos(u);
y2-=l*2*Math::Sin(u);
u+=Math::PI/2;
koch_q(n-1,x2,y2,l,u);

x2+=l*Math::Cos(u);
y2-=l*Math::Sin(u);
u+=Math::PI/2;
koch_q(n-1,x2,y2,l,u);

x2+=l*Math::Cos(u);
y2-=l*Math::Sin(u);
u-=Math::PI/2;
koch_q(n-1,x2,y2,l,u);

}
}

b) Fractalii de tip iterativ (Mandelbrot şi Julia) sunt construiţi prin iteraţii succesive ale
fiecărui punct din planul considerat după formula xn+1= xn2+C.

Fiecărui număr de iteraţii necesar unui punct pentru ca, prin iteraţii succesive, modului său să
scape din intervalul [0,2] îi corespunde o culoare. De acees, numărul maxim de culori trebuie
să fie egal cu numărul maxim de iteraţii efectuate pe un număr pentru a testa limita şirului.
Am notat această constantă cu NMAXC, iniţializată cu 2560. Culoarea neagră corespunde
mulţimii punctelor ce nu ies din intervalul [0,2] după NMAXC iteraţii.
Punctele interesante din setul Mandelbrot se află aproximativ în intervalul [-2,1] pe axa Ox şi
[-1,2] pe Oy. Am reţinut în patru variabile coordonatele din planul complex ce vor mărgini
suprafaţa: xmin=-2.2, xmax=1, ymin=-1.2, ymax=1.2 (astfel încât W/H=(xmax-
xmin)/(ymax-ymin)=3/2, unde H reprezintă înălţimea, iar W – lungimea casetei imaginii).
Parcurgerea casetei de imagine se face cu variabilele xe şi ye, iar variabilele xf şi yf reţin
transformarea coordonatelor xe şi ye în coordonatele corespunzătoare în intervalele
[xmin,xmax] şi [ymax,ymax].

Pentru fiecare punct de coordonate xf şi yf, se efectuează iteraţii atâta timp cât modulul
numărului complex este mai mic decât 2 (pătratul său este mai mic decât 4) sau atâta timp cât
nu am depăşit numărul maxim de iteraţii. La ieşirea din instrucţiunea while, numărul iteraţiei
(reţinut în variabila ic) va da indicele culorii: dacă s-a ieşit din while pentru că numărul de
iteraţii a depăşit numărul maxim de culori, înseamnă că şirul x n nu a scăpat spre infinit, iar
punctul este colorat în negru; altfel, xf a avut nevoie de ic iteraţii pentru a depăşi, în modul,
valoarea 2, prin urmare primeşte culoarea cu indicele ic.

int xe,ye;
double xf,yf,x,y,xnew,ynew,ic;
for(ye=0;ye<H;ye++)
{
for(xe=0;xe<W;xe++)
{
xf=(xmax-xmin)/W*xe+xmin;
yf=(ymin-ymax)/H*ye+ymax;
x=0;y=0;
ic=0;
while(x*x+y*y<4&&ic<=NMAXC)
{
ic++;
xnew=x*x-y*y+xf;
ynew=2*x*y+yf;
x=xnew;y=ynew;
}
if(ic>NMAXC) ic=0;
b->SetPixel(xe,ye,c[ic]);
}
pictureBox1->Refresh();
}

Cazul setului Julia este similar, cu excepţia coordonatelor xmin=-1.6, xmax=1.6, ymin=-1.2,
ymax=1.2 şi a instrucţiunii while, unde xc şi yc sunt coordonatele constantei introduse de
utilizator în casetele numerice (numericUpDowns):
while(x*x+y*y<4&&ic<=NMAXC)
{
ic++;
xnew=x*x-y*y+xc;
ynew=2*x*y+yc;
x=xnew;y=ynew;
}

Valoarile minime şi maxime ce pot fi introduse în casetele numerice sunt -1, respectiv 1. În
afara acestor valori nu apar imagini interesante.

Proprietatea de autosimilitudine poate fi evidenţiată cu ajutorul posibilităţii de mărire. La


apăsarea butonului stâng al mouse-ului pe imagine, sunt reţinute coordonatele punctului în
variabilele xsel şi ysel:

private: System::Void pictureBox1_MouseDown_1(System::Object * sender,


System::Windows::Forms::MouseEventArgs * e)
{
pictureBox1->Refresh();
xsel=e->X;
ysel=e->Y;
}

Trebuie avute în vedere toate cazurile de direcţie a selecţiei. Am stabilit ca xsel şi ysel să fie
întotdeauna colţul din dreapa sus ale dreptunghiului ce marchează selecţia, iar xsel1 şi ysel1,
colţul din stânga jos. De asemenea, selecţia este condiţionată de păstrarea raportului dintre
lungimea şi lăţimea selecţiei egal cu raportul dintre lungimea şi lăţimea imaginii.

int x,y,h,w;
x=xsel<xsel1?xsel:xsel1;
y=ysel<ysel1?ysel:ysel1;

double rap=(double)b->Width/b->Height;
w=Math::Abs(xsel-xsel1);
h=Math::Abs(ysel-ysel1);
if(h*w==0)w=h=0;
else if((double)w/h>rap)
w=rap*h;
else
h=w/rap;
if(xsel>xsel1) x=xsel-w;
if(ysel>ysel1) y=ysel-h;

La eliberarea mouse-ului, utilizatorul este întrebat dacă doreşte mărirea zonei selectate. În
cazul unui răspuns afirmativ, coordonatele minime şi maxime sunt recalculate, iar fractalul
este desenat din nou:
double xmin1,xmax1,ymin1,ymax1;
xmin1=(xmax-xmin)*lastx/W+xmin;
xmax1=(xmax-xmin)*(lastx+lastw)/W+xmin;
ymin1=ymax-(ymax-ymin)*(lasty+lasth)/H;
ymax1=ymax-(ymax-ymin)*(lasty)/H;

xmin=xmin1;
xmax=xmax1;
ymin=ymin1;
ymax=ymax1;

Oricât am mări porţiuni din fractal, vom găsi întotdeauna o arie similară fractalului văzut de
la scară mai mare, după cum se observă în imaginile de la pagina alăturată.

Pentru evidenţierea zonelor fractalului într-un mod cât mai plăcut din punct de vedere
estetic, am creat câteva palete de culori. Acestea sunt formate din două sau mai multe culori
în gradaţie, reţinute într-un şir. Fiecărui set şi fiecărei măriri i se potriveşte mai bine o
anumită culoare. Experimentaţi cu ele!

De asemenea, experimentaţi diverse valori ale constantei setului Julia. Câteva dintre cele mai
interesante valori pe care le-am găsit, cu excepţia valorilor aflate iniţial în casetele numerice,
sunt:

-3.8+0.62i 0.19-0.56i

-0.70+0.096i -0.39+0,596i
4. Resurse hardware şi software compatibile

Programul rulează corect pe un sistem Windows XP cu procesor Intel(R) Celeron(R), 1.7


GHz, 510 MB RAM.
Pentru rularea programului nu este necesară instalarea niciunui program suplimentar.

5. Posibilităţi de dezvoltare

Există nenumărate tipuri de fractali; cei prezentaţi prin această aplicaţie sunt doar cei mai
cunoscuţi. Programul ar putea genera şi alte tipuri de fractali construiţi prin Divide-et-
Impera, sau setul Julia şi Mandelbrot pentru şirul definit prin relaţia de recurenţă
xn+1=xnk+1, cu k – un număr natural mai mare decât 1 introdus de utilizator.

De asemenea ar putea fi abordaţi fractalii ce folosesc numere generate aleator şi pseudo-


aleator, aceştia fiind cei mai utili în practică şi în domenii precum jocurile pe calculator, la
generarea peisajelor.
6. Bibliografie

 Benoît Mandelbrot, “Geometria Fractală a Naturii”

 http://www.tjhsst.edu/~dhyatt/supercomp/n106.html

 http://en.wikipedia.org/wiki/Fractal

 http://en.wikipedia.org/wiki/Koch_snowflake

 http://en.wikipedia.org/wiki/Sierpinski_triangle

 http://en.wikipedia.org/wiki/Mandelbrot_set

 http://en.wikipedia.org/wiki/Julia_set

S-ar putea să vă placă și