MapViewPosition: add method to animate zoom with pivot point

- improve animation code
- make scroll-wheel zoom use pointer position as pivot point
This commit is contained in:
Hannes Janetzek 2013-08-10 11:04:28 +02:00
parent 31e2f57b13
commit a86abf2921
2 changed files with 135 additions and 113 deletions

View File

@ -138,7 +138,6 @@ public class GdxMap implements ApplicationListener {
p.setPosition(53.08, 8.83);
//p.setPosition(0.0, 0.0);
mMapView.setMapPosition(p);
mMapRenderer.onSurfaceCreated();
mMapRenderer.onSurfaceChanged(w, h);
@ -181,7 +180,6 @@ public class GdxMap implements ApplicationListener {
@Override
public void resize(int w, int h) {
Log.d("", "resize " + w + "x" + h);
mWidth = w;
mHeight = h;
@ -293,7 +291,7 @@ public class GdxMap implements ApplicationListener {
mMapView.render();
break;
}
return true;
return false;
}
@Override
@ -376,21 +374,20 @@ public class GdxMap implements ApplicationListener {
@Override
public boolean scrolled(int amount) {
//float fx = mPosX - mMapView.getWidth() / 2;
//float fy = mPosY - mMapView.getHeight() / 2;
if (amount > 0) {
mMapPosition.animateZoom(0.9f, 150);
//mMapPosition.scaleMap(0.9f, fx, fy);
mMapPosition.animateZoom(150, 0.8f, 0, 0);
} else {
mMapPosition.animateZoom(1.1f, 150);
//mMapPosition.scaleMap(1.1f, fx, fy);
float fx = mPosX - mMapView.getWidth() / 2;
float fy = mPosY - mMapView.getHeight() / 2;
mMapPosition.animateZoom(150, 1.25f, fx, fy);
}
mMapView.updateMap(false);
return true;
}
}
class ViewController implements GestureListener {

View File

@ -15,7 +15,6 @@
*/
package org.oscim.view;
import org.oscim.backend.Log;
import org.oscim.core.BoundingBox;
import org.oscim.core.GeoPoint;
import org.oscim.core.MapPosition;
@ -26,7 +25,6 @@ import org.oscim.core.Tile;
import org.oscim.utils.FastMath;
import org.oscim.utils.Matrix4;
public class MapViewPosition {
private static final String TAG = MapViewPosition.class.getName();
@ -37,9 +35,6 @@ public class MapViewPosition {
public final static double MAX_SCALE = (1 << MAX_ZOOMLEVEL);
public final static double MIN_SCALE = (1 << MIN_ZOOMLEVEL);
// TODO: remove. only used for animations
public final static int ABS_ZOOMLEVEL = 22;
private final static float MAX_ANGLE = 65;
private final MapView mMapView;
@ -83,7 +78,7 @@ public class MapViewPosition {
// scale map plane at VIEW_DISTANCE to near plane
public final static float VIEW_SCALE = (VIEW_NEAR / VIEW_DISTANCE) * 0.5f;
MapViewPosition(MapView map){
MapViewPosition(MapView map) {
mMapView = map;
mAbsScale = 4;
@ -384,6 +379,11 @@ public class MapViewPosition {
// 4. translate to VIEW_DISTANCE
// 5. apply projection
while (mRotation > 360)
mRotation -= 360;
while (mRotation < 0)
mRotation += 360;
mRotMatrix.setRotation(mRotation, 0, 0, 1);
// tilt map
@ -444,38 +444,12 @@ public class MapViewPosition {
updatePosition();
}
private synchronized void moveAbs(double x, double y) {
double f = Tile.SIZE << ABS_ZOOMLEVEL;
mAbsX = x / f;
mAbsY = y / f;
// clamp latitude
mAbsY = FastMath.clamp(mAbsY, 0, 1);
// wrap longitude
while (mAbsX > 1)
mAbsX -= 1;
while (mAbsX < 0)
mAbsX += 1;
updatePosition();
}
private PointD applyRotation(float mx, float my) {
//if (mMapView.mRotationEnabled || mMapView.mCompassEnabled) {
double rad = Math.toRadians(mRotation);
double rcos = Math.cos(rad);
double rsin = Math.sin(rad);
float x = (float) (mx * rcos + my * rsin);
float y = (float) (mx * -rsin + my * rcos);
mx = x;
my = y;
//}
mMovePoint.x = mx;
mMovePoint.y = my;
double rad = Math.toRadians(mRotation);
double rcos = Math.cos(rad);
double rsin = Math.sin(rad);
mMovePoint.x = mx * rcos + my * rsin;
mMovePoint.y = mx * -rsin + my * rcos;
return mMovePoint;
}
@ -535,6 +509,7 @@ public class MapViewPosition {
}
public synchronized void setRotation(float f) {
mRotation = f;
updateMatrix();
}
@ -567,10 +542,13 @@ public class MapViewPosition {
}
public synchronized void setMapPosition(MapPosition mapPosition) {
setZoomLevelLimit(mapPosition.zoomLevel);
mAbsScale = FastMath.clamp(mapPosition.scale, MIN_SCALE, MAX_SCALE);
mAbsX = mapPosition.x;
mAbsY = mapPosition.y;
mTilt = mapPosition.tilt;
mRotation = mapPosition.angle;
updatePosition();
updateMatrix();
}
synchronized void setMapCenter(GeoPoint geoPoint) {
@ -578,12 +556,12 @@ public class MapViewPosition {
updatePosition();
}
private void setZoomLevelLimit(int zoomLevel) {
mAbsScale = FastMath.clamp(1 << zoomLevel, MIN_SCALE, MAX_SCALE);
}
/************************************************************************/
// TODO move to MapAnimator
class AnimState {
}
private double mScrollX;
private double mScrollY;
@ -597,73 +575,98 @@ public class MapViewPosition {
private float mStartRotation;
private float mDuration = 500;
private final static double LOG4 = Math.log(4);
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 minimum scale at which the bbox is completely visible
// 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 aspect = (Math.min(mWidth, mHeight) / Tile.SIZE);
double z = Math.min(
-LOG4 * Math.log(dx) + aspect,
-LOG4 * Math.log(dy) + aspect);
double zx = mWidth / (dx * Tile.SIZE);
double zy = mHeight / (dy * Tile.SIZE);
double newScale = Math.min(zx, zy);
double newScale = Math.pow(2, z);
//Log.d(TAG, "scale to " + bbox + " " + newScale + " " + mAbsScale
// + " " + FastMath.log2((int) newScale));
newScale = FastMath.clamp(newScale, MIN_SCALE, 1 << ABS_ZOOMLEVEL);
animateTo(500, bbox.getCenterPoint(), newScale, false);
}
float scale = (float) (newScale / mAbsScale);
public synchronized void animateTo(long duration, GeoPoint geoPoint, double scale,
boolean relative) {
Log.d(TAG, "scale to " + bbox + " " + z + " " + newScale + " " + mAbsScale
+ " " + FastMath.log2((int) newScale) + " " + scale);
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;
double f = Tile.SIZE << ABS_ZOOMLEVEL;
mStartX = mAbsX * f;
mStartY = mAbsY * f;
mStartX = mAbsX;
mStartY = mAbsY;
GeoPoint geoPoint = bbox.getCenterPoint();
mEndX = MercatorProjection.longitudeToX(geoPoint.getLongitude()) * f;
mEndY = MercatorProjection.latitudeToY(geoPoint.getLatitude()) * f;
mEndX = MercatorProjection.longitudeToX(geoPoint.getLongitude());
mEndY = MercatorProjection.latitudeToY(geoPoint.getLatitude());
mEndX -= mStartX;
mEndY -= mStartY;
mAnimMove = true;
mAnimScale = true;
mAnimFling = false;
animStart(500);
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) {
double f = Tile.SIZE << ABS_ZOOMLEVEL;
mStartX = mAbsX * f;
mStartY = mAbsY * f;
mEndX = MercatorProjection.longitudeToX(geoPoint.getLongitude()) * f;
mEndY = MercatorProjection.latitudeToY(geoPoint.getLatitude()) * f;
mEndX -= mStartX;
mEndY -= mStartY;
mAnimMove = true;
mAnimScale = false;
mAnimFling = false;
animStart(300);
animateTo(300, geoPoint, 1, true);
}
private void animStart(float duration) {
@ -675,6 +678,11 @@ public class MapViewPosition {
private void animCancel() {
mAnimEnd = -1;
mEndPos = null;
mAnimScale = false;
mAnimFling = false;
mAnimMove = false;
mAnimPivot = false;
}
synchronized boolean fling(float adv) {
@ -721,24 +729,6 @@ public class MapViewPosition {
animStart(duration);
}
public synchronized void animateZoom(float scale) {
animateZoom(scale, 300);
}
public synchronized void animateZoom(float scale, int duration) {
if (mAnimEnd > 0 && mAnimScale){
mEndScale = (mEndScale + mStartScale) * scale - (mAbsScale);
mStartScale = mAbsScale;
} else{
mStartScale = mAbsScale;
mEndScale = mAbsScale * scale - mAbsScale;
}
mAnimFling = false;
mAnimMove = false;
mAnimScale = true;
animStart(duration);
}
/**
* called by GLRenderer at begin of each frame.
*/
@ -751,18 +741,22 @@ public class MapViewPosition {
if (millisLeft <= 0) {
// set final position
if (mAnimMove) {
moveAbs(mStartX + mEndX, mStartY + mEndY);
if (mEndPos == null)
doMove(mStartX + mEndX, mStartY + mEndY);
else {
setMapCenter(mEndPos);
mEndPos = null;
}
}
if (mAnimScale) {
mAbsScale = mStartScale + mEndScale;
doScale(mFinalScale);
}
updatePosition();
mMapView.updateMap(true);
mAnimEnd = -1;
animCancel();
return;
}
@ -772,15 +766,15 @@ public class MapViewPosition {
if (mAnimScale) {
if (mEndScale > 0)
mAbsScale = mStartScale + (mEndScale * (Math.pow(2, adv) - 1));
doScale(mStartScale + (mEndScale * (Math.pow(2, adv) - 1)));
else
mAbsScale = mStartScale + (mEndScale * adv);
doScale(mStartScale + (mEndScale * adv));
changed = true;
}
if (mAnimMove) {
moveAbs(mStartX + mEndX * adv, mStartY + mEndY * adv);
doMove(mStartX + mEndX * adv, mStartY + mEndY * adv);
changed = true;
}
@ -805,4 +799,35 @@ public class MapViewPosition {
mMapView.render();
}
}
private void doScale(double newScale) {
double scale = mAbsScale;
mAbsScale = newScale;
if (mAnimPivot) {
scale = mAbsScale / scale;
PointD 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();
}
}