use crate::{grid_2d::Grid2d, Scalar};
use noise::{NoiseFn, OpenSimplex, Seedable};
use std::ops::Range;
#[derive(Clone)]
pub struct NoiseMapGenerator {
seed: u32,
chunk_size: usize,
zoom: Scalar,
generator: OpenSimplex,
}
impl NoiseMapGenerator {
pub fn new(seed: u32, chunk_size: usize, zoom: Scalar) -> Self {
Self {
seed,
chunk_size: chunk_size.max(1),
zoom,
generator: OpenSimplex::new().set_seed(seed),
}
}
pub fn seed(&self) -> u32 {
self.seed
}
pub fn chunk_size(&self) -> usize {
self.chunk_size
}
pub fn zoom(&self) -> Scalar {
self.zoom
}
pub fn sample_raw(&self, x: Scalar, y: Scalar, z: Scalar) -> Scalar {
self.generator.get([x as f64, y as f64, z as f64]) as Scalar + 1.0 * 0.5
}
pub fn sample(
&self,
coord: (isize, isize),
(col, row): (usize, usize),
margin: usize,
depth: Scalar,
) -> Scalar {
let col = col % (self.chunk_size + margin * 2);
let row = row % (self.chunk_size + margin * 2);
let x = self.zoom
* (coord.0 as Scalar * self.chunk_size as Scalar + col as Scalar - margin as Scalar)
/ self.chunk_size as Scalar;
let y = self.zoom
* (coord.1 as Scalar * self.chunk_size as Scalar + row as Scalar - margin as Scalar)
/ self.chunk_size as Scalar;
self.sample_raw(x, y, depth)
}
#[inline]
pub fn build_chunk(&self, coord: (isize, isize), margin: usize) -> Grid2d<Scalar> {
self.build_chunk_with_depth(coord, margin, 0.0)
}
pub fn build_chunk_with_depth(
&self,
coord: (isize, isize),
margin: usize,
depth: Scalar,
) -> Grid2d<Scalar> {
let mut cells = Vec::with_capacity(self.chunk_size * self.chunk_size);
let mcs = self.chunk_size + margin * 2;
for row in 0..mcs {
for col in 0..mcs {
cells.push(self.sample(coord, (col, row), margin, depth));
}
}
Grid2d::with_cells(mcs, cells)
}
#[inline]
pub fn build_chunks(&self, coord: Range<(isize, isize)>) -> Grid2d<Scalar> {
self.build_chunks_with_depth(coord, 0.0)
}
pub fn build_chunks_with_depth(
&self,
coord: Range<(isize, isize)>,
depth: Scalar,
) -> Grid2d<Scalar> {
let xs = (coord.end.0 - coord.start.0) as usize;
let ys = (coord.end.1 - coord.start.1) as usize;
let mut cells = Vec::with_capacity(self.chunk_size * self.chunk_size * xs * ys);
for row in 0..(self.chunk_size * ys) {
for col in 0..(self.chunk_size * xs) {
let xc = coord.start.0 + (col / self.chunk_size) as isize;
let yc = coord.start.1 + (row / self.chunk_size) as isize;
cells.push(self.sample(
(xc, yc),
(col % self.chunk_size, row % self.chunk_size),
0,
depth,
));
}
}
Grid2d::with_cells(self.chunk_size * xs, cells)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_print_ascii() {
let gen = NoiseMapGenerator::new(0, 8, 4.0);
fn print_chunk(chunk: Grid2d<Scalar>) {
for row in 0..chunk.rows() {
let cells = chunk
.get_row_cells(row)
.unwrap()
.into_iter()
.map(|f| if f >= 0.5 { '#' } else { ' ' })
.collect::<String>();
println!("|{}|", cells);
}
}
let chunk = gen.build_chunk((0, 0), 1);
print_chunk(chunk.clone());
println!();
print_chunk(chunk.get_part((1, 1)..(9, 9)));
}
}