package com.supaham.commons.bukkit.utils; import static com.google.common.base.Preconditions.checkArgument; import static com.supaham.commons.utils.NumberUtils.roundExact; import static com.supaham.commons.utils.StringUtils.checkNotNullOrEmpty; import com.google.common.base.Preconditions; import com.supaham.commons.bukkit.utils.ImmutableVector.ImmutableVectorSerializer; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.util.BlockVector; import org.bukkit.util.NumberConversions; import org.bukkit.util.Vector; import java.util.regex.Pattern; import javax.annotation.Nonnull; import javax.annotation.Nullable; import pluginbase.config.annotation.SerializeWith; import pluginbase.config.serializers.Serializer; import pluginbase.config.serializers.SerializerSet; /** * Represents an immutable version of Bukkit's {@link Vector} class; providing stability in * long-term use. This class does not actually extend {@link Vector}, to get an instance of * {@link Vector}, call {@link #toVector()}. The same applies with {@link Location}, * {@link #toLocation(World)}. * <br/> * {@link ImmutableVector}s may be cloned using {@link #ImmutableVector(ImmutableVector)} * <br/> * This class functions exactly like Bukkit's {@link Vector}, so things like {@link * #equals(Object)} utilizes an {@link #EPSILON} value of 0.000001 to ensure stability. * <p/> * <b>Note: This class supports PluginBase serialization, using {@link * ImmutableVectorSerializer}</b> * * @see #ImmutableVector(double, double, double) * @see #ImmutableVector(float, float, float) * @see #ImmutableVector(ImmutableVector) * @see #ImmutableVector(Vector) * @since 0.3.5 */ @SerializeWith(ImmutableVectorSerializer.class) public class ImmutableVector { public static final ImmutableVector ZERO = new ImmutableVector(0, 0, 0); public static final ImmutableVector ONE = new ImmutableVector(1, 1, 1); /** * Threshold for fuzzy equals(). */ private static final double EPSILON = 0.000001; protected final double x; protected final double y; protected final double z; public ImmutableVector(@Nonnull ImmutableVector vector) { Preconditions.checkNotNull(vector, "vector cannot be null."); this.x = vector.x; this.y = vector.y; this.z = vector.z; } public ImmutableVector(@Nonnull Vector vector) { Preconditions.checkNotNull(vector, "vector cannot be null."); this.x = vector.getX(); this.y = vector.getY(); this.z = vector.getZ(); } public ImmutableVector(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } public ImmutableVector(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } @Override public boolean equals(Object obj) { if (!(obj instanceof ImmutableVector)) { return false; } ImmutableVector o = (ImmutableVector) obj; return Math.abs(x - o.x) < EPSILON && Math.abs(y - o.y) < EPSILON && Math.abs(z - o.z) < EPSILON; } public boolean equals(Vector o) { return Math.abs(x - o.getX()) < EPSILON && Math.abs(y - o.getY()) < EPSILON && Math.abs(z - o.getZ()) < EPSILON; } @Override public int hashCode() { byte hash = 7; int hash1 = 79 * hash + (int) (Double.doubleToLongBits(this.x) ^ Double.doubleToLongBits(this.x) >>> 32); hash1 = 79 * hash1 + (int) (Double.doubleToLongBits(this.y) ^ Double.doubleToLongBits(this.y) >>> 32); hash1 = 79 * hash1 + (int) (Double.doubleToLongBits(this.z) ^ Double.doubleToLongBits(this.z) >>> 32); return hash1; } @Override public String toString() { return "ImmutableVector{" + "x=" + this.x + ", y=" + this.y + ", z=" + this.z + '}'; } /** * Creates a new {@link ImmutableVector} of this vector plus another {@link ImmutableVector}. If * the given vector is equals to {@link #ZERO} this same ImmutableVector instance is returned. * * @param o the other vector to add * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector add(ImmutableVector o) { return ZERO.equals(o) ? this : new ImmutableVector(this.x + o.x, this.y + o.y, this.z + o.z); } /** * Creates a new {@link ImmutableVector} of this vector plus another {@link Vector}. If * the given vector is equals to {@link #ZERO} this same ImmutableVector instance is returned. * * @param o the other vector to add * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector add(Vector o) { return ZERO.equals(o) ? this : new ImmutableVector(this.x + o.getX(), this.y + o.getY(), this.z + o.getZ()); } /** * Creates a new {@link ImmutableVector} of this vector plus the three given components. If * the given components are equal to 0 this same ImmutableVector instance is returned. * * @param x how much to add on the x axis * @param y how much to add on the y axis * @param z how much to add on the z axis * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector add(double x, double y, double z) { return x == 0 && y == 0 && z == 0 ? this : new ImmutableVector(this.x + x, this.y + y, this.z + z); } /** * Creates a new {@link ImmutableVector} of this vector plus the given int constant on all axes. * If the given constant is equal to 0, this same ImmutableVector instance is returned. * * @param a constant to add to all three components * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector add(int a) { return a == 0 ? this : new ImmutableVector(this.x + a, this.y + a, this.z + a); } /** * Creates a new {@link ImmutableVector} of this vector plus the given double constant on all * axes. If the given constant is equal to 0, this same ImmutableVector instance is returned. * * @param a constant to add to all three components * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector add(double a) { return a == 0 ? this : new ImmutableVector(this.x + a, this.y + a, this.z + a); } /** * Creates a new {@link ImmutableVector} of this vector plus the given float constant on all * axes. If the given constant is equal to 0, this same ImmutableVector instance is returned. * * @param a constant to add to all three components * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector add(float a) { return a == 0 ? this : new ImmutableVector(this.x + (double) a, this.y + (double) a, this.z + (double) a); } /** * Creates a new {@link ImmutableVector} of this vector minus another {@link ImmutableVector}. If * the given vector is equals to {@link #ZERO} this same ImmutableVector instance is returned. * * @param o the other vector to subtract * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector subtract(ImmutableVector o) { return ZERO.equals(o) ? this : new ImmutableVector(this.x - o.x, this.y - o.y, this.z - o.z); } /** * Creates a new {@link ImmutableVector} of this vector minus another {@link Vector}. If * the given vector is equals to {@link #ZERO} this same ImmutableVector instance is returned. * * @param o the other vector to subtract * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector subtract(Vector o) { return ZERO.equals(o) ? this : new ImmutableVector(this.x - o.getX(), this.y - o.getY(), this.z - o.getZ()); } /** * Creates a new {@link ImmutableVector} of this vector minus the three given components. If * the given components are equal to 0 this same ImmutableVector instance is returned. * * @param x how much to subtract on the x axis * @param y how much to subtract on the y axis * @param z how much to subtract on the z axis * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector subtract(double x, double y, double z) { return x == 0 && y == 0 && z == 0 ? this : new ImmutableVector(this.x - x, this.y - y, this.z - z); } /** * Creates a new {@link ImmutableVector} of this vector minus the given int constant on all axes. * If the given constant is equal to 0, this same ImmutableVector instance is returned. * * @param a constant to subtract from all three components * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector subtract(int a) { return a == 0 ? this : new ImmutableVector(this.x - a, this.y - a, this.z - a); } /** * Creates a new {@link ImmutableVector} of this vector minus the given double constant on all * axes. If the given constant is equal to 0, this same ImmutableVector instance is returned. * * @param a constant to subtract from all three components * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector subtract(double a) { return a == 0 ? this : new ImmutableVector(this.x - a, this.y - a, this.z - a); } /** * Creates a new {@link ImmutableVector} of this vector minus the given float constant on all * axes. If the given constant is equal to 0, this same ImmutableVector instance is returned. * * @param a constant to subtract from all three components * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector subtract(float a) { return a == 0 ? this : new ImmutableVector(this.x - (double) a, this.y - (double) a, this.z - (double) a); } /** * Creates a new {@link ImmutableVector} of this vector divided by another {@link * ImmutableVector}. If the given vector is equals to {@link #ONE} this same ImmutableVector * instance is returned. * * @param o the other vector to divide by * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector divide(ImmutableVector o) { return ONE.equals(o) ? this : new ImmutableVector(this.x / o.x, this.y / o.y, this.z / o.z); } /** * Creates a new {@link ImmutableVector} of this vector divided by another {@link Vector}. If * the given vector is equals to {@link #ONE} this same ImmutableVector instance is returned. * * @param o the other vector to divide by * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector divide(Vector o) { return ONE.equals(o) ? this : new ImmutableVector(this.x / o.getX(), this.y / o.getY(), this.z / o.getZ()); } /** * Creates a new {@link ImmutableVector} of this vector divided by the three given components. If * the given components are equal to 1 this same ImmutableVector instance is returned. * * @param x how much to divide by on the x axis * @param y how much to divide by on the y axis * @param z how much to divide by on the z axis * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector divide(double x, double y, double z) { return x == 1 && y == 1 && z == 1 ? this : new ImmutableVector(this.x / x, this.y / y, this.z / z); } /** * Creates a new {@link ImmutableVector} of this vector divided by the given int constant on all * axes. If the given constant is equal to 1, this same ImmutableVector instance is returned. * * @param d constant to divide by for all three components * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector divide(int d) { return d == 1 ? this : new ImmutableVector(this.x / d, this.y / d, this.z / d); } /** * Creates a new {@link ImmutableVector} of this vector divided by the given double constant on * all axes. If the given constant is equal to 1, this same ImmutableVector instance is returned. * * @param d constant to divide by for all three components * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector divide(double d) { return d == 1 ? this : new ImmutableVector(this.x / d, this.y / d, this.z / d); } /** * Creates a new {@link ImmutableVector} of this vector divided by the given float constant on * all axes. If the given constant is equal to 1, this same ImmutableVector instance is returned. * * @param d constant to divide by for all three components * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector divide(float d) { return d == 1 ? this : new ImmutableVector(this.x / (double) d, this.y / (double) d, this.z / (double) d); } /** * Creates a new {@link ImmutableVector} of this vector multiplied by another {@link * ImmutableVector}. If the given vector is equals to {@link #ONE} this same ImmutableVector * instance is returned. * * @param o the other vector to multiply by * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector multiply(ImmutableVector o) { return ONE.equals(o) ? this : new ImmutableVector(this.x * o.x, this.y * o.y, this.z * o.z); } /** * Creates a new {@link ImmutableVector} of this vector multiplied by another {@link Vector}. If * the given vector is equals to {@link #ONE} this same ImmutableVector instance is returned. * * @param o the other vector to multiply by * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector multiply(Vector o) { return ONE.equals(o) ? this : new ImmutableVector(this.x * o.getX(), this.y * o.getY(), this.z * o.getZ()); } /** * Creates a new {@link ImmutableVector} of this vector multiplied by the three given components. * If the given components are equal to 1 this same ImmutableVector instance is returned. * * @param x how much to multiply by on the x axis * @param y how much to multiply by on the y axis * @param z how much to multiply by on the z axis * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector multiply(double x, double y, double z) { return x == 1 && y == 1 && z == 1 ? this : new ImmutableVector(this.x * x, this.y * y, this.z * z); } /** * Creates a new {@link ImmutableVector} of this vector multiplied by the given int constant on * all axes. If the given constant is equal to 1, this same ImmutableVector instance is returned. * * @param m constant to multiply by for all three components * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector multiply(int m) { return m == 1 ? this : new ImmutableVector(this.x * m, this.y * m, this.z * m); } /** * Creates a new {@link ImmutableVector} of this vector multiplied by the given double constant * on all axes. If the given constant is equal to 1, this same ImmutableVector instance is * returned. * * @param m constant to multiply by for all three components * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector multiply(double m) { return m == 1 ? this : new ImmutableVector(this.x * m, this.y * m, this.z * m); } /** * Returns a new {@link ImmutableVector} of this vector multiplied by the given float constant * on all axes. If the given constant is equal to 1, this same ImmutableVector instance is * returned. * * @param m constant to multiply by for all three components * * @return a new {@link ImmutableVector} or the same instance if no modification was made */ public ImmutableVector multiply(float m) { return m == 1 ? this : new ImmutableVector(this.x * (double) m, this.y * (double) m, this.z * (double) m); } /** * Returns a new {@link ImmutableVector} of the midpoint vector between this vector and another * {@link ImmutableVector}. If the given constant is {@link #equals(Object)} to the given vector * {@code o}, this same ImmutableVector instance is returned. * * @param o the other vector * * @return a new {@link ImmutableVector} of the midpoint */ public ImmutableVector midpoint(ImmutableVector o) { return equals(o) ? this : new ImmutableVector((this.x + o.x) / 2.0D, (this.y + o.y) / 2.0D, (this.z + o.z) / 2.0D); } /** * Returns a new {@link ImmutableVector} of the midpoint vector between this vector and another * {@link Vector}. If the given constant is {@link #equals(Vector)} to the given vector {@code o} * , this same ImmutableVector instance is returned. * * @param o the other vector * * @return a new {@link ImmutableVector} of the midpoint */ public ImmutableVector midpoint(Vector o) { return equals(o) ? this : new ImmutableVector((this.x + o.getX()) / 2.0D, (this.y + o.getY()) / 2.0D, (this.z + o.getZ()) / 2.0D); } /** * Returns a new {@link ImmutableVector} of the cross product of this vector with another {@link * ImmutableVector}. The cross product is defined as: * <ul> * <li>x = y1 * z2 - y2 * z1 * <li>y = z1 * x2 - z2 * x1 * <li>z = x1 * y2 - x2 * y1 * </ul> * * @param o the other vector * * @return a new {@link ImmutableVector} of the cross product */ public ImmutableVector crossProduct(ImmutableVector o) { return equals(o) ? this : new ImmutableVector(this.y * o.z - o.y * this.z, this.z * o.x - o.z * this.x, this.x * o.y - o.x * this.y); } /** * Returns a new {@link ImmutableVector} of the cross product of this vector with another {@link * Vector}. The cross product is defined as: * <ul> * <li>x = y1 * z2 - y2 * z1 * <li>y = z1 * x2 - z2 * x1 * <li>z = x1 * y2 - x2 * y1 * </ul> * * @param o the other vector * * @return a new {@link ImmutableVector} of the cross product */ public ImmutableVector crossProduct(Vector o) { return equals(o) ? this : new ImmutableVector(this.y * o.getZ() - o.getY() * this.z, this.z * o.getX() - o.getZ() * this.x, this.x * o.getY() - o.getX() * this.y); } /** * Returns a new {@link ImmutableVector} of this vector to a unit vector (a vector with length * of 1). * * @return a new {@link ImmutableVector} of the unit vector */ public ImmutableVector normalize() { double length = this.length(); return new ImmutableVector(this.x / length, this.y / length, this.z / length); } /** * Returns the magnitude of this vector, defined as sqrt(x^2+y^2+z^2). The value of this method * is not cached and uses a costly square-root function, so do not repeatedly call this method to * get the vector's magnitude. NaN will be returned if the inner result of the sqrt() function * overflows, which will be caused if the length is too long. * * @return the magnitude */ public double length() { return Math.sqrt(NumberConversions.square(this.x) + NumberConversions.square(this.y) + NumberConversions.square(this.z)); } /** * Returns the magnitude of this vector squared. * * @return the squared magnitude */ public double lengthSquared() { return NumberConversions.square(this.x) + NumberConversions.square(this.y) + NumberConversions.square(this.z); } /** * Returns the distance between this vector and another {@link ImmutableVector}. The value of * this * method is not cached and uses a costly square-root function, so do not * repeatedly call this method to get the vector's magnitude. NaN will be * returned if the inner result of the sqrt() function overflows, which * will be caused if the distance is too long. * * @param o the other vector * * @return the distance */ public double distance(ImmutableVector o) { return Math.sqrt(NumberConversions.square(this.x - o.x) + NumberConversions.square(this.y - o.y) + NumberConversions.square(this.z - o.z)); } /** * Returns the distance between this vector and another {@link Vector}. The value of this * method is not cached and uses a costly square-root function, so do not * repeatedly call this method to get the vector's magnitude. NaN will be * returned if the inner result of the sqrt() function overflows, which * will be caused if the distance is too long. * * @param o the other vector * * @return the distance */ public double distance(Vector o) { return Math.sqrt(NumberConversions.square(this.x - o.getX()) + NumberConversions.square(this.y - o.getY()) + NumberConversions.square(this.z - o.getZ())); } /** * Returns the squared distance between this vector and another {@link ImmutableVector}. * * @param o the other vector * * @return the distance */ public double distanceSquared(ImmutableVector o) { return NumberConversions.square(this.x - o.x) + NumberConversions.square(this.y - o.y) + NumberConversions.square(this.z - o.z); } /** * Returns the squared distance between this vector and another {@link Vector}. * * @param o the other vector * * @return the distance */ public double distanceSquared(Vector o) { return NumberConversions.square(this.x - o.getX()) + NumberConversions.square(this.y - o.getY()) + NumberConversions.square(this.z - o.getZ()); } /** * Returns the angle between this vector and another in radians. * * @param o the other vector * * @return angle in radians */ public float angle(Vector o) { double dot = this.dot(o) / (this.length() * o.length()); return (float) Math.acos(dot); } /** * Calculates the dot product of this vector with another {@link ImmutableVector}. The dot * product is defined as x1*x2+y1*y2+z1*z2. The returned value is a scalar. * * @param o the other vector * * @return dot product */ public double dot(ImmutableVector o) { return this.x * o.x + this.y * o.y + this.z * o.z; } /** * Calculates the dot product of this vector with another {@link Vector}. The dot * product is defined as x1*x2+y1*y2+z1*z2. The returned value is a scalar. * * @param o the other vector * * @return dot product */ public double dot(Vector o) { return this.x * o.getX() + this.y * o.getY() + this.z * o.getZ(); } public boolean isSameBlock(ImmutableVector o) { return o != null && getBlockX() == o.getBlockX() && getBlockY() == o.getBlockY() && getBlockZ() == o.getBlockZ(); } /** * Returns whether this vector is in an axis-aligned bounding box of {@link ImmutableVector}s. * <b>The minimum and maximum vectors given must be truly the minimum and maximum X, Y and Z * components.</b> * * @param min minimum vector * @param max maximum vector * * @return whether this vector is in the AABB */ public boolean isInAABB(ImmutableVector min, ImmutableVector max) { return !(min == null || max == null) && this.x >= min.x && this.x <= max.x && this.y >= min.y && this.y <= max.y && this.z >= min.z && this.z <= max.z; } /** * Returns whether this vector is in an axis-aligned bounding box of {@link Vector}s. * <b>The minimum and maximum vectors given must be truly the minimum and maximum X, Y and Z * components.</b> * * @param min minimum vector * @param max maximum vector * * @return whether this vector is in the AABB */ public boolean isInAABB(Vector min, Vector max) { return !(min == null || max == null) && this.x >= min.getX() && this.x <= max.getX() && this.y >= min.getY() && this.y <= max.getY() && this.z >= min.getZ() && this.z <= max.getZ(); } /** * Returns whether this vector is within a sphere. * * @param origin sphere origin * @param radius sphere radius * * @return whether this vector is in the sphere */ public boolean isInSphere(ImmutableVector origin, double radius) { return origin != null && NumberConversions.square(origin.x - this.x) + NumberConversions.square(origin.y - this.y) + NumberConversions.square(origin.z - this.z) <= NumberConversions.square(radius); } /** * Returns whether this vector is within a sphere. * * @param origin sphere origin * @param radius sphere radius * * @return whether this vector is in the sphere */ public boolean isInSphere(Vector origin, double radius) { return origin != null && NumberConversions.square(origin.getX() - this.x) + NumberConversions.square(origin.getY() - this.y) + NumberConversions.square(origin.getZ() - this.z) <= NumberConversions .square(radius); } /** * Returns a mutable {@link Vector} using this vector's components. * * @return new mutable vector */ public Vector toVector() { return new Vector(this.x, this.y, this.z); } /** * Returns a mutable {@link BlockVector} using this vector's components. * * @return new mutable block vector */ public BlockVector toBlockVector() { return new BlockVector(this.x, this.y, this.z); } /** * Returns a mutable {@link Location} using this vector's components, with yaw and pitch as 0. * * @return new mutable location */ public Location toLocation(@Nullable World world) { return new Location(world, this.x, this.y, this.z); } /** * Returns the {@code x} component. * * @return {@code x} component. */ public double getX() { return x; } /** * Returns the {@code x} component floored (rounded down). * * @return {@code x} component. */ public int getBlockX() { return NumberConversions.floor(this.x); } /** * Returns the {@code y} component. * * @return {@code y} component. */ public double getY() { return y; } /** * Returns the {@code y} component floored (rounded down). * * @return {@code y} component. */ public int getBlockY() { return NumberConversions.floor(this.y); } /** * Returns the {@code z} component. * * @return {@code z} component. */ public double getZ() { return z; } /** * Returns the {@code z} component floored (rounded down). * * @return {@code z} component. */ public int getBlockZ() { return NumberConversions.floor(this.z); } public static final class ImmutableVectorSerializer implements Serializer<ImmutableVector> { private static final Pattern PATTERN = Pattern.compile("\\s*,\\s*"); @Nullable @Override public Object serialize(@Nullable ImmutableVector object, @Nonnull SerializerSet serializerSet) { if (object == null) { return null; } return roundExact(3, object.getX()) + "," + roundExact(3, object.getY()) + "," + roundExact(3, object.getZ()); } @Nullable @Override public ImmutableVector deserialize(@Nullable Object serialized, @Nonnull Class wantedType, @Nonnull SerializerSet serializerSet) { if (serialized == null) { return null; } checkNotNullOrEmpty(serialized.toString(), "serialized string"); String[] split = PATTERN.split(serialized.toString(), 3); checkArgument(split.length == 3, "string is in an invalid format."); return new ImmutableVector(Double.parseDouble(split[0]), Double.parseDouble(split[1]), Double.parseDouble(split[2])); } } }