-use {point, time_scope};
-use common::Point;
-use super::{Grid, Level};
+use common::{Point, Dimension};
+use std::rc::Rc;
use noise::{NoiseFn, OpenSimplex, Seedable};
use rand::Rng;
+use super::{Grid, Level, WallRegion};
+use {point, time_scope};
////////// LEVEL GENERATOR /////////////////////////////////////////////////////
}
pub fn generate(&self) -> Level {
- println!("new level from {:?}", self);
+ dbg!(self);
time_scope!("level generation");
let cell_size = 20;
let (width, height) = (2560 / cell_size, 1440 / cell_size);
let mut grid = Grid {
- cell_size,
- width,
- height,
+ cell_size: (cell_size, cell_size).into(),
+ size: (width, height).into(),
cells: vec!(vec!(true; height); width),
};
self.filter_regions(&mut grid);
let walls = self.find_walls(&grid);
- Level {
- gravity: point!(0.0, 0.1),
- grid,
- walls,
- }
+ Level::new(point!(0.0, 0.1), grid, walls)
}
#[allow(dead_code)]
fn smooth(&self, grid: &mut Grid<bool>) {
let distance = 1;
for _i in 0..self.iterations {
- let mut next = vec!(vec!(true; grid.height); grid.width);
- for x in distance..(grid.width - distance) {
- for y in distance..(grid.height - distance) {
+ let mut next = vec!(vec!(true; grid.size.height); grid.size.width);
+ for x in distance..(grid.size.width - distance) {
+ for y in distance..(grid.size.height - distance) {
match self.neighbours(&grid.cells, x, y, distance) {
n if n < 4 => next[x][y] = false,
n if n > 4 => next[x][y] = true,
let mut count = 0;
loop {
count += 1;
- let mut next = vec!(vec!(true; grid.height); grid.width);
- for x in distance..(grid.width - distance) {
- for y in distance..(grid.height - distance) {
+ let mut next = vec!(vec!(true; grid.size.height); grid.size.width);
+ for x in distance..(grid.size.width - distance) {
+ for y in distance..(grid.size.height - distance) {
match self.neighbours(&grid.cells, x, y, distance) {
n if n < 4 => next[x][y] = false,
n if n > 4 => next[x][y] = true,
}
fn set_each<F: FnMut(usize, usize) -> bool>(&self, grid: &mut Grid<bool>, mut func: F, walls: usize) {
- for x in walls..(grid.width - walls) {
- for y in walls..(grid.height - walls) {
+ for x in walls..(grid.size.width - walls) {
+ for y in walls..(grid.size.height - walls) {
grid.cells[x][y] = func(x, y);
}
}
}
fn subdivide(&self, grid: &mut Grid<bool>) -> Grid<bool> {
- let (width, height) = (grid.width * 2, grid.height * 2);
+ let (width, height) = (grid.size.width * 2, grid.size.height * 2);
let mut cells = vec!(vec!(true; height); width);
for x in 1..(width - 1) {
for y in 1..(height - 1) {
}
}
Grid {
- cell_size: grid.cell_size / 2,
- width,
- height,
+ cell_size: (grid.cell_size.width / 2, grid.cell_size.height / 2).into(),
+ size: (width, height).into(),
cells
}
}
fn find_regions(&self, grid: &Grid<bool>) -> Vec<Region> {
time_scope!(" finding all regions");
let mut regions = vec!();
- let mut marked = vec!(vec!(false; grid.height); grid.width);
- for x in 0..grid.width {
- for y in 0..grid.height {
+ let mut marked = vec!(vec!(false; grid.size.height); grid.size.width);
+ for x in 0..grid.size.width {
+ for y in 0..grid.size.height {
if !marked[x][y] {
regions.push(self.get_region_at_point(grid, x, y, &mut marked));
}
cells.push(p);
for i in &[(-1, 0), (1, 0), (0, -1), (0, 1)] {
let ip = (p.0 as isize + i.0, p.1 as isize + i.1);
- if ip.0 >= 0 && ip.0 < grid.width as isize && ip.1 >= 0 && ip.1 < grid.height as isize {
+ if ip.0 >= 0 && ip.0 < grid.size.width as isize && ip.1 >= 0 && ip.1 < grid.size.height as isize {
let up = (ip.0 as usize, ip.1 as usize);
if grid.cells[up.0][up.1] == value && !marked[up.0][up.1] {
marked[up.0][up.1] = true;
fn filter_regions(&self, grid: &mut Grid<bool>) {
let min_wall_size = 0.0015;
- println!(" grid size: ({}, {}) = {} cells", grid.width, grid.height, grid.width * grid.height);
- println!(" min wall size: {}", (grid.width * grid.height) as f64 * min_wall_size);
+ println!(" grid size: ({}, {}) = {} cells", grid.size.width, grid.size.height, grid.size.width * grid.size.height);
+ println!(" min wall size: {}", (grid.size.width * grid.size.height) as f64 * min_wall_size);
// delete all smaller wall regions
for r in self.find_regions(grid).iter().filter(|r| r.value) {
- let percent = r.cells.len() as f64 / (grid.width * grid.height) as f64;
+ let percent = r.cells.len() as f64 / (grid.size.width * grid.size.height) as f64;
if percent < min_wall_size {
// println!(" delete wall region of size {}", r.cells.len());
self.delete_region(grid, r);
}
}
- fn find_walls(&self, grid: &Grid<bool>) -> Vec<Vec<Point<isize>>> {
+ fn find_walls(&self, grid: &Grid<bool>) -> Vec<Rc<WallRegion>> {
let mut walls = vec!();
for r in self.find_regions(&grid) {
if r.value {
- let mut outline = r.outline(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);
+ let outline = r.outline(&grid.cell_size);
+ let mut floats = outline.iter().map(|p| point!(p.x as f64, p.y as f64)).collect();
+ self.smooth_wall(&mut floats, self.wall_smooth_radius as isize);
+ let wall = WallRegion::new(floats);
+ walls.push(wall);
}
}
walls
}
+
+ fn smooth_wall(&self, points: &mut Vec<Point<f64>>, radius: isize) {
+ let idx = |n| (n as isize + points.len() as isize) as usize % points.len();
+ let mut new_points = points.clone();
+ for i in 0..points.len() {
+ new_points[i] = ((i as isize + 1 - radius)..=(i as isize + radius)) // aggregates all points from -radius to +radius
+ .fold(points[idx(i as isize - radius)], |acc, o| acc + points[idx(o)]) // with addition
+ / (radius * 2 + 1) as f64;
+ }
+ *points = new_points;
+ }
}
////////// REGION //////////////////////////////////////////////////////////////
(min.0, min.1, 1 + max.0 - min.0, 1 + max.1 - min.1)
}
- pub fn outline(&self, scale: usize) -> Vec<Point<isize>> {
+ pub fn outline(&self, scale: &Dimension<usize>) -> Vec<Point<isize>> {
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 directions = vec!((1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1)); // 8 directions rotating right from starting direction right
+ let multiplier = (scale.width as isize, scale.height as isize);
+ let offset = (scale.width as isize / 2, scale.height as isize / 2);
let start = self.find_first_point_of_outline(&rect, &grid);
let mut p = start;
marked[p.x as usize][p.y as usize] = true;
loop {
- outline.push((p + (ox as isize, oy as isize)) * scale as isize);
+ outline.push((p + (ox as isize, oy as isize)) * multiplier + offset);
self.find_next_point_of_outline(&grid, &mut p, &mut directions);
if p == start {
break;
-use common::Point;
+use common::{Point, Dimension};
use core::render::Renderer;
use sprites::SpriteManager;
+use std::rc::Rc;
+use {point, dimen};
mod lvlgen;
pub struct Level {
pub gravity: Point<f64>,
pub grid: Grid<bool>,
- walls: Vec<Vec<Point<isize>>>,
+ walls: Vec<Rc<WallRegion>>,
+ wall_grid: Grid<Vec<Rc<WallEdge>>>,
}
impl Level {
- // pub fn new(gravity: Point<f64>) -> Self {
- // let seed = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() as u32;
- // let mut lvl = Level { gravity, grid: Grid::generate(seed, 10), iterations: 10, walls: vec!() };
- // lvl.filter_regions();
- // lvl
- // }
+ pub fn new(gravity: Point<f64>, grid: Grid<bool>, mut walls: Vec<Rc<WallRegion>>) -> Self {
+ let size = (2560, 1440); // TODO: get actual size from walls or something
+ let wall_grid = Level::build_wall_grid(&mut walls, &size.into());
+ dbg!(&wall_grid.cell_size);
+ Level {
+ gravity,
+ grid,
+ walls,
+ wall_grid,
+ }
+ }
+
+ /// Creates a grid of wall edges for fast lookup
+ fn build_wall_grid(walls: &mut Vec<Rc<WallRegion>>, lvlsize: &Dimension<usize>) -> Grid<Vec<Rc<WallEdge>>> {
+ let size = dimen!(lvlsize.width / 20, lvlsize.height / 20); // TODO: make sure all walls fit within the grid bounds
+ let cs = point!(lvlsize.width / size.width, lvlsize.height / size.height);
+ //let cs = point!(cell_size.width as f64, cell_size.height as f64);
+ let mut grid = vec!(vec!(vec!(); size.height); size.width);
+
+ for wall in walls {
+ for edge in &wall.edges {
+ // TODO: include cells that this edge overlaps
+ for p in &[edge.p1, edge.p2] {
+ let p = point!(p.x as usize, p.y as usize) / cs;
+ grid[0.max(p.x as usize).min(size.width - 1)][0.max(p.y as usize).min(size.height - 1)].push(Rc::clone(edge));
+ }
+ }
+ }
+
+ Grid {
+ size,
+ cell_size: dimen!(cs.x, cs.y),
+ cells: grid,
+ }
+ }
pub fn render(&mut self, renderer: &mut Renderer, _sprites: &SpriteManager) {
+ // original grid
renderer.canvas().set_draw_color((64, 64, 64));
- let size = self.grid.cell_size;
- for x in 0..self.grid.width {
- for y in 0..self.grid.height {
+ let size = &self.grid.cell_size;
+ for x in 0..self.grid.size.width {
+ for y in 0..self.grid.size.height {
if self.grid.cells[x][y] {
- renderer.canvas().fill_rect(sdl2::rect::Rect::new(x as i32 * size as i32, y as i32 * size as i32, size as u32, size as u32)).unwrap();
+ renderer.canvas().fill_rect(sdl2::rect::Rect::new(
+ x as i32 * size.width as i32,
+ y as i32 * size.height as i32,
+ size.width as u32,
+ size.height as u32)).unwrap();
+ }
+ }
+ }
+
+ // wall grid
+ renderer.canvas().set_draw_color((0, 32, 0));
+ let size = &self.wall_grid.cell_size;
+ for x in 0..self.wall_grid.size.width {
+ for y in 0..self.wall_grid.size.height {
+ if !self.wall_grid.cells[x][y].is_empty() {
+ renderer.canvas().fill_rect(sdl2::rect::Rect::new(
+ x as i32 * size.width as i32,
+ y as i32 * size.height as i32,
+ size.width as u32,
+ size.height as u32)).unwrap();
}
}
}
- let off = (size / 2) as i32;
+ // walls
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));
+ for e in &wall.edges {
+ renderer.draw_line(
+ <(i32, i32)>::from(e.p1.to_i32()),
+ <(i32, i32)>::from(e.p2.to_i32()),
+ (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)]
+#[derive(Debug, Default)]
pub struct Grid<T> {
- pub width: usize,
- pub height: usize,
- pub cell_size: usize,
+ pub size: Dimension<usize>,
+ pub cell_size: Dimension<usize>,
pub cells: Vec<Vec<T>>,
}
+
+////////// WALL REGION /////////////////////////////////////////////////////////
+
+#[derive(Debug)]
+pub struct WallRegion {
+ edges: Vec<Rc<WallEdge>>,
+}
+
+impl WallRegion {
+ pub fn new(points: Vec<Point<f64>>) -> Rc<Self> {
+ let mut edges = vec!();
+
+ for i in 0..points.len() {
+ let edge = Rc::new(WallEdge {
+ index: i,
+ p1: points[i],
+ p2: points[(i + 1) % points.len()],
+ });
+ edges.push(edge);
+ }
+
+ Rc::new(WallRegion { edges })
+ }
+
+ #[allow(dead_code)]
+ fn next(&self, index: usize) -> Rc<WallEdge> {
+ let index = (index + 1) % self.edges.len();
+ Rc::clone(&self.edges[index])
+ }
+
+ #[allow(dead_code)]
+ fn previous(&self, index: usize) -> Rc<WallEdge> {
+ let index = (index + self.edges.len() + 1) % self.edges.len();
+ Rc::clone(&self.edges[index])
+ }
+}
+
+////////// WALL EDGE ///////////////////////////////////////////////////////////
+
+#[derive(Debug, Default)]
+struct WallEdge {
+ index: usize,
+ pub p1: Point<f64>,
+ pub p2: Point<f64>,
+}