Type safety and widening.

git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@246 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
Michael Bayne
2007-05-15 16:59:58 +00:00
parent c755fd626a
commit 9a30debf42
3 changed files with 134 additions and 145 deletions
@@ -66,25 +66,22 @@ public abstract class AbstractMediaManager
} }
/** /**
* Must be called every frame so that the media can be properly * Must be called every frame so that the media can be properly updated.
* updated.
*/ */
public void tick (long tickStamp) public void tick (long tickStamp)
{ {
_tickStamp = tickStamp; _tickStamp = tickStamp;
tickAllMedia(tickStamp); tickAllMedia(tickStamp);
dispatchNotifications(); dispatchNotifications();
// we clear our tick stamp when we're about to be painted, this // we clear our tick stamp when we're about to be painted, this lets us handle situations
// lets us handle situations when yet more media is slipped in // when yet more media is slipped in between our being ticked and our being painted
// between our being ticked and our being painted
} }
/** /**
* This will always be called prior to the {@link #paint} calls for a * This will always be called prior to the {@link #paint} calls for a particular tick. Because
* particular tick. Because it is possible that there will be no dirty * it is possible that there will be no dirty regions and thus no calls to {@link #paint} this
* regions and thus no calls to {@link #paint} this method exists so * method exists so that the media manager can guarantee that it will be notified when all
* that the media manager can guarantee that it will be notified when * ticking is complete and the painting phase has begun.
* all ticking is complete and the painting phase has begun.
*/ */
public void willPaint () public void willPaint ()
{ {
@@ -93,41 +90,36 @@ public abstract class AbstractMediaManager
} }
/** /**
* Renders all registered media in the given layer that intersect * Renders all registered media in the given layer that intersect the supplied clipping
* the supplied clipping rectangle to the given graphics context. * rectangle to the given graphics context.
* *
* @param layer the layer to render; one of {@link #FRONT}, {@link * @param layer the layer to render; one of {@link #FRONT}, {@link #BACK}, or {@link #ALL}.
* #BACK}, or {@link #ALL}. The front layer contains all animations * The front layer contains all animations with a positive render order; the back layer
* with a positive render order; the back layer contains all * contains all animations with a negative render order; all, both.
* animations with a negative render order; all, both.
*/ */
public void paint (Graphics2D gfx, int layer, Shape clip) public void paint (Graphics2D gfx, int layer, Shape clip)
{ {
for (int ii = 0, nn = _media.size(); ii < nn; ii++) { for (int ii = 0, nn = _media.size(); ii < nn; ii++) {
AbstractMedia media = (AbstractMedia)_media.get(ii); AbstractMedia media = _media.get(ii);
int order = media.getRenderOrder(); int order = media.getRenderOrder();
try { try {
if (((layer == ALL) || if (((layer == ALL) || (layer == FRONT && order >= 0) ||
(layer == FRONT && order >= 0) || (layer == BACK && order < 0)) && clip.intersects(media.getBounds())) {
(layer == BACK && order < 0)) &&
clip.intersects(media.getBounds())) {
media.paint(gfx); media.paint(gfx);
} }
} catch (Exception e) { } catch (Exception e) {
Log.warning("Failed to render media " + Log.warning("Failed to render media [media=" + media + ", e=" + e + "].");
"[media=" + media + ", e=" + e + "].");
Log.logStackTrace(e); Log.logStackTrace(e);
} }
} }
} }
/** /**
* If the manager is paused for some length of time, it should * If the manager is paused for some length of time, it should be fast forwarded by the
* be fast forwarded by the appropriate number of milliseconds. This * appropriate number of milliseconds. This allows media to smoothly pick up where they left
* allows media to smoothly pick up where they left off rather than * off rather than abruptly jumping into the future, thinking that some outrageous amount of
* abruptly jumping into the future, thinking that some outrageous * time passed since their last tick.
* amount of time passed since their last tick.
*/ */
public void fastForward (long timeDelta) public void fastForward (long timeDelta)
{ {
@@ -136,14 +128,13 @@ public abstract class AbstractMediaManager
Thread.dumpStack(); Thread.dumpStack();
} }
for (int ii=0, nn=_media.size(); ii < nn; ii++) { for (int ii = 0, nn = _media.size(); ii < nn; ii++) {
((AbstractMedia) _media.get(ii)).fastForward(timeDelta); _media.get(ii).fastForward(timeDelta);
} }
} }
/** /**
* Returns true if the specified media is being managed by this media * Returns true if the specified media is being managed by this media manager.
* manager.
*/ */
public boolean isManaged (AbstractMedia media) public boolean isManaged (AbstractMedia media)
{ {
@@ -151,8 +142,7 @@ public abstract class AbstractMediaManager
} }
/** /**
* Called by a {@link VirtualMediaPanel} when the view that contains our * Called by a {@link VirtualMediaPanel} when the view that contains our media is scrolled.
* media is scrolled.
* *
* @param dx the scrolled distance in the x direction (in pixels). * @param dx the scrolled distance in the x direction (in pixels).
* @param dy the scrolled distance in the y direction (in pixels). * @param dy the scrolled distance in the y direction (in pixels).
@@ -161,7 +151,7 @@ public abstract class AbstractMediaManager
{ {
// let our media know // let our media know
for (int ii = 0, ll = _media.size(); ii < ll; ii++) { for (int ii = 0, ll = _media.size(); ii < ll; ii++) {
((AbstractMedia)_media.get(ii)).viewLocationDidChange(dx, dy); _media.get(ii).viewLocationDidChange(dx, dy);
} }
} }
@@ -180,29 +170,26 @@ public abstract class AbstractMediaManager
} }
/** /**
* Calls {@link AbstractMedia#tick} on all media to give them a chance * Calls {@link AbstractMedia#tick} on all media to give them a chance to move about, change
* to move about, change their look, generate dirty regions, and so * their look, generate dirty regions, and so forth.
* forth.
*/ */
protected void tickAllMedia (long tickStamp) protected void tickAllMedia (long tickStamp)
{ {
// we use _tickpos so that it can be adjusted if media is added or // we use _tickpos so that it can be adjusted if media is added or removed during the tick
// removed during the tick dispatch // dispatch
for (_tickpos = 0; _tickpos < _media.size(); _tickpos++) { for (_tickpos = 0; _tickpos < _media.size(); _tickpos++) {
tickMedia((AbstractMedia) _media.get(_tickpos), tickStamp); tickMedia(_media.get(_tickpos), tickStamp);
} }
_tickpos = -1; _tickpos = -1;
} }
/** /**
* Inserts the specified media into this manager, return true on * Inserts the specified media into this manager, return true on success.
* success.
*/ */
protected boolean insertMedia (AbstractMedia media) protected boolean insertMedia (AbstractMedia media)
{ {
if (_media.contains(media)) { if (_media.contains(media)) {
Log.warning("Attempt to insert media more than once " + Log.warning("Attempt to insert media more than once [media=" + media + "].");
"[media=" + media + "].");
Thread.dumpStack(); Thread.dumpStack();
return false; return false;
} }
@@ -210,20 +197,17 @@ public abstract class AbstractMediaManager
media.init(this); media.init(this);
int ipos = _media.insertSorted(media, RENDER_ORDER); int ipos = _media.insertSorted(media, RENDER_ORDER);
// if we've started our tick but have not yet painted our media, // if we've started our tick but have not yet painted our media, we need to take care that
// we need to take care that this newly added media will be ticked // this newly added media will be ticked before our upcoming render
// before our upcoming render
if (_tickStamp > 0L) { if (_tickStamp > 0L) {
if (_tickpos == -1) { if (_tickpos == -1) {
// if we're done with our own call to tick(), we // if we're done with our own call to tick(), we need to tick this new media
// definitely need to tick this new media
tickMedia(media, _tickStamp); tickMedia(media, _tickStamp);
} else if (ipos <= _tickpos) { } else if (ipos <= _tickpos) {
// otherwise, we're in the middle of our call to tick() // otherwise, we're in the middle of our call to tick() and we only need to tick
// and we only need to tick this guy if he's being // this guy if he's being inserted before our current tick position (if he's
// inserted before our current tick position (if he's // inserted after our current position, we'll get to him as part of this tick
// inserted after our current position, we'll get to him // iteration)
// as part of this tick iteration)
_tickpos++; _tickpos++;
tickMedia(media, _tickStamp); tickMedia(media, _tickStamp);
} }
@@ -242,8 +226,7 @@ public abstract class AbstractMediaManager
} }
/** /**
* Removes the specified media from this manager, return true on * Removes the specified media from this manager, return true on success.
* success.
*/ */
protected boolean removeMedia (AbstractMedia media) protected boolean removeMedia (AbstractMedia media)
{ {
@@ -252,22 +235,19 @@ public abstract class AbstractMediaManager
_media.remove(mpos); _media.remove(mpos);
media.invalidate(); media.invalidate();
media.shutdown(); media.shutdown();
// if we're in the middle of ticking, we need to adjust the // if we're in the middle of ticking, we need to adjust the _tickpos if necessary
// _tickpos if necessary
if (mpos <= _tickpos) { if (mpos <= _tickpos) {
_tickpos--; _tickpos--;
} }
return true; return true;
} }
Log.warning("Attempt to remove media that wasn't inserted " + Log.warning("Attempt to remove media that wasn't inserted [media=" + media + "].");
"[media=" + media + "].");
return false; return false;
} }
/** /**
* Clears all media from the manager and calls {@link * Clears all media from the manager and calls {@link AbstractMedia#shutdown} on each. This
* AbstractMedia#shutdown} on each. This does not invalidate the * does not invalidate the media's vacated bounds; it is assumed that it will be ok.
* media's vacated bounds; it is assumed that it will be ok.
*/ */
protected void clearMedia () protected void clearMedia ()
{ {
@@ -276,29 +256,30 @@ public abstract class AbstractMediaManager
Thread.dumpStack(); Thread.dumpStack();
} }
for (int ii=_media.size() - 1; ii >= 0; ii--) { for (int ii = _media.size() - 1; ii >= 0; ii--) {
AbstractMedia media = (AbstractMedia) _media.remove(ii); _media.remove(ii).shutdown();
media.shutdown();
} }
} }
/** /**
* Queues the notification for dispatching after we've ticked all the * Queues the notification for dispatching after we've ticked all the media.
* media.
*/ */
public void queueNotification (ObserverList observers, ObserverOp event) public void queueNotification (ObserverList observers, ObserverOp event)
{ {
_notify.add(new Tuple(observers, event)); _notify.add(new Tuple<ObserverList,ObserverOp>(observers, event));
} }
/** Type safety jockeying. */
protected abstract SortableArrayList<? extends AbstractMedia> createMediaList ();
/** /**
* Dispatches all queued media notifications. * Dispatches all queued media notifications.
*/ */
protected void dispatchNotifications () protected void dispatchNotifications ()
{ {
for (int ii = 0, nn = _notify.size(); ii < nn; ii++) { for (int ii = 0, nn = _notify.size(); ii < nn; ii++) {
Tuple tuple = (Tuple)_notify.get(ii); Tuple<ObserverList,ObserverOp> tuple = _notify.get(ii);
((ObserverList)tuple.left).apply((ObserverOp)tuple.right); tuple.left.apply(tuple.right);
} }
_notify.clear(); _notify.clear();
} }
@@ -310,28 +291,26 @@ public abstract class AbstractMediaManager
protected RegionManager _remgr; protected RegionManager _remgr;
/** List of observers to notify at the end of the tick. */ /** List of observers to notify at the end of the tick. */
protected ArrayList _notify = new ArrayList(); protected ArrayList<Tuple<ObserverList,ObserverOp>> _notify =
new ArrayList<Tuple<ObserverList,ObserverOp>>();
/** Our render-order sorted list of media. */ /** Our render-order sorted list of media. */
protected SortableArrayList _media = new SortableArrayList(); protected SortableArrayList<AbstractMedia> _media;
/** The position in our media list that we're ticking in the middle of /** The position in our media list that we're ticking (while in the middle of a call to {@link
* a call to {@link #tick} otherwise -1. */ * #tick}) otherwise -1. */
protected int _tickpos = -1; protected int _tickpos = -1;
/** The tick stamp if the manager is in the midst of a call to {@link /** The tick stamp if the manager is in the midst of a call to {@link #tick}, else 0. */
* #tick}, else <code>0</code>. */
protected long _tickStamp; protected long _tickStamp;
/** Used to sort media by render order. */ /** Used to sort media by render order. */
protected static final Comparator RENDER_ORDER = new Comparator() { protected static final Comparator<AbstractMedia> RENDER_ORDER =
public int compare (Object o1, Object o2) { new Comparator<AbstractMedia>() {
int result = (((AbstractMedia)o1)._renderOrder - public int compare (AbstractMedia am1, AbstractMedia am2) {
((AbstractMedia)o2)._renderOrder); int result = am1._renderOrder - am2._renderOrder;
return (result != 0) ? result : // if render order is same, use hashcode to keep them stable relative to each other
// find some other way to keep them stable relative to return (result != 0) ? result : am1.hashCode() - am2.hashCode();
// each other
o1.hashCode() - o2.hashCode();
} }
}; };
} }
@@ -21,19 +21,20 @@
package com.threerings.media.animation; package com.threerings.media.animation;
import com.samskivert.util.SortableArrayList;
import com.threerings.media.AbstractMedia;
import com.threerings.media.AbstractMediaManager; import com.threerings.media.AbstractMediaManager;
import com.threerings.media.MediaHost; import com.threerings.media.MediaHost;
/** /**
* Manages a collection of animations, ticking them when the animation * Manages a collection of animations, ticking them when the animation manager itself is ticked and
* manager itself is ticked and generating events when animations finish * generating events when animations finish and suchlike.
* and suchlike.
*/ */
public class AnimationManager extends AbstractMediaManager public class AnimationManager extends AbstractMediaManager
{ {
/** /**
* Registers the given {@link Animation} with the animation manager * Registers the given {@link Animation} with the animation manager for ticking and painting.
* for ticking and painting.
*/ */
public void registerAnimation (Animation anim) public void registerAnimation (Animation anim)
{ {
@@ -41,23 +42,22 @@ public class AnimationManager extends AbstractMediaManager
} }
/** /**
* Un-registers the given {@link Animation} from the animation * Un-registers the given {@link Animation} from the animation manager. The bounds of the
* manager. The bounds of the animation will automatically be * animation will automatically be invalidated so that they are properly rerendered in the
* invalidated so that they are properly rerendered in the absence of * absence of the animation.
* the animation.
*/ */
public void unregisterAnimation (Animation anim) public void unregisterAnimation (Animation anim)
{ {
removeMedia(anim); removeMedia(anim);
} }
// documentation inherited @Override // from AbstractMediaManager
protected void tickAllMedia (long tickStamp) protected void tickAllMedia (long tickStamp)
{ {
super.tickAllMedia(tickStamp); super.tickAllMedia(tickStamp);
for (int ii = _media.size() - 1; ii >= 0; ii--) { for (int ii = _anims.size() - 1; ii >= 0; ii--) {
Animation anim = (Animation)_media.get(ii); Animation anim = _anims.get(ii);
if (!anim.isFinished()) { if (!anim.isFinished()) {
continue; continue;
} }
@@ -69,4 +69,12 @@ public class AnimationManager extends AbstractMediaManager
// Log.info("Removed finished animation " + anim + "."); // Log.info("Removed finished animation " + anim + ".");
} }
} }
@Override // from AbstractMediaManager
protected SortableArrayList<? extends AbstractMedia> createMediaList ()
{
return (_anims = new SortableArrayList<Animation>());
}
protected SortableArrayList<Animation> _anims;
} }
@@ -29,7 +29,9 @@ import java.util.List;
import java.util.Iterator; import java.util.Iterator;
import com.samskivert.util.Predicate; import com.samskivert.util.Predicate;
import com.samskivert.util.SortableArrayList;
import com.threerings.media.AbstractMedia;
import com.threerings.media.AbstractMediaManager; import com.threerings.media.AbstractMediaManager;
import com.threerings.media.MediaHost; import com.threerings.media.MediaHost;
@@ -39,21 +41,19 @@ import com.threerings.media.MediaHost;
public class SpriteManager extends AbstractMediaManager public class SpriteManager extends AbstractMediaManager
{ {
/** /**
* When an animated view processes its dirty rectangles, it may * When an animated view processes its dirty rectangles, it may require an expansion of the
* require an expansion of the dirty region which may in turn * dirty region which may in turn require the invalidation of more sprites than were originally
* require the invalidation of more sprites than were originally * invalid. In such cases, the animated view can call back to the sprite manager, asking it to
* invalid. In such cases, the animated view can call back to the * append the sprites that intersect a particular region to the given list.
* sprite manager, asking it to append the sprites that intersect
* a particular region to the given list.
* *
* @param list the list to fill with any intersecting sprites. * @param list the list to fill with any intersecting sprites.
* @param shape the shape in which we have interest. * @param shape the shape in which we have interest.
*/ */
public void getIntersectingSprites (List list, Shape shape) public void getIntersectingSprites (List<Sprite> list, Shape shape)
{ {
int size = _media.size(); int size = _sprites.size();
for (int ii = 0; ii < size; ii++) { for (int ii = 0; ii < size; ii++) {
Sprite sprite = (Sprite)_media.get(ii); Sprite sprite = _sprites.get(ii);
if (sprite.intersects(shape)) { if (sprite.intersects(shape)) {
list.add(sprite); list.add(sprite);
} }
@@ -61,21 +61,20 @@ public class SpriteManager extends AbstractMediaManager
} }
/** /**
* When an animated view is determining what entity in its view is * When an animated view is determining what entity in its view is under the mouse pointer, it
* under the mouse pointer, it may require a list of sprites that are * may require a list of sprites that are "hit" by a particular pixel. The sprites' bounds are
* "hit" by a particular pixel. The sprites' bounds are first checked * first checked and sprites with bounds that contain the supplied point are further checked
* and sprites with bounds that contain the supplied point are further * for a non-transparent at the specified location.
* checked for a non-transparent at the specified location.
* *
* @param list the list to fill with any intersecting sprites, the * @param list the list to fill with any intersecting sprites, the sprites with the highest
* sprites with the highest render order provided first. * render order provided first.
* @param x the x (screen) coordinate to be checked. * @param x the x (screen) coordinate to be checked.
* @param y the y (screen) coordinate to be checked. * @param y the y (screen) coordinate to be checked.
*/ */
public void getHitSprites (List list, int x, int y) public void getHitSprites (List<Sprite> list, int x, int y)
{ {
for (int ii = _media.size() - 1; ii >= 0; ii--) { for (int ii = _sprites.size() - 1; ii >= 0; ii--) {
Sprite sprite = (Sprite)_media.get(ii); Sprite sprite = _sprites.get(ii);
if (sprite.hitTest(x, y)) { if (sprite.hitTest(x, y)) {
list.add(sprite); list.add(sprite);
} }
@@ -83,8 +82,7 @@ public class SpriteManager extends AbstractMediaManager
} }
/** /**
* Finds the sprite with the highest render order that hits the * Finds the sprite with the highest render order that hits the specified pixel.
* specified pixel.
* *
* @param x the x (screen) coordinate to be checked * @param x the x (screen) coordinate to be checked
* @param y the y (screen) coordinate to be checked * @param y the y (screen) coordinate to be checked
@@ -93,8 +91,8 @@ public class SpriteManager extends AbstractMediaManager
public Sprite getHighestHitSprite (int x, int y) public Sprite getHighestHitSprite (int x, int y)
{ {
// since they're stored in lowest -> highest order.. // since they're stored in lowest -> highest order..
for (int ii = _media.size() - 1; ii >= 0; ii--) { for (int ii = _sprites.size() - 1; ii >= 0; ii--) {
Sprite sprite = (Sprite)_media.get(ii); Sprite sprite = _sprites.get(ii);
if (sprite.hitTest(x, y)) { if (sprite.hitTest(x, y)) {
return sprite; return sprite;
} }
@@ -116,27 +114,25 @@ public class SpriteManager extends AbstractMediaManager
} }
/** /**
* Returns a list of all sprites registered with the sprite manager. * Returns a list of all sprites registered with the sprite manager. The returned list is
* The returned list is immutable, sprites should be added or removed * immutable, sprites should be added or removed using {@link #addSprite} or {@link
* using {@link #addSprite} or {@link #removeSprite}. * #removeSprite}.
*/ */
public List getSprites () public List<Sprite> getSprites ()
{ {
return Collections.unmodifiableList(_media); return Collections.unmodifiableList(_sprites);
} }
/** /**
* Returns an iterator over our managed sprites. Do not call * Returns an iterator over our managed sprites. Do not call {@link Iterator#remove}.
* {@link Iterator#remove}.
*/ */
public Iterator enumerateSprites () public Iterator<Sprite> enumerateSprites ()
{ {
return _media.iterator(); return _sprites.iterator();
} }
/** /**
* Removes the specified sprite from the set of sprites managed by * Removes the specified sprite from the set of sprites managed by this manager.
* this manager.
* *
* @param sprite the sprite to remove. * @param sprite the sprite to remove.
*/ */
@@ -151,14 +147,14 @@ public class SpriteManager extends AbstractMediaManager
public void removeSprites (Predicate<Sprite> pred) public void removeSprites (Predicate<Sprite> pred)
{ {
int idxoff = 0; int idxoff = 0;
for (int ii = 0, ll = _media.size(); ii < ll; ii++) { for (int ii = 0, ll = _sprites.size(); ii < ll; ii++) {
Sprite sprite = (Sprite)_media.get(ii-idxoff); Sprite sprite = _sprites.get(ii-idxoff);
if (pred.isMatch(sprite)) { if (pred.isMatch(sprite)) {
_media.remove(sprite); _sprites.remove(sprite);
sprite.invalidate(); sprite.invalidate();
sprite.shutdown(); sprite.shutdown();
// we need to preserve the original "index" relative to the // we need to preserve the original "index" relative to the current tick position,
// current tick position, so we don't decrement ii directly // so we don't decrement ii directly
idxoff++; idxoff++;
if (ii <= _tickpos) { if (ii <= _tickpos) {
_tickpos--; _tickpos--;
@@ -174,8 +170,8 @@ public class SpriteManager extends AbstractMediaManager
*/ */
public void renderSpritePaths (Graphics2D gfx) public void renderSpritePaths (Graphics2D gfx)
{ {
for (int ii=0, nn=_media.size(); ii < nn; ii++) { for (int ii=0, nn=_sprites.size(); ii < nn; ii++) {
Sprite sprite = (Sprite)_media.get(ii); Sprite sprite = _sprites.get(ii);
sprite.paintPath(gfx); sprite.paintPath(gfx);
} }
} }
@@ -192,7 +188,7 @@ public class SpriteManager extends AbstractMediaManager
// // gather a list of all sprite collisions // // gather a list of all sprite collisions
// int size = _sprites.size(); // int size = _sprites.size();
// for (int ii = 0; ii < size; ii++) { // for (int ii = 0; ii < size; ii++) {
// Sprite sprite = (Sprite)_sprites.get(ii); // Sprite sprite = _sprites.get(ii);
// checkCollisions(ii, size, sprite); // checkCollisions(ii, size, sprite);
// } // }
// } // }
@@ -224,7 +220,7 @@ public class SpriteManager extends AbstractMediaManager
// int edgeX = bounds.x + bounds.width; // int edgeX = bounds.x + bounds.width;
// //
// for (int ii = (idx + 1); ii < size; ii++) { // for (int ii = (idx + 1); ii < size; ii++) {
// Sprite other = (Sprite)_sprites.get(ii); // Sprite other = _sprites.get(ii);
// Rectangle obounds = other.getBounds(); // Rectangle obounds = other.getBounds();
// if (obounds.x > edgeX) { // if (obounds.x > edgeX) {
// // since sprites are stored in the list sorted by // // since sprites are stored in the list sorted by
@@ -243,13 +239,19 @@ public class SpriteManager extends AbstractMediaManager
// protected static final Comparator SPRITE_COMP = new SpriteComparator(); // protected static final Comparator SPRITE_COMP = new SpriteComparator();
// //
// /** Used to sort sprites. */ // /** Used to sort sprites. */
// protected static class SpriteComparator implements Comparator // protected static class SpriteComparator<Sprite> implements Comparator
// { // {
// public int compare (Object o1, Object o2) // public int compare (Sprite s1, Sprite s2)
// { // {
// Sprite s1 = (Sprite)o1;
// Sprite s2 = (Sprite)o2;
// return (s2.getX() - s1.getX()); // return (s2.getX() - s1.getX());
// } // }
// } // }
@Override // from AbstractMediaManager
protected SortableArrayList<? extends AbstractMedia> createMediaList ()
{
return (_sprites = new SortableArrayList<Sprite>());
}
protected SortableArrayList<Sprite> _sprites;
} }