summaryrefslogtreecommitdiffstats
path: root/examples/game_of_life
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2020-04-30 04:12:13 +0200
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2020-04-30 04:12:13 +0200
commitaf95d3972e4ab6bf4ace54ddd44379ffcebbcff6 (patch)
treede6cd798520fb063383daff18cc0eb413b8c1845 /examples/game_of_life
parent611d9e399c95268a3daf41bd6cbcc55391ccff87 (diff)
downloadiced-af95d3972e4ab6bf4ace54ddd44379ffcebbcff6.tar.gz
iced-af95d3972e4ab6bf4ace54ddd44379ffcebbcff6.tar.bz2
iced-af95d3972e4ab6bf4ace54ddd44379ffcebbcff6.zip
Implement camera panning in `game_of_life` example
Diffstat (limited to 'examples/game_of_life')
-rw-r--r--examples/game_of_life/src/main.rs165
1 files changed, 90 insertions, 75 deletions
diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs
index 3989e3ea..983d6cb4 100644
--- a/examples/game_of_life/src/main.rs
+++ b/examples/game_of_life/src/main.rs
@@ -136,13 +136,13 @@ impl Application for GameOfLife {
.align_items(Align::Center);
let controls = Row::new()
+ .padding(10)
.spacing(20)
.push(playback_controls)
.push(speed_controls);
let content = Column::new()
.spacing(10)
- .padding(10)
.align_items(Align::Center)
.push(self.grid.view().map(Message::Grid))
.push(controls);
@@ -167,16 +167,27 @@ mod grid {
#[derive(Default)]
pub struct Grid {
- alive_cells: HashSet<(usize, usize)>,
- mouse_pressed: bool,
+ alive_cells: HashSet<(isize, isize)>,
+ interaction: Option<Interaction>,
cache: canvas::Cache,
+ translation: Vector,
+ }
+
+ #[derive(Debug, Clone, Copy)]
+ pub enum Message {
+ Populate { cell: (isize, isize) },
+ }
+
+ enum Interaction {
+ Drawing,
+ Panning { translation: Vector, start: Point },
}
impl Grid {
fn with_neighbors(
- i: usize,
- j: usize,
- ) -> impl Iterator<Item = (usize, usize)> {
+ i: isize,
+ j: isize,
+ ) -> impl Iterator<Item = (isize, isize)> {
use itertools::Itertools;
let rows = i.saturating_sub(1)..=i.saturating_add(1);
@@ -188,7 +199,7 @@ mod grid {
pub fn tick(&mut self) {
use itertools::Itertools;
- let populated_neighbors: HashMap<(usize, usize), usize> = self
+ let populated_neighbors: HashMap<(isize, isize), usize> = self
.alive_cells
.iter()
.flat_map(|&(i, j)| Self::with_neighbors(i, j))
@@ -230,55 +241,26 @@ mod grid {
.into()
}
- fn populated_neighbors(&self, row: usize, column: usize) -> usize {
+ fn populated_neighbors(&self, row: isize, column: isize) -> usize {
let with_neighbors = Self::with_neighbors(row, column);
- let is_neighbor = |i: usize, j: usize| i != row || j != column;
+ let is_neighbor = |i: isize, j: isize| i != row || j != column;
let is_populated =
- |i: usize, j: usize| self.alive_cells.contains(&(i, j));
+ |i: isize, j: isize| self.alive_cells.contains(&(i, j));
with_neighbors
.filter(|&(i, j)| is_neighbor(i, j) && is_populated(i, j))
.count()
}
- fn region(&self, size: Size) -> Rectangle {
- let width =
- (size.width / CELL_SIZE as f32).floor() * CELL_SIZE as f32;
- let height =
- (size.height / CELL_SIZE as f32).floor() * CELL_SIZE as f32;
-
- Rectangle {
- x: (size.width - width) / 2.0,
- y: (size.height - height) / 2.0,
- width,
- height,
- }
- }
+ 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;
- fn cell_at(
- &self,
- region: Rectangle,
- position: Point,
- ) -> Option<(usize, usize)> {
- if region.contains(position) {
- let i = ((position.y - region.y) / CELL_SIZE as f32).ceil()
- as usize;
- let j = ((position.x - region.x) / CELL_SIZE as f32).ceil()
- as usize;
-
- Some((i.saturating_sub(1), j.saturating_sub(1)))
- } else {
- None
- }
+ Some((i.saturating_sub(1), j.saturating_sub(1)))
}
}
- #[derive(Debug, Clone, Copy)]
- pub enum Message {
- Populate { cell: (usize, usize) },
- }
-
impl<'a> canvas::Program<Message> for Grid {
fn update(
&mut self,
@@ -287,17 +269,15 @@ mod grid {
cursor: Cursor,
) -> Option<Message> {
if let Event::Mouse(mouse::Event::Input {
- button: mouse::Button::Left,
- state,
+ state: ButtonState::Released,
+ ..
}) = event
{
- self.mouse_pressed = state == ButtonState::Pressed;
+ self.interaction = None;
}
let cursor_position = cursor.position_in(&bounds)?;
-
- let region = self.region(bounds.size());
- let cell = self.cell_at(region, cursor_position)?;
+ let cell = self.cell_at(cursor_position - self.translation)?;
let populate = if self.alive_cells.contains(&cell) {
None
@@ -306,26 +286,53 @@ mod grid {
};
match event {
- Event::Mouse(mouse::Event::Input {
- button: mouse::Button::Left,
- ..
- }) if self.mouse_pressed => populate,
- Event::Mouse(mouse::Event::CursorMoved { .. })
- if self.mouse_pressed =>
- {
- populate
- }
- _ => None,
+ Event::Mouse(mouse_event) => match mouse_event {
+ mouse::Event::Input {
+ button,
+ state: ButtonState::Pressed,
+ } => match button {
+ mouse::Button::Left => {
+ self.interaction = Some(Interaction::Drawing);
+
+ populate
+ }
+ mouse::Button::Right => {
+ self.interaction = Some(Interaction::Panning {
+ translation: self.translation,
+ start: cursor_position,
+ });
+
+ None
+ }
+ _ => None,
+ },
+ mouse::Event::CursorMoved { .. } => {
+ match self.interaction {
+ Some(Interaction::Drawing) => populate,
+ Some(Interaction::Panning {
+ translation,
+ start,
+ }) => {
+ self.translation =
+ translation + (cursor_position - start);
+
+ self.cache.clear();
+
+ None
+ }
+ _ => None,
+ }
+ }
+ _ => None,
+ },
}
}
fn draw(&self, bounds: Rectangle, cursor: Cursor) -> Vec<Geometry> {
- let region = self.region(bounds.size());
let cell_size = Size::new(1.0, 1.0);
let life = self.cache.draw(bounds.size(), |frame| {
- let background =
- Path::rectangle(region.position(), region.size());
+ let background = Path::rectangle(Point::ORIGIN, frame.size());
frame.fill(
&background,
Color::from_rgb(
@@ -335,16 +342,25 @@ mod grid {
),
);
- let visible_rows = region.height as usize / CELL_SIZE;
- let visible_columns = region.width as usize / CELL_SIZE;
+ 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(Vector::new(region.x, region.y));
+ frame.translate(self.translation);
frame.scale(CELL_SIZE as f32);
let cells = Path::new(|p| {
- for i in 0..visible_rows {
- for j in 0..visible_columns {
+ 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),
@@ -361,11 +377,12 @@ mod grid {
let hovered_cell = {
let mut frame = Frame::new(bounds.size());
- frame.translate(Vector::new(region.x, region.y));
+ frame.translate(self.translation);
frame.scale(CELL_SIZE as f32);
if let Some(cursor_position) = cursor.position_in(&bounds) {
- if let Some((i, j)) = self.cell_at(region, cursor_position)
+ if let Some((i, j)) =
+ self.cell_at(cursor_position - self.translation)
{
let interaction = Path::rectangle(
Point::new(j as f32, i as f32),
@@ -393,12 +410,10 @@ mod grid {
bounds: Rectangle,
cursor: Cursor,
) -> MouseCursor {
- let region = self.region(bounds.size());
-
- match cursor.position_in(&bounds) {
- Some(position) if region.contains(position) => {
- MouseCursor::Crosshair
- }
+ match self.interaction {
+ Some(Interaction::Drawing) => MouseCursor::Crosshair,
+ Some(Interaction::Panning { .. }) => MouseCursor::Grabbing,
+ None if cursor.is_over(&bounds) => MouseCursor::Crosshair,
_ => MouseCursor::default(),
}
}