package com.bergerkiller.bukkit.common.utils; import java.util.EnumMap; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.util.Vector; public class FaceUtil { public static final BlockFace[] AXIS = new BlockFace[4]; public static final BlockFace[] RADIAL = {BlockFace.WEST, BlockFace.NORTH_WEST, BlockFace.NORTH, BlockFace.NORTH_EAST, BlockFace.EAST, BlockFace.SOUTH_EAST, BlockFace.SOUTH, BlockFace.SOUTH_WEST}; public static final BlockFace[] BLOCK_SIDES = {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST, BlockFace.UP, BlockFace.DOWN}; public static final BlockFace[] ATTACHEDFACES = {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST, BlockFace.UP}; public static final BlockFace[] ATTACHEDFACESDOWN = BLOCK_SIDES; private static final EnumMap<BlockFace, Integer> notches = new EnumMap<BlockFace, Integer>(BlockFace.class); static { for (int i = 0; i < RADIAL.length; i++) { notches.put(RADIAL[i], i); } for (int i = 0; i < AXIS.length; i++) { AXIS[i] = RADIAL[i << 1]; } } /** * Gets the Notch integer representation of a BlockFace<br> * <b>These are the horizontal faces, which exclude up and down</b> * * @param face to get * @return Notch of the face */ public static int faceToNotch(BlockFace face) { Integer notch = notches.get(face); return notch == null ? 0 : notch.intValue(); } /** * Checks whether a given face is an offset along the X-axis * * @param face to check * @return True if it is along the X-axis, False if not */ public static boolean isAlongX(BlockFace face) { return face.getModX() != 0 && face.getModZ() == 0; } /** * Checks whether a given face is an offset along the Y-axis * * @param face to check * @return True if it is along the Y-axis, False if not */ public static boolean isAlongY(BlockFace face) { return isVertical(face); } /** * Checks whether a given face is an offset along the Z-axis * * @param face to check * @return True if it is along the Z-axis, False if not */ public static boolean isAlongZ(BlockFace face) { return face.getModZ() != 0 && face.getModX() == 0; } /** * Gets the Block Face at the notch index specified<br> * <b>These are the horizontal faces, which exclude up and down</b> * * @param notch to get * @return BlockFace of the notch */ public static BlockFace notchToFace(int notch) { return RADIAL[notch & 0x7]; } /** * Rotates a given Block Face horizontally * * @param from face * @param notchCount to rotate at * @return rotated face */ public static BlockFace rotate(BlockFace from, int notchCount) { return notchToFace(faceToNotch(from) + notchCount); } /** * Combines two non-subcardinal faces into one face<br> * - NORTH and WEST returns NORTH_WEST<br> * - NORTH and SOUTH returns NORTH (not possible to combine) * * @param from face to combined * @param to face to combined * @return the combined face */ public static BlockFace combine(BlockFace from, BlockFace to) { if (from == BlockFace.NORTH) { if (to == BlockFace.WEST) { return BlockFace.NORTH_WEST; } else if (to == BlockFace.EAST) { return BlockFace.NORTH_EAST; } } else if (from == BlockFace.EAST) { if (to == BlockFace.NORTH) { return BlockFace.NORTH_EAST; } else if (to == BlockFace.SOUTH) { return BlockFace.SOUTH_EAST; } } else if (from == BlockFace.SOUTH) { if (to == BlockFace.WEST) { return BlockFace.SOUTH_WEST; } else if (to == BlockFace.EAST) { return BlockFace.SOUTH_EAST; } } else if (from == BlockFace.WEST) { if (to == BlockFace.NORTH) { return BlockFace.NORTH_WEST; } else if (to == BlockFace.SOUTH) { return BlockFace.SOUTH_WEST; } } return from; } /** * Subtracts two faces * * @param face1 * @param face2 to subtract from face1 * @return Block Face result ofthe subtraction */ public static BlockFace subtract(BlockFace face1, BlockFace face2) { return notchToFace(faceToNotch(face1) - faceToNotch(face2)); } /** * Adds two faces together * * @param face1 * @param face2 to add to face1 * @return Block Face result of the addition */ public static BlockFace add(BlockFace face1, BlockFace face2) { return notchToFace(faceToNotch(face1) + faceToNotch(face2)); } /** * Gets all the individual faces represented by a Block Face<br> * - NORTH_WEST returns NORTH and WEST<br> * - NORTH returns NORTH and SOUTH<br> * * @param main face to get the faces for * @return an array of length 2 containing all the faces */ public static BlockFace[] getFaces(BlockFace main) { switch (main) { case SOUTH_EAST: return new BlockFace[] {BlockFace.SOUTH, BlockFace.EAST}; case SOUTH_WEST: return new BlockFace[] {BlockFace.SOUTH, BlockFace.WEST}; case NORTH_EAST: return new BlockFace[] {BlockFace.NORTH, BlockFace.EAST}; case NORTH_WEST: return new BlockFace[] {BlockFace.NORTH, BlockFace.WEST}; default: return new BlockFace[] {main, main.getOppositeFace()}; } } /** * Gets the direction a minecart faces when on a given track * * @param raildirection of the rails * @return minecart direction */ public static BlockFace getRailsCartDirection(final BlockFace raildirection) { switch (raildirection) { case NORTH_EAST: case SOUTH_WEST: return BlockFace.NORTH_WEST; case NORTH_WEST: case SOUTH_EAST: return BlockFace.SOUTH_WEST; default: return raildirection; } } /** * Gets the rail direction from a Direction<br> * NORTH becomes SOUTH and WEST becomes EAST * * @param direction to convert * @return rail direction */ public static BlockFace toRailsDirection(BlockFace direction) { switch (direction) { case NORTH: return BlockFace.SOUTH; case WEST: return BlockFace.EAST; default: return direction; } } /** * Gets whether a given Block Face is sub-cardinal (such as NORTH_WEST) * * @param face to check * @return True if sub-cardinal, False if not */ public static boolean isSubCardinal(final BlockFace face) { switch (face) { case NORTH_EAST: case SOUTH_EAST: case SOUTH_WEST: case NORTH_WEST: return true; default: return false; } } /** * Checks whether a face is up or down * * @param face to check * @return True if it is UP or DOWN */ public static boolean isVertical(BlockFace face) { return face == BlockFace.UP || face == BlockFace.DOWN; } /** * Gets the BlockFace.UP or BlockFace.DOWN constant based on the up parameter * * @param up parameter * @return UP if up is true, DOWN if up is false */ public static BlockFace getVertical(boolean up) { return up ? BlockFace.UP : BlockFace.DOWN; } /** * Gets the BlockFace.UP or BlockFace.DOWN based on the delta-y parameter * * @param dy parameter * @return UP if dy >= 0, DOWN if dy < 0 */ public static BlockFace getVertical(double dy) { return getVertical(dy >= 0.0); } /** * Gets whether two faces have a sub-cardinal difference or less * * @param face1 to check * @param face2 to check * @return True if the difference <= 45 degrees */ public static boolean hasSubDifference(final BlockFace face1, final BlockFace face2) { return getFaceYawDifference(face1, face2) <= 45; } /** * Gets the Vector direction from a Block Face * * @param face to use * @param length of the vector * @return Vector of the direction and length */ public static Vector faceToVector(BlockFace face, double length) { return faceToVector(face).multiply(length); } /** * Gets the Vector direction from a Block Face * * @param face to use * @return Vector of the direction and length 1 */ public static Vector faceToVector(BlockFace face) { return new Vector(face.getModX(), face.getModY(), face.getModZ()); } /** * Gets the Block Face direction to go from one point to another * * @param from point * @param to point * @param useSubCardinalDirections setting * @return the Block Face of the direction */ public static BlockFace getDirection(Location from, Location to, boolean useSubCardinalDirections) { return getDirection(to.getX() - from.getX(), to.getZ() - from.getZ(), useSubCardinalDirections); } /** * Gets the Block Face direction to go from one block to another * * @param from block * @param to block * @param useSubCardinalDirections setting * @return the Block Face of the direction */ public static BlockFace getDirection(Block from, Block to, boolean useSubCardinalDirections) { return getDirection(to.getX() - from.getX(), to.getZ() - from.getZ(), useSubCardinalDirections); } /** * Gets the Block Face direction to go into the movement vector direction * * @param movement vector * @return the Block Face of the direction */ public static BlockFace getDirection(Vector movement) { return getDirection(movement, true); } /** * Gets the Block Face direction to go into the movement vector direction * * @param movement vector * @param useSubCardinalDirections setting * @return the Block Face of the direction */ public static BlockFace getDirection(Vector movement, boolean useSubCardinalDirections) { return getDirection(movement.getX(), movement.getZ(), useSubCardinalDirections); } /** * Gets the Block Face direction to go into the movement vector direction * * @param dx vector axis * @param dz vector axis * @param useSubCardinalDirections setting * @return the Block Face of the direction */ public static BlockFace getDirection(final double dx, final double dz, boolean useSubCardinalDirections) { return yawToFace(MathUtil.getLookAtYaw(dx, dz), useSubCardinalDirections); } /** * Gets the yaw angle in degrees difference between two Block Faces * @param face1 * @param face2 * @return angle in degrees */ public static int getFaceYawDifference(BlockFace face1, BlockFace face2) { return MathUtil.getAngleDifference(faceToYaw(face1), faceToYaw(face2)); } /** * Gets the co-sinus value from a Block Face treated as an Angle * * @param face to get the co-sinus value from * @return co-sinus value */ public static double cos(final BlockFace face) { switch (face) { case SOUTH_WEST: case NORTH_WEST: return -MathUtil.HALFROOTOFTWO; case SOUTH_EAST: case NORTH_EAST: return MathUtil.HALFROOTOFTWO; case EAST: return 1; case WEST: return -1; default: return 0; } } /** * Gets the sinus value from a Block Face treated as an Angle * * @param face to get the sinus value from * @return sinus value */ public static double sin(final BlockFace face) { switch (face) { case NORTH_EAST: case NORTH_WEST: return -MathUtil.HALFROOTOFTWO; case SOUTH_WEST: case SOUTH_EAST: return MathUtil.HALFROOTOFTWO; case NORTH: return -1; case SOUTH: return 1; default: return 0; } } /** * Gets the angle from a horizontal Block Face * * @param face to get the angle for * @return face angle */ public static int faceToYaw(final BlockFace face) { return MathUtil.wrapAngle(45 * faceToNotch(face)); } /** * Gets the horizontal Block Face from a given yaw angle<br> * This includes the NORTH_WEST faces * * @param yaw angle * @return The Block Face of the angle */ public static BlockFace yawToFace(float yaw) { return yawToFace(yaw, true); } /** * Gets the horizontal Block Face from a given yaw angle * * @param yaw angle * @param useSubCardinalDirections setting, True to allow NORTH_WEST to be returned * @return The Block Face of the angle */ public static BlockFace yawToFace(float yaw, boolean useSubCardinalDirections) { if (useSubCardinalDirections) { return RADIAL[Math.round(yaw / 45f) & 0x7]; } else { return AXIS[Math.round(yaw / 90f) & 0x3]; } } }