Skip to main content

noise/
permutationtable.rs

1use core::fmt;
2use rand::{
3    distr::{Distribution, StandardUniform},
4    seq::SliceRandom,
5    Rng, SeedableRng,
6};
7use rand_xorshift::XorShiftRng;
8
9const TABLE_SIZE: usize = 256;
10
11pub trait NoiseHasher: Send + Sync {
12    fn hash(&self, to_hash: &[isize]) -> usize;
13}
14
15/// A seed table, required by all noise functions.
16///
17/// Table creation is expensive, so in most circumstances you'll only want to
18/// create one of these per generator.
19#[derive(Copy, Clone)]
20pub struct PermutationTable {
21    values: [u8; TABLE_SIZE],
22}
23
24impl Distribution<PermutationTable> for StandardUniform {
25    /// Generates a PermutationTable using a random seed.
26    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> PermutationTable {
27        let mut perm_table = PermutationTable {
28            values: core::array::from_fn(|x| x as u8),
29        };
30        perm_table.values.shuffle(rng);
31
32        perm_table
33    }
34}
35
36impl PermutationTable {
37    /// Deterministically generates a new permutation table based on a `u32` seed value.
38    ///
39    /// Internally this uses a `XorShiftRng`, but we don't really need to worry
40    /// about cryptographic security when working with procedural noise.
41    pub fn new(seed: u32) -> Self {
42        let mut real = [0; 16];
43        real[0] = 1;
44        for i in 1..4 {
45            real[i * 4] = seed as u8;
46            real[(i * 4) + 1] = (seed >> 8) as u8;
47            real[(i * 4) + 2] = (seed >> 16) as u8;
48            real[(i * 4) + 3] = (seed >> 24) as u8;
49        }
50        let mut rng: XorShiftRng = SeedableRng::from_seed(real);
51        rng.random()
52    }
53}
54
55impl NoiseHasher for PermutationTable {
56    fn hash(&self, to_hash: &[isize]) -> usize {
57        let index = to_hash
58            .iter()
59            .map(|&a| (a & 0xff) as usize)
60            .reduce(|a, b| self.values[a] as usize ^ b)
61            .unwrap();
62        self.values[index] as usize
63    }
64}
65
66impl fmt::Debug for PermutationTable {
67    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68        write!(f, "PermutationTable {{ .. }}")
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use crate::{NoiseFn, Perlin, Seedable};
75    use rand::random;
76
77    #[test]
78    fn test_random_seed() {
79        let perlin = Perlin::default().set_seed(random());
80        let _ = perlin.get([1.0, 2.0, 3.0]);
81    }
82
83    #[test]
84    fn test_negative_params() {
85        let perlin = Perlin::default();
86        let _ = perlin.get([-1.0, 2.0, 3.0]);
87    }
88}