From c3bbaa3e4005e337e189d8c0ecfe291bc76d8eb5 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Wed, 26 Jun 2002 02:54:56 +0000 Subject: [PATCH] Modified getDirection() to use arc tangent so that it maps angles smoothly to their nearest compass direction instead of the wacky way it did before. Also added getFineDirection() along with rotateCW() and rotateCCW() to rotate through (fine) directions (though coarse directions can be rotated as well, simply by always requesting two ticks of rotation). Also wrote some test code to make sure everything works. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1545 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../com/threerings/util/DirectionUtil.java | 136 +++++++++++++----- .../com/threerings/util/DirectionTest.java | 49 +++++++ .../com/threerings/util/DirectionViz.java | 68 +++++++++ 3 files changed, 221 insertions(+), 32 deletions(-) create mode 100644 tests/src/java/com/threerings/util/DirectionTest.java create mode 100644 tests/src/java/com/threerings/util/DirectionViz.java diff --git a/src/java/com/threerings/util/DirectionUtil.java b/src/java/com/threerings/util/DirectionUtil.java index a56c318e0..a52029b54 100644 --- a/src/java/com/threerings/util/DirectionUtil.java +++ b/src/java/com/threerings/util/DirectionUtil.java @@ -1,5 +1,5 @@ // -// $Id: DirectionUtil.java,v 1.4 2002/05/17 21:12:14 mdb Exp $ +// $Id: DirectionUtil.java,v 1.5 2002/06/26 02:54:56 mdb Exp $ package com.threerings.util; @@ -23,7 +23,7 @@ public class DirectionUtil implements DirectionCodes */ public static String toString (int direction) { - return ((direction >= SOUTHWEST) && (direction <= SOUTH)) ? + return ((direction >= 0) && (direction < FINE_DIRECTION_COUNT)) ? DIR_STRINGS[direction] : "INVALID"; } @@ -33,7 +33,7 @@ public class DirectionUtil implements DirectionCodes */ public static String toShortString (int direction) { - return ((direction >= SOUTHWEST) && (direction <= SOUTH)) ? + return ((direction >= 0) && (direction < FINE_DIRECTION_COUNT)) ? SHORT_DIR_STRINGS[direction] : "?"; } @@ -54,9 +54,36 @@ public class DirectionUtil implements DirectionCodes } /** - * Returns the direction that point b lies in from point - * a as one of the {@link DirectionCodes} direction - * constants. + * 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 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) { @@ -64,44 +91,89 @@ public class DirectionUtil implements DirectionCodes } /** - * Returns the direction that point b lies in from point - * a as one of the {@link DirectionCodes} direction - * constants. + * 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) { - if (ax == bx && ay > by) { - return NORTH; - } else if (ax == bx && ay < by) { - return SOUTH; + double theta = Math.atan2(by-ay, bx-ax); + theta = ((theta + Math.PI) * 4) / Math.PI; + return (int)(Math.round(theta) + WEST) % 8; + } - } else if (ax < bx && ay > by) { - return NORTHEAST; - } else if (ax < bx && ay == by) { - return EAST; - } else if (ax < bx && ay < by) { - return SOUTHEAST; + /** + * 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); + } - } else if (ax > bx && ay < by) { - return SOUTHWEST; - } else if (ax > bx && ay == by) { - return WEST; - } else if (ax > bx && ay > by) { - return NORTHWEST; - - } else { - return NONE; - } + /** + * 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) + { + double theta = Math.atan2(by-ay, bx-ax); + theta = ((theta + Math.PI) * 8) / Math.PI; + return ANGLE_MAP[(int)Math.round(theta) % FINE_DIRECTION_COUNT]; } /** Direction constant string names. */ protected static final String[] DIR_STRINGS = { "SOUTHWEST", "WEST", "NORTHWEST", "NORTH", - "NORTHEAST", "EAST", "SOUTHEAST", "SOUTH" + "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" + "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 }; +} diff --git a/tests/src/java/com/threerings/util/DirectionTest.java b/tests/src/java/com/threerings/util/DirectionTest.java new file mode 100644 index 000000000..ebac4571f --- /dev/null +++ b/tests/src/java/com/threerings/util/DirectionTest.java @@ -0,0 +1,49 @@ +// +// $Id: DirectionTest.java,v 1.1 2002/06/26 02:54:56 mdb Exp $ + +package com.threerings.util; + +import junit.framework.Test; +import junit.framework.TestCase; + +/** + * Tests the {@link Direction} class. + */ +public class DirectionTest extends TestCase + implements DirectionCodes +{ + public DirectionTest () + { + super(DirectionTest.class.getName()); + } + + public void runTest () + { + int orient = NORTH; + + for (int i = 0; i < FINE_DIRECTION_COUNT; i++) { +// System.out.print(DirectionUtil.toShortString(orient) + " -> "); + orient = DirectionUtil.rotateCW(orient, 1); + } +// System.out.println(DirectionUtil.toShortString(orient)); + assertTrue("CW rotate", orient == NORTH); + + for (int i = 0; i < FINE_DIRECTION_COUNT; i++) { +// System.out.print(DirectionUtil.toShortString(orient) + " -> "); + orient = DirectionUtil.rotateCCW(orient, 1); + } +// System.out.println(DirectionUtil.toShortString(orient)); + assertTrue("CCW rotate", orient == NORTH); + } + + public static Test suite () + { + return new DirectionTest(); + } + + public static void main (String[] args) + { + DirectionTest test = new DirectionTest(); + test.runTest(); + } +} diff --git a/tests/src/java/com/threerings/util/DirectionViz.java b/tests/src/java/com/threerings/util/DirectionViz.java new file mode 100644 index 000000000..c90ecbb97 --- /dev/null +++ b/tests/src/java/com/threerings/util/DirectionViz.java @@ -0,0 +1,68 @@ +// +// $Id: DirectionViz.java,v 1.1 2002/06/26 02:54:56 mdb Exp $ + +package com.threerings.util; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Point; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +/** + * Renders the output of {@link DirectionUtil#getDirection} just for + * kicks. + */ +public class DirectionViz extends JPanel + implements MouseMotionListener +{ + public DirectionViz () + { + addMouseMotionListener(this); + } + + public void doLayout () + { + super.doLayout(); + _center = new Point(getWidth() / 2, getHeight() / 2); + } + + public void paintComponent (Graphics g) + { + super.paintComponent(g); + + g.setColor(Color.blue); + g.drawLine(_center.x, _center.y, _spot.x, _spot.y); + + int orient = DirectionUtil.getFineDirection(_center, _spot); + g.drawString(DirectionUtil.toShortString(orient), _spot.x, _spot.y); + } + + public void mouseDragged (MouseEvent event) + { + } + + public void mouseMoved (MouseEvent event) + { + _spot.x = event.getX(); + _spot.y = event.getY(); + repaint(); + } + + public static void main (String[] args) + { + JFrame frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.getContentPane().add(new DirectionViz(), BorderLayout.CENTER); + frame.setSize(300, 300); + frame.show(); + } + + protected Point _center; + protected Point _spot = new Point(); +}