diff --git a/petri/src/lib.rs b/petri/src/lib.rs index 37b1615..66521e1 100644 --- a/petri/src/lib.rs +++ b/petri/src/lib.rs @@ -20,9 +20,19 @@ pub struct Chunk { pub struct Rule { pub from: RulePattern, pub to: RulePattern, - // probability - // flip - // rotate + // enabled: bool + // probability: u8 + // flip: + // rotate: +} + +impl Rule { + pub fn new() -> Self { + Self { + from: RulePattern::new(), + to: RulePattern::new(), + } + } } impl Chunk { @@ -106,8 +116,6 @@ impl Dish { let x = random::() % CHUNK_SIZE; let y = random::() % CHUNK_SIZE; let rule = random::() % self.rules.len(); - // let rule = &self.rules[rule]; - self.fire_rule(rule, x, y); } @@ -164,6 +172,14 @@ pub struct RulePattern { } impl RulePattern { + pub fn new() -> Self { + Self { + width: 1, + height: 1, + contents: vec![None], + } + } + pub fn get(&self, x: usize, y: usize) -> Option { if x >= self.width || y >= self.height { None @@ -193,6 +209,44 @@ impl RulePattern { pub fn width(&self) -> usize { self.width } + + pub fn extend_left(&mut self) { + let new_width = self.width + 1; + let mut new_vec = vec![None; new_width * self.height]; + for y in 0..self.height { + for x in 0..self.width { + new_vec[x + new_width * y + 1] = self.contents[x + self.width * y]; + } + } + self.width += 1; + self.contents = new_vec; + } + + pub fn extend_right(&mut self) { + let new_width = self.width + 1; + let mut new_vec = vec![None; new_width * self.height]; + for y in 0..self.height { + for x in 0..self.width { + new_vec[x + new_width * y] = self.contents[x + self.width * y]; + } + } + self.width += 1; + self.contents = new_vec; + } + + pub fn extend_up(&mut self) { + for _ in 0..self.width { + self.contents.insert(0, None); + } + self.height += 1; + } + + pub fn extend_down(&mut self) { + for _ in 0..self.width { + self.contents.push(None); + } + self.height += 1; + } } impl Cell { diff --git a/uscope/src/main.rs b/uscope/src/main.rs index d1acfd3..7c26fb9 100644 --- a/uscope/src/main.rs +++ b/uscope/src/main.rs @@ -63,6 +63,9 @@ impl eframe::App for UScope { for rule in &mut self.dish.rules { rule_editor(ui, rule, &self.celltypes); } + if ui.button("add rule").clicked() { + self.dish.rules.push(Rule::new()); + } }); CentralPanel::default().show(ctx, |ui| { let bounds = ui.available_rect_before_wrap(); @@ -97,20 +100,74 @@ fn paint_chunk(painter: Painter, chunk: &Chunk, cells: &[CellData]) { const CSIZE: f32 = 24.; const OUTLINE: (f32, Color32) = (1., Color32::GRAY); fn rule_editor(ui: &mut Ui, rule: &mut Rule, cells: &[CellData]) { - let patt_height = rule.from.height(); - let patt_width = rule.from.height(); + let cells_y = rule.from.height(); + let cells_x = rule.from.width(); + let margin = 8.; + let patt_width = CSIZE * cells_x as f32; + let patt_height = CSIZE * cells_y as f32; let (_, bounds) = ui.allocate_space(Vec2::new( - CSIZE * (patt_width * 2 + 1) as f32, - CSIZE * patt_height as f32, + patt_width * 2. + margin * 4. + CSIZE, + patt_height + margin * 2., )); - for x in 0..patt_width { - for y in 0..patt_height { - rule_cell_edit(ui, bounds.min, &mut rule.from, x, y, cells); - let offset = Vec2::X * (patt_width as f32 + 1.) * CSIZE; - rule_cell_edit(ui, bounds.min + offset, &mut rule.to, x, y, cells); + + let from_cells_rect = Rect::from_min_size( + bounds.min + Vec2::splat(margin), + Vec2::new(patt_width, patt_height), + ); + let to_cells_rect = Rect::from_min_size( + bounds.min + Vec2::splat(margin) + Vec2::X * (patt_width + margin * 2. + CSIZE), + Vec2::new(patt_width, patt_height), + ); + + for x in 0..cells_x { + for y in 0..cells_y { + rule_cell_edit(ui, from_cells_rect.min, &mut rule.from, x, y, cells); + rule_cell_edit(ui, to_cells_rect.min, &mut rule.to, x, y, cells); } } + let mut resize_box = |x, y, w, h| { + let rect_a = Rect::from_min_size(Pos2::new(x, y), Vec2::new(w, h)); + let a = ui.allocate_rect(rect_a, Sense::click()); + let rect_b = rect_a.translate(to_cells_rect.min - from_cells_rect.min); + let b = ui.allocate_rect(rect_b, Sense::click()); + let result = a.union(b); + let color = if result.hovered() { + Color32::GRAY + } else { + Color32::DARK_GRAY + }; + ui.painter_at(bounds).rect_filled(rect_a, 0., color); + ui.painter_at(bounds).rect_filled(rect_b, 0., color); + + result.clicked() + }; + if resize_box(bounds.min.x, bounds.min.y + margin, margin, patt_height) { + rule.from.extend_left(); + rule.to.extend_left(); + } + if resize_box( + from_cells_rect.max.x, + bounds.min.y + margin, + margin, + patt_height, + ) { + rule.from.extend_right(); + rule.to.extend_right(); + } + if resize_box(bounds.min.x + margin, bounds.min.y, patt_width, margin) { + rule.from.extend_up(); + rule.to.extend_up(); + } + if resize_box( + bounds.min.x + margin, + bounds.max.y - margin, + patt_width, + margin, + ) { + rule.from.extend_down(); + rule.to.extend_down(); + } } fn rule_cell_edit(