IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

JavaScriptu & canvas - Mandelbrot množina

Mandelbrotova množina je jeden z najznámejších fraktálov. Jedná sa o fraktál ležiace v komplexnej rovine od [-2; -2] do [2; 2]. Vzorec pre jeho výpočet je Z = Z 2 + C. Z a C sú komplexné čísla, kde C je konštantná a jedná sa o pozíciu bodu, z ktorého fraktál počítame. Postup a podrobnejší opis je v článku Mandelbrot množina, ktorý odporúčam prečítať. Tak a my môžeme začítať programovať.

Vytvoríme si objekt MandelbrotovaMnozina, do ktorého uložíme pár premenných. /--code js var MandelbrotovaM­nozina = function() {
// Soukromé proměnné
var platno = this.platno = $('#platno').get(0);
var kontext = platno.getCon­text('2d');
var width = platno.width,
height = platno.height;

// Vytvoříme si objekt imageData, kde budou data o jednotlivých pixelech
this.imageData = kontext.create­ImageData(wid­th, height);
this.maxiter = 20; } \--

Premenná platno obsahuje element plátna. V tomto prípade som použil jQuery, ale možno to urobiť aj cez klasický DOM a to document.getElementById("platno"). Ďalšia premenná je 2D kontext na kreslenie a potom rozmery plátna. Základné práce s Canvas je vysvetlená v článku Canvas aneb grafika JavaScriptom. Zaujímavejšie sú ale verejné premenné. V premennej imaged je uložený objekt imaged, ktorý vracia metóda kontext.create­ImageData (width, height); Vytvoríme si tak pole pixelov na plátne. Nakoniec maxiter je premenná určujúca maximálny počet iterácií (maximálny počet pokusov na vyskúšanie, či daný bod naozaj patrí do množiny).

Metóda pre manipuláciu s pixelmi

JavaScript nemá priamo metódu, ktorá by vedela na užívateľsky prijateľnej úrovni manipulovať s pixelmi na plátne, a preto si ju musíme vytvoriť sami. Nazveme ju treba navstavPixel, ešte než ju ale vysvetlím, pokúsim sa definovať pixel. Pixel je nejaký bod na plátne. Tu je tvorený súborom subpixelov, čo sú jednotlivé zložky farieb a alfa kanál. Jeden pixel je tak v poli vyjadrený štyrmi číslami. Prvá je hodnota červenej, druhý hodnota zelenej, tretí modrej a štvrtý je práve ten alfa kanál (priehľadnosť). Toto je dôležité si uvedomiť. Ideme na to. /--code js // Funkce nastaví barvu pro daný pixel this.nastavPixel = function(x, y, r, g, b, a) {
// Zjistíme pozici v poli (pozici červeného subpixelu)
var index = (x + y * this.imageData­.width) * 4;
// Hodnota červené bervy
this.imageData­.data[index+0] = r;
// Hodnota zelené barvy
this.imageData­.data[index+1] = g;
// Hodnota modré barvy
this.imageData­.data[index+2] = b;
// Alfa kanál (průhlednost)
this.imageData­.data[index+3] = a; }; \--

Vstup funkcia bude bod x, y, hodnota červenej, hodnota zelenej, hodnota modrej a alfa kanál. Index je pozícia červeného (prvého) subpixelu. Musíme si ho vypočítať, pretože máme každý pixel vyjadrený štyrmi číslami. Potom len nastavujeme jednotlivé zložky daného pixelu, ako sa sami môžete presvedčiť.

Metóda pre vykreslenie fraktálu

Funkcia bude mať tri vstupné parametre. Jednak prostrednej x komplexného čísla, prostredná y komplexného čísla a potom priblíženia. Deklarujeme tiež premenné potrebné k vykreslenie fraktálu. /--code js // Funkce vykreslí Mandelbrotovu množinu this.vykresli = function( xCenter, yCenter, zoom ) {
// Uloží zoom
$(platno).data('zo­om', zoom);
// Vytvoří nová image data
this.imageData = kontext.create­ImageData(wid­th, height);

// Potřebné proměnné
var count, zr, zi, zr2, zi2;
// Přiblížení
var zoom = zoom > 1 ? zoom*3 : zoom;
// Y, od kterého začneme
var minY = yCenter - (1.5/(zoom));
// Y, u kterého skončíme
var maxY = yCenter + (1.5/(zoom));
// X, u kterého začneme
var minX = xCenter - (1.5/(zoom));
// X, u kterého skončíme
var maxX = xCenter + (1.5/(zoom));
// Vypočteme jeden krok cyklu for X
var dx = (maxX - minX) / width;
// Vypočteme jeden krok cyklu for Y
var dy = (maxY - minY) / height;
// Budeme ukládat reálnou pozici pixelu na plátně
var rX = 0, rY = 0; } \--

Keď máme deklarované premenné, môžeme začať vykresľovať. Musíme prejsť všetky pixely a tak budeme potrebovať dva do seba vnorené cykly for (jeden pre X a jeden pre Y). V každom kroku vynulujeme naše komplexné číslo Z. Vzorec pre výpočet je potom nasledovné: Reálna časť = zr 2 - zi 2 Imaginárna časť: zi * zr + zi * zr = 2 * Zr * Zi

Aby sme sa v tom nestrácali, radšej si vytvoríme premenné druhej mocniny reálne a imaginárne časti komplexného čísla Z. Uložíme si ich do premenných ZR2, zi2. V cykle budeme toto počítať kým nebudeme mimo množinu (súčet druhej mocniny reálne a imaginárne časti bude väčší ako štyri) alebo presiahne maximálny počet pokusov (iterácií). Ak budeme mimo množinu, zafarbíme si pixel podľa počtu pokusov. Inak zafarbíme pixel čierno.

// Projdeme všechny pixely na plátně
for(var y = minY; y < maxY; y += dy) {
    // Přičteme reálnou pozici Y
    rY++;
    // Jsme v novém řádku => vynulujeme widthCheck
        rX = 0;
    // Projdeme X a zároveň se ujistíme, že jsem stále na plátně
        for (var x = minX; x < maxX && rX < width; x += dx) {
        // Přičteme reálnou pozici X
            rX++;
        // Vynulujeme komplexní číslo Z a jeho druhou množinu
                zi = 0;
                zr = 0;
                zi2 = 0;
                zr2 = 0;
        // Nastavíme počet provedených iterací na 0
                count = 0;
        // Obarvíme pixel na černo
                this.nastavPixel(rX, rY, 0, 0, 0, 255);

        // Pokud jsme v množině a nevyčerpali jsme možný počet pokusů
                while ((zr2 + zi2) < 4 && count < this.maxiter) {
            // Uložíme si druhou mocninu reálné části (alernativa: zr*zr)
            zr2 = Math.pow(zr, 2);
            // Uložíme si druhou mocninu imaginární části (alternativa: zi*zi)
                        zi2 = Math.pow(zi, 2);
            // Vzorec: Z.i = 2 * Z.r * Z.i + posunY
                        zi = 2*zr*zi + y;
            // Vzorec: Z.z^2 - Z.i^2 + posunX
                        zr = zr2 - zi2 + x;
            // Přičteme počet provedených pokusů
                        count++;
                }

        // Pokud jsme mimo množinu, obarvíme pixel na nějakou barvu podle počtu provedených pokusů
                if ((zr2 + zi2) > 4)
            //Obarvíme pixel podle počtu provedených pokusů
                        this.nastavPixel(rX, rY, count * 12, count * 4, count * 3, 255);
                }
    }
}

A je hotovo. Teraz už môžeme fraktál vykresliť a prípadne pridať možnosť priblížiť fraktál po kliknutí na plátno. To by v jQuery mohlo vyzerať napríklad /--code js var manMnozina = new MandelbrotovaM­nozina(); manMnozina.vy­kresli(0, 1.5, 0.5); $(manMnozina.plat­no).click(fun­ction(e) {
e = $.event.fix(e);
var left = e.pageX-$(this).offset()­.left,
top = e.pageY-$(this).offset()­.top,
zoom = $(this).data('zo­om')+1;
manMnozina.vy­kresli(left/$(this)­.width(), top/$(this).he­ight(), zoom); }); \--

Výsledok bude niečo podobné tomuto obrázku:

mandelbrotova množina - JavaScriptu zdrojákoviště - Základná konštrukcia jazyka

 

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 635x (35.93 kB)
Aplikácia je vrátane zdrojových kódov v jazyku JavaScript

 

Všetky články v sekcii
JavaScriptu zdrojákoviště - Základná konštrukcia jazyka
Článok pre vás napísal Drahomír Hanák
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor v současné době studuje Informatiku. Zajímá se o programování, matematiku a grafiku.
Aktivity