
com.threerings.miso.util.MisoUtil Maven / Gradle / Ivy
//
// 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