001/*******************************************************************************
002 * This software is provided as a supplement to the authors' textbooks on digital
003 * image processing published by Springer-Verlag in various languages and editions.
004 * Permission to use and distribute this software is granted under the BSD 2-Clause
005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause).
006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved.
007 * Visit https://imagingbook.com for additional details.
008 ******************************************************************************/
009
010package imagingbook.common.noise.hashing;
011
012/**
013 * Hash functions for gradient (Perlin) noise.
014 * 
015 * @author WB
016 * @version 2022/11/24
017 */
018public abstract class Hash32 implements HashFunction {
019        
020        private static final int maxInt = 0x7fffffff;
021        
022        private static final int[] smallPrimes = {      // used for N-dimensional hashing
023                73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
024                127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 
025            179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
026            233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 
027            283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 
028            353, 359, 367, 373, 379, 383, 389, 397, 401, 409 
029        };
030        
031        final int seed;
032        
033        protected Hash32(int seed) {
034                this.seed = HashFunction.getRandomSeed(seed);
035        }
036
037        /**
038         * "Hashes" an <tt>int</tt> key to a "pseudo-random" <tt>int</tt> value in [-2147483648, 2147483647]. This method is
039         * to be implemented by concrete subclasses.
040         *
041         * @param key key to be hashed
042         * @return a integer value in [-2147483648, 2147483647].
043         */
044        abstract int hashInt(int key);
045        
046        @Override
047        public double hash(int u) {
048                int h = hashInt(73*u + seed) & maxInt;
049                return (double) h / maxInt;
050        }
051        
052//      public double[] hash(int u, int v) {
053//              int hx = hashInt(59*u + 67*v + seed) & maxInt;  
054//              int hy = hashInt(73*u + 79*v + seed) & maxInt;  
055//              return new double[] {(double) hx / maxInt, (double) hy / maxInt};
056//      }
057        
058        // call 1 hash function and extract 12-bit blocks 
059        @Override
060        public double[] hash(int u, int v) {
061                final int M = 0x00000FFF;
062                int h = hashInt(59*u + 67*v + seed);
063                int hx =  h & M;                // extract bits  0..11
064                int hy = (h >> 12) & M; // extract bits 12..23
065                return new double[] {(double) hx / M, (double) hy / M};
066        }
067        
068        // call 3 different hash functions for 3 dimensions
069//      public double[] hash(int u, int v, int w) {
070//              int M = 0x7FFFFFFF;
071//              int hx = hashInt(59*u + 67*v + 71*w + seed) & M;
072//              int hy = hashInt(73*u + 79*v + 83*w + seed) & M;
073//              int hz = hashInt(89*u + 97*v + 101*w + seed) & M;
074//              return new double[] {(double) hx/M, (double) hy/M, (double) hz/M};
075//      }
076        
077        
078        // call 1 hash function and extract bit blocks
079        @Override
080        public double[] hash(int u, int v, int w) {
081                final int M = 0x000000FF;
082                int h = hashInt(59*u + 67*v + 71*w + seed);
083                int hx =  h & M;                        // extract bits 0..7
084                int hy = (h >> 8) & M;  // extract bits 8..15
085                int hz = (h >> 16) & M; // extract bits 16..23
086                return new double[] {(double) hx / M, (double) hy / M, (double) hz / M};
087        }
088
089        /**
090         * N-dimensional permutation hash; this version does not use any bit splitting. Instead, the hashInt() function is
091         * applied repeatedly for every gradient dimension by using the dimension number (k) as a local seed - in addition
092         * to the global seed (seed).
093         */
094        @Override
095        public double[] hash(int[] p) { 
096                final int N = p.length;
097                double[] g = new double[N];
098                for (int k = 0; k < N; k++) { // dimension k
099                        int sum = seed;
100                        for (int l = 0; l < N; l++) { // dimension k
101                                sum = sum + smallPrimes[l + k] * p[l];
102                        }
103                        int h = hashInt(sum + k) & maxInt;
104                        g[k] = (double) h / maxInt;
105                }
106                return g;
107        }
108
109}