Математика: Ease In, облегчить смещение с помощью кривой Эрмита с ограничением по времени - PullRequest
12 голосов
/ 30 июля 2010

Я пытаюсь написать метод, который интерполирует от 0 до x (положение объекта в одном измерении) с течением времени, используя ускорение в начале и замедление в конце (замедление / облегчение) с единственными ограничениями, которые предоставляется общее время , а также продолжительность ускорения и замедления . движение должно повторять эффект инерции, и я рассматриваю кривую Эрмита для нелинейных частей.

double Interpolate(
    double timeToAccel, double timeCruising, double timeToDecel,
    double finalPosition,
    double currentTime)
{
    //...
}

Может кто-нибудь указать мне на часть кода, которая делает это? Я не знаю, как интегрировать кривую Эрмита, следовательно, не знаю, насколько я буду двигаться в ускоряющей части или в замедляющей части, и, в свою очередь, я не могу понять, какова будет скорость в линейной часть.

Спасибо.

Некоторая ссылка для иллюстрации моего вопроса.

Редактировать

  • начальная и конечная скорости равны нулю, и текущее время также является частью параметров метода, я обновил сигнатуру.
  • В основном идея состоит в том, чтобы представить движение с постоянной скоростью на расстоянии d, это дает общую продолжительность. Затем мы добавляем фазы ускорения и замедления, сохраняя одинаковую продолжительность, поэтому у нас есть неизвестная новая крейсерская скорость для определения (потому что мы перемещаемся в фазах Эрмита меньше, чем в линейных фазах, которые они заменили). Возможно, количество движения, потерянного в фазах Эрмита, по сравнению с линейным движением той же продолжительности - это соотношение между верхней и нижней областями на кривых, просто идея неопытного.

Редактировать : Роман и Bob10 предоставили полные рабочие решения. Я реализовал код от Романа. Спасибо вам обоим, ребята! Я ценю вашу прекрасную поддержку и ваши детальные решения, вы спасли меня от долгих поисков и испытаний.

Ответы [ 2 ]

22 голосов
/ 30 июля 2010

Во-первых, давайте создадим кубическую функцию сплайна Эрмита:

/*
  t  - in interval <0..1>
  p0 - Start position
  p1 - End position
  m0 - Start tangent
  m1 - End tangent
*/
double CubicHermite(double t, double p0, double p1, double m0, double m1) {
   t2 = t*t;
   t3 = t2*t;
   return (2*t3 - 3*t2 + 1)*p0 + (t3-2*t2+t)*m0 + (-2*t3+3*t2)*p1 + (t3-t2)*m1;
}

Теперь ваша задача - вычислить p0, p1, m0 и m1 для частей, облегчающих и облегчающих.Давайте добавим несколько переменных, чтобы облегчить написание математики:

double Interpolate(
    double timeToAccel, double timeCruising, double timeToDecel,
    double finalPosition,
    double currentTime) {

    double t1 = timeToAccel;
    double t2 = timeCruising;
    double t3 = timeToDecel;
    double x = finalPosition;
    double t = currentTime;

Нам нужно указать, где должен находиться объект, когда он прекращает ускоряться и начинает замедляться.Вы можете указать их по своему усмотрению и по-прежнему производить плавное движение, однако нам хотелось бы получить несколько «естественное» решение.

Предположим, что крейсерская скорость равна v.Во время дробления объект преодолевает расстояние x2 = v * t2.Теперь, когда объект ускоряется от 0 до скорости v, он проходит расстояние x1 = v * t1 / 2.То же самое для замедления x3 = v * t3 / 2.Сложите все вместе:

x1 + x2 + x3 = x

v * t1 / 2 + v * t2 + v * t3 / 2 = x

Из этого мы можемвычислим нашу скорость и расстояния:

    double v = x / (t1/2 + t2 + t3/2);
    double x1 = v * t1 / 2;
    double x2 = v * t2;
    double x3 = v * t3 / 2;

И теперь, когда мы знаем все, мы просто подаем его в наш сплайн-интерполятор кубического Эрмита

    if(t <= t1) {
       // Acceleration
       return CubicHermite(t/t1, 0, x1, 0, v*t1);
    } else if(t <= t1+t2) {
       // Cruising
       return x1 + x2 * (t-t1) / t2;
    } else {
       // Deceleration
       return CubicHermite((t-t1-t2)/t3, x1+x2, x, v*t3, 0);
    }
}

Я проверял это в Excel, вотэквивалентный код VBA для игры.Есть некоторые деления на ноль для граничных условий, я оставляю это в качестве упражнения для читателя


Public Function CubicHermite(t As Double, p0 As Double, p1 As Double, _
m0 As Double, m1 As Double) As Double
   t2 = t * t
   t3 = t2 * t
   CubicHermite = (2 * t3 - 3 * t2 + 1) * p0 + _
(t3 - 2 * t2 + t) * m0 + (-2 * t3 + 3 * t2) * p1 + (t3 - t2) * m1
End Function

Public Function Interpolate(t1 As Double, t2 As Double, t3 As Double, _
x As Double, t As Double) As Double
    Dim x1 As Double, x2 As Double, x3 As Double

    v = x / (t1 / 2 + t2 + t3 / 2)
    x1 = v * t1 / 2
    x2 = v * t2
    x3 = v * t3 / 2

    If (t <= t1) Then
       Interpolate = CubicHermite(t / t1, 0, x1, 0, v*t1)
    ElseIf t <= t1 + t2 Then
       Interpolate = x1 + x2 * (t - t1) / t2
    Else
       Interpolate = CubicHermite((t-t1-t2)/t3, x1+x2, x, v*t3, 0)
    End If
End Function
8 голосов
/ 30 июля 2010

Это просто, используя нормальное постоянное ускорение.Тогда возникает вопрос, к какой скорости (v) нужно ускоряться, чтобы завершить поездку в нужное время, и это скажет вам ускорение, необходимое для достижения этой скорости.

Если общее время равно t_t, а время ускорения равно t_a, то у вас есть расстояние, пройденное как две ускоряющие и замедляющие части, и часть с постоянной скоростью:

x = 2*(a*t_a*t_a/2) + v*(t_t-2*t_a)

Этоможно решить для ускорения путем подстановки в v = a * t_a, чтобы найти

a = x/(t_a*(t_t - t_a))

Вот некоторый код Python, который использует и отображает результат этих уравнений, который показывает, как использовать уравнения и чторезультат выглядит так:

from pylab import *

t_a, t_t, D = 3., 10., 1.  # input values

a = D/(t_a*(t_t - t_a))
segments = (t_a, a), (t_t-2*t_a, 0.), (t_a, -a)  # durations and accelerations for each segment

t0, x0, v0 = 0.0, 0.0, 0.0  #initial values for the segment
tdata, xdata = [], []
for t_segment, a in segments: # loop over the three segments
    times = arange(0, t_segment, .01)
    x = x0 + v0*times + .5*a*times*times
    xdata.append(x)
    tdata.append(times+t0)
    x0 = x[-1] # the last x calculated in the segment above
    v0 += a*t_segment
    t0 += t_segment

plot(tdata[0], xdata[0], 'r', tdata[1], xdata[1], 'r', tdata[2], xdata[2], 'r')
xlabel("time")
ylabel("position")
show()

альтернативный текст http://i26.tinypic.com/34sqzpi.png

...