noise/
permutationtable.rs

1use core::fmt;
2use rand::{
3    distributions::{Distribution, Standard},
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 Standard {
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: [0; TABLE_SIZE],
29        };
30
31        perm_table
32            .values
33            .iter_mut()
34            .enumerate()
35            .for_each(|(i, b)| *b = i as u8);
36        perm_table.values.shuffle(rng);
37
38        perm_table
39    }
40}
41
42impl PermutationTable {
43    /// Deterministically generates a new permutation table based on a `u32` seed value.
44    ///
45    /// Internally this uses a `XorShiftRng`, but we don't really need to worry
46    /// about cryptographic security when working with procedural noise.
47    pub fn new(seed: u32) -> Self {
48        let mut real = [0; 16];
49        real[0] = 1;
50        for i in 1..4 {
51            real[i * 4] = seed as u8;
52            real[(i * 4) + 1] = (seed >> 8) as u8;
53            real[(i * 4) + 2] = (seed >> 16) as u8;
54            real[(i * 4) + 3] = (seed >> 24) as u8;
55        }
56        let mut rng: XorShiftRng = SeedableRng::from_seed(real);
57        rng.gen()
58    }
59}
60
61impl NoiseHasher for PermutationTable {
62    fn hash(&self, to_hash: &[isize]) -> usize {
63        let index = to_hash
64            .iter()
65            .map(|&a| (a & 0xff) as usize)
66            .reduce(|a, b| self.values[a] as usize ^ b)
67            .unwrap();
68        self.values[index] as usize
69    }
70}
71
72impl fmt::Debug for PermutationTable {
73    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
74        write!(f, "PermutationTable {{ .. }}")
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use crate::{NoiseFn, Perlin, Seedable};
81    use rand::random;
82
83    #[test]
84    fn test_random_seed() {
85        let perlin = Perlin::default().set_seed(random());
86        let _ = perlin.get([1.0, 2.0, 3.0]);
87    }
88
89    #[test]
90    fn test_negative_params() {
91        let perlin = Perlin::default();
92        let _ = perlin.get([-1.0, 2.0, 3.0]);
93    }
94}