001package squidpony.squidgrid;
002
003import squidpony.squidmath.Coord;
004import squidpony.squidmath.NumberTools;
005
006/**
007 * Represents the eight grid directions and the deltaX, deltaY values associated
008 * with those directions.
009 *
010 * The grid referenced has x positive to the right and y positive downwards on
011 * screen.
012 *
013 * @author Eben Howard - http://squidpony.com - howard@squidpony.com
014 */
015public enum Direction {
016
017    UP(0, -1), DOWN(0, 1), LEFT(-1, 0), RIGHT(1, 0), UP_LEFT(-1, -1), UP_RIGHT(1, -1), DOWN_LEFT(-1, 1), DOWN_RIGHT(1, 1), NONE(0, 0);
018    /**
019     * An array which holds only the four cardinal directions.
020     */
021    public static final Direction[] CARDINALS = {UP, DOWN, LEFT, RIGHT};
022    /**
023     * An array which holds only the four cardinal directions in clockwise order.
024     */
025    public static final Direction[] CARDINALS_CLOCKWISE = {UP, RIGHT, DOWN, LEFT};
026    /**
027     * An array which holds only the four cardinal directions in counter-clockwise order.
028     */
029    public static final Direction[] CARDINALS_COUNTERCLOCKWISE = {UP, LEFT, DOWN, RIGHT};
030    /**
031     * An array which holds only the four diagonal directions.
032     */
033    public static final Direction[] DIAGONALS = {UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT};
034    /**
035     * An array which holds all eight OUTWARDS directions.
036     */
037    public static final Direction[] OUTWARDS = {UP, DOWN, LEFT, RIGHT, UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT};
038    /**
039     * An array which holds all eight OUTWARDS directions in clockwise order.
040     */
041    public static final Direction[] CLOCKWISE = {UP, UP_RIGHT, RIGHT, DOWN_RIGHT, DOWN, DOWN_LEFT, LEFT, UP_LEFT};
042    /**
043     * An array which holds all eight OUTWARDS directions in counter-clockwise order.
044     */
045    public static final Direction[] COUNTERCLOCKWISE = {UP, UP_LEFT, LEFT, DOWN_LEFT, DOWN, DOWN_RIGHT, RIGHT, UP_RIGHT};
046    /**
047     * The x coordinate difference for this direction.
048     */
049    public final int deltaX;
050    /**
051     * The y coordinate difference for this direction.
052     */
053    public final int deltaY;
054
055    /**
056     * Returns the direction that most closely matches the input. This can be any
057     * direction, including cardinal and diagonal directions as well as {@link #NONE}
058     * in the case that x and y are both 0.
059     * <br>
060     * This can be used to get the primary intercardinal direction
061     * from an origin point to an event point, such as a mouse click on a grid.
062     * If the point given is exactly on a boundary between directions then the
063     * direction clockwise is returned.
064     *
065     * @param x the x position relative to the origin (0,0)
066     * @param y the y position relative to the origin (0,0)
067     * @return the closest matching Direction enum, which may be {@link #NONE}
068     */
069    public static Direction getDirection(int x, int y) {
070        if ((x | y) == 0) return NONE;
071        return CLOCKWISE[(int)(NumberTools.atan2_(y, x) * 8f + 2.5f) & 7];
072    }
073
074    /**
075     * Gets an estimate at the correct direction that a position lies in given the distance towards it on the x and y
076     * axes. If x and y are both between -1 and 1 inclusive, this will always be accurate, and should be faster than
077     * {@link #getDirection(int, int)} by avoiding trigonometry or any other math on doubles. If at least one of x or y
078     * is 0, then this will also be accurate and will produce either a cardinal direction or NONE if both are 0. If x
079     * and y are both non-zero, this will always produce a diagonal, even if a cardinal direction should be more
080     * accurate; this behavior may sometimes be desirable to detect when some position is even slightly off from a true
081     * cardinal direction.
082     * @param x the x position relative to the origin (0,0)
083     * @param y the y position relative to the origin (0,0)
084     * @return the Direction that x,y lies in, roughly; will always be accurate for arguments between -1 and 1 inclusive
085     */
086    public static Direction getRoughDirection(int x, int y)
087    {
088        x = (x >> 31 | -x >>> 31); // project nayuki signum
089        y = (y >> 31 | -y >>> 31); // project nayuki signum
090        switch (x)
091        {
092            case -1:
093                switch (y)
094                {
095                    case 1: return DOWN_LEFT;
096                    case -1: return UP_LEFT;
097                    default: return LEFT;
098                }
099            case 1:
100                switch (y)
101                {
102                    case 1: return DOWN_RIGHT;
103                    case -1: return UP_RIGHT;
104                    default: return RIGHT;
105                }
106            default:
107                switch (y)
108                {
109                    case 1: return DOWN;
110                    case -1: return UP;
111                    default: return NONE;
112                }
113        }
114    }
115
116    /**
117     * Returns the direction that most closely matches the input.
118     * This can be any of the four cardinal directions as well as {@link #NONE}
119     * in the case that x and y are both 0.
120     * <br>
121     * This can be used to get the primary cardinal direction from an
122     * origin point to an event point, such as a mouse click on a grid.
123     * If the point given is directly diagonal then the direction clockwise is
124     * returned.
125     * <br>
126     * This method returned an incorrect value for several years (the bug was found on
127     * April 28, 2020 but the method hadn't been modified for a long time), and other
128     * code in SquidLib had workarounds in use before the bug was fixed. The nature of
129     * the bug was simply that {@link #UP} was returned when {@link #DOWN} should have
130     * been, and vice versa; the workaround used in various places was to negate y.
131     * Now you should no longer have to negate y or treat it differently than x, and
132     * earlier code should account for the vertical axis being corrected.
133     * 
134     * @param x the x position relative to the origin (0,0)
135     * @param y the y position relative to the origin (0,0)
136     * @return the closest matching cardinal Direction enum, which may also be {@link #NONE}
137     */
138    public static Direction getCardinalDirection(int x, int y) {
139        if ((x | y) == 0) return NONE;
140        return CARDINALS_CLOCKWISE[(int)(NumberTools.atan2_(y, x) * 4f + 1.5f) & 3];
141    }
142
143        /**
144         * @param from
145         *            The starting point.
146         * @param to
147         *            The desired point to reach.
148         * @return The direction to follow to go from {@code from} to {@code to}. It
149         *         can be cardinal or diagonal.
150         */
151        public static Direction toGoTo(Coord from, Coord to) {
152                return getDirection(to.x - from.x, to.y - from.y);
153        }
154
155    /**
156     * Returns the Direction one step clockwise including diagonals.
157     *
158     * If considering only Cardinal directions, calling this twice will get the
159     * next clockwise cardinal direction.
160     *
161     * @return
162     */
163    public Direction clockwise() {
164        switch (this) {
165            case UP:
166                return UP_RIGHT;
167            case DOWN:
168                return DOWN_LEFT;
169            case LEFT:
170                return UP_LEFT;
171            case RIGHT:
172                return DOWN_RIGHT;
173            case UP_LEFT:
174                return UP;
175            case UP_RIGHT:
176                return RIGHT;
177            case DOWN_LEFT:
178                return LEFT;
179            case DOWN_RIGHT:
180                return DOWN;
181            case NONE:
182            default:
183                return NONE;
184        }
185    }
186
187    /**
188     * Returns the Direction one step counterclockwise including diagonals.
189     *
190     * If considering only Cardinal directions, calling this twice will get the
191     * next counterclockwise cardinal direction.
192     *
193     * @return
194     */
195    public Direction counterClockwise() {
196        switch (this) {
197            case UP:
198                return UP_LEFT;
199            case DOWN:
200                return DOWN_RIGHT;
201            case LEFT:
202                return DOWN_LEFT;
203            case RIGHT:
204                return UP_RIGHT;
205            case UP_LEFT:
206                return LEFT;
207            case UP_RIGHT:
208                return UP;
209            case DOWN_LEFT:
210                return DOWN;
211            case DOWN_RIGHT:
212                return RIGHT;
213            case NONE:
214            default:
215                return NONE;
216        }
217    }
218
219    /**
220     * Returns the direction directly opposite of this one.
221     *
222     * @return
223     */
224    public Direction opposite() {
225        switch (this) {
226            case UP:
227                return DOWN;
228            case DOWN:
229                return UP;
230            case LEFT:
231                return RIGHT;
232            case RIGHT:
233                return LEFT;
234            case UP_LEFT:
235                return DOWN_RIGHT;
236            case UP_RIGHT:
237                return DOWN_LEFT;
238            case DOWN_LEFT:
239                return UP_RIGHT;
240            case DOWN_RIGHT:
241                return UP_LEFT;
242            case NONE:
243            default:
244                return NONE;
245        }
246    }
247
248    /**
249     * @return Whether this is a diagonal move.
250     */
251    public boolean isDiagonal() {
252        return (deltaX & deltaY) != 0;
253    }
254
255    /**
256     * @return Whether this is a cardinal-direction move.
257     */
258    public boolean isCardinal() {
259        return (deltaX + deltaY & 1) != 0;
260    }
261
262        /**
263         * @return {@code true} if {@code this} has an upward component.
264         */
265        public boolean hasUp() {
266                switch (this) {
267                case UP:
268                case UP_LEFT:
269                case UP_RIGHT:
270                        return true;
271                case DOWN:
272                case DOWN_LEFT:
273                case DOWN_RIGHT:
274                case LEFT:
275                case NONE:
276                case RIGHT:
277                        return false;
278                }
279                throw new IllegalStateException("Unmatched Direction: " + this);
280        }
281
282        /**
283         * @return {@code true} if {@code this} has a downward component.
284         */
285        public boolean hasDown() {
286                switch (this) {
287                case DOWN:
288                case DOWN_LEFT:
289                case DOWN_RIGHT:
290                        return true;
291                case LEFT:
292                case NONE:
293                case RIGHT:
294                case UP:
295                case UP_LEFT:
296                case UP_RIGHT:
297                        return false;
298                }
299                throw new IllegalStateException("Unmatched Direction: " + this);
300        }
301
302        /**
303         * @return {@code true} if {@code this} has a left component.
304         */
305        public boolean hasLeft() {
306                switch (this) {
307                case DOWN_LEFT:
308                case LEFT:
309                case UP_LEFT:
310                        return true;
311                case DOWN:
312                case DOWN_RIGHT:
313                case NONE:
314                case RIGHT:
315                case UP:
316                case UP_RIGHT:
317                        return false;
318                }
319                throw new IllegalStateException("Unmatched Direction: " + this);
320        }
321
322        /**
323         * @return {@code true} if {@code this} has a right component.
324         */
325        public boolean hasRight() {
326                switch (this) {
327                case RIGHT:
328                case DOWN_RIGHT:
329                case UP_RIGHT:
330                        return true;
331                case DOWN:
332                case NONE:
333                case UP:
334                case DOWN_LEFT:
335                case LEFT:
336                case UP_LEFT:
337                        return false;
338                }
339                throw new IllegalStateException("Unmatched Direction: " + this);
340        }
341
342    Direction(int x, int y) {
343        deltaX = x;
344        deltaY = y;
345    }
346}