Compare commits
10 Commits
650edff95c
...
1a3b137e95
| Author | SHA1 | Date | |
|---|---|---|---|
|
1a3b137e95
|
|||
|
f0d4e883f6
|
|||
|
3a9940dfba
|
|||
|
cb6e2e8133
|
|||
|
0c5ba3154a
|
|||
|
1b6cbce8b6
|
|||
|
13fa44ee12
|
|||
|
8e3944d5df
|
|||
|
7acf12a325
|
|||
|
c097446df4
|
713
Cargo.lock
generated
713
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
28
Cargo.toml
28
Cargo.toml
@@ -2,33 +2,19 @@
|
|||||||
name = "physarum"
|
name = "physarum"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Simon Gardling <titaniumtown@gmail.com>", "mindv0rtex <mindv0rtex@users.noreply.github.com>"]
|
authors = ["Simon Gardling <titaniumtown@gmail.com>", "mindv0rtex <mindv0rtex@users.noreply.github.com>"]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
image = "0.23.14"
|
image = "0.23"
|
||||||
indicatif = {version = "0.15.0", features = ["rayon"]}
|
indicatif = { version = "0.15", features = [ "rayon" ] }
|
||||||
itertools = "0.10"
|
itertools = "0.10"
|
||||||
rand = "0.8.3"
|
rand = "0.8"
|
||||||
rand_distr = "0.4.0"
|
rand_distr = "0.4"
|
||||||
rayon = {git = "https://github.com/rayon-rs/rayon.git"}
|
rayon = "1.10"
|
||||||
fastapprox = "0.3.0"
|
fastapprox = "0.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
criterion = "0.3.4"
|
|
||||||
|
|
||||||
[patch.crates-io]
|
|
||||||
rayon = {git = "https://github.com/rayon-rs/rayon.git"} #fixes dependency issues
|
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
codegen-units = 1
|
|
||||||
opt-level = 3
|
|
||||||
target-cpu = "native"
|
|
||||||
lto = "fat"
|
|
||||||
debug = true
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
target-cpu = "native"
|
|
||||||
lto = "fat"
|
lto = "fat"
|
||||||
debug = false
|
debug = false
|
||||||
3
Notes.md
3
Notes.md
@@ -15,3 +15,6 @@
|
|||||||
- 0.000018658ms (2.56% slower)
|
- 0.000018658ms (2.56% slower)
|
||||||
- fast_approx::faster::sin + fast_approx::faster::cos
|
- fast_approx::faster::sin + fast_approx::faster::cos
|
||||||
- 0.000015878ms (14.57% faster)
|
- 0.000015878ms (14.57% faster)
|
||||||
|
## sin and cos lookup table:
|
||||||
|
- With lookup tables: 0.000044201500713825226ms
|
||||||
|
- Without lookup tables: 0.000043705105781555176ms`
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -1,14 +1,3 @@
|
|||||||

|
# Physarum simulation
|
||||||
|
|
||||||
|
based on: https://github.com/yan-zaretskiy/physarum
|
||||||
I recently came across [this repo](https://github.com/fogleman/physarum) that uses Go to implement an extended version of the Physarum transport networks model, first described in:
|
|
||||||
|
|
||||||
> Jones, J. (2010). Characteristics of pattern formation and evolution in approximations of physarum transport networks. Artificial Life, 16(2), 127-153. https://doi.org/10.1162/artl.2010.16.2.16202
|
|
||||||
|
|
||||||
I was amazed by the images this model produces, so I decided to figure out how it works and reimplement it in Rust for no other reason than to have some fun and practice some Rust.
|
|
||||||
|
|
||||||
# Physarum Simulation
|
|
||||||
|
|
||||||
The simulation consists of a 2D grid that stores information about the spatial distribution of some chemotactic sensory stimuli. This is called the _trail_ layer in the original paper. The stimuli attract discrete agents that are meant to represent a particle of Physarum plasmodium gel-sol structure. Each agent knows its position and orientation on the said 2D grid. They are attracted to the stimuli. They have sensors to tell them which way they should turn and move to reach the location with the highest level of the thing that attracts them. As the agents move, they leave a trail of the same stimuli behind, effectively signaling other agents where they should follow. The trail layer is also subjected to a simple diffusion-decay operator after every agent got their change to move to a new location. This image (shamelessly taken from [here](https://sagejenson.com/physarum)) shows all the steps that happen during one iteration of the simulation:
|
|
||||||
|
|
||||||

|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
nightly
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
unstable_features = true
|
|
||||||
imports_granularity = "Crate"
|
|
||||||
wrap_comments = false
|
|
||||||
63
src/agent.rs
63
src/agent.rs
@@ -1,11 +1,8 @@
|
|||||||
use crate::{
|
use crate::{buffer::Buf, util::wrap};
|
||||||
util::wrap,
|
|
||||||
buffer::Buf,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use fastapprox::faster::{cos, sin};
|
||||||
use rand::{seq::SliceRandom, Rng};
|
use rand::{seq::SliceRandom, Rng};
|
||||||
use std::f32::consts::TAU;
|
use std::f32::consts::TAU;
|
||||||
use fastapprox::faster::{cos, sin};
|
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
// A single Physarum agent. The x and y positions are continuous, hence we use floating point numbers instead of integers.
|
// A single Physarum agent. The x and y positions are continuous, hence we use floating point numbers instead of integers.
|
||||||
@@ -30,7 +27,13 @@ impl Display for Agent {
|
|||||||
|
|
||||||
impl Agent {
|
impl Agent {
|
||||||
// Construct a new agent with random parameters.
|
// Construct a new agent with random parameters.
|
||||||
pub fn new<R: Rng + ?Sized>(width: usize, height: usize, id: usize, rng: &mut R, i: usize) -> Self {
|
pub fn new<R: Rng + ?Sized>(
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
id: usize,
|
||||||
|
rng: &mut R,
|
||||||
|
i: usize,
|
||||||
|
) -> Self {
|
||||||
let (x, y, angle) = rng.gen::<(f32, f32, f32)>();
|
let (x, y, angle) = rng.gen::<(f32, f32, f32)>();
|
||||||
Agent {
|
Agent {
|
||||||
x: x * width as f32,
|
x: x * width as f32,
|
||||||
@@ -41,44 +44,28 @@ impl Agent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_from_table(i: f32, table: &Vec<f32>) -> f32 {
|
|
||||||
let interval: f32 = 1.0/0.01;
|
|
||||||
|
|
||||||
let i_proc = (i * interval).round()+(360.0*interval);
|
|
||||||
let output = table[i_proc as usize];
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tick an agent
|
// Tick an agent
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn tick(&mut self, buf: &Buf, sensor_distance: f32, sensor_angle: f32, rotation_angle: f32, step_distance: f32, width: usize, height: usize, sin_table: &Vec<f32>, cos_table: &Vec<f32>) {
|
pub fn tick(
|
||||||
/*
|
&mut self,
|
||||||
|
buf: &Buf,
|
||||||
|
sensor_distance: f32,
|
||||||
|
sensor_angle: f32,
|
||||||
|
rotation_angle: f32,
|
||||||
|
step_distance: f32,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
) {
|
||||||
let xc = self.x + cos(self.angle) * sensor_distance;
|
let xc = self.x + cos(self.angle) * sensor_distance;
|
||||||
let yc = self.y + sin(self.angle) * sensor_distance;
|
let yc = self.y + sin(self.angle) * sensor_distance;
|
||||||
*/
|
|
||||||
|
|
||||||
// /*
|
|
||||||
let xc = self.x + Self::get_from_table(self.angle, &cos_table) * sensor_distance;
|
|
||||||
let yc = self.y + Self::get_from_table(self.angle, &sin_table) * sensor_distance;
|
|
||||||
// */
|
|
||||||
|
|
||||||
let agent_add_sens = self.angle + sensor_angle;
|
let agent_add_sens = self.angle + sensor_angle;
|
||||||
let agent_sub_sens = self.angle - sensor_angle;
|
let agent_sub_sens = self.angle - sensor_angle;
|
||||||
|
|
||||||
// /*
|
|
||||||
let xl = self.x + Self::get_from_table(agent_sub_sens, &cos_table) * sensor_distance;
|
|
||||||
let yl = self.y + Self::get_from_table(agent_sub_sens, &sin_table) * sensor_distance;
|
|
||||||
let xr = self.x + Self::get_from_table(agent_add_sens, &cos_table) * sensor_distance;
|
|
||||||
let yr = self.y + Self::get_from_table(agent_add_sens, &sin_table) * sensor_distance;
|
|
||||||
// */
|
|
||||||
|
|
||||||
/*
|
|
||||||
let xl = self.x + cos(agent_sub_sens) * sensor_distance;
|
let xl = self.x + cos(agent_sub_sens) * sensor_distance;
|
||||||
let yl = self.y + sin(agent_sub_sens) * sensor_distance;
|
let yl = self.y + sin(agent_sub_sens) * sensor_distance;
|
||||||
let xr = self.x + cos(agent_add_sens) * sensor_distance;
|
let xr = self.x + cos(agent_add_sens) * sensor_distance;
|
||||||
let yr = self.y + sin(agent_add_sens) * sensor_distance;
|
let yr = self.y + sin(agent_add_sens) * sensor_distance;
|
||||||
*/
|
|
||||||
|
|
||||||
// We sense from the buffer because this is where we previously combined data from all the grid.
|
// We sense from the buffer because this is where we previously combined data from all the grid.
|
||||||
let center = buf.get_buf(xc, yc);
|
let center = buf.get_buf(xc, yc);
|
||||||
@@ -102,16 +89,8 @@ impl Agent {
|
|||||||
let delta_angle = rotation_angle * direction;
|
let delta_angle = rotation_angle * direction;
|
||||||
|
|
||||||
self.angle = wrap(self.angle + delta_angle, TAU);
|
self.angle = wrap(self.angle + delta_angle, TAU);
|
||||||
self.x = wrap(
|
self.x = wrap(self.x + step_distance * cos(self.angle), width as f32);
|
||||||
// self.x + step_distance * cos(self.angle),
|
self.y = wrap(self.y + step_distance * sin(self.angle), height as f32);
|
||||||
self.x + step_distance * Self::get_from_table(self.angle, &cos_table),
|
|
||||||
width as f32,
|
|
||||||
);
|
|
||||||
self.y = wrap(
|
|
||||||
// self.y + step_distance * sin(self.angle),
|
|
||||||
self.y + step_distance * Self::get_from_table(self.angle, &sin_table),
|
|
||||||
height as f32,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
290
src/blur.rs
290
src/blur.rs
@@ -37,7 +37,7 @@ impl Blur {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Approximate 1D Gaussian filter of standard deviation sigma with N box filter passes. Each element in the output array contains the radius of the box filter for the corresponding pass.
|
// Approximate 1D Gaussian filter of standard deviation sigma with N box filter passes. Each element in the output array contains the radius of the box filter for the corresponding pass.
|
||||||
fn boxes_for_gaussian<const N: usize>(sigma: f32) -> ([usize; N]) {
|
fn boxes_for_gaussian<const N: usize>(sigma: f32) -> [usize; N] {
|
||||||
let w_ideal = (12.0 * sigma * sigma / N as f32 + 1.0).sqrt();
|
let w_ideal = (12.0 * sigma * sigma / N as f32 + 1.0).sqrt();
|
||||||
let mut w = w_ideal as usize;
|
let mut w = w_ideal as usize;
|
||||||
w -= 1 - (w & 1);
|
w -= 1 - (w & 1);
|
||||||
@@ -148,15 +148,69 @@ mod tests {
|
|||||||
// ndimage.uniform_filter(a, size=3, mode='wrap') # 2D blur
|
// ndimage.uniform_filter(a, size=3, mode='wrap') # 2D blur
|
||||||
|
|
||||||
let mut src: Vec<f32> = vec![
|
let mut src: Vec<f32> = vec![
|
||||||
0.32352856, 0.06571674, 0.01939427, 0.06352045, 0.708_527, 0.617_221_7, 0.16638431,
|
0.32352856,
|
||||||
0.628_400_74, 0.554_893_9, 0.240_076_77, 0.325_009_94, 0.08515139, 0.679_840_9, 0.6975669,
|
0.06571674,
|
||||||
0.736_234_25, 0.55053085, 0.692_227_66, 0.22727048, 0.13594262, 0.10002105, 0.16099514,
|
0.01939427,
|
||||||
0.07719103, 0.23984282, 0.9083058, 0.642_227_4, 0.968_934_2, 0.74662715, 0.715_620_1,
|
0.06352045,
|
||||||
0.736_546_5, 0.70610344, 0.221_011_18, 0.755_721_87, 0.691_958_84, 0.837_414, 0.27583158,
|
0.708_527,
|
||||||
0.572_570_5, 0.681_606, 0.392_373_38, 0.33524343, 0.893_968_34, 0.602_969_35, 0.171_301_13,
|
0.617_221_7,
|
||||||
0.1733834, 0.771_278_2, 0.99537134, 0.915_049_6, 0.493_121_1, 0.430_352_03, 0.70297265,
|
0.16638431,
|
||||||
0.367_341_8, 0.4551964, 0.471_043_14, 0.603_747_8, 0.738_726_85, 0.5630592, 0.974_402_25,
|
0.628_400_74,
|
||||||
0.633_682_85, 0.841_092_94, 0.24447136, 0.750384, 0.16893725, 0.542_256_65, 0.435_607_82,
|
0.554_893_9,
|
||||||
|
0.240_076_77,
|
||||||
|
0.325_009_94,
|
||||||
|
0.08515139,
|
||||||
|
0.679_840_9,
|
||||||
|
0.6975669,
|
||||||
|
0.736_234_25,
|
||||||
|
0.55053085,
|
||||||
|
0.692_227_66,
|
||||||
|
0.22727048,
|
||||||
|
0.13594262,
|
||||||
|
0.10002105,
|
||||||
|
0.16099514,
|
||||||
|
0.07719103,
|
||||||
|
0.23984282,
|
||||||
|
0.9083058,
|
||||||
|
0.642_227_4,
|
||||||
|
0.968_934_2,
|
||||||
|
0.74662715,
|
||||||
|
0.715_620_1,
|
||||||
|
0.736_546_5,
|
||||||
|
0.70610344,
|
||||||
|
0.221_011_18,
|
||||||
|
0.755_721_87,
|
||||||
|
0.691_958_84,
|
||||||
|
0.837_414,
|
||||||
|
0.27583158,
|
||||||
|
0.572_570_5,
|
||||||
|
0.681_606,
|
||||||
|
0.392_373_38,
|
||||||
|
0.33524343,
|
||||||
|
0.893_968_34,
|
||||||
|
0.602_969_35,
|
||||||
|
0.171_301_13,
|
||||||
|
0.1733834,
|
||||||
|
0.771_278_2,
|
||||||
|
0.99537134,
|
||||||
|
0.915_049_6,
|
||||||
|
0.493_121_1,
|
||||||
|
0.430_352_03,
|
||||||
|
0.70297265,
|
||||||
|
0.367_341_8,
|
||||||
|
0.4551964,
|
||||||
|
0.471_043_14,
|
||||||
|
0.603_747_8,
|
||||||
|
0.738_726_85,
|
||||||
|
0.5630592,
|
||||||
|
0.974_402_25,
|
||||||
|
0.633_682_85,
|
||||||
|
0.841_092_94,
|
||||||
|
0.24447136,
|
||||||
|
0.750384,
|
||||||
|
0.16893725,
|
||||||
|
0.542_256_65,
|
||||||
|
0.435_607_82,
|
||||||
0.414_971_23,
|
0.414_971_23,
|
||||||
];
|
];
|
||||||
let (width, height) = (8, 8);
|
let (width, height) = (8, 8);
|
||||||
@@ -165,15 +219,69 @@ mod tests {
|
|||||||
|
|
||||||
blur.box_blur_h(&src, &mut dst, width, 1);
|
blur.box_blur_h(&src, &mut dst, width, 1);
|
||||||
let mut sol: Vec<f32> = vec![
|
let mut sol: Vec<f32> = vec![
|
||||||
0.339_215_37, 0.136_213_18, 0.04954382, 0.263_813_9, 0.46308973, 0.497_377_7, 0.470_668_94,
|
0.339_215_37,
|
||||||
0.372_771_2, 0.448_500_5, 0.373_326_87, 0.21674603, 0.363_334_1, 0.48751974, 0.70454735,
|
0.136_213_18,
|
||||||
0.661_444, 0.613_886_36, 0.609_268, 0.351_813_58, 0.15441138, 0.1323196, 0.11273574,
|
0.04954382,
|
||||||
0.159343, 0.40844655, 0.613_458_75, 0.788_961_2, 0.785_929_56, 0.810_393_8, 0.732_931_26,
|
0.263_813_9,
|
||||||
0.719_423_35, 0.554_553_7, 0.560_945_5, 0.539_653_5, 0.807_780_4, 0.601_734_8, 0.561_938_7,
|
0.46308973,
|
||||||
0.510_002_7, 0.548_849_94, 0.46974093, 0.540_528_4, 0.640_390_2, 0.40154082, 0.315_884_62,
|
0.497_377_7,
|
||||||
0.371_987_58, 0.646_677_6, 0.893_899_74, 0.801_180_66, 0.612_840_9, 0.508_814_16, 0.681_572_2,
|
0.470_668_94,
|
||||||
0.508_503_6, 0.431_193_77, 0.509_995_76, 0.604_505_9, 0.635_177_9, 0.758_729_4, 0.746_811_33,
|
0.372_771_2,
|
||||||
0.629_915_65, 0.573_082_4, 0.611_982_76, 0.38793087, 0.48719263, 0.38226724, 0.464_278_58,
|
0.448_500_5,
|
||||||
|
0.373_326_87,
|
||||||
|
0.21674603,
|
||||||
|
0.363_334_1,
|
||||||
|
0.48751974,
|
||||||
|
0.70454735,
|
||||||
|
0.661_444,
|
||||||
|
0.613_886_36,
|
||||||
|
0.609_268,
|
||||||
|
0.351_813_58,
|
||||||
|
0.15441138,
|
||||||
|
0.1323196,
|
||||||
|
0.11273574,
|
||||||
|
0.159343,
|
||||||
|
0.40844655,
|
||||||
|
0.613_458_75,
|
||||||
|
0.788_961_2,
|
||||||
|
0.785_929_56,
|
||||||
|
0.810_393_8,
|
||||||
|
0.732_931_26,
|
||||||
|
0.719_423_35,
|
||||||
|
0.554_553_7,
|
||||||
|
0.560_945_5,
|
||||||
|
0.539_653_5,
|
||||||
|
0.807_780_4,
|
||||||
|
0.601_734_8,
|
||||||
|
0.561_938_7,
|
||||||
|
0.510_002_7,
|
||||||
|
0.548_849_94,
|
||||||
|
0.46974093,
|
||||||
|
0.540_528_4,
|
||||||
|
0.640_390_2,
|
||||||
|
0.40154082,
|
||||||
|
0.315_884_62,
|
||||||
|
0.371_987_58,
|
||||||
|
0.646_677_6,
|
||||||
|
0.893_899_74,
|
||||||
|
0.801_180_66,
|
||||||
|
0.612_840_9,
|
||||||
|
0.508_814_16,
|
||||||
|
0.681_572_2,
|
||||||
|
0.508_503_6,
|
||||||
|
0.431_193_77,
|
||||||
|
0.509_995_76,
|
||||||
|
0.604_505_9,
|
||||||
|
0.635_177_9,
|
||||||
|
0.758_729_4,
|
||||||
|
0.746_811_33,
|
||||||
|
0.629_915_65,
|
||||||
|
0.573_082_4,
|
||||||
|
0.611_982_76,
|
||||||
|
0.38793087,
|
||||||
|
0.48719263,
|
||||||
|
0.38226724,
|
||||||
|
0.464_278_58,
|
||||||
0.494_753_96,
|
0.494_753_96,
|
||||||
];
|
];
|
||||||
for (v1, v2) in dst.iter().zip(sol) {
|
for (v1, v2) in dst.iter().zip(sol) {
|
||||||
@@ -182,15 +290,69 @@ mod tests {
|
|||||||
|
|
||||||
blur.box_blur_v(&src, &mut dst, width, height, 1, 1.0);
|
blur.box_blur_v(&src, &mut dst, width, height, 1, 1.0);
|
||||||
sol = vec![
|
sol = vec![
|
||||||
0.504_035_1, 0.382_295_5, 0.19629186, 0.299_685_27, 0.519_101_74, 0.619_015_1, 0.446_075_47,
|
0.504_035_1,
|
||||||
0.531_300_96, 0.523_550_03, 0.177688, 0.16011561, 0.08289763, 0.516_454_34, 0.46399322,
|
0.382_295_5,
|
||||||
0.38082045, 0.695_745_8, 0.629_783_03, 0.47876048, 0.402_526_56, 0.300_264_18, 0.5257942,
|
0.19629186,
|
||||||
0.49362046, 0.3990294, 0.738_186_2, 0.675_471_3, 0.677_872_9, 0.386_133_8, 0.46273723,
|
0.299_685_27,
|
||||||
0.526_382_57, 0.391_889_3, 0.265_365_8, 0.852_665_36, 0.645_718_5, 0.659_216_46, 0.39861405,
|
0.519_101_74,
|
||||||
0.686_489_6, 0.804_508, 0.671_175_5, 0.349_791_88, 0.693_347_4, 0.665_966_9, 0.458_685_64,
|
0.619_015_1,
|
||||||
0.30147046, 0.604_963_96, 0.760_241_7, 0.682_05, 0.463_807_9, 0.766_240_9, 0.6465416,
|
0.446_075_47,
|
||||||
0.459_911_97, 0.291_017_06, 0.664_235_1, 0.589_352_13, 0.732_011, 0.497_262_72, 0.606_575_13,
|
0.531_300_96,
|
||||||
0.553_394_7, 0.42471716, 0.23968734, 0.428_315_88, 0.493_737_34, 0.632_735_1, 0.388_350_46,
|
0.523_550_03,
|
||||||
|
0.177688,
|
||||||
|
0.16011561,
|
||||||
|
0.08289763,
|
||||||
|
0.516_454_34,
|
||||||
|
0.46399322,
|
||||||
|
0.38082045,
|
||||||
|
0.695_745_8,
|
||||||
|
0.629_783_03,
|
||||||
|
0.47876048,
|
||||||
|
0.402_526_56,
|
||||||
|
0.300_264_18,
|
||||||
|
0.5257942,
|
||||||
|
0.49362046,
|
||||||
|
0.3990294,
|
||||||
|
0.738_186_2,
|
||||||
|
0.675_471_3,
|
||||||
|
0.677_872_9,
|
||||||
|
0.386_133_8,
|
||||||
|
0.46273723,
|
||||||
|
0.526_382_57,
|
||||||
|
0.391_889_3,
|
||||||
|
0.265_365_8,
|
||||||
|
0.852_665_36,
|
||||||
|
0.645_718_5,
|
||||||
|
0.659_216_46,
|
||||||
|
0.39861405,
|
||||||
|
0.686_489_6,
|
||||||
|
0.804_508,
|
||||||
|
0.671_175_5,
|
||||||
|
0.349_791_88,
|
||||||
|
0.693_347_4,
|
||||||
|
0.665_966_9,
|
||||||
|
0.458_685_64,
|
||||||
|
0.30147046,
|
||||||
|
0.604_963_96,
|
||||||
|
0.760_241_7,
|
||||||
|
0.682_05,
|
||||||
|
0.463_807_9,
|
||||||
|
0.766_240_9,
|
||||||
|
0.6465416,
|
||||||
|
0.459_911_97,
|
||||||
|
0.291_017_06,
|
||||||
|
0.664_235_1,
|
||||||
|
0.589_352_13,
|
||||||
|
0.732_011,
|
||||||
|
0.497_262_72,
|
||||||
|
0.606_575_13,
|
||||||
|
0.553_394_7,
|
||||||
|
0.42471716,
|
||||||
|
0.23968734,
|
||||||
|
0.428_315_88,
|
||||||
|
0.493_737_34,
|
||||||
|
0.632_735_1,
|
||||||
|
0.388_350_46,
|
||||||
0.672_591_45,
|
0.672_591_45,
|
||||||
];
|
];
|
||||||
for (v1, v2) in dst.iter().zip(sol) {
|
for (v1, v2) in dst.iter().zip(sol) {
|
||||||
@@ -199,15 +361,69 @@ mod tests {
|
|||||||
|
|
||||||
blur.box_blur(&mut src, &mut dst, width, height, 1, 1.0);
|
blur.box_blur(&mut src, &mut dst, width, height, 1, 1.0);
|
||||||
sol = vec![
|
sol = vec![
|
||||||
0.472_543_84, 0.36087415, 0.29275754, 0.338_359_62, 0.47926736, 0.528_064_1, 0.5321305,
|
0.472_543_84,
|
||||||
0.493_803_83, 0.465_661_3, 0.287_117_9, 0.140_233_76, 0.253_155_86, 0.3544484, 0.453_756,
|
0.36087415,
|
||||||
0.513_519_8, 0.5333721, 0.615_576_57, 0.503_69, 0.393_850_42, 0.40952832, 0.43989295,
|
0.29275754,
|
||||||
0.472_814_68, 0.543_612, 0.588_999_5, 0.735_336_54, 0.579826, 0.508_914_65, 0.458_417_86,
|
0.338_359_62,
|
||||||
0.460_336_36, 0.39454588, 0.503_306_8, 0.597_834_17, 0.666_094_1, 0.567_849_7, 0.581_440_03,
|
0.47926736,
|
||||||
0.62987053, 0.720_724_34, 0.608_491_8, 0.571_438_25, 0.562_952_64, 0.630_297_84, 0.475_374_34,
|
0.528_064_1,
|
||||||
0.455_04, 0.5555587, 0.682_418_5, 0.635_366_5, 0.63736624, 0.632_005_2, 0.571_009_6,
|
0.5321305,
|
||||||
0.465_823_53, 0.471_721_38, 0.5148681, 0.661_866_07, 0.606_208_6, 0.611_949_6, 0.583_459_8,
|
0.493_803_83,
|
||||||
0.550_234_44, 0.405_933_05, 0.364_240_14, 0.38724685, 0.518_262_74, 0.504_940_9, 0.564_559,
|
0.465_661_3,
|
||||||
|
0.287_117_9,
|
||||||
|
0.140_233_76,
|
||||||
|
0.253_155_86,
|
||||||
|
0.3544484,
|
||||||
|
0.453_756,
|
||||||
|
0.513_519_8,
|
||||||
|
0.5333721,
|
||||||
|
0.615_576_57,
|
||||||
|
0.503_69,
|
||||||
|
0.393_850_42,
|
||||||
|
0.40952832,
|
||||||
|
0.43989295,
|
||||||
|
0.472_814_68,
|
||||||
|
0.543_612,
|
||||||
|
0.588_999_5,
|
||||||
|
0.735_336_54,
|
||||||
|
0.579826,
|
||||||
|
0.508_914_65,
|
||||||
|
0.458_417_86,
|
||||||
|
0.460_336_36,
|
||||||
|
0.39454588,
|
||||||
|
0.503_306_8,
|
||||||
|
0.597_834_17,
|
||||||
|
0.666_094_1,
|
||||||
|
0.567_849_7,
|
||||||
|
0.581_440_03,
|
||||||
|
0.62987053,
|
||||||
|
0.720_724_34,
|
||||||
|
0.608_491_8,
|
||||||
|
0.571_438_25,
|
||||||
|
0.562_952_64,
|
||||||
|
0.630_297_84,
|
||||||
|
0.475_374_34,
|
||||||
|
0.455_04,
|
||||||
|
0.5555587,
|
||||||
|
0.682_418_5,
|
||||||
|
0.635_366_5,
|
||||||
|
0.63736624,
|
||||||
|
0.632_005_2,
|
||||||
|
0.571_009_6,
|
||||||
|
0.465_823_53,
|
||||||
|
0.471_721_38,
|
||||||
|
0.5148681,
|
||||||
|
0.661_866_07,
|
||||||
|
0.606_208_6,
|
||||||
|
0.611_949_6,
|
||||||
|
0.583_459_8,
|
||||||
|
0.550_234_44,
|
||||||
|
0.405_933_05,
|
||||||
|
0.364_240_14,
|
||||||
|
0.38724685,
|
||||||
|
0.518_262_74,
|
||||||
|
0.504_940_9,
|
||||||
|
0.564_559,
|
||||||
0.538_112_16,
|
0.538_112_16,
|
||||||
];
|
];
|
||||||
for (v1, v2) in src.iter().zip(sol) {
|
for (v1, v2) in src.iter().zip(sol) {
|
||||||
|
|||||||
@@ -17,11 +17,7 @@ impl Clone for Buf {
|
|||||||
|
|
||||||
impl Buf {
|
impl Buf {
|
||||||
pub fn new(width: usize, height: usize, buf: Vec<f32>) -> Self {
|
pub fn new(width: usize, height: usize, buf: Vec<f32>) -> Self {
|
||||||
Buf {
|
Buf { width, height, buf }
|
||||||
width,
|
|
||||||
height,
|
|
||||||
buf,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Truncate x and y and return a corresponding index into the data slice.
|
// Truncate x and y and return a corresponding index into the data slice.
|
||||||
|
|||||||
33
src/grid.rs
33
src/grid.rs
@@ -1,12 +1,8 @@
|
|||||||
use crate::{
|
use crate::{agent::Agent, blur::Blur, buffer::Buf};
|
||||||
blur::Blur,
|
|
||||||
agent::Agent,
|
|
||||||
buffer::Buf,
|
|
||||||
};
|
|
||||||
|
|
||||||
use rand::{distributions::Uniform, Rng};
|
use rand::{distributions::Uniform, Rng};
|
||||||
use std::fmt::{Display, Formatter};
|
|
||||||
use rayon::{iter::ParallelIterator, prelude::*};
|
use rayon::{iter::ParallelIterator, prelude::*};
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
// A population configuration.
|
// A population configuration.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -93,7 +89,7 @@ pub struct Grid {
|
|||||||
// pub buf: Vec<f32>,
|
// pub buf: Vec<f32>,
|
||||||
pub buf: Buf,
|
pub buf: Buf,
|
||||||
pub blur: Blur,
|
pub blur: Blur,
|
||||||
pub agents: Vec<Agent>
|
pub agents: Vec<Agent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Grid {
|
impl Clone for Grid {
|
||||||
@@ -112,7 +108,12 @@ impl Clone for Grid {
|
|||||||
|
|
||||||
impl Grid {
|
impl Grid {
|
||||||
// Create a new grid filled with random floats in the [0.0..1.0) range.
|
// 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, agents: Vec<Agent>) -> Self {
|
pub fn new<R: Rng + ?Sized>(
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
rng: &mut R,
|
||||||
|
agents: Vec<Agent>,
|
||||||
|
) -> Self {
|
||||||
if !width.is_power_of_two() || !height.is_power_of_two() {
|
if !width.is_power_of_two() || !height.is_power_of_two() {
|
||||||
panic!("Grid dimensions must be a power of two.");
|
panic!("Grid dimensions must be a power of two.");
|
||||||
}
|
}
|
||||||
@@ -164,7 +165,7 @@ impl Grid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn tick(&mut self, sin_table: Vec<f32>, cos_table: Vec<f32>) {
|
pub fn tick(&mut self) {
|
||||||
let (width, height) = (self.width, self.height);
|
let (width, height) = (self.width, self.height);
|
||||||
let PopulationConfig {
|
let PopulationConfig {
|
||||||
sensor_distance,
|
sensor_distance,
|
||||||
@@ -177,10 +178,15 @@ impl Grid {
|
|||||||
let buf = self.buf.clone();
|
let buf = self.buf.clone();
|
||||||
|
|
||||||
self.agents.par_iter_mut().for_each(|agent| {
|
self.agents.par_iter_mut().for_each(|agent| {
|
||||||
agent.tick(&buf,
|
agent.tick(
|
||||||
sensor_distance, sensor_angle,
|
&buf,
|
||||||
rotation_angle, step_distance,
|
sensor_distance,
|
||||||
width, height, &sin_table, &cos_table);
|
sensor_angle,
|
||||||
|
rotation_angle,
|
||||||
|
step_distance,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
self.deposit_all();
|
self.deposit_all();
|
||||||
}
|
}
|
||||||
@@ -258,6 +264,5 @@ mod tests {
|
|||||||
assert_eq!(grid.index(2.5, 0.6), 2);
|
assert_eq!(grid.index(2.5, 0.6), 2);
|
||||||
assert_eq!(grid.index(2.5, 1.6), 10);
|
assert_eq!(grid.index(2.5, 1.6), 10);
|
||||||
assert_eq!(grid.index(7.9, 7.9), 63);
|
assert_eq!(grid.index(7.9, 7.9), 63);
|
||||||
assert_eq!(grid.index(-0.5, -0.6), 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ pub struct ThinGridData {
|
|||||||
pub data: Vec<f32>,
|
pub data: Vec<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Clone for ThinGridData {
|
impl Clone for ThinGridData {
|
||||||
fn clone(&self) -> ThinGridData {
|
fn clone(&self) -> ThinGridData {
|
||||||
ThinGridData {
|
ThinGridData {
|
||||||
@@ -32,9 +31,10 @@ impl ThinGridData {
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn new_from_grid_vec(in_grids: Vec<Grid>) -> Vec<Self> {
|
pub fn new_from_grid_vec(in_grids: Vec<Grid>) -> Vec<Self> {
|
||||||
return in_grids.iter().map(|grid|{
|
in_grids
|
||||||
Self::new_from_grid(grid)
|
.iter()
|
||||||
}).collect();
|
.map(|grid| Self::new_from_grid(grid))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
// from grid.rs (needed in image gen)
|
// from grid.rs (needed in image gen)
|
||||||
@@ -60,12 +60,12 @@ impl ThinGridData {
|
|||||||
|
|
||||||
pub fn size_of(&self) -> usize {
|
pub fn size_of(&self) -> usize {
|
||||||
let mut output: usize = 0;
|
let mut output: usize = 0;
|
||||||
output = output + std::mem::size_of_val(&self.width);
|
output += std::mem::size_of_val(&self.width);
|
||||||
output = output + std::mem::size_of_val(&self.height);
|
output += std::mem::size_of_val(&self.height);
|
||||||
for i in self.data.iter() {
|
for i in self.data.iter() {
|
||||||
output = output + std::mem::size_of_val(&i);
|
output += std::mem::size_of_val(&i);
|
||||||
}
|
}
|
||||||
return output;
|
output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,12 +97,12 @@ impl ImgData {
|
|||||||
|
|
||||||
pub fn size_of(&self) -> usize {
|
pub fn size_of(&self) -> usize {
|
||||||
let mut output: usize = 0;
|
let mut output: usize = 0;
|
||||||
output = output + std::mem::size_of_val(&self.iteration);
|
output += std::mem::size_of_val(&self.iteration);
|
||||||
output = output + std::mem::size_of_val(&self.palette);
|
output += std::mem::size_of_val(&self.palette);
|
||||||
for grid in self.grids.iter() {
|
for grid in self.grids.iter() {
|
||||||
output = output + grid.size_of();
|
output += grid.size_of();
|
||||||
}
|
}
|
||||||
return output;
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
mod agent;
|
||||||
mod blur;
|
mod blur;
|
||||||
|
mod buffer;
|
||||||
mod grid;
|
mod grid;
|
||||||
mod imgdata; // for storing image data
|
mod imgdata; // for storing image data
|
||||||
pub mod model;
|
pub mod model;
|
||||||
mod palette;
|
mod palette;
|
||||||
mod util; // for math things
|
mod util; // for math things
|
||||||
mod agent;
|
|
||||||
mod buffer;
|
|
||||||
@@ -2,14 +2,14 @@ use physarum::model;
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// # of iterations to go through
|
// # of iterations to go through
|
||||||
let n_iterations = 128;
|
let n_iterations = 1024;
|
||||||
// let n_iterations = 2048;
|
// let n_iterations = 2048;
|
||||||
// let n_iterations = 1 << 14;
|
// let n_iterations = 1 << 14;
|
||||||
|
|
||||||
// Size of grid and pictures
|
// Size of grid and pictures
|
||||||
let (width, height) = (256, 256);
|
// let (width, height) = (256, 256);
|
||||||
// let (width, height) = (512, 512);
|
// let (width, height) = (512, 512);
|
||||||
// let (width, height) = (1024, 1024);
|
let (width, height) = (1024, 1024);
|
||||||
|
|
||||||
// # of agents
|
// # of agents
|
||||||
// let n_particles = 1 << 10;
|
// let n_particles = 1 << 10;
|
||||||
|
|||||||
58
src/model.rs
58
src/model.rs
@@ -1,8 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
|
agent::Agent,
|
||||||
grid::{combine, Grid},
|
grid::{combine, Grid},
|
||||||
imgdata::{ImgData, ThinGridData},
|
imgdata::{ImgData, ThinGridData},
|
||||||
palette::{random_palette, Palette},
|
palette::{random_palette, Palette},
|
||||||
agent::Agent,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use indicatif::{ParallelProgressIterator, ProgressBar, ProgressStyle};
|
use indicatif::{ParallelProgressIterator, ProgressBar, ProgressStyle};
|
||||||
@@ -10,7 +10,6 @@ use indicatif::{ParallelProgressIterator, ProgressBar, ProgressStyle};
|
|||||||
use rand_distr::{Distribution, Normal};
|
use rand_distr::{Distribution, Normal};
|
||||||
use rayon::{iter::ParallelIterator, prelude::*};
|
use rayon::{iter::ParallelIterator, prelude::*};
|
||||||
use std::{path::Path, time::Instant};
|
use std::{path::Path, time::Instant};
|
||||||
use fastapprox::faster::{cos, sin};
|
|
||||||
|
|
||||||
// Top-level simulation class.
|
// Top-level simulation class.
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
@@ -79,9 +78,8 @@ impl Model {
|
|||||||
let mut grids: Vec<Grid> = Vec::new();
|
let mut grids: Vec<Grid> = Vec::new();
|
||||||
for pop in 0..n_populations {
|
for pop in 0..n_populations {
|
||||||
let agents = (0..particles_per_grid)
|
let agents = (0..particles_per_grid)
|
||||||
.map(|i| {
|
.map(|i| Agent::new(width, height, pop, &mut rng, i))
|
||||||
Agent::new(width, height, pop, &mut rng, i)
|
.collect();
|
||||||
}).collect();
|
|
||||||
grids.push(Grid::new(width, height, &mut rng, agents));
|
grids.push(Grid::new(width, height, &mut rng, agents));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,32 +93,6 @@ impl Model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_table(opt: i32) -> Vec<f32> {
|
|
||||||
let mut output: Vec<f32> = Vec::new();
|
|
||||||
|
|
||||||
let max: f32 = 360.0;
|
|
||||||
let min: f32 = -360.0;
|
|
||||||
let interval: f32 = 0.01;
|
|
||||||
|
|
||||||
|
|
||||||
let mut i: f32;
|
|
||||||
let mut tmp: f32 = 0.0;
|
|
||||||
for i1 in ((min/interval)as i32)..((max/interval)as i32) {
|
|
||||||
i = (i1 as f32)*interval;
|
|
||||||
if opt == 0 {
|
|
||||||
tmp = sin(i);
|
|
||||||
} else if opt == 1 {
|
|
||||||
tmp = cos(i);
|
|
||||||
}
|
|
||||||
output.push(tmp);
|
|
||||||
|
|
||||||
}
|
|
||||||
println!("{}", output.len());
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Simulates `steps` # of steps
|
// Simulates `steps` # of steps
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn run(&mut self, steps: usize) {
|
pub fn run(&mut self, steps: usize) {
|
||||||
@@ -139,10 +111,6 @@ impl Model {
|
|||||||
let mut time_per_step_list: Vec<f64> = Vec::new();
|
let mut time_per_step_list: Vec<f64> = Vec::new();
|
||||||
|
|
||||||
let agents_num: usize = self.grids.iter().map(|grid| grid.agents.len()).sum();
|
let agents_num: usize = self.grids.iter().map(|grid| grid.agents.len()).sum();
|
||||||
let sin_table1 = Self::gen_table(0);
|
|
||||||
let cos_table1 = Self::gen_table(1);
|
|
||||||
let sin_table = sin_table1.clone();
|
|
||||||
let cos_table = cos_table1.clone();
|
|
||||||
for i in 0..steps {
|
for i in 0..steps {
|
||||||
if debug {
|
if debug {
|
||||||
println!("Starting tick for all agents...")
|
println!("Starting tick for all agents...")
|
||||||
@@ -156,14 +124,14 @@ impl Model {
|
|||||||
// Tick agents
|
// Tick agents
|
||||||
let diffusivity = self.diffusivity;
|
let diffusivity = self.diffusivity;
|
||||||
self.grids.par_iter_mut().for_each(|grid| {
|
self.grids.par_iter_mut().for_each(|grid| {
|
||||||
grid.tick(sin_table.clone(), cos_table.clone());
|
grid.tick();
|
||||||
grid.diffuse(diffusivity); // Diffuse + Decay
|
grid.diffuse(diffusivity); // Diffuse + Decay
|
||||||
});
|
});
|
||||||
|
|
||||||
self.save_image_data();
|
self.save_image_data();
|
||||||
|
|
||||||
let agents_tick_elapsed: f64 = agents_tick_time.elapsed().as_millis() as f64;
|
let agents_tick_elapsed: f64 = agents_tick_time.elapsed().as_millis() as f64;
|
||||||
let ms_per_agent: f64 = (agents_tick_elapsed as f64) / (agents_num as f64);
|
let ms_per_agent: f64 = agents_tick_elapsed / (agents_num as f64);
|
||||||
time_per_agent_list.push(ms_per_agent);
|
time_per_agent_list.push(ms_per_agent);
|
||||||
time_per_step_list.push(agents_tick_elapsed);
|
time_per_step_list.push(agents_tick_elapsed);
|
||||||
|
|
||||||
@@ -180,9 +148,9 @@ impl Model {
|
|||||||
pb.finish();
|
pb.finish();
|
||||||
|
|
||||||
let avg_per_step: f64 =
|
let avg_per_step: f64 =
|
||||||
time_per_step_list.iter().sum::<f64>() as f64 / time_per_step_list.len() as f64;
|
time_per_step_list.iter().sum::<f64>() / time_per_step_list.len() as f64;
|
||||||
let avg_per_agent: f64 =
|
let avg_per_agent: f64 =
|
||||||
time_per_agent_list.iter().sum::<f64>() as f64 / time_per_agent_list.len() as f64;
|
time_per_agent_list.iter().sum::<f64>() / time_per_agent_list.len() as f64;
|
||||||
println!(
|
println!(
|
||||||
"Average time per step: {}ms\nAverage time per agent: {}ms",
|
"Average time per step: {}ms\nAverage time per agent: {}ms",
|
||||||
avg_per_step, avg_per_agent
|
avg_per_step, avg_per_agent
|
||||||
@@ -190,7 +158,7 @@ impl Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn size_of_imgdata_vec(&self) -> usize {
|
fn size_of_imgdata_vec(&self) -> usize {
|
||||||
return (self.img_data_vec[0].size_of() as usize) * (self.img_data_vec.len() as usize);
|
self.img_data_vec[0].size_of() * self.img_data_vec.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save_image_data(&mut self) {
|
fn save_image_data(&mut self) {
|
||||||
@@ -198,12 +166,16 @@ impl Model {
|
|||||||
let img_data = ImgData::new(grids, self.palette, self.iteration);
|
let img_data = ImgData::new(grids, self.palette, self.iteration);
|
||||||
self.img_data_vec.push(img_data);
|
self.img_data_vec.push(img_data);
|
||||||
let size: usize = self.size_of_imgdata_vec();
|
let size: usize = self.size_of_imgdata_vec();
|
||||||
let mb = size/1024/1024;
|
let mb = size / 1024 / 1024;
|
||||||
// println!("{} B | {} KB | {} MB", size, size/1024, size/1024/1024);
|
// println!("{} B | {} KB | {} MB", size, size/1024, size/1024/1024);
|
||||||
|
|
||||||
let max_mb = 6000;
|
let max_mb = 6000;
|
||||||
if mb >= max_mb {
|
if mb >= max_mb {
|
||||||
println!("ram usage is over {} MB (and len of {}), flushing to disk\n", max_mb, self.img_data_vec.len());
|
println!(
|
||||||
|
"ram usage is over {} MB (and len of {}), flushing to disk\n",
|
||||||
|
max_mb,
|
||||||
|
self.img_data_vec.len()
|
||||||
|
);
|
||||||
self.render_all_imgdata();
|
self.render_all_imgdata();
|
||||||
self.flush_image_data();
|
self.flush_image_data();
|
||||||
}
|
}
|
||||||
@@ -231,7 +203,7 @@ impl Model {
|
|||||||
pb.finish();
|
pb.finish();
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(&self.img_data_vec)
|
self.img_data_vec
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.progress_with(pb)
|
.progress_with(pb)
|
||||||
.for_each(|img| {
|
.for_each(|img| {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub fn random_palette() -> Palette {
|
|||||||
const fn hex_to_color(c: usize) -> image::Rgb<u8> {
|
const fn hex_to_color(c: usize) -> image::Rgb<u8> {
|
||||||
let r = (c >> 16) & 0xff;
|
let r = (c >> 16) & 0xff;
|
||||||
let g = (c >> 8) & 0xff;
|
let g = (c >> 8) & 0xff;
|
||||||
let b = (c >> 0) & 0xff;
|
let b = c & 0xff;
|
||||||
image::Rgb::<u8>([r as u8, g as u8, b as u8])
|
image::Rgb::<u8>([r as u8, g as u8, b as u8])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user