some Animator improvements
- fixes annoying zoomTo jumping
This commit is contained in:
parent
5d07d45f86
commit
46878c81b7
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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);
|
public synchronized void animateTo(long duration, GeoPoint geoPoint,
|
||||||
|
double scale, boolean relative) {
|
||||||
|
mMap.getMapPosition(mStartPos);
|
||||||
|
|
||||||
if (relative) {
|
if (relative)
|
||||||
if (mAnimEnd > 0 && (mState & ANIM_SCALE) != 0)
|
scale = mStartPos.scale * scale;
|
||||||
scale = mDeltaPos.scale * scale;
|
|
||||||
else
|
|
||||||
scale = mPos.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* remember current map position */
|
if (millisLeft <= 0)
|
||||||
v.getMapPosition(mPos);
|
animCancel();
|
||||||
|
|
||||||
|
/* remember current map position */
|
||||||
|
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,9 +252,13 @@ public class Animator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doScale(double newScale) {
|
private void doScale(Viewport v, float adv) {
|
||||||
mViewport.scaleMap((float) (newScale / mPos.scale),
|
double newScale;
|
||||||
(float) mPivot.x, (float) mPivot.y);
|
|
||||||
|
newScale = mStartPos.scale + (mDeltaPos.scale * adv);
|
||||||
|
|
||||||
|
v.scaleMap((float) (newScale / mCurPos.scale),
|
||||||
|
(float) mPivot.x, (float) mPivot.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void cancel() {
|
public synchronized void cancel() {
|
||||||
|
@ -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() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user