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-2016 Wilhelm Burger, Mark J. Burge. All rights reserved. 
007 * Visit http://imagingbook.com for additional details.
008 *******************************************************************************/
009
010package imagingbook.pub.noise.perlin;
011
012import imagingbook.lib.math.Matrix;
013import imagingbook.pub.noise.hashing.HashFun;
014
015/**
016 * Gradient (Perlin) noise implementation.
017 * This class implements an N-dimensional Perlin noise generator.
018 */
019public class PerlinNoiseGenNd extends PerlinNoiseGen {
020        
021        final int N;            // dimensionality, default 1
022        final int K;                    // number of hypercube vertices, default 2
023        final int[][] Q;                        // vertex coordinates of the unit hypercube
024        
025        public PerlinNoiseGenNd(int N, double f_min, double f_max, double persistence, HashFun hf) {
026                super(f_min, f_max, persistence, hf);
027                this.N = N;
028                this.K = (int) Math.pow(2, N);  // number of hypercube vertices
029                this.Q = new int[K][N];                 // vertices of the unit hypercube
030                for (int j = 0; j < K; j++) {
031                        Q[j] = vertex(j, N);
032                }
033        }
034        
035        /**
036         * N-dim combined (multi-frequency) Perlin noise function. 
037         * @param X Interpolation position X (N-dimensional).
038         * @return The value of the combined Perlin
039         * noise function for the N-dimensional position X.
040         */
041        public double NOISE(double[] X) {
042                double sum = 0;
043                for (int i = 0; i < F.length; i++) { // for all frequencies
044                        sum = sum + A[i] * noise(Matrix.multiply(F[i], X));
045                }
046                return sum;
047        }
048        
049        /**
050         * 2D elementary (single-frequency) Perlin noise function. 
051         * @param X Interpolation position X (N-dimensional).
052         * @return The value of the elementary Perlin
053         * noise function for the N-dimensional position X.
054         */
055        public double noise(double[] X) {
056                int[] P0 = Matrix.floor(X);             // origin of hypercube around X
057                 
058                // get the 2^N gradient vectors for all hypercube corners:
059                double[][] G = new double[K][N];
060                for(int j=0; j<K; j++) {        
061                        G[j] = gradient(Matrix.add(P0,Q[j]));                   // gradient vector at cube corner j
062                }
063                
064                double[] X01 = Matrix.subtract(X,P0);                                   // X01[k] are in [0,1]
065                
066                // get the 2^N gradient values at all vertices for position X
067                double[] W = new double[K];
068                for (int j = 0; j < K; j++) {   
069                        W[j] = Matrix.dotProduct(G[j], Matrix.subtract(X01, Q[j]));
070                }
071                
072                return interpolate(X01, W, 0);
073        }
074        
075        /**
076         * @param p discrete position.
077         * @return A pseudo-random gradient vector for 
078         * the discrete lattice point p (N-dimensional).
079         */
080        double[] gradient(int[] p) {    
081                if (p.length == 2) {
082                        return gradient(p[0],p[1]);
083                }
084                // hash() always returns a new double[], g[i] in [0,1]
085                double[] g = hashFun.hash(p);   // STILL TO BE DONE!!!
086                for (int i=0; i<g.length; i++) {
087                        g[i] = 2.0 * g[i] - 1;
088                }
089                return g;
090        }
091        
092        /**
093         * Local interpolation function.
094         * @param X01 Interpolation position in [0,1]^N
095         * @param WW  A vector of length 2^(N-d) with
096         * the tangent values for the hypercube corners.
097         * @param k The interpolation dimension (axis).
098         * @return  The interpolated noise value at position X01.
099         */
100        double interpolate(double[] X01, double[] WW, int k) { 
101                if (WW.length == 1) {                   // (d == N)
102                        return WW[0];                           // done, end of recursion
103                }
104                else {                                                  // d < N
105                        double x01 = X01[k];            // select dimension d of vector X
106                        double s = this.s(x01);                 // blending function
107                        int M = WW.length/2;
108                        double[] W = new double[M];             // W is half the length of WW
109                        for (int i=0; i<M; i++) {
110                                double wa = WW[2*i];
111                                double wb = WW[2*i+1];
112                                W[i] = (1-s)*wa + s*wb;         // the actual interpolation
113                        }
114                        return interpolate(X01, W, k+1);
115                }
116        }
117
118        /**
119         * @param j Vertex number (0..2^N-1)
120         * @param N Dimension of the hypercube
121         * @return The coordinate vector for vertex j of the N-dimensional
122         * hypercube.
123         */
124        private int[] vertex(int j, int N) { 
125                int[] v = new int[N];
126                // copy the bit representation of j into v,
127                // v[0] is the most significant bit 
128                for (int k = 0; k < N; k++) {
129                         v[k] = j & 0x00000001;         // select least significant bit (bit 0)
130                         j = j >>> 1;                           // j <- j/2
131                }
132                return v;
133        }
134        
135
136
137        
138        // from 2D example
139        double[] gradient(int i, int j) {
140                double[] g = hashFun.hash(i,j);         // hash() always returns a new double[]
141                g[0] = 2.0 * g[0] - 1;
142                g[1] = 2.0 * g[1] - 1;
143                return g;
144        }
145
146//      private int Power2(int k) {
147//              return 1 << k;
148//      }
149
150}