├── .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 |
4 |
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 |
5 |
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 |
10 |
11 |
12 |
13 |
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 | }
--------------------------------------------------------------------------------