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 }