org.spongepowered.api.util.Direction Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spongeapi Show documentation
Show all versions of spongeapi Show documentation
A plugin API for Minecraft: Java Edition
The newest version!
/*
* This file is part of SpongeAPI, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.spongepowered.api.util;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.spongepowered.api.data.type.StringRepresentable;
import org.spongepowered.math.GenericMath;
import org.spongepowered.math.TrigMath;
import org.spongepowered.math.vector.Vector3d;
import org.spongepowered.math.vector.Vector3i;
/**
* Represent the 16 main and secondary cardinal directions plus up and down.
* With the following assumptions:
*
* - {@link #NORTH} targeting towards -Z
* - {@link #EAST} targeting towards +X
* - {@link #SOUTH} targeting towards +Z
* - {@link #WEST} targeting towards -X
* - {@link #UP} targeting towards +Y
* - {@link #DOWN} targeting towards -Y
*
*/
public enum Direction implements StringRepresentable {
NORTH(new Vector3d(0, 0, -1), Division.CARDINAL),
NORTH_NORTHEAST(new Vector3d(C.S8, 0, -C.C8), Division.SECONDARY_ORDINAL),
NORTHEAST(new Vector3d(1, 0, -1), Division.ORDINAL),
EAST_NORTHEAST(new Vector3d(C.C8, 0, -C.S8), Division.SECONDARY_ORDINAL),
EAST(new Vector3d(1, 0, 0), Division.CARDINAL),
EAST_SOUTHEAST(new Vector3d(C.C8, 0, C.S8), Division.SECONDARY_ORDINAL),
SOUTHEAST(new Vector3d(1, 0, 1), Division.ORDINAL),
SOUTH_SOUTHEAST(new Vector3d(C.S8, 0, C.C8), Division.SECONDARY_ORDINAL),
SOUTH(new Vector3d(0, 0, 1), Division.CARDINAL),
SOUTH_SOUTHWEST(new Vector3d(-C.S8, 0, C.C8), Division.SECONDARY_ORDINAL),
SOUTHWEST(new Vector3d(-1, 0, 1), Division.ORDINAL),
WEST_SOUTHWEST(new Vector3d(-C.C8, 0, C.S8), Division.SECONDARY_ORDINAL),
WEST(new Vector3d(-1, 0, 0), Division.CARDINAL),
WEST_NORTHWEST(new Vector3d(-C.C8, 0, -C.S8), Division.SECONDARY_ORDINAL),
NORTHWEST(new Vector3d(-1, 0, -1), Division.ORDINAL),
NORTH_NORTHWEST(new Vector3d(-C.S8, 0, -C.C8), Division.SECONDARY_ORDINAL),
UP(new Vector3d(0, 1, 0), Division.CARDINAL),
DOWN(new Vector3d(0, -1, 0), Division.CARDINAL),
NONE(new Vector3d(0, 0, 0), Division.NONE);
private static final Direction[] SECONDARY_ORDINAL_SET = {
Direction.NORTH, Direction.NORTH_NORTHEAST, Direction.NORTHEAST, Direction.EAST_NORTHEAST,
Direction.EAST, Direction.EAST_SOUTHEAST, Direction.SOUTHEAST, Direction.SOUTH_SOUTHEAST,
Direction.SOUTH, Direction.SOUTH_SOUTHWEST, Direction.SOUTHWEST, Direction.WEST_SOUTHWEST,
Direction.WEST, Direction.WEST_NORTHWEST, Direction.NORTHWEST, Direction.NORTH_NORTHWEST,
};
private static final Direction[] ORDINAL_SET = {
Direction.NORTH, Direction.NORTHEAST, Direction.EAST, Direction.SOUTHEAST,
Direction.SOUTH, Direction.SOUTHWEST, Direction.WEST, Direction.NORTHWEST,
};
private static final Direction[] CARDINAL_SET = {
Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST
};
private final Vector3d offset;
private final Vector3i blockOffset;
private final Division division;
@SuppressWarnings("ImmutableEnumChecker")
private @MonotonicNonNull Direction opposite;
static {
Direction.NORTH.opposite = Direction.SOUTH;
Direction.EAST.opposite = Direction.WEST;
Direction.SOUTH.opposite = Direction.NORTH;
Direction.WEST.opposite = Direction.EAST;
Direction.UP.opposite = Direction.DOWN;
Direction.DOWN.opposite = Direction.UP;
Direction.NONE.opposite = Direction.NONE;
Direction.NORTHEAST.opposite = Direction.SOUTHWEST;
Direction.NORTHWEST.opposite = Direction.SOUTHEAST;
Direction.SOUTHEAST.opposite = Direction.NORTHWEST;
Direction.SOUTHWEST.opposite = Direction.NORTHEAST;
Direction.WEST_NORTHWEST.opposite = Direction.EAST_SOUTHEAST;
Direction.WEST_SOUTHWEST.opposite = Direction.EAST_NORTHEAST;
Direction.NORTH_NORTHWEST.opposite = Direction.SOUTH_SOUTHEAST;
Direction.NORTH_NORTHEAST.opposite = Direction.SOUTH_SOUTHWEST;
Direction.EAST_SOUTHEAST.opposite = Direction.WEST_NORTHWEST;
Direction.EAST_NORTHEAST.opposite = Direction.WEST_SOUTHWEST;
Direction.SOUTH_SOUTHEAST.opposite = Direction.NORTH_NORTHWEST;
Direction.SOUTH_SOUTHWEST.opposite = Direction.NORTH_NORTHEAST;
}
Direction(Vector3d direction, Division division) {
if (direction.lengthSquared() == 0) {
// Prevent normalization of the zero direction
this.offset = direction;
} else {
this.offset = direction.normalize();
}
this.blockOffset = direction.round().toInt();
this.division = division;
}
/**
* Gets the closest direction from the given vector. If the vector is the
* 0-Vector, this method returns {@link #NONE}. If the vector has the same
* horizontal and vertical length, a horizontal direction will be returned.
* If the vector is halfway between two directions the clockwise next will
* be selected.
*
* @param vector The vector to convert to a direction
* @return The closest horizontal direction.
*/
public static Direction closest(Vector3d vector) {
return Direction.closest(vector, Division.SECONDARY_ORDINAL);
}
/**
* Gets the closest direction from the given vector. If the vector is the
* 0-Vector, this method returns {@link #NONE}. If the vector has the same
* horizontal and vertical length, a horizontal direction will be returned.
* If the vector is halfway between two directions the clockwise next will
* be selected.
*
* @param vector The vector to convert to a direction
* @param smallestDivision The smallest compass division that can be
* returned
* @return The closest horizontal direction.
*/
public static Direction closest(Vector3d vector, Division smallestDivision) {
if (vector.y() * vector.y() <= vector.x() * vector.x() + vector.z() * vector.z()) {
return Direction.closestHorizontal(vector, smallestDivision);
} else if (vector.y() > 0) {
return Direction.UP;
} else {
return Direction.DOWN;
}
}
/**
* Gets the closest horizontal direction from the given vector. If the
* vector is the 0-Vector (ignoring y), this method returns {@link #NONE}.
* If the vector is halfway between two directions the clockwise next will
* be selected.
*
* @param vector The vector to convert to a direction
* @return The closest horizontal direction.
*/
public static Direction closestHorizontal(Vector3d vector) {
return Direction.closestHorizontal(vector, Division.SECONDARY_ORDINAL);
}
/**
* Gets the closest horizontal direction from the given vector. If the
* vector is the 0-Vector (ignoring y), this method returns {@link #NONE}.
* If the vector is halfway between two directions the clockwise next will
* be selected.
*
* @param vector The vector to convert to a direction
* @param smallestDivision The smallest compass division that can be
* returned
* @return The closest horizontal direction.
*/
public static Direction closestHorizontal(Vector3d vector, Division smallestDivision) {
// Ignore vectors not in the xz plane
if (Math.abs(vector.x()) <= GenericMath.DBL_EPSILON && Math.abs(vector.z()) <= GenericMath.DBL_EPSILON) {
return Direction.NONE;
}
// Normalize so it lies on the unit circle in xz
vector = vector.normalize();
// Get the angle from the x component and correct for complement with z
double angle = TrigMath.acos(vector.x());
if (vector.z() < 0) {
angle = TrigMath.TWO_PI - angle;
}
// Make the angle positive, offset for MC's system, then wrap in [0, 2pi)
angle = (angle + TrigMath.TWO_PI + TrigMath.HALF_PI) % TrigMath.TWO_PI;
// Use a direction set; it needs to be sorted and the directions evenly spaced
final Direction[] set;
switch (smallestDivision) {
case CARDINAL:
set = Direction.CARDINAL_SET;
break;
case ORDINAL:
set = Direction.ORDINAL_SET;
break;
case SECONDARY_ORDINAL:
set = Direction.SECONDARY_ORDINAL_SET;
break;
default:
throw new IllegalArgumentException(smallestDivision.name());
}
// Round to the closest index in the direction set
return set[(int) Math.round(angle * set.length / TrigMath.TWO_PI) % set.length];
}
/**
* Gets the direction associated with the given axis.
*
* @param axis The axis
* @return The direction
*/
public static Direction fromAxis(final Axis axis) {
switch (axis) {
case X:
return Direction.EAST;
case Y:
return Direction.UP;
case Z:
return Direction.SOUTH;
default:
throw new IllegalArgumentException(axis.name());
}
}
/**
* Gets the direction of the axis along the given {@link AxisDirection}.
*
* @param axis The axis
* @param direction The direction along the axis
* @return The direction
*/
public static Direction fromAxis(final Axis axis, final AxisDirection direction) {
switch (direction) {
case PLUS:
return Direction.fromAxis(axis);
case ZERO:
return Direction.NONE;
case MINUS:
return Direction.fromAxis(axis).opposite();
default:
throw new IllegalArgumentException(axis.name());
}
}
/**
* Gets the opposite direction i.e. 180 degrees from this direction.
*
* @return The opposite direction
*/
public Direction opposite() {
return this.opposite;
}
/**
* Returns whether the given direction is opposite this.
*
* @param d Direction to test
* @return True if it is opposite
*/
public boolean isOpposite(Direction d) {
return this.opposite == d;
}
/**
* Return true if the direction is of a cardinal direction (north, west
* east, and south).
*
* This evaluates as false for directions that have a non-zero
* Y-component.
*
* @return True if cardinal
*/
public boolean isCardinal() {
return this.division == Division.CARDINAL;
}
/**
* Return true if the direction is of an ordinal direction (northwest,
* southwest, southeast, northeast).
*
* @return True if ordinal
*/
public boolean isOrdinal() {
return this.division == Division.ORDINAL;
}
/**
* Return true if the direction is of a secondary ordinal direction
* (north-northwest, north-northeast, south-southwest, etc.).
*
* @return True if secondary ordinal
*/
public boolean isSecondaryOrdinal() {
return this.division == Division.SECONDARY_ORDINAL;
}
/**
* Return whether Y component is non-zero.
*
* @return True if the Y component is non-zero
*/
public boolean isUpright() {
return this == Direction.UP || this == Direction.DOWN;
}
/**
* Returns the direction as a unit offset vector.
* This vector is also suitable as a unit direction vector.
*
* @return The direction as an offset
*/
public Vector3d asOffset() {
return this.offset;
}
/**
* Returns the direction as a block offset vector.
* For secondary ordinals the results are approximated to the nearest
* block.
*
* The difference between this offset and {@link #asOffset()} is that
* a block offset has unit components instead of unit length.
*
* @return The direction as a block offset
*/
public Vector3i asBlockOffset() {
return this.blockOffset;
}
@Override
public String serializationString() {
return this.name();
}
private interface C {
double C8 = Math.cos(Math.PI / 8);
double S8 = Math.sin(Math.PI / 8);
}
/**
* The compass division supported by this direction implementation.
*/
public enum Division {
CARDINAL,
ORDINAL,
SECONDARY_ORDINAL,
NONE
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy