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}