diff --git a/vtm-gdx/src/org/oscim/gdx/GestureHandler.java b/vtm-gdx/src/org/oscim/gdx/GestureHandler.java
index 41793dff..be87480a 100644
--- a/vtm-gdx/src/org/oscim/gdx/GestureHandler.java
+++ b/vtm-gdx/src/org/oscim/gdx/GestureHandler.java
@@ -21,7 +21,9 @@ import com.badlogic.gdx.input.GestureDetector.GestureListener;
import com.badlogic.gdx.math.Vector2;
import org.oscim.core.Tile;
+import org.oscim.map.Animator2;
import org.oscim.map.Map;
+import org.oscim.utils.Parameters;
public class GestureHandler implements GestureListener {
private boolean mayFling = true;
@@ -84,7 +86,10 @@ public class GestureHandler implements GestureListener {
//log.debug("fling " + button + " " + velocityX + "/" + velocityY);
if (mayFling && button == Buttons.LEFT) {
int m = Tile.SIZE * 4;
- mMap.animator().animateFling((int) velocityX, (int) velocityY, -m, m, -m, m);
+ if (Parameters.ANIMATOR2)
+ ((Animator2) mMap.animator()).animateFlingScroll(velocityX, velocityY, -m, m, -m, m);
+ else
+ mMap.animator().animateFling(velocityX, velocityY, -m, m, -m, m);
return true;
}
return false;
diff --git a/vtm-gdx/src/org/oscim/gdx/InputHandler.java b/vtm-gdx/src/org/oscim/gdx/InputHandler.java
index 32c316fb..3daed996 100644
--- a/vtm-gdx/src/org/oscim/gdx/InputHandler.java
+++ b/vtm-gdx/src/org/oscim/gdx/InputHandler.java
@@ -31,7 +31,7 @@ import org.oscim.layers.tile.buildings.BuildingLayer;
import org.oscim.map.Map;
import org.oscim.map.ViewController;
import org.oscim.theme.VtmThemes;
-import org.oscim.utils.Easing;
+import org.oscim.utils.animation.Easing;
import java.util.List;
diff --git a/vtm/src/org/oscim/layers/MapEventLayer.java b/vtm/src/org/oscim/layers/MapEventLayer.java
index f2c1d40c..c2e7a93a 100644
--- a/vtm/src/org/oscim/layers/MapEventLayer.java
+++ b/vtm/src/org/oscim/layers/MapEventLayer.java
@@ -19,15 +19,19 @@
*/
package org.oscim.layers;
+import org.oscim.backend.CanvasAdapter;
+import org.oscim.backend.Platform;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.event.Event;
import org.oscim.event.Gesture;
import org.oscim.event.GestureListener;
import org.oscim.event.MotionEvent;
+import org.oscim.map.Animator2;
import org.oscim.map.Map;
import org.oscim.map.Map.InputListener;
import org.oscim.map.ViewController;
+import org.oscim.utils.Parameters;
import static org.oscim.backend.CanvasAdapter.dpi;
import static org.oscim.utils.FastMath.withinSquaredDist;
@@ -469,8 +473,14 @@ public class MapEventLayer extends AbstractMapEventLayer implements InputListene
int w = Tile.SIZE * 5;
int h = Tile.SIZE * 5;
- mMap.animator().animateFling(velocityX * 2, velocityY * 2,
- -w, w, -h, h);
+ if (Parameters.ANIMATOR2) {
+ if (!CanvasAdapter.platform.isDesktop() && CanvasAdapter.platform != Platform.WEBGL) {
+ velocityX *= 2;
+ velocityY *= 2;
+ }
+ ((Animator2) mMap.animator()).animateFlingScroll(velocityX, velocityY, -w, w, -h, h);
+ } else
+ mMap.animator().animateFling(velocityX * 2, velocityY * 2, -w, w, -h, h);
return true;
}
diff --git a/vtm/src/org/oscim/layers/MapEventLayer2.java b/vtm/src/org/oscim/layers/MapEventLayer2.java
index 6c137e41..95e66eb4 100644
--- a/vtm/src/org/oscim/layers/MapEventLayer2.java
+++ b/vtm/src/org/oscim/layers/MapEventLayer2.java
@@ -21,14 +21,17 @@
package org.oscim.layers;
import org.oscim.backend.CanvasAdapter;
+import org.oscim.backend.Platform;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.event.Event;
import org.oscim.event.Gesture;
import org.oscim.event.MotionEvent;
+import org.oscim.map.Animator2;
import org.oscim.map.Map;
import org.oscim.map.Map.InputListener;
import org.oscim.map.ViewController;
+import org.oscim.utils.Parameters;
import org.oscim.utils.async.Task;
import static org.oscim.backend.CanvasAdapter.dpi;
@@ -569,8 +572,14 @@ public class MapEventLayer2 extends AbstractMapEventLayer implements InputListen
int w = Tile.SIZE * 5;
int h = Tile.SIZE * 5;
- mMap.animator().animateFling(velocityX * 2, velocityY * 2,
- -w, w, -h, h);
+ if (Parameters.ANIMATOR2) {
+ if (!CanvasAdapter.platform.isDesktop() && CanvasAdapter.platform != Platform.WEBGL) {
+ velocityX *= 2;
+ velocityY *= 2;
+ }
+ ((Animator2) mMap.animator()).animateFlingScroll(velocityX, velocityY, -w, w, -h, h);
+ } else
+ mMap.animator().animateFling(velocityX * 2, velocityY * 2, -w, w, -h, h);
return true;
}
diff --git a/vtm/src/org/oscim/map/Animator.java b/vtm/src/org/oscim/map/Animator.java
index f11631fa..2f15896e 100644
--- a/vtm/src/org/oscim/map/Animator.java
+++ b/vtm/src/org/oscim/map/Animator.java
@@ -28,8 +28,8 @@ import org.oscim.core.MapPosition;
import org.oscim.core.Point;
import org.oscim.core.Tile;
import org.oscim.renderer.MapRenderer;
-import org.oscim.utils.Easing;
import org.oscim.utils.ThreadUtils;
+import org.oscim.utils.animation.Easing;
import org.oscim.utils.async.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -48,21 +48,21 @@ public class Animator {
public final static int ANIM_TILT = 1 << 3;
public final static int ANIM_FLING = 1 << 4;
- private final Map mMap;
+ final Map mMap;
- private final MapPosition mCurPos = new MapPosition();
- private final MapPosition mStartPos = new MapPosition();
- private final MapPosition mDeltaPos = new MapPosition();
+ final MapPosition mCurPos = new MapPosition();
+ final MapPosition mStartPos = new MapPosition();
+ final MapPosition mDeltaPos = new MapPosition();
private final Point mScroll = new Point();
- private final Point mPivot = new Point();
+ final Point mPivot = new Point();
private final Point mVelocity = new Point();
- private float mDuration = 500;
- private long mAnimEnd = -1;
- private Easing.Type mEasingType = Easing.Type.LINEAR;
+ float mDuration = 500;
+ long mAnimEnd = -1;
+ Easing.Type mEasingType = Easing.Type.LINEAR;
- private int mState = ANIM_NONE;
+ int mState = ANIM_NONE;
public Animator(Map map) {
mMap = map;
@@ -251,7 +251,7 @@ public class Animator {
animStart(duration, ANIM_FLING, Easing.Type.SINE_OUT);
}
- private void animStart(float duration, int state, Easing.Type easingType) {
+ void animStart(float duration, int state, Easing.Type easingType) {
if (!isActive())
mMap.events.fire(Map.ANIM_START, mMap.mMapPosition);
mCurPos.copy(mStartPos);
@@ -332,7 +332,7 @@ public class Animator {
}
}
- private Task updateTask = new Task() {
+ Task updateTask = new Task() {
@Override
public int go(boolean canceled) {
if (!canceled)
@@ -341,7 +341,7 @@ public class Animator {
}
};
- private double doScale(ViewController v, float adv) {
+ double doScale(ViewController v, float adv) {
double newScale = mStartPos.scale + mDeltaPos.scale * Math.sqrt(adv);
v.scaleMap((float) (newScale / mCurPos.scale),
diff --git a/vtm/src/org/oscim/map/Animator2.java b/vtm/src/org/oscim/map/Animator2.java
new file mode 100644
index 00000000..a74d425e
--- /dev/null
+++ b/vtm/src/org/oscim/map/Animator2.java
@@ -0,0 +1,254 @@
+/*
+ * 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 final static int ANIM_KINETIC = 1 << 5;
+
+ /**
+ * The minimum changes that are pleasant for users.
+ */
+ private static final float DEFAULT_MIN_VISIBLE_CHANGE_PIXELS = 0.5f;
+
+ private static final float FLING_FRICTION_MOVE = 0.9f;
+
+ 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);
+ }
+
+ /**
+ * @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;
+
+ mMap.getMapPosition(mStartPos);
+
+ 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.setValueThreshold(DEFAULT_MIN_VISIBLE_CHANGE_PIXELS);
+ mFlingScrollForce.setFrictionScalar(FLING_FRICTION_MOVE);
+ mFlingScrollForce.setValueAndVelocity(0f, (float) Math.sqrt(velocityX * velocityX + velocityY * velocityY));
+
+ animFlingStart(ANIM_MOVE);
+ }
+
+ 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();
+ }
+
+ /**
+ * @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) {
+ 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;
+ boolean isAnimationFinished = true;
+
+ 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);
+ }
+ isAnimationFinished = (velocity == 0);
+ }
+
+ 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);
+ }
+
+ isAnimationFinished = isAnimationFinished && (velocity == 0);
+ }
+
+ if ((mState & ANIM_ROTATE) != 0) {
+ float valueDelta = mFlingRotateForce.updateValueAndVelocity(deltaT);
+ float velocity = mFlingRotateForce.getVelocity();
+
+ v.rotateMap(valueDelta, (float) mPivot.x, (float) mPivot.y);
+
+ isAnimationFinished = isAnimationFinished && (velocity == 0);
+ }
+
+ /*if ((mState & ANIM_TILT) != 0) {
+ // Do some tilt fling
+ isAnimationFinished = isAnimationFinished && (velocity == 0);
+ }*/
+
+ if (isAnimationFinished) {
+ //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);
+ }
+ }
+}
diff --git a/vtm/src/org/oscim/map/Map.java b/vtm/src/org/oscim/map/Map.java
index d5279ab3..9ad3e330 100644
--- a/vtm/src/org/oscim/map/Map.java
+++ b/vtm/src/org/oscim/map/Map.java
@@ -132,7 +132,10 @@ public abstract class Map implements TaskQueue {
ThreadUtils.init();
mViewport = new ViewController();
- mAnimator = new Animator(this);
+ if (Parameters.ANIMATOR2)
+ mAnimator = new Animator2(this);
+ else
+ mAnimator = new Animator(this);
mLayers = new Layers(this);
input = new EventDispatcher() {
diff --git a/vtm/src/org/oscim/map/ViewController.java b/vtm/src/org/oscim/map/ViewController.java
index af6d2a4a..44f352e2 100644
--- a/vtm/src/org/oscim/map/ViewController.java
+++ b/vtm/src/org/oscim/map/ViewController.java
@@ -86,12 +86,12 @@ public class ViewController extends Viewport {
* @param mx the amount of pixels to move the map horizontally.
* @param my the amount of pixels to move the map vertically.
*/
- public void moveMap(float mx, float my) {
+ public synchronized void moveMap(float mx, float my) {
ThreadUtils.assertMainThread();
- Point p = applyRotation(mx, my);
+ applyRotation(mx, my, mPos.bearing, mMovePoint);
double tileScale = mPos.scale * Tile.SIZE;
- moveTo(mPos.x - p.x / tileScale, mPos.y - p.y / tileScale);
+ moveTo(mPos.x - mMovePoint.x / tileScale, mPos.y - mMovePoint.y / tileScale);
}
/* used by MapAnimator */
@@ -120,18 +120,26 @@ public class ViewController extends Viewport {
mPos.y = mMinY;
}
- private synchronized Point applyRotation(double mx, double my) {
- if (mPos.bearing == 0) {
- mMovePoint.x = mx;
- mMovePoint.y = my;
+ /**
+ * @param mx the amount of pixels to move the map horizontally.
+ * @param my the amount of pixels to move the map vertically.
+ * @param bearing the bearing to rotate the map.
+ * @param out the position where to move.
+ */
+ public static void applyRotation(double mx, double my, float bearing, Point out) {
+ if (out == null)
+ out = new Point();
+
+ if (bearing == 0) {
+ out.x = mx;
+ out.y = my;
} else {
- double rad = Math.toRadians(mPos.bearing);
+ double rad = Math.toRadians(bearing);
double rcos = Math.cos(rad);
double rsin = Math.sin(rad);
- mMovePoint.x = mx * rcos + my * rsin;
- mMovePoint.y = mx * -rsin + my * rcos;
+ out.x = mx * rcos + my * rsin;
+ out.y = mx * -rsin + my * rcos;
}
- return mMovePoint;
}
/**
diff --git a/vtm/src/org/oscim/utils/Parameters.java b/vtm/src/org/oscim/utils/Parameters.java
index 486c64bb..b60073f4 100644
--- a/vtm/src/org/oscim/utils/Parameters.java
+++ b/vtm/src/org/oscim/utils/Parameters.java
@@ -16,6 +16,11 @@ package org.oscim.utils;
public final class Parameters {
+ /**
+ * If true the Animator2
will be used instead of default Animator
.
+ */
+ public static boolean ANIMATOR2 = false;
+
/**
* Allow custom tile size instead of the calculated one.
*/
diff --git a/vtm/src/org/oscim/utils/animation/DragForce.java b/vtm/src/org/oscim/utils/animation/DragForce.java
new file mode 100644
index 00000000..93ed4fb7
--- /dev/null
+++ b/vtm/src/org/oscim/utils/animation/DragForce.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.oscim.utils.animation;
+
+/**
+ * See https://developer.android.com/reference/android/support/animation/FlingAnimation.html
+ * Class android.support.animation.FlingAnimation
+ */
+public final class DragForce {
+
+ private static final float DEFAULT_FRICTION = -4.2f;
+ private static final float DEFAULT_MIN_VISIBLE_CHANGE_PIXELS = 0.5f;
+
+ // This multiplier is used to calculate the velocity threshold given a certain value
+ // threshold. The idea is that if it takes >= 1 frame to move the value threshold amount,
+ // then the velocity is a reasonable threshold.
+ private static final float VELOCITY_THRESHOLD_MULTIPLIER = 1000f / 16f; // 1 frame ≙ 16 ms (62.5 fps)
+ private float mFriction = DEFAULT_FRICTION;
+ private float mVelocityThreshold = DEFAULT_MIN_VISIBLE_CHANGE_PIXELS * VELOCITY_THRESHOLD_MULTIPLIER;
+
+ // Internal state to hold a value/velocity pair.
+ private float mValue;
+ private float mVelocity;
+
+ public void setFrictionScalar(float frictionScalar) {
+ mFriction = frictionScalar * DEFAULT_FRICTION;
+ }
+
+ public float getFrictionScalar() {
+ return mFriction / DEFAULT_FRICTION;
+ }
+
+ /**
+ * Updates the animation state (i.e. value and velocity).
+ *
+ * @param deltaT time elapsed in millisecond since last frame
+ * @return the value delta since last frame
+ */
+ public float updateValueAndVelocity(long deltaT) {
+ float velocity = mVelocity;
+ mVelocity = (float) (velocity * Math.exp((deltaT / 1000f) * mFriction));
+ float valueDelta = (mVelocity - velocity);
+ mValue += valueDelta;
+ if (isAtEquilibrium(mValue, mVelocity)) {
+ mVelocity = 0f;
+ }
+ return valueDelta;
+ }
+
+ public void setValueAndVelocity(float value, float velocity) {
+ mValue = value;
+ mVelocity = velocity;
+ }
+
+ public float getValue() {
+ return mValue;
+ }
+
+ public float getVelocity() {
+ return mVelocity;
+ }
+
+ public float getAcceleration(float position, float velocity) {
+ return velocity * mFriction;
+ }
+
+ public boolean isAtEquilibrium(float value, float velocity) {
+ return Math.abs(velocity) < mVelocityThreshold;
+ }
+
+ public void setValueThreshold(float threshold) {
+ mVelocityThreshold = threshold * VELOCITY_THRESHOLD_MULTIPLIER;
+ }
+}
diff --git a/vtm/src/org/oscim/utils/Easing.java b/vtm/src/org/oscim/utils/animation/Easing.java
similarity index 99%
rename from vtm/src/org/oscim/utils/Easing.java
rename to vtm/src/org/oscim/utils/animation/Easing.java
index b6f3d4de..c5c34ed7 100644
--- a/vtm/src/org/oscim/utils/Easing.java
+++ b/vtm/src/org/oscim/utils/animation/Easing.java
@@ -15,7 +15,7 @@
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see .
*/
-package org.oscim.utils;
+package org.oscim.utils.animation;
import static org.oscim.utils.FastMath.clamp;