From 738e22bd4ad5f232fbebf39c50c2294bc15c26c3 Mon Sep 17 00:00:00 2001 From: CrispyPin Date: Fri, 3 May 2024 16:51:20 +0200 Subject: [PATCH] save & load rulesets --- .gitignore | 1 + Cargo.lock | 249 ++++++++++++++++++++++++++++++++++++++++++--- petri/Cargo.toml | 2 + petri/src/lib.rs | 14 ++- uscope/Cargo.toml | 6 +- uscope/src/main.rs | 77 +++++++++++--- 6 files changed, 320 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index ea8c4bf..6c93088 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +*.json diff --git a/Cargo.lock b/Cargo.lock index 6c19a55..4a4417f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,6 +23,10 @@ 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" @@ -104,6 +108,7 @@ dependencies = [ "cfg-if", "getrandom", "once_cell", + "serde", "version_check", "zerocopy", ] @@ -192,6 +197,12 @@ 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" @@ -651,6 +662,22 @@ 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" @@ -661,8 +688,8 @@ dependencies = [ "block", "cocoa-foundation", "core-foundation", - "core-graphics", - "foreign-types", + "core-graphics 0.23.2", + "foreign-types 0.5.0", "libc", "objc", ] @@ -763,6 +790,19 @@ 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" @@ -772,7 +812,7 @@ dependencies = [ "bitflags 1.3.2", "core-foundation", "core-graphics-types", - "foreign-types", + "foreign-types 0.5.0", "libc", ] @@ -821,6 +861,12 @@ 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" @@ -848,6 +894,27 @@ 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" @@ -885,6 +952,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10" dependencies = [ "bytemuck", + "serde", ] [[package]] @@ -894,7 +962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "020e2ccef6bbcec71dbc542f7eed64a5846fc3076727f5746da8fd307c91bab2" dependencies = [ "bytemuck", - "cocoa", + "cocoa 0.25.0", "document-features", "egui", "egui-wgpu", @@ -932,6 +1000,7 @@ dependencies = [ "epaint", "log", "nohash-hasher", + "serde", ] [[package]] @@ -985,6 +1054,12 @@ 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" @@ -992,6 +1067,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4c3a552cfca14630702449d35f41c84a0d15963273771c6059175a803620f3f" dependencies = [ "bytemuck", + "serde", ] [[package]] @@ -1015,6 +1091,17 @@ 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" @@ -1029,6 +1116,7 @@ dependencies = [ "log", "nohash-hasher", "parking_lot", + "serde", ] [[package]] @@ -1146,6 +1234,15 @@ 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" @@ -1153,7 +1250,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared", + "foreign-types-shared 0.3.1", ] [[package]] @@ -1167,6 +1264,12 @@ 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" @@ -1537,6 +1640,12 @@ 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" @@ -1631,6 +1740,16 @@ 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" @@ -1716,7 +1835,7 @@ dependencies = [ "bitflags 2.5.0", "block", "core-graphics-types", - "foreign-types", + "foreign-types 0.5.0", "log", "objc", "paste", @@ -1752,6 +1871,27 @@ 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" @@ -1841,6 +1981,17 @@ 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" @@ -1947,6 +2098,15 @@ 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" @@ -1959,7 +2119,7 @@ version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" dependencies = [ - "libredox", + "libredox 0.0.2", ] [[package]] @@ -2027,6 +2187,8 @@ name = "petri" version = "0.1.0" dependencies = [ "rand", + "serde", + "serde_json", ] [[package]] @@ -2196,6 +2358,15 @@ 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" @@ -2235,6 +2406,17 @@ 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" @@ -2303,6 +2485,12 @@ 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" @@ -2339,24 +2527,35 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.199" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" 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" @@ -2743,8 +2942,12 @@ name = "uscope" version = "0.1.0" dependencies = [ "eframe", + "egui", + "native-dialog", "petri", "rand", + "serde", + "serde_json", ] [[package]] @@ -2987,6 +3190,16 @@ 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" @@ -3089,6 +3302,18 @@ 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" @@ -3397,7 +3622,7 @@ dependencies = [ "calloop", "cfg_aliases", "core-foundation", - "core-graphics", + "core-graphics 0.23.2", "cursor-icon", "icrate", "js-sys", diff --git a/petri/Cargo.toml b/petri/Cargo.toml index b027842..9fb2237 100644 --- a/petri/Cargo.toml +++ b/petri/Cargo.toml @@ -7,3 +7,5 @@ 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 4076dc3..5370873 100644 --- a/petri/src/lib.rs +++ b/petri/src/lib.rs @@ -1,8 +1,9 @@ use rand::prelude::*; +use serde::{Deserialize, Serialize}; pub const CHUNK_SIZE: usize = 32; -#[derive(Default, Debug, PartialEq, Clone, Copy)] +#[derive(Default, Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] pub struct Cell(pub u16); #[derive(Debug)] @@ -16,9 +17,10 @@ pub struct Chunk { pub contents: Box<[[Cell; CHUNK_SIZE]; CHUNK_SIZE]>, } -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct Rule { base: SubRule, + #[serde(skip)] variants: Vec, pub enabled: bool, // probability: u8 @@ -27,7 +29,7 @@ pub struct Rule { pub rotate: bool, } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] struct SubRule { width: usize, height: usize, @@ -287,6 +289,12 @@ impl Dish { } } + pub fn update_rules(&mut self) { + for rule in &mut self.rules { + rule.generate_variants(); + } + } + pub fn fire_blindly(&mut self) { if self.rules.is_empty() { return; diff --git a/uscope/Cargo.toml b/uscope/Cargo.toml index c5f5ad4..61ab251 100644 --- a/uscope/Cargo.toml +++ b/uscope/Cargo.toml @@ -7,5 +7,9 @@ edition = "2021" [dependencies] petri = { path = "../petri" } -eframe = "0.27.2" +eframe = "0.27" +egui = { version = "*", features = ["serde"] } 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 c20f6f7..c40866d 100644 --- a/uscope/src/main.rs +++ b/uscope/src/main.rs @@ -1,3 +1,8 @@ +use std::{ + fs::{self, File}, + io::Write, +}; + use eframe::{ egui::{ CentralPanel, Color32, Painter, Pos2, Rect, ScrollArea, Sense, SidePanel, Slider, Ui, Vec2, @@ -5,12 +10,16 @@ use eframe::{ epaint::Hsva, NativeOptions, }; -use petri::{Cell, Chunk, Dish, Rule, CHUNK_SIZE}; +use native_dialog::FileDialog; use rand::prelude::*; +use serde::{Deserialize, Serialize}; + +use petri::{Cell, Chunk, Dish, Rule, CHUNK_SIZE}; +use serde_json::{json, Value}; fn main() { eframe::run_native( - "V3 World Editor", + "µscope", NativeOptions::default(), Box::new(|_cc| Box::new(UScope::new(_cc))), ) @@ -23,10 +32,10 @@ struct UScope { brush: Cell, speed: usize, show_grid: bool, - celltypes: Vec, + cell_types: Vec, } -#[derive(Default, Debug)] +#[derive(Default, Debug, Serialize, Deserialize)] pub struct CellData { name: String, color: Color32, @@ -39,12 +48,46 @@ impl UScope { speed: 250, show_grid: false, brush: Cell(1), - celltypes: vec![ + cell_types: 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, + }); + 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 rules = serde_json::from_value(data["rules"].clone()).ok()?; + self.cell_types = cell_types; + self.dish.rules = rules; + self.dish.update_rules(); + } + Some(()) + } } impl eframe::App for UScope { @@ -58,13 +101,18 @@ 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"); - if ui.button("fill").clicked() { - self.dish.chunk.contents.fill([self.brush; CHUNK_SIZE]); - } + ui.horizontal(|ui| { + if ui.button("Save").clicked() { + self.save_universe(); + } + if ui.button("Open").clicked() { + self.open_universe(); + } + }); ui.separator(); ui.heading("Cells"); - for (i, cell) in self.celltypes.iter_mut().enumerate() { + for (i, cell) in self.cell_types.iter_mut().enumerate() { ui.horizontal(|ui| { ui.set_width(120.); ui.radio_value(&mut self.brush.0, i as u16, ""); @@ -78,8 +126,11 @@ 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.celltypes.len()); - self.celltypes.push(CellData { name, color }) + 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]); } ui.separator(); @@ -88,7 +139,7 @@ impl eframe::App for UScope { let mut to_remove = None; for (i, rule) in self.dish.rules.iter_mut().enumerate() { ui.separator(); - rule_editor(ui, rule, &self.celltypes); + rule_editor(ui, rule, &self.cell_types); if ui.button("delete").clicked() { to_remove = Some(i); } @@ -105,7 +156,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.celltypes, self.show_grid); + paint_chunk(painter, &self.dish.chunk, &self.cell_types, self.show_grid); let rect = ui.allocate_rect(bounds, Sense::click_and_drag()); if let Some(pos) = rect.interact_pointer_pos() {