oxygengine_utils/
noise_map_generator.rs1use 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_chunk((0, 0), 1);
129 print_chunk(chunk.clone());
130 println!();
131 print_chunk(chunk.get_part((1, 1)..(9, 9)));
132 }
133}