Add Position

This commit is contained in:
Jake Potrebic 2022-03-20 10:42:28 -07:00
parent 23095683d0
commit 30e04bfa2f
6 changed files with 385 additions and 1 deletions

View File

@ -0,0 +1,100 @@
package io.papermc.paper.math;
import org.bukkit.Axis;
import org.bukkit.block.BlockFace;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.NullMarked;
/**
* A position represented with integers.
* <p>
* <b>May see breaking changes until Experimental annotation is removed.</b>
*
* @see FinePosition
*/
@ApiStatus.Experimental
@NullMarked
public interface BlockPosition extends Position {
@Override
default double x() {
return this.blockX();
}
@Override
default double y() {
return this.blockY();
}
@Override
default double z() {
return this.blockZ();
}
@Override
default boolean isBlock() {
return true;
}
@Override
default boolean isFine() {
return false;
}
@Override
default BlockPosition toBlock() {
return this;
}
@Override
default BlockPosition offset(final int x, final int y, final int z) {
return x == 0 && y == 0 && z == 0 ? this : new BlockPositionImpl(this.blockX() + x, this.blockY() + y, this.blockZ() + z);
}
@Override
default FinePosition offset(final double x, final double y, final double z) {
return new FinePositionImpl(this.blockX() + x, this.blockY() + y, this.blockZ() + z);
}
/**
* Returns a block position offset by 1 in the direction specified.
*
* @param blockFace the block face to offset towards
* @return the offset block position
*/
@Contract(value = "_ -> new", pure = true)
default BlockPosition offset(final BlockFace blockFace) {
return this.offset(blockFace, 1);
}
/**
* Returns a block position offset in the direction specified
* multiplied by the amount.
*
* @param blockFace the block face to offset towards
* @param amount the number of times to move in that direction
* @return the offset block position
*/
@Contract(pure = true)
default BlockPosition offset(final BlockFace blockFace, final int amount) {
return amount == 0 ? this : new BlockPositionImpl(this.blockX() + (blockFace.getModX() * amount), this.blockY() + (blockFace.getModY() * amount), this.blockZ() + (blockFace.getModZ() * amount));
}
/**
* Returns a block position offset by the amount along
* the specified axis.
*
* @param axis the axis to offset along
* @param amount the amount to offset along that axis
* @return the offset block position
*/
@Contract(pure = true)
default BlockPosition offset(final Axis axis, final int amount) {
return amount == 0 ? this : switch (axis) {
case X -> new BlockPositionImpl(this.blockX() + amount, this.blockY(), this.blockZ());
case Y -> new BlockPositionImpl(this.blockX(), this.blockY() + amount, this.blockZ());
case Z -> new BlockPositionImpl(this.blockX(), this.blockY(), this.blockZ() + amount);
};
}
}

View File

@ -0,0 +1,4 @@
package io.papermc.paper.math;
record BlockPositionImpl(int blockX, int blockY, int blockZ) implements BlockPosition {
}

View File

@ -0,0 +1,57 @@
package io.papermc.paper.math;
import org.bukkit.util.NumberConversions;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* A position represented with doubles.
* <p>
* <b>May see breaking changes until Experimental annotation is removed.</b>
*
* @see BlockPosition
*/
@ApiStatus.Experimental
@NullMarked
public interface FinePosition extends Position {
@Override
default int blockX() {
return NumberConversions.floor(this.x());
}
@Override
default int blockY() {
return NumberConversions.floor(this.y());
}
@Override
default int blockZ() {
return NumberConversions.floor(this.z());
}
@Override
default boolean isBlock() {
return false;
}
@Override
default boolean isFine() {
return true;
}
@Override
default BlockPosition toBlock() {
return new BlockPositionImpl(this.blockX(), this.blockY(), this.blockZ());
}
@Override
default FinePosition offset(final int x, final int y, final int z) {
return this.offset((double) x, y, z);
}
@Override
default FinePosition offset(final double x, final double y, final double z) {
return x == 0.0 && y == 0.0 && z == 0.0 ? this : new FinePositionImpl(this.x() + x, this.y() + y, this.z() + z);
}
}

View File

@ -0,0 +1,4 @@
package io.papermc.paper.math;
record FinePositionImpl(double x, double y, double z) implements FinePosition {
}

View File

@ -0,0 +1,192 @@
package io.papermc.paper.math;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.NullMarked;
/**
* Common interface for {@link FinePosition} and {@link BlockPosition}.
* <p>
* <b>May see breaking changes until Experimental annotation is removed.</b>
*/
@ApiStatus.Experimental
@NullMarked
public interface Position {
FinePosition FINE_ZERO = new FinePositionImpl(0, 0, 0);
BlockPosition BLOCK_ZERO = new BlockPositionImpl(0, 0, 0);
/**
* Gets the block x value for this position
*
* @return the block x value
*/
int blockX();
/**
* Gets the block x value for this position
*
* @return the block x value
*/
int blockY();
/**
* Gets the block x value for this position
*
* @return the block x value
*/
int blockZ();
/**
* Gets the x value for this position
*
* @return the x value
*/
double x();
/**
* Gets the y value for this position
*
* @return the y value
*/
double y();
/**
* Gets the z value for this position
*
* @return the z value
*/
double z();
/**
* Checks of this position represents a {@link BlockPosition}
*
* @return true if block
*/
boolean isBlock();
/**
* Checks if this position represents a {@link FinePosition}
*
* @return true if fine
*/
boolean isFine();
/**
* Checks if each component of this position is finite.
*/
default boolean isFinite() {
return Double.isFinite(this.x()) && Double.isFinite(this.y()) && Double.isFinite(this.z());
}
/**
* Returns a position offset by the specified amounts.
*
* @param x x value to offset
* @param y y value to offset
* @param z z value to offset
* @return the offset position
*/
Position offset(int x, int y, int z);
/**
* Returns a position offset by the specified amounts.
*
* @param x x value to offset
* @param y y value to offset
* @param z z value to offset
* @return the offset position
*/
FinePosition offset(double x, double y, double z);
/**
* Returns a new position at the center of the block position this represents
*
* @return a new center position
*/
@Contract(value = "-> new", pure = true)
default FinePosition toCenter() {
return new FinePositionImpl(this.blockX() + 0.5, this.blockY() + 0.5, this.blockZ() + 0.5);
}
/**
* Returns the block position of this position
* or itself if it already is a block position
*
* @return the block position
*/
@Contract(pure = true)
BlockPosition toBlock();
/**
* Converts this position to a vector
*
* @return a new vector
*/
@Contract(value = "-> new", pure = true)
default Vector toVector() {
return new Vector(this.x(), this.y(), this.z());
}
/**
* Creates a new location object at this position with the specified world
*
* @param world the world for the location object
* @return a new location
*/
@Contract(value = "_ -> new", pure = true)
default Location toLocation(final World world) {
return new Location(world, this.x(), this.y(), this.z());
}
/**
* Creates a position at the coordinates
*
* @param x x coord
* @param y y coord
* @param z z coord
* @return a position with those coords
*/
@Contract(value = "_, _, _ -> new", pure = true)
static BlockPosition block(final int x, final int y, final int z) {
return new BlockPositionImpl(x, y, z);
}
/**
* Creates a position from the location.
*
* @param location the location to copy the position of
* @return a new position at that location
*/
@Contract(value = "_ -> new", pure = true)
static BlockPosition block(final Location location) {
return new BlockPositionImpl(location.getBlockX(), location.getBlockY(), location.getBlockZ());
}
/**
* Creates a position at the coordinates
*
* @param x x coord
* @param y y coord
* @param z z coord
* @return a position with those coords
*/
@Contract(value = "_, _, _ -> new", pure = true)
static FinePosition fine(final double x, final double y, final double z) {
return new FinePositionImpl(x, y, z);
}
/**
* Creates a position from the location.
*
* @param location the location to copy the position of
* @return a new position at that location
*/
@Contract(value = "_ -> new", pure = true)
static FinePosition fine(final Location location) {
return new FinePositionImpl(location.getX(), location.getY(), location.getZ());
}
}

View File

@ -20,7 +20,7 @@ import org.jetbrains.annotations.Nullable;
* magnitude than 360 are valid, but may be normalized to any other equivalent
* representation by the implementation.
*/
public class Location implements Cloneable, ConfigurationSerializable {
public class Location implements Cloneable, ConfigurationSerializable, io.papermc.paper.math.FinePosition { // Paper
private Reference<World> world;
private double x;
private double y;
@ -706,4 +706,31 @@ public class Location implements Cloneable, ConfigurationSerializable {
}
return pitch;
}
// Paper - add Position
@Override
public double x() {
return this.getX();
}
@Override
public double y() {
return this.getY();
}
@Override
public double z() {
return this.getZ();
}
@Override
public boolean isFinite() {
return io.papermc.paper.math.FinePosition.super.isFinite() && Float.isFinite(this.getYaw()) && Float.isFinite(this.getPitch());
}
@Override
public @NotNull Location toLocation(@NotNull World world) {
return new Location(world, this.x(), this.y(), this.z(), this.getYaw(), this.getPitch());
}
// Paper end
}