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 static java.lang.Double.parseDouble; import com.google.common.base.Preconditions; import com.supaham.commons.utils.RandomUtils; import com.supaham.commons.utils.StringUtils; import org.bukkit.Location; import org.bukkit.util.Vector; import java.util.regex.Pattern; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * Utility methods for working with {@link Vector} instances. This class contains methods such as * {@link #deserialize(String)}, {@link #serialize(Vector)}, and more. * * @since 0.1 */ public class VectorUtils { private static final Pattern DESERIALIZE = Pattern.compile("\\s*,\\s*"); /** * Deserializes a {@link String} to represent a {@link Vector}. <p> * VectorUtils.deserialize("123.0,64.0,124.5") = {@link Vector}(123.0D, 64.0D, 124.5D) <br /> * * VectorUtils.deserialize("123.0,64.0") = {@link IllegalArgumentException} too few args * <br /> * * VectorUtils.deserialize("123.0,64.0,124.5") = {@link IllegalArgumentException} too many args <br /> </p> * * @param string string representing to deserialize * * @return returns the deserialized {@link Location} * * @throws NullPointerException thrown if the world in the {@code string} is null * @throws IllegalArgumentException thrown if the {@code string} is in the incorrect format */ @Nonnull public static Vector deserialize(@Nonnull String string) throws NullPointerException { checkNotNullOrEmpty(string); String[] split = DESERIALIZE.split(string, 4); checkArgument(split.length == 3, string + " is in an invalid format."); return new Vector(parseDouble(split[0]), parseDouble(split[1]), parseDouble(split[2])); } /** * Serializes a {@link Vector} in the form of 'x,y,z'. The x, y, and z * coordinates are rounded to <em>two</em> decimal places. * * @param vector vector to serialize * * @return serialized {@code vector} */ public static String serialize(Vector vector) { if (vector == null) { return null; } return roundExact(2, vector.getX()) + "," + roundExact(2, vector.getY()) + "," + roundExact(2, vector.getZ()); } public static Vector getRandomVectorWithin(Vector min, Vector max) { return new Vector(RandomUtils.nextInt(min.getBlockX(), max.getBlockX()), RandomUtils.nextInt(min.getBlockY(), max.getBlockY()), RandomUtils.nextInt(min.getBlockY(), max.getBlockY())); } /** * Checks if a {@link Vector} is within two other {@link Vector}s. * * @param test vector to test. * @param min minimum point of a cuboid region. * @param max maximum point of a cuboid region. * * @return whether the {@code test} vector is within the {@code min} and {@code max} vectors. */ public static boolean isWithin(Vector test, Vector min, Vector max) { Preconditions.checkNotNull(test); Preconditions.checkNotNull(min); Preconditions.checkNotNull(max); double x = test.getX(); double y = test.getY(); double z = test.getZ(); return x >= min.getBlockX() && x < max.getBlockX() + 1 && y >= min.getBlockY() && y < max.getBlockY() + 1 && z >= min.getBlockZ() && z < max.getBlockZ() + 1; } /** * Checks if two {@link Vector} instances are within the same block. If both of them are null, * true is returned. * * @param o first {@link Vector} to check * @param o2 second {@link Vector} to check * * @return true if {@code o} and {@code o2} are the same block */ public static boolean isSameBlock(@Nullable Vector o, @Nullable Vector o2) { return o == null && o2 == null || (o != null && o2 != null) && (o.getBlockX() == o2.getBlockX()) && (o.getBlockY() == o2 .getBlockY()) && (o.getBlockZ() == o2.getBlockZ()); } /* ================================ * >> Relative Vectors * ================================ */ /** * Deserializes a {@link String} to represent a {@link RelativeVector}. <p> * VectorUtils.deserializeRelative("123.0, 64.0, 124.5") = {@link RelativeVector}(123.0D, 64.0D, 124.5D, false, false, false) * <br /> * VectorUtils.deserializeRelative("~123.0, 64.0, 124.5") = {@link RelativeVector}(123.0D, 64.0D, 124.5D, true, false, false) * <br /> * VectorUtils.deserializeRelative("~123.0, ~64.0, 124.5") = {@link RelativeVector}(123.0D, 64.0D, 124.5D, true, true, false) * <br /> * VectorUtils.deserializeRelative("~123.0, ~64.0, ~124.5") = {@link RelativeVector}(123.0D, 64.0D, 124.5D, true, true, true) * <br /> * * VectorUtils.deserializeRelative("123.0,64.0") = {@link IllegalArgumentException} too few args * <br /> * * VectorUtils.deserializeRelative("123.0,64.0,124.5") = {@link IllegalArgumentException} too many args <br /> </p> * * @param string string representing to deserialize * * @return returns the deserialized {@link Location} * * @throws NullPointerException thrown if the world in the {@code string} is null * @throws IllegalArgumentException thrown if the {@code string} is in the incorrect format * @see #deserialize(String) */ @Nonnull public static RelativeVector deserializeRelative(@Nonnull String string) throws NullPointerException { checkNotNullOrEmpty(string); String[] split = DESERIALIZE.split(string, 4); checkArgument(split.length == 3, string + " is in an invalid format."); boolean xRel = split[0].startsWith("~"); boolean yRel = split[1].startsWith("~"); boolean zRel = split[2].startsWith("~"); double x = parseDouble(StringUtils.defaultIfEmpty(split[0].substring(xRel ? 1 : 0), "0")); double y = parseDouble(StringUtils.defaultIfEmpty(split[1].substring(yRel ? 1 : 0), "0")); double z = parseDouble(StringUtils.defaultIfEmpty(split[2].substring(zRel ? 1 : 0), "0")); return new RelativeVector(x, y, z, xRel, yRel, zRel); } /** * Serializes a {@link RelativeVector} in the form of '~x,~y,~z'. The x, y, and z * coordinates are rounded to <em>two</em> decimal places. The tildes are only inserted if the component is relative. * * @param vector vector to serialize * * @return serialized {@code vector} */ public static String serializeRelative(RelativeVector vector) { if (vector == null) { return null; } return (vector.isXRelative() ? "~" : "") + roundExact(2, vector.getX()) + "," + (vector.isYRelative() ? "~" : "") + roundExact(2, vector.getY()) + "," + (vector.isZRelative() ? "~" : "") + roundExact(2, vector.getZ()); } }