I\'m implementing the scrolling behaviour of a touch screen UI but I\'m too tired in the moment to wrap my mind around some supposedly trivial piece of math:
y (
After reading the comments, I'd like to change my answer: Multiply the velocity by k < 1, like k = 0.955, to make it decay exponentially.
Explanation (with graphs, and a tuneable equation!) follows...
I interpret the graph in your original question as showing velocity staying near the starting value, then decreasing increasingly rapidly. But if you imagine sliding a book across a table, it moves away from you quickly, then slows down, then coasts to a stop. I agree with @Chris Farmer
that the right model to use is a drag force that is proportional to speed. I'm going to take this model and derive the answer I suggested above. I apologize in advance for the length of this. I'm sure someone better at math could simplify this considerably. Also, I put links to the graphs in directly, there are some characters in the links that the SO parser doesn't like.
URLs fixed now.
I'm going to use the following definitions:
x -> time
a(x) -> acceleration as a function of time
v(x) -> velocity as a function of time
y(x) -> position as a function of time
u -> constant coefficient of drag
colon : denotes proportionality
We know that the force due to drag is proportional to speed. We also know that force is proportional to acceleration.
a(x) : -u v(x) (Eqn. 1)
The minus sign ensures that the acceleration is opposite the current direction of travel.
We know that velocity is the integrated acceleration.
v(x) : integral( -u v(x) dx ) (Eqn. 2)
This means the velocity is proportional to its own integral. We know that e^x
satisfies this condition. So we suppose that
v(x) : e^(-u x) (Eqn. 3)
The drag coefficient in the exponent is so that when we solve the integral in Eqn. 2 the u
cancels to get back to Eqn. 3.
Now we need to figure out the value of u
. As @BlueRaja
pointed out, e^x
never equals zero, regardless of x. But it approaches zero for sufficiently negative x. Let's consider 1% of our original speed to be 'stopped' (your idea of a threshold), and let's say we want to stop within x = 2 seconds (you can tune this later). Then we need to solve
e^(-2u) = 0.01 (Eqn. 4)
which leads us to calculate
u = -ln(0.01)/2 ~= 2.3 (Eqn. 5)
Let's graph v(x).
Looks like it exponentially decays to a small value in 2 seconds. So far, so good.
We don't necessarily want to calculate exponentials in our GUI. We know that we can convert exponential bases easily,
e^(-u x) = (e^-u)^x (Eqn. 6)
We also don't want to keep track of time in seconds. We know we have an update rate of 20 ms, so let's define a timetick n
with a tick rate of 50 ticks/sec.
n = 50 x (Eqn. 7)
Substituting the value of u from Eqn. 5 into Eqn. 6, combining with Eqn. 7, and substituting into Eqn. 3, we get
v(n) : k^n, k = e^(ln(0.01)/2/50) ~= 0.955 (Eqn. 8)
Let's plot this with our new x-axis in timeticks.
Again, our velocity function is proportional to something that decays to 1% in the desired number of iterations, and follows a 'coasting under the influence of friction' model. We can now multiply our initial velocity v0
by Eqn. 8 to get the actual velocity at any timestep n:
v(n) = v0 k^n (Eqn. 9)
Note that in implementation, it is not necessary to keep track of v0! We can convert the closed form v0 * k^n
to a recursion to get the final answer
v(n+1) = v(n)*k (Eqn. 10)
This answer satisfies your constraint of not caring what the initial velocity is - the next velocity can always be calculated using just the current velocity.
It's worth checking to make sure the position behavior makes sense. The position following such a velocity model is
y(n) = y0 + sum(0..n)(v(n)) (Eqn. 11)
The sum in Eqn. 11 is easily solved using the form of Eqn 9. Using an index variable p:
sum(p = 0..n-1)(v0 k^p) = v0 (1-k^n)/(1-k) (Eqn. 12)
So we have
y(n) = y0 + v0 (1-k^n)/(1-k) (Eqn. 13)
Let's plot this with y0 = 0 and v0 = 1.
So we see a rapid move away from the origin, followed a graceful coast to a stop. I believe this graph is a more faithful depiction of sliding than your original graph.
In general, you can tune k
by using the equation
k = e^(ln(Threshold)/Time/Tickrate) (Eqn. 14)
where:
Threshold is the fraction of starting velocity at which static friction kicks in
Time is the time in seconds at which the speed should drop to Threshold
Tickrate is your discrete sampling interval
(THANKS to @poke
for demonstrating the use of Wolfram Alpha for plots - that's pretty sweet.)
OLD ANSWER
Multiply the velocity by k < 1, like k = 0.98, to make it decay exponentially.