001package squidpony.squidmath;
002
003/**
004 * An arbitrary-dimensional noise generator; it's not suitable for real-time use, but could be very useful when used
005 * with unconventional axes, particularly during level generation. It produces smooth, non-artifact-prone gradient noise
006 * with lots of rounded "raindrops on a window" shapes. It is very biased toward central results (near 0.0) and only
007 * rarely returns results near -1.0 or 1.0, the extremes of its range.
008 * <br>
009 * <a href="https://i.imgur.com/WR062LY.png">Sample of 2D MitchellNoise</a>.
010 * <br>
011 * Created by Tommy Ettinger on 11/6/2019 using
012 * <a href="https://twitter.com/DonaldM38768041/status/1191771541354078208">code by Donald Mitchell</a>.
013 * @see PhantomNoise PhantomNoise also produces arbitrary-dimensional noise, is faster, and is less centrally-biased
014 */
015public class MitchellNoise {
016    public final int MAX_DIM;
017    //   public Vec4[] grad;
018    public final Vec4[] coef;
019    public final int[] floors;
020    public long seed;
021
022    public MitchellNoise() {
023        MAX_DIM = 20;
024        seed = 0x1234567890ABCDEFL;
025//                grad = new Vec4[MAX_DIM];
026        coef = new Vec4[MAX_DIM];
027        floors = new int[MAX_DIM];
028        for (int i = 0; i < MAX_DIM; i++) {
029            coef[i] = new Vec4();
030        }
031    }
032
033    public MitchellNoise(long seed, int maxDimension) {
034        this.seed = seed;
035        MAX_DIM = Math.max(1, maxDimension);
036//               grad = new Vec4[MAX_DIM];
037        coef = new Vec4[MAX_DIM];
038        floors = new int[MAX_DIM];
039        for (int i = 0; i < MAX_DIM; i++) {
040            coef[i] = new Vec4();
041        }
042    }
043
044    public static class Vec4 {
045        public double x, y, z, w;
046
047        public Vec4() {
048            this(0, 0, 0, 0);
049        }
050
051        public Vec4(double x, double y, double z, double w) {
052            this.x = x;
053            this.y = y;
054            this.z = z;
055            this.w = w;
056        }
057
058        public double dot(Vec4 o) {
059            return x * o.x + y * o.y + z * o.z + w * o.w;
060        }
061
062        public double dot(double ox, double oy, double oz, double ow) {
063            return x * ox + y * oy + z * oz + w * ow;
064        }
065    }
066
067    public static long latticeValue(long lat) {
068        lat = lat * 0xE95E1DD17D35800DL + 0x9E3779B97F4A7C15L;
069        return (lat << lat | lat >>> -lat);
070    }
071
072    public double spline(int dim, long lattice) {
073        int floor;
074        long h0, h1, h2, h3;
075        double kx, ky, kz, kw;
076
077        floor = floors[dim];
078        h0 = latticeValue(floor - 1L + lattice);
079        h1 = latticeValue(floor + lattice);
080        h2 = latticeValue(floor + 1L + lattice);
081        h3 = latticeValue(floor + 2L + lattice);
082
083        if (dim == 0) {
084            kx = h0 >> 13;
085            ky = h1 >> 13;
086            kz = h2 >> 13;
087            kw = h3 >> 13;
088        } else {
089            --dim;
090            kx = spline(dim, h0);
091            ky = spline(dim, h1);
092            kz = spline(dim, h2);
093            kw = spline(dim, h3);
094            ++dim;
095        }
096        return coef[dim].dot(kx, ky, kz, kw);
097    }
098
099    public double arbitraryNoise(long seed, double... points) {
100        int floor, size = points.length;
101        double x, gain = 1.0;
102        for (int i = 0, dim = size - 1; i < size; i++, dim--) {
103            gain *= 1.27;
104            floor = Noise.fastFloor(points[i]);
105            x = points[i] - floor;
106            floors[dim] = floor;
107            coef[dim].x = 1.0/6.0 + x*(x*(0.5 - (1.0/6.0)*x)- 0.5);
108            coef[dim].y = 2.0/3.0 + x*x*(0.5 * x - 1.0);
109            coef[dim].z = 1.0/6.0 + 0.5*x*(1.0 + x*(1.0 - x));
110            coef[dim].w = 1.0/6.0 * x*x*x;
111        }
112        return gain * spline(size-1, seed) * 0.9 * 0x1p-51;
113    }
114}