a TON of stuff and changes
This commit is contained in:
parent
e694c48d0a
commit
50b3f35059
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,6 @@
|
||||
/target
|
||||
/tmp
|
||||
/test.mp4
|
||||
|
||||
# CLion
|
||||
**/.idea
|
||||
13
Cargo.lock
generated
13
Cargo.lock
generated
@ -320,9 +320,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.23.13"
|
||||
version = "0.23.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "293f07a1875fa7e9c5897b51aa68b2d8ed8271b87e1a44cb64b9c3d98aabbc0d"
|
||||
checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
@ -346,6 +346,7 @@ dependencies = [
|
||||
"console",
|
||||
"lazy_static",
|
||||
"number_prefix",
|
||||
"rayon",
|
||||
"regex",
|
||||
]
|
||||
|
||||
@ -399,9 +400,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.86"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
|
||||
checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@ -769,9 +770,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.123"
|
||||
version = "1.0.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
|
||||
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
|
||||
|
||||
[[package]]
|
||||
name = "serde_cbor"
|
||||
|
||||
20
Cargo.toml
20
Cargo.toml
@ -5,11 +5,11 @@ authors = ["mindv0rtex <mindv0rtex@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4"
|
||||
image = "0.23"
|
||||
indicatif = "0.15"
|
||||
chrono = "0.4.19"
|
||||
image = "0.23.14"
|
||||
indicatif = {version = "0.15.0", features = ["rayon"]}
|
||||
itertools = "0.10"
|
||||
rand = "0.8"
|
||||
rand = "0.8.3"
|
||||
rand_distr = "0.4"
|
||||
rayon = "1.5"
|
||||
|
||||
@ -19,3 +19,15 @@ criterion = "0.3"
|
||||
[[bench]]
|
||||
name = "trig"
|
||||
harness = false
|
||||
|
||||
[profile.dev]
|
||||
codegen-units = 1
|
||||
opt-level = 3
|
||||
target-cpu = "native"
|
||||
lto = "thin"
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
opt-level = 3
|
||||
target-cpu = "native"
|
||||
lto = "fat"
|
||||
3
TODO.md
Normal file
3
TODO.md
Normal file
@ -0,0 +1,3 @@
|
||||
### Todo:
|
||||
- Auto create a mp4 from generate images
|
||||
- Instead of using the command `ffmpeg -r 20 -i tmp/out_%d.png -vcodec libx264 -crf 25 test.mp4` maybe use a rust library to do the same (more research needed)
|
||||
@ -6,6 +6,14 @@ pub struct Blur {
|
||||
row_buffer: Vec<f32>,
|
||||
}
|
||||
|
||||
impl Clone for Blur {
|
||||
fn clone(&self) -> Blur {
|
||||
return Blur {
|
||||
row_buffer: self.row_buffer.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Blur {
|
||||
pub fn new(width: usize) -> Self {
|
||||
Blur {
|
||||
|
||||
27
src/grid.rs
27
src/grid.rs
@ -16,6 +16,19 @@ pub struct PopulationConfig {
|
||||
deposition_amount: f32,
|
||||
}
|
||||
|
||||
impl Clone for PopulationConfig {
|
||||
fn clone(&self) -> PopulationConfig {
|
||||
return PopulationConfig {
|
||||
sensor_distance: self.sensor_distance,
|
||||
step_distance: self.step_distance,
|
||||
sensor_angle: self.sensor_angle,
|
||||
rotation_angle: self.rotation_angle,
|
||||
decay_factor: self.decay_factor,
|
||||
deposition_amount: self.deposition_amount,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PopulationConfig {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
@ -78,6 +91,20 @@ pub struct Grid {
|
||||
blur: Blur,
|
||||
}
|
||||
|
||||
impl Clone for Grid {
|
||||
fn clone(&self) -> Grid {
|
||||
return Grid {
|
||||
config: self.config.clone(),
|
||||
width: self.width.clone(),
|
||||
height: self.height.clone(),
|
||||
data: self.data.clone(),
|
||||
buf: self.buf.clone(),
|
||||
blur: self.blur.clone(),
|
||||
}
|
||||
// return Grid::new();
|
||||
}
|
||||
}
|
||||
|
||||
impl Grid {
|
||||
/// Create a new grid filled with random floats in the [0.0..1.0) range.
|
||||
pub fn new<R: Rng + ?Sized>(width: usize, height: usize, rng: &mut R) -> Self {
|
||||
|
||||
53
src/imgdata.rs
Normal file
53
src/imgdata.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use crate::{
|
||||
grid::{combine, Grid, PopulationConfig},
|
||||
palette::{random_palette, Palette},
|
||||
};
|
||||
|
||||
|
||||
use rayon::iter::{ParallelIterator, IntoParallelIterator};
|
||||
|
||||
// for file stuff
|
||||
use std::fs;
|
||||
use std::io::{BufRead, Write, BufReader};
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use std::fs::OpenOptions;
|
||||
|
||||
/*
|
||||
fn get_resumed_primes(file_path: &str) -> Vec<i32> {
|
||||
let path = Path::new(file_path);
|
||||
let lines = lines_from_file(path);
|
||||
|
||||
let resumed_primes = lines.par_iter().map(|x| {
|
||||
return str::replace(&str::replace(x, "Invalid: ", ""), "Prime: ", "").parse::<i32>().unwrap();
|
||||
}).collect();
|
||||
return resumed_primes;
|
||||
}
|
||||
*/
|
||||
|
||||
// Class for storing data that will be used to create images
|
||||
pub struct ImgData {
|
||||
pub grids: Vec<Grid>,
|
||||
pub palette: Palette,
|
||||
pub iteration: i32,
|
||||
}
|
||||
|
||||
impl Clone for ImgData {
|
||||
fn clone(&self) -> ImgData {
|
||||
return ImgData {
|
||||
grids: self.grids.clone(),
|
||||
palette: self.palette.clone(),
|
||||
iteration: self.iteration.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImgData {
|
||||
pub fn new(in_grids: Vec<Grid>, in_palette: Palette, in_iteration: i32) -> Self {
|
||||
ImgData {
|
||||
grids: in_grids,
|
||||
palette: in_palette,
|
||||
iteration: in_iteration,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,3 +4,4 @@ pub mod model;
|
||||
mod palette;
|
||||
pub mod trig; // for benchmarking
|
||||
mod util;
|
||||
mod imgdata; // for storing image data
|
||||
46
src/main.rs
46
src/main.rs
@ -4,33 +4,35 @@ use physarum::model;
|
||||
use rand::Rng;
|
||||
|
||||
fn main() {
|
||||
let n_iterations = 400;
|
||||
let (width, height) = (1024, 1024);
|
||||
let n_iterations = 16384;
|
||||
// let (width, height) = (512, 512);
|
||||
// let (width, height) = (1024, 1024);
|
||||
let (width, height) = (2048, 2048);
|
||||
|
||||
let n_particles = 1 << 22;
|
||||
println!("n_particles: {}", n_particles);
|
||||
let diffusivity = 1;
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
loop {
|
||||
let pb = ProgressBar::new(n_iterations);
|
||||
pb.set_style(
|
||||
ProgressStyle::default_bar()
|
||||
.template(
|
||||
"{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})",
|
||||
)
|
||||
.progress_chars("#>-"),
|
||||
);
|
||||
let pb = ProgressBar::new(n_iterations);
|
||||
pb.set_style(
|
||||
ProgressStyle::default_bar()
|
||||
.template(
|
||||
"{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta} {percent}%, {per_sec})",
|
||||
)
|
||||
.progress_chars("#>-"),
|
||||
);
|
||||
|
||||
let n_populations = 1 + rng.gen_range(1..4);
|
||||
let mut model = model::Model::new(width, height, n_particles, n_populations, diffusivity);
|
||||
model.print_configurations();
|
||||
let n_populations = 1 + rng.gen_range(1..4);
|
||||
let mut model = model::Model::new(width, height, n_particles, n_populations, diffusivity);
|
||||
model.print_configurations();
|
||||
|
||||
for i in 0..n_iterations {
|
||||
model.step();
|
||||
pb.set_position(i);
|
||||
}
|
||||
pb.finish();
|
||||
|
||||
let now: DateTime<Utc> = Utc::now();
|
||||
model.save_to_image(format!("out_{}.png", now.timestamp()).as_str());
|
||||
for i in 0..n_iterations {
|
||||
model.step();
|
||||
pb.set_position(i);
|
||||
}
|
||||
pb.finish();
|
||||
|
||||
model.render_all_imgdata();
|
||||
model.flush_image_data();
|
||||
}
|
||||
|
||||
98
src/model.rs
98
src/model.rs
@ -1,8 +1,11 @@
|
||||
use crate::{
|
||||
grid::{combine, Grid, PopulationConfig},
|
||||
palette::{random_palette, Palette},
|
||||
imgdata::ImgData,
|
||||
};
|
||||
|
||||
|
||||
|
||||
use rand::{seq::SliceRandom, Rng};
|
||||
use rand_distr::{Distribution, Normal};
|
||||
use rayon::prelude::*;
|
||||
@ -10,6 +13,11 @@ use rayon::prelude::*;
|
||||
use itertools::multizip;
|
||||
use std::f32::consts::TAU;
|
||||
|
||||
use std::time::{Duration, Instant};
|
||||
use rayon::iter::{ParallelIterator, IntoParallelIterator};
|
||||
|
||||
use indicatif::{ParallelProgressIterator, ProgressBar, ProgressStyle};
|
||||
|
||||
/// A single Physarum agent. The x and y positions are continuous, hence we use floating point
|
||||
/// numbers instead of integers.
|
||||
#[derive(Debug)]
|
||||
@ -67,6 +75,9 @@ pub struct Model {
|
||||
iteration: i32,
|
||||
|
||||
palette: Palette,
|
||||
|
||||
// List of ImgData to be processed post-simulation into images
|
||||
img_data_vec: Vec<ImgData>,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
@ -123,29 +134,33 @@ impl Model {
|
||||
diffusivity,
|
||||
iteration: 0,
|
||||
palette: random_palette(),
|
||||
img_data_vec: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn pick_direction<R: Rng + ?Sized>(center: f32, left: f32, right: f32, rng: &mut R) -> f32 {
|
||||
if (center > left) && (center > right) {
|
||||
0.0
|
||||
return 0.0;
|
||||
} else if (center < left) && (center < right) {
|
||||
*[-1.0, 1.0].choose(rng).unwrap()
|
||||
return *[-1.0, 1.0].choose(rng).unwrap();
|
||||
} else if left < right {
|
||||
1.0
|
||||
return 1.0;
|
||||
} else if right < left {
|
||||
-1.0
|
||||
} else {
|
||||
0.0
|
||||
return -1.0;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/// Perform a single simulation step.
|
||||
pub fn step(&mut self) {
|
||||
let save_image: bool = true;
|
||||
|
||||
// Combine grids
|
||||
let grids = &mut self.grids;
|
||||
combine(grids, &self.attraction_table);
|
||||
|
||||
println!("Starting tick for all agents...");
|
||||
let agents_tick_time = Instant::now();
|
||||
self.agents.par_iter_mut().for_each(|agent| {
|
||||
let grid = &grids[agent.population_id];
|
||||
let PopulationConfig {
|
||||
@ -159,10 +174,14 @@ impl Model {
|
||||
|
||||
let xc = agent.x + agent.angle.cos() * sensor_distance;
|
||||
let yc = agent.y + agent.angle.sin() * sensor_distance;
|
||||
let xl = agent.x + (agent.angle - sensor_angle).cos() * sensor_distance;
|
||||
let yl = agent.y + (agent.angle - sensor_angle).sin() * sensor_distance;
|
||||
let xr = agent.x + (agent.angle + sensor_angle).cos() * sensor_distance;
|
||||
let yr = agent.y + (agent.angle + sensor_angle).sin() * sensor_distance;
|
||||
|
||||
let agent_add_sens = agent.angle + sensor_angle;
|
||||
let agent_sub_sens = agent.angle - sensor_angle;
|
||||
|
||||
let xl = agent.x + agent_sub_sens.cos() * sensor_distance;
|
||||
let yl = agent.y + agent_sub_sens.sin() * sensor_distance;
|
||||
let xr = agent.x + agent_add_sens.cos() * sensor_distance;
|
||||
let yr = agent.y + agent_add_sens.sin() * sensor_distance;
|
||||
|
||||
// Sense. We sense from the buffer because this is where we previously combined data
|
||||
// from all the grid.
|
||||
@ -176,6 +195,10 @@ impl Model {
|
||||
agent.rotate_and_move(direction, rotation_angle, step_distance, width, height);
|
||||
});
|
||||
|
||||
let agents_tick_elapsed = agents_tick_time.elapsed().as_millis();
|
||||
let ms_per_agent: f64 = (agents_tick_elapsed as f64) / (self.agents.len() as f64);
|
||||
println!("Finished tick for all agents. took {}ms\nTime peragent: {}ms", agents_tick_time.elapsed().as_millis(), ms_per_agent);
|
||||
|
||||
// Deposit
|
||||
for agent in self.agents.iter() {
|
||||
self.grids[agent.population_id].deposit(agent.x, agent.y);
|
||||
@ -186,15 +209,56 @@ impl Model {
|
||||
self.grids.par_iter_mut().for_each(|grid| {
|
||||
grid.diffuse(diffusivity);
|
||||
});
|
||||
|
||||
/*
|
||||
println!("Saving image...");
|
||||
let image_save_time = Instant::now();
|
||||
self.save_to_image(format!("./tmp/out_{}.png", self.iteration).as_str());
|
||||
println!("Saved image took {}", image_save_time.elapsed().as_millis());
|
||||
*/
|
||||
println!("Saving imgdata...");
|
||||
let image_save_time = Instant::now();
|
||||
self.save_image_data();
|
||||
println!("Saved imgdata, took {}", image_save_time.elapsed().as_millis());
|
||||
|
||||
self.iteration += 1;
|
||||
}
|
||||
|
||||
/// Output the current trail layer as a grayscale image.
|
||||
pub fn save_to_image(&self, name: &str) {
|
||||
let (width, height) = (self.grids[0].width, self.grids[0].height);
|
||||
|
||||
fn save_image_data(&mut self) {
|
||||
let grids = self.grids.clone();
|
||||
self.img_data_vec.push(ImgData::new(grids, self.palette, self.iteration));
|
||||
}
|
||||
|
||||
pub fn flush_image_data(&mut self) {
|
||||
self.img_data_vec.clear();
|
||||
}
|
||||
|
||||
pub fn render_all_imgdata(&self) {
|
||||
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})",
|
||||
));
|
||||
|
||||
for img in &self.img_data_vec {
|
||||
Self::save_to_image(img.to_owned());
|
||||
pb.inc(1);
|
||||
}
|
||||
pb.finish();
|
||||
|
||||
/*
|
||||
img_data_list.par_iter().progress_with(pb)
|
||||
.foreach(|&img| {
|
||||
save_to_image(img);
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
pub fn save_to_image(imgdata: ImgData) {
|
||||
let (width, height) = (imgdata.grids[0].width, imgdata.grids[0].height);
|
||||
let mut img = image::RgbImage::new(width as u32, height as u32);
|
||||
|
||||
let max_values: Vec<_> = self
|
||||
let max_values: Vec<_> = imgdata
|
||||
.grids
|
||||
.iter()
|
||||
.map(|grid| grid.quantile(0.999) * 1.5)
|
||||
@ -205,8 +269,7 @@ impl Model {
|
||||
let i = y * width + x;
|
||||
let (mut r, mut g, mut b) = (0.0_f32, 0.0_f32, 0.0_f32);
|
||||
for (grid, max_value, color) in
|
||||
multizip((&self.grids, &max_values, &self.palette.colors))
|
||||
{
|
||||
multizip((&imgdata.grids, &max_values, &imgdata.palette.colors)) {
|
||||
let mut t = (grid.data()[i] / max_value).clamp(0.0, 1.0);
|
||||
t = t.powf(1.0 / 2.2); // gamma correction
|
||||
r += color.0[0] as f32 * t;
|
||||
@ -220,6 +283,7 @@ impl Model {
|
||||
}
|
||||
}
|
||||
|
||||
img.save(name).unwrap();
|
||||
|
||||
img.save(format!("./tmp/out_{}.png", imgdata.iteration).as_str()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,14 +9,14 @@ pub fn random_palette() -> Palette {
|
||||
let mut rng = thread_rng();
|
||||
let mut palette = PALETTES[rng.gen_range(0..PALETTES.len())];
|
||||
palette.colors.shuffle(&mut rng);
|
||||
palette
|
||||
return palette;
|
||||
}
|
||||
|
||||
const fn hex_to_color(c: usize) -> image::Rgb<u8> {
|
||||
let r = (c >> 16) & 0xff;
|
||||
let g = (c >> 8) & 0xff;
|
||||
let b = (c >> 0) & 0xff;
|
||||
image::Rgb::<u8>([r as u8, g as u8, b as u8])
|
||||
return image::Rgb::<u8>([r as u8, g as u8, b as u8]);
|
||||
}
|
||||
|
||||
const PALETTES: [Palette; 8] = [
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user