/*
* Copyright 2013 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.math;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import org.terasology.math.geom.Vector3f;
import org.terasology.math.geom.Vector3i;
import java.util.EnumMap;
/**
* The six sides of a block and a slew of related utility.
* <br><br>
* Note that the FRONT of the block faces towards the player - this means Left and Right are a player's right and left.
* See Direction for an enumeration of directions in terms of the player's perspective.
*
*/
public enum Side {
TOP(Vector3i.up(), true, false, true),
BOTTOM(Vector3i.down(), true, false, true),
LEFT(new Vector3i(-1, 0, 0), false, true, true),
RIGHT(new Vector3i(1, 0, 0), false, true, true),
FRONT(new Vector3i(0, 0, -1), true, true, false),
BACK(new Vector3i(0, 0, 1), true, true, false);
private static EnumMap<Side, Side> reverseMap;
private static ImmutableList<Side> horizontalSides;
private static ImmutableList<Side> verticalSides;
private static EnumMap<Side, Side> clockwiseYawSide;
private static EnumMap<Side, Side> anticlockwiseYawSide;
private static EnumMap<Side, Side> clockwisePitchSide;
private static EnumMap<Side, Side> anticlockwisePitchSide;
private static EnumMap<Side, Side> clockwiseRollSide;
private static EnumMap<Side, Side> anticlockwiseRollSide;
private static EnumMap<Side, Direction> conversionMap;
private static EnumMap<Side, ImmutableList<Side>> tangents;
static {
tangents = new EnumMap<>(Side.class);
tangents.put(TOP, ImmutableList.of(LEFT, RIGHT, FRONT, BACK));
tangents.put(BOTTOM, ImmutableList.of(LEFT, RIGHT, FRONT, BACK));
tangents.put(LEFT, ImmutableList.of(TOP, BOTTOM, FRONT, BACK));
tangents.put(RIGHT, ImmutableList.of(TOP, BOTTOM, FRONT, BACK));
tangents.put(FRONT, ImmutableList.of(TOP, BOTTOM, LEFT, RIGHT));
tangents.put(BACK, ImmutableList.of(TOP, BOTTOM, LEFT, RIGHT));
reverseMap = new EnumMap<>(Side.class);
reverseMap.put(TOP, BOTTOM);
reverseMap.put(LEFT, RIGHT);
reverseMap.put(RIGHT, LEFT);
reverseMap.put(FRONT, BACK);
reverseMap.put(BACK, FRONT);
reverseMap.put(BOTTOM, TOP);
conversionMap = new EnumMap<>(Side.class);
conversionMap.put(TOP, Direction.UP);
conversionMap.put(BOTTOM, Direction.DOWN);
conversionMap.put(BACK, Direction.FORWARD);
conversionMap.put(FRONT, Direction.BACKWARD);
conversionMap.put(RIGHT, Direction.LEFT);
conversionMap.put(LEFT, Direction.RIGHT);
clockwiseYawSide = new EnumMap<>(Side.class);
anticlockwiseYawSide = new EnumMap<>(Side.class);
clockwiseYawSide.put(Side.FRONT, Side.LEFT);
anticlockwiseYawSide.put(Side.FRONT, Side.RIGHT);
clockwiseYawSide.put(Side.RIGHT, Side.FRONT);
anticlockwiseYawSide.put(Side.RIGHT, Side.BACK);
clockwiseYawSide.put(Side.BACK, Side.RIGHT);
anticlockwiseYawSide.put(Side.BACK, Side.LEFT);
clockwiseYawSide.put(Side.LEFT, Side.BACK);
anticlockwiseYawSide.put(Side.LEFT, Side.FRONT);
clockwisePitchSide = Maps.newEnumMap(Side.class);
anticlockwisePitchSide = Maps.newEnumMap(Side.class);
clockwisePitchSide.put(Side.FRONT, Side.TOP);
anticlockwisePitchSide.put(Side.FRONT, Side.BOTTOM);
clockwisePitchSide.put(Side.BOTTOM, Side.FRONT);
anticlockwisePitchSide.put(Side.BOTTOM, Side.BACK);
clockwisePitchSide.put(Side.BACK, Side.BOTTOM);
anticlockwisePitchSide.put(Side.BACK, Side.TOP);
clockwisePitchSide.put(Side.TOP, Side.BACK);
anticlockwisePitchSide.put(Side.TOP, Side.FRONT);
clockwiseRollSide = Maps.newEnumMap(Side.class);
anticlockwiseRollSide = Maps.newEnumMap(Side.class);
clockwiseRollSide.put(Side.TOP, Side.LEFT);
anticlockwiseRollSide.put(Side.TOP, Side.RIGHT);
clockwiseRollSide.put(Side.LEFT, Side.BOTTOM);
anticlockwiseRollSide.put(Side.LEFT, Side.TOP);
clockwiseRollSide.put(Side.BOTTOM, Side.RIGHT);
anticlockwiseRollSide.put(Side.BOTTOM, Side.LEFT);
clockwiseRollSide.put(Side.RIGHT, Side.TOP);
anticlockwiseRollSide.put(Side.RIGHT, Side.BOTTOM);
horizontalSides = ImmutableList.of(LEFT, RIGHT, FRONT, BACK);
verticalSides = ImmutableList.of(TOP, BOTTOM);
}
private Vector3i vector3iDir;
private boolean canYaw;
private boolean canPitch;
private boolean canRoll;
Side(Vector3i vector3i, boolean canPitch, boolean canYaw, boolean canRoll) {
this.vector3iDir = vector3i;
this.canPitch = canPitch;
this.canYaw = canYaw;
this.canRoll = canRoll;
}
/**
* @return The horizontal sides, for iteration
*/
public static ImmutableList<Side> horizontalSides() {
return horizontalSides;
}
/**
* @return The vertical sides, for iteration
*/
public static ImmutableList<Side> verticalSides() {
return verticalSides;
}
public static Side inDirection(int x, int y, int z) {
if (TeraMath.fastAbs(x) > TeraMath.fastAbs(y)) {
if (TeraMath.fastAbs(x) > TeraMath.fastAbs(z)) {
return (x > 0) ? RIGHT : LEFT;
}
} else if (TeraMath.fastAbs(y) > TeraMath.fastAbs(z)) {
return (y > 0) ? TOP : BOTTOM;
}
return (z > 0) ? BACK : FRONT;
}
public static Side inDirection(Vector3f dir) {
return inDirection(dir.x, dir.y, dir.z);
}
/**
* Determines which direction the player is facing
*
* @param x right/left
* @param y top/bottom
* @param z back/front
* @return Side enum with the appropriate direction
*/
public static Side inDirection(double x, double y, double z) {
if (TeraMath.fastAbs(x) > TeraMath.fastAbs(y)) {
if (TeraMath.fastAbs(x) > TeraMath.fastAbs(z)) {
return (x > 0) ? RIGHT : LEFT;
}
} else if (TeraMath.fastAbs(y) > TeraMath.fastAbs(z)) {
return (y > 0) ? TOP : BOTTOM;
}
return (z > 0) ? BACK : FRONT;
}
/**
* Determines which horizontal direction the player is facing
*
* @param x right/left
* @param z back/front
* @return Side enum with the appropriate direction
*/
public static Side inHorizontalDirection(double x, double z) {
if (TeraMath.fastAbs(x) > TeraMath.fastAbs(z)) {
return (x > 0) ? RIGHT : LEFT;
}
return (z > 0) ? BACK : FRONT;
}
/**
* @return The vector3i in the direction of the side. Do not modify.
*/
public Vector3i getVector3i() {
return vector3iDir;
}
/**
* @return Whether this is one of the horizontal directions.
*/
public boolean isHorizontal() {
return canYaw;
}
/**
* @return The opposite side to this side.
*/
public Side reverse() {
return reverseMap.get(this);
}
public Side yawClockwise(int turns) {
if (!canYaw) {
return this;
}
int steps = turns;
if (steps < 0) {
steps = -steps + 2;
}
steps = steps % 4;
switch (steps) {
case 1:
return clockwiseYawSide.get(this);
case 2:
return reverseMap.get(this);
case 3:
return anticlockwiseYawSide.get(this);
default:
return this;
}
}
public Side pitchClockwise(int turns) {
if (!canPitch) {
return this;
}
int steps = turns;
if (steps < 0) {
steps = -steps + 2;
}
steps = steps % 4;
switch (steps) {
case 1:
return clockwisePitchSide.get(this);
case 2:
return reverseMap.get(this);
case 3:
return anticlockwisePitchSide.get(this);
default:
return this;
}
}
public Direction toDirection() {
return conversionMap.get(this);
}
public Side rollClockwise(int turns) {
if (!canRoll) {
return this;
}
int steps = turns;
if (steps < 0) {
steps = -steps + 2;
}
steps = steps % 4;
switch (steps) {
case 1:
return clockwiseRollSide.get(this);
case 2:
return reverseMap.get(this);
case 3:
return anticlockwiseRollSide.get(this);
default:
return this;
}
}
public Vector3i getAdjacentPos(Vector3i position) {
Vector3i result = new Vector3i(position);
result.add(vector3iDir);
return result;
}
public Side getRelativeSide(Direction direction) {
if (direction == Direction.UP) {
return pitchClockwise(1);
} else if (direction == Direction.DOWN) {
return pitchClockwise(-1);
} else if (direction == Direction.LEFT) {
return yawClockwise(1);
} else if (direction == Direction.RIGHT) {
return yawClockwise(-1);
} else if (direction == Direction.BACKWARD) {
return reverse();
} else {
return this;
}
}
public boolean isVertical() {
return !canYaw;
}
public Iterable<Side> tangents() {
return tangents.get(this);
}
}