Skip to main content

perlin2/
lib.rs

1use rand::{seq::SliceRandom, Rng};
2#[cfg(feature = "serialize")]
3use serde::{Deserialize, Serialize};
4
5const PERMUTATION_TABLE: [u8; 256] = [
6    151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69,
7    142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219,
8    203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175,
9    74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230,
10    220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76,
11    132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173,
12    186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206,
13    59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163,
14    70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232,
15    178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162,
16    241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204,
17    176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141,
18    128, 195, 78, 66, 215, 61, 156, 180,
19];
20
21fn smoother_step(w: f64) -> f64 {
22    (w * (w * 6. - 15.) + 10.) * w * w * w
23}
24
25fn gradient_dot_weighted(
26    (offset_x, offset_y): (f64, f64),
27    (gradient_x, gradient_y): (f64, f64),
28) -> f64 {
29    smoother_step(1. - offset_x.abs())
30        * smoother_step(1. - offset_y.abs())
31        * ((gradient_x * offset_x) + (gradient_y * offset_y))
32}
33
34#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
35#[derive(Debug, Clone)]
36pub struct Perlin2 {
37    grads: Vec<(f64, f64)>,
38}
39
40impl Perlin2 {
41    pub fn new<R: Rng>(rng: &mut R) -> Self {
42        let mut grads = Vec::with_capacity(256);
43        for i in 0..256 {
44            let angle = (std::f64::consts::PI * 2. * (i as f64)) / 256.;
45            grads.push((angle.cos(), angle.sin()));
46        }
47        grads.shuffle(rng);
48        Self { grads }
49    }
50
51    pub fn noise(&self, (x, y): (f64, f64)) -> f64 {
52        let left_x = x.floor() as i32;
53        let right_x = left_x + 1;
54        let top_y = y.floor() as i32;
55        let bottom_y = top_y + 1;
56        // top
57        let mut gradient_index_y = PERMUTATION_TABLE[(top_y & 0xFF) as usize];
58        // top left
59        let mut gradient_index =
60            PERMUTATION_TABLE[((gradient_index_y as i32 + left_x) & 0xFF) as usize];
61        let mut ret = gradient_dot_weighted(
62            (x - left_x as f64, y - top_y as f64),
63            self.grads[gradient_index as usize],
64        );
65        // top right
66        gradient_index = PERMUTATION_TABLE[((gradient_index_y as i32 + right_x) & 0xFF) as usize];
67        ret += gradient_dot_weighted(
68            (x - right_x as f64, y - top_y as f64),
69            self.grads[gradient_index as usize],
70        );
71        // bottom
72        gradient_index_y = PERMUTATION_TABLE[(bottom_y & 0xFF) as usize];
73        // bottom left
74        gradient_index = PERMUTATION_TABLE[((gradient_index_y as i32 + left_x) & 0xFF) as usize];
75        ret += gradient_dot_weighted(
76            (x - left_x as f64, y - bottom_y as f64),
77            self.grads[gradient_index as usize],
78        );
79        // bottom right
80        gradient_index = PERMUTATION_TABLE[((gradient_index_y as i32 + right_x) & 0xFF) as usize];
81        ret += gradient_dot_weighted(
82            (x - right_x as f64, y - bottom_y as f64),
83            self.grads[gradient_index as usize],
84        );
85        ret
86    }
87
88    pub fn noise01(&self, coord: (f64, f64)) -> f64 {
89        (1. + self.noise(coord)) / 2.
90    }
91}