1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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_chunks((-2, 0)..(3, 2));
        let chunk = gen.build_chunk((0, 0), 1);
        print_chunk(chunk.clone());
        println!();
        print_chunk(chunk.get_part((1, 1)..(9, 9)));
    }
}