how to calculate a negative acceleration?

后端 未结 15 1798
既然无缘
既然无缘 2021-02-08 12:11

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 (         


        
15条回答
  •  时光取名叫无心
    2021-02-08 12:27

    [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:

    1. k>1 gives deceleration decreasing by modulo as time passes.
    2. k<1 gives deceleration increasing by modulo as time passes.
    3. k=1 gives constant deceleration.

    [A longer one (with rationale and plots)]

    So the goal essentialy is:

    1. 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).

    2. [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) .

    3. 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:

    1. 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).

    2. 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.

    3. 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:)

提交回复
热议问题