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 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 46a9d3d..b3d227d 100644 --- a/rust_particles/src/main.rs +++ b/rust_particles/src/main.rs @@ -1,6 +1,8 @@ -extern crate piston_window; -use piston_window::*; use rand::Rng; +use speedy2d::color::Color; +use speedy2d::dimen::Vec2; +use speedy2d::window::{MouseButton, WindowHandler, WindowHelper, WindowStartupInfo}; +use speedy2d::{Graphics2D, Window}; mod particle; @@ -8,45 +10,90 @@ 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(); - - 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 0..p.len() { - ellipse(color::alpha(p[i].lifetime), p[i].show(), c.transform, g); - p[i].update(); - } - }); - counter = counter + 1; - if counter % 10 == 0 { - p.push(particle::Particle::new( - vec![200., 200.], + 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![(WIDTH / 2) as f32, (HEIGHT / 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( + 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; + } + 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.] } - for i in (0..(p.len() - 1)).rev() { - if p[i].clone().finished() { - p.remove(i); - } + + 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 4c870d9..1b81466 100644 --- a/rust_particles/src/particle.rs +++ b/rust_particles/src/particle.rs @@ -1,46 +1,50 @@ -#[derive(Clone)] +use speedy2d::{dimen::Vec2, color::Color}; 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 { - return Particle { - pos: pos, - vel: vel, - acc: acc, + 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: 10., - colour: [1.0, 0.0, 0.0, 1.0], + size: 5., + colour, max_speed: 10.0, max_acc: 0.5, - height: height as f64, - width: width as f64, - }; + height, + width, + } } 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.colour = [1.0, 0.0, 0.0, self.lifetime]; + self.lifetime -= 0.01; + self.colour[3] = self.lifetime; + } + + pub fn particle_pos(&self) -> Vec2 { + Vec2::new(self.pos[0], self.pos[1]) } - pub fn show(&self) -> [f64; 4] { - return [self.pos[0], self.pos[1], self.size, self.size]; + 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) { @@ -59,15 +63,15 @@ 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; + 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 { - self.vel[0] = self.vel[0] * -1.0; + if self.pos[0] >= self.width as f32 || self.pos[0] <= 0.0 { + self.vel[0] *= -1.0; } } - pub fn finished(self) -> bool { - return self.lifetime <= 0.; + pub fn finished(&self) -> bool { + self.lifetime <= 0. } }