From 33e641d5f2b6d3b23dec31ea7667668d640f901c Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 26 Apr 2014 19:48:21 -0700 Subject: [PATCH] Added a new Location and added Entity.getLocation(). --- .../worldedit/bukkit/BukkitCommandSender.java | 16 +- .../sk89q/worldedit/bukkit/BukkitPlayer.java | 9 + .../sk89q/worldedit/forge/ForgePlayer.java | 9 + .../java/com/sk89q/worldedit/Location.java | 4 + .../java/com/sk89q/worldedit/WorldVector.java | 5 +- .../com/sk89q/worldedit/entity/Entity.java | 13 +- .../com/sk89q/worldedit/util/Location.java | 300 ++++++++++++++++++ .../com/sk89q/worldedit/util/Vectors.java | 65 ++++ .../sk89q/worldedit/util/LocationTest.java | 156 +++++++++ 9 files changed, 565 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/sk89q/worldedit/util/Location.java create mode 100644 src/main/java/com/sk89q/worldedit/util/Vectors.java create mode 100644 src/test/java/com/sk89q/worldedit/util/LocationTest.java diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java index 508fe4e32..e2067fd90 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java @@ -19,17 +19,12 @@ package com.sk89q.worldedit.bukkit; +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.util.Location; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import com.sk89q.worldedit.LocalPlayer; -import com.sk89q.worldedit.LocalWorld; -import com.sk89q.worldedit.PlayerNeededException; -import com.sk89q.worldedit.ServerInterface; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.WorldVector; -import com.sk89q.worldedit.extent.inventory.BlockBag; - public class BukkitCommandSender extends LocalPlayer { private CommandSender sender; private WorldEditPlugin plugin; @@ -97,6 +92,11 @@ public class BukkitCommandSender extends LocalPlayer { throw new PlayerNeededException(); } + @Override + public Location getLocation() { + throw new PlayerNeededException(); + } + @Override public WorldVector getPosition() { throw new PlayerNeededException(); diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java index 2bbc55a71..1499cd09f 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit.bukkit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.util.Vectors; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -168,4 +169,12 @@ public class BukkitPlayer extends LocalPlayer { setPosition(new Vector(x + 0.5, y, z + 0.5)); player.setFlying(true); } + + @Override + public com.sk89q.worldedit.util.Location getLocation() { + Location nativeLocation = player.getLocation(); + Vector position = BukkitUtil.toVector(nativeLocation); + Vector direction = Vectors.fromEulerDeg(nativeLocation.getYaw(), nativeLocation.getPitch()); + return new com.sk89q.worldedit.util.Location(getWorld(), position, direction); + } } diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java b/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java index dec4dd7b6..01e178f71 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java @@ -27,6 +27,8 @@ import com.sk89q.worldedit.WorldVector; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.internal.LocalWorldAdapter; import com.sk89q.worldedit.internal.cui.CUIEvent; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.Vectors; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemStack; import net.minecraft.network.packet.Packet250CustomPayload; @@ -49,6 +51,13 @@ public class ForgePlayer extends LocalPlayer { return this.player.username; } + @Override + public Location getLocation() { + Vector position = new Vector(this.player.posX, this.player.posY, this.player.posZ); + Vector direction = Vectors.fromEulerDeg(this.player.cameraYaw, this.player.cameraPitch); + return new Location(ForgeWorldEdit.inst.getWorld(this.player.worldObj), position, direction); + } + public WorldVector getPosition() { return new WorldVector(LocalWorldAdapter.wrap(ForgeWorldEdit.inst.getWorld(this.player.worldObj)), this.player.posX, this.player.posY, this.player.posZ); } diff --git a/src/main/java/com/sk89q/worldedit/Location.java b/src/main/java/com/sk89q/worldedit/Location.java index ccf3a338d..5097fa414 100644 --- a/src/main/java/com/sk89q/worldedit/Location.java +++ b/src/main/java/com/sk89q/worldedit/Location.java @@ -19,6 +19,10 @@ package com.sk89q.worldedit; +/** + * @deprecated Use {@link com.sk89q.worldedit.util.Location} wherever possible + */ +@Deprecated public class Location { private final LocalWorld world; private final Vector position; diff --git a/src/main/java/com/sk89q/worldedit/WorldVector.java b/src/main/java/com/sk89q/worldedit/WorldVector.java index 090358769..2e901f823 100644 --- a/src/main/java/com/sk89q/worldedit/WorldVector.java +++ b/src/main/java/com/sk89q/worldedit/WorldVector.java @@ -20,10 +20,9 @@ package com.sk89q.worldedit; /** - * A vector with a world component. - * - * @author sk89q + * @deprecated Use {@link com.sk89q.worldedit.util.Location} wherever possible */ +@Deprecated public class WorldVector extends Vector { /** * Represents the world. diff --git a/src/main/java/com/sk89q/worldedit/entity/Entity.java b/src/main/java/com/sk89q/worldedit/entity/Entity.java index 73fc3a56e..950557bc3 100644 --- a/src/main/java/com/sk89q/worldedit/entity/Entity.java +++ b/src/main/java/com/sk89q/worldedit/entity/Entity.java @@ -19,8 +19,12 @@ package com.sk89q.worldedit.entity; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.PlayerDirection; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldVector; +import com.sk89q.worldedit.WorldVectorFace; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.World; /** @@ -166,6 +170,13 @@ public interface Entity { */ PlayerDirection getCardinalDirection(); + /** + * Get the location of this entity. + * + * @return the location of the entity + */ + Location getLocation(); + /** * Get the actor's position. *

diff --git a/src/main/java/com/sk89q/worldedit/util/Location.java b/src/main/java/com/sk89q/worldedit/util/Location.java new file mode 100644 index 000000000..e5ae26909 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/Location.java @@ -0,0 +1,300 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.world.World; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Represents a location in a world with has a direction. + *

+ * Like {@code Vectors}, {@code Locations} are immutable and mutator methods + * will create a new copy. + *

+ * At the moment, but this may change in the future, {@link #hashCode()} and + * {@link #equals(Object)} are subject to minor differences caused by + * floating point errors. + */ +public class Location { + + private final World world; + private final Vector position; + private final Vector direction; + + /** + * Create a new instance in the given world at 0, 0, 0 with a + * direction vector of 0, 0, 0. + * + * @param world the world + */ + public Location(World world) { + this(world, new Vector(), new Vector()); + } + + /** + * Create a new instance in the given world with the given coordinates + * with a direction vector of 0, 0, 0. + * + * @param world the world + * @param x the X coordinate + * @param y the Y coordinate + * @param z the Z coordinate + */ + public Location(World world, double x, double y, double z) { + this(world, new Vector(x, y, z), new Vector()); + } + + /** + * Create a new instance in the given world with the given position + * vector and a direction vector of 0, 0, 0. + * + * @param world the world + * @param position the position vector + */ + public Location(World world, Vector position) { + this(world, position, new Vector()); + } + + /** + * Create a new instance in the given world with the given coordinates + * and the given direction vector. + * + * @param world the world + * @param x the X coordinate + * @param y the Y coordinate + * @param z the Z coordinate + * @param direction the direction vector + */ + public Location(World world, double x, double y, double z, Vector direction) { + this(world, new Vector(x, y, z), direction); + } + + /** + * Create a new instance in the given world with the given position vector + * and the given direction vector. + * + * @param world the world + * @param position the position vector + * @param direction the direction vector + */ + public Location(World world, Vector position, Vector direction) { + checkNotNull(world); + checkNotNull(position); + checkNotNull(direction); + this.world = world; + this.position = position; + this.direction = direction; + } + + /** + * Get the world. + * + * @return the world + */ + public World getWorld() { + return world; + } + + /** + * Create a clone of this object with the given world. + * + * @param world the new world + * @return the new instance + */ + public Location setWorld(World world) { + return new Location(world, position, getDirection()); + } + + /** + * Get the direction. + *

+ * The direction vector may be a null vector. It may or may not + * be a unit vector. + * + * @return the direction + */ + public Vector getDirection() { + return direction; + } + + /** + * Create a clone of this object with the given direction. + * + * @param direction the new direction + * @return the new instance + */ + public Location setDirection(Vector direction) { + return new Location(world, position, direction); + } + + /** + * Get a {@link Vector} form of this location's position. + * + * @return a vector + */ + public Vector toVector() { + return position; + } + + /** + * Get the X component of the position vector. + * + * @return the X component + */ + public double getX() { + return position.getX(); + } + + /** + * Get the rounded X component of the position vector. + * + * @return the rounded X component + */ + public int getBlockX() { + return position.getBlockX(); + } + + /** + * Return a copy of this object with the X component of the new object + * set to the given value. + * + * @param x the new value for the X component + * @return a new immutable instance + */ + public Location setX(double x) { + return new Location(world, position.setX(x), direction); + } + + /** + * Return a copy of this object with the X component of the new object + * set to the given value. + * + * @param x the new value for the X component + * @return a new immutable instance + */ + public Location setX(int x) { + return new Location(world, position.setX(x), direction); + } + + /** + * Get the Y component of the position vector. + * + * @return the Y component + */ + public double getY() { + return position.getY(); + } + + /** + * Get the rounded Y component of the position vector. + * + * @return the rounded Y component + */ + public int getBlockY() { + return position.getBlockY(); + } + + /** + * Return a copy of this object with the Y component of the new object + * set to the given value. + * + * @param y the new value for the Y component + * @return a new immutable instance + */ + public Location setY(double y) { + return new Location(world, position.setY(y), direction); + } + + /** + * Return a copy of this object with the Y component of the new object + * set to the given value. + * + * @param y the new value for the Y component + * @return a new immutable instance + */ + public Location setY(int y) { + return new Location(world, position.setY(y), direction); + } + + /** + * Get the Z component of the position vector. + * + * @return the Z component + */ + public double getZ() { + return position.getZ(); + } + + /** + * Get the rounded Z component of the position vector. + * + * @return the rounded Z component + */ + public int getBlockZ() { + return position.getBlockZ(); + } + + /** + * Return a copy of this object with the Z component of the new object + * set to the given value. + * + * @param z the new value for the Y component + * @return a new immutable instance + */ + public Location setZ(double z) { + return new Location(world, position.setZ(z), direction); + } + + /** + * Return a copy of this object with the Z component of the new object + * set to the given value. + * + * @param z the new value for the Y component + * @return a new immutable instance + */ + public Location setZ(int z) { + return new Location(world, position.setZ(z), direction); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Location location = (Location) o; + + if (!direction.equals(location.direction)) return false; + if (!position.equals(location.position)) return false; + if (!world.equals(location.world)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = world.hashCode(); + result = 31 * result + position.hashCode(); + result = 31 * result + direction.hashCode(); + return result; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/Vectors.java b/src/main/java/com/sk89q/worldedit/util/Vectors.java new file mode 100644 index 000000000..277bf9c3d --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/Vectors.java @@ -0,0 +1,65 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util; + +import com.sk89q.worldedit.Vector; + +/** + * Utility methods for {@link Vector}s. + */ +public final class Vectors { + + private Vectors() { + } + + /** + * Create a new {@link Vector} using Euler angles specified in degrees, + * but with no roll. + * + * @param yaw the yaw + * @param pitch the pitch + * @return a new {@link Vector} + */ + public static Vector fromEulerDeg(double yaw, double pitch) { + final double yawRadians = Math.toRadians(yaw); + final double pitchRadians = Math.toRadians(pitch); + return fromEulerRad(yawRadians, pitchRadians); + } + + /** + * Create a new {@link Vector} using Euler angles specified in radians, + * but with no roll. + * + * @param yaw the yaw + * @param pitch the pitch + * @return a new {@link Vector} + */ + public static Vector fromEulerRad(double yaw, double pitch) { + final double y = -Math.sin(pitch); + + final double h = Math.cos(pitch); + + final double x = -h * Math.sin(yaw); + final double z = h * Math.cos(yaw); + + return new Vector(x, y, z); + } + +} diff --git a/src/test/java/com/sk89q/worldedit/util/LocationTest.java b/src/test/java/com/sk89q/worldedit/util/LocationTest.java new file mode 100644 index 000000000..ecf3bc327 --- /dev/null +++ b/src/test/java/com/sk89q/worldedit/util/LocationTest.java @@ -0,0 +1,156 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.world.World; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +/** + * Tests {@link Location}. + */ +public class LocationTest { + + private static final int TEST_VALUE = 10; + private static final double EPSILON = 0.0001; + + @Test + public void testGetWorld() throws Exception { + World world = mock(World.class); + Location location = new Location(world); + assertEquals(world, location.getWorld()); + } + + @Test + public void testSetWorld() throws Exception { + World world1 = mock(World.class); + World world2 = mock(World.class); + Location location1 = new Location(world1); + Location location2 = location1.setWorld(world2); + assertEquals(world1, location1.getWorld()); + assertEquals(world2, location2.getWorld()); + } + + @Test + public void testGetDirection() throws Exception { + World world = mock(World.class); + Vector direction = new Vector(1, 1, 1); + Location location = new Location(world, new Vector(), direction); + assertEquals(direction, location.getDirection()); + } + + @Test + public void testSetDirection() throws Exception { + World world = mock(World.class); + Vector direction1 = new Vector(1, 1, 1); + Vector direction2 = new Vector(2, 2, 2); + Location location1 = new Location(world, new Vector(), direction1); + Location location2 = location1.setDirection(direction2); + assertEquals(direction1, location1.getDirection()); + assertEquals(direction2, location2.getDirection()); + } + + @Test + public void testToVector() throws Exception { + World world = mock(World.class); + Vector position = new Vector(1, 1, 1); + Location location = new Location(world, position); + assertEquals(position, location.toVector()); + } + + @Test + public void testGetX() throws Exception { + World world = mock(World.class); + Location location = new Location(world, new Vector(TEST_VALUE, 0, 0)); + assertEquals(TEST_VALUE, location.getX(), EPSILON); + } + + @Test + public void testGetBlockX() throws Exception { + World world = mock(World.class); + Location location = new Location(world, new Vector(TEST_VALUE, 0, 0)); + assertEquals(TEST_VALUE, location.getBlockX()); + } + + @Test + public void testSetX() throws Exception { + World world = mock(World.class); + Location location1 = new Location(world, new Vector()); + Location location2 = location1.setX(TEST_VALUE); + assertEquals(0, location1.getX(), EPSILON); + assertEquals(TEST_VALUE, location2.getX(), EPSILON); + assertEquals(0, location2.getY(), EPSILON); + assertEquals(0, location2.getZ(), EPSILON); + } + + @Test + public void testGetY() throws Exception { + World world = mock(World.class); + Location location = new Location(world, new Vector(0, TEST_VALUE, 0)); + assertEquals(TEST_VALUE, location.getY(), EPSILON); + } + + @Test + public void testGetBlockY() throws Exception { + World world = mock(World.class); + Location location = new Location(world, new Vector(0, TEST_VALUE, 0)); + assertEquals(TEST_VALUE, location.getBlockY()); + } + + @Test + public void testSetY() throws Exception { + World world = mock(World.class); + Location location1 = new Location(world, new Vector()); + Location location2 = location1.setY(TEST_VALUE); + assertEquals(0, location1.getY(), EPSILON); + assertEquals(0, location2.getX(), EPSILON); + assertEquals(TEST_VALUE, location2.getY(), EPSILON); + assertEquals(0, location2.getZ(), EPSILON); + } + + @Test + public void testGetZ() throws Exception { + World world = mock(World.class); + Location location = new Location(world, new Vector(0, 0, TEST_VALUE)); + assertEquals(TEST_VALUE, location.getZ(), EPSILON); + } + + @Test + public void testGetBlockZ() throws Exception { + World world = mock(World.class); + Location location = new Location(world, new Vector(0, 0, TEST_VALUE)); + assertEquals(TEST_VALUE, location.getBlockZ()); + } + + @Test + public void testSetZ() throws Exception { + World world = mock(World.class); + Location location1 = new Location(world, new Vector()); + Location location2 = location1.setZ(TEST_VALUE); + assertEquals(0, location1.getZ(), EPSILON); + assertEquals(0, location2.getX(), EPSILON); + assertEquals(0, location2.getY(), EPSILON); + assertEquals(TEST_VALUE, location2.getZ(), EPSILON); + } + +}