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:
parent
31e2f57b13
commit
a86abf2921
@ -138,7 +138,6 @@ public class GdxMap implements ApplicationListener {
|
|||||||
p.setPosition(53.08, 8.83);
|
p.setPosition(53.08, 8.83);
|
||||||
//p.setPosition(0.0, 0.0);
|
//p.setPosition(0.0, 0.0);
|
||||||
mMapView.setMapPosition(p);
|
mMapView.setMapPosition(p);
|
||||||
|
|
||||||
mMapRenderer.onSurfaceCreated();
|
mMapRenderer.onSurfaceCreated();
|
||||||
mMapRenderer.onSurfaceChanged(w, h);
|
mMapRenderer.onSurfaceChanged(w, h);
|
||||||
|
|
||||||
@ -181,7 +180,6 @@ public class GdxMap implements ApplicationListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resize(int w, int h) {
|
public void resize(int w, int h) {
|
||||||
Log.d("", "resize " + w + "x" + h);
|
|
||||||
mWidth = w;
|
mWidth = w;
|
||||||
mHeight = h;
|
mHeight = h;
|
||||||
|
|
||||||
@ -293,7 +291,7 @@ public class GdxMap implements ApplicationListener {
|
|||||||
mMapView.render();
|
mMapView.render();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -376,21 +374,20 @@ public class GdxMap implements ApplicationListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean scrolled(int amount) {
|
public boolean scrolled(int amount) {
|
||||||
//float fx = mPosX - mMapView.getWidth() / 2;
|
|
||||||
//float fy = mPosY - mMapView.getHeight() / 2;
|
|
||||||
|
|
||||||
if (amount > 0) {
|
if (amount > 0) {
|
||||||
mMapPosition.animateZoom(0.9f, 150);
|
|
||||||
//mMapPosition.scaleMap(0.9f, fx, fy);
|
mMapPosition.animateZoom(150, 0.8f, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
mMapPosition.animateZoom(1.1f, 150);
|
float fx = mPosX - mMapView.getWidth() / 2;
|
||||||
//mMapPosition.scaleMap(1.1f, fx, fy);
|
float fy = mPosY - mMapView.getHeight() / 2;
|
||||||
|
|
||||||
|
mMapPosition.animateZoom(150, 1.25f, fx, fy);
|
||||||
}
|
}
|
||||||
mMapView.updateMap(false);
|
mMapView.updateMap(false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewController implements GestureListener {
|
class ViewController implements GestureListener {
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.oscim.view;
|
package org.oscim.view;
|
||||||
|
|
||||||
import org.oscim.backend.Log;
|
|
||||||
import org.oscim.core.BoundingBox;
|
import org.oscim.core.BoundingBox;
|
||||||
import org.oscim.core.GeoPoint;
|
import org.oscim.core.GeoPoint;
|
||||||
import org.oscim.core.MapPosition;
|
import org.oscim.core.MapPosition;
|
||||||
@ -26,7 +25,6 @@ import org.oscim.core.Tile;
|
|||||||
import org.oscim.utils.FastMath;
|
import org.oscim.utils.FastMath;
|
||||||
import org.oscim.utils.Matrix4;
|
import org.oscim.utils.Matrix4;
|
||||||
|
|
||||||
|
|
||||||
public class MapViewPosition {
|
public class MapViewPosition {
|
||||||
private static final String TAG = MapViewPosition.class.getName();
|
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 MAX_SCALE = (1 << MAX_ZOOMLEVEL);
|
||||||
public final static double MIN_SCALE = (1 << MIN_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 static float MAX_ANGLE = 65;
|
||||||
|
|
||||||
private final MapView mMapView;
|
private final MapView mMapView;
|
||||||
@ -83,7 +78,7 @@ public class MapViewPosition {
|
|||||||
// scale map plane at VIEW_DISTANCE to near plane
|
// scale map plane at VIEW_DISTANCE to near plane
|
||||||
public final static float VIEW_SCALE = (VIEW_NEAR / VIEW_DISTANCE) * 0.5f;
|
public final static float VIEW_SCALE = (VIEW_NEAR / VIEW_DISTANCE) * 0.5f;
|
||||||
|
|
||||||
MapViewPosition(MapView map){
|
MapViewPosition(MapView map) {
|
||||||
mMapView = map;
|
mMapView = map;
|
||||||
|
|
||||||
mAbsScale = 4;
|
mAbsScale = 4;
|
||||||
@ -384,6 +379,11 @@ public class MapViewPosition {
|
|||||||
// 4. translate to VIEW_DISTANCE
|
// 4. translate to VIEW_DISTANCE
|
||||||
// 5. apply projection
|
// 5. apply projection
|
||||||
|
|
||||||
|
while (mRotation > 360)
|
||||||
|
mRotation -= 360;
|
||||||
|
while (mRotation < 0)
|
||||||
|
mRotation += 360;
|
||||||
|
|
||||||
mRotMatrix.setRotation(mRotation, 0, 0, 1);
|
mRotMatrix.setRotation(mRotation, 0, 0, 1);
|
||||||
|
|
||||||
// tilt map
|
// tilt map
|
||||||
@ -444,38 +444,12 @@ public class MapViewPosition {
|
|||||||
updatePosition();
|
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) {
|
private PointD applyRotation(float mx, float my) {
|
||||||
|
double rad = Math.toRadians(mRotation);
|
||||||
//if (mMapView.mRotationEnabled || mMapView.mCompassEnabled) {
|
double rcos = Math.cos(rad);
|
||||||
double rad = Math.toRadians(mRotation);
|
double rsin = Math.sin(rad);
|
||||||
double rcos = Math.cos(rad);
|
mMovePoint.x = mx * rcos + my * rsin;
|
||||||
double rsin = Math.sin(rad);
|
mMovePoint.y = mx * -rsin + my * rcos;
|
||||||
float x = (float) (mx * rcos + my * rsin);
|
|
||||||
float y = (float) (mx * -rsin + my * rcos);
|
|
||||||
mx = x;
|
|
||||||
my = y;
|
|
||||||
//}
|
|
||||||
|
|
||||||
mMovePoint.x = mx;
|
|
||||||
mMovePoint.y = my;
|
|
||||||
return mMovePoint;
|
return mMovePoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -535,6 +509,7 @@ public class MapViewPosition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setRotation(float f) {
|
public synchronized void setRotation(float f) {
|
||||||
|
|
||||||
mRotation = f;
|
mRotation = f;
|
||||||
updateMatrix();
|
updateMatrix();
|
||||||
}
|
}
|
||||||
@ -567,10 +542,13 @@ public class MapViewPosition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setMapPosition(MapPosition mapPosition) {
|
public synchronized void setMapPosition(MapPosition mapPosition) {
|
||||||
setZoomLevelLimit(mapPosition.zoomLevel);
|
mAbsScale = FastMath.clamp(mapPosition.scale, MIN_SCALE, MAX_SCALE);
|
||||||
mAbsX = mapPosition.x;
|
mAbsX = mapPosition.x;
|
||||||
mAbsY = mapPosition.y;
|
mAbsY = mapPosition.y;
|
||||||
|
mTilt = mapPosition.tilt;
|
||||||
|
mRotation = mapPosition.angle;
|
||||||
updatePosition();
|
updatePosition();
|
||||||
|
updateMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void setMapCenter(GeoPoint geoPoint) {
|
synchronized void setMapCenter(GeoPoint geoPoint) {
|
||||||
@ -578,12 +556,12 @@ public class MapViewPosition {
|
|||||||
updatePosition();
|
updatePosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setZoomLevelLimit(int zoomLevel) {
|
|
||||||
mAbsScale = FastMath.clamp(1 << zoomLevel, MIN_SCALE, MAX_SCALE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
// TODO move to MapAnimator
|
// TODO move to MapAnimator
|
||||||
|
class AnimState {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private double mScrollX;
|
private double mScrollX;
|
||||||
private double mScrollY;
|
private double mScrollY;
|
||||||
@ -597,73 +575,98 @@ public class MapViewPosition {
|
|||||||
private float mStartRotation;
|
private float mStartRotation;
|
||||||
|
|
||||||
private float mDuration = 500;
|
private float mDuration = 500;
|
||||||
private final static double LOG4 = Math.log(4);
|
|
||||||
|
|
||||||
private long mAnimEnd = -1;
|
private long mAnimEnd = -1;
|
||||||
|
|
||||||
private boolean mAnimMove;
|
private boolean mAnimMove;
|
||||||
private boolean mAnimFling;
|
private boolean mAnimFling;
|
||||||
private boolean mAnimScale;
|
private boolean mAnimScale;
|
||||||
|
private boolean mAnimPivot;
|
||||||
|
private GeoPoint mEndPos;
|
||||||
|
private double mFinalScale;
|
||||||
|
|
||||||
public synchronized void animateTo(BoundingBox bbox) {
|
public synchronized void animateTo(BoundingBox bbox) {
|
||||||
|
// calculate the maximum scale at which the bbox is completely visible
|
||||||
// calculate the minimum scale at which the bbox is completely visible
|
|
||||||
double dx = Math.abs(MercatorProjection.longitudeToX(bbox.getMaxLongitude())
|
double dx = Math.abs(MercatorProjection.longitudeToX(bbox.getMaxLongitude())
|
||||||
- MercatorProjection.longitudeToX(bbox.getMinLongitude()));
|
- MercatorProjection.longitudeToX(bbox.getMinLongitude()));
|
||||||
|
|
||||||
double dy = Math.abs(MercatorProjection.latitudeToY(bbox.getMinLatitude())
|
double dy = Math.abs(MercatorProjection.latitudeToY(bbox.getMinLatitude())
|
||||||
- MercatorProjection.latitudeToY(bbox.getMaxLatitude()));
|
- MercatorProjection.latitudeToY(bbox.getMaxLatitude()));
|
||||||
|
|
||||||
double aspect = (Math.min(mWidth, mHeight) / Tile.SIZE);
|
double zx = mWidth / (dx * Tile.SIZE);
|
||||||
double z = Math.min(
|
double zy = mHeight / (dy * Tile.SIZE);
|
||||||
-LOG4 * Math.log(dx) + aspect,
|
double newScale = Math.min(zx, zy);
|
||||||
-LOG4 * Math.log(dy) + aspect);
|
|
||||||
|
|
||||||
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
|
if (relative) {
|
||||||
+ " " + FastMath.log2((int) newScale) + " " + scale);
|
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;
|
mEndScale = mAbsScale * scale - mAbsScale;
|
||||||
mStartScale = mAbsScale;
|
mStartScale = mAbsScale;
|
||||||
mStartRotation = mRotation;
|
mStartRotation = mRotation;
|
||||||
|
|
||||||
double f = Tile.SIZE << ABS_ZOOMLEVEL;
|
mStartX = mAbsX;
|
||||||
mStartX = mAbsX * f;
|
mStartY = mAbsY;
|
||||||
mStartY = mAbsY * f;
|
|
||||||
|
|
||||||
GeoPoint geoPoint = bbox.getCenterPoint();
|
mEndX = MercatorProjection.longitudeToX(geoPoint.getLongitude());
|
||||||
|
mEndY = MercatorProjection.latitudeToY(geoPoint.getLatitude());
|
||||||
mEndX = MercatorProjection.longitudeToX(geoPoint.getLongitude()) * f;
|
|
||||||
mEndY = MercatorProjection.latitudeToY(geoPoint.getLatitude()) * f;
|
|
||||||
mEndX -= mStartX;
|
mEndX -= mStartX;
|
||||||
mEndY -= mStartY;
|
mEndY -= mStartY;
|
||||||
mAnimMove = true;
|
mAnimMove = true;
|
||||||
mAnimScale = true;
|
mAnimScale = true;
|
||||||
mAnimFling = false;
|
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) {
|
public synchronized void animateTo(GeoPoint geoPoint) {
|
||||||
double f = Tile.SIZE << ABS_ZOOMLEVEL;
|
animateTo(300, geoPoint, 1, true);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void animStart(float duration) {
|
private void animStart(float duration) {
|
||||||
@ -675,6 +678,11 @@ public class MapViewPosition {
|
|||||||
|
|
||||||
private void animCancel() {
|
private void animCancel() {
|
||||||
mAnimEnd = -1;
|
mAnimEnd = -1;
|
||||||
|
mEndPos = null;
|
||||||
|
mAnimScale = false;
|
||||||
|
mAnimFling = false;
|
||||||
|
mAnimMove = false;
|
||||||
|
mAnimPivot = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized boolean fling(float adv) {
|
synchronized boolean fling(float adv) {
|
||||||
@ -721,24 +729,6 @@ public class MapViewPosition {
|
|||||||
animStart(duration);
|
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.
|
* called by GLRenderer at begin of each frame.
|
||||||
*/
|
*/
|
||||||
@ -751,18 +741,22 @@ public class MapViewPosition {
|
|||||||
if (millisLeft <= 0) {
|
if (millisLeft <= 0) {
|
||||||
// set final position
|
// set final position
|
||||||
if (mAnimMove) {
|
if (mAnimMove) {
|
||||||
moveAbs(mStartX + mEndX, mStartY + mEndY);
|
if (mEndPos == null)
|
||||||
|
doMove(mStartX + mEndX, mStartY + mEndY);
|
||||||
|
else {
|
||||||
|
setMapCenter(mEndPos);
|
||||||
|
mEndPos = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mAnimScale) {
|
if (mAnimScale) {
|
||||||
mAbsScale = mStartScale + mEndScale;
|
doScale(mFinalScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePosition();
|
updatePosition();
|
||||||
mMapView.updateMap(true);
|
mMapView.updateMap(true);
|
||||||
|
|
||||||
mAnimEnd = -1;
|
animCancel();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,15 +766,15 @@ public class MapViewPosition {
|
|||||||
|
|
||||||
if (mAnimScale) {
|
if (mAnimScale) {
|
||||||
if (mEndScale > 0)
|
if (mEndScale > 0)
|
||||||
mAbsScale = mStartScale + (mEndScale * (Math.pow(2, adv) - 1));
|
doScale(mStartScale + (mEndScale * (Math.pow(2, adv) - 1)));
|
||||||
else
|
else
|
||||||
mAbsScale = mStartScale + (mEndScale * adv);
|
doScale(mStartScale + (mEndScale * adv));
|
||||||
|
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mAnimMove) {
|
if (mAnimMove) {
|
||||||
moveAbs(mStartX + mEndX * adv, mStartY + mEndY * adv);
|
doMove(mStartX + mEndX * adv, mStartY + mEndY * adv);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -805,4 +799,35 @@ public class MapViewPosition {
|
|||||||
mMapView.render();
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user