// #![feature(alloc_system, global_allocator, allocator_api)] // extern crate alloc_system; // use alloc_system::System; // #[global_allocator] // static A: System = System; extern crate graphics; extern crate piston_window; extern crate rand; use graphics::math::{ Vec2d, add, mul_scalar }; use piston_window::{ PistonWindow, WindowSettings, clear, rectangle }; use rand::distributions::{IndependentSample, Range}; type RGBA = [f32; 4]; const WHITE: RGBA = [1.0; 4]; const GRAY: RGBA = [0.7, 0.7, 0.7, 0.3]; const N_PARTICLES: usize = 500; struct World { current_turn: usize, shapes: Vec>, height: u32, width: u32, } struct Shape { height: f64, width: f64, position: Vec2d, velocity: Vec2d, acceleration: Vec2d, color: RGBA, } impl Shape { fn new(x: f64, y: f64) -> Self { let mut rng = rand::thread_rng(); let legal_range = Range::new(-5_f64, 5_f64); let x_speed = legal_range.ind_sample(&mut rng); let y_speed = legal_range.ind_sample(&mut rng); let x_accel = 0.1 * legal_range.ind_sample(&mut rng); let y_accel = 0.1 * legal_range.ind_sample(&mut rng); Shape { height: 10.0, width: 10.0, position: [x, y], velocity: [x_speed, y_speed], acceleration: [x_accel, y_accel], color: GRAY, } } fn update(&mut self) { self.velocity = add(self.velocity, self.acceleration); // <> There is no matrix/vector math operators within the language. `graphics::math` is providing this functionality for us. self.position = add(self.position, self.velocity); self.acceleration = mul_scalar(self.acceleration, 0.7); // <> Slow down the shape's movement self.color[3] *= 0.97; } } impl World { fn new(width: u32, height: u32) -> World { World { current_turn: 0, shapes: Vec::>::new(), height: height, width: width, } } fn add_shapes(&mut self, n: usize) { let x = (self.width / 2) as f64; let y = (self.height / 2) as f64; for _ in 0..n { self.shapes.push(Box::new(Shape::new(x, y))); }; } fn remove_shapes(&mut self, n: usize) { let n_shapes = self.shapes.len(); let to_remove = if n > n_shapes { n_shapes } else { n }; // let to_remove = cmp::min(n as usize, self.shapes.len()); for _ in 0..to_remove { self.shapes.remove(0); // Remove the oldest particle. This is quite an inefficient operation, as all remaining particles are shifted to fill the now-empty slot. A smarter strategy would be to use `std::collections::VecDeque`, which supports removing from the front. } self.shapes.shrink_to_fit(); // Will help to force a re-allocation later when shapes are added. } fn calc_population_change(&self) -> isize { const N: f64 = N_PARTICLES as f64; // <> Shorter alias const MAX: f64 = N*0.5; const MIN: f64 = -N*0.5; let x: f64 = self.current_turn as f64; //let n: f64 = N_PARTICLES; let n = 0.4*N*(0.1*x).sin() + 0.1*N*x.sin(); n.max(MIN).min(MAX).round() as isize // limit range of growth/death then convert to `isize` } fn update(&mut self) { let n = self.calc_population_change(); //let n = as usize; // <> Convert f64 to usize if n > 0 { self.add_shapes(n as usize); } else { self.remove_shapes(n.abs() as usize); } self.current_turn += 1; } } // impl Iterator for World { // type Item = isize; // fn next(&mut self) -> Option { // let x = self.current_turn; // // 30sin(x) + 10sin(0.1x) + 20sin(0.2x) + 30sin(0.3x) + 40sin(0.4x) + 50sin(0.5x) // let y = 50.0*(0.1*x).sin() + 30.0*x.sin(); // self.current_turn += 1.0; // Some(y.round() as isize) // } // } fn main() { let (width, height) = (640, 480); let mut window: PistonWindow = WindowSettings::new("particles", [width, height]) .exit_on_esc(true) .build() .expect("Could not create a window."); // Initialize let mut world = World::new(width, height); world.add_shapes(N_PARTICLES); while let Some(event) = window.next() { // main loop // Update Step for shape in &mut world.shapes { shape.update(); } world.update(); // Render Step window.draw_2d(&event, |ctx, renderer| { clear(WHITE, renderer); for s in &mut world.shapes { let rect = [s.position[0], s.position[1], s.width, s.height]; let transformation_matrix = ctx.transform; rectangle(s.color, rect, transformation_matrix, renderer); // create a graphics::Rectangle and call draw() on it } }); } }