Browse Source

Finally, finally fix the linear parametric interpolation for DoubleKeyFrames 😄

Credits to Georgia Tech's keyframe slides : https://www.cc.gatech.edu/classes/AY2012/cs4496_spring/slides/Keyframe.pdf
pull/1461/head
Jumar Macato 9 years ago
parent
commit
ee1960ba00
  1. 16
      samples/RenderTest/Pages/AnimationsPage.xaml
  2. 59
      src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs

16
samples/RenderTest/Pages/AnimationsPage.xaml

@ -7,7 +7,6 @@
<Setter Property="Width" Value="100"/> <Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="100"/> <Setter Property="Height" Value="100"/>
</Style> </Style>
<Style Selector="Rectangle.Rect1:pointerover"> <Style Selector="Rectangle.Rect1:pointerover">
<Style.Animations> <Style.Animations>
<Animation Duration="0:0:2.5" Easing="BounceEaseInOut"> <Animation Duration="0:0:2.5" Easing="BounceEaseInOut">
@ -18,7 +17,6 @@
</Animation> </Animation>
</Style.Animations> </Style.Animations>
</Style> </Style>
<Style Selector="Rectangle.Rect2:pointerover"> <Style Selector="Rectangle.Rect2:pointerover">
<Style.Animations> <Style.Animations>
<Animation Duration="0:0:0.5" Easing="SineEaseInOut"> <Animation Duration="0:0:0.5" Easing="SineEaseInOut">
@ -33,21 +31,17 @@
</Animation> </Animation>
</Style.Animations> </Style.Animations>
</Style> </Style>
<Style Selector="Rectangle.Rect3:pointerover"> <Style Selector="Rectangle.Rect3:pointerover">
<Style.Animations> <Style.Animations>
<Animation Duration="0:0:3" Easing="BounceEaseInOut"> <Animation Duration="0:0:3" Easing="BounceEaseInOut">
<TransformKeyFrames Property="TranslateTransform.Y"> <TransformKeyFrames Property="TranslateTransform.Y">
<KeyFrame Cue="0%" Value="0"/> <KeyFrame Cue="0%" Value="0"/>
<KeyFrame Cue="48%" Value="-100"/> <KeyFrame Cue="48%" Value="-100"/>
<KeyFrame Cue="52%" Value="-100"/>
<KeyFrame Cue="100%" Value="0"/> <KeyFrame Cue="100%" Value="0"/>
</TransformKeyFrames> </TransformKeyFrames>
</Animation> </Animation>
</Style.Animations> </Style.Animations>
</Style> </Style>
<Style Selector="Rectangle.Rect4:pointerover"> <Style Selector="Rectangle.Rect4:pointerover">
<Style.Animations> <Style.Animations>
<Animation Duration="0:0:3" Easing="CircularEaseInOut"> <Animation Duration="0:0:3" Easing="CircularEaseInOut">
@ -60,12 +54,14 @@
</Animation> </Animation>
</Style.Animations> </Style.Animations>
</Style> </Style>
</Styles> </Styles>
</Grid.Styles> </Grid.Styles>
<StackPanel VerticalAlignment="Center" <StackPanel VerticalAlignment="Center"
HorizontalAlignment="Center"> HorizontalAlignment="Center"
<TextBlock HorizontalAlignment="Center">Hover to activate Transform Keyframe Animations.</TextBlock> ClipToBounds="False">
<TextBlock HorizontalAlignment="Center">
Hover to activate Transform Keyframe Animations.
</TextBlock>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
ClipToBounds="False"> ClipToBounds="False">
<Rectangle Classes="Test Rect1" Fill="DarkRed"> <Rectangle Classes="Test Rect1" Fill="DarkRed">

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

@ -30,43 +30,56 @@ namespace Avalonia.Animation.Keyframes
// Get a pair of keyframes to make the interpolation. // Get a pair of keyframes to make the interpolation.
KeyValuePair<double, double> firstCue, lastCue; KeyValuePair<double, double> firstCue, lastCue;
firstCue = sortedkeyValues.First();
lastCue = sortedkeyValues.Last();
// This should be changed later for a much more efficient one // This should be changed later for a much more efficient one
if (sortedkeyValues.Count() > 2) if (sortedkeyValues.Count() > 2)
{ {
bool isWithinRange_Start = DoubleUtils.AboutEqual(x, 0.0) || x > 0.0; bool OutsideRange_Start = DoubleUtils.AboutEqual(x, 0.0) || x < 0.0;
bool isWithinRange_End = DoubleUtils.AboutEqual(x, 1.0) || x < 1.0; bool OutsideRange_End = DoubleUtils.AboutEqual(x, 1.0) || x > 1.0;
var kyF = sortedkeyValues.ToArray();
if (isWithinRange_Start && isWithinRange_End) if (OutsideRange_Start)
{
firstCue = sortedkeyValues.Where(j => j.Key <= x).Last();
lastCue = sortedkeyValues.Where(j => j.Key >= x).First();
}
else if (!isWithinRange_Start)
{ {
firstCue = sortedkeyValues.First(); firstCue = kyF[0];
lastCue = sortedkeyValues.Skip(1).First(); lastCue = kyF[1];
} }
else if (!isWithinRange_End) else if (OutsideRange_End)
{ {
firstCue = sortedkeyValues.Skip(sortedkeyValues.Count() - 1).First(); var count = kyF.Count();
lastCue = sortedkeyValues.Last(); firstCue = kyF[count - 2];
lastCue = kyF[count - 1];
} }
else else
{ {
throw new InvalidOperationException firstCue = sortedkeyValues.Where(j => j.Key <= x).Last();
($"Can't find KeyFrames within the specified Easing time {x}"); lastCue = sortedkeyValues.Where(j => j.Key >= x).First();
} }
} }
else
{
firstCue = sortedkeyValues.First();
lastCue = sortedkeyValues.Last();
}
double x0, y0, x1, y1;
// Piecewise Linear interpolation, courtesy of wikipedia // Swap interpolants if its descending
var y0 = firstCue.Value; // if (firstCue.Value > lastCue.Value)
var x0 = firstCue.Key; // // {
var y1 = lastCue.Value; // y1 = firstCue.Value;
var x1 = lastCue.Key; // x1 = firstCue.Key;
var y = ((y0 * (x1 - x)) + (y1 * (x - x0))) / x1 - x0; // y0 = lastCue.Value;
// x0 = lastCue.Key;
// }
// else
// {
y0 = firstCue.Value;
x0 = firstCue.Key;
y1 = lastCue.Value;
x1 = lastCue.Key;
var y = y0 + ((x - x0)/(x1 - x0)) * (y1-y0);
return y; return y;
} }

Loading…
Cancel
Save