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;
}
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.
*/

View File

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

View File

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