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
This commit is contained in:
Michael Bayne
2002-06-26 02:54:56 +00:00
parent 5124b68339
commit c3bbaa3e40
3 changed files with 221 additions and 32 deletions
+104 -32
View File
@@ -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; package com.threerings.util;
@@ -23,7 +23,7 @@ public class DirectionUtil implements DirectionCodes
*/ */
public static String toString (int direction) public static String toString (int direction)
{ {
return ((direction >= SOUTHWEST) && (direction <= SOUTH)) ? return ((direction >= 0) && (direction < FINE_DIRECTION_COUNT)) ?
DIR_STRINGS[direction] : "INVALID"; DIR_STRINGS[direction] : "INVALID";
} }
@@ -33,7 +33,7 @@ public class DirectionUtil implements DirectionCodes
*/ */
public static String toShortString (int direction) public static String toShortString (int direction)
{ {
return ((direction >= SOUTHWEST) && (direction <= SOUTH)) ? return ((direction >= 0) && (direction < FINE_DIRECTION_COUNT)) ?
SHORT_DIR_STRINGS[direction] : "?"; SHORT_DIR_STRINGS[direction] : "?";
} }
@@ -54,9 +54,36 @@ public class DirectionUtil implements DirectionCodes
} }
/** /**
* Returns the direction that point <code>b</code> lies in from point * Rotates the requested <em>fine</em> direction constant clockwise by
* <code>a</code> as one of the {@link DirectionCodes} direction * the requested number of ticks.
* constants. */
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 <em>fine</em> 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
* <code>b</code> lies in from point <code>a</code> as one of the
* {@link DirectionCodes} direction constants. <em>Note:</em> that the
* coordinates supplied are assumed to be logical (screen) rather than
* cartesian coordinates and <code>NORTH</code> is considered to point
* toward the top of the screen.
*/ */
public static int getDirection (Point a, Point b) public static int getDirection (Point a, Point b)
{ {
@@ -64,44 +91,89 @@ public class DirectionUtil implements DirectionCodes
} }
/** /**
* Returns the direction that point <code>b</code> lies in from point * Returns which of the eight compass directions that point
* <code>a</code> as one of the {@link DirectionCodes} direction * <code>b</code> lies in from point <code>a</code> as one of the
* constants. * {@link DirectionCodes} direction constants. <em>Note:</em> that the
* coordinates supplied are assumed to be logical (screen) rather than
* cartesian coordinates and <code>NORTH</code> is considered to point
* toward the top of the screen.
*/ */
public static int getDirection (int ax, int ay, int bx, int by) public static int getDirection (int ax, int ay, int bx, int by)
{ {
if (ax == bx && ay > by) { double theta = Math.atan2(by-ay, bx-ax);
return NORTH; theta = ((theta + Math.PI) * 4) / Math.PI;
} else if (ax == bx && ay < by) { return (int)(Math.round(theta) + WEST) % 8;
return SOUTH; }
} else if (ax < bx && ay > by) { /**
return NORTHEAST; * Returns which of the sixteen compass directions that point
} else if (ax < bx && ay == by) { * <code>b</code> lies in from point <code>a</code> as one of the
return EAST; * {@link DirectionCodes} direction constants. <em>Note:</em> that the
} else if (ax < bx && ay < by) { * coordinates supplied are assumed to be logical (screen) rather than
return SOUTHEAST; * cartesian coordinates and <code>NORTH</code> 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; * Returns which of the sixteen compass directions that point
} else if (ax > bx && ay == by) { * <code>b</code> lies in from point <code>a</code> as one of the
return WEST; * {@link DirectionCodes} direction constants. <em>Note:</em> that the
} else if (ax > bx && ay > by) { * coordinates supplied are assumed to be logical (screen) rather than
return NORTHWEST; * cartesian coordinates and <code>NORTH</code> is considered to point
* toward the top of the screen.
} else { */
return NONE; 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. */ /** Direction constant string names. */
protected static final String[] DIR_STRINGS = { protected static final String[] DIR_STRINGS = {
"SOUTHWEST", "WEST", "NORTHWEST", "NORTH", "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. */ /** Abbreviated direction constant string names. */
protected static final String[] SHORT_DIR_STRINGS = { 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 };
}
@@ -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();
}
}
@@ -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();
}