Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (2)

19. 5. 2015
Doba čtení: 20 minut

Sdílet

V dnešní části seriálu o programovacím jazyku Clojure i o knihovnách, které mohou programátoři pracující v Clojure využít, budeme pokračovat v popisu knihovny Clisk. Zabývat se budeme pokročilejšími funkcemi nabízenými touto knihovnou: zejména funkcí pro vykreslování obecných fraktálů a Perlinovým šumem.

Obsah

1. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (2)

2. Perlinova šumová funkce

3. Funkce vnoise, vsnoise a Perlinův šum

4. Úplný zdrojový kód demonstračního příkladu clisktest4

5. Podpora pro vykreslování fraktálů

6. Mandelbrotova množina

7. Kubická Mandelbrotova množina

8. Úplný zdrojový kód demonstračního příkladu clisktest5 

9. Repositář s demonstračními příklady

10. Odkazy na předchozí části seriálu

11. Odkazy na Internetu

1. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (dokončení)

V dnešní části seriálu, v němž se zabýváme různými obvyklými i poněkud neobvyklými aspekty programovacího jazyka Clojure a taktéž knihovnami, které mohou programátoři pracující v jazyku Clojure použít, dokončíme popis knihovny Clisk. Tato knihovna mj. podporuje tvorbu textur s využitím takzvané Perlinovy šumové funkce, která má v počítačové grafice široké možnosti využití, a to jak pro tvorbu běžných dvourozměrných textur, tak i textur trojrozměrných (volumetrických) či čtyřrozměrných (animované mraky atd.). Ve druhé kapitole si popíšeme princip této funkce, její konkrétní využití je naznačeno v kapitole třetí. V navazujících kapitolách bude zmíněna možnost využití fraktálů pro tvorbu textur, následně (ale až v příští části) si popíšeme již existující (předdefinované) textury a nakonec si ukážeme jeden ze způsobů využití Voroného diagramů při vytváření uživatelsky definovaných textur.

Obrázek 1: Perlinova šumová funkce použitá při generování dvourozměrných textur. Pro vytvoření tohoto obrázku byla použita barvová paleta mapující výsledné hodnoty z rozsahu 0 až 1 na vybraných 256 barev.

2. Perlinova šumová funkce

Perlinova šumová funkce (Perlin noise (function)) byla s velkým úspěchem použita v mnoha aplikacích počítačové grafiky; například prakticky každý film, ve kterém je využita renderovaná grafika (CG), tuto funkci nějakým způsobem použil. Její úspěch spočívá v tom, že se pomocí ní dají do původně přesných a „počítačově chladných“ modelů vnést náhodné prvky, takže se model či celá vytvářená scéna více přiblíží realitě. Podobný princip vnesení náhodnosti ostatně umožňují i fraktály, zejména ty vytvářené na stochastickém základě (plasma, stochastické L-systémy apod.). Ken Perlin svoji šumovou funkci navrhl už v roce 1983, v roce 1985 o její aplikaci vznikl článek prezentovaný na SIGGRAPHu (jedna z nejvýznamnějších konferencí počítačové grafiky) a v letech 1986 až 1988 tuto funkci s některými modifikacemi používaly takové firmy, jako Pixar, Alias, SoftImage apod. Dnes ji mj. najdeme například i v raytraceru POVRay.

Obrázek 2: Aplikace Perlinovy šumové funkce při tvorbě trojrozměrných (volumetrických) textur. Povšimněte si, že nikde nedochází k tvorbě „polů“, které by byly při použití běžné dvourozměrné textury jasně viditelné.

Při tvorbě textur, které by měly reprezentovat přírodní vzorky jako je mramor, dřevo či mraky, není možné použít ani klasický generátor pseudonáhodných čísel RNG (tím je myšlena například funkce rand() ze standardní céčkové knihovny), ale ani základní matematické funkce. V minulosti byly prováděny pokusy o využití goniometrických funkcí, které posléze vyústily v úspěšnou metodu generování plasmy pomocí Fourierovy syntézy. Při přímé aplikaci generátorů pseudonáhodných čísel sice získáme šum, ten je však příliš náhodný a vůbec se nehodí pro generování textur, a to ani po své filtraci. Perlin pro účely vytváření přírodních textur navrhl výpočet, který sice využívá generátor pseudonáhodných čísel, ale mezi jednotlivými vypočtenými náhodnými hodnotami je prováděna interpolace, která výsledný průběh funkce vyhladí, takže se již nebude jednat o zcela náhodný šum. Pro vyhlazení je možné použít velké množství matematických funkcí, od jednoduché lineární interpolace přes kvadratické a kubické funkce až po funkce goniometrické a jejich vzájemné kombinace.

Obrázek 3: Jednorozměrná Perlinova šumová funkce s parametry alpha=2, beta=2, n=10. Změnou těchto parametrů je možné řídit vyhlazování průběhu, četnost změn apod.

Dvourozměrný Perlinův šum je vypočten prakticky stejným způsobem jako šum jednorozměrný. Jediný podstatný rozdíl spočívá v tom, že se interpolace provádí ve dvou směrech a výsledek této interpolace je sečten. V praxi se opět používá součet několika šumových funkcí s klesající amplitudou a rostoucí „frekvencí“. Dvourozměrné textury však mají pro použití v praxi několik vážných nevýhod, z nichž největší nevýhoda spočívá v obtížném mapování na nerovné povrchy. Například mapování obdélníkové dvourozměrné textury na kouli vede k tomu, že se textura na pólech smrští a naopak na rovníku příliš roztáhne. Výsledek je většinou neuspokojivý, zvláště při aplikaci šumové funkce (ta by měla být směrově invariantní). Přechodem k výpočtům 3D (volumetrických) textur se tohoto problému zbavíme (viz obrázek číslo 2, protože pro každý bod v prostoru je možné zjistit hodnotu šumu bez nutnosti mapování.

Obrázek 4: Aplikace dvourozměrné Perlinovy šumové funkce s parametry alpha=2, beta=2, n=10. Opět je použita barevná paleta.

3. Funkce vnoise, vsnoise a Perlinův šum

V knihovně Clisk je Perlinova šumová funkce implementována ve funkcích nazvaných vnoise a vsnoise. Počáteční písmeno těchto funkcí naznačuje, že výsledkem výpočtu je vektor, a to konkrétně čtyřrozměrný vektor. V případě funkce vnoise mohou jednotlivé složky vektoru nabývat hodnot z rozsahu 0 až 1, u funkce vsnoise je pak rozsah rozšířen na –1 až 1 (což zjednodušuje některé výpočty). To znamená, že funkce vnoise a vsnoise je možné použít jak pro generování dvourozměrných textur (což si ukážeme v demonstračním příkladu), tak i pro tvorbu trojrozměrných (volumetrických) textur. V případě dvourozměrných textur je návratová hodnota použita pro určení barvových složek red, green a blue, pro vytvoření monochromatické textury lze použít funkci monochrome. Podívejme se na jednoduchý příklad použití Perlinovy funkce v praxi:

(defn noise-patterns-test
    "Otestování funkce vnoise a vsnoise."
    [noise-function filename-prefix]
    (let [patterns [noise-function
                    (monochrome noise-function)
                    (scale 1/10 noise-function)
                    (monochrome (scale 1/10 noise-function))
                    (v- (v* 5 noise-function) 1)
                    (scale 1/4 (offset noise-function (checker yellow blue)))
                    (scale 1/4 (offset (v* 1/5 noise-function) (checker yellow blue)))
                    (scale 1/4 (offset (v* 1/2 noise-function) (checker yellow blue)))
                    (scale 1/4 (offset (v* 2 noise-function)   (checker yellow blue)))
                    (scale 1/4 (offset (v* 5 noise-function)   (checker yellow blue)))
                    (scale 1/4 (rotate (v* 5 noise-function)   (checker yellow blue)))
                    (scale 1/4 (rotate (v* 5 noise-function)   (offset (v* 5 noise-function) (checker yellow blue))))
                    ]]
        ; postupně projít všemi prvky vektoru "patterns", vytvořit
        ; dvouprvkový vektor [index+patter], vytvořit jméno výstupního
        ; souboru a následně zavolat funkci write-pattern
        (doseq [ [i pattern] (map-indexed vector patterns)]
            (write-pattern pattern (construct-filename filename-prefix i)))))
 
(println "Vnoise test...")
(noise-patterns-test vnoise "vnoise")
(println "Done")
(println "Vsnoise test...")
(noise-patterns-test vsnoise "vsnoise")

Obrázek 5: Textura vytvořená s využitím výrazu vnoise-function. Pro každý pixel textury je vypočten vektor převedený na barvu (složky vektoru odpovídají barvovým složkám red, green a blue).

Obrázek 6: Textura vytvořená s využitím výrazu (monochrome vnoise-function). Funkce monochrome slouží k převodu z prostoru RGB na stupně šedi (monochromatický obrázek).

Obrázek 7: Textura vytvořená s využitím výrazu (scale 1/10 vnoise-function). Funkce scale zde provádí změnu měřítka (samozřejmě bez ztráty kvality).

Obrázek 8: Textura vytvořená s využitím výrazu (monochrome (scale 1/10 vnoise-function)).

Obrázek 9: Textura vytvořená s využitím výrazu (v- (v* 5 vnoise-function) 1).

Obrázek 10: Textura vytvořená s využitím výrazu (scale 1/4 (offset vnoise-function (checker yellow blue))). Perlinův šum je zde použit nepřímo pro změnu souřadnic bodů, které tvoří vstup do funkce checker).

Obrázek 11: Textura vytvořená s využitím výrazu (scale 1/4 (offset (v* 1/5 vnoise-function) (checker yellow blue))). Zmenšení amplitudy posunu bodů.

Obrázek 12: Textura vytvořená s využitím výrazu (scale 1/4 (offset (v* 1/2 vnoise-function) (checker yellow blue))).

Obrázek 13: Textura vytvořená s využitím výrazu (scale 1/4 (offset (v* 2 vnoise-function) (checker yellow blue))). Zvětšení amplitudy posunu bodů.

Obrázek 14: Textura vytvořená s využitím výrazu (scale 1/4 (offset (v* 5 vnoise-function) (checker yellow blue))). Zvětšení amplitudy posunu bodů.

Obrázek 15: Textura vytvořená s využitím výrazu (scale 1/4 (rotate (v* 5 vnoise-function) (checker yellow blue))). 3D rotace v RGB prostoru.

Obrázek 16: Textura vytvořená s využitím výrazu (scale 1/4 (rotate (v* 5 vnoise-function) (offset (v* 5 vnoise-function) (checker yellow blue))))

Obrázek 17: Textura vytvořená s využitím výrazu vsnoise-function.

Obrázek 18: Textura vytvořená s využitím výrazu (monochrome vsnoise-function).

Obrázek 19: Textura vytvořená s využitím výrazu (scale 1/10 vsnoise-function).

Obrázek 20: Textura vytvořená s využitím výrazu (monochrome (scale 1/10 vsnoise-function)).

Obrázek 21: Textura vytvořená s využitím výrazu (v- (v* 5 vsnoise-function) 1).

Obrázek 22: Textura vytvořená s využitím výrazu (scale 1/4 (offset vsnoise-function (checker yellow blue))).

Obrázek 23: Textura vytvořená s využitím výrazu (scale 1/4 (offset (v* 1/5 vsnoise-function) (checker yellow blue))).

Obrázek 24: Textura vytvořená s využitím výrazu (scale 1/4 (offset (v* 1/2 vsnoise-function) (checker yellow blue))).

Obrázek 25: Textura vytvořená s využitím výrazu (scale 1/4 (offset (v* 2 vsnoise-function) (checker yellow blue))).

Obrázek 26: Textura vytvořená s využitím výrazu (scale 1/4 (offset (v* 5 vsnoise-function) (checker yellow blue))).

Obrázek 27: Textura vytvořená s využitím výrazu (scale 1/4 (rotate (v* 5 vsnoise-function) (checker yellow blue))).

Obrázek 28: Textura vytvořená s využitím výrazu (scale 1/4 (rotate (v* 5 vsnoise-function) (offset (v* 5 vsnoise-function) (checker yellow blue)))).

4. Úplný zdrojový kód demonstračního příkladu clisktest4

V této kapitole bude uveden výpis úplného zdrojového kódu dnešního prvního demonstračního příkladu nazvaného clisktest4, z něhož jsme používali úryvky a ukázky v předchozí kapitole:

Obsah souboru project.clj:

(defproject clisktest2 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [net.mikera/clisk "0.10.0"]]
  :main ^:skip-aot clisktest4.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

Obsah souboru core.clj:

(ns clisktest4.core
    (:gen-class)
    (:use clisk.live))
 
(import java.io.File)
(import javax.imageio.ImageIO)
 
(defn write-image
    "Uložení rastrového obrázku typu BufferedImage do souboru."
    [image file-name]
    (ImageIO/write image "png" (File. file-name)))
 
(defn write-pattern
    "Vytvoření rastrového obrázku na základě předaného patternu."
    [pattern file-name]
    (write-image (image pattern) file-name))
 
(defn construct-filename
    [filename-prefix index]
    (format "%s_%02d.png" filename-prefix index))
 
(defn noise-patterns-test
    "Otestování funkce vnoise a vsnoise."
    [noise-function filename-prefix]
    (let [patterns [noise-function
                    (monochrome noise-function)
                    (scale 1/10 noise-function)
                    (monochrome (scale 1/10 noise-function))
                    (v- (v* 5 noise-function) 1)
                    (scale 1/4 (offset noise-function (checker yellow blue)))
                    (scale 1/4 (offset (v* 1/5 noise-function) (checker yellow blue)))
                    (scale 1/4 (offset (v* 1/2 noise-function) (checker yellow blue)))
                    (scale 1/4 (offset (v* 2 noise-function)   (checker yellow blue)))
                    (scale 1/4 (offset (v* 5 noise-function)   (checker yellow blue)))
                    (scale 1/4 (rotate (v* 5 noise-function)   (checker yellow blue)))
                    (scale 1/4 (rotate (v* 5 noise-function)   (offset (v* 5 noise-function) (checker yellow blue))))
                    ]]
        ; postupně projít všemi prvky vektoru "patterns", vytvořit
        ; dvouprvkový vektor [index+patter], vytvořit jméno výstupního
        ; souboru a následně zavolat funkci write-pattern
        (doseq [ [i pattern] (map-indexed vector patterns)]
            (write-pattern pattern (construct-filename filename-prefix i)))))
 
(defn -main
    [& args]
    (try
        (println "Vnoise test...")
        (noise-patterns-test vnoise "vnoise")
        (println "Done")
        (println "Vsnoise test...")
        (noise-patterns-test vsnoise "vsnoise")
        (println "Done")
        (catch Throwable e
            (println (.toString e)))
        (finally ; jistota, že program vždy korektně skončí
            (System/exit 0))))

5. Podpora pro vykreslování fraktálů

V knihovně Clisk je podporováno i generování textur s využitím fraktálů, přesněji řečeno takových fraktálů, které lze vykreslit v rovině takovým způsobem, že se každému bodu (popř. pixelu) přiřadí barva na základě počtu iterací nutných pro splnění nějaké podmínky, přičemž vstupními informacemi pro výpočet je pozice bodu v rovině nebo v prostoru, maximální počet iterací, podmínka pro ukončení výpočtu a taktéž funkce použitá pro obarvení bodu. Do této kategorie fraktálů spadají prakticky všechny známé fraktály vykreslované v komplexní rovině, především pak Mandelbrotova množina, Newtonova množina, fraktály typu Phonenix, Magnet apod. V navazujících dvou kapitolách si ukážeme použití knihovny Clisk pro vykreslení klasické Mandelbrotovy množiny i kubické Mandelbrotovy množiny.

Obrázek 29: Mandelbrotova množina (zvětšeno).

Obrázek 30: Newtonova množina.

Obrázek 31: Fraktál typu Magnet.

Obrázek 32: Fraktál typu Magnet (odlišný způsob zobrazení).

6. Mandelbrotova množina

Mandelbrotova množina je vytvořena pomocí jednoduchého dynamického systému, který je založený na iteraci funkce komplexní paraboly:

zn+1=zn2+c

kde proměnné z a c leží v komplexní rovině.

V průběhu výpočtu se hodnota z postupně mění, zatímco hodnota c zůstává konstantní – tato se mění pouze při přesunu výpočtu na nový bod. Celý iterační proces začíná s určitou startovní hodnotou z0, takže systém postupně generuje sekvenci hodnot zk, které nazýváme orbit. Postup si ukážeme na prvních čtyřech iteracích:

z1=(z0)2+c
z2=((z0)2+c)2+c
z3=(((z0)2+c)2+c)2+c
z4=((((z0)2+c)2+c)2+c)2+c
 

Při práci se systémem popsaným v předchozím textu nás zajímá, zda pro danou startovní hodnotu z0 a konstantu c posloupnost zk konverguje či diverguje. Ukazuje se, že pro některé počáteční hodnoty nastává také oscilace – hodnoty zk se opakují s určitou periodou, to znamená, že platí rovnost zk=zk+i, kde i je perioda oscilace. Bližším studiem vlivu počátečních podmínek na budoucí stav systému se zabýval právě Benoit B. Mandelbrot a před ním i Fatou.

Mandelbrot se omezit na případ, kdy počáteční hodnota z0 je nulová a pro každý počítaný bod se mění pouze konstanta c. Iterativním výpočtem vzniknou pouze orbity nuly. Orbity nuly lze podle jejich vlastností rozdělit do dvou kategorií:

  1. Pro některé hodnoty c je orbit konečný, tzn. všechny hodnoty zk jsou konečné. Do této kategorie spadají také hodnoty, které oscilují.
  2. Pro další hodnoty c je orbit nekonečný, tzn. po určité době rostou hodnoty zk nade všechny meze.

Mandelbrotova množina je poté definována jako množina všech komplexních čísel c, které produkují konečný orbit nuly.

Obrázek 33: Mandelbrotova množina vytvořená funkcí mandelbrot-fractal. Střed obdélníku je nastaven na souřadnice [-1/2 0], zvětšení na hodnotu 3/4 a maximální počet iterací na 1000. Vnitřek Mandelbrotovy množiny má černou barvu.

Podívejme se nyní na způsob, jakým je v knihovně Clisk možné vykreslit Mandelbrotovu množinu. K tomu musíme použít dvě funkce nazvané viewport a fractal. První z těchto funkcí slouží (poněkud zjednodušeně řečeno) k nastavení umístění a rozměrů obdélníku v rovině. Všechny body tvořící vstupní parametry pro výpočet fraktálu jsou pravidelně rozmístěny právě v tomto obdélníku (leží v pravidelné mřížce). V mnoha případech je však zadávání rozměrů a umístění obdélníku v rovině poněkud pracné, proto jsem použil odlišný a v praxi i používanější způsob – do funkce pro výpočet fraktálu nazvané jednoduše mandelbrot-fractal vstupují informace o středu obdélníku center-x, center-y a třetí informace o požadovaném zvětšení fraktálu scale. Z těchto tří parametrů se vypočítají rozměry zmíněného obdélníku xmin, xmax, ymin a ymax, které jsou předány do funkce viewport.

Obrázek 34: Mandelbrotova množina vytvořená funkcí mandelbrot-fractal. Střed obdélníku je nastaven na souřadnice [0 1], zvětšení na hodnotu 4 a maximální počet iterací na 1000. Vnitřek Mandelbrotovy množiny má černou barvu.

Mnohem zajímavější je funkce nazvaná fractal, která je taktéž uživatelům v knihovně Clisk nabízena. Této funkci lze předat větší počet parametrů uvozených klíči :init, :while, :update, :result, :bailout-result a :max-iterations. Význam jednotlivých parametrů shrnuje tabulka:

Parametr uvozený klíčem Význam
:init inicializace pomocných lokálních proměnných atd.
:while podmínka pro ukončení iterační smyčky
:update výraz zavolaný v průběhu každé iterace, právě zde se provádí výpočet
:result jaká hodnota se má vrátit, pokud se splní podmínka pro ukončení iterační smyčky
:bailout-result hodnota (většinou barva) vrácená ve chvíli, kdy se překročí maximální počet iterací
:max-iterations maximální počet iterací, pokud dojde k překročení, vrátí se hodnota :bailout-result

Obrázek 35: Mandelbrotova množina vytvořená funkcí mandelbrot-fractal. Střed obdélníku je nastaven na souřadnice [0 1], zvětšení na hodnotu 10 a maximální počet iterací na 1000. Vnitřek Mandelbrotovy množiny má černou barvu.

Iterační vzorec pro výpočet Mandelbrotovy množiny známe, stejně tak známe podmínku pro ukončení iterační smyčky. Nyní je „pouze“ nutné tyto vztahy (používající komplexní čísla) přepsat takovým způsobem, aby se používaly vektorové operace. Podívejme se na řešení, kde se explicitně používají lokální proměnné x a y a vektorové operace v-, v+, v* a length:

(defn mandelbrot-fractal
    "Výpočet Mandelbrotovy množiny."
    [center-x center-y scale maxiter]
    (let [xmin (- center-x (/ 1.0 scale))
          xmax (+ center-x (/ 1.0 scale))
          ymin (- center-y (/ 1.0 scale))
          ymax (+ center-y (/ 1.0 scale))]
        (viewport [xmin ymin] [xmax ymax]
            (fractal
                ; podmínka pro ukončení iterační smyčky
                :while (v- 2 (length [x y]))
                ; výpočet z=z^2+c převedený na operace nad vektory
                :update (v+ c [(v- (v* x x) (v* y y)) ; zx^2-zy^2 + cx
                               (v* 2 x y)])           ; 2*zx*zy + cy
                ; výpočet barvy výsledného vzorku
                :result (v* 'i 0.01)
                ; barva, která se vrátí ve chvíli,
                ; kdy se dosáhne maximálního počtu iterací
                :bailout-result black
                ; maximální počet iterací
                :max-iterations maxiter))))

Obrázek 36: Mandelbrotova množina vytvořená funkcí mandelbrot-fractal.

Tuto funkci lze snadno otestovat, zde s použitím „tabulky“ (vektoru vektorů), v níž jsou zaznamenány středy fraktálu v komplexní rovině, zvětšení, maximální počet iterací a jméno výsledného souboru s texturou:

(defn mandelbrot-rendering-test
    []
    ;                      x0   y0 scale maxiter filename
    (let [mandel-params [[-1/2  0  3/4   1000    "mandelbrot1.png"]
                         [ 0   -1  4     1000    "mandelbrot2.png"]
                         [ 0   -1  10    1000    "mandelbrot3.png"]
                         [-1.74809088500000000 0.00069335009900000 110116 1000 "mandelbrot4.png"]
                         [-0.80594802749999990 0.20140617800000000 50 10000 "mandelbrot5.png"]]]
        (doseq [mandel-param mandel-params]
            (-> (mandelbrot-fractal (nth mandel-param 0)
                                    (nth mandel-param 1)
                                    (nth mandel-param 2)
                                    (nth mandel-param 3))
                (write-pattern (nth mandel-param 4))))))

Obrázek 37: Mandelbrotova množina vytvořená funkcí mandelbrot-fractal.

7. Kubická Mandelbrotova množina

Pro ilustraci možností funkce fractal popsané v předchozí kaptitole si ukažme, jakým způsobem je možné vykreslit kubickou Mandelbrotovu množinu, u níž se namísto iteračního vztahu:

zn+1=zn2+c

používá vztah:

zn+1=zn3+c

kde proměnné z a c opět leží v komplexní rovině.

Převod tohoto vztahu na „vektorovou“ podobu v parametru :update je poněkud složitější, ale snadno pochopitelný, když si uvědomíme, jak se počítá zn3 v komplexní rovině:

(defn cubic-mandelbrot-fractal
    "Výpočet kubické Mandelbrotovy množiny."
    [center-x center-y scale maxiter]
    (let [xmin (- center-x (/ 1.0 scale))
          xmax (+ center-x (/ 1.0 scale))
          ymin (- center-y (/ 1.0 scale))
          ymax (+ center-y (/ 1.0 scale))]
        (viewport [xmin ymin] [xmax ymax]
            (fractal
                ; podmínka pro ukončení iterační smyčky
                :while (v- 2 (length [x y]))
                ; výpočet z=z^2+c převedený na operace nad vektory
                :update (v+ c [(v- (v* x x x)   (v* 3 x y y)) ; zxn= zx*zx*zx-3.0*zx*zy*zy+cx;
                               (v- (v* 3 x x y) (v* y y y))]) ; zyn=-zy*zy*zy+3.0*zx*zx*zy+cy;
                ; výpočet barvy výsledného vzorku
                :result (v* 'i 0.01)
                ; barva, která se vrátí ve chvíli,
                ; kdy se dosáhne maximálního počtu iterací
                :bailout-result black
                ; maximální počet iterací
                :max-iterations maxiter))))

Obrázek 38: Kubická Mandelbrotova množina vytvořená funkcí cubic-mandelbrot-fractal.

8. Úplný zdrojový kód demonstračního příkladu clisktest5

V této kapitole bude uveden výpis úplného zdrojového kódu dnešního druhého a současně i posledního demonstračního příkladu nazvaného clisktest5, z něhož jsme používali úryvky a ukázky v předchozích dvou kapitolách:

Obsah souboru project.clj:

bitcoin_skoleni

(defproject clisktest5 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [net.mikera/clisk "0.10.0"]]
  :main ^:skip-aot clisktest5.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

Obsah souboru core.clj:

(ns clisktest5.core
    (:gen-class)
    (:use clisk.live))
 
(import java.io.File)
(import javax.imageio.ImageIO)
 
(defn write-image
    "Uložení rastrového obrázku typu BufferedImage do souboru."
    [image file-name]
    (ImageIO/write image "png" (File. file-name)))
 
(defn write-pattern
    "Vytvoření rastrového obrázku na základě předaného patternu."
    [pattern file-name]
    (write-image (image pattern) file-name))
 
(defn mandelbrot-fractal
    "Výpočet Mandelbrotovy množiny."
    [center-x center-y scale maxiter]
    (let [xmin (- center-x (/ 1.0 scale))
          xmax (+ center-x (/ 1.0 scale))
          ymin (- center-y (/ 1.0 scale))
          ymax (+ center-y (/ 1.0 scale))]
        (viewport [xmin ymin] [xmax ymax]
            (fractal
                ; podmínka pro ukončení iterační smyčky
                :while (v- 2 (length [x y]))
                ; výpočet z=z^2+c převedený na operace nad vektory
                :update (v+ c [(v- (v* x x) (v* y y)) ; zx^2-zy^2 + cx
                               (v* 2 x y)])           ; 2*zx*zy + cy
                ; výpočet barvy výsledného vzorku
                :result (v* 'i 0.01)
                ; barva, která se vrátí ve chvíli,
                ; kdy se dosáhne maximálního počtu iterací
                :bailout-result black
                ; maximální počet iterací
                :max-iterations maxiter))))
 
(defn cubic-mandelbrot-fractal
    "Výpočet kubické Mandelbrotovy množiny."
    [center-x center-y scale maxiter]
    (let [xmin (- center-x (/ 1.0 scale))
          xmax (+ center-x (/ 1.0 scale))
          ymin (- center-y (/ 1.0 scale))
          ymax (+ center-y (/ 1.0 scale))]
        (viewport [xmin ymin] [xmax ymax]
            (fractal
                ; podmínka pro ukončení iterační smyčky
                :while (v- 2 (length [x y]))
                ; výpočet z=z^2+c převedený na operace nad vektory
                :update (v+ c [(v- (v* x x x)   (v* 3 x y y)) ; zxn= zx*zx*zx-3.0*zx*zy*zy+cx;
                               (v- (v* 3 x x y) (v* y y y))]) ; zyn=-zy*zy*zy+3.0*zx*zx*zy+cy;
                ; výpočet barvy výsledného vzorku
                :result (v* 'i 0.01)
                ; barva, která se vrátí ve chvíli,
                ; kdy se dosáhne maximálního počtu iterací
                :bailout-result black
                ; maximální počet iterací
                :max-iterations maxiter))))
 
(defn mandelbrot-rendering-test
    []
    ;                      x0   y0 scale maxiter filename
    (let [mandel-params [[-1/2  0  3/4   1000    "mandelbrot1.png"]
                         [ 0   -1  4     1000    "mandelbrot2.png"]
                         [ 0   -1  10    1000    "mandelbrot3.png"]
                         [-1.74809088500000000 0.00069335009900000 110116 1000 "mandelbrot4.png"]
                         [-0.80594802749999990 0.20140617800000000 50 10000 "mandelbrot5.png"]]]
        (doseq [mandel-param mandel-params]
            (-> (mandelbrot-fractal (nth mandel-param 0)
                                    (nth mandel-param 1)
                                    (nth mandel-param 2)
                                    (nth mandel-param 3))
                (write-pattern (nth mandel-param 4))))))
 
(defn fractal-rendering-test
    []
    (mandelbrot-rendering-test)
    (-> (cubic-mandelbrot-fractal 0 0 3/4 1000) (write-pattern "mandelbrot6.png")))
 
(defn -main
    [& args]
    (try
        (println "fractal rendering test...")
        (fractal-rendering-test)
        (println "Done")
        (catch Throwable e
            (println (.toString e)))
        (finally ; jistota, že program vždy korektně skončí
            (System/exit 0))))

9. Repositář s demonstračními příklady

Oba dva dnes popsané demonstrační příklady byly, podobně jako v předchozích částech tohoto seriálu, uloženy do GIT repositáře dostupného na adrese https://github.com/tisnik/clojure-examples. V tabulce zobrazené pod tímto odstavcem naleznete na jednotlivé příklady přímé odkazy:

10. Odkazy na předchozí části seriálu

  1. Leiningen: nástroj pro správu projektů napsaných v Clojure
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure/
  2. Leiningen: nástroj pro správu projektů napsaných v Clojure (2)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-2/
  3. Leiningen: nástroj pro správu projektů napsaných v Clojure (3)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-3/
  4. Leiningen: nástroj pro správu projektů napsaných v Clojure (4)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-4/
  5. Leiningen: nástroj pro správu projektů napsaných v Clojure (5)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-5/
  6. Leiningen: nástroj pro správu projektů napsaných v Clojure (6)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-6/
  7. Programovací jazyk Clojure a databáze (1.část)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/
  8. Pluginy pro Leiningen
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/
  9. Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi/
  10. Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi-2/
  11. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk
    http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk/

11. Odkazy na Internetu

  1. Clisk
    https://github.com/mikera/clisk
  2. clojars: net.mikera/clisk
    https://clojars.org/net.mikera/clisk
  3. clojure.inspector
    http://clojure.github.io/clo­jure/clojure.inspector-api.html
  4. Clisk: wiki
    https://github.com/mikera/clisk/wiki
  5. Dokumentace vygenerovaná pro knihovnu core.matrix
    https://cloojure.github.i­o/doc/core.matrix/index.html
  6. Size and Dimensionality
    https://groups.google.com/fo­rum/#!topic/numerical-clojure/zebBCa68eTw/discussion
  7. Towards core.matrix for Clojure?
    https://clojurefun.wordpres­s.com/2013/01/05/towards-core-matrix-for-clojure/
  8. The Clojure Toolbox
    http://www.clojure-toolbox.com/
  9. Neanderthal
    http://neanderthal.uncomplicate.org/
  10. Hello world project
    https://github.com/uncompli­cate/neanderthal/blob/mas­ter/examples/hello-world/project.clj
  11. vectorz-clj
    https://github.com/mikera/vectorz-clj
  12. vectorz – Examples
    https://github.com/mikera/vectorz-clj/wiki/Examples
  13. gloss
    https://github.com/ztellman/gloss
  14. HTTP client/server for Clojure
    http://www.http-kit.org/
  15. Array Programming
    https://en.wikipedia.org/wi­ki/Array_programming
  16. Discovering Array Languages
    http://archive.vector.org­.uk/art10008110
  17. no stinking loops – Kalothi
    http://www.nsl.com/
  18. Vector (obsahuje odkazy na články, knihy a blogy o programovacích jazycích APL, J a K)
    http://www.vector.org.uk/
  19. APL Interpreters
    http://www.vector.org.uk/?a­rea=interpreters
  20. APL_(programming_language
    http://en.wikipedia.org/wi­ki/APL_(programming_langu­age
  21. APL FAQ
    http://www.faqs.org/faqs/apl-faq/
  22. APL FAQ (nejnovější verze)
    http://home.earthlink.net/~swsir­lin/apl.faq.html
  23. A+
    http://www.aplusdev.org/
  24. APLX
    http://www.microapl.co.uk/
  25. FreeAPL
    http://www.pyr.fi/apl/index.htm
  26. J: a modern, high-level, general-purpose, high-performance programming language
    http://www.jsoftware.com/
  27. K, Kdb: an APL derivative for Solaris, Linux, Windows
    http://www.kx.com
  28. openAPL (GPL)
    http://sourceforge.net/pro­jects/openapl
  29. Parrot APL (GPL)
    http://www.parrotcode.org/
  30. Learning J (Roger Stokes)
    http://www.jsoftware.com/hel­p/learning/contents.htm
  31. Rosetta Code
    http://rosettacode.org/wiki/Main_Page
  32. Why APL
    http://www.acm.org/sigapl/whyapl.htm
  33. java.jdbc API Reference
    https://clojure.github.io/java.jdbc/
  34. Hiccup
    https://github.com/weavejester/hiccup
  35. Clojure Ring na GitHubu
    https://github.com/ring-clojure/ring
  36. A brief overview of the Clojure web stack
    https://brehaut.net/blog/2011/rin­g_introduction
  37. Getting Started with Ring
    http://www.learningclojure­.com/2013/01/getting-started-with-ring.html
  38. Getting Started with Ring and Compojure – Clojure Web Programming
    http://www.myclojureadven­ture.com/2011/03/getting-started-with-ring-and-compojure.html
  39. Unit Testing in Clojure
    http://nakkaya.com/2009/11/18/unit-testing-in-clojure/
  40. Testing in Clojure (Part-1: Unit testing)
    http://blog.knoldus.com/2014/03/22/tes­ting-in-clojure-part-1-unit-testing/
  41. API for clojure.test – Clojure v1.6 (stable)
    https://clojure.github.io/clo­jure/clojure.test-api.html
  42. Leiningen: úvodní stránka
    http://leiningen.org/
  43. Leiningen: Git repository
    https://github.com/techno­mancy/leiningen
  44. leiningen-win-installer
    http://leiningen-win-installer.djpowell.net/
  45. Clojure 1: Úvod
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/
  46. Clojure 2: Symboly, kolekce atd.
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/
  47. Clojure 3: Funkcionální programování
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-3-cast-funkcionalni-programovani/
  48. Clojure 4: Kolekce, sekvence a lazy sekvence
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-4-cast-kolekce-sekvence-a-lazy-sekvence/
  49. Clojure 5: Sekvence, lazy sekvence a paralelní programy
    http://www.root.cz/clanky/clojure-a-bezpecne-aplikace-pro-jvm-sekvence-lazy-sekvence-a-paralelni-programy/
  50. Clojure 6: Podpora pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/
  51. Clojure 7: Další funkce pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/
  52. Clojure 8: Identity, stavy, neměnné hodnoty a reference
    http://www.root.cz/clanky/programovaci-jazyk-clojure-8-identity-stavy-nemenne-hodnoty-a-referencni-typy/
  53. Clojure 9: Validátory, pozorovatelé a kooperace s Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-9-validatory-pozorovatele-a-kooperace-mezi-clojure-a-javou/
  54. Clojure 10: Kooperace mezi Clojure a Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/
  55. Clojure 11: Generátorová notace seznamu/list comprehension
    http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/
  56. Clojure 12: Překlad programů z Clojure do bajtkódu JVM I
    http://www.root.cz/clanky/programovaci-jazyk-clojure-12-preklad-programu-z-clojure-do-bajtkodu-jvm/
  57. Clojure 13: Překlad programů z Clojure do bajtkódu JVM II
    2) http://www.root.cz/clanky/programovaci-jazyk-clojure-13-preklad-programu-z-clojure-do-bajtkodu-jvm-pokracovani/
  58. Clojure 14: Základy práce se systémem maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/
  59. Clojure 15: Tvorba uživatelských maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/
  60. Clojure 16: Složitější uživatelská makra
    http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/
  61. Clojure 17: Využití standardních maker v praxi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/
  62. Clojure 18: Základní techniky optimalizace aplikací
    http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/
  63. Clojure 19: Vývojová prostředí pro Clojure
    http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/
  64. Clojure 20: Vývojová prostředí pro Clojure (Vimu s REPL)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-20-vyvojova-prostredi-pro-clojure-integrace-vimu-s-repl/
  65. Clojure 21: ClojureScript aneb překlad Clojure do JS
    http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.