diff --git a/.gitignore b/.gitignore index 6c93088..ea8c4bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ /target -*.json diff --git a/Cargo.lock b/Cargo.lock index 4a4417f..6c19a55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,10 +23,6 @@ name = "accesskit" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74a4b14f3d99c1255dcba8f45621ab1a2e7540a0009652d33989005a4d0bfc6b" -dependencies = [ - "enumn", - "serde", -] [[package]] name = "accesskit_consumer" @@ -108,7 +104,6 @@ dependencies = [ "cfg-if", "getrandom", "once_cell", - "serde", "version_check", "zerocopy", ] @@ -197,12 +192,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" -[[package]] -name = "ascii" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" - [[package]] name = "ash" version = "0.37.3+1.3.251" @@ -662,22 +651,6 @@ dependencies = [ "error-code", ] -[[package]] -name = "cocoa" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics 0.22.3", - "foreign-types 0.3.2", - "libc", - "objc", -] - [[package]] name = "cocoa" version = "0.25.0" @@ -688,8 +661,8 @@ dependencies = [ "block", "cocoa-foundation", "core-foundation", - "core-graphics 0.23.2", - "foreign-types 0.5.0", + "core-graphics", + "foreign-types", "libc", "objc", ] @@ -790,19 +763,6 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" -[[package]] -name = "core-graphics" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types 0.3.2", - "libc", -] - [[package]] name = "core-graphics" version = "0.23.2" @@ -812,7 +772,7 @@ dependencies = [ "bitflags 1.3.2", "core-foundation", "core-graphics-types", - "foreign-types 0.5.0", + "foreign-types", "libc", ] @@ -861,12 +821,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "cty" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" - [[package]] name = "cursor-icon" version = "1.1.0" @@ -894,27 +848,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "dispatch" version = "0.2.0" @@ -952,7 +885,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10" dependencies = [ "bytemuck", - "serde", ] [[package]] @@ -962,7 +894,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "020e2ccef6bbcec71dbc542f7eed64a5846fc3076727f5746da8fd307c91bab2" dependencies = [ "bytemuck", - "cocoa 0.25.0", + "cocoa", "document-features", "egui", "egui-wgpu", @@ -1000,7 +932,6 @@ dependencies = [ "epaint", "log", "nohash-hasher", - "serde", ] [[package]] @@ -1054,12 +985,6 @@ dependencies = [ "winit", ] -[[package]] -name = "either" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" - [[package]] name = "emath" version = "0.27.2" @@ -1067,7 +992,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4c3a552cfca14630702449d35f41c84a0d15963273771c6059175a803620f3f" dependencies = [ "bytemuck", - "serde", ] [[package]] @@ -1091,17 +1015,6 @@ dependencies = [ "syn 2.0.60", ] -[[package]] -name = "enumn" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.60", -] - [[package]] name = "epaint" version = "0.27.2" @@ -1116,7 +1029,6 @@ dependencies = [ "log", "nohash-hasher", "parking_lot", - "serde", ] [[package]] @@ -1234,15 +1146,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared 0.1.1", -] - [[package]] name = "foreign-types" version = "0.5.0" @@ -1250,7 +1153,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared 0.3.1", + "foreign-types-shared", ] [[package]] @@ -1264,12 +1167,6 @@ dependencies = [ "syn 2.0.60", ] -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -1640,12 +1537,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - [[package]] name = "jni" version = "0.21.1" @@ -1740,16 +1631,6 @@ dependencies = [ "redox_syscall 0.4.1", ] -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.5.0", - "libc", -] - [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -1835,7 +1716,7 @@ dependencies = [ "bitflags 2.5.0", "block", "core-graphics-types", - "foreign-types 0.5.0", + "foreign-types", "log", "objc", "paste", @@ -1871,27 +1752,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "native-dialog" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bbf55edb2747e4e4b3a9cd3989194b88aae32274b4422635dcf98aa6e84197b" -dependencies = [ - "ascii", - "block", - "cocoa 0.24.1", - "dirs-next", - "objc", - "objc-foundation", - "objc_id", - "once_cell", - "raw-window-handle 0.4.3", - "thiserror", - "wfd", - "which", - "winapi", -] - [[package]] name = "ndk" version = "0.8.0" @@ -1981,17 +1841,6 @@ dependencies = [ "objc_exception", ] -[[package]] -name = "objc-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" -dependencies = [ - "block", - "objc", - "objc_id", -] - [[package]] name = "objc-sys" version = "0.2.0-beta.2" @@ -2098,15 +1947,6 @@ dependencies = [ "cc", ] -[[package]] -name = "objc_id" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" -dependencies = [ - "objc", -] - [[package]] name = "once_cell" version = "1.19.0" @@ -2119,7 +1959,7 @@ version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" dependencies = [ - "libredox 0.0.2", + "libredox", ] [[package]] @@ -2187,8 +2027,6 @@ name = "petri" version = "0.1.0" dependencies = [ "rand", - "serde", - "serde_json", ] [[package]] @@ -2358,15 +2196,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "raw-window-handle" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" -dependencies = [ - "cty", -] - [[package]] name = "raw-window-handle" version = "0.5.2" @@ -2406,17 +2235,6 @@ dependencies = [ "bitflags 2.5.0", ] -[[package]] -name = "redox_users" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" -dependencies = [ - "getrandom", - "libredox 0.1.3", - "thiserror", -] - [[package]] name = "regex" version = "1.10.4" @@ -2485,12 +2303,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "ryu" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" - [[package]] name = "same-file" version = "1.0.6" @@ -2527,35 +2339,24 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.200" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.200" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", "syn 2.0.60", ] -[[package]] -name = "serde_json" -version = "1.0.116" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" -dependencies = [ - "itoa", - "ryu", - "serde", -] - [[package]] name = "serde_repr" version = "0.1.19" @@ -2942,12 +2743,8 @@ name = "uscope" version = "0.1.0" dependencies = [ "eframe", - "egui", - "native-dialog", "petri", "rand", - "serde", - "serde_json", ] [[package]] @@ -3190,16 +2987,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wfd" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e713040b67aae5bf1a0ae3e1ebba8cc29ab2b90da9aa1bff6e09031a8a41d7a8" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "wgpu" version = "0.19.4" @@ -3302,18 +3089,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix 0.38.34", -] - [[package]] name = "widestring" version = "1.1.0" @@ -3622,7 +3397,7 @@ dependencies = [ "calloop", "cfg_aliases", "core-foundation", - "core-graphics 0.23.2", + "core-graphics", "cursor-icon", "icrate", "js-sys", diff --git a/petri/Cargo.toml b/petri/Cargo.toml index 9fb2237..b027842 100644 --- a/petri/Cargo.toml +++ b/petri/Cargo.toml @@ -7,5 +7,3 @@ edition = "2021" [dependencies] rand = "0.8.5" -serde = "1.0.200" -serde_json = "1.0.116" diff --git a/petri/src/lib.rs b/petri/src/lib.rs index 2c1428f..4076dc3 100644 --- a/petri/src/lib.rs +++ b/petri/src/lib.rs @@ -1,16 +1,14 @@ use rand::prelude::*; -use serde::{Deserialize, Serialize}; pub const CHUNK_SIZE: usize = 32; -#[derive(Default, Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] +#[derive(Default, Debug, PartialEq, Clone, Copy)] pub struct Cell(pub u16); #[derive(Debug)] pub struct Dish { pub chunk: Chunk, pub rules: Vec, - pub cell_groups: Vec>>, } #[derive(Debug)] @@ -18,10 +16,9 @@ pub struct Chunk { pub contents: Box<[[Cell; CHUNK_SIZE]; CHUNK_SIZE]>, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug)] pub struct Rule { base: SubRule, - #[serde(skip)] variants: Vec, pub enabled: bool, // probability: u8 @@ -30,35 +27,11 @@ pub struct Rule { pub rotate: bool, } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq)] struct SubRule { width: usize, height: usize, - contents: Vec<(RuleCellFrom, RuleCellTo)>, -} - -#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] -pub enum RuleCellFrom { - /// matches anything - #[default] - Any, - /// matches one cell type - One(Cell), - /// matches anything defined in the group referenced by this index - Group(usize), -} - -#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] -pub enum RuleCellTo { - /// don't modify this cell - #[default] - None, - /// set to this cell - One(Cell), - /// randomly choose from the group - GroupRandom(usize), - /// copy the cell from the corresponding input position - Copy(usize), + contents: Vec<(Option, Option)>, } impl SubRule { @@ -66,36 +39,36 @@ impl SubRule { Self { width: 1, height: 1, - contents: vec![Default::default()], + contents: vec![(None, None)], } } - fn get(&self, x: usize, y: usize) -> (RuleCellFrom, RuleCellTo) { + fn get(&self, x: usize, y: usize) -> (Option, Option) { if x >= self.width || y >= self.height { - Default::default() + (None, None) } else { self.contents[x + self.width * y].clone() } } - fn get_mut(&mut self, x: usize, y: usize) -> &mut (RuleCellFrom, RuleCellTo) { + fn get_mut(&mut self, x: usize, y: usize) -> &mut (Option, Option) { assert!(x < self.width || y < self.height); &mut self.contents[x + self.width * y] } - fn set_both(&mut self, x: usize, y: usize, cells: (RuleCellFrom, RuleCellTo)) { + fn set_both(&mut self, x: usize, y: usize, cells: (Option, Option)) { if x < self.width && y < self.height { self.contents[x + self.width * y] = cells; } } - fn set_from(&mut self, x: usize, y: usize, cell: RuleCellFrom) { + fn set_from(&mut self, x: usize, y: usize, cell: Option) { if x < self.width && y < self.height { self.contents[x + self.width * y].0 = cell; } } - fn set_to(&mut self, x: usize, y: usize, cell: RuleCellTo) { + fn set_to(&mut self, x: usize, y: usize, cell: Option) { if x < self.width && y < self.height { self.contents[x + self.width * y].1 = cell; } @@ -124,20 +97,20 @@ impl Rule { } } - pub fn get(&self, x: usize, y: usize) -> (RuleCellFrom, RuleCellTo) { + pub fn get(&self, x: usize, y: usize) -> (Option, Option) { self.base.get(x, y) } - pub fn get_mut(&mut self, x: usize, y: usize) -> &mut (RuleCellFrom, RuleCellTo) { + pub fn get_mut(&mut self, x: usize, y: usize) -> &mut (Option, Option) { self.base.get_mut(x, y) } - pub fn set_from(&mut self, x: usize, y: usize, cell: RuleCellFrom) { + pub fn set_from(&mut self, x: usize, y: usize, cell: Option) { self.base.set_from(x, y, cell); self.generate_variants(); } - pub fn set_to(&mut self, x: usize, y: usize, cell: RuleCellTo) { + pub fn set_to(&mut self, x: usize, y: usize, cell: Option) { self.base.set_to(x, y, cell); self.generate_variants(); } @@ -158,7 +131,7 @@ impl Rule { if new_width < 1 || new_height < 1 { return; } - let mut new_contents = vec![Default::default(); new_width * new_height]; + let mut new_contents = vec![(None, None); new_width * new_height]; for nx in 0..new_width { let oldx = nx.wrapping_add_signed(dx); @@ -281,8 +254,8 @@ impl Dish { width: 1, height: 2, contents: vec![ - (RuleCellFrom::One(Cell(1)), RuleCellTo::One(Cell(0))), - (RuleCellFrom::One(Cell(0)), RuleCellTo::One(Cell(1))), + (Some(Cell(1)), Some(Cell(0))), + (Some(Cell(0)), Some(Cell(1))), ], }, ..Rule::new() @@ -293,10 +266,10 @@ impl Dish { width: 2, height: 2, contents: vec![ - (RuleCellFrom::One(Cell(1)), RuleCellTo::One(Cell(0))), - (RuleCellFrom::Any, RuleCellTo::None), - (RuleCellFrom::One(Cell(1)), RuleCellTo::None), - (RuleCellFrom::One(Cell(0)), RuleCellTo::One(Cell(1))), + (Some(Cell(1)), Some(Cell(0))), + (None, None), + (Some(Cell(1)), None), + (Some(Cell(0)), Some(Cell(1))), ], }, flip_h: true, @@ -311,13 +284,6 @@ impl Dish { Self { chunk: Chunk::new().fill_random(), rules: default_rules, - cell_groups: vec![vec![None, Some(Cell(1))]], - } - } - - pub fn update_rules(&mut self) { - for rule in &mut self.rules { - rule.generate_variants(); } } @@ -325,6 +291,8 @@ impl Dish { if self.rules.is_empty() { return; } + let x = random::() % CHUNK_SIZE; + let y = random::() % CHUNK_SIZE; let enabled_rules = self .rules .iter() @@ -336,82 +304,47 @@ impl Dish { } let rule = random::() % enabled_rules.len(); let rule = enabled_rules[rule]; - self.fire_rule(rule); + self.fire_rule(rule, x, y); } - fn fire_rule(&mut self, rule_index: usize) { + fn fire_rule(&mut self, rule_index: usize, x: usize, y: usize) { let rule = &self.rules[rule_index]; - let variant_index = random::() % rule.variants.len(); - let variant = &rule.variants[variant_index].clone(); - let border_x = variant.width - 1; - let border_y = variant.height - 1; - let x = ((random::() % (CHUNK_SIZE + border_x)) as isize) - .wrapping_sub_unsigned(border_x); - let y = ((random::() % (CHUNK_SIZE + border_y)) as isize) - .wrapping_sub_unsigned(border_y); - - if !self.subrule_matches(x, y, variant) { + // find matching variants + let mut matching_variants = Vec::new(); + for (i, v) in rule.variants.iter().enumerate() { + if self.subrule_matches(x, y, v) { + matching_variants.push(i); + } + } + if matching_variants.is_empty() { return; } + let variant_index = random::() % matching_variants.len(); + let variant = rule.variants[matching_variants[variant_index]].clone(); + let width = variant.width; let height = variant.height; - let mut old_state = Vec::new(); - for dy in 0..height { - for dx in 0..width { - old_state.push( - self.get_cell((x as usize).wrapping_add(dx), (y as usize).wrapping_add(dy)), - ); - } - } - for dx in 0..width { for dy in 0..height { - let px = x.wrapping_add_unsigned(dx) as usize; - let py = y.wrapping_add_unsigned(dy) as usize; - match variant.get(dx, dy).1 { - RuleCellTo::One(rule_cell) => { - self.set_cell(px, py, rule_cell.clone()); - } - RuleCellTo::GroupRandom(group_id) => { - let group = &self.cell_groups[group_id]; - let i = random::() % group.len(); - let cell = group[i]; - if let Some(cell) = cell { - self.set_cell(px, py, cell); - } - } - RuleCellTo::Copy(index) => { - let cell = old_state[index]; - if let Some(cell) = cell { - // if the copy source is outside the world, do nothing - self.set_cell(px, py, cell); - } - } - RuleCellTo::None => (), + let x = x + dx; + let y = y + dy; + if let Some(rule_cell) = variant.get(dx, dy).1 { + self.set_cell(x, y, rule_cell.clone()); } } } } - fn subrule_matches(&self, x: isize, y: isize, subrule: &SubRule) -> bool { + fn subrule_matches(&self, x: usize, y: usize, subrule: &SubRule) -> bool { for dx in 0..subrule.width { for dy in 0..subrule.height { - let x = x.wrapping_add_unsigned(dx) as usize; - let y = y.wrapping_add_unsigned(dy) as usize; - let cell = self.get_cell(x, y); - match subrule.get(dx, dy).0 { - RuleCellFrom::One(rule_cell) => { - if cell != Some(rule_cell) { - return false; - } + let x = x + dx; + let y = y + dy; + if let Some(rule_cell) = subrule.get(dx, dy).0 { + if self.get_cell(x, y) != Some(rule_cell) { + return false; } - RuleCellFrom::Group(group_id) => { - if !self.cell_groups[group_id].contains(&cell) { - return false; - } - } - RuleCellFrom::Any => (), } } } diff --git a/uscope/Cargo.toml b/uscope/Cargo.toml index 61ab251..c5f5ad4 100644 --- a/uscope/Cargo.toml +++ b/uscope/Cargo.toml @@ -7,9 +7,5 @@ edition = "2021" [dependencies] petri = { path = "../petri" } -eframe = "0.27" -egui = { version = "*", features = ["serde"] } +eframe = "0.27.2" rand = "0.8.5" -serde = "1.0.200" -serde_json = "1.0.116" -native-dialog = "0.6.4" diff --git a/uscope/src/main.rs b/uscope/src/main.rs index 9df2a37..c20f6f7 100644 --- a/uscope/src/main.rs +++ b/uscope/src/main.rs @@ -1,8 +1,3 @@ -use std::{ - fs::{self, File}, - io::Write, -}; - use eframe::{ egui::{ CentralPanel, Color32, Painter, Pos2, Rect, ScrollArea, Sense, SidePanel, Slider, Ui, Vec2, @@ -10,17 +5,12 @@ use eframe::{ epaint::Hsva, NativeOptions, }; -use egui::PointerButton; -use native_dialog::FileDialog; +use petri::{Cell, Chunk, Dish, Rule, CHUNK_SIZE}; use rand::prelude::*; -use serde::{Deserialize, Serialize}; - -use petri::{Cell, Chunk, Dish, Rule, RuleCellFrom, RuleCellTo, CHUNK_SIZE}; -use serde_json::{json, Value}; fn main() { eframe::run_native( - "µscope", + "V3 World Editor", NativeOptions::default(), Box::new(|_cc| Box::new(UScope::new(_cc))), ) @@ -33,10 +23,10 @@ struct UScope { brush: Cell, speed: usize, show_grid: bool, - cell_types: Vec, + celltypes: Vec, } -#[derive(Default, Debug, Serialize, Deserialize)] +#[derive(Default, Debug)] pub struct CellData { name: String, color: Color32, @@ -46,52 +36,15 @@ impl UScope { fn new(_cc: &eframe::CreationContext<'_>) -> Self { Self { dish: Dish::new(), - speed: 500, + speed: 250, show_grid: false, brush: Cell(1), - cell_types: vec![ + celltypes: vec![ CellData::new("air", 0, 0, 0), CellData::new("pink_sand", 255, 147, 219), ], } } - - fn save_universe(&self) -> Option<()> { - if let Ok(Some(path)) = FileDialog::new() - .set_filename("universe_1.json") - .add_filter("JSON", &["json"]) - .show_save_single_file() - { - let out = json!({ - "cell_types": self.cell_types, - "rules": self.dish.rules, - "groups": self.dish.cell_groups, - }); - let out = serde_json::to_string(&out).ok()?; - let mut file = File::create(path).ok()?; - file.write_all(out.as_bytes()).ok()?; - } - Some(()) - } - - fn open_universe(&mut self) -> Option<()> { - if let Ok(Some(path)) = FileDialog::new() - .set_filename("universe_1.json") - .add_filter("JSON", &["json"]) - .show_open_single_file() - { - let s = fs::read_to_string(path).ok()?; - let data: Value = serde_json::from_str(&s).ok()?; - let cell_types = serde_json::from_value(data["cell_types"].clone()).ok()?; - let groups = serde_json::from_value(data["groups"].clone()).ok()?; - let rules = serde_json::from_value(data["rules"].clone()).ok()?; - self.cell_types = cell_types; - self.dish.rules = rules; - self.dish.cell_groups = groups; - self.dish.update_rules(); - } - Some(()) - } } impl eframe::App for UScope { @@ -105,18 +58,13 @@ impl eframe::App for UScope { ui.label("speed"); ui.add(Slider::new(&mut self.speed, 0..=5000)); ui.checkbox(&mut self.show_grid, "show grid"); - ui.horizontal(|ui| { - if ui.button("Save").clicked() { - self.save_universe(); - } - if ui.button("Open").clicked() { - self.open_universe(); - } - }); + if ui.button("fill").clicked() { + self.dish.chunk.contents.fill([self.brush; CHUNK_SIZE]); + } ui.separator(); ui.heading("Cells"); - for (i, cell) in self.cell_types.iter_mut().enumerate() { + for (i, cell) in self.celltypes.iter_mut().enumerate() { ui.horizontal(|ui| { ui.set_width(120.); ui.radio_value(&mut self.brush.0, i as u16, ""); @@ -130,49 +78,17 @@ impl eframe::App for UScope { let s = random::() * 0.5 + 0.5; let v = random::() * 0.5 + 0.5; let color = Hsva::new(h, s, v, 1.).into(); - let name = format!("cell #{}", self.cell_types.len()); - self.cell_types.push(CellData { name, color }) - } - if ui.button("fill").clicked() { - self.dish.chunk.contents.fill([self.brush; CHUNK_SIZE]); + let name = format!("cell #{}", self.celltypes.len()); + self.celltypes.push(CellData { name, color }) } ui.separator(); - ui.heading("Groups"); - for group in &mut self.dish.cell_groups { - let (rect, _response) = ui.allocate_exact_size(Vec2::splat(CSIZE), Sense::click()); - draw_group(ui, rect, group, &self.cell_types); - ui.menu_button("edit", |ui| { - let mut void = group.contains(&None); - if ui.checkbox(&mut void, "void").changed() { - if void { - group.push(None); - } else { - group.retain(|c| c.is_some()); - } - } - for (i, celldata) in self.cell_types.iter().enumerate() { - let mut included = group.contains(&Some(Cell(i as u16))); - if ui.checkbox(&mut included, &celldata.name).changed() { - if included { - group.push(Some(Cell(i as u16))); - } else { - group.retain(|c| c != &Some(Cell(i as u16))); - } - } - } - }); - } - if ui.button("add group").clicked() { - self.dish.cell_groups.push(Vec::new()); - } - ui.heading("Rules"); ScrollArea::vertical().show(ui, |ui| { let mut to_remove = None; for (i, rule) in self.dish.rules.iter_mut().enumerate() { ui.separator(); - rule_editor(ui, rule, &self.cell_types, &self.dish.cell_groups); + rule_editor(ui, rule, &self.celltypes); if ui.button("delete").clicked() { to_remove = Some(i); } @@ -189,7 +105,7 @@ impl eframe::App for UScope { CentralPanel::default().show(ctx, |ui| { let bounds = ui.available_rect_before_wrap(); let painter = ui.painter_at(bounds); - paint_chunk(painter, &self.dish.chunk, &self.cell_types, self.show_grid); + paint_chunk(painter, &self.dish.chunk, &self.celltypes, self.show_grid); let rect = ui.allocate_rect(bounds, Sense::click_and_drag()); if let Some(pos) = rect.interact_pointer_pos() { @@ -221,10 +137,8 @@ fn paint_chunk(painter: Painter, chunk: &Chunk, cells: &[CellData], grid: bool) } const CSIZE: f32 = 24.; -const RESIZE_BUTTON_WIDTH: f32 = 8.; - const OUTLINE: (f32, Color32) = (2., Color32::GRAY); -fn rule_editor(ui: &mut Ui, rule: &mut Rule, cells: &[CellData], groups: &[Vec>]) { +fn rule_editor(ui: &mut Ui, rule: &mut Rule, cells: &[CellData]) { ui.checkbox(&mut rule.enabled, "enable rule"); ui.horizontal(|ui| { ui.label("flip"); @@ -241,41 +155,29 @@ fn rule_editor(ui: &mut Ui, rule: &mut Rule, cells: &[CellData], groups: &[Vec, x: usize, y: usize, cells: &[CellData], - groups: &[Vec>], ) -> bool { let mut changed = false; let rect = Rect::from_min_size( @@ -373,150 +260,22 @@ fn rule_cell_edit_from( Vec2::splat(CSIZE), ); let aabb = ui.allocate_rect(rect, Sense::click()); - let cycle_colors = aabb.clicked_by(PointerButton::Primary); - let switch_type = aabb.clicked_by(PointerButton::Secondary); - - // draw - match rule { - RuleCellFrom::Any => (), - RuleCellFrom::One(cell) => { - let color = cells[cell.id()].color; - ui.painter() - .rect(rect.shrink(OUTLINE.0 / 2.), 0., color, OUTLINE); - } - RuleCellFrom::Group(group_id) => { - let group = &groups[*group_id]; - draw_group(ui, rect, group, cells); - } - } - // update - if cycle_colors { - match rule { - RuleCellFrom::Any => (), - RuleCellFrom::One(cell) => { - cell.0 += 1; - cell.0 %= cells.len() as u16; - changed = true; - } - RuleCellFrom::Group(group_id) => { - *group_id += 1; - *group_id %= groups.len(); - changed = true; - } - } - } - if switch_type { - changed = true; - match rule { - RuleCellFrom::Any => { - *rule = RuleCellFrom::One(Cell(0)); - } - RuleCellFrom::One(_) => { - *rule = RuleCellFrom::Group(0); - } - RuleCellFrom::Group(_) => { - *rule = RuleCellFrom::Any; - } - } - } - changed -} - -fn rule_cell_edit_to( - ui: &mut Ui, - origin: Pos2, - rule: &mut RuleCellTo, - (x, y): (usize, usize), - cells: &[CellData], - groups: &[Vec>], - (rule_width, rule_height): (usize, usize), - overlay_lines: &mut Vec<(Pos2, Pos2)>, -) -> bool { - let mut changed = false; - let rect = Rect::from_min_size( - origin + Vec2::from((x as f32, y as f32)) * CSIZE, - Vec2::splat(CSIZE), - ); - let aabb = ui.allocate_rect(rect, Sense::click()); - let cycle_colors = aabb.clicked_by(PointerButton::Primary); - let switch_type = aabb.clicked_by(PointerButton::Secondary); - - // draw - match rule { - RuleCellTo::None => (), - RuleCellTo::One(cell) => { - let color = cells[cell.id()].color; - ui.painter() - .rect(rect.shrink(OUTLINE.0 / 2.), 0., color, OUTLINE); - } - RuleCellTo::GroupRandom(group_id) => { - let group = &groups[*group_id]; - draw_group(ui, rect, group, cells); - } - RuleCellTo::Copy(index) => { - let this = rect.center(); - let x = *index % rule_width; - let y = *index / rule_width; - let target = origin + Vec2::from((x as f32, y as f32)) * CSIZE - - Vec2::X * (CSIZE * (rule_width as f32 + 1.) + RESIZE_BUTTON_WIDTH * 2.) - + Vec2::splat(CSIZE) * 0.5; - overlay_lines.push((this, target)); - } - } - - if cycle_colors { - match rule { - RuleCellTo::None => (), - RuleCellTo::One(cell) => { - cell.0 += 1; - cell.0 %= cells.len() as u16; - changed = true; - } - RuleCellTo::GroupRandom(group_id) => { - *group_id += 1; - *group_id %= groups.len(); - changed = true; - } - RuleCellTo::Copy(index) => { - *index = (*index + 1) % (rule_width * rule_height); - changed = true; - } - } - } - - if switch_type { - changed = true; - match rule { - RuleCellTo::None => { - *rule = RuleCellTo::One(Cell(0)); - } - RuleCellTo::One(_) => { - *rule = RuleCellTo::GroupRandom(0); - } - RuleCellTo::GroupRandom(_) => { - *rule = RuleCellTo::Copy(0); - } - RuleCellTo::Copy(_) => { - *rule = RuleCellTo::None; - } - } - } - changed -} - -fn draw_group(ui: &mut Ui, rect: Rect, group: &[Option], cells: &[CellData]) { - let group_size = group.len(); - let radius_per_color = (CSIZE * 0.7) / (group_size as f32); - for (i, cell) in group.iter().flatten().enumerate() { + if let Some(cell) = rule { let color = cells[cell.id()].color; - let radius = radius_per_color * ((group_size - i) as f32); - ui.painter_at(rect) - .circle_filled(rect.center(), radius, color); - } - if group.contains(&None) { - ui.painter_at(rect) - .line_segment([rect.min, rect.max], (1., Color32::WHITE)); + ui.painter() + .rect(rect.shrink(OUTLINE.0 / 2.), 0., color, OUTLINE); + if aabb.clicked() { + changed = true; + cell.0 += 1; + if cell.0 as usize == cells.len() { + *rule = None; + } + } + } else if aabb.clicked() { + *rule = Some(Cell(0)); + changed = true; } + changed } impl CellData {