From 6929af356b200681430e728a989970271ee9c493 Mon Sep 17 00:00:00 2001 From: Marc Jones Date: Thu, 15 Sep 2022 17:29:13 -0600 Subject: [PATCH 1/9] Fix Rust Clippy recomendations Fix the following clippy recomendations: the loop variable `i` is only used to index `p` `#[warn(clippy::needless_range_loop)]` on by default for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loopclippyneedless_range_loop main.rs(31, 17): consider using an iterator: ``, `&mut p` manual implementation of an assign operation for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_patternclippyassign_op_pattern main.rs(36, 9): replace it with: `counter += 1` unneeded `return` statement `#[warn(clippy::needless_return)]` on by default for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_returnclippyneedless_return particle.rs(17, 9): remove `return`: `Particle { redundant field names in struct initialization `#[warn(clippy::redundant_field_names)]` on by default for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_namesclippyredundant_field_names particle.rs(18, 13): replace it with: `pos` manual implementation of an assign operation `#[warn(clippy::assign_op_pattern)]` on by default for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_patternclippyassign_op_pattern particle.rs(32, 9): replace it with: `self.pos[0] += self.vel[0]` Signed-off-by: Marc Jones fix clippy Signed-off-by: Marc Jones --- rust_particles/src/main.rs | 8 ++++---- rust_particles/src/particle.rs | 28 ++++++++++++++-------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/rust_particles/src/main.rs b/rust_particles/src/main.rs index 46a9d3d..e95516e 100644 --- a/rust_particles/src/main.rs +++ b/rust_particles/src/main.rs @@ -28,12 +28,12 @@ fn main() { while let Some(e) = window.next() { window.draw_2d(&e, |c, g, _device| { clear([0.0, 0.0, 0.0, 1.0], g); - for i in 0..p.len() { - ellipse(color::alpha(p[i].lifetime), p[i].show(), c.transform, g); - p[i].update(); + for i in &mut p { + ellipse(i.colour, i.show(), c.transform, g); + i.update(); } }); - counter = counter + 1; + counter += 1; if counter % 10 == 0 { p.push(particle::Particle::new( vec![200., 200.], diff --git a/rust_particles/src/particle.rs b/rust_particles/src/particle.rs index 4c870d9..c7e0dad 100644 --- a/rust_particles/src/particle.rs +++ b/rust_particles/src/particle.rs @@ -14,10 +14,10 @@ pub struct Particle { impl Particle { pub fn new(pos: Vec, vel: Vec, acc: Vec, height: u32, width: u32) -> Particle { - return Particle { - pos: pos, - vel: vel, - acc: acc, + Particle { + pos, + vel, + acc, lifetime: 1.0, size: 10., colour: [1.0, 0.0, 0.0, 1.0], @@ -25,22 +25,22 @@ impl Particle { max_acc: 0.5, height: height as f64, width: width as f64, - }; + } } pub fn update(&mut self) { - self.pos[0] = self.pos[0] + self.vel[0]; - self.vel[0] = self.vel[0] + self.acc[0]; - self.pos[1] = self.pos[1] + self.vel[1]; - self.vel[1] = self.vel[1] + self.acc[1]; + self.pos[0] += self.vel[0]; + self.vel[0] += self.acc[0]; + self.pos[1] += self.vel[1]; + self.vel[1] += self.acc[1]; self.check_limits(); self.edges(); - self.lifetime = self.lifetime - 0.01; + self.lifetime -= 0.01; self.colour = [1.0, 0.0, 0.0, self.lifetime]; } pub fn show(&self) -> [f64; 4] { - return [self.pos[0], self.pos[1], self.size, self.size]; + [self.pos[0], self.pos[1], self.size, self.size] } fn check_limits(&mut self) { @@ -60,14 +60,14 @@ impl Particle { fn edges(&mut self) { if self.pos[1] >= self.height || self.pos[1] <= 0.0 { - self.vel[1] = self.vel[1] * -1.0; + self.vel[1] *= -1.0; } if self.pos[0] >= self.width || self.pos[0] <= 0.0 { - self.vel[0] = self.vel[0] * -1.0; + self.vel[0] *= -1.0; } } pub fn finished(self) -> bool { - return self.lifetime <= 0.; + self.lifetime <= 0. } } From 51e904c84f390ef151f8ea63bc44e7124fb7ea04 Mon Sep 17 00:00:00 2001 From: Marc Jones Date: Thu, 15 Sep 2022 17:36:47 -0600 Subject: [PATCH 2/9] Use iterator instead of loop for particle cleanup Similar to the previous clippy recomendation, use Rust iterator instead of a for loop to remove particles that have reached the end of lifetime. Note that the article mentions that the for loop goes backwards, but that isn't neccesary for two reasons. [1] 1. The partical vector is a FIFO, not a stack. The first particals are the oldest, so it should scan front to back. 2. The order of scanning doesn't matter since it is all done before the screen is redrawn. Since we don't really care about what order, the particals are removed from the vector, we can use vector iterator and simplify the code to retain all particals that are not finished. [1] https://medium.com/codex/nature-of-rust-particles-40cec0a8c25e Signed-off-by: Marc Jones --- rust_particles/src/main.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/rust_particles/src/main.rs b/rust_particles/src/main.rs index e95516e..7aa39da 100644 --- a/rust_particles/src/main.rs +++ b/rust_particles/src/main.rs @@ -43,10 +43,6 @@ fn main() { WIDTH, )); } - for i in (0..(p.len() - 1)).rev() { - if p[i].clone().finished() { - p.remove(i); - } - } + p.retain(|x| !x.clone().finished()); } } From e478568ddaa55ee78a7404d3dc18e3aded7e0f8d Mon Sep 17 00:00:00 2001 From: Marc Jones Date: Thu, 15 Sep 2022 17:55:11 -0600 Subject: [PATCH 3/9] Refactor adding Particals to the vector Move adding particals to the vector into the top of the window.next animation loop. We only need to add particals in one place. Signed-off-by: Marc Jones --- rust_particles/src/main.rs | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/rust_particles/src/main.rs b/rust_particles/src/main.rs index 7aa39da..62527ac 100644 --- a/rust_particles/src/main.rs +++ b/rust_particles/src/main.rs @@ -12,28 +12,12 @@ fn main() { let mut counter = 0; let mut rng = rand::thread_rng(); - p.push(particle::Particle::new( - vec![200., 200.], - vec![rng.gen_range(-1.0..1.0), rng.gen_range(-1.0..1.0)], - vec![0.0, 0.1], - HEIGHT, - WIDTH, - )); - let mut window: PistonWindow = WindowSettings::new("Particle System", [WIDTH, HEIGHT]) .exit_on_esc(true) .build() .unwrap(); // The animation loop for the window while let Some(e) = window.next() { - window.draw_2d(&e, |c, g, _device| { - clear([0.0, 0.0, 0.0, 1.0], g); - for i in &mut p { - ellipse(i.colour, i.show(), c.transform, g); - i.update(); - } - }); - counter += 1; if counter % 10 == 0 { p.push(particle::Particle::new( vec![200., 200.], @@ -43,6 +27,14 @@ fn main() { WIDTH, )); } + window.draw_2d(&e, |c, g, _device| { + clear([0.0, 0.0, 0.0, 1.0], g); + for i in &mut p { + ellipse(i.colour, i.show(), c.transform, g); + i.update(); + } + }); + counter += 1; p.retain(|x| !x.clone().finished()); } } From 37908c3c612d32a5684e8ecb0d38b19389441dd3 Mon Sep 17 00:00:00 2001 From: Marc Jones Date: Thu, 15 Sep 2022 18:00:33 -0600 Subject: [PATCH 4/9] Use WIDTH and HEIGHT to set initial partical location Use the constants to set the initial partical location to the center of the width and height. Signed-off-by: Marc Jones --- rust_particles/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust_particles/src/main.rs b/rust_particles/src/main.rs index 62527ac..fddd379 100644 --- a/rust_particles/src/main.rs +++ b/rust_particles/src/main.rs @@ -20,7 +20,7 @@ fn main() { while let Some(e) = window.next() { if counter % 10 == 0 { p.push(particle::Particle::new( - vec![200., 200.], + vec![f64::from(WIDTH/2), f64::from(HEIGHT/2)], vec![rng.gen_range(-1.0..1.0), rng.gen_range(-1.0..1.0)], vec![0.0, 0.1], HEIGHT, From 910b2c25a2eff19cae2163c93e7f4a449ef4f021 Mon Sep 17 00:00:00 2001 From: Marc Jones Date: Tue, 20 Sep 2022 16:28:20 -0600 Subject: [PATCH 5/9] Initial port to speedy2d crate Port to the speedy2d crate. Signed-off-by: Marc Jones --- rust_particles/Cargo.toml | 7 ++--- rust_particles/src/main.rs | 57 ++++++++++++++++++++-------------- rust_particles/src/particle.rs | 38 +++++++++++++---------- 3 files changed, 59 insertions(+), 43 deletions(-) diff --git a/rust_particles/Cargo.toml b/rust_particles/Cargo.toml index e0baf74..19d34b8 100644 --- a/rust_particles/Cargo.toml +++ b/rust_particles/Cargo.toml @@ -1,11 +1,10 @@ [package] name = "rust_particles" -version = "0.1.0" -edition = "2018" +version = "0.2.0" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -piston = "0.53.0" -piston_window = "0.120.0" +speedy2d = "1.8.0" rand = "0.8.4" \ No newline at end of file diff --git a/rust_particles/src/main.rs b/rust_particles/src/main.rs index fddd379..cf32bc7 100644 --- a/rust_particles/src/main.rs +++ b/rust_particles/src/main.rs @@ -1,6 +1,7 @@ -extern crate piston_window; -use piston_window::*; use rand::Rng; +use speedy2d::color::Color; +use speedy2d::window::{WindowHandler, WindowHelper}; +use speedy2d::{Graphics2D, Window}; mod particle; @@ -8,33 +9,43 @@ pub const WIDTH: u32 = 400; pub const HEIGHT: u32 = 400; fn main() { - let mut p: Vec = Vec::new(); - let mut counter = 0; - let mut rng = rand::thread_rng(); + let window = Window::new_centered("Particle System", (WIDTH, HEIGHT)).unwrap(); + window.run_loop(MyWindowHandler { + p: Vec::new(), + counter: 0, + }); +} + +struct MyWindowHandler { + p: Vec, + counter: u32, +} - let mut window: PistonWindow = WindowSettings::new("Particle System", [WIDTH, HEIGHT]) - .exit_on_esc(true) - .build() - .unwrap(); - // The animation loop for the window - while let Some(e) = window.next() { - if counter % 10 == 0 { - p.push(particle::Particle::new( - vec![f64::from(WIDTH/2), f64::from(HEIGHT/2)], +impl WindowHandler for MyWindowHandler { + fn on_draw(&mut self, helper: &mut WindowHelper, graphics: &mut Graphics2D) { + let mut rng = rand::thread_rng(); + if self.counter % 10 == 0 { + self.p.push(particle::Particle::new( + vec![(WIDTH / 2) as f32, (HEIGHT / 2) as f32], vec![rng.gen_range(-1.0..1.0), rng.gen_range(-1.0..1.0)], vec![0.0, 0.1], HEIGHT, WIDTH, )); + self.counter=0; } - window.draw_2d(&e, |c, g, _device| { - clear([0.0, 0.0, 0.0, 1.0], g); - for i in &mut p { - ellipse(i.colour, i.show(), c.transform, g); - i.update(); - } - }); - counter += 1; - p.retain(|x| !x.clone().finished()); + graphics.clear_screen(Color::from_rgb(0., 0., 0.)); + for i in &mut self.p { + graphics.draw_circle( + i.particle_pos(), + i.size, + Color::from_rgba(i.colour[0], i.colour[1], i.colour[2], i.colour[3]), + ); + i.update(); + } + self.counter += 1; + self.p.retain(|x| !x.clone().finished()); + + helper.request_redraw(); } } diff --git a/rust_particles/src/particle.rs b/rust_particles/src/particle.rs index c7e0dad..d696950 100644 --- a/rust_particles/src/particle.rs +++ b/rust_particles/src/particle.rs @@ -1,30 +1,31 @@ +use speedy2d::dimen::Vec2; #[derive(Clone)] pub struct Particle { - pub pos: Vec, - pub vel: Vec, - pub acc: Vec, - size: f64, + pub pos: Vec, + pub vel: Vec, + pub acc: Vec, + pub size: f32, pub lifetime: f32, pub colour: [f32; 4], - max_speed: f64, - max_acc: f64, - height: f64, - width: f64, + max_speed: f32, + max_acc: f32, + height: u32, + width: u32, } impl Particle { - pub fn new(pos: Vec, vel: Vec, acc: Vec, height: u32, width: u32) -> Particle { + pub fn new(pos: Vec, vel: Vec, acc: Vec, height: u32, width: u32) -> Particle { Particle { pos, vel, acc, lifetime: 1.0, - size: 10., + size: 5., colour: [1.0, 0.0, 0.0, 1.0], max_speed: 10.0, max_acc: 0.5, - height: height as f64, - width: width as f64, + height, + width, } } @@ -39,8 +40,13 @@ impl Particle { self.colour = [1.0, 0.0, 0.0, self.lifetime]; } - pub fn show(&self) -> [f64; 4] { - [self.pos[0], self.pos[1], self.size, self.size] + pub fn particle_pos(&self) -> Vec2 { + Vec2::new(self.pos[0], self.pos[1]) + } + +// This doesn't work yet + pub fn particle_rgba(&self) -> (f32, f32, f32, f32) { + (self.colour[0], self.colour[1], self.colour[2], self.colour[3]) } fn check_limits(&mut self) { @@ -59,10 +65,10 @@ impl Particle { } fn edges(&mut self) { - if self.pos[1] >= self.height || self.pos[1] <= 0.0 { + if self.pos[1] >= self.height as f32 || self.pos[1] <= 0.0 { self.vel[1] *= -1.0; } - if self.pos[0] >= self.width || self.pos[0] <= 0.0 { + if self.pos[0] >= self.width as f32 || self.pos[0] <= 0.0 { self.vel[0] *= -1.0; } } From 28c8253e123ba171da12decf13e78bfaa4e33bf3 Mon Sep 17 00:00:00 2001 From: Marc Jones Date: Wed, 21 Sep 2022 11:49:03 -0600 Subject: [PATCH 6/9] Borrow self in Partical finalize check We don't need to clone if we borrow self in the finalize function. Signed-off-by: Marc Jones --- rust_particles/src/main.rs | 2 +- rust_particles/src/particle.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/rust_particles/src/main.rs b/rust_particles/src/main.rs index cf32bc7..c74a22f 100644 --- a/rust_particles/src/main.rs +++ b/rust_particles/src/main.rs @@ -44,7 +44,7 @@ impl WindowHandler for MyWindowHandler { i.update(); } self.counter += 1; - self.p.retain(|x| !x.clone().finished()); + self.p.retain(|x| !x.finished()); helper.request_redraw(); } diff --git a/rust_particles/src/particle.rs b/rust_particles/src/particle.rs index d696950..7c3625f 100644 --- a/rust_particles/src/particle.rs +++ b/rust_particles/src/particle.rs @@ -1,5 +1,4 @@ use speedy2d::dimen::Vec2; -#[derive(Clone)] pub struct Particle { pub pos: Vec, pub vel: Vec, @@ -73,7 +72,7 @@ impl Particle { } } - pub fn finished(self) -> bool { + pub fn finished(&self) -> bool { self.lifetime <= 0. } } From 3ab50f7b89056a645e6bfa977f410f8086f30344 Mon Sep 17 00:00:00 2001 From: Marc Jones Date: Fri, 23 Sep 2022 12:46:42 -0600 Subject: [PATCH 7/9] Add Keyboard and Mouse Features Add Keyboard r,g,b feature to change the colors of the particles. Keys other than r,g,b turn the particles black. Add Mouse click to move the particles starting point. Signed-off-by: Marc Jones --- rust_particles/src/main.rs | 70 ++++++++++++++++++++++++++++------ rust_particles/src/particle.rs | 13 +++---- 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/rust_particles/src/main.rs b/rust_particles/src/main.rs index c74a22f..735758c 100644 --- a/rust_particles/src/main.rs +++ b/rust_particles/src/main.rs @@ -1,6 +1,7 @@ use rand::Rng; use speedy2d::color::Color; -use speedy2d::window::{WindowHandler, WindowHelper}; +use speedy2d::dimen::Vec2; +use speedy2d::window::{MouseButton, WindowHandler, WindowHelper, WindowStartupInfo}; use speedy2d::{Graphics2D, Window}; mod particle; @@ -10,42 +11,89 @@ pub const HEIGHT: u32 = 400; fn main() { let window = Window::new_centered("Particle System", (WIDTH, HEIGHT)).unwrap(); + window.run_loop(MyWindowHandler { p: Vec::new(), counter: 0, + color: [1., 0., 0., 1.], + mouse_pos: vec![0., 0.], + start_pos: vec![(HEIGHT / 2) as f32, (WIDTH / 2) as f32], }); } struct MyWindowHandler { p: Vec, counter: u32, + color: [f32; 4], + mouse_pos: Vec, + start_pos: Vec, } impl WindowHandler for MyWindowHandler { + fn on_start(&mut self, helper: &mut WindowHelper, _info: WindowStartupInfo) { + helper.set_resizable(false); // We don't handle resize yet. + } + fn on_draw(&mut self, helper: &mut WindowHelper, graphics: &mut Graphics2D) { let mut rng = rand::thread_rng(); if self.counter % 10 == 0 { self.p.push(particle::Particle::new( - vec![(WIDTH / 2) as f32, (HEIGHT / 2) as f32], + self.start_pos.clone(), vec![rng.gen_range(-1.0..1.0), rng.gen_range(-1.0..1.0)], vec![0.0, 0.1], HEIGHT, WIDTH, + self.color, )); - self.counter=0; + self.counter = 0; } - graphics.clear_screen(Color::from_rgb(0., 0., 0.)); - for i in &mut self.p { - graphics.draw_circle( - i.particle_pos(), - i.size, - Color::from_rgba(i.colour[0], i.colour[1], i.colour[2], i.colour[3]), - ); - i.update(); + graphics.clear_screen(Color::BLACK); + for part in &mut self.p { + graphics.draw_circle(part.particle_pos(), part.size, part.particle_color()); + part.update(); } self.counter += 1; self.p.retain(|x| !x.finished()); helper.request_redraw(); } + + fn on_mouse_move(&mut self, _helper: &mut WindowHelper, position: Vec2) { + self.mouse_pos[0] = position.x; + self.mouse_pos[1] = position.y; + } + + fn on_mouse_button_down( + &mut self, + helper: &mut WindowHelper<()>, + button: speedy2d::window::MouseButton, + ) { + if button == MouseButton::Left { + self.start_pos = self.mouse_pos.clone(); + } + helper.request_redraw(); + } + + fn on_keyboard_char(&mut self, helper: &mut WindowHelper<()>, unicode_codepoint: char) { + let new_color: [f32; 4]; + + if unicode_codepoint == 'r' { + new_color = [1., 0., 0., 1.] + } else if unicode_codepoint == 'g' { + new_color = [0., 1., 0., 1.] + } else if unicode_codepoint == 'b' { + new_color = [0., 0., 1., 1.] + } else { + new_color = [0., 0., 0., 0.] + } + + self.color = new_color; + for i in &mut self.p { + i.colour[0] = new_color[0]; // fix this to be RUSTY + i.colour[1] = new_color[1]; + i.colour[2] = new_color[2]; + } + + helper.request_redraw(); + } } diff --git a/rust_particles/src/particle.rs b/rust_particles/src/particle.rs index 7c3625f..1b81466 100644 --- a/rust_particles/src/particle.rs +++ b/rust_particles/src/particle.rs @@ -1,4 +1,4 @@ -use speedy2d::dimen::Vec2; +use speedy2d::{dimen::Vec2, color::Color}; pub struct Particle { pub pos: Vec, pub vel: Vec, @@ -13,14 +13,14 @@ pub struct Particle { } impl Particle { - pub fn new(pos: Vec, vel: Vec, acc: Vec, height: u32, width: u32) -> Particle { + pub fn new(pos: Vec, vel: Vec, acc: Vec, height: u32, width: u32, colour: [f32; 4]) -> Particle { Particle { pos, vel, acc, lifetime: 1.0, size: 5., - colour: [1.0, 0.0, 0.0, 1.0], + colour, max_speed: 10.0, max_acc: 0.5, height, @@ -36,16 +36,15 @@ impl Particle { self.check_limits(); self.edges(); self.lifetime -= 0.01; - self.colour = [1.0, 0.0, 0.0, self.lifetime]; + self.colour[3] = self.lifetime; } pub fn particle_pos(&self) -> Vec2 { Vec2::new(self.pos[0], self.pos[1]) } -// This doesn't work yet - pub fn particle_rgba(&self) -> (f32, f32, f32, f32) { - (self.colour[0], self.colour[1], self.colour[2], self.colour[3]) + pub fn particle_color(&self) -> Color { + Color::from_rgba(self.colour[0], self.colour[1], self.colour[2], self.colour[3]) } fn check_limits(&mut self) { From b25991946b89ac3636f4457c0402a3d0e85bf1f6 Mon Sep 17 00:00:00 2001 From: Marc Jones Date: Tue, 27 Sep 2022 19:03:37 -0600 Subject: [PATCH 8/9] Add .gitignore Signed-off-by: Marc Jones --- rust_particles/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 rust_particles/.gitignore diff --git a/rust_particles/.gitignore b/rust_particles/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/rust_particles/.gitignore @@ -0,0 +1 @@ +/target From 3fdd33df72ad6e0d06412a120c1b1cbedfd4e14b Mon Sep 17 00:00:00 2001 From: Marc Jones Date: Tue, 27 Sep 2022 19:06:13 -0600 Subject: [PATCH 9/9] Fix start_pos dimensions The start_pos dimensions were switched. Set x to WIDTH and y to HEIGHT. Signed-off-by: Marc Jones --- rust_particles/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust_particles/src/main.rs b/rust_particles/src/main.rs index 735758c..b3d227d 100644 --- a/rust_particles/src/main.rs +++ b/rust_particles/src/main.rs @@ -17,7 +17,7 @@ fn main() { counter: 0, color: [1., 0., 0., 1.], mouse_pos: vec![0., 0.], - start_pos: vec![(HEIGHT / 2) as f32, (WIDTH / 2) as f32], + start_pos: vec![(WIDTH / 2) as f32, (HEIGHT / 2) as f32], }); }