Browse Source

Make KeyFrameStateMachine an IObservable.

pull/1461/head
Jumar Macato 9 years ago
parent
commit
1acfc74fb1
  1. 24
      src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs
  2. 46
      src/Avalonia.Animation/Keyframes/KeyFrames.cs
  3. 90
      src/Avalonia.Animation/Keyframes/KeyFramesStateMachine.cs
  4. 0
      src/Avalonia.Animation/PlaybackDirection.cs
  5. 4
      src/Avalonia.DotNetCoreRuntime/AppBuilder.cs
  6. 4
      src/Avalonia.Visuals/Animation/Keyframes/TransformKeyFrames.cs

24
src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs

@ -14,21 +14,19 @@ namespace Avalonia.Animation.Keyframes
/// </summary>
public class DoubleKeyFrames : KeyFrames<double>
{
/// <inheritdocs/>
public override IObservable<double> DoInterpolation(IObservable<double> timer, Animation animation, Animatable control)
=> timer.Select(t =>
{
// Get a pair of keyframes to make the interpolation.
var pair = GetKeyFramePairByTime(t);
double y0 = pair.FirstKeyFrame.Value;
double t0 = pair.FirstKeyFrame.Key;
double y1 = pair.SecondKeyFrame.Value;
double t1 = pair.SecondKeyFrame.Key;
/// <inheritdocs/>
public override double DoInterpolation(double t)
{
var pair = GetKeyFramePairByTime(t);
// Do linear parametric interpolation
return y0 + ((t - t0) / (t1 - t0)) * (y1 - y0);
});
double y0 = pair.FirstKeyFrame.Value;
double t0 = pair.FirstKeyFrame.Key;
double y1 = pair.SecondKeyFrame.Value;
double t1 = pair.SecondKeyFrame.Key;
// Do linear parametric interpolation
return y0 + ((t - t0) / (t1 - t0)) * (y1 - y0);
}
}
}

46
src/Avalonia.Animation/Keyframes/KeyFrames.cs

@ -39,14 +39,10 @@ namespace Avalonia.Animation.Keyframes
return obsMatch
.Where(p => p == true)
// Ignore triggers when global timers are paused.
.Where(p => Timing.GetGlobalPlayState() == PlayState.Run)
.Where(p => Timing.GetGlobalPlayState() != PlayState.Pause)
.Subscribe(_ =>
{
var timerObs = SetupAnimation(animation, control);
var interp = DoInterpolation(timerObs, animation, control)
.Select(p => (object)p);
control.Bind(Property, interp, BindingPriority.Animation);
var timerObs = RunKeyFrames(animation, control);
});
}
@ -86,33 +82,35 @@ namespace Avalonia.Animation.Keyframes
/// <summary>
/// Returns an observable timer with the specific Animation
/// duration and delay and applies the Animation's easing function.
/// Runs the KeyFrames Animation.
/// </summary>
public IObservable<double> SetupAnimation(Animation animation, Animatable control)
public IDisposable RunKeyFrames(Animation animation, Animatable control)
{
var newStateMachine = new KeyFramesStateMachine();
newStateMachine.Start(animation, control);
var playStateObs = Timing.AnimationStateTimer
.TakeWhile(p => !newStateMachine._unsubscribe)
.Select(p =>
{
return newStateMachine.Step(p);
});
// var newTimer = Timing.GetAnimationsTimer(animation, control, animation.Duration, animation.Delay);
return playStateObs.Select(t => animation.Easing.Ease(t));
}
var _kfStateMach = new KeyFramesStateMachine<T>();
_kfStateMach.Start(animation);
Timing.AnimationStateTimer
.TakeWhile(_ => !_kfStateMach._unsubscribe)
.Subscribe(p =>
{
_kfStateMach.Step(p, DoInterpolation);
});
return control.Bind(Property, _kfStateMach, BindingPriority.Animation);
}
/// <summary>
/// Interpolates the given keyframes to the control.
/// Interpolates a value given the desired time.
/// </summary>
public abstract IObservable<T> DoInterpolation(IObservable<double> timer, Animation animation,
Animatable control);
public abstract T DoInterpolation(double time);
// public abstract IObservable<T> DoInterpolation(IObservable<double> timer, Animation animation,
// Animatable control);
/// <summary>
/// Verifies and converts keyframe values according to this class type parameter.
/// Verifies and converts keyframe values according to this class's type parameter.
/// </summary>
private void VerifyConvertKeyFrames(Animation animation, Type type)
{

90
src/Avalonia.Animation/Keyframes/KeyFramesStateMachine.cs

@ -5,9 +5,8 @@ namespace Avalonia.Animation.Keyframes
/// <summary>
/// Provides statefulness for an iteration of a keyframe group animation.
/// </summary>
internal class KeyFramesStateMachine
internal class KeyFramesStateMachine<T> : IObservable<object>, IDisposable
{
ulong _delayTotalFrameCount,
_durationTotalFrameCount,
_delayFrameCount,
@ -18,11 +17,11 @@ namespace Avalonia.Animation.Keyframes
bool _isLooping, _isRepeating, _isReversed;
private PlaybackDirection _animationDirection;
KeyFramesStates _currentState;
KeyFramesStates _currentState, _savedState;
private Animation parentAnimation;
internal bool _unsubscribe = false;
private Animation _parentAnimation;
private Animatable _targetAnimatable;
double _outputTime = 0d;
private IObserver<object> targetObserver;
private enum KeyFramesStates
{
@ -32,21 +31,14 @@ namespace Avalonia.Animation.Keyframes
RUN_FORWARDS,
RUN_BACKWARDS,
RUN_COMPLETE,
STOP
PAUSE,
STOP,
DISPOSED
}
public void Start(Animation animation, Animatable control)
public void Start(Animation animation)
{
this._parentAnimation = animation;
this._targetAnimatable = control;
// int _delayFrameCount,
// _durationFrameCount,
// _repeatCount,
// _iterationDirection,
// _currentIteration,
// _totalIteration;
parentAnimation = animation;
_delayTotalFrameCount = (ulong)(animation.Delay.Ticks / Timing.FrameTick.Ticks);
_durationTotalFrameCount = (ulong)(animation.Duration.Ticks / Timing.FrameTick.Ticks);
@ -78,37 +70,39 @@ namespace Avalonia.Animation.Keyframes
}
break;
}
_animationDirection = animation.PlaybackDirection;
switch (_animationDirection)
switch (animation.PlaybackDirection)
{
case PlaybackDirection.Reverse:
case PlaybackDirection.AlternateReverse:
SetInitialPlaybackDirection(true);
_isReversed = true;
break;
default:
SetInitialPlaybackDirection(false);
_isReversed = false;
break;
}
_currentState = KeyFramesStates.DO_RUN;
}
private void SetInitialPlaybackDirection(bool isReversed)
{
_isReversed = isReversed;
}
private bool GetPlaybackDirection()
public double Step(PlayState _playState, Func<double, T> Interpolator)
{
_isReversed = !_isReversed;
return _isReversed;
}
if (_currentState == KeyFramesStates.DISPOSED) throw new InvalidProgramException("This KeyFrames Animation is already disposed.");
public double Step(PlayState _playState)
{
if (_playState == PlayState.Stop) _currentState = KeyFramesStates.STOP;
// Save state and pause the machine
if (_playState == PlayState.Pause && _currentState != KeyFramesStates.PAUSE)
{
_savedState = _currentState;
_currentState = KeyFramesStates.PAUSE;
}
// Resume the previous state
if (_playState != PlayState.Pause && _currentState == KeyFramesStates.PAUSE)
_currentState = _savedState;
checkstate:
switch (_currentState)
{
case KeyFramesStates.DO_DELAY:
@ -120,11 +114,22 @@ namespace Avalonia.Animation.Keyframes
return 0d;
case KeyFramesStates.DO_RUN:
// temporary stuff.
_currentState = KeyFramesStates.RUN_FORWARDS;
goto checkstate;
break;
case KeyFramesStates.RUN_FORWARDS:
// break;
// temporary stuff.
if (_durationFrameCount > _durationTotalFrameCount)
_currentState = KeyFramesStates.RUN_COMPLETE;
var tmp1 = (double)_durationFrameCount / _durationTotalFrameCount;
var easedTime = parentAnimation.Easing.Ease(tmp1);
_durationFrameCount++;
targetObserver.OnNext(Interpolator(easedTime));
break;
case KeyFramesStates.RUN_BACKWARDS:
// break;
@ -133,11 +138,24 @@ namespace Avalonia.Animation.Keyframes
// break;
case KeyFramesStates.STOP:
targetObserver.OnCompleted();
_unsubscribe = true;
break;
}
return 0;
}
public IDisposable Subscribe(IObserver<object> observer)
{
this.targetObserver = observer;
return this;
}
public void Dispose()
{
_unsubscribe = true;
_currentState = KeyFramesStates.DISPOSED;
targetObserver = null;
}
}
}

0
src/Avalonia.Animation/Direction.cs → src/Avalonia.Animation/PlaybackDirection.cs

4
src/Avalonia.DotNetCoreRuntime/AppBuilder.cs

@ -47,8 +47,6 @@ namespace Avalonia
//that CLR doesn't try to load dependencies before referencing method is jitted
if (os == OperatingSystemType.WinNT)
LoadWin32();
else if(os==OperatingSystemType.OSX)
LoadMonoMac();
else
LoadGtk3();
this.UseSkia();
@ -56,7 +54,7 @@ namespace Avalonia
return this;
}
void LoadMonoMac() => this.UseMonoMac();
void LoadWin32() => this.UseWin32();
void LoadGtk3() => this.UseGtk3();
}

4
src/Avalonia.Visuals/Animation/Keyframes/TransformKeyFrames.cs

@ -73,8 +73,6 @@ namespace Avalonia.Animation.Keyframes
}
/// <inheritdocs/>
public override IObservable<double> DoInterpolation(IObservable<double> timer, Animation animation, Animatable control) => null;
public override double DoInterpolation(double time) => 0;
}
}

Loading…
Cancel
Save