diff options
author | 2020-05-01 00:50:40 +0200 | |
---|---|---|
committer | 2020-05-01 00:50:40 +0200 | |
commit | ee97887409849395ecfd63e499c5d5372b121aa3 (patch) | |
tree | cb3d8faebc657563841651b084faa0c2d24196be /examples/game_of_life | |
parent | 005ad6215aa9e2a8f159454c8006b5dc6aeb3635 (diff) | |
download | iced-ee97887409849395ecfd63e499c5d5372b121aa3.tar.gz iced-ee97887409849395ecfd63e499c5d5372b121aa3.tar.bz2 iced-ee97887409849395ecfd63e499c5d5372b121aa3.zip |
Introduce `Cell` type in `game_of_life`
Diffstat (limited to 'examples/game_of_life')
-rw-r--r-- | examples/game_of_life/Cargo.toml | 2 | ||||
-rw-r--r-- | examples/game_of_life/src/main.rs | 182 |
2 files changed, 98 insertions, 86 deletions
diff --git a/examples/game_of_life/Cargo.toml b/examples/game_of_life/Cargo.toml index 413493ae..b1054537 100644 --- a/examples/game_of_life/Cargo.toml +++ b/examples/game_of_life/Cargo.toml @@ -6,5 +6,5 @@ edition = "2018" publish = false [dependencies] -iced = { path = "../..", features = ["canvas", "tokio"] } +iced = { path = "../..", features = ["canvas", "tokio", "debug"] } itertools = "0.9" diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index f0891db1..9f43b56a 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -161,11 +161,9 @@ mod grid { }; use std::collections::{HashMap, HashSet}; - const CELL_SIZE: usize = 20; - #[derive(Default)] pub struct Grid { - alive_cells: HashSet<(isize, isize)>, + life: HashSet<Cell>, interaction: Option<Interaction>, cache: canvas::Cache, translation: Vector, @@ -173,7 +171,7 @@ mod grid { #[derive(Debug, Clone, Copy)] pub enum Message { - Populate { cell: (isize, isize) }, + Populate(Cell), } enum Interaction { @@ -182,41 +180,26 @@ mod grid { } impl Grid { - fn with_neighbors( - i: isize, - j: isize, - ) -> impl Iterator<Item = (isize, isize)> { - use itertools::Itertools; - - let rows = i.saturating_sub(1)..=i.saturating_add(1); - let columns = j.saturating_sub(1)..=j.saturating_add(1); - - rows.cartesian_product(columns) - } - pub fn tick(&mut self) { use itertools::Itertools; - let populated_neighbors: HashMap<(isize, isize), usize> = self - .alive_cells + let populated_neighbors: HashMap<Cell, usize> = self + .life .iter() - .flat_map(|&(i, j)| Self::with_neighbors(i, j)) + .flat_map(Cell::cluster) .unique() - .map(|(i, j)| ((i, j), self.populated_neighbors(i, j))) + .map(|cell| (cell, self.count_adjacent_life(cell))) .collect(); - for (&(i, j), amount) in populated_neighbors.iter() { - let is_populated = self.alive_cells.contains(&(i, j)); - + for (cell, amount) in populated_neighbors.iter() { match amount { - 2 | 3 if is_populated => {} + 2 => {} 3 => { - let _ = self.alive_cells.insert((i, j)); + let _ = self.life.insert(*cell); } - _ if is_populated => { - let _ = self.alive_cells.remove(&(i, j)); + _ => { + let _ = self.life.remove(cell); } - _ => {} } } @@ -225,8 +208,8 @@ mod grid { pub fn update(&mut self, message: Message) { match message { - Message::Populate { cell } => { - self.alive_cells.insert(cell); + Message::Populate(cell) => { + self.life.insert(cell); self.cache.clear() } } @@ -239,24 +222,16 @@ mod grid { .into() } - fn populated_neighbors(&self, row: isize, column: isize) -> usize { - let with_neighbors = Self::with_neighbors(row, column); + fn count_adjacent_life(&self, cell: Cell) -> usize { + let cluster = Cell::cluster(&cell); - let is_neighbor = |i: isize, j: isize| i != row || j != column; - let is_populated = - |i: isize, j: isize| self.alive_cells.contains(&(i, j)); + let is_neighbor = |candidate| candidate != cell; + let is_populated = |cell| self.life.contains(&cell); - with_neighbors - .filter(|&(i, j)| is_neighbor(i, j) && is_populated(i, j)) + cluster + .filter(|&cell| is_neighbor(cell) && is_populated(cell)) .count() } - - fn cell_at(&self, position: Point) -> Option<(isize, isize)> { - let i = (position.y / CELL_SIZE as f32).ceil() as isize; - let j = (position.x / CELL_SIZE as f32).ceil() as isize; - - Some((i.saturating_sub(1), j.saturating_sub(1))) - } } impl<'a> canvas::Program<Message> for Grid { @@ -271,12 +246,12 @@ mod grid { } let cursor_position = cursor.position_in(&bounds)?; - let cell = self.cell_at(cursor_position - self.translation)?; + let cell = Cell::at(cursor_position - self.translation); - let populate = if self.alive_cells.contains(&cell) { + let populate = if self.life.contains(&cell) { None } else { - Some(Message::Populate { cell }) + Some(Message::Populate(cell)) }; match event { @@ -333,31 +308,24 @@ mod grid { ), ); - let first_row = - (-self.translation.y / CELL_SIZE as f32).floor() as isize; - let first_column = - (-self.translation.x / CELL_SIZE as f32).floor() as isize; - - let visible_rows = - (frame.height() / CELL_SIZE as f32).ceil() as isize; - let visible_columns = - (frame.width() / CELL_SIZE as f32).ceil() as isize; - frame.with_save(|frame| { frame.translate(self.translation); - frame.scale(CELL_SIZE as f32); + frame.scale(Cell::SIZE as f32); let cells = Path::new(|p| { - for i in first_row..=(first_row + visible_rows) { - for j in - first_column..=(first_column + visible_columns) - { - if self.alive_cells.contains(&(i, j)) { - p.rectangle( - Point::new(j as f32, i as f32), - cell_size, - ); - } + let region = Rectangle { + x: -self.translation.x, + y: -self.translation.y, + width: frame.width(), + height: frame.height(), + }; + + for cell in Cell::all_visible_in(region) { + if self.life.contains(&cell) { + p.rectangle( + Point::new(cell.j as f32, cell.i as f32), + cell_size, + ); } } }); @@ -369,25 +337,23 @@ mod grid { let mut frame = Frame::new(bounds.size()); frame.translate(self.translation); - frame.scale(CELL_SIZE as f32); + frame.scale(Cell::SIZE as f32); if let Some(cursor_position) = cursor.position_in(&bounds) { - if let Some((i, j)) = - self.cell_at(cursor_position - self.translation) - { - let interaction = Path::rectangle( - Point::new(j as f32, i as f32), - cell_size, - ); - - frame.fill( - &interaction, - Color { - a: 0.5, - ..Color::BLACK - }, - ); - } + let cell = Cell::at(cursor_position - self.translation); + + let interaction = Path::rectangle( + Point::new(cell.j as f32, cell.i as f32), + cell_size, + ); + + frame.fill( + &interaction, + Color { + a: 0.5, + ..Color::BLACK + }, + ); } frame.into_geometry() @@ -413,4 +379,50 @@ mod grid { } } } + + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct Cell { + i: isize, + j: isize, + } + + impl Cell { + const SIZE: usize = 20; + + fn at(position: Point) -> Cell { + let i = (position.y / Cell::SIZE as f32).ceil() as isize; + let j = (position.x / Cell::SIZE as f32).ceil() as isize; + + Cell { + i: i.saturating_sub(1), + j: j.saturating_sub(1), + } + } + + fn cluster(cell: &Cell) -> impl Iterator<Item = Cell> { + use itertools::Itertools; + + let rows = cell.i.saturating_sub(1)..=cell.i.saturating_add(1); + let columns = cell.j.saturating_sub(1)..=cell.j.saturating_add(1); + + rows.cartesian_product(columns).map(|(i, j)| Cell { i, j }) + } + + fn all_visible_in(region: Rectangle) -> impl Iterator<Item = Cell> { + use itertools::Itertools; + + let first_row = (region.y / Cell::SIZE as f32).floor() as isize; + let first_column = (region.x / Cell::SIZE as f32).floor() as isize; + + let visible_rows = + (region.height / Cell::SIZE as f32).ceil() as isize; + let visible_columns = + (region.width / Cell::SIZE as f32).ceil() as isize; + + let rows = first_row..=first_row + visible_rows; + let columns = first_column..=first_column + visible_columns; + + rows.cartesian_product(columns).map(|(i, j)| Cell { i, j }) + } + } } |