some Animator improvements

- fixes annoying zoomTo jumping
This commit is contained in:
Hannes Janetzek 2014-02-16 03:39:45 +01:00
parent 5d07d45f86
commit 46878c81b7
3 changed files with 92 additions and 135 deletions

View File

@ -80,6 +80,21 @@ public class MapPosition {
this.zoomLevel = other.zoomLevel; this.zoomLevel = other.zoomLevel;
} }
public void set(double x, double y, double scale, float rotation, float tilt) {
this.x = x;
this.y = y;
this.scale = scale;
while (rotation > 180)
rotation -= 360;
while (rotation < -180)
rotation += 360;
this.angle = rotation;
this.tilt = tilt;
this.zoomLevel = FastMath.log2((int) scale);
}
/** /**
* @return scale relative to zoom-level. * @return scale relative to zoom-level.
*/ */

View File

@ -26,30 +26,28 @@ import org.oscim.core.MapPosition;
import org.oscim.core.Point; import org.oscim.core.Point;
import org.oscim.core.Tile; import org.oscim.core.Tile;
import org.oscim.renderer.MapRenderer; import org.oscim.renderer.MapRenderer;
import org.slf4j.Logger;
// TODO: rewrite import org.slf4j.LoggerFactory;
public class Animator { public class Animator {
static final Logger log = LoggerFactory.getLogger(Animator.class);
//static final Logger log = LoggerFactory.getLogger(MapAnimator.class); //static final Logger log = LoggerFactory.getLogger(MapAnimator.class);
public Animator(Map map, Viewport viewport) { public Animator(Map map) {
mViewport = viewport;
mMap = map; mMap = map;
} }
private final int ANIM_NONE = 0; private final int ANIM_NONE = 0;
private final int ANIM_MOVE = 1 << 0; private final int ANIM_MOVE = 1 << 0;
private final int ANIM_SCALE = 1 << 1; private final int ANIM_SCALE = 1 << 1;
private final int ANIM_FLING = 1 << 2; private final int ANIM_ROTATE = 1 << 2;
private final int ANIM_BBOX = 1 << 3; private final int ANIM_TILT = 1 << 3;
private final int ANIM_ROTATE = 1 << 4; private final int ANIM_FLING = 1 << 4;
private final int ANIM_TILT = 1 << 5;
private final Map mMap; private final Map mMap;
private final Viewport mViewport;
private final MapPosition mPos = new MapPosition(); private final MapPosition mCurPos = new MapPosition();
private final MapPosition mStartPos = new MapPosition(); private final MapPosition mStartPos = new MapPosition();
private final MapPosition mDeltaPos = new MapPosition(); private final MapPosition mDeltaPos = new MapPosition();
@ -57,125 +55,97 @@ public class Animator {
private final Point mPivot = new Point(); private final Point mPivot = new Point();
private final Point mVelocity = new Point(); private final Point mVelocity = new Point();
private double mScaleBy;
private float mDuration = 500; private float mDuration = 500;
private long mAnimEnd = -1; private long mAnimEnd = -1;
private int mState = ANIM_NONE; private int mState = ANIM_NONE;
public synchronized void animateTo(BoundingBox bbox) { public synchronized void animateTo(long duration, BoundingBox bbox) {
mMap.getMapPosition(mStartPos);
/* TODO for large distance first scale out, then in /* TODO for large distance first scale out, then in
* calculate the maximum scale at which the bbox is completely visible */ * calculate the maximum scale at which the BoundingBox
* is completely visible */
double dx = Math.abs(longitudeToX(bbox.getMaxLongitude()) double dx = Math.abs(longitudeToX(bbox.getMaxLongitude())
- longitudeToX(bbox.getMinLongitude())); - longitudeToX(bbox.getMinLongitude()));
double dy = Math.abs(latitudeToY(bbox.getMinLatitude()) double dy = Math.abs(latitudeToY(bbox.getMinLatitude())
- latitudeToY(bbox.getMaxLatitude())); - latitudeToY(bbox.getMaxLatitude()));
log.debug("anim bbox " + bbox);
double zx = mMap.getWidth() / (dx * Tile.SIZE); double zx = mMap.getWidth() / (dx * Tile.SIZE);
double zy = mMap.getHeight() / (dy * Tile.SIZE); double zy = mMap.getHeight() / (dy * Tile.SIZE);
double newScale = Math.min(zx, zy); double newScale = Math.min(zx, zy);
animateTo(500, bbox.getCenterPoint(), newScale, false); GeoPoint p = bbox.getCenterPoint();
mState = ANIM_MOVE | ANIM_SCALE | ANIM_BBOX; mDeltaPos.set(longitudeToX(p.getLongitude()) - mStartPos.x,
latitudeToY(p.getLatitude()) - mStartPos.y,
newScale - mStartPos.scale,
-mStartPos.angle,
-mStartPos.tilt);
animStart(duration, ANIM_MOVE | ANIM_SCALE | ANIM_ROTATE | ANIM_TILT);
} }
public synchronized void animateTo(long duration, GeoPoint geoPoint, double scale, public synchronized void animateTo(BoundingBox bbox) {
boolean relative) { animateTo(1000, bbox);
mViewport.getMapPosition(mPos);
if (relative) {
if (mAnimEnd > 0 && (mState & ANIM_SCALE) != 0)
scale = mDeltaPos.scale * scale;
else
scale = mPos.scale * scale;
} }
public synchronized void animateTo(long duration, GeoPoint geoPoint,
double scale, boolean relative) {
mMap.getMapPosition(mStartPos);
if (relative)
scale = mStartPos.scale * scale;
scale = clamp(scale, Viewport.MIN_SCALE, Viewport.MAX_SCALE); scale = clamp(scale, Viewport.MIN_SCALE, Viewport.MAX_SCALE);
mDeltaPos.scale = scale;
mScaleBy = scale - mPos.scale; mDeltaPos.set(longitudeToX(geoPoint.getLongitude()) - mStartPos.x,
latitudeToY(geoPoint.getLatitude()) - mStartPos.y,
scale - mStartPos.scale,
0, 0);
mStartPos.scale = mPos.scale; animStart(duration, ANIM_MOVE | ANIM_SCALE);
mStartPos.angle = mPos.angle; }
mStartPos.x = mPos.x; public synchronized void animateTo(GeoPoint p) {
mStartPos.y = mPos.y; animateTo(500, p, 1, true);
mDeltaPos.x = longitudeToX(geoPoint.getLongitude());
mDeltaPos.y = latitudeToY(geoPoint.getLatitude());
mDeltaPos.x -= mStartPos.x;
mDeltaPos.y -= mStartPos.y;
mState = ANIM_MOVE | ANIM_SCALE;
animStart(duration);
} }
public synchronized void animateTo(long duration, MapPosition pos) { public synchronized void animateTo(long duration, MapPosition pos) {
mMap.getMapPosition(mStartPos);
mViewport.getMapPosition(mPos);
pos.scale = clamp(pos.scale, pos.scale = clamp(pos.scale,
Viewport.MIN_SCALE, Viewport.MIN_SCALE,
Viewport.MAX_SCALE); Viewport.MAX_SCALE);
mDeltaPos.scale = pos.scale;
mScaleBy = pos.scale - mPos.scale; mDeltaPos.set(pos.x - mStartPos.x,
pos.y - mStartPos.y,
pos.scale - mStartPos.scale,
mStartPos.angle - pos.angle,
clamp(pos.tilt, 0, Viewport.MAX_TILT) - mStartPos.tilt);
mStartPos.x = mPos.x; animStart(duration, ANIM_MOVE | ANIM_SCALE | ANIM_ROTATE | ANIM_TILT);
mStartPos.y = mPos.y;
mStartPos.scale = mPos.scale;
mStartPos.angle = mPos.angle;
mStartPos.tilt = mPos.tilt;
mDeltaPos.x = longitudeToX(pos.getLongitude()) - mStartPos.x;
mDeltaPos.y = latitudeToY(pos.getLatitude()) - mStartPos.y;
mDeltaPos.angle = mStartPos.angle - pos.angle;
while (mDeltaPos.angle > 180)
mDeltaPos.angle -= 360;
while (mDeltaPos.angle < -180)
mDeltaPos.angle += 360;
mDeltaPos.tilt = clamp(pos.tilt, 0, Viewport.MAX_TILT) - mStartPos.tilt;
mState = ANIM_MOVE | ANIM_SCALE | ANIM_ROTATE | ANIM_TILT;
animStart(duration);
} }
public synchronized void animateZoom(long duration, double scale, float pivotX, float pivotY) { public synchronized void animateZoom(long duration, double scaleBy,
float pivotX, float pivotY) {
mMap.getMapPosition(mStartPos);
mViewport.getMapPosition(mPos); if (mState == ANIM_SCALE)
scaleBy = (mStartPos.scale + mDeltaPos.scale) * scaleBy;
if (mAnimEnd > 0 && (mState & ANIM_SCALE) != 0)
scale = mDeltaPos.scale * scale;
else else
scale = mPos.scale * scale; scaleBy = mStartPos.scale * scaleBy;
scale = clamp(scale, Viewport.MIN_SCALE, Viewport.MAX_SCALE); scaleBy = clamp(scaleBy, Viewport.MIN_SCALE, Viewport.MAX_SCALE);
mDeltaPos.scale = scale;
mScaleBy = scale - mPos.scale; mDeltaPos.scale = scaleBy - mStartPos.scale;
mStartPos.scale = mPos.scale;
mStartPos.angle = mPos.angle;
mPivot.x = pivotX; mPivot.x = pivotX;
mPivot.y = pivotY; mPivot.y = pivotY;
mState = ANIM_SCALE; animStart(duration, ANIM_SCALE);
animStart(duration);
}
public synchronized void animateTo(GeoPoint geoPoint) {
animateTo(300, geoPoint, 1, true);
} }
public synchronized void animateFling(int velocityX, int velocityY, public synchronized void animateFling(int velocityX, int velocityY,
@ -184,7 +154,7 @@ public class Animator {
if (velocityX * velocityX + velocityY * velocityY < 2048) if (velocityX * velocityX + velocityY * velocityY < 2048)
return; return;
mViewport.getMapPosition(mPos); mMap.getMapPosition(mStartPos);
mScroll.x = 0; mScroll.x = 0;
mScroll.y = 0; mScroll.y = 0;
@ -198,12 +168,12 @@ public class Animator {
clamp(mVelocity.x, minX, maxX); clamp(mVelocity.x, minX, maxX);
clamp(mVelocity.y, minY, maxY); clamp(mVelocity.y, minY, maxY);
mState = ANIM_FLING; animStart(duration, ANIM_FLING);
animStart(duration);
} }
private void animStart(float duration) { private void animStart(float duration, int state) {
mState = state;
mCurPos.copy(mStartPos);
mDuration = duration; mDuration = duration;
mAnimEnd = System.currentTimeMillis() + (long) duration; mAnimEnd = System.currentTimeMillis() + (long) duration;
mMap.render(); mMap.render();
@ -225,30 +195,13 @@ public class Animator {
long millisLeft = mAnimEnd - MapRenderer.frametime; long millisLeft = mAnimEnd - MapRenderer.frametime;
boolean changed = false; boolean changed = false;
synchronized (mViewport) {
Viewport v = mViewport;
Viewport v = mMap.viewport();
synchronized (v) {
/* cancel animation when position was changed since last /* cancel animation when position was changed since last
* update, i.e. when it was modified outside the animator. */ * update, i.e. when it was modified outside the animator. */
if (v.getMapPosition(mPos)) { if (v.getMapPosition(mCurPos)) {
animCancel();
return;
}
if (millisLeft <= 0) {
/* set final position */
if ((mState & ANIM_MOVE) != 0)
v.moveTo(mStartPos.x + mDeltaPos.x,
mStartPos.y + mDeltaPos.y);
if ((mState & ANIM_SCALE) != 0) {
if (mScaleBy > 0)
doScale(mStartPos.scale + (mScaleBy - 1));
else
doScale(mStartPos.scale + mScaleBy);
}
mMap.updateMap(true);
animCancel(); animCancel();
return; return;
} }
@ -256,25 +209,12 @@ public class Animator {
float adv = clamp(1.0f - millisLeft / mDuration, 0, 1); float adv = clamp(1.0f - millisLeft / mDuration, 0, 1);
if ((mState & ANIM_SCALE) != 0) { if ((mState & ANIM_SCALE) != 0) {
if (mScaleBy > 0) doScale(v, adv);
doScale(mStartPos.scale + (mScaleBy * (Math.pow(2, adv) - 1)));
else
doScale(mStartPos.scale + (mScaleBy * adv));
changed = true;
} }
if ((mState & ANIM_MOVE) != 0) { if ((mState & ANIM_MOVE) != 0) {
v.moveTo(mStartPos.x + mDeltaPos.x * adv, v.moveTo(mStartPos.x + mDeltaPos.x * adv,
mStartPos.y + mDeltaPos.y * adv); mStartPos.y + mDeltaPos.y * adv);
changed = true;
}
if ((mState & ANIM_BBOX) != 0) {
if (mPos.angle > 180)
mPos.angle -= 360;
v.setRotation(mPos.angle * (1 - adv));
v.setTilt(mPos.tilt * (1 - adv));
} }
if ((mState & ANIM_FLING) != 0) { if ((mState & ANIM_FLING) != 0) {
@ -282,30 +222,27 @@ public class Animator {
double dx = mVelocity.x * adv; double dx = mVelocity.x * adv;
double dy = mVelocity.y * adv; double dy = mVelocity.y * adv;
if ((dx - mScroll.x) != 0 || (dy - mScroll.y) != 0) { if ((dx - mScroll.x) != 0 || (dy - mScroll.y) != 0) {
v.moveMap((float) (dx - mScroll.x), v.moveMap((float) (dx - mScroll.x),
(float) (dy - mScroll.y)); (float) (dy - mScroll.y));
mScroll.x = dx; mScroll.x = dx;
mScroll.y = dy; mScroll.y = dy;
changed = true;
} }
} }
if ((mState & ANIM_ROTATE) != 0) { if ((mState & ANIM_ROTATE) != 0) {
v.setRotation(mStartPos.angle + mDeltaPos.angle * adv); v.setRotation(mStartPos.angle + mDeltaPos.angle * adv);
changed = true;
} }
if ((mState & ANIM_TILT) != 0) { if ((mState & ANIM_TILT) != 0) {
v.setTilt(mStartPos.tilt + mDeltaPos.tilt * adv); v.setTilt(mStartPos.tilt + mDeltaPos.tilt * adv);
changed = true;
} }
if (millisLeft <= 0)
animCancel();
/* remember current map position */ /* remember current map position */
v.getMapPosition(mPos); changed = v.getMapPosition(mCurPos);
} }
/* continue animation */
if (changed) { if (changed) {
/* render and inform layers that position has changed */ /* render and inform layers that position has changed */
mMap.updateMap(true); mMap.updateMap(true);
@ -315,8 +252,12 @@ public class Animator {
} }
} }
private void doScale(double newScale) { private void doScale(Viewport v, float adv) {
mViewport.scaleMap((float) (newScale / mPos.scale), double newScale;
newScale = mStartPos.scale + (mDeltaPos.scale * adv);
v.scaleMap((float) (newScale / mCurPos.scale),
(float) mPivot.x, (float) mPivot.y); (float) mPivot.x, (float) mPivot.y);
} }

View File

@ -83,12 +83,13 @@ public abstract class Map {
mViewport = new Viewport(this); mViewport = new Viewport(this);
mAnimator = new Animator(this, mViewport); mAnimator = new Animator(this, mViewport);
mMapPosition = new MapPosition();
mLayers = new Layers(this); mLayers = new Layers(this);
mAsyncExecutor = new AsyncExecutor(2); mAsyncExecutor = new AsyncExecutor(2);
mMapPosition = new MapPosition();
mEventLayer = new MapEventLayer(this); mEventLayer = new MapEventLayer(this);
mLayers.add(0, mEventLayer); mLayers.add(0, mEventLayer);
} }
public MapEventLayer getEventLayer() { public MapEventLayer getEventLayer() {