com.threerings.util.DirectionUtil Maven / Gradle / Ivy
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// https://github.com/threerings/nenya
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.util;
import java.awt.Point;
import com.samskivert.util.IntListUtil;
/**
* Direction related utility functions.
*/
public class DirectionUtil implements DirectionCodes
{
/**
* Returns an array of names corresponding to each direction constant.
*/
public static String[] getDirectionNames ()
{
return DIR_STRINGS;
}
/**
* Returns a string representation of the supplied direction code.
*/
public static String toString (int direction)
{
return ((direction >= 0) && (direction < FINE_DIRECTION_COUNT)) ?
DIR_STRINGS[direction] : "INVALID";
}
/**
* Returns an abbreviated string representation of the supplied direction code.
*/
public static String toShortString (int direction)
{
return ((direction >= 0) && (direction < FINE_DIRECTION_COUNT)) ?
SHORT_DIR_STRINGS[direction] : "?";
}
/**
* Returns the direction code that corresponds to the supplied string or {@link #NONE} if the
* string does not correspond to a known direction code.
*/
public static int fromString (String dirstr)
{
for (int ii = 0; ii < FINE_DIRECTION_COUNT; ii++) {
if (DIR_STRINGS[ii].equals(dirstr)) {
return ii;
}
}
return NONE;
}
/**
* Returns the direction code that corresponds to the supplied short string or {@link #NONE}
* if the string does not correspond to a known direction code.
*/
public static int fromShortString (String dirstr)
{
for (int ii = 0; ii < FINE_DIRECTION_COUNT; ii++) {
if (SHORT_DIR_STRINGS[ii].equals(dirstr)) {
return ii;
}
}
return NONE;
}
/**
* Returns a string representation of an array of direction codes. The directions are
* represented by the abbreviated names.
*/
public static String toString (int[] directions)
{
StringBuilder buf = new StringBuilder("{");
for (int ii = 0; ii < directions.length; ii++) {
if (ii > 0) {
buf.append(", ");
}
buf.append(toShortString(directions[ii]));
}
return buf.append("}").toString();
}
/**
* Rotates the requested fine direction constant clockwise by the requested number of
* ticks.
*/
public static int rotateCW (int direction, int ticks)
{
for (int ii = 0; ii < ticks; ii++) {
direction = FINE_CW_ROTATE[direction];
}
return direction;
}
/**
* Rotates the requested fine direction constant counter-clockwise by the requested
* number of ticks.
*/
public static int rotateCCW (int direction, int ticks)
{
for (int ii = 0; ii < ticks; ii++) {
direction = FINE_CCW_ROTATE[direction];
}
return direction;
}
/**
* Returns the opposite of the specified direction.
*/
public static int getOpposite (int direction)
{
return rotateCW(direction, FINE_CW_ROTATE.length/2);
}
/**
* Get the cardinal direction closest to the specified direction (preferring a clockwise match).
*/
public static int getClosestCardinal (int direction)
{
return getClosest(direction, CARDINAL_DIRECTIONS, true);
}
/**
* Get the direction closest to the specified direction, out of the directions in the possible
* list (preferring a clockwise match).
*/
public static int getClosest (int direction, int[] possible)
{
return getClosest(direction, possible, true);
}
/**
* Get the direction closest to the specified direction, out of the directions in the possible
* list.
*
* @param preferCW whether to prefer a clockwise match or a counter-clockwise match.
*/
public static int getClosest (int direction, int[] possible,
boolean preferCW)
{
// rotate a tick at a time, looking for matches
int first = direction;
int second = direction;
for (int ii = 0; ii <= FINE_DIRECTION_COUNT / 2; ii++) {
if (IntListUtil.contains(possible, first)) {
return first;
}
if (ii != 0 && IntListUtil.contains(possible, second)) {
return second;
}
first = preferCW ? rotateCW(first, 1) : rotateCCW(first, 1);
second = preferCW ? rotateCCW(second, 1) : rotateCW(second, 1);
}
return NONE;
}
/**
* Returns which of the eight compass directions that point b lies in from point
* a as one of the {@link DirectionCodes} direction constants. Note:
* that the coordinates supplied are assumed to be logical (screen) rather than cartesian
* coordinates and NORTH is considered to point toward the top of the screen.
*/
public static int getDirection (Point a, Point b)
{
return getDirection(a.getX(), a.getY(), b.getX(), b.getY());
}
/**
* Returns which of the eight compass directions that point b lies in from point
* a as one of the {@link DirectionCodes} direction constants. Note:
* that the coordinates supplied are assumed to be logical (screen) rather than cartesian
* coordinates and NORTH is considered to point toward the top of the screen.
*/
public static int getDirection (int ax, int ay, int bx, int by)
{
return getDirection(Math.atan2(by-ay, bx-ax));
}
/**
* Returns which of the eight compass directions that point b lies in from point
* a as one of the {@link DirectionCodes} direction constants. Note:
* that the coordinates supplied are assumed to be logical (screen) rather than cartesian
* coordinates and NORTH is considered to point toward the top of the screen.
*/
public static int getDirection (double ax, double ay, double bx, double by)
{
return getDirection(Math.atan2(by-ay, bx-ax));
}
/**
* Returns which of the eight compass directions is associated with the specified angle theta.
* Note: that the angle supplied is assumed to increase clockwise around the origin
* (which screen angles do) rather than counter-clockwise around the origin (which cartesian
* angles do) and NORTH is considered to point toward the top of the screen.
*/
public static int getDirection (double theta)
{
theta = ((theta + Math.PI) * 4) / Math.PI;
return (int)(Math.round(theta) + WEST) % 8;
}
/**
* Returns which of the sixteen compass directions that point b lies in from
* point a as one of the {@link DirectionCodes} direction constants.
* Note: that the coordinates supplied are assumed to be logical (screen) rather than
* cartesian coordinates and NORTH is considered to point toward the top of the
* screen.
*/
public static int getFineDirection (Point a, Point b)
{
return getFineDirection(a.x, a.y, b.x, b.y);
}
/**
* Returns which of the sixteen compass directions that point b lies in from
* point a as one of the {@link DirectionCodes} direction constants.
* Note: that the coordinates supplied are assumed to be logical (screen) rather than
* cartesian coordinates and NORTH is considered to point toward the top of the
* screen.
*/
public static int getFineDirection (int ax, int ay, int bx, int by)
{
return getFineDirection(Math.atan2(by-ay, bx-ax));
}
/**
* Returns which of the sixteen compass directions is associated with the specified angle
* theta. Note: that the angle supplied is assumed to increase clockwise around the
* origin (which screen angles do) rather than counter-clockwise around the origin (which
* cartesian angles do) and NORTH is considered to point toward the top of the
* screen.
*/
public static int getFineDirection (double theta)
{
theta = ((theta + Math.PI) * 8) / Math.PI;
return ANGLE_MAP[(int)Math.round(theta) % FINE_DIRECTION_COUNT];
}
/**
* Move the specified point in the specified screen direction, adjusting by the specified
* adjustments. Fine directions are not supported.
*/
public static void moveDirection (Point p, int direction, int dx, int dy)
{
if (direction >= DIRECTION_COUNT) {
throw new IllegalArgumentException("Fine coordinates not supported.");
}
switch (direction) {
case NORTH: case NORTHWEST: case NORTHEAST: p.y -= dy;
}
switch (direction) {
case SOUTH: case SOUTHWEST: case SOUTHEAST: p.y += dy;
}
switch (direction) {
case WEST: case SOUTHWEST: case NORTHWEST: p.x -= dx;
}
switch (direction) {
case EAST: case SOUTHEAST: case NORTHEAST: p.x += dx;
}
}
/** Direction constant string names. */
protected static final String[] DIR_STRINGS = {
"SOUTHWEST", "WEST", "NORTHWEST", "NORTH",
"NORTHEAST", "EAST", "SOUTHEAST", "SOUTH",
"WESTSOUTHWEST", "WESTNORTHWEST", "NORTHNORTHWEST", "NORTHNORTHEAST",
"EASTNORTHEAST", "EASTSOUTHEAST", "SOUTHSOUTHEAST", "SOUTHSOUTHWEST",
};
/** Abbreviated direction constant string names. */
protected static final String[] SHORT_DIR_STRINGS = {
"SW", "W", "NW", "N", "NE", "E", "SE", "S",
"WSW", "WNW", "NNW", "NNE", "ENE", "ESE", "SSE", "SSW",
};
/** Used to rotate a fine compass direction clockwise. */
protected static final int[] FINE_CW_ROTATE = {
/* SW -> */ WESTSOUTHWEST, /* W -> */ WESTNORTHWEST,
/* NW -> */ NORTHNORTHWEST, /* N -> */ NORTHNORTHEAST,
/* NE -> */ EASTNORTHEAST, /* E -> */ EASTSOUTHEAST,
/* SE -> */ SOUTHSOUTHEAST, /* S -> */ SOUTHSOUTHWEST,
/* WSW -> */ WEST, /* WNW -> */ NORTHWEST,
/* NNW -> */ NORTH, /* NNE -> */ NORTHEAST,
/* ENE -> */ EAST, /* ESE -> */ SOUTHEAST,
/* SSE -> */ SOUTH, /* SSW -> */ SOUTHWEST
};
/** Used to rotate a fine compass direction counter-clockwise. */
protected static final int[] FINE_CCW_ROTATE = {
/* SW -> */ SOUTHSOUTHWEST, /* W -> */ WESTSOUTHWEST,
/* NW -> */ WESTNORTHWEST, /* N -> */ NORTHNORTHWEST,
/* NE -> */ NORTHNORTHEAST, /* E -> */ EASTNORTHEAST,
/* SE -> */ EASTSOUTHEAST, /* S -> */ SOUTHSOUTHEAST,
/* WSW -> */ SOUTHWEST, /* WNW -> */ WEST,
/* NNW -> */ NORTHWEST, /* NNE -> */ NORTH,
/* ENE -> */ NORTHEAST, /* ESE -> */ EAST,
/* SSE -> */ SOUTHEAST, /* SSW -> */ SOUTH
};
/** Used to map an angle to a fine compass direction. */
protected static final int[] ANGLE_MAP = {
WEST, WESTNORTHWEST, NORTHWEST, NORTHNORTHWEST, NORTH, NORTHNORTHEAST,
NORTHEAST, EASTNORTHEAST, EAST, EASTSOUTHEAST, SOUTHEAST,
SOUTHSOUTHEAST, SOUTH, SOUTHSOUTHWEST, SOUTHWEST, WESTSOUTHWEST };
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy