All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.oscim.map.Animator2 Maven / Gradle / Ivy
/*
* Copyright 2013 Hannes Janetzek
* Copyright 2016 Stephan Leuschner
* Copyright 2016 devemux86
* Copyright 2016 Izumi Kawashima
* Copyright 2017 Wolfgang Schramm
* Copyright 2018 Gustl22
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see .
*/
package org.oscim.map;
import org.oscim.backend.CanvasAdapter;
import org.oscim.core.Point;
import org.oscim.core.Tile;
import org.oscim.renderer.MapRenderer;
import org.oscim.utils.ThreadUtils;
import org.oscim.utils.animation.DragForce;
import org.oscim.utils.animation.Easing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.oscim.utils.FastMath.clamp;
public class Animator2 extends Animator {
private static final Logger log = LoggerFactory.getLogger(Animator2.class);
private static final int ANIM_KINETIC = 1 << 5;
/**
* The minimum changes that are pleasant for users.
*/
private static final float DEFAULT_MIN_VISIBLE_CHANGE_PIXELS = 0.6f;
private static final float DEFAULT_MIN_VISIBLE_CHANGE_RADIAN = 0.001f;
private static final float DEFAULT_MIN_VISIBLE_CHANGE_SCALE = 1f;
/**
* The friction scalar for fling movements (1 as base).
*/
public static float FLING_FRICTION_MOVE = 1.0f;
/**
* The friction scalar for fling rotations (1 as base).
*/
public static float FLING_FRICTION_ROTATE = 1.2f;
/**
* The friction scalar for fling scales (1 as base).
*/
public static float FLING_FRICTION_SCALE = 1.2f;
private final DragForce mFlingRotateForce = new DragForce();
private final DragForce mFlingScaleForce = new DragForce();
private final DragForce mFlingScrollForce = new DragForce();
private final Point mMovePoint = new Point();
private final Point mScrollRatio = new Point();
private long mFrameStart = -1;
private float mScrollDet2D = 1f;
public Animator2(Map map) {
super(map);
// Init fling force thresholds
mFlingRotateForce.setValueThreshold(DEFAULT_MIN_VISIBLE_CHANGE_RADIAN);
mFlingScrollForce.setValueThreshold(DEFAULT_MIN_VISIBLE_CHANGE_PIXELS);
mFlingScaleForce.setValueThreshold(DEFAULT_MIN_VISIBLE_CHANGE_SCALE);
}
/**
* Animates a physical fling for rotations.
*
* @param angularVelocity angular velocity in radians
*/
public void animateFlingRotate(float angularVelocity, float pivotX, float pivotY) {
ThreadUtils.assertMainThread();
float flingFactor = -0.25f; // Can be changed but should be standardized for all callers
angularVelocity *= flingFactor;
mFlingRotateForce.setFrictionScalar(FLING_FRICTION_ROTATE);
mFlingRotateForce.setValueAndVelocity(0f, angularVelocity);
if (!isActive()) {
mMap.getMapPosition(mStartPos);
mPivot.x = pivotX;
mPivot.y = pivotY;
animFlingStart(ANIM_ROTATE);
} else {
mState |= ANIM_ROTATE;
}
}
/**
* Animates a physical fling for scrolls.
*
* @param velocityX the x velocity depends on screen resolution
* @param velocityY the y velocity depends on screen resolution
*/
public void animateFlingScroll(float velocityX, float velocityY,
int xmin, int xmax, int ymin, int ymax) {
ThreadUtils.assertMainThread();
if (velocityX * velocityX + velocityY * velocityY < 2048)
return;
float flingFactor = 2.0f; // Can be changed but should be standardized for all callers
float screenFactor = CanvasAdapter.DEFAULT_DPI / CanvasAdapter.dpi;
velocityX *= screenFactor * flingFactor;
velocityY *= screenFactor * flingFactor;
velocityX = clamp(velocityX, xmin, xmax);
velocityY = clamp(velocityY, ymin, ymax);
float sumVelocity = Math.abs(velocityX) + Math.abs(velocityY);
mScrollRatio.x = velocityX / sumVelocity;
mScrollRatio.y = velocityY / sumVelocity;
mScrollDet2D = (float) (mScrollRatio.x * mScrollRatio.x + mScrollRatio.y * mScrollRatio.y);
mFlingScrollForce.setFrictionScalar(FLING_FRICTION_MOVE);
mFlingScrollForce.setValueAndVelocity(0f, (float) Math.sqrt(velocityX * velocityX + velocityY * velocityY));
if (!isActive()) {
mMap.getMapPosition(mStartPos);
animFlingStart(ANIM_MOVE);
} else {
mState |= ANIM_MOVE;
}
}
/**
* Animates a physical fling for zooms.
*
* @param scaleVelocity the scale velocity depends on screen resolution
*/
public void animateFlingZoom(float scaleVelocity, float pivotX, float pivotY) {
ThreadUtils.assertMainThread();
float flingFactor = -1.0f; // Can be changed but should be standardized for all callers
float screenFactor = CanvasAdapter.DEFAULT_DPI / CanvasAdapter.dpi;
scaleVelocity *= flingFactor * screenFactor;
mFlingScaleForce.setFrictionScalar(FLING_FRICTION_SCALE);
mFlingScaleForce.setValueAndVelocity(0f, scaleVelocity);
if (!isActive()) {
mMap.getMapPosition(mStartPos);
mPivot.x = pivotX;
mPivot.y = pivotY;
animFlingStart(ANIM_SCALE);
} else {
mState |= ANIM_SCALE;
}
}
private void animFlingStart(int state) {
if (!isActive())
mMap.events.fire(Map.ANIM_START, mMap.mMapPosition);
mCurPos.copy(mStartPos);
mState |= ANIM_FLING | state;
mFrameStart = MapRenderer.frametime; // CurrentTimeMillis would cause negative delta
mMap.render();
}
/**
* Alternative implementation of Animator's animateFling
.
* Uses scheme of predictable animations using mDeltaPos.
*
* @param velocityX the x velocity depends on screen resolution
* @param velocityY the y velocity depends on screen resolution
*/
public void kineticScroll(float velocityX, float velocityY,
int xmin, int xmax, int ymin, int ymax) {
ThreadUtils.assertMainThread();
if (velocityX * velocityX + velocityY * velocityY < 2048)
return;
mMap.getMapPosition(mStartPos);
float duration = 500;
float screenFactor = CanvasAdapter.DEFAULT_DPI / CanvasAdapter.dpi;
velocityX = velocityX * screenFactor;
velocityY = velocityY * screenFactor;
velocityX = clamp(velocityX, xmin, xmax);
velocityY = clamp(velocityY, ymin, ymax);
if (Float.isNaN(velocityX) || Float.isNaN(velocityY)) {
log.debug("fling NaN!");
return;
}
double tileScale = mStartPos.scale * Tile.SIZE;
ViewController.applyRotation(-velocityX, -velocityY, mStartPos.bearing, mMovePoint);
mDeltaPos.setX(mMovePoint.x / tileScale);
mDeltaPos.setY(mMovePoint.y / tileScale);
animStart(duration, ANIM_KINETIC | ANIM_MOVE, Easing.Type.SINE_OUT);
}
/**
* called by MapRenderer at begin of each frame.
*/
@Override
void updateAnimation() {
if (mState == ANIM_NONE)
return;
ViewController v = mMap.viewport();
/* cancel animation when position was changed since last
* update, i.e. when it was modified outside the animator. */
if (v.getMapPosition(mCurPos)) {
log.debug("cancel anim - changed");
cancel();
return;
}
final long currentFrametime = MapRenderer.frametime;
if ((mState & ANIM_FLING) == 0) {
// Do predicted animations
long millisLeft = mAnimEnd - currentFrametime;
float adv = clamp(1.0f - millisLeft / mDuration, 1E-6f, 1);
// Avoid redundant calculations in case of linear easing
if (mEasingType != Easing.Type.LINEAR) {
adv = Easing.ease(0, (long) (adv * Long.MAX_VALUE), Long.MAX_VALUE, mEasingType);
adv = clamp(adv, 0, 1);
}
double scaleAdv = 1;
if ((mState & ANIM_SCALE) != 0) {
scaleAdv = doScale(v, adv);
}
if ((mState & ANIM_KINETIC) != 0) {
// Reduce value to simulate kinetic behaviour
adv = (float) Math.sqrt(adv);
}
if ((mState & ANIM_MOVE) != 0) {
v.moveTo(mStartPos.x + mDeltaPos.x * (adv / scaleAdv),
mStartPos.y + mDeltaPos.y * (adv / scaleAdv));
}
if ((mState & ANIM_ROTATE) != 0) {
v.setRotation(mStartPos.bearing + mDeltaPos.bearing * adv);
}
if ((mState & ANIM_TILT) != 0) {
v.setTilt(mStartPos.tilt + mDeltaPos.tilt * adv);
}
if (millisLeft <= 0) {
//log.debug("animate END");
cancel();
}
} else {
// Do physical fling animation
long deltaT = currentFrametime - mFrameStart;
mFrameStart = currentFrametime;
if ((mState & ANIM_SCALE) != 0) {
float valueDelta = mFlingScaleForce.updateValueAndVelocity(deltaT) / 1000f;
float velocity = mFlingScaleForce.getVelocity();
if (valueDelta != 0) {
valueDelta = valueDelta > 0 ? valueDelta + 1 : -1 / (valueDelta - 1);
v.scaleMap(valueDelta, (float) mPivot.x, (float) mPivot.y);
}
if (velocity == 0) {
mState &= (~ANIM_SCALE); // End scale mode
}
}
if ((mState & ANIM_MOVE) != 0) {
float valueDelta = mFlingScrollForce.updateValueAndVelocity(deltaT);
float velocity = mFlingScrollForce.getVelocity();
float valFactor = (float) Math.sqrt((valueDelta * valueDelta) / mScrollDet2D);
float dx = (float) mScrollRatio.x * valFactor;
float dy = (float) mScrollRatio.y * valFactor;
if (dx != 0 || dy != 0) {
v.moveMap(dx, dy);
}
if (velocity == 0) {
mState &= (~ANIM_MOVE); // End move mode
}
}
if ((mState & ANIM_ROTATE) != 0) {
float valueDelta = mFlingRotateForce.updateValueAndVelocity(deltaT);
float velocity = mFlingRotateForce.getVelocity();
v.rotateMap(valueDelta, (float) mPivot.x, (float) mPivot.y);
if (velocity == 0) {
mState &= (~ANIM_ROTATE); // End rotate mode
}
}
/*if ((mState & ANIM_TILT) != 0) {
// Do some tilt fling
if(velocity == 0) {
mState &= (~ANIM_TILT); // End tilt mode
}
}*/
if ((mState & (ANIM_MOVE | ANIM_ROTATE | ANIM_SCALE)) == 0) {
//log.debug("animate END");
cancel();
}
}
/* remember current map position */
final boolean changed = v.getMapPosition(mCurPos);
if (changed) {
mMap.updateMap(true);
} else {
mMap.postDelayed(updateTask, 10);
}
}
}