From 2e4fba61e481612df78f8734e8de2390ce819104 Mon Sep 17 00:00:00 2001 From: klangner Date: Thu, 19 Nov 2020 20:53:34 +0100 Subject: [PATCH] implemented Voronoi Hive. Fixed: 30 --- README.md | 2 +- src/filter/mod.rs | 2 + src/filter/voronoi.rs | 98 +++++++++++++++++++++++++++++++++++++++++++ src/map.rs | 6 ++- 4 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 src/filter/voronoi.rs diff --git a/README.md b/README.md index 3b8618c..c5d9ed5 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ This library consists of different map filters which can be combined to create c * [ ] Prefabs * [x] Room corridors nearest * [x] Simple rooms - * [ ] Voronoi hive + * [x] Voronoi hive * [ ] Wave Function Collapse diff --git a/src/filter/mod.rs b/src/filter/mod.rs index 0c191a6..5df3895 100644 --- a/src/filter/mod.rs +++ b/src/filter/mod.rs @@ -12,6 +12,7 @@ pub mod noise_generator; pub mod simple_rooms; pub mod rooms_corridors_nearest; pub mod starting_point; +pub mod voronoi; pub use bsp_interior::BspInterior; pub use bsp_rooms::BspRooms; @@ -24,4 +25,5 @@ pub use noise_generator::NoiseGenerator; pub use simple_rooms::SimpleRooms; pub use rooms_corridors_nearest::NearestCorridors; pub use starting_point::{AreaStartingPosition, XStart, YStart}; +pub use voronoi::VoronoiHive; diff --git a/src/filter/voronoi.rs b/src/filter/voronoi.rs new file mode 100644 index 0000000..b7777cf --- /dev/null +++ b/src/filter/voronoi.rs @@ -0,0 +1,98 @@ +//! Example generator usage: +//! ``` +//! use rand::prelude::*; +//! use mapgen::{Map, MapFilter}; +//! use mapgen::filter::VoronoiHive; +//! +//! let mut rng = StdRng::seed_from_u64(100); +//! let gen = VoronoiHive::new(); +//! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); +//! +//! assert_eq!(map.width, 80); +//! assert_eq!(map.height, 50); +//! ``` +//! + +use rand::prelude::*; +use crate::MapFilter; +use crate::{ + map::{Map, TileType}, + random::Rng, + geometry::Point, +}; + + +pub struct VoronoiHive { + n_seeds: usize, +} + + +impl MapFilter for VoronoiHive { + fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { + self.build(rng, map) + } +} + +impl VoronoiHive { + pub fn new() -> Box { + Box::new(VoronoiHive{ + n_seeds: 64, + }) + } + + fn build(&self, rng: &mut StdRng, map: &Map) -> Map { + let mut new_map = map.clone(); + let seeds = self.generate_seeds(rng, map.width, map.height); + + let mut voronoi_distance = vec![(0, 0.0f32) ; self.n_seeds]; + let mut voronoi_membership : Vec = vec![0 ; map.width as usize * map.height as usize]; + for (i, vid) in voronoi_membership.iter_mut().enumerate() { + let x = i % map.width; + let y = i / map.width; + + for (seed, pos) in seeds.iter().enumerate() { + let distance = pos.distance_to(&Point::new(x, y)); + voronoi_distance[seed] = (seed, distance); + } + + voronoi_distance.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap()); + + *vid = voronoi_distance[0].0 as i32; + } + + for y in 1..new_map.height-1 { + for x in 1..new_map.width-1 { + let mut neighbors = 0; + let my_idx = new_map.xy_idx(x, y); + let my_seed = voronoi_membership[my_idx]; + if voronoi_membership[new_map.xy_idx(x-1, y)] != my_seed { neighbors += 1; } + if voronoi_membership[new_map.xy_idx(x+1, y)] != my_seed { neighbors += 1; } + if voronoi_membership[new_map.xy_idx(x, y-1)] != my_seed { neighbors += 1; } + if voronoi_membership[new_map.xy_idx(x, y+1)] != my_seed { neighbors += 1; } + + if neighbors < 2 { + new_map.set_tile(x, y, TileType::Floor); + } + } + } + + new_map + } + + /// Generate random seeds + fn generate_seeds(&self, rng: &mut StdRng, width: usize, height: usize) -> Vec { + let mut seeds: Vec = Vec::new(); + + while seeds.len() < self.n_seeds { + let vx = rng.roll_dice(1, width-1); + let vy = rng.roll_dice(1, height-1); + let candidate = Point::new(vx, vy); + if !seeds.contains(&candidate) { + seeds.push(candidate); + } + } + + seeds + } + +} \ No newline at end of file diff --git a/src/map.rs b/src/map.rs index 149dad3..085057b 100644 --- a/src/map.rs +++ b/src/map.rs @@ -106,10 +106,14 @@ impl Map { /// Modify tile at the given location pub fn set_tile(&mut self, x: usize, y: usize, tile: TileType) { if x < self.width && y < self.height { - let idx = (y as usize) * self.width + (x as usize); + let idx = self.xy_idx(x as usize, y as usize); self.tiles[idx] = tile; } } + + pub fn xy_idx(&self, x: usize, y: usize) -> usize { + y * self.width + x + } /// Create room on the map at given location /// Room is created by setting all tiles in the room to the Floor