All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.threerings.util.DirectionUtil Maven / Gradle / Ivy

The newest version!
//
// 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