Подтвердить что ты не робот

Эффект демпфирования системы Spring -Mass (или это ElasticEase?)

Я пытаюсь подражать эффекту анимации в коде (почти любой язык будет делать, как представляется, математика, а не язык). По сути, это эмуляция системы массой spring. Я смотрел на WPF/Silverlight ElasticEase, и это похоже на то, что я ищу, но не совсем.

Прежде всего, вот что я ищу - объект, путешествующий определенное количество секунд, попав в место и немедленно замедляясь, чтобы оциллировать в течение определенного количества секунд, чтобы отдохнуть в той же точке, где было применено демпфирование, Чтобы визуализировать это, скажем, у меня есть холст 600 Вт /900 ч, и у меня есть квадрат, который начинает анимироваться от 900 пикселей до 150 пикселей в TranslateTransform.Y. Требуется 4 секунды, чтобы достичь высоты 150 пикселей (187,5 пикселей в секунду), на какой стадии он сразу же затухает и только перемещается на 35 пикселей больше на 0,4 секунды (87,5 пикселей в секунду) до высоты 115 пикселей, а затем отскакивает на 1 секунду до высоты 163 пикселей (48px и 48px в секунду), а затем восстанавливается до 146px (17px и 17px в секунду) и так далее до тех пор, пока ocillations не замедлят его до конечного места отдыха 150px. Период оциляции составляет 16 секунд.

Пример, описанный выше, - это верхний левый синий прямоугольник: enter image description here

Здесь то, что я буду знать заранее - расстояние между пикселями и количество секунд, которое требуется, чтобы добраться от точки A до точки B, количество секунд для оциляции. Такие вещи, как масса, как представляется, не имеют значения.

Я пробовал ElasticEase, и проблема в том, что я не могу заставить объект двигаться без ослабления в течение 4 секунд, а затем "отскок" в течение следующих 16 секунд. .Springiness также всегда слишком много, даже если я установил его как очень высокое число, например 20.

ILSpy показывает свою функцию как:

protected override double EaseInCore(double normalizedTime)
        {
            double num = Math.Max(0.0, (double)this.Oscillations);
            double num2 = Math.Max(0.0, this.Springiness);
            double num3;
            if (DoubleUtil.IsZero(num2))
            {
                num3 = normalizedTime;
            }
            else
            {
                num3 = (Math.Exp(num2 * normalizedTime) - 1.0) / (Math.Exp(num2) - 1.0);
            }
            return num3 * Math.Sin((6.2831853071795862 * num + 1.5707963267948966) * normalizedTime);
        }

Я включил 2 видео и файл Excel в папку с zipped на DropBox. Думаю, этот вопрос будет скорее продолжением работы, так как люди задают более разъясняющие вопросы.

(ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я не знаю, о чем говорю, когда дело доходит до многих вещей)

4b9b3361

Ответ 1

Пропустите физику и просто перейдите прямо к уравнению.

параметры: "Здесь, что я буду знать заранее - расстояние между пикселями [D] и количеством секунд [T0] требуется, чтобы добраться от точки A до точки B, количество секунд для колебаний [T1]". Кроме того, я добавлю в качестве бесплатных параметров: максимальный размер колебаний, Amax, постоянную времени демпфирования, Tc и частоту кадров Rf, то есть в какое время требуется новое значение позиции. Я предполагаю, что вы не хотите вычислять это навсегда, поэтому я просто сделаю 10 секунд, Ttotal, но есть множество разумных условий остановки...

код: Вот код (в Python). Главное - это уравнение, найденное в def Y(t):

from numpy import pi, arange, sin, exp

Ystart, D = 900., 900.-150.  # all time units in seconds, distance in pixels, Rf in frames/second
T0, T1, Tc, Amax, Rf, Ttotal = 5., 2., 2., 90., 30., 10. 

A0 = Amax*(D/T0)*(4./(900-150))  # basically a momentum... scales the size of the oscillation with the speed 

def Y(t):
    if t<T0:  # linear part
        y = Ystart-(D/T0)*t
    else:  # decaying oscillations
        y = Ystart-D-A0*sin((2*pi/T1)*(t-T0))*exp(-abs(T0-t)/Tc)
    return y

y_result = []
for t in arange(0, Ttotal, 1./Rf):  # or one could do "for i in range(int(Ttotal*Rf))" to stick with ints    
    y = Y(t)
    y_result.append(y)

Идея - линейное движение до точки, за которой следует затухающее колебание. Осцилляция обеспечивается sin и распадом, умножая ее на exp. Конечно, измените параметры, чтобы получить любое расстояние, размер колебаний и т.д., Которые вы хотите.

enter image description here

заметки:

  • Большинство людей в комментариях предлагают физические подходы. Я не использовал их, потому что, если кто-то задает определенное движение, он немного переутомляется, чтобы начать с физики, перейти к дифференциальным уравнениям, а затем вычислить движение и настроить параметры, чтобы получить последнюю вещь, Мог бы просто пойти прямо к последней вещи. Если, конечно, нет интуиции для физики, от которой они хотят работать.
  • Часто в таких проблемах хочется сохранить непрерывную скорость (первая производная), но вы говорите, что "немедленно замедляется", поэтому я этого не делал.
  • Обратите внимание, что период и амплитуда колебаний не будут точно такими же, как указано при подавлении демпфирования, но это, вероятно, более подробно, чем вы заботитесь.
  • Если вам нужно выразить это как одно уравнение, вы можете сделать это, используя функцию "Heaviside", чтобы включить и выключить вкладки.

Рискуя сделать это слишком долго, я понял, что могу сделать gif в GIMP, так что это выглядит так:

enter image description here

Я могу опубликовать полный код, чтобы сделать графики, если есть интерес, но в основном я просто вызываю Y с разными значениями D и T0 для каждого временного времени. Если бы я снова это сделал, я мог бы увеличить демпфирование (т.е. Уменьшить Tc), но это немного хлопот, поэтому я оставляю его как есть.

Ответ 2

Я думал по тем же строкам, что и @tom10. (Я также рассмотрел IEasingFunction, который взял IList<IEasingFunction>, но было бы сложно взломать желаемое поведение из существующих).

// Based on the example at
// http://msdn.microsoft.com/en-us/library/system.windows.media.animation.easingfunctionbase.aspx
namespace Org.CheddarMonk
{
    public class OtakuEasingFunction : EasingFunctionBase
    {
        // The time proportion at which the cutoff from linear movement to
        // bounce occurs. E.g. for a 4 second movement followed by a 16
        // second bounce this would be 4 / (4 + 16) = 0.2.
        private double _CutoffPoint;
        public double CutoffPoint {
            get { return _CutoffPoint; }
            set {
                if (value <= 0 || value => 1 || double.IsNaN(value)) {
                    throw new ArgumentException();
                }
                _CutoffPoint = value;
            }
        }

        // The size of the initial bounce envelope, as a proportion of the
        // animation distance. E.g. if the animation moves from 900 to 150
        // and you want the maximum bounce to be no more than 35 you would
        // set this to 35 / (900 - 150) ~= 0.0467.
        private double _EnvelopeHeight;
        public double EnvelopeHeight {
            get { return _EnvelopeHeight; }
            set {
                if (value <= 0 || double.IsNaN(value)) {
                    throw new ArgumentException();
                }
                _EnvelopeHeight = value;
            }
        }

        // A parameter controlling how fast the bounce height should decay.
        // The higher the decay, the sooner the bounce becomes negligible.
        private double _EnvelopeDecay;
        public double EnvelopeDecay {
            get { return _EnvelopeDecay; }
            set {
                if (value <= 0 || double.IsNaN(value)) {
                    throw new ArgumentException();
                }
                _EnvelopeDecay = value;
            }
        }

        // The number of half-bounces.
        private int _Oscillations;
        public int Oscillations {
            get { return _Oscillations; }
            set {
                if (value <= 0) {
                    throw new ArgumentException();
                }
                _Oscillations = value;
            }
        }

        public OtakuEasingFunction() {
            // Sensible default values.
            CutoffPoint = 0.7;
            EnvelopeHeight = 0.3;
            EnvelopeDecay = 1;
            Oscillations = 3;
        }

        protected override double EaseInCore(double normalizedTime) {
            // If we get an out-of-bounds value, be nice.
            if (normalizedTime < 0) return 0;
            if (normalizedTime > 1) return 1;

            if (normalizedTime < _CutoffPoint) {
                return normalizedTime / _CutoffPoint;
            }

            // Renormalise the time.
            double t = (normalizedTime - _CutoffPoint) / (1 - _CutoffPoint);
            double envelope = EnvelopeHeight * Math.Exp(-t * EnvelopeDecay);
            double bounce = Math.Sin(t * Oscillations * Math.PI);
            return envelope * bounce;
        }

        protected override Freezable CreateInstanceCore() {
            return new OtakuEasingFunction();
        }
    }
}

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