Math: Ease In, ease Извлечение смещения с использованием кривой Эрмита с временным ограничением
Я пытаюсь написать метод, который интерполирует от 0 до x (положение объекта в одном измерении) с течением времени, используя ускорение в начале и замедление в конце (облегчить/облегчить) с единственными ограничениями, которые общее время, а также продолжительность ускорения и замедления. движение должно повторить эффект инерции, и я рассматриваю кривую Эрмита для нелинейных частей.
double Interpolate(
double timeToAccel, double timeCruising, double timeToDecel,
double finalPosition,
double currentTime)
{
//...
}
Может кто-то указать мне на часть кода, которая это делает? Я не знаю, как интегрировать кривую Эрмита, поэтому не знаю, сколько я буду двигаться в ускоряющей части или в замедляющей части, и в свою очередь я не могу понять, какая будет скорость в линейной часть.
Спасибо.
Некоторая ссылка, чтобы проиллюстрировать мой вопрос.
Edit:
- начальная и конечная скорости равны нулю, а текущее время также является частью параметров в методе, я обновил подпись.
- В принципе идея состоит в том, чтобы представить движение с постоянной скоростью на расстоянии d, что дает общую продолжительность. Затем мы добавляем фазы ускорения и замедления, сохраняя при этом одну и ту же продолжительность, поэтому у нас есть неизвестная новая скорость круиза для определения (потому что мы меньше двигаемся в фазах Эрмита, чем в линейных фазах, которые они заменили). Возможно, количество перемещений, потерянных в фазах Эрмита, по сравнению с линейным движением одной и той же продолжительности - это соотношение между верхней и нижней областью кривых, просто идея от эксперта.
Изменить: Roman и Bob10 предоставили полные рабочие решения. Я реализовал код от Романа. Спасибо вам, ребята! Я ценю вашу прекрасную поддержку и ваши подробные решения, вы сохранили мне долгие поиски и испытания.
Ответы
Ответ 1
Сначала сделаем кубическую гермитовую сплайн-функцию:
/*
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
Ответ 2
Это простое использование с обычным постоянным ускорением. Тогда возникает вопрос, какую скорость (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()
alt text http://i26.tinypic.com/34sqzpi.png