package org.oscim.map;
import org.oscim.core.MapPosition;
import org.oscim.core.Point;
import org.oscim.core.Tile;
import org.oscim.renderer.GLMatrix;
import org.oscim.utils.FastMath;
public class ViewController extends Viewport {
public synchronized void setScreenSize(int width, int height) {
mHeight = height;
mWidth = width;
/* setup projection matrix:
* 0. scale to window coordinates
* 1. translate to VIEW_DISTANCE
* 2. apply projection
* setup inverse projection:
* 0. invert projection
* 1. invert translate to VIEW_DISTANCE */
float ratio = (mHeight / mWidth) * VIEW_SCALE;
float[] tmp = new float[16];
GLMatrix.frustumM(tmp, 0, -VIEW_SCALE, VIEW_SCALE,
ratio, -ratio, VIEW_NEAR, VIEW_FAR);
mProjMatrix.set(tmp);
mTmpMatrix.setTranslation(0, 0, -VIEW_DISTANCE);
mProjMatrix.multiplyRhs(mTmpMatrix);
/* set inverse projection matrix (without scaling) */
mProjMatrix.get(tmp);
GLMatrix.invertM(tmp, 0, tmp, 0);
mProjMatrixInverse.set(tmp);
mProjMatrixUnscaled.copy(mProjMatrix);
/* scale to window coordinates */
mTmpMatrix.setScale(1 / mWidth, 1 / mWidth, 1 / mWidth);
mProjMatrix.multiplyRhs(mTmpMatrix);
updateMatrices();
}
/**
* Moves this Viewport by the given amount of pixels.
*
* @param mx the amount of pixels to move the map horizontally.
* @param my the amount of pixels to move the map vertically.
*/
public synchronized void moveMap(float mx, float my) {
Point p = applyRotation(mx, my);
double tileScale = mPos.scale * Tile.SIZE;
moveTo(mPos.x - p.x / tileScale, mPos.y - p.y / tileScale);
}
/* used by MapAnimator */
void moveTo(double x, double y) {
mPos.x = x;
mPos.y = y;
/* clamp latitude */
mPos.y = FastMath.clamp(mPos.y, 0, 1);
/* wrap longitude */
while (mPos.x > 1)
mPos.x -= 1;
while (mPos.x < 0)
mPos.x += 1;
}
private Point applyRotation(double mx, double my) {
if (mPos.bearing == 0) {
mMovePoint.x = mx;
mMovePoint.y = my;
} else {
double rad = Math.toRadians(mPos.bearing);
double rcos = Math.cos(rad);
double rsin = Math.sin(rad);
mMovePoint.x = mx * rcos + my * rsin;
mMovePoint.y = mx * -rsin + my * rcos;
}
return mMovePoint;
}
/**
* Scale map by scale width center at pivot in pixel relative to
* screen center. Map scale is clamp to MIN_SCALE and MAX_SCALE.
*
* @param scale
* @param pivotX
* @param pivotY
* @return true if scale was changed
*/
public synchronized boolean scaleMap(float scale, float pivotX, float pivotY) {
// just sanitize input
//scale = FastMath.clamp(scale, 0.5f, 2);
if (scale < 0.000001)
return false;
double newScale = mPos.scale * scale;
newScale = FastMath.clamp(newScale, MIN_SCALE, MAX_SCALE);
if (newScale == mPos.scale)
return false;
scale = (float) (newScale / mPos.scale);
mPos.scale = newScale;
if (pivotX != 0 || pivotY != 0)
moveMap(pivotX * (1.0f - scale),
pivotY * (1.0f - scale));
return true;
}
/**
* Rotate map by radians around pivot. Pivot is in pixel relative
* to screen center.
*
* @param radians
* @param pivotX
* @param pivotY
*/
public synchronized void rotateMap(double radians, float pivotX, float pivotY) {
double rsin = Math.sin(radians);
double rcos = Math.cos(radians);
float x = (float) (pivotX - pivotX * rcos + pivotY * rsin);
float y = (float) (pivotY - pivotX * rsin - pivotY * rcos);
moveMap(x, y);
setRotation(mPos.bearing + Math.toDegrees(radians));
}
public synchronized void setRotation(double degree) {
while (degree > 180)
degree -= 360;
while (degree < -180)
degree += 360;
mPos.bearing = (float) degree;
updateMatrices();
}
public synchronized boolean tiltMap(float move) {
return setTilt(mPos.tilt + move);
}
public synchronized boolean setTilt(float tilt) {
tilt = FastMath.clamp(tilt, 0, MAX_TILT);
if (tilt == mPos.tilt)
return false;
mPos.tilt = tilt;
updateMatrices();
return true;
}
public synchronized void setMapPosition(MapPosition mapPosition) {
mPos.scale = FastMath.clamp(mapPosition.scale, MIN_SCALE, MAX_SCALE);
mPos.x = mapPosition.x;
mPos.y = mapPosition.y;
mPos.tilt = mapPosition.tilt;
mPos.bearing = mapPosition.bearing;
updateMatrices();
}
private void updateMatrices() {
/* - view matrix:
* 0. apply rotate
* 1. apply tilt */
mRotationMatrix.setRotation(mPos.bearing, 0, 0, 1);
mTmpMatrix.setRotation(mPos.tilt, 1, 0, 0);
/* apply first rotation, then tilt */
mRotationMatrix.multiplyLhs(mTmpMatrix);
mViewMatrix.copy(mRotationMatrix);
mViewProjMatrix.multiplyMM(mProjMatrix, mViewMatrix);
/* inverse projection matrix: */
/* invert scale */
mUnprojMatrix.setScale(mWidth, mWidth, 1);
/* invert rotation and tilt */
mTmpMatrix.transposeM(mRotationMatrix);
/* (AB)^-1 = B^-1*A^-1, invert scale, tilt and rotation */
mTmpMatrix.multiplyLhs(mUnprojMatrix);
/* (AB)^-1 = B^-1*A^-1, invert projection */
mUnprojMatrix.multiplyMM(mTmpMatrix, mProjMatrixInverse);
}
}