Behold, Nenya, Ring of Water and repository for our media and animation related

goodies, both Java 2D and LWJGL/JME 3D.


git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@1 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
Michael Bayne
2006-06-23 18:07:28 +00:00
commit c2117ee86d
570 changed files with 61913 additions and 0 deletions
@@ -0,0 +1,437 @@
//
// $Id: AStarPathUtil.java 3413 2005-03-18 01:13:09Z mdb $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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 <code>AStarPathUtil</code> class provides a facility for
* finding a reasonable path between two points in a scene using the
* A* search algorithm.
*
* <p> See the path-finding article on
* <a href="http://www.gamasutra.com/features/19990212/sm_01.htm">
* Gamasutra</a> 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);
}
/**
* 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 <code>Point</code> objects representing a path
* from coordinates <code>(ax, by)</code> to <code>(bx, by)</code>,
* 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 getPath (TraversalPred tpred, Stepper stepper,
Object trav, int longest, int ax, int ay,
int bx, int by, boolean partial)
{
Info info = new Info(tpred, trav, longest, bx, by);
// set up the starting node
Node s = info.getNode(ax, ay);
s.g = 0;
s.h = getDistanceEstimate(ax, ay, bx, by);
s.f = s.g + s.h;
// push starting node on the open list
info.open.add(s);
_considered = 1;
// track the best path
float bestdist = Float.MAX_VALUE;
Node bestpath = null;
// while there are more nodes on the open list
while (info.open.size() > 0) {
// pop the best node so far from open
Node n = (Node)info.open.first();
info.open.remove(n);
// if node is a goal node
if (n.x == bx && n.y == by) {
// construct and return the acceptable path
return getNodePath(n);
} else if (partial) {
float pathdist = MathUtil.distance(n.x, n.y, bx, by);
if (pathdist < bestdist) {
bestdist = pathdist;
bestpath = n;
}
}
// consider each successor of the node
stepper.init(info, n);
stepper.considerSteps(n.x, n.y);
// push the node on the closed list
info.closed.add(n);
}
// return the best path we could find if we were asked to do so
if (bestpath != null) {
return getNodePath(bestpath);
}
// no path found
return null;
}
/**
* Gets a path with the default stepper which assumes the piece can
* move one in any of the eight cardinal directions.
*/
public static List getPath (TraversalPred tpred, Object trav, int longest,
int ax, int ay, int bx, int by, boolean partial)
{
return getPath(
tpred, new Stepper(), trav, longest, ax, ay, bx, by, partial);
}
/**
* Returns the number of nodes considered in computing the most recent
* path.
*/
public static int getConsidered ()
{
return _considered;
}
/**
* Consider the step <code>(n.x, n.y)</code> to <code>(x, y)</code>
* 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 <code>Point</code> 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 getNodePath (Node n)
{
Node cur = n;
ArrayList path = new ArrayList();
while (cur != null) {
// add to the head of the list since we're traversing from
// the end to the beginning
path.add(0, new Point(cur.x, cur.y));
// advance to the next node in the path
cur = cur.parent;
}
return path;
}
/**
* Return a heuristic estimate of the cost to get from <code>(ax,
* ay)</code> to <code>(bx, by)</code>.
*/
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 open;
/** The set of closed nodes being searched. */
public ArrayList closed;
/** The destination coordinates in the tile array. */
public int destx, desty;
/** The maximum cost of any path that we'll consider. */
public int maxcost;
public Info (TraversalPred tpred, Object trav,
int longest, int destx, int desty)
{
// save off references
this.tpred = tpred;
this.trav = trav;
this.destx = destx;
this.desty = desty;
// compute our maximum path cost
this.maxcost = longest * ADJACENT_COST;
// construct the open and closed lists
open = new TreeSet();
closed = new ArrayList();
}
/**
* Returns whether moving from the given source to destination
* coordinates is a valid move.
*/
protected boolean isStepValid (int sx, int sy, int dx, int dy)
{
// not traversable if the destination itself fails test
if (!isTraversable(dx, dy)) {
return false;
}
// if the step is diagonal, make sure the corners don't impede
// our progress
if ((Math.abs(dx - sx) == 1) && (Math.abs(dy - sy) == 1)) {
return isTraversable(dx, sy) && isTraversable(sx, dy);
}
// non-diagonals are always traversable
return true;
}
/**
* Returns whether the given coordinate is valid and traversable.
*/
protected boolean isTraversable (int x, int y)
{
return tpred.canTraverse(trav, x, y);
}
/**
* Get or create the node for the specified point.
*/
public Node getNode (int x, int y)
{
// note: this _could_ break for unusual values of x and y.
// perhaps use a IntTuple as a key? Bleah.
int key = (x << 16) | (y & 0xffff);
Node node = (Node) _nodes.get(key);
if (node == null) {
node = new Node(x, y);
_nodes.put(key, node);
}
return node;
}
/** The nodes being considered in the path. */
protected HashIntMap _nodes = new HashIntMap();
}
/**
* A class that represents a single traversable node in the tile array
* along with its current A*-specific search information.
*/
protected static class Node implements Comparable
{
/** The node coordinates. */
public int x, y;
/** The actual cheapest cost of arriving here from the start. */
public int g;
/** The heuristic estimate of the cost to the goal from here. */
public int h;
/** The score assigned to this node. */
public int f;
/** The node from which we reached this node. */
public Node parent;
/** The node's monotonically-increasing unique identifier. */
public int id;
public Node (int x, int y)
{
this.x = x;
this.y = y;
id = _nextid++;
}
public int compareTo (Object o)
{
int bf = ((Node)o).f;
// since the set contract is fulfilled using the equality results
// returned here, and we'd like to allow multiple nodes with
// equivalent scores in our set, we explicitly define object
// equivalence as the result of object.equals(), else we use the
// unique node id since it will return a consistent ordering for
// the objects.
if (f == bf) {
return (this == o) ? 0 : (id - ((Node)o).id);
}
return f - bf;
}
/** The next unique node id. */
protected static int _nextid = 0;
}
/** The number of nodes considered in computing our path. */
protected static int _considered = 0;
}
@@ -0,0 +1,238 @@
//
// $Id: ArcPath.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import com.samskivert.util.StringUtil;
import com.threerings.util.DirectionUtil;
/**
* The line path is used to cause a pathable to go from point A to point B
* along the arc of an ellipse in a certain number of milliseconds.
*/
public class ArcPath extends TimedPath
{
/** An orientation constant indicating that the normal (eight)
* directions should be used to orient the pathable being made to
* follow this path. */
public static final int NORMAL = 0;
/** An orientation constant indicating that the fine (sixteen)
* directions should be used to orient the pathable being made to
* follow this path. */
public static final int FINE = 1;
/** An orientation indicating that the pathable should not be oriented
* as it moves along the path.
*/
public static final int NONE = 2;
/**
* Creates an arc path that will animate a pathable from the specified
* starting position along an ellipse defined by the supplied
* parameters. The pathable will travel the specified number of
* radians along the arc of that ellipse. A positive number of radians
* indicates counter-clockwise travel along the circle, a negative
* number, clockwise rotation.
*
* @param start the starting point for the pathable.
* @param xradius the length of the x radius.
* @param yradius the length of the y radius.
* @param sangle the starting angle.
* @param delta the angle through which the pathable should be moved.
* @param duration the number of milliseconds during which to effect
* the animation.
* @param orient an orientation code indicating how the pathable
* should be oriented when following the path, either {@link #NORMAL},
* or {@link #FINE}.
*/
public ArcPath (Point start, double xradius, double yradius,
double sangle, double delta, long duration,
int orient)
{
super(duration);
_xradius = xradius;
_yradius = yradius;
_sangle = sangle;
_delta = delta;
_orient = orient;
// compute the center of the ellipse
_center = new Point(
(int)(start.x - Math.round(Math.cos(sangle) * xradius)),
(int)(start.y - Math.round(Math.sin(sangle) * yradius)));
}
/**
* Return a copy of the path, translated by the specified amounts.
*/
public Path getTranslatedInstance (int x, int y)
{
int startx =
(int)(_center.x + Math.round(Math.cos(_sangle) * _xradius));
int starty =
(int)(_center.y + Math.round(Math.sin(_sangle) * _yradius));
return new ArcPath(new Point (startx + x, starty + y),
_xradius, _yradius, _sangle, _delta, _duration, _orient);
}
/**
* Sets the offset that is applied to the pathable whenever it is
* oriented. This offset is in clockwise units whose granularity is
* specified by the {@link #NORMAL} or {@link #FINE} setting supplied
* to the path at construct time. The intent here is to allow arc
* paths to be applied that don't orient the pathable in the direction
* they are traveling but instead in some fixed offset from that
* direction.
*/
public void setOrientOffset (int offset)
{
_orientOffset = offset;
}
// documentation inherited
public boolean tick (Pathable pable, long timestamp)
{
double angle;
boolean modified = false;
// if we've blown past our arrival time...
if (timestamp >= _startStamp + _duration) {
// ...force the angle to the destination angle
angle = _sangle + _delta;
} else {
// otherwise, compute the angle at which we should place the
// pathable based on the elapsed time
long elapsed = timestamp - _startStamp;
angle = _sangle + _delta * elapsed / _duration;
}
// determine where we should be along the path
computePosition(_center, _xradius, _yradius, angle, _tpos);
// Skip this if we are not reorienting as we follow the path.
if (_orient != NONE) {
// compute the pathable's new orientation
double theta = angle + ((_delta > 0) ? Math.PI/2 : -Math.PI/2);
int orient;
switch (_orient) {
default:
case NORMAL:
orient = DirectionUtil.getDirection(theta);
// adjust it appropriately
orient = DirectionUtil.rotateCW(orient, 2*_orientOffset);
break;
case FINE:
orient = DirectionUtil.getFineDirection(theta);
// adjust it appropriately
orient = DirectionUtil.rotateCW(orient, _orientOffset);
break;
}
// update the pathable's orientation if it changed
if (pable.getOrientation() != orient) {
pable.setOrientation(orient);
modified = true;
}
}
// update the pathable's location if it moved
if (pable.getX() != _tpos.x || pable.getY() != _tpos.y) {
pable.setLocation(_tpos.x, _tpos.y);
modified = true;
}
// if we completed our path, let the sprite know
if (angle == _sangle + _delta) {
pable.pathCompleted(timestamp);
}
return modified;
}
// documentation inherited
public void paint (Graphics2D gfx)
{
int x = (int)(_center.x - _xradius), y = (int)(_center.y - _yradius);
int width = (int)(2*_xradius), height = (int)(2*_yradius);
int sangle = (int)(Math.round(180 * _sangle / Math.PI)),
delta = (int)(Math.round(180 * _delta / Math.PI));
gfx.setColor(Color.blue);
gfx.drawRect(x, y, width-1, height-1);
gfx.setColor(Color.yellow);
gfx.drawArc(x, y, width-1, height-1, 0, 360);
gfx.setColor(Color.red);
gfx.drawArc(x, y, width-1, height-1, 360-sangle, -delta);
}
// documentation inherited
protected void toString (StringBuilder buf)
{
super.toString(buf);
buf.append(", center=").append(StringUtil.toString(_center));
buf.append(", sangle=").append(_sangle);
buf.append(", delta=").append(_delta);
buf.append(", radii=").append(_xradius).append("/").append(_yradius);
}
/**
* Computes the position of an entity along the path defined by the
* supplied parameters assuming that it must finish the path in the
* specified duration (in millis) and has been traveling the path for
* the specified number of elapsed milliseconds.
*/
public static void computePosition (
Point center, double xradius, double yradius, double angle, Point pos)
{
pos.x = (int)Math.round(center.x + xradius * Math.cos(angle));
pos.y = (int)Math.round(center.y + yradius * Math.sin(angle));
}
/** The center of our ellipse. */
protected Point _center;
/** Our ellipse radii. */
protected double _xradius, _yradius;
/** Our starting and delta angles. */
protected double _sangle, _delta;
/** The method to be used to orient the pathable. */
protected int _orient;
/** An orientation offset used when orienting our pathable. */
protected int _orientOffset = 0;
/** A temporary point used when computing our position along the
* path. */
protected Point _tpos = new Point();
}
@@ -0,0 +1,208 @@
//
// $Id: BackgroundTiler.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.Graphics;
import java.awt.image.BufferedImage;
import com.threerings.media.Log;
/**
* Used to tile a background image into regions of various sizes. The
* source image is divided into nine quadrants (of mostly equal size)
* which are tiled accordingly to fill whatever size background image is
* desired.
*/
public class BackgroundTiler
{
/**
* Creates a background tiler with the specified source image.
*/
public BackgroundTiler (BufferedImage src)
{
// make sure we were given the goods
if (src == null) {
Log.info("Backgrounder given null source image. Coping.");
return;
}
// compute some values
int width = src.getWidth(null);
int height = src.getHeight(null);
_w3 = width/3;
_cw3 = width-2*_w3;
_h3 = height/3;
_ch3 = height-2*_h3;
// make sure the image suits our minimum useful dimensions
if (_w3 <= 0 || _cw3 <= 0 || _h3 <= 0 || _ch3 <= 0) {
Log.warning("Backgrounder given source image of insufficient " +
"size for tiling " +
"[width=" + width + ", height=" + height + "].");
return;
}
// create our sub-divided images
_tiles = new BufferedImage[9];
int[] sy = { 0, _h3, _h3+_ch3 };
int[] thei = { _h3, _ch3, _h3 };
for (int i = 0; i < 3; i++) {
_tiles[3*i] = src.getSubimage(0, sy[i], _w3, thei[i]);
_tiles[3*i+1] = src.getSubimage(_w3, sy[i], _cw3, thei[i]);
_tiles[3*i+2] =
src.getSubimage(width-_w3, sy[i], _w3, thei[i]);
}
}
/**
* Returns the "natural" width of the image being used to tile the
* background.
*/
public int getNaturalWidth ()
{
return _w3*2+_cw3;
}
/**
* Returns the "natural" height of the image being used to tile the
* background.
*/
public int getNaturalHeight ()
{
return _h3*2+_ch3;
}
/**
* Fills the requested region with the background defined by our
* source image.
*/
public void paint (Graphics g, int x, int y, int width, int height)
{
// bail out now if we were passed a bogus source image at
// construct time
if (_tiles == null) {
return;
}
int rwid = width-2*_w3, rhei = height-2*_h3;
g.drawImage(_tiles[0], x, y, _w3, _h3, null);
g.drawImage(_tiles[1], x + _w3, y, rwid, _h3, null);
g.drawImage(_tiles[2], x + _w3 + rwid, y, _w3, _h3, null);
y += _h3;
g.drawImage(_tiles[3], x, y, _w3, rhei, null);
g.drawImage(_tiles[4], x + _w3, y, rwid, rhei, null);
g.drawImage(_tiles[5], x + _w3 + rwid, y, _w3, rhei, null);
y += rhei;
g.drawImage(_tiles[6], x, y, _w3, _h3, null);
g.drawImage(_tiles[7], x + _w3, y, rwid, _h3, null);
g.drawImage(_tiles[8], x + _w3 + rwid, y, _w3, _h3, null);
}
/** Our nine sub-divided images. */
protected BufferedImage[] _tiles;
/** One third of width/height of our source image. */
protected int _w3, _h3;
/** The size of the center chunk of our subdivided images. */
protected int _cw3, _ch3;
}
// Below is an alternate implementation that uses only the source image
// without chopping it up. It ends up running very slightly slower, that
// may be because the documentation for the version of drawImage used below
// states that scaled instances will not be cached, that the scaling will
// happen each time on the fly. On the other hand, maybe that's a good thing.
// I like it better because it doesn't have to have 9 separate images and
// because it does the render in a loop, which is good fun.
//public class BackgroundTiler
//{
// /**
// * Creates a background tiler with the specified source image.
// */
// public BackgroundTiler (BufferedImage src)
// {
// // make sure we were given the goods
// if (src == null) {
// Log.info("Backgrounder given null source image. Coping.");
// return;
// }
//
// _src = src;
//
// // compute some values
// int width = src.getWidth(null);
// int height = src.getHeight(null);
//
// _w3 = width/3;
// _cw3 = width-2*_w3;
// _h3 = height/3;
// _ch3 = height-2*_h3;
//
// _sx = new int[] { 0, _w3, width - _w3, width };
// _sy = new int[] { 0, _h3, height - _h3, height };
// _dx = new int[4];
// _dy = new int[4];
// }
//
// /**
// * Fills the requested region with the background defined by our
// * source image.
// */
// public void paint (Graphics g, int x, int y, int width, int height)
// {
// _dx[0] = x;
// _dx[1] = x + _w3;
// _dx[2] = x + width - _w3;
// _dx[3] = x + width;
//
// _dy[0] = y;
// _dy[1] = y + _h3;
// _dy[2] = y + height - _h3;
// _dy[3] = y + height;
//
// for (int jj=0; jj < 3; jj++) {
// for (int ii=0; ii < 3; ii++) {
// g.drawImage(_src, _dx[ii], _dy[jj], _dx[ii + 1], _dy[jj + 1],
// _sx[ii], _sy[jj], _sx[ii + 1], _sy[jj + 1], null);
// }
// }
// }
//
// /** Our source image. */
// protected BufferedImage _src;
//
// /** Coordinates. */
// protected int[] _sx, _sy, _dx, _dy;
//
// /** One third of width/height of our source image. */
// protected int _w3, _h3;
//
// /** The size of the center chunk of our subdivided images. */
// protected int _cw3, _ch3;
//}
@@ -0,0 +1,191 @@
//
// $Id: BobblePath.java 4188 2006-06-13 18:03:48Z mdb $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.Color;
import java.awt.Graphics2D;
import com.samskivert.util.RandomUtil;
import com.threerings.media.Log;
/**
* Bobble a Pathable.
*/
public class BobblePath implements Path
{
/**
* Construct a bobble path that updates as often as possible.
*
* @param dx the variance in the x direction.
* @param dy the variance in the y direction.
* @param duration the duration to bobble, or -1 to bobble until
* stop() is called.
*/
public BobblePath (int dx, int dy, long duration)
{
this(dx, dy, duration, 1L);
}
/**
* Construct a bobble path.
*
* @param dx the variance in the x direction.
* @param dy the variance in the y direction.
* @param duration the duration to bobble, or -1 to bobble until
* stop() is called.
* @param updateFreq how often to update the Pathable's location.
*/
public BobblePath (int dx, int dy, long duration, long updateFreq)
{
_updateFreq = updateFreq;
_duration = duration;
setVariance(dx, dy);
}
/**
* Set the variance of bobblin' in each direction.
*/
public void setVariance (int dx, int dy)
{
if ((dx < 0) || (dy < 0) || ((dx == 0) && (dy == 0))) {
throw new IllegalArgumentException(
"Variance values must be positive, " +
"and at least one must be non-zero.");
} else {
_dx = dx;
_dy = dy;
}
}
/**
* Set a new update frequency.
*/
public void setUpdateFrequency (long freq)
{
_updateFreq = freq;
}
/**
* Have the Pathable stop bobbling asap.
*/
public void stop ()
{
// it will stop on the next tick..
_stopTime = 0L;
}
// documentation inherited from interface Path
public void init (Pathable pable, long tickstamp)
{
_sx = pable.getX();
_sy = pable.getY();
// change the duration to a real stop time
if (_duration == -1L) {
_stopTime = Long.MAX_VALUE;
} else {
_stopTime = tickstamp + _duration;
}
_nextMove = tickstamp;
}
// documentation inherited from interface Path
public boolean tick (Pathable pable, long tickStamp)
{
// see if we need to stop
if (_stopTime <= tickStamp) {
boolean updated = updatePositionTo(pable, _sx, _sy);
pable.pathCompleted(tickStamp);
return updated;
}
// see if it's time to move..
if (_nextMove > tickStamp) {
return false;
}
// when bobbling, it's bad form to bobble into the same position
int newx, newy;
do {
newx = _sx + RandomUtil.getInt(_dx * 2 + 1) - _dx;
newy = _sy + RandomUtil.getInt(_dy * 2 + 1) - _dy;
} while (! updatePositionTo(pable, newx, newy));
// and update the next time to move
_nextMove = tickStamp + _updateFreq;
return true;
}
// documentation inherited from interface Path
public void fastForward (long timeDelta)
{
_stopTime += timeDelta;
_nextMove += timeDelta;
}
// documentation inherited from interface Path
public void paint (Graphics2D gfx)
{
// for debugging, show the bobble bounds
gfx.setColor(Color.RED);
gfx.drawRect(_sx - _dx, _sy - _dy, _dx * 2, _dy * 2);
}
// documentation inherited from interface
public void wasRemoved (Pathable pable)
{
// reset the pathable to its initial location
pable.setLocation(_sx, _sy);
}
/**
* Update the position of the pathable or return false
* if it's already there.
*/
protected boolean updatePositionTo (Pathable pable, int x, int y)
{
if ((pable.getX() == x) && (pable.getY() == y)) {
return false;
} else {
pable.setLocation(x, y);
return true;
}
}
/** The initial position of the pathable. */
protected int _sx, _sy;
/** The variance we will bobble around that initial position. */
protected int _dx, _dy;
/** How long we'll bobble. */
protected long _duration;
/** How often we update the locations. */
protected long _updateFreq;
/** The time at which we'll stop pathin'. */
protected long _stopTime;
/** The time at which we'll next update the position of the pathable. */
protected long _nextMove = 0L;
}
@@ -0,0 +1,91 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.Graphics2D;
import java.awt.Point;
/**
* A convenience path that waits a specified amount of time.
*/
public class DelayPath extends TimedPath
{
/**
* Cause the current path to remain unchanged for the duration.
*/
public DelayPath (long duration)
{
this(null, duration);
}
/**
* Move to the sprite to the supplied location then wait for the duration.
*/
public DelayPath (int x, int y, long duration)
{
this(new Point(x, y), duration);
}
/**
* Move to the sprite to the supplied location then wait for the duration.
*/
public DelayPath (Point source, long duration)
{
super(duration);
_source = source;
}
// documentation inherited
public void init (Pathable pable, long timestamp)
{
super.init(pable, timestamp);
}
// documentation inherited
public void paint (Graphics2D gfx)
{
}
// documentation inherited
public boolean tick (Pathable pable, long tickstamp)
{
if (tickstamp >= _startStamp + _duration) {
if (_source != null) {
pable.setLocation(_source.x, _source.y);
}
pable.pathCompleted(tickstamp);
return (_source != null);
}
// If necessary, move the sprite to the supplied location
if (_source != null && (pable.getX() != _source.x ||
pable.getY() != _source.y)) {
pable.setLocation(_source.x, _source.y);
return true;
}
return false;
}
/** Source point. */
protected Point _source;
}
@@ -0,0 +1,87 @@
//
// $Id: DelegatingPathable.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.Rectangle;
/**
* Delegates all calls to a delegate pathable. One would derive from this
* class and override just the methods in which that one desired to
* intervene.
*/
public class DelegatingPathable implements Pathable
{
public DelegatingPathable (Pathable delegate)
{
_delegate = delegate;
}
// documentation inherited from interface
public int getX ()
{
return _delegate.getX();
}
// documentation inherited from interface
public int getY ()
{
return _delegate.getY();
}
// documentation inherited from interface
public Rectangle getBounds ()
{
return _delegate.getBounds();
}
// documentation inherited from interface
public void setLocation (int x, int y)
{
_delegate.setLocation(x, y);
}
// documentation inherited from interface
public void setOrientation (int orient)
{
_delegate.setOrientation(orient);
}
// documentation inherited from interface
public int getOrientation ()
{
return _delegate.getOrientation();
}
// documentation inherited from interface
public void pathBeginning ()
{
_delegate.pathBeginning();
}
// documentation inherited from interface
public void pathCompleted (long timestamp)
{
_delegate.pathCompleted(timestamp);
}
protected Pathable _delegate;
}
@@ -0,0 +1,113 @@
//
// $Id: FrameSequencer.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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;
/**
* Used to control animation timing when displaying a {@link
* MultiFrameImage}. This interface allows for constant framerates, or
* more sophisticated animation timing.
*/
public interface FrameSequencer
{
/**
* Called prior to the execution of an animation sequence (not
* necessarily immediately, so time stamps should be obtained in the
* first call to tick).
*
* @param source the multie-frame image that is providing the
* animation frames.
*/
public void init (MultiFrameImage source);
/**
* Called every display frame, the frame sequencer should return the
* index of the animation frame that should be displayed during this
* tick. If the sequencer returns -1, it is taken as an indication
* that the animation is finished and should be stopped.
*/
public int tick (long tickStamp);
/**
* Called if the display is paused for some length of time and then
* unpaused. Sequencers should adjust any time stamps they are
* maintaining internally by the delta so that time maintains the
* illusion of flowing smoothly forward.
*/
public void fastForward (long timeDelta);
/**
* A frame sequencer that delivers a constant frame rate in either one
* shot or looping mode.
*/
public static class ConstantRate implements FrameSequencer
{
/**
* Creates a constant rate frame sequencer with the desired target
* frames per second.
*
* @param framesPerSecond the target frames per second.
* @param loop if false, the sequencer will report the end of
* the animation after progressing through all of the frames once,
* otherwise it will loop indefinitely.
*/
public ConstantRate (double framesPerSecond, boolean loop)
{
_millisPerFrame = (long)(1000d / framesPerSecond);
_loop = loop;
}
// documentation inherited from interface
public void init (MultiFrameImage source)
{
_frameCount = source.getFrameCount();
_startStamp = 0l;
}
// documentation inherited from interface
public int tick (long tickStamp)
{
// obtain our starting timestamp if we don't already have one
if (_startStamp == 0) {
_startStamp = tickStamp;
}
// compute our current frame index
int frameIdx = (int)((tickStamp - _startStamp) / _millisPerFrame);
// if we're not looping and we've exhausted our frames, we
// return -1 to indicate that the animation should stop
return (_loop || frameIdx < _frameCount) ? (frameIdx % _frameCount)
: -1;
}
// documentation inherited from interface
public void fastForward (long timeDelta)
{
_startStamp += timeDelta;
}
protected long _millisPerFrame;
protected boolean _loop;
protected int _frameCount;
protected long _startStamp;
}
}
@@ -0,0 +1,151 @@
//
// $Id: LinePath.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import com.samskivert.util.StringUtil;
/**
* The line path is used to cause a pathable to go from point A to point B
* in a certain number of milliseconds.
*/
public class LinePath extends TimedPath
{
/**
* Constructs a line path between the two specified points that will
* be followed in the specified number of milliseconds.
*/
public LinePath (int x1, int y1, int x2, int y2, long duration)
{
this(new Point(x1, y1), new Point(x2, y2), duration);
}
/**
* Constructs a line path between the two specified points that will
* be followed in the specified number of milliseconds.
*/
public LinePath (Point source, Point dest, long duration)
{
super(duration);
_source = source;
_dest = dest;
}
/**
* Constructs a line path that moves a pathable from
* whatever its location is at init time to the dest point over
* the specified number of milliseconds.
*/
public LinePath (Point dest, long duration)
{
this(null, dest, duration);
}
/**
* Return a copy of the path, translated by the specified amounts.
*/
public Path getTranslatedInstance (int x, int y)
{
if (_source == null) {
return new LinePath(null, new Point(_dest.x + x, _dest.y + y),
_duration);
} else {
return new LinePath(_source.x + x, _source.y + y, _dest.x + x,
_dest.y + y, _duration);
}
}
// documentation inherited
public void init (Pathable pable, long timestamp)
{
super.init(pable, timestamp);
// fill in the source if necessary.
if (_source == null) {
_source = new Point(pable.getX(), pable.getY());
}
}
// documentation inherited
public boolean tick (Pathable pable, long timestamp)
{
// if we've blown past our arrival time, we need to get our bootay
// to the prearranged spot and get the hell out
if (timestamp >= _startStamp + _duration) {
pable.setLocation(_dest.x, _dest.y);
pable.pathCompleted(timestamp);
return true;
}
// determine where we should be along the path
long elapsed = timestamp - _startStamp;
computePosition(_source, _dest, elapsed, _duration, _tpos);
// only update the pathable's location if it actually moved
if (pable.getX() != _tpos.x || pable.getY() != _tpos.y) {
pable.setLocation(_tpos.x, _tpos.y);
return true;
}
return false;
}
// documentation inherited
public void paint (Graphics2D gfx)
{
gfx.setColor(Color.red);
gfx.drawLine(_source.x, _source.y, _dest.x, _dest.y);
}
// documentation inherited
protected void toString (StringBuilder buf)
{
super.toString(buf);
buf.append(", src=").append(StringUtil.toString(_source));
buf.append(", dest=").append(StringUtil.toString(_dest));
}
/**
* Computes the position of an entity along the path defined by the
* supplied start and end points assuming that it must finish the path
* in the specified duration (in millis) and has been traveling the
* path for the specified number of elapsed milliseconds.
*/
public static void computePosition (Point start, Point end, long elapsed,
long duration, Point pos)
{
float pct = (float)elapsed / duration;
int travx = (int)((end.x - start.x) * pct);
int travy = (int)((end.y - start.y) * pct);
pos.setLocation(start.x + travx, start.y + travy);
}
/** Our source and destination points. */
protected Point _source, _dest;
/** A temporary point used when computing our position along the
* path. */
protected Point _tpos = new Point();
}
@@ -0,0 +1,388 @@
//
// $Id: LineSegmentPath.java 3880 2006-02-22 18:29:05Z mjohnson $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.Color;
import java.awt.Point;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.samskivert.util.StringUtil;
import com.threerings.util.DirectionCodes;
import com.threerings.util.DirectionUtil;
import com.threerings.media.Log;
import com.threerings.media.util.MathUtil;
/**
* The line segment path is used to cause a pathable to follow a path that
* is made up of a sequence of line segments. There must be at least two
* nodes in any worthwhile path. The direction of the first node in the
* path is meaningless since the pathable begins at that node and will
* therefore never be heading towards it.
*/
public class LineSegmentPath
implements DirectionCodes, Path
{
/**
* Constructs an empty line segment path.
*/
public LineSegmentPath ()
{
}
/**
* Constructs a line segment path that consists of a single segment
* connecting the point <code>(x1, y1)</code> with <code>(x2,
* y2)</code>. The orientation for the first node is set arbitrarily
* and the second node is oriented based on the vector between the two
* nodes (in top-down coordinates).
*/
public LineSegmentPath (int x1, int y1, int x2, int y2)
{
addNode(x1, y1, NORTH);
Point p1 = new Point(x1, y1), p2 = new Point(x2, y2);
int dir = DirectionUtil.getDirection(p1, p2);
addNode(x2, y2, dir);
}
/**
* Construct a line segment path between the two nodes with the
* specified direction.
*/
public LineSegmentPath (Point p1, Point p2, int dir)
{
addNode(p1.x, p1.y, NORTH);
addNode(p2.x, p2.y, dir);
}
/**
* Constructs a line segment path with the specified list of points.
* An arbitrary direction will be assigned to the starting node.
*/
public LineSegmentPath (List points)
{
createPath(points);
}
/**
* Returns the orientation the sprite will face at the end of the
* path.
*/
public int getFinalOrientation ()
{
return (_nodes.size() == 0) ? NORTH :
((PathNode)_nodes.get(_nodes.size()-1)).dir;
}
/**
* Add a node to the path with the specified destination point and
* facing direction.
*
* @param x the x-position.
* @param y the y-position.
* @param dir the facing direction.
*/
public void addNode (int x, int y, int dir)
{
_nodes.add(new PathNode(x, y, dir));
}
/**
* Return the requested node index in the path, or null if no such
* index exists.
*
* @param idx the node index.
*
* @return the path node.
*/
public PathNode getNode (int idx)
{
return (PathNode)_nodes.get(idx);
}
/**
* Return the number of nodes in the path.
*/
public int size ()
{
return _nodes.size();
}
/**
* Sets the velocity of this pathable in pixels per millisecond. The
* velocity is measured as pixels traversed along the path that the
* pathable is traveling rather than in the x or y directions
* individually. Note that the pathable velocity should not be
* changed while a path is being traversed; doing so may result in the
* pathable position changing unexpectedly.
*
* @param velocity the pathable velocity in pixels per millisecond.
*/
public void setVelocity (float velocity)
{
_vel = velocity;
}
/**
* Computes the velocity at which the pathable will need to travel
* along this path such that it will arrive at the destination in
* approximately the specified number of milliseconds. Efforts are
* taken to get the pathable there as close to the desired time as
* possible, but framerate variation may prevent it from arriving
* exactly on time.
*/
public void setDuration (long millis)
{
// if we have only zero or one nodes, we don't have enough
// information to compute our velocity
int ncount = _nodes.size();
if (ncount < 2) {
Log.warning("Requested to set duration of bogus path " +
"[path=" + this + ", duration=" + millis + "].");
return;
}
// compute the total distance along our path
float distance = 0;
PathNode start = (PathNode)_nodes.get(0);
for (int ii = 1; ii < ncount; ii++) {
PathNode end = (PathNode)_nodes.get(ii);
distance += MathUtil.distance(
start.loc.x, start.loc.y, end.loc.x, end.loc.y);
start = end;
}
// set the velocity accordingly
setVelocity(distance/millis);
}
// documentation inherited
public void init (Pathable pable, long timestamp)
{
// give the pathable a chance to perform any starting antics
pable.pathBeginning();
// if we have only one node then let the pathable know that we're
// done straight away
if (size() < 2) {
// move the pathable to the location specified by the first
// node (assuming we have a first node)
if (size() == 1) {
PathNode node = (PathNode)_nodes.get(0);
pable.setLocation(node.loc.x, node.loc.y);
}
// and let the pathable know that we're done
pable.pathCompleted(timestamp);
return;
}
// and an enumeration of the path nodes
_niter = _nodes.iterator();
// pretend like we were previously heading to our starting position
_dest = getNextNode();
// begin traversing the path
headToNextNode(pable, timestamp, timestamp);
}
// documentation inherited
public boolean tick (Pathable pable, long timestamp)
{
// figure out how far along this segment we should be
long msecs = timestamp - _nodestamp;
float travpix = msecs * _vel;
float pctdone = travpix / _seglength;
// if we've moved beyond the end of the path, we need to adjust
// the timestamp to determine how much time we used getting to the
// end of this node, then move to the next one
if (pctdone >= 1.0) {
long used = (long)(_seglength / _vel);
return headToNextNode(pable, _nodestamp + used, timestamp);
}
// otherwise we position the pathable along the path
int ox = pable.getX();
int oy = pable.getY();
int nx = _src.loc.x + (int)((_dest.loc.x - _src.loc.x) * pctdone);
int ny = _src.loc.y + (int)((_dest.loc.y - _src.loc.y) * pctdone);
// Log.info("Moving pathable [msecs=" + msecs + ", pctdone=" + pctdone +
// ", travpix=" + travpix + ", seglength=" + _seglength +
// ", dx=" + (nx-ox) + ", dy=" + (ny-oy) + "].");
// only update the pathable's location if it actually moved
if (ox != nx || oy != ny) {
pable.setLocation(nx, ny);
return true;
}
return false;
}
// documentation inherited
public void fastForward (long timeDelta)
{
_nodestamp += timeDelta;
}
// documentation inherited from interface
public void wasRemoved (Pathable pable)
{
// nothing doing
}
/**
* Place the pathable moving along the path at the end of the previous
* path node, face it appropriately for the next node, and start it on
* its way. Returns whether the pathable position moved.
*/
protected boolean headToNextNode (
Pathable pable, long startstamp, long now)
{
if (_niter == null) {
throw new IllegalStateException(
"headToNextNode() called before init()");
}
// check to see if we've completed our path
if (!_niter.hasNext()) {
// move the pathable to the location of our last destination
pable.setLocation(_dest.loc.x, _dest.loc.y);
pable.pathCompleted(now);
return true;
}
// our previous destination is now our source
_src = _dest;
// pop the next node off the path
_dest = getNextNode();
// adjust the pathable's orientation
if (_dest.dir != NONE) {
pable.setOrientation(_dest.dir);
}
// make a note of when we started traversing this node
_nodestamp = startstamp;
// figure out the distance from source to destination
_seglength = MathUtil.distance(
_src.loc.x, _src.loc.y, _dest.loc.x, _dest.loc.y);
// if we're already there (the segment length is zero), we skip to
// the next segment
if (_seglength == 0) {
return headToNextNode(pable, startstamp, now);
}
// now update the pathable's position based on our progress thus far
return tick(pable, now);
}
// documentation inherited
public void paint (Graphics2D gfx)
{
gfx.setColor(Color.red);
Point prev = null;
int size = size();
for (int ii = 0; ii < size; ii++) {
PathNode n = (PathNode)getNode(ii);
if (prev != null) {
gfx.drawLine(prev.x, prev.y, n.loc.x, n.loc.y);
}
prev = n.loc;
}
}
// documentation inherited
public String toString ()
{
return StringUtil.toString(_nodes.iterator());
}
/**
* Populate the path with the path nodes that lead the pathable from
* its starting position to the given destination coordinates
* following the given list of screen coordinates.
*/
protected void createPath (List points)
{
Point last = null;
int size = points.size();
for (int ii = 0; ii < size; ii++) {
Point p = (Point)points.get(ii);
int dir = (ii == 0) ? NORTH : DirectionUtil.getDirection(last, p);
addNode(p.x, p.y, dir);
last = p;
}
}
/**
* Gets the next node in the path.
*/
protected PathNode getNextNode ()
{
return (PathNode)_niter.next();
}
/** The nodes that make up the path. */
protected ArrayList _nodes = new ArrayList();
/** We use this when moving along this path. */
protected Iterator _niter;
/** When moving, the pathable's source path node. */
protected PathNode _src;
/** When moving, the pathable's destination path node. */
protected PathNode _dest;
/** The time at which we started traversing the current node. */
protected long _nodestamp;
/** The length in pixels of the current path segment. */
protected float _seglength;
/** The path velocity in pixels per millisecond. */
protected float _vel = DEFAULT_VELOCITY;
/** When moving, the pathable position including fractional pixels. */
protected float _movex, _movey;
/** When moving, the distance to move on each axis per tick. */
protected float _incx, _incy;
/** The distance to move on the straight path line per tick. */
protected float _fracx, _fracy;
/** Default pathable velocity. */
protected static final float DEFAULT_VELOCITY = 200f/1000f;
}
@@ -0,0 +1,22 @@
//
// $Id: LinearTimeFunction.java 4191 2006-06-13 22:42:20Z ray $
package com.threerings.media.util;
/**
* Varies a value linearly with time.
*/
public class LinearTimeFunction extends TimeFunction
{
public LinearTimeFunction (int start, int end, int duration)
{
super(start, end, duration);
}
// documentation inherited
protected int computeValue (int dt)
{
int dv = (_end - _start);
return (dt * dv / _duration) + _start;
}
}
@@ -0,0 +1,162 @@
//
// $Id: MathUtil.java 3580 2005-05-27 23:04:42Z tedv $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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;
/**
* Provides miscellaneous useful utility routines for mathematical
* calculations.
*/
public class MathUtil
{
/**
* Bounds the supplied value within the specified range.
*
* @return low if value < low, high if value > high and value
* otherwise.
*/
public static int bound (int low, int value, int high)
{
return Math.min(high, Math.max(low, value));
}
/**
* Return the squared distance between the given points.
*/
public static int distanceSq (int x0, int y0, int x1, int y1)
{
return ((x1 - x0) * (x1 - x0)) + ((y1 - y0) * (y1 - y0));
}
/**
* Return the distance between the given points.
*/
public static float distance (int x0, int y0, int x1, int y1)
{
return (float)Math.sqrt(((x1 - x0) * (x1 - x0)) +
((y1 - y0) * (y1 - y0)));
}
/**
* Return the distance between the given points.
*/
public static float distance (Point source, Point dest)
{
return MathUtil.distance(source.x, source.y, dest.x, dest.y);
}
/**
* Return a string representation of the given line.
*/
public static String lineToString (int x0, int y0, int x1, int y1)
{
return "(" + x0 + ", " + y0 + ") -> (" + x1 + ", " + y1 + ")";
}
/**
* Return a string representation of the given line.
*/
public static String lineToString (Point p1, Point p2)
{
return lineToString(p1.x, p1.y, p2.x, p2.y);
}
/**
* Returns the approximate circumference of the ellipse defined by the
* specified minor and major axes. The formula used (due to Ramanujan,
* via a paper of his entitled "Modular Equations and Approximations
* to Pi"), is <code>Pi(3a + 3b - sqrt[(a+3b)(b+3a)])</code>.
*/
public static double ellipseCircum (double a, double b)
{
return Math.PI * (3*a + 3*b - Math.sqrt((a + 3*b) * (b + 3*a)));
}
/**
* Returns positive 1 if the sign of the argument is positive, or -1
* if the sign of the argument is negative.
*/
public static int sign (int value)
{
return (value < 0) ? -1 : 1;
}
/**
* Computes the floored division <code>dividend/divisor</code> which
* is useful when dividing potentially negative numbers into bins. For
* positive numbers, it is the same as normal division, for negative
* numbers it returns <code>(dividend - divisor + 1) / divisor</code>.
*
* <p> For example, the following numbers floorDiv 10 are:
* <pre>
* -15 -10 -8 -2 0 2 8 10 15
* -2 -1 -1 -1 0 0 0 1 1
* </pre>
*/
public static int floorDiv (int dividend, int divisor)
{
return ((dividend >= 0) ? dividend : (dividend - divisor + 1))/divisor;
}
/**
* Computes the standard deviation of the supplied values.
*
* @return an array of three values: the mean, variance and standard
* deviation, in that order.
*/
public static float[] stddev (int[] values, int start, int length)
{
// first we need the mean
float mean = 0f;
for (int ii = start, end = start + length; ii < end; ii++) {
mean += values[ii];
}
mean /= length;
// next we compute the variance
float variance = 0f;
for (int ii = start, end = start + length; ii < end; ii++) {
float value = values[ii] - mean;
variance += value * value;
}
variance /= (length - 1);
// the standard deviation is the square root of the variance
return new float[] { mean, variance, (float)Math.sqrt(variance) };
}
/**
* Computes (N choose K), the number of ways to select K different
* elements from a set of size N.
*/
public static int choose (int n, int k)
{
// Base case: One way to select or not select the whole set
if (k <= 0 || k >= n) {
return 1;
}
// Recurse using pascal's triangle
return (choose(n-1, k-1) + choose(n-1, k));
}
}
@@ -0,0 +1,99 @@
//
// $Id: ModeUtil.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.DisplayMode;
import java.awt.GraphicsDevice;
import java.util.Comparator;
import java.util.TreeSet;
/**
* Display mode related utilities.
*/
public class ModeUtil
{
/**
* Gets a display mode that matches the specified parameters. The
* screen resolution must match the specified resolution exactly, the
* specified desired depth will be used if it is available, and if
* not, the highest depth greater than or equal to the specified
* minimum depth is used. The highest refresh rate available for the
* desired mode is also used.
*/
public static DisplayMode getDisplayMode (
GraphicsDevice gd, int width, int height,
int desiredDepth, int minimumDepth)
{
DisplayMode[] modes = gd.getDisplayModes();
final int ddepth = desiredDepth;
// we sort modes in order of desirability
Comparator mcomp = new Comparator () {
public int compare (Object o1, Object o2) {
DisplayMode m1 = (DisplayMode)o1;
DisplayMode m2 = (DisplayMode)o2;
int bd1 = m1.getBitDepth(), bd2 = m2.getBitDepth();
int rr1 = m1.getRefreshRate(), rr2 = m2.getRefreshRate();
// prefer the desired depth
if (bd1 == ddepth && bd2 != ddepth) {
return -1;
} else if (bd2 == ddepth && bd1 != ddepth) {
return 1;
}
// otherwise prefer higher depths
if (bd1 != bd2) {
return bd2 - bd1;
}
// for same bitrates, prefer higher refresh rates
return rr2 - rr1;
}
};
// but we only add modes that meet our minimum requirements
TreeSet mset = new TreeSet(mcomp);
for (int i = 0; i < modes.length; i++) {
if (modes[i].getWidth() == width &&
modes[i].getHeight() == height &&
modes[i].getBitDepth() >= minimumDepth &&
modes[i].getRefreshRate() <= 75) {
mset.add(modes[i]);
}
}
return (mset.size() > 0) ? (DisplayMode)mset.first() : null;
}
/**
* Returns a string representation of the supplied display mode.
*/
public static String toString (DisplayMode mode)
{
return "[width=" + mode.getWidth() +
", height=" + mode.getHeight() +
", depth=" + mode.getBitDepth() +
", refresh=" + mode.getRefreshRate() + "]";
}
}
@@ -0,0 +1,58 @@
//
// $Id: MultiFrameImage.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.Graphics2D;
/**
* The multi-frame image interface provides encapsulated access to a set
* of images that are used to create a multi-frame animation.
*/
public interface MultiFrameImage
{
/**
* Returns the number of frames in this multi-frame image.
*/
public int getFrameCount ();
/**
* Returns the width of the specified frame image.
*/
public int getWidth (int index);
/**
* Returns the height of the specified frame image.
*/
public int getHeight (int index);
/**
* Renders the specified frame into the specified graphics object at
* the specified coordinates.
*/
public void paintFrame (Graphics2D g, int index, int x, int y);
/**
* Returns true if the specified frame contains a non-transparent
* pixel at the specified coordinates.
*/
public boolean hitTest (int index, int x, int y);
}
@@ -0,0 +1,75 @@
//
// $Id: MultiFrameImageImpl.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.Graphics2D;
import com.threerings.media.image.Mirage;
/**
* A basic implementation of the {@link MultiFrameImage} interface
* intended to facilitate the creation of MFIs whose display frames
* consist of multiple image objects.
*/
public class MultiFrameImageImpl implements MultiFrameImage
{
/**
* Constructs a multiple frame image object.
*/
public MultiFrameImageImpl (Mirage[] mirages)
{
_mirages = mirages;
}
// documentation inherited
public int getFrameCount ()
{
return _mirages.length;
}
// documentation inherited from interface
public int getWidth (int index)
{
return _mirages[index].getWidth();
}
// documentation inherited from interface
public int getHeight (int index)
{
return _mirages[index].getHeight();
}
// documentation inherited from interface
public void paintFrame (Graphics2D g, int index, int x, int y)
{
_mirages[index].paint(g, x, y);
}
// documentation inherited from interface
public boolean hitTest (int index, int x, int y)
{
return _mirages[index].hitTest(x, y);
}
/** The frame images. */
protected Mirage[] _mirages;
}
@@ -0,0 +1,82 @@
//
// $Id: Path.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.Graphics2D;
/**
* A path is used to cause a {@link Pathable} to follow a particular path
* along the screen. The {@link Pathable} is responsible for calling
* {@link #tick} on the path with reasonable frequency (generally as a
* part of the frame tick. The path is responsible for updating the
* position of the {@link Pathable} based on the time that has elapsed
* since the {@link Pathable} started down the path.
*
* <p> The path should call the appropriate callbacks on the {@link
* Pathable} when appropriate (e.g. {@link Pathable#pathBeginning}, {@link
* Pathable#pathCompleted}).
*/
public interface Path
{
/**
* Called once to let the path prepare itself for the process of
* animating the supplied pathable. Path users should also call {@link
* #tick} after {@link #init} with the same initialization timestamp.
*/
public void init (Pathable pable, long tickStamp);
/**
* Called to request that this path update the position of the
* specified pathable based on the supplied timestamp information. A
* path should record its initial timestamp and determine the progress
* of the pathable along the path based on the time elapsed since the
* pathable began down the path.
*
* @param pable the pathable whose position should be updated.
* @param tickStamp the timestamp associated with this frame.
*
* @return true if the pathable's position was updated, false if the
* path determined that the pathable should not move at this time.
*/
public boolean tick (Pathable pable, long tickStamp);
/**
* This is called if the pathable is paused for some length of time
* and then unpaused. Paths should adjust any time stamps they are
* maintaining internally by the delta so that time maintains the
* illusion of flowing smoothly forward.
*/
public void fastForward (long timeDelta);
/**
* Paint this path on the screen (used for debugging purposes only).
*/
public void paint (Graphics2D gfx);
/**
* When a path is removed from a pathable, whether that is because the
* path was completed or because it was replaced by another path, this
* method will be called to let the path know that it is no longer
* associated with this pathable.
*/
public void wasRemoved (Pathable pable);
}
@@ -0,0 +1,72 @@
//
// $Id: PathNode.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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;
/**
* A path node is a single destination point in a {@link Path}.
*/
public class PathNode
{
/** The node coordinates in screen pixels. */
public Point loc;
/** The direction to face while heading toward the node. */
public int dir;
/**
* Construct a path node object.
*
* @param x the node x-position.
* @param y the node y-position.
* @param dir the facing direction.
*/
public PathNode (int x, int y, int dir)
{
loc = new Point(x, y);
this.dir = dir;
}
/**
* Return a string representation of this path node.
*/
public String toString ()
{
StringBuilder buf = new StringBuilder();
buf.append("[");
toString(buf);
return buf.append("]").toString();
}
/**
* This should be overridden by derived classes (which should be sure
* to call <code>super.toString()</code>) to append the derived class
* specific path node information to the string buffer.
*/
public void toString (StringBuilder buf)
{
buf.append("x=").append(loc.x);
buf.append(", y=").append(loc.y);
buf.append(", dir=").append(dir);
}
}
@@ -0,0 +1,143 @@
//
// $Id: PathSequence.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.Graphics2D;
import java.util.ArrayList;
import java.util.List;
import com.threerings.media.Log;
/**
* Used to create a path that is a sequence of several other paths.
*/
public class PathSequence
implements Path
{
/**
* Conveniently construct a path sequence with the two specified
* paths.
*/
public PathSequence (Path first, Path second)
{
this(new ArrayList());
_paths.add(first);
_paths.add(second);
}
/**
* Construct a path sequence with the list of paths.
* Note: Paths may be added to the end of the list while
* the pathable is still traversing earlier paths!
*/
public PathSequence (List paths)
{
_paths = paths;
}
// documentation inherited from interface Path
public void init (Pathable pable, long tickStamp)
{
_pable = pable;
_pableRep = new DelegatingPathable(_pable) {
public void pathCompleted (long timeStamp) {
long initStamp;
// if we just finished a timed path, we can figure out how
// long ago it really finished and init the next path at
// that time in the past.
if (_curPath instanceof TimedPath) {
initStamp = _lastInit + ((TimedPath) _curPath)._duration;
} else {
// we don't know
initStamp = timeStamp;
}
initNextPath(initStamp, timeStamp);
}
};
initNextPath(tickStamp, tickStamp);
}
// documentation inherited from interface Path
public boolean tick (Pathable pable, long tickStamp)
{
if (pable != _pable) {
Log.warning("PathSequence ticked with different path than " +
"it was inited with.");
}
return _curPath.tick(_pableRep, tickStamp);
}
// documentation inherited from interface Path
public void fastForward (long timeDelta)
{
_lastInit += timeDelta;
_curPath.fastForward(timeDelta);
}
// documentation inherited from interface Path
public void paint (Graphics2D gfx)
{
// for now..
_curPath.paint(gfx);
}
// documentation inherited from interface Path
public void wasRemoved (Pathable pable)
{
if (_curPath != null) {
_curPath.wasRemoved(_pableRep);
}
}
/**
* Initialize and start the next path in the sequence.
*/
protected void initNextPath (long initStamp, long tickStamp)
{
if (_paths.size() == 0) {
_pable.pathCompleted(tickStamp);
} else {
_curPath = (Path) _paths.remove(0);
_lastInit = initStamp;
_curPath.init(_pableRep, initStamp);
_curPath.tick(_pableRep, tickStamp);
}
}
/** The list of paths. */
protected List _paths;
/** The timestamp at which we last inited a path. */
protected long _lastInit;
/** The current path we're pathing. */
protected Path _curPath;
/** The pathable we're duping bigtime. */
protected Pathable _pable;
/** A fake pathable that we pass to the subpaths. */
protected Pathable _pableRep;
}
@@ -0,0 +1,77 @@
//
// $Id: Pathable.java 3310 2005-01-24 23:08:21Z mdb $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.Rectangle;
import com.threerings.util.DirectionCodes;
/**
* Used in conjunction with a {@link Path}.
*/
public interface Pathable
{
/**
* Returns the pathable's current x coordinate.
*/
public int getX ();
/**
* Returns the pathable's current y coordinate.
*/
public int getY ();
/**
* Returns the rectangle that bounds the pathable.
*/
public Rectangle getBounds ();
/**
* Updates the pathable's current coordinates.
*/
public void setLocation (int x, int y);
/**
* Will be called by a path when it moves the pathable in the
* specified direction. Pathables that wish to face in the direction
* they are moving can take advantage of this callback.
*
* @see DirectionCodes
*/
public void setOrientation (int orient);
/**
* Should return the orientation of the pathable, or {@link
* DirectionCodes#NONE} if the pathable does not support orientation.
*/
public int getOrientation ();
/**
* Called by a path when this pathable is made to start along a path.
*/
public void pathBeginning ();
/**
* Called by a path when this pathable finishes moving along its path.
*/
public void pathCompleted (long timestamp);
}
@@ -0,0 +1,215 @@
//
// $Id: PerformanceMonitor.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.util.HashMap;
import com.threerings.media.Log;
import com.threerings.media.timer.MediaTimer;
import com.threerings.media.timer.SystemMediaTimer;
/**
* Provides a simple mechanism for monitoring the number of times an
* action takes place within a certain time period.
*
* <p> The action being tracked should be registered with a suitable name
* via {@link #register}, and {@link #tick} should be called each time the
* action is performed.
*
* <p> Whenever {@link #tick} is called and the checkpoint time interval
* has elapsed since the last checkpoint (if any), the observer will be
* notified to that effect by a call to {@link
* PerformanceObserver#checkpoint}.
*
* <p> Note that this is <em>not</em> intended to be used as an
* industrial-strength profiling or performance monitoring tool. The
* checkpoint time interval granularity is in milliseconds, not
* microseconds, and the observer's <code>checkpoint()</code> method will
* never be called until/unless a subsequent call to {@link #tick} is made
* after the requested number of milliseconds have passed since the last
* checkpoint.
*/
public class PerformanceMonitor
{
/**
* Register a new action with an observer, the action name, and
* the milliseconds to wait between checkpointing the action's
* performance.
*
* @param obs the action observer.
* @param name the action name.
* @param delta the milliseconds between checkpoints.
*/
public static void register (PerformanceObserver obs, String name,
long delta)
{
// get the observer's action hashtable
HashMap actions = (HashMap)_observers.get(obs);
if (actions == null) {
// create it if it didn't exist
_observers.put(obs, actions = new HashMap());
}
// add the action to the set we're tracking
actions.put(name, new PerformanceAction(obs, name, delta));
}
/**
* Un-register the named action associated with the given observer.
*
* @param obs the action observer.
* @param name the action name.
*/
public static void unregister (PerformanceObserver obs, String name)
{
// get the observer's action hashtable
HashMap actions = (HashMap)_observers.get(obs);
if (actions == null) {
Log.warning("Attempt to unregister by unknown observer " +
"[observer=" + obs + ", name=" + name + "].");
return;
}
// attempt to remove the specified action
PerformanceAction action = (PerformanceAction)actions.remove(name);
if (action == null) {
Log.warning("Attempt to unregister unknown action " +
"[observer=" + obs + ", name=" + name + "].");
return;
}
// if the observer has no actions left, remove the observer's action
// hash in its entirety
if (actions.size() == 0) {
_observers.remove(obs);
}
}
/**
* Tick the named action associated with the given observer.
*
* @param obs the action observer.
* @param name the action name.
*/
public static void tick (PerformanceObserver obs, String name)
{
// get the observer's action hashtable
HashMap actions = (HashMap)_observers.get(obs);
if (actions == null) {
Log.warning("Attempt to tick by unknown observer " +
"[observer=" + obs + ", name=" + name + "].");
return;
}
// get the specified action
PerformanceAction action = (PerformanceAction)actions.get(name);
if (action == null) {
Log.warning("Attempt to tick unknown value " +
"[observer=" + obs + ", name=" + name + "].");
return;
}
// tick the action
action.tick();
}
/**
* Used to configure the performance monitor with a particular {@link
* MediaTimer} implementation. By default it uses a pure-Java
* implementation which isn't extremely accurate across platforms.
*/
public static void setMediaTimer (MediaTimer timer)
{
_timer = timer;
}
/** Used by the performance actions. */
protected synchronized static long getTimeStamp ()
{
return _timer.getElapsedMillis();
}
/** The observers monitoring some set of actions. */
protected static HashMap _observers = new HashMap();
/** Used to obtain high resolution time stamps. */
protected static MediaTimer _timer = new SystemMediaTimer();
}
/**
* This class represents the individual actions being tracked by the
* <code>PerformanceMonitor</code> class.
*/
class PerformanceAction
{
public PerformanceAction (PerformanceObserver obs, String name, long delta)
{
_obs = obs;
_name = name;
_delta = delta;
_lastDelta = PerformanceMonitor.getTimeStamp();
}
public void tick ()
{
_numTicks++;
long now = PerformanceMonitor.getTimeStamp();
long passed = now - _lastDelta;
if (passed >= _delta) {
// update the last checkpoint time
_lastDelta = now + (passed - _delta);
// notify our observer of the checkpoint
_obs.checkpoint(_name, _numTicks);
// reset the tick count
_numTicks = 0;
}
}
public String toString ()
{
StringBuilder buf = new StringBuilder();
buf.append("[obs=").append(_obs);
buf.append(", name=").append(_name);
buf.append(", delta=").append(_delta);
buf.append(", lastDelta=").append(_lastDelta);
buf.append(", numTicks=").append(_numTicks);
return buf.append("]").toString();
}
/** The performance observer. */
protected PerformanceObserver _obs;
/** The action name. */
protected String _name;
/** The number of milliseconds between each checkpoint. */
protected long _delta;
/** The time the last time a checkpoint was made. */
protected long _lastDelta;
/** The number of ticks since the last checkpoint. */
protected int _numTicks;
}
@@ -0,0 +1,39 @@
//
// $Id: PerformanceObserver.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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;
/**
* This interface should be implemented by classes that wish to register
* actions to be monitored by the {@link PerformanceMonitor} class.
*/
public interface PerformanceObserver
{
/**
* This method is called by the {@link PerformanceMonitor} class
* whenever an action's requested time interval between checkpoints
* has expired.
*
* @param name the action name.
* @param ticks the ticks since the last checkpoint.
*/
public void checkpoint (String name, int ticks);
}
@@ -0,0 +1,75 @@
//
// $Id: SingleFrameImageImpl.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.Graphics2D;
import com.threerings.media.image.Mirage;
/**
* The single frame image class is a basic implementation of the {@link
* MultiFrameImage} interface intended to facilitate the creation of MFIs
* whose display frames consist of only a single image.
*/
public class SingleFrameImageImpl implements MultiFrameImage
{
/**
* Constructs a single frame image object.
*/
public SingleFrameImageImpl (Mirage mirage)
{
_mirage = mirage;
}
// documentation inherited
public int getFrameCount ()
{
return 1;
}
// documentation inherited from interface
public int getWidth (int index)
{
return _mirage.getWidth();
}
// documentation inherited from interface
public int getHeight (int index)
{
return _mirage.getHeight();
}
// documentation inherited from interface
public void paintFrame (Graphics2D g, int index, int x, int y)
{
_mirage.paint(g, x, y);
}
// documentation inherited from interface
public boolean hitTest (int index, int x, int y)
{
return _mirage.hitTest(x, y);
}
/** The frame image. */
protected Mirage _mirage;
}
@@ -0,0 +1,75 @@
//
// $Id: SingleTileImageImpl.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.Graphics2D;
import com.threerings.media.tile.Tile;
/**
* The single frame image class is a basic implementation of the {@link
* MultiFrameImage} interface intended to facilitate the creation of MFIs
* whose display frames consist of only a single tile image.
*/
public class SingleTileImageImpl implements MultiFrameImage
{
/**
* Constructs a single frame image object.
*/
public SingleTileImageImpl (Tile tile)
{
_tile = tile;
}
// documentation inherited
public int getFrameCount ()
{
return 1;
}
// documentation inherited from interface
public int getWidth (int index)
{
return _tile.getWidth();
}
// documentation inherited from interface
public int getHeight (int index)
{
return _tile.getHeight();
}
// documentation inherited from interface
public void paintFrame (Graphics2D g, int index, int x, int y)
{
_tile.paint(g, x, y);
}
// documentation inherited from interface
public boolean hitTest (int index, int x, int y)
{
return _tile.hitTest(x, y);
}
/** The frame image. */
protected Tile _tile;
}
@@ -0,0 +1,86 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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 com.samskivert.util.RandomUtil;
/**
* Bobble a Pathable smoothly.
*/
public class SmoothBobblePath extends BobblePath
{
public SmoothBobblePath (int dx, int dy, long duration)
{
this(dx, dy, duration, 1L);
}
public SmoothBobblePath (int dx, int dy, long duration, long updateFreq)
{
super(dx, dy, duration, updateFreq);
}
// documentation inherited
public void init (Pathable pable, long tickstamp)
{
super.init(pable, tickstamp);
_newx = _sx;
_newy = _sy;
_oldx = _sx;
_oldy = _sy;
}
// documentation inherited
public boolean tick (Pathable pable, long tickStamp)
{
// see if we need to stop
if (_stopTime <= tickStamp) {
boolean updated = updatePositionTo(pable, _sy, _sy);
pable.pathCompleted(tickStamp);
return updated;
}
// see if it's time to update the position
if (_nextMove < tickStamp) {
_oldx = _newx;
_oldy = _newy;
do {
_newx = _sx + RandomUtil.getInt(_dx * 2 + 1) - _dx;
_newy = _sy + RandomUtil.getInt(_dy * 2 + 1) - _dy;
} while (_newx == _oldx && _newy == _oldy);
_nextMove = tickStamp + _updateFreq;
}
float movePerc = (float)(_nextMove - tickStamp) / (float)_updateFreq;
int x = _oldx + (int)((_newx - _oldx) * movePerc);
int y = _oldy + (int)((_newy - _oldy) * movePerc);
// update the position
return updatePositionTo(pable, x, y);
}
/** The previous position. */
protected int _oldx, _oldy;
/** The next position. */
protected int _newx, _newy;
}
@@ -0,0 +1,61 @@
//
// $Id: TiledArea.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.JComponent;
/**
* A component that can be inserted into a user interface to fill a
* particular area using a {@link BackgroundTiler}.
*/
public class TiledArea extends JComponent
{
public TiledArea (BufferedImage imgsrc)
{
this(new BackgroundTiler(imgsrc));
}
public TiledArea (BackgroundTiler tiler)
{
_tiler = tiler;
setOpaque(true);
}
// documentation inherited
public void paintComponent (Graphics g)
{
super.paintComponent(g);
_tiler.paint(g, 0, 0, getWidth(), getHeight());
}
// documentation inherited
public Dimension getPreferredSize ()
{
return new Dimension(_tiler.getNaturalWidth(),
_tiler.getNaturalHeight());
}
protected BackgroundTiler _tiler;
}
@@ -0,0 +1,83 @@
//
// $Id: TimeFunction.java 4191 2006-06-13 22:42:20Z ray $
package com.threerings.media.util;
/**
* Used to vary a value over time where time is provided at discrete
* increments (on the frame tick) and the value is computed appropriately.
*/
public abstract class TimeFunction
{
/**
* Every time function varies a value from some starting value to some
* ending value over some duration. The way in which it varies
* (linearly, for example) is up to the derived class.
*
* <p><em>Note:</em> it is assumed that we will operate with
* relatively short durations such that integer arithmetic may be used
* rather than long arithmetic.
*/
public TimeFunction (int start, int end, int duration)
{
_start = start;
_end = end;
_duration = duration;
}
/**
* Configures this function with a starting time. This method need not
* be called, and instead the first vall to {@link #getValue} will be
* used to obtain a starting time stamp.
*/
public void init (long tickStamp)
{
_startStamp = tickStamp;
}
/**
* Called to fast forward our time stamps if we are ever paused and
* need to resume where we left off.
*/
public void fastForward (long timeDelta)
{
_startStamp += timeDelta;
}
/**
* Returns the current value given the supplied time stamp. The value
* will be bounded to the originally supplied starting and ending
* values at times 0 (and below) and {@link #_duration} (and above)
* respectively.
*/
public int getValue (long tickStamp)
{
if (_startStamp == 0L) {
_startStamp = tickStamp;
}
int dt = (int)(tickStamp - _startStamp);
if (dt <= 0) {
return _start;
} else if (dt >= _duration) {
return _end;
} else {
return computeValue(dt);
}
}
/**
* This must be implemented by our derived class to compute our value
* given the specified elapsed time (in millis).
*/
protected abstract int computeValue (int dt);
/** Our starting and ending values. */
protected int _start, _end;
/** The number of milliseconds over which we vary our value. */
protected int _duration;
/** The timestamp at which we began varying our value. */
protected long _startStamp;
}
@@ -0,0 +1,98 @@
//
// $Id: TimedPath.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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 com.threerings.media.Log;
/**
* A base class for path implementations that endeavor to move their
* pathables along a path in a specified number of milliseconds.
*/
public abstract class TimedPath implements Path
{
/**
* Configures the timed path with the duration in which it must
* traverse its path.
*/
public TimedPath (long duration)
{
// sanity check some things
if (duration <= 0) {
Log.warning("Requested path with illegal duration (<=0) " +
"[duration=" + duration + "]");
Thread.dumpStack();
duration = 1; // assume something short but non-zero
}
_duration = duration;
}
// documentation inherited
public void init (Pathable pable, long timestamp)
{
// give the pable a chance to perform any starting antics
pable.pathBeginning();
// make a note of when we started
_startStamp = timestamp;
// we'll be ticked immediately following init() which will update
// our position to the start of our path
}
// documentation inherited
public void fastForward (long timeDelta)
{
_startStamp += timeDelta;
}
// documentation inherited from interface
public void wasRemoved (Pathable pable)
{
// nothing doing
}
/**
* Generates a string representation of this instance.
*/
public String toString ()
{
StringBuilder buf = new StringBuilder("[");
toString(buf);
return buf.append("]").toString();
}
/**
* An extensible method for generating a string representation of this
* instance.
*/
protected void toString (StringBuilder buf)
{
buf.append("duration=").append(_duration).append("ms");
}
/** The duration that we're to spend following the path. */
protected long _duration;
/** The time at which we started along the path. */
protected long _startStamp;
}
@@ -0,0 +1,81 @@
//
// $Id: TrailingAverage.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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;
/**
* Used to compute a trailing average of a value.
*/
public class TrailingAverage
{
/**
* Creates a trailing average instance with the default number of
* values used to compute the average (10).
*/
public TrailingAverage ()
{
this(10);
}
/**
* Creates a trailing average instance with the specified number of
* values used to compute the average.
*/
public TrailingAverage (int history)
{
_history = new int[history];
}
/**
* Records a new value.
*/
public void record (int value)
{
_history[_index++%_history.length] = value;
}
/**
* Returns the current averaged value.
*/
public int value ()
{
int end = Math.min(_history.length, _index);
int value = 0;
for (int ii = 0; ii < end; ii++) {
value += _history[ii];
}
return (end > 0) ? (value/end) : 0;
}
/**
* Returns the current trailing average value as a string.
*/
public String toString ()
{
return Integer.toString(value());
}
/** The history of values. */
protected int[] _history;
/** The index where we will next record a value. */
protected int _index;
}