From a8cf2c052a457e743c2e9fc95ba3d4463f8511fa Mon Sep 17 00:00:00 2001 From: jacob janzen Date: Sun, 28 Aug 2022 12:55:57 -0500 Subject: refactor --- src/lib.rs | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 86 insertions(+), 16 deletions(-) (limited to 'src/lib.rs') diff --git a/src/lib.rs b/src/lib.rs index 0601ddf..8d784ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,9 @@ with this program. If not, see https://www.gnu.org/licenses/. */ use rand::Rng; +use std::fs::File; +use std::sync::mpsc; +use std::thread; use std::{f64::consts::PI, vec}; use clap::Parser; @@ -74,12 +77,15 @@ pub struct Args { } impl Args { + /// Read the command line arguments pub fn read() -> Self { Args::parse() } } impl Image { + /// Create the basic info for the image from + /// the command line arguments pub fn create_from_args(args: &Args) -> Self { Image { height: args.height, @@ -101,13 +107,11 @@ impl Image { } } - pub fn fill_canvas(&mut self, frame: u16) { - self.generate_noise(frame); - } - + /// Generate the noise for a given frame fn generate_noise(&mut self, frame: u16) { let mut max_dist = 0.0; + // set the point on each ellipse to use as the centre of each cell let pos = 2.0 * PI * frame as f64 / self.frames as f64; for ellipse in &mut self.ellipses { let sin_theta = ((ellipse.angle + pos) * ellipse.direction).sin(); @@ -149,7 +153,6 @@ impl Image { for y in 0..self.height { for x in 0..self.width { let index = y as usize * self.width as usize + x as usize; - //let val = 0xFF - (0xFF as f64 * self.point_data[index].min_dist) as u8; let red; let green; let blue; @@ -176,6 +179,7 @@ impl Image { } } + /// Set the colour at a given pixel fn set_pixel(&mut self, r: u8, g: u8, b: u8, p: Point) { self.pixels[3 * (self.width as usize * p.y as usize + p.x as usize)] = r; self.pixels[3 * (self.width as usize * p.y as usize + p.x as usize) + 1] = g; @@ -184,12 +188,15 @@ impl Image { } impl PointData { + /// get closest point and distance to said point + /// from a given pixel on the frame fn get_point_data(image: &Image, p: Point) -> Self { let mut pd = PointData { min_dist: image.cross_distance, closest_point: Point { x: 0, y: 0 }, }; + // find closest point for ellipse in &image.ellipses { let d = p.distance(&ellipse.curr_point); if d < pd.min_dist { @@ -203,6 +210,7 @@ impl PointData { } impl Point { + /// calculates distance between two points fn distance(&self, other: &Point) -> f64 { let x_dist: f64 = other.x as f64 - self.x as f64; let y_dist: f64 = other.y as f64 - self.y as f64; @@ -211,7 +219,36 @@ impl Point { } } +/// Creates a random ellipse on the frame +fn create_random_ellipse(width: u16, height: u16) -> Ellipse { + Ellipse { + // random point on the frame is the centre point + centre: Point { + x: rand::thread_rng().gen_range(0..width) as i64, + y: rand::thread_rng().gen_range(0..height) as i64, + }, + // height and width of ellipse are capped at 1/5 of the + // respective dimension of the frame + height: rand::thread_rng().gen_range(1.0..height as f64 / 5.0), + width: rand::thread_rng().gen_range(1.0..width as f64 / 5.0), + // angle determines rotation of the ellipse + angle: rand::thread_rng().gen_range(0.0..PI), + // curr point is just set to 0 for now + curr_point: Point { x: 0, y: 0 }, + // determines clockwise or counter-clockwise direction + // of points on ellipse + direction: if rand::thread_rng().gen_range(0..=1) == 1 { + 1.0 + } else { + -1.0 + }, + } +} + +/// Generates a vector of `num_cells` `Ellipse`s that are used to +/// determine the points where cells should be calculated from fn generate_points(width: u16, height: u16, num_cells: usize) -> Vec { + // create vector of 0'd ellipses let mut ellipses = vec![ Ellipse { centre: Point { x: 0, y: 0 }, @@ -224,19 +261,52 @@ fn generate_points(width: u16, height: u16, num_cells: usize) -> Vec { num_cells ]; + // assign the ellipses values for ellipse in &mut ellipses { - ellipse.centre.x = rand::thread_rng().gen_range(0..width) as i64; - ellipse.centre.y = rand::thread_rng().gen_range(0..height) as i64; - ellipse.height = rand::thread_rng().gen_range(1.0..height as f64 / 5.0); - ellipse.width = rand::thread_rng().gen_range(1.0..width as f64 / 5.0); - ellipse.angle = rand::thread_rng().gen_range(0.0..2.0 * PI); - let direction = rand::thread_rng().gen_range(0..=1); - if direction == 1 { - ellipse.direction = 1.0; - } else { - ellipse.direction = -1.0; - } + *ellipse = create_random_ellipse(width, height); } ellipses } + +/// Show a progress bar and percent complete. +/// Should be run in a separate thread from the main +/// to prevent it from blocking the main thread +fn progress_bar(width: u16, max: u16, rx: mpsc::Receiver) { + for received in rx { + let percent_done = received as f64 / max as f64; + + for i in 0..(width as i32 - 6) { + if i < (percent_done * width as f64) as i32 { + print!("="); + } else { + print!("-"); + } + } + print!("[{}%]\r", (percent_done * 100.0) as i32); + } +} + +/// Generate the image itself +pub fn create_image(image: &mut Image, encoder: &mut gif::Encoder<&mut File>) { + let (tx, rx) = mpsc::channel(); // message passing variables + let width = termsize::get().unwrap().cols; // the width of the terminal + + // start progress bar thread + let frames = image.frames; + let t = thread::spawn(move || progress_bar(width, frames, rx)); + + for i in 0..image.frames { + tx.send(i).unwrap(); + // Create pixel array + image.generate_noise(i); + let frame = gif::Frame::from_rgb(image.width, image.height, &mut image.pixels); + + // Write frame to file + encoder.write_frame(&frame).unwrap(); + } + + // close the progress bar thread + drop(tx); + t.join().unwrap(); +} -- cgit v1.2.3