Hlavní navigace

Piston – knihovna pro práci s 2D i 3D grafikou v Rustu

31. 8. 2017
Doba čtení: 26 minut

Sdílet

 Autor: Rust project
V dalším článku o jazyku Rust se seznámíme se základními koncepty, na nichž je postavena knihovna Piston. Ta umožňuje práci s 2D i 3D grafikou s využitím různých „backendů“, mezi něž samozřejmě patří i moderní OpenGL.

Obsah

1. Piston – knihovna pro práci s 2D i 3D grafikou v Rustu

2. Koncepty, na nichž je knihovna Piston postavena

3. Vykreslovací backendy

4. Vytvoření nového projektu používajícího moduly Pistonu

5. Stažení a překlad potřebných knihoven a modulů

6. Práce s okny

7. Vytvoření nového okna s jeho navázáním na OpenGL backend

8. Smyčka událostí

9. Překreslení 2D scény

10. Hlavní funkce programu, překlad a spuštění aplikace

11. Nepatrná vylepšení: specifikace velikosti okna, uzavření okna klávesou Esc, odstranění varování při překlad

12. Filtrování událostí

13. Grafický kontext

14. Vykreslení čtverce

15. Vykreslení úsečky

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

19. Odkazy na Internetu

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:

  1. Input – veškeré události vznikající činností uživatele
  2. Loop – periodicky se opakující události (idle, překreslení okna…)
  3. Další specifické, konfigurovatelné události (nebudeme je používat)

Do první skupiny řadíme události:

  1. Button
  2. Move
  3. Text
  4. Resize
  5. Focus
  6. Cursor
  7. Close

Do druhé skupiny pak události:

  1. Render
  2. AfterRender
  3. Update
  4. 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/pis­ton_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ě:

CS24_early

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:

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/pre­sentations. 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):

19. Odkazy na Internetu

  1. Secrets design – optional information in bool and f64
    https://github.com/Piston­Developers/dyon/issues/266
  2. Option value design
    https://github.com/Piston­Developers/dyon/issues/172
  3. Result value design ( err(x)/ok(x))
    https://github.com/Piston­Developers/dyon/issues/82
  4. ∑/sum, ∏/prod, min, max, sift, ∃/any, ∀/all loops
    https://github.com/Piston­Developers/dyon/issues/119
  5. Dyon: tutoriál
    http://www.piston.rs/dyon-tutorial/
  6. Repositář s programovacím jazykem Dyon
    https://github.com/Piston­Developers/dyon
  7. Dyon: A rusty dynamically typed scripting language
    https://rust.libhunt.com/project/dyon
  8. Dyon snippets
    https://github.com/Piston­Developers/dyon_snippets
  9. Scripting without garbage collector
    http://blog.piston.rs/2016/02/21/scrip­ting-without-garbage-collector/
  10. Podpora pro „matematické“ smyčky
    https://github.com/Piston­Developers/dyon/issues/119
  11. Rust-clippy Wiki
    https://github.com/rust-lang-nursery/rust-clippy/wiki
  12. Rust-clippy
    https://rust.libhunt.com/project/rust-clippy
  13. ndarray – dokumentace k modulu
    https://bluss.github.io/rust-ndarray/master/ndarray/index.html
  14. ndarray – Crate
    https://crates.io/crates/ndarray
  15. rustup
    https://www.rustup.rs/
  16. rustup: the Rust toolchain installer (Git repositář + dokumentace)
    https://github.com/rust-lang-nursery/rustup.rs
  17. The Rust FFI Omnibus
    http://jakegoulding.com/rust-ffi-omnibus/
  18. Build Script Support
    http://doc.crates.io/build-script.html
  19. Calling Rust From Python
    https://bheisler.github.i­o/post/calling-rust-in-python/
  20. Calling Rust in Python (komentáře k předchozímu článku)
    https://www.reddit.com/r/rus­t/comments/63iy5a/calling_rus­t_in_python/
  21. CFFI Documentation
    https://cffi.readthedocs.i­o/en/latest/
  22. Build Script Support
    http://doc.crates.io/build-script.html
  23. Creating a shared and static library with the gnu compiler [gcc]
    http://www.adp-gmbh.ch/cpp/gcc/create_lib.html
  24. ctypes — A foreign function library for Python
    https://docs.python.org/2/li­brary/ctypes.html
  25. FFI: Foreign Function Interface
    https://doc.rust-lang.org/book/ffi.html
  26. Primitive Type pointer
    https://doc.rust-lang.org/std/primitive.pointer.html
  27. Cargo: správce projektů a balíčků pro programovací jazyk Rust
    https://mojefedora.cz/cargo-spravce-projektu-a-balicku-pro-programovaci-jazyk-rust/
  28. Network Communication and Serialization in Rust
    https://www.safaribookson­line.com/blog/2014/01/28/net­work-communication-serialization-rust/
  29. Crate bincode
    http://tyoverby.com/binco­de/bincode/index.html
  30. Struct std::fs::File
    https://doc.rust-lang.org/std/fs/struct.File.html
  31. Trait std::io::Seek
    https://doc.rust-lang.org/std/io/trait.Seek.html
  32. Trait std::io::Read
    https://doc.rust-lang.org/std/io/trait.Read.html
  33. Trait std::io::Write
    https://doc.rust-lang.org/std/io/trait.Write.html
  34. Trait std::io::BufRead
    https://doc.rust-lang.org/std/io/trait.BufRead.html
  35. Module std::io::prelude
    https://doc.rust-lang.org/std/io/prelude/index.html
  36. std::net::IpAddr
    https://doc.rust-lang.org/std/net/enum.IpAddr.html
  37. std::net::Ipv4Addr
    https://doc.rust-lang.org/std/net/struct.Ipv4Addr.html
  38. std::net::Ipv6Addr
    https://doc.rust-lang.org/std/net/struct.Ipv6Addr.html
  39. TcpListener
    https://doc.rust-lang.org/std/net/struct.TcpLis­tener.html
  40. TcpStream
    https://doc.rust-lang.org/std/net/struct.TcpStream.html
  41. Binary heap (Wikipedia)
    https://en.wikipedia.org/wi­ki/Binary_heap
  42. Binární halda (Wikipedia)
    https://cs.wikipedia.org/wi­ki/Bin%C3%A1rn%C3%AD_halda
  43. Halda (datová struktura)
    https://cs.wikipedia.org/wi­ki/Halda_%28datov%C3%A1_struk­tura%29
  44. Struct std::collections::HashSet
    https://doc.rust-lang.org/std/collections/struc­t.HashSet.html
  45. Struct std::collections::BTreeSet
    https://doc.rust-lang.org/std/collections/struc­t.BTreeSet.html
  46. Struct std::collections::BinaryHeap
    https://doc.rust-lang.org/std/collections/struc­t.BinaryHeap.html
  47. Set (abstract data type)
    https://en.wikipedia.org/wi­ki/Set_%28abstract_data_ty­pe%29#Language_support
  48. Associative array
    https://en.wikipedia.org/wi­ki/Associative_array
  49. Hash Table
    https://en.wikipedia.org/wi­ki/Hash_table
  50. B-tree
    https://en.wikipedia.org/wiki/B-tree
  51. Pedro Celis: Robin Hood Hashing (naskenované PDF!)
    https://cs.uwaterloo.ca/re­search/tr/1986/CS-86–14.pdf
  52. Robin Hood hashing
    http://codecapsule.com/2013/11/11/ro­bin-hood-hashing/
  53. Robin Hood hashing: backward shift deletion
    http://codecapsule.com/2013/11/17/ro­bin-hood-hashing-backward-shift-deletion/
  54. Module std::collections
    https://doc.rust-lang.org/std/collections/
  55. Module std::vec
    https://doc.rust-lang.org/nightly/std/vec/index.html
  56. Struct std::collections::VecDeque
    https://doc.rust-lang.org/std/collections/struc­t.VecDeque.html
  57. Struct std::collections::LinkedList
    https://doc.rust-lang.org/std/collections/struc­t.LinkedList.html
  58. Module std::fmt
    https://doc.rust-lang.org/std/fmt/
  59. Macro std::println
    https://doc.rust-lang.org/std/macro.println.html
  60. Enum std::result::Result
    https://doc.rust-lang.org/std/result/enum.Result.html
  61. Module std::result
    https://doc.rust-lang.org/std/result/
  62. Result
    http://rustbyexample.com/std/re­sult.html
  63. Rust stdlib: Option
    https://doc.rust-lang.org/std/option/enum.Option.html
  64. Module std::option
    https://doc.rust-lang.org/std/option/index.html
  65. Rust by example: option
    http://rustbyexample.com/std/op­tion.html
  66. Rust by example: if-let
    http://rustbyexample.com/flow_con­trol/if_let.html
  67. Rust by example: while let
    http://rustbyexample.com/flow_con­trol/while_let.html
  68. Rust by example: Option<i32>
    http://rustbyexample.com/std/op­tion.html
  69. An Overview of Macros in Rust
    http://words.steveklabnik.com/an-overview-of-macros-in-rust
  70. A Practical Intro to Macros in Rust 1.0
    https://danielkeep.github.io/practical-intro-to-macros.html
  71. The Rust Programming Language: macros
    https://doc.rust-lang.org/beta/book/macros.html
  72. Rust by example: 15 macro_rules!
    http://rustbyexample.com/macros.html
  73. Primitive Type isize
    https://doc.rust-lang.org/nightly/std/primi­tive.isize.html
  74. Primitive Type usize
    https://doc.rust-lang.org/nightly/std/primi­tive.usize.html
  75. Primitive Type array
    https://doc.rust-lang.org/nightly/std/primi­tive.array.html
  76. Module std::slice
    https://doc.rust-lang.org/nightly/std/slice/
  77. Rust by Example: 2.3 Arrays and Slices
    http://rustbyexample.com/pri­mitives/array.html
  78. What is the difference between Slice and Array (stackoverflow)
    http://stackoverflow.com/qu­estions/30794235/what-is-the-difference-between-slice-and-array
  79. Learning Rust With Entirely Too Many Linked Lists
    http://cglab.ca/~abeinges/blah/too-many-lists/book/
  80. Testcase: linked list
    http://rustbyexample.com/cus­tom_types/enum/testcase_lin­ked_list.html
  81. Operators and Overloading
    https://doc.rust-lang.org/book/operators-and-overloading.html
  82. Module std::ops
    https://doc.rust-lang.org/std/ops/index.html
  83. Module std::cmp
    https://doc.rust-lang.org/std/cmp/index.html
  84. Trait std::ops::Add
    https://doc.rust-lang.org/stable/std/ops/trait.Add.html
  85. Trait std::ops::AddAssign
    https://doc.rust-lang.org/std/ops/trait.AddAssign.html
  86. Trait std::ops::Drop
    https://doc.rust-lang.org/std/ops/trait.Drop.html
  87. Trait std::cmp::Eq
    https://doc.rust-lang.org/std/cmp/trait.Eq.html
  88. Struct std::boxed::Box
    https://doc.rust-lang.org/std/boxed/struct.Box.html
  89. Explore the ownership system in Rust
    https://nercury.github.io/rus­t/guide/2015/01/19/ownership­.html
  90. Rust's ownership and move semantic
    http://www.slideshare.net/sa­neyuki/rusts-ownership-and-move-semantics
  91. Trait std::marker::Copy
    https://doc.rust-lang.org/stable/std/marker/tra­it.Copy.html
  92. Trait std::clone::Clone
    https://doc.rust-lang.org/stable/std/clone/tra­it.Clone.html
  93. The Stack and the Heap
    https://doc.rust-lang.org/book/the-stack-and-the-heap.html
  94. Rust Compare: Pointers & References
    http://www.rust-compare.com/site/pointers.html
  95. Rust Compare: Parameters
    http://www.rust-compare.com/site/params.html
  96. Why does this compile? Automatic dereferencing?
    https://users.rust-lang.org/t/why-does-this-compile-automatic-dereferencing/2183
  97. Understanding Pointers, Ownership, and Lifetimes in Rust
    http://koerbitz.me/posts/Understanding-Pointers-Ownership-and-Lifetimes-in-Rust.html
  98. Rust lang series episode #25 — pointers (#rust-series)
    https://steemit.com/rust-series/@jimmco/rust-lang-series-episode-25-pointers-rust-series
  99. Rust – home page
    https://www.rust-lang.org/en-US/
  100. Rust – Frequently Asked Questions
    https://www.rust-lang.org/en-US/faq.html
  101. Destructuring and Pattern Matching
    https://pzol.github.io/get­ting_rusty/posts/20140417_des­tructuring_in_rust/
  102. The Rust Programming Language
    https://doc.rust-lang.org/book/
  103. Rust (programming language)
    https://en.wikipedia.org/wi­ki/Rust_%28programming_lan­guage%29
  104. Go – home page
    https://golang.org/
  105. Stack Overflow – Most Loved, Dreaded, and Wanted language
    https://stackoverflow.com/re­search/developer-survey-2016#technology-most-loved-dreaded-and-wanted
  106. 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/
  107. Rust vs Go: My experience
    https://www.reddit.com/r/go­lang/comments/21m6jq/rust_vs_go_my_ex­perience/
  108. Friends of Rust (Organizations running Rust in production)
    https://www.rust-lang.org/en-US/friends.html
  109. Rust programs versus C++ g++
    https://benchmarksgame.ali­oth.debian.org/u64q/compa­re.php?lang=rust&lang2=gpp
  110. Další benchmarky (nejedná se o reálné příklady „ze života“)
    https://github.com/kostya/benchmarks
  111. Go na Redditu
    https://www.reddit.com/r/golang/
  112. Rust vs. Go
    http://vschart.com/compare/rust/vs/go-language
  113. Abstraction without overhead: traits in Rust
    https://blog.rust-lang.org/2015/05/11/traits.html
  114. Method Syntax
    https://doc.rust-lang.org/book/method-syntax.html
  115. Traits in Rust
    https://doc.rust-lang.org/book/traits.html
  116. Functional Programming in Rust – Part 1 : Function Abstraction
    http://blog.madhukaraphatak­.com/functional-programming-in-rust-part-1/
  117. 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
  118. Chytré ukazatele (moderní verze jazyka C++) [MSDN]
    https://msdn.microsoft.com/cs-cz/library/hh279674.aspx
  119. UTF-8 Everywhere
    http://utf8everywhere.org/
  120. Rust by Example
    http://rustbyexample.com/
  121. Rust oficiálně ve Fedoře
    https://mojefedora.cz/rust-oficialne-ve-fedore/
  122. Resource acquisition is initialization
    https://en.wikipedia.org/wi­ki/Resource_acquisition_is_i­nitialization
  123. TIOBE index (October 2016)
    http://www.tiobe.com/tiobe-index/
  124. Porovnání Go, D a Rustu na OpenHubu:
    https://www.openhub.net/lan­guages/compare?language_na­me[]=-1&language_name[]=-1&language_name[]=dmd&lan­guage_name[]=golang&langu­age_name[]=rust&language_na­me[]=-1&measure=commits
  125. String Types in Rust
    http://www.suspectsemantic­s.com/blog/2016/03/27/str­ing-types-in-rust/
  126. Trait (computer programming)
    https://en.wikipedia.org/wi­ki/Trait_%28computer_program­ming%29
  127. Type inference
    https://en.wikipedia.org/wi­ki/Type_inference

Byl pro vás článek přínosný?

Autor článku

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