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}