From 278ccafb116533da1b0e272456fc8461184db927 Mon Sep 17 00:00:00 2001 From: Simon Gardling Date: Thu, 27 Mar 2025 13:25:45 -0400 Subject: [PATCH] ffmpeg stuff --- .gitignore | 5 +-- Cargo.lock | 4 +- src/imgdata.rs | 2 +- src/lib.rs | 2 +- src/main.rs | 75 ++++++++++++++++++++----------- src/model.rs | 119 +++++++++++++++---------------------------------- 6 files changed, 91 insertions(+), 116 deletions(-) diff --git a/.gitignore b/.gitignore index 99c4228..4e48666 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ /target -/tmp -/test.mp4 +/output.mp4 /.vscode # CLion -**/.idea \ No newline at end of file +**/.idea diff --git a/Cargo.lock b/Cargo.lock index fa942eb..a59099b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -297,9 +297,9 @@ checksum = "17b02fc0ff9a9e4b35b3342880f48e896ebf69f2967921fe8646bf5b7125956a" [[package]] name = "once_cell" -version = "1.21.1" +version = "1.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" +checksum = "c2806eaa3524762875e21c3dcd057bc4b7bfa01ce4da8d46be1cd43649e1cc6b" [[package]] name = "physarum" diff --git a/src/imgdata.rs b/src/imgdata.rs index 1259cf0..87f26b6 100644 --- a/src/imgdata.rs +++ b/src/imgdata.rs @@ -24,7 +24,7 @@ impl ThinGridData { pub fn new_from_grid_vec(in_grids: &[Grid]) -> Vec { in_grids .iter() - .map(|grid| Self::new_from_grid(grid)) + .map(Self::new_from_grid) .collect() } diff --git a/src/lib.rs b/src/lib.rs index 7093f2c..d5b6a41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ mod agent; mod blur; mod buffer; mod grid; -mod imgdata; // for storing image data +pub mod imgdata; // for storing image data pub mod model; mod palette; mod util; // for math things diff --git a/src/main.rs b/src/main.rs index 341cdc5..c9b27a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,38 +1,61 @@ -use physarum::model; +use physarum::{ + imgdata::{ImgData, ThinGridData}, + model, +}; +use std::io::Write; fn main() { - // # of iterations to go through let n_iterations = 1024; - // let n_iterations = 2048; - // let n_iterations = 1 << 14; - - // Size of grid and pictures - // let (width, height) = (256, 256); - // let (width, height) = (512, 512); let (width, height) = (1024, 1024); - - // # of agents - // let n_particles = 1 << 10; - // let n_particles = 1 << 16; - // let n_particles = 1 << 20; - let n_particles = 1 << 24; - println!("n_particles: {}", n_particles); - + let n_particles = 1 << 22; let diffusivity = 1; - - // `n_populations` is the # of types of agents - // let n_populations = 4; let n_populations = 1; - // let n_populations = 1 + rng.gen_range(1..4); // make # of populations between 2 and 5 - let mut model = model::Model::new(width, height, n_particles, n_populations, diffusivity); // Create the model + let mut model = model::Model::new(width, height, n_particles, n_populations, diffusivity); + model.print_configurations(); - model.print_configurations(); // Print config for model + // Setup ffmpeg + let mut ffmpeg = std::process::Command::new("ffmpeg") + .args(&[ + "-y", + "-f", + "rawvideo", + "-pix_fmt", + "rgb24", + "-s", + &format!("{}x{}", width, height), + "-r", + "30", + "-i", + "-", + "-c:v", + "libx264", + "-preset", + "fast", + "-crf", + "23", + "output.mp4", + ]) + .stdin(std::process::Stdio::piped()) + .spawn() + .expect("Failed to start ffmpeg"); + let mut stdin = ffmpeg.stdin.take().unwrap(); - model.run(n_iterations); // Actually run the model + for _ in 0..n_iterations { + model.step(); - // export saved image data - println!("Rendering all saved image data...."); - model.render_all_imgdata(); + // Generate image + let grids = ThinGridData::new_from_grid_vec(model.population_grids()); + let img_data = ImgData::new(grids, model.palette()); + let img = img_data.to_image(); + let raw_data = img.into_raw(); + + // Write to ffmpeg + stdin.write_all(&raw_data).unwrap(); + } + + // Cleanup + drop(stdin); + ffmpeg.wait().unwrap(); println!("Done!"); } diff --git a/src/model.rs b/src/model.rs index d09a800..670b154 100644 --- a/src/model.rs +++ b/src/model.rs @@ -1,15 +1,14 @@ use crate::{ agent::Agent, grid::{combine, Grid}, - imgdata::{ImgData, ThinGridData}, palette::{random_palette, Palette}, }; -use indicatif::{ParallelProgressIterator, ProgressBar, ProgressStyle}; +use indicatif::{ProgressBar, ProgressStyle}; // use rand::Rng; use rand_distr::{Distribution, Normal}; use rayon::{iter::ParallelIterator, prelude::*}; -use std::{path::Path, time::Instant}; +use std::time::Instant; // Top-level simulation class. pub struct Model { @@ -28,8 +27,8 @@ pub struct Model { // Color palette palette: Palette, - // List of ImgData to be processed post-simulation into images - img_data_vec: Vec<(usize, ImgData)>, + time_per_agent_list: Vec, + time_per_step_list: Vec, } impl Model { @@ -89,102 +88,56 @@ impl Model { diffusivity, iteration: 0, palette: random_palette(), - img_data_vec: Vec::new(), + time_per_agent_list: Vec::new(), + time_per_step_list: Vec::new(), } } - // Simulates `steps` # of steps - #[inline] + pub fn step(&mut self) { + combine(&mut self.population_grids, &self.attraction_table); + + let agents_tick_time = Instant::now(); + self.population_grids.par_iter_mut().for_each(|grid| { + grid.tick(); + grid.diffuse(self.diffusivity); + }); + let agents_tick_elapsed = agents_tick_time.elapsed().as_millis() as f64; + let agents_num: usize = self.population_grids.iter().map(|g| g.agents.len()).sum(); + let ms_per_agent = agents_tick_elapsed / agents_num as f64; + + self.time_per_agent_list.push(ms_per_agent); + self.time_per_step_list.push(agents_tick_elapsed); + self.iteration += 1; + } + pub fn run(&mut self, steps: usize) { let pb = ProgressBar::new(steps as u64); - pb.set_style( - ProgressStyle::default_bar() - .template( - "{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta} {percent}%, {per_sec})", - ) - .progress_chars("#>-"), - ); + pb.set_style(ProgressStyle::default_bar() + .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta} {percent}%, {per_sec})") + .progress_chars("#>-")); - let mut time_per_agent_list: Vec = Vec::new(); - let mut time_per_step_list: Vec = Vec::new(); - - let agents_num: usize = self - .population_grids - .iter() - .map(|grid| grid.agents.len()) - .sum(); - (0..steps).for_each(|_| { - // Combine grids - combine(&mut self.population_grids, &self.attraction_table); - let agents_tick_time = Instant::now(); - - // Tick agents - self.population_grids.par_iter_mut().for_each(|grid| { - grid.tick(); - grid.diffuse(self.diffusivity); // Diffuse + Decay - }); - - self.save_image_data(); - - let agents_tick_elapsed: f64 = agents_tick_time.elapsed().as_millis() as f64; - let ms_per_agent: f64 = agents_tick_elapsed / (agents_num as f64); - time_per_agent_list.push(ms_per_agent); - time_per_step_list.push(agents_tick_elapsed); - - self.iteration += 1; + for _ in 0..steps { + self.step(); pb.inc(1); - }); + } pb.finish(); let avg_per_step: f64 = - time_per_step_list.iter().sum::() / time_per_step_list.len() as f64; + self.time_per_step_list.iter().sum::() / self.time_per_step_list.len() as f64; let avg_per_agent: f64 = - time_per_agent_list.iter().sum::() / time_per_agent_list.len() as f64; - + self.time_per_agent_list.iter().sum::() / self.time_per_agent_list.len() as f64; println!( "Average time per step: {}ms\nAverage time per agent: {}ms", avg_per_step, avg_per_agent ); } - fn save_image_data(&mut self) { - let grids = ThinGridData::new_from_grid_vec(&self.population_grids); - let img_data = ImgData::new(grids, self.palette); - self.img_data_vec.push((self.iteration + 1, img_data)); - let size: usize = std::mem::size_of_val(&self.img_data_vec); - let mb = size / 1024 / 1024; - // println!("{} B | {} KB | {} MB", size, size/1024, size/1024/1024); - - let max_mb = 6000; - if mb >= max_mb { - println!( - "ram usage is over {} MB (and len of {}), flushing to disk\n", - max_mb, - self.img_data_vec.len() - ); - self.render_all_imgdata(); - } + // Accessors for rendering + pub fn population_grids(&self) -> &[Grid] { + &self.population_grids } - pub fn render_all_imgdata(&mut self) { - if !Path::new("./tmp").exists() { - std::fs::create_dir("./tmp").expect("could create directory"); - } - - let pb = ProgressBar::new(self.img_data_vec.len() as u64); - pb.set_style(ProgressStyle::default_bar().template( - "{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] ({pos}/{len}, {percent}%, {per_sec})", - )); - - self.img_data_vec - .drain(..) - .collect::>() - .par_iter() - .progress_with(pb) - .for_each(|(i, img)| { - img.to_image() - .save(format!("./tmp/out_{}.png", i).as_str()) - .unwrap(); - }); + pub fn palette(&self) -> Palette { + self.palette } }