001package squidpony.squidgrid;
002
003import squidpony.squidmath.Coord;
004
005/**
006 * Represents the eight grid directions and the deltaX, deltaY values associated
007 * with those directions.
008 *
009 * The grid referenced has x positive to the right and y positive downwards on
010 * screen.
011 *
012 * @author Eben Howard - http://squidpony.com - howard@squidpony.com
013 */
014public enum Direction {
015
016    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);
017    /**
018     * An array which holds only the four cardinal directions.
019     */
020    public static final Direction[] CARDINALS = {UP, DOWN, LEFT, RIGHT};
021    /**
022     * An array which holds only the four diagonal directions.
023     */
024    public static final Direction[] DIAGONALS = {UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT};
025    /**
026     * An array which holds all eight OUTWARDS directions.
027     */
028    public static final Direction[] OUTWARDS = {UP, DOWN, LEFT, RIGHT, UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT};
029    /**
030     * The x coordinate difference for this direction.
031     */
032    public final int deltaX;
033    /**
034     * The y coordinate difference for this direction.
035     */
036    public final int deltaY;
037
038    /**
039     * Returns the direction that most closely matches the input.
040     *
041     * This can be used to get the primary magnitude intercardinal direction
042     * from an origin point to an event point, such as a mouse click on a grid.
043     *
044     * If the point given is exactly on a boundary between directions then the
045     * direction clockwise is returned.
046     *
047     * @param x
048     * @param y
049     * @return
050     */
051    public static Direction getDirection(int x, int y) {
052        if (x == 0 && y == 0) {
053            return NONE;
054        }
055
056        double angle = Math.atan2(y, x);
057        double degree = Math.toDegrees(angle);
058        degree += 450;//rotate to all positive and 0 is up
059        degree %= 360;//normalize
060        if (degree < 22.5) {
061            return UP;
062        } else if (degree < 67.5) {
063            return UP_RIGHT;
064        } else if (degree < 112.5) {
065            return RIGHT;
066        } else if (degree < 157.5) {
067            return DOWN_RIGHT;
068        } else if (degree < 202.5) {
069            return DOWN;
070        } else if (degree < 247.5) {
071            return DOWN_LEFT;
072        } else if (degree < 292.5) {
073            return LEFT;
074        } else if (degree < 337.5) {
075            return UP_LEFT;
076        } else {
077            return UP;
078        }
079    }
080
081    /**
082     * Returns the direction that most closely matches the input.
083     *
084     * This can be used to get the primary magnitude cardinal direction from an
085     * origin point to an event point, such as a mouse click on a grid.
086     *
087     * If the point given is directly diagonal then the direction clockwise is
088     * returned.
089     *
090     * @param x
091     * @param y
092     * @return
093     */
094    public static Direction getCardinalDirection(int x, int y) {
095        if (x == 0 && y == 0) {
096            return NONE;
097        }
098
099        int absx = Math.abs(x);
100
101        if (y > absx) {
102            return UP;
103        }
104
105        int absy = Math.abs(y);
106
107        if (absy > absx) {
108            return DOWN;
109        }
110
111        if (x > 0) {
112            if (-y == x) {//on diagonal
113                return DOWN;
114            }
115            return RIGHT;
116        }
117
118        if (y == x) {//on diagonal
119            return UP;
120        }
121        return LEFT;
122
123    }
124
125        /**
126         * @param from
127         *            The starting point.
128         * @param to
129         *            The desired point to reach.
130         * @return The direction to follow to go from {@code from} to {@code to}. It
131         *         can be cardinal or diagonal.
132         */
133        public static Direction toGoTo(Coord from, Coord to) {
134                return getDirection(to.x - from.x, to.y - from.y);
135        }
136
137    /**
138     * Returns the Direction one step clockwise including diagonals.
139     *
140     * If considering only Cardinal directions, calling this twice will get the
141     * next clockwise cardinal direction.
142     *
143     * @return
144     */
145    public Direction clockwise() {
146        switch (this) {
147            case UP:
148                return UP_RIGHT;
149            case DOWN:
150                return DOWN_LEFT;
151            case LEFT:
152                return UP_LEFT;
153            case RIGHT:
154                return DOWN_RIGHT;
155            case UP_LEFT:
156                return UP;
157            case UP_RIGHT:
158                return RIGHT;
159            case DOWN_LEFT:
160                return LEFT;
161            case DOWN_RIGHT:
162                return DOWN;
163            case NONE:
164            default:
165                return NONE;
166        }
167    }
168
169    /**
170     * Returns the Direction one step counterclockwise including diagonals.
171     *
172     * If considering only Cardinal directions, calling this twice will get the
173     * next counterclockwise cardinal direction.
174     *
175     * @return
176     */
177    public Direction counterClockwise() {
178        switch (this) {
179            case UP:
180                return UP_LEFT;
181            case DOWN:
182                return DOWN_RIGHT;
183            case LEFT:
184                return DOWN_LEFT;
185            case RIGHT:
186                return UP_RIGHT;
187            case UP_LEFT:
188                return LEFT;
189            case UP_RIGHT:
190                return UP;
191            case DOWN_LEFT:
192                return DOWN;
193            case DOWN_RIGHT:
194                return RIGHT;
195            case NONE:
196            default:
197                return NONE;
198        }
199    }
200
201    /**
202     * Returns the direction directly opposite of this one.
203     *
204     * @return
205     */
206    public Direction opposite() {
207        switch (this) {
208            case UP:
209                return DOWN;
210            case DOWN:
211                return UP;
212            case LEFT:
213                return RIGHT;
214            case RIGHT:
215                return LEFT;
216            case UP_LEFT:
217                return DOWN_RIGHT;
218            case UP_RIGHT:
219                return DOWN_LEFT;
220            case DOWN_LEFT:
221                return UP_RIGHT;
222            case DOWN_RIGHT:
223                return UP_LEFT;
224            case NONE:
225            default:
226                return NONE;
227        }
228    }
229
230        /**
231         * @return Whether this is a diagonal move.
232         */
233        public boolean isDiagonal() {
234                switch (this) {
235                case DOWN_LEFT:
236                case DOWN_RIGHT:
237                case UP_LEFT:
238                case UP_RIGHT:
239                        return true;
240                case DOWN:
241                case LEFT:
242                case NONE:
243                case RIGHT:
244                case UP:
245                        return false;
246                }
247                throw new IllegalStateException("Unmatched " + getClass().getSimpleName() + ": " + this);
248        }
249
250        /**
251         * @return {@code true} if {@code this} has an upward component.
252         */
253        public boolean hasUp() {
254                switch (this) {
255                case UP:
256                case UP_LEFT:
257                case UP_RIGHT:
258                        return true;
259                case DOWN:
260                case DOWN_LEFT:
261                case DOWN_RIGHT:
262                case LEFT:
263                case NONE:
264                case RIGHT:
265                        return false;
266                }
267                throw new IllegalStateException("Unmatched " + getClass().getSimpleName() + ": " + this);
268        }
269
270        /**
271         * @return {@code true} if {@code this} has a downward component.
272         */
273        public boolean hasDown() {
274                switch (this) {
275                case DOWN:
276                case DOWN_LEFT:
277                case DOWN_RIGHT:
278                        return true;
279                case LEFT:
280                case NONE:
281                case RIGHT:
282                case UP:
283                case UP_LEFT:
284                case UP_RIGHT:
285                        return false;
286                }
287                throw new IllegalStateException("Unmatched " + getClass().getSimpleName() + ": " + this);
288        }
289
290        /**
291         * @return {@code true} if {@code this} has a left component.
292         */
293        public boolean hasLeft() {
294                switch (this) {
295                case DOWN_LEFT:
296                case LEFT:
297                case UP_LEFT:
298                        return true;
299                case DOWN:
300                case DOWN_RIGHT:
301                case NONE:
302                case RIGHT:
303                case UP:
304                case UP_RIGHT:
305                        return false;
306                }
307                throw new IllegalStateException("Unmatched " + getClass().getSimpleName() + ": " + this);
308        }
309
310        /**
311         * @return {@code true} if {@code this} has a right component.
312         */
313        public boolean hasRight() {
314                switch (this) {
315                case RIGHT:
316                case DOWN_RIGHT:
317                case UP_RIGHT:
318                        return true;
319                case DOWN:
320                case NONE:
321                case UP:
322                case DOWN_LEFT:
323                case LEFT:
324                case UP_LEFT:
325                        return false;
326                }
327                throw new IllegalStateException("Unmatched " + getClass().getSimpleName() + ": " + this);
328        }
329
330
331    Direction(int x, int y) {
332        deltaX = x;
333        deltaY = y;
334    }
335}