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 (
You can just reduce velocity by a constant amount each iteration. Example: you start with a velocity of 50, next iteration it is 40, then 30, 20, 10, stop. This would represent a constant "friction", independent of velocity, and this is actually quite close to reality (see friction on Wikipedia).
If you do not like the appearance of this, you need to make friction dependent on velocity. I would assume that a linear relationship friction = base-friction + (coefficient * velocity)
, with a rather small coefficient, would be enough.
I will add a thought as well. Looks like you don't want a constant (negative) acceleration. That would result in an equation like:
v(t) = v(0) + a*t,
where a
is the negative acceleration, t
is the time, and v(t)
is velocity at time t
. This gives you:
v(t2) - v(t1) = a(t2-t1),
and that means that for a given Δt the velocity difference is equal to aΔt, a constant.
What you may be looking for is for a "friction" term, that depends upon the current velocity. Under that assumption, the rate of change of velocity is proportional to the current velocity:
d v(t) / d t = -b*v(t).
Solving the above is easy, and you get: v(t) = v(0) e−b t.
Integrating this equation, we get x(t) = v(0)(1−e−b t) / b, where x is the position. The position plot1 for v(0) = 1, b = 0.1 looks like something you could use. Playing with the values of b, and adding a scale factor to the equation might be what you want to do.
1 http://www.wolframalpha.com/input/?i=plot+%281+-+1+e^%28-0.1+x%29+%29+%2F+0.1+for+x+%3D+0+to+100
A non-linear change in velocity means that acceleration is not constant. A non-constant acceleration means that the system is under the influence of jerk. Take all your acceleration equations and add "(1/6)jt3". Fix a, and give j a small negative value until v hits 0.
I would cut down velocity as something like v=v*0.9 Then i would have a velocity which is considered the stopped velocity. This way the object would come to rest eventually and not continue consuming resources as moving. so something like for(v=startingVelocity;v<1.0;v*=0.9) { x+=v; }
[Short answer (assuming C
syntax)]
double v(double old_v, double dt) {
t = t_for(old_v);
new_t = t - dt;
return (new_t <= 0)?0:v_for(t);
}
double t_for(double v)
and double v_for(double t)
are return values from a v-to-t bidirectional mapping (function in mathematical sence), which is arbitrary with the constraint that it is monothonic and defined for v >=0
(and hence has a point where v=0
). An example is:
double v_for(double t) { return pow(t, k); }
double t_for(double v) { return pow(v, 1.0/k); }
where having:
k>1
gives deceleration decreasing by modulo as time passes.k<1
gives deceleration increasing by modulo as time passes.k=1
gives constant deceleration.[A longer one (with rationale and plots)]
So the goal essentialy is:
To find a function v(t+dt)=f(v(t),dt)
which takes current velocity value v
and time delta dt
and returns the velocity at the moment t+dt
(it does not require actually specifying t
since v(t)
is already known and supplied as a parameter and dt
is just time delta). In other words, the task is to implement a routine double next_v(curr_v, dt);
with specific properties (see below).
[Please Note] The function in question has a useful (and desired) property of returning the same result regardless of the "history" of previous velocity changes. That means, that, for example, if the series of consecutive velocities is [100, 50, 10, 0] (for the starting velocity v0=100
), any other sequence larger than this will have the same "tail": [150, 100, 50, 10, 0] (for the starting velocity v0=150
), etc. In other words, regardless of the starting velocity, all velocity-to-time plots will effectively be copies of each other just offset along the time axis each by its own value (see the graph below, note the plots' parts between the lines t=0.0
and t=2.0
are identical) .
Besides, acceleration w(t)=dv(t)/dt
must be a descending function of time t
(for the purpose of visually pleasing and "intuitive" behaviour of the moving GUI object which we model here).
The proposed idea is:
First you choose a monothonic velocity function with desired properties (in your case it is gradually decreasing acceleration, though, as the example below shows, it is easier to use "accelerated" ones). This function must not also have an upper boundary, so that you could use it for whatever large velocity values. Also, it must have a point where velocity is zero. Some examples are: v(t) = k*t
(not exactly your case, since deceleration k
is constant here), v=sqrt(-t)
(this one is ok, being defined on the interval t <= 0
).
Then, for any given velocity, you find the point with this velocity value on the above function's plot (there will be a point, since the function is not bound, and only one since it is monothonic), advance by time delta towards smaller velocity values, thus acquiring the next one. Iteration will gradually (and inevitably) bring you to the point where velocity is zero.
That's basically all, there is even no need to produce some "final" formula, dependencies on time value or initial (not current) velocities go away, and the programming becomes really simple.
For two simple cases this small python script produces the plots below (initial velocities given were 1.0
to 10.0
), and, as you can see, from any given velocity "level" and "downwards" the plots "behave" the same which is of couse because no matter at what velocity you start to slow down (decelerate), you are "moving" along the same curve RELATIVE to the point where velocity is (becomes) zero:
import numpy
import pylab
import math
class VelocityCurve(object):
"""
An interface for the velocity 'curve'.
Must represent a _monotonically_ _growing_
(i.e. with one-to-one correspondence
between argument and value) function
(think of a deceleration reverse-played)
Must be defined for all larger-than-zero 'v' and 't'
"""
def v(self, t):
raise NotImplementedError
def t(self, v):
raise NotImplementedError
class VelocityValues(object):
def __init__(self, v0, velocity_curve):
assert v0 >= 0
assert velocity_curve
self._v = v0
self._vc = velocity_curve
def next_v(self, dt):
t = self._vc.t(self._v)
new_t = t - dt
if new_t <= 0:
self._v = 0
else:
self._v = self._vc.v(new_t)
return self._v
class LinearVelocityCurve(VelocityCurve):
def __init__(self, k):
"""k is for 'v(t)=k*t'"""
super(LinearVelocityCurve, self).__init__()
self._k = k
def v(self, t):
assert t >= 0
return self._k*t
def t(self, v):
assert v >= 0
return v/self._k
class RootVelocityCurve(VelocityCurve):
def __init__(self, k):
"""k is for 'v(t)=t^(1/k)'"""
super(RootVelocityCurve, self).__init__()
self._k = k
def v(self, t):
assert t >= 0
return math.pow(t, 1.0/self._k)
def t(self, v):
assert v >= 0
return math.pow(v, self._k)
def plot_v_arr(v0, velocity_curve, dt):
vel = VelocityValues(v0, velocity_curve)
v_list = [v0]
while True:
v = vel.next_v(dt)
v_list.append(v)
if v <= 0:
break
v_arr = numpy.array(list(v_list))
t_arr = numpy.array(xrange(len(v_list)))*dt
pylab.plot(t_arr, v_arr)
dt = 0.1
for v0 in range(1, 11):
plot_v_arr(v0, LinearVelocityCurve(1), dt)
for v0 in range(1, 11):
plot_v_arr(v0, RootVelocityCurve(2), dt)
pylab.xlabel('Time ')
pylab.ylabel('Velocity')
pylab.grid(True)
pylab.show()
This gives the following plots (linear ones for the linear decelerating (i.e. constant deceleration), "curvy" -- for the case of the "square root" one (see the code above)):
Also please beware that to run the above script one needs pylab, numpy and friends installed (but only for the plotting part, the "core" classes depend on nothing and can be of course used on their own).
P.S. With this approach, one can really "construct" (for example, augmenting different functions for different t
intervals or even smoothing a hand-drawn (recorded) "ergonomic" curve) a "drag" he likes:)
Seems like you are looking for deceleration which increases over time.
Try computing
Delta_v = -(A*t + B)
, selecting reasonable constants A and B, which suit you.
t is the total time till that point.
Change your velocity by adding Delta_v
.
This basically corresponds to linear negative acceleration.
You can basically select any function which increases over time (say f(t))
and compute
Delta_v = -f(t)
An appropriate choice for f(t) would give you the effect you desire.
Some examples you could use:
f(t) = At + B.
f(t) = A*exp(Bt)
Of course, you will have to play around a bit and try to figure out the right constants.