Obsah
1. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk
2. Koncepty, na nichž je postavena knihovna Clisk
4. Úplný zdrojový kód demonstračního příkladu clisktest1
5. Převod vzorku na rastrový obraz a uložení obrazu do souboru
6. Jednoduché uživatelsky definované vzorky
7. Složitější uživatelsky definované vzorky
8. Úplný zdrojový kód demonstračního příkladu clisktest2
9. Generátory, které jsou součástí knihovny Clisk
10. Funkce compose a její použití při tvorbě textur
11. Vnořené vzorky, funkce offset, rotate a scale
12. Úplný zdrojový kód demonstračního příkladu clisktest3
13. Repositář s demonstračními příklady
14. Odkazy na předchozí části seriálu
1. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk
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, si popíšeme některé vybrané vlastnosti knihovny nazvané poněkud záhadně Clisk. Jedná se o knihovnu určenou pro tvorbu (syntézu) různých typů procedurálních textur, a to nejenom klasických 2D textur reprezentovaných rastrovými obrázky, ale i trojrozměrných a dokonce i čtyřrozměrných textur, které lze použít například v několika raytracerech (čtyřrozměrné textury lze animovat a tvořit například oblaka). V knihovně Clisk jsou deklarovány různé generátory šumu, předdefinované vzorky (šachovnice, mřížka) atd. ovšem mnohem důležitější a současně i zajímavější je podpora pro tvorbu vlastních generátorů, možnost kompozice několika generátorů, programové řízení generování textur atd. Knihovna Clisk taktéž obsahuje podporu pro rendering fraktálů různých typů, například i slavné Mandelbrotovy množiny (lze však deklarovat i jakýkoli jiný fraktál vykreslovaný v komplexní rovině).
Zajímavý je taktéž fakt, že knihovna Clisk poměrně důsledně dodržuje některá pravidla funkcionálního programování (zejména předávání funkcí v parametrech jiným funkcím, kompozice funkcí atd.), takže se může jednat o velmi užitečnou a pro studenty i poměrně přitažlivou učební pomůcku při vysvětlování tohoto na mnoha školách stále opomíjeného paradigmatu. K tomu dopomáhá i možnost zobrazit si vygenerovanou texturu ihned po její deklaraci s využitím funkce show (viz též kapitolu číslo 3), což mj. znamená, že pro experimentování s možnostmi nabízenými knihovnou Click je více než vhodné používat interaktivní smyčku REPL, kterou je programovací jazyk Clojure samozřejmě vybaven. V praxi je však mnohem efektivnější použití vylepšené smyčky REPL nabízené nástrojem Leiningen, ideálně propojené s textovým editorem a/nebo vhodně nakonfigurovaným integrovaným vývojovým prostředím (příkladem může být relativně úspěšné Counterclockwise).
2. Koncepty, na nichž je postavena knihovna Clisk
Jak jsme si již řekli v první kapitole, dokáže knihovna Clisk generovat dvourozměrné, trojrozměrné i čtyřrozměrné procedurální textury, ovšem v dnešním článku se pro jednoduchost budeme zabývat „pouze“ texturami dvourozměrnými, tj. běžnými rastrovými obrázky. Toto zjednodušení nám mj. umožní snazší zobrazení vygenerované textury bez nutnosti použití raytraceru. Obrázky reprezentující výslednou texturu jsou tvořeny běžným dvourozměrným polem pixelů, přičemž každý pixel je specifikován trojicí barvových složek [red, green, blue], což je z pohledu programátora vektor. V knihovně Clisk se dvourozměrné textury tvoří vlastně velmi jednoduše – deklarováním funkce, která pro předané souřadnice [x, y] vrátí vektor s trojicí prvků [red, green, blue]. Nezávisle na rozlišení výsledného rastrového obrázku nabývá souřadnice x hodnot v rozsahu <0, 1>. Totéž pravidlo platí i pro souřadnici y. Dokonce i hodnoty barvových složek red, green a blue leží v rozsahu <0, 1>.
Pro zjednodušení aplikací je možné, aby uživatelská funkce použitá pro tvorbu textury nevracela vektor s trojicí hodnot [red, green, blue], ale pouze jedinou skalární hodnotu. Výsledkem pak bude obrázek ve stupních šedi, popř. lze takovou funkci vhodným způsobem zkombinovat s funkcí jinou. V knihovně Clisk je již mnoho funkcí vhodných pro tvorbu procedurálních textur deklarováno; některé z nich si popíšeme v navazujících kapitolách. Další zjednodušení představuje několik nových funkcí a taktéž operátorů, které mohou pracovat s operandy typu vektor. Na rozdíl od běžných skalárních operátorů typu +, -, * a / mají nové vektorové operátory názvy v+, v-, v*, vdivide, vpow, vmod a vsqrt. (připomeňme si jen, že v programovacím jazyce Clojure se všechny operátory zapisují stejným způsobem jako volání funkcí, protože Clojure de facto pojem „operátor“ v běžném významu známém z matematiky ani nepoužívá). Použití funkcí a operátorů pracujících s vektory si ukážeme v dnešním druhém demonstračním příkladu.
3. Funkce show a checker
V této kapitole se seznámíme s dvojicí funkcí deklarovaných v knihovně Clisk. První z těchto funkcí se jmenuje show. Název již malinko napovídá, co funkce show provádí – jako parametr se této funkci předá deklarace textury (taktéž ve formě funkce), textura se vypočítá a následně zobrazí v samostatném okně. Druhá důležitá funkce se jmenuje checker. Jedná se o takzvaný pattern, tj. o funkci používanou pro výpočet barvy jednotlivých pixelů textury na základě přečtených hodnot x a y. Pro zajímavost se podívejme, jak je funkce checker deklarována. Jejími parametry je dvojice barev, kterými budou vybarvena políčka šachovnice, uvnitř se pak pracuje s hodnotami x a y:
(defn checker "Checker pattern in (x,y) space, with 2*2 grid in [0..1,0..1] range" ([a b] (vif '(clojure.core/* (clojure.core/- (clisk.functions/frac x) 0.5) (clojure.core/- (clisk.functions/frac y) 0.5)) a b)))
Použití funkcí show a checker je velmi jednoduché, samozřejmě ve chvíli, kdy je projekt správně nastaven a je proveden import všech funkcí nabízených knihovnou Clisk (viz následující kapitolu):
(show (checker black white))
Obrázek 1: Výsledek vyhodnocení (show (checker black white)). Okno s grafickým uživatelským rozhraním bylo vytvořeno knihovnou Clisk.
Základní rozlišení dvourozměrné textury je 256×256 pixelů, ovšem s využitím pojmenovaného parametru :size lze tuto velikost (rozlišení) jednoduše změnit:
(show (checker yellow blue) :size 512)
Obrázek 2: Výsledek vyhodnocení (show (checker yellow blue) :size 512). Okno s grafickým uživatelským rozhraním bylo vytvořeno knihovnou Clisk
Poznámka: deklarace základních barev je samozřejmě taktéž součástí knihovny Clisk.
4. Úplný zdrojový kód demonstračního příkladu clisktest1
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 clisktest1, z něhož jsme používali úryvky v předchozích kapitolách:
Obsah souboru project.clj:
(defproject clisktest1 "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 clisktest1.core :target-path "target/%s" :profiles {:uberjar {:aot :all}})
Obsah souboru core.clj:
(ns clisktest1.core (:gen-class) (:use clisk.live)) (defn -main "Začínáme..." [& args] ; funkce "show" zobrazí vzorek (show (checker black white)) (show (checker yellow blue) :size 512) ; nutno odkomentovat v případě, že se má aplikace automaticky ukončit ;(System/exit 0) )
5. Převod vzorku na rastrový obraz a uložení obrazu do souboru
Funkce show, kterou jsme použili v dnešním prvním demonstračním příkladu, je sice vhodná pro interaktivní tvorbu textur s využitím interaktivní smyčky REPL, ovšem pro „opravdovou“ práci musíme mít možnost vygenerovanou texturu uložit do souboru ve vhodném rastrovém formátu. Ve skutečnosti je to poměrně jednoduché, protože v knihovně Clisk existuje funkce nazvaná image, která na svém vstupu očekává vzorek (například již zmíněnou funkci checker) a na výstup vrátí obrázek reprezentovaný objektem typu BufferedImage (používá se zde tedy Java interop). Můžeme si tedy vytvořit pomocnou funkci pro převod vzorku na rastrový obrázek a zavolání jiné funkce pro uložení rastrového obrázku do souboru:
(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))
Pro uložení rastrového obrázku do souboru opět využijeme Java interop, zde konkrétně statickou metodu nazvanou write, která je deklarována ve třídě javax.imageio.ImageIO. Metoda write akceptuje tři parametry – rastrový obrázek typu BufferedImage či WritableRenderedImage, řetězec s názvem výstupního formátu a objekt typu java.io.File reprezentující cestu k výslednému souboru. Naše druhá pomocná funkce je tedy opět velmi jednoduchá, o čemž se lze snadno přesvědčit z jejího výpisu:
(defn write-image "Uložení rastrového obrázku typu BufferedImage do souboru." [image file-name] (ImageIO/write image "png" (File. file-name)))
Lze se však obejít i bez těchto funkcí, což si můžeme snadno ukázat na příkladech:
(def test-img (image (checker black white))) (javax.imageio.ImageIO/write test-img "png" (java.io.File. "test.png"))
Nebo ještě snadněji s využitím takzvaného threading makra:
(-> (checker black white) image (javax.imageio.ImageIO/write "png" (java.io.File. "test1.png")))
6. Jednoduché uživatelsky definované vzorky
Uživatelskou funkci nazvanou write-pattern, s níž jsme se seznámili v předchozí kapitole, můžeme použít pro vytvoření první série jednoduchých textur. Podívejme se na jednotlivé volání této funkce s krátkými komentáři:
(defn basic-patterns-test "Jednoduché uživatelsky definované vzorky." [] ; nezávisle na hodnotách x a y se vrací konstanta ; black je symbol reprezentující vektor představující černou barvu (write-pattern black "basic0.png") ; nezávisle na hodnotách x a y se vrací konstanta ; 1/2 je skalární hodnota: šedá barva (write-pattern 1/2 "basic1.png") ; nezávisle na hodnotách x a y se vrací konstanta ; 2/3 červené barvy + 2/3 modré barvy (write-pattern [2/3 0 2/3] "basic2.png") ; červená složka odpovídá hodnotě souřadnice x ; modrá složka odpovídá hodnotě souřadnice y (write-pattern [x 1/2 y] "basic3.png") ; vytvoření gradientního přechodu (write-pattern [y y x] "basic4.png") ; symbol pos lze použít namísto [x y z] ; zde nás složka z nezajímá (je nulová), tudíž je modrá složka taktéž nulová (write-pattern pos "basic5.png"))
Obrázek 3: Textura tvořená vzorkem black. Celý obrázek je vyplněn černou barvou.
Obrázek 4: Textura tvořená vzorkem 1/2. Celý obrázek je vyplněn šedou barvou.
Obrázek 5: Textura tvořená vzorkem [2/3 0 2/3]. Celý obrázek je vyplněn fialovou barvou.
Obrázek 6: Textura tvořená vzorkem [x 1/2 y]. Výsledkem je gradientní přechod.
Obrázek 7: Textura tvořená vzorkem [y y x]. Výsledkem je gradientní přechod
Obrázek 8: Textura tvořená vzorkem pos. Výsledkem je opět gradientní přechod
7. Složitější uživatelsky definované vzorky
Podívejme se nyní na poněkud složitější vzorky definované uživatelem. V těchto vzorcích se využijí některé pomocné funkce z knihovny Clisk, například již zmíněný operátor v+ (vektorový součet), v* (násobení prvků se stejným indexem) či vsin (aplikace funkce sinus na jednotlivé prvky vektoru). Nejprve si musíme deklarovat pomocné symboly – nikoli funkce!:
(def sin-wave1 (v* 0.7 (vsin (v* 11 x)))) (def sin-wave2 (v* 0.5 (vsin (v* 17 y)))) (def sin-wave3 (v* 0.3 (vsin (v* 19 (v+ x y)))))
Dále vytvoříme pomocnou funkci nazvanou x2, která provádí tento výpočet: (x-1/2)2:
(defn x2 "Pomocná funkce při výpočtu vzorků." [x] (v* (v- x 1/2) (v- x 1/2)))
Tyto symboly a funkce jsou použity v dalším programovém kódu pro vytvoření zajímavějších textur:
(defn trickier-patterns-test "Složitější uživatelsky definované vzorky." [] ; diagonální gradientní přechod od černé k bílé (počítá se se skaláry) ; (zde ve skutečnosti není zapotřebí používat vektorové operace :-) (write-pattern (v* (v+ x y) 1/2) "tricky1.png") ; složitější gradientní přechod, zde již v prostoru RGB (write-pattern [(v* (v+ x y) 1/2) 0 (vabs (v- x y))] "tricky2.png") ; využití výše deklarovaného symbolu sin-wave1 ; který ovlivňuje zelenou barvovou složku (write-pattern [x sin-wave1 2/3] "tricky3.png") ; využití výše deklarovaných symbolů sin-wave1, sin-wave2 a sin-wave3 ; které ovlivňují všechny tři barvové složky (write-pattern [sin-wave1 sin-wave2 sin-wave3] "tricky4.png") ; kruhový vzorek - vlny (write-pattern (vsin (v* 100 (v+ (x2 x) (x2 y)))) "tricky5.png") ; funkce rgb-from-hsl provádí převod z barvového prostoru HSL do RGB (write-pattern (rgb-from-hsl (v+ [100 100 100] [x y z])) "tricky6.png") ; funkce rgb-from-hsl provádí převod z barvového prostoru HSL do RGB (write-pattern (rgb-from-hsl (v* 3/2 [sin-wave1 sin-wave2 sin-wave3])) "tricky7.png"))
Obrázek 9: Textura tvořená vzorkem (v* (v+ x y) 1/2). Výsledkem je gradientní přechod od černé barvy k barvě bílé.
Obrázek 10: Textura tvořená vzorkem [(v* (v+ x y) 1/2) 0 (vabs (v- x y))].
Obrázek 11: Textura tvořená vzorkem [x sin-wave1 2/3].
Obrázek 12: Textura tvořená vzorkem [sin-wave1 sin-wave2 sin-wave3]. Povšimněte si i modrých diagonálních pruhů.
Obrázek 13: Textura tvořená vzorkem (vsin (v* 100 (v+ (x2 x) (x2 y)))). Pixely se stejnou vzdáleností od středu obrázku mají vždy stejnou barvu.
Obrázek 14: Textura tvořená vzorkem (rgb-from-hsl (v+ [100 100 100] [x y z])).
Obrázek 15: Textura tvořená vzorkem (rgb-from-hsl (v* 3/2 [sin-wave1 sin-wave2 sin-wave3])).
8. Úplný zdrojový kód demonstračního příkladu clisktest2
Všechny funkce popsané v předchozích třech kapitolách byly zakomponovány do dnešního druhého demonstračního příkladu nazvaného clisktest2. Následuje výpis obou nejdůležitějších souborů tvořících tento příklad – jak projektového souboru project.clj, tak i vlastního „jádra“ aplikace tvořeného souborem core.clj.
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 clisktest2.core :target-path "target/%s" :profiles {:uberjar {:aot :all}})
Obsah souboru core.clj:
(ns clisktest2.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 basic-patterns-test "Jednoduché uživatelsky definované vzorky." [] (write-pattern black "basic0.png") (write-pattern 1/2 "basic1.png") (write-pattern [2/3 0 2/3] "basic2.png") (write-pattern [x 1/2 y] "basic3.png") (write-pattern [y y x] "basic4.png") (write-pattern pos "basic5.png")) (def sin-wave1 (v* 0.7 (vsin (v* 11 x)))) (def sin-wave2 (v* 0.5 (vsin (v* 17 y)))) (def sin-wave3 (v* 0.3 (vsin (v* 19 (v+ x y))))) (defn x2 "Pomocná funkce při výpočtu vzorků." [x] (v* (v- x 1/2) (v- x 1/2))) (defn trickier-patterns-test "Složitější uživatelsky definované vzorky." [] (write-pattern (v* (v+ x y) 1/2) "tricky1.png") (write-pattern [(v* (v+ x y) 1/2) 0 (vabs (v- x y))] "tricky2.png") (write-pattern [x sin-wave1 2/3] "tricky3.png") (write-pattern [sin-wave1 sin-wave2 sin-wave3] "tricky4.png") (write-pattern (vsin (v* 100 (v+ (x2 x) (x2 y)))) "tricky5.png") (write-pattern (rgb-from-hsl (v+ [100 100 100] [x y z])) "tricky6.png") (write-pattern (rgb-from-hsl (v* 3/2 [sin-wave1 sin-wave2 sin-wave3])) "tricky7.png")) (defn -main [& args] (try (println "Basic patterns test...") (basic-patterns-test) (println "Trickier patterns test...") (trickier-patterns-test) (println "Done") (catch Throwable e (println (.toString e))) (finally ; jistota, že program vždy korektně skončí (System/exit 0))))
9. Generátory, které jsou součástí knihovny Clisk
V knihovně Clisk je již definováno poměrně velké množství funkcí, které samy o sobě mohou sloužit pro tvorbu textury, popř. je možné tyto funkce zkombinovat s využitím funkce compose popsané v navazující kapitole. Mezi funkcemi určenými pro přímou i nepřímou tvorbu textur patří již dříve popsaná funkce checker, ale například i funkce turbulence, perlin-noise (generuje klasický Perlinův šum), perlin-snoise, plasma atd. Některé z těchto funkcí si otestujeme v následujícím úryvku kódu:
(defn predefined-patterns-test- "Otestování funkcí pro vytváření vzorků/textur 2D obrázků." [] (write-pattern (checker black white) "pattern_0.png") (write-pattern (scale 1/4 (checker black white)) "pattern_1.png") (write-pattern turbulence "pattern_2.png") (write-pattern (scale 1/10 perlin-noise) "pattern_3.png") (write-pattern (scale 1/40 perlin-noise) "pattern_4.png") (write-pattern (scale 1/10 perlin-snoise) "pattern_5.png") (write-pattern (scale 1/10 simplex-noise) "pattern_6.png") (write-pattern (scale 1/10 simplex-snoise) "pattern_7.png") (write-pattern (scale 1/10 plasma) "pattern_8.png") (write-pattern (scale 1/10 splasma) "pattern_9.png"))
Osobně však preferuji přepis predefined-patterns-test- s využitím smyčky představované makrem doseq a pomocnou (zde velmi užitečnou) funkcí map-indexed, pomocí níž lze imlementovat průchod vektorem a současně i použití čítače (tato funkce vrátí pro každý prvek vektoru patterns dvouprvkový vektor obsahující index a původní prvek):
(defn predefined-patterns-test "Otestování funkcí pro vytváření vzorků/textur 2D obrázků." [] (let [patterns [(checker black white) (scale 1/4 (checker black white)) turbulence spots blotches (scale 1/10 perlin-noise) (scale 1/40 perlin-noise) (scale 1/10 perlin-snoise) (scale 1/10 simplex-noise) (scale 1/10 simplex-snoise) (scale 1/10 plasma) (scale 1/10 splasma)]] ; postupně projít všemi prvky vektoru "patterns", vytvořit ; dvouprvkový vektor [index+patter] a zavolat pro tento ; vektor funkci write-pattern (doseq [ [i pattern] (map-indexed vector patterns)] (write-pattern pattern (str "pattern_" i ".png")))))
Obrázek 16: Textura tvořená vzorkem (checker black white).
Obrázek 17: Textura tvořená vzorkem (scale 1/4 (checker black white)).
Obrázek 18: Textura tvořená vzorkem turbulence.
Obrázek 19: Textura tvořená vzorkem spots.
Obrázek 20: Textura tvořená vzorkem blotches.
Obrázek 21: Textura tvořená vzorkem (scale 1/10 perlin-noise).
Obrázek 22: Textura tvořená vzorkem (scale 1/40 perlin-noise).
Obrázek 23: Textura tvořená vzorkem (scale 1/10 perlin-snoise).
Obrázek 24: Textura tvořená vzorkem (scale 1/10 simplex-noise).
Obrázek 25: Textura tvořená vzorkem (scale 1/10 simplex-snoise).
Obrázek 26: Textura tvořená vzorkem (scale 1/10 plasma).
Obrázek 27: Textura tvořená vzorkem (scale 1/10 splasma).
10. Funkce compose a její použití při tvorbě textur
Při tvorbě textur může být užitečná i funkce compose, která vypadá následovně:
(defn compose "Composes two or more vector functions" ([f g] (warp g f)) ([f g & more] (compose f (apply compose g more))))
Pokud jsou této funkci předány další dvě funkce, vyhodnotí se (warp g f), kde warp je pomocná funkce určená pro manipulaci s datovou strukturou představující „předpis“ pro generování textury. V praxi se funkce compose může použít následujícím způsobem pro kompozici dvou funkcí pro generování textury:
(defn composited-patterns-test "Otestování funkce compose." [] (let [patterns [(compose turbulence spots) (compose turbulence plasma) (compose (scale 1/40 (checker black white)) plasma) (compose (scale 1/40 (checker black white)) turbulence)]] ; postupně projít všemi prvky vektoru "patterns", vytvořit ; dvouprvkový vektor [index+patter] a zavolat pro tento ; vektor funkci write-pattern (doseq [ [i pattern] (map-indexed vector patterns)] (write-pattern pattern (str "composite_" i ".png")))))
Obrázek 28: Vzorek získaný výrazem (compose turbulence spots).
Obrázek 29: Vzorek získaný výrazem (compose turbulence plasma).
Obrázek 30: Vzorek získaný výrazem (compose (scale 1/40 (checker black white)) plasma).
Obrázek 31: Vzorek získaný výrazem (compose (scale 1/40 (checker black white)) turbulence).
11. Vnořené vzorky, funkce offset, rotate a scale
Posledními dvěma vlastnostmi knihovny Clisk, s nimiž se v dnešním článku seznámíme, jsou funkce offset, rotate, scale a taktéž možnost tvorby takzvaných vnořených vzorků (nested patterns). Tuto možnost si můžeme nejlépe ukázat na textuře šachovnice, protože namísto dvoubarevných políček může být každé políčko (například původně každé bílé políčko šachovnice) nahrazeno jiným vzorkem. Vliv funkcí offset, rotate a scale je zřejmý: slouží k posunu vzorku, jeho rotaci či změně měřítka (a to samozřejmě bez ztráty kvality, neboť tyto transformace jsou prováděny během výpočtu a nikoli na výsledné bitmapě). Vliv těchto funkcí je možné skládat, protože interně se textura podrobuje libovolné afinní transformaci. Podívejme se na následující příklady, z nichž bude patrný jak způsob tvorby vnořených vzorků, tak i způsob použití afinních transformací:
(defn nested-patterns-test [] (let [patterns [(checker black white) (checker black (scale 1/4 (checker blue yellow))) (checker (scale 1/4 (checker white black)) (scale 1/4 (checker blue yellow))) (checker (rotate 45 (scale 1/4 (checker white black))) (scale 1/4 (checker blue yellow))) (rotate 20 (checker (rotate 20 (scale 1/4 (checker white black))) (rotate 40 (scale 1/4 (checker blue yellow))))) ]] ; postupně projít všemi prvky vektoru "patterns", vytvořit ; dvouprvkový vektor [index+patter] a zavolat pro tento ; vektor funkci write-pattern (doseq [ [i pattern] (map-indexed vector patterns)] (write-pattern pattern (str "nested_" i ".png")))))
Obrázek 32: Vzorek získaný výrazem (checker black white). Výsledkem je nám již známá šachovnice.
Obrázek 33: Vzorek získaný výrazem (checker black (scale 1/4 (checker blue yellow))). Vliv funkce scale je zřejmý – došlo ke zmenšení rozměrů vzorku na 1/4 původní hodnoty. Navíc jsou původně bílá políčka nahrazena dalším vzorkem zmenšeným právě o onu 1/4.
Obrázek 34: Vzorek získaný výrazem (checker (scale 1/4 (checker white black)) (scale 1/4 (checker blue yellow))) Variace na předchozí příklad, zde ovšem došlo k náhradě jak bílých, tak i černých políček za jiné vzorky.
Obrázek 35: Vzorek získaný výrazem (checker (rotate 45 (scale 1/4 (checker white black))) (scale 1/4 (checker blue yellow))). „Vnitřní“ vzorek je otočen o 45 stupňů.
Obrázek 36: Vzorek získaný výrazem (rotate 20 (checker (rotate 20 (scale 1/4 (checker white black))) (rotate 40 (scale 1/4 (checker blue yellow))))).
12. Úplný zdrojový kód demonstračního příkladu clisktest3
Všechny funkce popsané v předchozích třech kapitolách jsou součástí dnešního třetího a současně i posledního demonstračního příkladu nazvaného jednoduše clisktest3. Opět si uvedeme výpis obou nejdůležitějších souborů, z nichž se tento příklad skládá: projektového souboru i „jádra“ aplikace.
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 clisktest3.core :target-path "target/%s" :profiles {:uberjar {:aot :all}})
Obsah souboru core.clj:
(ns clisktest3.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 predefined-patterns-test- "Otestování funkcí pro vytváření vzorků/textur 2D obrázků." [] (write-pattern (checker black white) "pattern_0.png") (write-pattern (scale 1/4 (checker black white)) "pattern_1.png") (write-pattern turbulence "pattern_2.png") (write-pattern (scale 1/10 perlin-noise) "pattern_3.png") (write-pattern (scale 1/40 perlin-noise) "pattern_4.png") (write-pattern (scale 1/10 perlin-snoise) "pattern_5.png") (write-pattern (scale 1/10 simplex-noise) "pattern_6.png") (write-pattern (scale 1/10 simplex-snoise) "pattern_7.png") (write-pattern (scale 1/10 plasma) "pattern_8.png") (write-pattern (scale 1/10 splasma) "pattern_9.png")) (defn predefined-patterns-test "Otestování funkcí pro vytváření vzorků/textur 2D obrázků." [] (let [patterns [(checker black white) (scale 1/4 (checker black white)) turbulence spots blotches (scale 1/10 perlin-noise) (scale 1/40 perlin-noise) (scale 1/10 perlin-snoise) (scale 1/10 simplex-noise) (scale 1/10 simplex-snoise) (scale 1/10 plasma) (scale 1/10 splasma)]] ; postupně projít všemi prvky vektoru "patterns", vytvořit ; dvouprvkový vektor [index+patter] a zavolat pro tento ; vektor funkci write-pattern (doseq [ [i pattern] (map-indexed vector patterns)] (write-pattern pattern (str "pattern_" i ".png"))))) (defn composited-patterns-test "Otestování funkce compose." [] (let [patterns [(compose turbulence spots) (compose turbulence plasma) (compose (scale 1/40 (checker black white)) plasma) (compose (scale 1/40 (checker black white)) turbulence)]] ; postupně projít všemi prvky vektoru "patterns", vytvořit ; dvouprvkový vektor [index+patter] a zavolat pro tento ; vektor funkci write-pattern (doseq [ [i pattern] (map-indexed vector patterns)] (write-pattern pattern (str "composite_" i ".png"))))) (defn nested-patterns-test [] (let [patterns [(checker black white) (checker black (scale 1/4 (checker blue yellow))) (checker (scale 1/4 (checker white black)) (scale 1/4 (checker blue yellow))) (checker (rotate 45 (scale 1/4 (checker white black))) (scale 1/4 (checker blue yellow))) (rotate 20 (checker (rotate 20 (scale 1/4 (checker white black))) (rotate 40 (scale 1/4 (checker blue yellow))))) ]] ; postupně projít všemi prvky vektoru "patterns", vytvořit ; dvouprvkový vektor [index+patter] a zavolat pro tento ; vektor funkci write-pattern (doseq [ [i pattern] (map-indexed vector patterns)] (write-pattern pattern (str "nested_" i ".png"))))) (defn -main [& args] (try (println "Predefined patterns test...") ;(predefined-patterns-test-) (predefined-patterns-test) (println "Composited patterns test...") (composited-patterns-test) (println "Nested patterns test...") (nested-patterns-test) (catch Throwable e (println (.toString e))) (finally ; jistota, že program vždy korektně skončí (System/exit 0))))
13. Repositář s demonstračními příklady
Všechny tři 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:
# | Příklad | Github |
---|---|---|
1 | clisktest1 | https://github.com/tisnik/clojure-examples/tree/master/clisktest1 |
2 | clisktest2 | https://github.com/tisnik/clojure-examples/tree/master/clisktest2 |
3 | clisktest3 | https://github.com/tisnik/clojure-examples/tree/master/clisktest3 |
14. Odkazy na předchozí části seriálu
- Leiningen: nástroj pro správu projektů napsaných v Clojure
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure/ - 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/ - 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/ - 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/ - 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/ - 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/ - Programovací jazyk Clojure a databáze (1.část)
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/ - Pluginy pro Leiningen
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/ - 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/ - 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/
15. Odkazy na Internetu
- Clisk
https://github.com/mikera/clisk - clojars: net.mikera/clisk
https://clojars.org/net.mikera/clisk - clojure.inspector
http://clojure.github.io/clojure/clojure.inspector-api.html - Clisk: wiki
https://github.com/mikera/clisk/wiki - Dokumentace vygenerovaná pro knihovnu core.matrix
https://cloojure.github.io/doc/core.matrix/index.html - Size and Dimensionality
https://groups.google.com/forum/#!topic/numerical-clojure/zebBCa68eTw/discussion - Towards core.matrix for Clojure?
https://clojurefun.wordpress.com/2013/01/05/towards-core-matrix-for-clojure/ - The Clojure Toolbox
http://www.clojure-toolbox.com/ - Neanderthal
http://neanderthal.uncomplicate.org/ - Hello world project
https://github.com/uncomplicate/neanderthal/blob/master/examples/hello-world/project.clj - vectorz-clj
https://github.com/mikera/vectorz-clj - vectorz – Examples
https://github.com/mikera/vectorz-clj/wiki/Examples - gloss
https://github.com/ztellman/gloss - HTTP client/server for Clojure
http://www.http-kit.org/ - Array Programming
https://en.wikipedia.org/wiki/Array_programming - Discovering Array Languages
http://archive.vector.org.uk/art10008110 - no stinking loops – Kalothi
http://www.nsl.com/ - Vector (obsahuje odkazy na články, knihy a blogy o programovacích jazycích APL, J a K)
http://www.vector.org.uk/ - APL Interpreters
http://www.vector.org.uk/?area=interpreters - APL_(programming_language
http://en.wikipedia.org/wiki/APL_(programming_language - APL FAQ
http://www.faqs.org/faqs/apl-faq/ - APL FAQ (nejnovější verze)
http://home.earthlink.net/~swsirlin/apl.faq.html - A+
http://www.aplusdev.org/ - APLX
http://www.microapl.co.uk/ - FreeAPL
http://www.pyr.fi/apl/index.htm - J: a modern, high-level, general-purpose, high-performance programming language
http://www.jsoftware.com/ - K, Kdb: an APL derivative for Solaris, Linux, Windows
http://www.kx.com - openAPL (GPL)
http://sourceforge.net/projects/openapl - Parrot APL (GPL)
http://www.parrotcode.org/ - Learning J (Roger Stokes)
http://www.jsoftware.com/help/learning/contents.htm - Rosetta Code
http://rosettacode.org/wiki/Main_Page - Why APL
http://www.acm.org/sigapl/whyapl.htm - java.jdbc API Reference
https://clojure.github.io/java.jdbc/ - Hiccup
https://github.com/weavejester/hiccup - Clojure Ring na GitHubu
https://github.com/ring-clojure/ring - A brief overview of the Clojure web stack
https://brehaut.net/blog/2011/ring_introduction - Getting Started with Ring
http://www.learningclojure.com/2013/01/getting-started-with-ring.html - Getting Started with Ring and Compojure – Clojure Web Programming
http://www.myclojureadventure.com/2011/03/getting-started-with-ring-and-compojure.html - Unit Testing in Clojure
http://nakkaya.com/2009/11/18/unit-testing-in-clojure/ - Testing in Clojure (Part-1: Unit testing)
http://blog.knoldus.com/2014/03/22/testing-in-clojure-part-1-unit-testing/ - API for clojure.test – Clojure v1.6 (stable)
https://clojure.github.io/clojure/clojure.test-api.html - Leiningen: úvodní stránka
http://leiningen.org/ - Leiningen: Git repository
https://github.com/technomancy/leiningen - leiningen-win-installer
http://leiningen-win-installer.djpowell.net/ - Clojure 1: Úvod
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/ - Clojure 2: Symboly, kolekce atd.
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/ - 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/ - 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/ - 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/ - Clojure 6: Podpora pro paralelní programování
http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/ - Clojure 7: Další funkce pro paralelní programování
http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/ - 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/ - 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/ - Clojure 10: Kooperace mezi Clojure a Javou
http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/ - Clojure 11: Generátorová notace seznamu/list comprehension
http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/ - 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/ - 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/ - Clojure 14: Základy práce se systémem maker
http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/ - Clojure 15: Tvorba uživatelských maker
http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/ - Clojure 16: Složitější uživatelská makra
http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/ - Clojure 17: Využití standardních maker v praxi
http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/ - Clojure 18: Základní techniky optimalizace aplikací
http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/ - Clojure 19: Vývojová prostředí pro Clojure
http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/ - 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/ - Clojure 21: ClojureScript aneb překlad Clojure do JS
http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/