diff --git a/petri/src/lib.rs b/petri/src/lib.rs index bbf8f76..2c1428f 100644 --- a/petri/src/lib.rs +++ b/petri/src/lib.rs @@ -10,7 +10,7 @@ pub struct Cell(pub u16); pub struct Dish { pub chunk: Chunk, pub rules: Vec, - pub cell_groups: Vec>, + pub cell_groups: Vec>>, } #[derive(Debug)] @@ -311,7 +311,7 @@ impl Dish { Self { chunk: Chunk::new().fill_random(), rules: default_rules, - cell_groups: vec![vec![Cell(0), Cell(1)]], + cell_groups: vec![vec![None, Some(Cell(1))]], } } @@ -325,8 +325,6 @@ impl Dish { if self.rules.is_empty() { return; } - let x = random::() % CHUNK_SIZE; - let y = random::() % CHUNK_SIZE; let enabled_rules = self .rules .iter() @@ -338,38 +336,39 @@ impl Dish { } let rule = random::() % enabled_rules.len(); let rule = enabled_rules[rule]; - self.fire_rule(rule, x, y); + self.fire_rule(rule); } - fn fire_rule(&mut self, rule_index: usize, x: usize, y: usize) { + fn fire_rule(&mut self, rule_index: usize) { let rule = &self.rules[rule_index]; - // 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() { + 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) { 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 + dx, y + dy).unwrap()); + 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 + dx; - let py = y + dy; + 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()); @@ -378,11 +377,16 @@ impl Dish { let group = &self.cell_groups[group_id]; let i = random::() % group.len(); let cell = group[i]; - self.set_cell(px, py, cell); + if let Some(cell) = cell { + self.set_cell(px, py, cell); + } } RuleCellTo::Copy(index) => { let cell = old_state[index]; - self.set_cell(px, py, cell); + if let Some(cell) = cell { + // if the copy source is outside the world, do nothing + self.set_cell(px, py, cell); + } } RuleCellTo::None => (), } @@ -390,17 +394,15 @@ impl Dish { } } - fn subrule_matches(&self, x: usize, y: usize, subrule: &SubRule) -> bool { + fn subrule_matches(&self, x: isize, y: isize, subrule: &SubRule) -> bool { for dx in 0..subrule.width { for dy in 0..subrule.height { - let x = x + dx; - let y = y + dy; - let Some(cell) = self.get_cell(x, y) else { - return false; - }; + 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 != rule_cell { + if cell != Some(rule_cell) { return false; } } diff --git a/uscope/src/main.rs b/uscope/src/main.rs index 850e432..9df2a37 100644 --- a/uscope/src/main.rs +++ b/uscope/src/main.rs @@ -143,19 +143,25 @@ 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.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(&Cell(i as u16)); + let mut included = group.contains(&Some(Cell(i as u16))); if ui.checkbox(&mut included, &celldata.name).changed() { if included { - group.push(Cell(i as u16)); + group.push(Some(Cell(i as u16))); } else { - group.retain(|c| c.0 != i as u16); + group.retain(|c| c != &Some(Cell(i as u16))); } } } }); - // if response.clicked(){ - // } } if ui.button("add group").clicked() { self.dish.cell_groups.push(Vec::new()); @@ -218,7 +224,7 @@ 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], groups: &[Vec>]) { ui.checkbox(&mut rule.enabled, "enable rule"); ui.horizontal(|ui| { ui.label("flip"); @@ -359,7 +365,7 @@ fn rule_cell_edit_from( x: usize, y: usize, cells: &[CellData], - groups: &[Vec], + groups: &[Vec>], ) -> bool { let mut changed = false; let rect = Rect::from_min_size( @@ -422,7 +428,7 @@ fn rule_cell_edit_to( rule: &mut RuleCellTo, (x, y): (usize, usize), cells: &[CellData], - groups: &[Vec], + groups: &[Vec>], (rule_width, rule_height): (usize, usize), overlay_lines: &mut Vec<(Pos2, Pos2)>, ) -> bool { @@ -498,15 +504,19 @@ fn rule_cell_edit_to( changed } -fn draw_group(ui: &mut Ui, rect: Rect, group: &[Cell], cells: &[CellData]) { +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().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.contains(&None) { + ui.painter_at(rect) + .line_segment([rect.min, rect.max], (1., Color32::WHITE)); + } } impl CellData {