Initial work on support for objects whose images span multiple tiles.

Made TileSet an interface.  Throw exceptions for unknown tile or tile
set requests.  General clean-up and documentation.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@427 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Walter Korman
2001-10-11 00:41:27 +00:00
parent 5c79f8f082
commit ad7b64d4a1
30 changed files with 811 additions and 521 deletions
@@ -0,0 +1,24 @@
//
// $Id: NoSuchTileException.java,v 1.1 2001/10/11 00:41:26 shaper Exp $
package com.threerings.media.tile;
/**
* Thrown when an attempt is made to retrieve a non-existent tile from the
* tile manager.
*/
public class NoSuchTileException extends TileException
{
public NoSuchTileException (int tid)
{
super("No such tile [tid=" + tid + "]");
_tid = tid;
}
public int getTileId ()
{
return _tid;
}
protected int _tid;
}
@@ -0,0 +1,24 @@
//
// $Id: NoSuchTileSetException.java,v 1.1 2001/10/11 00:41:26 shaper Exp $
package com.threerings.media.tile;
/**
* Thrown when an attempt is made to retrieve a non-existent tile set
* from the tile set manager.
*/
public class NoSuchTileSetException extends TileException
{
public NoSuchTileSetException (int tsid)
{
super("No such tile set [tsid=" + tsid + "]");
_tsid = tsid;
}
public int getTileSetId ()
{
return _tsid;
}
protected int _tsid;
}
@@ -0,0 +1,40 @@
//
// $Id: ObjectTile.java,v 1.1 2001/10/11 00:41:26 shaper Exp $
package com.threerings.media.tile;
import java.awt.Graphics2D;
import java.awt.Shape;
/**
* An object tile extends the base tile to provide support for objects
* whose image spans more than one tile. The object has dimensions
* that represent its footprint or "shadow", which the scene
* containing the tile can reference to do things like make the
* footprint tiles impassable.
*/
public class ObjectTile extends Tile
{
/** The object footprint dimensions in unit tile units. */
public int baseWidth, baseHeight;
/**
* Constructs a new object tile.
*/
public ObjectTile (int tsid, int tid, int baseWidth, int baseHeight)
{
super(tsid, tid);
this.baseWidth = baseWidth;
this.baseHeight = baseHeight;
}
// documentation inherited
public void paint (Graphics2D gfx, Shape dest)
{
super.paint(gfx, dest);
// TODO: draw the object image offset to account for its
// actual dimensions
}
}
+12 -2
View File
@@ -1,9 +1,9 @@
//
// $Id: Tile.java,v 1.13 2001/10/08 21:04:25 shaper Exp $
// $Id: Tile.java,v 1.14 2001/10/11 00:41:26 shaper Exp $
package com.threerings.media.tile;
import java.awt.Image;
import java.awt.*;
/**
* A tile represents a single square in a single layer in a scene.
@@ -48,6 +48,16 @@ public class Tile
return ((int)tsid << 16) | tid;
}
/**
* Render the tile into the given rectangle in the given graphics
* context.
*/
public void paint (Graphics2D gfx, Shape dest)
{
Rectangle bounds = dest.getBounds();
gfx.drawImage(img, bounds.x, bounds.y, null);
}
/**
* Return a string representation of the tile information.
*/
@@ -0,0 +1,15 @@
//
// $Id: TileException.java,v 1.1 2001/10/11 00:41:26 shaper Exp $
package com.threerings.media.tile;
/**
* Thrown when an error occurs while working with tiles.
*/
public class TileException extends Exception
{
public TileException (String message)
{
super(message);
}
}
@@ -1,5 +1,5 @@
//
// $Id: TileManager.java,v 1.17 2001/09/28 00:44:31 shaper Exp $
// $Id: TileManager.java,v 1.18 2001/10/11 00:41:26 shaper Exp $
package com.threerings.media.tile;
@@ -39,6 +39,7 @@ public class TileManager
* @return the tile object, or null if an error occurred.
*/
public Tile getTile (int tsid, int tid)
throws NoSuchTileSetException, NoSuchTileException
{
// the fully unique tile id is the conjoined tile set and tile id
int utid = (tsid << 16) | tid;
+7 -188
View File
@@ -1,14 +1,8 @@
//
// $Id: TileSet.java,v 1.16 2001/10/08 21:04:25 shaper Exp $
// $Id: TileSet.java,v 1.17 2001/10/11 00:41:26 shaper Exp $
package com.threerings.media.tile;
import java.awt.Image;
import java.awt.Point;
import java.awt.image.*;
import com.samskivert.util.StringUtil;
import com.threerings.media.Log;
import com.threerings.media.ImageManager;
/**
@@ -21,90 +15,25 @@ import com.threerings.media.ImageManager;
* the image is tile id 0 and tiles are numbered left to right, top to
* bottom, in ascending order.
*/
public class TileSet
public interface TileSet
{
/**
* Construct a new <code>TileSet</code> object.
*/
public TileSet ()
{
_model = new TileSetModel();
}
/**
* Return the tileset identifier.
*/
public int getId ()
{
return _model.tsid;
}
public int getId ();
/**
* Return the tileset name.
*/
public String getName ()
{
return _model.name;
}
public String getName ();
/**
* Return the number of tiles in the tileset.
*/
public int getNumTiles ()
{
return _model.numTiles;
}
public int getNumTiles ();
/**
* Return the image corresponding to the specified tile id within
* this tile set.
*
* @param imgmgr the image manager.
* @param tid the tile id.
*
* @return the tile image.
*/
protected Image getTileImage (ImageManager imgmgr, int tid)
{
// load the full tile image if we don't already have it
if (_imgTiles == null) {
if ((_imgTiles = imgmgr.getImage(_model.imgFile)) == null) {
Log.warning("Failed to retrieve full tileset image " +
"[file=" + _model.imgFile + "].");
return null;
}
}
// find the row number containing the sought-after tile
int ridx, tcount, ty, tx;
ridx = tcount = 0;
// start tile image position at image start offset
tx = _model.offsetPos.x;
ty = _model.offsetPos.y;
while ((tcount += _model.tileCount[ridx]) < tid + 1) {
// increment tile image position by row height and gap distance
ty += (_model.rowHeight[ridx++] + _model.gapDist.y);
}
// determine the horizontal index of this tile in the row
int xidx = tid - (tcount - _model.tileCount[ridx]);
// final image x-position is based on tile width and gap distance
tx += (xidx * (_model.rowWidth[ridx] + _model.gapDist.x));
// Log.info("Retrieving tile image [tid=" + tid + ", ridx=" +
// ridx + ", xidx=" + xidx + ", tx=" + tx +
// ", ty=" + ty + "].");
// crop the tile-sized image chunk from the full image
return imgmgr.getImageCropped(
_imgTiles, tx, ty, _model.rowWidth[ridx], _model.rowHeight[ridx]);
}
/**
* Return the {@link Tile} object from this tileset corresponding
* Returns the {@link Tile} object from this tileset corresponding
* to the specified tile id, or <code>null</code> if no such tile
* id exists. The tile image is retrieved from the given image
* manager.
@@ -115,115 +44,5 @@ public class TileSet
* @return the tile object, or null if no such tile exists.
*/
public Tile getTile (ImageManager imgmgr, int tid)
{
// bail if there's no such tile
if (tid > (_model.numTiles - 1)) {
return null;
}
// create the tile object
Tile tile = createTile(tid);
// retrieve the tile image
tile.img = getTileImage(imgmgr, tid);
if (tile.img == null) {
Log.warning("Null tile image " +
"[tsid=" + _model.tsid + ", tid=" + tid + "].");
}
// populate the tile's dimensions
BufferedImage bimg = (BufferedImage)tile.img;
tile.height = (short)bimg.getHeight();
tile.width = (short)bimg.getWidth();
return tile;
}
/**
* Returns the tile set model.
*/
public TileSetModel getModel ()
{
return _model;
}
/**
* Return a string representation of the tileset information.
*/
public String toString ()
{
return _model.toString();
}
/**
* Construct and return a new tile object for further population
* with tile-specific information.
*/
protected Tile createTile (int tid)
{
return new Tile(_model.tsid, tid);
}
/** The image containing all tile images for this set. */
protected Image _imgTiles;
/** The tile set data model. */
protected TileSetModel _model;
/**
* The model that details the attributes of each tile in a
* tileset. Storing the data separately from the tile set object
* itself allows for the wealth of information associated with a
* tile set to be more cleanly gathered and passed on to the tile
* set constructor by those that need to do so.
*/
public static class TileSetModel
{
/** The tileset name. */
public String name;
/** The tileset unique identifier. */
public int tsid;
/** The file containing the tile images. */
public String imgFile;
/** The width of the tiles in each row in pixels. */
public int[] rowWidth;
/** The height of the tiles in each row in pixels. */
public int[] rowHeight;
/** The number of tiles in each row. */
public int[] tileCount;
/** The number of tiles in the tileset. */
public int numTiles;
/**
* The offset distance (x, y) in pixels from the top-left of the
* image to the start of the first tile image.
*/
public Point offsetPos = new Point();
/**
* The distance (x, y) in pixels between each tile in each row
* horizontally, and between each row of tiles vertically.
*/
public Point gapDist = new Point();
public TileSetModel ()
{
}
public String toString ()
{
StringBuffer buf = new StringBuffer();
buf.append("[name=").append(name);
buf.append(", file=").append(imgFile);
buf.append(", tsid=").append(tsid);
buf.append(", numtiles=").append(numTiles);
return buf.append("]").toString();
}
}
throws NoSuchTileException;
}
@@ -0,0 +1,197 @@
//
// $Id: TileSetImpl.java,v 1.1 2001/10/11 00:41:26 shaper Exp $
package com.threerings.media.tile;
import java.awt.Image;
import java.awt.Point;
import java.awt.image.*;
import com.samskivert.util.HashIntMap;
import com.samskivert.util.StringUtil;
import com.threerings.media.Log;
import com.threerings.media.ImageManager;
// documentation inherited
public class TileSetImpl implements TileSet
{
/** The tileset name. */
public String name;
/** The tileset unique identifier. */
public int tsid;
/** The file containing the tile images. */
public String imgFile;
/** The width of the tiles in each row in pixels. */
public int[] rowWidth;
/** The height of the tiles in each row in pixels. */
public int[] rowHeight;
/** The number of tiles in each row. */
public int[] tileCount;
/** The number of tiles in the tileset. */
public int numTiles;
/** Mapping of object tile ids to object dimensions. */
public HashIntMap objects = new HashIntMap();
/**
* The offset distance (x, y) in pixels from the top-left of the
* image to the start of the first tile image.
*/
public Point offsetPos = new Point();
/**
* The distance (x, y) in pixels between each tile in each row
* horizontally, and between each row of tiles vertically.
*/
public Point gapDist = new Point();
// documentation inherited
public int getId ()
{
return tsid;
}
// documentation inherited
public String getName ()
{
return name;
}
// documentation inherited
public int getNumTiles ()
{
return numTiles;
}
// documentation inherited
public Tile getTile (ImageManager imgmgr, int tid)
throws NoSuchTileException
{
// bail if there's no such tile
if (tid < 0 || tid > (numTiles - 1)) {
throw new NoSuchTileException(tid);
}
// create and populate the tile object
Tile tile = createTile(tid);
// retrieve the tile image
tile.img = getTileImage(imgmgr, tile.tid);
if (tile.img == null) {
Log.warning("Null tile image [tile=" + tile + "].");
}
// populate the tile's dimensions
BufferedImage bimg = (BufferedImage)tile.img;
tile.height = (short)bimg.getHeight();
tile.width = (short)bimg.getWidth();
// allow sub-classes to fill in their tile information
populateTile(tile);
return tile;
}
/**
* Return a string representation of the tileset information.
*/
public String toString ()
{
StringBuffer buf = new StringBuffer();
buf.append("[name=").append(name);
buf.append(", file=").append(imgFile);
buf.append(", tsid=").append(tsid);
buf.append(", numtiles=").append(numTiles);
return buf.append("]").toString();
}
/**
* Construct and return a new tile object for further population
* with tile-specific information. Derived classes can override
* this method to create their own sub-class of <code>Tile</code>.
*
* @param tid the tile id for the new tile.
*
* @return the new tile object.
*/
protected Tile createTile (int tid)
{
int size[] = (int[])objects.get(tid);
if (size != null) {
return new ObjectTile(tsid, tid, size[0], size[1]);
}
return new Tile(tsid, tid);
}
/**
* Populates the given tile object with its detailed tile
* information. Derived classes can override this method to add
* in their own tile information, but should be sure to call
* <code>super.populateTile()</code>.
*
* @param tile the tile to populate.
*/
protected void populateTile (Tile tile)
{
// nothing for now
}
/**
* Returns the image corresponding to the specified tile id within
* this tile set.
*
* @param imgmgr the image manager.
* @param tid the tile id.
*
* @return the tile image.
*/
protected Image getTileImage (ImageManager imgmgr, int tid)
{
// load the full tile image if we don't already have it
if (_imgTiles == null) {
if ((_imgTiles = imgmgr.getImage(imgFile)) == null) {
Log.warning("Failed to retrieve full tileset image " +
"[file=" + imgFile + "].");
return null;
}
}
// find the row number containing the sought-after tile
int ridx, tcount, ty, tx;
ridx = tcount = 0;
// start tile image position at image start offset
tx = offsetPos.x;
ty = offsetPos.y;
while ((tcount += tileCount[ridx]) < tid + 1) {
// increment tile image position by row height and gap distance
ty += (rowHeight[ridx++] + gapDist.y);
}
// determine the horizontal index of this tile in the row
int xidx = tid - (tcount - tileCount[ridx]);
// final image x-position is based on tile width and gap distance
tx += (xidx * (rowWidth[ridx] + gapDist.x));
// Log.info("Retrieving tile image [tid=" + tid + ", ridx=" +
// ridx + ", xidx=" + xidx + ", tx=" + tx +
// ", ty=" + ty + "].");
// crop the tile-sized image chunk from the full image
return imgmgr.getImageCropped(
_imgTiles, tx, ty, rowWidth[ridx], rowHeight[ridx]);
}
/** The image containing all tile images for this set. */
protected Image _imgTiles;
}
@@ -1,5 +1,5 @@
//
// $Id: TileSetManager.java,v 1.12 2001/08/16 23:14:20 mdb Exp $
// $Id: TileSetManager.java,v 1.13 2001/10/11 00:41:26 shaper Exp $
package com.threerings.media.tile;
@@ -26,7 +26,8 @@ public interface TileSetManager
* @param tsid the tileset identifier.
* @return the number of tiles.
*/
public int getNumTilesInSet (int tsid);
public int getNumTilesInSet (int tsid)
throws NoSuchTileSetException;
/**
* Return an <code>ArrayList</code> containing all
@@ -43,7 +44,8 @@ public interface TileSetManager
* @param tsid the tileset identifier.
* @return the tileset object.
*/
public TileSet getTileSet (int tsid);
public TileSet getTileSet (int tsid)
throws NoSuchTileSetException;
/**
* Return the tile object corresponding to the specified tileset
@@ -54,7 +56,8 @@ public interface TileSetManager
*
* @return the tile object.
*/
public Tile getTile (int tsid, int tid);
public Tile getTile (int tsid, int tid)
throws NoSuchTileSetException, NoSuchTileException;
/**
* Return the total number of tilesets available for use.
@@ -1,5 +1,5 @@
//
// $Id: TileSetManagerImpl.java,v 1.12 2001/09/17 05:18:21 mdb Exp $
// $Id: TileSetManagerImpl.java,v 1.13 2001/10/11 00:41:26 shaper Exp $
package com.threerings.media.tile;
@@ -28,20 +28,26 @@ public abstract class TileSetManagerImpl implements TileSetManager
}
public int getNumTilesInSet (int tsid)
throws NoSuchTileSetException
{
TileSet tset = getTileSet(tsid);
return (tset != null) ? tset.getNumTiles() : -1;
return getTileSet(tsid).getNumTiles();
}
public TileSet getTileSet (int tsid)
throws NoSuchTileSetException
{
return (TileSet)_tilesets.get(tsid);
TileSet tset = (TileSet)_tilesets.get(tsid);
if (tset == null) {
throw new NoSuchTileSetException(tsid);
}
return tset;
}
public Tile getTile (int tsid, int tid)
throws NoSuchTileSetException, NoSuchTileException
{
TileSet tset = getTileSet(tsid);
return (tset == null) ? null : tset.getTile(_imgmgr, tid);
return getTileSet(tsid).getTile(_imgmgr, tid);
}
public ArrayList getAllTileSets ()
@@ -0,0 +1,46 @@
//
// $Id: TileUtil.java,v 1.1 2001/10/11 00:41:26 shaper Exp $
package com.threerings.media.tile;
import java.awt.Image;
import com.threerings.media.Log;
/**
* Miscellaneous utility routines for working with tiles.
*/
public class TileUtil
{
/**
* Returns the image associated with the given tile from the given
* tile manager, or null if an error occurred. Any exceptions
* that occur are logged.
*/
public static Image getTileImage (TileManager tilemgr, int tsid, int tid)
{
try {
Tile tile = tilemgr.getTile(tsid, tid);
return tile.img;
} catch (TileException te) {
Log.warning("Exception retrieving tile image [te=" + te + "].");
return null;
}
}
/**
* Returns the given tile from the given tile manager, or null if
* an error occurred. Any exceptions that occur are logged.
*/
public static Tile getTile (TileManager tilemgr, int tsid, int tid)
{
try {
return tilemgr.getTile(tsid, tid);
} catch (TileException te) {
Log.warning("Exception retrieving tile [te=" + te + "].");
return null;
}
}
}
@@ -1,5 +1,5 @@
//
// $Id: XMLTileSetParser.java,v 1.15 2001/10/08 21:04:25 shaper Exp $
// $Id: XMLTileSetParser.java,v 1.16 2001/10/11 00:41:26 shaper Exp $
package com.threerings.media.tile;
@@ -35,27 +35,27 @@ public class XMLTileSetParser extends DefaultHandler
protected void finishElement (String qName, String str)
{
if (qName.equals("imagefile")) {
_model.imgFile = str;
_tset.imgFile = str;
} else if (qName.equals("rowwidth")) {
_model.rowWidth = StringUtil.parseIntArray(str);
_tset.rowWidth = StringUtil.parseIntArray(str);
} else if (qName.equals("rowheight")) {
_model.rowHeight = StringUtil.parseIntArray(str);
_tset.rowHeight = StringUtil.parseIntArray(str);
} else if (qName.equals("tilecount")) {
_model.tileCount = StringUtil.parseIntArray(str);
_tset.tileCount = StringUtil.parseIntArray(str);
// calculate the total number of tiles in the tileset
for (int ii = 0; ii < _model.tileCount.length; ii++) {
_model.numTiles += _model.tileCount[ii];
for (int ii = 0; ii < _tset.tileCount.length; ii++) {
_tset.numTiles += _tset.tileCount[ii];
}
} else if (qName.equals("offsetpos")) {
getPoint(str, _model.offsetPos);
getPoint(str, _tset.offsetPos);
} else if (qName.equals("gapdist")) {
getPoint(str, _model.gapDist);
getPoint(str, _tset.gapDist);
} else if (qName.equals("tileset")) {
// construct the tileset on tag close and add it to the
@@ -67,19 +67,31 @@ public class XMLTileSetParser extends DefaultHandler
}
}
// documentation inherited
public void startElement (String uri, String localName,
String qName, Attributes attributes)
{
_tag = qName;
if (_tag.equals("tileset")) {
_model.tsid = getTileSetId(attributes.getValue("tsid"));
_tset.tsid = getInt(attributes.getValue("tsid"));
String str = attributes.getValue("name");
_model.name = (str == null) ? DEF_NAME : str;
_tset.name = (str == null) ? DEF_NAME : str;
} else if (_tag.equals("object")) {
// TODO: should we bother checking to make sure we only
// see <object> tags while within an <objects> tag?
int tid = getInt(attributes.getValue("tid"));
int wid = getInt(attributes.getValue("width"));
int hei = getInt(attributes.getValue("height"));
// add the object info to the tileset object hashtable
_tset.objects.put(tid, new int[] { wid, hei });
}
}
// documentation inherited
public void endElement (String uri, String localName, String qName)
{
// we know we've received the entirety of the character data
@@ -96,6 +108,7 @@ public class XMLTileSetParser extends DefaultHandler
_chars = new StringBuffer();
}
// documentation inherited
public void characters (char ch[], int start, int length)
{
// bail if we're not within a meaningful tag
@@ -106,6 +119,7 @@ public class XMLTileSetParser extends DefaultHandler
_chars.append(ch, start, length);
}
// documentation inherited
public ArrayList loadTileSets (String fname) throws IOException
{
try {
@@ -141,15 +155,16 @@ public class XMLTileSetParser extends DefaultHandler
{
_chars = new StringBuffer();
_tset = createTileSet();
_model = _tset.getModel();
}
/**
* Constructs and returns a new tile set object.
* Constructs and returns a new tile set object. Derived classes
* may override this method to create their own sub-classes of the
* <code>TileSet</code> object.
*/
protected TileSet createTileSet ()
protected TileSetImpl createTileSet ()
{
return new TileSet();
return new TileSetImpl();
}
/**
@@ -167,26 +182,19 @@ public class XMLTileSetParser extends DefaultHandler
}
/**
* Returns the integer tileset id corresponding to the given
* string, or <code>DEF_TSID</code> if an error occurred.
* Returns the integer represented by the given string or -1 if an
* error occurred.
*/
protected int getTileSetId (String str)
protected int getInt (String str)
{
if (str == null) {
return DEF_TSID;
}
try {
return Integer.parseInt(str);
return (str == null) ? -1 : Integer.parseInt(str);
} catch (NumberFormatException nfe) {
Log.warning("Malformed integer tileset id [str=" + str + "].");
return DEF_TSID;
Log.warning("Malformed integer value [str=" + str + "].");
return -1;
}
}
/** Default tileset id. */
protected static final int DEF_TSID = -1;
/** Default tileset name. */
protected static final String DEF_NAME = "Untitled";
@@ -199,9 +207,6 @@ public class XMLTileSetParser extends DefaultHandler
/** Temporary storage of character data while parsing. */
protected StringBuffer _chars;
/** The tile set whose model is populated while parsing. */
protected TileSet _tset;
/** The tile set data model populated while parsing. */
protected TileSet.TileSetModel _model;
/** The tile set populated while parsing. */
protected TileSetImpl _tset;
}