001package squidpony.squidgrid.mapping;
002
003import squidpony.ArrayTools;
004import squidpony.LZSPlus;
005import squidpony.squidmath.Coord;
006import squidpony.squidmath.FastNoise;
007import squidpony.squidmath.GWTRNG;
008import squidpony.squidmath.GreasedRegion;
009import squidpony.squidmath.IntVLA;
010import squidpony.squidmath.Noise;
011import squidpony.squidmath.Noise.Noise2D;
012import squidpony.squidmath.Noise.Noise3D;
013import squidpony.squidmath.Noise.Noise4D;
014import squidpony.squidmath.NumberTools;
015
016import java.io.Serializable;
017import java.util.Arrays;
018
019/**
020 * Can be used to generate world maps with a wide variety of data, starting with height, temperature and moisture.
021 * From there, you can determine biome information in as much detail as your game needs, with default implementations
022 * available; one assigns a single biome to each cell based on heat/moisture, and the other gives a gradient between two
023 * biome types for every cell. The maps this produces with {@link SphereMap} are valid for spherical world projections,
024 * while the maps from {@link TilingMap} are for toroidal world projections and will wrap from edge to opposite edge
025 * seamlessly thanks to <a href="https://www.gamedev.net/blog/33/entry-2138456-seamless-noise/">a technique from the
026 * Accidental Noise Library</a> that involves getting a 2D slice of 4D Simplex noise. Because of how Simplex noise
027 * works, this also allows extremely high zoom levels for all types of map as long as certain parameters are within
028 * reason. Other world maps produce more conventional shapes, like {@link SpaceViewMap} and {@link RotatingSpaceMap}
029 * make a view of a marble-like world from space, and others make more unconventional shapes, like {@link EllipticalMap}
030 * or {@link EllipticalHammerMap}, which form a 2:1 ellipse shape that accurately keeps sizes but not relative shapes,
031 * {@link RoundSideMap}, which forms a pill-shape, and {@link HyperellipticalMap}, which takes parameters so it can fit
032 * any shape between a circle or ellipse and a rectangle (the default is a slightly squared-off ellipse). You can access
033 * the height map with the {@link #heightData} field, the heat map with the {@link #heatData} field, the moisture map
034 * with the {@link #moistureData} field, and a special map that stores ints representing the codes for various ranges of
035 * elevation (0 to 8 inclusive, with 0 the deepest ocean and 8 the highest mountains) with {@link #heightCodeData}. The
036 * last map should be noted as being the simplest way to find what is land and what is water; any height code 4 or
037 * greater is land, and any height code 3 or less is water.
038 * <br>
039 * Biome mapping is likely to need customization per-game, but some good starting points are {@link SimpleBiomeMapper},
040 * which stores one biome per cell, and {@link DetailedBiomeMapper}, which gives each cell a midway value between two
041 * biomes.
042 */
043public abstract class WorldMapGenerator implements Serializable {
044    private static final long serialVersionUID = 1L;
045    public final int width, height;
046    public int seedA, seedB, cacheA, cacheB;
047    public GWTRNG rng;
048    public final double[][] heightData, heatData, moistureData;
049    public final GreasedRegion landData;
050    public final int[][] heightCodeData;
051    public double landModifier = -1.0, heatModifier = 1.0,
052            minHeight = Double.POSITIVE_INFINITY, maxHeight = Double.NEGATIVE_INFINITY,
053            minHeightActual = Double.POSITIVE_INFINITY, maxHeightActual = Double.NEGATIVE_INFINITY,
054            minHeat = Double.POSITIVE_INFINITY, maxHeat = Double.NEGATIVE_INFINITY,
055            minWet = Double.POSITIVE_INFINITY, maxWet = Double.NEGATIVE_INFINITY;
056    protected double centerLongitude;
057
058    public int zoom, startX, startY, usedWidth, usedHeight;
059    protected IntVLA startCacheX = new IntVLA(8), startCacheY = new IntVLA(8);
060    protected int zoomStartX, zoomStartY;
061
062    /**
063     * A FastNoise that has a higher frequency than that class defaults to, which is useful for maps here. With the
064     * default FastNoise frequency of 1f/32f, the maps this produces are giant blurs.
065     * <br>
066     * Even though this is a FastNoise and so technically can be edited, that seems to have issues when there's more
067     * than one WorldMapGenerator that uses this field. So you can feel free to use this as a Noise2D or Noise3D when
068     * generators need one, but don't change it too much, if at all.
069     */
070    public static final FastNoise DEFAULT_NOISE = new FastNoise(0x1337CAFE, 1f, FastNoise.SIMPLEX, 1);
071
072    /**
073     * Used to implement most of the copy constructor for subclasses; this cannot copy Noise implementations and leaves
074     * that up to the subclass, but will copy all non-static fields defined in WorldMapGenerator from other.
075     * @param other a WorldMapGenerator (subclass) to copy fields from
076     */
077    protected WorldMapGenerator(WorldMapGenerator other) {
078        width = other.width;
079        height = other.height;
080        usedWidth = other.usedWidth;
081        usedHeight = other.usedHeight;
082        landModifier = other.landModifier;
083        heatModifier = other.heatModifier;
084        minHeat = other.minHeat;
085        maxHeat = other.maxHeat;
086        minHeight = other.minHeight;
087        maxHeight = other.maxHeight;
088        minHeightActual = other.minHeightActual;
089        maxHeightActual = other.maxHeightActual;
090        minWet = other.minWet;
091        maxWet = other.maxWet;
092        centerLongitude = other.centerLongitude;
093        zoom = other.zoom;
094        startX = other.startX;
095        startY = other.startY;
096        startCacheX.addAll(other.startCacheX);
097        startCacheY.addAll(other.startCacheY);
098        zoomStartX = other.zoomStartX;
099        zoomStartY = other.zoomStartY;
100        seedA = other.seedA;
101        seedB = other.seedB;
102        cacheA = other.cacheA;
103        cacheB = other.cacheB;
104        rng = other.rng.copy();
105        heightData = ArrayTools.copy(other.heightData);
106        heatData = ArrayTools.copy(other.heatData);
107        moistureData = ArrayTools.copy(other.moistureData);
108        landData = other.landData.copy();
109        heightCodeData = ArrayTools.copy(other.heightCodeData);
110    }
111
112    /**
113     * Gets the longitude line the map is centered on, which should usually be between 0 and 2 * PI.
114     * @return the longitude line the map is centered on, in radians from 0 to 2 * PI
115     */
116    public double getCenterLongitude() {
117        return centerLongitude;
118    }
119
120    /**
121     * Sets the center longitude line to a longitude measured in radians, from 0 to 2 * PI. Positive arguments will be
122     * corrected with modulo, but negative ones may not always act as expected, and are strongly discouraged.
123     * @param centerLongitude the longitude to center the map projection on, from 0 to 2 * PI (can be any non-negative double).
124     */
125    public void setCenterLongitude(double centerLongitude) {
126        this.centerLongitude = centerLongitude % 6.283185307179586;
127    }
128
129    public static final double
130            deepWaterLower = -1.0, deepWaterUpper = -0.7,        // 0
131            mediumWaterLower = -0.7, mediumWaterUpper = -0.3,    // 1
132            shallowWaterLower = -0.3, shallowWaterUpper = -0.1,  // 2
133            coastalWaterLower = -0.1, coastalWaterUpper = 0.02,   // 3
134            sandLower = 0.02, sandUpper = 0.12,                   // 4
135            grassLower = 0.14, grassUpper = 0.35,                // 5
136            forestLower = 0.35, forestUpper = 0.6,               // 6
137            rockLower = 0.6, rockUpper = 0.8,                    // 7
138            snowLower = 0.8, snowUpper = 1.0;                    // 8
139
140    protected static double removeExcess(double radians)
141    {
142        radians *= 0.6366197723675814;
143        final int floor = (radians >= 0.0 ? (int) radians : (int) radians - 1);
144        return (radians - (floor & -2) - ((floor & 1) << 1)) * (Math.PI);
145//        if(radians < -Math.PI || radians > Math.PI)
146//            System.out.println("UH OH, radians produced: " + radians);
147//        if(Math.random() < 0.00001)
148//            System.out.println(radians);
149//        return radians;
150
151    }
152    /**
153     * Constructs a WorldMapGenerator (this class is abstract, so you should typically call this from a subclass or as
154     * part of an anonymous class that implements {@link #regenerate(int, int, int, int, double, double, int, int)}).
155     * Always makes a 256x256 map. If you were using {@link WorldMapGenerator#WorldMapGenerator(long, int, int)}, then
156     * this would be the same as passing the parameters {@code 0x1337BABE1337D00DL, 256, 256}.
157     */
158    protected WorldMapGenerator()
159    {
160        this(0x1337BABE1337D00DL, 256, 256);
161    }
162    /**
163     * Constructs a WorldMapGenerator (this class is abstract, so you should typically call this from a subclass or as
164     * part of an anonymous class that implements {@link #regenerate(int, int, int, int, double, double, int, int)}).
165     * Takes only the width/height of the map. The initial seed is set to the same large long
166     * every time, and it's likely that you would set the seed when you call {@link #generate(long)}. The width and
167     * height of the map cannot be changed after the fact, but you can zoom in.
168     *
169     * @param mapWidth the width of the map(s) to generate; cannot be changed later
170     * @param mapHeight the height of the map(s) to generate; cannot be changed later
171     */
172    protected WorldMapGenerator(int mapWidth, int mapHeight)
173    {
174        this(0x1337BABE1337D00DL, mapWidth, mapHeight);
175    }
176    /**
177     * Constructs a WorldMapGenerator (this class is abstract, so you should typically call this from a subclass or as
178     * part of an anonymous class that implements {@link #regenerate(int, int, int, int, double, double, int, int)}).
179     * Takes an initial seed and the width/height of the map. The {@code initialSeed}
180     * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
181     * The width and height of the map cannot be changed after the fact, but you can zoom in.
182     *
183     * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
184     * @param mapWidth the width of the map(s) to generate; cannot be changed later
185     * @param mapHeight the height of the map(s) to generate; cannot be changed later
186     */
187    protected WorldMapGenerator(long initialSeed, int mapWidth, int mapHeight)
188    {
189        width = mapWidth;
190        height = mapHeight;
191        usedWidth = width;
192        usedHeight = height;
193        seedA = (int) (initialSeed & 0xFFFFFFFFL);
194        seedB = (int) (initialSeed >>> 32);
195        cacheA = ~seedA;
196        cacheB = ~seedB;
197        rng = new GWTRNG(seedA, seedB);
198        heightData = new double[width][height];
199        heatData = new double[width][height];
200        moistureData = new double[width][height];
201        landData = new GreasedRegion(width, height);
202        heightCodeData = new int[width][height];
203
204//        riverData = new GreasedRegion(width, height);
205//        lakeData = new GreasedRegion(width, height);
206//        partialRiverData = new GreasedRegion(width, height);
207//        partialLakeData = new GreasedRegion(width, height);
208//        workingData = new GreasedRegion(width, height);
209    }
210
211    /**
212     * Generates a world using a random RNG state and all parameters randomized.
213     * The worlds this produces will always have width and height as specified in the constructor (default 256x256).
214     * You can call {@link #zoomIn(int, int, int)} to double the resolution and center on the specified area, but the width
215     * and height of the 2D arrays this changed, such as {@link #heightData} and {@link #moistureData} will be the same.
216     */
217    public void generate()
218    {
219        generate(rng.nextLong());
220    }
221
222    /**
223     * Generates a world using the specified RNG state as a long. Other parameters will be randomized, using the same
224     * RNG state to start with.
225     * The worlds this produces will always have width and height as specified in the constructor (default 256x256).
226     * You can call {@link #zoomIn(int, int, int)} to double the resolution and center on the specified area, but the width
227     * and height of the 2D arrays this changed, such as {@link #heightData} and {@link #moistureData} will be the same.
228     * @param state the state to give this generator's RNG; if the same as the last call, this will reuse data
229     */
230    public void generate(long state) {
231        generate(-1.0, -1.0, state);
232    }
233
234    /**
235     * Generates a world using the specified RNG state as a long, with specific land and heat modifiers that affect
236     * the land-water ratio and the average temperature, respectively.
237     * The worlds this produces will always have width and height as specified in the constructor (default 256x256).
238     * You can call {@link #zoomIn(int, int, int)} to double the resolution and center on the specified area, but the width
239     * and height of the 2D arrays this changed, such as {@link #heightData} and {@link #moistureData} will be the same.
240     * @param landMod 1.0 is Earth-like, less than 1 is more-water, more than 1 is more-land; a random value will be used if this is negative
241     * @param heatMod 1.125 is Earth-like, less than 1 is cooler, more than 1 is hotter; a random value will be used if this is negative
242     * @param state the state to give this generator's RNG; if the same as the last call, this will reuse data
243     */
244    public void generate(double landMod, double heatMod, long state)
245    {
246        if(cacheA != (int) (state & 0xFFFFFFFFL) || cacheB != (int) (state >>> 32) ||
247                landMod != landModifier || heatMod != heatModifier)
248        {
249            seedA = (int) (state & 0xFFFFFFFFL);
250            seedB = (int) (state >>> 32);
251            zoom = 0;
252            startCacheX.clear();
253            startCacheY.clear();
254            startCacheX.add(0);
255            startCacheY.add(0);
256            zoomStartX = width >> 1;
257            zoomStartY = height >> 1;
258
259        }
260        //System.out.printf("generate, zoomStartX: %d, zoomStartY: %d\n", zoomStartX, zoomStartY);
261
262        regenerate(startX = (zoomStartX >> zoom) - (width >> 1 + zoom), startY = (zoomStartY >> zoom) - (height >> 1 + zoom),
263                //startCacheX.peek(), startCacheY.peek(),
264                usedWidth = (width >> zoom), usedHeight = (height >> zoom), landMod, heatMod, seedA, seedB);
265    }
266
267    /**
268     * Halves the resolution of the map and doubles the area it covers; the 2D arrays this uses keep their sizes. This
269     * version of zoomOut always zooms out from the center of the currently used area.
270     * <br>
271     * Only has an effect if you have previously zoomed in using {@link #zoomIn(int, int, int)} or its overload.
272     */
273    public void zoomOut()
274    {
275        zoomOut(1, width >> 1, height >> 1);
276    }
277    /**
278     * Halves the resolution of the map and doubles the area it covers repeatedly, halving {@code zoomAmount} times; the
279     * 2D arrays this uses keep their sizes. This version of zoomOut allows you to specify where the zoom should be
280     * centered, using the current coordinates (if the map size is 256x256, then coordinates should be between 0 and
281     * 255, and will refer to the currently used area and not necessarily the full world size).
282     * <br>
283     * Only has an effect if you have previously zoomed in using {@link #zoomIn(int, int, int)} or its overload.
284     * @param zoomCenterX the center X position to zoom out from; if too close to an edge, this will stop moving before it would extend past an edge
285     * @param zoomCenterY the center Y position to zoom out from; if too close to an edge, this will stop moving before it would extend past an edge
286     */
287    public void zoomOut(int zoomAmount, int zoomCenterX, int zoomCenterY)
288    {
289        zoomAmount = Math.min(zoom, zoomAmount);
290        if(zoomAmount == 0) return;
291        if(zoomAmount < 0) {
292            zoomIn(-zoomAmount, zoomCenterX, zoomCenterY);
293            return;
294        }
295        if(zoom > 0)
296        {
297            if(cacheA != seedA || cacheB != seedB)
298            {
299                generate(rng.nextLong());
300            }
301            zoomStartX = Math.min(Math.max(
302                    (zoomStartX + (zoomCenterX - (width >> 1))) >> zoomAmount,
303                    width >> 1), (width << zoom - zoomAmount) - (width >> 1));
304            zoomStartY = Math.min(Math.max(
305                    (zoomStartY + (zoomCenterY - (height >> 1))) >> zoomAmount,
306                    height >> 1), (height << zoom - zoomAmount) - (height >> 1));
307//            System.out.printf("zoomOut, zoomStartX: %d, zoomStartY: %d\n", zoomStartX, zoomStartY);
308            zoom -= zoomAmount;
309            startCacheX.pop();
310            startCacheY.pop();
311            startCacheX.add(Math.min(Math.max(startCacheX.pop() + (zoomCenterX >> zoom + 1) - (width >> zoom + 2),
312                    0), width - (width >> zoom)));
313            startCacheY.add(Math.min(Math.max(startCacheY.pop() + (zoomCenterY >> zoom + 1) - (height >> zoom + 2),
314                    0), height - (height >> zoom)));
315//            zoomStartX = Math.min(Math.max((zoomStartX >> 1) + (zoomCenterX >> zoom + 1) - (width >> zoom + 2),
316//                    0), width - (width >> zoom));
317//            zoomStartY = Math.min(Math.max((zoomStartY >> 1) + (zoomCenterY >> zoom + 1) - (height >> zoom + 2),
318//                    0), height - (height >> zoom));
319            regenerate(startX = (zoomStartX >> zoom) - (width >> zoom + 1), startY = (zoomStartY >> zoom) - (height >> zoom + 1),
320                    //startCacheX.peek(), startCacheY.peek(),
321                    usedWidth = width >> zoom,  usedHeight = height >> zoom,
322                    landModifier, heatModifier, cacheA, cacheB);
323            rng.setState(cacheA, cacheB);
324        }
325
326    }
327    /**
328     * Doubles the resolution of the map and halves the area it covers; the 2D arrays this uses keep their sizes. This
329     * version of zoomIn always zooms in to the center of the currently used area.
330     * <br>
331     * Although there is no technical restriction on maximum zoom, zooming in more than 5 times (64x scale or greater)
332     * will make the map appear somewhat less realistic due to rounded shapes appearing more bubble-like and less like a
333     * normal landscape.
334     */
335    public void zoomIn()
336    {
337        zoomIn(1, width >> 1, height >> 1);
338    }
339    /**
340     * Doubles the resolution of the map and halves the area it covers repeatedly, doubling {@code zoomAmount} times;
341     * the 2D arrays this uses keep their sizes. This version of zoomIn allows you to specify where the zoom should be
342     * centered, using the current coordinates (if the map size is 256x256, then coordinates should be between 0 and
343     * 255, and will refer to the currently used area and not necessarily the full world size).
344     * <br>
345     * Although there is no technical restriction on maximum zoom, zooming in more than 5 times (64x scale or greater)
346     * will make the map appear somewhat less realistic due to rounded shapes appearing more bubble-like and less like a
347     * normal landscape.
348     * @param zoomCenterX the center X position to zoom in to; if too close to an edge, this will stop moving before it would extend past an edge
349     * @param zoomCenterY the center Y position to zoom in to; if too close to an edge, this will stop moving before it would extend past an edge
350     */
351    public void zoomIn(int zoomAmount, int zoomCenterX, int zoomCenterY)
352    {
353        if(zoomAmount == 0) return;
354        if(zoomAmount < 0)
355        {
356            zoomOut(-zoomAmount, zoomCenterX, zoomCenterY);
357            return;
358        }
359        if(seedA != cacheA || seedB != cacheB)
360        {
361            generate(rng.nextLong());
362        }
363        zoomStartX = Math.min(Math.max(
364                (zoomStartX + zoomCenterX - (width >> 1) << zoomAmount),
365                width >> 1), (width << zoom + zoomAmount) - (width >> 1));
366//        int oldZoomY = zoomStartY;
367        zoomStartY = Math.min(Math.max(
368                (zoomStartY + zoomCenterY - (height >> 1) << zoomAmount),
369                height >> 1), (height << zoom + zoomAmount) - (height >> 1));
370//        System.out.printf("zoomIn, zoomStartX: %d, zoomStartY: %d, oldZoomY: %d, unedited: %d, upperCap: %d\n", zoomStartX, zoomStartY,
371//                oldZoomY, (oldZoomY + zoomCenterY - (height >> 1) << zoomAmount), (height << zoom + zoomAmount) - (height >> 1));
372        zoom += zoomAmount;
373        if(startCacheX.isEmpty())
374        {
375            startCacheX.add(0);
376            startCacheY.add(0);
377        }
378        else {
379            startCacheX.add(Math.min(Math.max(startCacheX.peek() + (zoomCenterX >> zoom - 1) - (width >> zoom + 1),
380                    0), width - (width >> zoom)));
381            startCacheY.add(Math.min(Math.max(startCacheY.peek() + (zoomCenterY >> zoom - 1) - (height >> zoom + 1),
382                    0), height - (height >> zoom)));
383        }
384        regenerate(startX = (zoomStartX >> zoom) - (width >> 1 + zoom), startY = (zoomStartY >> zoom) - (height >> 1 + zoom),
385                //startCacheX.peek(), startCacheY.peek(),
386                usedWidth = width >> zoom, usedHeight = height >> zoom,
387                landModifier, heatModifier, cacheA, cacheB);
388        rng.setState(cacheA, cacheB);
389    }
390
391    protected abstract void regenerate(int startX, int startY, int usedWidth, int usedHeight,
392                                       double landMod, double heatMod, int stateA, int stateB);
393    /**
394     * Given a latitude and longitude in radians (the conventional way of describing points on a globe), this gets the
395     * (x,y) Coord on the map projection this generator uses that corresponds to the given lat-lon coordinates. If this
396     * generator does not represent a globe (if it is toroidal, for instance) or if there is no "good way" to calculate
397     * the projection for a given lat-lon coordinate, this returns null. The default implementation always returns null.
398     * If this is a supported operation and the parameters are valid, this returns a Coord with x between 0 and
399     * {@link #width}, and y between 0 and {@link #height}, both exclusive. Automatically wraps the Coord's values using
400     * {@link #wrapX(int, int)} and {@link #wrapY(int, int)}.
401     * @param latitude the latitude, from {@code Math.PI * -0.5} to {@code Math.PI * 0.5}
402     * @param longitude the longitude, from {@code 0.0} to {@code Math.PI * 2.0}
403     * @return the point at the given latitude and longitude, as a Coord with x between 0 and {@link #width} and y between 0 and {@link #height}, or null if unsupported
404     */
405    public Coord project(double latitude, double longitude)
406    {
407        return null;
408    }
409
410    public int codeHeight(final double high)
411    {
412        if(high < deepWaterUpper)
413            return 0;
414        if(high < mediumWaterUpper)
415            return 1;
416        if(high < shallowWaterUpper)
417            return 2;
418        if(high < coastalWaterUpper)
419            return 3;
420        if(high < sandUpper)
421            return 4;
422        if(high < grassUpper)
423            return 5;
424        if(high < forestUpper)
425            return 6;
426        if(high < rockUpper)
427            return 7;
428        return 8;
429    }
430    protected final int decodeX(final int coded)
431    {
432        return coded % width;
433    }
434    protected final int decodeY(final int coded)
435    {
436        return coded / width;
437    }
438    public int wrapX(final int x, final int y)  {
439        return (x + width) % width;
440    }
441    public int wrapY(final int x, final int y)  {
442        return (y + height) % height;
443    }
444    
445//    private static final Direction[] reuse = new Direction[6];
446//    private void appendDirToShuffle(RNG rng) {
447//        rng.randomPortion(Direction.CARDINALS, reuse);
448//        reuse[rng.next(2)] = Direction.DIAGONALS[rng.next(2)];
449//        reuse[4] = Direction.DIAGONALS[rng.next(2)];
450//        reuse[5] = Direction.OUTWARDS[rng.next(3)];
451//    }
452
453//    protected void addRivers()
454//    {
455//        landData.refill(heightCodeData, 4, 999);
456//        long rebuildState = rng.nextLong();
457//        //workingData.allOn();
458//                //.empty().insertRectangle(8, 8, width - 16, height - 16);
459//        riverData.empty().refill(heightCodeData, 6, 100);
460//        riverData.quasiRandomRegion(0.0036);
461//        int[] starts = riverData.asTightEncoded();
462//        int len = starts.length, currentPos, choice, adjX, adjY, currX, currY, tcx, tcy, stx, sty, sbx, sby;
463//        riverData.clear();
464//        lakeData.clear();
465//        PER_RIVER:
466//        for (int i = 0; i < len; i++) {
467//            workingData.clear();
468//            currentPos = starts[i];
469//            stx = tcx = currX = decodeX(currentPos);
470//            sty = tcy = currY = decodeY(currentPos);
471//            while (true) {
472//
473//                double best = 999999;
474//                choice = -1;
475//                appendDirToShuffle(rng);
476//
477//                for (int d = 0; d < 5; d++) {
478//                    adjX = wrapX(currX + reuse[d].deltaX);
479//                    /*
480//                    if (adjX < 0 || adjX >= width)
481//                    {
482//                        if(rng.next(4) == 0)
483//                            riverData.or(workingData);
484//                        continue PER_RIVER;
485//                    }*/
486//                    adjY = wrapY(currY + reuse[d].deltaY);
487//                    if (heightData[adjX][adjY] < best && !workingData.contains(adjX, adjY)) {
488//                        best = heightData[adjX][adjY];
489//                        choice = d;
490//                        tcx = adjX;
491//                        tcy = adjY;
492//                    }
493//                }
494//                currX = tcx;
495//                currY = tcy;
496//                if (best >= heightData[stx][sty]) {
497//                    tcx = rng.next(2);
498//                    adjX = wrapX(currX + ((tcx & 1) << 1) - 1);
499//                    adjY = wrapY(currY + (tcx & 2) - 1);
500//                    lakeData.insert(currX, currY);
501//                    lakeData.insert(wrapX(currX+1), currY);
502//                    lakeData.insert(wrapX(currX-1), currY);
503//                    lakeData.insert(currX, wrapY(currY+1));
504//                    lakeData.insert(currX, wrapY(currY-1));
505//
506//                    if(heightCodeData[adjX][adjY] <= 3) {
507//                        riverData.or(workingData);
508//                        continue PER_RIVER;
509//                    }
510//                    else if((heightData[adjX][adjY] -= 0.0002) < 0.0) {
511//                        if (rng.next(3) == 0)
512//                            riverData.or(workingData);
513//                        continue PER_RIVER;
514//                    }
515//                    tcx = rng.next(2);
516//                    adjX = wrapX(currX + ((tcx & 1) << 1) - 1);
517//                    adjY = wrapY(currY + (tcx & 2) - 1);
518//                    if(heightCodeData[adjX][adjY] <= 3) {
519//                        riverData.or(workingData);
520//                        continue PER_RIVER;
521//                    }
522//                    else if((heightData[adjX][adjY] -= 0.0002) < 0.0) {
523//                        if (rng.next(3) == 0)
524//                            riverData.or(workingData);
525//                        continue PER_RIVER;
526//                    }
527//                }
528//                if(choice != -1 && reuse[choice].isDiagonal())
529//                {
530//                    tcx = wrapX(currX - reuse[choice].deltaX);
531//                    tcy = wrapY(currY - reuse[choice].deltaY);
532//                    if(heightData[tcx][currY] <= heightData[currX][tcy] && !workingData.contains(tcx, currY))
533//                    {
534//                        if(heightCodeData[tcx][currY] < 3 || riverData.contains(tcx, currY))
535//                        {
536//                            riverData.or(workingData);
537//                            continue PER_RIVER;
538//                        }
539//                        workingData.insert(tcx, currY);
540//                    }
541//                    else if(!workingData.contains(currX, tcy))
542//                    {
543//                        if(heightCodeData[currX][tcy] < 3 || riverData.contains(currX, tcy))
544//                        {
545//                            riverData.or(workingData);
546//                            continue PER_RIVER;
547//                        }
548//                        workingData.insert(currX, tcy);
549//
550//                    }
551//                }
552//                if(heightCodeData[currX][currY] < 3 || riverData.contains(currX, currY))
553//                {
554//                    riverData.or(workingData);
555//                    continue PER_RIVER;
556//                }
557//                workingData.insert(currX, currY);
558//            }
559//        }
560//
561//        GreasedRegion tempData = new GreasedRegion(width, height);
562//        int riverCount = riverData.size() >> 4, currentMax = riverCount >> 3, idx = 0, prevChoice;
563//        for (int h = 5; h < 9; h++) { //, currentMax += riverCount / 18
564//            workingData.empty().refill(heightCodeData, h).and(riverData);
565//            RIVER:
566//            for (int j = 0; j < currentMax && idx < riverCount; j++) {
567//                double vdc = VanDerCorputQRNG.weakDetermine(idx++), best = -999999;
568//                currentPos = workingData.atFractionTight(vdc);
569//                if(currentPos < 0)
570//                    break;
571//                stx = sbx = tcx = currX = decodeX(currentPos);
572//                sty = sby = tcy = currY = decodeY(currentPos);
573//                appendDirToShuffle(rng);
574//                choice = -1;
575//                prevChoice = -1;
576//                for (int d = 0; d < 5; d++) {
577//                    adjX = wrapX(currX + reuse[d].deltaX);
578//                    adjY = wrapY(currY + reuse[d].deltaY);
579//                    if (heightData[adjX][adjY] > best) {
580//                        best = heightData[adjX][adjY];
581//                        prevChoice = choice;
582//                        choice = d;
583//                        sbx = tcx;
584//                        sby = tcy;
585//                        tcx = adjX;
586//                        tcy = adjY;
587//                    }
588//                }
589//                currX = sbx;
590//                currY = sby;
591//                if (prevChoice != -1 && heightCodeData[currX][currY] >= 4) {
592//                    if (reuse[prevChoice].isDiagonal()) {
593//                        tcx = wrapX(currX - reuse[prevChoice].deltaX);
594//                        tcy = wrapY(currY - reuse[prevChoice].deltaY);
595//                        if (heightData[tcx][currY] <= heightData[currX][tcy]) {
596//                            if(heightCodeData[tcx][currY] < 3)
597//                            {
598//                                riverData.or(tempData);
599//                                continue;
600//                            }
601//                            tempData.insert(tcx, currY);
602//                        }
603//                        else
604//                        {
605//                            if(heightCodeData[currX][tcy] < 3)
606//                            {
607//                                riverData.or(tempData);
608//                                continue;
609//                            }
610//                            tempData.insert(currX, tcy);
611//                        }
612//                    }
613//                    if(heightCodeData[currX][currY] < 3)
614//                    {
615//                        riverData.or(tempData);
616//                        continue;
617//                    }
618//                    tempData.insert(currX, currY);
619//                }
620//
621//                while (true) {
622//                    best = -999999;
623//                    appendDirToShuffle(rng);
624//                    choice = -1;
625//                    for (int d = 0; d < 6; d++) {
626//                        adjX = wrapX(currX + reuse[d].deltaX);
627//                        adjY = wrapY(currY + reuse[d].deltaY);
628//                        if (heightData[adjX][adjY] > best && !riverData.contains(adjX, adjY)) {
629//                            best = heightData[adjX][adjY];
630//                            choice = d;
631//                            sbx = adjX;
632//                            sby = adjY;
633//                        }
634//                    }
635//                    currX = sbx;
636//                    currY = sby;
637//                    if (choice != -1) {
638//                        if (reuse[choice].isDiagonal()) {
639//                            tcx = wrapX(currX - reuse[choice].deltaX);
640//                            tcy = wrapY(currY - reuse[choice].deltaY);
641//                            if (heightData[tcx][currY] <= heightData[currX][tcy]) {
642//                                if(heightCodeData[tcx][currY] < 3)
643//                                {
644//                                    riverData.or(tempData);
645//                                    continue RIVER;
646//                                }
647//                                tempData.insert(tcx, currY);
648//                            }
649//                            else
650//                            {
651//                                if(heightCodeData[currX][tcy] < 3)
652//                                {
653//                                    riverData.or(tempData);
654//                                    continue RIVER;
655//                                }
656//                                tempData.insert(currX, tcy);
657//                            }
658//                        }
659//                        if(heightCodeData[currX][currY] < 3)
660//                        {
661//                            riverData.or(tempData);
662//                            continue RIVER;
663//                        }
664//                        tempData.insert(currX, currY);
665//                    }
666//                    else
667//                    {
668//                        riverData.or(tempData);
669//                        tempData.clear();
670//                        continue RIVER;
671//                    }
672//                    if (best <= heightData[stx][sty] || heightData[currX][currY] > rng.nextDouble(280.0)) {
673//                        riverData.or(tempData);
674//                        tempData.clear();
675//                        if(heightCodeData[currX][currY] < 3)
676//                            continue RIVER;
677//                        lakeData.insert(currX, currY);
678//                        sbx = rng.next(8);
679//                        sbx &= sbx >>> 4;
680//                        if ((sbx & 1) == 0)
681//                            lakeData.insert(wrapX(currX + 1), currY);
682//                        if ((sbx & 2) == 0)
683//                            lakeData.insert(wrapX(currX - 1), currY);
684//                        if ((sbx & 4) == 0)
685//                            lakeData.insert(currX, wrapY(currY + 1));
686//                        if ((sbx & 8) == 0)
687//                            lakeData.insert(currX, wrapY(currY - 1));
688//                        sbx = rng.next(2);
689//                        lakeData.insert(wrapX(currX + (-(sbx & 1) | 1)), wrapY(currY + ((sbx & 2) - 1))); // random diagonal
690//                        lakeData.insert(currX, wrapY(currY + ((sbx & 2) - 1))); // ortho next to random diagonal
691//                        lakeData.insert(wrapX(currX + (-(sbx & 1) | 1)), currY); // ortho next to random diagonal
692//
693//                        continue RIVER;
694//                    }
695//                }
696//            }
697//
698//        }
699//
700//        rng.setState(rebuildState);
701//    }
702
703    public interface BiomeMapper
704    {
705        /**
706         * Gets the most relevant biome code for a given x,y point on the map. Some mappers can store more than one
707         * biome at a location, but only the one with the highest influence will be returned by this method. Biome codes
708         * are always ints, and are typically between 0 and 60, both inclusive; they are meant to be used as indices
709         * into a table of names or other objects that identify a biome, accessible via {@link #getBiomeNameTable()}.
710         * Although different classes may define biome codes differently, they should all be able to be used as indices
711         * into the String array returned by getBiomeNameTable().
712         * @param x the x-coordinate on the map
713         * @param y the y-coordinate on the map
714         * @return an int that can be used as an index into the array returned by {@link #getBiomeNameTable()}
715         */
716        int getBiomeCode(int x, int y);
717
718        /**
719         * Gets a heat code for a given x,y point on a map, usually as an int between 0 and 5 inclusive. Some
720         * implementations may use more or less detail for heat codes, but 0 is always the coldest code used, and the
721         * highest value this can return for a given implementation refers to the hottest code used.
722         * @param x the x-coordinate on the map
723         * @param y the y-coordinate on the map
724         * @return an int that can be used to categorize how hot an area is, with 0 as coldest
725         */
726        int getHeatCode(int x, int y);
727        /**
728         * Gets a moisture code for a given x,y point on a map, usually as an int between 0 and 5 inclusive. Some
729         * implementations may use more or less detail for moisture codes, but 0 is always the driest code used, and the
730         * highest value this can return for a given implementation refers to the wettest code used. Some
731         * implementations may allow seasonal change in moisture, e.g. monsoon seasons, to be modeled differently from
732         * average precipitation in an area, but the default assumption is that this describes the average amount of
733         * moisture (rain, humidity, and possibly snow/hail or other precipitation) that an area receives annually.
734         * @param x the x-coordinate on the map
735         * @param y the y-coordinate on the map
736         * @return an int that can be used to categorize how much moisture an area tends to receive, with 0 as driest
737         */
738        int getMoistureCode(int x, int y);
739
740        /**
741         * Gets a String array where biome codes can be used as indices to look up a name for the biome they refer to. A
742         * sample table is in {@link SimpleBiomeMapper#biomeTable}; the 61-element array format documented for that
743         * field is encouraged for implementing classes if they use 6 levels of heat and 6 levels of moisture, and track
744         * rivers, coastlines, lakes, and oceans as potentially different types of terrain. Biome codes can be obtained
745         * with {@link #getBiomeCode(int, int)}, or for some implementing classes other methods may provide more
746         * detailed information.
747         * @return a String array that often contains 61 elements, to be used with biome codes as indices.
748         */
749        String[] getBiomeNameTable();
750        /**
751         * Analyzes the last world produced by the given WorldMapGenerator and uses all of its generated information to
752         * assign biome codes for each cell (along with heat and moisture codes). After calling this, biome codes can be
753         * retrieved with {@link #getBiomeCode(int, int)} and used as indices into {@link #getBiomeNameTable()} or a
754         * custom biome table.
755         * @param world a WorldMapGenerator that should have generated at least one map; it may be at any zoom
756         */
757        void makeBiomes(WorldMapGenerator world);
758    }
759    /**
760     * A way to get biome information for the cells on a map when you only need a single value to describe a biome, such
761     * as "Grassland" or "TropicalRainforest".
762     * <br>
763     * To use: 1, Construct a SimpleBiomeMapper (constructor takes no arguments). 2, call
764     * {@link #makeBiomes(WorldMapGenerator)} with a WorldMapGenerator that has already produced at least one world map.
765     * 3, get biome codes from the {@link #biomeCodeData} field, where a code is an int that can be used as an index
766     * into the {@link #biomeTable} static field to get a String name for a biome type, or used with an alternate biome
767     * table of your design. Biome tables in this case are 61-element arrays organized into groups of 6 elements, with
768     * the last element reserved for empty space where the map doesn't cover (as with some map projections). Each
769     * group goes from the coldest temperature first to the warmest temperature last in the group. The first group of 6
770     * contains the dryest biomes, the next 6 are medium-dry, the next are slightly-dry, the next slightly-wet, then
771     * medium-wet, then wettest. After this first block of dry-to-wet groups, there is a group of 6 for coastlines, a
772     * group of 6 for rivers, a group of 6 for lakes, a group of 6 for oceans, and then one element for space outside
773     * the map. The last element, with code 60, is by convention the String "Empty", but normally the code should be
774     * enough to tell that a space is off-map. This also assigns moisture codes and heat codes from 0 to 5 for each
775     * cell, which may be useful to simplify logic that deals with those factors.
776     */
777    public static class SimpleBiomeMapper implements BiomeMapper
778    {
779        /**
780         * The heat codes for the analyzed map, from 0 to 5 inclusive, with 0 coldest and 5 hottest.
781         */
782        public int[][] heatCodeData,
783        /**
784         * The moisture codes for the analyzed map, from 0 to 5 inclusive, with 0 driest and 5 wettest.
785         */
786        moistureCodeData,
787        /**
788         * The biome codes for the analyzed map, from 0 to 60 inclusive. You can use {@link #biomeTable} to look up
789         * String names for biomes, or construct your own table as you see fit (see docs in {@link SimpleBiomeMapper}).
790         */
791        biomeCodeData;
792
793        @Override
794        public int getBiomeCode(int x, int y) {
795            return biomeCodeData[x][y];
796        }
797
798        @Override
799        public int getHeatCode(int x, int y) {
800            return heatCodeData[x][y];
801        }
802
803        @Override
804        public int getMoistureCode(int x, int y) {
805            return moistureCodeData[x][y];
806        }
807
808        /**
809         * Gets a String array where biome codes can be used as indices to look up a name for the biome they refer to.
810         * This table uses 6 levels of heat and 6 levels of moisture, and tracks rivers, coastlines, lakes, and oceans
811         * as potentially different types of terrain. Biome codes can be obtained with {@link #getBiomeCode(int, int)}.
812         * This method returns a direct reference to {@link #biomeTable}, so modifying the returned array is discouraged
813         * (you should implement {@link BiomeMapper} using this class as a basis if you want to change its size).
814         * @return a direct reference to {@link #biomeTable}, a String array containing names of biomes
815         */
816        @Override
817        public String[] getBiomeNameTable() {
818            return biomeTable;
819        }
820
821        public static final double
822                coldestValueLower = 0.0,   coldestValueUpper = 0.15, // 0
823                colderValueLower = 0.15,   colderValueUpper = 0.31,  // 1
824                coldValueLower = 0.31,     coldValueUpper = 0.5,     // 2
825                warmValueLower = 0.5,      warmValueUpper = 0.69,    // 3
826                warmerValueLower = 0.69,   warmerValueUpper = 0.85,  // 4
827                warmestValueLower = 0.85,  warmestValueUpper = 1.0,  // 5
828
829        driestValueLower = 0.0,    driestValueUpper  = 0.27, // 0
830                drierValueLower = 0.27,    drierValueUpper   = 0.4,  // 1
831                dryValueLower = 0.4,       dryValueUpper     = 0.6,  // 2
832                wetValueLower = 0.6,       wetValueUpper     = 0.8,  // 3
833                wetterValueLower = 0.8,    wetterValueUpper  = 0.9,  // 4
834                wettestValueLower = 0.9,   wettestValueUpper = 1.0;  // 5
835
836        /**
837         * The default biome table to use with biome codes from {@link #biomeCodeData}. Biomes are assigned based on
838         * heat and moisture for the first 36 of 61 elements (coldest to warmest for each group of 6, with the first
839         * group as the dryest and the last group the wettest), then the next 6 are for coastlines (coldest to warmest),
840         * then rivers (coldest to warmest), then lakes (coldest to warmest), then oceans (coldest to warmest), and
841         * lastly a single "biome" for empty space outside the map (meant for projections that don't fill a rectangle).
842         */
843        public static final String[] biomeTable = {
844                //COLDEST //COLDER        //COLD            //HOT                  //HOTTER              //HOTTEST
845                "Ice",    "Ice",          "Grassland",      "Desert",              "Desert",             "Desert",             //DRYEST
846                "Ice",    "Tundra",       "Grassland",      "Grassland",           "Desert",             "Desert",             //DRYER
847                "Ice",    "Tundra",       "Woodland",       "Woodland",            "Savanna",            "Desert",             //DRY
848                "Ice",    "Tundra",       "SeasonalForest", "SeasonalForest",      "Savanna",            "Savanna",            //WET
849                "Ice",    "Tundra",       "BorealForest",   "TemperateRainforest", "TropicalRainforest", "Savanna",            //WETTER
850                "Ice",    "BorealForest", "BorealForest",   "TemperateRainforest", "TropicalRainforest", "TropicalRainforest", //WETTEST
851                "Rocky",  "Rocky",        "Beach",          "Beach",               "Beach",              "Beach",              //COASTS
852                "Ice",    "River",        "River",          "River",               "River",              "River",              //RIVERS
853                "Ice",    "River",        "River",          "River",               "River",              "River",              //LAKES
854                "Ocean",  "Ocean",        "Ocean",          "Ocean",               "Ocean",              "Ocean",              //OCEAN
855                "Empty",                                                                                                       //SPACE
856        };
857
858        /**
859         * Simple constructor; pretty much does nothing. Make sure to call {@link #makeBiomes(WorldMapGenerator)} before
860         * using fields like {@link #biomeCodeData}.
861         */
862        public SimpleBiomeMapper()
863        {
864            heatCodeData = null;
865            moistureCodeData = null;
866            biomeCodeData = null;
867        }
868
869        /**
870         * Analyzes the last world produced by the given WorldMapGenerator and uses all of its generated information to
871         * assign biome codes for each cell (along with heat and moisture codes). After calling this, biome codes can be
872         * taken from {@link #biomeCodeData} and used as indices into {@link #biomeTable} or a custom biome table.
873         * @param world a WorldMapGenerator that should have generated at least one map; it may be at any zoom
874         */
875        @Override
876        public void makeBiomes(WorldMapGenerator world) {
877            if(world == null || world.width <= 0 || world.height <= 0)
878                return;
879            if(heatCodeData == null || (heatCodeData.length != world.width || heatCodeData[0].length != world.height))
880                heatCodeData = new int[world.width][world.height];
881            if(moistureCodeData == null || (moistureCodeData.length != world.width || moistureCodeData[0].length != world.height))
882                moistureCodeData = new int[world.width][world.height];
883            if(biomeCodeData == null || (biomeCodeData.length != world.width || biomeCodeData[0].length != world.height))
884                biomeCodeData = new int[world.width][world.height];
885            final double i_hot = (world.maxHeat == world.minHeat) ? 1.0 : 1.0 / (world.maxHeat - world.minHeat);
886            for (int x = 0; x < world.width; x++) {
887                for (int y = 0; y < world.height; y++) {
888                    final double hot = (world.heatData[x][y] - world.minHeat) * i_hot, moist = world.moistureData[x][y];
889                    final int heightCode = world.heightCodeData[x][y];
890                    if(heightCode == 1000) {
891                        biomeCodeData[x][y] = 60;
892                        continue;
893                    }
894                    int hc, mc;
895                    boolean isLake = false,// world.generateRivers && heightCode >= 4 && fresh > 0.65 && fresh + moist * 2.35 > 2.75,//world.partialLakeData.contains(x, y) && heightCode >= 4,
896                            isRiver = false;// world.generateRivers && !isLake && heightCode >= 4 && fresh > 0.55 && fresh + moist * 2.2 > 2.15;//world.partialRiverData.contains(x, y) && heightCode >= 4;
897                    if(heightCode < 4) {
898                        mc = 9;
899                    }
900                    else if (moist > wetterValueUpper) {
901                        mc = 5;
902                    } else if (moist > wetValueUpper) {
903                        mc = 4;
904                    } else if (moist > dryValueUpper) {
905                        mc = 3;
906                    } else if (moist > drierValueUpper) {
907                        mc = 2;
908                    } else if (moist > driestValueUpper) {
909                        mc = 1;
910                    } else {
911                        mc = 0;
912                    }
913
914                    if (hot > warmerValueUpper) {
915                        hc = 5;
916                    } else if (hot > warmValueUpper) {
917                        hc = 4;
918                    } else if (hot > coldValueUpper) {
919                        hc = 3;
920                    } else if (hot > colderValueUpper) {
921                        hc = 2;
922                    } else if (hot > coldestValueUpper) {
923                        hc = 1;
924                    } else {
925                        hc = 0;
926                    }
927
928                    heatCodeData[x][y] = hc;
929                    moistureCodeData[x][y] = mc;
930                    // 54 == 9 * 6, 9 is used for Ocean groups
931                    biomeCodeData[x][y] = heightCode < 4 ? hc + 54 // 54 == 9 * 6, 9 is used for Ocean groups
932                            : isLake ? hc + 48 : heightCode == 4 ? hc + 36 : hc + mc * 6;
933                }
934            }
935        }
936    }
937    /**
938     * A way to get biome information for the cells on a map when you want an area's biome to be a combination of two
939     * main biome types, such as "Grassland" or "TropicalRainforest", with the biomes varying in weight between areas.
940     * <br>
941     * To use: 1, Construct a DetailedBiomeMapper (constructor takes no arguments). 2, call
942     * {@link #makeBiomes(WorldMapGenerator)} with a WorldMapGenerator that has already produced at least one world map.
943     * 3, get biome codes from the {@link #biomeCodeData} field, where a code is an int that can be used with the
944     * extract methods in this class to get various information from it (these are {@link #extractBiomeA(int)},
945     * {@link #extractBiomeB(int)}, {@link #extractPartA(int)}, {@link #extractPartB(int)}, and
946     * {@link #extractMixAmount(int)}). You can get predefined names for biomes using the extractBiome methods (these
947     * names can be changed in {@link #biomeTable}), or raw indices into some (usually 61-element) collection or array
948     * with the extractPart methods. The extractMixAmount() method gets a float that is the amount by which biome B
949     * affects biome A; if this is higher than 0.5, then biome B is the "dominant" biome in the area.
950     */
951    public static class DetailedBiomeMapper implements BiomeMapper
952    {
953        /**
954         * The heat codes for the analyzed map, from 0 to 5 inclusive, with 0 coldest and 5 hottest.
955         */
956        public int[][] heatCodeData,
957        /**
958         * The moisture codes for the analyzed map, from 0 to 5 inclusive, with 0 driest and 5 wettest.
959         */
960        moistureCodeData,
961        /**
962         * The biome codes for the analyzed map, using one int to store the codes for two biomes and the degree by which
963         * the second biome affects the first. These codes can be used with methods in this class like
964         * {@link #extractBiomeA(int)}, {@link #extractBiomeB(int)}, and {@link #extractMixAmount(int)} to find the two
965         * dominant biomes in an area, called biome A and biome B, and the mix amount, for finding how much biome B
966         * affects biome A.
967         */
968        biomeCodeData;
969
970
971        /**
972         * Gets the biome code for the dominant biome at a given x,y position. This is equivalent to getting the raw
973         * biome code from {@link #biomeCodeData}, calling {@link #extractMixAmount(int)} on that raw biome code, and
974         * chooosing whether to call {@link #extractPartA(int)} or {@link #extractPartB(int)} based on whether the mix
975         * amount is lower than 0.5 (yielding part A) or higher (yielding part B).
976         * @param x the x-coordinate on the map
977         * @param y the y-coordinate on the map
978         * @return the biome code for the dominant biome part at the given location
979         */
980        @Override
981        public int getBiomeCode(int x, int y) {
982            int code = biomeCodeData[x][y];
983            if(code < 0x2000000) return code & 1023;
984            return (code >>> 10) & 1023;
985        }
986
987        @Override
988        public int getHeatCode(int x, int y) {
989            return heatCodeData[x][y];
990        }
991
992        @Override
993        public int getMoistureCode(int x, int y) {
994            return moistureCodeData[x][y];
995        }
996
997        /**
998         * Gets a String array where biome codes can be used as indices to look up a name for the biome they refer to.
999         * This table uses 6 levels of heat and 6 levels of moisture, and tracks rivers, coastlines, lakes, and oceans
1000         * as potentially different types of terrain. Biome codes can be obtained with {@link #getBiomeCode(int, int)}.
1001         * This method returns a direct reference to {@link #biomeTable}, so modifying the returned array is discouraged
1002         * (you should implement {@link BiomeMapper} using this class as a basis if you want to change its size).
1003         * @return a direct reference to {@link #biomeTable}, a String array containing names of biomes
1004         */
1005        @Override
1006        public String[] getBiomeNameTable() {
1007            return biomeTable;
1008        }
1009
1010        public static final double
1011                coldestValueLower = 0.0,   coldestValueUpper = 0.15, // 0
1012                colderValueLower = 0.15,   colderValueUpper = 0.31,  // 1
1013                coldValueLower = 0.31,     coldValueUpper = 0.5,     // 2
1014                warmValueLower = 0.5,      warmValueUpper = 0.69,     // 3
1015                warmerValueLower = 0.69,    warmerValueUpper = 0.85,   // 4
1016                warmestValueLower = 0.85,   warmestValueUpper = 1.0,  // 5
1017
1018        driestValueLower = 0.0,    driestValueUpper  = 0.27, // 0
1019                drierValueLower = 0.27,    drierValueUpper   = 0.4,  // 1
1020                dryValueLower = 0.4,       dryValueUpper     = 0.6,  // 2
1021                wetValueLower = 0.6,       wetValueUpper     = 0.8,  // 3
1022                wetterValueLower = 0.8,    wetterValueUpper  = 0.9,  // 4
1023                wettestValueLower = 0.9,   wettestValueUpper = 1.0;  // 5
1024
1025        /**
1026         * The default biome table to use with parts of biome codes from {@link #biomeCodeData}. Biomes are assigned by
1027         * heat and moisture for the first 36 of 61 elements (coldest to warmest for each group of 6, with the first
1028         * group as the dryest and the last group the wettest), then the next 6 are for coastlines (coldest to warmest),
1029         * then rivers (coldest to warmest), then lakes (coldest to warmest). The last is reserved for empty space.
1030         * <br>
1031         * Unlike with {@link SimpleBiomeMapper}, you cannot use a biome code directly from biomeCodeData as an index
1032         * into this in almost any case; you should pass the biome code to one of the extract methods.
1033         * {@link #extractBiomeA(int)} or {@link #extractBiomeB(int)} will work if you want a biome name, or
1034         * {@link #extractPartA(int)} or {@link #extractPartB(int)} should be used if you want a non-coded int that
1035         * represents one of the biomes' indices into something like this. You can also get the amount by which biome B
1036         * is affecting biome A with {@link #extractMixAmount(int)}.
1037         */
1038        public static final String[] biomeTable = {
1039                //COLDEST //COLDER        //COLD            //HOT                  //HOTTER              //HOTTEST
1040                "Ice",    "Ice",          "Grassland",      "Desert",              "Desert",             "Desert",             //DRYEST
1041                "Ice",    "Tundra",       "Grassland",      "Grassland",           "Desert",             "Desert",             //DRYER
1042                "Ice",    "Tundra",       "Woodland",       "Woodland",            "Savanna",            "Desert",             //DRY
1043                "Ice",    "Tundra",       "SeasonalForest", "SeasonalForest",      "Savanna",            "Savanna",            //WET
1044                "Ice",    "Tundra",       "BorealForest",   "TemperateRainforest", "TropicalRainforest", "Savanna",            //WETTER
1045                "Ice",    "BorealForest", "BorealForest",   "TemperateRainforest", "TropicalRainforest", "TropicalRainforest", //WETTEST
1046                "Rocky",  "Rocky",        "Beach",          "Beach",               "Beach",              "Beach",              //COASTS
1047                "Ice",    "River",        "River",          "River",               "River",              "River",              //RIVERS
1048                "Ice",    "River",        "River",          "River",               "River",              "River",              //LAKES
1049                "Ocean",  "Ocean",        "Ocean",          "Ocean",               "Ocean",              "Ocean",              //OCEAN
1050                "Empty",                                                                                                       //SPACE
1051        };
1052
1053        /**
1054         * Gets the int stored in part A of the given biome code, which can be used as an index into other collections.
1055         * This int should almost always range from 0 to 60 (both inclusive), so collections this is used as an index
1056         * for should have a length of at least 61.
1057         * @param biomeCode a biome code that was probably received from {@link #biomeCodeData}
1058         * @return an int stored in the biome code's part A; almost always between 0 and 60, inclusive.
1059         */
1060        public int extractPartA(int biomeCode)
1061        {
1062            return biomeCode & 1023;
1063        }
1064        /**
1065         * Gets a String from {@link #biomeTable} that names the appropriate biome in part A of the given biome code.
1066         * @param biomeCode a biome code that was probably received from {@link #biomeCodeData}
1067         * @return a String that names the biome in part A of biomeCode, or "Empty" if none can be found
1068         */
1069        public String extractBiomeA(int biomeCode)
1070        {
1071            biomeCode &= 1023;
1072            if(biomeCode < 60)
1073                return biomeTable[biomeCode];
1074            return "Empty";
1075        }
1076        /**
1077         * Gets the int stored in part B of the given biome code, which can be used as an index into other collections.
1078         * This int should almost always range from 0 to 60 (both inclusive), so collections this is used as an index
1079         * for should have a length of at least 61.
1080         * @param biomeCode a biome code that was probably received from {@link #biomeCodeData}
1081         * @return an int stored in the biome code's part B; almost always between 0 and 60, inclusive.
1082         */
1083        public int extractPartB(int biomeCode)
1084        {
1085            return (biomeCode >>> 10) & 1023;
1086        }
1087
1088        /**
1089         * Gets a String from {@link #biomeTable} that names the appropriate biome in part B of the given biome code.
1090         * @param biomeCode a biome code that was probably received from {@link #biomeCodeData}
1091         * @return a String that names the biome in part B of biomeCode, or "Ocean" if none can be found
1092         */
1093        public String extractBiomeB(int biomeCode)
1094        {
1095            biomeCode = (biomeCode >>> 10) & 1023;
1096            if(biomeCode < 60)
1097                return biomeTable[biomeCode];
1098            return "Empty";
1099        }
1100
1101        /**
1102         * This gets the portion of a biome code that represents the amount of mixing between two biomes.
1103         * Biome codes are normally obtained from the {@link #biomeCodeData} field, and aren't very usable on their own
1104         * without calling methods like this, {@link #extractBiomeA(int)}, and {@link #extractBiomeB(int)}. This returns
1105         * a float between 0.0f (inclusive) and 1.0f (exclusive), with 0.0f meaning biome B has no effect on an area and
1106         * biome A is the only one used, 0.5f meaning biome A and biome B have equal effect, and 0.75f meaning biome B
1107         * has most of the effect, three-fourths of the area, and biome A has less, one-fourth of the area.
1108         * @param biomeCode a biome code that was probably received from {@link #biomeCodeData}
1109         * @return a float between 0.0f (inclusive) and 1.0f (exclusive) representing mixing of biome B into biome A
1110         */
1111        public float extractMixAmount(int biomeCode)
1112        {
1113            return (biomeCode >>> 20) * 0x1p-10f;
1114        }
1115
1116        /**
1117         * Simple constructor; pretty much does nothing. Make sure to call {@link #makeBiomes(WorldMapGenerator)} before
1118         * using fields like {@link #biomeCodeData}.
1119         */
1120        public DetailedBiomeMapper()
1121        {
1122            heatCodeData = null;
1123            moistureCodeData = null;
1124            biomeCodeData = null;
1125        }
1126
1127        /**
1128         * Analyzes the last world produced by the given WorldMapGenerator and uses all of its generated information to
1129         * assign biome codes for each cell (along with heat and moisture codes). After calling this, biome codes can be
1130         * taken from {@link #biomeCodeData} and used with methods in this class like {@link #extractBiomeA(int)},
1131         * {@link #extractBiomeB(int)}, and {@link #extractMixAmount(int)} to find the two dominant biomes in an area,
1132         * called biome A and biome B, and the mix amount, for finding how much biome B affects biome A.
1133         * @param world a WorldMapGenerator that should have generated at least one map; it may be at any zoom
1134         */
1135        @Override
1136        public void makeBiomes(WorldMapGenerator world) {
1137            if(world == null || world.width <= 0 || world.height <= 0)
1138                return;
1139            if(heatCodeData == null || (heatCodeData.length != world.width || heatCodeData[0].length != world.height))
1140                heatCodeData = new int[world.width][world.height];
1141            if(moistureCodeData == null || (moistureCodeData.length != world.width || moistureCodeData[0].length != world.height))
1142                moistureCodeData = new int[world.width][world.height];
1143            if(biomeCodeData == null || (biomeCodeData.length != world.width || biomeCodeData[0].length != world.height))
1144                biomeCodeData = new int[world.width][world.height];
1145            final int[][] heightCodeData = world.heightCodeData;
1146            final double[][] heatData = world.heatData, moistureData = world.moistureData, heightData = world.heightData;
1147            int hc, mc, heightCode, bc;
1148            double hot, moist, high, i_hot = 1.0 / world.maxHeat;
1149            for (int x = 0; x < world.width; x++) {
1150                for (int y = 0; y < world.height; y++) {
1151
1152                    heightCode = heightCodeData[x][y];
1153                    if(heightCode == 1000) {
1154                        biomeCodeData[x][y] = 60;
1155                        continue;
1156                    }
1157                    hot = heatData[x][y];
1158                    moist = moistureData[x][y];
1159                    high = heightData[x][y];
1160//                    fresh = world.freshwaterData[x][y];
1161                    boolean isLake = false,//world.generateRivers && heightCode >= 4 && fresh > 0.65 && fresh + moist * 2.35 > 2.75,//world.partialLakeData.contains(x, y) && heightCode >= 4,
1162                            isRiver = false;//world.generateRivers && !isLake && heightCode >= 4 && fresh > 0.55 && fresh + moist * 2.2 > 2.15;//world.partialRiverData.contains(x, y) && heightCode >= 4;
1163                    if (moist >= (wettestValueUpper - (wetterValueUpper - wetterValueLower) * 0.2)) {
1164                        mc = 5;
1165                    } else if (moist >= (wetterValueUpper - (wetValueUpper - wetValueLower) * 0.2)) {
1166                        mc = 4;
1167                    } else if (moist >= (wetValueUpper - (dryValueUpper - dryValueLower) * 0.2)) {
1168                        mc = 3;
1169                    } else if (moist >= (dryValueUpper - (drierValueUpper - drierValueLower) * 0.2)) {
1170                        mc = 2;
1171                    } else if (moist >= (drierValueUpper - (driestValueUpper) * 0.2)) {
1172                        mc = 1;
1173                    } else {
1174                        mc = 0;
1175                    }
1176
1177                    if (hot >= (warmestValueUpper - (warmerValueUpper - warmerValueLower) * 0.2) * i_hot) {
1178                        hc = 5;
1179                    } else if (hot >= (warmerValueUpper - (warmValueUpper - warmValueLower) * 0.2) * i_hot) {
1180                        hc = 4;
1181                    } else if (hot >= (warmValueUpper - (coldValueUpper - coldValueLower) * 0.2) * i_hot) {
1182                        hc = 3;
1183                    } else if (hot >= (coldValueUpper - (colderValueUpper - colderValueLower) * 0.2) * i_hot) {
1184                        hc = 2;
1185                    } else if (hot >= (colderValueUpper - (coldestValueUpper) * 0.2) * i_hot) {
1186                        hc = 1;
1187                    } else {
1188                        hc = 0;
1189                    }
1190
1191                    heatCodeData[x][y] = hc;
1192                    moistureCodeData[x][y] = mc;
1193                    // 54 == 9 * 6, 9 is used for Ocean groups
1194                    bc = heightCode < 4 ? hc + 54 // 54 == 9 * 6, 9 is used for Ocean groups
1195                            : isLake ? hc + 48 : heightCode == 4 ? hc + 36 : hc + mc * 6;
1196
1197                    if(heightCode < 4) {
1198                        mc = 9;
1199                    }
1200                    else if (moist >= (wetterValueUpper + (wettestValueUpper - wettestValueLower) * 0.2)) {
1201                        mc = 5;
1202                    } else if (moist >= (wetValueUpper + (wetterValueUpper - wetterValueLower) * 0.2)) {
1203                        mc = 4;
1204                    } else if (moist >= (dryValueUpper + (wetValueUpper - wetValueLower) * 0.2)) {
1205                        mc = 3;
1206                    } else if (moist >= (drierValueUpper + (dryValueUpper - dryValueLower) * 0.2)) {
1207                        mc = 2;
1208                    } else if (moist >= (driestValueUpper + (drierValueUpper - drierValueLower) * 0.2)) {
1209                        mc = 1;
1210                    } else {
1211                        mc = 0;
1212                    }
1213
1214                    if (hot >= (warmerValueUpper + (warmestValueUpper - warmestValueLower) * 0.2) * i_hot) {
1215                        hc = 5;
1216                    } else if (hot >= (warmValueUpper + (warmerValueUpper - warmerValueLower) * 0.2) * i_hot) {
1217                        hc = 4;
1218                    } else if (hot >= (coldValueUpper + (warmValueUpper - warmValueLower) * 0.2) * i_hot) {
1219                        hc = 3;
1220                    } else if (hot >= (colderValueUpper + (coldValueUpper - coldValueLower) * 0.2) * i_hot) {
1221                        hc = 2;
1222                    } else if (hot >= (coldestValueUpper + (colderValueUpper - colderValueLower) * 0.2) * i_hot) {
1223                        hc = 1;
1224                    } else {
1225                        hc = 0;
1226                    }
1227
1228                    bc |= (hc + mc * 6) << 10;
1229                    if(heightCode < 4)
1230                        biomeCodeData[x][y] = bc | (int)((heightData[x][y] + 1.0) * 1000.0) << 20;
1231                    else biomeCodeData[x][y] = bc | (int) ((heightCode == 4)
1232                            ? (sandUpper - high) * 10240.0 // multiplier affected by changes to sandLower
1233                            : NumberTools.sway((high + moist) * (4.1 + high - hot)) * 512 + 512) << 20;
1234                }
1235            }
1236        }
1237    }
1238
1239    /**
1240     * A concrete implementation of {@link WorldMapGenerator} that tiles both east-to-west and north-to-south. It tends
1241     * to not appear distorted like {@link SphereMap} does in some areas, even though this is inaccurate for a
1242     * rectangular projection of a spherical world (that inaccuracy is likely what players expect in a map, though).
1243     * You may want {@link LocalMap} instead, for non-world maps that don't tile.
1244     * <a href="http://squidpony.github.io/SquidLib/DetailedWorldMapDemo.png" >Example map</a>.
1245     */
1246    public static class TilingMap extends WorldMapGenerator {
1247        //protected static final double terrainFreq = 1.5, terrainRidgedFreq = 1.3, heatFreq = 2.8, moistureFreq = 2.9, otherFreq = 4.5;
1248//        protected static final double terrainFreq = 1.175, terrainRidgedFreq = 1.3, heatFreq = 2.3, moistureFreq = 2.4, otherFreq = 3.5;
1249        protected static final double terrainFreq = 0.95, terrainRidgedFreq = 3.1, heatFreq = 2.1, moistureFreq = 2.125, otherFreq = 3.375;
1250        private double minHeat0 = Double.POSITIVE_INFINITY, maxHeat0 = Double.NEGATIVE_INFINITY,
1251                minHeat1 = Double.POSITIVE_INFINITY, maxHeat1 = Double.NEGATIVE_INFINITY,
1252                minWet0 = Double.POSITIVE_INFINITY, maxWet0 = Double.NEGATIVE_INFINITY;
1253
1254        public final Noise4D terrain, terrainRidged, heat, moisture, otherRidged;
1255
1256        /**
1257         * Constructs a concrete WorldMapGenerator for a map that can be used as a tiling, wrapping east-to-west as well
1258         * as north-to-south. Always makes a 256x256 map.
1259         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
1260         * If you were using {@link TilingMap#TilingMap(long, int, int, Noise4D, double)}, then this would be the
1261         * same as passing the parameters {@code 0x1337BABE1337D00DL, 256, 256, WorldMapGenerator.DEFAULT_NOISE, 1.0}.
1262         */
1263        public TilingMap() {
1264            this(0x1337BABE1337D00DL, 256, 256, WorldMapGenerator.DEFAULT_NOISE, 1.0);
1265        }
1266
1267        /**
1268         * Constructs a concrete WorldMapGenerator for a map that can be used as a tiling, wrapping east-to-west as well
1269         * as north-to-south.
1270         * Takes only the width/height of the map. The initial seed is set to the same large long
1271         * every time, and it's likely that you would set the seed when you call {@link #generate(long)}. The width and
1272         * height of the map cannot be changed after the fact, but you can zoom in.
1273         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
1274         *
1275         * @param mapWidth  the width of the map(s) to generate; cannot be changed later
1276         * @param mapHeight the height of the map(s) to generate; cannot be changed later
1277         */
1278        public TilingMap(int mapWidth, int mapHeight) {
1279            this(0x1337BABE1337D00DL, mapWidth, mapHeight, DEFAULT_NOISE, 1.0);
1280        }
1281
1282        /**
1283         * Constructs a concrete WorldMapGenerator for a map that can be used as a tiling, wrapping east-to-west as well
1284         * as north-to-south.
1285         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
1286         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
1287         * The width and height of the map cannot be changed after the fact, but you can zoom in.
1288         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
1289         *
1290         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
1291         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
1292         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
1293         */
1294        public TilingMap(long initialSeed, int mapWidth, int mapHeight) {
1295            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, 1.0);
1296        }
1297
1298        /**
1299         * Constructs a concrete WorldMapGenerator for a map that can be used as a tiling, wrapping east-to-west as well
1300         * as north-to-south. Takes an initial seed, the width/height of the map, and a noise generator (a
1301         * {@link Noise4D} implementation, which is usually {@link FastNoise#instance}. The {@code initialSeed}
1302         * parameter may or may not be used, since you can specify the seed to use when you call
1303         * {@link #generate(long)}. The width and height of the map cannot be changed after the fact, but you can zoom
1304         * in. Any seed supplied to the Noise4D given to this (if it takes one) will be ignored, and
1305         * {@link Noise4D#getNoiseWithSeed(double, double, double, double, long)} will be used to specify the seed many
1306         * times. The detail level, which is the {@code octaveMultiplier} parameter that can be passed to another
1307         * constructor, is always 1.0 with this constructor.
1308         *
1309         * @param initialSeed      the seed for the GWTRNG this uses; this may also be set per-call to generate
1310         * @param mapWidth         the width of the map(s) to generate; cannot be changed later
1311         * @param mapHeight        the height of the map(s) to generate; cannot be changed later
1312         * @param noiseGenerator   an instance of a noise generator capable of 4D noise, recommended to be {@link FastNoise#instance}
1313         */
1314        public TilingMap(long initialSeed, int mapWidth, int mapHeight, final Noise4D noiseGenerator) {
1315            this(initialSeed, mapWidth, mapHeight, noiseGenerator, 1.0);
1316        }
1317
1318        /**
1319         * Constructs a concrete WorldMapGenerator for a map that can be used as a tiling, wrapping east-to-west as well
1320         * as north-to-south. Takes an initial seed, the width/height of the map, and parameters for noise
1321         * generation (a {@link Noise4D} implementation, which is usually {@link FastNoise#instance}, and a
1322         * multiplier on how many octaves of noise to use, with 1.0 being normal (high) detail and higher multipliers
1323         * producing even more detailed noise when zoomed-in). The {@code initialSeed} parameter may or may not be used,
1324         * since you can specify the seed to use when you call {@link #generate(long)}. The width and height of the map
1325         * cannot be changed after the fact, but you can zoom in. Any seed supplied to the Noise4D given to this (if it takes one) will be ignored, and
1326         * {@link Noise4D#getNoiseWithSeed(double, double, double, double, long)} will be used to specify the seed many
1327         * times. The {@code octaveMultiplier} parameter should probably be no lower than 0.5, but can be arbitrarily
1328         * high if you're willing to spend much more time on generating detail only noticeable at very high zoom;
1329         * normally 1.0 is fine and may even be too high for maps that don't require zooming.
1330         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
1331         * @param mapWidth the width of the map(s) to generate; cannot be changed later
1332         * @param mapHeight the height of the map(s) to generate; cannot be changed later
1333         * @param noiseGenerator an instance of a noise generator capable of 4D noise, almost always {@link FastNoise}
1334         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
1335         */
1336        public TilingMap(long initialSeed, int mapWidth, int mapHeight, final Noise4D noiseGenerator, double octaveMultiplier) {
1337            super(initialSeed, mapWidth, mapHeight);
1338            terrain = new Noise.InverseLayered4D(noiseGenerator, (int) (0.5 + octaveMultiplier * 8), terrainFreq);
1339            terrainRidged = new Noise.Ridged4D(noiseGenerator, (int) (0.5 + octaveMultiplier * 10), terrainRidgedFreq);
1340            heat = new Noise.InverseLayered4D(noiseGenerator, (int) (0.5 + octaveMultiplier * 3), heatFreq);
1341            moisture = new Noise.InverseLayered4D(noiseGenerator, (int) (0.5 + octaveMultiplier * 4), moistureFreq);
1342            otherRidged = new Noise.Ridged4D(noiseGenerator, (int) (0.5 + octaveMultiplier * 6), otherFreq);
1343        }
1344
1345        /**
1346         * Copies the TilingMap {@code other} to construct a new one that is exactly the same. References will only be
1347         * shared to Noise classes.
1348         * @param other a TilingMap to copy
1349         */
1350        public TilingMap(TilingMap other)
1351        {
1352            super(other);
1353            terrain = other.terrain;
1354            terrainRidged = other.terrainRidged;
1355            heat = other.heat;
1356            moisture = other.moisture;
1357            otherRidged = other.otherRidged;
1358            minHeat0 = other.minHeat0;
1359            maxHeat0 = other.maxHeat0;
1360            minHeat1 = other.minHeat1;
1361            maxHeat1 = other.maxHeat1;
1362            minWet0 = other.minWet0;
1363            maxWet0 = other.maxWet0;
1364        }
1365
1366        protected void regenerate(int startX, int startY, int usedWidth, int usedHeight,
1367                                  double landMod, double heatMod, int stateA, int stateB)
1368        {
1369            boolean fresh = false;
1370            if(cacheA != stateA || cacheB != stateB || landMod != landModifier || heatMod != heatModifier)
1371            {
1372                minHeight = Double.POSITIVE_INFINITY;
1373                maxHeight = Double.NEGATIVE_INFINITY;
1374                minHeat0 = Double.POSITIVE_INFINITY;
1375                maxHeat0 = Double.NEGATIVE_INFINITY;
1376                minHeat1 = Double.POSITIVE_INFINITY;
1377                maxHeat1 = Double.NEGATIVE_INFINITY;
1378                minHeat = Double.POSITIVE_INFINITY;
1379                maxHeat = Double.NEGATIVE_INFINITY;
1380                minWet0 = Double.POSITIVE_INFINITY;
1381                maxWet0 = Double.NEGATIVE_INFINITY;
1382                minWet = Double.POSITIVE_INFINITY;
1383                maxWet = Double.NEGATIVE_INFINITY;
1384                cacheA = stateA;
1385                cacheB = stateB;
1386                fresh = true;
1387            }
1388            rng.setState(stateA, stateB);
1389            long seedA = rng.nextLong(), seedB = rng.nextLong(), seedC = rng.nextLong();
1390            int t;
1391
1392            landModifier = (landMod <= 0) ? rng.nextDouble(0.1875) + 0.99 : landMod;
1393            heatModifier = (heatMod <= 0) ? rng.nextDouble(0.45) * (rng.nextDouble()-0.5) + 1.1 : heatMod;
1394
1395            double p, q,
1396                    ps, pc,
1397                    qs, qc,
1398                    h, temp,
1399                    i_w = 6.283185307179586 / width, i_h = 6.283185307179586 / height,
1400                    xPos = startX, yPos = startY, i_uw = usedWidth / (double)width, i_uh = usedHeight / (double)height;
1401            double[] trigTable = new double[width << 1];
1402            for (int x = 0; x < width; x++, xPos += i_uw) {
1403                p = xPos * i_w;
1404                trigTable[x<<1]   = NumberTools.sin(p);
1405                trigTable[x<<1|1] = NumberTools.cos(p);
1406            }
1407            for (int y = 0; y < height; y++, yPos += i_uh) {
1408                q = yPos * i_h;
1409                qs = NumberTools.sin(q);
1410                qc = NumberTools.cos(q);
1411                for (int x = 0, xt = 0; x < width; x++) {
1412                    ps = trigTable[xt++];//NumberTools.sin(p);
1413                    pc = trigTable[xt++];//NumberTools.cos(p);
1414                    heightData[x][y] = (h = terrain.getNoiseWithSeed(pc +
1415                                    terrainRidged.getNoiseWithSeed(pc, ps, qc, qs,seedB - seedA) * 0.25,
1416                            ps, qc, qs, seedA) + landModifier - 1.0);
1417                    heatData[x][y] = (p = heat.getNoiseWithSeed(pc, ps, qc
1418                                    + otherRidged.getNoiseWithSeed(pc, ps, qc, qs, seedB + seedC)
1419                            , qs, seedB));
1420                    moistureData[x][y] = (temp = moisture.getNoiseWithSeed(pc, ps, qc, qs
1421                                    + otherRidged.getNoiseWithSeed(pc, ps, qc, qs, seedC + seedA)
1422                            , seedC));
1423                    minHeightActual = Math.min(minHeightActual, h);
1424                    maxHeightActual = Math.max(maxHeightActual, h);
1425                    if(fresh) {
1426                        minHeight = Math.min(minHeight, h);
1427                        maxHeight = Math.max(maxHeight, h);
1428
1429                        minHeat0 = Math.min(minHeat0, p);
1430                        maxHeat0 = Math.max(maxHeat0, p);
1431
1432                        minWet0 = Math.min(minWet0, temp);
1433                        maxWet0 = Math.max(maxWet0, temp);
1434
1435                    }
1436                }
1437                minHeightActual = Math.min(minHeightActual, minHeight);
1438                maxHeightActual = Math.max(maxHeightActual, maxHeight);
1439
1440            }
1441            double heightDiff = 2.0 / (maxHeightActual - minHeightActual),
1442                    heatDiff = 0.8 / (maxHeat0 - minHeat0),
1443                    wetDiff = 1.0 / (maxWet0 - minWet0),
1444                    hMod,
1445                    halfHeight = (height - 1) * 0.5, i_half = 1.0 / halfHeight;
1446            double minHeightActual0 = minHeightActual;
1447            double maxHeightActual0 = maxHeightActual;
1448            yPos = startY;
1449            ps = Double.POSITIVE_INFINITY;
1450            pc = Double.NEGATIVE_INFINITY;
1451
1452            for (int y = 0; y < height; y++, yPos += i_uh) {
1453                temp = Math.abs(yPos - halfHeight) * i_half;
1454                temp *= (2.4 - temp);
1455                temp = 2.2 - temp;
1456                for (int x = 0; x < width; x++) {
1457//                    heightData[x][y] = (h = (heightData[x][y] - minHeightActual) * heightDiff - 1.0);
1458//                    minHeightActual0 = Math.min(minHeightActual0, h);
1459//                    maxHeightActual0 = Math.max(maxHeightActual0, h);
1460                    h = heightData[x][y];
1461                    heightCodeData[x][y] = (t = codeHeight(h));
1462                    hMod = 1.0;
1463                    switch (t) {
1464                        case 0:
1465                        case 1:
1466                        case 2:
1467                        case 3:
1468                            h = 0.4;
1469                            hMod = 0.2;
1470                            break;
1471                        case 6:
1472                            h = -0.1 * (h - forestLower - 0.08);
1473                            break;
1474                        case 7:
1475                            h *= -0.25;
1476                            break;
1477                        case 8:
1478                            h *= -0.4;
1479                            break;
1480                        default:
1481                            h *= 0.05;
1482                    }
1483                    heatData[x][y] = (h = (((heatData[x][y] - minHeat0) * heatDiff * hMod) + h + 0.6) * temp);
1484                    if (fresh) {
1485                        ps = Math.min(ps, h); //minHeat0
1486                        pc = Math.max(pc, h); //maxHeat0
1487                    }
1488                }
1489            }
1490            if(fresh)
1491            {
1492                minHeat1 = ps;
1493                maxHeat1 = pc;
1494            }
1495            heatDiff = heatModifier / (maxHeat1 - minHeat1);
1496            qs = Double.POSITIVE_INFINITY;
1497            qc = Double.NEGATIVE_INFINITY;
1498            ps = Double.POSITIVE_INFINITY;
1499            pc = Double.NEGATIVE_INFINITY;
1500
1501
1502            for (int y = 0; y < height; y++) {
1503                for (int x = 0; x < width; x++) {
1504                    heatData[x][y] = (h = ((heatData[x][y] - minHeat1) * heatDiff));
1505                    moistureData[x][y] = (temp = (moistureData[x][y] - minWet0) * wetDiff);
1506                    if (fresh) {
1507                        qs = Math.min(qs, h);
1508                        qc = Math.max(qc, h);
1509                        ps = Math.min(ps, temp);
1510                        pc = Math.max(pc, temp);
1511                    }
1512                }
1513            }
1514            if(fresh)
1515            {
1516                minHeat = qs;
1517                maxHeat = qc;
1518                minWet = ps;
1519                maxWet = pc;
1520            }
1521            landData.refill(heightCodeData, 4, 999);
1522            /*
1523            if(generateRivers) {
1524                if (fresh) {
1525                    addRivers();
1526                    riverData.connect8way().thin().thin();
1527                    lakeData.connect8way().thin();
1528                    partialRiverData.remake(riverData);
1529                    partialLakeData.remake(lakeData);
1530                } else {
1531                    partialRiverData.remake(riverData);
1532                    partialLakeData.remake(lakeData);
1533                    int stx = (zoomStartX >> (zoom)) - (width >> 1),
1534                            sty = (zoomStartY >> (zoom)) - (height >> 1);
1535                    for (int i = 1; i <= zoom; i++) {
1536//                        int stx = (startCacheX.get(i) - startCacheX.get(i - 1)) << (i - 1),
1537//                                sty = (startCacheY.get(i) - startCacheY.get(i - 1)) << (i - 1);
1538                        if ((i & 3) == 3) {
1539                            partialRiverData.zoom(stx, sty).connect8way();
1540                            partialRiverData.or(workingData.remake(partialRiverData).fringe().quasiRandomRegion(0.4));
1541                            partialLakeData.zoom(stx, sty).connect8way();
1542                            partialLakeData.or(workingData.remake(partialLakeData).fringe().quasiRandomRegion(0.55));
1543                        } else {
1544                            partialRiverData.zoom(stx, sty).connect8way().thin();
1545                            partialRiverData.or(workingData.remake(partialRiverData).fringe().quasiRandomRegion(0.5));
1546                            partialLakeData.zoom(stx, sty).connect8way().thin();
1547                            partialLakeData.or(workingData.remake(partialLakeData).fringe().quasiRandomRegion(0.7));
1548                        }
1549                    }
1550                }
1551            }
1552            */
1553        }
1554    }
1555
1556    /**
1557     * A concrete implementation of {@link WorldMapGenerator} that distorts the map as it nears the poles, expanding the
1558     * smaller-diameter latitude lines in extreme north and south regions so they take up the same space as the equator;
1559     * this counteracts certain artifacts that are common in Simplex noise world maps by using a 4D noise call to
1560     * generate terrain, using a normal 3D noise call's result as the extra 4th dimension. This generator allows
1561     * choosing a {@link Noise3D}, which is used for most of the generation. This is ideal for projecting onto a 3D
1562     * sphere, which could squash the poles to counteract the stretch this does. You might also want to produce an oval
1563     * map that more-accurately represents the changes in the diameter of a latitude line on a spherical world; you
1564     * should use {@link EllipticalMap} or {@link EllipticalHammerMap} for this.
1565     * {@link HyperellipticalMap} is also a nice option because it can project onto a shape between a
1566     * rectangle (like this class) and an ellipse (like EllipticalMap), with all-round sides.
1567     * <a href="http://squidpony.github.io/SquidLib/SphereWorld.png" >Example map</a>.
1568     */
1569    public static class SphereMap extends WorldMapGenerator {
1570        protected static final double terrainFreq = 1.45, terrainRidgedFreq = 3.1, heatFreq = 2.1, moistureFreq = 2.125, otherFreq = 3.375;
1571        //protected static final double terrainFreq = 1.65, terrainRidgedFreq = 1.8, heatFreq = 2.1, moistureFreq = 2.125, otherFreq = 3.375, riverRidgedFreq = 21.7;
1572        private double minHeat0 = Double.POSITIVE_INFINITY, maxHeat0 = Double.NEGATIVE_INFINITY,
1573                minHeat1 = Double.POSITIVE_INFINITY, maxHeat1 = Double.NEGATIVE_INFINITY,
1574                minWet0 = Double.POSITIVE_INFINITY, maxWet0 = Double.NEGATIVE_INFINITY;
1575
1576        public final Noise3D terrain, heat, moisture, otherRidged, terrainLayered;
1577        public final double[][] xPositions,
1578                yPositions,
1579                zPositions;
1580
1581
1582        /**
1583         * Constructs a concrete WorldMapGenerator for a map that can be used to wrap a sphere (as with a texture on a
1584         * 3D model), with seamless east-west wrapping, no north-south wrapping, and distortion that causes the poles to
1585         * have significantly-exaggerated-in-size features while the equator is not distorted.
1586         * Always makes a 256x128 map.
1587         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
1588         * If you were using {@link SphereMap#SphereMap(long, int, int, Noise3D, double)}, then this would be the
1589         * same as passing the parameters {@code 0x1337BABE1337D00DL, 256, 128, DEFAULT_NOISE, 1.0}.
1590         */
1591        public SphereMap() {
1592            this(0x1337BABE1337D00DL, 256, 128, DEFAULT_NOISE, 1.0);
1593        }
1594
1595        /**
1596         * Constructs a concrete WorldMapGenerator for a map that can be used to wrap a sphere (as with a texture on a
1597         * 3D model), with seamless east-west wrapping, no north-south wrapping, and distortion that causes the poles to
1598         * have significantly-exaggerated-in-size features while the equator is not distorted.
1599         * Takes only the width/height of the map. The initial seed is set to the same large long
1600         * every time, and it's likely that you would set the seed when you call {@link #generate(long)}. The width and
1601         * height of the map cannot be changed after the fact, but you can zoom in.
1602         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
1603         *
1604         * @param mapWidth  the width of the map(s) to generate; cannot be changed later
1605         * @param mapHeight the height of the map(s) to generate; cannot be changed later
1606         */
1607        public SphereMap(int mapWidth, int mapHeight) {
1608            this(0x1337BABE1337D00DL, mapWidth, mapHeight,  DEFAULT_NOISE,1.0);
1609        }
1610
1611        /**
1612         * Constructs a concrete WorldMapGenerator for a map that can be used to wrap a sphere (as with a texture on a
1613         * 3D model), with seamless east-west wrapping, no north-south wrapping, and distortion that causes the poles to
1614         * have significantly-exaggerated-in-size features while the equator is not distorted.
1615         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
1616         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
1617         * The width and height of the map cannot be changed after the fact, but you can zoom in.
1618         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
1619         *
1620         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
1621         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
1622         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
1623         */
1624        public SphereMap(long initialSeed, int mapWidth, int mapHeight) {
1625            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, 1.0);
1626        }
1627
1628        /**
1629         * Constructs a concrete WorldMapGenerator for a map that can be used to wrap a sphere (as with a texture on a
1630         * 3D model), with seamless east-west wrapping, no north-south wrapping, and distortion that causes the poles to
1631         * have significantly-exaggerated-in-size features while the equator is not distorted.
1632         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
1633         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
1634         * The width and height of the map cannot be changed after the fact, but you can zoom in.
1635         * Uses FastNoise as its noise generator, with the given octave multiplier affecting detail.
1636         *
1637         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
1638         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
1639         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
1640         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
1641         */
1642        public SphereMap(long initialSeed, int mapWidth, int mapHeight, double octaveMultiplier) {
1643            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, octaveMultiplier);
1644        }
1645
1646        /**
1647         * Constructs a concrete WorldMapGenerator for a map that can be used to wrap a sphere (as with a texture on a
1648         * 3D model), with seamless east-west wrapping, no north-south wrapping, and distortion that causes the poles to
1649         * have significantly-exaggerated-in-size features while the equator is not distorted.
1650         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
1651         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
1652         * The width and height of the map cannot be changed after the fact, but you can zoom in.
1653         * Uses the given noise generator, with 1.0 as the octave multiplier affecting detail.
1654         *
1655         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
1656         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
1657         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
1658         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise}
1659         */
1660        public SphereMap(long initialSeed, int mapWidth, int mapHeight, Noise3D noiseGenerator) {
1661            this(initialSeed, mapWidth, mapHeight, noiseGenerator, 1.0);
1662        }
1663
1664        /**
1665         * Constructs a concrete WorldMapGenerator for a map that can be used to wrap a sphere (as with a texture on a
1666         * 3D model), with seamless east-west wrapping, no north-south wrapping, and distortion that causes the poles to
1667         * have significantly-exaggerated-in-size features while the equator is not distorted.
1668         * Takes an initial seed, the width/height of the map, and parameters for noise
1669         * generation (a {@link Noise3D} implementation, which is usually {@link FastNoise#instance}, and a
1670         * multiplier on how many octaves of noise to use, with 1.0 being normal (high) detail and higher multipliers
1671         * producing even more detailed noise when zoomed-in). The {@code initialSeed} parameter may or may not be used,
1672         * since you can specify the seed to use when you call {@link #generate(long)}. The width and height of the map
1673         * cannot be changed after the fact, but you can zoom in. FastNoise will be the fastest 3D generator to use for
1674         * {@code noiseGenerator}, and the seed it's constructed with doesn't matter because this will change the
1675         * seed several times at different scales of noise (it's fine to use the static {@link FastNoise#instance}
1676         * because it has no changing state between runs of the program). The {@code octaveMultiplier} parameter should
1677         * probably be no lower than 0.5, but can be arbitrarily high if you're willing to spend much more time on
1678         * generating detail only noticeable at very high zoom; normally 1.0 is fine and may even be too high for maps
1679         * that don't require zooming.
1680         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
1681         * @param mapWidth the width of the map(s) to generate; cannot be changed later
1682         * @param mapHeight the height of the map(s) to generate; cannot be changed later
1683         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise#instance}
1684         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
1685         */
1686        public SphereMap(long initialSeed, int mapWidth, int mapHeight, Noise3D noiseGenerator, double octaveMultiplier) {
1687            super(initialSeed, mapWidth, mapHeight);
1688            xPositions = new double[width][height];
1689            yPositions = new double[width][height];
1690            zPositions = new double[width][height];
1691
1692            terrain = new Noise.Ridged3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 10), terrainFreq);
1693            terrainLayered = new Noise.InverseLayered3D(noiseGenerator, (int) (1 + octaveMultiplier * 6), terrainRidgedFreq * 0.325);
1694            heat = new Noise.InverseLayered3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 3), heatFreq, 0.75);
1695            moisture = new Noise.InverseLayered3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 4), moistureFreq, 0.55);
1696            otherRidged = new Noise.Ridged3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 6), otherFreq);
1697        }
1698        @Override
1699        public int wrapY(final int x, final int y)  {
1700            return Math.max(0, Math.min(y, height - 1));
1701        }
1702
1703        /**
1704         * Given a latitude and longitude in radians (the conventional way of describing points on a globe), this gets the
1705         * (x,y) Coord on the map projection this generator uses that corresponds to the given lat-lon coordinates. If this
1706         * generator does not represent a globe (if it is toroidal, for instance) or if there is no "good way" to calculate
1707         * the projection for a given lat-lon coordinate, this returns null. This implementation never returns null.
1708         * If this is a supported operation and the parameters are valid, this returns a Coord with x between 0 and
1709         * {@link #width}, and y between 0 and {@link #height}, both exclusive. Automatically wraps the Coord's values using
1710         * {@link #wrapX(int, int)} and {@link #wrapY(int, int)}.
1711         * @param latitude the latitude, from {@code Math.PI * -0.5} to {@code Math.PI * 0.5}
1712         * @param longitude the longitude, from {@code 0.0} to {@code Math.PI * 2.0}
1713         * @return the point at the given latitude and longitude, as a Coord with x between 0 and {@link #width} and y between 0 and {@link #height}, or null if unsupported
1714         */
1715        // 0.7978845608028654 1.2533141373155001
1716        @Override
1717        public Coord project(double latitude, double longitude) {
1718            int x = (int)((((longitude - getCenterLongitude()) + 12.566370614359172) % 6.283185307179586) * 0.15915494309189535 * width),
1719                    y = (int)((NumberTools.sin(latitude) * 0.5 + 0.5) * height);
1720            return Coord.get(
1721                    wrapX(x, y),
1722                    wrapY(x, y));
1723        }
1724
1725        /**
1726         * Copies the SphereMap {@code other} to construct a new one that is exactly the same. References will only be
1727         * shared to Noise classes.
1728         * @param other a SphereMap to copy
1729         */
1730        public SphereMap(SphereMap other)
1731        {
1732            super(other);
1733            terrain = other.terrain;
1734            terrainLayered = other.terrainLayered;
1735            heat = other.heat;
1736            moisture = other.moisture;
1737            otherRidged = other.otherRidged;
1738            minHeat0 = other.minHeat0;
1739            maxHeat0 = other.maxHeat0;
1740            minHeat1 = other.minHeat1;
1741            maxHeat1 = other.maxHeat1;
1742            minWet0 = other.minWet0;
1743            maxWet0 = other.maxWet0;
1744            xPositions = ArrayTools.copy(other.xPositions);
1745            yPositions = ArrayTools.copy(other.yPositions);
1746            zPositions = ArrayTools.copy(other.zPositions);
1747        }
1748
1749        protected void regenerate(int startX, int startY, int usedWidth, int usedHeight,
1750                                  double landMod, double heatMod, int stateA, int stateB)
1751        {
1752            boolean fresh = false;
1753            if(cacheA != stateA || cacheB != stateB || landMod != landModifier || heatMod != heatModifier)
1754            {
1755                minHeight = Double.POSITIVE_INFINITY;
1756                maxHeight = Double.NEGATIVE_INFINITY;
1757                minHeat0 = Double.POSITIVE_INFINITY;
1758                maxHeat0 = Double.NEGATIVE_INFINITY;
1759                minHeat1 = Double.POSITIVE_INFINITY;
1760                maxHeat1 = Double.NEGATIVE_INFINITY;
1761                minHeat = Double.POSITIVE_INFINITY;
1762                maxHeat = Double.NEGATIVE_INFINITY;
1763                minWet0 = Double.POSITIVE_INFINITY;
1764                maxWet0 = Double.NEGATIVE_INFINITY;
1765                minWet = Double.POSITIVE_INFINITY;
1766                maxWet = Double.NEGATIVE_INFINITY;
1767                cacheA = stateA;
1768                cacheB = stateB;
1769                fresh = true;
1770            }
1771            rng.setState(stateA, stateB);
1772            long seedA = rng.nextLong(), seedB = rng.nextLong(), seedC = rng.nextLong();
1773            int t;
1774
1775            landModifier = (landMod <= 0) ? rng.nextDouble(0.29) + 0.91 : landMod;
1776            heatModifier = (heatMod <= 0) ? rng.nextDouble(0.45) * (rng.nextDouble()-0.5) + 1.1 : heatMod;
1777
1778            double p,
1779                    ps, pc,
1780                    qs, qc,
1781                    h, temp,
1782                    i_w = 6.283185307179586 / width, i_h = 2.0 / (height+2.0),//(3.141592653589793) / (height+2.0),
1783                    xPos = startX, yPos, i_uw = usedWidth / (double)width, i_uh = usedHeight * i_h / (height+2.0);
1784            final double[] trigTable = new double[width << 1];
1785            for (int x = 0; x < width; x++, xPos += i_uw) {
1786                p = xPos * i_w + centerLongitude;
1787                // 0.7978845608028654 1.2533141373155001
1788                trigTable[x<<1]   = NumberTools.sin(p);// * 1.2533141373155001;
1789                trigTable[x<<1|1] = NumberTools.cos(p);// * 0.7978845608028654;
1790            }
1791            yPos = startY * i_h + i_uh;
1792            for (int y = 0; y < height; y++, yPos += i_uh) {
1793                qs = -1 + yPos;//-1.5707963267948966 + yPos;
1794                qc = NumberTools.cos(NumberTools.asin(qs));
1795                //qs = qs;
1796                //qs = NumberTools.sin(qs);
1797                for (int x = 0, xt = 0; x < width; x++) {
1798                    ps = trigTable[xt++] * qc;//NumberTools.sin(p);
1799                    pc = trigTable[xt++] * qc;//NumberTools.cos(p);
1800                    xPositions[x][y] = pc;
1801                    yPositions[x][y] = ps;
1802                    zPositions[x][y] = qs;
1803                    heightData[x][y] = (h = terrainLayered.getNoiseWithSeed(pc +
1804                                    terrain.getNoiseWithSeed(pc, ps, qs,seedB - seedA) * 0.5,
1805                            ps, qs, seedA) + landModifier - 1.0);
1806                    heatData[x][y] = (p = heat.getNoiseWithSeed(pc, ps
1807                                    + otherRidged.getNoiseWithSeed(pc, ps, qs,seedB + seedC)
1808                            , qs, seedB));
1809                    moistureData[x][y] = (temp = moisture.getNoiseWithSeed(pc, ps, qs
1810                                    + otherRidged.getNoiseWithSeed(pc, ps, qs, seedC + seedA)
1811                            , seedC));
1812
1813                    minHeightActual = Math.min(minHeightActual, h);
1814                    maxHeightActual = Math.max(maxHeightActual, h);
1815                    if(fresh) {
1816                        minHeight = Math.min(minHeight, h);
1817                        maxHeight = Math.max(maxHeight, h);
1818
1819                        minHeat0 = Math.min(minHeat0, p);
1820                        maxHeat0 = Math.max(maxHeat0, p);
1821
1822                        minWet0 = Math.min(minWet0, temp);
1823                        maxWet0 = Math.max(maxWet0, temp);
1824                    }
1825                }
1826                minHeightActual = Math.min(minHeightActual, minHeight);
1827                maxHeightActual = Math.max(maxHeightActual, maxHeight);
1828
1829            }
1830            double  heatDiff = 0.8 / (maxHeat0 - minHeat0),
1831                    wetDiff = 1.0 / (maxWet0 - minWet0),
1832                    hMod;
1833            yPos = startY * i_h + i_uh;
1834            ps = Double.POSITIVE_INFINITY;
1835            pc = Double.NEGATIVE_INFINITY;
1836
1837            for (int y = 0; y < height; y++, yPos += i_uh) {
1838                temp = Math.abs(yPos - 1.0);
1839                temp *= (2.4 - temp);
1840                temp = 2.2 - temp;
1841                for (int x = 0; x < width; x++) {
1842//                    heightData[x][y] = (h = (heightData[x][y] - minHeightActual) * heightDiff - 1.0);
1843//                    minHeightActual0 = Math.min(minHeightActual0, h);
1844//                    maxHeightActual0 = Math.max(maxHeightActual0, h);
1845                    h = heightData[x][y];
1846                    heightCodeData[x][y] = (t = codeHeight(h));
1847                    hMod = 1.0;
1848                    switch (t) {
1849                        case 0:
1850                        case 1:
1851                        case 2:
1852                        case 3:
1853                            h = 0.4;
1854                            hMod = 0.2;
1855                            break;
1856                        case 6:
1857                            h = -0.1 * (h - forestLower - 0.08);
1858                            break;
1859                        case 7:
1860                            h *= -0.25;
1861                            break;
1862                        case 8:
1863                            h *= -0.4;
1864                            break;
1865                        default:
1866                            h *= 0.05;
1867                    }
1868                    heatData[x][y] = (h = (((heatData[x][y] - minHeat0) * heatDiff * hMod) + h + 0.6) * temp);
1869                    if (fresh) {
1870                        ps = Math.min(ps, h); //minHeat0
1871                        pc = Math.max(pc, h); //maxHeat0
1872                    }
1873                }
1874            }
1875            if(fresh)
1876            {
1877                minHeat1 = ps;
1878                maxHeat1 = pc;
1879            }
1880            heatDiff = heatModifier / (maxHeat1 - minHeat1);
1881            qs = Double.POSITIVE_INFINITY;
1882            qc = Double.NEGATIVE_INFINITY;
1883            ps = Double.POSITIVE_INFINITY;
1884            pc = Double.NEGATIVE_INFINITY;
1885
1886
1887            for (int y = 0; y < height; y++) {
1888                for (int x = 0; x < width; x++) {
1889                    heatData[x][y] = (h = ((heatData[x][y] - minHeat1) * heatDiff));
1890                    moistureData[x][y] = (temp = (moistureData[x][y] - minWet0) * wetDiff);
1891                    if (fresh) {
1892                        qs = Math.min(qs, h);
1893                        qc = Math.max(qc, h);
1894                        ps = Math.min(ps, temp);
1895                        pc = Math.max(pc, temp);
1896                    }
1897                }
1898            }
1899            if(fresh)
1900            {
1901                minHeat = qs;
1902                maxHeat = qc;
1903                minWet = ps;
1904                maxWet = pc;
1905            }
1906            landData.refill(heightCodeData, 4, 999);
1907            /*
1908            if(generateRivers) {
1909                if (fresh) {
1910                    addRivers();
1911                    riverData.connect8way().thin().thin();
1912                    lakeData.connect8way().thin();
1913                    partialRiverData.remake(riverData);
1914                    partialLakeData.remake(lakeData);
1915                } else {
1916                    partialRiverData.remake(riverData);
1917                    partialLakeData.remake(lakeData);
1918                    int stx = Math.min(Math.max((zoomStartX >> zoom) - (width >> 2), 0), width),
1919                            sty = Math.min(Math.max((zoomStartY >> zoom) - (height >> 2), 0), height);
1920                    for (int i = 1; i <= zoom; i++) {
1921                        int stx2 = (startCacheX.get(i) - startCacheX.get(i - 1)) << (i - 1),
1922                                sty2 = (startCacheY.get(i) - startCacheY.get(i - 1)) << (i - 1);
1923                        //(zoomStartX >> zoom) - (width >> 1 + zoom), (zoomStartY >> zoom) - (height >> 1 + zoom)
1924
1925//                        Map is 200x100, GreasedRegions have that size too.
1926//                        Zoom 0 only allows 100,50 as the center, 0,0 as the corner
1927//                        Zoom 1 allows 100,50 to 300,150 as the center (x2 coordinates), 0,0 to 200,100 (refers to 200,100) as the corner
1928//                        Zoom 2 allows 100,50 to 700,350 as the center (x4 coordinates), 0,0 to 200,100 (refers to 600,300) as the corner
1929
1930
1931                        System.out.printf("zoomStartX: %d zoomStartY: %d, stx: %d sty: %d, stx2: %d, sty2: %d\n", zoomStartX, zoomStartY, stx, sty, stx2, sty2);
1932                        if ((i & 3) == 3) {
1933                            partialRiverData.zoom(stx, sty).connect8way();
1934                            partialRiverData.or(workingData.remake(partialRiverData).fringe().quasiRandomRegion(0.4));
1935                            partialLakeData.zoom(stx, sty).connect8way();
1936                            partialLakeData.or(workingData.remake(partialLakeData).fringe().quasiRandomRegion(0.55));
1937                        } else {
1938                            partialRiverData.zoom(stx, sty).connect8way().thin();
1939                            partialRiverData.or(workingData.remake(partialRiverData).fringe().quasiRandomRegion(0.5));
1940                            partialLakeData.zoom(stx, sty).connect8way().thin();
1941                            partialLakeData.or(workingData.remake(partialLakeData).fringe().quasiRandomRegion(0.7));
1942                        }
1943                        //stx = (width >> 1) ;//Math.min(Math.max(, 0), width);
1944                        //sty = (height >> 1);//Math.min(Math.max(, 0), height);
1945                    }
1946                    System.out.println();
1947                }
1948            }
1949            */
1950        }
1951    }
1952    /**
1953     * A concrete implementation of {@link WorldMapGenerator} that projects the world map onto an ellipse that should be
1954     * twice as wide as it is tall (although you can stretch it by width and height that don't have that ratio).
1955     * This uses the <a href="https://en.wikipedia.org/wiki/Mollweide_projection">Mollweide projection</a>.
1956     * <a href="http://squidpony.github.io/SquidLib/EllipseWorld.png" >Example map</a>.
1957     */
1958    public static class EllipticalMap extends WorldMapGenerator {
1959        //        protected static final double terrainFreq = 1.35, terrainRidgedFreq = 1.8, heatFreq = 2.1, moistureFreq = 2.125, otherFreq = 3.375, riverRidgedFreq = 21.7;
1960        protected static final double terrainFreq = 1.45, terrainRidgedFreq = 3.1, heatFreq = 2.1, moistureFreq = 2.125, otherFreq = 3.375;
1961        protected double minHeat0 = Double.POSITIVE_INFINITY, maxHeat0 = Double.NEGATIVE_INFINITY,
1962                minHeat1 = Double.POSITIVE_INFINITY, maxHeat1 = Double.NEGATIVE_INFINITY,
1963                minWet0 = Double.POSITIVE_INFINITY, maxWet0 = Double.NEGATIVE_INFINITY;
1964
1965        public final Noise3D terrain, heat, moisture, otherRidged, terrainLayered;
1966        public final double[][] xPositions,
1967                yPositions,
1968                zPositions;
1969        protected final int[] edges;
1970
1971
1972        /**
1973         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
1974         * ellipse without distortion of the sizes of features but with significant distortion of shape.
1975         * Always makes a 200x100 map.
1976         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
1977         * If you were using {@link EllipticalMap#EllipticalMap(long, int, int, Noise3D, double)}, then this would be the
1978         * same as passing the parameters {@code 0x1337BABE1337D00DL, 200, 100, DEFAULT_NOISE, 1.0}.
1979         */
1980        public EllipticalMap() {
1981            this(0x1337BABE1337D00DL, 200, 100, DEFAULT_NOISE, 1.0);
1982        }
1983
1984        /**
1985         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
1986         * ellipse without distortion of the sizes of features but with significant distortion of shape.
1987         * Takes only the width/height of the map. The initial seed is set to the same large long
1988         * every time, and it's likely that you would set the seed when you call {@link #generate(long)}. The width and
1989         * height of the map cannot be changed after the fact, but you can zoom in.
1990         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
1991         *
1992         * @param mapWidth  the width of the map(s) to generate; cannot be changed later
1993         * @param mapHeight the height of the map(s) to generate; cannot be changed later
1994         */
1995        public EllipticalMap(int mapWidth, int mapHeight) {
1996            this(0x1337BABE1337D00DL, mapWidth, mapHeight,  DEFAULT_NOISE,1.0);
1997        }
1998
1999        /**
2000         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
2001         * ellipse without distortion of the sizes of features but with significant distortion of shape.
2002         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
2003         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
2004         * The width and height of the map cannot be changed after the fact, but you can zoom in.
2005         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
2006         *
2007         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
2008         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
2009         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
2010         */
2011        public EllipticalMap(long initialSeed, int mapWidth, int mapHeight) {
2012            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, 1.0);
2013        }
2014
2015        /**
2016         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
2017         * ellipse without distortion of the sizes of features but with significant distortion of shape.
2018         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
2019         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
2020         * The width and height of the map cannot be changed after the fact, but you can zoom in.
2021         * Uses FastNoise as its noise generator, with the given octave multiplier affecting detail.
2022         *
2023         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
2024         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
2025         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
2026         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
2027         */
2028        public EllipticalMap(long initialSeed, int mapWidth, int mapHeight, double octaveMultiplier) {
2029            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, octaveMultiplier);
2030        }
2031
2032        /**
2033         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
2034         * ellipse without distortion of the sizes of features but with significant distortion of shape.
2035         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
2036         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
2037         * The width and height of the map cannot be changed after the fact, but you can zoom in.
2038         * Uses the given noise generator, with 1.0 as the octave multiplier affecting detail. The suggested Noise3D
2039         * implementation to use is {@link FastNoise#instance}.
2040         *
2041         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
2042         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
2043         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
2044         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise}
2045         */
2046        public EllipticalMap(long initialSeed, int mapWidth, int mapHeight, Noise3D noiseGenerator) {
2047            this(initialSeed, mapWidth, mapHeight, noiseGenerator, 1.0);
2048        }
2049
2050        /**
2051         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
2052         * ellipse without distortion of the sizes of features but with significant distortion of shape.
2053         * Takes an initial seed, the width/height of the map, and parameters for noise generation (a
2054         * {@link Noise3D} implementation, where {@link FastNoise#instance} is suggested, and a
2055         * multiplier on how many octaves of noise to use, with 1.0 being normal (high) detail and higher multipliers
2056         * producing even more detailed noise when zoomed-in). The {@code initialSeed} parameter may or may not be used,
2057         * since you can specify the seed to use when you call {@link #generate(long)}. The width and height of the map
2058         * cannot be changed after the fact, but you can zoom in.  FastNoise will be the fastest 3D generator to use for
2059         * {@code noiseGenerator}, and the seed it's constructed with doesn't matter because this will change the
2060         * seed several times at different scales of noise (it's fine to use the static {@link FastNoise#instance}
2061         * because it has no changing state between runs of the program). The {@code octaveMultiplier} parameter should
2062         * probably be no lower than 0.5, but can be arbitrarily high if you're willing to spend much more time on
2063         * generating detail only noticeable at very high zoom; normally 1.0 is fine and may even be too high for maps
2064         * that don't require zooming.
2065         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
2066         * @param mapWidth the width of the map(s) to generate; cannot be changed later
2067         * @param mapHeight the height of the map(s) to generate; cannot be changed later
2068         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise}
2069         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
2070         */
2071        public EllipticalMap(long initialSeed, int mapWidth, int mapHeight, Noise3D noiseGenerator, double octaveMultiplier) {
2072            super(initialSeed, mapWidth, mapHeight);
2073            xPositions = new double[width][height];
2074            yPositions = new double[width][height];
2075            zPositions = new double[width][height];
2076            edges = new int[height << 1];
2077            terrain = new Noise.Ridged3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 10), terrainFreq);
2078            terrainLayered = new Noise.InverseLayered3D(noiseGenerator, (int) (1 + octaveMultiplier * 6), terrainRidgedFreq * 0.325);
2079            heat = new Noise.InverseLayered3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 3), heatFreq, 0.75);
2080            moisture = new Noise.InverseLayered3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 4), moistureFreq, 0.55);
2081            otherRidged = new Noise.Ridged3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 6), otherFreq);
2082        }
2083
2084        /**
2085         * Copies the EllipticalMap {@code other} to construct a new one that is exactly the same. References will only
2086         * be shared to Noise classes.
2087         * @param other an EllipticalMap to copy
2088         */
2089        public EllipticalMap(EllipticalMap other)
2090        {
2091            super(other);
2092            terrain = other.terrain;
2093            terrainLayered = other.terrainLayered;
2094            heat = other.heat;
2095            moisture = other.moisture;
2096            otherRidged = other.otherRidged;
2097            minHeat0 = other.minHeat0;
2098            maxHeat0 = other.maxHeat0;
2099            minHeat1 = other.minHeat1;
2100            maxHeat1 = other.maxHeat1;
2101            minWet0 = other.minWet0;
2102            maxWet0 = other.maxWet0;
2103            xPositions = ArrayTools.copy(other.xPositions);
2104            yPositions = ArrayTools.copy(other.yPositions);
2105            zPositions = ArrayTools.copy(other.zPositions);
2106            edges = Arrays.copyOf(other.edges, other.edges.length);
2107        }
2108
2109        @Override
2110        public int wrapX(final int x, int y) {
2111            y = Math.max(0, Math.min(y, height - 1));
2112            if(x < edges[y << 1])
2113                return edges[y << 1 | 1];
2114            else if(x > edges[y << 1 | 1])
2115                return edges[y << 1];
2116            else return x;
2117        }
2118
2119        @Override
2120        public int wrapY(final int x, final int y)  {
2121            return Math.max(0, Math.min(y, height - 1));
2122        }
2123
2124        protected void regenerate(int startX, int startY, int usedWidth, int usedHeight,
2125                                  double landMod, double heatMod, int stateA, int stateB)
2126        {
2127            boolean fresh = false;
2128            if(cacheA != stateA || cacheB != stateB || landMod != landModifier || heatMod != heatModifier)
2129            {
2130                minHeight = Double.POSITIVE_INFINITY;
2131                maxHeight = Double.NEGATIVE_INFINITY;
2132                minHeat0 = Double.POSITIVE_INFINITY;
2133                maxHeat0 = Double.NEGATIVE_INFINITY;
2134                minHeat1 = Double.POSITIVE_INFINITY;
2135                maxHeat1 = Double.NEGATIVE_INFINITY;
2136                minHeat = Double.POSITIVE_INFINITY;
2137                maxHeat = Double.NEGATIVE_INFINITY;
2138                minWet0 = Double.POSITIVE_INFINITY;
2139                maxWet0 = Double.NEGATIVE_INFINITY;
2140                minWet = Double.POSITIVE_INFINITY;
2141                maxWet = Double.NEGATIVE_INFINITY;
2142                cacheA = stateA;
2143                cacheB = stateB;
2144                fresh = true;
2145            }
2146            rng.setState(stateA, stateB);
2147            long seedA = rng.nextLong(), seedB = rng.nextLong(), seedC = rng.nextLong();
2148            int t;
2149
2150            landModifier = (landMod <= 0) ? rng.nextDouble(0.2) + 0.91 : landMod;
2151            heatModifier = (heatMod <= 0) ? rng.nextDouble(0.45) * (rng.nextDouble()-0.5) + 1.1 : heatMod;
2152
2153            double p,
2154                    ps, pc,
2155                    qs, qc,
2156                    h, temp, yPos, xPos,
2157                    i_uw = usedWidth / (double)width,
2158                    i_uh = usedHeight / (double)height,
2159                    th, thx, thy, lon, lat, ipi = 0.99999 / Math.PI,
2160                    rx = width * 0.25, irx = 1.0 / rx, hw = width * 0.5,
2161                    ry = height * 0.5, iry = 1.0 / ry;
2162
2163            yPos = startY - ry;
2164            for (int y = 0; y < height; y++, yPos += i_uh) {
2165                thx = NumberTools.asin((yPos) * iry);
2166                lon = (thx == Math.PI * 0.5 || thx == Math.PI * -0.5) ? thx : Math.PI * irx * 0.5 / NumberTools.cos(thx);
2167                thy = thx * 2.0;
2168                lat = NumberTools.asin((thy + NumberTools.sin(thy)) * ipi);
2169
2170                qc = NumberTools.cos(lat);
2171                qs = NumberTools.sin(lat);
2172
2173                boolean inSpace = true;
2174                xPos = startX;
2175                for (int x = 0; x < width; x++, xPos += i_uw) {
2176                    th = lon * (xPos - hw);
2177                    if(th < -3.141592653589793 || th > 3.141592653589793) {
2178                        heightCodeData[x][y] = 10000;
2179                        inSpace = true;
2180                        continue;
2181                    }
2182                    if(inSpace)
2183                    {
2184                        inSpace = false;
2185                        edges[y << 1] = x;
2186                    }
2187                    edges[y << 1 | 1] = x;
2188                    th += centerLongitude;
2189                    ps = NumberTools.sin(th) * qc;
2190                    pc = NumberTools.cos(th) * qc;
2191                    xPositions[x][y] = pc;
2192                    yPositions[x][y] = ps;
2193                    zPositions[x][y] = qs;
2194                    heightData[x][y] = (h = terrainLayered.getNoiseWithSeed(pc +
2195                                    terrain.getNoiseWithSeed(pc, ps, qs,seedB - seedA) * 0.5,
2196                            ps, qs, seedA) + landModifier - 1.0);
2197                    heatData[x][y] = (p = heat.getNoiseWithSeed(pc, ps
2198                                    + otherRidged.getNoiseWithSeed(pc, ps, qs,seedB + seedC)
2199                            , qs, seedB));
2200                    moistureData[x][y] = (temp = moisture.getNoiseWithSeed(pc, ps, qs
2201                                    + otherRidged.getNoiseWithSeed(pc, ps, qs, seedC + seedA)
2202                            , seedC));
2203                    minHeightActual = Math.min(minHeightActual, h);
2204                    maxHeightActual = Math.max(maxHeightActual, h);
2205                    if(fresh) {
2206                        minHeight = Math.min(minHeight, h);
2207                        maxHeight = Math.max(maxHeight, h);
2208
2209                        minHeat0 = Math.min(minHeat0, p);
2210                        maxHeat0 = Math.max(maxHeat0, p);
2211
2212                        minWet0 = Math.min(minWet0, temp);
2213                        maxWet0 = Math.max(maxWet0, temp);
2214                    }
2215                }
2216                minHeightActual = Math.min(minHeightActual, minHeight);
2217                maxHeightActual = Math.max(maxHeightActual, maxHeight);
2218
2219            }
2220            double  heatDiff = 0.8 / (maxHeat0 - minHeat0),
2221                    wetDiff = 1.0 / (maxWet0 - minWet0),
2222                    hMod,
2223                    halfHeight = (height - 1) * 0.5, i_half = 1.0 / halfHeight;
2224            yPos = startY + i_uh;
2225            ps = Double.POSITIVE_INFINITY;
2226            pc = Double.NEGATIVE_INFINITY;
2227
2228            for (int y = 0; y < height; y++, yPos += i_uh) {
2229                temp = Math.abs(yPos - halfHeight) * i_half;
2230                temp *= (2.4 - temp);
2231                temp = 2.2 - temp;
2232                for (int x = 0; x < width; x++) {
2233                    h = heightData[x][y];
2234                    if(heightCodeData[x][y] == 10000) {
2235                        heightCodeData[x][y] = 1000;
2236                        continue;
2237                    }
2238                    else
2239                        heightCodeData[x][y] = (t = codeHeight(h));
2240                    hMod = 1.0;
2241                    switch (t) {
2242                        case 0:
2243                        case 1:
2244                        case 2:
2245                        case 3:
2246                            h = 0.4;
2247                            hMod = 0.2;
2248                            break;
2249                        case 6:
2250                            h = -0.1 * (h - forestLower - 0.08);
2251                            break;
2252                        case 7:
2253                            h *= -0.25;
2254                            break;
2255                        case 8:
2256                            h *= -0.4;
2257                            break;
2258                        default:
2259                            h *= 0.05;
2260                    }
2261                    heatData[x][y] = (h = (((heatData[x][y] - minHeat0) * heatDiff * hMod) + h + 0.6) * temp);
2262                    if (fresh) {
2263                        ps = Math.min(ps, h); //minHeat0
2264                        pc = Math.max(pc, h); //maxHeat0
2265                    }
2266                }
2267            }
2268            if(fresh)
2269            {
2270                minHeat1 = ps;
2271                maxHeat1 = pc;
2272            }
2273            heatDiff = heatModifier / (maxHeat1 - minHeat1);
2274            qs = Double.POSITIVE_INFINITY;
2275            qc = Double.NEGATIVE_INFINITY;
2276            ps = Double.POSITIVE_INFINITY;
2277            pc = Double.NEGATIVE_INFINITY;
2278
2279
2280            for (int y = 0; y < height; y++) {
2281                for (int x = 0; x < width; x++) {
2282                    heatData[x][y] = (h = ((heatData[x][y] - minHeat1) * heatDiff));
2283                    moistureData[x][y] = (temp = (moistureData[x][y] - minWet0) * wetDiff);
2284                    if (fresh) {
2285                        qs = Math.min(qs, h);
2286                        qc = Math.max(qc, h);
2287                        ps = Math.min(ps, temp);
2288                        pc = Math.max(pc, temp);
2289                    }
2290                }
2291            }
2292            if(fresh)
2293            {
2294                minHeat = qs;
2295                maxHeat = qc;
2296                minWet = ps;
2297                maxWet = pc;
2298            }
2299            landData.refill(heightCodeData, 4, 999);
2300        }
2301    }
2302
2303    /**
2304     * An unusual map generator that imitates an existing map (such as a map of Earth, which it can do by default). It
2305     * uses the Mollweide projection (an elliptical map projection, the same as what EllipticalMap uses) for both its
2306     * input and output; <a href="https://squidpony.github.io/SquidLib/MimicWorld.png">an example can be seen here</a>,
2307     * imitating Earth using a 512x256 world map as a GreasedRegion for input.
2308     */
2309    public static class MimicMap extends EllipticalMap
2310    {
2311        public GreasedRegion earth;
2312        public GreasedRegion shallow;
2313        public GreasedRegion coast;
2314        public GreasedRegion earthOriginal;
2315        /**
2316         * Constructs a concrete WorldMapGenerator for a map that should look like Earth using an elliptical projection
2317         * (specifically, a Mollweide projection).
2318         * Always makes a 512x256 map.
2319         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
2320         * If you were using {@link MimicMap#MimicMap(long, Noise3D, double)}, then this would be the
2321         * same as passing the parameters {@code 0x1337BABE1337D00DL, DEFAULT_NOISE, 1.0}.
2322         */
2323        public MimicMap() {
2324            this(0x1337BABE1337D00DL
2325                    , DEFAULT_NOISE, 1.0);
2326        }
2327
2328        /**
2329         * Constructs a concrete WorldMapGenerator for a map that should have land in roughly the same places as the
2330         * given GreasedRegion's "on" cells, using an elliptical projection (specifically, a Mollweide projection).
2331         * The initial seed is set to the same large long every time, and it's likely that you would set the seed when
2332         * you call {@link #generate(long)}. The width and height of the map cannot be changed after the fact.
2333         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
2334         *
2335         * @param toMimic the world map to imitate, as a GreasedRegion with land as "on"; the height and width will be copied
2336         */
2337        public MimicMap(GreasedRegion toMimic) {
2338            this(0x1337BABE1337D00DL, toMimic,  DEFAULT_NOISE,1.0);
2339        }
2340
2341        /**
2342         * Constructs a concrete WorldMapGenerator for a map that should have land in roughly the same places as the
2343         * given GreasedRegion's "on" cells, using an elliptical projection (specifically, a Mollweide projection).
2344         * Takes an initial seed and the GreasedRegion containing land positions. The {@code initialSeed}
2345         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
2346         * The width and height of the map cannot be changed after the fact.
2347         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
2348         *
2349         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
2350         * @param toMimic the world map to imitate, as a GreasedRegion with land as "on"; the height and width will be copied
2351         */
2352        public MimicMap(long initialSeed, GreasedRegion toMimic) {
2353            this(initialSeed, toMimic, DEFAULT_NOISE, 1.0);
2354        }
2355
2356        /**
2357         * Constructs a concrete WorldMapGenerator for a map that should have land in roughly the same places as the
2358         * given GreasedRegion's "on" cells, using an elliptical projection (specifically, a Mollweide projection).
2359         * Takes an initial seed, the GreasedRegion containing land positions, and a multiplier that affects the level
2360         * of detail by increasing or decreasing the number of octaves of noise used. The {@code initialSeed}
2361         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
2362         * The width and height of the map cannot be changed after the fact.
2363         * Uses FastNoise as its noise generator, with the given octave multiplier affecting detail.
2364         *
2365         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
2366         * @param toMimic the world map to imitate, as a GreasedRegion with land as "on"; the height and width will be copied
2367         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
2368         */
2369        public MimicMap(long initialSeed, GreasedRegion toMimic, double octaveMultiplier) {
2370            this(initialSeed, toMimic, DEFAULT_NOISE, octaveMultiplier);
2371        }
2372
2373        /**
2374         * Constructs a concrete WorldMapGenerator for a map that should have land in roughly the same places as the
2375         * given GreasedRegion's "on" cells, using an elliptical projection (specifically, a Mollweide projection).
2376         * Takes an initial seed, the GreasedRegion containing land positions, and parameters for noise generation (a
2377         * {@link Noise3D} implementation, which is usually {@link FastNoise#instance}. The {@code initialSeed}
2378         * parameter may or may not be used, since you can specify the seed to use when you call
2379         * {@link #generate(long)}. The width and height of the map cannot be changed after the fact. Both FastNoise
2380         * and FastNoise make sense to use for {@code noiseGenerator}, and the seed it's constructed with doesn't matter
2381         * because this will change the seed several times at different scales of noise (it's fine to use the static
2382         * {@link FastNoise#instance} or {@link FastNoise#instance} because they have no changing state between runs
2383         * of the program). Uses the given noise generator, with 1.0 as the octave multiplier affecting detail.
2384         *
2385         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
2386         * @param toMimic the world map to imitate, as a GreasedRegion with land as "on"; the height and width will be copied
2387         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise} or {@link FastNoise}
2388         */
2389        public MimicMap(long initialSeed, GreasedRegion toMimic, Noise3D noiseGenerator) {
2390            this(initialSeed, toMimic, noiseGenerator, 1.0);
2391        }
2392
2393        /**
2394         * Constructs a concrete WorldMapGenerator for a map that should have land in roughly the same places as the
2395         * given GreasedRegion's "on" cells, using an elliptical projection (specifically, a Mollweide projection).
2396         * Takes an initial seed, the GreasedRegion containing land positions, parameters for noise generation (a
2397         * {@link Noise3D} implementation, which is usually {@link FastNoise#instance}, and a multiplier on how many
2398         * octaves of noise to use, with 1.0 being normal (high) detail and higher multipliers producing even more
2399         * detailed noise when zoomed-in). The {@code initialSeed} parameter may or may not be used,
2400         * since you can specify the seed to use when you call {@link #generate(long)}. The width and height of the map
2401         * cannot be changed after the fact.  FastNoise will be the fastest 3D generator to use for
2402         * {@code noiseGenerator}, and the seed it's constructed with doesn't matter because this will change the
2403         * seed several times at different scales of noise (it's fine to use the static {@link FastNoise#instance}
2404         * because it has no changing state between runs of the program). The {@code octaveMultiplier} parameter should
2405         * probably be no lower than 0.5, but can be arbitrarily high if you're willing to spend much more time on
2406         * generating detail only noticeable at very high zoom; normally 1.0 is fine and may even be too high for maps
2407         * that don't require zooming.
2408         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
2409         * @param toMimic the world map to imitate, as a GreasedRegion with land as "on"; the height and width will be copied
2410         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise} or {@link FastNoise}
2411         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
2412         */
2413        public MimicMap(long initialSeed, GreasedRegion toMimic, Noise3D noiseGenerator, double octaveMultiplier) {
2414            super(initialSeed, toMimic.width, toMimic.height, noiseGenerator, octaveMultiplier);
2415            earth = toMimic;
2416            earthOriginal = earth.copy();
2417            coast   = earth.copy().not().fringe(2);
2418            shallow = earth.copy().fringe(2);
2419        }
2420
2421        /**
2422         * Constructs a 512x256 elliptical world map that will use land forms with a similar shape to Earth.
2423         * @param initialSeed
2424         * @param noiseGenerator
2425         * @param octaveMultiplier
2426         */
2427        public MimicMap(long initialSeed, Noise3D noiseGenerator, double octaveMultiplier)
2428        {
2429            this(initialSeed,
2430                    GreasedRegion.decompress(LZSPlus.decompress(
2431                            "Ƥ䊅⑃л䣁ᩡġˤȰࠨ⑀ၰ䁶ިШဵ⃠䁠ႠʹƨЬࠨư්洤㘢啰ీᆕߊ偌怫本\u242AФd˄д.㖈่ը˨ըᠥࠨ䠤㐪䠼ࠬ栶ࠨଈڇ䎵兊Ƅ䊡ᒠ䒩Ԣ㤠ᬡ䴡䵒خ㐯簤ᐫ䐤ᐸŨϤሡ囁䓈䂔⁓ၯࠥ吥ᨡ儡⡖䁚⁊ဪ䠤⑥❺ᇕ粨䁓灜儯ُⓤϡǒᛤ䪆ୀᅔь掭稡缡ত䁲⁀Ұ\u0EA8䐢娠洩们䁔恄⍠⡨٤̭डಠ䉠㻀ᔲ䱍㐡ᨢࡕ\u089C䱢ⶀՐO廴∣℡䳕䂊⁊ှ栥ᦅي儠\u1CA0䣴⁐䠰ܨդϢü╏䑠䒤䉀མҫ于◧вႠ㥀ᑈȲl\u1CB4䄶ヘ䈩LJᅂ䈶┵℠湠㖰դʈ甤嫤利በڰl±ì\u2029䠩ন娼㠇砩\u175F䌬\u1AD9䱠ྰɔıT戽㑢£䁔ྸ䍂㣠ᑰඅ℠䥠∳ԋ棸玲䑂m䀣專*ơ噓NJေ㩈囤Ŵ䄈㤴ះ۸†䠨䫀纆ဣ䐣冸楚ಫ牴歀ઈdž傠Ѩᴮ㨡䊠涻䢤ȑ÷ᚵР泡⡠௰ס嫼吠㪤ȃÿ\u202D䐪䅅㸵ᅨɶIJ䑀وւ夸‾⠪䈡ᑠ䉀ඈƀҨ㧃⌂¶䁼唨Ö䄈䨡ࢤŵì\u3130̈́Ƞɠ癀ۨã㺨㥓ⱂÏဳ᠊䈡㱠ీ塃\u1754㰭䪁ᜂ䂸⺑扬䈫䱥紤⣩䭲倩⊂Z怦ݠᩝᑡ*瀲⋒瘱Ƞ❠㨭㧳ᜩ䕕⫣瘈♨‿⨮㧌℧懢ච\u20C3ᗨƉ¿媖ഞ䛤\u08C9ᢅ䪤ᘬ¸䁰䠢礠䐿揠槌С喢ጫ™䈹ਠр㴬懯\u2028䰣惤ኰ悠ᄬDzだഘقɀ䋑Ɏ♡Wࠦ掲ฺ戠\u2E50ম峼\u2DA7掸㦨ŀ䁹༠墡ဣ⤠孠أő䉚Р㊠⿹䍂玳ɀ䀣ᴧ⁘‥ᐡ戶ႤÏ䁴\u0D50䲚Ⲫ嚆\u0E91㢙䠒ၠĈր婠煍㼱\u0B91^瀥˅Ɂᓺ䈸↞ቘ⛀䀻堮㔒怮吡カ孧䉊偦IJ兄ø䁴Ǧ䡂ࠃࠩ哃互⠠¡䂮吡䓠縸䝐䀨⠨⇆䦀䖹᪈ÃĈׄ⢤ᕶౄಣ⺉㊧₠ᥰ൘塠Ǹݡ〥浑\"倭ᔂ⓽Ƞ䃠≩䔠匄䬨ㆸ䆆\u1942ⱋ㻭ぷ‡簥碯唥\u08C6\u0A64梴㉄Ë䂇Ւ䔮३昳ࠡ㒠㚤溁崴䛭࿖暁:㠭ዀ戭୮ጣȤ慤掝ᦵŒያ\u20C1ᛶジ♱ႅ硧ࢶ㦰䐂憰ı¤ڨ⌅╳呬ᢌ戚怨Ⱕ\u20FFΣᄈ၃◶Ӑ\u0DE5㪁ᦠ\u0890ઢv‼曬渢瑨ᝤກ磒ᵡ婕沱2䐣厜䀿䠮䃸䪨ƿ\u20C8ʬõ䀻≒䱠ঘʱ≔憘㙀ĂÖ劦惐䀸䠤樞ȍ䄤•愢尳㸊ņຢ梠ްઃ串ṹ⑮檢\u0080怺⌁昵㣨䩗ᐩ䑁Ӗ䑱䛭ᵁС㚠䐰ƃpႨ³èᄤė䁴̔癤䜳㹢\u0FF7樆ࠢ傡栤櫐ಐ䐠䪠ዬႸ䀴㠨℠㧀ၤm堡ࠡठТ㢩ᑀۜ̏房㽋欦㛺㳘召‧℡\u08CCन㨤T࡚Ɛ‡樠ໟ恥₡祤䖄ᘦ峤߱嘰Ƴdʬ㷨䇆ᢐ䱀\u008D~ᰁ灕姆ᬗ䊟Ϣ器⋸ශᡮ㈚伩墵凑᪂穅ڼ\u2D28Å䁃唀ᵧၐ䑡+䠧ᢄഐᨻȖၣ⸩㈠⭙ᔢ\"〭愞ண澮ጪュ湠䆀央刁ડⲿや⻁\u242A檈ᜧ沋坘㗠籽⢰缱ؘȒ&堵ĠᏰࡡ䠴ᆊա籖!⬂屴Ѳ䁊䑖ס࠹䢑ᮣ砷凬‧删ิ㮜ᚠヴР棠⨡ᴾム㩩ნ楨аƍ䂤ٸ榣䅕ᰃ両ȁؠ猨㎸㉪恉ᡀⒹ䁒⬰灁Ƞ᳀ዉົ࡞◢⡔݀猊䄉䯐砻ƨހ䭓拝℥䁄ཾమ䢠ʤʸ⻤ჴࡁ#Э䢠ਔ۠㟣を@奟椨ވ\u08CF䏌⩶晠Ϻİ㏤U⁄‰┣㈡Ð硭ウੁ9䠧䂠\u0B84˄ⶃ傠ňթ吡⻈䡡⭹䆤Ꮓ慀ʞĊ䫡灀ȊŰဣ◠㘫ᗌ॰ᅓƯḃㅤɴ䀨∦䱜Դጯዤࠠ嶠ᛨu䂌䘪ᬮ᪴ˠฦ凞ᘂ牀͡→ᗑ㦖⁘ំ墉䞤ଫ婤݁࠺ુ屣㉇撱\u0CF4制児䉍・䶁ཏĠӐի䅩礠ᶨ\u0FE0ॸ儠‚\u0080殯䀳儢B㠠ᔨు屏䈠ጤڥ灋挲∥咂դ⩦↩୵䙏ᑐ\u1CB1ⵟ⋀‡璠斅䒱☤Ś̔亩䈒塁#⠧椐⇜沱Ġనӊᠹ焘༢W瀲\u0A78ဠ⊠ع䄞ᠫ㸢ℽჵࡤ֊⾼Ⴏᔁⶫ⊮婕ࡀϒÚ⥢倴皏ⵦ璌犰䅬ᣠ€碵Ġːऺ缨椴ၲ㞱櫂⺵⁁勠含めᣜ紧悌ྷ䱻\u0088᭄⹅ⓖ琸䢢ฐ㞹䄠ጨ\u0BA1瓣䂫Ⴂ爩㈠ႈႡኄ∠㏨Ẋ\u3040ΡȘဢ㬡䔱ଥΦ၀.łඪ≂ʬㄠ坤ۼ懱㖢Ѹ籤憗ၠ楑\u0BE2•䜠檷┶ᨧ瀫\u0560䀦ɦٴۘ䈢䠶ߍ娴℠ࣰฤ琫ᕎ\u0AB1ᇀ䄠  "
2432                    )),
2433                    noiseGenerator, octaveMultiplier);
2434        }
2435
2436        /**
2437         * Copies the MimicMap {@code other} to construct a new one that is exactly the same. References will only
2438         * be shared to Noise classes.
2439         * @param other a MimicMap to copy
2440         */
2441        public MimicMap(MimicMap other)
2442        {
2443            super(other);
2444            earth = other.earth.copy();
2445            earthOriginal = other.earthOriginal.copy();
2446            coast   = other.coast.copy();
2447            shallow = other.shallow.copy();
2448        }
2449
2450
2451
2452        /**
2453         * Meant for making maps conform to the Mollweide (elliptical) projection that MimicMap uses.
2454         * @param rectangular A GreasedRegion where "on" represents land and "off" water, using any rectangular projection
2455         * @return a reprojected version of {@code rectangular} that uses an elliptical projection
2456         */
2457        public static GreasedRegion reprojectToElliptical(GreasedRegion rectangular) {
2458            int width = rectangular.width, height = rectangular.height;
2459            GreasedRegion t = new GreasedRegion(width, height);
2460            double yPos, xPos,
2461                    th, thx, thy, lon, lat, ipi = 0.99999 / Math.PI,
2462                    rx = width * 0.25, irx = 1.0 / rx, hw = width * 0.5,
2463                    ry = height * 0.5, iry = 1.0 / ry;
2464    
2465            yPos = -ry;
2466            for (int y = 0; y < height; y++, yPos++) {
2467                thx = NumberTools.asin((yPos) * iry);
2468                lon = (thx == Math.PI * 0.5 || thx == Math.PI * -0.5) ? thx : Math.PI * irx * 0.5 / NumberTools.cos(thx);
2469                thy = thx * 2.0;
2470                lat = NumberTools.asin((thy + NumberTools.sin(thy)) * ipi);
2471                xPos = 0;
2472                for (int x = 0; x < width; x++, xPos++) {
2473                    th = lon * (xPos - hw);
2474                    if (th >= -3.141592653589793 && th <= 3.141592653589793
2475                            && rectangular.contains((int) ((th + 1) * hw), (int) ((lat + 1) * ry))) {
2476                        t.insert(x, y);
2477                    }
2478                }
2479            }
2480            return t;
2481        }
2482
2483        @Override
2484        public int wrapX(final int x, int y) {
2485            y = Math.max(0, Math.min(y, height - 1));
2486            if(x < edges[y << 1])
2487                return edges[y << 1 | 1];
2488            else if(x > edges[y << 1 | 1])
2489                return edges[y << 1];
2490            else return x;
2491        }
2492
2493        @Override
2494        public int wrapY(final int x, final int y)  {
2495            return Math.max(0, Math.min(y, height - 1));
2496        }
2497
2498        protected void regenerate(int startX, int startY, int usedWidth, int usedHeight,
2499                                  double landMod, double heatMod, int stateA, int stateB)
2500        {
2501            boolean fresh = false;
2502            if(cacheA != stateA || cacheB != stateB || landMod != landModifier || heatMod != heatModifier)
2503            {
2504                minHeight = Double.POSITIVE_INFINITY;
2505                maxHeight = Double.NEGATIVE_INFINITY;
2506                minHeat0 = Double.POSITIVE_INFINITY;
2507                maxHeat0 = Double.NEGATIVE_INFINITY;
2508                minHeat1 = Double.POSITIVE_INFINITY;
2509                maxHeat1 = Double.NEGATIVE_INFINITY;
2510                minHeat = Double.POSITIVE_INFINITY;
2511                maxHeat = Double.NEGATIVE_INFINITY;
2512                minWet0 = Double.POSITIVE_INFINITY;
2513                maxWet0 = Double.NEGATIVE_INFINITY;
2514                minWet = Double.POSITIVE_INFINITY;
2515                maxWet = Double.NEGATIVE_INFINITY;
2516                cacheA = stateA;
2517                cacheB = stateB;
2518                fresh = true;
2519            }
2520            rng.setState(stateA, stateB);
2521            long seedA = rng.nextLong(), seedB = rng.nextLong(), seedC = rng.nextLong();
2522            int t;
2523
2524            landModifier = (landMod <= 0) ? rng.nextDouble(0.29) + 0.91 : landMod;
2525            heatModifier = (heatMod <= 0) ? rng.nextDouble(0.45) * (rng.nextDouble()-0.5) + 1.1 : heatMod;
2526
2527            earth.remake(earthOriginal);
2528
2529            if(zoom > 0)
2530            {
2531                int stx = Math.min(Math.max((zoomStartX - (width  >> 1)) / ((2 << zoom) - 2), 0), width ),
2532                        sty = Math.min(Math.max((zoomStartY - (height >> 1)) / ((2 << zoom) - 2), 0), height);
2533                for (int z = 0; z < zoom; z++) {
2534                    earth.zoom(stx, sty).expand8way().fray(0.5).expand();
2535                }
2536                coast.remake(earth).not().fringe(2 << zoom).expand().fray(0.5);
2537                shallow.remake(earth).fringe(2 << zoom).expand().fray(0.5);
2538            }
2539            else
2540            {
2541                coast.remake(earth).not().fringe(2);
2542                shallow.remake(earth).fringe(2);
2543            }
2544            double p,
2545                    ps, pc,
2546                    qs, qc,
2547                    h, temp, yPos, xPos,
2548                    i_uw = usedWidth / (double)width,
2549                    i_uh = usedHeight / (double)height,
2550                    th, thx, thy, lon, lat, ipi = 0.99999 / Math.PI,
2551                    rx = width * 0.25, irx = 1.0 / rx, hw = width * 0.5,
2552                    ry = height * 0.5, iry = 1.0 / ry;
2553            yPos = startY - ry;
2554            for (int y = 0; y < height; y++, yPos += i_uh) {
2555
2556                thx = NumberTools.asin((yPos) * iry);
2557                lon = (thx == Math.PI * 0.5 || thx == Math.PI * -0.5) ? thx : Math.PI * irx * 0.5 / NumberTools.cos(thx);
2558                thy = thx * 2.0;
2559                lat = NumberTools.asin((thy + NumberTools.sin(thy)) * ipi);
2560
2561                qc = NumberTools.cos(lat);
2562                qs = NumberTools.sin(lat);
2563
2564                boolean inSpace = true;
2565                xPos = startX;
2566                for (int x = 0/*, xt = 0*/; x < width; x++, xPos += i_uw) {
2567                    th = lon * (xPos - hw);
2568                    if(th < -3.141592653589793 || th > 3.141592653589793) {
2569                        heightCodeData[x][y] = 10000;
2570                        inSpace = true;
2571                        continue;
2572                    }
2573                    if(inSpace)
2574                    {
2575                        inSpace = false;
2576                        edges[y << 1] = x;
2577                    }
2578                    edges[y << 1 | 1] = x;
2579                    ps = NumberTools.sin(th) * qc;
2580                    pc = NumberTools.cos(th) * qc;
2581                    xPositions[x][y] = pc;
2582                    yPositions[x][y] = ps;
2583                    zPositions[x][y] = qs;
2584                    if(earth.contains(x, y))
2585                    {
2586                        h = NumberTools.swayTight(terrainLayered.getNoiseWithSeed(pc + terrain.getNoiseWithSeed(pc, ps, qs,seedB - seedA) * 0.5,
2587                                ps, qs, seedA)) * 0.85;
2588                        if(coast.contains(x, y))
2589                            h += 0.05;
2590                        else
2591                            h += 0.15;
2592                    }
2593                    else
2594                    {
2595                        h = NumberTools.swayTight(terrainLayered.getNoiseWithSeed(pc + terrain.getNoiseWithSeed(pc, ps, qs,seedB - seedA) * 0.5,
2596                                ps, qs, seedA)) * -0.9;
2597                        if(shallow.contains(x, y))
2598                            h = (h - 0.08) * 0.375;
2599                        else
2600                            h = (h - 0.125) * 0.75;
2601                    }
2602                    heightData[x][y] = h;
2603                    heatData[x][y] = (p = heat.getNoiseWithSeed(pc, ps
2604                                    + otherRidged.getNoiseWithSeed(pc, ps, qs,seedB + seedC)
2605                            , qs, seedB));
2606                    moistureData[x][y] = (temp = moisture.getNoiseWithSeed(pc, ps, qs
2607                                    + otherRidged.getNoiseWithSeed(pc, ps, qs, seedC + seedA)
2608                            , seedC));
2609                    minHeightActual = Math.min(minHeightActual, h);
2610                    maxHeightActual = Math.max(maxHeightActual, h);
2611                    if(fresh) {
2612                        minHeight = Math.min(minHeight, h);
2613                        maxHeight = Math.max(maxHeight, h);
2614
2615                        minHeat0 = Math.min(minHeat0, p);
2616                        maxHeat0 = Math.max(maxHeat0, p);
2617
2618                        minWet0 = Math.min(minWet0, temp);
2619                        maxWet0 = Math.max(maxWet0, temp);
2620                    }
2621                }
2622                minHeightActual = Math.min(minHeightActual, minHeight);
2623                maxHeightActual = Math.max(maxHeightActual, maxHeight);
2624
2625            }
2626            double heightDiff = 2.0 / (maxHeightActual - minHeightActual),
2627                    heatDiff = 0.8 / (maxHeat0 - minHeat0),
2628                    wetDiff = 1.0 / (maxWet0 - minWet0),
2629                    hMod,
2630                    halfHeight = (height - 1) * 0.5, i_half = 1.0 / (halfHeight);
2631            double minHeightActual0 = minHeightActual;
2632            double maxHeightActual0 = maxHeightActual;
2633            yPos = startY + i_uh;
2634            ps = Double.POSITIVE_INFINITY;
2635            pc = Double.NEGATIVE_INFINITY;
2636
2637            for (int y = 0; y < height; y++, yPos += i_uh) {
2638                temp = Math.pow(Math.abs(yPos - halfHeight) * i_half, 1.5);
2639                temp *= (2.4 - temp);
2640                temp = 2.2 - temp;
2641                for (int x = 0; x < width; x++) {
2642//                    heightData[x][y] = (h = (heightData[x][y] - minHeightActual) * heightDiff - 1.0);
2643//                    minHeightActual0 = Math.min(minHeightActual0, h);
2644//                    maxHeightActual0 = Math.max(maxHeightActual0, h);
2645                    h = heightData[x][y];
2646                    if(heightCodeData[x][y] == 10000) {
2647                        heightCodeData[x][y] = 1000;
2648                        continue;
2649                    }
2650                    else
2651                        heightCodeData[x][y] = (t = codeHeight(h));
2652                    hMod = 1.0;
2653                    switch (t) {
2654                        case 0:
2655                        case 1:
2656                        case 2:
2657                        case 3:
2658                            h = 0.4;
2659                            hMod = 0.2;
2660                            break;
2661                        case 6:
2662                            h = -0.1 * (h - forestLower - 0.08);
2663                            break;
2664                        case 7:
2665                            h *= -0.25;
2666                            break;
2667                        case 8:
2668                            h *= -0.4;
2669                            break;
2670                        default:
2671                            h *= 0.05;
2672                    }
2673                    heatData[x][y] = (h = (((heatData[x][y] - minHeat0) * heatDiff * hMod) + h + 0.6) * temp);
2674                    if (fresh) {
2675                        ps = Math.min(ps, h); //minHeat0
2676                        pc = Math.max(pc, h); //maxHeat0
2677                    }
2678                }
2679            }
2680            if(fresh)
2681            {
2682                minHeat1 = ps;
2683                maxHeat1 = pc;
2684            }
2685            heatDiff = heatModifier / (maxHeat1 - minHeat1);
2686            qs = Double.POSITIVE_INFINITY;
2687            qc = Double.NEGATIVE_INFINITY;
2688            ps = Double.POSITIVE_INFINITY;
2689            pc = Double.NEGATIVE_INFINITY;
2690
2691
2692            for (int y = 0; y < height; y++) {
2693                for (int x = 0; x < width; x++) {
2694                    heatData[x][y] = (h = ((heatData[x][y] - minHeat1) * heatDiff));
2695                    moistureData[x][y] = (temp = (moistureData[x][y] - minWet0) * wetDiff);
2696                    if (fresh) {
2697                        qs = Math.min(qs, h);
2698                        qc = Math.max(qc, h);
2699                        ps = Math.min(ps, temp);
2700                        pc = Math.max(pc, temp);
2701                    }
2702                }
2703            }
2704            if(fresh)
2705            {
2706                minHeat = qs;
2707                maxHeat = qc;
2708                minWet = ps;
2709                maxWet = pc;
2710            }
2711            landData.refill(heightCodeData, 4, 999);
2712        }
2713
2714    }
2715    /**
2716     * A concrete implementation of {@link WorldMapGenerator} that imitates an infinite-distance perspective view of a
2717     * world, showing only one hemisphere, that should be as wide as it is tall (its outline is a circle). This uses an
2718     * <a href="https://en.wikipedia.org/wiki/Orthographic_projection_in_cartography">Orthographic projection</a> with
2719     * the latitude always at the equator.
2720     * <a href="http://squidpony.github.io/SquidLib/SpaceViewMap.png" >Example map, showing circular shape as if viewed
2721     * from afar</a>
2722     */
2723    public static class SpaceViewMap extends WorldMapGenerator {
2724        //        protected static final double terrainFreq = 1.65, terrainRidgedFreq = 1.8, heatFreq = 2.1, moistureFreq = 2.125, otherFreq = 3.375, riverRidgedFreq = 21.7;
2725        protected static final double terrainFreq = 1.45, terrainRidgedFreq = 3.1, heatFreq = 2.1, moistureFreq = 2.125, otherFreq = 3.375;
2726        protected double minHeat0 = Double.POSITIVE_INFINITY, maxHeat0 = Double.NEGATIVE_INFINITY,
2727                minHeat1 = Double.POSITIVE_INFINITY, maxHeat1 = Double.NEGATIVE_INFINITY,
2728                minWet0 = Double.POSITIVE_INFINITY, maxWet0 = Double.NEGATIVE_INFINITY;
2729
2730        public final Noise3D terrain, heat, moisture, otherRidged, terrainLayered;
2731        public final double[][] xPositions,
2732                yPositions,
2733                zPositions;
2734        protected final int[] edges;
2735
2736        /**
2737         * Constructs a concrete WorldMapGenerator for a map that can be used to view a spherical world from space,
2738         * showing only one hemisphere at a time.
2739         * Always makes a 100x100 map.
2740         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
2741         * If you were using {@link SpaceViewMap#SpaceViewMap(long, int, int, Noise3D, double)}, then this would be the
2742         * same as passing the parameters {@code 0x1337BABE1337D00DL, 100, 100, DEFAULT_NOISE, 1.0}.
2743         */
2744        public SpaceViewMap() {
2745            this(0x1337BABE1337D00DL, 100, 100, DEFAULT_NOISE, 1.0);
2746        }
2747
2748        /**
2749         * Constructs a concrete WorldMapGenerator for a map that can be used to view a spherical world from space,
2750         * showing only one hemisphere at a time.
2751         * Takes only the width/height of the map. The initial seed is set to the same large long
2752         * every time, and it's likely that you would set the seed when you call {@link #generate(long)}. The width and
2753         * height of the map cannot be changed after the fact, but you can zoom in.
2754         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
2755         *
2756         * @param mapWidth  the width of the map(s) to generate; cannot be changed later
2757         * @param mapHeight the height of the map(s) to generate; cannot be changed later
2758         */
2759        public SpaceViewMap(int mapWidth, int mapHeight) {
2760            this(0x1337BABE1337D00DL, mapWidth, mapHeight,  DEFAULT_NOISE,1.0);
2761        }
2762
2763        /**
2764         * Constructs a concrete WorldMapGenerator for a map that can be used to view a spherical world from space,
2765         * showing only one hemisphere at a time.
2766         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
2767         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
2768         * The width and height of the map cannot be changed after the fact, but you can zoom in.
2769         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
2770         *
2771         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
2772         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
2773         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
2774         */
2775        public SpaceViewMap(long initialSeed, int mapWidth, int mapHeight) {
2776            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, 1.0);
2777        }
2778
2779        /**
2780         * Constructs a concrete WorldMapGenerator for a map that can be used to view a spherical world from space,
2781         * showing only one hemisphere at a time.
2782         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
2783         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
2784         * The width and height of the map cannot be changed after the fact, but you can zoom in.
2785         * Uses FastNoise as its noise generator, with the given octave multiplier affecting detail.
2786         *
2787         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
2788         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
2789         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
2790         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
2791         */
2792        public SpaceViewMap(long initialSeed, int mapWidth, int mapHeight, double octaveMultiplier) {
2793            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, octaveMultiplier);
2794        }
2795
2796        /**
2797         * Constructs a concrete WorldMapGenerator for a map that can be used to view a spherical world from space,
2798         * showing only one hemisphere at a time.
2799         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
2800         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
2801         * The width and height of the map cannot be changed after the fact, but you can zoom in.
2802         * Uses the given noise generator, with 1.0 as the octave multiplier affecting detail.
2803         *
2804         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
2805         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
2806         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
2807         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise}
2808         */
2809        public SpaceViewMap(long initialSeed, int mapWidth, int mapHeight, Noise3D noiseGenerator) {
2810            this(initialSeed, mapWidth, mapHeight, noiseGenerator, 1.0);
2811        }
2812
2813        /**
2814         * Constructs a concrete WorldMapGenerator for a map that can be used to view a spherical world from space,
2815         * showing only one hemisphere at a time.
2816         * Takes an initial seed, the width/height of the map, and parameters for noise
2817         * generation (a {@link Noise3D} implementation, which is usually {@link FastNoise#instance}, and a
2818         * multiplier on how many octaves of noise to use, with 1.0 being normal (high) detail and higher multipliers
2819         * producing even more detailed noise when zoomed-in). The {@code initialSeed} parameter may or may not be used,
2820         * since you can specify the seed to use when you call {@link #generate(long)}. The width and height of the map
2821         * cannot be changed after the fact, but you can zoom in. FastNoise will be the fastest 3D generator to use for
2822         * {@code noiseGenerator}, and the seed it's constructed with doesn't matter because this will change the
2823         * seed several times at different scales of noise (it's fine to use the static {@link FastNoise#instance}
2824         * because it has no changing state between runs of the program). The {@code octaveMultiplier} parameter should
2825         * probably be no lower than 0.5, but can be arbitrarily high if you're willing to spend much more time on
2826         * generating detail only noticeable at very high zoom; normally 1.0 is fine and may even be too high for maps
2827         * that don't require zooming.
2828         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
2829         * @param mapWidth the width of the map(s) to generate; cannot be changed later
2830         * @param mapHeight the height of the map(s) to generate; cannot be changed later
2831         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise}
2832         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
2833         */
2834        public SpaceViewMap(long initialSeed, int mapWidth, int mapHeight, Noise3D noiseGenerator, double octaveMultiplier) {
2835            super(initialSeed, mapWidth, mapHeight);
2836            xPositions = new double[width][height];
2837            yPositions = new double[width][height];
2838            zPositions = new double[width][height];
2839            edges = new int[height << 1];
2840            terrain = new Noise.Ridged3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 10), terrainFreq);
2841            terrainLayered = new Noise.InverseLayered3D(noiseGenerator, (int) (1 + octaveMultiplier * 6), terrainRidgedFreq * 0.325, 0.475);
2842//            terrain = new Noise.Ridged3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 8), terrainFreq);
2843//            terrainLayered = new Noise.Layered3D(noiseGenerator, (int) (1 + octaveMultiplier * 6), terrainRidgedFreq * 5.25, 0.475);
2844            heat = new Noise.InverseLayered3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 3), heatFreq, 0.75);
2845            moisture = new Noise.InverseLayered3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 4), moistureFreq, 0.55);
2846            otherRidged = new Noise.Ridged3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 6), otherFreq);
2847        }
2848
2849        /**
2850         * Copies the SpaceViewMap {@code other} to construct a new one that is exactly the same. References will only
2851         * be shared to Noise classes.
2852         * @param other a SpaceViewMap to copy
2853         */
2854        public SpaceViewMap(SpaceViewMap other)
2855        {
2856            super(other);
2857            terrain = other.terrain;
2858            terrainLayered = other.terrainLayered;
2859            heat = other.heat;
2860            moisture = other.moisture;
2861            otherRidged = other.otherRidged;
2862            minHeat0 = other.minHeat0;
2863            maxHeat0 = other.maxHeat0;
2864            minHeat1 = other.minHeat1;
2865            maxHeat1 = other.maxHeat1;
2866            minWet0 = other.minWet0;
2867            maxWet0 = other.maxWet0;
2868            xPositions = ArrayTools.copy(other.xPositions);
2869            yPositions = ArrayTools.copy(other.yPositions);
2870            zPositions = ArrayTools.copy(other.zPositions);
2871            edges = Arrays.copyOf(other.edges, other.edges.length);
2872        }
2873        
2874        @Override
2875        public int wrapX(int x, int y) {
2876            y = Math.max(0, Math.min(y, height - 1));
2877            return Math.max(edges[y << 1], Math.min(x, edges[y << 1 | 1]));
2878        }
2879
2880        @Override
2881        public int wrapY(final int x, final int y)  {
2882            return Math.max(0, Math.min(y, height - 1));
2883        }
2884
2885        //private static final double root2 = Math.sqrt(2.0), inverseRoot2 = 1.0 / root2, halfInverseRoot2 = 0.5 / root2;
2886
2887        protected void regenerate(int startX, int startY, int usedWidth, int usedHeight,
2888                                  double landMod, double heatMod, int stateA, int stateB)
2889        {
2890            boolean fresh = false;
2891            if(cacheA != stateA || cacheB != stateB || landMod != landModifier || heatMod != heatModifier)
2892            {
2893                minHeight = Double.POSITIVE_INFINITY;
2894                maxHeight = Double.NEGATIVE_INFINITY;
2895                minHeightActual = Double.POSITIVE_INFINITY;
2896                maxHeightActual = Double.NEGATIVE_INFINITY;
2897                minHeat0 = Double.POSITIVE_INFINITY;
2898                maxHeat0 = Double.NEGATIVE_INFINITY;
2899                minHeat1 = Double.POSITIVE_INFINITY;
2900                maxHeat1 = Double.NEGATIVE_INFINITY;
2901                minHeat = Double.POSITIVE_INFINITY;
2902                maxHeat = Double.NEGATIVE_INFINITY;
2903                minWet0 = Double.POSITIVE_INFINITY;
2904                maxWet0 = Double.NEGATIVE_INFINITY;
2905                minWet = Double.POSITIVE_INFINITY;
2906                maxWet = Double.NEGATIVE_INFINITY;
2907                cacheA = stateA;
2908                cacheB = stateB;
2909                fresh = true;
2910            }
2911            rng.setState(stateA, stateB);
2912            long seedA = rng.nextLong(), seedB = rng.nextLong(), seedC = rng.nextLong();
2913            int t;
2914
2915            landModifier = (landMod <= 0) ? rng.nextDouble(0.2) + 0.91 : landMod;
2916            heatModifier = (heatMod <= 0) ? rng.nextDouble(0.45) * (rng.nextDouble()-0.5) + 1.1 : heatMod;
2917
2918            double p,
2919                    ps, pc,
2920                    qs, qc,
2921                    h, temp, yPos, xPos, iyPos, ixPos,
2922                    i_uw = usedWidth / (double)width,
2923                    i_uh = usedHeight / (double)height,
2924                    th, lon, lat, rho,
2925                    rx = width * 0.5, irx = i_uw / rx,
2926                    ry = height * 0.5, iry = i_uh / ry;
2927
2928            yPos = startY - ry;
2929            iyPos = yPos / ry;
2930            for (int y = 0; y < height; y++, yPos += i_uh, iyPos += iry) {
2931
2932                boolean inSpace = true;
2933                xPos = startX - rx;
2934                ixPos = xPos / rx;
2935                for (int x = 0; x < width; x++, xPos += i_uw, ixPos += irx) {
2936                    rho = Math.sqrt(ixPos * ixPos + iyPos * iyPos);
2937                    if(rho > 1.0) {
2938                        heightCodeData[x][y] = 10000;
2939                        inSpace = true;
2940                        continue;
2941                    }
2942                    if(inSpace)
2943                    {
2944                        inSpace = false;
2945                        edges[y << 1] = x;
2946                    }
2947                    edges[y << 1 | 1] = x;
2948                    th = NumberTools.asin(rho); // c
2949                    lat = NumberTools.asin(iyPos);
2950                    lon = centerLongitude + NumberTools.atan2(ixPos * rho, rho * NumberTools.cos(th));
2951
2952                    qc = NumberTools.cos(lat);
2953                    qs = NumberTools.sin(lat);
2954
2955                    pc = NumberTools.cos(lon) * qc;
2956                    ps = NumberTools.sin(lon) * qc;
2957
2958                    xPositions[x][y] = pc;
2959                    yPositions[x][y] = ps;
2960                    zPositions[x][y] = qs;
2961                    heightData[x][y] = (h = terrainLayered.getNoiseWithSeed(pc +
2962                                    terrain.getNoiseWithSeed(pc, ps, qs,seedB - seedA) * 0.5,
2963                            ps, qs, seedA) + landModifier - 1.0);
2964//                    heightData[x][y] = (h = terrain4D.getNoiseWithSeed(pc, ps, qs,
2965//                            (terrainLayered.getNoiseWithSeed(pc, ps, qs, seedB - seedA)
2966//                                    + terrain.getNoiseWithSeed(pc, ps, qs, seedC - seedB)) * 0.5,
2967//                            seedA) * landModifier);
2968                    heatData[x][y] = (p = heat.getNoiseWithSeed(pc, ps
2969                                    + otherRidged.getNoiseWithSeed(pc, ps, qs,seedB + seedC)
2970                            , qs, seedB));
2971                    moistureData[x][y] = (temp = moisture.getNoiseWithSeed(pc, ps, qs
2972                                    + otherRidged.getNoiseWithSeed(pc, ps, qs, seedC + seedA)
2973                            , seedC));
2974                    minHeightActual = Math.min(minHeightActual, h);
2975                    maxHeightActual = Math.max(maxHeightActual, h);
2976                    if(fresh) {
2977                        minHeight = Math.min(minHeight, h);
2978                        maxHeight = Math.max(maxHeight, h);
2979
2980                        minHeat0 = Math.min(minHeat0, p);
2981                        maxHeat0 = Math.max(maxHeat0, p);
2982
2983                        minWet0 = Math.min(minWet0, temp);
2984                        maxWet0 = Math.max(maxWet0, temp);
2985                    }
2986                }
2987                minHeightActual = Math.min(minHeightActual, minHeight);
2988                maxHeightActual = Math.max(maxHeightActual, maxHeight);
2989
2990            }
2991            double  heatDiff = 0.8 / (maxHeat0 - minHeat0),
2992                    wetDiff = 1.0 / (maxWet0 - minWet0),
2993                    hMod,
2994                    halfHeight = (height - 1) * 0.5, i_half = 1.0 / halfHeight;
2995            yPos = startY + i_uh;
2996            ps = Double.POSITIVE_INFINITY;
2997            pc = Double.NEGATIVE_INFINITY;
2998
2999            for (int y = 0; y < height; y++, yPos += i_uh) {
3000                temp = Math.abs(yPos - halfHeight) * i_half;
3001                temp *= (2.4 - temp);
3002                temp = 2.2 - temp;
3003                for (int x = 0; x < width; x++) {
3004                    h = heightData[x][y];
3005                    if(heightCodeData[x][y] == 10000) {
3006                        heightCodeData[x][y] = 1000;
3007                        continue;
3008                    }
3009                    else
3010                        heightCodeData[x][y] = (t = codeHeight(h));
3011                    hMod = 1.0;
3012                    switch (t) {
3013                        case 0:
3014                        case 1:
3015                        case 2:
3016                        case 3:
3017                            h = 0.4;
3018                            hMod = 0.2;
3019                            break;
3020                        case 6:
3021                            h = -0.1 * (h - forestLower - 0.08);
3022                            break;
3023                        case 7:
3024                            h *= -0.25;
3025                            break;
3026                        case 8:
3027                            h *= -0.4;
3028                            break;
3029                        default:
3030                            h *= 0.05;
3031                    }
3032                    heatData[x][y] = (h = (((heatData[x][y] - minHeat0) * heatDiff * hMod) + h + 0.6) * temp);
3033                    if (fresh) {
3034                        ps = Math.min(ps, h); //minHeat0
3035                        pc = Math.max(pc, h); //maxHeat0
3036                    }
3037                }
3038            }
3039            if(fresh)
3040            {
3041                minHeat1 = ps;
3042                maxHeat1 = pc;
3043            }
3044            heatDiff = heatModifier / (maxHeat1 - minHeat1);
3045            qs = Double.POSITIVE_INFINITY;
3046            qc = Double.NEGATIVE_INFINITY;
3047            ps = Double.POSITIVE_INFINITY;
3048            pc = Double.NEGATIVE_INFINITY;
3049
3050
3051            for (int y = 0; y < height; y++) {
3052                for (int x = 0; x < width; x++) {
3053                    heatData[x][y] = (h = ((heatData[x][y] - minHeat1) * heatDiff));
3054                    moistureData[x][y] = (temp = (moistureData[x][y] - minWet0) * wetDiff);
3055                    if (fresh) {
3056                        qs = Math.min(qs, h);
3057                        qc = Math.max(qc, h);
3058                        ps = Math.min(ps, temp);
3059                        pc = Math.max(pc, temp);
3060                    }
3061                }
3062            }
3063            if(fresh)
3064            {
3065                minHeat = qs;
3066                maxHeat = qc;
3067                minWet = ps;
3068                maxWet = pc;
3069            }
3070            landData.refill(heightCodeData, 4, 999);
3071        }
3072    }
3073    /**
3074     * A concrete implementation of {@link WorldMapGenerator} that projects the world map onto a shape with a flat top
3075     * and bottom but near-circular sides. This is an equal-area projection, like EllipticalMap, so effects that fill
3076     * areas on a map like {@link PoliticalMapper} will fill (almost) equally on any part of the map. This has less
3077     * distortion on the far left and far right edges of the map than EllipticalMap, but the flat top and bottom are
3078     * probably very distorted in a small area near the poles.
3079     * This uses the <a href="https://en.wikipedia.org/wiki/Eckert_IV_projection">Eckert IV projection</a>.
3080     * <a href="https://squidpony.github.io/SquidLib/RoundSideWorldMap.png">Example map</a>
3081     */
3082    public static class RoundSideMap extends WorldMapGenerator {
3083        //        protected static final double terrainFreq = 1.35, terrainRidgedFreq = 1.8, heatFreq = 2.1, moistureFreq = 2.125, otherFreq = 3.375, riverRidgedFreq = 21.7;
3084        protected static final double terrainFreq = 1.45, terrainRidgedFreq = 3.1, heatFreq = 2.1, moistureFreq = 2.125, otherFreq = 3.375;
3085        protected double minHeat0 = Double.POSITIVE_INFINITY, maxHeat0 = Double.NEGATIVE_INFINITY,
3086                minHeat1 = Double.POSITIVE_INFINITY, maxHeat1 = Double.NEGATIVE_INFINITY,
3087                minWet0 = Double.POSITIVE_INFINITY, maxWet0 = Double.NEGATIVE_INFINITY;
3088
3089        public final Noise3D terrain, heat, moisture, otherRidged, terrainLayered;
3090        public final double[][] xPositions,
3091                yPositions,
3092                zPositions;
3093        protected final int[] edges;
3094
3095
3096        /**
3097         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3098         * ellipse without distortion of the sizes of features but with significant distortion of shape.
3099         * Always makes a 200x100 map.
3100         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
3101         * If you were using {@link RoundSideMap#RoundSideMap(long, int, int, Noise3D, double)}, then this would be the
3102         * same as passing the parameters {@code 0x1337BABE1337D00DL, 200, 100, DEFAULT_NOISE, 1.0}.
3103         */
3104        public RoundSideMap() {
3105            this(0x1337BABE1337D00DL, 200, 100, DEFAULT_NOISE, 1.0);
3106        }
3107
3108        /**
3109         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3110         * ellipse without distortion of the sizes of features but with significant distortion of shape.
3111         * Takes only the width/height of the map. The initial seed is set to the same large long
3112         * every time, and it's likely that you would set the seed when you call {@link #generate(long)}. The width and
3113         * height of the map cannot be changed after the fact, but you can zoom in.
3114         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
3115         *
3116         * @param mapWidth  the width of the map(s) to generate; cannot be changed later
3117         * @param mapHeight the height of the map(s) to generate; cannot be changed later
3118         */
3119        public RoundSideMap(int mapWidth, int mapHeight) {
3120            this(0x1337BABE1337D00DL, mapWidth, mapHeight,  DEFAULT_NOISE,1.0);
3121        }
3122
3123        /**
3124         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3125         * ellipse without distortion of the sizes of features but with significant distortion of shape.
3126         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
3127         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
3128         * The width and height of the map cannot be changed after the fact, but you can zoom in.
3129         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
3130         *
3131         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
3132         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
3133         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
3134         */
3135        public RoundSideMap(long initialSeed, int mapWidth, int mapHeight) {
3136            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, 1.0);
3137        }
3138
3139        /**
3140         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3141         * ellipse without distortion of the sizes of features but with significant distortion of shape.
3142         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
3143         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
3144         * The width and height of the map cannot be changed after the fact, but you can zoom in.
3145         * Uses FastNoise as its noise generator, with the given octave multiplier affecting detail.
3146         *
3147         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
3148         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
3149         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
3150         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
3151         */
3152        public RoundSideMap(long initialSeed, int mapWidth, int mapHeight, double octaveMultiplier) {
3153            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, octaveMultiplier);
3154        }
3155
3156        /**
3157         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3158         * ellipse without distortion of the sizes of features but with significant distortion of shape.
3159         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
3160         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
3161         * The width and height of the map cannot be changed after the fact, but you can zoom in.
3162         * Uses the given noise generator, with 1.0 as the octave multiplier affecting detail. The suggested Noise3D
3163         * implementation to use is {@link FastNoise#instance}
3164         *
3165         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
3166         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
3167         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
3168         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise}
3169         */
3170        public RoundSideMap(long initialSeed, int mapWidth, int mapHeight, Noise3D noiseGenerator) {
3171            this(initialSeed, mapWidth, mapHeight, noiseGenerator, 1.0);
3172        }
3173
3174        /**
3175         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3176         * ellipse without distortion of the sizes of features but with significant distortion of shape.
3177         * Takes an initial seed, the width/height of the map, and parameters for noise generation (a
3178         * {@link Noise3D} implementation, where {@link FastNoise#instance} is suggested, and a
3179         * multiplier on how many octaves of noise to use, with 1.0 being normal (high) detail and higher multipliers
3180         * producing even more detailed noise when zoomed-in). The {@code initialSeed} parameter may or may not be used,
3181         * since you can specify the seed to use when you call {@link #generate(long)}. The width and height of the map
3182         * cannot be changed after the fact, but you can zoom in. FastNoise will be the fastest 3D generator to use for
3183         * {@code noiseGenerator}, and the seed it's constructed with doesn't matter because this will change the
3184         * seed several times at different scales of noise (it's fine to use the static {@link FastNoise#instance}
3185         * because it has no changing state between runs of the program). The {@code octaveMultiplier} parameter should
3186         * probably be no lower than 0.5, but can be arbitrarily high if you're willing to spend much more time on
3187         * generating detail only noticeable at very high zoom; normally 1.0 is fine and may even be too high for maps
3188         * that don't require zooming.
3189         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
3190         * @param mapWidth the width of the map(s) to generate; cannot be changed later
3191         * @param mapHeight the height of the map(s) to generate; cannot be changed later
3192         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise}
3193         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
3194         */
3195        public RoundSideMap(long initialSeed, int mapWidth, int mapHeight, Noise3D noiseGenerator, double octaveMultiplier) {
3196            super(initialSeed, mapWidth, mapHeight);
3197            xPositions = new double[width][height];
3198            yPositions = new double[width][height];
3199            zPositions = new double[width][height];
3200            edges = new int[height << 1];
3201            terrain = new Noise.Ridged3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 10), terrainFreq);
3202            terrainLayered = new Noise.InverseLayered3D(noiseGenerator, (int) (1 + octaveMultiplier * 6), terrainRidgedFreq * 0.325);
3203//            terrain = new Noise.Ridged3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 8), terrainFreq);
3204//            terrainLayered = new Noise.Layered3D(noiseGenerator, (int) (1 + octaveMultiplier * 6), terrainRidgedFreq * 5.25);
3205            heat = new Noise.InverseLayered3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 3), heatFreq, 0.75);
3206            moisture = new Noise.InverseLayered3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 4), moistureFreq, 0.55);
3207            otherRidged = new Noise.Ridged3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 6), otherFreq);
3208        }
3209
3210        /**
3211         * Copies the RoundSideMap {@code other} to construct a new one that is exactly the same. References will only
3212         * be shared to Noise classes.
3213         * @param other a RoundSideMap to copy
3214         */
3215        public RoundSideMap(RoundSideMap other)
3216        {
3217            super(other);
3218            terrain = other.terrain;
3219            terrainLayered = other.terrainLayered;
3220            heat = other.heat;
3221            moisture = other.moisture;
3222            otherRidged = other.otherRidged;
3223            minHeat0 = other.minHeat0;
3224            maxHeat0 = other.maxHeat0;
3225            minHeat1 = other.minHeat1;
3226            maxHeat1 = other.maxHeat1;
3227            minWet0 = other.minWet0;
3228            maxWet0 = other.maxWet0;
3229            xPositions = ArrayTools.copy(other.xPositions);
3230            yPositions = ArrayTools.copy(other.yPositions);
3231            zPositions = ArrayTools.copy(other.zPositions);
3232            edges = Arrays.copyOf(other.edges, other.edges.length);
3233        }
3234
3235        @Override
3236        public int wrapX(final int x, int y) {
3237            y = Math.max(0, Math.min(y, height - 1));
3238            if(x < edges[y << 1])
3239                return edges[y << 1 | 1];
3240            else if(x > edges[y << 1 | 1])
3241                return edges[y << 1];
3242            else return x;
3243        }
3244
3245        @Override
3246        public int wrapY(final int x, final int y)  {
3247            return Math.max(0, Math.min(y, height - 1));
3248        }
3249
3250        protected void regenerate(int startX, int startY, int usedWidth, int usedHeight,
3251                                  double landMod, double heatMod, int stateA, int stateB)
3252        {
3253            boolean fresh = false;
3254            if(cacheA != stateA || cacheB != stateB || landMod != landModifier || heatMod != heatModifier)
3255            {
3256                minHeight = Double.POSITIVE_INFINITY;
3257                maxHeight = Double.NEGATIVE_INFINITY;
3258                minHeightActual = Double.POSITIVE_INFINITY;
3259                maxHeightActual = Double.NEGATIVE_INFINITY;
3260                minHeat0 = Double.POSITIVE_INFINITY;
3261                maxHeat0 = Double.NEGATIVE_INFINITY;
3262                minHeat1 = Double.POSITIVE_INFINITY;
3263                maxHeat1 = Double.NEGATIVE_INFINITY;
3264                minHeat = Double.POSITIVE_INFINITY;
3265                maxHeat = Double.NEGATIVE_INFINITY;
3266                minWet0 = Double.POSITIVE_INFINITY;
3267                maxWet0 = Double.NEGATIVE_INFINITY;
3268                minWet = Double.POSITIVE_INFINITY;
3269                maxWet = Double.NEGATIVE_INFINITY;
3270                cacheA = stateA;
3271                cacheB = stateB;
3272                fresh = true;
3273            }
3274            rng.setState(stateA, stateB);
3275            long seedA = rng.nextLong(), seedB = rng.nextLong(), seedC = rng.nextLong();
3276            int t;
3277
3278            landModifier = (landMod <= 0) ? rng.nextDouble(0.2) + 0.91 : landMod;
3279            heatModifier = (heatMod <= 0) ? rng.nextDouble(0.45) * (rng.nextDouble()-0.5) + 1.1 : heatMod;
3280
3281            double p,
3282                    ps, pc,
3283                    qs, qc,
3284                    h, temp, yPos, xPos,
3285                    i_uw = usedWidth / (double)width,
3286                    i_uh = usedHeight / (double)height,
3287                    th, thb, thx, thy, lon, lat,
3288                    rx = width * 0.25, irx = 1.326500428177002 / rx, hw = width * 0.5,
3289                    ry = height * 0.5, iry = 1.0 / ry;
3290
3291            yPos = startY - ry;
3292            for (int y = 0; y < height; y++, yPos += i_uh) {
3293                thy = yPos * iry;//NumberTools.sin(thb);
3294                thb = NumberTools.asin(thy);
3295                thx = NumberTools.cos(thb);
3296                //1.3265004 0.7538633073600218  1.326500428177002
3297                lon = (thx == Math.PI * 0.5 || thx == Math.PI * -0.5) ? 0x1.0p100 : irx / (0.42223820031577125 * (1.0 + thx));
3298                qs = (thb + (thx + 2.0) * thy) * 0.2800495767557787;
3299                lat = NumberTools.asin(qs);
3300
3301                qc = NumberTools.cos(lat);
3302
3303                boolean inSpace = true;
3304                xPos = startX - hw;
3305                for (int x = 0/*, xt = 0*/; x < width; x++, xPos += i_uw) {
3306                    th = lon * xPos;
3307                    if(th < -3.141592653589793 || th > 3.141592653589793) {
3308                        heightCodeData[x][y] = 10000;
3309                        inSpace = true;
3310                        continue;
3311                    }
3312                    if(inSpace)
3313                    {
3314                        inSpace = false;
3315                        edges[y << 1] = x;
3316                    }
3317                    edges[y << 1 | 1] = x;
3318                    th += centerLongitude;
3319                    ps = NumberTools.sin(th) * qc;
3320                    pc = NumberTools.cos(th) * qc;
3321                    xPositions[x][y] = pc;
3322                    yPositions[x][y] = ps;
3323                    zPositions[x][y] = qs;
3324                    heightData[x][y] = (h = terrainLayered.getNoiseWithSeed(pc +
3325                                    terrain.getNoiseWithSeed(pc, ps, qs,seedB - seedA) * 0.5,
3326                            ps, qs, seedA) + landModifier - 1.0);
3327                    heatData[x][y] = (p = heat.getNoiseWithSeed(pc, ps
3328                                    + otherRidged.getNoiseWithSeed(pc, ps, qs,seedB + seedC)
3329                            , qs, seedB));
3330                    moistureData[x][y] = (temp = moisture.getNoiseWithSeed(pc, ps, qs
3331                                    + otherRidged.getNoiseWithSeed(pc, ps, qs, seedC + seedA)
3332                            , seedC));
3333                    minHeightActual = Math.min(minHeightActual, h);
3334                    maxHeightActual = Math.max(maxHeightActual, h);
3335                    if(fresh) {
3336                        minHeight = Math.min(minHeight, h);
3337                        maxHeight = Math.max(maxHeight, h);
3338
3339                        minHeat0 = Math.min(minHeat0, p);
3340                        maxHeat0 = Math.max(maxHeat0, p);
3341
3342                        minWet0 = Math.min(minWet0, temp);
3343                        maxWet0 = Math.max(maxWet0, temp);
3344                    }
3345                }
3346                minHeightActual = Math.min(minHeightActual, minHeight);
3347                maxHeightActual = Math.max(maxHeightActual, maxHeight);
3348
3349            }
3350            double  heatDiff = 0.8 / (maxHeat0 - minHeat0),
3351                    wetDiff = 1.0 / (maxWet0 - minWet0),
3352                    hMod,
3353                    halfHeight = (height - 1) * 0.5, i_half = 1.0 / halfHeight;
3354            yPos = startY + i_uh;
3355            ps = Double.POSITIVE_INFINITY;
3356            pc = Double.NEGATIVE_INFINITY;
3357
3358            for (int y = 0; y < height; y++, yPos += i_uh) {
3359                temp = Math.abs(yPos - halfHeight) * i_half;
3360                temp *= (2.4 - temp);
3361                temp = 2.2 - temp;
3362                for (int x = 0; x < width; x++) {
3363                    h = heightData[x][y];
3364                    if(heightCodeData[x][y] == 10000) {
3365                        heightCodeData[x][y] = 1000;
3366                        continue;
3367                    }
3368                    else
3369                        heightCodeData[x][y] = (t = codeHeight(h));
3370                    hMod = 1.0;
3371                    switch (t) {
3372                        case 0:
3373                        case 1:
3374                        case 2:
3375                        case 3:
3376                            h = 0.4;
3377                            hMod = 0.2;
3378                            break;
3379                        case 6:
3380                            h = -0.1 * (h - forestLower - 0.08);
3381                            break;
3382                        case 7:
3383                            h *= -0.25;
3384                            break;
3385                        case 8:
3386                            h *= -0.4;
3387                            break;
3388                        default:
3389                            h *= 0.05;
3390                    }
3391                    heatData[x][y] = (h = (((heatData[x][y] - minHeat0) * heatDiff * hMod) + h + 0.6) * temp);
3392                    if (fresh) {
3393                        ps = Math.min(ps, h); //minHeat0
3394                        pc = Math.max(pc, h); //maxHeat0
3395                    }
3396                }
3397            }
3398            if(fresh)
3399            {
3400                minHeat1 = ps;
3401                maxHeat1 = pc;
3402            }
3403            heatDiff = heatModifier / (maxHeat1 - minHeat1);
3404            qs = Double.POSITIVE_INFINITY;
3405            qc = Double.NEGATIVE_INFINITY;
3406            ps = Double.POSITIVE_INFINITY;
3407            pc = Double.NEGATIVE_INFINITY;
3408
3409
3410            for (int y = 0; y < height; y++) {
3411                for (int x = 0; x < width; x++) {
3412                    heatData[x][y] = (h = ((heatData[x][y] - minHeat1) * heatDiff));
3413                    moistureData[x][y] = (temp = (moistureData[x][y] - minWet0) * wetDiff);
3414                    if (fresh) {
3415                        qs = Math.min(qs, h);
3416                        qc = Math.max(qc, h);
3417                        ps = Math.min(ps, temp);
3418                        pc = Math.max(pc, temp);
3419                    }
3420                }
3421            }
3422            if(fresh)
3423            {
3424                minHeat = qs;
3425                maxHeat = qc;
3426                minWet = ps;
3427                maxWet = pc;
3428            }
3429            landData.refill(heightCodeData, 4, 999);
3430        }
3431    }
3432    /**
3433     * A concrete implementation of {@link WorldMapGenerator} that projects the world map onto a shape that resembles a
3434     * mix part-way between an ellipse and a rectangle. This is an equal-area projection, like EllipticalMap, so effects that fill
3435     * areas on a map like {@link PoliticalMapper} will fill (almost) equally on any part of the map. This has less
3436     * distortion around all the edges than the other maps here, especially when comparing the North and South poles
3437     * with RoundSideMap.
3438     * This uses the <a href="https://en.wikipedia.org/wiki/Tobler_hyperelliptical_projection">Tobler hyperelliptical projection</a>.
3439     * <a href="">Example map</a>
3440     */
3441    public static class HyperellipticalMap extends WorldMapGenerator {
3442        protected static final double terrainFreq = 1.45, terrainRidgedFreq = 3.1, heatFreq = 2.1, moistureFreq = 2.125, otherFreq = 3.375;
3443        protected double minHeat0 = Double.POSITIVE_INFINITY, maxHeat0 = Double.NEGATIVE_INFINITY,
3444                minHeat1 = Double.POSITIVE_INFINITY, maxHeat1 = Double.NEGATIVE_INFINITY,
3445                minWet0 = Double.POSITIVE_INFINITY, maxWet0 = Double.NEGATIVE_INFINITY;
3446
3447        public final Noise3D terrain, heat, moisture, otherRidged, terrainLayered;
3448        public final double[][] xPositions,
3449                yPositions,
3450                zPositions;
3451        protected final int[] edges;
3452        private final double alpha, kappa, epsilon;
3453        private final double[] Z;
3454
3455
3456        /**
3457         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3458         * ellipse without distortion of the sizes of features but with significant distortion of shape.
3459         * Always makes a 200x100 map.
3460         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
3461         * If you were using {@link HyperellipticalMap#HyperellipticalMap(long, int, int, Noise3D, double)}, then this would be the
3462         * same as passing the parameters {@code 0x1337BABE1337D00DL, 200, 100, DEFAULT_NOISE, 1.0}.
3463         * <a href="http://squidpony.github.io/SquidLib/HyperellipseWorld.png" >Example map, showing special shape</a>
3464         */
3465        public HyperellipticalMap() {
3466            this(0x1337BABE1337D00DL, 200, 100, DEFAULT_NOISE, 1.0);
3467        }
3468
3469        /**
3470         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3471         * ellipse without distortion of the sizes of features but with significant distortion of shape.
3472         * Takes only the width/height of the map. The initial seed is set to the same large long
3473         * every time, and it's likely that you would set the seed when you call {@link #generate(long)}. The width and
3474         * height of the map cannot be changed after the fact, but you can zoom in.
3475         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
3476         *
3477         * @param mapWidth  the width of the map(s) to generate; cannot be changed later
3478         * @param mapHeight the height of the map(s) to generate; cannot be changed later
3479         */
3480        public HyperellipticalMap(int mapWidth, int mapHeight) {
3481            this(0x1337BABE1337D00DL, mapWidth, mapHeight,  DEFAULT_NOISE,1.0);
3482        }
3483
3484        /**
3485         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3486         * ellipse without distortion of the sizes of features but with significant distortion of shape.
3487         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
3488         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
3489         * The width and height of the map cannot be changed after the fact, but you can zoom in.
3490         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
3491         *
3492         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
3493         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
3494         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
3495         */
3496        public HyperellipticalMap(long initialSeed, int mapWidth, int mapHeight) {
3497            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, 1.0);
3498        }
3499
3500        /**
3501         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3502         * ellipse without distortion of the sizes of features but with significant distortion of shape.
3503         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
3504         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
3505         * The width and height of the map cannot be changed after the fact, but you can zoom in.
3506         * Uses FastNoise as its noise generator, with the given octave multiplier affecting detail.
3507         *
3508         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
3509         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
3510         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
3511         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
3512         */
3513        public HyperellipticalMap(long initialSeed, int mapWidth, int mapHeight, double octaveMultiplier) {
3514            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, octaveMultiplier);
3515        }
3516
3517        /**
3518         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3519         * ellipse without distortion of the sizes of features but with significant distortion of shape.
3520         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
3521         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
3522         * The width and height of the map cannot be changed after the fact, but you can zoom in.
3523         * Uses the given noise generator, with 1.0 as the octave multiplier affecting detail. The suggested Noise3D
3524         * implementation to use is {@link FastNoise#instance}.
3525         *
3526         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
3527         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
3528         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
3529         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise}
3530         */
3531        public HyperellipticalMap(long initialSeed, int mapWidth, int mapHeight, Noise3D noiseGenerator) {
3532            this(initialSeed, mapWidth, mapHeight, noiseGenerator, 1.0);
3533        }
3534
3535        /**
3536         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3537         * ellipse without distortion of the sizes of features but with significant distortion of shape.
3538         * Takes an initial seed, the width/height of the map, and parameters for noise generation (a
3539         * {@link Noise3D} implementation, where {@link FastNoise#instance} is suggested, and a
3540         * multiplier on how many octaves of noise to use, with 1.0 being normal (high) detail and higher multipliers
3541         * producing even more detailed noise when zoomed-in). The {@code initialSeed} parameter may or may not be used,
3542         * since you can specify the seed to use when you call {@link #generate(long)}. The width and height of the map
3543         * cannot be changed after the fact, but you can zoom in. FastNoise will be the fastest 3D generator to use for
3544         * {@code noiseGenerator}, and the seed it's constructed with doesn't matter because this will change the
3545         * seed several times at different scales of noise (it's fine to use the static {@link FastNoise#instance}
3546         * because it has no changing state between runs of the program). The {@code octaveMultiplier} parameter should
3547         * probably be no lower than 0.5, but can be arbitrarily high if you're willing to spend much more time on
3548         * generating detail only noticeable at very high zoom; normally 1.0 is fine and may even be too high for maps
3549         * that don't require zooming.
3550         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
3551         * @param mapWidth the width of the map(s) to generate; cannot be changed later
3552         * @param mapHeight the height of the map(s) to generate; cannot be changed later
3553         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise}
3554         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
3555         */
3556        public HyperellipticalMap(long initialSeed, int mapWidth, int mapHeight, Noise3D noiseGenerator, double octaveMultiplier){
3557            this(initialSeed, mapWidth, mapHeight, noiseGenerator, octaveMultiplier, 0.0625, 2.5);
3558        }
3559
3560        /**
3561         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3562         * ellipse without distortion of the sizes of features but with significant distortion of shape.
3563         * Takes an initial seed, the width/height of the map, and parameters for noise generation (a
3564         * {@link Noise3D} implementation, where {@link FastNoise#instance} is suggested, and a
3565         * multiplier on how many octaves of noise to use, with 1.0 being normal (high) detail and higher multipliers
3566         * producing even more detailed noise when zoomed-in). The {@code initialSeed} parameter may or may not be used,
3567         * since you can specify the seed to use when you call {@link #generate(long)}. The width and height of the map
3568         * cannot be changed after the fact, but you can zoom in. FastNoise will be the fastest 3D generator to use for
3569         * {@code noiseGenerator}, and the seed it's constructed with doesn't matter because this will change the
3570         * seed several times at different scales of noise (it's fine to use the static {@link FastNoise#instance}
3571         * because it has no changing state between runs of the program). The {@code octaveMultiplier} parameter should
3572         * probably be no lower than 0.5, but can be arbitrarily high if you're willing to spend much more time on
3573         * generating detail only noticeable at very high zoom; normally 1.0 is fine and may even be too high for maps
3574         * that don't require zooming.
3575         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
3576         * @param mapWidth the width of the map(s) to generate; cannot be changed later
3577         * @param mapHeight the height of the map(s) to generate; cannot be changed later
3578         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise}
3579         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
3580         * @param alpha one of the Tobler parameters;  0.0625 is the default and this can range from 0.0 to 1.0 at least
3581         * @param kappa one of the Tobler parameters; 2.5 is the default but 2.0-5.0 range values are also often used
3582         */
3583        public HyperellipticalMap(long initialSeed, int mapWidth, int mapHeight, Noise3D noiseGenerator,
3584                                  double octaveMultiplier, double alpha, double kappa){
3585            super(initialSeed, mapWidth, mapHeight);
3586            xPositions = new double[width][height];
3587            yPositions = new double[width][height];
3588            zPositions = new double[width][height];
3589            edges = new int[height << 1];
3590            terrain = new Noise.Ridged3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 10), terrainFreq);
3591            terrainLayered = new Noise.InverseLayered3D(noiseGenerator, (int) (1 + octaveMultiplier * 6), terrainRidgedFreq * 0.325);
3592            heat = new Noise.InverseLayered3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 3), heatFreq, 0.75);
3593            moisture = new Noise.InverseLayered3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 4), moistureFreq, 0.55);
3594            otherRidged = new Noise.Ridged3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 6), otherFreq);
3595            this.alpha = alpha;
3596            this.kappa = kappa;
3597            this.Z = new double[height << 2];
3598            this.epsilon = ProjectionTools.simpsonIntegrateHyperellipse(0.0, 1.0, 0.25 / height, kappa);
3599            ProjectionTools.simpsonODESolveHyperellipse(1, this.Z, 0.25 / height, alpha, kappa, epsilon);
3600        }
3601        /**
3602         * Copies the HyperellipticalMap {@code other} to construct a new one that is exactly the same. References will only
3603         * be shared to Noise classes.
3604         * @param other a HyperellipticalMap to copy
3605         */
3606        public HyperellipticalMap(HyperellipticalMap other)
3607        {
3608            super(other);
3609            terrain = other.terrain;
3610            terrainLayered = other.terrainLayered;
3611            heat = other.heat;
3612            moisture = other.moisture;
3613            otherRidged = other.otherRidged;
3614            minHeat0 = other.minHeat0;
3615            maxHeat0 = other.maxHeat0;
3616            minHeat1 = other.minHeat1;
3617            maxHeat1 = other.maxHeat1;
3618            minWet0 = other.minWet0;
3619            maxWet0 = other.maxWet0;
3620            xPositions = ArrayTools.copy(other.xPositions);
3621            yPositions = ArrayTools.copy(other.yPositions);
3622            zPositions = ArrayTools.copy(other.zPositions);
3623            edges = Arrays.copyOf(other.edges, other.edges.length);
3624            alpha = other.alpha;
3625            kappa = other.kappa;
3626            epsilon = other.epsilon;
3627            Z = Arrays.copyOf(other.Z, other.Z.length);
3628        }
3629
3630
3631        @Override
3632        public int wrapX(final int x, int y) {
3633            y = Math.max(0, Math.min(y, height - 1));
3634            if(x < edges[y << 1])
3635                return edges[y << 1 | 1];
3636            else if(x > edges[y << 1 | 1])
3637                return edges[y << 1];
3638            else return x;
3639        }
3640
3641        @Override
3642        public int wrapY(final int x, final int y)  {
3643            return Math.max(0, Math.min(y, height - 1));
3644        }
3645
3646        /**
3647         * Given a latitude and longitude in radians (the conventional way of describing points on a globe), this gets the
3648         * (x,y) Coord on the map projection this generator uses that corresponds to the given lat-lon coordinates. If this
3649         * generator does not represent a globe (if it is toroidal, for instance) or if there is no "good way" to calculate
3650         * the projection for a given lat-lon coordinate, this returns null. This implementation never returns null.
3651         * If this is a supported operation and the parameters are valid, this returns a Coord with x between 0 and
3652         * {@link #width}, and y between 0 and {@link #height}, both exclusive. Automatically wraps the Coord's values using
3653         * {@link #wrapX(int, int)} and {@link #wrapY(int, int)}.
3654         *
3655         * @param latitude  the latitude, from {@code Math.PI * -0.5} to {@code Math.PI * 0.5}
3656         * @param longitude the longitude, from {@code 0.0} to {@code Math.PI * 2.0}
3657         * @return the point at the given latitude and longitude, as a Coord with x between 0 and {@link #width} and y between 0 and {@link #height}, or null if unsupported
3658         */
3659        @Override
3660        public Coord project(double latitude, double longitude) {
3661            final double z0 = Math.abs(NumberTools.sin(latitude));
3662            final int i = Arrays.binarySearch(Z, z0);
3663            final double y;
3664            if (i >= 0)
3665                y = i/(Z.length-1.);
3666            else if (-i-1 >= Z.length)
3667                y = Z[Z.length-1];
3668            else
3669                y = ((z0-Z[-i-2])/(Z[-i-1]-Z[-i-2]) + (-i-2))/(Z.length-1.);
3670            final int xx = (int)(((longitude - getCenterLongitude() + 12.566370614359172) % 6.283185307179586) * Math.abs(alpha + (1-alpha)*Math.pow(1 - Math.pow(Math.abs(y),kappa), 1/kappa)) + 0.5);
3671            final int yy = (int)(y * Math.signum(latitude) * height * 0.5 + 0.5);
3672            return Coord.get(wrapX(xx, yy), wrapY(xx, yy));
3673        }
3674
3675        protected void regenerate(int startX, int startY, int usedWidth, int usedHeight,
3676                                  double landMod, double heatMod, int stateA, int stateB)
3677        {
3678            boolean fresh = false;
3679            if(cacheA != stateA || cacheB != stateB || landMod != landModifier || heatMod != heatModifier)
3680            {
3681                minHeight = Double.POSITIVE_INFINITY;
3682                maxHeight = Double.NEGATIVE_INFINITY;
3683                minHeightActual = Double.POSITIVE_INFINITY;
3684                maxHeightActual = Double.NEGATIVE_INFINITY;
3685                minHeat0 = Double.POSITIVE_INFINITY;
3686                maxHeat0 = Double.NEGATIVE_INFINITY;
3687                minHeat1 = Double.POSITIVE_INFINITY;
3688                maxHeat1 = Double.NEGATIVE_INFINITY;
3689                minHeat = Double.POSITIVE_INFINITY;
3690                maxHeat = Double.NEGATIVE_INFINITY;
3691                minWet0 = Double.POSITIVE_INFINITY;
3692                maxWet0 = Double.NEGATIVE_INFINITY;
3693                minWet = Double.POSITIVE_INFINITY;
3694                maxWet = Double.NEGATIVE_INFINITY;
3695                cacheA = stateA;
3696                cacheB = stateB;
3697                fresh = true;
3698            }
3699            rng.setState(stateA, stateB);
3700            long seedA = rng.nextLong(), seedB = rng.nextLong(), seedC = rng.nextLong();
3701            int t;
3702
3703            landModifier = (landMod <= 0) ? rng.nextDouble(0.2) + 0.91 : landMod;
3704            heatModifier = (heatMod <= 0) ? rng.nextDouble(0.45) * (rng.nextDouble()-0.5) + 1.1 : heatMod;
3705
3706            double p,
3707                    ps, pc,
3708                    qs, qc,
3709                    h, temp, yPos, xPos,
3710                    i_uw = usedWidth / (double)width,
3711                    i_uh = usedHeight / (double)height,
3712                    th, lon,
3713                    rx = width * 0.5, irx = Math.PI / rx, hw = width * 0.5,
3714                    ry = height * 0.5, iry = 1.0 / ry;
3715
3716            yPos = startY - ry;
3717            for (int y = 0; y < height; y++, yPos += i_uh) {
3718//                thy = yPos * iry;//NumberTools.sin(thb);
3719//                thb = asin(thy);
3720//                thx = NumberTools.cos(thb);
3721//                //1.3265004 0.7538633073600218  1.326500428177002
3722//                lon = (thx == Math.PI * 0.5 || thx == Math.PI * -0.5) ? 0x1.0p100 : irx / (0.42223820031577125 * (1.0 + thx));
3723//                qs = (thb + (thx + 2.0) * thy) * 0.2800495767557787;
3724//                lat = asin(qs);
3725//
3726//                qc = NumberTools.cos(lat);
3727
3728                lon = NumberTools.asin(Z[(int)(0.5 + Math.abs(yPos*iry)*(Z.length-1))])*Math.signum(yPos);
3729                qs = NumberTools.sin(lon);
3730                qc = NumberTools.cos(lon);
3731
3732                boolean inSpace = true;
3733                xPos = startX - hw;
3734                for (int x = 0/*, xt = 0*/; x < width; x++, xPos += i_uw) {
3735                    //th = lon * xPos;
3736                    th = xPos * irx / Math.abs(alpha + (1-alpha)*ProjectionTools.hyperellipse(yPos * iry, kappa));
3737                    if(th < -3.141592653589793 || th > 3.141592653589793) {
3738                        //if(th < -2.0 || th > 2.0) {
3739                        heightCodeData[x][y] = 10000;
3740                        inSpace = true;
3741                        continue;
3742                    }
3743                    if(inSpace)
3744                    {
3745                        inSpace = false;
3746                        edges[y << 1] = x;
3747                    }
3748                    edges[y << 1 | 1] = x;
3749                    th += centerLongitude;
3750                    ps = NumberTools.sin(th) * qc;
3751                    pc = NumberTools.cos(th) * qc;
3752                    xPositions[x][y] = pc;
3753                    yPositions[x][y] = ps;
3754                    zPositions[x][y] = qs;
3755                    heightData[x][y] = (h = terrainLayered.getNoiseWithSeed(pc +
3756                                    terrain.getNoiseWithSeed(pc, ps, qs, seedB - seedA) * 0.5,
3757                            ps, qs, seedA) + landModifier - 1.0);
3758                    heatData[x][y] = (p = heat.getNoiseWithSeed(pc, ps
3759                                    + otherRidged.getNoiseWithSeed(pc, ps, qs, seedB + seedC)
3760                            , qs, seedB));
3761                    moistureData[x][y] = (temp = moisture.getNoiseWithSeed(pc, ps, qs
3762                                    + otherRidged.getNoiseWithSeed(pc, ps, qs, seedC + seedA)
3763                            , seedC));
3764                    minHeightActual = Math.min(minHeightActual, h);
3765                    maxHeightActual = Math.max(maxHeightActual, h);
3766                    if(fresh) {
3767                        minHeight = Math.min(minHeight, h);
3768                        maxHeight = Math.max(maxHeight, h);
3769
3770                        minHeat0 = Math.min(minHeat0, p);
3771                        maxHeat0 = Math.max(maxHeat0, p);
3772
3773                        minWet0 = Math.min(minWet0, temp);
3774                        maxWet0 = Math.max(maxWet0, temp);
3775                    }
3776                }
3777                minHeightActual = Math.min(minHeightActual, minHeight);
3778                maxHeightActual = Math.max(maxHeightActual, maxHeight);
3779
3780            }
3781            double  heatDiff = 0.8 / (maxHeat0 - minHeat0),
3782                    wetDiff = 1.0 / (maxWet0 - minWet0),
3783                    hMod,
3784                    halfHeight = (height - 1) * 0.5, i_half = 1.0 / halfHeight;
3785            yPos = startY + i_uh;
3786            ps = Double.POSITIVE_INFINITY;
3787            pc = Double.NEGATIVE_INFINITY;
3788
3789            for (int y = 0; y < height; y++, yPos += i_uh) {
3790                temp = Math.abs(yPos - halfHeight) * i_half;
3791                temp *= (2.4 - temp);
3792                temp = 2.2 - temp;
3793                for (int x = 0; x < width; x++) {
3794                    h = heightData[x][y];
3795                    if(heightCodeData[x][y] == 10000) {
3796                        heightCodeData[x][y] = 1000;
3797                        continue;
3798                    }
3799                    else
3800                        heightCodeData[x][y] = (t = codeHeight(h));
3801                    hMod = 1.0;
3802                    switch (t) {
3803                        case 0:
3804                        case 1:
3805                        case 2:
3806                        case 3:
3807                            h = 0.4;
3808                            hMod = 0.2;
3809                            break;
3810                        case 6:
3811                            h = -0.1 * (h - forestLower - 0.08);
3812                            break;
3813                        case 7:
3814                            h *= -0.25;
3815                            break;
3816                        case 8:
3817                            h *= -0.4;
3818                            break;
3819                        default:
3820                            h *= 0.05;
3821                    }
3822                    heatData[x][y] = (h = (((heatData[x][y] - minHeat0) * heatDiff * hMod) + h + 0.6) * temp);
3823                    if (fresh) {
3824                        ps = Math.min(ps, h); //minHeat0
3825                        pc = Math.max(pc, h); //maxHeat0
3826                    }
3827                }
3828            }
3829            if(fresh)
3830            {
3831                minHeat1 = ps;
3832                maxHeat1 = pc;
3833            }
3834            heatDiff = heatModifier / (maxHeat1 - minHeat1);
3835            qs = Double.POSITIVE_INFINITY;
3836            qc = Double.NEGATIVE_INFINITY;
3837            ps = Double.POSITIVE_INFINITY;
3838            pc = Double.NEGATIVE_INFINITY;
3839
3840
3841            for (int y = 0; y < height; y++) {
3842                for (int x = 0; x < width; x++) {
3843                    heatData[x][y] = (h = ((heatData[x][y] - minHeat1) * heatDiff));
3844                    moistureData[x][y] = (temp = (moistureData[x][y] - minWet0) * wetDiff);
3845                    if (fresh) {
3846                        qs = Math.min(qs, h);
3847                        qc = Math.max(qc, h);
3848                        ps = Math.min(ps, temp);
3849                        pc = Math.max(pc, temp);
3850                    }
3851                }
3852            }
3853            if(fresh)
3854            {
3855                minHeat = qs;
3856                maxHeat = qc;
3857                minWet = ps;
3858                maxWet = pc;
3859            }
3860            landData.refill(heightCodeData, 4, 999);
3861        }
3862    }
3863
3864    /**
3865     * A concrete implementation of {@link WorldMapGenerator} that projects the world map onto an ellipse that should be
3866     * twice as wide as it is tall (although you can stretch it by width and height that don't have that ratio).
3867     * This uses the <a href="https://en.wikipedia.org/wiki/Hammer_projection">Hammer projection</a>, so the latitude
3868     * lines are curved instead of flat. The Mollweide projection that {@link EllipticalMap} uses has flat lines, but
3869     * the two projection are otherwise very similar, and are both equal-area (Hammer tends to have less significant
3870     * distortion around the edges, but the curvature of the latitude lines can be hard to visualize).
3871     * <a href="https://i.imgur.com/nmN6lMK.gifv">Preview image link of a world rotating</a>.
3872     */
3873    public static class EllipticalHammerMap extends WorldMapGenerator {
3874        //        protected static final double terrainFreq = 1.35, terrainRidgedFreq = 1.8, heatFreq = 2.1, moistureFreq = 2.125, otherFreq = 3.375, riverRidgedFreq = 21.7;
3875        protected static final double terrainFreq = 1.45, terrainRidgedFreq = 3.1, heatFreq = 2.1, moistureFreq = 2.125, otherFreq = 3.375;
3876        protected double minHeat0 = Double.POSITIVE_INFINITY, maxHeat0 = Double.NEGATIVE_INFINITY,
3877                minHeat1 = Double.POSITIVE_INFINITY, maxHeat1 = Double.NEGATIVE_INFINITY,
3878                minWet0 = Double.POSITIVE_INFINITY, maxWet0 = Double.NEGATIVE_INFINITY;
3879
3880        public final Noise3D terrain, heat, moisture, otherRidged, terrainLayered;
3881        public final double[][] xPositions,
3882                yPositions,
3883                zPositions;
3884        protected final int[] edges;
3885
3886
3887        /**
3888         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3889         * ellipse without distortion of the sizes of features but with significant distortion of shape. This is very
3890         * similar to {@link EllipticalMap}, but has curved latitude lines instead of flat ones (it also may see more
3891         * internal usage because some operations on this projection are much faster and simpler).
3892         * Always makes a 200x100 map.
3893         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
3894         * If you were using {@link EllipticalHammerMap#EllipticalHammerMap(long, int, int, Noise3D, double)}, then this would be the
3895         * same as passing the parameters {@code 0x1337BABE1337D00DL, 200, 100, DEFAULT_NOISE, 1.0}.
3896         */
3897        public EllipticalHammerMap() {
3898            this(0x1337BABE1337D00DL, 200, 100, DEFAULT_NOISE, 1.0);
3899        }
3900
3901        /**
3902         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3903         * ellipse without distortion of the sizes of features but with significant distortion of shape. This is very
3904         * similar to {@link EllipticalMap}, but has curved latitude lines instead of flat ones (it also may see more
3905         * internal usage because some operations on this projection are much faster and simpler).
3906         * Takes only the width/height of the map. The initial seed is set to the same large long
3907         * every time, and it's likely that you would set the seed when you call {@link #generate(long)}. The width and
3908         * height of the map cannot be changed after the fact, but you can zoom in.
3909         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
3910         *
3911         * @param mapWidth  the width of the map(s) to generate; cannot be changed later
3912         * @param mapHeight the height of the map(s) to generate; cannot be changed later
3913         */
3914        public EllipticalHammerMap(int mapWidth, int mapHeight) {
3915            this(0x1337BABE1337D00DL, mapWidth, mapHeight,  DEFAULT_NOISE,1.0);
3916        }
3917
3918        /**
3919         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3920         * ellipse without distortion of the sizes of features but with significant distortion of shape. This is very
3921         * similar to {@link EllipticalMap}, but has curved latitude lines instead of flat ones (it also may see more
3922         * internal usage because some operations on this projection are much faster and simpler).
3923         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
3924         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
3925         * The width and height of the map cannot be changed after the fact, but you can zoom in.
3926         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
3927         *
3928         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
3929         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
3930         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
3931         */
3932        public EllipticalHammerMap(long initialSeed, int mapWidth, int mapHeight) {
3933            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, 1.0);
3934        }
3935
3936        /**
3937         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3938         * ellipse without distortion of the sizes of features but with significant distortion of shape. This is very
3939         * similar to {@link EllipticalMap}, but has curved latitude lines instead of flat ones (it also may see more
3940         * internal usage because some operations on this projection are much faster and simpler).
3941         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
3942         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
3943         * The width and height of the map cannot be changed after the fact, but you can zoom in.
3944         * Uses FastNoise as its noise generator, with the given octave multiplier affecting detail.
3945         *
3946         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
3947         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
3948         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
3949         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
3950         */
3951        public EllipticalHammerMap(long initialSeed, int mapWidth, int mapHeight, double octaveMultiplier) {
3952            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, octaveMultiplier);
3953        }
3954
3955        /**
3956         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3957         * ellipse without distortion of the sizes of features but with significant distortion of shape. This is very
3958         * similar to {@link EllipticalMap}, but has curved latitude lines instead of flat ones (it also may see more
3959         * internal usage because some operations on this projection are much faster and simpler).
3960         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
3961         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
3962         * The width and height of the map cannot be changed after the fact, but you can zoom in.
3963         * Uses the given noise generator, with 1.0 as the octave multiplier affecting detail. The suggested Noise3D
3964         * implementation to use is {@link FastNoise#instance}.
3965         *
3966         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
3967         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
3968         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
3969         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise}
3970         */
3971        public EllipticalHammerMap(long initialSeed, int mapWidth, int mapHeight, Noise3D noiseGenerator) {
3972            this(initialSeed, mapWidth, mapHeight, noiseGenerator, 1.0);
3973        }
3974
3975        /**
3976         * Constructs a concrete WorldMapGenerator for a map that can be used to display a projection of a globe onto an
3977         * ellipse without distortion of the sizes of features but with significant distortion of shape. This is very
3978         * similar to {@link EllipticalMap}, but has curved latitude lines instead of flat ones (it also may see more
3979         * internal usage because some operations on this projection are much faster and simpler).
3980         * Takes an initial seed, the width/height of the map, and parameters for noise generation (a
3981         * {@link Noise3D} implementation, where {@link FastNoise#instance} is suggested, and a
3982         * multiplier on how many octaves of noise to use, with 1.0 being normal (high) detail and higher multipliers
3983         * producing even more detailed noise when zoomed-in). The {@code initialSeed} parameter may or may not be used,
3984         * since you can specify the seed to use when you call {@link #generate(long)}. The width and height of the map
3985         * cannot be changed after the fact, but you can zoom in. FastNoise will be the fastest 3D generator to use for
3986         * {@code noiseGenerator}, and the seed it's constructed with doesn't matter because this will change the
3987         * seed several times at different scales of noise (it's fine to use the static {@link FastNoise#instance}
3988         * because it has no changing state between runs of the program). The {@code octaveMultiplier} parameter should
3989         * probably be no lower than 0.5, but can be arbitrarily high if you're willing to spend much more time on
3990         * generating detail only noticeable at very high zoom; normally 1.0 is fine and may even be too high for maps
3991         * that don't require zooming.
3992         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
3993         * @param mapWidth the width of the map(s) to generate; cannot be changed later
3994         * @param mapHeight the height of the map(s) to generate; cannot be changed later
3995         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise}
3996         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
3997         */
3998        public EllipticalHammerMap(long initialSeed, int mapWidth, int mapHeight, Noise3D noiseGenerator, double octaveMultiplier) {
3999            super(initialSeed, mapWidth, mapHeight);
4000            xPositions = new double[width][height];
4001            yPositions = new double[width][height];
4002            zPositions = new double[width][height];
4003            edges = new int[height << 1];
4004            terrain = new Noise.Ridged3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 10), terrainFreq);
4005            terrainLayered = new Noise.InverseLayered3D(noiseGenerator, (int) (1 + octaveMultiplier * 6), terrainRidgedFreq * 0.325);
4006//            terrain = new Noise.Ridged3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 8), terrainFreq);
4007//            terrainLayered = new Noise.Layered3D(noiseGenerator, (int) (1 + octaveMultiplier * 6), terrainRidgedFreq * 5.25);
4008            heat = new Noise.InverseLayered3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 3), heatFreq, 0.75);
4009            moisture = new Noise.InverseLayered3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 4), moistureFreq, 0.55);
4010            otherRidged = new Noise.Ridged3D(noiseGenerator, (int) (0.5 + octaveMultiplier * 6), otherFreq);
4011        }
4012
4013        /**
4014         * Copies the EllipticalHammerMap {@code other} to construct a new one that is exactly the same. References will only
4015         * be shared to Noise classes.
4016         * @param other an EllipticalHammerMap to copy
4017         */
4018        public EllipticalHammerMap(EllipticalHammerMap other)
4019        {
4020            super(other);
4021            terrain = other.terrain;
4022            terrainLayered = other.terrainLayered;
4023            heat = other.heat;
4024            moisture = other.moisture;
4025            otherRidged = other.otherRidged;
4026            minHeat0 = other.minHeat0;
4027            maxHeat0 = other.maxHeat0;
4028            minHeat1 = other.minHeat1;
4029            maxHeat1 = other.maxHeat1;
4030            minWet0 = other.minWet0;
4031            maxWet0 = other.maxWet0;
4032            xPositions = ArrayTools.copy(other.xPositions);
4033            yPositions = ArrayTools.copy(other.yPositions);
4034            zPositions = ArrayTools.copy(other.zPositions);
4035            edges = Arrays.copyOf(other.edges, other.edges.length);
4036        }
4037
4038        @Override
4039        public int wrapX(final int x, int y) {
4040            y = Math.max(0, Math.min(y, height - 1));
4041            if(x < edges[y << 1])
4042                return edges[y << 1 | 1];
4043            else if(x > edges[y << 1 | 1])
4044                return edges[y << 1];
4045            else return x;
4046        }
4047
4048        @Override
4049        public int wrapY(final int x, final int y)  {
4050            return Math.max(0, Math.min(y, height - 1));
4051        }
4052
4053        protected void regenerate(int startX, int startY, int usedWidth, int usedHeight,
4054                                  double landMod, double heatMod, int stateA, int stateB)
4055        {
4056            boolean fresh = false;
4057            if(cacheA != stateA || cacheB != stateB || landMod != landModifier || heatMod != heatModifier)
4058            {
4059                minHeight = Double.POSITIVE_INFINITY;
4060                maxHeight = Double.NEGATIVE_INFINITY;
4061                minHeightActual = Double.POSITIVE_INFINITY;
4062                maxHeightActual = Double.NEGATIVE_INFINITY;
4063                minHeat0 = Double.POSITIVE_INFINITY;
4064                maxHeat0 = Double.NEGATIVE_INFINITY;
4065                minHeat1 = Double.POSITIVE_INFINITY;
4066                maxHeat1 = Double.NEGATIVE_INFINITY;
4067                minHeat = Double.POSITIVE_INFINITY;
4068                maxHeat = Double.NEGATIVE_INFINITY;
4069                minWet0 = Double.POSITIVE_INFINITY;
4070                maxWet0 = Double.NEGATIVE_INFINITY;
4071                minWet = Double.POSITIVE_INFINITY;
4072                maxWet = Double.NEGATIVE_INFINITY;
4073                cacheA = stateA;
4074                cacheB = stateB;
4075                fresh = true;
4076            }
4077            rng.setState(stateA, stateB);
4078            long seedA = rng.nextLong(), seedB = rng.nextLong(), seedC = rng.nextLong();
4079            int t;
4080
4081            landModifier = (landMod <= 0) ? rng.nextDouble(0.2) + 0.91 : landMod;
4082            heatModifier = (heatMod <= 0) ? rng.nextDouble(0.45) * (rng.nextDouble()-0.5) + 1.1 : heatMod;
4083
4084            double p,
4085                    ps, pc,
4086                    qs, qc,
4087                    h, temp, yPos, xPos,
4088                    z, th, lon, lat,
4089                    rx = width * 0.5, hw = width * 0.5, root2 = Math.sqrt(2.0),
4090                    irx = 1.0 / rx, iry = 2.0 / (double) height,
4091                    xAdj, yAdj,
4092                    i_uw = usedWidth / (double)(width),
4093                    i_uh = usedHeight / (double)(height);
4094
4095            yPos = (startY - height * 0.5);
4096            for (int y = 0; y < height; y++, yPos += i_uh) {
4097                boolean inSpace = true;
4098                yAdj = yPos * iry;
4099                xPos = (startX - hw);
4100                for (int x = 0; x < width; x++, xPos += i_uw) {
4101                    xAdj = xPos * irx;
4102                    z = Math.sqrt(1.0 - 0.5 * xAdj * xAdj - 0.5 * yAdj * yAdj);
4103                    th = z * yAdj * root2;
4104                    lon = 2.0 * NumberTools.atan2((2.0 * z * z - 1.0), (z * xAdj * root2));
4105                    if(th != th || lon < 0.0) {
4106                        heightCodeData[x][y] = 10000;
4107                        inSpace = true;
4108                        continue;
4109                    }
4110                    lat = NumberTools.asin(th);
4111                    qc = NumberTools.cos(lat);
4112                    qs = th;
4113                    th = Math.PI - lon + centerLongitude;
4114                    if(inSpace)
4115                    {
4116                        inSpace = false;
4117                        edges[y << 1] = x;
4118                    }
4119                    edges[y << 1 | 1] = x;
4120                    ps = NumberTools.sin(th) * qc;
4121                    pc = NumberTools.cos(th) * qc;
4122                    xPositions[x][y] = pc;
4123                    yPositions[x][y] = ps;
4124                    zPositions[x][y] = qs;
4125                    heightData[x][y] = (h = terrainLayered.getNoiseWithSeed(pc +
4126                                    terrain.getNoiseWithSeed(pc, ps, qs,seedB - seedA) * 0.5,
4127                            ps, qs, seedA) + landModifier - 1.0);
4128                    heatData[x][y] = (p = heat.getNoiseWithSeed(pc, ps
4129                                    + otherRidged.getNoiseWithSeed(pc, ps, qs,seedB + seedC)
4130                            , qs, seedB));
4131                    moistureData[x][y] = (temp = moisture.getNoiseWithSeed(pc, ps, qs
4132                                    + otherRidged.getNoiseWithSeed(pc, ps, qs, seedC + seedA)
4133                            , seedC));
4134                    minHeightActual = Math.min(minHeightActual, h);
4135                    maxHeightActual = Math.max(maxHeightActual, h);
4136                    if(fresh) {
4137                        minHeight = Math.min(minHeight, h);
4138                        maxHeight = Math.max(maxHeight, h);
4139
4140                        minHeat0 = Math.min(minHeat0, p);
4141                        maxHeat0 = Math.max(maxHeat0, p);
4142
4143                        minWet0 = Math.min(minWet0, temp);
4144                        maxWet0 = Math.max(maxWet0, temp);
4145                    }
4146                }
4147                minHeightActual = Math.min(minHeightActual, minHeight);
4148                maxHeightActual = Math.max(maxHeightActual, maxHeight);
4149
4150            }
4151            double  heatDiff = 0.8 / (maxHeat0 - minHeat0),
4152                    wetDiff = 1.0 / (maxWet0 - minWet0),
4153                    hMod,
4154                    halfHeight = (height - 1) * 0.5, i_half = 1.0 / halfHeight;
4155            yPos = startY + i_uh;
4156            ps = Double.POSITIVE_INFINITY;
4157            pc = Double.NEGATIVE_INFINITY;
4158
4159            for (int y = 0; y < height; y++, yPos += i_uh) {
4160                temp = Math.abs(yPos - halfHeight) * i_half;
4161                temp *= (2.4 - temp);
4162                temp = 2.2 - temp;
4163                for (int x = 0; x < width; x++) {
4164                    h = heightData[x][y];
4165                    if(heightCodeData[x][y] == 10000) {
4166                        heightCodeData[x][y] = 1000;
4167                        continue;
4168                    }
4169                    else
4170                        heightCodeData[x][y] = (t = codeHeight(h));
4171                    hMod = 1.0;
4172                    switch (t) {
4173                        case 0:
4174                        case 1:
4175                        case 2:
4176                        case 3:
4177                            h = 0.4;
4178                            hMod = 0.2;
4179                            break;
4180                        case 6:
4181                            h = -0.1 * (h - forestLower - 0.08);
4182                            break;
4183                        case 7:
4184                            h *= -0.25;
4185                            break;
4186                        case 8:
4187                            h *= -0.4;
4188                            break;
4189                        default:
4190                            h *= 0.05;
4191                    }
4192                    heatData[x][y] = (h = (((heatData[x][y] - minHeat0) * heatDiff * hMod) + h + 0.6) * temp);
4193                    if (fresh) {
4194                        ps = Math.min(ps, h); //minHeat0
4195                        pc = Math.max(pc, h); //maxHeat0
4196                    }
4197                }
4198            }
4199            if(fresh)
4200            {
4201                minHeat1 = ps;
4202                maxHeat1 = pc;
4203            }
4204            heatDiff = heatModifier / (maxHeat1 - minHeat1);
4205            qs = Double.POSITIVE_INFINITY;
4206            qc = Double.NEGATIVE_INFINITY;
4207            ps = Double.POSITIVE_INFINITY;
4208            pc = Double.NEGATIVE_INFINITY;
4209
4210
4211            for (int y = 0; y < height; y++) {
4212                for (int x = 0; x < width; x++) {
4213                    heatData[x][y] = (h = ((heatData[x][y] - minHeat1) * heatDiff));
4214                    moistureData[x][y] = (temp = (moistureData[x][y] - minWet0) * wetDiff);
4215                    if (fresh) {
4216                        qs = Math.min(qs, h);
4217                        qc = Math.max(qc, h);
4218                        ps = Math.min(ps, temp);
4219                        pc = Math.max(pc, temp);
4220                    }
4221                }
4222            }
4223            if(fresh)
4224            {
4225                minHeat = qs;
4226                maxHeat = qc;
4227                minWet = ps;
4228                maxWet = pc;
4229            }
4230            landData.refill(heightCodeData, 4, 999);
4231        }
4232    }
4233
4234
4235
4236    /**
4237     * A concrete implementation of {@link WorldMapGenerator} that imitates an infinite-distance perspective view of a
4238     * world, showing only one hemisphere, that should be as wide as it is tall (its outline is a circle). It should
4239     * look as a world would when viewed from space, and implements rotation differently to allow the planet to be
4240     * rotated without recalculating all the data, though it cannot zoom. Note that calling
4241     * {@link #setCenterLongitude(double)} does a lot more work than in other classes, but less than fully calling
4242     * {@link #generate()} in those classes, since it doesn't remake the map data at a slightly different rotation and
4243     * instead keeps a single map in use the whole time, using sections of it. This uses an
4244     * <a href="https://en.wikipedia.org/wiki/Orthographic_projection_in_cartography">Orthographic projection</a> with
4245     * the latitude always at the equator; the internal map is stored as a {@link SphereMap}, which uses a
4246     * <a href="https://en.wikipedia.org/wiki/Cylindrical_equal-area_projection#Discussion">cylindrical equal-area
4247     * projection</a>, specifically the Smyth equal-surface projection.
4248     * <br>
4249     * <a href="https://i.imgur.com/WNa5nQ1.gifv">Example view of a planet rotating</a>.
4250     * <a href="https://i.imgur.com/NV5IMd6.gifv">Another example</a>.
4251     */
4252    public static class RotatingSpaceMap extends WorldMapGenerator {
4253        protected double minHeat0 = Double.POSITIVE_INFINITY, maxHeat0 = Double.NEGATIVE_INFINITY,
4254                minHeat1 = Double.POSITIVE_INFINITY, maxHeat1 = Double.NEGATIVE_INFINITY,
4255                minWet0 = Double.POSITIVE_INFINITY, maxWet0 = Double.NEGATIVE_INFINITY;
4256
4257        public final double[][] xPositions,
4258                yPositions,
4259                zPositions;
4260        protected final int[] edges;
4261        public final SphereMap storedMap;
4262        /**
4263         * Constructs a concrete WorldMapGenerator for a map that can be used to view a spherical world from space,
4264         * showing only one hemisphere at a time.
4265         * Always makes a 100x100 map.
4266         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
4267         * If you were using {@link RotatingSpaceMap#RotatingSpaceMap(long, int, int, Noise3D, double)}, then this would be the
4268         * same as passing the parameters {@code 0x1337BABE1337D00DL, 100, 100, DEFAULT_NOISE, 1.0}.
4269         */
4270        public RotatingSpaceMap() {
4271            this(0x1337BABE1337D00DL, 100, 100, DEFAULT_NOISE, 1.0);
4272        }
4273
4274        /**
4275         * Constructs a concrete WorldMapGenerator for a map that can be used to view a spherical world from space,
4276         * showing only one hemisphere at a time.
4277         * Takes only the width/height of the map. The initial seed is set to the same large long
4278         * every time, and it's likely that you would set the seed when you call {@link #generate(long)}. The width and
4279         * height of the map cannot be changed after the fact, but you can zoom in.
4280         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
4281         *
4282         * @param mapWidth  the width of the map(s) to generate; cannot be changed later
4283         * @param mapHeight the height of the map(s) to generate; cannot be changed later
4284         */
4285        public RotatingSpaceMap(int mapWidth, int mapHeight) {
4286            this(0x1337BABE1337D00DL, mapWidth, mapHeight, DEFAULT_NOISE, 1.0);
4287        }
4288
4289        /**
4290         * Constructs a concrete WorldMapGenerator for a map that can be used to view a spherical world from space,
4291         * showing only one hemisphere at a time.
4292         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
4293         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
4294         * The width and height of the map cannot be changed after the fact, but you can zoom in.
4295         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
4296         *
4297         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
4298         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
4299         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
4300         */
4301        public RotatingSpaceMap(long initialSeed, int mapWidth, int mapHeight) {
4302            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, 1.0);
4303        }
4304
4305        /**
4306         * Constructs a concrete WorldMapGenerator for a map that can be used to view a spherical world from space,
4307         * showing only one hemisphere at a time.
4308         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
4309         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
4310         * The width and height of the map cannot be changed after the fact, but you can zoom in.
4311         * Uses FastNoise as its noise generator, with the given octave multiplier affecting detail.
4312         *
4313         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
4314         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
4315         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
4316         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
4317         */
4318        public RotatingSpaceMap(long initialSeed, int mapWidth, int mapHeight, double octaveMultiplier) {
4319            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, octaveMultiplier);
4320        }
4321
4322        /**
4323         * Constructs a concrete WorldMapGenerator for a map that can be used to view a spherical world from space,
4324         * showing only one hemisphere at a time.
4325         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
4326         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
4327         * The width and height of the map cannot be changed after the fact, but you can zoom in.
4328         * Uses the given noise generator, with 1.0 as the octave multiplier affecting detail.
4329         *
4330         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
4331         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
4332         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
4333         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise}
4334         */
4335        public RotatingSpaceMap(long initialSeed, int mapWidth, int mapHeight, Noise3D noiseGenerator) {
4336            this(initialSeed, mapWidth, mapHeight, noiseGenerator, 1.0);
4337        }
4338
4339        /**
4340         * Constructs a concrete WorldMapGenerator for a map that can be used to view a spherical world from space,
4341         * showing only one hemisphere at a time.
4342         * Takes an initial seed, the width/height of the map, and parameters for noise
4343         * generation (a {@link Noise3D} implementation, which is usually {@link FastNoise#instance}, and a
4344         * multiplier on how many octaves of noise to use, with 1.0 being normal (high) detail and higher multipliers
4345         * producing even more detailed noise when zoomed-in). The {@code initialSeed} parameter may or may not be used,
4346         * since you can specify the seed to use when you call {@link #generate(long)}. The width and height of the map
4347         * cannot be changed after the fact, but you can zoom in. FastNoise will be the fastest 3D generator to use for
4348         * {@code noiseGenerator}, and the seed it's constructed with doesn't matter because this will change the
4349         * seed several times at different scales of noise (it's fine to use the static {@link FastNoise#instance}
4350         * because it has no changing state between runs of the program). The {@code octaveMultiplier} parameter should
4351         * probably be no lower than 0.5, but can be arbitrarily high if you're willing to spend much more time on
4352         * generating detail only noticeable at very high zoom; normally 1.0 is fine and may even be too high for maps
4353         * that don't require zooming.
4354         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
4355         * @param mapWidth the width of the map(s) to generate; cannot be changed later
4356         * @param mapHeight the height of the map(s) to generate; cannot be changed later
4357         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise}
4358         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
4359         */
4360        public RotatingSpaceMap(long initialSeed, int mapWidth, int mapHeight, Noise3D noiseGenerator, double octaveMultiplier) {
4361            super(initialSeed, mapWidth, mapHeight);
4362            xPositions = new double[mapWidth][mapHeight];
4363            yPositions = new double[mapWidth][mapHeight];
4364            zPositions = new double[mapWidth][mapHeight];
4365            edges = new int[height << 1];
4366            storedMap = new SphereMap(initialSeed, mapWidth << 1, mapHeight, noiseGenerator, octaveMultiplier);
4367        }
4368
4369        /**
4370         * Copies the RotatingSpaceMap {@code other} to construct a new one that is exactly the same. References will only
4371         * be shared to Noise classes.
4372         * @param other a RotatingSpaceMap to copy
4373         */
4374        public RotatingSpaceMap(RotatingSpaceMap other)
4375        {
4376            super(other);
4377            minHeat0 = other.minHeat0;
4378            maxHeat0 = other.maxHeat0;
4379            minHeat1 = other.minHeat1;
4380            maxHeat1 = other.maxHeat1;
4381            minWet0 = other.minWet0;
4382            maxWet0 = other.maxWet0;
4383            xPositions = ArrayTools.copy(other.xPositions);
4384            yPositions = ArrayTools.copy(other.yPositions);
4385            zPositions = ArrayTools.copy(other.zPositions);
4386            edges = Arrays.copyOf(other.edges, other.edges.length);
4387            storedMap = new SphereMap(other.storedMap);
4388        }
4389
4390
4391        @Override
4392        public int wrapX(int x, int y) {
4393            y = Math.max(0, Math.min(y, height - 1));
4394            return Math.max(edges[y << 1], Math.min(x, edges[y << 1 | 1]));
4395        }
4396
4397        @Override
4398        public int wrapY(final int x, final int y)  {
4399            return Math.max(0, Math.min(y, height - 1));
4400        }
4401
4402        @Override
4403        public void setCenterLongitude(double centerLongitude) {
4404            super.setCenterLongitude(centerLongitude);
4405            int ax, ay;
4406            double
4407                    ps, pc,
4408                    qs, qc,
4409                    h, yPos, xPos, iyPos, ixPos,
4410                    i_uw = usedWidth / (double)width,
4411                    i_uh = usedHeight / (double)height,
4412                    th, lon, lat, rho,
4413                    i_pi = 1.0 / Math.PI,
4414                    rx = width * 0.5, irx = i_uw / rx,
4415                    ry = height * 0.5, iry = i_uh / ry;
4416
4417            yPos = startY - ry;
4418            iyPos = yPos / ry;
4419            for (int y = 0; y < height; y++, yPos += i_uh, iyPos += iry) {
4420                boolean inSpace = true;
4421                xPos = startX - rx;
4422                ixPos = xPos / rx;
4423                lat = NumberTools.asin(iyPos);
4424                for (int x = 0; x < width; x++, xPos += i_uw, ixPos += irx) {
4425                    rho = (ixPos * ixPos + iyPos * iyPos);
4426                    if(rho > 1.0) {
4427                        heightCodeData[x][y] = 1000;
4428                        inSpace = true;
4429                        continue;
4430                    }
4431                    rho = Math.sqrt(rho);
4432                    if(inSpace)
4433                    {
4434                        inSpace = false;
4435                        edges[y << 1] = x;
4436                    }
4437                    edges[y << 1 | 1] = x;
4438                    th = NumberTools.asin(rho); // c
4439                    lon = removeExcess((centerLongitude + (NumberTools.atan2(ixPos * rho, rho * NumberTools.cos(th)))) * 0.5);
4440
4441                    qs = lat * 0.6366197723675814;
4442                    qc = qs + 1.0;
4443                    int sf = (qs >= 0.0 ? (int) qs : (int) qs - 1) & -2;
4444                    int cf = (qc >= 0.0 ? (int) qc : (int) qc - 1) & -2;
4445                    qs -= sf;
4446                    qc -= cf;
4447                    qs *= 2.0 - qs;
4448                    qc *= 2.0 - qc;
4449                    qs = qs * (-0.775 - 0.225 * qs) * ((sf & 2) - 1);
4450                    qc = qc * (-0.775 - 0.225 * qc) * ((cf & 2) - 1);
4451
4452
4453                    ps = lon * 0.6366197723675814;
4454                    pc = ps + 1.0;
4455                    sf = (ps >= 0.0 ? (int) ps : (int) ps - 1) & -2;
4456                    cf = (pc >= 0.0 ? (int) pc : (int) pc - 1) & -2;
4457                    ps -= sf;
4458                    pc -= cf;
4459                    ps *= 2.0 - ps;
4460                    pc *= 2.0 - pc;
4461                    ps = ps * (-0.775 - 0.225 * ps) * ((sf & 2) - 1);
4462                    pc = pc * (-0.775 - 0.225 * pc) * ((cf & 2) - 1);
4463
4464                    ax = (int)((lon * i_pi + 1.0) * width);
4465                    ay = (int)((qs + 1.0) * ry);
4466                    
4467//                    // Hammer projection, not an inverse projection like we usually use
4468//                    z = 1.0 / Math.sqrt(1 + qc * NumberTools.cos(lon * 0.5));
4469//                    ax = (int)((qc * NumberTools.sin(lon * 0.5) * z + 1.0) * width);
4470//                    ay = (int)((qs * z + 1.0) * height * 0.5);
4471
4472                    if(ax >= storedMap.width || ax < 0 || ay >= storedMap.height || ay < 0)
4473                    {
4474                        heightCodeData[x][y] = 1000;
4475                        continue;
4476                    }
4477                    if(storedMap.heightCodeData[ax][ay] >= 1000) // for the seam we get when looping around
4478                    {
4479                        ay = storedMap.wrapY(ax, ay);
4480                        ax = storedMap.wrapX(ax, ay);
4481                    }
4482
4483                    xPositions[x][y] = pc * qc;
4484                    yPositions[x][y] = ps * qc;
4485                    zPositions[x][y] = qs;
4486
4487                    heightData[x][y] = h = storedMap.heightData[ax][ay];
4488                    heightCodeData[x][y] = codeHeight(h);
4489                    heatData[x][y] = storedMap.heatData[ax][ay];
4490                    moistureData[x][y] = storedMap.moistureData[ax][ay];
4491
4492                    minHeightActual = Math.min(minHeightActual, h);
4493                    maxHeightActual = Math.max(maxHeightActual, h);
4494                }
4495                minHeightActual = Math.min(minHeightActual, minHeight);
4496                maxHeightActual = Math.max(maxHeightActual, maxHeight);
4497            }
4498
4499        }
4500
4501        protected void regenerate(int startX, int startY, int usedWidth, int usedHeight,
4502                                  double landMod, double heatMod, int stateA, int stateB)
4503        {
4504            if(cacheA != stateA || cacheB != stateB)// || landMod != storedMap.landModifier || coolMod != storedMap.coolingModifier)
4505            {
4506                storedMap.regenerate(0, 0, width << 1, height, landMod, heatMod, stateA, stateB);
4507                minHeightActual = Double.POSITIVE_INFINITY;
4508                maxHeightActual = Double.NEGATIVE_INFINITY;
4509
4510                minHeight = storedMap.minHeight;
4511                maxHeight = storedMap.maxHeight;
4512
4513                minHeat0 = storedMap.minHeat0;
4514                maxHeat0 = storedMap.maxHeat0;
4515
4516                minHeat1 = storedMap.minHeat1;
4517                maxHeat1 = storedMap.maxHeat1;
4518
4519                minWet0 = storedMap.minWet0;
4520                maxWet0 = storedMap.maxWet0;
4521
4522                minHeat = storedMap.minHeat;
4523                maxHeat = storedMap.maxHeat;
4524
4525                minWet = storedMap.minWet;
4526                maxWet = storedMap.maxWet;
4527
4528                cacheA = stateA;
4529                cacheB = stateB;
4530            }
4531            setCenterLongitude(centerLongitude);
4532            landData.refill(heightCodeData, 4, 999);
4533        }
4534    }
4535    /**
4536     * A concrete implementation of {@link WorldMapGenerator} that does no projection of the map, as if the area were
4537     * completely flat or small enough that curvature is impossible to see. This also does not change heat levels at the
4538     * far north and south regions of the map, since it is meant for areas that are all about the same heat level.
4539     * <a href="http://squidpony.github.io/SquidLib/LocalMap.png" >Example map, showing lack of polar ice</a>
4540     */
4541    public static class LocalMap extends WorldMapGenerator {
4542        protected static final double terrainFreq = 1.45, terrainRidgedFreq = 3.1, heatFreq = 2.1, moistureFreq = 2.125, otherFreq = 3.375;
4543        //protected static final double terrainFreq = 1.65, terrainRidgedFreq = 1.8, heatFreq = 2.1, moistureFreq = 2.125, otherFreq = 3.375, riverRidgedFreq = 21.7;
4544        protected double minHeat0 = Double.POSITIVE_INFINITY, maxHeat0 = Double.NEGATIVE_INFINITY,
4545                minHeat1 = Double.POSITIVE_INFINITY, maxHeat1 = Double.NEGATIVE_INFINITY,
4546                minWet0 = Double.POSITIVE_INFINITY, maxWet0 = Double.NEGATIVE_INFINITY;
4547
4548        public final Noise.Ridged2D terrain, otherRidged;
4549        public final Noise.InverseLayered2D heat, moisture, terrainLayered;
4550        public final double[][] xPositions,
4551                yPositions,
4552                zPositions;
4553
4554
4555        /**
4556         * Constructs a concrete WorldMapGenerator for a map that can be used to wrap a sphere (as with a texture on a
4557         * 3D model), with seamless east-west wrapping, no north-south wrapping, and distortion that causes the poles to
4558         * have significantly-exaggerated-in-size features while the equator is not distorted.
4559         * Always makes a 256x128 map.
4560         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
4561         * If you were using {@link LocalMap#LocalMap(long, int, int, Noise2D, double)}, then this would be the
4562         * same as passing the parameters {@code 0x1337BABE1337D00DL, 256, 128, DEFAULT_NOISE, 1.0}.
4563         */
4564        public LocalMap() {
4565            this(0x1337BABE1337D00DL, 256, 128, DEFAULT_NOISE, 1.0);
4566        }
4567
4568        /**
4569         * Constructs a concrete WorldMapGenerator for a map that can be used to wrap a sphere (as with a texture on a
4570         * 3D model), with seamless east-west wrapping, no north-south wrapping, and distortion that causes the poles to
4571         * have significantly-exaggerated-in-size features while the equator is not distorted.
4572         * Takes only the width/height of the map. The initial seed is set to the same large long
4573         * every time, and it's likely that you would set the seed when you call {@link #generate(long)}. The width and
4574         * height of the map cannot be changed after the fact, but you can zoom in.
4575         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
4576         *
4577         * @param mapWidth  the width of the map(s) to generate; cannot be changed later
4578         * @param mapHeight the height of the map(s) to generate; cannot be changed later
4579         */
4580        public LocalMap(int mapWidth, int mapHeight) {
4581            this(0x1337BABE1337D00DL, mapWidth, mapHeight,  DEFAULT_NOISE,1.0);
4582        }
4583
4584        /**
4585         * Constructs a concrete WorldMapGenerator for a map that can be used to wrap a sphere (as with a texture on a
4586         * 3D model), with seamless east-west wrapping, no north-south wrapping, and distortion that causes the poles to
4587         * have significantly-exaggerated-in-size features while the equator is not distorted.
4588         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
4589         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
4590         * The width and height of the map cannot be changed after the fact, but you can zoom in.
4591         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
4592         *
4593         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
4594         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
4595         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
4596         */
4597        public LocalMap(long initialSeed, int mapWidth, int mapHeight) {
4598            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, 1.0);
4599        }
4600
4601        /**
4602         * Constructs a concrete WorldMapGenerator for a map that can be used to wrap a sphere (as with a texture on a
4603         * 3D model), with seamless east-west wrapping, no north-south wrapping, and distortion that causes the poles to
4604         * have significantly-exaggerated-in-size features while the equator is not distorted.
4605         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
4606         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
4607         * The width and height of the map cannot be changed after the fact, but you can zoom in.
4608         * Uses FastNoise as its noise generator, with the given octave multiplier affecting detail.
4609         *
4610         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
4611         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
4612         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
4613         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
4614         */
4615        public LocalMap(long initialSeed, int mapWidth, int mapHeight, double octaveMultiplier) {
4616            this(initialSeed, mapWidth, mapHeight, DEFAULT_NOISE, octaveMultiplier);
4617        }
4618
4619        /**
4620         * Constructs a concrete WorldMapGenerator for a map that can be used to wrap a sphere (as with a texture on a
4621         * 3D model), with seamless east-west wrapping, no north-south wrapping, and distortion that causes the poles to
4622         * have significantly-exaggerated-in-size features while the equator is not distorted.
4623         * Takes an initial seed and the width/height of the map. The {@code initialSeed}
4624         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
4625         * The width and height of the map cannot be changed after the fact, but you can zoom in.
4626         * Uses the given noise generator, with 1.0 as the octave multiplier affecting detail.
4627         *
4628         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
4629         * @param mapWidth    the width of the map(s) to generate; cannot be changed later
4630         * @param mapHeight   the height of the map(s) to generate; cannot be changed later
4631         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise}
4632         */
4633        public LocalMap(long initialSeed, int mapWidth, int mapHeight, Noise2D noiseGenerator) {
4634            this(initialSeed, mapWidth, mapHeight, noiseGenerator, 1.0);
4635        }
4636
4637        /**
4638         * Constructs a concrete WorldMapGenerator for a map that can be used to wrap a sphere (as with a texture on a
4639         * 3D model), with seamless east-west wrapping, no north-south wrapping, and distortion that causes the poles to
4640         * have significantly-exaggerated-in-size features while the equator is not distorted.
4641         * Takes an initial seed, the width/height of the map, and parameters for noise
4642         * generation (a {@link Noise3D} implementation, which is usually {@link FastNoise#instance}, and a
4643         * multiplier on how many octaves of noise to use, with 1.0 being normal (high) detail and higher multipliers
4644         * producing even more detailed noise when zoomed-in). The {@code initialSeed} parameter may or may not be used,
4645         * since you can specify the seed to use when you call {@link #generate(long)}. The width and height of the map
4646         * cannot be changed after the fact, but you can zoom in. FastNoise will be the fastest 3D generator to use for
4647         * {@code noiseGenerator}, and the seed it's constructed with doesn't matter because this will change the
4648         * seed several times at different scales of noise (it's fine to use the static {@link FastNoise#instance}
4649         * because it has no changing state between runs of the program). The {@code octaveMultiplier} parameter should
4650         * probably be no lower than 0.5, but can be arbitrarily high if you're willing to spend much more time on
4651         * generating detail only noticeable at very high zoom; normally 1.0 is fine and may even be too high for maps
4652         * that don't require zooming.
4653         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
4654         * @param mapWidth the width of the map(s) to generate; cannot be changed later
4655         * @param mapHeight the height of the map(s) to generate; cannot be changed later
4656         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise#instance}
4657         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
4658         */
4659        public LocalMap(long initialSeed, int mapWidth, int mapHeight, Noise2D noiseGenerator, double octaveMultiplier) {
4660            super(initialSeed, mapWidth, mapHeight);
4661            xPositions = new double[width][height];
4662            yPositions = new double[width][height];
4663            zPositions = new double[width][height];
4664
4665            terrain = new Noise.Ridged2D(noiseGenerator, (int) (0.5 + octaveMultiplier * 10), terrainFreq);
4666            terrainLayered = new Noise.InverseLayered2D(noiseGenerator, (int) (1 + octaveMultiplier * 6), terrainRidgedFreq * 0.325);
4667            heat = new Noise.InverseLayered2D(noiseGenerator, (int) (0.5 + octaveMultiplier * 3), heatFreq, 0.75);
4668            moisture = new Noise.InverseLayered2D(noiseGenerator, (int) (0.5 + octaveMultiplier * 4), moistureFreq, 0.55);
4669            otherRidged = new Noise.Ridged2D(noiseGenerator, (int) (0.5 + octaveMultiplier * 6), otherFreq);
4670        }
4671
4672        /**
4673         * Copies the LocalMap {@code other} to construct a new one that is exactly the same. References will only
4674         * be shared to Noise classes.
4675         * @param other a LocalMap to copy
4676         */
4677        public LocalMap(LocalMap other)
4678        {
4679            super(other);
4680            terrain = other.terrain;
4681            terrainLayered = other.terrainLayered;
4682            heat = other.heat;
4683            moisture = other.moisture;
4684            otherRidged = other.otherRidged;
4685            minHeat0 = other.minHeat0;
4686            maxHeat0 = other.maxHeat0;
4687            minHeat1 = other.minHeat1;
4688            maxHeat1 = other.maxHeat1;
4689            minWet0 = other.minWet0;
4690            maxWet0 = other.maxWet0;
4691            xPositions = ArrayTools.copy(other.xPositions);
4692            yPositions = ArrayTools.copy(other.yPositions);
4693            zPositions = ArrayTools.copy(other.zPositions);
4694        }
4695
4696        @Override
4697        public int wrapX(final int x, final int y)  {
4698            return Math.max(0, Math.min(x, width - 1));
4699        }
4700
4701        @Override
4702        public int wrapY(final int x, final int y)  {
4703            return Math.max(0, Math.min(y, height - 1));
4704        }
4705        
4706        protected void regenerate(int startX, int startY, int usedWidth, int usedHeight,
4707                                  double landMod, double heatMod, int stateA, int stateB)
4708        {
4709            boolean fresh = false;
4710            if(cacheA != stateA || cacheB != stateB || landMod != landModifier || heatMod != heatModifier)
4711            {
4712                minHeight = Double.POSITIVE_INFINITY;
4713                maxHeight = Double.NEGATIVE_INFINITY;
4714                minHeat0 = Double.POSITIVE_INFINITY;
4715                maxHeat0 = Double.NEGATIVE_INFINITY;
4716                minHeat1 = Double.POSITIVE_INFINITY;
4717                maxHeat1 = Double.NEGATIVE_INFINITY;
4718                minHeat = Double.POSITIVE_INFINITY;
4719                maxHeat = Double.NEGATIVE_INFINITY;
4720                minWet0 = Double.POSITIVE_INFINITY;
4721                maxWet0 = Double.NEGATIVE_INFINITY;
4722                minWet = Double.POSITIVE_INFINITY;
4723                maxWet = Double.NEGATIVE_INFINITY;
4724                cacheA = stateA;
4725                cacheB = stateB;
4726                fresh = true;
4727            }
4728            rng.setState(stateA, stateB);
4729            long seedA = rng.nextLong(), seedB = rng.nextLong(), seedC = rng.nextLong();
4730            int t;
4731
4732            landModifier = (landMod <= 0) ? rng.nextDouble(0.29) + 0.91 : landMod;
4733            heatModifier = (heatMod <= 0) ? rng.nextDouble(0.45) * (rng.nextDouble()-0.5) + 1.1 : heatMod;
4734
4735            double p,
4736                    ps, pc,
4737                    qs, qc,
4738                    h, temp,
4739                    i_w = 1.0 / width, i_h = 1.0 / (height),  ii = Math.max(i_w, i_h),
4740                    i_uw = usedWidth * i_w * ii, i_uh = usedHeight * i_h * ii, xPos, yPos = startY * i_h;
4741            for (int y = 0; y < height; y++, yPos += i_uh) { 
4742                xPos = startX * i_w;
4743                for (int x = 0; x < width; x++, xPos += i_uw) {
4744                    xPositions[x][y] = xPos;
4745                    yPositions[x][y] = yPos;
4746                    zPositions[x][y] = 0.0;
4747                    heightData[x][y] = (h = terrainLayered.getNoiseWithSeed(xPos +
4748                                    terrain.getNoiseWithSeed(xPos, yPos, seedB - seedA) * 0.5,
4749                            yPos, seedA) + landModifier - 1.0);
4750                    heatData[x][y] = (p = heat.getNoiseWithSeed(xPos, yPos
4751                                    + otherRidged.getNoiseWithSeed(xPos, yPos, seedB + seedC),
4752                            seedB));
4753                    temp = otherRidged.getNoiseWithSeed(xPos, yPos, seedC + seedA);
4754                    moistureData[x][y] = (temp = moisture.getNoiseWithSeed(xPos - temp, yPos + temp, seedC));
4755
4756                    minHeightActual = Math.min(minHeightActual, h);
4757                    maxHeightActual = Math.max(maxHeightActual, h);
4758                    if(fresh) {
4759                        minHeight = Math.min(minHeight, h);
4760                        maxHeight = Math.max(maxHeight, h);
4761
4762                        minHeat0 = Math.min(minHeat0, p);
4763                        maxHeat0 = Math.max(maxHeat0, p);
4764
4765                        minWet0 = Math.min(minWet0, temp);
4766                        maxWet0 = Math.max(maxWet0, temp);
4767                    }
4768                }
4769                minHeightActual = Math.min(minHeightActual, minHeight);
4770                maxHeightActual = Math.max(maxHeightActual, maxHeight);
4771
4772            }
4773            double  heatDiff = 0.8 / (maxHeat0 - minHeat0),
4774                    wetDiff = 1.0 / (maxWet0 - minWet0),
4775                    hMod;
4776            yPos = startY * i_h + i_uh;
4777            ps = Double.POSITIVE_INFINITY;
4778            pc = Double.NEGATIVE_INFINITY;
4779
4780            for (int y = 0; y < height; y++, yPos += i_uh) {
4781                for (int x = 0; x < width; x++) {
4782                    h = heightData[x][y];
4783                    heightCodeData[x][y] = (t = codeHeight(h));
4784                    hMod = 1.0;
4785                    switch (t) {
4786                        case 0:
4787                        case 1:
4788                        case 2:
4789                        case 3:
4790                            h = 0.4;
4791                            hMod = 0.2;
4792                            break;
4793                        case 6:
4794                            h = -0.1 * (h - forestLower - 0.08);
4795                            break;
4796                        case 7:
4797                            h *= -0.25;
4798                            break;
4799                        case 8:
4800                            h *= -0.4;
4801                            break;
4802                        default:
4803                            h *= 0.05;
4804                    }
4805                    heatData[x][y] = (h = ((heatData[x][y] - minHeat0) * heatDiff * hMod) + h + 0.6);
4806                    if (fresh) {
4807                        ps = Math.min(ps, h); //minHeat0
4808                        pc = Math.max(pc, h); //maxHeat0
4809                    }
4810                }
4811            }
4812            if(fresh)
4813            {
4814                minHeat1 = ps;
4815                maxHeat1 = pc;
4816            }
4817            heatDiff = heatModifier / (maxHeat1 - minHeat1);
4818            qs = Double.POSITIVE_INFINITY;
4819            qc = Double.NEGATIVE_INFINITY;
4820            ps = Double.POSITIVE_INFINITY;
4821            pc = Double.NEGATIVE_INFINITY;
4822
4823
4824            for (int y = 0; y < height; y++) {
4825                for (int x = 0; x < width; x++) {
4826                    heatData[x][y] = (h = ((heatData[x][y] - minHeat1) * heatDiff));
4827                    moistureData[x][y] = (temp = (moistureData[x][y] - minWet0) * wetDiff);
4828                    if (fresh) {
4829                        qs = Math.min(qs, h);
4830                        qc = Math.max(qc, h);
4831                        ps = Math.min(ps, temp);
4832                        pc = Math.max(pc, temp);
4833                    }
4834                }
4835            }
4836            if(fresh)
4837            {
4838                minHeat = qs;
4839                maxHeat = qc;
4840                minWet = ps;
4841                maxWet = pc;
4842            }
4843            landData.refill(heightCodeData, 4, 999);
4844        }
4845    }
4846
4847    /**
4848     * An unusual map generator that imitates an existing local map (such as a map of Australia, which it can do by
4849     * default), without applying any projection or changing heat levels in the polar regions or equator.
4850     * <a href="http://squidpony.github.io/SquidLib/LocalMimicMap.png" >Example map, showing a variant on Australia</a>
4851     */
4852    public static class LocalMimicMap extends LocalMap
4853    {
4854        public GreasedRegion earth;
4855        public GreasedRegion shallow;
4856        public GreasedRegion coast;
4857        public GreasedRegion earthOriginal;
4858        /**
4859         * Constructs a concrete WorldMapGenerator for a map that should look like Australia, without projecting the
4860         * land positions or changing heat by latitude. Always makes a 256x256 map.
4861         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
4862         * If you were using {@link LocalMimicMap#LocalMimicMap(long, Noise2D, double)}, then this would be the
4863         * same as passing the parameters {@code 0x1337BABE1337D00DL, DEFAULT_NOISE, 1.0}.
4864         */
4865        public LocalMimicMap() {
4866            this(0x1337BABE1337D00DL
4867                    , DEFAULT_NOISE, 1.0);
4868        }
4869
4870        /**
4871         * Constructs a concrete WorldMapGenerator for a map that should have land in roughly the same places as the
4872         * given GreasedRegion's "on" cells, without projecting the land positions or changing heat by latitude.
4873         * The initial seed is set to the same large long every time, and it's likely that you would set the seed when
4874         * you call {@link #generate(long)}. The width and height of the map cannot be changed after the fact.
4875         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
4876         *
4877         * @param toMimic the world map to imitate, as a GreasedRegion with land as "on"; the height and width will be copied
4878         */
4879        public LocalMimicMap(GreasedRegion toMimic) {
4880            this(0x1337BABE1337D00DL, toMimic,  DEFAULT_NOISE,1.0);
4881        }
4882
4883        /**
4884         * Constructs a concrete WorldMapGenerator for a map that should have land in roughly the same places as the
4885         * given GreasedRegion's "on" cells, without projecting the land positions or changing heat by latitude.
4886         * Takes an initial seed and the GreasedRegion containing land positions. The {@code initialSeed}
4887         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
4888         * The width and height of the map cannot be changed after the fact.
4889         * Uses FastNoise as its noise generator, with 1.0 as the octave multiplier affecting detail.
4890         *
4891         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
4892         * @param toMimic the world map to imitate, as a GreasedRegion with land as "on"; the height and width will be copied
4893         */
4894        public LocalMimicMap(long initialSeed, GreasedRegion toMimic) {
4895            this(initialSeed, toMimic, DEFAULT_NOISE, 1.0);
4896        }
4897
4898        /**
4899         * Constructs a concrete WorldMapGenerator for a map that should have land in roughly the same places as the
4900         * given GreasedRegion's "on" cells, without projecting the land positions or changing heat by latitude.
4901         * Takes an initial seed, the GreasedRegion containing land positions, and a multiplier that affects the level
4902         * of detail by increasing or decreasing the number of octaves of noise used. The {@code initialSeed}
4903         * parameter may or may not be used, since you can specify the seed to use when you call {@link #generate(long)}.
4904         * The width and height of the map cannot be changed after the fact.
4905         * Uses FastNoise as its noise generator, with the given octave multiplier affecting detail.
4906         *
4907         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
4908         * @param toMimic the world map to imitate, as a GreasedRegion with land as "on"; the height and width will be copied
4909         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
4910         */
4911        public LocalMimicMap(long initialSeed, GreasedRegion toMimic, double octaveMultiplier) {
4912            this(initialSeed, toMimic, DEFAULT_NOISE, octaveMultiplier);
4913        }
4914
4915        /**
4916         * Constructs a concrete WorldMapGenerator for a map that should have land in roughly the same places as the
4917         * given GreasedRegion's "on" cells, without projecting the land positions or changing heat by latitude.
4918         * Takes an initial seed, the GreasedRegion containing land positions, and parameters for noise generation (a
4919         * {@link Noise3D} implementation, which is usually {@link FastNoise#instance}. The {@code initialSeed}
4920         * parameter may or may not be used, since you can specify the seed to use when you call
4921         * {@link #generate(long)}. The width and height of the map cannot be changed after the fact. Both FastNoise
4922         * and FastNoise make sense to use for {@code noiseGenerator}, and the seed it's constructed with doesn't matter
4923         * because this will change the seed several times at different scales of noise (it's fine to use the static
4924         * {@link FastNoise#instance} or {@link FastNoise#instance} because they have no changing state between runs
4925         * of the program). Uses the given noise generator, with 1.0 as the octave multiplier affecting detail.
4926         *
4927         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
4928         * @param toMimic the world map to imitate, as a GreasedRegion with land as "on"; the height and width will be copied
4929         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise} or {@link FastNoise}
4930         */
4931        public LocalMimicMap(long initialSeed, GreasedRegion toMimic, Noise2D noiseGenerator) {
4932            this(initialSeed, toMimic, noiseGenerator, 1.0);
4933        }
4934
4935        /**
4936         * Constructs a concrete WorldMapGenerator for a map that should have land in roughly the same places as the
4937         * given GreasedRegion's "on" cells, using an elliptical projection (specifically, a Mollweide projection).
4938         * Takes an initial seed, the GreasedRegion containing land positions, parameters for noise generation (a
4939         * {@link Noise3D} implementation, which is usually {@link FastNoise#instance}, and a multiplier on how many
4940         * octaves of noise to use, with 1.0 being normal (high) detail and higher multipliers producing even more
4941         * detailed noise when zoomed-in). The {@code initialSeed} parameter may or may not be used,
4942         * since you can specify the seed to use when you call {@link #generate(long)}. The width and height of the map
4943         * cannot be changed after the fact.  FastNoise will be the fastest 3D generator to use for
4944         * {@code noiseGenerator}, and the seed it's constructed with doesn't matter because this will change the
4945         * seed several times at different scales of noise (it's fine to use the static {@link FastNoise#instance}
4946         * because it has no changing state between runs of the program). The {@code octaveMultiplier} parameter should
4947         * probably be no lower than 0.5, but can be arbitrarily high if you're willing to spend much more time on
4948         * generating detail only noticeable at very high zoom; normally 1.0 is fine and may even be too high for maps
4949         * that don't require zooming.
4950         * @param initialSeed the seed for the GWTRNG this uses; this may also be set per-call to generate
4951         * @param toMimic the world map to imitate, as a GreasedRegion with land as "on"; the height and width will be copied
4952         * @param noiseGenerator an instance of a noise generator capable of 3D noise, usually {@link FastNoise} or {@link FastNoise}
4953         * @param octaveMultiplier used to adjust the level of detail, with 0.5 at the bare-minimum detail and 1.0 normal
4954         */
4955        public LocalMimicMap(long initialSeed, GreasedRegion toMimic, Noise2D noiseGenerator, double octaveMultiplier) {
4956            super(initialSeed, toMimic.width, toMimic.height, noiseGenerator, octaveMultiplier);
4957            earth = toMimic;
4958            earthOriginal = earth.copy();
4959            coast   = earth.copy().not().fringe(2);
4960            shallow = earth.copy().fringe(2);
4961        }
4962
4963        /**
4964         * Constructs a 256x256 unprojected local map that will use land forms with a similar shape to Australia.
4965         * @param initialSeed
4966         * @param noiseGenerator
4967         * @param octaveMultiplier
4968         */
4969        public LocalMimicMap(long initialSeed, Noise2D noiseGenerator, double octaveMultiplier)
4970        {
4971            this(initialSeed,
4972                    GreasedRegion.decompress(LZSPlus.decompress(
4973                            "Ƥ䒅⒐᮰囨䈢ħ䐤࠰ࠨ•Ⱙအ䎢ŘňÆ䴣ȢؤF䭠゠ᔤ∠偰ഀ\u0560₠ኼܨā᭮笁\u242AЦᇅ扰रࠦ吠䠪ࠦ䠧娮⠬䠬❁Ềក\u1CAA͠敠ἒ慽Ê䄄洡儠䋻䨡㈠䙬坈མŨྈ䞻䛊哚晪⁞倰h·䡂Ļæ抂㴢္\u082E搧䈠ᇩᒠ᩠ɀ༨ʨڤʃ奲ࢠ፠ᆙả䝆䮳りĩ(ॠી᧰྄e॑ᤙ䒠剠⁌ဥࠩФΝ䂂⢴ᑠ㺀ᢣ䗨dBqÚ扜冢\u0FE5\u0A62䐠劣ေ¯䂍䞀ၰ\u0E67ᐓ〈ᄠ塠Ѡ̀ာ⠤ᡤŒęጓ憒‱〿䌳℔ᐼ䊢⁚䤿ӣ◚㙀\u0C74Ӹ抠⣀ĨNJǸ䁃း₺Ý䂁ᜤ䢑V⁄樫焠\u0A60\u2E78⎲Ĉ䁎勯戡璠悈ᠥ嘡⩩‰ನ檨㡕䶪၁@恑ࠣ䘣ࢠᅀᡎ劰桠Өॢಸ熛փࢸ䀹ఽ䅠勖ਰ۴̄ጺಢ䈠ᙠᨭ\u2FE0焠Ӡܼ䇂䒠ᯀԨĠ愜᪅䦥㶐ୀ\u09C5Ƣ*䂕ॹ∠咠р\u0604У無~⁆Г椠痠\u1CA9Ⱓס㩖ᝋ司楠२ญⳘ䬣汤ǿã㱩ᖷ掠Àݒ㑁c‾䮴,\u2452僢ᰣ缠ɋ乨\u0378䁡绑ס傓䁔瀾ሺÑ䀤ो刡开烀\u0A76Ё䈠䈰״Áj⁑䠡戢碠㘀አ䃉㪙嘈ʂø⸪௰₈㐲暤ƩDᬿ䂖剙書\u0FE0㴢\u0089㘩Ĉ䰵掀栰杁4〡Ƞ⭀\u1AE0㠰㹨Zコത\u009E䂖ࠠⴠ縣吠ᆠʡ㡀䀧否䣝Ӧ愠Ⓚ\u1CA2ಠո*①ӈԥ獀խ@㟬箬㐱\u31BE簽Ɛᩆᇞ稯禚⟶⣑аβǚ㥎Ḇ⌢㑆 搡⁗ဣ刣\u0C45䑒8怺₵⤦a5ਵ㏰ᩄ猢ฦ䬞㐷䈠呠カ愠ۀ\u1C92傠ᅼ߃ᙊ䢨ၠླྀš亀ƴ̰刷ʼ墨愠  "
4974                    )),
4975                    noiseGenerator, octaveMultiplier);
4976        }
4977
4978        /**
4979         * Copies the LocalMimicMap {@code other} to construct a new one that is exactly the same. References will only
4980         * be shared to Noise classes.
4981         * @param other a LocalMimicMap to copy
4982         */
4983        public LocalMimicMap(LocalMimicMap other)
4984        {
4985            super(other);
4986            earth = other.earth.copy();
4987            earthOriginal = other.earthOriginal.copy();
4988            coast   = other.coast.copy();
4989            shallow = other.shallow.copy();
4990        }
4991
4992
4993
4994        protected void regenerate(int startX, int startY, int usedWidth, int usedHeight,
4995                                  double landMod, double heatMod, int stateA, int stateB)
4996        {
4997            boolean fresh = false;
4998            if(cacheA != stateA || cacheB != stateB || landMod != landModifier || heatMod != heatModifier)
4999            {
5000                minHeight = Double.POSITIVE_INFINITY;
5001                maxHeight = Double.NEGATIVE_INFINITY;
5002                minHeat0 = Double.POSITIVE_INFINITY;
5003                maxHeat0 = Double.NEGATIVE_INFINITY;
5004                minHeat1 = Double.POSITIVE_INFINITY;
5005                maxHeat1 = Double.NEGATIVE_INFINITY;
5006                minHeat = Double.POSITIVE_INFINITY;
5007                maxHeat = Double.NEGATIVE_INFINITY;
5008                minWet0 = Double.POSITIVE_INFINITY;
5009                maxWet0 = Double.NEGATIVE_INFINITY;
5010                minWet = Double.POSITIVE_INFINITY;
5011                maxWet = Double.NEGATIVE_INFINITY;
5012                cacheA = stateA;
5013                cacheB = stateB;
5014                fresh = true;
5015            }
5016            rng.setState(stateA, stateB);
5017            long seedA = rng.nextLong(), seedB = rng.nextLong(), seedC = rng.nextLong();
5018            int t;
5019
5020            landModifier = (landMod <= 0) ? rng.nextDouble(0.29) + 0.91 : landMod;
5021            heatModifier = (heatMod <= 0) ? rng.nextDouble(0.45) * (rng.nextDouble()-0.5) + 1.1 : heatMod;
5022
5023            earth.remake(earthOriginal);
5024
5025            if(zoom > 0)
5026            {
5027                int stx = Math.min(Math.max((zoomStartX - (width  >> 1)) / ((2 << zoom) - 2), 0), width ),
5028                        sty = Math.min(Math.max((zoomStartY - (height >> 1)) / ((2 << zoom) - 2), 0), height);
5029                for (int z = 0; z < zoom; z++) {
5030                    earth.zoom(stx, sty).expand8way().fray(0.5).expand();
5031                }
5032                coast.remake(earth).not().fringe(2 << zoom).expand().fray(0.5);
5033                shallow.remake(earth).fringe(2 << zoom).expand().fray(0.5);
5034            }
5035            else
5036            {
5037                coast.remake(earth).not().fringe(2);
5038                shallow.remake(earth).fringe(2);
5039            }
5040            double p,
5041                    ps, pc,
5042                    qs, qc,
5043                    h, temp,
5044                    i_w = 1.0 / width, i_h = 1.0 / (height),
5045                    i_uw = usedWidth * i_w * i_w, i_uh = usedHeight * i_h * i_h, xPos, yPos = startY * i_h;
5046            for (int y = 0; y < height; y++, yPos += i_uh) {
5047                xPos = startX * i_w;
5048                for (int x = 0, xt = 0; x < width; x++, xPos += i_uw) {
5049                    xPositions[x][y] = (xPos - .5) * 2.0;
5050                    yPositions[x][y] = (yPos - .5) * 2.0;
5051                    zPositions[x][y] = 0.0;
5052
5053                    if(earth.contains(x, y))
5054                    {
5055                        h = NumberTools.swayTight(terrainLayered.getNoiseWithSeed(xPos +
5056                                        terrain.getNoiseWithSeed(xPos, yPos, seedB - seedA) * 0.5,
5057                                yPos, seedA)) * 0.85;
5058                        if(coast.contains(x, y))
5059                            h += 0.05;
5060                        else
5061                            h += 0.15;
5062                    }
5063                    else
5064                    {
5065                        h = NumberTools.swayTight(terrainLayered.getNoiseWithSeed(xPos +
5066                                        terrain.getNoiseWithSeed(xPos, yPos, seedB - seedA) * 0.5,
5067                                yPos, seedA)) * -0.9;
5068                        if(shallow.contains(x, y))
5069                            h = (h - 0.08) * 0.375;
5070                        else
5071                            h = (h - 0.125) * 0.75;
5072                    }
5073                    //h += landModifier - 1.0;
5074                    heightData[x][y] = h;
5075                    heatData[x][y] = (p = heat.getNoiseWithSeed(xPos, yPos
5076                                    + otherRidged.getNoiseWithSeed(xPos, yPos, seedB + seedC),
5077                            seedB));
5078                    temp = otherRidged.getNoiseWithSeed(xPos, yPos, seedC + seedA);
5079                    moistureData[x][y] = (temp = moisture.getNoiseWithSeed(xPos - temp, yPos + temp, seedC));
5080
5081                    minHeightActual = Math.min(minHeightActual, h);
5082                    maxHeightActual = Math.max(maxHeightActual, h);
5083                    if(fresh) {
5084                        minHeight = Math.min(minHeight, h);
5085                        maxHeight = Math.max(maxHeight, h);
5086
5087                        minHeat0 = Math.min(minHeat0, p);
5088                        maxHeat0 = Math.max(maxHeat0, p);
5089
5090                        minWet0 = Math.min(minWet0, temp);
5091                        maxWet0 = Math.max(maxWet0, temp);
5092                    }
5093                }
5094                minHeightActual = Math.min(minHeightActual, minHeight);
5095                maxHeightActual = Math.max(maxHeightActual, maxHeight);
5096
5097            }
5098            double  heatDiff = 0.8 / (maxHeat0 - minHeat0),
5099                    wetDiff = 1.0 / (maxWet0 - minWet0),
5100                    hMod;
5101            yPos = startY * i_h + i_uh;
5102            ps = Double.POSITIVE_INFINITY;
5103            pc = Double.NEGATIVE_INFINITY;
5104
5105            for (int y = 0; y < height; y++, yPos += i_uh) {
5106                for (int x = 0; x < width; x++) {
5107                    h = heightData[x][y];
5108                    heightCodeData[x][y] = (t = codeHeight(h));
5109                    hMod = 1.0;
5110                    switch (t) {
5111                        case 0:
5112                        case 1:
5113                        case 2:
5114                        case 3:
5115                            h = 0.4;
5116                            hMod = 0.2;
5117                            break;
5118                        case 6:
5119                            h = -0.1 * (h - forestLower - 0.08);
5120                            break;
5121                        case 7:
5122                            h *= -0.25;
5123                            break;
5124                        case 8:
5125                            h *= -0.4;
5126                            break;
5127                        default:
5128                            h *= 0.05;
5129                    }
5130                    heatData[x][y] = (h = ((heatData[x][y] - minHeat0) * heatDiff * hMod) + h + 0.6);
5131                    if (fresh) {
5132                        ps = Math.min(ps, h); //minHeat0
5133                        pc = Math.max(pc, h); //maxHeat0
5134                    }
5135                }
5136            }
5137            if(fresh)
5138            {
5139                minHeat1 = ps;
5140                maxHeat1 = pc;
5141            }
5142            heatDiff = heatModifier / (maxHeat1 - minHeat1);
5143            qs = Double.POSITIVE_INFINITY;
5144            qc = Double.NEGATIVE_INFINITY;
5145            ps = Double.POSITIVE_INFINITY;
5146            pc = Double.NEGATIVE_INFINITY;
5147
5148
5149            for (int y = 0; y < height; y++) {
5150                for (int x = 0; x < width; x++) {
5151                    heatData[x][y] = (h = ((heatData[x][y] - minHeat1) * heatDiff));
5152                    moistureData[x][y] = (temp = (moistureData[x][y] - minWet0) * wetDiff);
5153                    if (fresh) {
5154                        qs = Math.min(qs, h);
5155                        qc = Math.max(qc, h);
5156                        ps = Math.min(ps, temp);
5157                        pc = Math.max(pc, temp);
5158                    }
5159                }
5160            }
5161            if(fresh)
5162            {
5163                minHeat = qs;
5164                maxHeat = qc;
5165                minWet = ps;
5166                maxWet = pc;
5167            }
5168            landData.refill(heightCodeData, 4, 999);
5169        }
5170    }
5171}