diff --git a/petri/src/lib.rs b/petri/src/lib.rs index c2d82d5..73406ef 100644 --- a/petri/src/lib.rs +++ b/petri/src/lib.rs @@ -10,14 +10,7 @@ pub struct Cell(pub u16); pub struct Dish { pub chunk: Chunk, pub rules: Vec, - pub cell_groups: Vec, -} - -#[derive(Debug, Default, Serialize, Deserialize)] -pub struct CellGroup { - pub name: String, - pub void: bool, - pub cells: Vec, + pub cell_groups: Vec>>, } #[derive(Debug)] @@ -33,11 +26,12 @@ pub struct Rule { #[serde(skip)] variants: Vec, pub enabled: bool, + // probability: u8 + #[serde(alias = "flip_h")] pub flip_x: bool, + #[serde(alias = "flip_v")] pub flip_y: bool, pub rotate: bool, - #[serde(default)] - pub failrate: u8, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -132,7 +126,6 @@ impl Rule { flip_x: false, flip_y: false, rotate: false, - failrate: 0, } } @@ -338,11 +331,7 @@ impl Dish { Self { chunk: Chunk::new().fill_random(), rules: default_rules, - cell_groups: vec![CellGroup { - name: "empty".into(), - void: true, - cells: vec![Cell(0)], - }], + cell_groups: vec![vec![None, Some(Cell(1))]], } } @@ -385,11 +374,6 @@ impl Dish { return; } - let fail: u8 = random(); - if rule.failrate > fail { - return; - } - let width = variant.width; let height = variant.height; let mut old_state = Vec::new(); @@ -411,9 +395,11 @@ impl Dish { } RuleCellTo::GroupRandom(group_id) => { let group = &self.cell_groups[group_id]; - let i = random::() % group.cells.len(); - let cell = group.cells[i]; - self.set_cell(px, py, cell); + let i = random::() % group.len(); + let cell = group[i]; + if let Some(cell) = cell { + self.set_cell(px, py, cell); + } } RuleCellTo::Copy(x, y) => { let cell = old_state[x + y * variant.width]; @@ -441,12 +427,7 @@ impl Dish { } } RuleCellFrom::Group(group_id) => { - let group = &self.cell_groups[group_id]; - if let Some(cell) = cell { - if !group.cells.contains(&cell) { - return false; - } - } else if !group.void { + if !self.cell_groups[group_id].contains(&cell) { return false; } } diff --git a/uscope/src/main.rs b/uscope/src/main.rs index 65f4cdb..8daacb4 100644 --- a/uscope/src/main.rs +++ b/uscope/src/main.rs @@ -10,12 +10,12 @@ use eframe::{ epaint::Hsva, NativeOptions, }; -use egui::{collapsing_header::CollapsingState, DragValue, PointerButton}; +use egui::{collapsing_header::CollapsingState, PointerButton}; use native_dialog::FileDialog; use rand::prelude::*; use serde::{Deserialize, Serialize}; -use petri::{Cell, CellGroup, Chunk, Dish, Rule, RuleCellFrom, RuleCellTo, CHUNK_SIZE}; +use petri::{Cell, Chunk, Dish, Rule, RuleCellFrom, RuleCellTo, CHUNK_SIZE}; use serde_json::{json, Value}; fn main() { @@ -147,25 +147,29 @@ impl eframe::App for UScope { let (rect, _response) = ui.allocate_exact_size(Vec2::splat(CSIZE), Sense::click()); draw_group(ui, rect, group, &self.cell_types); - ui.horizontal(|ui| { - ui.menu_button("edit", |ui| { - ui.checkbox(&mut group.void, "void"); - for (i, celldata) in self.cell_types.iter().enumerate() { - let mut included = group.cells.contains(&Cell(i as u16)); - if ui.checkbox(&mut included, &celldata.name).changed() { - if included { - group.cells.push(Cell(i as u16)); - } else { - group.cells.retain(|c| c != &Cell(i as u16)); - } + 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))); } } - }); - ui.text_edit_singleline(&mut group.name); + } }); } if ui.button("add group").clicked() { - self.dish.cell_groups.push(CellGroup::default()); + self.dish.cell_groups.push(Vec::new()); } ui.heading("Rules"); @@ -173,6 +177,7 @@ impl eframe::App for UScope { let mut to_remove = None; let mut to_clone = None; for (i, rule) in self.dish.rules.iter_mut().enumerate() { + // ui.separator(); rule_editor( ui, rule, @@ -247,7 +252,7 @@ fn rule_editor( rule: &mut Rule, index: usize, cells: &[CellData], - groups: &[CellGroup], + groups: &[Vec>], to_remove: &mut Option, to_clone: &mut Option, ) { @@ -272,13 +277,10 @@ fn rule_editor( rule.generate_variants(); } }); - ui.horizontal(|ui| { - if ui.checkbox(&mut rule.rotate, "rotate").changed() { - rule.generate_variants(); - } - ui.label("fail rate"); - ui.add(DragValue::new(&mut rule.failrate)); - }); + if ui.checkbox(&mut rule.rotate, "rotate").changed() { + rule.generate_variants(); + } + let cells_y = rule.height(); let cells_x = rule.width(); let patt_width = CSIZE * cells_x as f32; @@ -406,7 +408,7 @@ fn rule_cell_edit_from( x: usize, y: usize, cells: &[CellData], - groups: &[CellGroup], + groups: &[Vec>], ) -> bool { let mut changed = false; let rect = Rect::from_min_size( @@ -469,7 +471,7 @@ fn rule_cell_edit_to( rule: &mut RuleCellTo, (x, y): (usize, usize), cells: &[CellData], - groups: &[CellGroup], + groups: &[Vec>], (rule_width, rule_height): (usize, usize), overlay_lines: &mut Vec<(Pos2, Pos2)>, ) -> bool { @@ -550,21 +552,23 @@ fn rule_cell_edit_to( changed } -fn draw_group(ui: &mut Ui, rect: Rect, group: &CellGroup, cells: &[CellData]) { - let group_size = group.cells.len(); +fn draw_group(ui: &mut Ui, rect: Rect, group: &[Option], cells: &[CellData]) { + let mut group_size = group.len(); + let has_void = group.contains(&None); + if has_void { + group_size -= 1; + } let radius_per_color = (CSIZE * 0.7) / (group_size as f32); - for (i, cell) in group.cells.iter().enumerate() { + for (i, cell) in group.iter().flatten().enumerate() { 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.void { + if has_void { ui.painter_at(rect) .line_segment([rect.min, rect.max], (1., Color32::WHITE)); } - ui.allocate_rect(rect, Sense::hover()) - .on_hover_text(&group.name); } impl CellData {