cell groups in rules
This commit is contained in:
parent
738e22bd4a
commit
f23c06f5d7
2 changed files with 242 additions and 45 deletions
107
petri/src/lib.rs
107
petri/src/lib.rs
|
@ -10,6 +10,7 @@ pub struct Cell(pub u16);
|
||||||
pub struct Dish {
|
pub struct Dish {
|
||||||
pub chunk: Chunk,
|
pub chunk: Chunk,
|
||||||
pub rules: Vec<Rule>,
|
pub rules: Vec<Rule>,
|
||||||
|
pub cell_groups: Vec<Vec<Cell>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -33,7 +34,31 @@ pub struct Rule {
|
||||||
struct SubRule {
|
struct SubRule {
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
contents: Vec<(Option<Cell>, Option<Cell>)>,
|
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),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubRule {
|
impl SubRule {
|
||||||
|
@ -41,36 +66,36 @@ impl SubRule {
|
||||||
Self {
|
Self {
|
||||||
width: 1,
|
width: 1,
|
||||||
height: 1,
|
height: 1,
|
||||||
contents: vec![(None, None)],
|
contents: vec![Default::default()],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, x: usize, y: usize) -> (Option<Cell>, Option<Cell>) {
|
fn get(&self, x: usize, y: usize) -> (RuleCellFrom, RuleCellTo) {
|
||||||
if x >= self.width || y >= self.height {
|
if x >= self.width || y >= self.height {
|
||||||
(None, None)
|
Default::default()
|
||||||
} else {
|
} else {
|
||||||
self.contents[x + self.width * y].clone()
|
self.contents[x + self.width * y].clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_mut(&mut self, x: usize, y: usize) -> &mut (Option<Cell>, Option<Cell>) {
|
fn get_mut(&mut self, x: usize, y: usize) -> &mut (RuleCellFrom, RuleCellTo) {
|
||||||
assert!(x < self.width || y < self.height);
|
assert!(x < self.width || y < self.height);
|
||||||
&mut self.contents[x + self.width * y]
|
&mut self.contents[x + self.width * y]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_both(&mut self, x: usize, y: usize, cells: (Option<Cell>, Option<Cell>)) {
|
fn set_both(&mut self, x: usize, y: usize, cells: (RuleCellFrom, RuleCellTo)) {
|
||||||
if x < self.width && y < self.height {
|
if x < self.width && y < self.height {
|
||||||
self.contents[x + self.width * y] = cells;
|
self.contents[x + self.width * y] = cells;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_from(&mut self, x: usize, y: usize, cell: Option<Cell>) {
|
fn set_from(&mut self, x: usize, y: usize, cell: RuleCellFrom) {
|
||||||
if x < self.width && y < self.height {
|
if x < self.width && y < self.height {
|
||||||
self.contents[x + self.width * y].0 = cell;
|
self.contents[x + self.width * y].0 = cell;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_to(&mut self, x: usize, y: usize, cell: Option<Cell>) {
|
fn set_to(&mut self, x: usize, y: usize, cell: RuleCellTo) {
|
||||||
if x < self.width && y < self.height {
|
if x < self.width && y < self.height {
|
||||||
self.contents[x + self.width * y].1 = cell;
|
self.contents[x + self.width * y].1 = cell;
|
||||||
}
|
}
|
||||||
|
@ -99,20 +124,20 @@ impl Rule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, x: usize, y: usize) -> (Option<Cell>, Option<Cell>) {
|
pub fn get(&self, x: usize, y: usize) -> (RuleCellFrom, RuleCellTo) {
|
||||||
self.base.get(x, y)
|
self.base.get(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mut(&mut self, x: usize, y: usize) -> &mut (Option<Cell>, Option<Cell>) {
|
pub fn get_mut(&mut self, x: usize, y: usize) -> &mut (RuleCellFrom, RuleCellTo) {
|
||||||
self.base.get_mut(x, y)
|
self.base.get_mut(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_from(&mut self, x: usize, y: usize, cell: Option<Cell>) {
|
pub fn set_from(&mut self, x: usize, y: usize, cell: RuleCellFrom) {
|
||||||
self.base.set_from(x, y, cell);
|
self.base.set_from(x, y, cell);
|
||||||
self.generate_variants();
|
self.generate_variants();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_to(&mut self, x: usize, y: usize, cell: Option<Cell>) {
|
pub fn set_to(&mut self, x: usize, y: usize, cell: RuleCellTo) {
|
||||||
self.base.set_to(x, y, cell);
|
self.base.set_to(x, y, cell);
|
||||||
self.generate_variants();
|
self.generate_variants();
|
||||||
}
|
}
|
||||||
|
@ -133,7 +158,7 @@ impl Rule {
|
||||||
if new_width < 1 || new_height < 1 {
|
if new_width < 1 || new_height < 1 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut new_contents = vec![(None, None); new_width * new_height];
|
let mut new_contents = vec![Default::default(); new_width * new_height];
|
||||||
|
|
||||||
for nx in 0..new_width {
|
for nx in 0..new_width {
|
||||||
let oldx = nx.wrapping_add_signed(dx);
|
let oldx = nx.wrapping_add_signed(dx);
|
||||||
|
@ -256,8 +281,8 @@ impl Dish {
|
||||||
width: 1,
|
width: 1,
|
||||||
height: 2,
|
height: 2,
|
||||||
contents: vec![
|
contents: vec![
|
||||||
(Some(Cell(1)), Some(Cell(0))),
|
(RuleCellFrom::One(Cell(1)), RuleCellTo::One(Cell(0))),
|
||||||
(Some(Cell(0)), Some(Cell(1))),
|
(RuleCellFrom::One(Cell(0)), RuleCellTo::One(Cell(1))),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
..Rule::new()
|
..Rule::new()
|
||||||
|
@ -268,10 +293,10 @@ impl Dish {
|
||||||
width: 2,
|
width: 2,
|
||||||
height: 2,
|
height: 2,
|
||||||
contents: vec![
|
contents: vec![
|
||||||
(Some(Cell(1)), Some(Cell(0))),
|
(RuleCellFrom::One(Cell(1)), RuleCellTo::One(Cell(0))),
|
||||||
(None, None),
|
(RuleCellFrom::Any, RuleCellTo::None),
|
||||||
(Some(Cell(1)), None),
|
(RuleCellFrom::One(Cell(1)), RuleCellTo::None),
|
||||||
(Some(Cell(0)), Some(Cell(1))),
|
(RuleCellFrom::One(Cell(0)), RuleCellTo::One(Cell(1))),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
flip_h: true,
|
flip_h: true,
|
||||||
|
@ -286,6 +311,7 @@ impl Dish {
|
||||||
Self {
|
Self {
|
||||||
chunk: Chunk::new().fill_random(),
|
chunk: Chunk::new().fill_random(),
|
||||||
rules: default_rules,
|
rules: default_rules,
|
||||||
|
cell_groups: vec![vec![Cell(0), Cell(1)]],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,12 +359,32 @@ impl Dish {
|
||||||
|
|
||||||
let width = variant.width;
|
let width = variant.width;
|
||||||
let height = variant.height;
|
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(dx, dy).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for dx in 0..width {
|
for dx in 0..width {
|
||||||
for dy in 0..height {
|
for dy in 0..height {
|
||||||
let x = x + dx;
|
let px = x + dx;
|
||||||
let y = y + dy;
|
let py = y + dy;
|
||||||
if let Some(rule_cell) = variant.get(dx, dy).1 {
|
match variant.get(dx, dy).1 {
|
||||||
self.set_cell(x, y, rule_cell.clone());
|
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::<usize>() % group.len();
|
||||||
|
let cell = group[i];
|
||||||
|
self.set_cell(px, py, cell);
|
||||||
|
}
|
||||||
|
RuleCellTo::Copy(index) => {
|
||||||
|
let cell = old_state[index];
|
||||||
|
self.set_cell(px, py, cell);
|
||||||
|
}
|
||||||
|
RuleCellTo::None => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -349,11 +395,22 @@ impl Dish {
|
||||||
for dy in 0..subrule.height {
|
for dy in 0..subrule.height {
|
||||||
let x = x + dx;
|
let x = x + dx;
|
||||||
let y = y + dy;
|
let y = y + dy;
|
||||||
if let Some(rule_cell) = subrule.get(dx, dy).0 {
|
let Some(cell) = self.get_cell(x, y) else {
|
||||||
if self.get_cell(x, y) != Some(rule_cell) {
|
return false;
|
||||||
|
};
|
||||||
|
match subrule.get(dx, dy).0 {
|
||||||
|
RuleCellFrom::One(rule_cell) => {
|
||||||
|
if cell != rule_cell {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RuleCellFrom::Group(group_id) => {
|
||||||
|
if !self.cell_groups[group_id].contains(&cell) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RuleCellFrom::Any => (),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|
|
@ -10,11 +10,15 @@ use eframe::{
|
||||||
epaint::Hsva,
|
epaint::Hsva,
|
||||||
NativeOptions,
|
NativeOptions,
|
||||||
};
|
};
|
||||||
|
use egui::{
|
||||||
|
menu::{SubMenu, SubMenuButton},
|
||||||
|
popup, Layout, PointerButton,
|
||||||
|
};
|
||||||
use native_dialog::FileDialog;
|
use native_dialog::FileDialog;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use petri::{Cell, Chunk, Dish, Rule, CHUNK_SIZE};
|
use petri::{Cell, Chunk, Dish, Rule, RuleCellFrom, RuleCellTo, CHUNK_SIZE};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -45,7 +49,7 @@ impl UScope {
|
||||||
fn new(_cc: &eframe::CreationContext<'_>) -> Self {
|
fn new(_cc: &eframe::CreationContext<'_>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
dish: Dish::new(),
|
dish: Dish::new(),
|
||||||
speed: 250,
|
speed: 500,
|
||||||
show_grid: false,
|
show_grid: false,
|
||||||
brush: Cell(1),
|
brush: Cell(1),
|
||||||
cell_types: vec![
|
cell_types: vec![
|
||||||
|
@ -134,12 +138,35 @@ impl eframe::App for UScope {
|
||||||
}
|
}
|
||||||
ui.separator();
|
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| {
|
||||||
|
for (i, celldata) in self.cell_types.iter().enumerate() {
|
||||||
|
let mut included = group.contains(&Cell(i as u16));
|
||||||
|
if ui.checkbox(&mut included, &celldata.name).changed() {
|
||||||
|
if included {
|
||||||
|
group.push(Cell(i as u16));
|
||||||
|
} else {
|
||||||
|
group.retain(|c| c.0 != i as u16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// if response.clicked(){
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
if ui.button("add group").clicked() {
|
||||||
|
self.dish.cell_groups.push(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
ui.heading("Rules");
|
ui.heading("Rules");
|
||||||
ScrollArea::vertical().show(ui, |ui| {
|
ScrollArea::vertical().show(ui, |ui| {
|
||||||
let mut to_remove = None;
|
let mut to_remove = None;
|
||||||
for (i, rule) in self.dish.rules.iter_mut().enumerate() {
|
for (i, rule) in self.dish.rules.iter_mut().enumerate() {
|
||||||
ui.separator();
|
ui.separator();
|
||||||
rule_editor(ui, rule, &self.cell_types);
|
rule_editor(ui, rule, &self.cell_types, &self.dish.cell_groups);
|
||||||
if ui.button("delete").clicked() {
|
if ui.button("delete").clicked() {
|
||||||
to_remove = Some(i);
|
to_remove = Some(i);
|
||||||
}
|
}
|
||||||
|
@ -189,7 +216,7 @@ fn paint_chunk(painter: Painter, chunk: &Chunk, cells: &[CellData], grid: bool)
|
||||||
|
|
||||||
const CSIZE: f32 = 24.;
|
const CSIZE: f32 = 24.;
|
||||||
const OUTLINE: (f32, Color32) = (2., Color32::GRAY);
|
const OUTLINE: (f32, Color32) = (2., Color32::GRAY);
|
||||||
fn rule_editor(ui: &mut Ui, rule: &mut Rule, cells: &[CellData]) {
|
fn rule_editor(ui: &mut Ui, rule: &mut Rule, cells: &[CellData], groups: &[Vec<Cell>]) {
|
||||||
ui.checkbox(&mut rule.enabled, "enable rule");
|
ui.checkbox(&mut rule.enabled, "enable rule");
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("flip");
|
ui.label("flip");
|
||||||
|
@ -227,8 +254,10 @@ fn rule_editor(ui: &mut Ui, rule: &mut Rule, cells: &[CellData]) {
|
||||||
for x in 0..cells_x {
|
for x in 0..cells_x {
|
||||||
for y in 0..cells_y {
|
for y in 0..cells_y {
|
||||||
let (left, right) = rule.get_mut(x, y);
|
let (left, right) = rule.get_mut(x, y);
|
||||||
let changed_left = rule_cell_edit(ui, from_cells_rect.min, left, x, y, cells);
|
let changed_left =
|
||||||
let changed_right = rule_cell_edit(ui, to_cells_rect.min, right, x, y, cells);
|
rule_cell_edit_from(ui, from_cells_rect.min, left, x, y, cells, groups);
|
||||||
|
let changed_right =
|
||||||
|
rule_cell_edit_to(ui, to_cells_rect.min, right, x, y, cells, groups);
|
||||||
if changed_left || changed_right {
|
if changed_left || changed_right {
|
||||||
rule.generate_variants();
|
rule.generate_variants();
|
||||||
}
|
}
|
||||||
|
@ -297,13 +326,14 @@ fn rule_editor(ui: &mut Ui, rule: &mut Rule, cells: &[CellData]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rule_cell_edit(
|
fn rule_cell_edit_from(
|
||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
origin: Pos2,
|
origin: Pos2,
|
||||||
rule: &mut Option<Cell>,
|
rule: &mut RuleCellFrom,
|
||||||
x: usize,
|
x: usize,
|
||||||
y: usize,
|
y: usize,
|
||||||
cells: &[CellData],
|
cells: &[CellData],
|
||||||
|
groups: &[Vec<Cell>],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
let rect = Rect::from_min_size(
|
let rect = Rect::from_min_size(
|
||||||
|
@ -311,24 +341,134 @@ fn rule_cell_edit(
|
||||||
Vec2::splat(CSIZE),
|
Vec2::splat(CSIZE),
|
||||||
);
|
);
|
||||||
let aabb = ui.allocate_rect(rect, Sense::click());
|
let aabb = ui.allocate_rect(rect, Sense::click());
|
||||||
if let Some(cell) = rule {
|
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;
|
let color = cells[cell.id()].color;
|
||||||
ui.painter()
|
ui.painter()
|
||||||
.rect(rect.shrink(OUTLINE.0 / 2.), 0., color, OUTLINE);
|
.rect(rect.shrink(OUTLINE.0 / 2.), 0., color, OUTLINE);
|
||||||
if aabb.clicked() {
|
}
|
||||||
changed = true;
|
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 += 1;
|
||||||
if cell.0 as usize == cells.len() {
|
cell.0 %= cells.len() as u16;
|
||||||
*rule = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if aabb.clicked() {
|
|
||||||
*rule = Some(Cell(0));
|
|
||||||
changed = true;
|
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
|
changed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn rule_cell_edit_to(
|
||||||
|
ui: &mut Ui,
|
||||||
|
origin: Pos2,
|
||||||
|
rule: &mut RuleCellTo,
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
cells: &[CellData],
|
||||||
|
groups: &[Vec<Cell>],
|
||||||
|
) -> 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(_) => todo!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
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(_) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if switch_type {
|
||||||
|
changed = true;
|
||||||
|
match rule {
|
||||||
|
RuleCellTo::None => {
|
||||||
|
*rule = RuleCellTo::One(Cell(0));
|
||||||
|
}
|
||||||
|
RuleCellTo::One(_) => {
|
||||||
|
*rule = RuleCellTo::GroupRandom(0);
|
||||||
|
}
|
||||||
|
RuleCellTo::GroupRandom(_) => {
|
||||||
|
*rule = RuleCellTo::None;
|
||||||
|
}
|
||||||
|
RuleCellTo::Copy(_) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_group(ui: &mut Ui, rect: Rect, group: &[Cell], 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() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CellData {
|
impl CellData {
|
||||||
fn new(name: &str, r: u8, g: u8, b: u8) -> Self {
|
fn new(name: &str, r: u8, g: u8, b: u8) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
Loading…
Reference in a new issue