diff --git a/src/java/com/threerings/nodemap/DuplicateEdgeException.java b/src/java/com/threerings/nodemap/DuplicateEdgeException.java
new file mode 100644
index 000000000..86b3f9262
--- /dev/null
+++ b/src/java/com/threerings/nodemap/DuplicateEdgeException.java
@@ -0,0 +1,16 @@
+//
+// $Id: DuplicateEdgeException.java,v 1.1 2001/08/20 22:56:55 shaper Exp $
+
+package com.threerings.nodemap;
+
+/**
+ * Thrown when an action is attempted on an edge that is already
+ * present in the involved node.
+ */
+public class DuplicateEdgeException extends NodeMapException
+{
+ public DuplicateEdgeException ()
+ {
+ super("error.duplicate_edge");
+ }
+}
diff --git a/src/java/com/threerings/nodemap/DuplicateNodeException.java b/src/java/com/threerings/nodemap/DuplicateNodeException.java
new file mode 100644
index 000000000..d05d1962c
--- /dev/null
+++ b/src/java/com/threerings/nodemap/DuplicateNodeException.java
@@ -0,0 +1,16 @@
+//
+// $Id: DuplicateNodeException.java,v 1.1 2001/08/20 22:56:55 shaper Exp $
+
+package com.threerings.nodemap;
+
+/**
+ * Thrown when an action is attempted on a node that is already
+ * present in the node map.
+ */
+public class DuplicateNodeException extends NodeMapException
+{
+ public DuplicateNodeException ()
+ {
+ super("error.duplicate_node");
+ }
+}
diff --git a/src/java/com/threerings/nodemap/Edge.java b/src/java/com/threerings/nodemap/Edge.java
new file mode 100644
index 000000000..619589c65
--- /dev/null
+++ b/src/java/com/threerings/nodemap/Edge.java
@@ -0,0 +1,59 @@
+//
+// $Id: Edge.java,v 1.1 2001/08/20 22:56:55 shaper Exp $
+
+package com.threerings.nodemap;
+
+import java.awt.Graphics;
+
+/**
+ * The edge class represents an edge connecting two nodes.
+ */
+public abstract class Edge
+{
+ /** The source node. */
+ public Node src;
+
+ /** The destination node. */
+ public Node dst;
+
+ /**
+ * Construct an edge object connecting the given nodes.
+ *
+ * @param src the source node.
+ * @param dst the destination node.
+ */
+ public Edge (Node src, Node dst)
+ {
+ this.src = src;
+ this.dst = dst;
+ }
+
+ /**
+ * Paint the edge to the given graphics context.
+ *
+ * @param g the graphics context.
+ */
+ public abstract void paint (Graphics g);
+
+ /**
+ * Return a string representation of this edge.
+ */
+ public String toString ()
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append("[");
+ toString(buf);
+ return buf.append("]").toString();
+ }
+
+ /**
+ * This should be overridden by derived classes (which should be sure
+ * to call super.toString()) to append the derived class
+ * specific event information to the string buffer.
+ */
+ public void toString (StringBuffer buf)
+ {
+ buf.append("src=").append(src);
+ buf.append(", dst=").append(dst);
+ }
+}
diff --git a/src/java/com/threerings/nodemap/LayoutManager.java b/src/java/com/threerings/nodemap/LayoutManager.java
new file mode 100644
index 000000000..d646155c1
--- /dev/null
+++ b/src/java/com/threerings/nodemap/LayoutManager.java
@@ -0,0 +1,24 @@
+//
+// $Id: LayoutManager.java,v 1.1 2001/08/20 22:56:55 shaper Exp $
+
+package com.threerings.nodemap;
+
+import java.util.List;
+
+/**
+ * The layout manager class provides an interface that lays out nodes
+ * in a node map via some preordained desirable methodology.
+ */
+public interface LayoutManager
+{
+ /**
+ * Lay out the nodes in the list in the fashion dictated by this
+ * particular layout manager. The node positions are updated via
+ * {@link Node#setPosition} such that subsequent rendering of the
+ * nodes will place them in appropriate positions.
+ *
+ * @param root the root node for the graph.
+ * @param nodes the list of nodes.
+ */
+ public void layout (Node root, List nodes);
+}
diff --git a/src/java/com/threerings/nodemap/Log.java b/src/java/com/threerings/nodemap/Log.java
new file mode 100644
index 000000000..6dce46501
--- /dev/null
+++ b/src/java/com/threerings/nodemap/Log.java
@@ -0,0 +1,38 @@
+//
+// $Id: Log.java,v 1.1 2001/08/20 22:56:55 shaper Exp $
+
+package com.threerings.nodemap;
+
+/**
+ * A placeholder class that contains a reference to the log object used by
+ * the nodemap package.
+ */
+public class Log
+{
+ public static com.samskivert.util.Log log =
+ new com.samskivert.util.Log("nodemap");
+
+ /** Convenience function. */
+ public static void debug (String message)
+ {
+ log.debug(message);
+ }
+
+ /** Convenience function. */
+ public static void info (String message)
+ {
+ log.info(message);
+ }
+
+ /** Convenience function. */
+ public static void warning (String message)
+ {
+ log.warning(message);
+ }
+
+ /** Convenience function. */
+ public static void logStackTrace (Throwable t)
+ {
+ log.logStackTrace(com.samskivert.util.Log.WARNING, t);
+ }
+}
diff --git a/src/java/com/threerings/nodemap/NoSuchNodeException.java b/src/java/com/threerings/nodemap/NoSuchNodeException.java
new file mode 100644
index 000000000..5ef4c9c20
--- /dev/null
+++ b/src/java/com/threerings/nodemap/NoSuchNodeException.java
@@ -0,0 +1,15 @@
+//
+// $Id: NoSuchNodeException.java,v 1.1 2001/08/20 22:56:55 shaper Exp $
+
+package com.threerings.nodemap;
+
+/**
+ * Thrown when a node cannot be found in the node map.
+ */
+public class NoSuchNodeException extends NodeMapException
+{
+ public NoSuchNodeException ()
+ {
+ super("error.no_such_node");
+ }
+}
diff --git a/src/java/com/threerings/nodemap/Node.java b/src/java/com/threerings/nodemap/Node.java
new file mode 100644
index 000000000..d983015c1
--- /dev/null
+++ b/src/java/com/threerings/nodemap/Node.java
@@ -0,0 +1,179 @@
+//
+// $Id: Node.java,v 1.1 2001/08/20 22:56:55 shaper Exp $
+
+package com.threerings.nodemap;
+
+import java.awt.Graphics;
+import java.awt.Point;
+import java.util.*;
+
+/**
+ * The node class represents a single node in a directed graph of nodes.
+ *
+ * @see NodeMap
+ */
+public abstract class Node
+{
+ /**
+ * Construct a node with the given attributes.
+ *
+ * @param name the node name.
+ * @param width the width in pixels.
+ * @param height the height in pixels.
+ */
+ public Node (String name, int width, int height)
+ {
+ _name = name;
+ _width = width;
+ _height = height;
+
+ _pos = new Point();
+ _edges = new ArrayList();
+ }
+
+ /**
+ * Add a connecting edge to this node. An edge may not be
+ * added to the node more than once.
+ *
+ * @param e the new edge.
+ */
+ protected void addEdge (Edge e) throws DuplicateEdgeException
+ {
+ // don't allow adding the same edge object twice
+ if (_edges.contains(e)) {
+ throw new DuplicateEdgeException();
+ }
+
+ // save off the new connecting edge
+ _edges.add(e);
+ }
+
+ /**
+ * Return an Iterator object that iterates over the
+ * edges leaving this node.
+ *
+ * @return the iterator object.
+ */
+ public Iterator getEdges ()
+ {
+ return (Collections.unmodifiableList(_edges)).iterator();
+ }
+
+ /**
+ * Return the node's current position in pixels.
+ *
+ * @return point the node position.
+ */
+ public Point getPosition ()
+ {
+ return _pos;
+ }
+
+ /**
+ * Set the node's position in pixels.
+ *
+ * @param x the x-position.
+ * @param y the y-position.
+ */
+ public void setPosition (int x, int y)
+ {
+ _pos.setLocation(x, y);
+ }
+
+ /**
+ * Return whether the node contains the given point.
+ *
+ * @param x the x-position.
+ * @param y the y-position.
+ *
+ * @return true if the node contains the point, false if not.
+ */
+ public boolean contains (int x, int y)
+ {
+ return (x >= _pos.x && x <= (_pos.x + _width) &&
+ y >= _pos.y && y <= (_pos.y + _height));
+ }
+
+ /**
+ * Return the node width in pixels.
+ */
+ public int getWidth ()
+ {
+ return _width;
+ }
+
+ /**
+ * Return the node height in pixels.
+ */
+ public int getHeight ()
+ {
+ return _height;
+ }
+
+ /**
+ * Render the node to the given graphics context.
+ *
+ * @param g the graphics context.
+ */
+ public abstract void paint (Graphics g);
+
+ /**
+ * Render the node's connecting edges to the given graphics context.
+ *
+ * @param g the graphics context.
+ */
+ public void paintEdges (Graphics g)
+ {
+ int size = _edges.size();
+ for (int ii = 0; ii < size; ii++) {
+ ((Edge)_edges.get(ii)).paint(g);
+ }
+ }
+
+ /**
+ * Handle mouse-entered events for this node.
+ */
+ public void handleMouseEntered ()
+ {
+ Log.info("handleMouseEntered [n=" + this + "].");
+ }
+
+ /**
+ * Handle mouse-exited events for this node.
+ */
+ public void handleMouseExited ()
+ {
+ Log.info("handleMouseExited [n=" + this + "].");
+ }
+
+ /**
+ * Handle mouse-clicked events for this node.
+ *
+ * @param x the x-coordinate in intra-node pixels.
+ * @param y the y-coordinate in intra-node pixels.
+ */
+ public void handleMouseClicked (int x, int y)
+ {
+ Log.info("handleMouseClicked [n=" + this + "].");
+ }
+
+ /**
+ * Return a string representation of this node.
+ */
+ public String toString ()
+ {
+ return _name;
+ }
+
+ /** The position in pixel coordinates. */
+ protected Point _pos;
+
+ /** The dimensions in pixel coordinates. */
+ protected int _width, _height;
+
+ /** The node name. */
+ protected String _name;
+
+ /** The edges leaving this node. */
+ protected ArrayList _edges;
+}
diff --git a/src/java/com/threerings/nodemap/NodeMap.java b/src/java/com/threerings/nodemap/NodeMap.java
new file mode 100644
index 000000000..b4a0634a7
--- /dev/null
+++ b/src/java/com/threerings/nodemap/NodeMap.java
@@ -0,0 +1,270 @@
+//
+// $Id: NodeMap.java,v 1.1 2001/08/20 22:56:55 shaper Exp $
+
+package com.threerings.nodemap;
+
+import java.awt.*;
+import java.util.*;
+
+/**
+ * The node map class represents a directed graph of nodes comprised
+ * of {@link Node} objects. A user-specifiable {@link LayoutManager}
+ * is used to lay out the nodes in the graph.
+ */
+public class NodeMap
+{
+ /**
+ * Construct a node map object with the given layout manager.
+ *
+ * @param lgmr the layout manager.
+ */
+ public NodeMap (LayoutManager lmgr)
+ {
+ _lmgr = lmgr;
+ _nodes = new ArrayList();
+ _dirty = false;
+ }
+
+ /**
+ * Force the nodes in the map to be re-laid out.
+ */
+ public void layout ()
+ {
+ // do nothing if we have no nodes
+ if (_nodes.size() == 0) return;
+
+ // default to using the first node as the root
+ if (_root == null) {
+ _root = (Node)_nodes.get(0);
+ }
+
+ // lay out the nodes
+ _lmgr.layout(_root, _nodes);
+
+ // update bounding node positions
+ updateBounds();
+
+ // note that we're no longer dirty
+ _dirty = false;
+ }
+
+ /**
+ * Calculate the bounding rectangle that wholly contains the node map.
+ */
+ protected void updateBounds ()
+ {
+ Iterator iter = _nodes.iterator();
+ while (iter.hasNext()) {
+ Node n = (Node)iter.next();
+ Point pos = n.getPosition();
+ int wid = n.getWidth(), hei = n.getHeight();
+
+ _minx = Math.min(_minx, pos.x);
+ _maxx = Math.max(_maxx, pos.x + wid);
+ _miny = Math.min(_miny, pos.y);
+ _maxy = Math.max(_maxy, pos.y + hei);
+ }
+ }
+
+ /**
+ * Render the node map to the given graphics context.
+ *
+ * @param g the graphics context.
+ */
+ public void paint (Graphics g)
+ {
+ // lay out the nodes if necessary
+ if (_dirty) {
+ layout();
+ }
+
+ // translate context to node map origin
+ g.translate(-_minx, -_miny);
+
+ // render all node edges
+ int size = _nodes.size();
+ for (int ii = 0; ii < size; ii++) {
+ Node n = (Node)_nodes.get(ii);
+ n.paintEdges(g);
+ }
+
+ // render all nodes
+ for (int ii = 0; ii < size; ii++) {
+ Node n = (Node)_nodes.get(ii);
+ n.paint(g);
+ }
+
+ // highlight the last node entered
+ if (_lastnode != null) {
+ g.setColor(Color.red);
+ Point pos = _lastnode.getPosition();
+ int wid = _lastnode.getWidth(), hei = _lastnode.getHeight();
+ g.drawRect(pos.x, pos.y, wid, hei);
+ }
+
+ // restore original origin
+ g.translate(_minx, _miny);
+ }
+
+ /**
+ * Add the given node to the node map. A given node may not be
+ * present in the node map more than once.
+ *
+ * @param n the node to add.
+ */
+ public void addNode (Node n) throws DuplicateNodeException
+ {
+ // don't allow adding a node more than once
+ if (_nodes.contains(n)) {
+ throw new DuplicateNodeException();
+ }
+
+ // add the node
+ _nodes.add(n);
+ _dirty = true;
+ }
+
+ /**
+ * Add the given edge to the node. Edges may not be added to a
+ * node more than once, or to a node that is not present in the
+ * node map.
+ *
+ * @param n the node.
+ * @param e the edge to add.
+ */
+ public void addEdge (Node n, Edge e)
+ throws NoSuchNodeException, DuplicateEdgeException
+ {
+ // don't allow adding an edge to an unknown node
+ if (!_nodes.contains(n)) {
+ throw new NoSuchNodeException();
+ }
+
+ // add the edge to the node
+ n.addEdge(e);
+ _dirty = true;
+ }
+
+ /**
+ * Set the root node for the node map. The given node must be
+ * present in the node map.
+ *
+ * @param n the root node.
+ */
+ public void setRootNode (Node n) throws NoSuchNodeException
+ {
+ if (!_nodes.contains(n)) {
+ throw new NoSuchNodeException();
+ }
+
+ _root = n;
+ _dirty = true;
+ }
+
+ /**
+ * Return the dimensions of the node map in pixels.
+ */
+ public Dimension getSize ()
+ {
+ // lay out the nodes if necessary
+ if (_dirty) {
+ layout();
+ }
+
+ return new Dimension(_maxx - _minx, _maxy - _miny);
+ }
+
+ /**
+ * Handle mouse-moved events passed on by the containing panel.
+ * Inform any affected nodes of mouse-entered and mouse-exited
+ * events.
+ *
+ * @param x the mouse x-coordinate.
+ * @param y the ymouse y-coordinate.
+ */
+ public void handleMouseMoved (int x, int y)
+ {
+ Node enternode = null;
+
+ // translate coordinates to node map origin
+ x += _minx;
+ y += _miny;
+
+ int size = _nodes.size();
+ for (int ii = 0; ii < size; ii++) {
+ Node n = (Node)_nodes.get(ii);
+
+ if (n.contains(x, y)) {
+
+ // bail if we're in the same node we were in last
+ if (_lastnode == n) return;
+
+ // note that we're entering this node
+ enternode = n;
+
+ break;
+ }
+ }
+
+ // exit the last node, if any
+ if (_lastnode != null) {
+ _lastnode.handleMouseExited();
+ }
+
+ // enter the new node, if any
+ if (enternode != null) {
+ enternode.handleMouseEntered();
+ }
+
+ // update the last node entered
+ _lastnode = enternode;
+ }
+
+ /**
+ * Handle mouse-clicked events passed on by the containing panel.
+ * Inform any affected nodes of mouse-clicked events.
+ *
+ * @param x the mouse x-coordinate.
+ * @param y the ymouse y-coordinate.
+ */
+ public void handleMouseClicked (int x, int y)
+ {
+ // translate coordinates to node map origin
+ x += _minx;
+ y += _miny;
+
+ int size = _nodes.size();
+ for (int ii = 0; ii < size; ii++) {
+ Node n = (Node)_nodes.get(ii);
+
+ if (n.contains(x, y)) {
+
+ // inform the node of the mouse click, with
+ // coordinates translated to suit node interior
+ Point pos = n.getPosition();
+ n.handleMouseClicked(x - pos.x, y - pos.y);
+
+ // nodes can't overlap for now, so we're done
+ return;
+ }
+ }
+ }
+
+ /** The layout manager. */
+ protected LayoutManager _lmgr;
+
+ /** The nodes in the node map. */
+ protected ArrayList _nodes;
+
+ /** The root node. */
+ protected Node _root;
+
+ /** The bounding box coordinates. */
+ protected int _minx, _maxx, _miny, _maxy;
+
+ /** The last node the mouse entered. */
+ protected Node _lastnode;
+
+ /** Whether the node map needs to be laid out. */
+ protected boolean _dirty;
+}
diff --git a/src/java/com/threerings/nodemap/NodeMapException.java b/src/java/com/threerings/nodemap/NodeMapException.java
new file mode 100644
index 000000000..d6cf688ec
--- /dev/null
+++ b/src/java/com/threerings/nodemap/NodeMapException.java
@@ -0,0 +1,16 @@
+//
+// $Id: NodeMapException.java,v 1.1 2001/08/20 22:56:55 shaper Exp $
+
+package com.threerings.nodemap;
+
+/**
+ * Thrown when an error occurs while performing certain activities
+ * involving a node map object.
+ */
+public class NodeMapException extends Exception
+{
+ public NodeMapException (String message)
+ {
+ super(message);
+ }
+}
diff --git a/src/java/com/threerings/nodemap/NodeMapPanel.java b/src/java/com/threerings/nodemap/NodeMapPanel.java
new file mode 100644
index 000000000..d13af7487
--- /dev/null
+++ b/src/java/com/threerings/nodemap/NodeMapPanel.java
@@ -0,0 +1,61 @@
+//
+// $Id: NodeMapPanel.java,v 1.1 2001/08/20 22:56:55 shaper Exp $
+
+package com.threerings.nodemap;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+
+/**
+ * The node map panel handles display of a node map and passes Swing
+ * UI events on to the appropriate node map event handling methods.
+ */
+public class NodeMapPanel extends JPanel
+ implements MouseListener, MouseMotionListener
+{
+ public NodeMapPanel (NodeMap map)
+ {
+ _map = map;
+
+ // listen to our mouse events
+ addMouseListener(this);
+ addMouseMotionListener(this);
+ }
+
+ public void paintComponent (Graphics g)
+ {
+ super.paintComponent(g);
+ _map.paint(g);
+ }
+
+ public Dimension getPreferredSize ()
+ {
+ return _map.getSize();
+ }
+
+ /** MouseListener interface methods */
+
+ public void mouseClicked (MouseEvent e)
+ {
+ _map.handleMouseClicked(e.getX(), e.getY());
+ }
+
+ public void mousePressed (MouseEvent e) { }
+ public void mouseReleased (MouseEvent e) { }
+ public void mouseEntered (MouseEvent e) { }
+ public void mouseExited (MouseEvent e) { }
+
+ /** MouseMotionListener interface methods */
+
+ public void mouseMoved (MouseEvent e)
+ {
+ _map.handleMouseMoved(e.getX(), e.getY());
+ repaint();
+ }
+
+ public void mouseDragged (MouseEvent e) { }
+
+ /** The node map. */
+ protected NodeMap _map;
+}
diff --git a/src/java/com/threerings/nodemap/direction/DirectionalEdge.java b/src/java/com/threerings/nodemap/direction/DirectionalEdge.java
new file mode 100644
index 000000000..c7d0a579c
--- /dev/null
+++ b/src/java/com/threerings/nodemap/direction/DirectionalEdge.java
@@ -0,0 +1,35 @@
+//
+// $Id: DirectionalEdge.java,v 1.1 2001/08/20 22:56:55 shaper Exp $
+
+package com.threerings.nodemap.direction;
+
+import java.awt.*;
+
+import com.threerings.nodemap.*;
+
+public class DirectionalEdge extends Edge
+{
+ /** The edge direction as a {@link Directions} constant. */
+ public int dir;
+
+ public DirectionalEdge (Node src, Node dst, int dir)
+ {
+ super(src, dst);
+ this.dir = dir;
+ }
+
+ public void paint (Graphics g)
+ {
+ g.setColor(Color.black);
+
+ Point spos = src.getPosition(), dpos = dst.getPosition();
+
+ int csx = spos.x + (src.getWidth() / 2);
+ int csy = spos.y + (src.getHeight() / 2);
+
+ int cdx = dpos.x + (dst.getWidth() / 2);
+ int cdy = dpos.y + (dst.getHeight() / 2);
+
+ g.drawLine(csx, csy, cdx, cdy);
+ }
+}
diff --git a/src/java/com/threerings/nodemap/direction/DirectionalLayoutManager.java b/src/java/com/threerings/nodemap/direction/DirectionalLayoutManager.java
new file mode 100644
index 000000000..2f164a7ab
--- /dev/null
+++ b/src/java/com/threerings/nodemap/direction/DirectionalLayoutManager.java
@@ -0,0 +1,100 @@
+//
+// $Id: DirectionalLayoutManager.java,v 1.1 2001/08/20 22:56:55 shaper Exp $
+
+package com.threerings.nodemap.direction;
+
+import java.util.*;
+
+import com.threerings.nodemap.*;
+
+/**
+ * The directional layout manager lays nodes out according to the
+ * eight cardinal directions as specified in the {@link Directions}
+ * class. The nodes must be connected with {@link DirectionalEdge}
+ * edges.
+ */
+public class DirectionalLayoutManager implements LayoutManager
+{
+ public DirectionalLayoutManager (int edgelen)
+ {
+ _edgelen = edgelen;
+ _closed = new HashMap();
+ }
+
+ public void layout (Node root, List nodes)
+ {
+ _closed.clear();
+
+ // lay out all nodes
+ layoutNode(root, 0, 0, root);
+ }
+
+ protected void layoutNode (Node root, int x, int y, Node n)
+ {
+ // consider each node only once for now
+ if (_closed.containsKey(n)) {
+ return;
+ }
+
+ // set the node's location
+ n.setPosition(x, y);
+
+ // remember that we've placed this node
+ _closed.put(n, null);
+
+ Iterator iter = n.getEdges();
+ while (iter.hasNext()) {
+ DirectionalEdge e = (DirectionalEdge)iter.next();
+
+ int tx = x, ty = y;
+ int wid = e.dst.getWidth(), hei = e.dst.getHeight();
+
+ // shift the node in the appropriate direction
+ switch (e.dir) {
+ case Directions.NORTH:
+ tx -= (_edgelen + wid);
+ ty -= (_edgelen + hei);
+ break;
+
+ case Directions.NORTHEAST:
+ ty -= (_edgelen + hei);
+ break;
+
+ case Directions.EAST:
+ tx += (_edgelen + wid);
+ ty -= (_edgelen + hei);
+ break;
+
+ case Directions.SOUTHEAST:
+ tx += (_edgelen + wid);
+ break;
+
+ case Directions.SOUTH:
+ tx += (_edgelen + wid);
+ ty += (_edgelen + hei);
+ break;
+
+ case Directions.SOUTHWEST:
+ ty += (_edgelen + hei);
+ break;
+
+ case Directions.WEST:
+ tx -= (_edgelen + wid);
+ ty += (_edgelen + hei);
+ break;
+
+ case Directions.NORTHWEST:
+ tx -= (_edgelen + wid);
+ break;
+ }
+
+ layoutNode(root, tx, ty, e.dst);
+ }
+ }
+
+ /** The length of the edges between nodes in pixels. */
+ protected int _edgelen;
+
+ /** The set of nodes already positioned. */
+ protected HashMap _closed;
+}
diff --git a/src/java/com/threerings/nodemap/direction/Directions.java b/src/java/com/threerings/nodemap/direction/Directions.java
new file mode 100644
index 000000000..66f8ed329
--- /dev/null
+++ b/src/java/com/threerings/nodemap/direction/Directions.java
@@ -0,0 +1,24 @@
+//
+// $Id: Directions.java,v 1.1 2001/08/20 22:56:55 shaper Exp $
+
+package com.threerings.nodemap.direction;
+
+/**
+ * This class provides a holding place for constants referenced by the
+ * classes that support and make use of directional node maps.
+ */
+public class Directions
+{
+ /** Constants for the eight cardinal compass-point directions. */
+ public static final int NORTH = 0;
+ public static final int NORTHEAST = 1;
+ public static final int EAST = 2;
+ public static final int SOUTHEAST = 3;
+ public static final int SOUTH = 4;
+ public static final int SOUTHWEST = 5;
+ public static final int WEST = 6;
+ public static final int NORTHWEST = 7;
+
+ /** The number of directions. */
+ public static final int NUM_DIRECTIONS = 8;
+}