2011-09-09 3 views
12

코드에서 애니메이션 효과를 에뮬레이트하려고합니다. 언어가 아니라 수학으로 보이는 것처럼 거의 모든 언어가 이에 해당됩니다. 본질적으로 질량 스프링 시스템의 에뮬레이션입니다. 나는 WPF/Silverlight의 ElasticEase을보고 있었는데, 이것은 내가 찾고있는 것에 매우 가깝게 보인다.스프링 - 매스 시스템의 댐핑 효과 (또는이 ElasticEase는 무엇입니까?)

우선, 내가 원하는 것은 - 물체가 특정 초 수의 시간 동안 움직이며 위치를 지정하고 즉시 댐핑이있는 지점에서 휴식을 취할 수 있도록 일정 시간 동안 속도를 늦추는 것입니다. 적용된. 이것을 시각화하기 위해, 600w/900h 캔버스가 있고, TranslateTransform.Y에서 900px에서 150px로 애니메이션을 시작하는 사각형이 있다고 가정 해 봅시다. 즉각적으로 150px 높이 (초당 187.5px)에 도달하는 데 4 초가 걸리며 즉각적으로 감속되고 약 0.4 초 (초당 87.5px)에서 약 115px 높이로 약 35px 더 이동 한 다음 1 초에서 163px 높이까지 리바운드합니다. (초당 48px 및 48px), 다시 최대 146px (17px 및 17px/초)로 리바운드 한 다음 ocillations가 최종 휴지 위치 인 150px까지 속도를 늦 춥니 다. 오크주기는 16 초입니다. 의는 A 지점에서 B 지점으로 데 걸리는 시간 (초) 픽셀 거리와 번호를 - 여기 enter image description here

내가 사전에 알 수있는 작업은 다음과 같습니다

제가 위에서 설명 된 예는 여기에 왼쪽 상단 파란색 사각형이 ocillation의 초 수. 질량과 같은 것은 중요하지 않습니다.

나는 ElasticEase을 시도했는데 문제가 4 초 동안 여유없이 여행하고 다음 16 초 동안 "바운스"할 수없는 개체 인 것으로 보입니다. 내가이 비디오를 포함하고 엑셀했습니다

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); 
     } 

다음 .Springiness 나는대로 20

ILSpy 쇼의 그 기능과 같은 정말 높은 숫자로 설정 한 경우에도, 항상 너무 많은도 압축 된 폴더에있는 파일 DropBox에 있습니다. 나는이 질문이 사람들이 더 명확한 질문을 할 때 더 많은 일이 진행될 것이라고 생각한다.

(면책 조항 : 나는 그것이이 물건의 대부분에 올 때 내가 무슨 말을하는지 알 수없는) 물리학 건너 뛰기

+0

문제는 귀하가 생각하기에 수학/물리학 문제입니다. 이것은 최초의 물리학 과정 댐핑 진동 문제이지만, 완전히 설명하기 위해서는 몇백 개의 단어 (그리고 몇 가지 수학 공식, 이는 매우 나빠질 것입니다)가 필요합니다. 기본적인 문제는 댐핑이 기하 급수적이므로 이산 적으로 꼬리 진동이 손실된다는 것입니다. http://en.wikipedia.org/wiki/Damping 대신 전체 설명을 원하십니까? :) –

+0

고맙습니다 @ belisarius. 위의 표본이 질량을 고려하지 않는 것으로 생각하면 특히 수학/물리학은 이걸 가지고 저를 넘어 설 것입니다. 당신이 제공 할 수있는 설명이나 코드 샘플은 제가 전에 알고있는 변수들로 이것을 시작하는데 도움이 될 수 있습니다. –

+0

나는 시도 할 것이다. 그러나 내가 그것을 잘하기 위해 나에게 많은 시간이 걸리게 할 것을 예기하는 것에 따라, 아마 물리학 자/학생이 현상금을 쫓는 것은 나를 예기 할 수 있었다 :). BTW, 질량은 거기 있지만, 문제의 다른 모든 상수 (탄성 상수 및 감쇠 계수)는 질량 독립 문제를 일으키기 위해 질량으로 나누어집니다 (Fm * a == 0은 0과 같으므로, 당신이 원하는 모든 상수에 의해 모든 조건). F는 탄성력 k와 위치에 비례하는 탄성력과 속도 및 감쇠 상수에 비례하는 감쇠력의 두 가지 힘의 합성입니다. –

답변

8

그냥 방정식으로 바로 이동합니다.

매개 변수 : "여기에 내가 미리 알거야 - 초 픽셀 거리 [D]와 수를 [T0가]는 A 지점에서 B 지점으로 데 걸리는, 진동에 대한 시간 (초) [T1 ] 또한, 자유 진동수, 진동의 최대 크기, Amax, 감쇠 시간 상수, Tc 및 프레임 속도 (Rf)를 추가 할 예정입니다. 즉, 새로운 위치 값을 몇 번이나 추가 할 것인지를 결정합니다. 난 당신이 영원히을 계산하지 않으려는 가정, 그래서 10 초 TTOTAL 그냥 할거야, 합리적인 정지 조건의 다양한 ...

코드이 있습니다 여기 코드입니다 (파이썬).

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) 

아이디어는 부패 진동 다음 지점까지의 직선 운동입니다 : 중요한 것은 def Y(t)에서 볼 수있는 식이다. 진동은 sin에 의해 제공되고,이를 붕괴에 exp으로 곱함으로써 제공됩니다. 물론 원하는 거리, 진동 크기 등을 얻기 위해 매개 변수를 변경하십시오.

enter image description here

노트 : 코멘트에

  1. 대부분의 사람들은 물리학의 방법을 제안한다. 나는 이것을 사용하지 않았습니다. 왜냐하면 어떤 모션을 지정하면, 물리학에서 시작하여 미분 방정식으로 이동 한 다음 모션을 계산하고 매개 변수를 조정하여 최종 결과를 얻으려고하기 때문에 . 그냥 마지막 일로 넘어갈 수도 있습니다. 즉, 그들이 일하고 싶은 물리학에 대한 직관력이 없다면 말입니다.
  2. 종종 이런 문제에서 연속 속도 (1 차 미분)를 유지하려고하지만 "즉시 속도가 느려집니다"라고 말하면서 여기에서하지 않았습니다.
  3. 진동의주기와 진폭은 댐핑이 적용될 때 정확하게 지정되지는 않지만 사용자가 신경 쓰는 것보다 더 자세합니다.
  4. 이것을 하나의 방정식으로 표현해야하는 경우 "Heaviside 함수"를 사용하여 기여도를 설정 및 해제 할 수 있습니다. 이 너무 오래 만드는 위험을 무릅

, 난 김프에서 GIF를 만들 수 실현, 그래서 이것은처럼 보이는 것입니다 : 내가 만드는 전체 코드를 게시 할 수 있습니다

enter image description here

관심이 있다면 플롯하지만 기본적으로 각 타임 스텝마다 다른 D와 T0 값을 가진 Y를 호출하고 있습니다. 이 작업을 다시 수행하려면 댐핑 (즉, Tc 감소)을 늘릴 수 있지만 약간의 번거 로움 때문에 그대로 두겠습니다.

+0

동의하면 실제로는 질량이 필요하지 않습니다. 속력과 속진은 충분합니다. 봄 - 그래프 레이아웃을 할 때 그것을 발견했습니다. 순진한 방법은 속도, 질량, 힘 등으로 전체 물리 시뮬레이션을하는 것입니다. 그런 다음 짜증나는 부작용으로 두 번 작업하는 것으로 나타났습니다. – gjvdkamp

+0

정말 대단한 설명 !! 고맙습니다! 나는 다음 일 또는 이틀에 걸쳐이 일을 할 수 있는지 알아보기 위해 노력할 것입니다. –

+0

@Otaku D에서 유래 물이 연속적이지 않은 것 같습니다. 시체가 처음으로 휴식 위치에 도달했을 때 급격한 속도 변화를 느낄 수 있습니다. –

5

@ tom10과 같은 줄을 생각하고있었습니다. (나는 IList<IEasingFunction>을 취한 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 편집기의 속성에 추가해야하는지 확실하지 않습니다.

+0

와우 피터! 이거 엄청나 네! 나는 다음 일 또는 이틀에 걸쳐 이것을 시험해보고 당신에게 알려줄 것입니다. –

+0

피터, 당신이 제공 한 것은 톰만큼이나 훌륭합니다. 나는 이것에 또 다른 현상금을 열고 2 일 안에 포인트를 줄 것이다. –