From e21a61250e25f72dc59cf34f4fe907d12689e90f Mon Sep 17 00:00:00 2001 From: mindv0rtex Date: Wed, 24 Feb 2021 22:26:37 -0500 Subject: [PATCH] Initial commit. WIP blur implementation. Grid struct is tentatively ready. Model struct is in its nascency. --- .gitignore | 4 + Cargo.lock | 472 +++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 12 ++ rust-toolchain | 1 + rustfmt.toml | 4 + src/blur.rs | 124 +++++++++++++ src/grid.rs | 95 ++++++++++ src/main.rs | 8 + src/model.rs | 32 ++++ 9 files changed, 752 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 rust-toolchain create mode 100644 rustfmt.toml create mode 100644 src/blur.rs create mode 100644 src/grid.rs create mode 100644 src/main.rs create mode 100644 src/model.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf89451 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target + +# CLion +**/.idea \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1a63fb3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,472 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bytemuck" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bed57e2090563b83ba8f83366628ce535a7584c9afa4c9fc0612a03925c6df58" + +[[package]] +name = "byteorder" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" + +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d60ab4a8dba064f2fbb5aa270c28da5cf4bbd0e72dae1140a6b0353a779dbe00" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "lazy_static", + "loom", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae8f328835f8f5a6ceb6a7842a7f2d0c03692adb5c889347235d59194731fe3" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", + "loom", +] + +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "generator" +version = "0.6.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9fed24fd1e18827652b4d55652899a1e9da8e54d91624dc3437a5bc3a9f9a9c" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gif" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02efba560f227847cb41463a7395c514d127d4f74fff12ef0137fff1b84b96c4" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + +[[package]] +name = "image" +version = "0.23.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "293f07a1875fa7e9c5897b51aa68b2d8ed8271b87e1a44cb64b9c3d98aabbc0d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "gif", + "jpeg-decoder", + "num-iter", + "num-rational", + "num-traits", + "png", + "scoped_threadpool", + "tiff", +] + +[[package]] +name = "jpeg-decoder" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" +dependencies = [ + "rayon", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "loom" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d44c73b4636e497b4917eb21c33539efa3816741a2d3ff26c6316f1b529481a4" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", +] + +[[package]] +name = "memoffset" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "physarum" +version = "0.1.0" +dependencies = [ + "image", + "rand", + "rayon", +] + +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide 0.3.7", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rayon" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "rustversion" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5d2a036dc6d2d8fd16fde3498b04306e29bd193bf306a57427019b823d5acd" + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "tiff" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437" +dependencies = [ + "jpeg-decoder", + "miniz_oxide 0.4.3", + "weezl", +] + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "weezl" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a32b378380f4e9869b22f0b5177c68a5519f03b3454fde0b291455ddbae266c" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1f1c1bd --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "physarum" +version = "0.1.0" +authors = ["mindv0rtex "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +image = "*" +rand = "*" +rayon = "*" diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..07ade69 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..297bae1 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,4 @@ +unstable_features = true +imports_granularity = "Crate" +wrap_comments = true +comment_width = 100 \ No newline at end of file diff --git a/src/blur.rs b/src/blur.rs new file mode 100644 index 0000000..c6ae1d2 --- /dev/null +++ b/src/blur.rs @@ -0,0 +1,124 @@ +/// 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. +pub fn boxes_for_gaussian(sigma: f32) -> ([usize; N]) { + let w_ideal = (12.0 * sigma * sigma / N as f32 + 1.0).sqrt(); + let mut w = w_ideal as usize; + w -= 1 - w & 1; + + let mut m = ((w * w + 4 * w + 3) * N) as f32; + m -= 12.0 * sigma * sigma; + m *= 0.25; + m /= (w + 1) as f32; + let m = m.round() as usize; + + let mut result = [0; N]; + for (i, value) in result.iter_mut().enumerate() { + *value = (if i < m { w - 1 } else { w + 1 }) / 2; + } + result +} + +/// Blur an image with 3 box filter passes. The result will be written to the src slice, while the +/// buf slice is used as a scratch space. +pub fn approximate_gauss_blur( + src: &mut [f32], + buf: &mut [f32], + width: usize, + height: usize, + sigma: f32, + decay: f32, +) { + let boxes = boxes_for_gaussian::<3>(sigma); + box_blur(src, buf, width, height, boxes[0], 1.0); + box_blur(src, buf, width, height, boxes[1], 1.0); + box_blur(src, buf, width, height, boxes[2], decay); +} + +/// Perform one pass of the 2D box filter of the given radius. The result will be written to the src +/// slice, while the buf slice is used as a scratch space. +fn box_blur( + src: &mut [f32], + buf: &mut [f32], + width: usize, + height: usize, + radius: usize, + decay: f32, +) { + box_blur_h(src, buf, width, height, radius, 1.0); + box_blur_v(buf, src, width, height, radius, decay); +} + +/// Perform one pass of the 1D box filter of the given radius along x axis. Applies the decay factor +/// to the destination buffer. +fn box_blur_h( + src: &[f32], + dst: &mut [f32], + width: usize, + height: usize, + radius: usize, + decay: f32, +) { + let weight = decay / (2 * radius + 1) as f32; + + // TODO: Parallelize with rayon + for i in 0..height { + // First we build a value for the beginning of each row. We assume periodic boundary + // conditions, so we need to push the left index to the opposite side of the row. + let mut value = src[(i + 1) * width - radius - 1]; + for j in 0..radius { + value += src[(i + 1) * width - radius + j] + src[i * width + j]; + } + // At this point "value" contains the unweighted sum for the right-most row element. + + for current_id in i * width..(i + 1) * width { + let left_id = ((current_id + width - radius - 1) & (width - 1)) + i * width; + let right_id = ((current_id + radius) & (width - 1)) + i * width; + value += src[right_id] - src[left_id]; + dst[current_id] = value * weight; + } + } +} + +/// Perform one pass of the 1D box filter of the given radius along y axis. Applies the decay factor +/// to the destination buffer. +fn box_blur_v( + src: &[f32], + dst: &mut [f32], + width: usize, + height: usize, + radius: usize, + decay: f32, +) { + let weight = decay / (2 * radius + 1) as f32; + + // TODO: Parallelize with rayon + for i in 0..width { + // First we build a value for the beginning of each column. We assume periodic boundary + // conditions, so we need to push the bottom index to the opposite side of the column. + let mut value = src[i + (height - radius - 1) * width]; + for j in 0..radius { + value += src[i + (height - radius + j) * width] + src[i + j * width]; + } + // At this point "value" contains the unweighted sum for the top-most column element. + + for current_id in (i..i + height * width).step_by(width) { + let bottom_id = (current_id + (height - radius - 1) * width) & (width * height - 1); + let top_id = (current_id + radius * width) & (width * height - 1); + value += src[top_id] - src[bottom_id]; + dst[current_id] = value * weight; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_blur() { + let src = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]; + let mut dst = vec![0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]; + box_blur_v(&src, &mut dst, 2, 4, 1, 1.0); + println!("Out: {:?}", dst); + } +} diff --git a/src/grid.rs b/src/grid.rs new file mode 100644 index 0000000..37b93cc --- /dev/null +++ b/src/grid.rs @@ -0,0 +1,95 @@ +use crate::blur::approximate_gauss_blur; +use rand::{distributions::Uniform, Rng}; + +/// A 2D grid with a scalar value per each grid block. +#[derive(Debug)] +pub struct Grid { + width: usize, + height: usize, + data: Vec, + buf: Vec, +} + +#[inline(always)] +fn is_power_of_two(x: usize) -> bool { + (x & (x - 1)) == 0 +} + +impl Grid { + /// Create a new grid filled with random floats in the [0.0..1.0) range. + pub fn new(width: usize, height: usize) -> Self { + if !is_power_of_two(width) || !is_power_of_two(height) { + panic!("Grid dimensitions must be a power of two."); + } + let rng = rand::thread_rng(); + let range = Uniform::from(0.0..1.0); + let data = rng.sample_iter(range).take(width * height).collect(); + + Grid { + width, + height, + data, + buf: vec![0.0; width * height], + } + } + + /// Truncate x and y and return a corresponding index into the data slice. + fn index(&self, x: f32, y: f32) -> usize { + let i = (x as usize + self.width) & (self.width - 1); + let j = (y as usize + self.height) & (self.height - 1); + j * self.width + i + } + + /// Get the data value at a given position. The implementation effectively treats data as + /// periodic, hence any finite position will produce a value. + pub fn get(&self, x: f32, y: f32) -> f32 { + self.data[self.index(x, y)] + } + + /// Get the buffer value at a given position. The implementation effectively treats data as + /// periodic, hence any finite position will produce a value. + pub fn get_buf(&self, x: f32, y: f32) -> f32 { + self.buf[self.index(x, y)] + } + + /// Add a value to the grid data at a given position. + pub fn add(&mut self, x: f32, y: f32, value: f32) { + let idx = self.index(x, y); + self.data[idx] += value + } + + /// Diffuse grid data and apply a decay multiplier. + pub fn diffuse(&mut self, radius: usize, decay_factor: f32) { + approximate_gauss_blur( + &mut self.data, + &mut self.buf, + self.width, + self.height, + radius as f32, + decay_factor, + ); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[should_panic] + fn test_grid_new_panics() { + let _ = Grid::new(5, 5); + } + + #[test] + fn test_grid_new() { + let grid = Grid::new(8, 8); + assert_eq!(grid.index(0.5, 0.6), 0); + assert_eq!(grid.index(1.5, 0.6), 1); + assert_eq!(grid.index(0.5, 1.6), 8); + assert_eq!(grid.index(2.5, 0.6), 2); + assert_eq!(grid.index(2.5, 1.6), 10); + assert_eq!(grid.index(7.9, 7.9), 63); + assert_eq!(grid.index(-0.5, -0.6), 0); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..b58ee81 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,8 @@ +mod blur; +mod grid; +mod model; + +fn main() { + let boxes = blur::boxes_for_gaussian::<3>(2.5); + println!("boxes: {:?}", boxes); +} diff --git a/src/model.rs b/src/model.rs new file mode 100644 index 0000000..d4ed288 --- /dev/null +++ b/src/model.rs @@ -0,0 +1,32 @@ +use crate::grid::Grid; + +/// A single Physarum agent. The x and y positions are continuous, hence we use floating point +/// numbers instead of integers. +#[derive(Debug)] +pub struct Agent { + pub x: f32, + pub y: f32, + pub angle: f32, +} + +/// A model configuration. We make it into a separate type, because we will eventually have multiple +/// configurations in one model. +#[derive(Debug)] +pub struct PopulationConfig { + pub sensor_angle: f32, + pub sensor_distance: f32, + pub rotation_angle: f32, + pub step_distance: f32, + pub decay_factor: f32, +} + +impl PopulationConfig {} + +/// Top-level simulation class. +#[derive(Debug)] +pub struct Model { + grid: Grid, + agents: Vec, + + config: PopulationConfig, +}