From 1f8c3018c79993d7ec07dc5622016d78e3d71f50 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tomas=20Wenstr=C3=B6m?= Date: Sat, 30 Jan 2021 11:40:41 +0100 Subject: [PATCH] Find, filter and outline regions --- src/core/game.rs | 2 +- src/core/level.rs | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 138 insertions(+), 5 deletions(-) diff --git a/src/core/game.rs b/src/core/game.rs index 6c04969..125bd1a 100644 --- a/src/core/game.rs +++ b/src/core/game.rs @@ -58,7 +58,7 @@ impl AppState for GameState { return Some(StateChange::Push(Box::new(ActiveState::new((800, 600))))) } Event::KeyDown { keycode: Some(Keycode::Space), .. } => { - self.world.level.regenerate(); + self.world.level = Level::new(self.world.level.gravity); } Event::KeyDown { keycode: Some(Keycode::KpPlus), .. } => { self.world.level.increase_iteration(); diff --git a/src/core/level.rs b/src/core/level.rs index c3f4aae..4937551 100644 --- a/src/core/level.rs +++ b/src/core/level.rs @@ -12,31 +12,46 @@ pub struct Level { pub gravity: Point2D, pub grid: Grid, iterations: u8, + walls: Vec>>, } impl Level { pub fn new(gravity: Point2D) -> Self { - Level { gravity, grid: Grid::generate(10), iterations: 10 } + let mut lvl = Level { gravity, grid: Grid::generate(10), iterations: 10, walls: vec!() }; + lvl.filter_regions(); + lvl } - pub fn regenerate(&mut self) { + fn generate(&mut self) { self.grid = Grid::generate(self.iterations); } pub fn increase_iteration(&mut self) { self.iterations += 1; - self.regenerate(); + self.generate(); println!("iterate {} time(s)", self.iterations); } pub fn decrease_iteration(&mut self) { self.iterations -= 1; - self.regenerate(); + self.generate(); println!("iterate {} time(s)", self.iterations); } pub fn filter_regions(&mut self) { self.grid.filter_regions(); + let mut walls = vec!(); + for mut r in self.grid.find_regions() { + if r.value { + let mut outline = r.outline(self.grid.cell_size); + for i in 2..(outline.len() - 2) { +// outline[i] = (outline[i - 1] + outline[i] + outline[i + 1]) / 3; + outline[i] = (outline[i - 2] + outline[i - 1] + outline[i] + outline[i + 1] + outline[i + 2]) / 5; + } + walls.push(outline); + } + } + self.walls = walls; } pub fn render(&mut self, renderer: &mut Renderer, _sprites: &SpriteManager) { @@ -49,11 +64,21 @@ impl Level { } } } + + let off = (size / 2) as i32; + for wall in &self.walls { + for w in wall.windows(2) { + renderer.draw_line((w[0].x as i32 + off, w[0].y as i32 + off), (w[1].x as i32 + off, w[1].y as i32 + off), (255, 255, 0)); + } + let last = wall.len() - 1; + renderer.draw_line((wall[0].x as i32 + off, wall[0].y as i32 + off), (wall[last].x as i32 + off, wall[last].y as i32 + off), (255, 255, 0)); + } } } ////////// GRID //////////////////////////////////////////////////////////////// + #[derive(Default)] pub struct Grid { pub width: usize, @@ -265,3 +290,111 @@ struct Region { value: bool, cells: Vec<(usize, usize)>, } + +impl Region { + fn enclosing_rect(&self) -> (usize, usize, usize, usize) { + let mut min = (usize::MAX, usize::MAX); + let mut max = (0, 0); + for c in &self.cells { + if c.0 < min.0 { min.0 = c.0; } + else if c.0 > max.0 { max.0 = c.0; } + if c.1 < min.1 { min.1 = c.1; } + else if c.1 > max.1 { max.1 = c.1; } + } + (min.0, min.1, 1 + max.0 - min.0, 1 + max.1 - min.1) + } + + pub fn outline(&mut self, scale: usize) -> Vec> { + let rect = self.enclosing_rect(); + let (ox, oy, w, h) = rect; + let grid = self.grid(&rect); + let mut marked = vec!(vec!(false; h); w); + let mut outline = vec!(); + + let (mut p, mut dir) = self.find_first_point_of_outline(&rect, &grid); +// println!("starting at {:?} with dir {:?}", p, dir); + marked[p.x as usize][p.y as usize] = true; + loop { + outline.push((p + (ox as isize, oy as isize)) * scale as isize); + let result = self.find_next_point_of_outline(&grid, p, dir); + p = result.0; + dir = result.1; +// println!("next at {:?} with dir {:?}", p, dir); + if marked[p.x as usize][p.y as usize] { + // we're back at the beginning + break; + } + marked[p.x as usize][p.y as usize] = true; + } + + outline + } + + fn grid(&self, rect: &(usize, usize, usize, usize)) -> Vec> { + let (x, y, w, h) = rect; + let mut grid = vec!(vec!(false; *h); *w); + for c in &self.cells { + grid[c.0 - x][c.1 - y] = true; + } + grid + } + + fn find_first_point_of_outline(&self, rect: &(usize, usize, usize, usize), grid: &Vec>) -> (Point2D, Point2D) { + let (ox, oy, w, h) = rect; + let is_outer_wall = (ox, oy) == (&0, &0); // we know this is always the outer wall of the level + for x in 0..*w { + for y in 0..*h { + if is_outer_wall && !grid[x][y] { + return (point!(x as isize, y as isize - 1), point!(0, 1)) // one step back because we're not on a wall tile + } + else if !is_outer_wall && grid[x][y] { + return (point!(x as isize, y as isize), point!(1, 0)) + } + } + } + panic!("no wall found!"); + } + + fn find_next_point_of_outline(&self, grid: &Vec>, p: Point2D, dir: Point2D) -> (Point2D, Point2D) { + let left = match dir.into() { + (-1, 0) => (0, 1), + (0, 1) => (1, 0), + (1, 0) => (0, -1), + (0, -1) => (-1, 0), + _ => (0, 0), + }; + let right = match dir.into() { + (0, 1) => (-1, 0), + (1, 0) => (0, 1), + (0, -1) => (1, 0), + (-1, 0) => (0, -1), + _ => (0, 0), + }; + if self.check(p + dir, grid) { +// println!("{:?} is true", p + dir); + if self.check(p + dir + left, grid) { +// println!("going left to {:?}", p + dir + left); + return (p + dir + left, left.into()) + } else { + return (p + dir, dir) + } + } else { +// println!("{:?} is false", p + dir); + if self.check(p + dir + right, grid) { +// println!("going right to {:?}", p + dir + right); + return (p + dir + right, dir) + } else { +// println!("going right from p to {:?}", p + right); + return (p + right, right.into()) + } + } + } + + fn check(&self, p: Point2D, grid: &Vec>) -> bool { + if p.x < 0 || p.x >= grid.len() as isize || p.y < 0 || p.y >= grid[0].len() as isize { + false + } else { + grid[p.x as usize][p.y as usize] + } + } +} -- 2.11.0