package squidpony.squidgrid;
import squidpony.squidmath.Coord;
/**
* Represents the eight grid directions and the deltaX, deltaY values associated
* with those directions.
*
* The grid referenced has x positive to the right and y positive downwards on
* screen.
*
* @author Eben Howard - http://squidpony.com - howard@squidpony.com
*/
public enum Direction {
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);
/**
* An array which holds only the four cardinal directions.
*/
public static final Direction[] CARDINALS = {UP, DOWN, LEFT, RIGHT};
/**
* An array which holds only the four cardinal directions in clockwise order.
*/
public static final Direction[] CARDINALS_CLOCKWISE = {UP, RIGHT, DOWN, LEFT};
/**
* An array which holds only the four cardinal directions in counter-clockwise order.
*/
public static final Direction[] CARDINALS_COUNTERCLOCKWISE = {UP, LEFT, DOWN, RIGHT};
/**
* An array which holds only the four diagonal directions.
*/
public static final Direction[] DIAGONALS = {UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT};
/**
* An array which holds all eight OUTWARDS directions.
*/
public static final Direction[] OUTWARDS = {UP, DOWN, LEFT, RIGHT, UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT};
/**
* An array which holds all eight OUTWARDS directions in clockwise order.
*/
public static final Direction[] CLOCKWISE = {UP, UP_RIGHT, RIGHT, DOWN_RIGHT, DOWN, DOWN_LEFT, LEFT, UP_LEFT};
/**
* An array which holds all eight OUTWARDS directions in counter-clockwise order.
*/
public static final Direction[] COUNTERCLOCKWISE = {UP, UP_LEFT, LEFT, DOWN_LEFT, DOWN, DOWN_RIGHT, RIGHT, UP_RIGHT};
/**
* The x coordinate difference for this direction.
*/
public final int deltaX;
/**
* The y coordinate difference for this direction.
*/
public final int deltaY;
/**
* Returns the direction that most closely matches the input.
*
* This can be used to get the primary magnitude intercardinal direction
* from an origin point to an event point, such as a mouse click on a grid.
*
* If the point given is exactly on a boundary between directions then the
* direction clockwise is returned.
*
* @param x
* @param y
* @return
*/
public static Direction getDirection(int x, int y) {
if (x == 0 && y == 0) {
return NONE;
}
double angle = Math.atan2(y, x);
double degree = Math.toDegrees(angle);
degree += 450;//rotate to all positive and 0 is up
degree %= 360;//normalize
if (degree < 22.5) {
return UP;
} else if (degree < 67.5) {
return UP_RIGHT;
} else if (degree < 112.5) {
return RIGHT;
} else if (degree < 157.5) {
return DOWN_RIGHT;
} else if (degree < 202.5) {
return DOWN;
} else if (degree < 247.5) {
return DOWN_LEFT;
} else if (degree < 292.5) {
return LEFT;
} else if (degree < 337.5) {
return UP_LEFT;
} else {
return UP;
}
}
/**
* Returns the direction that most closely matches the input.
*
* This can be used to get the primary magnitude cardinal direction from an
* origin point to an event point, such as a mouse click on a grid.
*
* If the point given is directly diagonal then the direction clockwise is
* returned.
*
* @param x
* @param y
* @return
*/
public static Direction getCardinalDirection(int x, int y) {
if (x == 0 && y == 0) {
return NONE;
}
int absx = Math.abs(x);
if (y > absx) {
return UP;
}
int absy = Math.abs(y);
if (absy > absx) {
return DOWN;
}
if (x > 0) {
if (-y == x) {//on diagonal
return DOWN;
}
return RIGHT;
}
if (y == x) {//on diagonal
return UP;
}
return LEFT;
}
/**
* @param from
* The starting point.
* @param to
* The desired point to reach.
* @return The direction to follow to go from {@code from} to {@code to}. It
* can be cardinal or diagonal.
*/
public static Direction toGoTo(Coord from, Coord to) {
return getDirection(to.x - from.x, to.y - from.y);
}
/**
* Returns the Direction one step clockwise including diagonals.
*
* If considering only Cardinal directions, calling this twice will get the
* next clockwise cardinal direction.
*
* @return
*/
public Direction clockwise() {
switch (this) {
case UP:
return UP_RIGHT;
case DOWN:
return DOWN_LEFT;
case LEFT:
return UP_LEFT;
case RIGHT:
return DOWN_RIGHT;
case UP_LEFT:
return UP;
case UP_RIGHT:
return RIGHT;
case DOWN_LEFT:
return LEFT;
case DOWN_RIGHT:
return DOWN;
case NONE:
default:
return NONE;
}
}
/**
* Returns the Direction one step counterclockwise including diagonals.
*
* If considering only Cardinal directions, calling this twice will get the
* next counterclockwise cardinal direction.
*
* @return
*/
public Direction counterClockwise() {
switch (this) {
case UP:
return UP_LEFT;
case DOWN:
return DOWN_RIGHT;
case LEFT:
return DOWN_LEFT;
case RIGHT:
return UP_RIGHT;
case UP_LEFT:
return LEFT;
case UP_RIGHT:
return UP;
case DOWN_LEFT:
return DOWN;
case DOWN_RIGHT:
return RIGHT;
case NONE:
default:
return NONE;
}
}
/**
* Returns the direction directly opposite of this one.
*
* @return
*/
public Direction opposite() {
switch (this) {
case UP:
return DOWN;
case DOWN:
return UP;
case LEFT:
return RIGHT;
case RIGHT:
return LEFT;
case UP_LEFT:
return DOWN_RIGHT;
case UP_RIGHT:
return DOWN_LEFT;
case DOWN_LEFT:
return UP_RIGHT;
case DOWN_RIGHT:
return UP_LEFT;
case NONE:
default:
return NONE;
}
}
/**
* @return Whether this is a diagonal move.
*/
public boolean isDiagonal() {
return (deltaX & deltaY) != 0;
}
/**
* @return Whether this is a cardinal-direction move.
*/
public boolean isCardinal() {
return (deltaX + deltaY & 1) != 0;
}
/**
* @return {@code true} if {@code this} has an upward component.
*/
public boolean hasUp() {
switch (this) {
case UP:
case UP_LEFT:
case UP_RIGHT:
return true;
case DOWN:
case DOWN_LEFT:
case DOWN_RIGHT:
case LEFT:
case NONE:
case RIGHT:
return false;
}
throw new IllegalStateException("Unmatched " + getClass().getSimpleName() + ": " + this);
}
/**
* @return {@code true} if {@code this} has a downward component.
*/
public boolean hasDown() {
switch (this) {
case DOWN:
case DOWN_LEFT:
case DOWN_RIGHT:
return true;
case LEFT:
case NONE:
case RIGHT:
case UP:
case UP_LEFT:
case UP_RIGHT:
return false;
}
throw new IllegalStateException("Unmatched " + getClass().getSimpleName() + ": " + this);
}
/**
* @return {@code true} if {@code this} has a left component.
*/
public boolean hasLeft() {
switch (this) {
case DOWN_LEFT:
case LEFT:
case UP_LEFT:
return true;
case DOWN:
case DOWN_RIGHT:
case NONE:
case RIGHT:
case UP:
case UP_RIGHT:
return false;
}
throw new IllegalStateException("Unmatched " + getClass().getSimpleName() + ": " + this);
}
/**
* @return {@code true} if {@code this} has a right component.
*/
public boolean hasRight() {
switch (this) {
case RIGHT:
case DOWN_RIGHT:
case UP_RIGHT:
return true;
case DOWN:
case NONE:
case UP:
case DOWN_LEFT:
case LEFT:
case UP_LEFT:
return false;
}
throw new IllegalStateException("Unmatched " + getClass().getSimpleName() + ": " + this);
}
Direction(int x, int y) {
deltaX = x;
deltaY = y;
}
}