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    }