├── sudoku ├── .gitignore ├── images │ ├── docs.png │ ├── grid.png │ ├── highlight-cell.png │ └── fill-in-numbers.png ├── assets │ ├── FiraSans-Regular.ttf │ └── LICENSE ├── Cargo.toml ├── src │ ├── gameboard.rs │ ├── main.rs │ ├── gameboard_controller.rs │ └── gameboard_view.rs ├── README.md ├── chp-04.md ├── chp-06.md ├── chp-03.md ├── chp-02.md ├── chp-05.md └── Cargo.lock ├── getting-started ├── .gitignore ├── out.gif ├── Cargo.toml ├── src │ └── main.rs ├── readme.dev.md └── readme.md ├── .gitignore ├── roguelike ├── assets │ └── FiraSans-Regular.ttf ├── Cargo.toml ├── solutions │ ├── Chapter-1-Solution.rs │ ├── Chapter-2-Solution.rs │ └── Chapter-3-Solution.rs ├── README.md ├── chapters │ ├── Chapter_3-Player.md │ ├── Chapter_2-World-Map.md │ └── Chapter_1-Graphics.md └── src │ └── main.rs ├── Cargo.toml ├── .travis.yml ├── tutorial_util └── main.rs ├── README.md └── advanced ├── events.md └── writing-idiomatic-code.md /sudoku/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /getting-started/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | */target/ 2 | */Cargo.lock 3 | /target/ 4 | Cargo.lock 5 | 6 | -------------------------------------------------------------------------------- /sudoku/images/docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmahmood/Piston-Tutorials/master/sudoku/images/docs.png -------------------------------------------------------------------------------- /sudoku/images/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmahmood/Piston-Tutorials/master/sudoku/images/grid.png -------------------------------------------------------------------------------- /getting-started/out.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmahmood/Piston-Tutorials/master/getting-started/out.gif -------------------------------------------------------------------------------- /sudoku/images/highlight-cell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmahmood/Piston-Tutorials/master/sudoku/images/highlight-cell.png -------------------------------------------------------------------------------- /sudoku/assets/FiraSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmahmood/Piston-Tutorials/master/sudoku/assets/FiraSans-Regular.ttf -------------------------------------------------------------------------------- /sudoku/images/fill-in-numbers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmahmood/Piston-Tutorials/master/sudoku/images/fill-in-numbers.png -------------------------------------------------------------------------------- /roguelike/assets/FiraSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmahmood/Piston-Tutorials/master/roguelike/assets/FiraSans-Regular.ttf -------------------------------------------------------------------------------- /sudoku/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Sven Nilsen "] 3 | name = "sudoku" 4 | version = "0.1.0" 5 | [[bin]] 6 | name = "sudoku" 7 | 8 | [dependencies] 9 | piston = "*" 10 | piston2d-graphics = "*" 11 | piston2d-opengl_graphics = "*" 12 | pistoncore-glutin_window = "*" 13 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "piston_tutorials" 3 | version = "0.0.1" 4 | authors = [ 5 | "TyOverby ", 6 | "Indiv0 ", 7 | "maxsnew" 8 | ] 9 | 10 | [dependencies.md_rel] 11 | git = "https://github.com/TyOverby/md_rel.git" 12 | 13 | [[bin]] 14 | name = "markdown_compiler" 15 | path = "tutorial_util/main.rs" 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | notifications: 3 | irc: "irc.mozilla.org#piston-internals" 4 | os: 5 | - linux 6 | - osx 7 | before_install: # For OS X until addons support `brew` 8 | - "if [ ${TRAVIS_OS_NAME} = 'osx' ]; then brew update; fi" 9 | - "if [ ${TRAVIS_OS_NAME} = 'osx' ]; then brew install freetype ; fi" 10 | script: 11 | - cd ./getting-started 12 | - cargo build -v 13 | -------------------------------------------------------------------------------- /getting-started/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "spinning-square" 4 | version = "0.1.0" 5 | authors = [ 6 | "TyOverby ", 7 | "Nikita Pekin " 8 | ] 9 | 10 | [[bin]] 11 | name = "spinning-square" 12 | 13 | [dependencies] 14 | piston = "0.50.0" 15 | piston2d-graphics = "0.36.0" 16 | pistoncore-glutin_window = "0.64.0" 17 | piston2d-opengl_graphics = "0.72.0" 18 | -------------------------------------------------------------------------------- /roguelike/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "roguelike" 3 | version = "0.1.0" 4 | authors = ["Chetan Mistry "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | piston = "0.49.0" 11 | pistoncore-glutin_window = "0.63.0" 12 | piston2d-graphics = "0.36.0" 13 | piston2d-opengl_graphics = "0.71.0" 14 | -------------------------------------------------------------------------------- /tutorial_util/main.rs: -------------------------------------------------------------------------------- 1 | extern crate md_rel; 2 | 3 | use md_rel::transform_file; 4 | 5 | fn main() { 6 | let markdown_files = ["getting-started/readme.dev.md"]; 7 | for &file in &markdown_files { 8 | match transform_file(file) { 9 | Ok(_) => (), 10 | Err(x) => println!( 11 | "Failed to compile file {} with error {:?}", file, x) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sudoku/src/gameboard.rs: -------------------------------------------------------------------------------- 1 | //! Game board logic. 2 | 3 | /// Size of game board. 4 | const SIZE: usize = 9; 5 | 6 | /// Stores game board information. 7 | pub struct Gameboard { 8 | /// Stores the content of the cells. 9 | /// `0` is an empty cell. 10 | pub cells: [[u8; SIZE]; SIZE], 11 | } 12 | 13 | impl Gameboard { 14 | /// Creates a new game board. 15 | pub fn new() -> Gameboard { 16 | Gameboard { 17 | cells: [[0; SIZE]; SIZE], 18 | } 19 | } 20 | 21 | /// Gets the character at cell location. 22 | pub fn char(&self, ind: [usize; 2]) -> Option { 23 | Some(match self.cells[ind[1]][ind[0]] { 24 | 1 => '1', 25 | 2 => '2', 26 | 3 => '3', 27 | 4 => '4', 28 | 5 => '5', 29 | 6 => '6', 30 | 7 => '7', 31 | 8 => '8', 32 | 9 => '9', 33 | _ => return None, 34 | }) 35 | } 36 | 37 | /// Set cell value. 38 | pub fn set(&mut self, ind: [usize; 2], val: u8) { 39 | self.cells[ind[1]][ind[0]] = val; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /roguelike/solutions/Chapter-1-Solution.rs: -------------------------------------------------------------------------------- 1 | #![crate_name = "roguelike"] 2 | //! A Roguelike Game using Piston Engine 3 | 4 | extern crate glutin_window; 5 | extern crate piston; 6 | 7 | extern crate graphics; 8 | extern crate opengl_graphics; 9 | 10 | use glutin_window::GlutinWindow; 11 | use piston::WindowSettings; 12 | 13 | use piston::event_loop::{EventLoop, EventSettings, Events}; 14 | use piston::RenderEvent; 15 | 16 | use opengl_graphics::{GlGraphics, OpenGL}; 17 | 18 | fn main() { 19 | let opengl = OpenGL::V3_2; 20 | let settings = WindowSettings::new("Roguelike", [512; 2]).exit_on_esc(true); 21 | let mut window: GlutinWindow = settings.build().expect("Could not create window"); 22 | let mut gl = GlGraphics::new(opengl); 23 | 24 | let RED = [1.0, 0.0, 0.0, 1.0]; 25 | let GREEN = [0.0, 1.0, 0.0, 1.0]; 26 | let BLUE = [0.0, 0.0, 1.0, 1.0]; 27 | let WHITE = [1.0; 4]; 28 | 29 | let mut events = Events::new(EventSettings::new()); 30 | while let Some(e) = events.next(&mut window) { 31 | if let Some(r) = e.render_args() { 32 | gl.draw(r.viewport(), |_c, g| { 33 | graphics::clear(BLUE, g); 34 | }); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Piston-Tutorials [![Build Status](https://travis-ci.org/PistonDevelopers/Piston-Tutorials.svg)](https://travis-ci.org/PistonDevelopers/Piston-Tutorials) 2 | 3 | This is a repository for examples of Piston projects that have are 4 | accompanied by written tutorials explaining core concepts for that 5 | tutorial. 6 | 7 | ## Current Tutorials 8 | 9 | #### [Getting Started Spinning Square](./getting-started) 10 | A "tutorial" with instructions on compiling and running a very 11 | simple Piston. 12 | 13 | #### [Sudoku](./sudoku) (work in progress) 14 | Write a Sudoku game with Piston. 15 | 16 | #### [Roguelike](./roguelike) (work in progess) 17 | Write a Roguelike game with Piston. 18 | 19 | ## Planned Tutorials 20 | 21 | * graphics 22 | * piston 23 | 24 | ## Troubleshooting 25 | 26 | * [I get `ld: library not found for -lSDL2` error on OSX](https://github.com/PistonDevelopers/rust-empty/issues/175) 27 | 28 | * I get "GL context creation failed" when running an example. 29 | 30 | It's likely your hardware or driver doesn't support PistonWindow's default OpenGl spec. Just change it to something 31 | you can support at the beginning of the example. See hello_world.rs for an example. 32 | 33 | ## Making changes to the tutorials 34 | Because most of the tutorials will contain heavy amounts of 35 | code, TyOverby developed a markdown pre-processor that takes 36 | `readme.dev.md` files and includes code from the surrounding 37 | project. This way you don't need to make a change in the code 38 | for the tutorial and then also make the same change in the 39 | readme.md file; the preprocessor will do that for you! 40 | 41 | In order to run the pre-processor, simply invoke `cargo run` 42 | from the root directory (not the sub-tutorial directory) and 43 | it will rebuild all the markdown files that it knows about. 44 | 45 | [How to contribute](https://github.com/PistonDevelopers/piston/blob/master/CONTRIBUTING.md) 46 | -------------------------------------------------------------------------------- /sudoku/src/main.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | 3 | //! A Sudoku game. 4 | 5 | extern crate piston; 6 | extern crate glutin_window; 7 | extern crate graphics; 8 | extern crate opengl_graphics; 9 | 10 | use piston::window::WindowSettings; 11 | use piston::event_loop::{Events, EventLoop, EventSettings}; 12 | use piston::input::RenderEvent; 13 | use glutin_window::GlutinWindow; 14 | use opengl_graphics::{OpenGL, Filter, GlGraphics, GlyphCache, TextureSettings}; 15 | 16 | pub use crate::gameboard::Gameboard; 17 | pub use crate::gameboard_controller::GameboardController; 18 | pub use crate::gameboard_view::{GameboardView, GameboardViewSettings}; 19 | 20 | mod gameboard; 21 | mod gameboard_controller; 22 | mod gameboard_view; 23 | 24 | fn main() { 25 | let opengl = OpenGL::V3_2; 26 | let settings = WindowSettings::new("Sudoku", [512; 2]) 27 | .opengl(opengl) 28 | .exit_on_esc(true); 29 | let mut window: GlutinWindow = settings.build() 30 | .expect("Could not create window"); 31 | 32 | let mut events = Events::new(EventSettings::new().lazy(true)); 33 | let mut gl = GlGraphics::new(opengl); 34 | 35 | let gameboard = Gameboard::new(); 36 | let mut gameboard_controller = GameboardController::new(gameboard); 37 | let gameboard_view_settings = GameboardViewSettings::new(); 38 | let gameboard_view = GameboardView::new(gameboard_view_settings); 39 | 40 | let texture_settings = TextureSettings::new().filter(Filter::Nearest); 41 | let ref mut glyphs = GlyphCache::new("assets/FiraSans-Regular.ttf", (), texture_settings) 42 | .expect("Could not load font"); 43 | 44 | while let Some(e) = events.next(&mut window) { 45 | gameboard_controller.event(gameboard_view.settings.position, 46 | gameboard_view.settings.size, 47 | &e); 48 | if let Some(args) = e.render_args() { 49 | gl.draw(args.viewport(), |c, g| { 50 | use graphics::{clear}; 51 | 52 | clear([1.0; 4], g); 53 | gameboard_view.draw(&gameboard_controller, glyphs, &c, g); 54 | }); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /getting-started/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate glutin_window; 2 | extern crate graphics; 3 | extern crate opengl_graphics; 4 | extern crate piston; 5 | 6 | use glutin_window::GlutinWindow as Window; 7 | use opengl_graphics::{GlGraphics, OpenGL}; 8 | use piston::event_loop::{EventSettings, Events}; 9 | use piston::input::{RenderArgs, RenderEvent, UpdateArgs, UpdateEvent}; 10 | use piston::window::WindowSettings; 11 | 12 | pub struct App { 13 | gl: GlGraphics, // OpenGL drawing backend. 14 | rotation: f64, // Rotation for the square. 15 | } 16 | 17 | impl App { 18 | fn render(&mut self, args: &RenderArgs) { 19 | use graphics::*; 20 | 21 | const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0]; 22 | const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0]; 23 | 24 | let square = rectangle::square(0.0, 0.0, 50.0); 25 | let rotation = self.rotation; 26 | let (x, y) = (args.window_size[0] / 2.0, args.window_size[1] / 2.0); 27 | 28 | self.gl.draw(args.viewport(), |c, gl| { 29 | // Clear the screen. 30 | clear(GREEN, gl); 31 | 32 | let transform = c 33 | .transform 34 | .trans(x, y) 35 | .rot_rad(rotation) 36 | .trans(-25.0, -25.0); 37 | 38 | // Draw a box rotating around the middle of the screen. 39 | rectangle(RED, square, transform, gl); 40 | }); 41 | } 42 | 43 | fn update(&mut self, args: &UpdateArgs) { 44 | // Rotate 2 radians per second. 45 | self.rotation += 2.0 * args.dt; 46 | } 47 | } 48 | 49 | fn main() { 50 | // Change this to OpenGL::V2_1 if not working. 51 | let opengl = OpenGL::V3_2; 52 | 53 | // Create an Glutin window. 54 | let mut window: Window = WindowSettings::new("spinning-square", [200, 200]) 55 | .graphics_api(opengl) 56 | .exit_on_esc(true) 57 | .build() 58 | .unwrap(); 59 | 60 | // Create a new game and run it. 61 | let mut app = App { 62 | gl: GlGraphics::new(opengl), 63 | rotation: 0.0, 64 | }; 65 | 66 | let mut events = Events::new(EventSettings::new()); 67 | while let Some(e) = events.next(&mut window) { 68 | if let Some(args) = e.render_args() { 69 | app.render(&args); 70 | } 71 | 72 | if let Some(args) = e.update_args() { 73 | app.update(&args); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /sudoku/src/gameboard_controller.rs: -------------------------------------------------------------------------------- 1 | //! Gameboard controller. 2 | 3 | use piston::input::GenericEvent; 4 | 5 | use crate::Gameboard; 6 | 7 | /// Handles events for Sudoku game. 8 | pub struct GameboardController { 9 | /// Stores the gameboard state. 10 | pub gameboard: Gameboard, 11 | /// Selected cell. 12 | pub selected_cell: Option<[usize; 2]>, 13 | /// Stores last mouse cursor position. 14 | cursor_pos: [f64; 2], 15 | } 16 | 17 | impl GameboardController { 18 | /// Creates a new gameboard controller. 19 | pub fn new(gameboard: Gameboard) -> GameboardController { 20 | GameboardController { 21 | gameboard: gameboard, 22 | selected_cell: None, 23 | cursor_pos: [0.0; 2], 24 | } 25 | } 26 | 27 | /// Handles events. 28 | pub fn event(&mut self, pos: [f64; 2], size: f64, e: &E) { 29 | use piston::input::{Button, Key, MouseButton}; 30 | 31 | if let Some(pos) = e.mouse_cursor_args() { 32 | self.cursor_pos = pos; 33 | } 34 | if let Some(Button::Mouse(MouseButton::Left)) = e.press_args() { 35 | // Find coordinates relative to upper left corner. 36 | let x = self.cursor_pos[0] - pos[0]; 37 | let y = self.cursor_pos[1] - pos[1]; 38 | // Check that coordinates are inside board boundaries. 39 | if x >= 0.0 && x < size && y >= 0.0 && y < size { 40 | // Compute the cell position. 41 | let cell_x = (x / size * 9.0) as usize; 42 | let cell_y = (y / size * 9.0) as usize; 43 | self.selected_cell = Some([cell_x, cell_y]); 44 | } 45 | } 46 | if let Some(Button::Keyboard(key)) = e.press_args() { 47 | if let Some(ind) = self.selected_cell { 48 | // Set cell value. 49 | match key { 50 | Key::D1 => self.gameboard.set(ind, 1), 51 | Key::D2 => self.gameboard.set(ind, 2), 52 | Key::D3 => self.gameboard.set(ind, 3), 53 | Key::D4 => self.gameboard.set(ind, 4), 54 | Key::D5 => self.gameboard.set(ind, 5), 55 | Key::D6 => self.gameboard.set(ind, 6), 56 | Key::D7 => self.gameboard.set(ind, 7), 57 | Key::D8 => self.gameboard.set(ind, 8), 58 | Key::D9 => self.gameboard.set(ind, 9), 59 | _ => {} 60 | } 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /roguelike/solutions/Chapter-2-Solution.rs: -------------------------------------------------------------------------------- 1 | #![crate_name = "roguelike"] 2 | //! A Roguelike Game using Piston Engine 3 | 4 | extern crate glutin_window; 5 | extern crate piston; 6 | 7 | extern crate graphics; 8 | extern crate opengl_graphics; 9 | 10 | use glutin_window::GlutinWindow; 11 | use piston::WindowSettings; 12 | 13 | use piston::event_loop::{EventLoop, EventSettings, Events}; 14 | use piston::RenderEvent; 15 | 16 | use opengl_graphics::{GlGraphics, OpenGL}; 17 | 18 | type Colour = [f32; 4]; 19 | 20 | const RED: Colour = [1.0, 0.0, 0.0, 1.0]; 21 | const GREEN: Colour = [0.0, 1.0, 0.0, 1.0]; 22 | const BLUE: Colour = [0.0, 0.0, 1.0, 1.0]; 23 | const WHITE: Colour = [1.0; 4]; 24 | const BLACK: Colour = [0.0, 0.0, 0.0, 1.0]; 25 | 26 | const WINDOW_SIZE: i32 = 512; 27 | const PIXEL_SIZE: f64 = 32.0; 28 | const WORLD_SIZE: i32 = WINDOW_SIZE / PIXEL_SIZE as i32; 29 | 30 | #[derive(Clone)] 31 | struct Tile { 32 | colour: Colour, 33 | } 34 | 35 | impl Tile { 36 | pub fn empty() -> Self { 37 | Tile { colour: WHITE } 38 | } 39 | 40 | pub fn wall() -> Self { 41 | Tile { colour: BLACK } 42 | } 43 | } 44 | 45 | type Map = Vec>; 46 | 47 | fn make_map() -> Map { 48 | let mut map = vec![vec![Tile::empty(); WORLD_SIZE as usize]; WORLD_SIZE as usize]; 49 | map[WORLD_SIZE as usize / 2][WORLD_SIZE as usize / 2] = Tile::wall(); 50 | map 51 | } 52 | 53 | fn main() { 54 | let opengl = OpenGL::V3_2; 55 | let settings = WindowSettings::new("Roguelike", [512; 2]).exit_on_esc(true); 56 | let mut window: GlutinWindow = settings.build().expect("Could not create window"); 57 | let mut gl = GlGraphics::new(opengl); 58 | let map = make_map(); 59 | let mut events = Events::new(EventSettings::new()); 60 | while let Some(e) = events.next(&mut window) { 61 | if let Some(r) = e.render_args() { 62 | gl.draw(r.viewport(), |c, g| { 63 | graphics::clear(BLUE, g); 64 | 65 | for i in 0..WORLD_SIZE { 66 | for j in 0..WORLD_SIZE { 67 | let pos: [f64; 4] = [ 68 | PIXEL_SIZE * i as f64, 69 | PIXEL_SIZE * j as f64, 70 | PIXEL_SIZE * (i + 1) as f64, 71 | PIXEL_SIZE * (j + 1) as f64, 72 | ]; 73 | graphics::Rectangle::new(map[i as usize][j as usize].colour).draw( 74 | pos, 75 | &c.draw_state, 76 | c.transform, 77 | g, 78 | ); 79 | } 80 | } 81 | }); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /roguelike/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to the Roguelike Tutorial!# 2 | 3 | In this tutorial you will learn how to create a simple Roguelike game, 4 | comparable to the Pokemon Mystery Dungeon games. 5 | This tutorial consists of multiple parts, each of which will focus on a 6 | different aspect of the game. 7 | 8 | This tutorial has been designed to make you think rather than just blindly 9 | copying code segments, and so it will provide you with function templates for 10 | you to fill out yourself, however, at the end of each chapter, there will be a 11 | link to a working implementation of the code which can be used as a 12 | reference. 13 | 14 | # Chapter 0 - Getting Started # 15 | 16 | In this first section we are going to set up our project structure so that 17 | you will be able to gain the most benefit from this tutorial. 18 | 19 | ## Assumptions ## 20 | 21 | This tutorial will assume you have some level of comfort in using a terminal 22 | and general programming knowledge. If you have any questions however, feel 23 | free to create an issue or message me @cm16161 about it. 24 | 25 | ## Installing Rust and Cargo ## 26 | 27 | To start with, we need to make sure that we have both Rust and Cargo 28 | installed on our system. We can do this by following the instructions at: 29 | 30 | [https://doc.rust-lang.org/cargo/getting-started/installation.html](https://doc.rust-lang.org/cargo/getting-started/installation.html) 31 | 32 | ## Setting Up Project Structure ## 33 | 34 | With `Cargo` installed we will now use it to initialise our project structure 35 | by executing the following command in your terminal: 36 | 37 | `cargo new roguelike --bin` 38 | 39 | Which will automatically generate the following project structure: 40 | 41 | ``` shell 42 | . 43 | ├── Cargo.toml 44 | └── src 45 | └── main.rs 46 | 47 | ``` 48 | If you go into the directory and then execute the command: 49 | `cargo run` 50 | The following output (or similar) should be output: 51 | 52 | ``` shell 53 | Compiling roguelike v0.1.0 (/home/chetan/Documents/roguelike) 54 | Finished dev [unoptimized + debuginfo] target(s) in 0.48s 55 | Running `target/debug/roguelike` 56 | Hello, world! 57 | ``` 58 | 59 | The main takeaways are that it compiles successfully, and outputs `Hello, 60 | World!`. 61 | 62 | ## Cargo Docs ## 63 | 64 | This tutorial will utilise Cargo's ability to automatically generate 65 | documentation, and output the documentation. This can be achieved by using 66 | the terminal command: 67 | 68 | ``` shell 69 | cargo doc --open 70 | ``` 71 | 72 | It is important to keep the documentation updated as it will be useful for 73 | yourself, the programmer, and any other person who wants to use and 74 | understand your code. 75 | 76 | ## Chapter 1 - Graphics ## 77 | 78 | Follow the link to get to Chapter 1, which will handle setting up the 79 | Graphics of our game and creating a window which we can play our game on. 80 | 81 | [Chapter 1 - Graphics](chapters/Chapter_1-Graphics.md) 82 | -------------------------------------------------------------------------------- /getting-started/readme.dev.md: -------------------------------------------------------------------------------- 1 | ## Getting Started Spinning Square 2 | ### A spinning square 3 | 4 | [![Build Status](https://travis-ci.org/PistonDevelopers/Piston-Tutorials.svg?branch=master)](https://travis-ci.org/PistonDevelopers/Piston-Tutorials) 5 | 6 | In this tutorial, I hope to get you from an empty Cargo project to having a 7 | window with a rotating square in it. 8 | This tutorial does ___not___ explain concepts used in the game, as those 9 | will be covered by other tutorials. 10 | This tutorial only covers project setup and contains a sample "game" simply 11 | to test the build environment. 12 | 13 | 14 | I assume that you have installed Rust and Cargo, and have already built a 15 | hello-world project with Cargo. 16 | If you haven't met these criteria, please read the first few chapters of 17 | [The Rust Book](http://doc.rust-lang.org/book/) and come back once 18 | you've finished. 19 | 20 | ![Result](./out.gif) 21 | 22 | #### At this stage 23 | 24 | * You should be able to run the command `rustc -V` 25 | * You should be able to run the command `cargo -V` 26 | 27 | If you have failed either of these, please review the getting started 28 | guide and make sure that you have the latest versions of `rustc` and `cargo`. 29 | 30 | ## Installing Dependencies 31 | 32 | Parts of the Piston project depend on native C libraries. For example, in 33 | order to display a window and hook it up to an OpenGL context, we can use 34 | either Glutin, GLFW or SDL2 as the implementation of the windowing system. 35 | 36 | The rest of this tutorial uses Glutin for windowing, so we won't need to 37 | directly install any additional libraries for that purpose. 38 | 39 | ## Setting Up The Project 40 | 41 | If everything is set up correctly, it's time to create a Cargo project 42 | and specify dependencies. 43 | 44 | 45 | ```bash 46 | cargo new --bin getting-started 47 | cd getting-started 48 | ``` 49 | 50 | Now in your favorite editor, add project settings and dependencies to 51 | `Cargo.toml`. 52 | 53 | ^code(./Cargo.toml) 54 | 55 | You might be thinking that this is a lot of dependencies for such a simple 56 | example application. 57 | This is because of how the Piston Projects are organized. 58 | The `piston` and `graphics` libraries are able to do a lot of work by 59 | themselves, but they are made to be completely independent of a 60 | backing implementation. 61 | For example, when it comes to displaying a window and getting keyboard events 62 | in a cross-platform manner, you can use either Glutin, GLFW or SDL2. 63 | GLFW and SDL2 are both C and C++ cross-platform libraries for creating windows 64 | with an OpenGL context. Glutin - pure Rust alternative. 65 | In this tutorial I chose Glutin, so you will notice that in the cargo file, we 66 | imported `glutin_window`. 67 | `opengl_graphics` is another backend that implements the interface defined in 68 | `graphics`. 69 | `graphics` is a 2d graphics API that doesn't care about how things are 70 | *actually* drawn to the screen. 71 | If you implement the `graphics` interface yourself, you could route it 72 | through directx, or render straight to a png. 73 | In this tutorial, we are rendering using OpenGL, so we'll use `opengl_graphics`. 74 | 75 | The pattern of "interface" and "backend" is very common with Piston Projects. 76 | While other game engines might encompass lots of functionality, we prefer to have 77 | many libraries that are separate and extendable, but also work well when 78 | combined. 79 | 80 | 81 | ## Writing Some Code 82 | 83 | Ok, time for some game logic. Edit `src/main.rs` in your favorite editor: 84 | 85 | ^code(./src/main.rs) 86 | 87 | ## Compiling And Running 88 | 89 | Awesome! Now that we have the game code, let's get it running! 90 | With Cargo, downloading dependencies and building the application is as 91 | simple as running `cargo build` from the main project directory. 92 | 93 | If all goes well, you should have the binary `spinning-square` inside the `target/debug` 94 | directory. 95 | 96 | Run it by executing `cargo run`. 97 | 98 | On your screen you should have a rotating square that looks like this: 99 | 100 | ![Result](./out.gif) 101 | 102 | ## What's Next? 103 | 104 | Take a look at the [piston-examples](https://github.com/pistondevelopers/piston-examples) repository. 105 | -------------------------------------------------------------------------------- /sudoku/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to the Sudoku tutorial! 2 | by Sven Nilsen, 2017 3 | 4 | In this tutorial, you will learn how to create a Sudoku game with Piston. 5 | This tutorial is a bit long, because it goes step by step through the thinking process when developing a game. 6 | Expect to spend a day or two getting through this! 7 | 8 | I assume you have gotten through the [getting-started](../getting-started) tutorial and know how to set up a new Rust project. 9 | Some of these steps will be repeated briefly in the first chapter. 10 | 11 | ## Chapter 1 12 | 13 | Type the following command in the Terminal window: 14 | 15 | ``` 16 | cargo new --bin sudoku 17 | cd sudoku 18 | ``` 19 | 20 | In the file "src/main.rs", type the following: 21 | 22 | ```rust 23 | fn main() { 24 | println!("hi"); 25 | } 26 | ``` 27 | 28 | To test that it runs, type this in the Terminal window: 29 | 30 | ``` 31 | cargo run 32 | ``` 33 | 34 | What you should see: 35 | 36 | ``` 37 | $ cargo run 38 | Compiling sudoku v0.1.0 (file:///Users/sven/rust/Piston-Tutorials/sudoku) 39 | Finished dev [unoptimized + debuginfo] target(s) in 2.42 secs 40 | Running `target/debug/sudoku` 41 | hi 42 | ``` 43 | 44 | If this is not working, you might have forgotten the `--bin` flag after `cargo new`. Delete the folder and try again. 45 | 46 | Now, we need to add a few libraries to the Cargo.toml. 47 | To make this more efficient, we will install the tool `cargo-edit` such that we can type `cargo add `. 48 | To install `cargo-edit`, type the following: 49 | 50 | ``` 51 | cargo install cargo-edit 52 | ``` 53 | 54 | The first library we will add is `piston`. 55 | This is the core library of the Piston game engine. 56 | 57 | Type the following in the Terminal window: 58 | 59 | ``` 60 | cargo add piston 61 | ``` 62 | 63 | When you open up "Cargo.toml", you should see: 64 | 65 | ``` 66 | [dependencies] 67 | piston = "*" 68 | ``` 69 | 70 | The star tells Cargo to get the latest compatible version. 71 | This means that you do not have to figure out which library version 72 | works with other library versions, it will figure it out for you. 73 | 74 | Change the "main.rs" file: 75 | 76 | ```rust 77 | extern crate piston; 78 | 79 | use piston::window::WindowSettings; 80 | 81 | fn main() { 82 | let settings = WindowSettings::new("Sudoku", [512; 2]) 83 | .exit_on_esc(true); 84 | 85 | println!("{}", settings.get_exit_on_esc()); 86 | } 87 | ``` 88 | 89 | When typing `cargo run` in the Terminal window, you should see 90 | the program printing out "true". 91 | 92 | The first line, `extern crate piston;`, tells the Rust compiler to look 93 | for a crate called "piston". 94 | The second line `use piston::window::WindowSettings;` imports the 95 | `WindowSettings` struct from the submodule `window` in the `piston` crate. 96 | 97 | We created an object called `settings` by calling the function 98 | `WindowSettings::new`. 99 | This function takes two parameters, the title of the window, and the 100 | window size. 101 | Then we call `.exit_on_esc(true);` to set the flag that 102 | we want it the window to exit when we press ESC on the keyboard. 103 | 104 | The `WindowSettings` struct is used to tell the window backend 105 | how to load the window. 106 | It reads out the settings by calling the methods. 107 | For example, `settings.get_exit_on_esc()` like we just did. 108 | 109 | Not every window backend in Piston is guaranteed to respect the settings 110 | you choose. 111 | For example, if the window runs on a platform where all applications 112 | run fullscreen, it will ignore whether you set `.fullscreen(true)` or 113 | `.fullscreen(false)`. 114 | 115 | To view the docs, type the following in the Terminal window: 116 | 117 | ``` 118 | cargo doc --open 119 | ``` 120 | 121 | This will open up the project's documentation in the default browser. 122 | 123 | ![docs](./images/docs.png) 124 | 125 | On the left side are all the crates in the dependency graph of your project. 126 | At the top you can search for "WindowSettings" to find it in the docs. 127 | 128 | Here is a list of useful window settings: 129 | 130 | - fullscreen 131 | - exit_on_esc 132 | - samples 133 | - vsync 134 | - opengl 135 | - srgb 136 | - resizable 137 | - decorated 138 | - controllers 139 | 140 | [Goto Chapter 2](chp-02.md) 141 | -------------------------------------------------------------------------------- /roguelike/chapters/Chapter_3-Player.md: -------------------------------------------------------------------------------- 1 | # Chapter 3 - Player # 2 | 3 | In this chapter we will finally learn how to create our player that can move around our map! 4 | 5 | Before we get started we will need to `use` some more stuff. 6 | Add or update the following lines to the top of your file: 7 | 8 | ```rust 9 | 10 | use piston::input::{Button, ButtonState, Key}; 11 | use piston::{ButtonEvent, RenderEvent}; 12 | 13 | use graphics::character::CharacterCache; 14 | use opengl_graphics::{Filter, GlGraphics, GlyphCache, OpenGL, TextureSettings}; 15 | ``` 16 | 17 | ## Object Structure ## 18 | 19 | Last chapter we created the `struct Tile`, now we want to do the same thing but for a `character`: 20 | 21 | ```rust 22 | #[derive(Clone)] 23 | struct Object{ 24 | //TODO 25 | } 26 | ``` 27 | 28 | Fill in the above template with the following fields: 29 | *`x` position which of type `i32` 30 | *`y` position which of type `i32` 31 | *`character` of type `char` which represents what the character will appear as on screen 32 | * `colour` of type `Colour` representing what colour our character should be 33 | 34 | We now need to implement our `Object` struct like so: 35 | 36 | ``` rust 37 | impl Object { 38 | //TODO 39 | pub fn new(BLANK, BLANK, BLANK, BLANK) -> Self { 40 | Object { 41 | BLANK, 42 | BLANK, 43 | BLANK, 44 | BLANK, 45 | } 46 | } 47 | } 48 | ``` 49 | 50 | Fill in the `BLANK` spaces with the appropriate values. **(HINT: The fields we defined earlier)** 51 | 52 | ## Adding a player ## 53 | 54 | Now that we have defined what a `Object` is, we can create an instance of it for our `player`. 55 | In the main function add the following: 56 | 57 | ```rust 58 | let mut player : Object = Object::new(BLANK,BLANK,'@',RED); 59 | ``` 60 | Where the BLANKS represent the starting position in our map (eg. (0,0)). 61 | 62 | ## Displaying the Player ## 63 | 64 | We now have a player object, so lets go about displaying them on our window. To do this, copy the following lines into your `main()` function: 65 | 66 | ``` rust 67 | let texture_settings = TextureSettings::new().filter(Filter::Nearest); 68 | let ref mut glyphs = GlyphCache::new("assets/FiraSans-Regular.ttf", (), texture_settings) 69 | .expect("Could not load font"); 70 | ``` 71 | 72 | This creates a new `texture_settings` and `GlyphCache` so that we can print characters to our window. 73 | 74 | In our render function, underneath where we render the world map, we now want to display our player: 75 | 76 | ```rust 77 | use graphics::Transformed; 78 | let character = glyphs.character(PIXEL_SIZE, BLANK).unwrap(); 79 | graphics::Image::new_color(BLANK).draw( 80 | character.texture, 81 | &c.draw_state, 82 | c.transform.trans(BLANK , BLANK ), 83 | g, 84 | ); 85 | ``` 86 | 87 | The first `BLANK` represents what `character` our `player` is. 88 | The next `BLANK` represents what the `Colour` of our `player` is. 89 | The last set of blanks represent the `x` and `y` positions of our `player` 90 | 91 | ## Movement ## 92 | 93 | Make sure before starting this section that your code compiles and runs displaying your player on the screen. 94 | 95 | Assuming that this works we can now implement the logic that will allow our player to move around the screen! 96 | 97 | To do this we need to be able to handle `ButtonEvent`. 98 | Similar to how we were able to define what happens when we encounter a `RenderEvent`, we need to add the following code in our `while` loop: 99 | 100 | `if let Some(k) = e.button_args(){}` 101 | 102 | In this function we can now decide what to do when we push a button. 103 | To start with, we should only do something when a button is *pressed* by adding the following line within this `if` statement: 104 | 105 | `if k.state == ButtonState::Press{}` 106 | 107 | Now within this statement we can add the logic for a button press like so: 108 | 109 | ``` rust 110 | match k.button { 111 | Button::Keyboard(Key::Up) => BLANK -= PIXEL_SIZE as i32, 112 | Button::Keyboard(Key::Down) => player.y += PIXEL_SIZE as i32, 113 | Button::Keyboard(Key::Left) => player.x -= PIXEL_SIZE as i32, 114 | Button::Keyboard(Key::Right) => player.x += PIXEL_SIZE as i32, 115 | _ => (), 116 | } 117 | ``` 118 | 119 | Fill in the `BLANK` spaces with the logic required to move our player about depending on which arrow key is pressed. 120 | 121 | Once all this is done you should now have player who can walk around a map! 122 | -------------------------------------------------------------------------------- /roguelike/solutions/Chapter-3-Solution.rs: -------------------------------------------------------------------------------- 1 | #![crate_name = "roguelike"] 2 | //! A Roguelike Game using Piston Engine 3 | 4 | extern crate glutin_window; 5 | extern crate piston; 6 | 7 | extern crate graphics; 8 | extern crate opengl_graphics; 9 | 10 | use glutin_window::GlutinWindow; 11 | use piston::WindowSettings; 12 | 13 | use piston::event_loop::{EventLoop, EventSettings, Events}; 14 | use piston::input::{Button, ButtonState, Key}; 15 | use piston::{ButtonEvent, RenderEvent}; 16 | 17 | use graphics::character::CharacterCache; 18 | use opengl_graphics::{Filter, GlGraphics, GlyphCache, OpenGL, TextureSettings}; 19 | 20 | type Colour = [f32; 4]; 21 | 22 | const RED: Colour = [1.0, 0.0, 0.0, 1.0]; 23 | const GREEN: Colour = [0.0, 1.0, 0.0, 1.0]; 24 | const BLUE: Colour = [0.0, 0.0, 1.0, 1.0]; 25 | const WHITE: Colour = [1.0; 4]; 26 | const BLACK: Colour = [0.0, 0.0, 0.0, 1.0]; 27 | 28 | const WINDOW_SIZE: i32 = 512; 29 | const PIXEL_SIZE: f64 = 32.0; 30 | const WORLD_SIZE: i32 = WINDOW_SIZE / PIXEL_SIZE as i32; 31 | 32 | #[derive(Clone)] 33 | struct Tile { 34 | colour: Colour, 35 | } 36 | 37 | impl Tile { 38 | pub fn empty() -> Self { 39 | Tile { colour: WHITE } 40 | } 41 | 42 | pub fn wall() -> Self { 43 | Tile { colour: BLACK } 44 | } 45 | } 46 | 47 | #[derive(Clone)] 48 | struct Object { 49 | x: i32, 50 | y: i32, 51 | character: char, 52 | colour: Colour, 53 | } 54 | 55 | impl Object { 56 | pub fn new(x: i32, y: i32, character: char, colour: Colour) -> Self { 57 | Object { 58 | x, 59 | y, 60 | character, 61 | colour, 62 | } 63 | } 64 | } 65 | 66 | type Map = Vec>; 67 | 68 | fn make_map() -> Map { 69 | let mut map = vec![vec![Tile::empty(); WORLD_SIZE as usize]; WORLD_SIZE as usize]; 70 | map[WORLD_SIZE as usize / 2][WORLD_SIZE as usize / 2] = Tile::wall(); 71 | map 72 | } 73 | 74 | fn main() { 75 | let opengl = OpenGL::V3_2; 76 | let settings = WindowSettings::new("Roguelike", [512; 2]).exit_on_esc(true); 77 | let mut window: GlutinWindow = settings.build().expect("Could not create window"); 78 | let mut gl = GlGraphics::new(opengl); 79 | 80 | let map = make_map(); 81 | 82 | let texture_settings = TextureSettings::new().filter(Filter::Nearest); 83 | let ref mut glyphs = GlyphCache::new("assets/FiraSans-Regular.ttf", (), texture_settings) 84 | .expect("Could not load font"); 85 | 86 | 87 | 88 | let mut events = Events::new(EventSettings::new()); 89 | let mut player :Object = Object::new(0, 0, '@', RED); 90 | while let Some(e) = events.next(&mut window) { 91 | if let Some(r) = e.render_args() { 92 | gl.draw(r.viewport(), |c, g| { 93 | graphics::clear(BLUE, g); 94 | 95 | for i in 0..WORLD_SIZE { 96 | for j in 0..WORLD_SIZE { 97 | let pos: [f64; 4] = [ 98 | PIXEL_SIZE * i as f64, 99 | PIXEL_SIZE * j as f64, 100 | PIXEL_SIZE * (i + 1) as f64, 101 | PIXEL_SIZE * (j + 1) as f64, 102 | ]; 103 | graphics::Rectangle::new(map[i as usize][j as usize].colour).draw( 104 | pos, 105 | &c.draw_state, 106 | c.transform, 107 | g, 108 | ); 109 | } 110 | } 111 | use graphics::Transformed; 112 | let character = glyphs.character(PIXEL_SIZE, player.character).unwrap(); 113 | graphics::Image::new_color(player.colour).draw( 114 | character.texture, 115 | &c.draw_state, 116 | c.transform.trans(player.x as f64, player.y as f64), 117 | g 118 | ); 119 | }); 120 | } 121 | if let Some(k) = e.button_args() { 122 | if k.state == ButtonState::Press { 123 | match k.button { 124 | Button::Keyboard(Key::Up) => player.y -= 32, 125 | Button::Keyboard(Key::Down) => player.y += 32, 126 | Button::Keyboard(Key::Left) => player.x -= 32, 127 | Button::Keyboard(Key::Right) => player.x += 32, 128 | _ => (), 129 | } 130 | } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /roguelike/src/main.rs: -------------------------------------------------------------------------------- 1 | #![crate_name = "roguelike"] 2 | //! A Roguelike Game using Piston Engine 3 | 4 | extern crate glutin_window; 5 | extern crate piston; 6 | 7 | extern crate graphics; 8 | extern crate opengl_graphics; 9 | 10 | use glutin_window::GlutinWindow; 11 | use piston::WindowSettings; 12 | 13 | use piston::event_loop::{EventLoop, EventSettings, Events}; 14 | use piston::input::{Button, ButtonState, Key}; 15 | use piston::{ButtonEvent, RenderEvent}; 16 | 17 | use graphics::character::CharacterCache; 18 | use opengl_graphics::{Filter, GlGraphics, GlyphCache, OpenGL, TextureSettings}; 19 | 20 | type Colour = [f32; 4]; 21 | 22 | const RED: Colour = [1.0, 0.0, 0.0, 1.0]; 23 | const GREEN: Colour = [0.0, 1.0, 0.0, 1.0]; 24 | const BLUE: Colour = [0.0, 0.0, 1.0, 1.0]; 25 | const WHITE: Colour = [1.0; 4]; 26 | const BLACK: Colour = [0.0, 0.0, 0.0, 1.0]; 27 | 28 | const WINDOW_SIZE: i32 = 512; 29 | const PIXEL_SIZE: f64 = 32.0; 30 | const WORLD_SIZE: i32 = WINDOW_SIZE / PIXEL_SIZE as i32; 31 | 32 | #[derive(Clone)] 33 | struct Tile { 34 | colour: Colour, 35 | } 36 | 37 | impl Tile { 38 | pub fn empty() -> Self { 39 | Tile { colour: WHITE } 40 | } 41 | 42 | pub fn wall() -> Self { 43 | Tile { colour: BLACK } 44 | } 45 | } 46 | 47 | #[derive(Clone)] 48 | struct Object { 49 | x: i32, 50 | y: i32, 51 | character: char, 52 | colour: Colour, 53 | } 54 | 55 | impl Object { 56 | pub fn new(x: i32, y: i32, character: char, colour: Colour) -> Self { 57 | Object { 58 | x, 59 | y, 60 | character, 61 | colour, 62 | } 63 | } 64 | } 65 | 66 | type Map = Vec>; 67 | 68 | fn make_map() -> Map { 69 | let mut map = vec![vec![Tile::empty(); WORLD_SIZE as usize]; WORLD_SIZE as usize]; 70 | map[WORLD_SIZE as usize / 2][WORLD_SIZE as usize / 2] = Tile::wall(); 71 | map 72 | } 73 | 74 | fn main() { 75 | let opengl = OpenGL::V3_2; 76 | let settings = WindowSettings::new("Roguelike", [512; 2]).exit_on_esc(true); 77 | let mut window: GlutinWindow = settings.build().expect("Could not create window"); 78 | let mut gl = GlGraphics::new(opengl); 79 | let texture_settings = TextureSettings::new().filter(Filter::Nearest); 80 | let ref mut glyphs = GlyphCache::new("assets/FiraSans-Regular.ttf", (), texture_settings) 81 | .expect("Could not load font"); 82 | 83 | let map = make_map(); 84 | 85 | let mut events = Events::new(EventSettings::new()); 86 | let mut player = Object::new(0, 0, '@', RED); 87 | while let Some(e) = events.next(&mut window) { 88 | if let Some(r) = e.render_args() { 89 | gl.draw(r.viewport(), |c, g| { 90 | graphics::clear(BLUE, g); 91 | 92 | for i in 0..WORLD_SIZE { 93 | for j in 0..WORLD_SIZE { 94 | let pos: [f64; 4] = [ 95 | PIXEL_SIZE * i as f64, 96 | PIXEL_SIZE * j as f64, 97 | PIXEL_SIZE * (i + 1) as f64, 98 | PIXEL_SIZE * (j + 1) as f64, 99 | ]; 100 | graphics::Rectangle::new(map[i as usize][j as usize].colour).draw( 101 | pos, 102 | &c.draw_state, 103 | c.transform, 104 | g, 105 | ); 106 | } 107 | } 108 | use graphics::Transformed; 109 | let character = glyphs.character(32, player.character).unwrap(); 110 | graphics::Image::new_color(player.colour).draw( 111 | character.texture, 112 | &c.draw_state, 113 | c.transform.trans(player.x as f64, player.y as f64), 114 | g, 115 | ); 116 | }); 117 | } 118 | if let Some(k) = e.button_args() { 119 | if k.state == ButtonState::Press { 120 | match k.button { 121 | Button::Keyboard(Key::Up) => player.y -= PIXEL_SIZE as i32, 122 | Button::Keyboard(Key::Down) => player.y += PIXEL_SIZE as i32, 123 | Button::Keyboard(Key::Left) => player.x -= PIXEL_SIZE as i32, 124 | Button::Keyboard(Key::Right) => player.x += PIXEL_SIZE as i32, 125 | _ => (), 126 | } 127 | } 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /sudoku/assets/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ 2 | with Reserved Font Name Fira Sans. 3 | 4 | Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ 5 | with Reserved Font Name Fira Mono. 6 | 7 | Copyright (c) 2014, Telefonica S.A. 8 | 9 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 10 | This license is copied below, and is also available with a FAQ at: 11 | http://scripts.sil.org/OFL 12 | 13 | 14 | ----------------------------------------------------------- 15 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 16 | ----------------------------------------------------------- 17 | 18 | PREAMBLE 19 | The goals of the Open Font License (OFL) are to stimulate worldwide 20 | development of collaborative font projects, to support the font creation 21 | efforts of academic and linguistic communities, and to provide a free and 22 | open framework in which fonts may be shared and improved in partnership 23 | with others. 24 | 25 | The OFL allows the licensed fonts to be used, studied, modified and 26 | redistributed freely as long as they are not sold by themselves. The 27 | fonts, including any derivative works, can be bundled, embedded, 28 | redistributed and/or sold with any software provided that any reserved 29 | names are not used by derivative works. The fonts and derivatives, 30 | however, cannot be released under any other type of license. The 31 | requirement for fonts to remain under this license does not apply 32 | to any document created using the fonts or their derivatives. 33 | 34 | DEFINITIONS 35 | "Font Software" refers to the set of files released by the Copyright 36 | Holder(s) under this license and clearly marked as such. This may 37 | include source files, build scripts and documentation. 38 | 39 | "Reserved Font Name" refers to any names specified as such after the 40 | copyright statement(s). 41 | 42 | "Original Version" refers to the collection of Font Software components as 43 | distributed by the Copyright Holder(s). 44 | 45 | "Modified Version" refers to any derivative made by adding to, deleting, 46 | or substituting -- in part or in whole -- any of the components of the 47 | Original Version, by changing formats or by porting the Font Software to a 48 | new environment. 49 | 50 | "Author" refers to any designer, engineer, programmer, technical 51 | writer or other person who contributed to the Font Software. 52 | 53 | PERMISSION & CONDITIONS 54 | Permission is hereby granted, free of charge, to any person obtaining 55 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 56 | redistribute, and sell modified and unmodified copies of the Font 57 | Software, subject to the following conditions: 58 | 59 | 1) Neither the Font Software nor any of its individual components, 60 | in Original or Modified Versions, may be sold by itself. 61 | 62 | 2) Original or Modified Versions of the Font Software may be bundled, 63 | redistributed and/or sold with any software, provided that each copy 64 | contains the above copyright notice and this license. These can be 65 | included either as stand-alone text files, human-readable headers or 66 | in the appropriate machine-readable metadata fields within text or 67 | binary files as long as those fields can be easily viewed by the user. 68 | 69 | 3) No Modified Version of the Font Software may use the Reserved Font 70 | Name(s) unless explicit written permission is granted by the corresponding 71 | Copyright Holder. This restriction only applies to the primary font name as 72 | presented to the users. 73 | 74 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 75 | Software shall not be used to promote, endorse or advertise any 76 | Modified Version, except to acknowledge the contribution(s) of the 77 | Copyright Holder(s) and the Author(s) or with their explicit written 78 | permission. 79 | 80 | 5) The Font Software, modified or unmodified, in part or in whole, 81 | must be distributed entirely under this license, and must not be 82 | distributed under any other license. The requirement for fonts to 83 | remain under this license does not apply to any document created 84 | using the Font Software. 85 | 86 | TERMINATION 87 | This license becomes null and void if any of the above conditions are 88 | not met. 89 | 90 | DISCLAIMER 91 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 92 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 93 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 94 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 95 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 96 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 97 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 98 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 99 | OTHER DEALINGS IN THE FONT SOFTWARE. 100 | -------------------------------------------------------------------------------- /roguelike/chapters/Chapter_2-World-Map.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 - World Map # 2 | 3 | By the end of this chapter we will have taken our plain, simple window and create a World Map which 4 | we can play with. 5 | 6 | The Map will consist of a 16x16 grid of tiles, and a tile will have a set of rules associated with it, 7 | such as whether or not the player can pass through it. 8 | 9 | Before we start on making tiles, let's clean up some of the code we already have. 10 | Firstly, since we are using colours so much, lets define them specifically: 11 | ``` rust 12 | type Colour = [f32; 4]; 13 | 14 | ``` 15 | In essence this allows us to use the word `Colour` when we want to have an array of 4 32-bit floating point numbers. 16 | 17 | Secondly, we should make all of our previously defined colours as constants so that they can be used anywhere within the program by moving them from main, and placing them at the top of our program: 18 | 19 | ```rust 20 | const RED: Colour = [1.0, 0.0, 0.0, 1.0]; 21 | const GREEN: Colour = [0.0, 1.0, 0.0, 1.0]; 22 | const BLUE: Colour = [0.0, 0.0, 1.0, 1.0]; 23 | const WHITE: Colour = [1.0; 4]; 24 | const BLACK: Colour = [0.0, 0.0, 0.0, 1.0]; 25 | ``` 26 | Finally we should scale the size of our world, by the size of our window and the size of each tile, which means that we will also define the size of the window and the size of each tile: 27 | 28 | ```rust 29 | const WINDOW_SIZE: i32 = 512; 30 | const PIXEL_SIZE: f64 = 32.0; 31 | const WORLD_SIZE: i32 = WINDOW_SIZE / PIXEL_SIZE as i32; 32 | ``` 33 | 34 | The last line is saying that we want to cast `PIXEL_SIZE` as an `i32` number so that we can use it in the calculation. 35 | 36 | ## Tiles ## 37 | 38 | Now that that has been set up we can now define what a `Tile` is! 39 | For now, a `Tile` is a `struct` that contains a single variable: 40 | *`colour` which is of type `Colour` (as we previously defined) and determines what colour we should display it as 41 | 42 | So now we have defined what the `Tile struct`, we will need to implement how we can use it like so: 43 | 44 | ```rust 45 | impl Tile { 46 | pub fn empty() -> Self { 47 | Tile { 48 | colour: WHITE, 49 | } 50 | } 51 | 52 | pub fn wall() -> Self { 53 | Tile { 54 | colour: BLACK, 55 | } 56 | } 57 | } 58 | ``` 59 | This is simply saying that there are 2 types of tiles, `empty` and `wall` which correspond to an empty space or a wall respectively, and they also have different colours to represent that. 60 | 61 | Finally to finish off our tiles, we will need to derive the `Clone` trait for our `struct Tile`, we can do that by adding the following line above our `struct` definition: 62 | ```rust 63 | #[derive(Clone)] 64 | ``` 65 | 66 | ## Map ## 67 | So now we have tiles which we can use to fill up our map with, but what is a map? It's a 2D array of 68 | `Tiles`, or a `Vec` (vector) of `Vec` of `Tile`. Like how we defined the `Colour` type, we can do tthe same for the `Map`: 69 | 70 | ```rust 71 | type Map = Vec>; 72 | ``` 73 | 74 | For simplicity's sake, lets make a function (`fn`) to initialise our map, called `make_map()` and it returns a `Map`. Fill the body of your function with the following: 75 | 76 | ```rust 77 | let mut map = vec![vec![Tile::empty(); WORLD_SIZE as usize]; WORLD_SIZE as usize]; 78 | map[WORLD_SIZE as usize / 2][WORLD_SIZE as usize / 2] = Tile::wall(); 79 | map 80 | ``` 81 | 82 | What the first line does is define a mutable variable map which is made of a vector `vec!` of vectors which is initialised with `WORLD_SIZE` many `empty` tiles. 83 | The Second line just sets the middle square to be a `wall` so that we can see that everything is working correctly. 84 | Finall the last line allows us to return this newly created `map`. 85 | 86 | ## Rendering the Map ## 87 | 88 | We are nearly done now, all thats left is to create and output our map! 89 | To start with we will need to create an instance of our map in our `main()` function: 90 | 91 | ```rust 92 | //TODO 93 | let map = BLANK; 94 | ``` 95 | 96 | Now in our render function that we had earlier, we want to loop through from `0` to `WORLD_SIZE` twice so that we can access all of our squares: 97 | 98 | ``` rust 99 | 100 | for i in BLANK .. BLANK { 101 | for j in BLANK .. BLANK { 102 | 103 | } 104 | } 105 | ``` 106 | 107 | We have the loop structure set up so that we can access every tile in our world, now we need to decide what to do with each tile. 108 | Each tile is an instance of a `graphics::Rectangle`. 109 | To `draw` a rectangle, you will see in that you need have it's position. We can define that like so: 110 | 111 | ```rust 112 | let pos: [f64;4] = PIXEL_SIZE * i as f64, 113 | PIXEL_SIZE * j as f64, 114 | PIXEL_SIZE * (i + 1) as f64, 115 | PIXEL_SIZE * (j + 1) as f64, 116 | ] 117 | ``` 118 | The code above defines the position of the corners of our tile. Now that we have that, we can actually draw our tile: 119 | 120 | ```rust 121 | graphics::Rectangle::new(BLANK.colour).draw( 122 | BLANK, 123 | &c.draw_state, 124 | c.transform, 125 | g, 126 | ); 127 | ``` 128 | Try to think about what the first `BLANK` here represents. We want it to be the colour of the `Tile` at that `i`, `j` coordinate in our `map`. The second `BLANK` is the `pos` of our `Tile`. 129 | 130 | When you run this, your window should now be entirely white with a single black square in it meaning that our map has been correctly rendered! 131 | 132 | ## Chapter 3 - Player ## 133 | 134 | Follow the link to go to Chapter 3, which goes through creating a player character. 135 | 136 | [Chapter 3 - Player](Chapter_3-Player.md) 137 | -------------------------------------------------------------------------------- /sudoku/chp-04.md: -------------------------------------------------------------------------------- 1 | # Sudoku tutorial 2 | by Sven Nilsen, 2017 3 | 4 | ## Chapter 4 5 | 6 | Before we do anything, we will force ourselves to document the code. 7 | Add `#![deny(missing_docs)]` to the top of "src/main.rs" and a doc comment: 8 | 9 | ```rust 10 | #![deny(missing_docs)] 11 | 12 | //! A Sudoku game. 13 | 14 | extern crate piston; 15 | extern crate glutin_window; 16 | extern crate graphics; 17 | extern crate opengl_graphics; 18 | ``` 19 | 20 | The `//!` is used on doc comments that comment the object they are inside. 21 | When commenting something from the outside, you use `///`. 22 | 23 | Create a new file in the "src/" directory called "gameboard.rs". 24 | Add the following code: 25 | 26 | ```rust 27 | //! Game board logic. 28 | 29 | /// Size of game board. 30 | const SIZE: usize = 9; 31 | 32 | /// Stores game board information. 33 | pub struct Gameboard { 34 | /// Stores the content of the cells. 35 | /// `0` is an empty cell. 36 | pub cells: [[u8; SIZE]; SIZE], 37 | } 38 | 39 | impl Gameboard { 40 | /// Creates a new game board. 41 | pub fn new() -> Gameboard { 42 | Gameboard { 43 | cells: [[0; SIZE]; SIZE], 44 | } 45 | } 46 | } 47 | ``` 48 | 49 | Import the `Gameboard` struct in "main.rs": 50 | 51 | ```rust 52 | pub use crate::gameboard::Gameboard; 53 | 54 | mod gameboard; 55 | ``` 56 | 57 | Create a new file in the "src/" directory called "gameboard_controller.rs". 58 | Add the following code: 59 | 60 | ```rust 61 | //! Gameboard controller. 62 | 63 | use piston::input::GenericEvent; 64 | 65 | use crate::Gameboard; 66 | 67 | /// Handles events for Sudoku game. 68 | pub struct GameboardController { 69 | /// Stores the gameboard state. 70 | pub gameboard: Gameboard, 71 | } 72 | 73 | impl GameboardController { 74 | /// Creates a new gameboard controller. 75 | pub fn new(gameboard: Gameboard) -> GameboardController { 76 | GameboardController { 77 | gameboard: gameboard, 78 | } 79 | } 80 | 81 | /// Handles events. 82 | pub fn event(&mut self, e: &E) { 83 | 84 | } 85 | } 86 | ``` 87 | 88 | A controller is an object that handles events and manipulates some data. 89 | The data manipulated by a controller is called a "model". 90 | By separating model and controller you can reuse the model in other projects. 91 | 92 | Import `GameboardController` in "main.rs": 93 | 94 | ```rust 95 | pub use crate::gameboard::Gameboard; 96 | pub use crate::gameboard_controller::GameboardController; 97 | 98 | mod gameboard; 99 | mod gameboard_controller; 100 | ``` 101 | 102 | Create a new file in the "src/" directory called "gameboard_view.rs". 103 | Add the following code: 104 | 105 | ```rust 106 | //! Gameboard view. 107 | 108 | use graphics::types::Color; 109 | use graphics::{Context, Graphics}; 110 | 111 | use crate::gameboard_controller::GameboardController; 112 | 113 | /// Stores gameboard view settings. 114 | pub struct GameboardViewSettings { 115 | /// Position from left-top corner. 116 | pub position: [f64; 2], 117 | /// Size of gameboard along horizontal and vertical edge. 118 | pub size: f64, 119 | /// Background color. 120 | pub background_color: Color, 121 | /// Border color. 122 | pub border_color: Color, 123 | /// Edge color around the whole board. 124 | pub board_edge_color: Color, 125 | /// Edge color between the 3x3 sections. 126 | pub section_edge_color: Color, 127 | /// Edge color between cells. 128 | pub cell_edge_color: Color, 129 | /// Edge radius around the whole board. 130 | pub board_edge_radius: f64, 131 | /// Edge radius between the 3x3 sections. 132 | pub section_edge_radius: f64, 133 | /// Edge radius between cells. 134 | pub cell_edge_radius: f64, 135 | } 136 | 137 | impl GameboardViewSettings { 138 | /// Creates new gameboard view settings. 139 | pub fn new() -> GameboardViewSettings { 140 | GameboardViewSettings { 141 | position: [10.0; 2], 142 | size: 400.0, 143 | background_color: [0.8, 0.8, 1.0, 1.0], 144 | border_color: [0.0, 0.0, 0.2, 1.0], 145 | board_edge_color: [0.0, 0.0, 0.2, 1.0], 146 | section_edge_color: [0.0, 0.0, 0.2, 1.0], 147 | cell_edge_color: [0.0, 0.0, 0.2, 1.0], 148 | board_edge_radius: 3.0, 149 | section_edge_radius: 2.0, 150 | cell_edge_radius: 1.0, 151 | } 152 | } 153 | } 154 | 155 | /// Stores visual information about a gameboard. 156 | pub struct GameboardView { 157 | /// Stores gameboard view settings. 158 | pub settings: GameboardViewSettings, 159 | } 160 | 161 | impl GameboardView { 162 | /// Creates a new gameboard view. 163 | pub fn new(settings: GameboardViewSettings) -> GameboardView { 164 | GameboardView { 165 | settings: settings, 166 | } 167 | } 168 | 169 | /// Draw gameboard. 170 | pub fn draw(&self, controller: &GameboardController, c: &Context, g: &mut G) { 171 | 172 | } 173 | } 174 | ``` 175 | 176 | Import `GameboardView` in "main.rs": 177 | 178 | ```rust 179 | pub use crate::gameboard::Gameboard; 180 | pub use crate::gameboard_controller::GameboardController; 181 | pub use crate::gameboard_view::{GameboardView, GameboardViewSettings}; 182 | 183 | mod gameboard; 184 | mod gameboard_controller; 185 | mod gameboard_view; 186 | ``` 187 | 188 | Set up objects and handle events: 189 | 190 | ```rust 191 | let mut events = Events::new(EventSettings::new().lazy(true)); 192 | let mut gl = GlGraphics::new(opengl); 193 | 194 | let gameboard = Gameboard::new(); 195 | let mut gameboard_controller = GameboardController::new(gameboard); 196 | let gameboard_view_settings = GameboardViewSettings::new(); 197 | let gameboard_view = GameboardView::new(gameboard_view_settings); 198 | 199 | while let Some(e) = events.next(&mut window) { 200 | gameboard_controller.event(&e); 201 | if let Some(args) = e.render_args() { 202 | gl.draw(args.viewport(), |c, g| { 203 | use graphics::{clear}; 204 | 205 | clear([1.0; 4], g); 206 | gameboard_view.draw(&gameboard_controller, &c, g); 207 | }); 208 | } 209 | } 210 | ``` 211 | 212 | The call `gameboard_controller.event(&e);` passes events to the controller. 213 | 214 | The call `gameboard_view.draw(&gameboard_controller, &c, g);` 215 | renders the gameboard. 216 | 217 | [Goto Chapter 5](chp-05.md) 218 | -------------------------------------------------------------------------------- /sudoku/src/gameboard_view.rs: -------------------------------------------------------------------------------- 1 | //! Gameboard view. 2 | 3 | use graphics::types::Color; 4 | use graphics::character::CharacterCache; 5 | use graphics::{Context, Graphics}; 6 | 7 | use crate::GameboardController; 8 | 9 | /// Stores gameboard view settings. 10 | pub struct GameboardViewSettings { 11 | /// Position from left-top corner. 12 | pub position: [f64; 2], 13 | /// Size of gameboard along horizontal and vertical edge. 14 | pub size: f64, 15 | /// Background color. 16 | pub background_color: Color, 17 | /// Border color. 18 | pub border_color: Color, 19 | /// Edge color around the whole board. 20 | pub board_edge_color: Color, 21 | /// Edge color between the 3x3 sections. 22 | pub section_edge_color: Color, 23 | /// Edge color between cells. 24 | pub cell_edge_color: Color, 25 | /// Edge radius around the whole board. 26 | pub board_edge_radius: f64, 27 | /// Edge radius between the 3x3 sections. 28 | pub section_edge_radius: f64, 29 | /// Edge radius between cells. 30 | pub cell_edge_radius: f64, 31 | /// Selected cell background color. 32 | pub selected_cell_background_color: Color, 33 | /// Text color. 34 | pub text_color: Color, 35 | } 36 | 37 | impl GameboardViewSettings { 38 | /// Creates new gameboard view settings. 39 | pub fn new() -> GameboardViewSettings { 40 | GameboardViewSettings { 41 | position: [10.0; 2], 42 | size: 400.0, 43 | background_color: [0.8, 0.8, 1.0, 1.0], 44 | border_color: [0.0, 0.0, 0.2, 1.0], 45 | board_edge_color: [0.0, 0.0, 0.2, 1.0], 46 | section_edge_color: [0.0, 0.0, 0.2, 1.0], 47 | cell_edge_color: [0.0, 0.0, 0.2, 1.0], 48 | board_edge_radius: 3.0, 49 | section_edge_radius: 2.0, 50 | cell_edge_radius: 1.0, 51 | selected_cell_background_color: [0.9, 0.9, 1.0, 1.0], 52 | text_color: [0.0, 0.0, 0.1, 1.0], 53 | } 54 | } 55 | } 56 | 57 | /// Stores visual information about a gameboard. 58 | pub struct GameboardView { 59 | /// Stores gameboard view settings. 60 | pub settings: GameboardViewSettings, 61 | } 62 | 63 | impl GameboardView { 64 | /// Creates a new gameboard view. 65 | pub fn new(settings: GameboardViewSettings) -> GameboardView { 66 | GameboardView { 67 | settings: settings, 68 | } 69 | } 70 | 71 | /// Draw gameboard. 72 | pub fn draw( 73 | &self, 74 | controller: &GameboardController, 75 | glyphs: &mut C, 76 | c: &Context, 77 | g: &mut G 78 | ) 79 | where C: CharacterCache 80 | { 81 | use graphics::{Image, Line, Rectangle, Transformed}; 82 | 83 | let ref settings = self.settings; 84 | let board_rect = [ 85 | settings.position[0], settings.position[1], 86 | settings.size, settings.size, 87 | ]; 88 | 89 | // Draw board background. 90 | Rectangle::new(settings.background_color) 91 | .draw(board_rect, &c.draw_state, c.transform, g); 92 | 93 | // Draw selected cell background. 94 | if let Some(ind) = controller.selected_cell { 95 | let cell_size = settings.size / 9.0; 96 | let pos = [ind[0] as f64 * cell_size, ind[1] as f64 * cell_size]; 97 | let cell_rect = [ 98 | settings.position[0] + pos[0], settings.position[1] + pos[1], 99 | cell_size, cell_size 100 | ]; 101 | Rectangle::new(settings.selected_cell_background_color) 102 | .draw(cell_rect, &c.draw_state, c.transform, g); 103 | } 104 | 105 | // Draw characters. 106 | let text_image = Image::new_color(settings.text_color); 107 | let cell_size = settings.size / 9.0; 108 | for j in 0..9 { 109 | for i in 0..9 { 110 | if let Some(ch) = controller.gameboard.char([i, j]) { 111 | let pos = [ 112 | settings.position[0] + i as f64 * cell_size + 15.0, 113 | settings.position[1] + j as f64 * cell_size + 34.0 114 | ]; 115 | if let Ok(character) = glyphs.character(34, ch) { 116 | let ch_x = pos[0] + character.left(); 117 | let ch_y = pos[1] - character.top(); 118 | text_image.draw(character.texture, 119 | &c.draw_state, 120 | c.transform.trans(ch_x, ch_y), 121 | g); 122 | } 123 | } 124 | } 125 | } 126 | 127 | // Draw cell borders. 128 | let cell_edge = Line::new(settings.cell_edge_color, settings.cell_edge_radius); 129 | for i in 0..9 { 130 | // Skip lines that are covered by sections. 131 | if (i % 3) == 0 {continue;} 132 | 133 | let x = settings.position[0] + i as f64 / 9.0 * settings.size; 134 | let y = settings.position[1] + i as f64 / 9.0 * settings.size; 135 | let x2 = settings.position[0] + settings.size; 136 | let y2 = settings.position[1] + settings.size; 137 | 138 | let vline = [x, settings.position[1], x, y2]; 139 | cell_edge.draw(vline, &c.draw_state, c.transform, g); 140 | 141 | let hline = [settings.position[0], y, x2, y]; 142 | cell_edge.draw(hline, &c.draw_state, c.transform, g); 143 | } 144 | 145 | // Draw section borders. 146 | let section_edge = Line::new(settings.section_edge_color, settings.section_edge_radius); 147 | for i in 0..3 { 148 | // Set up coordinates. 149 | let x = settings.position[0] + i as f64 / 3.0 * settings.size; 150 | let y = settings.position[1] + i as f64 / 3.0 * settings.size; 151 | let x2 = settings.position[0] + settings.size; 152 | let y2 = settings.position[1] + settings.size; 153 | 154 | let vline = [x, settings.position[1], x, y2]; 155 | section_edge.draw(vline, &c.draw_state, c.transform, g); 156 | 157 | let hline = [settings.position[0], y, x2, y]; 158 | section_edge.draw(hline, &c.draw_state, c.transform, g); 159 | } 160 | 161 | // Draw board edge. 162 | Rectangle::new_border(settings.board_edge_color, settings.board_edge_radius) 163 | .draw(board_rect, &c.draw_state, c.transform, g); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /advanced/events.md: -------------------------------------------------------------------------------- 1 | # Advanced Piston 2 | 3 | ## Chapter 1 - Events 4 | 5 | ### Imports 6 | 7 | - The Piston core libraries 8 | - Used by generic libraries 9 | - `use input::*;` 10 | - The [piston](https://crates.io/crates/piston) crate 11 | - Reexports core libraries 12 | - Used by generic applications 13 | - `use piston::input::*;` 14 | - Window wrappers such as [piston_window](https://crates.io/crates/piston_window) 15 | - Used for convenience 16 | - Should not be depended on by libraries, because they choose a specific backend 17 | - `use piston_window::*;` 18 | 19 | The word "generic" is used to describe something that does not depend on a specific platform, operating system or data structure. 20 | 21 | ### Core libraries 22 | 23 | Piston consists of 3 small core libraries. 24 | The core libraries only have one purpose: To describe events, how they are retrieved from the window and how they behave in an event loop. 25 | Piston uses events to tell the application when to update, render and handle user input. 26 | 27 | - [pistoncore-input](https://crates.io/crates/pistoncore-input) 28 | - [pistoncore-window](https://crates.io/crates/pistoncore-window) 29 | - [pistoncore-event_loop](https://crates.io/crates/pistoncore-event_loop) 30 | 31 | These 3 libraries are reexported in the [piston](https://crates.io/crates/piston) crate. 32 | 33 | Reasons Piston uses a modular core: 34 | 35 | 1. Less maintenance when there are breaking changes 36 | 2. Customized design for windows and event loops 37 | 38 | ### How to handle events 39 | 40 | The recommended way of handling events is to import event traits. 41 | These are defined in the [pistoncore-input](https://crates.io/crates/pistoncore-input) crate. 42 | 43 | For example: 44 | 45 | ```rust 46 | use input::{RenderEvent, UpdateEvent}; 47 | 48 | while let Some(e) = events.next(&mut window) { 49 | // Closure style. 50 | e.render(|args| { 51 | ... 52 | }); 53 | // `if let` style. 54 | if let Some(args) = e.render_args() { 55 | ... 56 | } 57 | } 58 | ``` 59 | 60 | The reason to use event traits instead of matching on the `Input` structure, 61 | is that in generic code an event can be anything that implements the `GenericEvent` trait. 62 | 63 | Some libraries handles events for you, for example from the [piston_window](https://crates.io/crates/piston_window) crate: 64 | 65 | ```rust 66 | // Calls the closure on render events. 67 | window.draw_2d(&e, |c, g| { 68 | ... 69 | }); 70 | ``` 71 | 72 | A common trick used by such methods, is a generic return value `U` from the closure that is wrapped into an `Option`. 73 | If it returns `None`, the closure was not called. 74 | 75 | ### Window events 76 | 77 | When the user presses a key or scrolls with the mouse, 78 | this information is registered by the operating system and put in an event queue. 79 | The window manager keeps track of which events should be sent to window. 80 | On platforms with a mouse cursor device, the window manager keeps track of a focused window. 81 | Events, such as mouse coordinates, are sent to the currently active window. 82 | 83 | The window backend interfaces with the operating system and knows how to poll out 84 | events from the queue. 85 | A window backend consists of 2 things: 86 | 87 | - A window API library (e.g. [Glutin](https://crates.io/crates/glutin), [Glfw](https://crates.io/crates/glfw), [SDL2](https://crates.io/crates/sdl2)) 88 | - An window backend for Piston (e.g. [glutin_window](https://crates.io/crates/pistoncore-glutin_window), [glfw_window](https://crates.io/crates/pistoncore-glfw_window), [sdl2_window](https://crates.io/crates/pistoncore-sdl2_window)) 89 | 90 | Notice that window backends for Piston are prefixed with "pistoncore-", 91 | such that if you depend on e.g. "glutin_window" you write "pistoncore-glutin_window" in the Cargo.toml. 92 | The prefix "pistoncore-" is used on libraries that are often required to make an application up and running. 93 | 94 | In Piston, a window backend is a struct that implements the traits `Window` and `AdvancedWindow`. 95 | Those traits are defined in the [pistoncore-window](https://crates.io/crates/pistoncore-window) crate. 96 | The `Window` trait specifies the minimum requirements to get a game loop working. 97 | The `AdvancedWindow` trait is a fully supported window backend and includes all features that are supported in Piston. 98 | 99 | Piston also has a `NoWindow` implementation that can be used in game servers. 100 | This is included in the [pistoncore-window](https://crates.io/crates/pistoncore-window) crate. 101 | 102 | ### Update and render 103 | 104 | The event loop is an algorithm that makes sure to shedule update and render events at the right moments. 105 | These are timed to follow updates per second (UPS) and frames per second (FPS) respectively. 106 | 107 | - Updating is what the application "does", measured by UPS 108 | - Rendering is how the application "looks", measured by FPS 109 | 110 | When you are deciding which UPS and FPS to use, there is a tradeoff between energy usage, accuracy and graphics. 111 | In advanced applications, you might change these settings on the fly to spend less energy, e.g. when 112 | the window is not in focus. 113 | 114 | The way you set UPS and FPS is though `EventSettings`, defined in [pistoncore-event_loop](https://crates.io/crates/pistoncore-event_loop). 115 | 116 | Update events in Piston uses fixed time steps, such that an application using no inputs or random events is deterministic. 117 | This means you can run the application multiple times and expect the exact same behavior. 118 | By default, the updates per second (UPS) are set to 120, which is usually good enough for VR and first person shooters. 119 | For simple games, such as tetris, this is probably not needed, so you can set it lower to e.g. 20 or 30. 120 | 121 | Render events in Piston can slip over time when rendering takes too long. 122 | This means the event loop will not try to catch up. 123 | You should not put update logic in the render event because it will lead to undeterministic behavior. 124 | 125 | ### Benchmark mode 126 | 127 | `EventSettings::bench_mode` is a flag that tells the event loop to ignore user input 128 | and run updates and rendering as fast as it can, while pretending that update and render events are perfect. 129 | This is used to benchmark the performance of the whole game engine and give reliable results. 130 | A common technique is to make the application run e.g. 1000 render frames and then exit the loop. 131 | 132 | In the terminal window you can type `time ` to measure how long time the program takes to run. 133 | A standard test in the Piston project is to run 3 times and delete the slowest result. 134 | This reduces inaccuracy caused by background processes in the operating system. 135 | -------------------------------------------------------------------------------- /roguelike/chapters/Chapter_1-Graphics.md: -------------------------------------------------------------------------------- 1 | # Chapter 1 - Graphics # 2 | 3 | By the end of this chapter you will have a simple window which will be the 4 | foundation of our game! 5 | 6 | ## Window ## 7 | We want to create a window, and have it display a blank, white screen. To start 8 | with let's add the piston crate. 9 | 10 | On your terminal, type the following: 11 | `cargo add piston` 12 | 13 | If you look into you `Cargo.toml` file, underneath `[dependencies]`, you should 14 | now see: `piston = "0.49.0"` (or whatever the most recent version of piston 15 | is). 16 | 17 | Following on from this, you will want to add the GlutinWindow crate to your 18 | project like so: 19 | 20 | `cargo add pistoncore-glutin_window` 21 | 22 | Which again will add the most recent version of `glutin_window` to your 23 | dependency list. 24 | 25 | Now we can create a window which will allow us to run the game! 26 | To do this, **you** will need to create some Settings for your window: 27 | 28 | ``` rust 29 | //TODO 30 | let settings = WindowSettings::new(BLANK, BLANK).exit_on_exc(true); 31 | ``` 32 | Look through the generated documents and it should guide you on what to replace 33 | the `BLANK` spaces with (**hint: there are 2 BLANKS for a reason!**). 34 | 35 | Now this is not going to run, even though we have added the dependencies to our 36 | `Cargo.toml` file, we aren't actually using them, so let's do that! 37 | Add the following lines of code to the top of your file: 38 | 39 | ``` rust 40 | extern crate piston; 41 | use piston::WindowSettings; 42 | ``` 43 | 44 | Now that we have created our WindowSettings, let's create our Window itself. 45 | To do this, we will need to add and use the `glutin_window` crate by adding the 46 | following lines to the top of your file: 47 | 48 | ``` rust 49 | extern crate glutin_window; 50 | use glutin_window::GlutinWindow; 51 | ``` 52 | 53 | Now that the crate is available to use, let's actually use it and do something 54 | with it! 55 | 56 | To do this we are now going to want to create a window itself: 57 | 58 | `let mut window: GlutinWindow = settings.build().expect("Could not create window");` 59 | 60 | What this does is create a Window and, if there is an error creating is, will 61 | report the error message, otherwise it will return the window. 62 | 63 | **NB:** that we are using a `mut window`, the reason for using the keyword `mut` 64 | is because by default, everything in Rust is *immutable* which means that it 65 | cannot be changed, but with our window, we are going to want to change it 66 | constantly so that it can reflect the current state of our game and so it needs 67 | to be *mutable* hence the `mut` keyword. 68 | 69 | If you run this now you will see that the window appears and then disappears 70 | immediately! This happens because the program finishes and so it closes 71 | everything. 72 | 73 | ## Colours ## 74 | Now that we have a window, we will want to add some colours to it so that it 75 | looks nicer than a simple black screen. To start with, let's define a colour 76 | that you want to be the background colour. In Rust this is represented by 77 | an array of 4 numbers, which represent the R, G, B and Opacity channels. 78 | For convenience sake, I am going to provide you will 4 basic colours, and you 79 | can play around with values yourself if there is a particular colour you 80 | prefer. 81 | 82 | ``` rust 83 | let RED = [1.0, 0.0, 0.0, 1.0]; 84 | let GREEN = [0.0, 1.0, 0.0, 1.0]; 85 | let BLUE = [0.0, 0.0, 1.0, 1.0]; 86 | let WHITE = [1.0;4]; 87 | ``` 88 | 89 | 90 | The last line, `let WHITE = [1.0;4];` means that we want and array which stores the 91 | value `1.0`, `4` times. 92 | 93 | ## Events ## 94 | We now have a set of colours that we want to display as our background, so lets 95 | actually do that. 96 | 97 | To start with, lets set up the structure so that we can continually render our 98 | window, to do this we will need to `use` the following from the `piston` 99 | library: 100 | + `RenderEvent` 101 | 102 | We will also need to use some classes from the piston event loop like so: 103 | `use piston::event_loop::{EventSettings, Events, EventLoop};` 104 | 105 | Now we have access to game `Events`, we want to create a `new` event. If we 106 | look into the docs we can see that we can create an `Event` by using its 107 | constructor function `new` (**HINT:** the same thing can be done for 108 | `EventSettings`). 109 | 110 | `//TODO 111 | let mut events = Events::BLANK();` 112 | 113 | We now have an object that will allow us to get and handle a variety of events, 114 | so lets do that: 115 | 116 | `//TODO 117 | while let Some(e) = events.next(&mut BLANK){}` 118 | 119 | Again, fill in the `BLANK` with what's appropriate by looking at the docs. 120 | This now will create an infinite loop which will constantly poll the system 121 | getting events for us. 122 | 123 | The `Event` that we are most interested in is the `RenderEvent` since it allows 124 | us to change what's on the screen. 125 | How we handle this is by checking to see whether or not our `Event` is in fact 126 | a `RenderEvent` by using an if statement like this within the `while`loop: 127 | 128 | `if let Some(r) = e.render_args(){}` 129 | 130 | This creates an object that will allow us to 131 | 132 | ## Adding Graphics ## 133 | 134 | Now execute the following line in your terminal: 135 | `cargo add piston2d-graphics 136 | cargo add piston2d-opengl_graphics` 137 | To automatically add the crates we need for creating images to put onto our 138 | window to the dependency list. 139 | 140 | Now that these dependencies have been added, lets bring them into the game: 141 | `extern crate graphics; 142 | extern crate opengl_graphics;` 143 | 144 | This time we only need to `use` `GlGraphics` and `OpenGL` from 145 | `opengl_graphics`, so make sure to add that at the top of your file. 146 | 147 | Now that we can, let's go about using these two things, to start with let's 148 | define a variable to be the version of `OpenGL` that we want to use: 149 | `let opengl = OpenGL::BLANK;` 150 | 151 | Replace `BLANK` with whatever version you want to use, for example `V3_2` 152 | 153 | Now we want to create a `new` object that will handle all of our graphics and uses 154 | `OpenGL`: 155 | `let mut gl = GlGraphics::BLANK();` 156 | 157 | Use the docs to fill in the `BLANK` space. 158 | 159 | ## Rendering ## 160 | 161 | Finally we are now able to change the background of our window! The code to do 162 | this sits within the `if` statement we made earlier for the `RenderEvents`: 163 | 164 | ``` rust 165 | gl.draw(r.viewport(), |_c, g| { 166 | graphics::clear(YOUR_COLOUR, g); 167 | }); 168 | ``` 169 | 170 | If you replace `YOUR_COLOUR` with whatever colour you want run the code! 171 | 172 | ## Chapter 2 - World Map ## 173 | 174 | Follow the link to go to Chapter 2, which allows us to create a world map for our game. 175 | 176 | [Chapter 2 - World Map](Chapter_2-World-Map.md) 177 | -------------------------------------------------------------------------------- /getting-started/readme.md: -------------------------------------------------------------------------------- 1 | ## Getting Started Spinning Square 2 | ### A spinning square 3 | 4 | [![Build Status](https://travis-ci.org/PistonDevelopers/Piston-Tutorials.svg?branch=master)](https://travis-ci.org/PistonDevelopers/Piston-Tutorials) 5 | 6 | In this tutorial, I hope to get you from an empty Cargo project to having a 7 | window with a rotating square in it. 8 | This tutorial does ___not___ explain concepts used in the game, as those 9 | will be covered by other tutorials. 10 | This tutorial only covers project setup and contains a sample "game" simply 11 | to test the build environment. 12 | 13 | 14 | I assume that you have installed Rust and Cargo, and have already built a 15 | hello-world project with Cargo. 16 | If you haven't met these criteria, please read the first few chapters of 17 | [The Rust Book](http://doc.rust-lang.org/book/) and come back once 18 | you've finished. 19 | 20 | ![Result](./out.gif) 21 | 22 | #### At this stage 23 | 24 | * You should be able to run the command `rustc -V` 25 | * You should be able to run the command `cargo -V` 26 | 27 | If you have failed either of these, please review the getting started 28 | guide and make sure that you have the latest versions of `rustc` and `cargo`. 29 | 30 | ## Installing Dependencies 31 | 32 | Parts of the Piston project depend on native C libraries. For example, in 33 | order to display a window and hook it up to an OpenGL context, we can use 34 | either Glutin, GLFW or SDL2 as the implementation of the windowing system. 35 | 36 | The rest of this tutorial uses Glutin for windowing, so we won't need to 37 | directly install any additional libraries for that purpose. 38 | 39 | ## Setting Up The Project 40 | 41 | If everything is set up correctly, it's time to create a Cargo project 42 | and specify dependencies. 43 | 44 | 45 | ```bash 46 | cargo new --bin getting-started 47 | cd getting-started 48 | ``` 49 | 50 | Now in your favorite editor, add project settings and dependencies to 51 | `Cargo.toml`. 52 | 53 | ```toml 54 | [package] 55 | 56 | name = "spinning-square" 57 | version = "0.1.0" 58 | authors = [ 59 | "TyOverby ", 60 | "Nikita Pekin " 61 | ] 62 | 63 | [[bin]] 64 | name = "spinning-square" 65 | 66 | [dependencies] 67 | piston = "0.50.0" 68 | piston2d-graphics = "0.36.0" 69 | pistoncore-glutin_window = "0.64.0" 70 | piston2d-opengl_graphics = "0.72.0" 71 | 72 | ``` 73 | 74 | You might be thinking that this is a lot of dependencies for such a simple 75 | example application. 76 | This is because of how the Piston Projects are organized. 77 | The `piston` and `graphics` libraries are able to do a lot of work by 78 | themselves, but they are made to be completely independent of a 79 | backing implementation. 80 | For example, when it comes to displaying a window and getting keyboard events 81 | in a cross-platform manner, you can use either Glutin, GLFW or SDL2. 82 | GLFW and SDL2 are both C and C++ cross-platform libraries for creating windows 83 | with an OpenGL context. Glutin - pure Rust alternative. 84 | In this tutorial I chose Glutin, so you will notice that in the cargo file, we 85 | imported `glutin_window`. 86 | `opengl_graphics` is another backend that implements the interface defined in 87 | `graphics`. 88 | `graphics` is a 2d graphics API that doesn't care about how things are 89 | *actually* drawn to the screen. 90 | If you implement the `graphics` interface yourself, you could route it 91 | through directx, or render straight to a png. 92 | In this tutorial, we are rendering using OpenGL, so we'll use `opengl_graphics`. 93 | 94 | The pattern of "interface" and "backend" is very common with Piston Projects. 95 | While other game engines might encompass lots of functionality, we prefer to have 96 | many libraries that are separate and extendable, but also work well when 97 | combined. 98 | 99 | 100 | ## Writing Some Code 101 | 102 | Ok, time for some game logic. Edit `src/main.rs` in your favorite editor: 103 | 104 | ```rust 105 | extern crate glutin_window; 106 | extern crate graphics; 107 | extern crate opengl_graphics; 108 | extern crate piston; 109 | 110 | use glutin_window::GlutinWindow as Window; 111 | use opengl_graphics::{GlGraphics, OpenGL}; 112 | use piston::event_loop::{EventSettings, Events}; 113 | use piston::input::{RenderArgs, RenderEvent, UpdateArgs, UpdateEvent}; 114 | use piston::window::WindowSettings; 115 | 116 | pub struct App { 117 | gl: GlGraphics, // OpenGL drawing backend. 118 | rotation: f64, // Rotation for the square. 119 | } 120 | 121 | impl App { 122 | fn render(&mut self, args: &RenderArgs) { 123 | use graphics::*; 124 | 125 | const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0]; 126 | const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0]; 127 | 128 | let square = rectangle::square(0.0, 0.0, 50.0); 129 | let rotation = self.rotation; 130 | let (x, y) = (args.window_size[0] / 2.0, args.window_size[1] / 2.0); 131 | 132 | self.gl.draw(args.viewport(), |c, gl| { 133 | // Clear the screen. 134 | clear(GREEN, gl); 135 | 136 | let transform = c 137 | .transform 138 | .trans(x, y) 139 | .rot_rad(rotation) 140 | .trans(-25.0, -25.0); 141 | 142 | // Draw a box rotating around the middle of the screen. 143 | rectangle(RED, square, transform, gl); 144 | }); 145 | } 146 | 147 | fn update(&mut self, args: &UpdateArgs) { 148 | // Rotate 2 radians per second. 149 | self.rotation += 2.0 * args.dt; 150 | } 151 | } 152 | 153 | fn main() { 154 | // Change this to OpenGL::V2_1 if not working. 155 | let opengl = OpenGL::V3_2; 156 | 157 | // Create an Glutin window. 158 | let mut window: Window = WindowSettings::new("spinning-square", [200, 200]) 159 | .graphics_api(opengl) 160 | .exit_on_esc(true) 161 | .build() 162 | .unwrap(); 163 | 164 | // Create a new game and run it. 165 | let mut app = App { 166 | gl: GlGraphics::new(opengl), 167 | rotation: 0.0, 168 | }; 169 | 170 | let mut events = Events::new(EventSettings::new()); 171 | while let Some(e) = events.next(&mut window) { 172 | if let Some(args) = e.render_args() { 173 | app.render(&args); 174 | } 175 | 176 | if let Some(args) = e.update_args() { 177 | app.update(&args); 178 | } 179 | } 180 | } 181 | 182 | ``` 183 | 184 | ## Compiling And Running 185 | 186 | Awesome! Now that we have the game code, let's get it running! 187 | With Cargo, downloading dependencies and building the application is as 188 | simple as running `cargo build` from the main project directory. 189 | 190 | If all goes well, you should have the binary `spinning-square` inside the `target/debug` 191 | directory. 192 | 193 | Run it by executing `cargo run`. 194 | 195 | On your screen you should have a rotating square that looks like this: 196 | 197 | ![Result](./out.gif) 198 | 199 | ## What's Next? 200 | 201 | Take a look at the [piston-examples](https://github.com/pistondevelopers/piston-examples) repository. 202 | -------------------------------------------------------------------------------- /sudoku/chp-06.md: -------------------------------------------------------------------------------- 1 | # Sudoku tutorial 2 | by Sven Nilsen, 2017 3 | 4 | ## Chapter 6 5 | 6 | Now we will render the numbers. 7 | We need two new methods on the `Gameboard` struct: 8 | 9 | ```rust 10 | /// Gets the character at cell location. 11 | pub fn char(&self, ind: [usize; 2]) -> Option { 12 | Some(match self.cells[ind[1]][ind[0]] { 13 | 1 => '1', 14 | 2 => '2', 15 | 3 => '3', 16 | 4 => '4', 17 | 5 => '5', 18 | 6 => '6', 19 | 7 => '7', 20 | 8 => '8', 21 | 9 => '9', 22 | _ => return None, 23 | }) 24 | } 25 | 26 | /// Set cell value. 27 | pub fn set(&mut self, ind: [usize; 2], val: u8) { 28 | self.cells[ind[1]][ind[0]] = val; 29 | } 30 | ``` 31 | 32 | Add a new folder "assets/" to the "sudoku/" project folder. 33 | 34 | Copy "FiraSans-Regular.ttf" and "LICENSE" to the [assets](./assets/) folder. 35 | 36 | Change the `GameboardController::event` to the following: 37 | 38 | ```rust 39 | /// Handles events. 40 | pub fn event(&mut self, pos: [f64; 2], size: f64, e: &E) { 41 | use piston::input::{Button, Key, MouseButton}; 42 | 43 | if let Some(pos) = e.mouse_cursor_args() { 44 | self.cursor_pos = pos; 45 | } 46 | if let Some(Button::Mouse(MouseButton::Left)) = e.press_args() { 47 | // Find coordinates relative to upper left corner. 48 | let x = self.cursor_pos[0] - pos[0]; 49 | let y = self.cursor_pos[1] - pos[1]; 50 | // Check that coordinates are inside board boundaries. 51 | if x >= 0.0 && x <= size && y >= 0.0 && y <= size { 52 | // Compute the cell position. 53 | let cell_x = (x / size * 9.0) as usize; 54 | let cell_y = (y / size * 9.0) as usize; 55 | self.selected_cell = Some([cell_x, cell_y]); 56 | } 57 | } 58 | if let Some(Button::Keyboard(key)) = e.press_args() { 59 | if let Some(ind) = self.selected_cell { 60 | // Set cell value. 61 | match key { 62 | Key::D1 => self.gameboard.set(ind, 1), 63 | Key::D2 => self.gameboard.set(ind, 2), 64 | Key::D3 => self.gameboard.set(ind, 3), 65 | Key::D4 => self.gameboard.set(ind, 4), 66 | Key::D5 => self.gameboard.set(ind, 5), 67 | Key::D6 => self.gameboard.set(ind, 6), 68 | Key::D7 => self.gameboard.set(ind, 7), 69 | Key::D8 => self.gameboard.set(ind, 8), 70 | Key::D9 => self.gameboard.set(ind, 9), 71 | _ => {} 72 | } 73 | } 74 | } 75 | } 76 | ``` 77 | 78 | Import `CharacterCache` in "gameboard_view.rs": 79 | 80 | ```rust 81 | use graphics::character::CharacterCache; 82 | ``` 83 | 84 | Add a new field `text_color` to `GameboardViewSettings`: 85 | 86 | ```rust 87 | /// Selected cell background color. 88 | pub selected_cell_background_color: Color, 89 | /// Text color. 90 | pub text_color: Color, 91 | ``` 92 | 93 | Set the value of `text_color` in `GameboardViewSettings::new`: 94 | 95 | ```rust 96 | selected_cell_background_color: [0.9, 0.9, 1.0, 1.0], 97 | text_color: [0.0, 0.0, 0.1, 1.0], 98 | ``` 99 | 100 | Add a `glyphs` parameter to `GameboardView::draw` and import the trait `Transformed`: 101 | 102 | ```rust 103 | /// Draw gameboard. 104 | pub fn draw( 105 | &self, 106 | controller: &GameboardController, 107 | glyphs: &mut C, 108 | c: &Context, 109 | g: &mut G 110 | ) 111 | where C: CharacterCache 112 | { 113 | use graphics::{Image, Line, Rectangle, Transformed}; 114 | ... 115 | } 116 | ``` 117 | 118 | The trait constraint `CharacterCache` makes sure that 119 | the texture type matches the type used by the graphics backend. 120 | 121 | Draw characters after drawing selected cell background: 122 | 123 | ```rust 124 | // Draw selected cell background. 125 | if let Some(ind) = controller.selected_cell { 126 | let cell_size = settings.size / 9.0; 127 | let pos = [ind[0] as f64 * cell_size, ind[1] as f64 * cell_size]; 128 | let cell_rect = [ 129 | settings.position[0] + pos[0], settings.position[1] + pos[1], 130 | cell_size, cell_size 131 | ]; 132 | Rectangle::new(settings.selected_cell_background_color) 133 | .draw(cell_rect, &c.draw_state, c.transform, g); 134 | } 135 | 136 | // Draw characters. 137 | let text_image = Image::new_color(settings.text_color); 138 | let cell_size = settings.size / 9.0; 139 | for j in 0..9 { 140 | for i in 0..9 { 141 | if let Some(ch) = controller.gameboard.char([i, j]) { 142 | let pos = [ 143 | settings.position[0] + i as f64 * cell_size + 15.0, 144 | settings.position[1] + j as f64 * cell_size + 34.0 145 | ]; 146 | if let Ok(character) = glyphs.character(34, ch) { 147 | let ch_x = pos[0] + character.left(); 148 | let ch_y = pos[1] - character.top(); 149 | let text_image = text_image.src_rect([ 150 | character.atlas_offset[0], 151 | character.atlas_offset[1], 152 | character.atlas_size[0], 153 | character.atlas_size[1], 154 | ]); 155 | text_image.draw(character.texture, 156 | &c.draw_state, 157 | c.transform.trans(ch_x, ch_y), 158 | g); 159 | } 160 | } 161 | } 162 | } 163 | ``` 164 | 165 | The `let ch_y = pos[1] - character.top();` has a negative sign because the 166 | y-axis in font coordinates points upwards while the y-axis in drawing coordinates points downwards. 167 | 168 | In "main.rs", import `Filter`, `TextureSettings` and `GlyphCache`: 169 | 170 | ```rust 171 | use opengl_graphics::{OpenGL, Filter, GlGraphics, GlyphCache, TextureSettings}; 172 | ``` 173 | 174 | Before the event loop, load the font: 175 | 176 | ```rust 177 | let gameboard = Gameboard::new(); 178 | let mut gameboard_controller = GameboardController::new(gameboard); 179 | let gameboard_view_settings = GameboardViewSettings::new(); 180 | let gameboard_view = GameboardView::new(gameboard_view_settings); 181 | 182 | let texture_settings = TextureSettings::new().filter(Filter::Nearest); 183 | let ref mut glyphs = GlyphCache::new("assets/FiraSans-Regular.ttf", (), texture_settings) 184 | .expect("Could not load font"); 185 | ``` 186 | 187 | We use nearest neighbor filter to show sharper glyphs. 188 | Notice `ref mut` can be used to create a mutable reference. 189 | This saves us from putting `&` in front of an argument. 190 | 191 | Pass the `glyphs` object to `gameboard_view.draw`: 192 | 193 | ```rust 194 | gameboard_view.draw(&gameboard_controller, glyphs, &c, g); 195 | ``` 196 | 197 | When typing `cargo run` in the Terminal window, you should be able to fill 198 | in the numbers: 199 | 200 | ![fill in numbers](./images/fill-in-numbers.png) 201 | -------------------------------------------------------------------------------- /sudoku/chp-03.md: -------------------------------------------------------------------------------- 1 | # Sudoku tutorial 2 | by Sven Nilsen, 2017 3 | 4 | ## Chapter 3 5 | 6 | Due to rendering, we need to add a graphics API to our project. 7 | 8 | Piston has a 2D graphics API called Piston-Graphics, 9 | but it is completely separated from the core. 10 | You can choose another graphics API if you want to, 11 | or you can use Piston-Graphics in a project without the Piston core. 12 | 13 | When we started the Piston project, there was no working alternative for 2D 14 | that did not require bindings to other libraries. 15 | Actually, the Piston project started because we needed a way to test the 2D graphics library! 16 | 17 | Piston-Graphics is designed to take advantage of GPU rendering, 18 | but it does not support any complex features like setting shader parameters, 19 | concave shapes. Instead, it does simple 2D with some few blend settings 20 | that are supported by most graphics cards. 21 | 22 | If you need 3D or advanced looking 2D graphics, then you can use OpenGL 23 | or Gfx. This gives you more control over the performance and is more flexible. 24 | In case you start with prototyping something with Piston-Graphics and gradually 25 | transition to another API, 26 | you can create your own traits and implement them for the types that are 27 | used in Piston-Graphics. Such hybrid approaches are rare, but it is worth 28 | considering if you want to reuse the logic. 29 | 30 | Piston-Graphics is designed to be used with lower level graphics APIs. 31 | We will use the OpenGl backend for Piston-Graphics. 32 | 33 | In the Terminal window, type: 34 | 35 | ``` 36 | cargo add piston2d-graphics 37 | cargo add piston2d-opengl_graphics 38 | ``` 39 | 40 | In "main.rs", add `extern crate graphics;` and `extern crate opengl_graphics`: 41 | 42 | ```rust 43 | extern crate piston; 44 | extern crate glutin_window; 45 | extern crate graphics; 46 | extern crate opengl_graphics; 47 | ``` 48 | 49 | Import `OpenGL` and `GlGraphics`: 50 | 51 | ```rust 52 | use glutin_window::GlutinWindow; 53 | use opengl_graphics::{OpenGL, GlGraphics}; 54 | ``` 55 | 56 | Add a setting that tells the window backend which OpenGL version to use: 57 | 58 | ```rust 59 | let opengl = OpenGL::V3_2; 60 | let settings = WindowSettings::new("Sudoku", [512; 2]) 61 | .graphics_api(opengl) 62 | .exit_on_esc(true); 63 | ``` 64 | 65 | The most widely supported version is OpenGL 3.2, 66 | but if you are e.g. developing inside a virtual environment you might 67 | need to change it. 68 | 69 | Create a new `GlGraphics` object: 70 | 71 | ```rust 72 | let mut events = Events::new(EventSettings::new().lazy(true)); 73 | let mut gl = GlGraphics::new(opengl); 74 | ``` 75 | 76 | The `gl` object stores shaders and buffers that the OpenGL backend for Piston-Graphics needs to talk with the GPU. 77 | 78 | Now we will handle the render events emitted by the event loop. 79 | To do this we need a trait `RenderEvent` from the `input` submodule of 80 | the `piston` library: 81 | 82 | ```rust 83 | use piston::event_loop::{Events, EventLoop, EventSettings}; 84 | use piston::input::RenderEvent; 85 | ``` 86 | 87 | This puts some methods into scope such we can write: 88 | 89 | ```rust 90 | while let Some(e) = events.next(&mut window) { 91 | if let Some(args) = e.render_args() { 92 | 93 | } 94 | } 95 | ``` 96 | 97 | Every once in a while, the event loop emits a render event. 98 | The event object is an `enum`, so you could match on it directly to 99 | handle the event. However, in Piston it is recommended to use trait methods 100 | because: 101 | 102 | 1. A trait can be implemented on more than one type. 103 | 2. It is easier to keep code from breaking. 104 | 105 | In generic code, the `GenericEvent` is used because it is easier to 106 | reason about the code when there is only one trait constraint. 107 | It makes it easier to avoid a lot of work to fix breaking changes, e.g. in nested function calls. 108 | 109 | The `GenericEvent` trait also makes it possible to implement custom 110 | events. This is important on platforms that require special hardware. 111 | 112 | Inside the render `if let` block, we call a method on the `gl` object 113 | to create a `graphics::Context` and a graphics backend implementing 114 | the `graphics::Graphics` trait. 115 | 116 | ```rust 117 | if let Some(args) = e.render_args() { 118 | gl.draw(args.viewport(), |c, g| { 119 | 120 | }); 121 | } 122 | ``` 123 | 124 | OpenGL sets a rectangular area inside the frame buffer of the window 125 | where fragments are written. 126 | This rectangular area is called the "viewport". 127 | The render arguments `args` has a method `.viewport()` that returns the viewport rectangle. 128 | 129 | Before `GlGraphics` can render to the screen, it needs to make sure that 130 | the correct shader program is activated on the GPU. 131 | This is why you need to pass in a closure, so it can call it back when 132 | everything is ready for rendering. 133 | The `g` object is used to write the data from Piston-Graphics into buffers. 134 | After the closure is called, `GlGraphics` flushes the buffers such that 135 | stuff is rendered before the program does anything else OpenGL related rendering. 136 | 137 | It is common to call the `.draw` method once for all 2D graphics. 138 | This reduces the number of draw calls to the GPU. 139 | 140 | Piston-Graphics is an immediate API, which means all rendering is decided 141 | on the fly. The disadvantage with this approach is the bandwidth 142 | between the CPU and the GPU can limit how much stuff you can draw on the screen. 143 | The advantage with this approach is flexibility: 144 | You can decide what to draw for each frame, without needing to keep track of state. 145 | 146 | Game developers focus on *bottlenecks* when optimizing applications, 147 | because that results the largest local gains in efficiency. 148 | For applications that do not use animations, there is less benefit in 149 | handling static buffers on the GPU, unless rendering is very expensive. 150 | This is because rendering on user input only is a low frequency event. 151 | Performance optimizations that you implement have less gains on average because they are multiplied with a low number. 152 | When you press hardware to its limits, the bottlenecks are more likely to occur 153 | in places with *high frequency events*. 154 | One smart way to optimize an application is to reduce high frequency events into low frequency events, 155 | instead of focusing on micro-optimizing drawing routines. 156 | 157 | 1. Focus on the top-down approach to optimization. Observe and learn 158 | what the bottlenecks are. 159 | 2. Preparing code for micro-optimization is often more important than 160 | doing the micro-optimization right away. 161 | 3. Do micro-optimization when there are no large gains in large scale optimization. 162 | 163 | If you micro-optimize right away, 164 | it can be more difficult to recognize the architectural bottlenecks. 165 | By optimizing code from top to bottom, you make sure that more potential efficiency gains are implemented. 166 | Piston-Graphics is not the fastest way to do 2D graphics, 167 | but it will be fast enough for many kinds of applications. 168 | This is why the game engine is organized in a modular way, 169 | such that developers can make top-down decisions about their project. 170 | 171 | For the rest of the tutorial, we will focus on the game itself. 172 | 173 | Clear the window in a white color: 174 | 175 | ```rust 176 | gl.draw(args.viewport(), |c, g| { 177 | use graphics::{clear}; 178 | 179 | clear([1.0; 4], g); 180 | }); 181 | ``` 182 | 183 | Now we are ready to start working on the actual game! 184 | 185 | [Goto Chapter 4](chp-04.md) 186 | -------------------------------------------------------------------------------- /sudoku/chp-02.md: -------------------------------------------------------------------------------- 1 | # Sudoku tutorial 2 | by Sven Nilsen, 2017 3 | 4 | ## Chapter 2 5 | 6 | The Piston core is separate from any underlying window API. 7 | The reason for this is that developers need to ship to many different platforms, 8 | and it is very rare that a single window API is supported across all these devices. 9 | 10 | Sometimes there are tiny differences between window APIs that matter for user experience, 11 | e.g. how mouse coordinates are handled outside 12 | the window border. This means developers pick the API that works best for 13 | the specific kind of application they are developing. 14 | 15 | You can also write your own window backend to quickly set up a project just the way you like it. 16 | In Rust you can create a new library and reexport the stuff you need. 17 | Such window backends are called "window wrappers". 18 | 19 | One window wrapper that is often used for prototyping is the "piston_window" library. 20 | In this tutorial we will not use piston_window since I want to explain 21 | how the core of Piston works relative to window backends and graphics APIs. 22 | After learning how this works, you can decide which window backend is right for your project. 23 | 24 | In the Terminal window, type: 25 | 26 | ``` 27 | cargo add pistoncore-glutin_window 28 | ``` 29 | 30 | This adds the Glutin window backend for Piston. 31 | Notice the prefix "pistoncore-" in the package name. 32 | This is common for libraries in the Piston game engine, e.g. "piston2d-", 33 | "piston3d-" etc. 34 | The "pistoncore-" prefix means a library that is required to create an application. 35 | 36 | Glutin supports the 3 major operating systems: Linux, Window and OSX. 37 | 38 | In "main.rs", add `extern crate glutin_window;` and import `GlutinWindow`: 39 | 40 | ```rust 41 | extern crate piston; 42 | extern crate glutin_window; 43 | 44 | use piston::window::WindowSettings; 45 | use glutin_window::GlutinWindow; 46 | ``` 47 | 48 | In the `main()` function, create a window: 49 | 50 | ```rust 51 | let settings = WindowSettings::new("Sudoku", [512; 2]) 52 | .exit_on_esc(true); 53 | let window: GlutinWindow = settings.build() 54 | .expect("Could not create window"); 55 | ``` 56 | 57 | When we generated docs for the first time, we only added the "piston" crate. 58 | To update the docs, type `cargo doc` in the Terminal window. 59 | Click refresh in the browser to see the changes. 60 | 61 | When you look up `WindowSettings::build` in the docs, you will see this signature: 62 | 63 | ```rust 64 | fn build(&self) -> Result 65 | ``` 66 | 67 | This means the function takes a generic parameter `W` with a trait constraint 68 | `BuildFromWindowSettings`. All types that implement this trait are accepted. 69 | 70 | Rust has a smart type system that understands how to infer the `W` generic 71 | argument to the build method. 72 | Since you are specifying the type of the left side, the type system infers 73 | that `W` must be equal to `GlutinWindow`: 74 | 75 | ```rust 76 | let window: GlutinWindow = ... 77 | ``` 78 | 79 | You can picture how types propagate through your code and "fill the holes". 80 | This is how you pick the window backend to use with Piston. 81 | 82 | By using generics and traits and taking advantage of the smart type system, 83 | you can write portable code that runs on all backends. 84 | At the same time, the concrete type of a window backend is 85 | exposed at application level in case you need some platform specific code. 86 | 87 | The `GlutinWindow` struct exposes the underlying Glutin API, such that 88 | you can write platform specific code when you need it. 89 | 90 | The Piston core tries to put as little abstraction as possible 91 | between you and the underlying platform, while at the same time offering portability. 92 | Most of the code, perhaps 90%, can be written using generics and traits. 93 | The motivation for this design is to reuse code across projects. 94 | If Alice works on a library that Bob finds useful except for a small feature, 95 | then Bob might send a PR to Alice and they both save work. 96 | 97 | Notice that it is only the types and traits that matters, 98 | not where these types or traits are located. 99 | You can find them by all searching the docs. 100 | 101 | Some people are worried about these extra window backend libraries, 102 | but the smart thing is to focus on the larger ecosystem. 103 | A way to measure code reusability: 104 | How many libraries in a code base have as few dependencies as possible? 105 | The more libraries with few dependencies, 106 | the less likely are they to be affected by breaking changes. 107 | This means that generic libraries should have few dependencies. 108 | 109 | The Piston project organizes these types and traits in way to reuse 110 | code across projects with the minimum number of dependencies per library. 111 | There is a trade-off between the number of dependencies per library 112 | and the number of dependencies in an application, 113 | so a typical Piston project pulls in a lot of crates in the dependency graph. 114 | You should not worry about this at all, it does not slow down compilation, 115 | or development time, or makes your codebase more fragile. 116 | Actually, this pattern makes a large codebase less fragile, since most of 117 | the code, the generic libraries, breaks less often. 118 | 119 | Piston is under development and this leads to more frequent breaking changes 120 | than what is ideal. Over time the number of breaking changes goes down, 121 | so the expected payoff to organize code this way is a long term motivation. 122 | 123 | In addition, the way the Piston project is architected is to use *zero* 124 | shared dependencies when possible. 125 | This means people can work on projects that happens completely in parallel. 126 | For example, some projects are experimenting with an idea while others 127 | are building an application. 128 | Since this is equivalent to just using the larger Rust ecosystem, 129 | it is hard to see where the boundary goes between the Piston project 130 | and other projects. This does not matter at all, 131 | because the only thing that matters is *development efficiency*. 132 | Instead of sticking to a fixed idea of what a game engine is, 133 | we turn the problem around and ask "what is the best way to make games?". 134 | 135 | Now, add another import: 136 | 137 | ```rust 138 | use piston::window::WindowSettings; 139 | use piston::event_loop::{Events, EventSettings}; 140 | ``` 141 | 142 | This imports two new structs, `Events` and `EventSettings`, 143 | which we use to create an event loop. 144 | The event loop is a kind of iterator that polls events from the window 145 | and does its own internal logic. 146 | 147 | To create an event loop, you must first make the `window` mutable. 148 | Add the `mut` keyword before the variable name: 149 | 150 | ```rust 151 | let settings = WindowSettings::new("Sudoku", [512; 2]) 152 | .exit_on_esc(true); 153 | let mut window: GlutinWindow = settings.build() 154 | .expect("Could not create window"); 155 | 156 | let mut events = Events::new(EventSettings::new()); 157 | 158 | while let Some(e) = events.next(&mut window) { 159 | 160 | } 161 | ``` 162 | 163 | The `events` object contains a state that keeps track of what to do next. 164 | A typical game event loop does rendering, updating and handles input. 165 | 166 | By default, the event settings are set to a typical FPS shooter. 167 | This means that the event loop will consume a lot of energy 168 | that is not needed by every kind of game. 169 | The default frame rate and update rate settings are specified by `DEFAULT_MAX_FPS` and `DEFAULT_UPS`. 170 | 171 | We will not do any animations in our Sudoku game, 172 | so the game only needs to render when the window receives some user input. 173 | 174 | Import the trait `EventLoop`: 175 | 176 | ```rust 177 | use piston::event_loop::{Events, EventLoop, EventSettings}; 178 | ``` 179 | 180 | Add a `.lazy(true)`: 181 | 182 | ```rust 183 | let mut events = Events::new(EventSettings::new().lazy(true)); 184 | ``` 185 | 186 | This setting tells the event loop to not bother updating at all, 187 | and render only when user input is received. 188 | 189 | Notice how Piston separates settings, like `WindowSettings` and `EventSettings`, from the object that uses these settings. 190 | This is a common pattern in Piston libraries. 191 | 192 | [Goto Chapter 3](chp-03.md) 193 | -------------------------------------------------------------------------------- /sudoku/chp-05.md: -------------------------------------------------------------------------------- 1 | # Sudoku tutorial 2 | by Sven Nilsen, 2017 3 | 4 | # Chapter 5 5 | 6 | Add the following code to `GameboardView::draw`: 7 | 8 | ```rust 9 | /// Draw gameboard. 10 | pub fn draw(&self, controller: &GameboardController, c: &Context, g: &mut G) { 11 | use graphics::{Line, Rectangle}; 12 | 13 | let ref settings = self.settings; 14 | let board_rect = [ 15 | settings.position[0], settings.position[1], 16 | settings.size, settings.size, 17 | ]; 18 | 19 | // Draw board background. 20 | Rectangle::new(settings.background_color) 21 | .draw(board_rect, &c.draw_state, c.transform, g); 22 | 23 | // Declare the format for cell and section lines. 24 | let cell_edge = Line::new(settings.cell_edge_color, settings.cell_edge_radius); 25 | let section_edge = Line::new(settings.section_edge_color, settings.section_edge_radius); 26 | 27 | // Generate and draw the lines for the Sudoku Grid. 28 | for i in 0..9 { 29 | let x = settings.position[0] + i as f64 / 9.0 * settings.size; 30 | let y = settings.position[1] + i as f64 / 9.0 * settings.size; 31 | let x2 = settings.position[0] + settings.size; 32 | let y2 = settings.position[1] + settings.size; 33 | 34 | let vline = [x, settings.position[1], x, y2]; 35 | let hline = [settings.position[0], y, x2, y]; 36 | 37 | // Draw Section Lines instead of Cell Lines 38 | if (i % 3) == 0 { 39 | section_edge.draw(vline, &c.draw_state, c.transform, g); 40 | section_edge.draw(hline, &c.draw_state, c.transform, g); 41 | } 42 | // Draw the regular cell Lines 43 | else { 44 | cell_edge.draw(vline, &c.draw_state, c.transform, g); 45 | cell_edge.draw(hline, &c.draw_state, c.transform, g); 46 | } 47 | } 48 | 49 | // Draw board edge. 50 | Rectangle::new_border(settings.board_edge_color, settings.board_edge_radius) 51 | .draw(board_rect, &c.draw_state, c.transform, g); 52 | } 53 | ``` 54 | 55 | Piston-Graphics splits data into high and low frequency usage. 56 | `Line` and `Rectangle` store color and edge information, 57 | but the coordinates are passed to the `.draw` method call. 58 | This makes it easy to reuse these objects by declaring them before loops. 59 | 60 | `Line` stores the coordinates in the format `[x1, y1, x2, y2]`. 61 | `Rectangle` stores the coordinates in the format `[x, y, w, h]`. 62 | 63 | In addition to the shape, the `.draw` method requires the draw state 64 | from `Context`, a matrix transform (often `c.transform`) and the graphics backend. 65 | 66 | If you are familiar with other APIs like Cairo or GDI+, you might think 67 | the way Piston-Graphics does drawing is somewhat unfamiliar. 68 | In these APIs the context and the object that renders is the same thing. 69 | In Piston-Graphics, `Context` is separated from the object implementing 70 | `Graphics`. 71 | 72 | By manipulating the `Context` object, one can use the default stack as 73 | a way to push/pop transforms. 74 | 75 | The draw state stores things like scissor rectangle, stencil usage and 76 | blending settings. 77 | 78 | The matrix transform is use to translate, rotate, scale etc. the shape. 79 | 80 | When you type `cargo run` in the Terminal window, you should see: 81 | 82 | ![grid](./images/grid.png) 83 | 84 | Next, we want to be able to select a cell. 85 | 86 | Add a new field `selected_cell` to `GameboardController`: 87 | 88 | ```rust 89 | /// Handles events for Sudoku game. 90 | pub struct GameboardController { 91 | /// Stores the gameboard state. 92 | pub gameboard: Gameboard, 93 | /// Selected cell. 94 | pub selected_cell: Option<[usize; 2]>, 95 | } 96 | ``` 97 | 98 | In `GameboardController::new`, set `selected_cell` to `None`: 99 | 100 | ```rust 101 | /// Creates a new gameboard controller. 102 | pub fn new(gameboard: Gameboard) -> GameboardController { 103 | GameboardController { 104 | gameboard: gameboard, 105 | selected_cell: None, 106 | } 107 | } 108 | ``` 109 | 110 | Add a new field `cursor_pos` to `GameboardController`: 111 | 112 | ```rust 113 | /// Stores last mouse cursor position. 114 | cursor_pos: [f64; 2], 115 | ``` 116 | 117 | In `GameboardController::new`, set `cursor_pos` to `[0.0; 2]`: 118 | 119 | ```rust 120 | /// Creates a new gameboard controller. 121 | pub fn new(gameboard: Gameboard) -> GameboardController { 122 | GameboardController { 123 | gameboard: gameboard, 124 | selected_cell: None, 125 | cursor_pos: [0.0; 2], 126 | } 127 | } 128 | ``` 129 | 130 | In `GameboardController::event`, store the mouse cursor position: 131 | 132 | ```rust 133 | /// Handles events. 134 | pub fn event(&mut self, e: &E) { 135 | if let Some(pos) = e.mouse_cursor_args() { 136 | self.cursor_pos = pos; 137 | } 138 | } 139 | ``` 140 | 141 | Add two new arguments `pos` and `size` to `GameboardController::event`: 142 | 143 | ```rust 144 | pub fn event(&mut self, pos: [f64; 2], size: f64, e: &E) 145 | ``` 146 | 147 | These arguments will be used to compute which cell the user clicks on. 148 | 149 | In "main.rs" you need to pass in the position and size from the view: 150 | 151 | ```rust 152 | gameboard_controller.event(gameboard_view.settings.position, 153 | gameboard_view.settings.size, 154 | &e); 155 | ``` 156 | 157 | Handle the left mouse button press in `GameboardController::event`: 158 | 159 | ```rust 160 | /// Handles events. 161 | pub fn event(&mut self, pos: [f64; 2], size: f64, e: &E) { 162 | use piston::input::{Button, MouseButton}; 163 | 164 | if let Some(pos) = e.mouse_cursor_args() { 165 | self.cursor_pos = pos; 166 | } 167 | if let Some(Button::Mouse(MouseButton::Left)) = e.press_args() { 168 | // Find coordinates relative to upper left corner. 169 | let x = self.cursor_pos[0] - pos[0]; 170 | let y = self.cursor_pos[1] - pos[1]; 171 | // Check that coordinates are inside board boundaries. 172 | if x >= 0.0 && x < size && y >= 0.0 && y < size { 173 | // Compute the cell position. 174 | let cell_x = (x / size * 9.0) as usize; 175 | let cell_y = (y / size * 9.0) as usize; 176 | self.selected_cell = Some([cell_x, cell_y]); 177 | } 178 | } 179 | } 180 | ``` 181 | 182 | Add a new field `selected_cell_background_color` to `GameboardViewSettings`: 183 | 184 | ```rust 185 | /// Edge radius between cells. 186 | pub cell_edge_radius: f64, 187 | /// Selected cell background color. 188 | pub selected_cell_background_color: Color, 189 | ``` 190 | 191 | In `GameboardViewSettings::new`, set `selected_cell_background_color`: 192 | 193 | ```rust 194 | /// Creates new gameboard view settings. 195 | pub fn new() -> GameboardViewSettings { 196 | GameboardViewSettings { 197 | position: [10.0; 2], 198 | size: 400.0, 199 | background_color: [0.8, 0.8, 1.0, 1.0], 200 | border_color: [0.0, 0.0, 0.2, 1.0], 201 | board_edge_color: [0.0, 0.0, 0.2, 1.0], 202 | section_edge_color: [0.0, 0.0, 0.2, 1.0], 203 | cell_edge_color: [0.0, 0.0, 0.2, 1.0], 204 | board_edge_radius: 3.0, 205 | section_edge_radius: 2.0, 206 | cell_edge_radius: 1.0, 207 | selected_cell_background_color: [0.9, 0.9, 1.0, 1.0], 208 | } 209 | } 210 | ``` 211 | 212 | In `GameboardView::draw`, add this code after drawing board background: 213 | 214 | ```rust 215 | // Draw board background. 216 | Rectangle::new(settings.background_color) 217 | .draw(board_rect, &c.draw_state, c.transform, g); 218 | 219 | // Draw selected cell background. 220 | if let Some(ind) = controller.selected_cell { 221 | let cell_size = settings.size / 9.0; 222 | let pos = [ind[0] as f64 * cell_size, ind[1] as f64 * cell_size]; 223 | let cell_rect = [ 224 | settings.position[0] + pos[0], settings.position[1] + pos[1], 225 | cell_size, cell_size 226 | ]; 227 | Rectangle::new(settings.selected_cell_background_color) 228 | .draw(cell_rect, &c.draw_state, c.transform, g); 229 | } 230 | ``` 231 | 232 | You should now be able to click on a cell and it will get highlighted: 233 | 234 | ![hightlight cell](./images/highlight-cell.png) 235 | 236 | [Go to chapter 6](chp-06.md) 237 | -------------------------------------------------------------------------------- /advanced/writing-idiomatic-code.md: -------------------------------------------------------------------------------- 1 | # Advanced Piston 2 | 3 | ## Chapter 2 - Writing Idiomatic Code 4 | 5 | What is meant by "idiomatic" code is a pattern that works and has a predictable maintenance cost for large projects. 6 | It is not meant to mean that you can not do any better or that everyone should program this way. 7 | The great thing about Rust is that it allows a wide style of designs, e.g. functional APIs, stream processing etc. 8 | You should not think that one particular flavor is superior to another, 9 | but learn different techniques and figure out the trade-offs. 10 | You know best what works for you! 11 | 12 | ### Model/View/Controller 13 | 14 | The Piston project often organizes code by the [Model/View/Controller](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) pattern. 15 | This pattern has been tested in the industry for many years. 16 | The good thing about this pattern is that it scales, so you can combine it with other patterns at lower levels. 17 | 18 | - A model is simply some data structure, database etc. 19 | - A view is how something is rendered. 20 | - A controller stores the state, transforms input events into other events or actions, or deals with application logic in general. 21 | 22 | Currently, there are no traits defined for this abstraction, it is simply a way of splitting up code into reusable parts. 23 | 24 | Two simple examples of this pattern is [timer_controller](https://github.com/PistonDevelopers/timer_controller) and [button_controller](https://github.com/pistondevelopers/button_controller). 25 | More libraries will be added over time. 26 | You can take a look [here](https://github.com/PistonDevelopers/piston/wiki/Piston-overview) for an updated overview. 27 | 28 | This code is reusable for any model or view, but it is also more work to set up. You might want to use a tailored API for your use cases, e.g. [Conrod](https://github.com/PistonDevelopers/conrod/) for UI. This is why Piston uses a modular design with a small core, so the code that is reused for particular projects, e.g. editors, can be worked on in parallel with other projects. 29 | 30 | ### Handling events in controller 31 | 32 | Most controllers have an `event` method that handles events. 33 | This pattern allows users to hook up the controller logic using a single line of code. 34 | 35 | Here is an example from the [button_controller](https://github.com/PistonDevelopers/button_controller) library: 36 | 37 | ```rust 38 | pub fn event(&mut self, layout: Rectangle, transform: Matrix2d, e: &E) { 39 | ... 40 | } 41 | ``` 42 | 43 | It is common to pass in extra parameters before `e: &E` that changes dynamically or are controlled by some external logic. 44 | 45 | The `event` method is often called only when the controller is activated. 46 | When the controller contains enabled/disabled state or requires calling at all times, 47 | this must be documented on the `event` method. 48 | 49 | It is recommended to not do any rendering in the `event` method, 50 | because the order of rendering is often reversed event logic order. 51 | Instead, you can use a `View` struct to do rendering and a `Controller` struct to do application logic. 52 | 53 | ### Rendering 54 | 55 | A recommended pattern is to end names of structs that do rendering with `View` or `Visualizer`. 56 | 57 | - A `View` struct displays information from a controller 58 | - A `Visualizer` struct displays information without any controller 59 | 60 | By separating this code from controllers, one can reuse the same `View` struct with multiple controllers. 61 | 62 | E.g. if you are using the [button_controller](https://github.com/PistonDevelopers/button_controller) library, 63 | you might name a struct `ButtonView` that renders all buttons. 64 | 65 | ### Settings 66 | 67 | When the behavior of a controller or view depends on some settings, 68 | it is common to call the constructor with a settings struct. 69 | 70 | The Piston project recommends using [the builder pattern](https://doc.rust-lang.org/1.12.0/style/ownership/builders.html) 71 | and end the name with `Settings`. 72 | 73 | A settings struct might be used to build multiple types. 74 | One trick is to add a `BuildFromXSettings` trait and add a `build` method to the settings struct. 75 | 76 | For example, when you create a window in Piston, 77 | you specify the type of the window like this: 78 | 79 | ```rust 80 | let mut window: GlutinWindow = settings.build().expect("Could not create window"); 81 | ``` 82 | 83 | ### Properties 84 | 85 | When writing methods for getting and setting options, you can have `&mut self`, `self`, `&self` etc. 86 | This might easily lead to inconsistent usage across libraries. 87 | 88 | The Piston project recommends the following design: 89 | 90 | - Whenever a method starts with `set_` it should take `&mut self` 91 | - Whenever there is a `set_` method and you need a get, it should start with `get_` 92 | - Methods without a prefix are either read only or take `self` and returns `Self` 93 | 94 | This means that builder patterns doesn't need `set_` prefixes, for example: 95 | 96 | ``` Rust 97 | let events = events.max_fps(60).ups(120); 98 | ``` 99 | 100 | The method without prefix never gets in the way for the read only version, because when a property is read only you do not need a builder version. It also makes it possible to add either a read only or builder method later without breaking the API. 101 | 102 | For mutable references to the interior of an object: 103 | - `get_foo_mut` when there is a `set_` method 104 | - `foo_mut` when there is no `set_` method 105 | 106 | The Rust API guidelines distinguishes between Builder patterns and ordinary objects, so the `get_` prefix is removed for ordinary objects. 107 | See https://aturon.github.io/style/naming/README.html#getter/setter-methods-[rfc-344]. 108 | 109 | In several Piston libraries the distinction between builders and ordinary objects is blurry, so `get_` can be used avoid breaking the API. 110 | 111 | ### Scalar precision, vectors and matrices 112 | 113 | The recommended float number format for application logic is `f64`. 114 | If you do not know what precision you need in your application logic, you should use `f64`. 115 | It only takes up a little more memory, but it gives much, much better accuracy than `f32`. 116 | File formats should use `f64` when possible, because going from `f32` to `f64` is harder than vice versa. 117 | In the vast number of applications, choosing between `f32` and `f64` is not the bottleneck, 118 | so you should choose with `f64` unless there is a good reason. 119 | 120 | *Using `f32` instead of `f64`, when `f32` does not give good enough accuracy in application logic, might lead to serious accidents.* 121 | 122 | The default float number format for 2D graphics math is `f64`. 123 | Most modern CPUs have hardware support for `f64`. 124 | Counter-intuitively, `f32` is sometimes slower than `f64`, 125 | because the CPU might use `f64` under the hood for `f32` intructions. 126 | `f64` gives also larger range for invariant matrix compositions `M * M^-1 = I`. 127 | This is a common problem with `f32` in large hierarchial scenes for e.g. 2D animation software. 128 | 129 | The default float number format for 3D rendering is `f32`. 130 | In most cases, 3D rendering does not affect application logic. 131 | 132 | By default, [Piston-Graphics](https://github.com/pistondevelopers/graphics) converts from `f64` to `f32` after doing 133 | matrix transformations on the CPU to achieve better accuracy and pack triangles in dynamic buffers. 134 | This behavior can be overriden by a graphics backend by changing how the `Graphics` trait is implemented. 135 | 136 | The default float number format for colors is `f32`. 137 | Colors often do not need `f64` precision. 138 | The default color space for 2D APIs is sRGB. 139 | 140 | The Piston project recommends fixed arrays as vector format, e.g. `[f64; 4]`. 141 | One benefit of arrays over tuples is that they can be iterated over. 142 | 143 | The Piston project recommends fixed arrays as matrix format `[[f64; 4]; 4]`. 144 | 145 | ### SIMD optimizations 146 | 147 | When using Rust with LLVM, vector and matrix operations are often optimized with SIMD instructions. 148 | It is rarely need to use SIMD data types directly in your Rust code. 149 | 150 | ### Writing generic code for float formats 151 | 152 | The Piston project uses the [piston-float](https://crates.io/crates/piston-float) crate to support generic code for float formats. 153 | 154 | The `Float` trait inherits `Send` and `Sync` to make it easier to write multi-threaded code. 155 | 156 | ### Organizing math modules in Piston libraries 157 | 158 | Mathematics is often hard to formalize because it has many applications with slightly different usages. 159 | The architecture of Piston is designed to let users decide which math abstractions they want to use. 160 | 161 | The Piston project uses a small library [vecmath](https://github.com/PistonDevelopers/vecmath) for ecosystem integration. 162 | This library is often used by authors that contribute to the Piston ecosystem. 163 | 164 | A common pattern is to add a `math` module in your library and reexport/rename the functions that are needed. 165 | This allows one to add extra functions that are useful for that particular library. 166 | 167 | ### Converting custom types 168 | 169 | It is common to use the conversion trait `Into` when supporting user specified types. 170 | This allows developers to choose their own custom types for convenience. 171 | 172 | Here is an example from [Piston-Graphics](https://github.com/PistonDevelopers/graphics): 173 | 174 | ```rust 175 | pub fn rectangle, G>(color: types::Color, 176 | rect: R, 177 | transform: math::Matrix2d, 178 | g: &mut G) { 179 | ... 180 | } 181 | ``` 182 | -------------------------------------------------------------------------------- /sudoku/Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "sudoku" 3 | version = "0.1.0" 4 | dependencies = [ 5 | "piston 0.35.0 (registry+https://github.com/rust-lang/crates.io-index)", 6 | "piston2d-graphics 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", 7 | "piston2d-opengl_graphics 0.49.0 (registry+https://github.com/rust-lang/crates.io-index)", 8 | "pistoncore-glutin_window 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "adler32" 13 | version = "1.0.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | 16 | [[package]] 17 | name = "android_glue" 18 | version = "0.2.3" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | 21 | [[package]] 22 | name = "arrayvec" 23 | version = "0.4.6" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | dependencies = [ 26 | "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 27 | ] 28 | 29 | [[package]] 30 | name = "bitflags" 31 | version = "0.7.0" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | 34 | [[package]] 35 | name = "bitflags" 36 | version = "0.9.1" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | 39 | [[package]] 40 | name = "bitflags" 41 | version = "1.0.0" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | 44 | [[package]] 45 | name = "block" 46 | version = "0.1.6" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | 49 | [[package]] 50 | name = "byteorder" 51 | version = "0.4.2" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | 54 | [[package]] 55 | name = "byteorder" 56 | version = "1.1.0" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | 59 | [[package]] 60 | name = "cgl" 61 | version = "0.2.1" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | dependencies = [ 64 | "gleam 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", 65 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 66 | ] 67 | 68 | [[package]] 69 | name = "coco" 70 | version = "0.1.1" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | dependencies = [ 73 | "either 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 74 | "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 75 | ] 76 | 77 | [[package]] 78 | name = "cocoa" 79 | version = "0.9.2" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | dependencies = [ 82 | "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", 83 | "block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 84 | "core-graphics 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", 85 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 86 | "objc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 87 | ] 88 | 89 | [[package]] 90 | name = "color_quant" 91 | version = "1.0.0" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | 94 | [[package]] 95 | name = "core-foundation" 96 | version = "0.3.0" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | dependencies = [ 99 | "core-foundation-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 100 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 101 | ] 102 | 103 | [[package]] 104 | name = "core-foundation" 105 | version = "0.4.4" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | dependencies = [ 108 | "core-foundation-sys 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 109 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 110 | ] 111 | 112 | [[package]] 113 | name = "core-foundation-sys" 114 | version = "0.3.1" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | dependencies = [ 117 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 118 | ] 119 | 120 | [[package]] 121 | name = "core-foundation-sys" 122 | version = "0.4.4" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | dependencies = [ 125 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 126 | ] 127 | 128 | [[package]] 129 | name = "core-graphics" 130 | version = "0.8.2" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | dependencies = [ 133 | "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", 134 | "core-foundation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 135 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 136 | ] 137 | 138 | [[package]] 139 | name = "deflate" 140 | version = "0.7.17" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | dependencies = [ 143 | "adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 144 | "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 145 | ] 146 | 147 | [[package]] 148 | name = "dlib" 149 | version = "0.3.1" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | dependencies = [ 152 | "libloading 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 153 | ] 154 | 155 | [[package]] 156 | name = "dtoa" 157 | version = "0.4.2" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | 160 | [[package]] 161 | name = "dwmapi-sys" 162 | version = "0.1.0" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | dependencies = [ 165 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 166 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 167 | ] 168 | 169 | [[package]] 170 | name = "either" 171 | version = "1.3.0" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | 174 | [[package]] 175 | name = "enum_primitive" 176 | version = "0.1.1" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | dependencies = [ 179 | "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 180 | ] 181 | 182 | [[package]] 183 | name = "fnv" 184 | version = "1.0.5" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | 187 | [[package]] 188 | name = "fs2" 189 | version = "0.2.5" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | dependencies = [ 192 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 193 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 194 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 195 | ] 196 | 197 | [[package]] 198 | name = "fuchsia-zircon" 199 | version = "0.2.1" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | dependencies = [ 202 | "fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 203 | ] 204 | 205 | [[package]] 206 | name = "fuchsia-zircon-sys" 207 | version = "0.2.0" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | dependencies = [ 210 | "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 211 | ] 212 | 213 | [[package]] 214 | name = "futures" 215 | version = "0.1.17" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | 218 | [[package]] 219 | name = "gdi32-sys" 220 | version = "0.1.1" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | dependencies = [ 223 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 224 | ] 225 | 226 | [[package]] 227 | name = "gif" 228 | version = "0.9.2" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | dependencies = [ 231 | "color_quant 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 232 | "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 233 | ] 234 | 235 | [[package]] 236 | name = "gl" 237 | version = "0.6.5" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | dependencies = [ 240 | "gl_generator 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 241 | ] 242 | 243 | [[package]] 244 | name = "gl_generator" 245 | version = "0.6.1" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | dependencies = [ 248 | "khronos_api 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 249 | "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 250 | "xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 251 | ] 252 | 253 | [[package]] 254 | name = "gleam" 255 | version = "0.4.12" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | dependencies = [ 258 | "gl_generator 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 259 | "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 260 | ] 261 | 262 | [[package]] 263 | name = "glutin" 264 | version = "0.10.1" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | dependencies = [ 267 | "android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 268 | "cgl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 269 | "cocoa 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", 270 | "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 271 | "core-graphics 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", 272 | "dwmapi-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 273 | "gdi32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 274 | "gl_generator 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 275 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 276 | "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", 277 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 278 | "objc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 279 | "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 280 | "shared_library 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 281 | "shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 282 | "user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 283 | "wayland-client 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", 284 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 285 | "winit 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", 286 | "x11-dl 2.16.0 (registry+https://github.com/rust-lang/crates.io-index)", 287 | ] 288 | 289 | [[package]] 290 | name = "image" 291 | version = "0.17.0" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | dependencies = [ 294 | "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 295 | "enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 296 | "gif 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", 297 | "jpeg-decoder 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 298 | "num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", 299 | "num-rational 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 300 | "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 301 | "png 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 302 | "scoped_threadpool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 303 | ] 304 | 305 | [[package]] 306 | name = "inflate" 307 | version = "0.3.3" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | dependencies = [ 310 | "adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 311 | ] 312 | 313 | [[package]] 314 | name = "interpolation" 315 | version = "0.1.0" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | 318 | [[package]] 319 | name = "itoa" 320 | version = "0.3.4" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | 323 | [[package]] 324 | name = "jpeg-decoder" 325 | version = "0.1.13" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | dependencies = [ 328 | "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 329 | "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", 330 | ] 331 | 332 | [[package]] 333 | name = "kernel32-sys" 334 | version = "0.2.2" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | dependencies = [ 337 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 338 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 339 | ] 340 | 341 | [[package]] 342 | name = "khronos_api" 343 | version = "0.0.8" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | 346 | [[package]] 347 | name = "khronos_api" 348 | version = "2.0.0" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | 351 | [[package]] 352 | name = "lazy_static" 353 | version = "0.2.9" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | 356 | [[package]] 357 | name = "libc" 358 | version = "0.2.33" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | 361 | [[package]] 362 | name = "libloading" 363 | version = "0.3.4" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | dependencies = [ 366 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 367 | "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", 368 | "target_build_utils 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 369 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 370 | ] 371 | 372 | [[package]] 373 | name = "linked-hash-map" 374 | version = "0.5.0" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | 377 | [[package]] 378 | name = "log" 379 | version = "0.3.8" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | 382 | [[package]] 383 | name = "lzw" 384 | version = "0.10.0" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | 387 | [[package]] 388 | name = "malloc_buf" 389 | version = "0.0.6" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | dependencies = [ 392 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 393 | ] 394 | 395 | [[package]] 396 | name = "memmap" 397 | version = "0.4.0" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | dependencies = [ 400 | "fs2 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 401 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 402 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 403 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 404 | ] 405 | 406 | [[package]] 407 | name = "nodrop" 408 | version = "0.1.12" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | 411 | [[package]] 412 | name = "num-integer" 413 | version = "0.1.35" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | dependencies = [ 416 | "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 417 | ] 418 | 419 | [[package]] 420 | name = "num-iter" 421 | version = "0.1.34" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | dependencies = [ 424 | "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 425 | "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 426 | ] 427 | 428 | [[package]] 429 | name = "num-rational" 430 | version = "0.1.39" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | dependencies = [ 433 | "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 434 | "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 435 | ] 436 | 437 | [[package]] 438 | name = "num-traits" 439 | version = "0.1.40" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | 442 | [[package]] 443 | name = "num_cpus" 444 | version = "1.7.0" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | dependencies = [ 447 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 448 | ] 449 | 450 | [[package]] 451 | name = "objc" 452 | version = "0.2.2" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | dependencies = [ 455 | "malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 456 | ] 457 | 458 | [[package]] 459 | name = "osmesa-sys" 460 | version = "0.1.2" 461 | source = "registry+https://github.com/rust-lang/crates.io-index" 462 | dependencies = [ 463 | "shared_library 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 464 | ] 465 | 466 | [[package]] 467 | name = "phf" 468 | version = "0.7.21" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | dependencies = [ 471 | "phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", 472 | ] 473 | 474 | [[package]] 475 | name = "phf_codegen" 476 | version = "0.7.21" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | dependencies = [ 479 | "phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", 480 | "phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", 481 | ] 482 | 483 | [[package]] 484 | name = "phf_generator" 485 | version = "0.7.21" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | dependencies = [ 488 | "phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", 489 | "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", 490 | ] 491 | 492 | [[package]] 493 | name = "phf_shared" 494 | version = "0.7.21" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | dependencies = [ 497 | "siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 498 | ] 499 | 500 | [[package]] 501 | name = "piston" 502 | version = "0.35.0" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | dependencies = [ 505 | "pistoncore-event_loop 0.35.0 (registry+https://github.com/rust-lang/crates.io-index)", 506 | "pistoncore-input 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", 507 | "pistoncore-window 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", 508 | ] 509 | 510 | [[package]] 511 | name = "piston-float" 512 | version = "0.3.0" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | 515 | [[package]] 516 | name = "piston-shaders_graphics2d" 517 | version = "0.3.1" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | 520 | [[package]] 521 | name = "piston-texture" 522 | version = "0.6.0" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | 525 | [[package]] 526 | name = "piston-viewport" 527 | version = "0.3.0" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | dependencies = [ 530 | "piston-float 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 531 | ] 532 | 533 | [[package]] 534 | name = "piston2d-graphics" 535 | version = "0.23.0" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | dependencies = [ 538 | "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 539 | "interpolation 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 540 | "piston-texture 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 541 | "piston-viewport 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 542 | "read_color 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 543 | "rusttype 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 544 | "vecmath 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 545 | ] 546 | 547 | [[package]] 548 | name = "piston2d-opengl_graphics" 549 | version = "0.49.0" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | dependencies = [ 552 | "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 553 | "gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 554 | "image 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", 555 | "khronos_api 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", 556 | "piston-shaders_graphics2d 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 557 | "piston-texture 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 558 | "piston2d-graphics 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", 559 | "shader_version 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 560 | ] 561 | 562 | [[package]] 563 | name = "pistoncore-event_loop" 564 | version = "0.35.0" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | dependencies = [ 567 | "pistoncore-input 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", 568 | "pistoncore-window 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", 569 | ] 570 | 571 | [[package]] 572 | name = "pistoncore-glutin_window" 573 | version = "0.42.0" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | dependencies = [ 576 | "gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 577 | "glutin 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 578 | "pistoncore-input 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", 579 | "pistoncore-window 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", 580 | "shader_version 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 581 | ] 582 | 583 | [[package]] 584 | name = "pistoncore-input" 585 | version = "0.20.0" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | dependencies = [ 588 | "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 589 | "piston-viewport 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 590 | "serde 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", 591 | "serde_derive 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", 592 | ] 593 | 594 | [[package]] 595 | name = "pistoncore-window" 596 | version = "0.30.0" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | dependencies = [ 599 | "pistoncore-input 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", 600 | "shader_version 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 601 | ] 602 | 603 | [[package]] 604 | name = "pkg-config" 605 | version = "0.3.9" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | 608 | [[package]] 609 | name = "png" 610 | version = "0.11.0" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | dependencies = [ 613 | "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 614 | "deflate 0.7.17 (registry+https://github.com/rust-lang/crates.io-index)", 615 | "inflate 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 616 | "num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", 617 | ] 618 | 619 | [[package]] 620 | name = "quote" 621 | version = "0.3.15" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | 624 | [[package]] 625 | name = "rand" 626 | version = "0.3.18" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | dependencies = [ 629 | "fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 630 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 631 | ] 632 | 633 | [[package]] 634 | name = "rayon" 635 | version = "0.8.2" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | dependencies = [ 638 | "rayon-core 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 639 | ] 640 | 641 | [[package]] 642 | name = "rayon-core" 643 | version = "1.2.1" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | dependencies = [ 646 | "coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 647 | "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", 648 | "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", 649 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 650 | "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 651 | "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", 652 | ] 653 | 654 | [[package]] 655 | name = "read_color" 656 | version = "0.1.0" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | 659 | [[package]] 660 | name = "redox_syscall" 661 | version = "0.1.31" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | 664 | [[package]] 665 | name = "rusttype" 666 | version = "0.2.3" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | dependencies = [ 669 | "arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 670 | "linked-hash-map 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 671 | "stb_truetype 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 672 | ] 673 | 674 | [[package]] 675 | name = "scoped_threadpool" 676 | version = "0.1.8" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | 679 | [[package]] 680 | name = "scopeguard" 681 | version = "0.3.3" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | 684 | [[package]] 685 | name = "serde" 686 | version = "0.9.15" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | 689 | [[package]] 690 | name = "serde" 691 | version = "1.0.19" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | 694 | [[package]] 695 | name = "serde_derive" 696 | version = "1.0.19" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | dependencies = [ 699 | "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 700 | "serde_derive_internals 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", 701 | "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", 702 | ] 703 | 704 | [[package]] 705 | name = "serde_derive_internals" 706 | version = "0.17.0" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | dependencies = [ 709 | "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", 710 | "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", 711 | ] 712 | 713 | [[package]] 714 | name = "serde_json" 715 | version = "0.9.10" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | dependencies = [ 718 | "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 719 | "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 720 | "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 721 | "serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)", 722 | ] 723 | 724 | [[package]] 725 | name = "shader_version" 726 | version = "0.3.0" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | 729 | [[package]] 730 | name = "shared_library" 731 | version = "0.1.7" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | dependencies = [ 734 | "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", 735 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 736 | ] 737 | 738 | [[package]] 739 | name = "shell32-sys" 740 | version = "0.1.1" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | dependencies = [ 743 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 744 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 745 | ] 746 | 747 | [[package]] 748 | name = "siphasher" 749 | version = "0.2.2" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | 752 | [[package]] 753 | name = "stb_truetype" 754 | version = "0.2.1" 755 | source = "registry+https://github.com/rust-lang/crates.io-index" 756 | dependencies = [ 757 | "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 758 | ] 759 | 760 | [[package]] 761 | name = "syn" 762 | version = "0.11.11" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | dependencies = [ 765 | "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 766 | "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", 767 | "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 768 | ] 769 | 770 | [[package]] 771 | name = "synom" 772 | version = "0.11.3" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | dependencies = [ 775 | "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 776 | ] 777 | 778 | [[package]] 779 | name = "target_build_utils" 780 | version = "0.3.1" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | dependencies = [ 783 | "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", 784 | "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", 785 | "serde_json 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", 786 | ] 787 | 788 | [[package]] 789 | name = "tempfile" 790 | version = "2.2.0" 791 | source = "registry+https://github.com/rust-lang/crates.io-index" 792 | dependencies = [ 793 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 794 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 795 | "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", 796 | "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", 797 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 798 | ] 799 | 800 | [[package]] 801 | name = "unicode-xid" 802 | version = "0.0.4" 803 | source = "registry+https://github.com/rust-lang/crates.io-index" 804 | 805 | [[package]] 806 | name = "user32-sys" 807 | version = "0.1.2" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | dependencies = [ 810 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 811 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 812 | ] 813 | 814 | [[package]] 815 | name = "vecmath" 816 | version = "0.3.1" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | dependencies = [ 819 | "piston-float 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 820 | ] 821 | 822 | [[package]] 823 | name = "wayland-client" 824 | version = "0.9.10" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | dependencies = [ 827 | "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", 828 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 829 | "wayland-scanner 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", 830 | "wayland-sys 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", 831 | ] 832 | 833 | [[package]] 834 | name = "wayland-kbd" 835 | version = "0.9.1" 836 | source = "registry+https://github.com/rust-lang/crates.io-index" 837 | dependencies = [ 838 | "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 839 | "dlib 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 840 | "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", 841 | "memmap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 842 | "wayland-client 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", 843 | ] 844 | 845 | [[package]] 846 | name = "wayland-protocols" 847 | version = "0.9.10" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | dependencies = [ 850 | "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", 851 | "wayland-client 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", 852 | "wayland-scanner 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", 853 | "wayland-sys 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", 854 | ] 855 | 856 | [[package]] 857 | name = "wayland-scanner" 858 | version = "0.9.10" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | dependencies = [ 861 | "xml-rs 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 862 | ] 863 | 864 | [[package]] 865 | name = "wayland-sys" 866 | version = "0.9.10" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | dependencies = [ 869 | "dlib 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 870 | "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", 871 | ] 872 | 873 | [[package]] 874 | name = "wayland-window" 875 | version = "0.8.0" 876 | source = "registry+https://github.com/rust-lang/crates.io-index" 877 | dependencies = [ 878 | "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 879 | "tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 880 | "wayland-client 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", 881 | "wayland-protocols 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", 882 | ] 883 | 884 | [[package]] 885 | name = "winapi" 886 | version = "0.2.8" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | 889 | [[package]] 890 | name = "winapi-build" 891 | version = "0.1.1" 892 | source = "registry+https://github.com/rust-lang/crates.io-index" 893 | 894 | [[package]] 895 | name = "winit" 896 | version = "0.8.3" 897 | source = "registry+https://github.com/rust-lang/crates.io-index" 898 | dependencies = [ 899 | "android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 900 | "cocoa 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", 901 | "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 902 | "core-graphics 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", 903 | "dwmapi-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 904 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 905 | "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", 906 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 907 | "objc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 908 | "shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 909 | "tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 910 | "user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 911 | "wayland-client 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", 912 | "wayland-kbd 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", 913 | "wayland-protocols 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", 914 | "wayland-window 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 915 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 916 | "x11-dl 2.16.0 (registry+https://github.com/rust-lang/crates.io-index)", 917 | ] 918 | 919 | [[package]] 920 | name = "x11-dl" 921 | version = "2.16.0" 922 | source = "registry+https://github.com/rust-lang/crates.io-index" 923 | dependencies = [ 924 | "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", 925 | "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 926 | "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 927 | ] 928 | 929 | [[package]] 930 | name = "xml-rs" 931 | version = "0.6.1" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | dependencies = [ 934 | "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", 935 | ] 936 | 937 | [[package]] 938 | name = "xml-rs" 939 | version = "0.7.0" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | dependencies = [ 942 | "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 943 | ] 944 | 945 | [metadata] 946 | "checksum adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6cbd0b9af8587c72beadc9f72d35b9fbb070982c9e6203e46e93f10df25f8f45" 947 | "checksum android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" 948 | "checksum arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2f0ef4a9820019a0c91d918918c93dc71d469f581a49b47ddc1d285d4270bbe2" 949 | "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" 950 | "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" 951 | "checksum bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5cde24d1b2e2216a726368b2363a273739c91f4e3eb4e0dd12d672d396ad989" 952 | "checksum block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 953 | "checksum byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96c8b41881888cc08af32d47ac4edd52bc7fa27fef774be47a92443756451304" 954 | "checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" 955 | "checksum cgl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "86765cb42c2a2c497e142af72517c1b4d7ae5bb2f25dfa77a5c69642f2342d89" 956 | "checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd" 957 | "checksum cocoa 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4047fed6536f40cc2ae5e7834fb38e382c788270191c4cd69196f89686d076ce" 958 | "checksum color_quant 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a475fc4af42d83d28adf72968d9bcfaf035a1a9381642d8e85d8a04957767b0d" 959 | "checksum core-foundation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f51ce3b8ebe311c56de14231eb57572c15abebd2d32b3bcb99bcdb9c101f5ac3" 960 | "checksum core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5909502e547762013619f4c4e01cc7393c20fe2d52d7fa471c1210adb2320dc7" 961 | "checksum core-foundation-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "41115a6aa5d3e1e5ef98148373f25971d1fad53818553f216495f9e67e90a624" 962 | "checksum core-foundation-sys 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bc9fb3d6cb663e6fd7cf1c63f9b144ee2b1e4a78595a0451dd34bff85b9a3387" 963 | "checksum core-graphics 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9797d894882bbf37c0c1218a8d90333fae3c6b09d526534fd370aac2bc6efc21" 964 | "checksum deflate 0.7.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4dddda59aaab719767ab11d3efd9a714e95b610c4445d4435765021e9d52dfb1" 965 | "checksum dlib 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "148bce4ce1c36c4509f29cb54e62c2bd265551a9b00b38070fad551a851866ec" 966 | "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" 967 | "checksum dwmapi-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07c4c7cc7b396419bc0a4d90371d0cee16cb5053b53647d287c0b728000c41fe" 968 | "checksum either 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e311a7479512fbdf858fb54d91ec59f3b9f85bc0113659f46bba12b199d273ce" 969 | "checksum enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" 970 | "checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344" 971 | "checksum fs2 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bcd414e5a1a979b931bb92f41b7a54106d3f6d2e6c253e9ce943b7cd468251ef" 972 | "checksum fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f6c0581a4e363262e52b87f59ee2afe3415361c6ec35e665924eb08afe8ff159" 973 | "checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82" 974 | "checksum futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "118b49cac82e04121117cbd3121ede3147e885627d82c4546b87c702debb90c1" 975 | "checksum gdi32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "65256ec4dc2592e6f05bfc1ca3b956a4e0698aa90b1dff1f5687d55a5a3fd59a" 976 | "checksum gif 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e41945ba23db3bf51b24756d73d81acb4f28d85c3dccc32c6fae904438c25f" 977 | "checksum gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1c73b90c285f02059b34a6c66bc645ba5faa18c0e3ab332e0725654fc71db441" 978 | "checksum gl_generator 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "75d69f914b49d9ff32fdf394cbd798f8c716d74fd19f9cc29da3e99797b2a78d" 979 | "checksum gleam 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "c33943c68348c0f743b1e5e931a91ed64992b4441ddd0611fcc56db0553567d4" 980 | "checksum glutin 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5d459b91c4aac4c5aee285f1ac55d7b8cc85aa5d2ffd1bdac3b2707b6ab95e4a" 981 | "checksum image 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d1576ffa01849c91b484b95c01d54dddc242b4d50923eaa2d4d74a58c4b9e8fd" 982 | "checksum inflate 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10ec05638adf7c5c788bc0cfa608cd479a13572beda20feb4898fe1d85d2c64b" 983 | "checksum interpolation 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "84e53e2877f735534c2d3cdbb5ba1d04ee11107f599a1e811ab0ff3dd93fe66e" 984 | "checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" 985 | "checksum jpeg-decoder 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2805ccb10ffe4d10e06ef68a158ff94c255211ecbae848fbde2146b098f93ce7" 986 | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 987 | "checksum khronos_api 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "292227cfb1e811f7e974c427753fc8539394c6370a6849899306eedf2a478579" 988 | "checksum khronos_api 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d867c645cfeb8a7fec503731679eac03ac11b7105aa5a71cb8f8ee5271636add" 989 | "checksum lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c9e5e58fa1a4c3b915a561a78a22ee0cac6ab97dca2504428bc1cb074375f8d5" 990 | "checksum libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "5ba3df4dcb460b9dfbd070d41c94c19209620c191b0340b929ce748a2bcd42d2" 991 | "checksum libloading 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0a020ac941774eb37e9d13d418c37b522e76899bfc4e7b1a600d529a53f83a66" 992 | "checksum linked-hash-map 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2aab0478615bb586559b0114d94dd8eca4fdbb73b443adcb0d00b61692b4bf" 993 | "checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b" 994 | "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" 995 | "checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" 996 | "checksum memmap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "69253224aa10070855ea8fe9dbe94a03fc2b1d7930bb340c9e586a7513716fea" 997 | "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" 998 | "checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba" 999 | "checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01" 1000 | "checksum num-rational 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "288629c76fac4b33556f4b7ab57ba21ae202da65ba8b77466e6d598e31990790" 1001 | "checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0" 1002 | "checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d" 1003 | "checksum objc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "877f30f37acef6749b1841cceab289707f211aecfc756553cd63976190e6cc2e" 1004 | "checksum osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" 1005 | "checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc" 1006 | "checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f" 1007 | "checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03" 1008 | "checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2" 1009 | "checksum piston 0.35.0 (registry+https://github.com/rust-lang/crates.io-index)" = "585e35022b731ff709ca9825107596d1b26f03748872b1a0744ba3bcc5b70f97" 1010 | "checksum piston-float 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b058c3a640efd4bcf63266512e4bb03187192c1b29edd38b16d5a014613e3199" 1011 | "checksum piston-shaders_graphics2d 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "97bc17dac1dfff3e5cb84116062c7b46ff9d3dc0d88696a46d2f054cf64a10b6" 1012 | "checksum piston-texture 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3649b5f9d1ba48d95207976118e9e2ed473c1de36f6f79cc1b7ed5b75b655b61" 1013 | "checksum piston-viewport 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c5548a838fd9dc604c96d886c03c303f043a2d85f88719cca59dc7991d86343" 1014 | "checksum piston2d-graphics 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7bac05c4f46995c9e5661980249008663e68fbf652f3334d9e613461a4e829db" 1015 | "checksum piston2d-opengl_graphics 0.49.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9e541f540c9f829cfaefbebc3e70b0d09feabba056e9d4b251aa2b566ebd6dd8" 1016 | "checksum pistoncore-event_loop 0.35.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7018c8aeda06f0f030c42ac9b18901fca89d358e3a108018cdc67612db7afef0" 1017 | "checksum pistoncore-glutin_window 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)" = "770348d505cd6df834a8a152b53b7b84af8c506ec5014cda927bd690d2dffd7e" 1018 | "checksum pistoncore-input 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7fef44b03e1dfe7f16aa067a0d3591a1e75635206279c12493ddcb279fbcb66" 1019 | "checksum pistoncore-window 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "32a9c9c708945c6e7064ed271be93a246e4c0b19eefe4d70ba38c9978c93abed" 1020 | "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" 1021 | "checksum png 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f0b0cabbbd20c2d7f06dbf015e06aad59b6ca3d9ed14848783e98af9aaf19925" 1022 | "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" 1023 | "checksum rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6475140dfd8655aeb72e1fd4b7a1cc1c202be65d71669476e392fe62532b9edd" 1024 | "checksum rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b614fe08b6665cb9a231d07ac1364b0ef3cb3698f1239ee0c4c3a88a524f54c8" 1025 | "checksum rayon-core 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7febc28567082c345f10cddc3612c6ea020fc3297a1977d472cf9fdb73e6e493" 1026 | "checksum read_color 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "682bfa200630193df2954f2632b690c4643563fd6abc575edc1239bcfe57ad83" 1027 | "checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509" 1028 | "checksum rusttype 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "30047cc747a78ae042bf2cd65c79f83c3485d90107535b532d6e8f60e2c89cb1" 1029 | "checksum scoped_threadpool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4ea459fe3ceff01e09534847c49860891d3ff1c12b4eb7731b67f2778fb60190" 1030 | "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" 1031 | "checksum serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)" = "34b623917345a631dc9608d5194cc206b3fe6c3554cd1c75b937e55e285254af" 1032 | "checksum serde 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "0c9cab69e16835717c9b8bd13c29f92b6aa34fe32ce2866b1ab481cf2da8442a" 1033 | "checksum serde_derive 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "3bdafe3e71710131a919735916caa5b18c2754ad0d33d8ae5d586ccc804a403e" 1034 | "checksum serde_derive_internals 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "32f1926285523b2db55df263d2aa4eb69ddcfa7a7eade6430323637866b513ab" 1035 | "checksum serde_json 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ad8bcf487be7d2e15d3d543f04312de991d631cfe1b43ea0ade69e6a8a5b16a1" 1036 | "checksum shader_version 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a29e10c39144f4663c0f74de29b9a61237bf410be40753b1a3b682832abcf4aa" 1037 | "checksum shared_library 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "7822f9d0814224552cfd7e4ac72cd511740ccec0b811d1c0f9fa2a84c6509cee" 1038 | "checksum shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f20b8f3c060374edb8046591ba28f62448c369ccbdc7b02075103fb3a9e38d" 1039 | "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" 1040 | "checksum stb_truetype 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "21b5c3b588a493a477e0d99769ee091b3627625f9ba4bdd882e6b4b0b0958805" 1041 | "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" 1042 | "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" 1043 | "checksum target_build_utils 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "013d134ae4a25ee744ad6129db589018558f620ddfa44043887cdd45fa08e75c" 1044 | "checksum tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11ce2fe9db64b842314052e2421ac61a73ce41b898dc8e3750398b219c5fc1e0" 1045 | "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" 1046 | "checksum user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6717129de5ac253f5642fc78a51d0c7de6f9f53d617fc94e9bae7f6e71cf5504" 1047 | "checksum vecmath 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1bdd6034ee9c1e5e12485f3e4120e12777f6c81cf43bf9a73bff98ed2b479afe" 1048 | "checksum wayland-client 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "9b10f2880f3dedaa496609a0aa7117bc6824490a48309dfbbf26258e5acc5a9d" 1049 | "checksum wayland-kbd 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "75485a10a894e48f4d21c15c8673ac84a073aef402e15060715fb3501416e58e" 1050 | "checksum wayland-protocols 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "008c5b9bffb6afdfcf8df0b72fd37b2508476867305ed6d47610f5431a534ac6" 1051 | "checksum wayland-scanner 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "6820262132b76ee4aa7893312fb9a24ce5434934a2b421669a30869fcd4a2769" 1052 | "checksum wayland-sys 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "b433ca9dbd9289a8ae8a5c49148d2a0e724b89432d7648727ca553027c247c47" 1053 | "checksum wayland-window 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c03dae6f8f8be09335444fc253620298bb05f5b8fbc6237798bbbc90ea841c4" 1054 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 1055 | "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 1056 | "checksum winit 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74bcacc675f952f71c2ebc9750dfd90d605de2cbe2e8ea3b38a370498238a507" 1057 | "checksum x11-dl 2.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd409cc4061fe552ee165c7844cc88596f68d52605c64710b57d8231c700820b" 1058 | "checksum xml-rs 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e1945e12e16b951721d7976520b0832496ef79c31602c7a29d950de79ba74621" 1059 | "checksum xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c1cb601d29fe2c2ac60a2b2e5e293994d87a1f6fa9687a31a15270f909be9c2" 1060 | --------------------------------------------------------------------------------