//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/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.media.util;
import java.awt.Point;
import java.util.*;
import com.samskivert.util.HashIntMap;
import com.threerings.media.util.MathUtil;
/**
* The AStarPathUtil class provides a facility for
* finding a reasonable path between two points in a scene using the
* A* search algorithm.
*
*
See the path-finding article on
*
* Gamasutra for more detailed information.
*/
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);
}
/**
* Provides extended traversibility information when computing paths.
*/
public static interface ExtendedTraversalPred extends TraversalPred
{
/**
* Requests to know if the specific traverser (which was provided
* in the call to {@link #getPath}) can traverse from the specified
* source tile coordinate to the specified destination tile
* coodinate.
*/
public boolean canTraverse (
Object traverser, int sx, int sy, int dx, int dy);
}
/**
* Considers all the possible steps the piece in question can take.
*/
public static class Stepper
{
public void init (Info info, Node n)
{
_info = info;
_node = n;
}
/**
* Should call {@link #considerStep} in turn on all possible steps
* from the specified coordinates. No checking must be done as to
* whether the step is legal, that will be handled later. Just
* enumerate all possible steps.
*/
public void considerSteps (int x, int y)
{
considerStep(x - 1, y - 1, DIAGONAL_COST);
considerStep(x, y - 1, ADJACENT_COST);
considerStep(x + 1, y - 1, DIAGONAL_COST);
considerStep(x - 1, y, ADJACENT_COST);
considerStep(x + 1, y, ADJACENT_COST);
considerStep(x - 1, y + 1, DIAGONAL_COST);
considerStep(x, y + 1, ADJACENT_COST);
considerStep(x + 1, y + 1, DIAGONAL_COST);
}
protected void considerStep (int x, int y, int cost)
{
AStarPathUtil.considerStep(_info, _node, x, y, cost);
}
protected Info _info;
protected Node _node;
}
/** 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);
/**
* Return a list of Point objects representing a path
* from coordinates (ax, by) to (bx, by),
* inclusive, determined by performing an A* search in the given
* scene's base tile layer. Assumes the starting and destination nodes
* are traversable by the specified traverser.
*
* @param tpred lets us know what tiles are traversible.
* @param stepper enumerates the possible steps.
* @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 ay the starting y-position in tile coordinates.
* @param bx the ending x-position in tile coordinates.
* @param by the ending y-position in tile coordinates.
* @param partial if true, a partial path will be returned that gets
* us as close as we can to the goal in the event that a complete path
* cannot be located.
*
* @return the list of points in the path.
*/
public static List(n.x, n.y) to (x, y)
* for possible inclusion in the path.
*
* @param info the info object.
* @param n the originating node for the step.
* @param x the x-coordinate for the destination step.
* @param y the y-coordinate for the destination step.
*/
protected static void considerStep (
Info info, Node n, int x, int y, int cost)
{
// skip node if it's outside the map bounds or otherwise impassable
if (!info.isStepValid(n.x, n.y, x, y)) {
return;
}
// calculate the new cost for this node
int newg = n.g + cost;
// make sure the cost is reasonable
if (newg > info.maxcost) {
// Log.info("Rejected costly step.");
return;
}
// retrieve the node corresponding to this location
Node np = info.getNode(x, y);
// skip if it's already in the open or closed list or if its
// actual cost is less than the just-calculated cost
if ((info.open.contains(np) || info.closed.contains(np)) &&
np.g <= newg) {
return;
}
// remove the node from the open list since we're about to
// modify its score which determines its placement in the list
info.open.remove(np);
// update the node's information
np.parent = n;
np.g = newg;
np.h = getDistanceEstimate(np.x, np.y, info.destx, info.desty);
np.f = np.g + np.h;
// remove it from the closed list if it's present
info.closed.remove(np);
// add it to the open list for further consideration
info.open.add(np);
_considered++;
}
/**
* Return a list of Point objects detailing the path
* from the first node (the given node's ultimate parent) to the
* ending node (the given node itself.)
*
* @param n the ending node in the path.
*
* @return the list detailing the path.
*/
protected static List(ax,
* ay) to (bx, by).
*/
protected static int getDistanceEstimate (int ax, int ay, int bx, int by)
{
// we're doing all of our cost calculations based on geometric
// distance times ten
int xsq = bx - ax;
int ysq = by - ay;
return (int) (ADJACENT_COST * Math.sqrt(xsq * xsq + ysq * ysq));
}
/**
* A holding class to contain the wealth of information referenced
* while performing an A* search for a path through a tile array.
*/
protected static class Info
{
/** Knows whether or not tiles are traversable. */
public TraversalPred tpred;
/** The tile array dimensions. */
public int tilewid, tilehei;
/** The traverser moving along the path. */
public Object trav;
/** The set of open nodes being searched. */
public SortedSet