Exorcised scene width and height; make models unbounded by default;

cleaned up AStarPathUtil.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2407 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2003-04-12 02:14:52 +00:00
parent d933605343
commit bae9fdf1e0
4 changed files with 195 additions and 248 deletions
@@ -1,9 +1,11 @@
// //
// $Id: DisplayMisoScene.java,v 1.11 2003/04/01 02:17:58 mdb Exp $ // $Id: DisplayMisoScene.java,v 1.12 2003/04/12 02:14:52 mdb Exp $
package com.threerings.miso.client; package com.threerings.miso.client;
import com.threerings.media.tile.Tile; import com.threerings.media.tile.Tile;
import com.threerings.miso.client.util.AStarPathUtil;
import com.threerings.miso.data.MisoScene; import com.threerings.miso.data.MisoScene;
import com.threerings.miso.tile.BaseTile; import com.threerings.miso.tile.BaseTile;
@@ -11,7 +13,8 @@ import com.threerings.miso.tile.BaseTile;
* Extends the {@link MisoScene} with functionality needed only by * Extends the {@link MisoScene} with functionality needed only by
* entities that plan to display a miso scene. * entities that plan to display a miso scene.
*/ */
public interface DisplayMisoScene extends MisoScene public interface DisplayMisoScene
extends MisoScene, AStarPathUtil.TraversalPred
{ {
/** /**
* This will be called before the scene is displayed to give it a * This will be called before the scene is displayed to give it a
@@ -32,10 +35,11 @@ public interface DisplayMisoScene extends MisoScene
/** /**
* Returns true if the supplied traverser can traverse the specified * Returns true if the supplied traverser can traverse the specified
* tile coordinate. The traverser is whatever object is passed along * tile coordinate. The traverser is whatever object is passed along
* to the path finder when a path is being computed. Scene * to the path finder when a path is being computed.
* implementations which support custom traversal based on the type of *
* the traverser will want to reflect the traverser's class and act * <p> Scene implementations which support custom traversal based on
* acordingly. * the type of the traverser will want to reflect the traverser's
* class and act acordingly.
*/ */
public boolean canTraverse (Object traverser, int x, int y); public boolean canTraverse (Object traverser, int x, int y);
} }
@@ -1,5 +1,5 @@
// //
// $Id: IsoSceneView.java,v 1.136 2003/04/07 21:43:00 mdb Exp $ // $Id: IsoSceneView.java,v 1.137 2003/04/12 02:14:52 mdb Exp $
package com.threerings.miso.client; package com.threerings.miso.client;
@@ -515,10 +515,12 @@ public class IsoSceneView implements SceneView
_model, sprite.getX(), sprite.getY(), new Point()); _model, sprite.getX(), sprite.getY(), new Point());
Point dest = IsoUtil.screenToTile(_model, x, y, new Point()); Point dest = IsoUtil.screenToTile(_model, x, y, new Point());
// TODO: compute this value from the screen size or something
int longestPath = 50;
// get a reasonable tile path through the scene // get a reasonable tile path through the scene
List points = AStarPathUtil.getPath( List points = AStarPathUtil.getPath(
_scene, _model.scenewid, _model.scenehei, _scene, sprite, longestPath, src.x, src.y, dest.x, dest.y);
sprite, src.x, src.y, dest.x, dest.y);
// construct a path object to guide the sprite on its merry way // construct a path object to guide the sprite on its merry way
return (points == null) ? null : return (points == null) ? null :
@@ -1,5 +1,5 @@
// //
// $Id: IsoSceneViewModel.java,v 1.28 2003/01/31 23:10:45 mdb Exp $ // $Id: IsoSceneViewModel.java,v 1.29 2003/04/12 02:14:52 mdb Exp $
package com.threerings.miso.client; package com.threerings.miso.client;
@@ -30,15 +30,12 @@ public class IsoSceneViewModel
/** Number of fine coordinates on each axis within a tile. */ /** Number of fine coordinates on each axis within a tile. */
public int finegran; public int finegran;
/** Scene dimensions in tile count. */
public int scenewid, scenehei;
/** Size of the view in tile count. */ /** Size of the view in tile count. */
public int scenevwid, scenevhei; public int scenevwid, scenevhei;
/** Whether or not this view can extend beyond the bounds defined by /** Whether or not this view can extend beyond the bounds defined by
* the view width and height. True if it cannot, false if it can. */ * the view width and height. True if it cannot, false if it can. */
public boolean bounded = true; public boolean bounded = false;
/** The bounds of the view in screen pixel coordinates. */ /** The bounds of the view in screen pixel coordinates. */
public Rectangle bounds; public Rectangle bounds;
@@ -46,21 +43,12 @@ public class IsoSceneViewModel
/** The position in pixels at which tile (0, 0) is drawn. */ /** The position in pixels at which tile (0, 0) is drawn. */
public Point origin; public Point origin;
/** The total number of tile rows to render the full view. */
public int tilerows;
/** The length of a tile edge in pixels. */ /** The length of a tile edge in pixels. */
public float tilelen; public float tilelen;
/** The y-intercept of the x-axis line. */
public int bX;
/** The slope of the x- and y-axis lines. */ /** The slope of the x- and y-axis lines. */
public float slopeX, slopeY; public float slopeX, slopeY;
/** The x-axis line. */
public Point[] lineX;
/** The length between fine coordinates in pixels. */ /** The length between fine coordinates in pixels. */
public float finelen; public float finelen;
@@ -78,12 +66,6 @@ public class IsoSceneViewModel
*/ */
public IsoSceneViewModel () public IsoSceneViewModel ()
{ {
// set the scene tile dimensions
scenewid = MisoConfig.config.getValue(
SCENE_WIDTH_KEY, DEF_SCENE_WIDTH);
scenehei = MisoConfig.config.getValue(
SCENE_HEIGHT_KEY, DEF_SCENE_HEIGHT);
// and the view dimensions // and the view dimensions
scenevwid = MisoConfig.config.getValue( scenevwid = MisoConfig.config.getValue(
SCENE_VWIDTH_KEY, DEF_SCENE_VWIDTH); SCENE_VWIDTH_KEY, DEF_SCENE_VWIDTH);
@@ -122,8 +104,6 @@ public class IsoSceneViewModel
int svwid, int svhei, int offy) int svwid, int svhei, int offy)
{ {
// keep track of this stuff // keep track of this stuff
this.scenewid = scenewid;
this.scenehei = scenehei;
this.tilewid = tilewid; this.tilewid = tilewid;
this.tilehei = tilehei; this.tilehei = tilehei;
this.finegran = finegran; this.finegran = finegran;
@@ -137,13 +117,14 @@ public class IsoSceneViewModel
} }
/** /**
* Returns whether the given tile coordinate is a valid coordinate * Returns whether the given tile coordinate is a valid coordinate in
* within the scene. * our coordinate system (which allows tile coordinates from 0 to
* 2^15-1).
*/ */
public boolean isCoordinateValid (int x, int y) public boolean isCoordinateValid (int x, int y)
{ {
return (x >= 0 && x < scenewid && return (x >= 0 && x < Short.MAX_VALUE &&
y >= 0 && y < scenehei); y >= 0 && y < Short.MAX_VALUE);
} }
/** /**
@@ -174,26 +155,6 @@ public class IsoSceneViewModel
// pre-calculate tile-related data // pre-calculate tile-related data
precalculateTiles(); precalculateTiles();
// calculate scene-based x-axis line for conversion from
// screen to tile coordinates
// create the x- and y-axis lines
lineX = new Point[2];
for (int ii = 0; ii < 2; ii++) {
lineX[ii] = new Point();
}
// determine the starting point
lineX[0].setLocation(origin.x, origin.y);
bX = (int)-(slopeX * origin.x);
// determine the ending point
lineX[1].x = lineX[0].x + (tilehwid * scenewid);
lineX[1].y = lineX[0].y + (int)((slopeX * lineX[1].x) + bX);
// calculate tile-based x-axis line for conversion from
// tile-based pixel to fine coordinates
// calculate the edge length separating each fine coordinate // calculate the edge length separating each fine coordinate
finelen = tilelen / (float)finegran; finelen = tilelen / (float)finegran;
@@ -220,9 +181,6 @@ public class IsoSceneViewModel
tilelen = (float) Math.sqrt( tilelen = (float) Math.sqrt(
(tilehwid * tilehwid) + (tilehhei * tilehhei)); (tilehwid * tilehwid) + (tilehhei * tilehhei));
// calculate the number of tile rows to render
tilerows = (scenewid * scenehei) - 1;
// calculate the slope of the x- and y-axis lines // calculate the slope of the x- and y-axis lines
slopeX = (float)tilehei / (float)tilewid; slopeX = (float)tilehei / (float)tilewid;
slopeY = -slopeX; slopeY = -slopeX;
@@ -1,5 +1,5 @@
// //
// $Id: AStarPathUtil.java,v 1.26 2003/04/07 23:53:41 mdb Exp $ // $Id: AStarPathUtil.java,v 1.27 2003/04/12 02:14:52 mdb Exp $
package com.threerings.miso.client.util; package com.threerings.miso.client.util;
@@ -11,7 +11,6 @@ import com.samskivert.util.HashIntMap;
import com.threerings.media.util.MathUtil; import com.threerings.media.util.MathUtil;
import com.threerings.miso.Log; import com.threerings.miso.Log;
import com.threerings.miso.client.DisplayMisoScene;
import com.threerings.miso.tile.BaseTile; import com.threerings.miso.tile.BaseTile;
/** /**
@@ -25,6 +24,19 @@ import com.threerings.miso.tile.BaseTile;
*/ */
public class AStarPathUtil public class AStarPathUtil
{ {
/**
* Provides traversibility information when computing paths.
*/
public static interface TraversalPred
{
/**
* Requests to know if the specified traverser (which was provided
* in the call to {@link #getPath}) can traverse the specified
* tile coordinate.
*/
public boolean canTraverse (Object traverser, int x, int y);
}
/** /**
* Return a list of <code>Point</code> objects representing a path * Return a list of <code>Point</code> objects representing a path
* from coordinates <code>(ax, by)</code> to <code>(bx, by)</code>, * from coordinates <code>(ax, by)</code> to <code>(bx, by)</code>,
@@ -32,10 +44,9 @@ public class AStarPathUtil
* scene's base tile layer. Assumes the starting and destination nodes * scene's base tile layer. Assumes the starting and destination nodes
* are traversable by the specified traverser. * are traversable by the specified traverser.
* *
* @param scene the scene in which a path is to be computed. * @param tpred lets us know what tiles are traversible.
* @param tilewid the scene width in tiles.
* @param tilehei the scene height in tiles.
* @param trav the traverser to follow the path. * @param trav the traverser to follow the path.
* @param longest the longest allowable path in tile traversals.
* @param ax the starting x-position in tile coordinates. * @param ax the starting x-position in tile coordinates.
* @param ay the starting y-position in tile coordinates. * @param ay the starting y-position in tile coordinates.
* @param bx the ending x-position in tile coordinates. * @param bx the ending x-position in tile coordinates.
@@ -43,14 +54,13 @@ public class AStarPathUtil
* *
* @return the list of points in the path. * @return the list of points in the path.
*/ */
public static List getPath ( public static List getPath (TraversalPred tpred, Object trav,
DisplayMisoScene scene, int tilewid, int tilehei, Object trav, int longest, int ax, int ay, int bx, int by)
int ax, int ay, int bx, int by)
{ {
AStarInfo info = new AStarInfo(scene, tilewid, tilehei, trav, bx, by); Info info = new Info(tpred, trav, longest, bx, by);
// set up the starting node // set up the starting node
AStarNode s = info.getNode(ax, ay); Node s = info.getNode(ax, ay);
s.g = 0; s.g = 0;
s.h = getDistanceEstimate(ax, ay, bx, by); s.h = getDistanceEstimate(ax, ay, bx, by);
s.f = s.g + s.h; s.f = s.g + s.h;
@@ -62,7 +72,7 @@ public class AStarPathUtil
while (info.open.size() > 0) { while (info.open.size() > 0) {
// pop the best node so far from open // pop the best node so far from open
AStarNode n = (AStarNode)info.open.first(); Node n = (Node)info.open.first();
info.open.remove(n); info.open.remove(n);
// if node is a goal node // if node is a goal node
@@ -99,30 +109,24 @@ public class AStarPathUtil
* @param y the y-coordinate for the destination step. * @param y the y-coordinate for the destination step.
*/ */
protected static void considerStep ( protected static void considerStep (
AStarInfo info, AStarNode n, int x, int y, int cost) Info info, Node n, int x, int y, int cost)
{ {
// skip node if it's outside the map bounds or otherwise impassable // skip node if it's outside the map bounds or otherwise impassable
if (!info.isStepValid(n.x, n.y, x, y)) { if (!info.isStepValid(n.x, n.y, x, y)) {
return; return;
} }
// if it's offscreen, bang up the cost considerably
if (!info.isCoordinateValid(x, y)) {
cost += OFFSCREEN_COST;
}
// calculate the new cost for this node // calculate the new cost for this node
int newg = n.g + cost; int newg = n.g + cost;
// make sure the cost is reasonable (so we don't go crazy computing // make sure the cost is reasonable
// offscreen costs)
if (newg > info.maxcost) { if (newg > info.maxcost) {
// Log.info("Rejected costly step."); // Log.info("Rejected costly step.");
return; return;
} }
// retrieve the node corresponding to this location // retrieve the node corresponding to this location
AStarNode np = info.getNode(x, y); Node np = info.getNode(x, y);
// skip if it's already in the open or closed list or if its // skip if it's already in the open or closed list or if its
// actual cost is less than the just-calculated cost // actual cost is less than the just-calculated cost
@@ -157,9 +161,9 @@ public class AStarPathUtil
* *
* @return the list detailing the path. * @return the list detailing the path.
*/ */
protected static List getNodePath (AStarNode n) protected static List getNodePath (Node n)
{ {
AStarNode cur = n; Node cur = n;
ArrayList path = new ArrayList(); ArrayList path = new ArrayList();
while (cur != null) { while (cur != null) {
@@ -187,25 +191,14 @@ public class AStarPathUtil
return (int) (ADJACENT_COST * Math.sqrt(xsq * xsq + ysq * ysq)); return (int) (ADJACENT_COST * Math.sqrt(xsq * xsq + ysq * ysq));
} }
/** The standard cost to move between nodes. */
public static final int ADJACENT_COST = 10;
/** The cost to move diagonally. */
public static final int DIAGONAL_COST = (int) Math.sqrt(
(ADJACENT_COST * ADJACENT_COST) * 2);
/** A big old additional cost incurred for offscreen movement. */
public static final int OFFSCREEN_COST = 1000;
}
/** /**
* A holding class to contain the wealth of information referenced * A holding class to contain the wealth of information referenced
* while performing an A* search for a path through a tile array. * while performing an A* search for a path through a tile array.
*/ */
class AStarInfo protected static class Info
{ {
/** The scene whose base tile layer is being traversed. */ /** Knows whether or not tiles are traversable. */
public DisplayMisoScene scene; public TraversalPred tpred;
/** The tile array dimensions. */ /** The tile array dimensions. */
public int tilewid, tilehei; public int tilewid, tilehei;
@@ -225,38 +218,23 @@ class AStarInfo
/** The maximum cost of any path that we'll consider. */ /** The maximum cost of any path that we'll consider. */
public int maxcost; public int maxcost;
public AStarInfo ( public Info (TraversalPred tpred, Object trav,
DisplayMisoScene scene, int tilewid, int tilehei, Object trav, int longest, int destx, int desty)
int destx, int desty)
{ {
// save off references // save off references
this.scene = scene; this.tpred = tpred;
this.tilewid = tilewid;
this.tilehei = tilehei;
this.trav = trav; this.trav = trav;
this.destx = destx; this.destx = destx;
this.desty = desty; this.desty = desty;
// compute the maximum cost as the maximum onscreen path plus // compute our maximum path cost
// the maximum offscreen cost this.maxcost = longest * ADJACENT_COST;
this.maxcost = ((tilewid + tilehei) * AStarPathUtil.ADJACENT_COST) +
MAX_OFFSCREEN * AStarPathUtil.OFFSCREEN_COST;
// construct the open and closed lists // construct the open and closed lists
open = new TreeSet(); open = new TreeSet();
closed = new ArrayList(); closed = new ArrayList();
} }
/**
* Returns whether the given coordinate is valid based on the
* dimensions of the map being traversed.
*/
protected boolean isCoordinateValid (int x, int y)
{
return (x >= 0 && y >= 0 && x < tilewid && y < tilehei &&
(scene.getBaseTile(x, y) != null));
}
/** /**
* Returns whether moving from the given source to destination * Returns whether moving from the given source to destination
* coordinates is a valid move. * coordinates is a valid move.
@@ -283,20 +261,20 @@ class AStarInfo
*/ */
protected boolean isTraversable (int x, int y) protected boolean isTraversable (int x, int y)
{ {
return scene.canTraverse(trav, x, y); return tpred.canTraverse(trav, x, y);
} }
/** /**
* Get or create the node for the specified point. * Get or create the node for the specified point.
*/ */
public AStarNode getNode (int x, int y) public Node getNode (int x, int y)
{ {
// note: this _could_ break for unusual values of x and y. // note: this _could_ break for unusual values of x and y.
// perhaps use a IntTuple as a key? Bleah. // perhaps use a IntTuple as a key? Bleah.
int key = (x << 16) | (y & 0xffff); int key = (x << 16) | (y & 0xffff);
AStarNode node = (AStarNode) _nodes.get(key); Node node = (Node) _nodes.get(key);
if (node == null) { if (node == null) {
node = new AStarNode(x, y); node = new Node(x, y);
_nodes.put(key, node); _nodes.put(key, node);
} }
return node; return node;
@@ -304,16 +282,13 @@ class AStarInfo
/** The nodes being considered in the path. */ /** The nodes being considered in the path. */
protected HashIntMap _nodes = new HashIntMap(); protected HashIntMap _nodes = new HashIntMap();
/** The maximum number of offscreen points we'll consider. */
protected static final int MAX_OFFSCREEN = 6;
} }
/** /**
* A class that represents a single traversable node in the tile array * A class that represents a single traversable node in the tile array
* along with its current A*-specific search information. * along with its current A*-specific search information.
*/ */
class AStarNode implements Comparable protected static class Node implements Comparable
{ {
/** The node coordinates. */ /** The node coordinates. */
public int x, y; public int x, y;
@@ -328,12 +303,12 @@ class AStarNode implements Comparable
public int f; public int f;
/** The node from which we reached this node. */ /** The node from which we reached this node. */
public AStarNode parent; public Node parent;
/** The node's monotonically-increasing unique identifier. */ /** The node's monotonically-increasing unique identifier. */
public int id; public int id;
public AStarNode (int x, int y) public Node (int x, int y)
{ {
this.x = x; this.x = x;
this.y = y; this.y = y;
@@ -342,7 +317,7 @@ class AStarNode implements Comparable
public int compareTo (Object o) public int compareTo (Object o)
{ {
int bf = ((AStarNode)o).f; int bf = ((Node)o).f;
// since the set contract is fulfilled using the equality results // since the set contract is fulfilled using the equality results
// returned here, and we'd like to allow multiple nodes with // returned here, and we'd like to allow multiple nodes with
@@ -351,7 +326,7 @@ class AStarNode implements Comparable
// unique node id since it will return a consistent ordering for // unique node id since it will return a consistent ordering for
// the objects. // the objects.
if (f == bf) { if (f == bf) {
return (this == o) ? 0 : (id - ((AStarNode)o).id); return (this == o) ? 0 : (id - ((Node)o).id);
} }
return f - bf; return f - bf;
@@ -360,3 +335,11 @@ class AStarNode implements Comparable
/** The next unique node id. */ /** The next unique node id. */
protected static int _nextid = 0; protected static int _nextid = 0;
} }
/** The standard cost to move between nodes. */
public static final int ADJACENT_COST = 10;
/** The cost to move diagonally. */
public static final int DIAGONAL_COST = (int)Math.sqrt(
(ADJACENT_COST * ADJACENT_COST) * 2);
}