|
|
|
@ -19,7 +19,7 @@ namespace Avalonia.Animation |
|
|
|
/// <summary>
|
|
|
|
/// List of type-converted keyframes.
|
|
|
|
/// </summary>
|
|
|
|
private Dictionary<double, (T, bool isNeutral)> _convertedKeyframes = new Dictionary<double, (T, bool)>(); |
|
|
|
private readonly SortedList<double, (AnimatorKeyFrame, bool isNeutral)> _convertedKeyframes = new SortedList<double, (AnimatorKeyFrame, bool)>(); |
|
|
|
|
|
|
|
private bool _isVerfifiedAndConverted; |
|
|
|
|
|
|
|
@ -38,12 +38,11 @@ namespace Avalonia.Animation |
|
|
|
public virtual IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch) |
|
|
|
{ |
|
|
|
if (!_isVerfifiedAndConverted) |
|
|
|
VerifyConvertKeyFrames(animation, typeof(T)); |
|
|
|
VerifyConvertKeyFrames(); |
|
|
|
|
|
|
|
return obsMatch |
|
|
|
.Where(p => p == true) |
|
|
|
// Ignore triggers when global timers are paused.
|
|
|
|
.Where(p => Timing.GetGlobalPlayState() != PlayState.Pause) |
|
|
|
.Where(p => p && Timing.GetGlobalPlayState() != PlayState.Pause) |
|
|
|
.Subscribe(_ => |
|
|
|
{ |
|
|
|
var timerObs = RunKeyFrames(animation, control); |
|
|
|
@ -60,8 +59,8 @@ namespace Avalonia.Animation |
|
|
|
/// <param name="t">The time parameter, relative to the total animation time</param>
|
|
|
|
protected (double IntraKFTime, KeyFramePair<T> KFPair) GetKFPairAndIntraKFTime(double t) |
|
|
|
{ |
|
|
|
KeyValuePair<double, (T, bool)> firstCue, lastCue; |
|
|
|
int kvCount = _convertedKeyframes.Count(); |
|
|
|
KeyValuePair<double, (AnimatorKeyFrame frame, bool isNeutral)> firstCue, lastCue; |
|
|
|
int kvCount = _convertedKeyframes.Count; |
|
|
|
if (kvCount > 2) |
|
|
|
{ |
|
|
|
if (DoubleUtils.AboutEqual(t, 0.0) || t < 0.0) |
|
|
|
@ -76,8 +75,8 @@ namespace Avalonia.Animation |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
firstCue = _convertedKeyframes.Where(j => j.Key <= t).Last(); |
|
|
|
lastCue = _convertedKeyframes.Where(j => j.Key >= t).First(); |
|
|
|
firstCue = _convertedKeyframes.Last(j => j.Key <= t); |
|
|
|
lastCue = _convertedKeyframes.First(j => j.Key >= t); |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
@ -89,7 +88,9 @@ namespace Avalonia.Animation |
|
|
|
double t0 = firstCue.Key; |
|
|
|
double t1 = lastCue.Key; |
|
|
|
var intraframeTime = (t - t0) / (t1 - t0); |
|
|
|
return (intraframeTime, new KeyFramePair<T>(firstCue, lastCue)); |
|
|
|
var firstFrameData = (firstCue.Value.frame.GetTypedValue<T>(), firstCue.Value.isNeutral); |
|
|
|
var lastFrameData = (lastCue.Value.frame.GetTypedValue<T>(), lastCue.Value.isNeutral); |
|
|
|
return (intraframeTime, new KeyFramePair<T>(firstFrameData, lastFrameData)); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -98,17 +99,14 @@ namespace Avalonia.Animation |
|
|
|
/// </summary>
|
|
|
|
private IDisposable RunKeyFrames(Animation animation, Animatable control) |
|
|
|
{ |
|
|
|
var _kfStateMach = new AnimatorStateMachine<T>(); |
|
|
|
_kfStateMach.Initialize(animation, control, this); |
|
|
|
var stateMachine = new AnimatorStateMachine<T>(); |
|
|
|
stateMachine.Initialize(animation, control, this); |
|
|
|
|
|
|
|
Timing.AnimationStateTimer |
|
|
|
.TakeWhile(_ => !_kfStateMach._unsubscribe) |
|
|
|
.Subscribe(p => |
|
|
|
{ |
|
|
|
_kfStateMach.Step(p, DoInterpolation); |
|
|
|
}); |
|
|
|
.TakeWhile(_ => !stateMachine._unsubscribe) |
|
|
|
.Subscribe(p => stateMachine.Step(p, DoInterpolation)); |
|
|
|
|
|
|
|
return control.Bind(Property, _kfStateMach, BindingPriority.Animation); |
|
|
|
return control.Bind(Property, stateMachine, BindingPriority.Animation); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -119,39 +117,19 @@ namespace Avalonia.Animation |
|
|
|
/// <summary>
|
|
|
|
/// Verifies and converts keyframe values according to this class's target type.
|
|
|
|
/// </summary>
|
|
|
|
private void VerifyConvertKeyFrames(Animation animation, Type type) |
|
|
|
private void VerifyConvertKeyFrames() |
|
|
|
{ |
|
|
|
var typeConv = TypeDescriptor.GetConverter(type); |
|
|
|
|
|
|
|
foreach (AnimatorKeyFrame k in this) |
|
|
|
foreach (AnimatorKeyFrame keyframe in this) |
|
|
|
{ |
|
|
|
if (k.Value == null) |
|
|
|
{ |
|
|
|
throw new ArgumentNullException($"KeyFrame value can't be null."); |
|
|
|
} |
|
|
|
if (!typeConv.CanConvertTo(k.Value.GetType())) |
|
|
|
{ |
|
|
|
throw new InvalidCastException($"KeyFrame value doesnt match property type."); |
|
|
|
} |
|
|
|
|
|
|
|
T convertedValue = (T)typeConv.ConvertTo(k.Value, type); |
|
|
|
|
|
|
|
Cue _normalizedCue = k.Cue; |
|
|
|
|
|
|
|
if (k.timeSpanSet) |
|
|
|
{ |
|
|
|
_normalizedCue = new Cue(k.KeyTime.Ticks / animation.Duration.Ticks); |
|
|
|
} |
|
|
|
|
|
|
|
_convertedKeyframes.Add(_normalizedCue.CueValue, (convertedValue, false)); |
|
|
|
_convertedKeyframes.Add(keyframe.Cue.CueValue, (keyframe, false)); |
|
|
|
} |
|
|
|
|
|
|
|
SortKeyFrameCues(_convertedKeyframes); |
|
|
|
AddNeutralKeyFramesIfNeeded(); |
|
|
|
_isVerfifiedAndConverted = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
private void SortKeyFrameCues(Dictionary<double, (T, bool)> convertedValues) |
|
|
|
private void AddNeutralKeyFramesIfNeeded() |
|
|
|
{ |
|
|
|
bool hasStartKey, hasEndKey; |
|
|
|
hasStartKey = hasEndKey = false; |
|
|
|
@ -170,23 +148,20 @@ namespace Avalonia.Animation |
|
|
|
} |
|
|
|
|
|
|
|
if (!hasStartKey || !hasEndKey) |
|
|
|
AddNeutralKeyFrames(hasStartKey, hasEndKey, _convertedKeyframes); |
|
|
|
|
|
|
|
_convertedKeyframes = _convertedKeyframes.OrderBy(p => p.Key) |
|
|
|
.ToDictionary((k) => k.Key, (v) => v.Value); |
|
|
|
AddNeutralKeyFrames(hasStartKey, hasEndKey); |
|
|
|
} |
|
|
|
|
|
|
|
private void AddNeutralKeyFrames(bool hasStartKey, bool hasEndKey, Dictionary<double, (T, bool)> convertedKeyframes) |
|
|
|
private void AddNeutralKeyFrames(bool hasStartKey, bool hasEndKey) |
|
|
|
{ |
|
|
|
if (!hasStartKey) |
|
|
|
{ |
|
|
|
convertedKeyframes.Add(0.0d, (default(T), true)); |
|
|
|
_convertedKeyframes.Add(0.0d, (new AnimatorKeyFrame { Value = default(T) }, true)); |
|
|
|
} |
|
|
|
|
|
|
|
if (!hasEndKey) |
|
|
|
{ |
|
|
|
convertedKeyframes.Add(1.0d, (default(T), true)); |
|
|
|
_convertedKeyframes.Add(1.0d, (new AnimatorKeyFrame { Value = default(T) }, true)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|