├── .gitignore ├── game.config ├── src ├── game_engine │ ├── commands.rs │ ├── game_events.rs │ ├── game_data_model.rs │ ├── shapes.rs │ ├── mod.rs │ ├── point3d.rs │ └── game_board.rs └── lib.rs ├── game_cfg ├── .idea ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── vcs.xml ├── misc.xml ├── .gitignore ├── modules.xml └── git_toolbox_prj.xml ├── Cargo.toml └── pixel_game_engine.iml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /game.config: -------------------------------------------------------------------------------- 1 | width=800 2 | height=800 3 | map_size=80 4 | board_width=600 5 | board_height=600 6 | -------------------------------------------------------------------------------- /src/game_engine/commands.rs: -------------------------------------------------------------------------------- 1 | pub trait Commands { 2 | fn run_command(cmd_str: &str) -> bool; 3 | } 4 | -------------------------------------------------------------------------------- /game_cfg: -------------------------------------------------------------------------------- 1 | - Polygon 2 | P, 23.0, 15.0, 25.0, 20.0, 20.0, 20.0 3 | C, 1.0, 1.0, 0.0, 1.0 4 | # 5 | - Circle 6 | P, 50.0, 70.0, 15.0 7 | C, 1.0, 0.0, 0.0, 1.0 8 | # -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0]; 2 | pub const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0]; 3 | pub const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0]; 4 | 5 | pub mod game_engine; 6 | 7 | #[cfg(test)] 8 | mod tests { 9 | #[test] 10 | fn it_works() { 11 | assert_eq!(2 + 2, 4); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/game_engine/game_events.rs: -------------------------------------------------------------------------------- 1 | use piston::{RenderArgs, UpdateArgs, Event, Button, Key, MouseButton, ControllerButton, HatState, ControllerHat}; 2 | use crate::game_engine::game_board::PixelMap; 3 | 4 | pub trait PistonGameEvents { 5 | fn update_game_board(&mut self, args: &RenderArgs) -> PixelMap; 6 | fn update(&mut self, args: &UpdateArgs); 7 | fn handle_press_events(&mut self, button: &Button); 8 | fn handle_release_events(&mut self, button: &Button); 9 | // events 10 | } 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pixel_game_engine" 3 | version = "0.1.0" 4 | authors = ["Tarin Mahmood "] 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 | 11 | piston = "0.53.1" 12 | piston2d-graphics = "0.41.0" 13 | pistoncore-glutin_window = "0.69.0" 14 | piston_window = "0.121.0" 15 | piston2d-opengl_graphics = "0.79.0" 16 | mint="0.5.8" 17 | uuid="1.0.0-alpha.1" 18 | image="0.23.14" 19 | gfx = "0.18.2" 20 | rand = "0.8.4" 21 | rayon="1.5.1" -------------------------------------------------------------------------------- /.idea/git_toolbox_prj.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /pixel_game_engine.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/game_engine/game_data_model.rs: -------------------------------------------------------------------------------- 1 | use crate::game_engine::shapes::Block; 2 | use crate::game_engine::game_board::PixelMap; 3 | 4 | /// GameDataModel implements engine independent game data 5 | /// It should never bound to any specific game library 6 | pub trait GameDataModel { 7 | fn get_drawables(&self) -> (Vec, &Vec>); 8 | fn get_window_width(&self) -> i32; 9 | fn get_window_height(&self) -> i32; 10 | fn get_map_size(&self) -> i32; 11 | fn get_board_width(&self) -> f32; 12 | fn get_board_height(&self) -> f32; 13 | fn get_block_width(&self) -> f32; 14 | fn get_block_height(&self) -> f32; 15 | } 16 | -------------------------------------------------------------------------------- /src/game_engine/shapes.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::BLACK; 3 | use std::convert::TryInto; 4 | 5 | #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] 6 | pub enum ShapeKind { 7 | Rect, 8 | Polygon, 9 | Line, 10 | Ellipse, 11 | Circle, 12 | None 13 | } 14 | 15 | #[derive(Copy, Clone, Debug)] 16 | pub struct Block { 17 | pub x: f32, 18 | pub y: f32, 19 | pub color: [f32; 4], 20 | pub shape: ShapeKind, 21 | pub index: usize, 22 | } 23 | 24 | #[derive(Copy, Clone, Debug)] 25 | pub struct BlockBuilder { 26 | block: Block, 27 | index: Option, 28 | } 29 | 30 | impl BlockBuilder { 31 | pub fn empty() -> Self { 32 | BlockBuilder { 33 | block: Block { 34 | x: 0.0, y: 0.0, 35 | color: [0.0, 0.0, 0.0, 1.0], 36 | shape: ShapeKind::None, index: 0, 37 | }, index: None 38 | } 39 | } 40 | 41 | pub fn new(s: ShapeKind, x: f32, y: f32) -> Self { 42 | BlockBuilder { 43 | block: Block { 44 | x, y, 45 | color: [1.0, 1.0, 1.0, 1.0], 46 | shape: s, index: 0, 47 | }, index: None 48 | } 49 | } 50 | 51 | pub fn rect(x: f32, y: f32) -> Self { 52 | BlockBuilder::new(ShapeKind::Rect, x, y) 53 | } 54 | 55 | pub fn line(x: f32, y: f32) -> Self { 56 | BlockBuilder::new(ShapeKind::Line, x, y) 57 | } 58 | 59 | pub fn circle(x: f32, y: f32) -> Self { 60 | BlockBuilder::new(ShapeKind::Circle, x, y) 61 | } 62 | 63 | pub fn ellipse(x: f32, y: f32) -> Self { 64 | BlockBuilder::new(ShapeKind::Ellipse, x, y) 65 | } 66 | 67 | pub fn polygon(x: f32, y: f32) -> Self { 68 | BlockBuilder::new(ShapeKind::Polygon, x, y) 69 | } 70 | 71 | pub fn color(&mut self, color: Vec) -> &mut Self { 72 | self.block.color = [color[0], color[1], color[2], color[3]]; 73 | self 74 | } 75 | 76 | pub fn points(&mut self, points: Vec, points_list: &mut Vec>) -> &mut Self { 77 | self.index = Some(points_list.len()); 78 | points_list.push(points); 79 | self 80 | } 81 | 82 | pub fn build(&mut self) -> Block { 83 | if self.block.shape == ShapeKind::None { 84 | panic!("Block is not initialized"); 85 | } 86 | if self.index.is_none() { 87 | panic!("Position information not initialized"); 88 | } 89 | self.block.index = self.index.unwrap(); 90 | return self.block; 91 | } 92 | } 93 | 94 | impl Block { 95 | 96 | pub fn get_shape_info(&self, point_list: &Vec>) -> Vec { 97 | point_list[self.index].to_vec() 98 | } 99 | 100 | pub fn get_shape_kind(&self) -> ShapeKind { 101 | self.shape.to_owned() 102 | } 103 | 104 | pub fn get_index(&self) -> usize { 105 | self.index 106 | } 107 | } 108 | 109 | -------------------------------------------------------------------------------- /src/game_engine/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod point3d; 2 | pub mod game_data_model; 3 | pub mod shapes; 4 | pub mod commands; 5 | 6 | use crate::{BLACK, GREEN, RED}; 7 | use crate::game_engine::shapes::{ShapeKind, Block}; 8 | use crate::game_engine::game_data_model::GameDataModel; 9 | use crate::game_engine::game_board::{GameBoard, Pixel, PixelMap}; 10 | use std::collections::HashMap; 11 | use rayon::iter::{IntoParallelRefIterator, ParallelBridge, ParallelIterator}; 12 | use opengl_graphics::{GlGraphics, OpenGL}; 13 | use piston_window::{PistonWindow as Window, WindowSettings}; 14 | use piston::{RenderArgs, Events, EventSettings, RenderEvent, UpdateEvent, PressEvent, ReleaseEvent, ResizeEvent, Button, Key}; 15 | use graphics::{clear, rectangle}; 16 | use graphics::types::Color; 17 | use crate::game_engine::game_events::PistonGameEvents; 18 | 19 | 20 | pub mod game_board; 21 | pub mod game_events; 22 | 23 | pub struct GameEngineData { 24 | gl: GlGraphics, 25 | window: Window, 26 | } 27 | 28 | pub fn init_game_engine(size: [f64; 2], opengl: OpenGL) -> GameEngineData { 29 | let window = WindowSettings::new("Game", size) 30 | .graphics_api(opengl) 31 | .exit_on_esc(true) 32 | .build() 33 | .unwrap(); 34 | let gl = GlGraphics::new(opengl); 35 | GameEngineData { 36 | gl, 37 | window, 38 | } 39 | } 40 | 41 | pub fn draw_shapes(shapes: &Vec, point_list: &Vec>) -> PixelMap { 42 | let mut pixels: PixelMap = HashMap::new(); 43 | shapes.iter().for_each(|block| { 44 | let k = &point_list[block.index]; 45 | match block.shape { 46 | ShapeKind::Rect => { 47 | game_board::draw_rectangle( 48 | block.x + k[0], block.y + k[1], k[2], k[3], &mut pixels, 49 | Color::from(block.color), 50 | ); 51 | } 52 | ShapeKind::Circle => { 53 | game_board::draw_circle( 54 | block.x + k[0], block.y + k[1], k[2], &mut pixels, 55 | Color::from(block.color), 56 | ); 57 | } 58 | ShapeKind::Ellipse => { 59 | game_board::draw_ellipse( 60 | block.x + k[0], block.y + k[1], k[2], k[3], &mut pixels, 61 | Color::from(block.color), 62 | ); 63 | } 64 | ShapeKind::Polygon => { 65 | game_board::draw_polygon(k, &block, &mut pixels) 66 | } 67 | ShapeKind::Line => { 68 | game_board::draw_line( 69 | block.x + k[0], block.y + k[1], 70 | block.x + k[2], block.y + k[3], 71 | &mut pixels, 72 | Color::from(block.color), 73 | ); 74 | } 75 | ShapeKind::None => { 76 | panic!("This should not happen") 77 | } 78 | }; 79 | }); 80 | pixels 81 | } 82 | 83 | pub fn game_loop(mut app: T, mut game_data: GameEngineData) { 84 | use graphics::*; 85 | let mut events = Events::new(EventSettings::new()); 86 | // drawing context 87 | while let Some(e) = events.next(&mut game_data.window) { 88 | if let Some(args) = e.render_args() { 89 | // get all the drawable 90 | let pixels = app.update_game_board(&args); 91 | let block_width = app.get_block_width(); 92 | let block_height = app.get_block_height(); 93 | let board_width = app.get_board_width(); 94 | let board_height = app.get_board_height(); 95 | let base_rect = rectangle::rectangle_by_corners( 96 | 0.0, 0.0, 97 | block_width as f64, block_height as f64, 98 | ); 99 | const GRID_COLOR: [f32; 4] = [0.3, 0.3, 0.3, 0.3]; 100 | // doing drawing stuffS 101 | game_data.gl.draw(args.viewport(), |c, gl| { 102 | // Clear the screen. 103 | clear(BLACK, gl); 104 | let s = (block_width * 2.0) as i32; 105 | for ix in (s..board_width as i32).step_by(s as usize) { 106 | let l = [ix as f64, 0.0, ix as f64, board_height as f64]; 107 | line(GRID_COLOR, 1.0, l, c.transform, gl); 108 | } 109 | let s = (block_height * 2.0) as i32; 110 | for iy in (s..board_height as i32).step_by(s as usize) { 111 | let l = [0.0, iy as f64, board_height as f64, iy as f64]; 112 | line(GRID_COLOR, 1.0, l, c.transform, gl); 113 | } 114 | pixels.values().for_each(|pixel| { 115 | let transform = c.transform.trans( 116 | (pixel.point.x * block_width) as f64, 117 | (pixel.point.y * block_height) as f64 118 | ); 119 | rectangle(pixel.color, base_rect, transform, gl); 120 | }); 121 | }); 122 | } 123 | if let Some(button) = e.press_args() { 124 | app.handle_press_events(&button); 125 | } 126 | if let Some(button) = e.release_args() { 127 | app.handle_release_events(&button); 128 | } 129 | if let Some(args) = e.update_args() { 130 | app.update(&args); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/game_engine/point3d.rs: -------------------------------------------------------------------------------- 1 | use std::ops; 2 | use std::ops::{Neg, Mul, Index}; 3 | 4 | 5 | #[derive(Copy, Clone)] 6 | pub struct Point3D { 7 | e: [f32; 3] 8 | } 9 | 10 | impl Point3D { 11 | 12 | pub fn new(x: f32, y: f32, z: f32) -> Self { 13 | Point3D { e: [x, y, z] } 14 | } 15 | 16 | 17 | pub fn x(&self) -> f32 { self.e[0] } 18 | pub fn y(&self) -> f32 { self.e[1] } 19 | pub fn z(&self) -> f32 { self.e[2] } 20 | 21 | pub fn x_i32(&self) -> i32 { self.e[0] as i32} 22 | pub fn y_i32(&self) -> i32 { self.e[1] as i32} 23 | pub fn z_i32(&self) -> i32 { self.e[2] as i32} 24 | 25 | pub fn length(&self) -> f32 { 26 | self.length_squared().sqrt() 27 | } 28 | 29 | pub fn length_squared(&self) -> f32 { 30 | self.x() * self.x() + self.y() * self.y() + self.z() * self.z() 31 | } 32 | 33 | pub fn display(&self) { 34 | println!("{} {} {}", self.x(), self.y(), self.z()); 35 | } 36 | 37 | pub fn dot(&self, v2: Point3D) -> f32 { 38 | self.x() * v2.x() + self.y() * v2.y() + self.z() * v2.z() 39 | } 40 | 41 | pub fn unit_vector(&self) -> Point3D { 42 | *self / self.length() 43 | } 44 | 45 | pub fn cross(&self, v: Point3D) -> Point3D { 46 | Point3D { 47 | e: [ 48 | self.y() * v.z() - self.z() * v.y(), 49 | self.z() * v.x() - self.x() * v.z(), 50 | self.x() * v.y() - self.y() * v.x(), 51 | ] 52 | } 53 | } 54 | } 55 | 56 | impl ops::Neg for Point3D { 57 | type Output = Point3D; 58 | fn neg(self) -> Self::Output { 59 | self.mul(-1.0) } } 60 | 61 | impl ops::Add for Point3D { 62 | type Output = Point3D; 63 | fn add(self, _rhs: Point3D) -> Self::Output { 64 | Point3D { 65 | e: [ 66 | self.e[0] + _rhs.e[0], 67 | self.e[1] + _rhs.e[1], 68 | self.e[2] + _rhs.e[2], 69 | ] 70 | } 71 | } 72 | } 73 | 74 | impl ops::AddAssign for Point3D { 75 | fn add_assign(&mut self, _rhs: Point3D) { 76 | self.e[0] += _rhs.e[0]; 77 | self.e[1] += _rhs.e[1]; 78 | self.e[2] += _rhs.e[2]; 79 | } 80 | } 81 | 82 | impl ops::Sub for Point3D { 83 | type Output = Point3D; 84 | fn sub(self, _rhs: Point3D) -> Self::Output { 85 | Point3D { 86 | e: [ 87 | self.e[0] - _rhs.e[0], 88 | self.e[1] - _rhs.e[1], 89 | self.e[2] - _rhs.e[2], 90 | ] 91 | } 92 | } 93 | } 94 | 95 | impl ops::Mul for Point3D { 96 | type Output = Point3D; 97 | fn mul(self, rhs: Point3D) -> Self::Output { 98 | Point3D { 99 | e: [ 100 | self.e[0] * rhs.x(), 101 | self.e[1] * rhs.y(), 102 | self.e[2] * rhs.z(), 103 | ] 104 | } 105 | } 106 | } 107 | 108 | impl ops::Mul for Point3D { 109 | type Output = Point3D; 110 | fn mul(self, rhs: f32) -> Self::Output { 111 | Point3D { 112 | e: [ 113 | self.e[0] * rhs, 114 | self.e[1] * rhs, 115 | self.e[2] * rhs, 116 | ] 117 | } 118 | } 119 | } 120 | 121 | impl ops::MulAssign for Point3D { 122 | fn mul_assign(&mut self, rhs: f32) { 123 | self.e[0] = self.x() * rhs; 124 | self.e[1] = self.y() * rhs; 125 | self.e[2] = self.z() * rhs; 126 | } 127 | } 128 | 129 | impl ops::Div for Point3D { 130 | type Output = Point3D; 131 | 132 | fn div(self, rhs: f32) -> Point3D { 133 | self * (1f32 / rhs) 134 | } 135 | } 136 | 137 | impl ops::DivAssign for Point3D { 138 | fn div_assign(&mut self, rhs: f32) { 139 | self.e[0] = self.x() / rhs; 140 | self.e[1] = self.y() / rhs; 141 | self.e[2] = self.z() / rhs; 142 | } 143 | } 144 | 145 | #[cfg(test)] 146 | mod tests { 147 | use crate::game_engine::point3d::Point3D; 148 | 149 | #[test] 150 | fn test_cross() { 151 | let v1 = Point3D { e: [2f32, 3f32, 4f32] }; 152 | let v2 = Point3D { e: [5f32, 6f32, 7f32] }; 153 | let v3 = v1.cross(v2); 154 | assert_eq!(v3.e, [-3.0, 6.0, -3.0]); 155 | } 156 | 157 | #[test] 158 | fn test_div_assign() { 159 | let mut v = Point3D { e: [6f32, 9f32, 12f32] }; 160 | v /= 3f32; 161 | assert_eq!(v.e, [2f32, 3f32, 4f32]); 162 | } 163 | 164 | 165 | #[test] 166 | fn test_div() { 167 | let v = Point3D { e: [6f32, 9f32, 12f32] }; 168 | let k = v / 3f32; 169 | assert_eq!(k.e, [2f32, 3f32, 4f32]) 170 | } 171 | 172 | #[test] 173 | fn test_mul_assign() { 174 | let mut v1 = Point3D { e: [5f32, 2f32, 5f32] }; 175 | v1 *= 3f32; 176 | assert_eq!(v1.e, [15f32, 6f32, 15f32]) 177 | } 178 | 179 | 180 | #[test] 181 | fn test_sub() { 182 | let v = Point3D { e: [5f32, 2f32, 5f32] } - Point3D { e: [4f32, 1f32, 5f32] }; 183 | assert_eq!(v.e, [1f32, 1f32, 0f32]) 184 | } 185 | 186 | #[test] 187 | fn test_neg() { 188 | let v = -Point3D { e: [5f32, 2f32, 5f32] }; 189 | assert_eq!(v.e, [-5f32, -2f32, -5f32]) 190 | } 191 | 192 | 193 | #[test] 194 | fn test_addition_assign() { 195 | let mut v = Point3D { e: [3f32, 2f32, 5f32] }; 196 | v += Point3D { e: [4f32, 1f32, 5f32] }; 197 | assert_eq!(v.e, [7f32, 3f32, 10f32]) 198 | } 199 | } 200 | 201 | pub fn dot(a: &Point3D, b: &Point3D) -> f32 { 202 | a.x() * b.x() + a.y() * b.y() + a.z() * b.z() 203 | } 204 | 205 | pub fn unit_vector(a: &Point3D) -> Point3D { 206 | *a / a.length() 207 | } 208 | 209 | pub fn cross(a: &Point3D, v: &Point3D) -> Point3D { 210 | Point3D { 211 | e: [ 212 | a.y() * v.z() - a.z() * v.y(), 213 | a.z() * v.x() - a.x() * v.z(), 214 | a.x() * v.y() - a.y() * v.x(), 215 | ] 216 | } 217 | } 218 | 219 | -------------------------------------------------------------------------------- /src/game_engine/game_board.rs: -------------------------------------------------------------------------------- 1 | /// # Game Board 2 | /// game board handles the game without depending on any game library. 3 | /// This allows to implement our game using any game engine/backend we want. 4 | /// We are only modifying the game array here. 5 | 6 | 7 | use std::collections::HashMap; 8 | use crate::game_engine::shapes::Block; 9 | use std::mem::swap; 10 | use mint::Point2; 11 | use graphics::types::Color; 12 | 13 | pub type PixelMap = HashMap; 14 | 15 | fn make_key(x: f32, y: f32) -> String { 16 | format!("{},{}", x, y) 17 | } 18 | 19 | #[derive(Debug)] 20 | pub struct Pixel { 21 | pub point: Point2, 22 | pub color: Color, 23 | } 24 | 25 | pub struct GameBoard { 26 | pub pixels: PixelMap 27 | } 28 | 29 | impl GameBoard { 30 | pub fn new() -> Self { 31 | GameBoard { 32 | pixels: HashMap::new() 33 | } 34 | } 35 | } 36 | 37 | pub fn set_pixel(pixels: &mut PixelMap, x: f32, y: f32, color: Color) { 38 | pixels.insert(make_key(x, y), Pixel { 39 | point: Point2 { x, y }, 40 | color, 41 | }); 42 | } 43 | 44 | pub fn get_pixel(pixels: &PixelMap, x: f32, y: f32) -> Option<&Pixel> { 45 | pixels.get(&make_key(x, y)) 46 | } 47 | 48 | /// draw rectangle 49 | pub fn draw_rectangle(x: f32, y: f32, w: f32, h: f32, pixels: &mut PixelMap, color: Color) { 50 | let mut i = x; 51 | loop { 52 | let mut j = y; 53 | if i >= x + w { break; } 54 | loop { 55 | if j >= y + h { break; } 56 | set_pixel(pixels, i, j, color); 57 | j = j + 1.0; 58 | } 59 | i = i + 1.0; 60 | } 61 | } 62 | 63 | /// Drawing ellipse with pixels 64 | pub fn draw_ellipse(cx: f32, cy: f32, a: f32, b: f32, points: &mut PixelMap, color: Color) { 65 | let mut plot_ellipse = |x: f32, y: f32| { 66 | set_pixel(points, cx + x, cy + y, color); 67 | set_pixel(points, cx + x, cy - y, color); 68 | set_pixel(points, cx - x, cy + y, color); 69 | set_pixel(points, cx - x, cy - y, color); 70 | }; 71 | let a2 = a * a; 72 | let b2 = b * b; 73 | let mut x = 0.0; 74 | let mut y = b; 75 | let mut p = b2 + (a2 * (1.0 - 4.0 * b) - 2.0) / 4.0; 76 | let mut d_pe = 3.0 * b2; 77 | let mut d2_pe = 2.0 * b2; 78 | let mut d_pse = d_pe - 2.0 * a2 * (b - 1.0); 79 | let mut d2_pse = d2_pe + 2.0 * a2; 80 | plot_ellipse(x, y); 81 | while d_pse < 2.0 * a2 + 3.0 * b2 { 82 | if p < 0.0 { 83 | p = p + d_pe; 84 | d_pe = d_pe + d2_pe; 85 | d_pse = d_pse + d2_pe; 86 | } else { 87 | p = p + d_pse; 88 | d_pe = d_pe + d2_pe; 89 | d_pse = d_pse + d2_pse; 90 | y -= 1.0; 91 | } 92 | x += 1.0; 93 | plot_ellipse(x, y); 94 | } 95 | // let mut d_pse = d_pe - 2.0 * a2 * (b - 1.0); 96 | // let mut d2_pse = d2_pe + 2.0 * a2; 97 | p = p - (a2 * (4.0 * y - 3.0) + b2 * (4.0 * x + 3.0) + 2.0) / 4.0; 98 | let mut d2_ps = 2.0 * a2; 99 | d_pse = 2.0 * b2 + 3.0 * a2; 100 | while y > 0.0 { 101 | if p > 0.0 { 102 | p = p + d_pe; 103 | d_pe = d_pe + d2_ps; 104 | d_pse = d_pse + d2_ps; 105 | } else { 106 | p = p + d_pse; 107 | d_pe = d_pe + d2_ps; 108 | d_pse = d_pse + d2_pse; 109 | x += 1.0; 110 | } 111 | y -= 1.0; 112 | plot_ellipse(x, y); 113 | } 114 | } 115 | 116 | pub fn draw_line(_x1: f32, _y1: f32, _x2: f32, _y2: f32, points: &mut PixelMap, color: Color) { 117 | let mut x1 = _x1; 118 | let mut x2 = _x2; 119 | let mut y1 = _y1; 120 | let mut y2 = _y2; 121 | // difference between points 122 | let dx = (x2 - x1); 123 | let dy = (y2 - y1); 124 | // if any of dx/dy is zero ... it's a straight line along axis 125 | if dx == 0.0 { 126 | return draw_straight_line(x1, y1, y2, points, color, true); 127 | } 128 | if dy == 0.0 { 129 | return draw_straight_line(y1, x1, x2, points, color, false); 130 | } 131 | // lets start drawing 132 | let mut x = x1; 133 | let mut y = y1; 134 | let dx1 = dx.abs(); 135 | let dy1 = dy.abs(); 136 | let mut px = 2.0 * dy1 - dx1; 137 | let mut py = 2.0 * dx1 - dy1; 138 | let mut xe; 139 | let mut ye; 140 | // 141 | if dy1 <= dx1 { 142 | // find the starting point. 143 | if dx >= 0.0 { 144 | x = x1; 145 | y = y1; 146 | xe = x2; 147 | } else { 148 | x = x2; 149 | y = y2; 150 | xe = x1; 151 | } 152 | set_pixel(points, x, y, color); 153 | while x < xe { 154 | x = x + 1.0; 155 | if px < 0.0 { 156 | px = px + 2.0 * dy1; 157 | } else { 158 | if (dx < 0.0 && dy < 0.0) || (dx > 0.0 && dy > 0.0) { 159 | y = y + 1.0 160 | } else { 161 | y = y - 1.0 162 | } 163 | px = px + 2.0 * (dy1 - dx1); 164 | } 165 | set_pixel(points, x, y, color); 166 | } 167 | } else { 168 | if dy >= 0.0 { 169 | x = x1; 170 | y = y1; 171 | ye = y2; 172 | } else { 173 | x = x2; 174 | y = y2; 175 | ye = y1; 176 | } 177 | set_pixel(points, x, y, color); 178 | while y < ye { 179 | y = y + 1.0; 180 | if py <= 0.0 { 181 | py = py + 2.0 * dx1; 182 | } else { 183 | if (dx < 0.0 && dy < 0.0) || (dx > 0.0 && dy > 0.0) { 184 | x = x + 1.0 185 | } else { 186 | x = x - 1.0; 187 | } 188 | py = py + 2.0 * (dx1 - dy1); 189 | } 190 | set_pixel(points, x, y, color); 191 | } 192 | } 193 | } 194 | 195 | fn draw_straight_line(fixed_axis: f32, _p1: f32, _p2: f32, points: &mut PixelMap, color: Color, if_vertical: bool) { 196 | let mut p1 = _p1; 197 | let mut p2 = _p2; 198 | if _p2 < _p1 { 199 | p1 = _p2; 200 | p2 = _p1; 201 | } 202 | let mut change_axis = p1; 203 | if if_vertical { 204 | while change_axis <= p2 { 205 | set_pixel(points, fixed_axis, change_axis, color); 206 | change_axis = change_axis + 1.0; 207 | } 208 | } else { 209 | while change_axis <= p2 { 210 | set_pixel(points, change_axis, fixed_axis, color); 211 | change_axis = change_axis + 1.0; 212 | } 213 | } 214 | return; 215 | } 216 | 217 | pub fn draw_polygon(point_list: &Vec, block: &Block, pixels: &mut PixelMap) { 218 | let mut p: Vec = Vec::new(); 219 | point_list.chunks(2) 220 | // parse through the pairs 221 | .for_each(|item| { 222 | // add to point list 223 | p.extend(item); 224 | // we have 2 points, so draw the line 225 | if p.len() == 4 { 226 | draw_line( 227 | block.x + p[0], block.y + p[1], 228 | block.x + p[2], block.y + p[3], pixels, 229 | Color::from(block.color), 230 | ); 231 | // only keep last two points 232 | p = vec![p[2], p[3]]; 233 | } 234 | }); 235 | } 236 | 237 | pub fn draw_circle(x: f32, y: f32, r: f32, points: &mut PixelMap, color: Color) { 238 | let mut x0 = 0.0; 239 | let mut y0 = r; 240 | let mut d = 3.0 - 2.0 * r; 241 | while y0 >= x0 { 242 | set_pixel(points, x + x0, y - y0, color); 243 | set_pixel(points, x + y0, y - x0, color); 244 | set_pixel(points, x + y0, y + x0, color); 245 | set_pixel(points, x + x0, y + y0, color); 246 | set_pixel(points, x - x0, y + y0, color); 247 | set_pixel(points, x - y0, y + x0, color); 248 | set_pixel(points, x - y0, y - x0, color); 249 | set_pixel(points, x - x0, y - y0, color); 250 | x0 += 1.0; 251 | if d < 0.0 { 252 | d += 4.0 * x0 + 6.0; 253 | } else { 254 | y0 -= 1.0; 255 | d += 4.0 * (x0 - y0) + 10.0; 256 | } 257 | } 258 | } --------------------------------------------------------------------------------