Zbiór Mandelbrota w JavaScript



Fraktale to skomplikowane obrazy, których fragmenty są podobne do całości. Innymi słowy, kiedy powiększysz część fraktala, możesz uzyskać obraz podobny do tego, od którego zacząłeś. Powiększaj dalej, a znów zobaczysz podobny widok. I tak dalej - do nieskończoności!

Ten poradnik pozwoli ci szybko (tylko około 25 linii kodu!) stworzyć słynny fraktal nazwany zbiorem Mandelbrota.

Oto podstawowe informacje matematyczne na ten temat. Jeżeli jesteś bardziej zainteresowany programowaniem niż matematyką, możesz przeskoczyć na koniec szarego fragmentu.



Punkt na płaszczyźnie zespolonej należy do zbioru Mandelbrota, jeżeli orbita zero ciągu opisanego równaniem rekurencyjnym:
zn=z2n+1
nie dąży do nieskończoności.

Można nakreślić go na kwadracie pomiędzy -2-2i a 2+2i.

Z przyczyn praktycznych (by ograniczyć czas obliczeń) każdy punkt poddamy tylko 255 iteracjom. Dany punkt będziemy poddawać iteracjom, tylko jeżeli wartość bezwzględna zn będzie niższa niż 4. Jeżeli ta wartość wyniesie 4 lub więcej, to wiemy już, że w końcu ucieknie do nieskończoności, więc nie musimy kontynuować iteracji. Liczba wymaganych iteracji określi kolor danego puntku.



W skrócie wygląda to tak:

0. Wybierz punkt w lewym dolnym rogu obszaru (-2,-2)

1. Rozpocznij od zx=0 i zy=0

2. Oblicz:

        xt=zx*zy
        zx=zx*zx-zy*zy+cx
        zy=2*xt+cy

        Te obliczenia to tak naprawdę wzór zn=z2n+1. Wyglądają jednak inaczej, ponieważ i2=-1, co jest własnością liczb zespolonych.

3. Powtórz krok 2 jeżeli:

a. jeszcze nie osiągnąłeś 255 iteracji[i<255]
ORAZ
b. wartość bezwględna wyniku jest niższa niż 4 [zx*zx+zy*zy)<4]

4. zmień kolor punktu, który obliczasz (tzn. cx,cy) na kolor odpowiadający liczbie iteracji (liczbie powtórzeń, które wykonałeś dla kroków 2 and 3)

        Ustalamy wartości kolorów składowych czerwonego, zielonego i niebieskiego (RGB) kwadratu na ilość powtórzeń, co tworzy obraz w skali szarości.

5. Wybierz kolejny punkt (rzędami i kolumnami)

6. Przejdź do kroku 1, aż dotrzesz do górnego prawego rogu

Oto faktyczny kod programu:


<html>
<body>
        <canvas id="myCanvas" width="800" height="800"></canvas>
        <script>
        var x,y,i,xt;
        var cx,cy;
        var color;
        var canvas = document.getElementById('myCanvas');
        var context = canvas.getContext('2d');

        for(x=0;x<200;x++)
        {
                for(y=0;y<200;y++)
                {
                        i=0;
                        cx=-2+x/50;
                        cy=-2+y/50;
                        zx=0;
                        zy=0;                        

                        do
                        {
                                xt=zx*zy;
                                zx=zx*zx-zy*zy+cx;
                                zy=2*xt+cy;
                                i++;
                        }
                        while(i<255&&(zx*zx+zy*zy)<4);

                        color=i.toString(16);
                        context.beginPath();
                        context.rect(x*4, y*4, 4, 4);
                        context.fillStyle ='#'+color+color+color;
                        context.fill();
                }
        }
        </script>
</body>
</html>

Dla początkujących: możesz skopiować powyższy kod, wkleić go do pliku tekstowego (w Notatniku), nagrać jako 'mandel.htm' (plik HTML) i otworzyć go w przeglądarce. Twój ekran powinien wyglądać tak:



Ten plik HTML jest też dostępny TUTAJ. Jeżeli klikniesz na ten link, otworzy się nowe okno z obrazem zbioru, wygenerowanym przez program.


To działa i według mnie wygląda świetnie - stosunkowo prosty wzór matematyczny tworzy coś, co dla ludzkiego oka jest nieskończenie skomplikowane. Ale to dopiero początek! Prawdziwe piękno Mandelbrota ujawnia się dopiero po dodaniu kolorów i zagłębieniu się do wnętrza, by odkrywać jego nieskończące się jeziora i jaskinie.

Skoncentrowałem się na prostocie i zwięzłości programu, kosztem efektowności rezultatu. Kliknij tutaj, by przeczytać nieco trudniejszy poradnik o powiększaniu wnętrza fraktala.

Jeżeli nie masz ochoty na kolejny poradnik, możesz po prostu eksperymentować z różnymi wartościami w tych dwóch liniach:

                        cx=-2+x/50;
                        cy=-2+y/50;

Na przykład takie wartości:

                        cx=-.68+x/1200;
                        cy=-.74+y/1200;

dadzą nam taki obraz:



Jeżeli powyższe informacje nie wystarczyły, poniżej podaję kod programu z dokładniejszymi wyjaśnieniami:


<html>
<body>
        <canvas id="myCanvas" width="800" height="800"></canvas>


        <script>
        var x,y,i,xt;
        var cx,cy;
        var color;
Deklaracja zmiennych JS:

i - liczba powtórzeń
x,y - współrzędne ekranowe punktu, który obecnie poddajemy iteracjom. (0,0) jest w lewym górnym rogu. (200,200) w prawym dolnym.
cx,cy - współrzędne punktu, który obecnie poddajemy iteracjom, na płaszczyźnie zespolonej - odpowiadające x i y.(-2,-2) jest w lewym dolnym rogu; (2,2) w prawym górnym.

        var canvas = document.getElementById('myCanvas');
        var context = canvas.getContext('2d');

        for(x=0;x<200;x++)
        {
                for(y=0;y<200;y++)
                {
Dwie główne pętle - dzielimy nasz obszar na 200 rzędów i 200 kolumn, a następnie obrabiamy każdy puntk.

                        i=0;
                        cx=-2+x/50;
                        cy=-2+y/50;
Konwersja współrzędnych ekranowych na liczbę złożoną.

                        zx=0;
                        zy=0;
z0=0



                        do
                        {
                                xt=zx*zy;
                                zx=zx*zx-zy*zy+cx;
                                zy=2*xt+cy;
                                i++;
                        }
Powtarzaj wzór...

                        while(i<255&&(zx*zx+zy*zy)<4);

...aż osiągniesz 255 iteracji lub wartość bezwzględna osiągnie wartość wyższą niż 4.

                        color=i.toString(16);
Konwersja liczby powtórzeń na liczbę szestnastkową.

                        context.beginPath();
                        context.rect(x*4, y*4, 4, 4);
Rysujemy kwadrat 4x4 na obecnych współrzędnych ekranowych.

                        context.fillStyle ='#'+color+color+color;
Używamy liczby powtórzeń w formacie szestnastkowym jako składowe (czerwona, zielona, niebieska) koloru kwadratu.

                        context.fill();
        }
        </script>
</body>
</html>

Poradnik - interaktywne, animowane "duszki" (sprites) w JavaScript