Obsah
1. Piston – knihovna pro práci s 2D i 3D grafikou v Rustu
2. Koncepty, na nichž je knihovna Piston postavena
4. Vytvoření nového projektu používajícího moduly Pistonu
5. Stažení a překlad potřebných knihoven a modulů
7. Vytvoření nového okna s jeho navázáním na OpenGL backend
10. Hlavní funkce programu, překlad a spuštění aplikace
16. Úplný zdrojový kód třetího demonstračního příkladu
17. Dnes popsané struktury a traity
18. Repositář s demonstračními příklady
1. Piston – knihovna pro práci s 2D i 3D grafikou v Rustu
Po popisu skriptovacího jazyka Dyon [1] [2], který je určený primárně pro použití v rustovském ekosystému, se zaměříme na popis základních konceptů knihovny nazvané Piston. Knihovna Piston je určena pro tvorbu aplikací s grafickým výstupem, ať se již jedná o „pouhou“ 2D grafiku či o plnohodnotnou trojrozměrnou grafiku vykreslovanou s využitím grafického akcelerátoru. Může se jednat například o multimediální aplikace, grafická dema, hry atd. Ve skutečnosti knihovna Piston poměrně úzce souvisí s již popsaným Dyonem, protože Dyon byl vytvořen mj. i proto, aby se práce s grafikou do jisté míry zjednodušila (například se to týká podpory pro zpracování 2D, 3D i 4D vektorů, zaměření na práci s poli atd.) a aby bylo možné obejít některé těžkosti vyplývající z typového systému Rustu. Nicméně dnes se budeme zabývat použitím knihovny Piston přímo z Rustu; způsob propojení Dyonu a Pistonu bude popsán později.
2. Koncepty, na nichž je knihovna Piston postavena
Termín „knihovna Piston“ může být poněkud matoucí, protože si pod ním můžeme představit monolitickou knihovnu či ucelený (ale taktéž monolitický) framework. Ve skutečnosti je však na Piston implementován v mnoha modulech a záleží jen na programátorovi, které moduly ve svém projektu potřebuje a použije. Samotný překlad i slinkování se většinou provádí přes nástroj Cargo, který již známe, takže správa projektu a modulů, na nichž projekt závisí, je poměrně jednoduchá. Každý modul samozřejmě má svůj repositář, který je možné (pro naprostou většinu standardních modulů) nalézt na adrese https://github.com/PistonDevelopers. Povšimněte si, že zde nalezneme i repositář s dříve popsaným jazykem Dyon či knihovnu conrod, která si zaslouží vlastní článek; některé další repositáře obsahují různé utility apod.
3. Vykreslovací backendy
Navíc Piston pro vykreslování používá poměrně striktní oddělení frontendu od backendu, přičemž frontendem jsou v tomto kontextu myšleny především moduly piston a graphics, s jejichž využitím programátor řídí vykreslování a roli backendu přebírají moduly, které se starají o vytvoření okna, správu vstupních zařízení (klávesnice, myš, …) a nakonec i o vlastní rendering. Díky tomuto oddělení je například možné pro správu oken (vytvoření okna a nastavení grafického kontextu) použít knihovnu Glutin (naprogramovanou v Rustu), SDL2 (nativní C/C++), GLFW (nativní C/C++) atd., 2D a 3D grafiku vykreslovat buď přes OpenGL, DirectX či softwarově (na obrazovku či do rastrového obrázku) atd.
Poznámka: volba modulů pro backend může proběhnout jen na jediném místě – v projektovém souboru. To ostatně uvidíme i ve všech třech demonstračních příkladech, které si postupně popíšeme.
4. Vytvoření nového projektu používajícího moduly Pistonu
Ukažme si nyní, jakým způsobem je možné vytvořit projekt používající moduly knihovny Piston a jak se tento projekt následně přeloží a slinkuje. Není na tom nic složitého, protože tvorbu nového projektu (s využitím nástroje Cargo) již dobře známe:
cargo new --bin piston-demo1
Dále upravíme projektový soubor cargo.toml. Potřebujeme do něj doplnit sekci [dependencies] a přidat do ní pět modulů:
[package] name = "piston-demo1" version = "0.1.0" authors = ["Pavel Tisnovsky <ptisnovs@redhat.com>"] [dependencies] piston = "0.33.0" piston2d-graphics = "0.21.1" pistoncore-glutin_window = "0.39.0" piston2d-opengl_graphics = "0.46.0" piston_window = "0.70.0"
5. Stažení a překlad potřebných knihoven a modulů
Dále se pro jistotu přesvědčte, že používáte správnou verzi Rustu. V tomto případě lze použít i nightly verzi (na testovacím stroji mám starší verzi ze 7.8.2017):
rustup show Default host: x86_64-unknown-linux-gnu installed toolchains -------------------- stable-x86_64-unknown-linux-gnu nightly-x86_64-unknown-linux-gnu (default) active toolchain ---------------- nightly-x86_64-unknown-linux-gnu (default) rustc 1.21.0-nightly (cbbe17aa7 2017-08-07)
Následuje časově nejdelší krok, v němž se poprvé stáhnou všechny moduly ze sekce [dependencies] společně s (rekurzivně) závislými moduly a knihovnami. Připravte se na stažení přibližně 100 MB! Ihned po stažení se všechny moduly přeloží, což bude opět časově náročnější činnost (v závislosti na výkonnosti počítače až několik minut):
cargo build
Updating registry `https://github.com/rust-lang/crates.io-index` Downloading piston v0.33.0 Downloading pistoncore-glutin_window v0.39.0 Downloading piston2d-graphics v0.21.1 Downloading piston2d-opengl_graphics v0.46.0 Downloading pistoncore-input v0.19.0 Downloading pistoncore-window v0.28.0 Downloading pistoncore-event_loop v0.33.0 Downloading piston-viewport v0.3.0 Downloading piston-float v0.3.0 Downloading shader_version v0.2.2 Downloading glutin v0.9.2 Downloading gl v0.6.3 Downloading winit v0.7.6 Downloading libc v0.2.30 Downloading shared_library v0.1.7 Downloading gl_generator v0.5.5 Downloading xml-rs v0.6.1 Downloading khronos_api v1.0.1 Downloading interpolation v0.1.0 Downloading vecmath v0.3.0 Downloading piston-texture v0.5.0 Downloading fnv v1.0.5 Downloading rusttype v0.2.1 Downloading piston-shaders_graphics2d v0.3.1 Downloading image v0.15.0 Downloading stb_truetype v0.2.1 Downloading linked-hash-map v0.0.10 Downloading arrayvec v0.3.23 Downloading byteorder v0.4.2 Downloading nodrop v0.1.9 Downloading odds v0.2.25 Downloading jpeg-decoder v0.1.13 Downloading num-rational v0.1.39 Downloading scoped_threadpool v0.1.7 Downloading num-iter v0.1.34 Downloading gif v0.9.2 Downloading png v0.9.0 Downloading enum_primitive v0.1.1 Downloading byteorder v1.1.0 Downloading rayon v0.8.2 Downloading rayon-core v1.2.1 Downloading futures v0.1.15 Downloading coco v0.1.1 Downloading scopeguard v0.3.2 Downloading num-integer v0.1.35 Downloading color_quant v1.0.0 Downloading lzw v0.10.0 Downloading deflate v0.7.16 Downloading inflate v0.2.0 Downloading adler32 v1.0.2 Downloading x11-dl v2.15.0 Downloading wayland-client v0.9.9 Downloading osmesa-sys v0.1.2 Downloading pkg-config v0.3.9 Downloading tempfile v2.1.6 Downloading wayland-kbd v0.9.1 Downloading wayland-window v0.7.0 Downloading wayland-protocols v0.9.9 Downloading bitflags v0.7.0 Downloading memmap v0.4.0 Downloading dlib v0.3.1 Downloading wayland-sys v0.9.9 Downloading libloading v0.3.4 Downloading target_build_utils v0.3.1 Downloading phf v0.7.21 Downloading serde_json v0.9.10 Downloading phf_shared v0.7.21 Downloading siphasher v0.2.2 Downloading serde v0.9.15 Downloading dtoa v0.4.2 Downloading itoa v0.3.2 Downloading phf_codegen v0.7.21 Downloading phf_generator v0.7.21 Downloading wayland-scanner v0.9.9 Downloading xml-rs v0.3.6 Downloading fs2 v0.2.5 Downloading kernel32-sys v0.2.2 Downloading winapi v0.2.8 Downloading winapi-build v0.1.1 Compiling adler32 v1.0.2 Compiling siphasher v0.2.2 Compiling serde v1.0.11 Compiling linked-hash-map v0.0.10 Compiling log v0.3.8 Compiling rayon-core v1.2.1 Compiling piston-float v0.3.0 Compiling serde v0.9.15 Compiling winapi-build v0.1.1 Compiling unicode-xid v0.0.4 Compiling interpolation v0.1.0 Compiling fnv v1.0.5 Compiling scopeguard v0.3.2 Compiling piston-texture v0.5.0 Compiling num-traits v0.1.40 Compiling either v1.1.0 Compiling quote v0.3.15 Compiling odds v0.2.25 Compiling scoped_threadpool v0.1.7 Compiling read_color v0.1.0 Compiling shader_version v0.2.2 Compiling pkg-config v0.3.9 Compiling color_quant v1.0.0 Compiling futures v0.1.15 Compiling byteorder v0.4.2 Compiling byteorder v1.1.0 Compiling piston-shaders_graphics2d v0.3.1 Compiling lzw v0.10.0 Compiling bitflags v0.9.1 Compiling bitflags v0.7.0 Compiling inflate v0.2.0 Compiling itoa v0.3.2 Compiling dtoa v0.4.2 Compiling lazy_static v0.2.8 Compiling libc v0.2.30 Compiling winapi v0.2.8 Compiling khronos_api v1.0.1 Compiling phf_shared v0.7.21 Compiling piston-viewport v0.3.0 Compiling vecmath v0.3.0 Compiling synom v0.11.3 Compiling kernel32-sys v0.2.2 Compiling coco v0.1.1 Compiling enum_primitive v0.1.1 Compiling num-integer v0.1.35 Compiling nodrop v0.1.9 Compiling x11-dl v2.15.0 Compiling stb_truetype v0.2.1 Compiling deflate v0.7.16 Compiling xml-rs v0.6.1 Compiling xml-rs v0.3.6 Compiling gif v0.9.2 Compiling serde_json v0.9.10 Compiling num_cpus v1.6.2 Compiling shared_library v0.1.7 Compiling rand v0.3.16 Compiling phf v0.7.21 Compiling piston2d-graphics v0.21.1 Compiling syn v0.11.11 Compiling num-rational v0.1.39 Compiling num-iter v0.1.34 Compiling arrayvec v0.3.23 Compiling wayland-scanner v0.9.9 Compiling gl_generator v0.5.5 Compiling osmesa-sys v0.1.2 Compiling tempfile v2.1.6 Compiling phf_generator v0.7.21 Compiling png v0.9.0 Compiling rusttype v0.2.1 Compiling wayland-client v0.9.9 Compiling wayland-protocols v0.9.9 Compiling phf_codegen v0.7.21 Compiling fs2 v0.2.5 Compiling glutin v0.9.2 Compiling gl v0.6.3 Compiling serde_derive_internals v0.15.1 Compiling rayon v0.8.2 Compiling target_build_utils v0.3.1 Compiling memmap v0.4.0 Compiling serde_derive v1.0.11 Compiling jpeg-decoder v0.1.13 Compiling image v0.15.0 Compiling pistoncore-input v0.19.0 Compiling libloading v0.3.4 Compiling pistoncore-window v0.28.0 Compiling pistoncore-event_loop v0.33.0 Compiling piston v0.33.0 Compiling dlib v0.3.1 Compiling wayland-sys v0.9.9 Compiling wayland-kbd v0.9.1 Compiling wayland-window v0.7.0 Compiling winit v0.7.6 Compiling piston2d-opengl_graphics v0.46.0 Compiling pistoncore-glutin_window v0.39.0 Compiling piston-demo1 v0.1.0 (file:///home/tester/rust/projects/piston-demo1) Finished dev [unoptimized + debuginfo] target(s) in 139.55 secs
6. Práce s okny
Jedním ze základních úkolů každé aplikace, která používá knihovnu Piston, je vytvoření nového okna (to může být v případě potřeby zobrazeno přes celou obrazovku), zajištění takzvaného grafického kontextu pro toto okno a spuštění programové smyčky, v níž se zpracovávají události (event loop). Pro usnadnění vytváření okna a jeho konfigurace je určen balíček (crate) nazvaný jednoduše piston_window. Nové okno se vytvoří a nakonfiguruje v nejjednodušším případě takto:
let mut window: PistonWindow = WindowSettings::new("titulek okna", [horizontální_rozměr, vertikální_rozměr]) .build() .unwrap();
Povšimněte si poměrně typického „zřetězení“ volání metod/funkcí. Metoda build() vytvoří okno a vrátí hodnotu typu Result obsahující buď nové okno nebo chybovou zprávu. Metodou unwrap() pak ze struktury Result získáme objekt představující okno. V případě potřeby je možné na tomto místě použít pattern matching a vypsat případné chybové hlášení.
Ve výchozím nastavení se používá Gfx a backend Glutin. Pokud budeme potřebovat použít jiný backend, například knihovnu SDL2, postačuje nepatrná změna typu:
let window: PistonWindow<Sdl2Window> = WindowSettings::new("titulek okna", [horizontální_rozměr, vertikální_rozměr]) .build() .unwrap();
7. Vytvoření nového okna s jeho navázáním na OpenGL backend
Metodou WindowSettings:opengl() lze nastavit verzi OpenGL, která se použije pro vykreslování:
fn opengl(self, value: OpenGL) -> Self
Tato metoda vrací stejnou (i když samozřejmě modifikovanou) instanci struktury typu WindowSettings. To mj. znamená, že je možné volání této metody zřetězit s ostatními metodami, zejména s metodou WindowSettings:build (to jsme již viděli v předchozí kapitole):
fn create_window() -> PistonWindow { let opengl = OpenGL::V2_1; WindowSettings::new("piston-demo1", (400, 300)) .opengl(opengl) .build() .unwrap() }
Podporované verze OpenGL:
OpenGL::V2_0 |
OpenGL::V2_1 |
OpenGL::V3_0 |
OpenGL::V3_1 |
OpenGL::V3_2 |
OpenGL::V3_3 |
OpenGL::V4_0 |
OpenGL::V4_1 |
OpenGL::V4_2 |
OpenGL::V4_3 |
OpenGL::V4_4 |
OpenGL::V4_5 |
Samozřejmě je nutné použít takovou verzi OpenGL, která je podporována GPU a jejím ovladačem!
8. Smyčka událostí
Velmi důležitým konceptem, s nímž se v knihovně Piston (a samozřejmě nejenom zde) setkáme, je systém událostí (events). Při běhu aplikace totiž dochází ke vzniku různých událostí, které jsou vyvolány jak samotným systémem (časovač…), tak i uživatelem (stisk klávesy, posun kurzoru myši, …). Na tyto události může aplikace nějakým způsobem reagovat, typicky s použitím takzvaných callback funkcí. Nejdůležitější událostí, na níž musíme reagovat v každém případě, je požadavek překreslení okna aplikace. Programová smyčka, v níž se postupně načítají události z fronty událostí (events queue) může ve své nejjednodušší podobě vypadat následovně:
fn event_loop(mut window: PistonWindow) -> () { while let Some(event) = window.next() { on_event(&mut window, event) } }
Parametr typu PistonWindow musí být označen modifikátorem mut!
9. Překreslení 2D scény
Samotná reakce na žádost o překreslení scény je v idiomatickém kódu realizována uzávěrem, kterému se předají dva parametry – grafický kontext a struktura typu gfx_graphics::GfxGraphics, která slouží pro zadávání příkazů pro kreslení (tuto strukturu si můžeme představit jako jakýsi buffer příkazů, které jsou transformovány do volání funkcí OpenGL, SDL2 atd.). Nejjednodušší reakce na žádost o překreslení okna může vypadat takto:
fn on_event(window: &mut PistonWindow, event: Event) -> () { window.draw_2d(&event, |context, g2d| { clear([0.8, 1.0, 0.8, 1.0], g2d); }); }
Vidíme, že v uzávěru voláme jen funkci clear, která smaže celé okno barvou R=80%, G=100%, B=80%. Poslední hodnotou je průhlednost.
10. Hlavní funkce programu, překlad a spuštění aplikace
Zbývá nám pouze doplnit hlavní funkci programu, v níž se vytvoří nové okno a zavolá se obsluha smyčky událostí:
fn main() { let window: PistonWindow = create_window(); event_loop(window); }
Celý zdrojový kód dnešního prvního demonstračního příkladu vypadá následovně:
extern crate piston_window; use piston_window::*; fn create_window() -> PistonWindow { let opengl = OpenGL::V2_1; WindowSettings::new("piston-demo1", (400, 300)) .opengl(opengl) .build() .unwrap() } fn on_event(window: &mut PistonWindow, event: Event) -> () { window.draw_2d(&event, |context, g2d| { clear([0.8, 1.0, 0.8, 1.0], g2d); }); } fn event_loop(mut window: PistonWindow) -> () { while let Some(event) = window.next() { on_event(&mut window, event) } } fn main() { let window: PistonWindow = create_window(); event_loop(window); }
11. Nepatrná vylepšení: specifikace velikosti okna, uzavření okna klávesou Esc, odstranění varování při překladu
Předchozí program můžeme vylepšit, a to hned několika způsoby. Nejprve se podívejme na to, jakým způsobem je vlastně specifikována velikost okna. Při pohledu na předchozí demonstrační příklad by se mohlo zdát, že se jedná o jednoduchou n-tici (konkrétně dvojici) celočíselných hodnot představujících šířku a výšku okna (zde konkrétně 400×300 pixelů):
WindowSettings::new("piston-demo1", (400, 300)) .opengl(opengl) .build() .unwrap()
Ve skutečnosti je velikost okna představována datovou strukturou nazvanou Size:
pub struct Size { pub width: u32, pub height: u32, }
Důvod, proč bylo možné použít zápis (400, 300) je jednoduchý – tato struktura mj. implementuje i dva traity From<[u32; 2]> a From<(u32, u32)> s předepsanými konverzními funkcemi:
fn from(value: [u32; 2]) -> Size fn from(value: (u32, u32)) -> Size
Velikost okna však můžeme specifikovat i explicitně konstruktorem struktury Size:
let window_size: Size = Size { width: width, height: height, };
Další úprava spočívá v konfiguraci okna takovým způsobem, aby bylo možné okno zavřít klávesou Esc. K tomu není nutné psát event handler, ale postačí použít metodu exit_on_esc() pro datovou strukturu window::WindowSettings:
fn exit_on_esc(self, value: bool) -> Self
Povšimněte si, že tato metoda vrací modifikovanou strukturu s konfigurací okna, takže ji lze použít v řetězci nastavení:
fn create_window(width: u32, height: u32) -> PistonWindow { let opengl = OpenGL::V2_1; let window_size: Size = Size { width: width, height: height, }; WindowSettings::new("piston-demo2", window_size) .exit_on_esc(true) .opengl(opengl) .build() .unwrap() }
Poslední úprava zamezí zobrazení varování překladače o tom, že v anonymní funkci zavolané při požadavku na překreslení okna (prozatím) nevyužíváme první parametr. Pokud tento parametr přejmenujeme na _ (podtržítko), nebude varování vypisováno, protože podtržítko je považováno za zástupné jméno (placeholder), s čímž jsme se již ostatně několikrát setkali:
fn on_event(window: &mut PistonWindow, event: Event) -> () { window.draw_2d(&event, |_, g2d| { clear([0.8, 1.0, 0.8, 1.0], g2d); }); }
Úplný zdrojový kód dnešního druhého demonstračního příkladu, v němž byly provedeny všechny výše popsané změny, vypadá takto:
extern crate piston_window; use piston_window::*; fn create_window(width: u32, height: u32) -> PistonWindow { let opengl = OpenGL::V2_1; let window_size: Size = Size { width: width, height: height, }; WindowSettings::new("piston-demo2", window_size) .exit_on_esc(true) .opengl(opengl) .build() .unwrap() } fn on_event(window: &mut PistonWindow, event: Event) -> () { window.draw_2d(&event, |_, g2d| { clear([0.8, 1.0, 0.8, 1.0], g2d); }); } fn event_loop(mut window: PistonWindow) -> () { while let Some(event) = window.next() { on_event(&mut window, event) } } fn main() { let window: PistonWindow = create_window(400, 300); event_loop(window); }
12. Filtrování událostí
Metoda PistonWindow.next() vrací hodnotu (datovou strukturu) typu Option<Event>. Jedná se tedy o potenciálně prázdnou „obálku“ obsahující objekt typu Event, což je ve skutečnosti výčet, který může reprezentovat události spadající do tří skupin:
- Input – veškeré události vznikající činností uživatele
- Loop – periodicky se opakující události (idle, překreslení okna…)
- Další specifické, konfigurovatelné události (nebudeme je používat)
Do první skupiny řadíme události:
- Button
- Move
- Text
- Resize
- Focus
- Cursor
- Close
Do druhé skupiny pak události:
- Render
- AfterRender
- Update
- Idle
Jak lze však zjisti, zda událost, která je uložena v konkrétní instanci Option<Event>, odpovídá žádosti o překreslení okna? Můžeme využít toho, že je pro tuto událost traitem input::RenderEvent (http://docs.piston.rs/piston_window/src/input/render.rs.html#43–56) předepsána metoda render_args:
fn render_args(&self) -> Option<RenderArgs>
Pokud se skutečně jedná o žádost o překreslení okna, vrátí tato metoda hodnotu Some(RenderArgs), v opačném případě vrátí None. To znamená, že původní funkci:
fn event_loop(mut window: PistonWindow) -> () { while let Some(event) = window.next() { on_event(&mut window, event) } }
můžeme přepsat tak, aby se v ní testovalo, zda právě zpracovávaná událost vyžaduje překreslení okna aplikace, a to testem na Some:
fn event_loop(mut window: PistonWindow) -> () { while let Some(event) = window.next() { if let Some(_) = event.render_args() { on_event(&mut window, event) } } }
13. Grafický kontext
Grafický kontext je struktura nesoucí informaci o pohledové a transformační matici, o takzvaném viewportu (přes něj se zjednodušeně řečeno provádí mapování 2D souřadnic získaných po ortogonální či perspektivní projekci na adresy pixelů na obrazovce) i o aktuálně nastavených vykreslovacích režimech. Tato struktura vypadá následovně:
pub struct Context { pub viewport: Option<Viewport>, pub view: Matrix2d, pub transform: Matrix2d, pub draw_state: DrawState, }
S touto strukturou se prozatím setkáme při kreslení 2D objektů, protože u nich je zapotřebí přečíst transformační matici (Context.transform).
14. Vykreslení čtverce
Podívejme se nyní na to, jak je možné vykreslit nějaký jednoduchý 2D obrazec. Zcela nejjednodušší 2D entitou není v Pistonu kupodivu ani bod ani úsečka, ale obdélník nebo čtverec :-) Při vykreslování čtverce musíme především specifikovat jeho barvu, což je pole čtyř hodnot typu f32:
const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
Dále je nutné vytvořit strukturu obsahující pozici a rozměry čtverce. Povšimněte si, že souřadnice i rozměry jsou typu f64 a nikoli i32 či dokonce jen i16. Musíme totiž počítat s transformacemi:
const SQUARE_SIZE: f64 = 50.0; let size = window.window.size(); let width = size.width as f64; let height = size.height as f64; let x = (width - SQUARE_SIZE)/2.0; let y = (height - SQUARE_SIZE)/2.0; let square = rectangle::square(x, y, SQUARE_SIZE);
Vlastní vykreslení obdélníka či čtverce vyžaduje znalost transformační matice (získáme z grafického kontextu) a instanci G2d (tu získáme jako parametr vstupující do volaného uzávěru):
window.draw_2d(&event, |context, g2d| { clear([0.8, 1.0, 0.8, 1.0], g2d); g2d.clear_stencil(0); rectangle(RED, square, context.transform, g2d); });
Celá obsluha žádosti o překreslení okna může vypadat takto:
fn on_event(window: &mut PistonWindow, event: Event) -> () { const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0]; const SQUARE_SIZE: f64 = 50.0; let size = window.window.size(); let width = size.width as f64; let height = size.height as f64; let x = (width - SQUARE_SIZE)/2.0; let y = (height - SQUARE_SIZE)/2.0; let square = rectangle::square(x, y, SQUARE_SIZE); window.draw_2d(&event, |context, g2d| { clear([0.8, 1.0, 0.8, 1.0], g2d); g2d.clear_stencil(0); rectangle(RED, square, context.transform, g2d); }); }
15. Vykreslení úsečky
Při vykreslování úseček postupujeme podobně jako při vykreslování obdélníku či čtverce, ovšem musíme navíc specifikovat šířku úsečky. Struktura reprezentující geometrii úsečky je triviální – jedná se o pole čtyř hodnot typu f64 s obvyklým významem [x1, y2, x2, y1]. Vykreslení dvou úseček představujících úhlopříčky okna tedy bude vypadat takto:
fn on_event(window: &mut PistonWindow, event: Event) -> () { const BLUE: [f32; 4] = [0.0, 0.0, 1.0, 1.0]; const LINE_BORDER: f64 = 10.0; let size = window.window.size(); let width = size.width as f64; let height = size.height as f64; let x1 = LINE_BORDER; let y1 = LINE_BORDER; let x2 = width - LINE_BORDER; let y2 = height - LINE_BORDER; let line1:[f64;4] = [x1, y1, x2, y2]; let line2:[f64;4] = [x1, y2, x2, y1]; window.draw_2d(&event, |context, g2d| { clear([0.8, 1.0, 0.8, 1.0], g2d); g2d.clear_stencil(0); line(BLUE, 1., line1, context.transform, g2d); line(BLUE, 0.5, line2, context.transform, g2d); }); }
16. Úplný zdrojový kód třetího demonstračního příkladu
V předchozích dvou kapitolách jsme si ukazovali části kódu třetího příkladu, který vypadá následovně:
extern crate piston_window; use piston_window::*; fn create_window(width: u32, height: u32) -> PistonWindow { let opengl = OpenGL::V2_1; let window_size: Size = Size { width: width, height: height, }; WindowSettings::new("piston-demo3", window_size) .exit_on_esc(true) .opengl(opengl) .build() .unwrap() } fn on_event(window: &mut PistonWindow, event: Event) -> () { const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0]; const BLUE: [f32; 4] = [0.0, 0.0, 1.0, 1.0]; const SQUARE_SIZE: f64 = 50.0; const LINE_BORDER: f64 = 10.0; let size = window.window.size(); let width = size.width as f64; let height = size.height as f64; let x = (width - SQUARE_SIZE)/2.0; let y = (height - SQUARE_SIZE)/2.0; let square = rectangle::square(x, y, SQUARE_SIZE); let x1 = LINE_BORDER; let y1 = LINE_BORDER; let x2 = width - LINE_BORDER; let y2 = height - LINE_BORDER; let line1:[f64;4] = [x1, y1, x2, y2]; let line2:[f64;4] = [x1, y2, x2, y1]; window.draw_2d(&event, |context, g2d| { clear([0.8, 1.0, 0.8, 1.0], g2d); g2d.clear_stencil(0); line(BLUE, 1., line1, context.transform, g2d); line(BLUE, 0.5, line2, context.transform, g2d); rectangle(RED, square, context.transform, g2d); }); } fn event_loop(mut window: PistonWindow) -> () { while let Some(event) = window.next() { if let Some(_) = event.render_args() { on_event(&mut window, event) } } } fn main() { let window: PistonWindow = create_window(400, 300); event_loop(window); }
17. Dnes popsané struktury a traity
V této kapitole jsou uvedeny odkazy na dokumentaci ke všem dnes popsaným strukturám a traitům, které patří do knihovny Piston:
Struktura/Trait | Dokumentace |
---|---|
Struct window::WindowSettings | http://docs.piston.rs/piston_window/window/struct.WindowSettings.html |
Struct window::Size | http://docs.piston.rs/piston_window/window/struct.Size.html |
Struct piston_window::PistonWindow | http://docs.piston.rs/piston_window/piston_window/struct.PistonWindow.html |
Trait window::OpenGLWindow | http://docs.piston.rs/piston_window/window/trait.OpenGLWindow.html |
Trait window::BuildFromWindowSettings | http://docs.piston.rs/piston_window/window/trait.BuildFromWindowSettings.html |
Trait graphics::Graphics | http://docs.piston.rs/piston_window/graphics/trait.Graphics.html |
Trait input::RenderEvent | http://docs.piston.rs/piston_window/input/trait.RenderEvent.html |
Enum shader_version::opengl::OpenGL | http://docs.piston.rs/piston_window/shader_version/opengl/enum.OpenGL.html |
18. Repositář s demonstračními příklady
Všechny dnes popisované demonstrační příklady (Rustovské projekty) byly, ostatně podobně jako ve všech předchozích částech tohoto seriálu, uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/presentations. Demonstrační příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý repositář (ovšem u projektů je lepší mít celý repositář, abyste nemuseli pracně stahovat všechny potřebné soubory):
Příklad | Adresa |
---|---|
První projekt | piston-demo1 |
Cargo.toml | https://github.com/tisnik/presentations/blob/master/rust/projects/piston-demo1/Cargo.toml |
main.rs | https://github.com/tisnik/presentations/blob/master/rust/projects/piston-demo1/src/main.rs |
Druhý projekt | piston-demo2 |
Cargo.toml | https://github.com/tisnik/presentations/blob/master/rust/projects/piston-demo2/Cargo.toml |
main.rs | https://github.com/tisnik/presentations/blob/master/rust/projects/piston-demo2/src/main.rs |
Třetí projekt | piston-demo3 |
Cargo.toml | https://github.com/tisnik/presentations/blob/master/rust/projects/piston-demo3/Cargo.toml |
main.rs | https://github.com/tisnik/presentations/blob/master/rust/projects/piston-demo3/src/main.rs |
19. Odkazy na Internetu
- Secrets design – optional information in
bool
andf64
https://github.com/PistonDevelopers/dyon/issues/266 - Option value design
https://github.com/PistonDevelopers/dyon/issues/172 - Result value design (
err(x)/ok(x)
)
https://github.com/PistonDevelopers/dyon/issues/82 - ∑/sum, ∏/prod, min, max, sift, ∃/any, ∀/all loops
https://github.com/PistonDevelopers/dyon/issues/119 - Dyon: tutoriál
http://www.piston.rs/dyon-tutorial/ - Repositář s programovacím jazykem Dyon
https://github.com/PistonDevelopers/dyon - Dyon: A rusty dynamically typed scripting language
https://rust.libhunt.com/project/dyon - Dyon snippets
https://github.com/PistonDevelopers/dyon_snippets - Scripting without garbage collector
http://blog.piston.rs/2016/02/21/scripting-without-garbage-collector/ - Podpora pro „matematické“ smyčky
https://github.com/PistonDevelopers/dyon/issues/119 - Rust-clippy Wiki
https://github.com/rust-lang-nursery/rust-clippy/wiki - Rust-clippy
https://rust.libhunt.com/project/rust-clippy - ndarray – dokumentace k modulu
https://bluss.github.io/rust-ndarray/master/ndarray/index.html - ndarray – Crate
https://crates.io/crates/ndarray - rustup
https://www.rustup.rs/ - rustup: the Rust toolchain installer (Git repositář + dokumentace)
https://github.com/rust-lang-nursery/rustup.rs - The Rust FFI Omnibus
http://jakegoulding.com/rust-ffi-omnibus/ - Build Script Support
http://doc.crates.io/build-script.html - Calling Rust From Python
https://bheisler.github.io/post/calling-rust-in-python/ - Calling Rust in Python (komentáře k předchozímu článku)
https://www.reddit.com/r/rust/comments/63iy5a/calling_rust_in_python/ - CFFI Documentation
https://cffi.readthedocs.io/en/latest/ - Build Script Support
http://doc.crates.io/build-script.html - Creating a shared and static library with the gnu compiler [gcc]
http://www.adp-gmbh.ch/cpp/gcc/create_lib.html - ctypes — A foreign function library for Python
https://docs.python.org/2/library/ctypes.html - FFI: Foreign Function Interface
https://doc.rust-lang.org/book/ffi.html - Primitive Type pointer
https://doc.rust-lang.org/std/primitive.pointer.html - Cargo: správce projektů a balíčků pro programovací jazyk Rust
https://mojefedora.cz/cargo-spravce-projektu-a-balicku-pro-programovaci-jazyk-rust/ - Network Communication and Serialization in Rust
https://www.safaribooksonline.com/blog/2014/01/28/network-communication-serialization-rust/ - Crate bincode
http://tyoverby.com/bincode/bincode/index.html - Struct std::fs::File
https://doc.rust-lang.org/std/fs/struct.File.html - Trait std::io::Seek
https://doc.rust-lang.org/std/io/trait.Seek.html - Trait std::io::Read
https://doc.rust-lang.org/std/io/trait.Read.html - Trait std::io::Write
https://doc.rust-lang.org/std/io/trait.Write.html - Trait std::io::BufRead
https://doc.rust-lang.org/std/io/trait.BufRead.html - Module std::io::prelude
https://doc.rust-lang.org/std/io/prelude/index.html - std::net::IpAddr
https://doc.rust-lang.org/std/net/enum.IpAddr.html - std::net::Ipv4Addr
https://doc.rust-lang.org/std/net/struct.Ipv4Addr.html - std::net::Ipv6Addr
https://doc.rust-lang.org/std/net/struct.Ipv6Addr.html - TcpListener
https://doc.rust-lang.org/std/net/struct.TcpListener.html - TcpStream
https://doc.rust-lang.org/std/net/struct.TcpStream.html - Binary heap (Wikipedia)
https://en.wikipedia.org/wiki/Binary_heap - Binární halda (Wikipedia)
https://cs.wikipedia.org/wiki/Bin%C3%A1rn%C3%AD_halda - Halda (datová struktura)
https://cs.wikipedia.org/wiki/Halda_%28datov%C3%A1_struktura%29 - Struct std::collections::HashSet
https://doc.rust-lang.org/std/collections/struct.HashSet.html - Struct std::collections::BTreeSet
https://doc.rust-lang.org/std/collections/struct.BTreeSet.html - Struct std::collections::BinaryHeap
https://doc.rust-lang.org/std/collections/struct.BinaryHeap.html - Set (abstract data type)
https://en.wikipedia.org/wiki/Set_%28abstract_data_type%29#Language_support - Associative array
https://en.wikipedia.org/wiki/Associative_array - Hash Table
https://en.wikipedia.org/wiki/Hash_table - B-tree
https://en.wikipedia.org/wiki/B-tree - Pedro Celis: Robin Hood Hashing (naskenované PDF!)
https://cs.uwaterloo.ca/research/tr/1986/CS-86–14.pdf - Robin Hood hashing
http://codecapsule.com/2013/11/11/robin-hood-hashing/ - Robin Hood hashing: backward shift deletion
http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/ - Module std::collections
https://doc.rust-lang.org/std/collections/ - Module std::vec
https://doc.rust-lang.org/nightly/std/vec/index.html - Struct std::collections::VecDeque
https://doc.rust-lang.org/std/collections/struct.VecDeque.html - Struct std::collections::LinkedList
https://doc.rust-lang.org/std/collections/struct.LinkedList.html - Module std::fmt
https://doc.rust-lang.org/std/fmt/ - Macro std::println
https://doc.rust-lang.org/std/macro.println.html - Enum std::result::Result
https://doc.rust-lang.org/std/result/enum.Result.html - Module std::result
https://doc.rust-lang.org/std/result/ - Result
http://rustbyexample.com/std/result.html - Rust stdlib: Option
https://doc.rust-lang.org/std/option/enum.Option.html - Module std::option
https://doc.rust-lang.org/std/option/index.html - Rust by example: option
http://rustbyexample.com/std/option.html - Rust by example: if-let
http://rustbyexample.com/flow_control/if_let.html - Rust by example: while let
http://rustbyexample.com/flow_control/while_let.html - Rust by example: Option<i32>
http://rustbyexample.com/std/option.html - An Overview of Macros in Rust
http://words.steveklabnik.com/an-overview-of-macros-in-rust - A Practical Intro to Macros in Rust 1.0
https://danielkeep.github.io/practical-intro-to-macros.html - The Rust Programming Language: macros
https://doc.rust-lang.org/beta/book/macros.html - Rust by example: 15 macro_rules!
http://rustbyexample.com/macros.html - Primitive Type isize
https://doc.rust-lang.org/nightly/std/primitive.isize.html - Primitive Type usize
https://doc.rust-lang.org/nightly/std/primitive.usize.html - Primitive Type array
https://doc.rust-lang.org/nightly/std/primitive.array.html - Module std::slice
https://doc.rust-lang.org/nightly/std/slice/ - Rust by Example: 2.3 Arrays and Slices
http://rustbyexample.com/primitives/array.html - What is the difference between Slice and Array (stackoverflow)
http://stackoverflow.com/questions/30794235/what-is-the-difference-between-slice-and-array - Learning Rust With Entirely Too Many Linked Lists
http://cglab.ca/~abeinges/blah/too-many-lists/book/ - Testcase: linked list
http://rustbyexample.com/custom_types/enum/testcase_linked_list.html - Operators and Overloading
https://doc.rust-lang.org/book/operators-and-overloading.html - Module std::ops
https://doc.rust-lang.org/std/ops/index.html - Module std::cmp
https://doc.rust-lang.org/std/cmp/index.html - Trait std::ops::Add
https://doc.rust-lang.org/stable/std/ops/trait.Add.html - Trait std::ops::AddAssign
https://doc.rust-lang.org/std/ops/trait.AddAssign.html - Trait std::ops::Drop
https://doc.rust-lang.org/std/ops/trait.Drop.html - Trait std::cmp::Eq
https://doc.rust-lang.org/std/cmp/trait.Eq.html - Struct std::boxed::Box
https://doc.rust-lang.org/std/boxed/struct.Box.html - Explore the ownership system in Rust
https://nercury.github.io/rust/guide/2015/01/19/ownership.html - Rust's ownership and move semantic
http://www.slideshare.net/saneyuki/rusts-ownership-and-move-semantics - Trait std::marker::Copy
https://doc.rust-lang.org/stable/std/marker/trait.Copy.html - Trait std::clone::Clone
https://doc.rust-lang.org/stable/std/clone/trait.Clone.html - The Stack and the Heap
https://doc.rust-lang.org/book/the-stack-and-the-heap.html - Rust Compare: Pointers & References
http://www.rust-compare.com/site/pointers.html - Rust Compare: Parameters
http://www.rust-compare.com/site/params.html - Why does this compile? Automatic dereferencing?
https://users.rust-lang.org/t/why-does-this-compile-automatic-dereferencing/2183 - Understanding Pointers, Ownership, and Lifetimes in Rust
http://koerbitz.me/posts/Understanding-Pointers-Ownership-and-Lifetimes-in-Rust.html - Rust lang series episode #25 — pointers (#rust-series)
https://steemit.com/rust-series/@jimmco/rust-lang-series-episode-25-pointers-rust-series - Rust – home page
https://www.rust-lang.org/en-US/ - Rust – Frequently Asked Questions
https://www.rust-lang.org/en-US/faq.html - Destructuring and Pattern Matching
https://pzol.github.io/getting_rusty/posts/20140417_destructuring_in_rust/ - The Rust Programming Language
https://doc.rust-lang.org/book/ - Rust (programming language)
https://en.wikipedia.org/wiki/Rust_%28programming_language%29 - Go – home page
https://golang.org/ - Stack Overflow – Most Loved, Dreaded, and Wanted language
https://stackoverflow.com/research/developer-survey-2016#technology-most-loved-dreaded-and-wanted - Rust vs Go (dva roky staré hodnocení, od té doby došlo k posunům v obou jazycích)
http://jaredforsyth.com/2014/03/22/rust-vs-go/ - Rust vs Go: My experience
https://www.reddit.com/r/golang/comments/21m6jq/rust_vs_go_my_experience/ - Friends of Rust (Organizations running Rust in production)
https://www.rust-lang.org/en-US/friends.html - Rust programs versus C++ g++
https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=rust&lang2=gpp - Další benchmarky (nejedná se o reálné příklady „ze života“)
https://github.com/kostya/benchmarks - Go na Redditu
https://www.reddit.com/r/golang/ - Rust vs. Go
http://vschart.com/compare/rust/vs/go-language - Abstraction without overhead: traits in Rust
https://blog.rust-lang.org/2015/05/11/traits.html - Method Syntax
https://doc.rust-lang.org/book/method-syntax.html - Traits in Rust
https://doc.rust-lang.org/book/traits.html - Functional Programming in Rust – Part 1 : Function Abstraction
http://blog.madhukaraphatak.com/functional-programming-in-rust-part-1/ - Of the emerging systems languages Rust, D, Go and Nim, which is the strongest language and why?
https://www.quora.com/Of-the-emerging-systems-languages-Rust-D-Go-and-Nim-which-is-the-strongest-language-and-why - Chytré ukazatele (moderní verze jazyka C++) [MSDN]
https://msdn.microsoft.com/cs-cz/library/hh279674.aspx - UTF-8 Everywhere
http://utf8everywhere.org/ - Rust by Example
http://rustbyexample.com/ - Rust oficiálně ve Fedoře
https://mojefedora.cz/rust-oficialne-ve-fedore/ - Resource acquisition is initialization
https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization - TIOBE index (October 2016)
http://www.tiobe.com/tiobe-index/ - Porovnání Go, D a Rustu na OpenHubu:
https://www.openhub.net/languages/compare?language_name[]=-1&language_name[]=-1&language_name[]=dmd&language_name[]=golang&language_name[]=rust&language_name[]=-1&measure=commits - String Types in Rust
http://www.suspectsemantics.com/blog/2016/03/27/string-types-in-rust/ - Trait (computer programming)
https://en.wikipedia.org/wiki/Trait_%28computer_programming%29 - Type inference
https://en.wikipedia.org/wiki/Type_inference