Tehokas esimerkki rekursiosta, eli tekniikasta, jossa aliohjelma kutsuu itse itseään uudestaan erilaisin parametrein. Fraktaalit yleensä perustuvat tähän.
Tarkemmin niinkutsuttu puu-fraktaali siis kysessä. Ideana on, että funktio piirtää viivan, jonka jälkeen laskutoimitukset tehtyään se kutsuu itseään kahdesti uudestaan päinvastaisilla kulma-parametreilla. Nämä kaksi taas kutsuvat itseään taas kahdesti, jolloin aikaan ollaan saatu jo viisi "haaraa", mukaan lukien alkuhaaran.
Perinteisesti viivan pituus on puoliintunut per haara, mutta itse pidän "efektiä" kauniimpana kun pituudesta vähennetään esim. kolmasosa. Efektiä voi myös kaunistaa piirtämällä viimeisiin haaroihin pallurat, kuten esimerkissä ollaan tehty.
Sini ja cosini kannattanee laskea etukäteen, mutta selkeyden vuoksi pistin ne nyt sitten aina erikseen. Efektiä voi muuttaa paljonkin vaihtamalla kulma-muuttujaa main()-funktiossa sekä parametreja, joilla main()ista alunperin kutsutaan funktiota.
Kääntynee linukalla g++:n läpi näinikkäin: g++ -lSDL -o puufraktaali puufraktaali.cpp
Ja eiköhän se Windowsistakin kun vain antaa oikeat parametrit kääntäjälle. Pistimpä vielä valmiiksi mainiin parametrit :)
Kaksi kuvankaappausta: http://koti.mbnet.fi/~tzaeru/tree_fractal1.png
http://koti.mbnet.fi/~tzaeru/tree_fractal.png
#include <SDL/SDL.h> #include <math.h> class fraktaali { private: int i; public: float kulma; SDL_Surface *screen; void piirto(float x, float y, float x1, float y1, float r, float n); //x ja y ovat kohdat, joista kyseisen viivan piirto aloitetaan, x1 ja y1 ovat x- ja y-kulmat, r on viivan pituus ja n on kuinka monta haaraa piirretään. void bresenham(float r, float x, float y); //vanha tuttu Bresenhamin, myös winkeistä löytyvä, ympyrä. }; void fraktaali::piirto(float x, float y, float x1, float y1, float r, float n) { //piirretään viiva for (i = 0; i < r; i++) { x += x1; y += y1; *(Uint32 *)((Uint8 *)screen->pixels + (int)y*screen->pitch + ((int)x << 2)) = 255; } if (n == 0) // maksimimäärä piirtämisiä { bresenham(20, x, y); // Eli kun ollaan viimeisen haaran kohdalla, piirretään Bresenhamin ympyrä, jolloin saadaan "kasvustoa" latvoihin. return; } n--; r *= 0.7; //lyhennetään viivan pituutta piirto(x, y, cos(kulma) * x1 + sin(kulma) * y1, -sin(kulma) * x1 + cos(kulma) * y1, r, n); //kutustaan funktioita uudestaan eri suunta-parametreilla, käytännössä päinvastaisilla keskenään, jotta haara olisi symmetrinen piirto(x, y, cos(-kulma) * x1 + sin(-kulma) * y1, -sin(-kulma) * x1 + cos(-kulma) * y1, r, n); } void fraktaali::bresenham(float r, float x, float y) { //while (r > 0) //{ int vari = 255 << 8; //vihreä väriks. float pe = 3 - 2 * (int)r; float y1 = (int)r; float x1 = 0; while (x1 < y1) { // piirrä segmentti *(Uint32 *)((Uint8 *)screen->pixels + ((int)y + (int)y1) * screen->pitch + (((int)x + (int)x1) << 2)) = vari; *(Uint32 *)((Uint8 *)screen->pixels + ((int)y + (int)y1) * screen->pitch + (((int)x - (int)x1) << 2)) = vari; *(Uint32 *)((Uint8 *)screen->pixels + ((int)y - (int)y1) * screen->pitch + (((int)x + (int)x1) << 2)) = vari; *(Uint32 *)((Uint8 *)screen->pixels + ((int)y - (int)y1) * screen->pitch + (((int)x - (int)x1) << 2)) = vari; *(Uint32 *)((Uint8 *)screen->pixels + ((int)y + (int)x1) * screen->pitch + (((int)x + (int)y1) << 2)) = vari; *(Uint32 *)((Uint8 *)screen->pixels + ((int)y - (int)x1) * screen->pitch + (((int)x + (int)y1) << 2)) = vari; *(Uint32 *)((Uint8 *)screen->pixels + ((int)y + (int)x1) * screen->pitch + (((int)x - (int)y1) << 2)) = vari; *(Uint32 *)((Uint8 *)screen->pixels + ((int)y - (int)x1) * screen->pitch + (((int)x - (int)y1) << 2)) = vari; // korjaa sijaintia if (pe < 0) pe += 4 * x1 + 6; else { pe += 4 * (x1 - y1) + 10; y1 -= 1; } x1 += 1; } //r -= 20; vari = vari << 4; //} } int main(int argc, char *argv[]) { SDL_Surface *screen; screen = SDL_SetVideoMode(1248, 1024, 32, SDL_HWSURFACE | SDL_DOUBLEBUF); Uint8* nappi; SDL_Event tapahtuma; if(SDL_Init(0)==-1) { printf("SDL_Init: %s\n", SDL_GetError()); return 0; } bool paalla = true; fraktaali Fraktaali; Fraktaali.kulma = 0.25; Fraktaali.screen = screen; while (paalla) { // katsotaan, jos painaa sitä ruksia oikeassa yläkulmassa SDL_PollEvent(&tapahtuma); if ( tapahtuma.type == SDL_QUIT ) paalla = false;//poistuttaan // jos esc niin pois nappi = SDL_GetKeyState(NULL); if ( nappi[SDLK_ESCAPE] ) paalla = false; Fraktaali.piirto(600, 1024, 0, -1, 240, 10); //ensimäisenä on kohta, josta puu alkaa, seuraavaksi on alkusuunnat (tässä tapauksessa suoraan ylöspäin, seuraavana seuraa ensimäisen viivan pituus ja viimeisenä piirrettävien haarojen määrä. SDL_Flip(screen); } SDL_Quit(); return 0; }
Pikselin piirtoa olisi hyvä siistiä muotoon:
*(pos+leveys*y+x) = vari;
Tällä saa myös jänniä kuvioita ku muuttaa sitä kulmaa kesken piirtämisen, eli siis piirtää noi eri haarat/tasot/jotku eri kulmalla - tässähän se on aina sama. No joo, eihän se sit enää ihan puufraktaali oo, mut jännältä näyttää. Kattokaa esim. http://koodaa.mine.nu/a/jokusekov.png
En muista tarkasti miten noi kulmat tossa meni, mut kuitenki sillee että eri tommosilla tasoilla muutetaan eri verran sitä sillee heh ei kukaan tajuu :--D
Aihe on jo aika vanha, joten et voi enää vastata siihen.