committed by
GitHub
63 changed files with 1502 additions and 567 deletions
@ -0,0 +1,21 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="bool"/> properties.
|
|||
/// </summary>
|
|||
public class BoolAnimator : Animator<bool> |
|||
{ |
|||
/// <inheritdocs/>
|
|||
public override bool Interpolate(double progress, bool oldValue, bool newValue) |
|||
{ |
|||
if(progress >= 1d) |
|||
return newValue; |
|||
if(progress >= 0) |
|||
return oldValue; |
|||
return oldValue; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="byte"/> properties.
|
|||
/// </summary>
|
|||
public class ByteAnimator : Animator<byte> |
|||
{ |
|||
const double maxVal = (double)byte.MaxValue; |
|||
|
|||
/// <inheritdocs/>
|
|||
public override byte Interpolate(double progress, byte oldValue, byte newValue) |
|||
{ |
|||
var normOV = oldValue / maxVal; |
|||
var normNV = newValue / maxVal; |
|||
var deltaV = normNV - normOV; |
|||
return (byte)Math.Round(maxVal * ((deltaV * progress) + normOV)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="decimal"/> properties.
|
|||
/// </summary>
|
|||
public class DecimalAnimator : Animator<decimal> |
|||
{ |
|||
/// <inheritdocs/>
|
|||
public override decimal Interpolate(double progress, decimal oldValue, decimal newValue) |
|||
{ |
|||
return ((newValue - oldValue) * (decimal)progress) + oldValue; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="double"/> properties.
|
|||
/// </summary>
|
|||
public class DoubleAnimator : Animator<double> |
|||
{ |
|||
/// <inheritdocs/>
|
|||
public override double Interpolate(double progress, double oldValue, double newValue) |
|||
{ |
|||
return ((newValue - oldValue) * progress) + oldValue; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="float"/> properties.
|
|||
/// </summary>
|
|||
public class FloatAnimator : Animator<float> |
|||
{ |
|||
/// <inheritdocs/>
|
|||
public override float Interpolate(double progress, float oldValue, float newValue) |
|||
{ |
|||
return (float)(((newValue - oldValue) * progress) + oldValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="Int16"/> properties.
|
|||
/// </summary>
|
|||
public class Int16Animator : Animator<Int16> |
|||
{ |
|||
const double maxVal = (double)Int16.MaxValue; |
|||
|
|||
/// <inheritdocs/>
|
|||
public override Int16 Interpolate(double progress, Int16 oldValue, Int16 newValue) |
|||
{ |
|||
var normOV = oldValue / maxVal; |
|||
var normNV = newValue / maxVal; |
|||
var deltaV = normNV - normOV; |
|||
return (Int16)Math.Round(maxVal * ((deltaV * progress) + normOV)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="Int32"/> properties.
|
|||
/// </summary>
|
|||
public class Int32Animator : Animator<Int32> |
|||
{ |
|||
const double maxVal = (double)Int32.MaxValue; |
|||
|
|||
/// <inheritdocs/>
|
|||
public override Int32 Interpolate(double progress, Int32 oldValue, Int32 newValue) |
|||
{ |
|||
var normOV = oldValue / maxVal; |
|||
var normNV = newValue / maxVal; |
|||
var deltaV = normNV - normOV; |
|||
return (Int32)Math.Round(maxVal * ((deltaV * progress) + normOV)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="Int64"/> properties.
|
|||
/// </summary>
|
|||
public class Int64Animator : Animator<Int64> |
|||
{ |
|||
const double maxVal = (double)Int64.MaxValue; |
|||
|
|||
/// <inheritdocs/>
|
|||
public override Int64 Interpolate(double progress, Int64 oldValue, Int64 newValue) |
|||
{ |
|||
var normOV = oldValue / maxVal; |
|||
var normNV = newValue / maxVal; |
|||
var deltaV = normNV - normOV; |
|||
return (Int64)Math.Round(maxVal * ((deltaV * progress) + normOV)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="UInt16"/> properties.
|
|||
/// </summary>
|
|||
public class UInt16Animator : Animator<UInt16> |
|||
{ |
|||
const double maxVal = (double)UInt16.MaxValue; |
|||
|
|||
/// <inheritdocs/>
|
|||
public override UInt16 Interpolate(double progress, UInt16 oldValue, UInt16 newValue) |
|||
{ |
|||
var normOV = oldValue / maxVal; |
|||
var normNV = newValue / maxVal; |
|||
var deltaV = normNV - normOV; |
|||
return (UInt16)Math.Round(maxVal * ((deltaV * progress) + normOV)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="UInt32"/> properties.
|
|||
/// </summary>
|
|||
public class UInt32Animator : Animator<UInt32> |
|||
{ |
|||
const double maxVal = (double)UInt32.MaxValue; |
|||
|
|||
/// <inheritdocs/>
|
|||
public override UInt32 Interpolate(double progress, UInt32 oldValue, UInt32 newValue) |
|||
{ |
|||
var normOV = oldValue / maxVal; |
|||
var normNV = newValue / maxVal; |
|||
var deltaV = normNV - normOV; |
|||
return (UInt32)Math.Round(maxVal * ((deltaV * progress) + normOV)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="UInt64"/> properties.
|
|||
/// </summary>
|
|||
public class UInt64Animator : Animator<UInt64> |
|||
{ |
|||
const double maxVal = (double)UInt64.MaxValue; |
|||
|
|||
/// <inheritdocs/>
|
|||
public override UInt64 Interpolate(double progress, UInt64 oldValue, UInt64 newValue) |
|||
{ |
|||
var normOV = oldValue / maxVal; |
|||
var normNV = newValue / maxVal; |
|||
var deltaV = normNV - normOV; |
|||
return (UInt64)Math.Round(maxVal * ((deltaV * progress) + normOV)); |
|||
} |
|||
} |
|||
} |
|||
@ -1,35 +0,0 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Avalonia.Animation |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="double"/> properties.
|
|||
/// </summary>
|
|||
public class DoubleAnimator : Animator<double> |
|||
{ |
|||
|
|||
/// <inheritdocs/>
|
|||
protected override double DoInterpolation(double t, double neutralValue) |
|||
{ |
|||
var pair = GetKFPairAndIntraKFTime(t); |
|||
double y0, y1; |
|||
|
|||
var firstKF = pair.KFPair.FirstKeyFrame; |
|||
var secondKF = pair.KFPair.SecondKeyFrame; |
|||
|
|||
if (firstKF.isNeutral) |
|||
y0 = neutralValue; |
|||
else |
|||
y0 = firstKF.TargetValue; |
|||
|
|||
if (secondKF.isNeutral) |
|||
y1 = neutralValue; |
|||
else |
|||
y1 = secondKF.TargetValue; |
|||
|
|||
// Do linear parametric interpolation
|
|||
return y0 + (pair.IntraKFTime) * (y1 - y0); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,176 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Globalization; |
|||
|
|||
namespace Avalonia.Animation |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the valid modes for a <see cref="IterationCount"/>.
|
|||
/// </summary>
|
|||
public enum IterationType |
|||
{ |
|||
Many, |
|||
Infinite |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines the number of iterations of an animation.
|
|||
/// Also defines its repeat behavior.
|
|||
/// </summary>
|
|||
[TypeConverter(typeof(IterationCountTypeConverter))] |
|||
public struct IterationCount : IEquatable<IterationCount> |
|||
{ |
|||
private readonly IterationType _type; |
|||
private readonly ulong _value; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="IterationCount"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="value">The number of iterations of an animation.</param>
|
|||
public IterationCount(ulong value) |
|||
: this(value, IterationType.Many) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="IterationCount"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="value">The size of the IterationCount.</param>
|
|||
/// <param name="type">The unit of the IterationCount.</param>
|
|||
public IterationCount(ulong value, IterationType type) |
|||
{ |
|||
if (type > IterationType.Infinite) |
|||
{ |
|||
throw new ArgumentException("Invalid value", "type"); |
|||
} |
|||
|
|||
_type = type; |
|||
_value = value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets an instance of <see cref="IterationCount"/> that indicates that an animation
|
|||
/// should repeat forever.
|
|||
/// </summary>
|
|||
public static IterationCount Infinite => new IterationCount(0, IterationType.Infinite); |
|||
|
|||
/// <summary>
|
|||
/// Gets the unit of the <see cref="IterationCount"/>.
|
|||
/// </summary>
|
|||
public IterationType RepeatType => _type; |
|||
|
|||
/// <summary>
|
|||
/// Gets a value that indicates whether the <see cref="IterationCount"/> is set to loop.
|
|||
/// </summary>
|
|||
public bool IsInfinite => _type == IterationType.Infinite; |
|||
|
|||
/// <summary>
|
|||
/// Gets the number of repeat iterations.
|
|||
/// </summary>
|
|||
public ulong Value => _value; |
|||
|
|||
/// <summary>
|
|||
/// Compares two IterationCount structures for equality.
|
|||
/// </summary>
|
|||
/// <param name="a">The first IterationCount.</param>
|
|||
/// <param name="b">The second IterationCount.</param>
|
|||
/// <returns>True if the structures are equal, otherwise false.</returns>
|
|||
public static bool operator ==(IterationCount a, IterationCount b) |
|||
{ |
|||
return (a.IsInfinite && b.IsInfinite) |
|||
|| (a._value == b._value && a._type == b._type); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two IterationCount structures for inequality.
|
|||
/// </summary>
|
|||
/// <param name="rc1">The first IterationCount.</param>
|
|||
/// <param name="rc2">The first IterationCount.</param>
|
|||
/// <returns>True if the structures are unequal, otherwise false.</returns>
|
|||
public static bool operator !=(IterationCount rc1, IterationCount rc2) |
|||
{ |
|||
return !(rc1 == rc2); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether the <see cref="IterationCount"/> is equal to the specified object.
|
|||
/// </summary>
|
|||
/// <param name="o">The object with which to test equality.</param>
|
|||
/// <returns>True if the objects are equal, otherwise false.</returns>
|
|||
public override bool Equals(object o) |
|||
{ |
|||
if (o == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (!(o is IterationCount)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return this == (IterationCount)o; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two IterationCount structures for equality.
|
|||
/// </summary>
|
|||
/// <param name="IterationCount">The structure with which to test equality.</param>
|
|||
/// <returns>True if the structures are equal, otherwise false.</returns>
|
|||
public bool Equals(IterationCount IterationCount) |
|||
{ |
|||
return this == IterationCount; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a hash code for the IterationCount.
|
|||
/// </summary>
|
|||
/// <returns>The hash code.</returns>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return _value.GetHashCode() ^ _type.GetHashCode(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a string representation of the <see cref="IterationCount"/>.
|
|||
/// </summary>
|
|||
/// <returns>The string representation.</returns>
|
|||
public override string ToString() |
|||
{ |
|||
if (IsInfinite) |
|||
{ |
|||
return "Infinite"; |
|||
} |
|||
|
|||
string s = _value.ToString(); |
|||
return s; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Parses a string to return a <see cref="IterationCount"/>.
|
|||
/// </summary>
|
|||
/// <param name="s">The string.</param>
|
|||
/// <returns>The <see cref="IterationCount"/>.</returns>
|
|||
public static IterationCount Parse(string s) |
|||
{ |
|||
s = s.ToUpperInvariant().Trim(); |
|||
|
|||
if (s.EndsWith("INFINITE")) |
|||
{ |
|||
return Infinite; |
|||
} |
|||
else |
|||
{ |
|||
if (s.StartsWith("-")) |
|||
throw new InvalidCastException("IterationCount can't be a negative number."); |
|||
|
|||
var value = ulong.Parse(s, CultureInfo.InvariantCulture); |
|||
|
|||
return new IterationCount(value); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,33 +0,0 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Avalonia.Animation |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a pair of keyframe, usually the
|
|||
/// Start and End keyframes of a <see cref="Animator{T}"/> object.
|
|||
/// </summary>
|
|||
public struct KeyFramePair<T> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes this <see cref="KeyFramePair{T}"/>
|
|||
/// </summary>
|
|||
/// <param name="FirstKeyFrame"></param>
|
|||
/// <param name="LastKeyFrame"></param>
|
|||
public KeyFramePair((T TargetValue, bool isNeutral) FirstKeyFrame, (T TargetValue, bool isNeutral) LastKeyFrame) : this() |
|||
{ |
|||
this.FirstKeyFrame = FirstKeyFrame; |
|||
this.SecondKeyFrame = LastKeyFrame; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// First <see cref="KeyFrame"/> object.
|
|||
/// </summary>
|
|||
public (T TargetValue, bool isNeutral) FirstKeyFrame { get; } |
|||
|
|||
/// <summary>
|
|||
/// Second <see cref="KeyFrame"/> object.
|
|||
/// </summary>
|
|||
public (T TargetValue, bool isNeutral) SecondKeyFrame { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Avalonia.Collections; |
|||
|
|||
namespace Avalonia.Animation |
|||
{ |
|||
/// <summary>
|
|||
/// A collection of <see cref="KeyFrame"/>s.
|
|||
/// </summary>
|
|||
public class KeyFrames : AvaloniaList<KeyFrame> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="KeyFrames"/> class.
|
|||
/// </summary>
|
|||
public KeyFrames() |
|||
{ |
|||
ResetBehavior = ResetBehavior.Remove; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="KeyFrames"/> class.
|
|||
/// </summary>
|
|||
/// <param name="items">The initial items in the collection.</param>
|
|||
public KeyFrames(IEnumerable<KeyFrame> items) |
|||
: base(items) |
|||
{ |
|||
ResetBehavior = ResetBehavior.Remove; |
|||
} |
|||
} |
|||
} |
|||
@ -1,199 +0,0 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Globalization; |
|||
|
|||
namespace Avalonia.Animation |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the valid modes for a <see cref="RepeatCount"/>.
|
|||
/// </summary>
|
|||
public enum RepeatType |
|||
{ |
|||
None, |
|||
Repeat, |
|||
Loop |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines the number of iterations of an animation.
|
|||
/// Also defines its repeat behavior.
|
|||
/// </summary>
|
|||
[TypeConverter(typeof(RepeatCountTypeConverter))] |
|||
public struct RepeatCount : IEquatable<RepeatCount> |
|||
{ |
|||
private readonly RepeatType _type; |
|||
private readonly ulong _value; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="RepeatCount"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="value">The number of iterations of an animation.</param>
|
|||
public RepeatCount(ulong value) |
|||
: this(value, RepeatType.Repeat) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="RepeatCount"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="value">The size of the RepeatCount.</param>
|
|||
/// <param name="type">The unit of the RepeatCount.</param>
|
|||
public RepeatCount(ulong value, RepeatType type) |
|||
{ |
|||
if (type < RepeatType.None || type > RepeatType.Loop) |
|||
{ |
|||
throw new ArgumentException("Invalid value", "type"); |
|||
} |
|||
|
|||
_type = type; |
|||
_value = value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets an instance of <see cref="RepeatCount"/> that indicates that an animation
|
|||
/// should repeat forever.
|
|||
/// </summary>
|
|||
public static RepeatCount Loop => new RepeatCount(0, RepeatType.Loop); |
|||
|
|||
/// <summary>
|
|||
/// Gets an instance of <see cref="RepeatCount"/> that indicates that an animation
|
|||
/// should not repeat.
|
|||
/// </summary>
|
|||
public static RepeatCount None => new RepeatCount(0, RepeatType.None); |
|||
|
|||
/// <summary>
|
|||
/// Gets the unit of the <see cref="RepeatCount"/>.
|
|||
/// </summary>
|
|||
public RepeatType RepeatType => _type; |
|||
|
|||
/// <summary>
|
|||
/// Gets a value that indicates whether the <see cref="RepeatCount"/> is set to loop.
|
|||
/// </summary>
|
|||
public bool IsLoop => _type == RepeatType.Loop; |
|||
|
|||
/// <summary>
|
|||
/// Gets a value that indicates whether the <see cref="RepeatCount"/> is set to not repeat.
|
|||
/// </summary>
|
|||
public bool IsNone => _type == RepeatType.None; |
|||
|
|||
/// <summary>
|
|||
/// Gets the number of repeat iterations.
|
|||
/// </summary>
|
|||
public ulong Value => _value; |
|||
|
|||
/// <summary>
|
|||
/// Compares two RepeatCount structures for equality.
|
|||
/// </summary>
|
|||
/// <param name="a">The first RepeatCount.</param>
|
|||
/// <param name="b">The second RepeatCount.</param>
|
|||
/// <returns>True if the structures are equal, otherwise false.</returns>
|
|||
public static bool operator ==(RepeatCount a, RepeatCount b) |
|||
{ |
|||
return (a.IsNone && b.IsNone) && (a.IsLoop && b.IsLoop) |
|||
|| (a._value == b._value && a._type == b._type); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two RepeatCount structures for inequality.
|
|||
/// </summary>
|
|||
/// <param name="rc1">The first RepeatCount.</param>
|
|||
/// <param name="rc2">The first RepeatCount.</param>
|
|||
/// <returns>True if the structures are unequal, otherwise false.</returns>
|
|||
public static bool operator !=(RepeatCount rc1, RepeatCount rc2) |
|||
{ |
|||
return !(rc1 == rc2); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether the <see cref="RepeatCount"/> is equal to the specified object.
|
|||
/// </summary>
|
|||
/// <param name="o">The object with which to test equality.</param>
|
|||
/// <returns>True if the objects are equal, otherwise false.</returns>
|
|||
public override bool Equals(object o) |
|||
{ |
|||
if (o == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (!(o is RepeatCount)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return this == (RepeatCount)o; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two RepeatCount structures for equality.
|
|||
/// </summary>
|
|||
/// <param name="RepeatCount">The structure with which to test equality.</param>
|
|||
/// <returns>True if the structures are equal, otherwise false.</returns>
|
|||
public bool Equals(RepeatCount RepeatCount) |
|||
{ |
|||
return this == RepeatCount; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a hash code for the RepeatCount.
|
|||
/// </summary>
|
|||
/// <returns>The hash code.</returns>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return _value.GetHashCode() ^ _type.GetHashCode(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a string representation of the <see cref="RepeatCount"/>.
|
|||
/// </summary>
|
|||
/// <returns>The string representation.</returns>
|
|||
public override string ToString() |
|||
{ |
|||
if (IsLoop) |
|||
{ |
|||
return "Auto"; |
|||
} |
|||
else if (IsNone) |
|||
{ |
|||
return "None"; |
|||
} |
|||
|
|||
string s = _value.ToString(); |
|||
return s; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Parses a string to return a <see cref="RepeatCount"/>.
|
|||
/// </summary>
|
|||
/// <param name="s">The string.</param>
|
|||
/// <returns>The <see cref="RepeatCount"/>.</returns>
|
|||
public static RepeatCount Parse(string s) |
|||
{ |
|||
s = s.ToUpperInvariant().Trim(); |
|||
|
|||
if (s == "NONE") |
|||
{ |
|||
return None; |
|||
} |
|||
else if (s.EndsWith("LOOP")) |
|||
{ |
|||
return Loop; |
|||
} |
|||
else |
|||
{ |
|||
if(s.StartsWith("-")) |
|||
throw new InvalidCastException("RepeatCount can't be a negative number."); |
|||
|
|||
var value = ulong.Parse(s, CultureInfo.InvariantCulture); |
|||
|
|||
if (value == 1) |
|||
return None; |
|||
|
|||
return new RepeatCount(value); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
// Original color interpolation code was written by Romain Guy and Francois Blavoet
|
|||
// and adopted from LottieSharp Project (https://github.com/ascora/LottieSharp).
|
|||
|
|||
using System; |
|||
using System.Reactive.Disposables; |
|||
using Avalonia.Logging; |
|||
using Avalonia.Media; |
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that interpolates <see cref="Color"/> through
|
|||
/// gamma sRGB color space for better visual result.
|
|||
/// </summary>
|
|||
public class ColorAnimator : Animator<Color> |
|||
{ |
|||
// Opto-electronic conversion function for the sRGB color space
|
|||
// Takes a gamma-encoded sRGB value and converts it to a linear sRGB value
|
|||
private static double OECF_sRGB(double linear) |
|||
{ |
|||
// IEC 61966-2-1:1999
|
|||
return linear <= 0.0031308d ? linear * 12.92d : (double)(Math.Pow(linear, 1.0d / 2.4d) * 1.055d - 0.055d); |
|||
} |
|||
|
|||
// Electro-optical conversion function for the sRGB color space
|
|||
// Takes a linear sRGB value and converts it to a gamma-encoded sRGB value
|
|||
private static double EOCF_sRGB(double srgb) |
|||
{ |
|||
// IEC 61966-2-1:1999
|
|||
return srgb <= 0.04045d ? srgb / 12.92d : (double)Math.Pow((srgb + 0.055d) / 1.055d, 2.4d); |
|||
} |
|||
|
|||
public override Color Interpolate(double progress, Color oldValue, Color newValue) |
|||
{ |
|||
// normalize sRGB values.
|
|||
var oldA = oldValue.A / 255d; |
|||
var oldR = oldValue.R / 255d; |
|||
var oldG = oldValue.G / 255d; |
|||
var oldB = oldValue.B / 255d; |
|||
|
|||
var newA = newValue.A / 255d; |
|||
var newR = newValue.R / 255d; |
|||
var newG = newValue.G / 255d; |
|||
var newB = newValue.B / 255d; |
|||
|
|||
// convert from sRGB to linear
|
|||
oldR = EOCF_sRGB(oldR); |
|||
oldG = EOCF_sRGB(oldG); |
|||
oldB = EOCF_sRGB(oldB); |
|||
|
|||
newR = EOCF_sRGB(newR); |
|||
newG = EOCF_sRGB(newG); |
|||
newB = EOCF_sRGB(newB); |
|||
|
|||
// compute the interpolated color in linear space
|
|||
var a = oldA + progress * (newA - oldA); |
|||
var r = oldR + progress * (newR - oldR); |
|||
var g = oldG + progress * (newG - oldG); |
|||
var b = oldB + progress * (newB - oldB); |
|||
|
|||
// convert back to sRGB in the [0..255] range
|
|||
a = a * 255d; |
|||
r = OECF_sRGB(r) * 255d; |
|||
g = OECF_sRGB(g) * 255d; |
|||
b = OECF_sRGB(b) * 255d; |
|||
|
|||
return new Color((byte)Math.Round(a), (byte)Math.Round(r), (byte)Math.Round(g), (byte)Math.Round(b)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using System; |
|||
using Avalonia.Logging; |
|||
using Avalonia.Media; |
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="CornerRadius"/> properties.
|
|||
/// </summary>
|
|||
public class CornerRadiusAnimator : Animator<CornerRadius> |
|||
{ |
|||
public override CornerRadius Interpolate(double progress, CornerRadius oldValue, CornerRadius newValue) |
|||
{ |
|||
var deltaTL = newValue.TopLeft - oldValue.TopLeft; |
|||
var deltaTR = newValue.TopRight - oldValue.TopRight; |
|||
var deltaBR = newValue.BottomRight - oldValue.BottomRight; |
|||
var deltaBL = newValue.BottomLeft - oldValue.BottomLeft; |
|||
|
|||
var nTL = progress * deltaTL + oldValue.TopLeft; |
|||
var nTR = progress * deltaTR + oldValue.TopRight; |
|||
var nBR = progress * deltaBR + oldValue.BottomRight; |
|||
var nBL = progress * deltaBL + oldValue.BottomLeft; |
|||
|
|||
return new CornerRadius(nTL, nTR, nBR, nBL); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using Avalonia.Logging; |
|||
using Avalonia.Media; |
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="Point"/> properties.
|
|||
/// </summary>
|
|||
public class PointAnimator : Animator<Point> |
|||
{ |
|||
public override Point Interpolate(double progress, Point oldValue, Point newValue) |
|||
{ |
|||
return ((newValue - oldValue) * progress) + oldValue; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using System; |
|||
using Avalonia.Logging; |
|||
using Avalonia.Media; |
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="Rect"/> properties.
|
|||
/// </summary>
|
|||
public class RectAnimator : Animator<Rect> |
|||
{ |
|||
public override Rect Interpolate(double progress, Rect oldValue, Rect newValue) |
|||
{ |
|||
var deltaPos = newValue.Position - oldValue.Position; |
|||
var deltaSize = newValue.Size - oldValue.Size; |
|||
|
|||
var newPos = (deltaPos * progress) + oldValue.Position; |
|||
var newSize = (deltaSize * progress) + oldValue.Size; |
|||
|
|||
return new Rect(newPos, newSize); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using Avalonia.Logging; |
|||
using Avalonia.Media; |
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="Size"/> properties.
|
|||
/// </summary>
|
|||
public class SizeAnimator : Animator<Size> |
|||
{ |
|||
public override Size Interpolate(double progress, Size oldValue, Size newValue) |
|||
{ |
|||
return ((newValue - oldValue) * progress) + oldValue; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
using System; |
|||
using System.Reactive.Disposables; |
|||
using Avalonia.Logging; |
|||
using Avalonia.Media; |
|||
using Avalonia.Media.Immutable; |
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="SolidColorBrush"/>.
|
|||
/// </summary>
|
|||
public class SolidColorBrushAnimator : Animator<SolidColorBrush> |
|||
{ |
|||
ColorAnimator _colorAnimator; |
|||
|
|||
void InitializeColorAnimator() |
|||
{ |
|||
_colorAnimator = new ColorAnimator(); |
|||
|
|||
foreach (AnimatorKeyFrame keyframe in this) |
|||
{ |
|||
_colorAnimator.Add(keyframe); |
|||
} |
|||
|
|||
_colorAnimator.Property = SolidColorBrush.ColorProperty; |
|||
} |
|||
|
|||
public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> match, Action onComplete) |
|||
{ |
|||
foreach (var keyframe in this) |
|||
{ |
|||
if (keyframe.Value as ISolidColorBrush == null) |
|||
return Disposable.Empty; |
|||
|
|||
// Preprocess keyframe values to Color if the xaml parser converts them to ISCB.
|
|||
if (keyframe.Value.GetType() == typeof(ImmutableSolidColorBrush)) |
|||
{ |
|||
keyframe.Value = ((ImmutableSolidColorBrush)keyframe.Value).Color; |
|||
} |
|||
} |
|||
|
|||
// Add SCB if the target prop is empty.
|
|||
if (control.GetValue(Property) == null) |
|||
control.SetValue(Property, new SolidColorBrush(Colors.Transparent)); |
|||
|
|||
var targetVal = control.GetValue(Property); |
|||
|
|||
// Continue if target prop is not empty & is a SolidColorBrush derivative.
|
|||
if (typeof(ISolidColorBrush).IsAssignableFrom(targetVal.GetType())) |
|||
{ |
|||
if (_colorAnimator == null) |
|||
InitializeColorAnimator(); |
|||
|
|||
SolidColorBrush finalTarget; |
|||
|
|||
// If it's ISCB, change it back to SCB.
|
|||
if (targetVal.GetType() == typeof(ImmutableSolidColorBrush)) |
|||
{ |
|||
var col = (ImmutableSolidColorBrush)targetVal; |
|||
targetVal = new SolidColorBrush(col.Color); |
|||
control.SetValue(Property, targetVal); |
|||
} |
|||
|
|||
finalTarget = targetVal as SolidColorBrush; |
|||
|
|||
return _colorAnimator.Apply(animation, finalTarget, clock ?? control.Clock, match, onComplete); |
|||
} |
|||
|
|||
return Disposable.Empty; |
|||
} |
|||
|
|||
public override SolidColorBrush Interpolate(double p, SolidColorBrush o, SolidColorBrush n) => null; |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using Avalonia.Logging; |
|||
using Avalonia.Media; |
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="Thickness"/> properties.
|
|||
/// </summary>
|
|||
public class ThicknessAnimator : Animator<Thickness> |
|||
{ |
|||
public override Thickness Interpolate(double progress, Thickness oldValue, Thickness newValue) |
|||
{ |
|||
return ((newValue - oldValue) * progress) + oldValue; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using Avalonia.Logging; |
|||
using Avalonia.Media; |
|||
|
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="Vector"/> properties.
|
|||
/// </summary>
|
|||
public class VectorAnimator : Animator<Vector> |
|||
{ |
|||
public override Vector Interpolate(double progress, Vector oldValue, Vector newValue) |
|||
{ |
|||
return ((newValue - oldValue) * progress) + oldValue; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Reactive.Linq; |
|||
|
|||
namespace Avalonia.Animation |
|||
{ |
|||
/// <summary>
|
|||
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="CornerRadius"/> type.
|
|||
/// </summary>
|
|||
public class CornerRadiusTransition : Transition<CornerRadius> |
|||
{ |
|||
/// <inheritdocs/>
|
|||
public override IObservable<CornerRadius> DoTransition(IObservable<double> progress, CornerRadius oldValue, CornerRadius newValue) |
|||
{ |
|||
return progress |
|||
.Select(p => |
|||
{ |
|||
var f = Easing.Ease(p); |
|||
|
|||
var deltaTL = newValue.TopLeft - oldValue.TopLeft; |
|||
var deltaTR = newValue.TopRight - oldValue.TopRight; |
|||
var deltaBR = newValue.BottomRight - oldValue.BottomRight; |
|||
var deltaBL = newValue.BottomLeft - oldValue.BottomLeft; |
|||
|
|||
var nTL = f * deltaTL + oldValue.TopLeft; |
|||
var nTR = f * deltaTR + oldValue.TopRight; |
|||
var nBR = f * deltaBR + oldValue.BottomRight; |
|||
var nBL = f * deltaBL + oldValue.BottomLeft; |
|||
|
|||
return new CornerRadius(nTL, nTR, nBR, nBL); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Reactive.Linq; |
|||
|
|||
namespace Avalonia.Animation |
|||
{ |
|||
/// <summary>
|
|||
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="Size"/> type.
|
|||
/// </summary>
|
|||
public class SizeTransition : Transition<Size> |
|||
{ |
|||
/// <inheritdocs/>
|
|||
public override IObservable<Size> DoTransition(IObservable<double> progress, Size oldValue, Size newValue) |
|||
{ |
|||
return progress |
|||
.Select(p => |
|||
{ |
|||
var f = Easing.Ease(p); |
|||
return ((newValue - oldValue) * f) + oldValue; |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Reactive.Linq; |
|||
|
|||
namespace Avalonia.Animation |
|||
{ |
|||
/// <summary>
|
|||
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="Vector"/> type.
|
|||
/// </summary>
|
|||
public class VectorTransition : Transition<Vector> |
|||
{ |
|||
/// <inheritdocs/>
|
|||
public override IObservable<Vector> DoTransition(IObservable<double> progress, Vector oldValue, Vector newValue) |
|||
{ |
|||
return progress |
|||
.Select(p => |
|||
{ |
|||
var f = Easing.Ease(p); |
|||
return ((newValue - oldValue) * f) + oldValue; |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,77 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Animation; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Styling; |
|||
using Avalonia.UnitTests; |
|||
using Avalonia.Data; |
|||
using Xunit; |
|||
|
|||
namespace Avalonia.Animation.UnitTests |
|||
{ |
|||
public class AnimationIterationTests |
|||
{ |
|||
[Fact] |
|||
public void Check_Initial_Inter_and_Trailing_Delay_Values() |
|||
{ |
|||
var keyframe1 = new KeyFrame() |
|||
{ |
|||
Setters = |
|||
{ |
|||
new Setter(Border.WidthProperty, 200d), |
|||
}, |
|||
Cue = new Cue(1d) |
|||
}; |
|||
|
|||
var keyframe2 = new KeyFrame() |
|||
{ |
|||
Setters = |
|||
{ |
|||
new Setter(Border.WidthProperty, 100d), |
|||
}, |
|||
Cue = new Cue(0d) |
|||
}; |
|||
|
|||
var animation = new Animation() |
|||
{ |
|||
Duration = TimeSpan.FromSeconds(3), |
|||
Delay = TimeSpan.FromSeconds(3), |
|||
DelayBetweenIterations = TimeSpan.FromSeconds(3), |
|||
IterationCount = new IterationCount(2), |
|||
Children = |
|||
{ |
|||
keyframe2, |
|||
keyframe1 |
|||
} |
|||
}; |
|||
|
|||
var border = new Border() |
|||
{ |
|||
Height = 100d, |
|||
Width = 100d |
|||
}; |
|||
|
|||
var clock = new TestClock(); |
|||
var animationRun = animation.RunAsync(border, clock); |
|||
|
|||
clock.Step(TimeSpan.Zero); |
|||
|
|||
// Initial Delay.
|
|||
clock.Step(TimeSpan.FromSeconds(1)); |
|||
Assert.Equal(border.Width, 0d); |
|||
|
|||
clock.Step(TimeSpan.FromSeconds(6)); |
|||
|
|||
// First Inter-Iteration delay.
|
|||
clock.Step(TimeSpan.FromSeconds(8)); |
|||
Assert.Equal(border.Width, 200d); |
|||
|
|||
// Trailing Delay should be non-existent.
|
|||
clock.Step(TimeSpan.FromSeconds(14)); |
|||
Assert.True(animationRun.Status == TaskStatus.RanToCompletion); |
|||
Assert.Equal(border.Width, 100d); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0"> |
|||
<PropertyGroup> |
|||
<TargetFrameworks>netcoreapp2.0</TargetFrameworks> |
|||
<OutputType>Library</OutputType> |
|||
</PropertyGroup> |
|||
<Import Project="..\..\build\UnitTests.NetCore.targets" /> |
|||
<Import Project="..\..\build\Moq.props" /> |
|||
<Import Project="..\..\build\XUnit.props" /> |
|||
<Import Project="..\..\build\Rx.props" /> |
|||
<Import Project="..\..\build\Microsoft.Reactive.Testing.props" /> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj" /> |
|||
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" /> |
|||
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj" /> |
|||
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj" /> |
|||
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj" /> |
|||
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj" /> |
|||
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj" /> |
|||
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj" /> |
|||
<ProjectReference Include="..\Avalonia.UnitTests\Avalonia.UnitTests.csproj" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,10 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System.Reflection; |
|||
using Xunit; |
|||
|
|||
[assembly: AssemblyTitle("Avalonia.Animation.UnitTests")] |
|||
|
|||
// Don't run tests in parallel.
|
|||
[assembly: CollectionBehavior(DisableTestParallelization = true)] |
|||
@ -0,0 +1,28 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Avalonia.Animation.UnitTests |
|||
{ |
|||
internal class TestClock : IClock, IDisposable |
|||
{ |
|||
private IObserver<TimeSpan> _observer; |
|||
|
|||
public PlayState PlayState { get; set; } = PlayState.Run; |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_observer?.OnCompleted(); |
|||
} |
|||
|
|||
public void Step(TimeSpan time) |
|||
{ |
|||
_observer?.OnNext(time); |
|||
} |
|||
|
|||
public IDisposable Subscribe(IObserver<TimeSpan> observer) |
|||
{ |
|||
_observer = observer; |
|||
return this; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue