summaryrefslogtreecommitdiff
path: root/meap/meap-code/ch6/ch6-particles/src
diff options
context:
space:
mode:
authorAdam Carpenter <53hornet@gmail.com>2019-03-27 15:32:37 -0400
committerAdam Carpenter <53hornet@gmail.com>2019-03-27 15:32:37 -0400
commit67cdcc2e12118becb823e20a40cc2687f2b8425a (patch)
treeed92c3234b89079e6d4cf36f5e80c5ffa79def48 /meap/meap-code/ch6/ch6-particles/src
parente25482fca375d318a39c3b54db396b0db6e0b263 (diff)
downloadlearning-rust-67cdcc2e12118becb823e20a40cc2687f2b8425a.tar.xz
learning-rust-67cdcc2e12118becb823e20a40cc2687f2b8425a.zip
Started Rust in Action MEAP.
Diffstat (limited to 'meap/meap-code/ch6/ch6-particles/src')
-rw-r--r--meap/meap-code/ch6/ch6-particles/src/main.rs170
1 files changed, 170 insertions, 0 deletions
diff --git a/meap/meap-code/ch6/ch6-particles/src/main.rs b/meap/meap-code/ch6/ch6-particles/src/main.rs
new file mode 100644
index 0000000..93696fb
--- /dev/null
+++ b/meap/meap-code/ch6/ch6-particles/src/main.rs
@@ -0,0 +1,170 @@
+// #![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<Box<Shape>>,
+ height: u32,
+ width: u32,
+}
+
+struct Shape {
+ height: f64,
+ width: f64,
+ position: Vec2d<f64>,
+ velocity: Vec2d<f64>,
+ acceleration: Vec2d<f64>,
+ 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::<Box<Shape>>::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<isize> {
+// 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
+ }
+ });
+ }
+}