refactor: extract animation stuff from Viewport into MapAnimator

This commit is contained in:
Hannes Janetzek 2013-09-10 23:30:51 +02:00
parent 51c2c5d62a
commit eb1e8b63c5
7 changed files with 394 additions and 402 deletions

View File

@ -237,9 +237,9 @@ public class SearchBox {
if (b.maxLatitudeE6 - b.minLatitudeE6 < 100 && if (b.maxLatitudeE6 - b.minLatitudeE6 < 100 &&
b.maxLongitudeE6 - b.minLongitudeE6 < 100) b.maxLongitudeE6 - b.minLongitudeE6 < 100)
// for small bbox use zoom=16 to get an overview // for small bbox use zoom=16 to get an overview
map.getViewport().animateTo(500, b.getCenterPoint(), 1 << 16, false); map.getAnimator().animateTo(500, b.getCenterPoint(), 1 << 16, false);
else else
map.getViewport().animateTo(b); map.getAnimator().animateTo(b);
if (d instanceof NominatimData && ((NominatimData) d).getWkt() != null) { if (d instanceof NominatimData && ((NominatimData) d).getWkt() != null) {
String wkt = ((NominatimData) d).getWkt(); String wkt = ((NominatimData) d).getWkt();

View File

@ -365,12 +365,12 @@ public class GdxMap implements ApplicationListener {
if (amount > 0) { if (amount > 0) {
mMapPosition.animateZoom(150, 0.8f, 0, 0); mMap.getAnimator().animateZoom(150, 0.8f, 0, 0);
} else { } else {
float fx = mPosX - mMap.getWidth() / 2; float fx = mPosX - mMap.getWidth() / 2;
float fy = mPosY - mMap.getHeight() / 2; float fy = mPosY - mMap.getHeight() / 2;
mMapPosition.animateZoom(150, 1.25f, fx, fy); mMap.getAnimator().animateZoom(150, 1.25f, fx, fy);
} }
mMap.updateMap(false); mMap.updateMap(false);
@ -438,7 +438,7 @@ public class GdxMap implements ApplicationListener {
//Log.d("", "fling " + button + " " + velocityX + "/" + velocityY); //Log.d("", "fling " + button + " " + velocityX + "/" + velocityY);
if (mayFling && button == Buttons.LEFT) { if (mayFling && button == Buttons.LEFT) {
int m = Tile.SIZE * 4; int m = Tile.SIZE * 4;
mMapPosition.animateFling((int) velocityX, (int) velocityY, -m, m, -m, m); mMap.getAnimator().animateFling((int) velocityX, (int) velocityY, -m, m, -m, m);
return true; return true;
} }
return false; return false;

View File

@ -299,7 +299,7 @@ public class MapEventLayer extends InputLayer {
int w = Tile.SIZE * 3; int w = Tile.SIZE * 3;
int h = Tile.SIZE * 3; int h = Tile.SIZE * 3;
mMapPosition.animateFling( mMap.getAnimator().animateFling(
Math.round(velocityX), Math.round(velocityX),
Math.round(velocityY), Math.round(velocityY),
-w, w, -h, h); -w, w, -h, h);

View File

@ -294,8 +294,7 @@ public class GLRenderer {
MapPosition pos = mMapPosition; MapPosition pos = mMapPosition;
synchronized (mViewport) { synchronized (mViewport) {
// update MapPosition mMap.getAnimator().updateAnimation();
mViewport.updateAnimation();
// get current MapPosition // get current MapPosition
changed = mViewport.getMapPosition(pos); changed = mViewport.getMapPosition(pos);

View File

@ -35,6 +35,8 @@ public abstract class Map {
private final Layers mLayers; private final Layers mLayers;
private final Viewport mViewport; private final Viewport mViewport;
private final MapAnimator mAnimator;
private final MapPosition mMapPosition; private final MapPosition mMapPosition;
private final AsyncExecutor mAsyncExecutor; private final AsyncExecutor mAsyncExecutor;
@ -49,11 +51,12 @@ public abstract class Map {
public Map() { public Map() {
mViewport = new Viewport(this); mViewport = new Viewport(this);
mAnimator = new MapAnimator(this, mViewport);
mMapPosition = new MapPosition(); mMapPosition = new MapPosition();
mLayers = new Layers(); mLayers = new Layers();
mAsyncExecutor = new AsyncExecutor(2); mAsyncExecutor = new AsyncExecutor(2);
// FIXME!
mDebugSettings = new DebugSettings(); mDebugSettings = new DebugSettings();
MapTileLoader.setDebugSettings(mDebugSettings); MapTileLoader.setDebugSettings(mDebugSettings);
@ -216,4 +219,8 @@ public abstract class Map {
public BoundingBox getBoundingBox() { public BoundingBox getBoundingBox() {
return mViewport.getViewBox(); return mViewport.getViewBox();
} }
public MapAnimator getAnimator() {
return mAnimator;
}
} }

View File

@ -0,0 +1,274 @@
package org.oscim.view;
import org.oscim.core.BoundingBox;
import org.oscim.core.GeoPoint;
import org.oscim.core.MapPosition;
import org.oscim.core.MercatorProjection;
import org.oscim.core.Point;
import org.oscim.core.Tile;
import org.oscim.utils.FastMath;
// TODO: rewrite
public class MapAnimator {
//private static final String TAG = MapAnimator.class.getName();
public MapAnimator(Map map, Viewport viewport) {
mViewport = viewport;
mMap = map;
}
private final Map mMap;
private final Viewport mViewport;
private final MapPosition mPos = new MapPosition();
private final MapPosition mStartPos = new MapPosition();
private final MapPosition mDeltaPos = new MapPosition();
private final Point mScroll = new Point();
private final Point mPivot = new Point();
private final Point mVelocity = new Point();
private double mScaleBy;
private float mDuration = 500;
private long mAnimEnd = -1;
private boolean mAnimMove;
private boolean mAnimFling;
private boolean mAnimScale;
public synchronized void animateTo(BoundingBox bbox) {
// TODO for large distatance first scale out, then in
// calculate the maximum scale at which the bbox is completely visible
double dx = Math.abs(MercatorProjection.longitudeToX(bbox.getMaxLongitude())
- MercatorProjection.longitudeToX(bbox.getMinLongitude()));
double dy = Math.abs(MercatorProjection.latitudeToY(bbox.getMinLatitude())
- MercatorProjection.latitudeToY(bbox.getMaxLatitude()));
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);
}
public synchronized void animateTo(long duration, GeoPoint geoPoint, double scale,
boolean relative) {
mViewport.getMapPosition(mPos);
if (relative) {
if (mAnimEnd > 0 && mAnimScale)
scale = mDeltaPos.scale * scale;
else
scale = mPos.scale * scale;
}
scale = FastMath.clamp(scale, Viewport.MIN_SCALE, Viewport.MAX_SCALE);
mDeltaPos.scale = scale;
scale = (float) (scale / mPos.scale);
mScaleBy = mPos.scale * scale - mPos.scale;
mStartPos.scale = mPos.scale;
mStartPos.angle = mPos.angle;
mStartPos.x = mPos.x;
mStartPos.y = mPos.y;
mDeltaPos.x = MercatorProjection.longitudeToX(geoPoint.getLongitude());
mDeltaPos.y = MercatorProjection.latitudeToY(geoPoint.getLatitude());
mDeltaPos.x -= mStartPos.x;
mDeltaPos.y -= mStartPos.y;
mAnimMove = true;
mAnimScale = true;
mAnimFling = false;
animStart(duration);
}
public synchronized void animateZoom(long duration, double scale, float pivotX, float pivotY) {
mViewport.getMapPosition(mPos);
if (mAnimEnd > 0 && mAnimScale)
scale = mDeltaPos.scale * scale;
else
scale = mPos.scale * scale;
scale = FastMath.clamp(scale, Viewport.MIN_SCALE, Viewport.MAX_SCALE);
mDeltaPos.scale = scale;
scale = (float) (scale / mPos.scale);
mScaleBy = mPos.scale * scale - mPos.scale;
mStartPos.scale = mPos.scale;
mStartPos.angle = mPos.angle;
mPivot.x = pivotX;
mPivot.y = pivotY;
mAnimScale = true;
mAnimFling = false;
mAnimMove = false;
animStart(duration);
}
public synchronized void animateTo(GeoPoint geoPoint) {
animateTo(300, geoPoint, 1, true);
}
public synchronized void animateFling(int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY) {
if (velocityX * velocityX + velocityY * velocityY < 2048)
return;
mViewport.getMapPosition(mPos);
mScroll.x = 0;
mScroll.y = 0;
float duration = 500;
// pi times thumb..
float flingFactor = (duration / 2500);
mVelocity.x = velocityX * flingFactor;
mVelocity.y = velocityY * flingFactor;
FastMath.clamp(mVelocity.x, minX, maxX);
FastMath.clamp(mVelocity.y, minY, maxY);
mAnimFling = true;
mAnimMove = false;
mAnimScale = false;
animStart(duration);
}
private void animStart(float duration) {
mDuration = duration;
mAnimEnd = System.currentTimeMillis() + (long) duration;
mMap.render();
}
private void animCancel() {
mAnimEnd = -1;
mAnimScale = false;
mAnimFling = false;
mAnimMove = false;
mPivot.x = 0;
mPivot.y = 0;
}
private boolean fling(float adv) {
synchronized (mViewport) {
adv = (float) Math.sqrt(adv);
double dx = mVelocity.x * adv;
double dy = mVelocity.y * adv;
if (dx == 0 && dy == 0)
return false;
mViewport.moveMap((float) (dx - mScroll.x), (float) (dy - mScroll.y));
mScroll.x = dx;
mScroll.y = dy;
}
return true;
}
/**
* called by GLRenderer at begin of each frame.
*/
public void updateAnimation() {
if (mAnimEnd < 0)
return;
long millisLeft = mAnimEnd - System.currentTimeMillis();
synchronized (mViewport) {
// cancel animation when position was changed since last
// update, i.e. when it was modified outside the animator.
if (mViewport.getMapPosition(mPos)) {
animCancel();
return;
}
if (millisLeft <= 0) {
// set final position
if (mAnimMove && !mAnimFling)
mViewport.moveInternal(mStartPos.x + mDeltaPos.x, mStartPos.y + mDeltaPos.y);
if (mAnimScale) {
if (mScaleBy > 0)
doScale(mStartPos.scale + (mScaleBy - 1));
else
doScale(mStartPos.scale + mScaleBy);
}
mMap.updateMap(true);
animCancel();
return;
}
boolean changed = false;
float adv = (1.0f - millisLeft / mDuration);
if (mAnimScale) {
if (mScaleBy > 0)
doScale(mStartPos.scale + (mScaleBy * (Math.pow(2, adv) - 1)));
else
doScale(mStartPos.scale + (mScaleBy * adv));
changed = true;
}
if (mAnimMove) {
mViewport.moveInternal(
mStartPos.x + mDeltaPos.x * adv,
mStartPos.y + mDeltaPos.y * adv);
changed = true;
}
//if (mAnimMove && mAnimScale) {
// mPos.angle = mStartPos.angle * (1 - adv);
// updateMatrix();
//}
if (mAnimFling && fling(adv))
changed = true;
// continue animation
if (changed) {
// inform other layers that position has changed
mMap.updateMap(true);
} else {
// just render next frame
mMap.render();
}
// remember current map position
mViewport.getMapPosition(mPos);
}
}
private void doScale(double newScale) {
mViewport.scaleMap((float) (newScale / mPos.scale), (float)mPivot.x, (float)mPivot.y);
}
}

View File

@ -28,7 +28,6 @@ import org.oscim.utils.Matrix4;
public class Viewport { public class Viewport {
//private static final String TAG = Viewport.class.getName(); //private static final String TAG = Viewport.class.getName();
// needs to fit for int: 2 * 20 * Tile.SIZE
public final static int MAX_ZOOMLEVEL = 20; public final static int MAX_ZOOMLEVEL = 20;
public final static int MIN_ZOOMLEVEL = 2; public final static int MIN_ZOOMLEVEL = 2;
@ -37,24 +36,7 @@ public class Viewport {
private final static float MAX_TILT = 65; private final static float MAX_TILT = 65;
private final Map mMap; private final MapPosition mPos = new MapPosition();
private double mAbsScale;
private double mAbsX;
private double mAbsY;
// mAbsScale * Tile.SIZE
// i.e. size of tile 0/0/0 at current scale in pixel
private double mCurScale;
// mAbsX * mCurScale
private double mCurX;
// mAbsY * mCurScale
private double mCurY;
private float mRotation;
private float mTilt;
private final Matrix4 mProjMatrix = new Matrix4(); private final Matrix4 mProjMatrix = new Matrix4();
private final Matrix4 mProjMatrixI = new Matrix4(); private final Matrix4 mProjMatrixI = new Matrix4();
@ -81,22 +63,13 @@ public class Viewport {
public final static float VIEW_SCALE = (VIEW_NEAR / VIEW_DISTANCE) * 0.5f; public final static float VIEW_SCALE = (VIEW_NEAR / VIEW_DISTANCE) * 0.5f;
Viewport(Map map) { Viewport(Map map) {
mMap = map;
mAbsScale = 4; mPos.scale = 4;
mAbsX = 0.5; mPos.x = 0.5;
mAbsY = 0.5; mPos.y = 0.5;
mRotation = 0; mPos.angle = 0;
mTilt = 0; mPos.tilt = 0;
updatePosition();
}
private void updatePosition() {
mCurScale = mAbsScale * Tile.SIZE;
mCurX = mAbsX * mCurScale;
mCurY = mAbsY * mCurScale;
} }
public void setViewport(int width, int height) { public void setViewport(int width, int height) {
@ -129,25 +102,23 @@ public class Viewport {
*/ */
public synchronized boolean getMapPosition(MapPosition pos) { public synchronized boolean getMapPosition(MapPosition pos) {
int z = FastMath.log2((int) mAbsScale); int z = FastMath.log2((int) mPos.scale);
//z = FastMath.clamp(z, MIN_ZOOMLEVEL, MAX_ZOOMLEVEL);
//float scale = (float) (mAbsScale / (1 << z));
boolean changed = (pos.zoomLevel != z boolean changed = (pos.zoomLevel != z
|| pos.x != mAbsX || pos.x != mPos.x
|| pos.y != mAbsY || pos.y != mPos.y
|| pos.scale != mAbsScale || pos.scale != mPos.scale
|| pos.angle != mRotation || pos.angle != mPos.angle
|| pos.tilt != mTilt); || pos.tilt != mPos.tilt);
pos.angle = mRotation; pos.angle = mPos.angle;
pos.tilt = mTilt; pos.tilt = mPos.tilt;
pos.x = mAbsX; pos.x = mPos.x;
pos.y = mAbsY; pos.y = mPos.y;
pos.scale = mAbsScale; pos.scale = mPos.scale;
// for tiling // handy for tiling
pos.zoomLevel = z; pos.zoomLevel = z;
return changed; return changed;
@ -212,7 +183,7 @@ public class Viewport {
ua = 1; ua = 1;
else { else {
// tilt of the plane (center is kept on x = 0) // tilt of the plane (center is kept on x = 0)
double t = Math.toRadians(mTilt); double t = Math.toRadians(mPos.tilt);
double px = y * Math.sin(t); double px = y * Math.sin(t);
double py = y * Math.cos(t); double py = y * Math.cos(t);
ua = 1 + (px * ry) / (py * cx); ua = 1 + (px * ry) / (py * cx);
@ -240,8 +211,8 @@ public class Viewport {
/** @return the current center point of the MapView. */ /** @return the current center point of the MapView. */
public synchronized GeoPoint getMapCenter() { public synchronized GeoPoint getMapCenter() {
return new GeoPoint(MercatorProjection.toLatitude(mAbsY), return new GeoPoint(MercatorProjection.toLatitude(mPos.y),
MercatorProjection.toLongitude(mAbsX)); MercatorProjection.toLongitude(mPos.x));
} }
/** /**
@ -285,10 +256,15 @@ public class Viewport {
box.maxY = Math.max(box.maxY, coords[i + 1]); box.maxY = Math.max(box.maxY, coords[i + 1]);
} }
box.minX = (mCurX + box.minX) / mCurScale; //updatePosition();
box.maxX = (mCurX + box.maxX) / mCurScale; double cs = mPos.scale * Tile.SIZE;
box.minY = (mCurY + box.minY) / mCurScale; double cx = mPos.x * cs;
box.maxY = (mCurY + box.maxY) / mCurScale; double cy = mPos.y * cs;
box.minX = (cx + box.minX) / cs;
box.maxX = (cx + box.maxX) / cs;
box.minY = (cy + box.minY) / cs;
box.maxY = (cy + box.maxY) / cs;
} }
/** /**
@ -311,8 +287,8 @@ public class Viewport {
out.y = mu[1]; out.y = mu[1];
if (scale != 0) { if (scale != 0) {
out.x *= scale / mAbsScale; out.x *= scale / mPos.scale;
out.y *= scale / mAbsScale; out.y *= scale / mPos.scale;
} }
} }
@ -344,11 +320,15 @@ public class Viewport {
unproject(-mx, my, getZ(-my), mu, 0); unproject(-mx, my, getZ(-my), mu, 0);
double dx = mCurX + mu[0]; double cs = mPos.scale * Tile.SIZE;
double dy = mCurY + mu[1]; double cx = mPos.x * cs;
double cy = mPos.y * cs;
dx /= mCurScale; double dx = cx + mu[0];
dy /= mCurScale; double dy = cy + mu[1];
dx /= cs;
dy /= cs;
if (dx > 1) { if (dx > 1) {
while (dx > 1) while (dx > 1)
@ -386,8 +366,13 @@ public class Viewport {
*/ */
public synchronized void project(double x, double y, Point out) { public synchronized void project(double x, double y, Point out) {
mv[0] = (float) (x * mCurScale - mCurX); //updatePosition();
mv[1] = (float) (y * mCurScale - mCurY); double cs = mPos.scale * Tile.SIZE;
double cx = mPos.x * cs;
double cy = mPos.y * cs;
mv[0] = (float) (x * cs - cx);
mv[1] = (float) (y * cs - cy);
mv[2] = 0; mv[2] = 0;
mv[3] = 1; mv[3] = 1;
@ -408,15 +393,15 @@ public class Viewport {
// 4. translate to VIEW_DISTANCE // 4. translate to VIEW_DISTANCE
// 5. apply projection // 5. apply projection
while (mRotation > 360) while (mPos.angle > 360)
mRotation -= 360; mPos.angle -= 360;
while (mRotation < 0) while (mPos.angle < 0)
mRotation += 360; mPos.angle += 360;
mRotMatrix.setRotation(mRotation, 0, 0, 1); mRotMatrix.setRotation(mPos.angle, 0, 0, 1);
// tilt map // tilt map
mTmpMatrix.setRotation(mTilt, 1, 0, 0); mTmpMatrix.setRotation(mPos.tilt, 1, 0, 0);
// apply first rotation, then tilt // apply first rotation, then tilt
mRotMatrix.multiplyMM(mTmpMatrix, mRotMatrix); mRotMatrix.multiplyMM(mTmpMatrix, mRotMatrix);
@ -450,35 +435,42 @@ public class Viewport {
* @param my the amount of pixels to move the map vertically. * @param my the amount of pixels to move the map vertically.
*/ */
public synchronized void moveMap(float mx, float my) { public synchronized void moveMap(float mx, float my) {
// stop animation
animCancel();
Point p = applyRotation(mx, my); Point p = applyRotation(mx, my);
move(p.x, p.y); double tileScale = mPos.scale * Tile.SIZE;
moveBy(p.x / tileScale, p.y / tileScale);
} }
private synchronized void move(double mx, double my) { void moveInternal(double mx, double my){
mAbsX = (mCurX - mx) / mCurScale; Point p = applyRotation(mx, my);
mAbsY = (mCurY - my) / mCurScale; moveBy(p.x, p.y);
}
void moveBy(double mx, double my) {
mPos.x -= mx;
mPos.y -= my;
// clamp latitude // clamp latitude
mAbsY = FastMath.clamp(mAbsY, 0, 1); mPos.y = FastMath.clamp(mPos.y, 0, 1);
// wrap longitude // wrap longitude
while (mAbsX > 1) while (mPos.x > 1)
mAbsX -= 1; mPos.x -= 1;
while (mAbsX < 0) while (mPos.x < 0)
mAbsX += 1; mPos.x += 1;
updatePosition();
} }
private Point applyRotation(float mx, float my) { Point applyRotation(double mx, double my) {
double rad = Math.toRadians(mRotation); if (mPos.angle == 0) {
double rcos = Math.cos(rad); mMovePoint.x = mx;
double rsin = Math.sin(rad); mMovePoint.y = my;
mMovePoint.x = mx * rcos + my * rsin; } else {
mMovePoint.y = mx * -rsin + my * rcos; double rad = Math.toRadians(mPos.angle);
double rcos = Math.cos(rad);
double rsin = Math.sin(rad);
mMovePoint.x = mx * rcos + my * rsin;
mMovePoint.y = mx * -rsin + my * rcos;
}
return mMovePoint; return mMovePoint;
} }
@ -489,28 +481,25 @@ public class Viewport {
* @return true if scale was changed * @return true if scale was changed
*/ */
public synchronized boolean scaleMap(float scale, float pivotX, float pivotY) { public synchronized boolean scaleMap(float scale, float pivotX, float pivotY) {
// stop animation
animCancel();
// just sanitize input // just sanitize input
scale = FastMath.clamp(scale, 0.5f, 2); //scale = FastMath.clamp(scale, 0.5f, 2);
if (scale < 0.000001)
return false;
double newScale = mAbsScale * scale; double newScale = mPos.scale * scale;
newScale = FastMath.clamp(newScale, MIN_SCALE, MAX_SCALE); newScale = FastMath.clamp(newScale, MIN_SCALE, MAX_SCALE);
if (newScale == mAbsScale) if (newScale == mPos.scale)
return false; return false;
scale = (float) (newScale / mAbsScale); scale = (float) (newScale / mPos.scale);
mAbsScale = newScale; mPos.scale = newScale;
if (pivotX != 0 || pivotY != 0) if (pivotX != 0 || pivotY != 0)
moveMap(pivotX * (1.0f - scale), moveMap(pivotX * (1.0f - scale),
pivotY * (1.0f - scale)); pivotY * (1.0f - scale));
else
updatePosition();
return true; return true;
} }
@ -519,341 +508,64 @@ public class Viewport {
* rotate map around pivot cx,cy * rotate map around pivot cx,cy
* *
* @param radians ... * @param radians ...
* @param cx ... * @param pivotX ...
* @param cy ... * @param pivotY ...
*/ */
public synchronized void rotateMap(double radians, float cx, float cy) { public synchronized void rotateMap(double radians, float pivotX, float pivotY) {
double rsin = Math.sin(radians); double rsin = Math.sin(radians);
double rcos = Math.cos(radians); double rcos = Math.cos(radians);
float x = (float) (cx * rcos + cy * -rsin - cx); float x = (float) (pivotX * rcos + pivotY * -rsin - pivotX);
float y = (float) (cx * rsin + cy * rcos - cy); float y = (float) (pivotX * rsin + pivotY * rcos - pivotY);
moveMap(x, y); moveMap(x, y);
mRotation += Math.toDegrees(radians); mPos.angle += Math.toDegrees(radians);
updateMatrix(); updateMatrix();
} }
public synchronized void setRotation(float f) { public synchronized void setRotation(float f) {
mRotation = f; mPos.angle = f;
updateMatrix(); updateMatrix();
} }
public synchronized boolean tiltMap(float move) { public synchronized boolean tiltMap(float move) {
return setTilt(mTilt + move); return setTilt(mPos.tilt + move);
} }
public synchronized boolean setTilt(float tilt) { public synchronized boolean setTilt(float tilt) {
tilt = FastMath.clamp(tilt, 0, MAX_TILT); tilt = FastMath.clamp(tilt, 0, MAX_TILT);
if (tilt == mTilt) if (tilt == mPos.tilt)
return false; return false;
mTilt = tilt; mPos.tilt = tilt;
updateMatrix(); updateMatrix();
return true; return true;
} }
public synchronized float getTilt() { public synchronized float getTilt() {
return mTilt; return mPos.tilt;
} }
private void setMapCenter(double latitude, double longitude) { private void setMapCenter(double latitude, double longitude) {
latitude = MercatorProjection.limitLatitude(latitude); latitude = MercatorProjection.limitLatitude(latitude);
longitude = MercatorProjection.limitLongitude(longitude); longitude = MercatorProjection.limitLongitude(longitude);
mAbsX = MercatorProjection.longitudeToX(longitude); mPos.x = MercatorProjection.longitudeToX(longitude);
mAbsY = MercatorProjection.latitudeToY(latitude); mPos.y = MercatorProjection.latitudeToY(latitude);
} }
public synchronized void setMapPosition(MapPosition mapPosition) { public synchronized void setMapPosition(MapPosition mapPosition) {
mAbsScale = FastMath.clamp(mapPosition.scale, MIN_SCALE, MAX_SCALE); mPos.scale = FastMath.clamp(mapPosition.scale, MIN_SCALE, MAX_SCALE);
mAbsX = mapPosition.x; mPos.x = mapPosition.x;
mAbsY = mapPosition.y; mPos.y = mapPosition.y;
mTilt = mapPosition.tilt; mPos.tilt = mapPosition.tilt;
mRotation = mapPosition.angle; mPos.angle = mapPosition.angle;
updatePosition();
updateMatrix(); updateMatrix();
} }
synchronized void setMapCenter(GeoPoint geoPoint) { synchronized void setMapCenter(GeoPoint geoPoint) {
setMapCenter(geoPoint.getLatitude(), geoPoint.getLongitude()); setMapCenter(geoPoint.getLatitude(), geoPoint.getLongitude());
updatePosition();
}
/************************************************************************/
// TODO move to MapAnimator
class AnimState {
}
private double mScrollX;
private double mScrollY;
private double mStartX;
private double mStartY;
private double mEndX;
private double mEndY;
private double mStartScale;
private double mEndScale;
private float mStartRotation;
private float mDuration = 500;
private long mAnimEnd = -1;
private boolean mAnimMove;
private boolean mAnimFling;
private boolean mAnimScale;
private boolean mAnimPivot;
private GeoPoint mEndPos;
private double mFinalScale;
public synchronized void animateTo(BoundingBox bbox) {
// calculate the maximum scale at which the bbox is completely visible
double dx = Math.abs(MercatorProjection.longitudeToX(bbox.getMaxLongitude())
- MercatorProjection.longitudeToX(bbox.getMinLongitude()));
double dy = Math.abs(MercatorProjection.latitudeToY(bbox.getMinLatitude())
- MercatorProjection.latitudeToY(bbox.getMaxLatitude()));
double zx = mWidth / (dx * Tile.SIZE);
double zy = mHeight / (dy * Tile.SIZE);
double newScale = Math.min(zx, zy);
//Log.d(TAG, "scale to " + bbox + " " + newScale + " " + mAbsScale
// + " " + FastMath.log2((int) newScale));
animateTo(500, bbox.getCenterPoint(), newScale, false);
}
public synchronized void animateTo(long duration, GeoPoint geoPoint, double scale,
boolean relative) {
if (relative) {
if (mAnimEnd > 0 && mAnimScale)
scale = mFinalScale * scale;
else
scale = mAbsScale * scale;
}
scale = FastMath.clamp(scale, MIN_SCALE, MAX_SCALE);
mFinalScale = scale;
scale = (float) (scale / mAbsScale);
mEndScale = mAbsScale * scale - mAbsScale;
mStartScale = mAbsScale;
mStartRotation = mRotation;
mStartX = mAbsX;
mStartY = mAbsY;
mEndX = MercatorProjection.longitudeToX(geoPoint.getLongitude());
mEndY = MercatorProjection.latitudeToY(geoPoint.getLatitude());
mEndX -= mStartX;
mEndY -= mStartY;
mAnimMove = true;
mAnimScale = true;
mAnimFling = false;
mEndPos = geoPoint;
animStart(duration);
}
public synchronized void animateZoom(long duration, double scale, double pivotX, double pivotY) {
if (mAnimEnd > 0 && mAnimScale)
scale = mFinalScale * scale;
else
scale = mAbsScale * scale;
scale = FastMath.clamp(scale, MIN_SCALE, MAX_SCALE);
mFinalScale = scale;
scale = (float) (scale / mAbsScale);
mEndScale = mAbsScale * scale - mAbsScale;
mStartScale = mAbsScale;
mStartRotation = mRotation;
mScrollX = pivotX;
mScrollY = pivotY;
mAnimScale = true;
mAnimPivot = (pivotX != 0 || pivotY != 0);
mAnimFling = false;
mAnimMove = false;
animStart(duration);
}
public synchronized void animateTo(GeoPoint geoPoint) {
animateTo(300, geoPoint, 1, true);
}
private void animStart(float duration) {
mDuration = duration;
mAnimEnd = System.currentTimeMillis() + (long) duration;
mMap.render();
}
private void animCancel() {
mAnimEnd = -1;
mEndPos = null;
mAnimScale = false;
mAnimFling = false;
mAnimMove = false;
mAnimPivot = false;
}
synchronized boolean fling(float adv) {
adv = (float) Math.sqrt(adv);
float dx = mVelocityX * adv;
float dy = mVelocityY * adv;
if (dx != 0 || dy != 0) {
Point p = applyRotation((float) (dx - mScrollX), (float) (dy - mScrollY));
move(p.x, p.y);
mScrollX = dx;
mScrollY = dy;
}
return true;
}
private float mVelocityX;
private float mVelocityY;
public synchronized void animateFling(int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY) {
if (velocityX * velocityX + velocityY * velocityY < 2048)
return;
mScrollX = 0;
mScrollY = 0;
float duration = 500;
// pi times thumb..
float flingFactor = (duration / 2500);
mVelocityX = velocityX * flingFactor;
mVelocityY = velocityY * flingFactor;
FastMath.clamp(mVelocityX, minX, maxX);
FastMath.clamp(mVelocityY, minY, maxY);
mAnimFling = true;
mAnimMove = false;
mAnimScale = false;
animStart(duration);
}
/**
* called by GLRenderer at begin of each frame.
*/
public void updateAnimation() {
if (mAnimEnd < 0)
return;
long millisLeft = mAnimEnd - System.currentTimeMillis();
if (millisLeft <= 0) {
// set final position
if (mAnimMove) {
if (mEndPos == null)
doMove(mStartX + mEndX, mStartY + mEndY);
else {
setMapCenter(mEndPos);
mEndPos = null;
}
}
if (mAnimScale) {
doScale(mFinalScale);
}
updatePosition();
mMap.updateMap(true);
animCancel();
return;
}
boolean changed = false;
float adv = (1.0f - millisLeft / mDuration);
if (mAnimScale) {
if (mEndScale > 0)
doScale(mStartScale + (mEndScale * (Math.pow(2, adv) - 1)));
else
doScale(mStartScale + (mEndScale * adv));
changed = true;
}
if (mAnimMove) {
doMove(mStartX + mEndX * adv, mStartY + mEndY * adv);
changed = true;
}
if (mAnimMove && mAnimScale) {
mRotation = mStartRotation * (1 - adv);
updateMatrix();
}
if (changed) {
updatePosition();
}
if (mAnimFling && fling(adv))
changed = true;
// continue animation
if (changed) {
// inform other layers that position has changed
mMap.updateMap(true);
} else {
// just render next frame
mMap.render();
}
}
private void doScale(double newScale) {
double scale = mAbsScale;
mAbsScale = newScale;
if (mAnimPivot) {
scale = mAbsScale / scale;
Point p = applyRotation(
(float) (mScrollX * (1.0 - scale)),
(float) (mScrollY * (1.0 - scale)));
move(p.x, p.y);
}
}
private void doMove(double x, double y) {
mAbsX = x;
mAbsY = y;
// clamp latitude
mAbsY = FastMath.clamp(mAbsY, 0, 1);
// wrap longitude
while (mAbsX > 1)
mAbsX -= 1;
while (mAbsX < 0)
mAbsX += 1;
updatePosition();
} }
} }