001package squidpony.squidmath;
002
003import java.io.Serializable;
004
005/**
006 * Really strange noise functions that typically produce curving black and white shapes when rendered.
007 * This technique uses no floating-point math, surprisingly, which helps its performance a little.
008 * The shapes this produces <a href="https://i.imgur.com/mp23254.png">look like this in 2D</a> and
009 * <a href="https://i.imgur.com/qPLZw0k.gifv">look like this in 3D</a>. MerlinNoise implements 2D and 3D noise
010 * interfaces, allowing it to be used with the various support code in Noise like {@link Noise.Layered2D}.
011 * <br>
012 * MerlinNoise can be a good fit for some kinds of procedural generation that need smoothly-curving patterns that don't
013 * look altogether organic, like paint jobs on a space ship. It does tend to produce a somewhat-noticeable grid.
014 * <br>
015 * This is called Merlin noise because it has a roughly-similar implementation to "classic" Perlin Noise (with hashes
016 * per grid point used to blend values), and because working with noise functions makes me feel like a wizard.
017 * This was a completely unrelated noise algorithm that also avoided floating-point math, but was really pretty awful.
018 */
019public class MerlinNoise implements Noise.Noise2D, Noise.Noise3D, Serializable {
020
021    private static final long serialVersionUID = 2L;
022    public static final MerlinNoise instance = new MerlinNoise();
023    public long seed;
024    protected int bits = 8, resolution = 4;
025    private long resSize = 1L << resolution;
026    /**
027     * Constructor for a default MerlinNoise instance with 8-bit output and resolution 3 (yielding 8x8-cell zones that
028     * share their corners). The seed can be set at any point, but it will start at 1.
029     */
030    public MerlinNoise() {
031        seed = 1L;
032    }
033
034    /**
035     * Constructor for a MerlinNoise instance that allows specification of all parameters.
036     * @param seed the seed to use to alter the generated noise in {@link #noise2D(long, long)} and {@link #noise3D(long, long, long)}
037     * @param bits the number of bits to output; typically 8 to produce byte-sized values
038     * @param resolution an exponent that determines the size of a "zone" of cells that blend between the values at the zone's corners; commonly 1-6
039     */
040    public MerlinNoise(long seed, int bits, int resolution)
041    {
042        this.seed = seed;
043        this.bits = bits;
044        this.resolution = resolution & 31;
045        resSize = 1L << this.resolution;
046    }
047
048    public long getSeed() {
049        return seed;
050    }
051
052    public void setSeed(long seed) {
053        this.seed = seed;
054    }
055
056    public int getBits() {
057        return bits;
058    }
059
060    /**
061     * Sets the number of bits this will output; 8 is common to produce byte-sized values between 0 and 255.
062     * This value can be between 1 and 64. If bits is 8, then this should produce values of 255 or 0, plus or minus 1.
063     * If bits is some other value, then it may produce more than two values, or only produce one.
064     * @param bits the number of bits of output each call should generate.
065     */
066    public void setBits(int bits) {
067        this.bits = bits;
068    }
069
070    public int getResolution() {
071        return resolution;
072    }
073
074    /**
075     * Sets the resolution, which is an exponent that determines the width/height of each zone that shares the same four
076     * corners (where only the corners have their own hashed values). If resolution is 1, the size of a zone is 2x2, if
077     * it is 2, then the size of a zone is 4x4, if it is 3, then 8x8, and so on by powers of 2. The resolution can be as
078     * low as 0 (which won't blend corners' hashes at all) or as high as 31, but cannot easily be increased above that
079     * (10 is a really large size for a cell at 1024x1024, and 31 is over 2 billion by 2 billion). This doesn't slow
080     * down significantly (or at all) if resolution is particularly high or low, but this is often between 1 and 6.
081     * @param resolution an int between 0 and 31
082     */
083    public void setResolution(int resolution) {
084        this.resolution = resolution & 31;
085        resSize = 1L << this.resolution;
086    }
087
088//    private static long clorp(long start, long end, long a, long resolution) {
089//        a = a * a * ((3L << resolution) - (a << 1));
090//        end = ((1L << resolution * 3) - a) * start + a * end >> resolution * 3;
091//        return end;
092//    }
093
094    /**
095     * 2D Merlin noise; black and white much of the time but curving instead of angular.
096     *
097     * @param x x input
098     * @param y y input
099     */
100    public long noise2D(long x, long y)
101    {
102        return noise2D(x, y, seed, resolution, bits);
103    }
104
105    /**
106     * 2D Merlin noise; black and white much of the time but curving instead of angular.
107     *
108     * @param x x input
109     * @param y y input
110     * @param seed the seed to use to alter the generated noise
111     */
112    public long noise2D(long x, long y, long seed)
113    {
114        return noise2D(x, y, seed, resolution, bits);
115    }
116
117    /**
118     * 3D Merlin noise; black and white much of the time but curving instead of angular.
119     *
120     * @param x x input
121     * @param y y input
122     * @param z z input
123     */
124    public long noise3D(long x, long y, long z)
125    {
126        return noise3D(x, y, z, seed, resolution, bits);
127    }
128
129    /**
130     * 3D Merlin noise; black and white much of the time but curving instead of angular.
131     *
132     * @param x x input
133     * @param y y input
134     * @param z z input
135     * @param seed the seed to use to alter the generated noise
136     */
137    public long noise3D(long x, long y, long z, long seed)
138    {
139        return noise3D(x, y, z, seed, resolution, bits);
140    }
141    
142    private static long lorp(long start, long end, long a, long resolution) {
143        return ((1L << resolution) - a) * start + a * end >>> resolution;
144    }
145    /**
146     * 2D Merlin noise; black and white much of the time but curving instead of angular.
147     *
148     * @param x x input
149     * @param y y input
150     * @param state state to adjust the output
151     * @param resolution the number of cells between "vertices" where one hashed value is used fully
152     * @param bits how many bits should be used for each (signed long) output; often this is 8 to output a byte
153     * @return noise from {@code -(1L << bits)} to {@code (1L << bits) - 1L}, both inclusive
154     */
155    public static long noise2D(long x, long y, long state, int resolution, int bits) {
156        long xb = (x >> resolution) + state, yb = (y >> resolution) - state,
157                xr = (x & ~(-1L << resolution)), yr = (y & ~(-1L << resolution)),
158                x0 = LightRNG.determine(xb), x1 = LightRNG.determine(xb + 1),
159                y0 = LightRNG.determine(yb), y1 = LightRNG.determine(yb + 1),
160                x0y0 = (x0 * y0 ^ x0 - y0) >>> resolution, x1y0 = (x1 * y0 ^ x1 - y0) >>> resolution,
161                x0y1 = (x0 * y1 ^ x0 - y1) >>> resolution, x1y1 = (x1 * y1 ^ x1 - y1) >>> resolution;
162
163//                x0y0 = Noise.PointHash.hashAll(xb, yb, state) >> resolution, x1y0 = Noise.PointHash.hashAll(xb + 1, yb, state) >> resolution,
164//                x0y1 = Noise.PointHash.hashAll(xb, yb + 1, state) >> resolution, x1y1 = Noise.PointHash.hashAll(xb + 1, yb + 1, state) >> resolution;
165
166//                x0y0 = (x0y0b >> 2) + (ThrustAltRNG.determine(x0y0b + 1) >> 2)
167//                        + (ThrustAltRNG.determine(x0y0b + 2) >> 2) + (ThrustAltRNG.determine(x0y0b + 3) >> 2),
168//                x1y0 = (x1y0b >> 2) + (ThrustAltRNG.determine(x1y0b + 1) >> 2)
169//                        + (ThrustAltRNG.determine(x1y0b + 2) >> 2) + (ThrustAltRNG.determine(x1y0b + 3) >> 2),
170//                x0y1 = (x0y1b >> 2) + (ThrustAltRNG.determine(x0y1b + 1) >> 2)
171//                        + (ThrustAltRNG.determine(x0y1b + 2) >> 2) + (ThrustAltRNG.determine(x0y1b + 3) >> 2),
172//                x1y1 = (x1y1b >> 2) + (ThrustAltRNG.determine(x1y1b + 1) >> 2)
173//                        + (ThrustAltRNG.determine(x1y1b + 2) >> 2) + (ThrustAltRNG.determine(x1y1b + 3) >> 2)
174        return lorp(lorp(x0y0, x1y0, xr, resolution), lorp(x0y1, x1y1, xr, resolution), yr, resolution)
175                >>> -resolution - bits; // >> (- bits - resolution & 63)
176    }
177
178    /**
179     * 3D merlin noise.
180     *
181     * @param x x input
182     * @param y y input
183     * @param z z input
184     * @param state state to adjust the output
185     * @param resolution the number of cells between "vertices" where one hashed value is used fully
186     * @param bits how many bits should be used for each (signed long) output; often this is 8 to output a byte
187     * @return noise from {@code -(1L << bits)} to {@code (1L << bits) - 1L}, both inclusive
188     */
189    public static long noise3D(long x, long y, long z, long state, int resolution, int bits) {
190        long xb = (x >> resolution) + state, yb = (y >> resolution) - state, zb = (z >> resolution) + (0x9E3779B97F4A7C15L ^ state),
191                xr = x & ~(-1L << resolution), yr = y & ~(-1L << resolution), zr = z & ~(-1L << resolution),
192                x0 = LightRNG.determine(xb), x1 = LightRNG.determine(xb + 1),
193                y0 = LightRNG.determine(yb), y1 = LightRNG.determine(yb + 1),
194                z0 = LightRNG.determine(zb), z1 = LightRNG.determine(zb + 1),
195                x0y0z0 = (x0 * y0 * z0 ^ x0 - y0 + (z0 - x0 << 32 | y0 - z0 >>> 32)) >>> resolution, x1y0z0 = (x1 * y0 * z0 ^ x1 - y0 + (z0 - x1 << 32 | y0 - z0 >>> 32)) >>> resolution,
196                x0y1z0 = (x0 * y1 * z0 ^ x0 - y1 + (z0 - x0 << 32 | y1 - z0 >>> 32)) >>> resolution, x1y1z0 = (x1 * y1 * z0 ^ x1 - y1 + (z0 - x1 << 32 | y1 - z0 >>> 32)) >>> resolution,
197                x0y0z1 = (x0 * y0 * z1 ^ x0 - y0 + (z1 - x0 << 32 | y0 - z1 >>> 32)) >>> resolution, x1y0z1 = (x1 * y0 * z1 ^ x1 - y0 + (z1 - x1 << 32 | y0 - z1 >>> 32)) >>> resolution,
198                x0y1z1 = (x0 * y1 * z1 ^ x0 - y1 + (z1 - x0 << 32 | y1 - z1 >>> 32)) >>> resolution, x1y1z1 = (x1 * y1 * z1 ^ x1 - y1 + (z1 - x1 << 32 | y1 - z1 >>> 32)) >>> resolution;
199
200//                x0y0z0 = Noise.PointHash.hashAll(xb, yb, zb, state) >> resolution, x1y0z0 = Noise.PointHash.hashAll(xb + 1, yb, zb, state) >> resolution,
201//                x0y1z0 = Noise.PointHash.hashAll(xb, yb + 1, zb, state) >> resolution, x1y1z0 = Noise.PointHash.hashAll(xb + 1, yb + 1, zb, state) >> resolution,
202//                x0y0z1 = Noise.PointHash.hashAll(xb, yb, zb + 1, state) >> resolution, x1y0z1 = Noise.PointHash.hashAll(xb + 1, yb, zb + 1, state) >> resolution,
203//                x0y1z1 = Noise.PointHash.hashAll(xb, yb + 1, zb + 1, state) >> resolution, x1y1z1 = Noise.PointHash.hashAll(xb + 1, yb + 1, zb + 1, state) >> resolution;
204
205//                x0y0z0 = (x0y0z0b >> 2) + (ThrustAltRNG.determine(x0y0z0b + 1) >> 2)
206//                        + (ThrustAltRNG.determine(x0y0z0b + 2) >> 2) + (ThrustAltRNG.determine(x0y0z0b + 3) >> 2),
207//                x1y0z0 = (x1y0z0b >> 2) + (ThrustAltRNG.determine(x1y0z0b + 1) >> 2)
208//                        + (ThrustAltRNG.determine(x1y0z0b + 2) >> 2) + (ThrustAltRNG.determine(x1y0z0b + 3) >> 2),
209//                x0y1z0 = (x0y1z0b >> 2) + (ThrustAltRNG.determine(x0y1z0b + 1) >> 2)
210//                        + (ThrustAltRNG.determine(x0y1z0b + 2) >> 2) + (ThrustAltRNG.determine(x0y1z0b + 3) >> 2),
211//                x1y1z0 = (x1y1z0b >> 2) + (ThrustAltRNG.determine(x1y1z0b + 1) >> 2)
212//                        + (ThrustAltRNG.determine(x1y1z0b + 2) >> 2) + (ThrustAltRNG.determine(x1y1z0b + 3) >> 2),
213//                x0y0z1 = (x0y0z1b >> 2) + (ThrustAltRNG.determine(x0y0z1b + 1) >> 2)
214//                        + (ThrustAltRNG.determine(x0y0z1b + 2) >> 2) + (ThrustAltRNG.determine(x0y0z1b + 3) >> 2),
215//                x1y0z1 = (x1y0z1b >> 2) + (ThrustAltRNG.determine(x1y0z1b + 1) >> 2)
216//                        + (ThrustAltRNG.determine(x1y0z1b + 2) >> 2) + (ThrustAltRNG.determine(x1y0z1b + 3) >> 2),
217//                x0y1z1 = (x0y1z1b >> 2) + (ThrustAltRNG.determine(x0y1z1b + 1) >> 2)
218//                        + (ThrustAltRNG.determine(x0y1z1b + 2) >> 2) + (ThrustAltRNG.determine(x0y1z1b + 3) >> 2),
219//                x1y1z1 = (x1y1z1b >> 2) + (ThrustAltRNG.determine(x1y1z1b + 1) >> 2)
220//                        + (ThrustAltRNG.determine(x1y1z1b + 2) >> 2) + (ThrustAltRNG.determine(x1y1z1b + 3) >> 2);
221//        long xm = lorp(x0y0z0, x1y0z0, xr, resolution),
222//                xn = lorp(x0y1z0, x1y1z0, xr, resolution),
223//                xo = lorp(x0y0z1, x1y0z1, xr, resolution),
224//                xp = lorp(x0y1z1, x1y1z1, xr, resolution),
225//                ym = lorp(xm, xn, yr, resolution),
226//                yn = lorp(xo, xp, yr, resolution),
227//                zm = lorp(ym, yn, zr, resolution);
228//         zm >>>= -resolution-bits;
229//         return zm;
230        return lorp(lorp(lorp(x0y0z0, x1y0z0, xr, resolution), lorp(x0y1z0, x1y1z0, xr, resolution), yr, resolution),
231                lorp(lorp(x0y0z1, x1y0z1, xr, resolution), lorp(x0y1z1, x1y1z1, xr, resolution), yr, resolution), zr, resolution)
232                >>> -resolution - bits;
233    }
234
235    /**
236     * Generates higher-quality continuous-style noise than the other methods, but requires pre-calculating a grid.
237     * Does allow taking a seed because internally it uses a DiverRNG to quickly generate initial white noise before
238     * processing it into more continuous noise. This generates a lot of random numbers (at least 1 + 14 * height, or
239     * more if width is greater than 64), so DiverRNG's high speed and fairly good period are both assets here.
240     * <br>
241     * The 2D int array this produces has ints ranging from 1 to 255, with extreme values very unlikely. Because 0 is
242     * impossible for this to generate, it should be fine to use values from this as denominators in division.
243     *
244     * @param width  the width of the 2D int array to generate
245     * @param height the height of the 2D int array to generate
246     * @param seed   the RNG seed to use when pseudo-randomly generating the initial white noise this then processes
247     * @return a 2D int array where each int should be between 1 and 255, inclusive
248     */
249    public static int[][] preCalcNoise2D(int width, int height, long seed) {
250        DiverRNG random = new DiverRNG(seed);
251        int w = (width << 1) + 2, h = (height << 1) + 2;
252        GreasedRegion[] regions = new GreasedRegion[]{
253                new GreasedRegion(random, w, h).retract().expand(3), new GreasedRegion(random, w, h).retract().expand(3),
254                new GreasedRegion(random, w, h).retract().expand(3), new GreasedRegion(random, w, h).retract().expand(3),
255                new GreasedRegion(random, w, h).retract().expand(3), new GreasedRegion(random, w, h).retract().expand(3),
256                new GreasedRegion(random, w, h).retract().expand(3)
257        };
258        int[][] data = GreasedRegion.bitSum(regions);
259
260        regions = new GreasedRegion[]{
261                new GreasedRegion(random, w, h).retract().expand(3), new GreasedRegion(random, w, h).retract().expand(3),
262                new GreasedRegion(random, w, h).retract().expand(3), new GreasedRegion(random, w, h).retract().expand(3),
263                new GreasedRegion(random, w, h).retract().expand(3), new GreasedRegion(random, w, h).retract().expand(3),
264                new GreasedRegion(random, w, h).retract().expand(3)
265        };
266        int[][] data2 = GreasedRegion.bitSum(regions), data3 = new int[width][height];
267        for (int x = 0; x < w; x++) {
268            for (int y = 0; y < h; y++) {
269                data[x][y] += 128 - data2[x][y];
270            }
271        }
272        for (int x = 0, dx = 1; x < width; x++, dx += 2) {
273            for (int y = 0, dy = 1; y < height; y++, dy += 2) {
274                data3[x][y] = ((data[dx][dy] << 2) + data[dx - 1][dy] + data[dx + 1][dy] + data[dx][dy + 1] + data[dx][dy - 1]) >>> 3;
275            }
276        }
277        return data3;
278        /*
279        int[][] data = GreasedRegion.bitSum(regions);
280        return GreasedRegion.selectiveNegate(data, new GreasedRegion(random, width, height), 0xff);
281        */
282    }
283
284//    public static void main(String[] args)
285//    {
286//        long state = 9999L, bits = 32;
287//        for (int resolution = 0; resolution < 4; resolution++) {
288//            for (int x = 0; x < 10; x++) {
289//                for (int y = 0; y < 10; y++) {
290//                    long xb = x >>> resolution, yb = y >>> resolution, xr = (x & ~(-1L << resolution)), yr = (y & ~(-1L << resolution)),
291//                            x0y0 = Noise.PointHash.hashAll(xb, yb, state) >> resolution, x1y0 = Noise.PointHash.hashAll(xb + 1, yb, state) >> resolution,
292//                            x0y1 = Noise.PointHash.hashAll(xb, yb + 1, state) >> resolution, x1y1 = Noise.PointHash.hashAll(xb + 1, yb + 1, state) >> resolution;
293//                    long xly0 = lorp(x0y0, x1y0, xr, resolution), xly1 = lorp(x0y1, x1y1, xr, resolution),
294//                            yl = lorp(xly0, xly1, yr, resolution);
295//                    System.out.printf("x: %d, y: %d, r: %d = %08X\n", x, y, resolution, yl);// >> (- bits - resolution & 63));
296//                }
297//            }
298//        }
299//    }
300
301    @Override
302    public double getNoise(double x, double y) {
303        return 1 - (noise2D(Noise.longFloor(x * resSize), Noise.longFloor(y * resSize), seed, resolution << 1, 1) << 1);
304    }
305
306    @Override
307    public double getNoiseWithSeed(double x, double y, long seed) {
308        return 1 - (noise2D(Noise.longFloor(x * resSize), Noise.longFloor(y * resSize), seed, resolution << 1, 1) << 1);
309    }
310
311    @Override
312    public double getNoise(double x, double y, double z) {
313        return 1 - (noise3D(Noise.longFloor(x * resSize), Noise.longFloor(y * resSize), Noise.longFloor(z * resSize), seed, resolution << 1, 1) << 1);
314    }
315
316    @Override
317    public double getNoiseWithSeed(double x, double y, double z, long seed) {
318        return 1 - (noise3D(Noise.longFloor(x * resSize), Noise.longFloor(y * resSize), Noise.longFloor(z * resSize), seed, resolution << 1, 1) << 1);
319    }
320}