diff --git a/src/java/com/threerings/miso/client/MisoScenePanel.java b/src/java/com/threerings/miso/client/MisoScenePanel.java index 8a3956ba..c2f44201 100644 --- a/src/java/com/threerings/miso/client/MisoScenePanel.java +++ b/src/java/com/threerings/miso/client/MisoScenePanel.java @@ -99,15 +99,14 @@ public class MisoScenePanel extends VirtualMediaPanel super(ctx.getFrameManager()); _ctx = ctx; _metrics = metrics; + _rethinkOp = new RethinkOp(_metrics); + _applicator = new TileOpApplicator(_metrics); // set ourselves up setOpaque(true); addMouseListener(this); addMouseMotionListener(this); - // handy rectangle - _tbounds = new Rectangle(0, 0, _metrics.tilewid, _metrics.tilehei); - // create the resolver if it's not already around if (_resolver == null) { _resolver = new SceneBlockResolver(); @@ -803,7 +802,7 @@ public class MisoScenePanel extends VirtualMediaPanel } // compute the intersecting set of blocks - applyToTiles(_ibounds, _rethinkOp); + _applicator.applyToTiles(_ibounds, _rethinkOp); // Log.info("Influential blocks " + // StringUtil.toString(_rethinkOp.blocks) + "."); @@ -852,34 +851,39 @@ public class MisoScenePanel extends VirtualMediaPanel log.debug("Rethunk [pending=" + _pendingBlocks + ", visible=" + _visiBlocks.size() + "]."); return _visiBlocks.size(); } - /** - * Called during the {@link #rethink} process, configures {@link - * #_ibounds} to contain the bounds of the potentially "influential" - * world and {@link #_vibounds} to contain bounds that are used to - * determine which blocks should be resolved before making the view - * visible. - * - *

Everything that intersects the influential area will be - * resolved on the expectation that it could be scrolled into view at - * any time. The influential bounds should be large enough that the - * time between a block becoming influential and the time at which it - * is resolved is longer than the expected time by which it will be - * scrolled into view, otherwise the users will see the man behind the - * curtain. + * Calls through to {@link #computeInfluentialBounds(Rectangle, Rectangle, Rectangle)} with + * _vbounds, _ibounds and _vibounds. */ protected void computeInfluentialBounds () { - int infborx = 3*_vbounds.width/4; - int infbory = _vbounds.height/2; - _ibounds.setBounds(_vbounds.x-infborx, _vbounds.y-infbory, - _vbounds.width+2*infborx, - // we go extra on the height because objects - // below can influence fairly high up - _vbounds.height+3*infbory); - _vibounds.setBounds(_vbounds.x-_vbounds.width/4, _vbounds.y, - _vbounds.width+_vbounds.width/2, - _vbounds.height+infbory); + computeInfluentialBounds(_vbounds, _ibounds, _vibounds); + + } + + + /** + * Configures influentialBounds to contain the bounds of the potentially + * "influential" world and visibleBlockBounds to contain bounds that are used to + * determine which blocks should be resolved before making the view visible. + * + *

Everything that intersects the influential area will be resolved on the expectation + * that it could be scrolled into view at any time. The influential bounds should be large + * enough that the time between a block becoming influential and the time at which it is + * resolved is longer than the expected time by which it will be scrolled into view, otherwise + * the users will see the man behind the curtain. + */ + public static void computeInfluentialBounds (Rectangle visibleBounds, + Rectangle influentualBounds, Rectangle visibleBlockBounds) + { + int infborx = 3 * visibleBounds.width / 4; + int infbory = visibleBounds.height / 2; + // we go extra on the height because objects below can influence fairly high up + influentualBounds.setBounds(visibleBounds.x - infborx, visibleBounds.y - infbory, + visibleBounds.width + 2 * infborx, visibleBounds.height + 3 * infbory); + visibleBlockBounds.setBounds(visibleBounds.x - visibleBounds.width / 4, + visibleBounds.y, visibleBounds.width + visibleBounds.width / 2, + visibleBounds.height + infbory); } /** @@ -1357,87 +1361,6 @@ public class MisoScenePanel extends VirtualMediaPanel } } - /** - * Applies the supplied tile operation to all tiles that intersect the - * supplied screen rectangle. - */ - protected void applyToTiles (Rectangle bounds, TileOp op) - { - // determine which tiles intersect this 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 = MisoUtil.screenToTile(_metrics, bounds.x, bounds.y, new Point()); - - // determine which quadrant of the upper left tile we occupy - Point spos = MisoUtil.tileToScreen(_metrics, tpos.x, tpos.y, new Point()); - boolean left = (bounds.x - spos.x < _metrics.tilehwid); - boolean top = (bounds.y - spos.y < _metrics.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 = bounds.x + bounds.width, - bottomy = bounds.y + bounds.height; - -// Log.info("Preparing to apply [tpos=" + StringUtil.toString(tpos) + -// ", left=" + left + ", top=" + top + -// ", bounds=" + StringUtil.toString(bounds) + -// ", spos=" + StringUtil.toString(spos) + -// "]."); - - // obtain the coordinates of the tile that starts the first row - // and loop through, applying to the intersecting tiles - MisoUtil.tileToScreen(_metrics, 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("Applying to row [tx=" + tx + ", ty=" + ty + "]."); - - // apply to the tiles in this row - while (_tbounds.x < rightx) { - op.apply(tx, ty, _tbounds); - - // move one tile to the right - tx += 1; ty -= 1; - _tbounds.x += _metrics.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 - MisoUtil.tileToScreen(_metrics, tpos.x, tpos.y, spos); - } - } - /** * Renders the base and fringe layer tiles that intersect the * specified clipping rectangle. @@ -1446,7 +1369,7 @@ public class MisoScenePanel extends VirtualMediaPanel { // go through rendering our tiles _paintOp.setGraphics(gfx); - applyToTiles(clip, _paintOp); + _applicator.applyToTiles(clip, _paintOp); _paintOp.setGraphics(null); } @@ -1482,26 +1405,18 @@ public class MisoScenePanel extends VirtualMediaPanel /** Computes the fringe tile for the specified coordinate. */ protected BaseTile computeFringeTile (int tx, int ty) { - return _ctx.getTileManager().getAutoFringer().getFringeTile( - _model, tx, ty, _masks); + return _ctx.getTileManager().getAutoFringer().getFringeTile(_model, tx, ty, _masks); } /** - * Returns true if we're responding to user input. This is used to - * control the display of tooltips and other potential user - * interactions. By default we are always responsive. + * Returns true if we're responding to user input. This is used to control the display of + * tooltips and other potential user interactions. By default we are always responsive. */ protected boolean isResponsive () { return true; } - /** Used with {@link #applyToTiles}. */ - protected static interface TileOp - { - public void apply (int tx, int ty, Rectangle tbounds); - } - /** Used by {@link #paintTiles}. */ protected class PaintTileOp implements TileOp { @@ -1597,24 +1512,6 @@ public class MisoScenePanel extends VirtualMediaPanel protected Font _font = new Font("Arial", Font.PLAIN, 7); } - /** Used by {@link #rethink}. */ - protected class RethinkOp implements TileOp - { - public Set blocks = Sets.newHashSet(); - - public void apply (int tx, int ty, Rectangle tbounds) { - _key.x = MathUtil.floorDiv(tx, _metrics.blockwid) * - _metrics.blockwid; - _key.y = MathUtil.floorDiv(ty, _metrics.blockhei) * - _metrics.blockhei; - if (!blocks.contains(_key)) { - blocks.add(new Point(_key.x, _key.y)); - } - } - - protected Point _key = new Point(); - } - /** Provides access to a few things. */ protected MisoContext _ctx; @@ -1638,7 +1535,7 @@ public class MisoScenePanel extends VirtualMediaPanel protected Rectangle _vibounds = new Rectangle(); /** Used by {@link #rethink}. */ - protected RethinkOp _rethinkOp = new RethinkOp(); + protected RethinkOp _rethinkOp; /** Contains our scene blocks. See {@link #getBlock} for details. */ protected HashIntMap _blocks = new HashIntMap(); @@ -1665,9 +1562,6 @@ public class MisoScenePanel extends VirtualMediaPanel /** The working sprites list used when calculating dirty regions. */ protected List _dirtySprites = Lists.newArrayList(); - /** Used when rendering tiles. */ - protected Rectangle _tbounds; - /** Used to paint tiles. */ protected PaintTileOp _paintOp = new PaintTileOp(); @@ -1709,6 +1603,8 @@ public class MisoScenePanel extends VirtualMediaPanel // used to display debugging information on scene block resolution protected JFrame _dframe; protected ResolutionView _dpanel; + + protected TileOpApplicator _applicator; /** A debug hook that toggles debug rendering of traversable tiles. */ protected static RuntimeAdjust.BooleanAdjust _traverseDebug = diff --git a/src/java/com/threerings/miso/client/RethinkOp.java b/src/java/com/threerings/miso/client/RethinkOp.java new file mode 100644 index 00000000..361de55f --- /dev/null +++ b/src/java/com/threerings/miso/client/RethinkOp.java @@ -0,0 +1,36 @@ +package com.threerings.miso.client; + +import java.awt.Point; +import java.awt.Rectangle; +import java.util.Set; + +import com.google.common.collect.Sets; + +import com.threerings.media.util.MathUtil; +import com.threerings.miso.util.MisoSceneMetrics; + +/** + * Constructs the set of blocks that intersect the op bounds. + */ +public class RethinkOp + implements TileOp +{ + public Set blocks = Sets.newHashSet(); + + public RethinkOp (MisoSceneMetrics metrics) + { + _metrics = metrics; + } + + public void apply (int tx, int ty, Rectangle tbounds) + { + _key.x = MathUtil.floorDiv(tx, _metrics.blockwid) * _metrics.blockwid; + _key.y = MathUtil.floorDiv(ty, _metrics.blockhei) * _metrics.blockhei; + if (!blocks.contains(_key)) { + blocks.add(new Point(_key.x, _key.y)); + } + } + + protected Point _key = new Point(); + protected MisoSceneMetrics _metrics; +} \ No newline at end of file diff --git a/src/java/com/threerings/miso/client/SceneBlock.java b/src/java/com/threerings/miso/client/SceneBlock.java index b6d78775..c8c34265 100644 --- a/src/java/com/threerings/miso/client/SceneBlock.java +++ b/src/java/com/threerings/miso/client/SceneBlock.java @@ -36,40 +36,46 @@ import com.threerings.geom.GeomUtil; import com.threerings.media.tile.NoSuchTileSetException; import com.threerings.media.tile.ObjectTile; import com.threerings.media.tile.Tile; +import com.threerings.media.tile.TileManager; import com.threerings.media.tile.TileSet; import com.threerings.media.tile.TileUtil; import com.threerings.media.util.MathUtil; - import com.threerings.miso.data.MisoSceneModel; import com.threerings.miso.data.ObjectInfo; import com.threerings.miso.tile.BaseTile; +import com.threerings.miso.util.MisoSceneMetrics; import com.threerings.miso.util.MisoUtil; import com.threerings.miso.util.ObjectSet; import static com.threerings.miso.Log.log; /** - * Contains the base and object tile information on a particular - * rectangular region of a scene. + * 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. + * Creates a scene block belonging to panel in preparation for its later resolution. */ - public SceneBlock ( - MisoScenePanel panel, int tx, int ty, int width, int height) + public SceneBlock (MisoScenePanel panel, int tx, int ty, int width, int height) { - _panel = panel; + this(panel.getSceneModel(), panel.getSceneMetrics(), panel.getTileManager(), tx, ty, + width, height); + } + + public SceneBlock (MisoSceneModel model, MisoSceneMetrics metrics, TileManager tileMgr, + int tx, int ty, int width, int height) + { + _model = model; + _metrics = metrics; + _tileMgr = tileMgr; _bounds = new Rectangle(tx, ty, width, height); _base = new BaseTile[width*height]; _fringe = new BaseTile[width*height]; _covered = new boolean[width*height]; // compute our screen-coordinate footprint polygon - _footprint = MisoUtil.getFootprintPolygon( - panel.getSceneMetrics(), tx, ty, width, height); + _footprint = MisoUtil.getFootprintPolygon(_metrics, tx, ty, width, height); // the rest of our resolution will happen in resolve() } @@ -88,15 +94,17 @@ public class SceneBlock * block resolution thread to allow us to load up our image data * without blocking the AWT thread. */ - protected boolean resolve () + public boolean resolve () { // if we got canned before we were resolved, go ahead and bail now - if (_panel.getBlock(_bounds.x, _bounds.y) != this) { - // Log.info("Not resolving abandoned block " + this + "."); - _panel.blockAbandoned(this); - return false; + if (_panel != null) { + if (_panel.getBlock(_bounds.x, _bounds.y) != this) { + // Log.info("Not resolving abandoned block " + this + "."); + _panel.blockAbandoned(this); + return false; + } + _panel.blockResolving(this); } - _panel.blockResolving(this); // start with the bounds of the footprint polygon Rectangle sbounds = new Rectangle(_footprint.getBounds()); @@ -105,11 +113,10 @@ public class SceneBlock // resolve our base tiles long now = System.currentTimeMillis(); int baseCount = 0, fringeCount = 0; - MisoSceneModel model = _panel.getSceneModel(); for (int yy = 0; yy < _bounds.height; yy++) { for (int xx = 0; xx < _bounds.width; xx++) { int x = _bounds.x + xx, y = _bounds.y + yy; - int fqTileId = model.getBaseTileId(x, y); + int fqTileId = _model.getBaseTileId(x, y); if (fqTileId <= 0) { continue; } @@ -125,7 +132,7 @@ public class SceneBlock } // compute the fringe for this tile - _fringe[tidx] = _panel.computeFringeTile(x, y); + _fringe[tidx] = computeFringeTile(x, y); fringeCount++; } } @@ -142,11 +149,11 @@ public class SceneBlock // resolve our objects ObjectSet set = new ObjectSet(); - model.getObjects(_bounds, set); + _model.getObjects(_bounds, set); ArrayList scobjs = new ArrayList(); now = System.currentTimeMillis(); for (int ii = 0, ll = set.size(); ii < ll; ii++) { - SceneObject scobj = new SceneObject(_panel, set.get(ii)); + SceneObject scobj = makeSceneObject(set.get(ii)); // ignore this object if it failed to resolve if (scobj.bounds == null) { continue; @@ -168,10 +175,10 @@ public class SceneBlock _objects = scobjs.toArray(new SceneObject[scobjs.size()]); // resolve our default tileset - int bsetid = model.getDefaultBaseTileSet(); + int bsetid = _model.getDefaultBaseTileSet(); try { if (bsetid > 0) { - _defset = _panel.getTileManager().getTileSet(bsetid); + _defset = _tileMgr.getTileSet(bsetid); } } catch (Exception e) { log.warning("Unable to fetch default base tileset [tsid=" + bsetid + @@ -187,10 +194,22 @@ public class SceneBlock return true; } + + protected SceneObject makeSceneObject (ObjectInfo info) + { + return new SceneObject(_metrics, _tileMgr, + _panel == null ? null : _panel.getColorizer(info), info); + } + + /** Computes the fringe tile for the specified coordinate. */ + protected BaseTile computeFringeTile (int tx, int ty) + { + return _panel == null ? null : _panel.computeFringeTile(tx, ty); + } /** - * This is called by the {@link SceneBlockResolver} on the AWT thread - * when our resolution has completed. We inform our containing panel. + * This is called by the {@link SceneBlockResolver} on the AWT thread when our resolution has + * completed. We inform our containing panel. */ protected void wasResolved () { @@ -292,8 +311,7 @@ public class SceneBlock if (fqTileId <= 0) { _base[tidx] = null; } else { - _base[tidx] = (BaseTile) - _panel.getTileManager().getTile(fqTileId); + _base[tidx] = (BaseTile)_tileMgr.getTile(fqTileId); } // clear out the fringe (it must be recomputed by the caller) _fringe[tidx] = null; @@ -318,7 +336,7 @@ public class SceneBlock { int tidx = index(tx, ty); if (_base[tidx] != null) { - _fringe[tidx] = _panel.computeFringeTile(tx, ty); + _fringe[tidx] = computeFringeTile(tx, ty); } } @@ -340,7 +358,7 @@ public class SceneBlock } } - _objects = ArrayUtil.append(_objects, new SceneObject(_panel, info)); + _objects = ArrayUtil.append(_objects, makeSceneObject(info)); // clear out our neighbors array so that the subsequent update // causes us to recompute our coverage @@ -554,8 +572,14 @@ public class SceneBlock _covered[index(tx, ty)] = true; } - /** The panel for which we contain a block. */ + /** The panel for which we contain a block or null if we aren't backed by a panel. */ protected MisoScenePanel _panel; + + protected MisoSceneMetrics _metrics; + + protected MisoSceneModel _model; + + protected TileManager _tileMgr; /** The bounds of (in tile coordinates) of this block. */ protected Rectangle _bounds; diff --git a/src/java/com/threerings/miso/client/SceneObject.java b/src/java/com/threerings/miso/client/SceneObject.java index 8d6fcb6c..94ab708d 100644 --- a/src/java/com/threerings/miso/client/SceneObject.java +++ b/src/java/com/threerings/miso/client/SceneObject.java @@ -29,13 +29,15 @@ import java.awt.Point; import java.awt.Polygon; import java.awt.Rectangle; -import com.samskivert.swing.RuntimeAdjust; import com.samskivert.util.StringUtil; +import com.samskivert.swing.RuntimeAdjust; + import com.threerings.media.tile.NoSuchTileSetException; import com.threerings.media.tile.ObjectTile; +import com.threerings.media.tile.TileManager; import com.threerings.media.tile.TileUtil; - +import com.threerings.media.tile.TileSet.Colorizer; import com.threerings.miso.MisoPrefs; import com.threerings.miso.data.ObjectInfo; import com.threerings.miso.util.MisoSceneMetrics; @@ -59,16 +61,21 @@ public class SceneObject 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. + * 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(panel.getSceneMetrics(), panel.getTileManager(), panel.getColorizer(info), info); + } + + public SceneObject (MisoSceneMetrics metrics, TileManager mgr, Colorizer colorizer, + ObjectInfo info) { this.info = info; // resolve our object tile - refreshObjectTile(panel); + refreshObjectTile(metrics, mgr, colorizer); } /** @@ -241,30 +248,38 @@ public class SceneObject } /** - * Reloads and recolorizes our object tile. It is not intended for the - * actual object tile used by a scene object to change in its - * lifetime, only attributes of that object like its colorizations. So - * don't do anything crazy like change our {@link ObjectInfo}'s + * Reloads and recolorizes our object tile. It is not intended for the actual object tile used + * by a scene object to change in its lifetime, only attributes of that object like its + * colorizations. So don't do anything crazy like change our {@link ObjectInfo}'s * tileId and call this method or things might break. */ public void refreshObjectTile (MisoScenePanel panel) + { + refreshObjectTile(panel.getSceneMetrics(), panel.getTileManager(), + panel.getColorizer(info)); + } + + /** + * Reloads and recolorizes our object tile. It is not intended for the actual object tile used + * by a scene object to change in its lifetime, only attributes of that object like its + * colorizations. So don't do anything crazy like change our {@link ObjectInfo}'s + * tileId and call this method or things might break. + */ + public void refreshObjectTile (MisoSceneMetrics metrics, TileManager mgr, Colorizer colorizer) { int tsid = TileUtil.getTileSetId(info.tileId); int tidx = TileUtil.getTileIndex(info.tileId); try { - tile = (ObjectTile)panel.getTileManager().getTile( - tsid, tidx, panel.getColorizer(info)); - computeInfo(panel.getSceneMetrics()); + tile = (ObjectTile)mgr.getTile(tsid, tidx, colorizer); + computeInfo(metrics); } catch (NoSuchTileSetException te) { - log.warning("Scene contains non-existent object tileset " + - "[info=" + info + "]."); + log.warning("Scene contains non-existent object tileset [info=" + info + "]."); } } /** - * Computes our screen bounds, tile footprint and other useful cached - * metrics. + * Computes our screen bounds, tile footprint and other useful cached metrics. */ protected void computeInfo (MisoSceneMetrics metrics) { diff --git a/src/java/com/threerings/miso/client/TileOp.java b/src/java/com/threerings/miso/client/TileOp.java new file mode 100644 index 00000000..442a75c6 --- /dev/null +++ b/src/java/com/threerings/miso/client/TileOp.java @@ -0,0 +1,11 @@ +package com.threerings.miso.client; + +import java.awt.Rectangle; + +/** + * Exposes an operation to be applied to tiles by {@link TileOpApplicator}. + */ +public interface TileOp +{ + public void apply (int tx, int ty, Rectangle tbounds); +} \ No newline at end of file diff --git a/src/java/com/threerings/miso/client/TileOpApplicator.java b/src/java/com/threerings/miso/client/TileOpApplicator.java new file mode 100644 index 00000000..6578c054 --- /dev/null +++ b/src/java/com/threerings/miso/client/TileOpApplicator.java @@ -0,0 +1,111 @@ +package com.threerings.miso.client; + +import java.awt.Point; +import java.awt.Rectangle; + +import com.threerings.miso.util.MisoSceneMetrics; +import com.threerings.miso.util.MisoUtil; + +/** + * Applies a TileOp to all tiles within a region. + */ +public class TileOpApplicator +{ + + public TileOpApplicator (MisoSceneMetrics metrics) + { + _metrics = metrics; + _tbounds = new Rectangle(0, 0, _metrics.tilewid, _metrics.tilehei); + } + + /** + * Applies the supplied tile operation to all tiles that intersect the supplied screen + * rectangle. + */ + public void applyToTiles (Rectangle region, TileOp op) + { + // determine which tiles intersect this 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 = MisoUtil.screenToTile(_metrics, region.x, region.y, new Point()); + + // determine which quadrant of the upper left tile we occupy + Point spos = MisoUtil.tileToScreen(_metrics, tpos.x, tpos.y, new Point()); + boolean left = (region.x - spos.x < _metrics.tilehwid); + boolean top = (region.y - spos.y < _metrics.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 = region.x + region.width, bottomy = region.y + region.height; + +// Log.info("Preparing to apply [tpos=" + StringUtil.toString(tpos) + +// ", left=" + left + ", top=" + top + +// ", bounds=" + StringUtil.toString(bounds) + +// ", spos=" + StringUtil.toString(spos) + +// "]."); + + // obtain the coordinates of the tile that starts the first row + // and loop through, applying to the intersecting tiles + MisoUtil.tileToScreen(_metrics, 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("Applying to row [tx=" + tx + ", ty=" + ty + "]."); + + // apply to the tiles in this row + while (_tbounds.x < rightx) { + op.apply(tx, ty, _tbounds); + + // move one tile to the right + tx += 1; + ty -= 1; + _tbounds.x += _metrics.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 + MisoUtil.tileToScreen(_metrics, tpos.x, tpos.y, spos); + } + } + + protected MisoSceneMetrics _metrics; + + /** Used when rendering tiles. */ + protected Rectangle _tbounds; + +}