Obsah
1. Možnosti obarvení bodů ležících vně Mandelbrotovy množiny
2. Použití barvové palety
3. Barva odvozená od hodnoty reálné složky orbitu
4. Barva odvozená od hodnoty imaginární složky orbitu
5. Barva odvozená od součtu reálné a imaginární složky orbitu
6. Barva odvozená od úhlu orbitu
7. Obsah dalšího pokračování tohoto seriálu
1. Možnosti obarvení bodů ležících vně Mandelbrotovy množin
Podobně jako při výpočtu barev bodů ležících uvnitř Mandelbrotovy množiny (bližší informace jsou uvedeny v předchozím dílu tohoto seriálu), i při nastavování barev bodů ležících vně této množiny můžeme brát v potaz hodnotu orbitu, tj. aktuální komplexní hodnotu zn. Kromě toho je také možné využít znalosti o počtu iterací iter, po kterých došlo ke splnění podmínky divergence: |zn|>2 (pokud by tato podmínka splněna nebyla, ležel by daný bod uvnitř Mandelbrotovy množiny). Možností, jak alespoň trochu smysluplně zkombinovat komplexní hodnotu zn a počet iterací iter, je samozřejmě velmi mnoho, v dalších kapitolách si popíšeme pouze nejpoužívanější metody. Ve třetí kapitole bude pojednáno o metodě využívající reálnou složku orbitu, v kapitole čtvrté o metodě využívající imaginární složku orbitu. Metoda využívající současně reálnou i imaginární složku bude popsána v kapitole páté a konečně v šesté kapitole bude pojednáno o metodě vypočítávající úhel orbitu. Před vysvětlením všech těchto metod si však ukažme příklad implementující vyhledávací tabulky s barvovými paletami.
2. Použití barvové palety
Již v předchozích částech tohoto seriálu jsme si řekli, že jednou ze základních metod zvýraznění dynamických systémů ležících v komplexní rovině, je použití barvové palety, tj. běžné vyhledávací tabulky obsahující barvové složky RGB (Red, Green, Blue). Vhodně zvolená barvová paleta také slouží k dosažení „estetického“ vzhledu zobrazeného fraktálu. V dnešním prvním demonstračním příkladu (15.1) je ukázáno použití barvových palet, které mají délku 256 položek. Tyto barvové palety byly převzaty ze známého programu FractInt. V následující tabulce jsou jednotlivé barvové palety vyjmenovány spolu s jejich autory (u některých barvových palet jsem se však jména autorů nedozvěděl):
Název palety | Autor |
---|---|
Blues | Daniel Egnar |
Greens | Tom Schumm |
Ice | Tom Schumm |
Gold | autor neznámý |
Phong | autor neznámý |
Rose | autor neznámý |
Mandman | Jussi Kantola |
Juteblue | Jussi Kantola |
Jutemap | Jussi Kantola |
Jutes | Jussi Kantola |
Tyto barvové palety jsou přímo uloženy do zdrojového kódu prvního demonstračního příkladu ve formě statických polí, ze kterých je možné po zadání indexu (který běžně odpovídá počtu iterací) získat hodnoty jednotlivých barvových položek RGB. Ve zdrojovém kódu stojí za povšimnutí způsob deklarace polí s barvovými paletami. Pokud by v této deklaraci chyběl zdánlivě nepodstatný modifikátor static, celý program by se několikanásobně zpomalil, protože by pro každý pixel docházelo ke kopiím všech polí z kódového segmentu na zásobník. Ovládání prvního demonstračního příkladu je sepsáno v následující tabulce:
Klávesa | Význam |
, | snížení počtu iterací o jednotku |
. | zvýšení počtu iterací o jednotku |
< | snížení počtu iterací o deset jednotek |
> | zvýšení počtu iterací o deset jednotek |
S | uložení rastrového obrázku s fraktálem do externího souboru typu TGA |
Q | ukončení tohoto demonstračního příkladu |
Esc | má stejný význam jako klávesa Q |
šipky | posun obrazce ve směru šipek (o cca pět procent) |
Page Up | změna měřítka (zvětšení výřezu o deset procent) |
Page Down | změna měřítka (zmenšení výřezu o deset procent) |
0 | nastavení barvové palety „Blues“ (implicitní nastavení) |
1 | nastavení barvové palety „Gold“ |
2 | nastavení barvové palety „Greens“ |
3 | nastavení barvové palety „Ice“ |
4 | nastavení barvové palety „Juteblue“ |
5 | nastavení barvové palety „Jutemap“ |
6 | nastavení barvové palety „Jutes“ |
7 | nastavení barvové palety „Mandmap“ |
8 | nastavení barvové palety „Phong“ |
9 | nastavení barvové palety „Rose“ |
Zdrojový kód prvního demonstračního příkladu je dostupný ke stažení, k dispozici je i HTML verze se zvýrazněním syntaxe. Procedura, která provádí výpočet a zobrazení Mandelbrotovy množiny s pomocí předdefinovaných barvových palet, může být zapsána takto:
//-----------------------------------------------------------------------------
// Překreslení Mandelbrotovy množiny s využitím vybrané barvové palety
//-----------------------------------------------------------------------------
void recalcMandelbrot1( pixmap *pix, // pixmapa pro vykreslování
int maxiter, // maximální počet iterací
double scale, // měřítko obrazce
double xpos, // posun obrazce
double ypos,
int palette, // vybraná barvová paleta
int rf, int gf, int bf) // příznaky barvových složek
{
double zx, zy, zx2, zy2; // složky komplexní proměnné Z a Z^2
double cx0, cy0;
double cx, cy; // složky komplexní konstanty C
int x, y; // počitadlo sloupců a řádků v pixmapě
int iter; // počitadlo iterací
double xmin, ymin, xmax, ymax; // rohy vykreslovaného obrazce v komplexní
// rovině
unsigned char r, g, b; // barvové složky pixelu
calcCorner(xpos, ypos, scale, &xmin, &ymin, &xmax, &ymax);
cy0=ymin;
for (y=0; y<pix->height; y++) { // pro všechny řádky v pixmapě
cx0=xmin;
for (x=0; x<pix->width; x++) { // pro všechny pixely na řádku
cx=cx0; // nastavit počáteční hodnotu Z(0)
cy=cy0;
zx=zy=0.0; // bude se počítat orbit nuly
for (iter=0; iter<maxiter; iter++) {// iterační smyčka
zx2=zx*zx; // zkrácený výpočet druhé mocniny složek Z
zy2=zy*zy;
if (zx2+zy2>4.0) break; // kontrola překročení meze divergence
zy=2.0*zx*zy+cy; // výpočet Z(n+1)
zx=zx2-zy2+cx;
}
if (iter==maxiter) // pixely uvnitř Mandelbrotovy
r=g=b=0; // množiny jsou černé
else // výpočet barev podle počtu iterací
mapPalette(palette, iter, &r, &g, &b);
r=r*rf; // uživatelem řízené vynulování
g=g*gf; // vybraných barvových složek
b=b*bf;
putpixel(pix, x, y, r, g, b); // vykreslení pixelu
cx0+=(MAXX-MINX)/pix->width; // posun na další bod na řádku
}
cy0+=(MAXY-MINY)/pix->height; // posun na další řádek
}
}
Obrázek 1: Obrázek výřezu Mandelbrotovy množiny vykreslená s využitím barvové palety „Blues“
Obrázek 2: Obrázek výřezu Mandelbrotovy množiny vykreslená s využitím barvové palety „Gold“
Obrázek 3: Obrázek výřezu Mandelbrotovy množiny vykreslená s využitím barvové palety „Mandmap“
Obrázek 4: Obrázek výřezu Mandelbrotovy množiny vykreslená s využitím barvové palety „Jutemap“
3. Barva odvozená od hodnoty reálné složky orbitu
Jednou z dostupných a snadno implementovatelných možností, jak využít znalosti o koncové hodnotě orbitu, tj. o hodnotě zn, je zvýraznění pouze reálné složky zn. Na první pohled velmi jednoduchá metoda vede k tomu, že se – především při zobrazení detailů Mandelbrotovy množiny – na obrázcích začínají objevovat pruhy, které při správně nastavené barvové paletě obrázek činí zajímavějším. Tvorba pruhů souvisí s velkou citlivostí iteračního výpočtu na počátečních podmínkách. Při výpočtu barvy bodů je možné brát do úvahy buď pouze reálnou složku komplexní hodnoty zn, nebo se k této hodnotě přičte počet dosažených iterací. Procedura, která provádí výpočet barev právě popsanou metodou (s voláním funkce mapPalette() pro aplikaci barvové palety), může vypadat následovně:
//-----------------------------------------------------------------------------
// Překreslení Mandelbrotovy množiny s využitím vybrané barvové palety.
// Při výpočtu barev bodů ležících vně Mandelbrotovy množiny se využívá
// i informace o reálné složce orbitu.
//-----------------------------------------------------------------------------
void recalcMandelbrot2( pixmap *pix, // pixmapa pro vykreslování
int maxiter, // maximální počet iterací
double scale, // měřítko obrazce
double xpos, // posun obrazce
double ypos,
int palette, // vybraná barvová paleta
int rf, int gf, int bf) // příznaky barvových složek
{
double zx, zy, zx2, zy2; // složky komplexní proměnné Z a Z^2
double cx0, cy0;
double cx, cy; // složky komplexní konstanty C
int x, y; // počitadlo sloupců a řádků v pixmapě
int iter; // počitadlo iterací
double xmin, ymin, xmax, ymax; // rohy vykreslovaného obrazce v komplexní
// rovině
unsigned char r, g, b; // barvové složky pixelu
calcCorner(xpos, ypos, scale, &xmin, &ymin, &xmax, &ymax);
cy0=ymin;
for (y=0; y<pix->height; y++) { // pro všechny řádky v pixmapě
cx0=xmin;
for (x=0; x<pix->width; x++) { // pro všechny pixely na řádku
cx=cx0; // nastavit počáteční hodnotu Z(0)
cy=cy0;
zx=zy=0.0; // bude se počítat orbit nuly
for (iter=0; iter<maxiter; iter++) {// iterační smyčka
zx2=zx*zx; // zkrácený výpočet druhé mocniny složek Z
zy2=zy*zy;
if (zx2+zy2>4.0) break; // kontrola překročení meze divergence
zy=2.0*zx*zy+cy; // výpočet Z(n+1)
zx=zx2-zy2+cx;
}
if (iter==maxiter) // pixely uvnitř Mandelbrotovy
r=g=b=0; // množiny jsou černé
else // výpočet barev podle počtu iterací
mapPalette(palette, iter+10.0*zx, &r, &g, &b);
r=r*rf; // uživatelem řízené vynulování
g=g*gf; // vybraných barvových složek
b=b*bf;
putpixel(pix, x, y, r, g, b); // vykreslení pixelu
cx0+=(MAXX-MINX)/pix->width; // posun na další bod na řádku
}
cy0+=(MAXY-MINY)/pix->height; // posun na další řádek
}
}
Výše uvedená procedura recalcMandelbrot2() je použita i v dnešním druhém demonstračním příkladu (zdrojový kód a HTML verze se zvýrazněním syntaxe). Po překladu a spuštění tohoto demonstračního příkladu se zobrazí Mandelbrotova množina, ve které jsou body ležící mimo množinu zobrazeny pomocí kombinace reálné složky orbitu a počtu dosažených iterací. Body uvnitř množiny jsou zobrazeny barvou černou. Podobně jako v prvním demonstračním příkladu, i zde je možné pomocí kláves [0]-[9] zvolit jednu z deseti přednastavených barvových palet. Ovládání tohoto demonstračního příkladu je stejné jako u příkladu prvního.
Obrázek 5: Screenshot druhého demonstračního příkladu
4. Barva odvozená od hodnoty imaginární složky orbitu
Odvození barvy pixelu od hodnoty imaginární složky orbitu je prakticky stejné, jako v předešlém případě, pouze se bere do úvahy imaginární složka zn a nikoli složka reálná. Výsledkem však je odlišný obraz fraktálu, což je ukázáno i na šestém obrázku, který se od pátého obrázku odlišuje v posunech barvy – pro ilustraci rozdílů je ideální „přepínání“ mezi těmito obrázky v prohlížeči rastrových obrázků. Procedura, která provádí výpočet barev s využitím imaginární složky orbitu, vypadá následovně:
//-----------------------------------------------------------------------------
// Překreslení Mandelbrotovy množiny s využitím vybrané barvové palety.
// Při výpočtu barev bodů ležících vně Mandelbrotovy množiny se využívá
// i informace o imaginární složce orbitu.
//-----------------------------------------------------------------------------
void recalcMandelbrot3( pixmap *pix, // pixmapa pro vykreslování
int maxiter, // maximální počet iterací
double scale, // měřítko obrazce
double xpos, // posun obrazce
double ypos,
int palette, // vybraná barvová paleta
int rf, int gf, int bf) // příznaky barvových složek
{
double zx, zy, zx2, zy2; // složky komplexní proměnné Z a Z^2
double cx0, cy0;
double cx, cy; // složky komplexní konstanty C
int x, y; // počitadlo sloupců a řádků v pixmapě
int iter; // počitadlo iterací
double xmin, ymin, xmax, ymax; // rohy vykreslovaného obrazce v komplexní
// rovině
unsigned char r, g, b; // barvové složky pixelu
calcCorner(xpos, ypos, scale, &xmin, &ymin, &xmax, &ymax);
cy0=ymin;
for (y=0; y<pix->height; y++) { // pro všechny řádky v pixmapě
cx0=xmin;
for (x=0; x<pix->width; x++) { // pro všechny pixely na řádku
cx=cx0; // nastavit počáteční hodnotu Z(0)
cy=cy0;
zx=zy=0.0; // bude se počítat orbit nuly
for (iter=0; iter<maxiter; iter++) {// iterační smyčka
zx2=zx*zx; // zkrácený výpočet druhé mocniny složek Z
zy2=zy*zy;
if (zx2+zy2>4.0) break; // kontrola překročení meze divergence
zy=2.0*zx*zy+cy; // výpočet Z(n+1)
zx=zx2-zy2+cx;
}
if (iter==maxiter) // pixely uvnitř Mandelbrotovy
r=g=b=0; // množiny jsou černé
else // výpočet barev podle počtu iterací
mapPalette(palette, iter+10.0*zy, &r, &g, &b);
r=r*rf; // uživatelem řízené vynulování
g=g*gf; // vybraných barvových složek
b=b*bf;
putpixel(pix, x, y, r, g, b); // vykreslení pixelu
cx0+=(MAXX-MINX)/pix->width; // posun na další bod na řádku
}
cy0+=(MAXY-MINY)/pix->height; // posun na další řádek
}
}
Procedura recalcMandelbrot3() je použita ve třetím demonstračním příkladu (zdrojový kód a HTML verze se zvýrazněním syntaxe), jehož ovládání je shodné s příkladem prvním i druhým.
Obrázek 6: Screenshot třetího demonstračního příkladu
5. Barva odvozená od součtu reálné a imaginární složky orbitu
Obě předchozí metody určené pro výpočet barvy bodů ležících vně Mandelbrotovy množiny je možné sloučit do metody jediné, ve které se počítá jak s reálnou, tak i imaginární složkou orbitu, tj. s hodnotou real(zn) a imag(zn). Při vhodně zvolené barvové paletě je možné dosáhnout plastického vzhledu okrajů Mandelbrotovy množiny. Dále uvedená procedura, která počítá s reálnou i imaginární složkou orbitu, je použita ve třetím demonstračním příkladu (zdrojový kód a příslušná HTML verze). Na sedmém obrázku, jenž je pomocí tohoto příkladu vytvořen, si všimněte zejména obloukovitých oblastí v izoplochách vykreslených původně barvou s jedním odstínem.
//-----------------------------------------------------------------------------
// Překreslení Mandelbrotovy množiny s využitím vybrané barvové palety.
// Při výpočtu barev bodů ležících vně Mandelbrotovy množiny se využívá
// informace o reálné a současně i imaginární složce orbitu.
//-----------------------------------------------------------------------------
void recalcMandelbrot4( pixmap *pix, // pixmapa pro vykreslování
int maxiter, // maximální počet iterací
double scale, // měřítko obrazce
double xpos, // posun obrazce
double ypos,
int palette, // vybraná barvová paleta
int rf, int gf, int bf) // příznaky barvových složek
{
double zx, zy, zx2, zy2; // složky komplexní proměnné Z a Z^2
double cx0, cy0;
double cx, cy; // složky komplexní konstanty C
int x, y; // počitadlo sloupců a řádků v pixmapě
int iter; // počitadlo iterací
double xmin, ymin, xmax, ymax; // rohy vykreslovaného obrazce v komplexní
// rovině
unsigned char r, g, b; // barvové složky pixelu
calcCorner(xpos, ypos, scale, &xmin, &ymin, &xmax, &ymax);
cy0=ymin;
for (y=0; y<pix->height; y++) { // pro všechny řádky v pixmapě
cx0=xmin;
for (x=0; x<pix->width; x++) { // pro všechny pixely na řádku
cx=cx0; // nastavit počáteční hodnotu Z(0)
cy=cy0;
zx=zy=0.0; // bude se počítat orbit nuly
for (iter=0; iter<maxiter; iter++) {// iterační smyčka
zx2=zx*zx; // zkrácený výpočet druhé mocniny složek Z
zy2=zy*zy;
if (zx2+zy2>4.0) break; // kontrola překročení meze divergence
zy=2.0*zx*zy+cy; // výpočet Z(n+1)
zx=zx2-zy2+cx;
}
if (iter==maxiter) // pixely uvnitř Mandelbrotovy
r=g=b=0; // množiny jsou černé
else // výpočet barev podle počtu iterací
mapPalette(palette, iter+10.0*(zx+zy), &r, &g, &b);
r=r*rf; // uživatelem řízené vynulování
g=g*gf; // vybraných barvových složek
b=b*bf;
putpixel(pix, x, y, r, g, b); // vykreslení pixelu
cx0+=(MAXX-MINX)/pix->width; // posun na další bod na řádku
}
cy0+=(MAXY-MINY)/pix->height; // posun na další řádek
}
}
Obrázek 7: Screenshot čtvrtého demonstračního příkladu
6. Barva odvozená od úhlu orbitu
Již v předchozím pokračování tohoto seriálu jsme si ukázali, že při zvýrazňování struktury vnitřních částí Mandelbrotovy množiny je možné barvu vnitřních pixelů spočítat na základě úhlu koncového orbitu. Stejnou metodu je možné použít i při vykreslování pixelů, které leží vně této množiny. Úhel mezi koncovou hodnotou orbitu a reálnou či imaginární osou Gaussovy roviny je možné vypočítat například pomocí céčkovské funkce atan2(), která (na rozdíl od funkce atan()) zajistí i limitní případ, ve kterém by se provádělo dělení nulou. Dále uvedená procedura recalcMandelbrot5() je použita v pátém demonstračním příkladu. Zde se nachází zdrojový kód, příslušná HTML stránka je uložena zde.
//-----------------------------------------------------------------------------
// Překreslení Mandelbrotovy množiny s využitím vybrané barvové palety.
// Při výpočtu barev bodů ležících vně Mandelbrotovy množiny se využívá
// i o úhlu koncové hodnoty orbitu (počítá se úhel vůči imaginární ose).
//-----------------------------------------------------------------------------
void recalcMandelbrot5( pixmap *pix, // pixmapa pro vykreslování
int maxiter, // maximální počet iterací
double scale, // měřítko obrazce
double xpos, // posun obrazce
double ypos,
int palette, // vybraná barvová paleta
int rf, int gf, int bf) // příznaky barvových složek
{
double zx, zy, zx2, zy2; // složky komplexní proměnné Z a Z^2
double cx0, cy0;
double cx, cy; // složky komplexní konstanty C
int x, y; // počitadlo sloupců a řádků v pixmapě
int iter; // počitadlo iterací
double xmin, ymin, xmax, ymax; // rohy vykreslovaného obrazce v komplexní
// rovině
unsigned char r, g, b; // barvové složky pixelu
calcCorner(xpos, ypos, scale, &xmin, &ymin, &xmax, &ymax);
cy0=ymin;
for (y=0; y<pix->height; y++) { // pro všechny řádky v pixmapě
cx0=xmin;
for (x=0; x<pix->width; x++) { // pro všechny pixely na řádku
cx=cx0; // nastavit počáteční hodnotu Z(0)
cy=cy0;
zx=zy=0.0; // bude se počítat orbit nuly
for (iter=0; iter<maxiter; iter++) {// iterační smyčka
zx2=zx*zx; // zkrácený výpočet druhé mocniny složek Z
zy2=zy*zy;
if (zx2+zy2>4.0) break; // kontrola překročení meze divergence
zy=2.0*zx*zy+cy; // výpočet Z(n+1)
zx=zx2-zy2+cx;
}
if (iter==maxiter) // pixely uvnitř Mandelbrotovy
r=g=b=0; // množiny jsou černé
else // výpočet barev podle počtu iterací
mapPalette(palette, iter+5.0*atan2(zx, zy), &r, &g, &b);
r=r*rf; // uživatelem řízené vynulování
g=g*gf; // vybraných barvových složek
b=b*bf;
putpixel(pix, x, y, r, g, b); // vykreslení pixelu
cx0+=(MAXX-MINX)/pix->width; // posun na další bod na řádku
}
cy0+=(MAXY-MINY)/pix->height; // posun na další řádek
}
}
Obrázek 8: Screenshot pátého demonstračního příkladu
7. Obsah dalšího pokračování tohoto seriálu
V dalším pokračování tohoto seriálu se budeme zabývat dvěma dalšími problémy: způsobem animace „průletu“ Mandelbrotovou množinou a takzvanou mapou Mandelbrotovy množiny, tj. kódováním nejtypičtějších tvarů, které je možné v této nekonečně členité množině nalézt.