The big fat bernie revamp! Er, big fat miso revamp rather:

- Combined SceneViewPanel and IsoSceneView into one happy panel.
- Ditched the DisplayMisoScene notion; the new MisoScenePanel now manages
  resolved scene information (like base, object and fringe tiles) itself
  so that it can...
- ...support scrolling scenes by keeping blocks of resolved base, fringe
  and object information loaded only for what is potentially visible
  rather than for the whole scene.

Other things were surely cleaned up or broken in the process to keep a
keen eye out.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2413 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2003-04-17 19:21:17 +00:00
parent 3de98670db
commit d5701962a3
38 changed files with 2943 additions and 2532 deletions
@@ -1,5 +1,5 @@
//
// $Id: DirtyItemList.java,v 1.24 2003/04/07 23:32:47 mdb Exp $
// $Id: DirtyItemList.java,v 1.25 2003/04/17 19:21:16 mdb Exp $
package com.threerings.miso.client;
@@ -13,15 +13,14 @@ import java.awt.Shape;
import java.util.ArrayList;
import java.util.Comparator;
import com.samskivert.util.RuntimeAdjust;
import com.samskivert.util.SortableArrayList;
import com.samskivert.util.StringUtil;
import com.threerings.media.Log;
import com.threerings.media.sprite.Sprite;
import com.threerings.media.tile.ObjectTile;
import com.threerings.miso.MisoPrefs;
import com.threerings.miso.client.util.IsoUtil;
/**
* The dirty item list keeps track of dirty sprites and object tiles
@@ -33,9 +32,8 @@ public class DirtyItemList
* Creates a dirt item list that will handle dirty items for the
* specified view.
*/
public DirtyItemList (IsoSceneView view)
public DirtyItemList ()
{
_view = view;
}
/**
@@ -49,7 +47,7 @@ public class DirtyItemList
public void appendDirtySprite (Sprite sprite, int tx, int ty)
{
DirtyItem item = getDirtyItem();
item.init(sprite, null, null, tx, ty);
item.init(sprite, tx, ty);
_items.add(item);
}
@@ -58,13 +56,11 @@ public class DirtyItemList
* item list.
*
* @param scene the scene object that is dirty.
* @param footprint the footprint of the object tile if it should be
* rendered, null otherwise.
*/
public void appendDirtyObject (DisplayObjectInfo info, Shape footprint)
public void appendDirtyObject (SceneObject scobj)
{
DirtyItem item = getDirtyItem();
item.init(info, info.bounds, footprint, info.x, info.y);
item.init(scobj, scobj.info.x, scobj.info.y);
_items.add(item);
}
@@ -257,13 +253,6 @@ public class DirtyItemList
/** The dirtied object; one of either a sprite or an object tile. */
public Object obj;
/** The bounds of the dirty item if it's an object tile. */
public Rectangle bounds;
/** The footprint of the dirty item if it's an object tile and
* we're drawing footprints. */
public Shape footprint;
/** The origin tile coordinates. */
public int ox, oy;
@@ -276,12 +265,9 @@ public class DirtyItemList
/**
* Initializes a dirty item.
*/
public void init (Object obj, Rectangle bounds, Shape footprint,
int x, int y)
public void init (Object obj, int x, int y)
{
this.obj = obj;
this.bounds = bounds;
this.footprint = footprint;
this.ox = x;
this.oy = y;
@@ -290,8 +276,8 @@ public class DirtyItemList
// rightmost tiles are equivalent
lx = rx = ox;
ly = ry = oy;
if (obj instanceof DisplayObjectInfo) {
ObjectTile tile = ((DisplayObjectInfo)obj).tile;
if (obj instanceof SceneObject) {
ObjectTile tile = ((SceneObject)obj).tile;
lx -= (tile.getBaseWidth() - 1);
ry -= (tile.getBaseHeight() - 1);
}
@@ -304,47 +290,10 @@ public class DirtyItemList
*/
public void paint (Graphics2D gfx)
{
// if there's a footprint, paint that
if (footprint != null) {
gfx.setColor(Color.black);
gfx.draw(footprint);
}
// paint the item
if (obj instanceof Sprite) {
((Sprite)obj).paint(gfx);
} else if (!_hideObjects.getValue()) {
((DisplayObjectInfo)obj).tile.paint(gfx, bounds.x, bounds.y);
}
// and paint the object's spot if it has one
if (footprint != null && obj instanceof DisplayObjectInfo) {
DisplayObjectInfo info = (DisplayObjectInfo)obj;
if (info.tile.hasSpot()) {
// get the spot's center coordinate
Point fpos = _view.getObjectSpot(info);
Point spos = _view.getScreenCoords(fpos.x, fpos.y);
// translate the origin to center on the portal
gfx.translate(spos.x, spos.y);
// rotate to reflect the spot orientation
double rot = (Math.PI / 4.0f) *
info.tile.getSpotOrient();
gfx.rotate(rot);
// draw the spot triangle
gfx.setColor(Color.green);
gfx.fill(_spotTri);
// outline the triangle in black
gfx.setColor(Color.black);
gfx.draw(_spotTri);
// restore the original transform
gfx.rotate(-rot);
gfx.translate(-spos.x, -spos.y);
}
} else {
((SceneObject)obj).paint(gfx);
}
}
@@ -363,8 +312,8 @@ public class DirtyItemList
*/
public int getRenderPriority ()
{
if (obj instanceof DisplayObjectInfo) {
return ((DisplayObjectInfo)obj).getPriority();
if (obj instanceof SceneObject) {
return ((SceneObject)obj).getPriority();
} else {
return 0;
}
@@ -378,7 +327,6 @@ public class DirtyItemList
public void clear ()
{
obj = null;
bounds = null;
}
// documentation inherited
@@ -475,11 +423,11 @@ public class DirtyItemList
// if the two objects are scene objects and they overlap, we
// compare them solely based on their human assigned priority
if ((da.obj instanceof DisplayObjectInfo) &&
(db.obj instanceof DisplayObjectInfo)) {
DisplayObjectInfo soa = (DisplayObjectInfo)da.obj;
DisplayObjectInfo sob = (DisplayObjectInfo)db.obj;
if (IsoUtil.objectFootprintsOverlap(soa, sob)) {
if ((da.obj instanceof SceneObject) &&
(db.obj instanceof SceneObject)) {
SceneObject soa = (SceneObject)da.obj;
SceneObject sob = (SceneObject)db.obj;
if (soa.objectFootprintOverlaps(sob)) {
int result = soa.getPriority() - sob.getPriority();
if (DEBUG_COMPARE) {
String items = DirtyItemList.toString(da, db);
@@ -674,9 +622,6 @@ public class DirtyItemList
}
}
/** The view for which we're managing dirty items. */
protected IsoSceneView _view;
/** The list of dirty items. */
protected SortableArrayList _items = new SortableArrayList();
@@ -695,16 +640,6 @@ public class DirtyItemList
/** Unused dirty items. */
protected ArrayList _freelist = new ArrayList();
/** The triangle used to render an object's spot. */
protected static Polygon _spotTri;
static {
_spotTri = new Polygon();
_spotTri.addPoint(-3, -3);
_spotTri.addPoint(3, -3);
_spotTri.addPoint(0, 3);
};
/** Whether to log debug info when comparing pairs of dirty items. */
protected static final boolean DEBUG_COMPARE = false;
@@ -733,10 +668,4 @@ public class DirtyItemList
((DirtyItem)o2).getRearDepth());
}
};
/** A debug hook that toggles debug rendering of object footprints. */
protected static RuntimeAdjust.BooleanAdjust _hideObjects =
new RuntimeAdjust.BooleanAdjust(
"Turns off the rendering of objects in the scene.",
"narya.miso.hide_objects", MisoPrefs.config, false);
}
@@ -1,45 +0,0 @@
//
// $Id: DisplayMisoScene.java,v 1.12 2003/04/12 02:14:52 mdb Exp $
package com.threerings.miso.client;
import com.threerings.media.tile.Tile;
import com.threerings.miso.client.util.AStarPathUtil;
import com.threerings.miso.data.MisoScene;
import com.threerings.miso.tile.BaseTile;
/**
* Extends the {@link MisoScene} with functionality needed only by
* entities that plan to display a miso scene.
*/
public interface DisplayMisoScene
extends MisoScene, AStarPathUtil.TraversalPred
{
/**
* This will be called before the scene is displayed to give it a
* chance to look up its image data and prepare itself for display.
*/
public void init ();
/**
* Returns the base tile at the specified coordinates.
*/
public BaseTile getBaseTile (int x, int y);
/**
* Returns the fringe tile at the specified coordinates.
*/
public Tile getFringeTile (int x, int y);
/**
* Returns true if the supplied traverser can traverse the specified
* tile coordinate. The traverser is whatever object is passed along
* to the path finder when a path is being computed.
*
* <p> Scene implementations which support custom traversal based on
* the type of the traverser will want to reflect the traverser's
* class and act acordingly.
*/
public boolean canTraverse (Object traverser, int x, int y);
}
@@ -1,87 +0,0 @@
//
// $Id: DisplayObjectInfo.java,v 1.8 2003/02/12 05:37:19 mdb Exp $
package com.threerings.miso.client;
import java.awt.Rectangle;
import com.threerings.media.tile.ObjectTile;
import com.threerings.miso.data.ObjectInfo;
/**
* Used to track information about an object in the scene.
*/
public class DisplayObjectInfo extends ObjectInfo
{
/** A reference to the object tile itself. */
public ObjectTile tile;
/** The object's bounding rectangle. This will be filled in
* automatically by the scene view when the object is used. */
public Rectangle bounds;
/**
* Convenience constructor.
*/
public DisplayObjectInfo (int tileId, int x, int y)
{
super(tileId, x, y);
}
/**
* Convenience constructor.
*/
public DisplayObjectInfo (ObjectInfo source)
{
super(source);
}
/**
* Provides this display object with a reference to its object tile
* when it becomes available. This must be called before the tile is
* used in any sort of display circumstances.
*/
public void setObjectTile (ObjectTile tile)
{
this.tile = tile;
}
/**
* Returns the render priority of this object tile.
*/
public int getPriority ()
{
// if we have no specified priority return our object tile's
// default priority
return (priority == 0 && tile != null) ? tile.getPriority() : priority;
}
/**
* Overrides the render priority of this object.
*/
public void setPriority (byte priority)
{
this.priority = (byte)Math.max(
Byte.MIN_VALUE, Math.min(Byte.MAX_VALUE, priority));
}
/**
* Indicate to the scene object that it is being hovered over.
*
* @return true if the object should be repainted.
*/
public boolean setHovered (boolean hovered)
{
return false;
}
/**
* Handles the toString-ification of all public members. Derived
* classes can override and include non-public members if desired.
*/
protected void toString (StringBuffer buf)
{
super.toString(buf);
buf.append(", rprio=").append(getPriority());
}
}
@@ -1,796 +0,0 @@
//
// $Id: IsoSceneView.java,v 1.137 2003/04/12 02:14:52 mdb Exp $
package com.threerings.miso.client;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import com.samskivert.util.HashIntMap;
import com.samskivert.util.RuntimeAdjust;
import com.samskivert.util.StringUtil;
import com.threerings.media.RegionManager;
import com.threerings.media.sprite.Sprite;
import com.threerings.media.sprite.SpriteManager;
import com.threerings.media.util.Path;
import com.threerings.media.tile.ObjectTile;
import com.threerings.media.tile.Tile;
import com.threerings.miso.Log;
import com.threerings.miso.MisoPrefs;
import com.threerings.miso.client.DirtyItemList.DirtyItem;
import com.threerings.miso.client.util.AStarPathUtil;
import com.threerings.miso.client.util.IsoUtil;
import com.threerings.miso.client.util.ObjectSet;
import com.threerings.miso.tile.BaseTile;
/**
* The iso scene view provides an isometric view of a particular scene. It
* presently supports scrolling in a limited form. Object tiles are not
* handled properly, nor is mouse highlighting. Those should only be used
* if the view will not be scrolled.
*/
public class IsoSceneView implements SceneView
{
/** Instructs the scene view never to draw highlights around object
* tiles. */
public static final int HIGHLIGHT_NEVER = 0;
/** Instructs the scene view to highlight only object tiles that have
* a non-empty action string. */
public static final int HIGHLIGHT_WITH_ACTION = 1;
/** Instructs the scene view to highlight every object tile,
* regardless of whether it has a valid action string. */
public static final int HIGHLIGHT_ALWAYS = 2;
/** Instructs the scene view to highlight whatever tile the mouse is
* over, regardless of whether or not it is an object tile. This is
* generally only useful in an editor rather than a game. */
public static final int HIGHLIGHT_ALL = 3;
/**
* Constructs an iso scene view.
*
* @param spritemgr the sprite manager.
* @param model the data model.
* @param remgr the region manager that is collecting invalid regions
* for this view.
*/
public IsoSceneView (SpriteManager spritemgr, IsoSceneViewModel model,
RegionManager remgr)
{
// save off references
_spritemgr = spritemgr;
_model = model;
_remgr = remgr;
// handy rectangle
_tbounds = new Rectangle(0, 0, _model.tilewid, _model.tilehei);
}
/**
* Configures the scene view to highlight object tiles either never
* ({@link #HIGHLIGHT_NEVER}), only when an object tile has an
* associated action string ({@link #HIGHLIGHT_WITH_ACTION}), or
* always ({@link #HIGHLIGHT_ALWAYS}). It is also possible to
* configure the view to highlight whatever tile is under the cursor,
* even if it's not an object tile which is done in the {@link
* #HIGHLIGHT_ALL} mode.
*/
public void setHighlightMode (int hmode)
{
_hmode = hmode;
}
// documentation inherited
public void setScene (DisplayMisoScene scene)
{
_scene = scene;
_scene.init();
// obtain a list of the objects in the scene and generate records
// for each of them that contain precomputed metrics
refreshObjectList();
// invalidate the entire screen as there's a new scene in town;
// making sure we're on the AWT thread
if (EventQueue.isDispatchThread()) {
invalidate();
} else {
EventQueue.invokeLater(new Runnable() {
public void run () {
invalidate();
}
});
}
}
/**
* Big fat HACKola: checks the display scene for new objects that
* weren't there last time around and adds info for them into the
* scene.
*/
public void refreshObjectList ()
{
_objects.clear();
// grab all available objects
Rectangle rect = new Rectangle(Short.MIN_VALUE, Short.MIN_VALUE,
2*Short.MAX_VALUE, 2*Short.MAX_VALUE);
_scene.getObjects(rect, _objects);
addAdditionalObjects(_objects);
// and fill in the new objects' bounds
int ocount = _objects.size();
for (int ii = 0; ii < ocount; ii++) {
DisplayObjectInfo scobj = (DisplayObjectInfo)_objects.get(ii);
if (scobj.bounds == null) {
scobj.bounds = IsoUtil.getObjectBounds(_model, scobj);
}
}
}
// documentation inherited
public DisplayMisoScene getScene ()
{
return _scene;
}
/**
* Invalidate the entire visible scene view.
*/
public void invalidate ()
{
_remgr.invalidateRegion(_model.bounds);
}
/**
* Returns the location associated with the specified object's "spot"
* in fine coordinates or null if the object has no spot.
*/
public Point getObjectSpot (DisplayObjectInfo info)
{
if (info.tile.hasSpot()) {
return IsoUtil.tilePlusFineToFull(
_model, info.x, info.y, info.tile.getSpotX(),
info.tile.getSpotY(), new Point());
} else {
return null;
}
}
// documentation inherited from interface
public void paint (Graphics2D gfx, Rectangle dirtyRect)
{
if (_scene == null) {
Log.warning("Scene view painted with null scene.");
return;
}
// render any intersecting tiles
renderTiles(gfx, dirtyRect);
// render anything that goes on top of the tiles
renderBaseDecorations(gfx, dirtyRect);
// render our dirty sprites and objects
renderDirtyItems(gfx, dirtyRect);
// draw sprite paths
if (_pathsDebug.getValue()) {
_spritemgr.renderSpritePaths(gfx);
}
// paint our highlighted tile (if any)
paintHighlights(gfx, dirtyRect);
// paint any extra goodies
paintExtras(gfx, dirtyRect);
}
/**
* Paints the highlighted tile.
*
* @param gfx the graphics context.
*/
protected void paintHighlights (Graphics2D gfx, Rectangle clip)
{
// if we're not highlighting anything, bail now
if (_hmode == HIGHLIGHT_NEVER) {
return;
}
Polygon hpoly = null;
// if we have a hover object, do some business
if (_hobject != null && _hobject instanceof DisplayObjectInfo) {
DisplayObjectInfo scobj = (DisplayObjectInfo)_hobject;
if (scobj.action != null || _hmode == HIGHLIGHT_ALWAYS) {
hpoly = IsoUtil.getObjectFootprint(_model, scobj);
}
}
// if we had no valid hover object, but we're in HIGHLIGHT_ALWAYS,
// go for the tile outline
if (hpoly == null && _hmode == HIGHLIGHT_ALWAYS) {
hpoly = IsoUtil.getTilePolygon(_model, _hcoords.x, _hcoords.y);
}
if (hpoly != null) {
// set the desired stroke and color
Stroke ostroke = gfx.getStroke();
gfx.setStroke(_hstroke);
gfx.setColor(Color.green);
// draw the outline
gfx.draw(hpoly);
// restore the original stroke
gfx.setStroke(ostroke);
}
}
/**
* A function where derived classes can paint extra stuff while we've
* got the clipping region set up.
*/
protected void paintExtras (Graphics2D gfx, Rectangle clip)
{
// nothing for now
}
/**
* Renders the base and fringe layer tiles that intersect the
* specified clipping rectangle.
*/
protected void renderTiles (Graphics2D gfx, Rectangle clip)
{
// if we're showing coordinates, we need to do some setting up
int thw = 0, thh = 0, fhei = 0;
FontMetrics fm = null;
if (_coordsDebug.getValue()) {
fm = gfx.getFontMetrics(_font);
fhei = fm.getAscent();
thw = _model.tilehwid;
thh = _model.tilehhei;
gfx.setFont(_font);
}
// determine which tiles intersect this clipping region: this is
// going to be nearly incomprehensible without some sort of
// diagram; i'll do what i can to comment it, but you'll want to
// print out a scene diagram (docs/miso/scene.ps) and start making
// notes if you want to follow along
// obtain our upper left tile
Point tpos = IsoUtil.screenToTile(_model, clip.x, clip.y, new Point());
// determine which quadrant of the upper left tile we occupy
Point spos = IsoUtil.tileToScreen(_model, tpos.x, tpos.y, new Point());
boolean left = (clip.x - spos.x < _model.tilehwid);
boolean top = (clip.y - spos.y < _model.tilehhei);
// set up our tile position counters
int dx, dy;
if (left) {
dx = 0; dy = 1;
} else {
dx = 1; dy = 0;
}
// if we're in the top-half of the tile we need to move up a row,
// either forward or back depending on whether we're in the left
// or right half of the tile
if (top) {
if (left) {
tpos.x -= 1;
} else {
tpos.y -= 1;
}
// we'll need to start zig-zagging the other way as well
dx = 1 - dx;
dy = 1 - dy;
}
// these will bound our loops
int rightx = clip.x + clip.width, bottomy = clip.y + clip.height;
// Log.info("Preparing to render [tpos=" + StringUtil.toString(tpos) +
// ", left=" + left + ", top=" + top +
// ", clip=" + StringUtil.toString(clip) +
// ", spos=" + StringUtil.toString(spos) +
// "].");
// obtain the coordinates of the tile that starts the first row
// and loop through, rendering the intersecting tiles
IsoUtil.tileToScreen(_model, tpos.x, tpos.y, spos);
while (spos.y < bottomy) {
// set up our row counters
int tx = tpos.x, ty = tpos.y;
_tbounds.x = spos.x;
_tbounds.y = spos.y;
// Log.info("Rendering row [tx=" + tx + ", ty=" + ty + "].");
// render the tiles in this row
while (_tbounds.x < rightx) {
// draw the base and fringe tile images
try {
Tile tile;
boolean passable = true;
if ((tile = _scene.getBaseTile(tx, ty)) != null) {
tile.paint(gfx, _tbounds.x, _tbounds.y);
passable = ((BaseTile)tile).isPassable();
// highlight impassable tiles
if (_traverseDebug.getValue() && !passable) {
fillTile(gfx, tx, ty, Color.yellow);
}
} else {
// draw black where there are no tiles
Polygon poly = IsoUtil.getTilePolygon(_model, tx, ty);
gfx.setColor(Color.black);
gfx.fill(poly);
}
if ((tile = _scene.getFringeTile(tx, ty)) != null) {
tile.paint(gfx, _tbounds.x, _tbounds.y);
}
// highlight passable non-traversable tiles
if (_traverseDebug.getValue() && passable &&
!_scene.canTraverse(null, tx, ty)) {
fillTile(gfx, tx, ty, Color.green);
}
} catch (ArrayIndexOutOfBoundsException e) {
Log.warning("Whoops, booched it [tx=" + tx +
", ty=" + ty + ", tb.x=" + _tbounds.x +
", rightx=" + rightx + "].");
}
// if we're showing coordinates, do that
if (_coordsDebug.getValue()) {
gfx.setColor(Color.white);
// get the top-left screen coordinates of the tile
int sx = _tbounds.x, sy = _tbounds.y;
// draw x-coordinate
String str = String.valueOf(tx);
int xpos = sx + thw - (fm.stringWidth(str) / 2);
gfx.drawString(str, xpos, sy + thh);
// draw y-coordinate
str = String.valueOf(ty);
xpos = sx + thw - (fm.stringWidth(str) / 2);
gfx.drawString(str, xpos, sy + thh + fhei);
// draw the tile polygon as well
gfx.draw(IsoUtil.getTilePolygon(_model, tx, ty));
}
// move one tile to the right
tx += 1; ty -= 1;
_tbounds.x += _model.tilewid;
}
// update our tile coordinates
tpos.x += dx; dx = 1-dx;
tpos.y += dy; dy = 1-dy;
// obtain the screen coordinates of the next starting tile
IsoUtil.tileToScreen(_model, tpos.x, tpos.y, spos);
}
}
/**
* Fills the specified tile with the given color at 50% alpha.
* Intended for debug-only tile highlighting purposes.
*/
protected void fillTile (
Graphics2D gfx, int tx, int ty, Color color)
{
Composite ocomp = gfx.getComposite();
gfx.setComposite(ALPHA_FILL_TILE);
Polygon poly = IsoUtil.getTilePolygon(_model, tx, ty);
gfx.setColor(color);
gfx.fill(poly);
gfx.setComposite(ocomp);
}
/**
* A function where derived classes can paint things after the base
* tiles have been rendered but before anything else has been rendered
* (so that whatever is painted appears to be on the ground).
*/
protected void renderBaseDecorations (Graphics2D gfx, Rectangle clip)
{
// nothing for now
}
/**
* Renders the dirty sprites and objects in the scene to the given
* graphics context.
*/
protected void renderDirtyItems (Graphics2D gfx, Rectangle clip)
{
// if we don't yet have a scene, do nothing
if (_scene == null) {
return;
}
// add any sprites impacted by the dirty rectangle
_dirtySprites.clear();
_spritemgr.getIntersectingSprites(_dirtySprites, clip);
int size = _dirtySprites.size();
for (int ii = 0; ii < size; ii++) {
Sprite sprite = (Sprite)_dirtySprites.get(ii);
Rectangle bounds = sprite.getBounds();
if (!bounds.intersects(clip)) {
continue;
}
appendDirtySprite(_dirtyItems, sprite);
// Log.info("Dirtied item: " + sprite);
}
// add any objects impacted by the dirty rectangle
int ocount = _objects.size();
for (int ii = 0; ii < ocount; ii++) {
DisplayObjectInfo scobj = (DisplayObjectInfo)_objects.get(ii);
if (!scobj.bounds.intersects(clip)) {
continue;
}
// compute the footprint if we're rendering those
Polygon foot = null;
if (_fprintDebug.getValue()) {
foot = IsoUtil.getObjectFootprint(_model, scobj);
}
_dirtyItems.appendDirtyObject(scobj, foot);
// Log.info("Dirtied item: Object(" +
// scobj.x + ", " + scobj.y + ")");
}
// Log.info("renderDirtyItems [items=" + _dirtyItems.size() + "].");
// sort the dirty sprites and objects visually back-to-front;
// paint them and be done
_dirtyItems.sort();
_dirtyItems.paintAndClear(gfx);
}
/**
* Computes the tile coordinates of the supplied sprite and appends it
* to the supplied dirty item list.
*/
protected void appendDirtySprite (DirtyItemList list, Sprite sprite)
{
IsoUtil.screenToTile(_model, sprite.getX(), sprite.getY(), _tcoords);
list.appendDirtySprite(sprite, _tcoords.x, _tcoords.y);
}
/**
* A place for subclasses to add any additional scene objects they
* may have.
*/
protected void addAdditionalObjects (ObjectSet set)
{
// nothing by default
}
// documentation inherited
public Path getPath (Sprite sprite, int x, int y)
{
// make sure we have a scene
if (_scene == null) {
return null;
}
// get the destination tile coordinates
Point src = IsoUtil.screenToTile(
_model, sprite.getX(), sprite.getY(), new Point());
Point dest = IsoUtil.screenToTile(_model, x, y, new Point());
// TODO: compute this value from the screen size or something
int longestPath = 50;
// get a reasonable tile path through the scene
List points = AStarPathUtil.getPath(
_scene, sprite, longestPath, src.x, src.y, dest.x, dest.y);
// construct a path object to guide the sprite on its merry way
return (points == null) ? null :
new TilePath(_model, sprite, points, x, y);
}
// documentation inherited
public Point getScreenCoords (int x, int y)
{
return IsoUtil.fullToScreen(_model, x, y, new Point());
}
// documentation inherited
public Point getFullCoords (int x, int y)
{
return IsoUtil.screenToFull(_model, x, y, new Point());
}
// documentation inherited
public Point getTileCoords (int x, int y)
{
return IsoUtil.screenToTile(_model, x, y, new Point());
}
// documentation inherited
public boolean mouseMoved (MouseEvent e)
{
int x = e.getX(), y = e.getY();
boolean repaint = false;
// update the mouse's tile coordinates
boolean newtile = updateTileCoords(x, y, _hcoords);
// if we're highlighting base tiles, we may need to repaint
if (_hmode == HIGHLIGHT_ALL) {
repaint = (newtile || repaint);
}
// compute the list of objects over which the mouse is hovering
Object hobject = null;
// start with the sprites that contain the point
_spritemgr.getHitSprites(_hitSprites, x, y);
int hslen = _hitSprites.size();
for (int i = 0; i < hslen; i++) {
Sprite sprite = (Sprite)_hitSprites.get(i);
appendDirtySprite(_hitList, sprite);
}
// add the object tiles that contain the point
getHitObjects(_hitList, x, y);
// sort the list of hit items by rendering order
_hitList.sort();
// the last element in the array is what we want (assuming there
// are any items in the array)
int icount = _hitList.size();
if (icount > 0) {
DirtyItem item = (DirtyItem)_hitList.get(icount-1);
hobject = item.obj;
}
repaint |= changeHoverObject(hobject);
// clear out the hitlists
_hitList.clear();
_hitSprites.clear();
return repaint;
}
/**
* Change the hover object to the new object.
*
* @return true if we need to repaint the entire scene. Bah!
*/
protected boolean changeHoverObject (Object newHover)
{
if (newHover == _hobject) {
return false; // no change, no repaint
}
// unhover the old
if (_hobject instanceof DisplayObjectInfo) {
DisplayObjectInfo oldhov = (DisplayObjectInfo) _hobject;
if (oldhov.setHovered(false)) {
_remgr.invalidateRegion(oldhov.bounds);
}
}
hoverObjectChanged(_hobject, newHover);
// set the new
_hobject = newHover;
// hover the new
if (_hobject instanceof DisplayObjectInfo) {
DisplayObjectInfo newhov = (DisplayObjectInfo) _hobject;
if (newhov.setHovered(true)) {
_remgr.invalidateRegion(newhov.bounds);
}
}
return (_hmode != HIGHLIGHT_NEVER);
}
/**
* A place for subclasses to react to the hover object changing.
* One of the supplied arguments may be null.
*/
protected void hoverObjectChanged (Object oldHover, Object newHover)
{
// nothing by default
}
/**
* Adds to the supplied dirty item list, all of the object tiles that
* are hit by the specified point (meaning the point is contained
* within their bounds and intersects a non-transparent pixel in the
* actual object image.
*/
protected void getHitObjects (DirtyItemList list, int x, int y)
{
int ocount = _objects.size();
for (int ii = 0; ii < ocount; ii++) {
DisplayObjectInfo scobj = (DisplayObjectInfo)_objects.get(ii);
Rectangle pbounds = scobj.bounds;
// skip bounding rects that don't contain the point
if (!pbounds.contains(x, y)) {
continue;
}
// now check that the pixel in the tile image is
// non-transparent at that point
if (!scobj.tile.hitTest(x - pbounds.x, y - pbounds.y)) {
continue;
}
// we've passed the test, add the object to the list
list.appendDirtyObject(scobj, null);
}
}
// documentation inherited
public void mouseExited (MouseEvent e)
{
// clear the highlight tracking data
_hcoords.setLocation(-1, -1);
changeHoverObject(null);
}
// documentation inherited
public Object getHoverObject ()
{
return _hobject;
}
/**
* Returns the tile coordinates of the tile over which the mouse is
* hovering.
*/
public Point getHoverCoords ()
{
return _hcoords;
}
/**
* Converts the supplied screen coordinates into tile coordinates,
* writing the values into the supplied {@link Point} instance and
* returning true if the screen coordinates translated into a
* different set of tile coordinates than were already contained in
* the point (so that the caller can know to update a highlight, for
* example).
*
* @return true if the tile coordinates have changed.
*/
protected boolean updateTileCoords (int sx, int sy, Point tpos)
{
Point npos = IsoUtil.screenToTile(_model, sx, sy, new Point());
// make sure the new coordinate is both valid and different
if (_model.isCoordinateValid(npos.x, npos.y) && !tpos.equals(npos)) {
tpos.setLocation(npos.x, npos.y);
return true;
} else {
return false;
}
}
/** The sprite manager. */
protected SpriteManager _spritemgr;
/** The scene view model data. */
protected IsoSceneViewModel _model;
/** Our region manager. */
protected RegionManager _remgr;
/** The scene to be displayed. */
protected DisplayMisoScene _scene;
/** Information for all of the objects visible in the scene. */
protected ObjectSet _objects = new ObjectSet();
/** The dirty sprites and objects that need to be re-painted. */
protected DirtyItemList _dirtyItems = new DirtyItemList(this);
/** The working sprites list used when calculating dirty regions. */
protected ArrayList _dirtySprites = new ArrayList();
/** Used when rendering tiles. */
protected Rectangle _tbounds;
/** Used when dirtying sprites. */
protected Point _tcoords = new Point();
/** Used to collect the list of sprites "hit" by a particular mouse
* location. */
protected List _hitSprites = new ArrayList();
/** The list that we use to track and sort the items over which the
* mouse is hovering. */
protected DirtyItemList _hitList = new DirtyItemList(this);
/** The highlight mode. */
protected int _hmode = HIGHLIGHT_NEVER;
/** Info on the object that the mouse is currently hovering over. */
protected Object _hobject;
/** Used to track the tile coordinates over which the mouse is hovering. */
protected Point _hcoords = new Point();
/** The font to draw tile coordinates. */
protected Font _font = new Font("Arial", Font.PLAIN, 7);
/** The stroke object used to draw highlighted tiles and coordinates. */
protected BasicStroke _hstroke = new BasicStroke(2);
/** A debug hook that toggles debug rendering of traversable tiles. */
protected static RuntimeAdjust.BooleanAdjust _traverseDebug =
new RuntimeAdjust.BooleanAdjust(
"Toggles debug rendering of traversable and impassable tiles in " +
"the iso scene view.", "narya.miso.iso_traverse_debug_render",
MisoPrefs.config, false);
/** A debug hook that toggles debug rendering of tile coordinates. */
protected static RuntimeAdjust.BooleanAdjust _coordsDebug =
new RuntimeAdjust.BooleanAdjust(
"Toggles debug rendering of tile coordinates in the iso scene " +
"view.", "narya.miso.iso_coords_debug_render",
MisoPrefs.config, false);
/** A debug hook that toggles debug rendering of sprite paths. */
protected static RuntimeAdjust.BooleanAdjust _pathsDebug =
new RuntimeAdjust.BooleanAdjust(
"Toggles debug rendering of sprite paths in the iso scene view.",
"narya.miso.iso_paths_debug_render", MisoPrefs.config, false);
/** A debug hook that toggles debug rendering of object footprints. */
protected static RuntimeAdjust.BooleanAdjust _fprintDebug =
new RuntimeAdjust.BooleanAdjust(
"Toggles debug rendering of object footprints in the iso scene " +
"view.", "narya.miso.iso_fprint_debug_render",
MisoPrefs.config, false);
/** The stroke used to draw dirty rectangles. */
protected static final Stroke DIRTY_RECT_STROKE = new BasicStroke(2);
/** The alpha used to fill tiles for debugging purposes. */
protected static final Composite ALPHA_FILL_TILE =
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
}
@@ -1,222 +0,0 @@
//
// $Id: IsoSceneViewModel.java,v 1.29 2003/04/12 02:14:52 mdb Exp $
package com.threerings.miso.client;
import java.awt.Point;
import java.awt.Rectangle;
import com.threerings.miso.Log;
import com.threerings.miso.MisoConfig;
import com.threerings.miso.client.util.IsoUtil;
/**
* Provides a holding place for the myriad parameters and bits of data
* that describe the details of an isometric view of a scene.
*
* <p> The member data are public to facilitate speedy referencing by the
* {@link IsoSceneView} class. The model should only be modified through
* the configuration data provided via {@link MisoConfig} and the accessor
* methods.
*/
public class IsoSceneViewModel
{
/** Tile dimensions and half-dimensions in the view. */
public int tilewid, tilehei, tilehwid, tilehhei;
/** Fine coordinate dimensions. */
public int finehwid, finehhei;
/** Number of fine coordinates on each axis within a tile. */
public int finegran;
/** Size of the view in tile count. */
public int scenevwid, scenevhei;
/** Whether or not this view can extend beyond the bounds defined by
* the view width and height. True if it cannot, false if it can. */
public boolean bounded = false;
/** The bounds of the view in screen pixel coordinates. */
public Rectangle bounds;
/** The position in pixels at which tile (0, 0) is drawn. */
public Point origin;
/** The length of a tile edge in pixels. */
public float tilelen;
/** The slope of the x- and y-axis lines. */
public float slopeX, slopeY;
/** The length between fine coordinates in pixels. */
public float finelen;
/** The y-intercept of the x-axis line within a tile. */
public float fineBX;
/** The slope of the x- and y-axis lines within a tile. */
public float fineSlopeX, fineSlopeY;
/**
* Construct an iso scene view model with view parameters as
* specified in the given config object.
*
* @param config the config object.
*/
public IsoSceneViewModel ()
{
// and the view dimensions
scenevwid = MisoConfig.config.getValue(
SCENE_VWIDTH_KEY, DEF_SCENE_VWIDTH);
scenevhei = MisoConfig.config.getValue(
SCENE_VHEIGHT_KEY, DEF_SCENE_VHEIGHT);
// get the tile dimensions
tilewid = MisoConfig.config.getValue(TILE_WIDTH_KEY, DEF_TILE_WIDTH);
tilehei = MisoConfig.config.getValue(TILE_HEIGHT_KEY, DEF_TILE_HEIGHT);
// set the fine coordinate granularity
finegran = MisoConfig.config.getValue(FINE_GRAN_KEY, DEF_FINE_GRAN);
// precalculate various things
int offy = MisoConfig.config.getValue(SCENE_OFFSET_Y_KEY, DEF_OFFSET_Y);
precalculate(offy);
}
/**
* Constructs an iso scene view model by directly specifying the
* desired scene configuration parameters.
*
* @param scenewid the total scene width in tiles.
* @param scenehei the total scene height in tiles.
* @param tilewid the width in pixels of the tiles.
* @param tilehei the height in pixels of the tiles.
* @param finegran the number of sub-tile divisions to use for fine
* coordinates.
* @param svwid the width in tiles of the viewport.
* @param svhei the height in tiles of the viewport.
* @param offy the offset of the origin (in tiles) from the top of the
* viewport.
*/
public IsoSceneViewModel (int scenewid, int scenehei,
int tilewid, int tilehei, int finegran,
int svwid, int svhei, int offy)
{
// keep track of this stuff
this.tilewid = tilewid;
this.tilehei = tilehei;
this.finegran = finegran;
this.scenevwid = svwid;
this.scenevhei = svhei;
// let our flags default to false
// precalculate various things
precalculate(offy);
}
/**
* Returns whether the given tile coordinate is a valid coordinate in
* our coordinate system (which allows tile coordinates from 0 to
* 2^15-1).
*/
public boolean isCoordinateValid (int x, int y)
{
return (x >= 0 && x < Short.MAX_VALUE &&
y >= 0 && y < Short.MAX_VALUE);
}
/**
* Returns whether the given full coordinate is a valid coordinate
* within the scene.
*/
public boolean isFullCoordinateValid (int x, int y)
{
int tx = IsoUtil.fullToTile(x), ty = IsoUtil.fullToTile(y);
int fx = IsoUtil.fullToFine(x), fy = IsoUtil.fullToFine(y);
return (isCoordinateValid(tx, ty) &&
fx >= 0 && fx < finegran &&
fy >= 0 && fy < finegran);
}
/**
* Pre-calculate various member data that are commonly used in working
* with an isometric view.
*/
protected void precalculate (int offy)
{
// set the desired scene view bounds
bounds = new Rectangle(0, 0, scenevwid * tilewid, scenevhei * tilehei);
// set the scene display origin
origin = new Point((bounds.width / 2), (offy * tilehei));
// pre-calculate tile-related data
precalculateTiles();
// calculate the edge length separating each fine coordinate
finelen = tilelen / (float)finegran;
// calculate the fine-coordinate x-axis line
fineSlopeX = (float)tilehei / (float)tilewid;
fineBX = -(fineSlopeX * (float)tilehwid);
fineSlopeY = -fineSlopeX;
// calculate the fine coordinate dimensions
finehwid = (int)((float)tilehwid / (float)finegran);
finehhei = (int)((float)tilehhei / (float)finegran);
}
/**
* Pre-calculate various tile-related member data.
*/
protected void precalculateTiles ()
{
// halve the dimensions
tilehwid = (tilewid / 2);
tilehhei = (tilehei / 2);
// calculate the length of a tile edge in pixels
tilelen = (float) Math.sqrt(
(tilehwid * tilehwid) + (tilehhei * tilehhei));
// calculate the slope of the x- and y-axis lines
slopeX = (float)tilehei / (float)tilewid;
slopeY = -slopeX;
}
/** The config key for tile width in pixels. */
protected static final String TILE_WIDTH_KEY = "tile_width";
/** The config key for tile height in pixels. */
protected static final String TILE_HEIGHT_KEY = "tile_height";
/** The config key for tile fine coordinate granularity. */
protected static final String FINE_GRAN_KEY = "fine_granularity";
/** The config key for scene view width in tile count. */
protected static final String SCENE_VWIDTH_KEY = "scene_view_width";
/** The config key for scene view height in tile count. */
protected static final String SCENE_VHEIGHT_KEY = "scene_view_height";
/** The config key for scene width in tile count. */
protected static final String SCENE_WIDTH_KEY = "scene_width";
/** The config key for scene height in tile count. */
protected static final String SCENE_HEIGHT_KEY = "scene_height";
/** The config key for scene origin vertical offset in tile count. */
protected static final String SCENE_OFFSET_Y_KEY = "scene_offset_y";
/** Default scene view parameters. */
protected static final int DEF_TILE_WIDTH = 64;
protected static final int DEF_TILE_HEIGHT = 48;
protected static final int DEF_FINE_GRAN = 4;
protected static final int DEF_SCENE_VWIDTH = 10;
protected static final int DEF_SCENE_VHEIGHT = 12;
protected static final int DEF_SCENE_WIDTH = 22;
protected static final int DEF_SCENE_HEIGHT = 22;
protected static final int DEF_OFFSET_Y = -5;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,115 @@
//
// $Id: ObjectActionHandler.java,v 1.1 2003/04/17 19:21:16 mdb Exp $
package com.threerings.miso.client;
import java.awt.event.ActionEvent;
import java.util.HashMap;
import javax.swing.Icon;
import com.samskivert.swing.RadialMenu;
import com.samskivert.util.StringUtil;
import com.threerings.miso.Log;
import com.threerings.miso.client.SceneObject;
/**
* Objects in scenes can be configured to generate action events. Those
* events are grouped into types and an object action handler can be
* registered to handle all actions of a particular type.
*/
public class ObjectActionHandler
{
/**
* Returns true if we should allow this object action, false if we
* should not. This is used to completely hide actions that should not
* be visible without the proper privileges.
*/
public boolean actionAllowed (String action)
{
return true;
}
/**
* Returns the tooltip icon for the specified action or null if the
* action has no tooltip icon.
*/
public Icon getTipIcon (String action)
{
return null;
}
/**
* Return a {@link RadialMenu} or null if no menu needed.
*/
public RadialMenu handlePressed (SceneObject sourceObject)
{
return null;
}
/**
* Called when an action is generated for an object.
*/
public void handleAction (SceneObject scobj, ActionEvent event)
{
Log.warning("Unknown object action [scobj=" + scobj +
", action=" + event + "].");
}
/**
* Returns the type associated with this action command (which is
* mapped to a registered object action handler) or the empty string
* if it has no type.
*/
public static String getType (String command)
{
int cidx = StringUtil.blank(command) ? -1 : command.indexOf(':');
return (cidx == -1) ? "" : command.substring(0, cidx);
}
/**
* Returns the unqualified object action (minus the type, see {@link
* #getType}).
*/
public static String getAction (String command)
{
int cidx = StringUtil.blank(command) ? -1 : command.indexOf(':');
return (cidx == -1) ? command : command.substring(cidx+1);
}
/**
* Looks up the object action handler associated with the specified
* command.
*/
public static ObjectActionHandler lookup (String command)
{
return (ObjectActionHandler)_oahandlers.get(getType(command));
}
/**
* Registers an object action handler which will be called when a user
* clicks on an object in a scene that has an associated action.
*/
public static void register (String prefix, ObjectActionHandler handler)
{
// make sure we know about potential funny business
if (_oahandlers.containsKey(prefix)) {
Log.warning("Warning! Overwriting previous object action " +
"handler registration, all hell could shortly " +
"break loose [prefix=" + prefix +
", handler=" + handler + "].");
}
_oahandlers.put(prefix, handler);
}
/**
* Removes an object action handler registration.
*/
public static void unregister (String prefix)
{
_oahandlers.remove(prefix);
}
/** Our registered object action handlers. */
protected static HashMap _oahandlers = new HashMap();
}
@@ -0,0 +1,262 @@
//
// $Id: SceneBlock.java,v 1.1 2003/04/17 19:21:16 mdb Exp $
package com.threerings.miso.client;
import java.awt.Polygon;
import java.awt.Rectangle;
import com.samskivert.util.HashIntMap;
import com.samskivert.util.StringUtil;
import com.threerings.media.tile.NoSuchTileException;
import com.threerings.media.tile.NoSuchTileSetException;
import com.threerings.media.tile.Tile;
import com.threerings.media.tile.TileManager;
import com.threerings.media.tile.TileSet;
import com.threerings.miso.Log;
import com.threerings.miso.data.MisoSceneModel;
import com.threerings.miso.tile.BaseTile;
import com.threerings.miso.util.MisoUtil;
import com.threerings.miso.util.ObjectSet;
/**
* Contains the base and object tile information on a particular
* rectangular region of a scene.
*/
public class SceneBlock
{
/**
* Creates a scene block and resolves the base and object tiles that
* reside therein.
*/
public SceneBlock (
MisoScenePanel panel, int tx, int ty, int width, int height)
{
_bounds = new Rectangle(tx, ty, width, height);
_base = new BaseTile[width*height];
_fringe = new Tile[width*height];
_covered = new boolean[width*height];
// compute our screen-coordinate footprint polygon
_footprint = MisoUtil.getFootprintPolygon(
panel.getSceneMetrics(), tx, ty, width, height);
// resolve our base tiles
MisoSceneModel model = panel.getSceneModel();
TileManager tmgr = panel.getTileManager();
for (int yy = 0; yy < height; yy++) {
for (int xx = 0; xx < width; xx++) {
int x = tx + xx, y = ty + yy;
int fqTileId = model.getBaseTileId(x, y);
if (fqTileId <= 0) {
continue;
}
// this is a bit magical, but the tile manager will fetch
// tiles from the tileset repository and the tile set id
// from which we request this tile must map to a base tile
// as provided by the repository, so we just cast it to a
// base tile and know that all is well
String errmsg = null;
int tidx = yy*width+xx;
try {
_base[tidx] = (BaseTile)tmgr.getTile(fqTileId);
} catch (ClassCastException cce) {
errmsg = "Scene contains non-base tile in base layer";
} catch (NoSuchTileSetException nste) {
errmsg = "Scene contains non-existent tileset";
} catch (NoSuchTileException nste) {
errmsg = "Scene contains non-existent tile";
}
if (errmsg != null) {
Log.warning(errmsg + " [fqtid=" + fqTileId +
", x=" + x + ", y=" + y + "].");
}
if (_base[tidx] == null) {
continue;
}
// compute the fringe for this tile
_fringe[tidx] = panel.computeFringeTile(x, y);
}
}
// resolve our objects
ObjectSet set = new ObjectSet();
model.getObjects(_bounds, set);
_objects = new SceneObject[set.size()];
for (int ii = 0; ii < _objects.length; ii++) {
_objects[ii] = new SceneObject(panel, set.get(ii));
}
}
/**
* Returns the screen-coordinate polygon bounding the footprint of
* this block.
*/
public Polygon getFootprint ()
{
return _footprint;
}
/**
* Returns an array of all resolved scene objects in this block.
*/
public SceneObject[] getObjects ()
{
return _objects;
}
/**
* Returns the base tile at the specified coordinates or null if
* there's no tile at said coordinates.
*/
public BaseTile getBaseTile (int tx, int ty)
{
return _base[index(tx, ty)];
}
/**
* Returns the fringe tile at the specified coordinates or null if
* there's no tile at said coordinates.
*/
public Tile getFringeTile (int tx, int ty)
{
return _fringe[index(tx, ty)];
}
/**
* Returns true if the specified traverser can traverse the specified
* tile (which is assumed to be in the bounds of this scene block).
*/
public boolean canTraverse (Object traverser, int tx, int ty)
{
BaseTile base = getBaseTile(tx, ty);
return !_covered[index(tx, ty)] && (base != null && base.isPassable());
}
/**
* Returns the index into our arrays of the specified tile.
*/
protected final int index (int tx, int ty)
{
return (ty-_bounds.y)*_bounds.width + (tx-_bounds.x);
}
/**
* Returns a string representation of this instance.
*/
public String toString ()
{
return StringUtil.toString(_bounds) + ":" + _objects.length;
}
/**
* Links this block to its neighbors; informs neighboring blocks of
* object coverage.
*/
protected void update (HashIntMap blocks)
{
boolean recover = false;
// link up to our neighbors
for (int ii = 0; ii < DX.length; ii++) {
SceneBlock neigh = (SceneBlock)
blocks.get(neighborKey(DX[ii], DY[ii]));
if (neigh != _neighbors[ii]) {
_neighbors[ii] = neigh;
// if we're linking up to a neighbor for the first time;
// we need to recalculate our coverage
recover = recover || (neigh != null);
// Log.info(this + " was introduced to " + neigh + ".");
}
}
// if we need to regenerate the set of tiles covered by our
// objects, do so
if (recover) {
for (int ii = 0; ii < _objects.length; ii++) {
setCovered(blocks, _objects[ii]);
}
}
}
/** Computes the key of our neighbor. */
protected final int neighborKey (int dx, int dy)
{
return ((short)(_bounds.x/_bounds.width+dx) << 16 |
(short)(_bounds.y/_bounds.height+dy));
}
/** Computes the key for the block that holds the specified tile. */
protected final int blockKey (int tx, int ty)
{
return ((short)(tx/_bounds.width) << 16 |
(short)(ty/_bounds.height));
}
/**
* Sets the footprint of this object tile
*/
protected void setCovered (HashIntMap blocks, SceneObject scobj)
{
int endx = Math.max(0, (scobj.info.x - scobj.tile.getBaseWidth() + 1));
int endy = Math.max(0, (scobj.info.y - scobj.tile.getBaseHeight() + 1));
for (int xx = scobj.info.x; xx >= endx; xx--) {
if ((xx < 0) || (xx >= Short.MAX_VALUE)) {
continue;
}
for (int yy = scobj.info.y; yy >= endy; yy--) {
if ((yy < 0) || (yy >= Short.MAX_VALUE)) {
continue;
}
SceneBlock block = (SceneBlock)blocks.get(blockKey(xx, yy));
if (block != null) {
block.setCovered(xx, yy);
}
}
}
// Log.info("Updated coverage " + scobj.info + ".");
}
/**
* Indicates that this tile is covered by an object footprint.
*/
protected void setCovered (int tx, int ty)
{
_covered[index(tx, ty)] = true;
}
/** The bounds of (in tile coordinates) of this block. */
protected Rectangle _bounds;
/** A polygon bounding the footprint of this block. */
protected Polygon _footprint;
/** Our base tiles. */
protected BaseTile[] _base;
/** Our fringe tiles. */
protected Tile[] _fringe;
/** Indicates whether our tiles are covered by an object. */
protected boolean[] _covered;
/** Info on our objects. */
protected SceneObject[] _objects;
/** Our neighbors in the eight cardinal directions. */
protected SceneBlock[] _neighbors = new SceneBlock[DX.length];
// used to link up to our neighbors
protected static final int[] DX = { -1, -1, 0, 1, 1, 1, 0, -1 };
protected static final int[] DY = { 0, -1, -1, -1, 0, 1, 1, 1 };
}
@@ -0,0 +1,260 @@
//
// $Id: SceneObject.java,v 1.1 2003/04/17 19:21:16 mdb Exp $
package com.threerings.miso.client;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import com.samskivert.util.RuntimeAdjust;
import com.samskivert.util.StringUtil;
import com.threerings.media.tile.NoSuchTileException;
import com.threerings.media.tile.NoSuchTileSetException;
import com.threerings.media.tile.ObjectTile;
import com.threerings.media.tile.TileUtil;
import com.threerings.miso.Log;
import com.threerings.miso.MisoPrefs;
import com.threerings.miso.data.ObjectInfo;
import com.threerings.miso.util.MisoSceneMetrics;
import com.threerings.miso.util.MisoUtil;
/**
* Contains resolved information on an object in a scene.
*/
public class SceneObject
{
/** The object's info record. */
public ObjectInfo info;
/** The object tile used to display this object. */
public ObjectTile tile;
/** The screen coordinate bounds of our object tile given its position
* in the scene. */
public Rectangle bounds;
/**
* Creates a scene object for display by the specified panel. The
* appropriate object tile is resolved and the object's in-situ bounds
* are computed.
*/
public SceneObject (MisoScenePanel panel, ObjectInfo info)
{
this.info = info;
// resolve our object tile
int tsid = TileUtil.getTileSetId(info.tileId);
int tidx = TileUtil.getTileIndex(info.tileId);
try {
tile = (ObjectTile)panel.getTileManager().getTile(
tsid, tidx, panel.getColorizer(info));
init(panel);
} catch (NoSuchTileException nste) {
Log.warning("Scene contains non-existent object tile " +
"[info=" + info + "].");
} catch (NoSuchTileSetException te) {
Log.warning("Scene contains non-existent object tileset " +
"[info=" + info + "].");
}
}
/**
* Creates a scene object for display by the specified panel.
*/
public SceneObject (MisoScenePanel panel, ObjectInfo info, ObjectTile tile)
{
this.info = info;
this.tile = tile;
init(panel);
}
/**
* Finishes the initialization of this scene object.
*/
protected void init (MisoScenePanel panel)
{
// compute our object's screen bounds
MisoSceneMetrics metrics = panel.getSceneMetrics();
// start with the screen coordinates of our origin tile
Point tpos = MisoUtil.tileToScreen(
metrics, info.x, info.y, new Point());
// if the tile has an origin coordinate, use that, otherwise
// compute it from the tile footprint
int tox = tile.getOriginX(), toy = tile.getOriginY();
if (tox == Integer.MIN_VALUE) {
tox = tile.getBaseWidth() * metrics.tilehwid;
}
if (toy == Integer.MIN_VALUE) {
toy = tile.getHeight();
}
bounds = new Rectangle(tpos.x + metrics.tilehwid - tox,
tpos.y + metrics.tilehei - toy,
tile.getWidth(), tile.getHeight());
// compute our object footprint as well
_footprint = MisoUtil.getFootprintPolygon(
metrics, info.x-tile.getWidth()+1, info.y-tile.getHeight()+1,
tile.getWidth(), tile.getHeight());
// compute our object spot if we've got one
if (tile.hasSpot()) {
_fspot = MisoUtil.tilePlusFineToFull(
metrics, info.x, info.y, tile.getSpotX(), tile.getSpotY(),
new Point());
_sspot = MisoUtil.fullToScreen(
metrics, _fspot.x, _fspot.y, new Point());
}
}
/**
* Requests that this scene object render itself.
*/
public void paint (Graphics2D gfx)
{
if (_hideObjects.getValue()) {
return;
}
// if we're rendering footprints, paint that
boolean footpaint = _fprintDebug.getValue();
if (footpaint) {
gfx.setColor(Color.black);
gfx.draw(_footprint);
}
// paint our tile
tile.paint(gfx, bounds.x, bounds.y);
// and possibly paint the object's spot
if (footpaint && _sspot != null) {
// translate the origin to center on the portal
gfx.translate(_sspot.x, _sspot.y);
// rotate to reflect the spot orientation
double rot = (Math.PI / 4.0f) * tile.getSpotOrient();
gfx.rotate(rot);
// draw the spot triangle
gfx.setColor(Color.green);
gfx.fill(_spotTri);
// outline the triangle in black
gfx.setColor(Color.black);
gfx.draw(_spotTri);
// restore the original transform
gfx.rotate(-rot);
gfx.translate(-_sspot.x, -_sspot.y);
}
}
/**
* Returns the location associated with this object's "spot" in fine
* coordinates or null if it has no spot.
*/
public Point getObjectSpot ()
{
return _fspot;
}
/**
* Returns true if this object's footprint overlaps that of the
* specified other object.
*/
public boolean objectFootprintOverlaps (SceneObject so)
{
return (so.info.x > info.x - tile.getBaseWidth() &&
info.x > so.info.x - so.tile.getBaseWidth() &&
so.info.y > info.y - tile.getBaseHeight() &&
info.y > so.info.y - so.tile.getBaseHeight());
}
/**
* Returns a polygon bounding all footprint tiles of this scene
* object.
*
* @return the bounding polygon.
*/
public Polygon getObjectFootprint ()
{
return _footprint;
}
/**
* Returns the render priority of this scene object.
*/
public int getPriority ()
{
// if we have no overridden priority, return our object tile's
// default priority
return (info.priority == 0 && tile != null) ?
tile.getPriority() : info.priority;
}
/**
* Overrides the render priority of this object.
*/
public void setPriority (byte priority)
{
info.priority = (byte)Math.max(
Byte.MIN_VALUE, Math.min(Byte.MAX_VALUE, priority));
}
/**
* Informs this scene object that the mouse is now hovering over it.
* Custom objects may wish to adjust some internal state and return
* true from this method indicating that they should be repainted.
*/
public boolean setHovered (boolean hovered)
{
return false;
}
/**
* Returns a string representation of this instance.
*/
public String toString ()
{
return info + "[" + StringUtil.toString(bounds) + "]";
}
/** Our object footprint as a polygon. */
protected Polygon _footprint;
/** The full-coordinates of our object spot; or null if we have none. */
protected Point _fspot;
/** The screen-coordinates of our object spot; or null if we have none. */
protected Point _sspot;
/** A debug hook that toggles rendering of objects. */
protected static RuntimeAdjust.BooleanAdjust _hideObjects =
new RuntimeAdjust.BooleanAdjust(
"Toggles rendering of objects in the scene view.",
"narya.miso.hide_objects", MisoPrefs.config, false);
/** A debug hook that toggles rendering of object footprints. */
protected static RuntimeAdjust.BooleanAdjust _fprintDebug =
new RuntimeAdjust.BooleanAdjust(
"Toggles rendering of object footprints in the scene view.",
"narya.miso.iso_fprint_debug_render", MisoPrefs.config, false);
/** The triangle used to render an object's spot. */
protected static Polygon _spotTri;
static {
_spotTri = new Polygon();
_spotTri.addPoint(-3, -3);
_spotTri.addPoint(3, -3);
_spotTri.addPoint(0, 3);
};
}
@@ -0,0 +1,126 @@
//
// $Id: SceneObjectTip.java,v 1.1 2003/04/17 19:21:16 mdb Exp $
package com.threerings.miso.client;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.Collection;
import javax.swing.Icon;
import javax.swing.UIManager;
import com.samskivert.swing.Label;
import com.samskivert.swing.LabelSausage;
import com.samskivert.swing.LabelStyleConstants;
import com.samskivert.swing.util.SwingUtil;
import com.samskivert.util.StringUtil;
/**
* A lightweight tooltip used by the {@link MisoScenePanel}. The tip
* foreground and background are controlled by the following {@link
* UIManager} properties:
*
* <pre>
* SceneObjectTip.background
* SceneObjectTip.foreground
* SceneObjectTip.font (falls back to Label.font)
* </pre>
*/
public class SceneObjectTip extends LabelSausage
{
/** The bounding box of this tip, or null prior to layout(). */
public Rectangle bounds;
/**
* Construct a SceneObjectTip.
*/
public SceneObjectTip (String text, Icon icon)
{
super(new Label(text, _foreground, _font), icon);
}
/**
* Called to initialize the tip so that it can be painted.
*
* @param tipFor the bounding rectangle for the object we tip for.
* @param boundary the boundary of all displayable space.
* @param othertips other tip boundaries that we should avoid.
*/
public void layout (Graphics2D gfx, Rectangle tipFor, Rectangle boundary,
Collection othertips)
{
layout(gfx, PAD);
bounds = new Rectangle(_size);
// center in the on-screen portion of the bounding box of the
// object we're tipping for, but don't go above MAX_HEIGHT from
// the bottom...
Rectangle anchor = boundary.intersection(tipFor);
bounds.setLocation(
anchor.x + (anchor.width - bounds.width) / 2,
anchor.y + Math.max(
(anchor.height - bounds.height) / 2,
anchor.height - MAX_HEIGHT));
// and jiggle it to not overlap any other tips
SwingUtil.positionRect(bounds, boundary, othertips);
}
/**
* Paint this tip at it's location.
*/
public void paint (Graphics2D gfx)
{
paint(gfx, bounds.x, bounds.y, _background, null);
}
/**
* Generates a string representation of this instance.
*/
public String toString ()
{
return _label.getText() + "[" + StringUtil.toString(bounds) + "]";
}
// documentation inherited
protected void drawBase (Graphics2D gfx, int x, int y)
{
Composite ocomp = gfx.getComposite();
gfx.setComposite(ALPHA);
super.drawBase(gfx, x, y);
gfx.setComposite(ocomp);
}
/** The alpha we use for our base. */
protected static final Composite ALPHA = AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, .75f);
/** Colors to use when rendering the tip. */
protected static Color _background, _foreground;
/** The font to use when rendering the tip. */
protected static Font _font;
// initialize resources shared by all tips
static {
_background = UIManager.getColor("SceneObjectTip.background");
_foreground = UIManager.getColor("SceneObjectTip.foreground");
_font = UIManager.getFont("SceneObjectTip.font");
if (_font == null) {
_font = UIManager.getFont("Label.font");
}
}
/** The number of pixels to reserve between elements of the tip. */
protected static final int PAD = 3;
/** The maximum height above the bottom of the object bounds that we are
* to center ourselves. */
protected static final int MAX_HEIGHT = 80;
}
@@ -1,87 +0,0 @@
//
// $Id: SceneView.java,v 1.33 2003/01/31 23:10:45 mdb Exp $
package com.threerings.miso.client;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import com.threerings.media.sprite.Sprite;
import com.threerings.media.util.Path;
/**
* The scene view interface provides an interface to be implemented by
* classes that provide a view of a given scene by drawing the scene
* contents onto a particular GUI component.
*/
public interface SceneView
{
/**
* Renders an invalid porition of the scene to the given graphics
* context.
*
* @param gfx the graphics context.
* @param invalidRect the invalid region to be repainted.
*/
public void paint (Graphics2D gfx, Rectangle invalidRect);
/**
* Sets the scene that we're rendering.
*
* @param scene the scene to render in the view.
*/
public void setScene (DisplayMisoScene scene);
/**
* Returns the scene being rendered.
*/
public DisplayMisoScene getScene ();
/**
* Returns a {@link Path} object detailing a valid path for the
* given sprite to take in the scene to get from its current
* position to the destination position.
*
* @param sprite the sprite to move.
* @param x the destination x-position in pixel coordinates.
* @param y the destination y-position in pixel coordinates.
*
* @return the sprite's path, or null if no valid path exists.
*/
public Path getPath (Sprite sprite, int x, int y);
/**
* Returns screen coordinates given the specified full coordinates.
*/
public Point getScreenCoords (int x, int y);
/**
* Returns full coordinates given the specified screen coordinates.
*/
public Point getFullCoords (int x, int y);
/**
* Must be called by the containing panel when the mouse moves over
* the view.
*
* @return true if a repaint is required, false if not.
*/
public boolean mouseMoved (MouseEvent e);
/**
* Must be called by the containing panel when the mouse exits the
* view.
*/
public void mouseExited (MouseEvent e);
/**
* Returns information about the object over which the mouse is
* currently hovering (either a {@link DisplayObjectInfo} or a {@link
* Sprite}), or null if the mouse is not hovering over anything of
* interest.
*/
public Object getHoverObject ();
}
@@ -1,174 +0,0 @@
//
// $Id: SceneViewPanel.java,v 1.49 2003/01/31 23:10:45 mdb Exp $
package com.threerings.miso.client;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.List;
import com.samskivert.util.StringUtil;
import com.threerings.media.FrameManager;
import com.threerings.media.VirtualMediaPanel;
import com.threerings.media.sprite.Sprite;
import com.threerings.media.sprite.SpriteManager;
import com.threerings.miso.Log;
import com.threerings.miso.client.util.IsoUtil;
/**
* The scene view panel is responsible for managing a {@link
* SceneView}, rendering it to the screen, and handling view-related
* UI events.
*/
public class SceneViewPanel extends VirtualMediaPanel
{
/**
* Constructs the scene view panel with the supplied view model.
*/
public SceneViewPanel (FrameManager framemgr, IsoSceneViewModel model)
{
super(framemgr);
// we're going to want to be opaque
setOpaque(true);
// create the data model for the scene view
_viewmodel = model;
// create the scene view
_view = newSceneView(_spritemgr, _viewmodel);
// listen to mouse...
addMouseListener(new MouseAdapter() {
public void mouseExited (MouseEvent e) {
_view.mouseExited(e);
repaint();
}
});
// ...and mouse motion events
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved (MouseEvent e) {
if (_view.mouseMoved(e)) {
repaint();
}
}
public void mouseDragged (MouseEvent e) {
if (_view.mouseMoved(e)) {
repaint();
}
}
});
}
/**
* Gets the iso scene view model associated with this panel.
*/
public IsoSceneViewModel getModel ()
{
return _viewmodel;
}
/**
* Constructs the underlying scene view implementation.
*/
protected SceneView newSceneView (
SpriteManager smgr, IsoSceneViewModel model)
{
return new IsoSceneView(smgr, model, _remgr);
}
/**
* Sets the scene managed by the panel.
*/
public void setScene (DisplayMisoScene scene)
{
if (scene == null) {
Log.warning("Ignoring request to set null scene.");
} else {
_view.setScene(scene);
}
}
/**
* Gets the scene managed by the panel.
*/
public SceneView getSceneView ()
{
return _view;
}
// documentation inherited
public void doLayout ()
{
super.doLayout();
// figure out our viewport offsets
Dimension size = getSize();
// start out centered in the display
setViewLocation((_viewmodel.bounds.width - size.width)/2,
(_viewmodel.bounds.height - size.height)/2);
// Log.info("Size: " + StringUtil.toString(size) +
// ", vsize: " + StringUtil.toString(_viewmodel.bounds) +
// ", nx: " + _nx + ", ny: " + _ny + ".");
}
// documentation inherited
public void setViewLocation (int x, int y)
{
// if we're bounded, make sure no one tries to set our view
// location outside the bounds defined by the view model
if (_viewmodel.bounded) {
int minx = _viewmodel.bounds.x,
maxx = _viewmodel.bounds.width-getWidth();
int miny = _viewmodel.bounds.y,
maxy = _viewmodel.bounds.height-getHeight();
if (x < minx) { x = minx; } else if (x > maxx) { x = maxx; }
if (y < miny) { y = miny; } else if (y > maxy) { y = maxy; }
}
super.setViewLocation(x, y);
}
// documentation inherited
protected void paintBetween (Graphics2D gfx, Rectangle dirty)
{
_view.paint(gfx, dirty);
}
/**
* We don't want sprites rendered using the standard mechanism because
* we intersperse them with objects in our scene and need to manage
* their z-order.
*/
protected void paintBits (Graphics2D gfx, int layer, Rectangle dirty)
{
_animmgr.renderMedia(gfx, layer, dirty);
}
/**
* Returns the desired size for the panel based on the requested
* and calculated bounds of the scene view.
*/
public Dimension getPreferredSize ()
{
return (_viewmodel == null || isPreferredSizeSet()) ?
super.getPreferredSize() : _viewmodel.bounds.getSize();
}
/** The scene view data model. */
protected IsoSceneViewModel _viewmodel;
/** The scene view we're managing. */
protected SceneView _view;
}
@@ -1,320 +0,0 @@
//
// $Id: SimpleDisplayMisoSceneImpl.java,v 1.4 2003/04/07 22:04:39 mdb Exp $
package com.threerings.miso.client;
import java.util.HashMap;
import com.threerings.media.tile.NoSuchTileException;
import com.threerings.media.tile.NoSuchTileSetException;
import com.threerings.media.tile.ObjectTile;
import com.threerings.media.tile.Tile;
import com.threerings.media.tile.TileSet;
import com.threerings.miso.Log;
import com.threerings.miso.client.util.ObjectSet;
import com.threerings.miso.data.ObjectInfo;
import com.threerings.miso.data.SimpleMisoSceneImpl;
import com.threerings.miso.data.SimpleMisoSceneModel;
import com.threerings.miso.tile.AutoFringer;
import com.threerings.miso.tile.BaseTile;
import com.threerings.miso.tile.MisoTileManager;
/**
* An implementation of the {@link DisplayMisoScene} interface that uses a
* simple miso scene model.
*/
public class SimpleDisplayMisoSceneImpl extends SimpleMisoSceneImpl
implements DisplayMisoScene
{
/**
* Constructs an instance that will be used to display the supplied
* miso scene data. The tiles identified by the scene model will be
* loaded via the supplied tile manager.
*
* @param model the scene data that we'll be displaying.
* @param tmgr the tile manager from which to load our tiles.
*/
public SimpleDisplayMisoSceneImpl (SimpleMisoSceneModel model,
MisoTileManager tmgr)
{
super(model);
_tmgr = tmgr;
_fringer = tmgr.getAutoFringer();
int swid = _model.width;
int shei = _model.height;
// create the individual tile layer objects
_base = new BaseTile[swid*shei];
_fringe = new Tile[swid*shei];
_covered = new boolean[swid*shei];
}
// documentation inherited from interface
public void init ()
{
int swid = _model.width;
int shei = _model.height;
// load up the tiles for our base layer
for (int column = 0; column < shei; column++) {
for (int row = 0; row < swid; row++) {
int fqTileId = getBaseTileId(column, row);
if (fqTileId > 0) {
populateBaseTile(fqTileId, column, row);
}
}
}
// populate our display objects with object tiles
for (int ii = 0; ii < _objects.size(); ii++) {
if (!populateObject((DisplayObjectInfo)_objects.get(ii))) {
// initialization failed, remove the object
_objects.remove(ii--);
}
}
}
// documentation inherited from interface
public void setBaseTile (int fqTileId, int x, int y)
{
super.setBaseTile(fqTileId, x, y);
populateBaseTile(fqTileId, x, y);
// setting a base tile has the side-effect of clearing out the
// surrounding fringe tiles.
for (int xx=Math.max(x - 1, 0),
xn=Math.min(x + 1, _model.width - 1); xx <= xn; xx++) {
for (int yy=Math.max(y - 1, 0),
yn=Math.min(y + 1, _model.height - 1); yy <= yn; yy++) {
_fringe[yy * _model.width + xx] = null;
}
}
}
// documentation inherited from interface
public ObjectInfo addObject (int fqTileId, int x, int y)
{
DisplayObjectInfo info = (DisplayObjectInfo)
super.addObject(fqTileId, x, y);
populateObject(info);
return info;
}
// documentation inherited from interface
public boolean removeObject (ObjectInfo info)
{
if (super.removeObject(info)) {
// clear out this object's "shadow"
DisplayObjectInfo dinfo = (DisplayObjectInfo)info;
setObjectTileFootprint(dinfo.tile, dinfo.x, dinfo.y, false);
return true;
} else {
return false;
}
}
// documentation inherited from interface
public BaseTile getBaseTile (int x, int y)
{
if (x < 0 || y < 0 || x >= _model.width || y >= _model.height) {
return null;
}
return _base[y*_model.width+x];
}
// documentation inherited from interface
public Tile getFringeTile (int x, int y)
{
if (x < 0 || y < 0 || x >= _model.width || y >= _model.height) {
return null;
}
// if we have not yet composed this fringe tile, do so
int idx = y * _model.width + x;
if (_fringe[idx] == null) {
_fringe[idx] = _fringer.getFringeTile(this, x, y, _masks, _rando);
// make a note of non-fringed tiles that have been resolved
// but have no fringe tile
if (_fringe[idx] == null) {
_fringe[idx] = _nullFringe;
}
}
return (_fringe[idx] == _nullFringe) ? null : _fringe[idx];
}
// documentation inherited from interface
public boolean canTraverse (Object trav, int x, int y)
{
BaseTile tile = getBaseTile(x, y);
return (((tile != null) && tile.isPassable()) &&
!_covered[y*_model.width+x]);
}
/**
* Called to populate a coordinate with its base tile.
*/
protected void populateBaseTile (int fqTileId, int x, int y)
{
if (x < 0 || y < 0 || x >= _model.width || y >= _model.height) {
// nothing doing
return;
}
// this is a bit magical, but the tile manager will fetch tiles
// from the tileset repository and the tile set id from which we
// request this tile must map to a base tile as provided by the
// repository, so we just cast it to a base tile and know that all
// is well
String errmsg = null;
try {
_base[y*_model.width+x] = (BaseTile)_tmgr.getTile(fqTileId);
} catch (ClassCastException cce) {
errmsg = "Scene contains non-base tile in base layer";
} catch (NoSuchTileSetException nste) {
errmsg = "Scene contains non-existent tileset";
} catch (NoSuchTileException nste) {
errmsg = "Scene contains non-existent tile";
}
if (errmsg != null) {
Log.warning(errmsg + " [fqtid=" + fqTileId +
", x=" + x + ", y=" + y + "].");
}
}
/**
* Called to populate an object with its object tile.
*/
protected boolean populateObject (DisplayObjectInfo info)
{
int tsid = (info.tileId >> 16) & 0xFFFF, tid = (info.tileId & 0xFFFF);
try {
initObject(info, (ObjectTile)_tmgr.getTile(
tsid, tid, getColorizer(info)));
return true;
} catch (NoSuchTileException nste) {
Log.warning("Scene contains non-existent object tile " +
"[info=" + info + "].");
} catch (NoSuchTileSetException te) {
Log.warning("Scene contains non-existent object tileset " +
"[info=" + info + "].");
}
return false;
}
/**
* Initializes the supplied object with its object tile and configures
* any necessary peripheral scene business that couldn't be configured
* prior to the object having its tile.
*/
protected void initObject (DisplayObjectInfo info, ObjectTile tile)
{
// configure the object info with its object tile
info.setObjectTile(tile);
// generate a "shadow" for this object tile by toggling the
// "covered" flag on in all base tiles below it (to prevent
// sprites from walking on those tiles)
setObjectTileFootprint(info.tile, info.x, info.y, true);
}
/**
* Locates the display object info record for the object tile at the
* specified location. Two of the same kind of object tile cannot
* exist at the same location.
*/
protected DisplayObjectInfo getObjectInfo (ObjectTile tile, int x, int y)
{
int ocount = _objects.size();
for (int ii = 0; ii < ocount; ii++) {
DisplayObjectInfo oinfo = (DisplayObjectInfo)_objects.get(ii);
if (oinfo.tile == tile && oinfo.x == x && oinfo.y == y) {
return oinfo;
}
}
return null;
}
/**
* Sets the "covered" flag on all base tiles that are in the footprint
* of the specified object tile.
*
* @param otile the object tile whose footprint should be set.
* @param x the tile x-coordinate.
* @param y the tile y-coordinate.
* @param covered whether or not the footprint is being covered or
* uncovered.
*/
protected void setObjectTileFootprint (
ObjectTile otile, int x, int y, boolean covered)
{
int endx = Math.max(0, (x - otile.getBaseWidth() + 1));
int endy = Math.max(0, (y - otile.getBaseHeight() + 1));
for (int xx = x; xx >= endx; xx--) {
if ((xx < 0) || (xx >= _model.width)) {
continue;
}
for (int yy = y; yy >= endy; yy--) {
if ((yy < 0) || (yy >= _model.height)) {
continue;
}
_covered[yy*_model.width+xx] = true;
}
}
// Log.info("Set object tile footprint [tile=" + otile + ", sx=" + x +
// ", sy=" + y + ", ex=" + endx + ", ey=" + endy + "].");
}
// documentation inherited
protected ObjectInfo createObjectInfo (int tileId, int x, int y)
{
return new DisplayObjectInfo(tileId, x, y);
}
// documentation inherited
protected ObjectInfo createObjectInfo (ObjectInfo source)
{
return new DisplayObjectInfo(source);
}
/**
* Returns the colorizer for the specified display object. The
* colorizer must provide colorization assignments that will be used
* to recolor the tile image when it is obtained from the tile
* manager. For an object with no colorizations, it is valid to return
* null here.
*/
protected TileSet.Colorizer getColorizer (DisplayObjectInfo oinfo)
{
return null;
}
/** The tile manager from which we load tiles. */
protected MisoTileManager _tmgr;
/** The base layer of tiles. */
protected BaseTile[] _base;
/** The fringe layer of tiles. */
protected Tile[] _fringe;
/** Contains cached fringe mask tiles. TODO: LRU this or something. */
protected HashMap _masks = new HashMap();
/** Used to identify non-existent fringe tiles. */
protected Tile _nullFringe;
/** Information on which tiles are covered by object tiles. */
protected boolean[] _covered;
/** The autofringer. */
protected AutoFringer _fringer;
}
@@ -1,5 +1,5 @@
//
// $Id: TilePath.java,v 1.13 2003/01/31 23:10:45 mdb Exp $
// $Id: TilePath.java,v 1.14 2003/04/17 19:21:16 mdb Exp $
package com.threerings.miso.client;
@@ -14,7 +14,8 @@ import com.threerings.media.util.PathNode;
import com.threerings.media.util.Pathable;
import com.threerings.miso.Log;
import com.threerings.miso.client.util.IsoUtil;
import com.threerings.miso.util.MisoSceneMetrics;
import com.threerings.miso.util.MisoUtil;
/**
* The tile path represents a path of tiles through a scene. The path is
@@ -28,7 +29,8 @@ public class TilePath extends LineSegmentPath
/**
* Constructs a tile path.
*
* @param model the iso scene view model the path is associated with.
* @param metrics the metrics for the scene the with which the path is
* associated.
* @param sprite the sprite to follow the path.
* @param tiles the tiles to be traversed during the path.
* @param destx the destination x-position in screen pixel
@@ -36,10 +38,10 @@ public class TilePath extends LineSegmentPath
* @param desty the destination y-position in screen pixel
* coordinates.
*/
public TilePath (IsoSceneViewModel model, Sprite sprite,
public TilePath (MisoSceneMetrics metrics, Sprite sprite,
List tiles, int destx, int desty)
{
_model = model;
_metrics = metrics;
// set up the path nodes
createPath(sprite, tiles, destx, desty);
@@ -58,7 +60,7 @@ public class TilePath extends LineSegmentPath
// check whether we've arrived at the destination tile
if (!_arrived) {
// get the sprite's latest tile coordinates
IsoUtil.screenToTile(_model, sx, sy, pos);
MisoUtil.screenToTile(_metrics, sx, sy, pos);
// if the sprite has reached the destination tile,
// update the sprite's tile location and remember
@@ -98,12 +100,12 @@ public class TilePath extends LineSegmentPath
{
// constrain destination pixels to fine coordinates
Point fpos = new Point();
IsoUtil.screenToFull(_model, destx, desty, fpos);
MisoUtil.screenToFull(_metrics, destx, desty, fpos);
// add the starting path node
Point ipos = new Point();
int sx = sprite.getX(), sy = sprite.getY();
IsoUtil.screenToTile(_model, sx, sy, ipos);
MisoUtil.screenToTile(_metrics, sx, sy, ipos);
addNode(ipos.x, ipos.y, sx, sy, NORTH);
// TODO: make more visually appealing path segments from start
@@ -117,15 +119,15 @@ public class TilePath extends LineSegmentPath
Point next = (Point)tiles.get(ii);
// determine the direction from previous to next node
int dir = IsoUtil.getIsoDirection(prev.x, prev.y, next.x, next.y);
int dir = MisoUtil.getIsoDirection(prev.x, prev.y, next.x, next.y);
// determine the node's position in screen pixel coordinates
IsoUtil.tileToScreen(_model, next.x, next.y, spos);
MisoUtil.tileToScreen(_metrics, next.x, next.y, spos);
// add the node to the path, wandering through the middle
// of each tile in the path for now
int dsx = spos.x + _model.tilehwid;
int dsy = spos.y + _model.tilehhei;
int dsx = spos.x + _metrics.tilehwid;
int dsy = spos.y + _metrics.tilehhei;
addNode(next.x, next.y, dsx, dsy, dir);
prev = next;
@@ -133,22 +135,22 @@ public class TilePath extends LineSegmentPath
// get the final destination point's screen coordinates
// constrained to the closest full coordinate
IsoUtil.fullToScreen(_model, fpos.x, fpos.y, spos);
MisoUtil.fullToScreen(_metrics, fpos.x, fpos.y, spos);
// get the tile coordinates for the final destination tile
int tdestx = IsoUtil.fullToTile(fpos.x);
int tdesty = IsoUtil.fullToTile(fpos.y);
int tdestx = MisoUtil.fullToTile(fpos.x);
int tdesty = MisoUtil.fullToTile(fpos.y);
// get the facing direction for the final node
int dir;
if (prev.x == ipos.x && prev.y == ipos.y) {
// if destination is within starting tile, direction is
// determined by studying the fine coordinates
dir = IsoUtil.getDirection(_model, sx, sy, spos.x, spos.y);
dir = MisoUtil.getDirection(_metrics, sx, sy, spos.x, spos.y);
} else {
// else it's based on the last tile we traversed
dir = IsoUtil.getIsoDirection(prev.x, prev.y, tdestx, tdesty);
dir = MisoUtil.getIsoDirection(prev.x, prev.y, tdestx, tdesty);
}
// add the final destination path node
@@ -176,6 +178,6 @@ public class TilePath extends LineSegmentPath
/** The destination tile path node. */
protected TilePathNode _dest;
/** The iso scene view model. */
protected IsoSceneViewModel _model;
/** The scene metrics. */
protected MisoSceneMetrics _metrics;
}
@@ -1,87 +0,0 @@
//
// $Id: VirtualDisplayMisoSceneImpl.java,v 1.2 2003/04/01 02:17:58 mdb Exp $
package com.threerings.miso.client;
import java.awt.Rectangle;
import com.threerings.media.tile.Tile;
import com.threerings.miso.client.util.ObjectSet;
import com.threerings.miso.data.MisoSceneModel;
import com.threerings.miso.data.ObjectInfo;
import com.threerings.miso.tile.BaseTile;
/**
* Provides a useful base class for "virtual" {@link DisplayMisoScene}
* implementations. These return tiles based on some algorithm rather than
* a repository of predefined tile data.
*/
public class VirtualDisplayMisoSceneImpl
implements DisplayMisoScene
{
// documentation inherited from interface
public void init ()
{
}
// documentation inherited from interface
public int getBaseTileId (int x, int y)
{
return -1;
}
// documentation inherited from interface
public void getObjects (Rectangle region, ObjectSet set)
{
// nothing doing
}
// documentation inherited from interface
public void setBaseTile (int fqTileId, int x, int y)
{
// nothing doing
}
// documentation inherited from interface
public void setBaseTiles (Rectangle r, int setId, int setSize)
{
// nothing doing
}
// documentation inherited from interface
public ObjectInfo addObject (int fqTileId, int x, int y)
{
return null;
}
// documentation inherited from interface
public boolean removeObject (ObjectInfo info)
{
return false;
}
// documentation inherited from interface
public MisoSceneModel getSceneModel ()
{
return null;
}
// documentation inherited from interface
public BaseTile getBaseTile (int x, int y)
{
return null;
}
// documentation inherited from interface
public Tile getFringeTile (int x, int y)
{
return null;
}
// documentation inherited from interface
public boolean canTraverse (Object traverser, int x, int y)
{
return true;
}
}
@@ -1,345 +0,0 @@
//
// $Id: AStarPathUtil.java,v 1.27 2003/04/12 02:14:52 mdb Exp $
package com.threerings.miso.client.util;
import java.awt.Point;
import java.util.*;
import com.samskivert.util.HashIntMap;
import com.threerings.media.util.MathUtil;
import com.threerings.miso.Log;
import com.threerings.miso.tile.BaseTile;
/**
* 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);
}
/**
* 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 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.
*
* @return the list of points in the path.
*/
public static List getPath (TraversalPred tpred, Object trav,
int longest, int ax, int ay, int bx, int by)
{
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);
// 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);
}
// consider each successor of the node
considerStep(info, n, n.x - 1, n.y - 1, DIAGONAL_COST);
considerStep(info, n, n.x, n.y - 1, ADJACENT_COST);
considerStep(info, n, n.x + 1, n.y - 1, DIAGONAL_COST);
considerStep(info, n, n.x - 1, n.y, ADJACENT_COST);
considerStep(info, n, n.x + 1, n.y, ADJACENT_COST);
considerStep(info, n, n.x - 1, n.y + 1, DIAGONAL_COST);
considerStep(info, n, n.x, n.y + 1, ADJACENT_COST);
considerStep(info, n, n.x + 1, n.y + 1, DIAGONAL_COST);
// push the node on the closed list
info.closed.add(n);
}
// no path found
return null;
}
/**
* 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);
}
/**
* 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 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);
}
@@ -1,591 +0,0 @@
//
// $Id: IsoUtil.java,v 1.45 2003/02/12 05:36:18 mdb Exp $
package com.threerings.miso.client.util;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import com.samskivert.swing.SmartPolygon;
import com.threerings.media.sprite.Sprite;
import com.threerings.media.util.MathUtil;
import com.threerings.util.DirectionCodes;
import com.threerings.util.DirectionUtil;
import com.threerings.miso.Log;
import com.threerings.miso.client.IsoSceneViewModel;
import com.threerings.miso.client.DisplayObjectInfo;
/**
* The <code>IsoUtil</code> class is a holding place for miscellaneous
* isometric-display-related utility routines.
*/
public class IsoUtil
implements DirectionCodes
{
/**
* Returns a polygon bounding all footprint tiles of the given
* object tile.
*
* @param model the scene view model.
* @param tx the x tile-coordinate of the object tile.
* @param ty the y tile-coordinate of the object tile.
* @param tile the object tile.
*
* @return the bounding polygon.
*/
public static Polygon getObjectFootprint (
IsoSceneViewModel model, DisplayObjectInfo scobj)
{
Polygon boundsPoly = new SmartPolygon();
Point tpos = tileToScreen(model, scobj.x, scobj.y, new Point());
int bwid = scobj.tile.getBaseWidth(), bhei = scobj.tile.getBaseHeight();
int oox = tpos.x + model.tilehwid, ooy = tpos.y + model.tilehei;
int rx = oox, ry = ooy;
// bottom-center point
boundsPoly.addPoint(rx, ry);
// left point
rx -= bwid * model.tilehwid;
ry -= bwid * model.tilehhei;
boundsPoly.addPoint(rx, ry);
// top-center point
rx += bhei * model.tilehwid;
ry -= bhei * model.tilehhei;
boundsPoly.addPoint(rx, ry);
// right point
rx += bwid * model.tilehwid;
ry += bwid * model.tilehhei;
boundsPoly.addPoint(rx, ry);
// bottom-center point
boundsPoly.addPoint(rx, ry);
return boundsPoly;
}
/**
* Returns a rectangle that encloses the entire object image, with the
* upper left set to the appropriate values for rendering the object
* image.
*
* @param model the scene view model.
* @param tx the x tile-coordinate of the object tile.
* @param ty the y tile-coordinate of the object tile.
* @param tile the object tile.
*
* @return the bounding rectangle.
*/
public static Rectangle getObjectBounds (
IsoSceneViewModel model, DisplayObjectInfo scobj)
{
Point tpos = tileToScreen(model, scobj.x, scobj.y, new Point());
// if the tile has an origin, use that, otherwise compute the
// origin based on the tile footprint
int tox = scobj.tile.getOriginX(), toy = scobj.tile.getOriginY();
if (tox == Integer.MIN_VALUE) {
tox = scobj.tile.getBaseWidth() * model.tilehwid;
}
if (toy == Integer.MIN_VALUE) {
toy = scobj.tile.getHeight();
}
int oox = tpos.x + model.tilehwid, ooy = tpos.y + model.tilehei;
int sx = oox - tox, sy = ooy - toy;
return new Rectangle(
sx, sy, scobj.tile.getWidth(), scobj.tile.getHeight());
}
/**
* Returns true if the footprints of the two object tiles overlap when
* the objects occupy the specified coordinates, false if not.
*/
public static boolean objectFootprintsOverlap (
DisplayObjectInfo so1, DisplayObjectInfo so2)
{
return (so2.x > so1.x - so1.tile.getBaseWidth() &&
so1.x > so2.x - so2.tile.getBaseWidth() &&
so2.y > so1.y - so1.tile.getBaseHeight() &&
so1.y > so2.y - so2.tile.getBaseHeight());
}
/**
* 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 (
IsoSceneViewModel model, 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(model, ax, ay, afpos);
screenToFull(model, bx, by, bfpos);
// pull out the tile coordinates for each point
int tax = afpos.x / FULL_TILE_FACTOR;
int tay = afpos.y / FULL_TILE_FACTOR;
int tbx = bfpos.x / FULL_TILE_FACTOR;
int tby = bfpos.y / FULL_TILE_FACTOR;
// 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 (val / FULL_TILE_FACTOR);
}
/**
* Returns the fine coordinate of the given full coordinate.
*/
public static int fullToFine (int val)
{
return (val - ((val / FULL_TILE_FACTOR) * 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 (
IsoSceneViewModel model, int sx, int sy, Point tpos)
{
// determine the upper-left of the quadrant that contains our
// point
int zx = (int)Math.floor((float)(sx - model.origin.x) / model.tilewid);
int zy = (int)Math.floor((float)(sy - model.origin.y) / model.tilehei);
// these are the screen coordinates of the tile's top
int ox = (zx * model.tilewid + model.origin.x),
oy = (zy * model.tilehei + model.origin.y);
// 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(model.slopeY * dx + model.tilehei) <= dy) {
tpos.x += 1;
}
if (Math.round(model.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. 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 (
IsoSceneViewModel model, int x, int y, Point spos)
{
spos.x = model.origin.x + ((x - y - 1) * model.tilehwid);
spos.y = model.origin.y + ((x + y) * model.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 (
IsoSceneViewModel model, int x, int y, Point ppos)
{
ppos.x = model.tilehwid + ((x - y) * model.finehwid);
ppos.y = (x + y) * model.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 (
IsoSceneViewModel model, 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 - (model.fineSlopeY * x);
// determine intersection of x- and y-axis lines
int crossx = (int)((bY - model.fineBX) /
(model.fineSlopeX - model.fineSlopeY));
int crossy = (int)((model.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(model.tilehwid, 0, crossx, crossy);
fpos.x = (int)(xdist / model.finelen);
// determine distance along the y-axis
float ydist = MathUtil.distance(x, y, crossx, crossy);
fpos.y = (int)(ydist / model.finelen);
}
/**
* 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 (
IsoSceneViewModel model, int sx, int sy, Point fpos)
{
// get the tile coordinates
Point tpos = new Point();
screenToTile(model, sx, sy, tpos);
// get the screen coordinates for the containing tile
Point spos = tileToScreen(model, tpos.x, tpos.y, new Point());
// get the fine coordinates within the containing tile
pixelToFine(model, 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 (
IsoSceneViewModel model, int x, int y, Point spos)
{
// get the tile screen position
int tx = x / FULL_TILE_FACTOR, ty = y / FULL_TILE_FACTOR;
Point tspos = tileToScreen(model, 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(model, 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 (IsoSceneViewModel model, int fine)
{
return toFull(fine / model.finegran, fine % model.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 (
IsoSceneViewModel model, int x, int y)
{
// get the top-left screen coordinate for the tile
Point spos = IsoUtil.tileToScreen(model, x, y, new Point());
// create a polygon framing the tile
Polygon poly = new SmartPolygon();
poly.addPoint(spos.x, spos.y + model.tilehhei);
poly.addPoint(spos.x + model.tilehwid, spos.y);
poly.addPoint(spos.x + model.tilewid, spos.y + model.tilehhei);
poly.addPoint(spos.x + model.tilehwid, spos.y + model.tilehei);
return poly;
}
/**
* Return a screen-coordinates polygon framing the two specified
* tile-coordinate points.
*/
public static Polygon getMultiTilePolygon (IsoSceneViewModel model,
Point sp1, Point sp2)
{
int minx, maxx, miny, maxy;
Point[] p = new Point[4];
// load in all possible screen coords
p[0] = IsoUtil.tileToScreen(model, sp1.x, sp1.y, new Point());
p[1] = IsoUtil.tileToScreen(model, sp2.x, sp2.y, new Point());
p[2] = IsoUtil.tileToScreen(model, sp1.x, sp2.y, new Point());
p[3] = IsoUtil.tileToScreen(model, sp2.x, sp1.y, new Point());
// locate the indexes of min/max for x and y
minx = maxx = miny = maxy = 0;
for (int ii=1; ii < 4; ii++) {
if (p[ii].x < p[minx].x) {
minx = ii;
} else if (p[ii].x > p[maxx].x) {
maxx = ii;
}
if (p[ii].y < p[miny].y) {
miny = ii;
} else if (p[ii].y > p[maxy].y) {
maxy = ii;
}
}
// now make the polygon! Whoo!
Polygon poly = new SmartPolygon();
poly.addPoint(p[minx].x, p[minx].y + model.tilehhei);
poly.addPoint(p[miny].x + model.tilehwid, p[miny].y);
poly.addPoint(p[maxx].x + model.tilewid, p[maxx].y + model.tilehhei);
poly.addPoint(p[maxy].x + model.tilehwid, p[maxy].y + model.tilehei);
return poly;
}
/**
* Adds the supplied fine coordinates to the supplied tile coordinates
* to compute full coordinates.
*
* @retun the point object supplied as <code>full</code>.
*/
public static Point tilePlusFineToFull (IsoSceneViewModel model,
int tileX, int tileY,
int fineX, int fineY,
Point full)
{
int dtx = fineX / model.finegran;
int dty = fineY / model.finegran;
int fx = fineX - dtx * model.finegran;
if (fx < 0) {
dtx--;
fx += model.finegran;
}
int fy = fineY - dty * model.finegran;
if (fy < 0) {
dty--;
fy += model.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;
}
@@ -1,153 +0,0 @@
//
// $Id: ObjectSet.java,v 1.4 2003/02/12 05:36:44 mdb Exp $
package com.threerings.miso.client.util;
import java.util.Arrays;
import java.util.Comparator;
import com.samskivert.util.ArrayUtil;
import com.samskivert.util.ListUtil;
import com.threerings.miso.Log;
import com.threerings.miso.data.ObjectInfo;
/**
* Used to store an (arbitrarily) ordered, low-impact iteratable (doesn't
* require object creation), set of {@link ObjectInfo} instances.
*/
public class ObjectSet
{
/**
* Inserts the supplied object into the set.
*
* @return true if it was inserted, false if the object was already in
* the set.
*/
public boolean insert (ObjectInfo info)
{
// bail if it's already in the set
int ipos = indexOf(info);
if (ipos >= 0) {
// log a warning because the caller shouldn't be doing this
Log.warning("Requested to add a display object to a set that " +
"already contains such an object [ninfo=" + info +
", oinfo=" + _objs[ipos] + "].");
return false;
}
// otherwise insert it
ipos = -(ipos+1);
_objs = ListUtil.insert(_objs, ipos, info);
_size++;
return true;
}
/**
* Returns true if the specified object is in the set, false if it is
* not.
*/
public boolean contains (ObjectInfo info)
{
return (indexOf(info) >= 0);
}
/**
* Returns the number of objects in this set.
*/
public int size ()
{
return _size;
}
/**
* Returns the object with the specified index. The index must & be
* between <code>0</code> and {@link #size}<code>-1</code>.
*/
public ObjectInfo get (int index)
{
return (ObjectInfo)_objs[index];
}
/**
* Removes the object at the specified index.
*/
public void remove (int index)
{
ListUtil.remove(_objs, index);
_size--;
}
/**
* Removes the specified object from the set.
*
* @return true if it was removed, false if it was not in the set.
*/
public boolean remove (ObjectInfo info)
{
int opos = indexOf(info);
if (opos >= 0) {
remove(opos);
return true;
} else {
return false;
}
}
/**
* Clears out the contents of this set.
*/
public void clear ()
{
_size = 0;
Arrays.fill(_objs, null);
}
/**
* Returns a string representation of this instance.
*/
public String toString ()
{
StringBuffer buf = new StringBuffer("[");
for (int ii = 0; ii < _size; ii++) {
if (ii > 0) {
buf.append(", ");
}
buf.append(_objs[ii]);
}
return buf.append("]").toString();
}
/**
* Returns the index of the object or it's insertion index if it is
* not in the set.
*/
protected final int indexOf (ObjectInfo info)
{
return ArrayUtil.binarySearch(_objs, 0, _size, info, INFO_COMP);
}
/** Our sorted array of objects. */
protected Object[] _objs = new Object[DEFAULT_SIZE];
/** The number of objects in the set. */
protected int _size;
/** We simply sort the objects in order of their hash code. We don't
* care about their order, it exists only to support binary search. */
protected static final Comparator INFO_COMP = new Comparator() {
public int compare (Object o1, Object o2) {
ObjectInfo do1 = (ObjectInfo)o1;
ObjectInfo do2 = (ObjectInfo)o2;
if (do1.tileId == do2.tileId) {
return ((do1.x << 16) + do1.y) - ((do2.x << 16) + do2.y);
} else {
return do1.tileId - do2.tileId;
}
}
};
/** We start big because we know these will in general contain at
* least in the tens of objects. */
protected static final int DEFAULT_SIZE = 16;
}