oxygengine_utils/
noise_map_generator.rs

1use crate::{grid_2d::Grid2d, Scalar};
2use noise::{NoiseFn, OpenSimplex};
3use std::ops::Range;
4
5#[derive(Clone)]
6pub struct NoiseMapGenerator {
7    seed: u32,
8    chunk_size: usize,
9    zoom: Scalar,
10    generator: OpenSimplex,
11}
12
13impl NoiseMapGenerator {
14    pub fn new(seed: u32, chunk_size: usize, zoom: Scalar) -> Self {
15        Self {
16            seed,
17            chunk_size: chunk_size.max(1),
18            zoom,
19            generator: OpenSimplex::new(seed),
20        }
21    }
22
23    pub fn seed(&self) -> u32 {
24        self.seed
25    }
26
27    pub fn chunk_size(&self) -> usize {
28        self.chunk_size
29    }
30
31    pub fn zoom(&self) -> Scalar {
32        self.zoom
33    }
34
35    pub fn sample_raw(&self, x: Scalar, y: Scalar, z: Scalar) -> Scalar {
36        self.generator.get([x as f64, y as f64, z as f64]) as Scalar + 1.0 * 0.5
37    }
38
39    pub fn sample(
40        &self,
41        coord: (isize, isize),
42        (col, row): (usize, usize),
43        margin: usize,
44        depth: Scalar,
45    ) -> Scalar {
46        let col = col % (self.chunk_size + margin * 2);
47        let row = row % (self.chunk_size + margin * 2);
48        let x = self.zoom
49            * (coord.0 as Scalar * self.chunk_size as Scalar + col as Scalar - margin as Scalar)
50            / self.chunk_size as Scalar;
51        let y = self.zoom
52            * (coord.1 as Scalar * self.chunk_size as Scalar + row as Scalar - margin as Scalar)
53            / self.chunk_size as Scalar;
54        self.sample_raw(x, y, depth)
55    }
56
57    #[inline]
58    pub fn build_chunk(&self, coord: (isize, isize), margin: usize) -> Grid2d<Scalar> {
59        self.build_chunk_with_depth(coord, margin, 0.0)
60    }
61
62    pub fn build_chunk_with_depth(
63        &self,
64        coord: (isize, isize),
65        margin: usize,
66        depth: Scalar,
67    ) -> Grid2d<Scalar> {
68        let mut cells = Vec::with_capacity(self.chunk_size * self.chunk_size);
69        let mcs = self.chunk_size + margin * 2;
70        for row in 0..mcs {
71            for col in 0..mcs {
72                cells.push(self.sample(coord, (col, row), margin, depth));
73            }
74        }
75        Grid2d::with_cells(mcs, cells)
76    }
77
78    #[inline]
79    pub fn build_chunks(&self, coord: Range<(isize, isize)>) -> Grid2d<Scalar> {
80        self.build_chunks_with_depth(coord, 0.0)
81    }
82
83    pub fn build_chunks_with_depth(
84        &self,
85        coord: Range<(isize, isize)>,
86        depth: Scalar,
87    ) -> Grid2d<Scalar> {
88        let xs = (coord.end.0 - coord.start.0) as usize;
89        let ys = (coord.end.1 - coord.start.1) as usize;
90        let mut cells = Vec::with_capacity(self.chunk_size * self.chunk_size * xs * ys);
91        for row in 0..(self.chunk_size * ys) {
92            for col in 0..(self.chunk_size * xs) {
93                let xc = coord.start.0 + (col / self.chunk_size) as isize;
94                let yc = coord.start.1 + (row / self.chunk_size) as isize;
95                cells.push(self.sample(
96                    (xc, yc),
97                    (col % self.chunk_size, row % self.chunk_size),
98                    0,
99                    depth,
100                ));
101            }
102        }
103        Grid2d::with_cells(self.chunk_size * xs, cells)
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn test_print_ascii() {
113        let gen = NoiseMapGenerator::new(0, 8, 4.0);
114
115        fn print_chunk(chunk: Grid2d<Scalar>) {
116            for row in 0..chunk.rows() {
117                let cells = chunk
118                    .get_row_cells(row)
119                    .unwrap()
120                    .into_iter()
121                    .map(|f| if f >= 0.5 { '#' } else { ' ' })
122                    .collect::<String>();
123                println!("|{}|", cells);
124            }
125        }
126
127        // let chunk = gen.build_chunks((-2, 0)..(3, 2));
128        let chunk = gen.build_chunk((0, 0), 1);
129        print_chunk(chunk.clone());
130        println!();
131        print_chunk(chunk.get_part((1, 1)..(9, 9)));
132    }
133}