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:
@@ -0,0 +1,518 @@
|
||||
//
|
||||
// $Id: MisoUtil.java 3628 2005-06-28 17:42:55Z 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.miso.util;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.Polygon;
|
||||
|
||||
import com.samskivert.swing.SmartPolygon;
|
||||
|
||||
import com.threerings.util.DirectionCodes;
|
||||
import com.threerings.util.DirectionUtil;
|
||||
|
||||
import com.threerings.media.util.MathUtil;
|
||||
|
||||
/**
|
||||
* Miscellaneous isometric-display-related utility routines.
|
||||
*/
|
||||
public class MisoUtil
|
||||
implements DirectionCodes
|
||||
{
|
||||
/**
|
||||
* Given two points in screen pixel coordinates, return the
|
||||
* compass direction that point B lies in from point A from an
|
||||
* isometric perspective.
|
||||
*
|
||||
* @param ax the x-position of point A.
|
||||
* @param ay the y-position of point A.
|
||||
* @param bx the x-position of point B.
|
||||
* @param by the y-position of point B.
|
||||
*
|
||||
* @return the direction specified as one of the <code>Sprite</code>
|
||||
* class's direction constants.
|
||||
*/
|
||||
public static int getDirection (
|
||||
MisoSceneMetrics metrics, int ax, int ay, int bx, int by)
|
||||
{
|
||||
Point afpos = new Point(), bfpos = new Point();
|
||||
|
||||
// convert screen coordinates to full coordinates to get both
|
||||
// tile coordinates and fine coordinates
|
||||
screenToFull(metrics, ax, ay, afpos);
|
||||
screenToFull(metrics, bx, by, bfpos);
|
||||
|
||||
// pull out the tile coordinates for each point
|
||||
int tax = fullToTile(afpos.x);
|
||||
int tay = fullToTile(afpos.y);
|
||||
|
||||
int tbx = fullToTile(bfpos.x);
|
||||
int tby = fullToTile(bfpos.y);
|
||||
|
||||
// compare tile coordinates to determine direction
|
||||
int dir = getIsoDirection(tax, tay, tbx, tby);
|
||||
if (dir != DirectionCodes.NONE) {
|
||||
return dir;
|
||||
}
|
||||
|
||||
// destination point is in the same tile as the
|
||||
// origination point, so consider fine coordinates
|
||||
|
||||
// pull out the fine coordinates for each point
|
||||
int fax = afpos.x - (tax * FULL_TILE_FACTOR);
|
||||
int fay = afpos.y - (tay * FULL_TILE_FACTOR);
|
||||
|
||||
int fbx = bfpos.x - (tbx * FULL_TILE_FACTOR);
|
||||
int fby = bfpos.y - (tby * FULL_TILE_FACTOR);
|
||||
|
||||
// compare fine coordinates to determine direction
|
||||
dir = getIsoDirection(fax, fay, fbx, fby);
|
||||
|
||||
// arbitrarily return southwest if fine coords were also equivalent
|
||||
return (dir == -1) ? SOUTHWEST : dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given two points in an isometric coordinate system (in which {@link
|
||||
* #NORTH} is in the direction of the negative x-axis and {@link
|
||||
* #WEST} in the direction of the negative y-axis), return the compass
|
||||
* direction that point B lies in from point A. This method is used
|
||||
* to determine direction for both tile coordinates and fine
|
||||
* coordinates within a tile, since the coordinate systems are the
|
||||
* same.
|
||||
*
|
||||
* @param ax the x-position of point A.
|
||||
* @param ay the y-position of point A.
|
||||
* @param bx the x-position of point B.
|
||||
* @param by the y-position of point B.
|
||||
*
|
||||
* @return the direction specified as one of the <code>Sprite</code>
|
||||
* class's direction constants, or <code>DirectionCodes.NONE</code> if
|
||||
* point B is equivalent to point A.
|
||||
*/
|
||||
public static int getIsoDirection (int ax, int ay, int bx, int by)
|
||||
{
|
||||
// head off a div by 0 at the pass..
|
||||
if (bx == ax) {
|
||||
if (by == ay) {
|
||||
return DirectionCodes.NONE;
|
||||
}
|
||||
return (by < ay) ? EAST : WEST;
|
||||
}
|
||||
|
||||
// figure direction base on the slope of the line
|
||||
float slope = ((float) (ay - by)) / ((float) Math.abs(ax - bx));
|
||||
if (slope > 2f) {
|
||||
return EAST;
|
||||
}
|
||||
if (slope > .5f) {
|
||||
return (bx < ax) ? NORTHEAST : SOUTHEAST;
|
||||
}
|
||||
if (slope > -.5f) {
|
||||
return (bx < ax) ? NORTH : SOUTH;
|
||||
}
|
||||
if (slope > -2f) {
|
||||
return (bx < ax) ? NORTHWEST : SOUTHWEST;
|
||||
}
|
||||
return WEST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given two points in screen coordinates, return the isometrically
|
||||
* projected compass direction that point B lies in from point A.
|
||||
*
|
||||
* @param ax the x-position of point A.
|
||||
* @param ay the y-position of point A.
|
||||
* @param bx the x-position of point B.
|
||||
* @param by the y-position of point B.
|
||||
*
|
||||
* @return the direction specified as one of the <code>Sprite</code>
|
||||
* class's direction constants, or <code>DirectionCodes.NONE</code> if
|
||||
* point B is equivalent to point A.
|
||||
*/
|
||||
public static int getProjectedIsoDirection (int ax, int ay, int bx, int by)
|
||||
{
|
||||
return toIsoDirection(DirectionUtil.getDirection(ax, ay, bx, by));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a non-isometric orientation (where north points toward the
|
||||
* top of the screen) to an isometric orientation where north points
|
||||
* toward the upper-left corner of the screen.
|
||||
*/
|
||||
public static int toIsoDirection (int dir)
|
||||
{
|
||||
if (dir != DirectionCodes.NONE) {
|
||||
// rotate the direction clockwise (ie. change SOUTHEAST to
|
||||
// SOUTH)
|
||||
dir = DirectionUtil.rotateCW(dir, 2);
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tile coordinate of the given full coordinate.
|
||||
*/
|
||||
public static int fullToTile (int val)
|
||||
{
|
||||
return MathUtil.floorDiv(val, FULL_TILE_FACTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fine coordinate of the given full coordinate.
|
||||
*/
|
||||
public static int fullToFine (int val)
|
||||
{
|
||||
return (val - (fullToTile(val) * FULL_TILE_FACTOR));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given screen-based pixel coordinates to their
|
||||
* corresponding tile-based coordinates. Converted coordinates
|
||||
* are placed in the given point object.
|
||||
*
|
||||
* @param sx the screen x-position pixel coordinate.
|
||||
* @param sy the screen y-position pixel coordinate.
|
||||
* @param tpos the point object to place coordinates in.
|
||||
*
|
||||
* @return the point instance supplied via the <code>tpos</code>
|
||||
* parameter.
|
||||
*/
|
||||
public static Point screenToTile (
|
||||
MisoSceneMetrics metrics, int sx, int sy, Point tpos)
|
||||
{
|
||||
// determine the upper-left of the quadrant that contains our
|
||||
// point
|
||||
int zx = (int)Math.floor((float)sx / metrics.tilewid);
|
||||
int zy = (int)Math.floor((float)sy / metrics.tilehei);
|
||||
|
||||
// these are the screen coordinates of the tile's top
|
||||
int ox = (zx * metrics.tilewid), oy = (zy * metrics.tilehei);
|
||||
|
||||
// these are the tile coordinates
|
||||
tpos.x = zy + zx; tpos.y = zy - zx;
|
||||
|
||||
// now determine which of the four tiles our point occupies
|
||||
int dx = sx - ox, dy = sy - oy;
|
||||
|
||||
if (Math.round(metrics.slopeY * dx + metrics.tilehei) <= dy) {
|
||||
tpos.x += 1;
|
||||
}
|
||||
|
||||
if (Math.round(metrics.slopeX * dx) > dy) {
|
||||
tpos.y -= 1;
|
||||
}
|
||||
|
||||
// Log.info("Converted [sx=" + sx + ", sy=" + sy +
|
||||
// ", zx=" + zx + ", zy=" + zy +
|
||||
// ", ox=" + ox + ", oy=" + oy +
|
||||
// ", dx=" + dx + ", dy=" + dy +
|
||||
// ", tpos.x=" + tpos.x + ", tpos.y=" + tpos.y + "].");
|
||||
return tpos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given tile-based coordinates to their corresponding
|
||||
* screen-based pixel coordinates. The screen coordinate for a tile is
|
||||
* the upper-left coordinate of the rectangle that bounds the tile
|
||||
* polygon. Converted coordinates are placed in the given point
|
||||
* object.
|
||||
*
|
||||
* @param x the tile x-position coordinate.
|
||||
* @param y the tile y-position coordinate.
|
||||
* @param spos the point object to place coordinates in.
|
||||
*
|
||||
* @return the point instance supplied via the <code>spos</code>
|
||||
* parameter.
|
||||
*/
|
||||
public static Point tileToScreen (
|
||||
MisoSceneMetrics metrics, int x, int y, Point spos)
|
||||
{
|
||||
spos.x = (x - y - 1) * metrics.tilehwid;
|
||||
spos.y = (x + y) * metrics.tilehhei;
|
||||
return spos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given fine coordinates to pixel coordinates within
|
||||
* the containing tile. Converted coordinates are placed in the
|
||||
* given point object.
|
||||
*
|
||||
* @param x the x-position fine coordinate.
|
||||
* @param y the y-position fine coordinate.
|
||||
* @param ppos the point object to place coordinates in.
|
||||
*/
|
||||
public static void fineToPixel (
|
||||
MisoSceneMetrics metrics, int x, int y, Point ppos)
|
||||
{
|
||||
ppos.x = metrics.tilehwid + ((x - y) * metrics.finehwid);
|
||||
ppos.y = (x + y) * metrics.finehhei;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given pixel coordinates, whose origin is at the
|
||||
* top-left of a tile's containing rectangle, to fine coordinates
|
||||
* within that tile. Converted coordinates are placed in the
|
||||
* given point object.
|
||||
*
|
||||
* @param x the x-position pixel coordinate.
|
||||
* @param y the y-position pixel coordinate.
|
||||
* @param fpos the point object to place coordinates in.
|
||||
*/
|
||||
public static void pixelToFine (
|
||||
MisoSceneMetrics metrics, int x, int y, Point fpos)
|
||||
{
|
||||
// calculate line parallel to the y-axis (from the given
|
||||
// x/y-pos to the x-axis)
|
||||
float bY = y - (metrics.fineSlopeY * x);
|
||||
|
||||
// determine intersection of x- and y-axis lines
|
||||
int crossx = (int)((bY - metrics.fineBX) /
|
||||
(metrics.fineSlopeX - metrics.fineSlopeY));
|
||||
int crossy = (int)((metrics.fineSlopeY * crossx) + bY);
|
||||
|
||||
// TODO: final position should check distance between our
|
||||
// position and the surrounding fine coords and return the
|
||||
// actual closest fine coord, rather than just dividing.
|
||||
|
||||
// determine distance along the x-axis
|
||||
float xdist = MathUtil.distance(metrics.tilehwid, 0, crossx, crossy);
|
||||
fpos.x = (int)(xdist / metrics.finelen);
|
||||
|
||||
// determine distance along the y-axis
|
||||
float ydist = MathUtil.distance(x, y, crossx, crossy);
|
||||
fpos.y = (int)(ydist / metrics.finelen);
|
||||
|
||||
// Log.info("Pixel to fine " + StringUtil.coordsToString(x, y) +
|
||||
// " -> " + StringUtil.toString(fpos) + ".");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given screen-based pixel coordinates to full
|
||||
* scene-based coordinates that include both the tile coordinates
|
||||
* and the fine coordinates in each dimension. Converted
|
||||
* coordinates are placed in the given point object.
|
||||
*
|
||||
* @param sx the screen x-position pixel coordinate.
|
||||
* @param sy the screen y-position pixel coordinate.
|
||||
* @param fpos the point object to place coordinates in.
|
||||
*
|
||||
* @return the point passed in to receive the coordinates.
|
||||
*/
|
||||
public static Point screenToFull (
|
||||
MisoSceneMetrics metrics, int sx, int sy, Point fpos)
|
||||
{
|
||||
// get the tile coordinates
|
||||
Point tpos = new Point();
|
||||
screenToTile(metrics, sx, sy, tpos);
|
||||
|
||||
// get the screen coordinates for the containing tile
|
||||
Point spos = tileToScreen(metrics, tpos.x, tpos.y, new Point());
|
||||
|
||||
// Log.info("Screen to full " +
|
||||
// "[screen=" + StringUtil.coordsToString(sx, sy) +
|
||||
// ", tpos=" + StringUtil.toString(tpos) +
|
||||
// ", spos=" + StringUtil.toString(spos) +
|
||||
// ", fpix=" + StringUtil.coordsToString(
|
||||
// sx-spos.x, sy-spos.y) + "].");
|
||||
|
||||
// get the fine coordinates within the containing tile
|
||||
pixelToFine(metrics, sx - spos.x, sy - spos.y, fpos);
|
||||
|
||||
// toss in the tile coordinates for good measure
|
||||
fpos.x += (tpos.x * FULL_TILE_FACTOR);
|
||||
fpos.y += (tpos.y * FULL_TILE_FACTOR);
|
||||
|
||||
return fpos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given full coordinates to screen-based pixel
|
||||
* coordinates. Converted coordinates are placed in the given
|
||||
* point object.
|
||||
*
|
||||
* @param x the x-position full coordinate.
|
||||
* @param y the y-position full coordinate.
|
||||
* @param spos the point object to place coordinates in.
|
||||
*
|
||||
* @return the point passed in to receive the coordinates.
|
||||
*/
|
||||
public static Point fullToScreen (
|
||||
MisoSceneMetrics metrics, int x, int y, Point spos)
|
||||
{
|
||||
// get the tile screen position
|
||||
int tx = fullToTile(x), ty = fullToTile(y);
|
||||
Point tspos = tileToScreen(metrics, tx, ty, new Point());
|
||||
|
||||
// get the pixel position of the fine coords within the tile
|
||||
Point ppos = new Point();
|
||||
int fx = x - (tx * FULL_TILE_FACTOR), fy = y - (ty * FULL_TILE_FACTOR);
|
||||
fineToPixel(metrics, fx, fy, ppos);
|
||||
|
||||
// final position is tile position offset by fine position
|
||||
spos.x = tspos.x + ppos.x;
|
||||
spos.y = tspos.y + ppos.y;
|
||||
|
||||
return spos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given fine coordinate to a full coordinate (a tile
|
||||
* coordinate plus a fine coordinate remainder). The fine coordinate
|
||||
* is assumed to be relative to tile <code>(0, 0)</code>.
|
||||
*/
|
||||
public static int fineToFull (MisoSceneMetrics metrics, int fine)
|
||||
{
|
||||
return toFull(fine / metrics.finegran, fine % metrics.finegran);
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes the supplied tile coordinate and fine coordinate offset
|
||||
* into a full coordinate.
|
||||
*/
|
||||
public static int toFull (int tile, int fine)
|
||||
{
|
||||
return tile * FULL_TILE_FACTOR + fine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a polygon framing the specified tile.
|
||||
*
|
||||
* @param x the tile x-position coordinate.
|
||||
* @param y the tile y-position coordinate.
|
||||
*/
|
||||
public static Polygon getTilePolygon (
|
||||
MisoSceneMetrics metrics, int x, int y)
|
||||
{
|
||||
return getFootprintPolygon(metrics, x, y, 1, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a screen-coordinates polygon framing the two specified
|
||||
* tile-coordinate points.
|
||||
*/
|
||||
public static Polygon getMultiTilePolygon (MisoSceneMetrics metrics,
|
||||
Point sp1, Point sp2)
|
||||
{
|
||||
int x = Math.min(sp1.x, sp2.x), y = Math.min(sp1.y, sp2.y);
|
||||
int width = Math.abs(sp1.x-sp2.x)+1, height = Math.abs(sp1.y-sp2.y)+1;
|
||||
return getFootprintPolygon(metrics, x, y, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a polygon framing the specified scene footprint.
|
||||
*
|
||||
* @param x the x tile coordinate of the "upper-left" of the footprint.
|
||||
* @param y the y tile coordinate of the "upper-left" of the footprint.
|
||||
* @param width the width in tiles of the footprint.
|
||||
* @param height the height in tiles of the footprint.
|
||||
*/
|
||||
public static Polygon getFootprintPolygon (
|
||||
MisoSceneMetrics metrics, int x, int y, int width, int height)
|
||||
{
|
||||
SmartPolygon footprint = new SmartPolygon();
|
||||
Point tpos = MisoUtil.tileToScreen(metrics, x, y, new Point());
|
||||
|
||||
// start with top-center point
|
||||
int rx = tpos.x + metrics.tilehwid, ry = tpos.y;
|
||||
footprint.addPoint(rx, ry);
|
||||
// right point
|
||||
rx += width * metrics.tilehwid;
|
||||
ry += width * metrics.tilehhei;
|
||||
footprint.addPoint(rx, ry);
|
||||
// bottom-center point
|
||||
rx -= height * metrics.tilehwid;
|
||||
ry += height * metrics.tilehhei;
|
||||
footprint.addPoint(rx, ry);
|
||||
// left point
|
||||
rx -= width * metrics.tilehwid;
|
||||
ry -= width * metrics.tilehhei;
|
||||
footprint.addPoint(rx, ry);
|
||||
// end with top-center point
|
||||
rx += height * metrics.tilehwid;
|
||||
ry -= height * metrics.tilehhei;
|
||||
footprint.addPoint(rx, ry);
|
||||
|
||||
return footprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the supplied fine coordinates to the supplied tile coordinates
|
||||
* to compute full coordinates.
|
||||
*
|
||||
* @return the point object supplied as <code>full</code>.
|
||||
*/
|
||||
public static Point tilePlusFineToFull (MisoSceneMetrics metrics,
|
||||
int tileX, int tileY,
|
||||
int fineX, int fineY,
|
||||
Point full)
|
||||
{
|
||||
int dtx = fineX / metrics.finegran;
|
||||
int dty = fineY / metrics.finegran;
|
||||
int fx = fineX - dtx * metrics.finegran;
|
||||
if (fx < 0) {
|
||||
dtx--;
|
||||
fx += metrics.finegran;
|
||||
}
|
||||
int fy = fineY - dty * metrics.finegran;
|
||||
if (fy < 0) {
|
||||
dty--;
|
||||
fy += metrics.finegran;
|
||||
}
|
||||
|
||||
full.x = toFull(tileX + dtx, fx);
|
||||
full.y = toFull(tileY + dty, fy);
|
||||
return full;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns x and y scene coordinates into an integer key.
|
||||
*
|
||||
* @return the hash key, given x and y.
|
||||
*/
|
||||
public static final int coordsToKey (int x, int y)
|
||||
{
|
||||
return ((y << 16) & (0xFFFF0000)) | (x & 0xFFFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the x coordinate from an integer hash key.
|
||||
*
|
||||
* @return the x coordinate.
|
||||
*/
|
||||
public static final int xCoordFromKey (int key)
|
||||
{
|
||||
return (key & 0xFFFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the y coordinate from an integer hash key.
|
||||
*
|
||||
* @return the y coordinate from the hash key.
|
||||
*/
|
||||
public static final int yCoordFromKey (int key)
|
||||
{
|
||||
return ((key >> 16) & 0xFFFF);
|
||||
}
|
||||
|
||||
/** Multiplication factor to embed tile coords in full coords. */
|
||||
protected static final int FULL_TILE_FACTOR = 100;
|
||||
}
|
||||
Reference in New Issue
Block a user