001package squidpony.squidmath;
002
003import squidpony.squidgrid.Direction;
004
005import java.io.Serializable;
006
007/**
008 * A 2D coordinate.
009 * 
010 * Created by Tommy Ettinger on 8/12/2015.
011 */
012public class Coord implements Serializable {
013    private static final long serialVersionUID = 300L;
014
015        /** The x-coordinate. */
016        public final int x;
017
018        /** The y-coordinate (the ordinate) */
019        public final int y;
020
021    protected Coord()
022    {
023        this(0, 0);
024    }
025    protected Coord(int x, int y)
026    {
027        this.x = x;
028        this.y = y;
029    }
030    public static Coord get(int x, int y)
031    {
032        if(x >= -3 && y >= -3 && x < POOL.length - 3 && y < POOL[x + 3].length - 3)
033            return POOL[x + 3][y + 3];
034        else return new Coord(x, y);
035    }
036
037        /**
038     * Gets the angle in degrees to go between two Coords; 0 is up.
039         * @param from the starting Coord to measure from
040         * @param to the ending Coord to measure to
041         * @return The degree from {@code from} to {@code to}; 0 is up
042         */
043        public static double degrees(Coord from, Coord to) {
044                final int x = to.x - from.x;
045                final int y = to.y - from.y;
046                double angle = Math.atan2(y, x);
047                double degree = Math.toDegrees(angle);
048                degree += 450;// rotate to all positive and 0 is up
049                degree %= 360;// normalize
050                return degree;
051        }
052
053    /**
054     * Provided for compatibility with earlier code that used the AWT Point API.
055     * @return this Coord, without changes
056     */
057    public Coord getLocation()
058    {
059        return this;
060    }
061
062    /**
063     * Takes this Coord, adds x to its x and y to its y, and returns the Coord at that position.
064     * @param x the amount of x distance to move
065     * @param y the amount of y distance to move
066     * @return a Coord (usually cached and not a new instance) that has been moved the specified distance
067     */
068    public Coord translate(int x, int y)
069    {
070        return get(this.x + x, this.y + y);
071    }
072    /**
073     * Takes this Coord, adds x to its x and y to its y, limiting x from 0 to width and limiting y from 0 to height,
074     * and returns the Coord at that position.
075     * @param x the amount of x distance to move
076     * @param y the amount of y distance to move
077     * @param width one higher than the maximum x value this can use; typically the length of an array
078     * @param height one higher than the maximum y value this can use; typically the length of an array
079     * @return a Coord (usually cached and not a new instance) that has been moved the specified distance
080     */
081    public Coord translateCapped(int x, int y, int width, int height)
082    {
083        return get(Math.min(Math.max(0, this.x + x), width - 1), Math.min(Math.max(0, this.y + y), height - 1));
084    }
085
086    /**
087     * Separately combines the x and y positions of this Coord and other, producing a different Coord as their "sum."
088     * @param other another Coord
089     * @return a Coord (usually cached and not a new instance) with {@code x = this.x + other.x; y = this.y + other.y}
090     */
091    public Coord add(Coord other)
092    {
093        return get(x + other.x, y + other.y);
094    }
095
096    /**
097     * Separately adds the x and y positions of this Coord to operand, producing a different Coord as their
098     * "sum."
099     * @param operand a value to add each of x and y to
100     * @return a Coord (usually cached and not a new instance) with {@code x = this.x + operand; y = this.y + operand}
101     */
102    public Coord add(int operand)
103    {
104        return get(x + operand, y + operand);
105    }
106
107    /**
108     * Separately adds the x and y positions of this Coord to operand, rounding to the nearest int for each of x
109     * and y and producing a different Coord as their "sum."
110     * @param operand a value to add each of x and y to
111     * @return a Coord (usually cached and not a new instance) with {@code x = this.x + operand; y = this.y +
112     *          operand}, with both x and y rounded accordingly
113     */
114    public Coord add(double operand)
115    {
116        return get((int)Math.round(x + operand), (int)Math.round(y + operand));
117    }
118
119    /**
120     * Separately subtracts the x and y positions of other from this Coord, producing a different Coord as their
121     * "difference."
122     * @param other another Coord
123     * @return a Coord (usually cached and not a new instance) with {@code x = this.x - other.x; y = this.y - other.y}
124     */
125    public Coord subtract(Coord other)
126    {
127        return get(x - other.x, y - other.y);
128    }
129
130    /**
131     * Separately subtracts operand from the x and y positions of this Coord, producing a different Coord as their
132     * "difference."
133     * @param operand a value to subtract from each of x and y
134     * @return a Coord (usually cached and not a new instance) with {@code x = this.x - operand; y = this.y - operand}
135     */
136    public Coord subtract(int operand)
137    {
138        return get(x - operand, y - operand);
139    }
140
141    /**
142     * Separately subtracts operand from the x and y positions of this Coord, rounding to the nearest int for each of x
143     * and y and producing a different Coord as their "difference."
144     * @param operand a value to subtract from each of x and y
145     * @return a Coord (usually cached and not a new instance) with {@code x = this.x - operand; y = this.y -
146     *          operand}, with both x and y rounded accordingly
147     */
148    public Coord subtract(double operand)
149    {
150        return get((int)Math.round(x - operand), (int)Math.round(y - operand));
151    }
152    /**
153     * Separately multiplies the x and y positions of other from this Coord, producing a different Coord as their
154     * "product."
155     * @param other another Coord
156     * @return a Coord (usually cached and not a new instance) with {@code x = this.x * other.x; y = this.y * other.y}
157     */
158    public Coord multiply(Coord other)
159    {
160        return get(x * other.x, y * other.y);
161    }
162    /**
163     * Separately multiplies the x and y positions of this Coord by operand, producing a different Coord as their
164     * "product."
165     * @param operand a value to multiply each of x and y by
166     * @return a Coord (usually cached and not a new instance) with {@code x = this.x * operand; y = this.y * operand}
167     */
168    public Coord multiply(int operand)
169    {
170        return get(x * operand, y * operand);
171    }
172
173    /**
174     * Separately multiplies the x and y positions of this Coord by operand, rounding to the nearest int for each of x
175     * and y and producing a different Coord as their "product."
176     * @param operand a value to multiply each of x and y by
177     * @return a Coord (usually cached and not a new instance) with {@code x = this.x * operand; y = this.y *
178     *          operand}, with both x and y rounded accordingly
179     */
180    public Coord multiply(double operand)
181    {
182        return get((int)Math.round(x * operand), (int)Math.round(y * operand));
183    }
184
185    /**
186     * Separately divides the x and y positions of this Coord by other, producing a different Coord as their
187     * "quotient." If other has 0 for x or y, this will throw an exception, as dividing by 0 is expected to do.
188     * @param other another Coord
189     * @return a Coord (usually cached and not a new instance) with {@code x = this.x / other.x; y = this.y / other.y}
190     */
191    public Coord divide(Coord other)
192    {
193        return get(x / other.x, y / other.y);
194    }
195    /**
196     * Separately divides the x and y positions of this Coord by operand, producing a different Coord as their
197     * "quotient." If operand is 0, this will throw an exception, as dividing by 0 is expected to do.
198     * @param operand a value to divide each of x and y by
199     * @return a Coord (usually cached and not a new instance) with {@code x = this.x / operand; y = this.y / operand}
200     */
201    public Coord divide(int operand)
202    {
203        return get(x / operand, y / operand);
204    }
205
206    /**
207     * Separately divides the x and y positions of this Coord by operand, flooring to a lower int for each of x and
208     * y and producing a different Coord as their "quotient." If operand is 0.0, expect strange results (infinity and
209     * NaN are both possibilities).
210     * @param operand a value to divide each of x and y by
211     * @return a Coord (usually cached and not a new instance) with {@code x = this.x / operand; y = this.y /
212     *          operand}, with both x and y rounded accordingly
213     */
214    public Coord divide(double operand)
215    {
216        return get((int)(x / operand), (int)(y / operand));
217    }
218
219    /**
220     * Separately divides the x and y positions of this Coord by operand, rounding to the nearest int for each of x and
221     * y and producing a different Coord as their "quotient." If operand is 0.0, expect strange results (infinity and
222     * NaN are both possibilities).
223     * @param operand a value to divide each of x and y by
224     * @return a Coord (usually cached and not a new instance) with {@code x = this.x / operand; y = this.y /
225     *          operand}, with both x and y rounded accordingly
226     */
227    public Coord divideRounding(double operand)
228    {
229        return get((int)Math.round(x / operand), (int)Math.round(y / operand));
230    }
231
232    /**
233     * Separately averages the x and y positions of this Coord with other, producing a different Coord as their
234     * "midpoint."
235     * @param other another Coord
236     * @return a Coord (usually cached and not a new instance) halfway between this and other, rounded nearest.
237     */
238    public Coord average(Coord other)
239    {
240        return get(Math.round((x + other.x) / 2.0f), Math.round((y + other.y) / 2.0f));
241    }
242        /**
243         * @param d
244         *            A non-{@code null} direction.
245         * @return The coordinate obtained by applying {@code d} on {@code this}.
246         */
247        public Coord translate(Direction d) {
248                return Coord.get(x + d.deltaX, y + d.deltaY);
249        }
250
251        /**
252         * @param i
253         * @return {@code (x*i,y*i)}.
254         */
255        public Coord scale(int i) {
256                return Coord.get(x * i, y * i);
257        }
258
259        /**
260         * @param i
261         * @return {@code (x*i,y*j)}.
262         */
263        public Coord scale(int i, int j) {
264                return Coord.get(x * i, y * j);
265        }
266
267    public double distance(double x2, double y2)
268    {
269        return Math.sqrt((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y));
270    }
271    public double distance(Coord co)
272    {
273        return Math.sqrt((co.x - x) * (co.x - x) + (co.y - y) * (co.y - y));
274    }
275    public double distanceSq(double x2, double y2)
276    {
277        return (x2 - x) * (x2 - x) + (y2 - y) * (y2 - y);
278    }
279    public double distanceSq(Coord co)
280    {
281        return (co.x - x) * (co.x - x) + (co.y - y) * (co.y - y);
282    }
283
284        /**
285         * @param c
286         * @return Whether {@code this} is adjacent to {@code c}. Not that a cell is
287         *         not adjacent to itself with this method.
288         */
289        public boolean isAdjacent(Coord c) {
290                switch (Math.abs(x - c.x)) {
291                case 0:
292                        return Math.abs(y - c.y) == 1;
293                case 1:
294                        return y == c.y || Math.abs(y - c.y) == 1;
295                default:
296                        return false;
297                }
298        }
299
300        /**
301         * Precondition: {@code this} is {@link #isAdjacent(Coord) adjacent} to
302         * {@code adjacent}.
303         * 
304         * @param adjacent
305         *            A {@link Coord} that is {@link #isAdjacent(Coord) adjacent} to
306         *            {@code this}.
307         * @return The direction to go from {@code this} to {@code adjacent} i.e.
308         *         the direction {@code d} such that {@code translate(this, d)}
309         *         yields {@code adjacent}.
310         * @throws IllegalStateException
311         *             If {@code this} isn't adjacent to {@code adjacent}.
312         */
313        /* KISS implementation */
314        public Direction toGoTo(Coord adjacent) {
315                assert isAdjacent(adjacent);
316                for (Direction d : Direction.values()) {
317                        /* Not calling #translate, to avoid calling the cache */
318                        if (x + d.deltaX == adjacent.x && y + d.deltaY == adjacent.y)
319                                return d;
320                }
321                throw new IllegalStateException(this + " is not adjacent to " + adjacent);
322        }
323
324    /**
325     * Returns true if x is between 0 (inclusive) and width (exclusive) and y is between 0 (inclusive) and height
326     * (exclusive), false otherwise.
327     * @param width the upper limit on x to check, exclusive
328     * @param height the upper limit on y to check, exclusive
329     * @return true if this Coord is within the limits of width and height and has non-negative x and y
330     */
331    public boolean isWithin(int width, int height)
332    {
333        return x >= 0 && y >= 0 && x < width && y < height;
334    }
335    /**
336     * Returns true if x is between minX (inclusive) and maxX (exclusive) and y is between minY (inclusive) and maxY
337     * (exclusive), false otherwise.
338     * @param minX the lower limit on x to check, inclusive
339     * @param minY the lower limit on y to check, inclusive
340     * @param maxX the upper limit on x to check, exclusive
341     * @param maxY the upper limit on y to check, exclusive
342     * @return true if this Coord is within the limits of the given parameters
343     */
344    public boolean isWithinRectangle(int minX, int minY, int maxX, int maxY)
345    {
346        return x >= minX && y >= minY && x < maxX && y < maxY;
347    }
348    public int getX() {
349        return x;
350    }
351
352    public Coord setX(int x) {
353        return get(x, y);
354    }
355
356    public int getY() {
357        return y;
358    }
359
360    public Coord setY(int y) {
361        return get(x, y);
362    }
363
364    @Override
365    public String toString()
366    {
367        return "Coord (x " + x + ", y " + y + ")";
368    }
369
370    @Override
371    public int hashCode() {
372        int hash = 7;
373        hash = 113 * hash + x;
374        hash = 113 * hash + y;
375        return hash;
376    }
377
378    @Override
379    public boolean equals(Object o) {
380        if (o instanceof Coord) {
381            Coord other = (Coord) o;
382            return x == other.x && y == other.y;
383        } else {
384            return false;
385        }
386    }
387    private static Coord[][] POOL = new Coord[259][259];
388    static {
389        int width = POOL.length, height = POOL[0].length;
390        for (int i = 0; i < width; i++) {
391            for (int j = 0; j < height; j++) {
392                POOL[i][j] = new Coord(i - 3, j - 3);
393            }
394        }
395    }
396
397    /**
398     * Gets the width of the pool used as a cache for Coords, not including negative Coords.
399     * Unless expandPool() has been called, this should be 256.
400     * Useful for finding the upper (exclusive) bound for x values that can be used efficiently in Coords.
401     * Requesting a Coord with a x greater than or equal to this value will result in a new Coord being allocated and
402     * not cached, which may cause problems with code that expects the normal reference equality of Coords to be upheld
403     * and in extreme cases may require more time garbage collecting than is normally necessary.
404     * @return the width of the Coord cache, disregarding negative Coords
405     */
406    public static int getCacheWidth()
407    {
408        return POOL.length - 3;
409    }
410
411    /**
412     * Gets the height of the pool used as a cache for Coords, not including negative Coords.
413     * Unless expandPool() has been called, this should be 256.
414     * Useful for finding the upper (exclusive) bound for y values that can be used efficiently in Coords.
415     * Requesting a Coord with a y greater than or equal to this value will result in a new Coord being allocated and
416     * not cached, which may cause problems with code that expects the normal reference equality of Coords to be upheld
417     * and in extreme cases may require more time garbage collecting than is normally necessary.
418     * @return the height of the Coord cache, disregarding negative Coords
419     */
420    public static int getCacheHeight()
421    {
422        return POOL[0].length - 3;
423    }
424
425    public static void expandPool(int xIncrease, int yIncrease)
426    {
427        if(xIncrease < 0 || yIncrease < 0)
428            return;
429        int width = POOL.length, height = POOL[0].length;
430        Coord[][] POOL2 = new Coord[width + xIncrease][height + yIncrease];
431        for (int i = 0; i < width; i++) {
432            POOL2[i] = new Coord[height + yIncrease];
433            System.arraycopy(POOL[i], 0, POOL2[i], 0, height);
434            for (int j = 0; j < height + yIncrease; j++) {
435                if(POOL2[i][j] == null) POOL2[i][j] = new Coord(i - 3, j - 3);
436            }
437        }
438        for (int i = width; i < width + xIncrease; i++) {
439            POOL2[i] = new Coord[height + yIncrease];
440            for (int j = 0; j < height + yIncrease; j++) {
441                POOL2[i][j] = new Coord(i - 3, j - 3);
442            }
443        }
444        POOL = POOL2;
445    }
446}