001 package org.bukkit; 002 003 import org.bukkit.block.Block; 004 import org.bukkit.util.NumberConversions; 005 import org.bukkit.util.Vector; 006 007 /** 008 * Represents a 3-dimensional position in a world 009 */ 010 public class Location implements Cloneable { 011 private World world; 012 private double x; 013 private double y; 014 private double z; 015 private float pitch; 016 private float yaw; 017 018 /** 019 * Constructs a new Location with the given coordinates 020 * 021 * @param world The world in which this location resides 022 * @param x The x-coordinate of this new location 023 * @param y The y-coordinate of this new location 024 * @param z The z-coordinate of this new location 025 */ 026 public Location(final World world, final double x, final double y, final double z) { 027 this(world, x, y, z, 0, 0); 028 } 029 030 /** 031 * Constructs a new Location with the given coordinates and direction 032 * 033 * @param world The world in which this location resides 034 * @param x The x-coordinate of this new location 035 * @param y The y-coordinate of this new location 036 * @param z The z-coordinate of this new location 037 * @param yaw The absolute rotation on the x-plane, in degrees 038 * @param pitch The absolute rotation on the y-plane, in degrees 039 */ 040 public Location(final World world, final double x, final double y, final double z, final float yaw, final float pitch) { 041 this.world = world; 042 this.x = x; 043 this.y = y; 044 this.z = z; 045 this.pitch = pitch; 046 this.yaw = yaw; 047 } 048 049 /** 050 * Sets the world that this location resides in 051 * 052 * @param world New world that this location resides in 053 */ 054 public void setWorld(World world) { 055 this.world = world; 056 } 057 058 /** 059 * Gets the world that this location resides in 060 * 061 * @return World that contains this location 062 */ 063 public World getWorld() { 064 return world; 065 } 066 067 /** 068 * Gets the chunk at the represented location 069 * 070 * @return Chunk at the represented location 071 */ 072 public Chunk getChunk() { 073 return world.getChunkAt(this); 074 } 075 076 /** 077 * Gets the block at the represented location 078 * 079 * @return Block at the represented location 080 */ 081 public Block getBlock() { 082 return world.getBlockAt(this); 083 } 084 085 /** 086 * Sets the x-coordinate of this location 087 * 088 * @param x X-coordinate 089 */ 090 public void setX(double x) { 091 this.x = x; 092 } 093 094 /** 095 * Gets the x-coordinate of this location 096 * 097 * @return x-coordinate 098 */ 099 public double getX() { 100 return x; 101 } 102 103 /** 104 * Gets the floored value of the X component, indicating the block that 105 * this location is contained with. 106 * 107 * @return block X 108 */ 109 public int getBlockX() { 110 return locToBlock(x); 111 } 112 113 /** 114 * Sets the y-coordinate of this location 115 * 116 * @param y y-coordinate 117 */ 118 public void setY(double y) { 119 this.y = y; 120 } 121 122 /** 123 * Gets the y-coordinate of this location 124 * 125 * @return y-coordinate 126 */ 127 public double getY() { 128 return y; 129 } 130 131 /** 132 * Gets the floored value of the Y component, indicating the block that 133 * this location is contained with. 134 * 135 * @return block y 136 */ 137 public int getBlockY() { 138 return locToBlock(y); 139 } 140 141 /** 142 * Sets the z-coordinate of this location 143 * 144 * @param z z-coordinate 145 */ 146 public void setZ(double z) { 147 this.z = z; 148 } 149 150 /** 151 * Gets the z-coordinate of this location 152 * 153 * @return z-coordinate 154 */ 155 public double getZ() { 156 return z; 157 } 158 159 /** 160 * Gets the floored value of the Z component, indicating the block that 161 * this location is contained with. 162 * 163 * @return block z 164 */ 165 public int getBlockZ() { 166 return locToBlock(z); 167 } 168 169 /** 170 * Sets the yaw of this location, measured in degrees. 171 * <ul> 172 * <li>A yaw of 0 or 360 represents the positive z direction. 173 * <li>A yaw of 180 represents the negative z direction. 174 * <li>A yaw of 90 represents the negative x direction. 175 * <li>A yaw of 270 represents the positive x direction. 176 * </ul> 177 * Increasing yaw values are the equivalent of turning to your 178 * right-facing, increasing the scale of the next respective axis, and 179 * decreasing the scale of the previous axis. 180 * 181 * @param yaw new rotation's yaw 182 */ 183 public void setYaw(float yaw) { 184 this.yaw = yaw; 185 } 186 187 /** 188 * Gets the yaw of this location, measured in degrees. 189 * <ul> 190 * <li>A yaw of 0 or 360 represents the positive z direction. 191 * <li>A yaw of 180 represents the negative z direction. 192 * <li>A yaw of 90 represents the negative x direction. 193 * <li>A yaw of 270 represents the positive x direction. 194 * </ul> 195 * Increasing yaw values are the equivalent of turning to your 196 * right-facing, increasing the scale of the next respective axis, and 197 * decreasing the scale of the previous axis. 198 * 199 * @return the rotation's yaw 200 */ 201 public float getYaw() { 202 return yaw; 203 } 204 205 /** 206 * Sets the pitch of this location, measured in degrees. 207 * <ul> 208 * <li>A pitch of 0 represents level forward facing. 209 * <li>A pitch of 90 represents downward facing, or negative y 210 * direction. 211 * <li>A pitch of -90 represents upward facing, or positive y direction. 212 * <ul> 213 * Increasing pitch values the equivalent of looking down. 214 * 215 * @param pitch new incline's pitch 216 */ 217 public void setPitch(float pitch) { 218 this.pitch = pitch; 219 } 220 221 /** 222 * Gets the pitch of this location, measured in degrees. 223 * <ul> 224 * <li>A pitch of 0 represents level forward facing. 225 * <li>A pitch of 90 represents downward facing, or negative y 226 * direction. 227 * <li>A pitch of -90 represents upward facing, or positive y direction. 228 * <ul> 229 * Increasing pitch values the equivalent of looking down. 230 * 231 * @return the incline's pitch 232 */ 233 public float getPitch() { 234 return pitch; 235 } 236 237 /** 238 * Gets a unit-vector pointing in the direction that this Location is 239 * facing. 240 * 241 * @return a vector pointing the direction of this location's {@link 242 * #getPitch() pitch} and {@link #getYaw() yaw} 243 */ 244 public Vector getDirection() { 245 Vector vector = new Vector(); 246 247 double rotX = this.getYaw(); 248 double rotY = this.getPitch(); 249 250 vector.setY(-Math.sin(Math.toRadians(rotY))); 251 252 double xz = Math.cos(Math.toRadians(rotY)); 253 254 vector.setX(-xz * Math.sin(Math.toRadians(rotX))); 255 vector.setZ(xz * Math.cos(Math.toRadians(rotX))); 256 257 return vector; 258 } 259 260 /** 261 * Sets the {@link #getYaw() yaw} and {@link #getPitch() pitch} to point 262 * in the direction of the vector. 263 */ 264 public Location setDirection(Vector vector) { 265 /* 266 * Sin = Opp / Hyp 267 * Cos = Adj / Hyp 268 * Tan = Opp / Adj 269 * 270 * x = -Opp 271 * z = Adj 272 */ 273 final double _2PI = 2 * Math.PI; 274 final double x = vector.getX(); 275 final double z = vector.getZ(); 276 277 if (x == 0 && z == 0) { 278 pitch = vector.getY() > 0 ? -90 : 90; 279 return this; 280 } 281 282 double theta = Math.atan2(-x, z); 283 yaw = (float) Math.toDegrees((theta + _2PI) % _2PI); 284 285 double x2 = NumberConversions.square(x); 286 double z2 = NumberConversions.square(z); 287 double xz = Math.sqrt(x2 + z2); 288 pitch = (float) Math.toDegrees(Math.atan(-vector.getY() / xz)); 289 290 return this; 291 } 292 293 /** 294 * Adds the location by another. 295 * 296 * @see Vector 297 * @param vec The other location 298 * @return the same location 299 * @throws IllegalArgumentException for differing worlds 300 */ 301 public Location add(Location vec) { 302 if (vec == null || vec.getWorld() != getWorld()) { 303 throw new IllegalArgumentException("Cannot add Locations of differing worlds"); 304 } 305 306 x += vec.x; 307 y += vec.y; 308 z += vec.z; 309 return this; 310 } 311 312 /** 313 * Adds the location by a vector. 314 * 315 * @see Vector 316 * @param vec Vector to use 317 * @return the same location 318 */ 319 public Location add(Vector vec) { 320 this.x += vec.getX(); 321 this.y += vec.getY(); 322 this.z += vec.getZ(); 323 return this; 324 } 325 326 /** 327 * Adds the location by another. Not world-aware. 328 * 329 * @see Vector 330 * @param x X coordinate 331 * @param y Y coordinate 332 * @param z Z coordinate 333 * @return the same location 334 */ 335 public Location add(double x, double y, double z) { 336 this.x += x; 337 this.y += y; 338 this.z += z; 339 return this; 340 } 341 342 /** 343 * Subtracts the location by another. 344 * 345 * @see Vector 346 * @param vec The other location 347 * @return the same location 348 * @throws IllegalArgumentException for differing worlds 349 */ 350 public Location subtract(Location vec) { 351 if (vec == null || vec.getWorld() != getWorld()) { 352 throw new IllegalArgumentException("Cannot add Locations of differing worlds"); 353 } 354 355 x -= vec.x; 356 y -= vec.y; 357 z -= vec.z; 358 return this; 359 } 360 361 /** 362 * Subtracts the location by a vector. 363 * 364 * @see Vector 365 * @param vec The vector to use 366 * @return the same location 367 */ 368 public Location subtract(Vector vec) { 369 this.x -= vec.getX(); 370 this.y -= vec.getY(); 371 this.z -= vec.getZ(); 372 return this; 373 } 374 375 /** 376 * Subtracts the location by another. Not world-aware and 377 * orientation independent. 378 * 379 * @see Vector 380 * @param x X coordinate 381 * @param y Y coordinate 382 * @param z Z coordinate 383 * @return the same location 384 */ 385 public Location subtract(double x, double y, double z) { 386 this.x -= x; 387 this.y -= y; 388 this.z -= z; 389 return this; 390 } 391 392 /** 393 * Gets the magnitude of the location, defined as sqrt(x^2+y^2+z^2). The 394 * value of this method is not cached and uses a costly square-root 395 * function, so do not repeatedly call this method to get the location's 396 * magnitude. NaN will be returned if the inner result of the sqrt() 397 * function overflows, which will be caused if the length is too long. Not 398 * world-aware and orientation independent. 399 * 400 * @see Vector 401 * @return the magnitude 402 */ 403 public double length() { 404 return Math.sqrt(NumberConversions.square(x) + NumberConversions.square(y) + NumberConversions.square(z)); 405 } 406 407 /** 408 * Gets the magnitude of the location squared. Not world-aware and 409 * orientation independent. 410 * 411 * @see Vector 412 * @return the magnitude 413 */ 414 public double lengthSquared() { 415 return NumberConversions.square(x) + NumberConversions.square(y) + NumberConversions.square(z); 416 } 417 418 /** 419 * Get the distance between this location and another. The value of this 420 * method is not cached and uses a costly square-root function, so do not 421 * repeatedly call this method to get the location's magnitude. NaN will 422 * be returned if the inner result of the sqrt() function overflows, which 423 * will be caused if the distance is too long. 424 * 425 * @see Vector 426 * @param o The other location 427 * @return the distance 428 * @throws IllegalArgumentException for differing worlds 429 */ 430 public double distance(Location o) { 431 return Math.sqrt(distanceSquared(o)); 432 } 433 434 /** 435 * Get the squared distance between this location and another. 436 * 437 * @see Vector 438 * @param o The other location 439 * @return the distance 440 * @throws IllegalArgumentException for differing worlds 441 */ 442 public double distanceSquared(Location o) { 443 if (o == null) { 444 throw new IllegalArgumentException("Cannot measure distance to a null location"); 445 } else if (o.getWorld() == null || getWorld() == null) { 446 throw new IllegalArgumentException("Cannot measure distance to a null world"); 447 } else if (o.getWorld() != getWorld()) { 448 throw new IllegalArgumentException("Cannot measure distance between " + getWorld().getName() + " and " + o.getWorld().getName()); 449 } 450 451 return NumberConversions.square(x - o.x) + NumberConversions.square(y - o.y) + NumberConversions.square(z - o.z); 452 } 453 454 /** 455 * Performs scalar multiplication, multiplying all components with a 456 * scalar. Not world-aware. 457 * 458 * @param m The factor 459 * @see Vector 460 * @return the same location 461 */ 462 public Location multiply(double m) { 463 x *= m; 464 y *= m; 465 z *= m; 466 return this; 467 } 468 469 /** 470 * Zero this location's components. Not world-aware. 471 * 472 * @see Vector 473 * @return the same location 474 */ 475 public Location zero() { 476 x = 0; 477 y = 0; 478 z = 0; 479 return this; 480 } 481 482 @Override 483 public boolean equals(Object obj) { 484 if (obj == null) { 485 return false; 486 } 487 if (getClass() != obj.getClass()) { 488 return false; 489 } 490 final Location other = (Location) obj; 491 492 if (this.world != other.world && (this.world == null || !this.world.equals(other.world))) { 493 return false; 494 } 495 if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(other.x)) { 496 return false; 497 } 498 if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(other.y)) { 499 return false; 500 } 501 if (Double.doubleToLongBits(this.z) != Double.doubleToLongBits(other.z)) { 502 return false; 503 } 504 if (Float.floatToIntBits(this.pitch) != Float.floatToIntBits(other.pitch)) { 505 return false; 506 } 507 if (Float.floatToIntBits(this.yaw) != Float.floatToIntBits(other.yaw)) { 508 return false; 509 } 510 return true; 511 } 512 513 @Override 514 public int hashCode() { 515 int hash = 3; 516 517 hash = 19 * hash + (this.world != null ? this.world.hashCode() : 0); 518 hash = 19 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32)); 519 hash = 19 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32)); 520 hash = 19 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32)); 521 hash = 19 * hash + Float.floatToIntBits(this.pitch); 522 hash = 19 * hash + Float.floatToIntBits(this.yaw); 523 return hash; 524 } 525 526 @Override 527 public String toString() { 528 return "Location{" + "world=" + world + ",x=" + x + ",y=" + y + ",z=" + z + ",pitch=" + pitch + ",yaw=" + yaw + '}'; 529 } 530 531 /** 532 * Constructs a new {@link Vector} based on this Location 533 * 534 * @return New Vector containing the coordinates represented by this 535 * Location 536 */ 537 public Vector toVector() { 538 return new Vector(x, y, z); 539 } 540 541 @Override 542 public Location clone() { 543 try { 544 return (Location) super.clone(); 545 } catch (CloneNotSupportedException e) { 546 throw new Error(e); 547 } 548 } 549 550 /** 551 * Safely converts a double (location coordinate) to an int (block 552 * coordinate) 553 * 554 * @param loc Precise coordinate 555 * @return Block coordinate 556 */ 557 public static int locToBlock(double loc) { 558 return NumberConversions.floor(loc); 559 } 560 }