
com.threerings.miso.util.MisoUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nenya Show documentation
Show all versions of nenya Show documentation
Facilities for making networked multiplayer games.
The newest version!
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// https://github.com/threerings/nenya
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.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 Sprite
* 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 Sprite
* class's direction constants, or DirectionCodes.NONE
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 Sprite
* class's direction constants, or DirectionCodes.NONE
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 tpos
* 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 spos
* 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 (0, 0)
.
*/
public static int fineToFull (MisoSceneMetrics metrics, int fine)
{
return toFull(fine / metrics.finegran, fine % metrics.finegran);
}
/**
* Returns the supplied tile coordinate as a full coordinate assuming a fine offset of 0.
*/
public static int tileToFull (int tile)
{
return toFull(tile, 0);
}
/**
* 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 full
.
*/
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;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy