chore: use file-scoped namespace
This commit is contained in:
1 parent
99d2fafb72
commit
bbf1b7d4c6
748 files changed
+57929
-58678
No files matched your search
@@ -2,24 +2,23 @@
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace HandyControl.Media.Animation
|
||||
namespace HandyControl.Media.Animation;
|
||||
|
||||
public class DiscreteGeometryKeyFrame : GeometryKeyFrame
|
||||
{
|
||||
public class DiscreteGeometryKeyFrame : GeometryKeyFrame
|
||||
public DiscreteGeometryKeyFrame()
|
||||
{
|
||||
public DiscreteGeometryKeyFrame()
|
||||
{
|
||||
}
|
||||
|
||||
public DiscreteGeometryKeyFrame(Geometry value) : base(value)
|
||||
{
|
||||
}
|
||||
|
||||
public DiscreteGeometryKeyFrame(Geometry value, KeyTime keyTime) : base(value, keyTime)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Freezable CreateInstanceCore() => new DiscreteGeometryKeyFrame();
|
||||
|
||||
protected override double[] InterpolateValueCore(double[] baseValue, double keyFrameProgress) => keyFrameProgress < 1.0 ? baseValue : Numbers;
|
||||
}
|
||||
}
|
||||
|
||||
public DiscreteGeometryKeyFrame(Geometry value) : base(value)
|
||||
{
|
||||
}
|
||||
|
||||
public DiscreteGeometryKeyFrame(Geometry value, KeyTime keyTime) : base(value, keyTime)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Freezable CreateInstanceCore() => new DiscreteGeometryKeyFrame();
|
||||
|
||||
protected override double[] InterpolateValueCore(double[] baseValue, double keyFrameProgress) => keyFrameProgress < 1.0 ? baseValue : Numbers;
|
||||
}
|
||||
@@ -4,60 +4,59 @@ using System.Windows.Media.Animation;
|
||||
using HandyControl.Expression.Drawing;
|
||||
using HandyControl.Tools;
|
||||
|
||||
namespace HandyControl.Media.Animation
|
||||
namespace HandyControl.Media.Animation;
|
||||
|
||||
public class EasingGeometryKeyFrame : GeometryKeyFrame
|
||||
{
|
||||
public class EasingGeometryKeyFrame : GeometryKeyFrame
|
||||
public EasingGeometryKeyFrame()
|
||||
{
|
||||
public EasingGeometryKeyFrame()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public EasingGeometryKeyFrame(Geometry value) : base(value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public EasingGeometryKeyFrame(Geometry value, KeyTime keyTime) : base(value, keyTime)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public EasingGeometryKeyFrame(Geometry value, KeyTime keyTime, IEasingFunction easingFunction) : base(value, keyTime)
|
||||
{
|
||||
EasingFunction = easingFunction;
|
||||
}
|
||||
|
||||
protected override Freezable CreateInstanceCore() => new EasingGeometryKeyFrame();
|
||||
|
||||
protected override double[] InterpolateValueCore(double[] baseValue, double keyFrameProgress)
|
||||
{
|
||||
var easingFunction = EasingFunction;
|
||||
if (easingFunction != null)
|
||||
{
|
||||
keyFrameProgress = easingFunction.Ease(keyFrameProgress);
|
||||
}
|
||||
|
||||
if (MathHelper.IsVerySmall(keyFrameProgress))
|
||||
{
|
||||
return baseValue;
|
||||
}
|
||||
|
||||
if (MathHelper.AreClose(keyFrameProgress, 1))
|
||||
{
|
||||
return Numbers;
|
||||
}
|
||||
|
||||
return AnimationHelper.InterpolateGeometryValue(baseValue, Numbers, keyFrameProgress);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty EasingFunctionProperty = DependencyProperty.Register(
|
||||
"EasingFunction", typeof(IEasingFunction), typeof(EasingGeometryKeyFrame), new PropertyMetadata(default(IEasingFunction)));
|
||||
|
||||
public IEasingFunction EasingFunction
|
||||
{
|
||||
get => (IEasingFunction) GetValue(EasingFunctionProperty);
|
||||
set => SetValue(EasingFunctionProperty, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EasingGeometryKeyFrame(Geometry value) : base(value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public EasingGeometryKeyFrame(Geometry value, KeyTime keyTime) : base(value, keyTime)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public EasingGeometryKeyFrame(Geometry value, KeyTime keyTime, IEasingFunction easingFunction) : base(value, keyTime)
|
||||
{
|
||||
EasingFunction = easingFunction;
|
||||
}
|
||||
|
||||
protected override Freezable CreateInstanceCore() => new EasingGeometryKeyFrame();
|
||||
|
||||
protected override double[] InterpolateValueCore(double[] baseValue, double keyFrameProgress)
|
||||
{
|
||||
var easingFunction = EasingFunction;
|
||||
if (easingFunction != null)
|
||||
{
|
||||
keyFrameProgress = easingFunction.Ease(keyFrameProgress);
|
||||
}
|
||||
|
||||
if (MathHelper.IsVerySmall(keyFrameProgress))
|
||||
{
|
||||
return baseValue;
|
||||
}
|
||||
|
||||
if (MathHelper.AreClose(keyFrameProgress, 1))
|
||||
{
|
||||
return Numbers;
|
||||
}
|
||||
|
||||
return AnimationHelper.InterpolateGeometryValue(baseValue, Numbers, keyFrameProgress);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty EasingFunctionProperty = DependencyProperty.Register(
|
||||
"EasingFunction", typeof(IEasingFunction), typeof(EasingGeometryKeyFrame), new PropertyMetadata(default(IEasingFunction)));
|
||||
|
||||
public IEasingFunction EasingFunction
|
||||
{
|
||||
get => (IEasingFunction) GetValue(EasingFunctionProperty);
|
||||
set => SetValue(EasingFunctionProperty, value);
|
||||
}
|
||||
}
|
||||
@@ -7,174 +7,173 @@ using HandyControl.Data;
|
||||
using HandyControl.Tools;
|
||||
|
||||
// ReSharper disable PossibleInvalidOperationException
|
||||
namespace HandyControl.Media.Animation
|
||||
namespace HandyControl.Media.Animation;
|
||||
|
||||
public class GeometryAnimation : GeometryAnimationBase
|
||||
{
|
||||
public class GeometryAnimation : GeometryAnimationBase
|
||||
private string[] _strings;
|
||||
|
||||
private double[] _numbersFrom;
|
||||
|
||||
private double[] _numbersTo;
|
||||
|
||||
private double[] _numbersAccumulator;
|
||||
|
||||
public GeometryAnimation()
|
||||
{
|
||||
private string[] _strings;
|
||||
|
||||
private double[] _numbersFrom;
|
||||
}
|
||||
|
||||
private double[] _numbersTo;
|
||||
public GeometryAnimation(string fromValue, string toValue) : this()
|
||||
{
|
||||
From = Geometry.Parse(fromValue);
|
||||
To = Geometry.Parse(toValue);
|
||||
}
|
||||
|
||||
private double[] _numbersAccumulator;
|
||||
private void UpdateValue()
|
||||
{
|
||||
if (_numbersFrom == null || _numbersTo == null || _numbersFrom.Length != _numbersTo.Length) return;
|
||||
|
||||
public GeometryAnimation()
|
||||
_numbersAccumulator = new double[_numbersFrom.Length];
|
||||
for (var i = 0; i < _numbersFrom.Length; i++)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public GeometryAnimation(string fromValue, string toValue) : this()
|
||||
{
|
||||
From = Geometry.Parse(fromValue);
|
||||
To = Geometry.Parse(toValue);
|
||||
}
|
||||
|
||||
private void UpdateValue()
|
||||
{
|
||||
if (_numbersFrom == null || _numbersTo == null || _numbersFrom.Length != _numbersTo.Length) return;
|
||||
|
||||
_numbersAccumulator = new double[_numbersFrom.Length];
|
||||
for (var i = 0; i < _numbersFrom.Length; i++)
|
||||
{
|
||||
_numbersAccumulator[i] = _numbersTo[i] - _numbersFrom[i];
|
||||
}
|
||||
}
|
||||
|
||||
public GeometryAnimation(Geometry fromValue, Geometry toValue) : this()
|
||||
{
|
||||
From = fromValue;
|
||||
To = toValue;
|
||||
}
|
||||
|
||||
public GeometryAnimation(Geometry fromValue, Geometry toValue, Duration duration) : this()
|
||||
{
|
||||
From = fromValue;
|
||||
To = toValue;
|
||||
Duration = duration;
|
||||
}
|
||||
|
||||
public GeometryAnimation(Geometry fromValue, Geometry toValue, Duration duration, FillBehavior fillBehavior) : this()
|
||||
{
|
||||
From = fromValue;
|
||||
To = toValue;
|
||||
Duration = duration;
|
||||
FillBehavior = fillBehavior;
|
||||
}
|
||||
|
||||
public new GeometryAnimation Clone() => (GeometryAnimation) base.Clone();
|
||||
|
||||
protected override Freezable CreateInstanceCore() => new GeometryAnimation();
|
||||
|
||||
protected override Geometry GetCurrentValueCore(Geometry defaultOriginValue, Geometry defaultDestinationValue, AnimationClock animationClock)
|
||||
{
|
||||
if (_numbersAccumulator == null)
|
||||
{
|
||||
if (_numbersFrom == null)
|
||||
{
|
||||
var geometryStr = defaultOriginValue.ToString(CultureInfo.InvariantCulture);
|
||||
AnimationHelper.DecomposeGeometryStr(geometryStr, out _numbersFrom);
|
||||
}
|
||||
|
||||
if (_numbersTo == null)
|
||||
{
|
||||
var geometryStr = defaultDestinationValue.ToString(CultureInfo.InvariantCulture);
|
||||
AnimationHelper.DecomposeGeometryStr(geometryStr, out _numbersTo);
|
||||
_strings = Regex.Split(geometryStr, RegexPatterns.DigitsPattern);
|
||||
}
|
||||
|
||||
UpdateValue();
|
||||
}
|
||||
|
||||
if (_numbersAccumulator == null) return defaultOriginValue;
|
||||
|
||||
var progress = animationClock.CurrentProgress.Value;
|
||||
|
||||
var easingFunction = EasingFunction;
|
||||
if (easingFunction != null)
|
||||
{
|
||||
progress = easingFunction.Ease(progress);
|
||||
}
|
||||
|
||||
var accumulated = new double[_numbersAccumulator.Length];
|
||||
|
||||
if (IsCumulative)
|
||||
{
|
||||
var currentRepeat = (double) (animationClock.CurrentIteration - 1);
|
||||
|
||||
if (currentRepeat > 0.0)
|
||||
{
|
||||
accumulated = new double[_numbersAccumulator.Length];
|
||||
for (var i = 0; i < _numbersAccumulator.Length; i++)
|
||||
{
|
||||
accumulated[i] = _numbersAccumulator[i] * currentRepeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var numbers = new double[_numbersAccumulator.Length];
|
||||
|
||||
for (var i = 0; i < _numbersAccumulator.Length; i++)
|
||||
{
|
||||
numbers[i] = accumulated[i] + _numbersFrom[i] + _numbersAccumulator[i] * progress;
|
||||
}
|
||||
|
||||
return AnimationHelper.ComposeGeometry(_strings, numbers);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty FromProperty = DependencyProperty.Register(
|
||||
"From", typeof(Geometry), typeof(GeometryAnimation), new PropertyMetadata(default(Geometry), OnFromChanged));
|
||||
|
||||
private static void OnFromChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var obj = (GeometryAnimation) d;
|
||||
if (e.NewValue is Geometry geometry)
|
||||
{
|
||||
AnimationHelper.DecomposeGeometryStr(geometry.ToString(CultureInfo.InvariantCulture), out obj._numbersFrom);
|
||||
obj.UpdateValue();
|
||||
}
|
||||
}
|
||||
|
||||
public Geometry From
|
||||
{
|
||||
get => (Geometry) GetValue(FromProperty);
|
||||
set => SetValue(FromProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ToProperty = DependencyProperty.Register(
|
||||
"To", typeof(Geometry), typeof(GeometryAnimation), new PropertyMetadata(default(Geometry), OnToChanged));
|
||||
|
||||
private static void OnToChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var obj = (GeometryAnimation) d;
|
||||
if (e.NewValue is Geometry geometry)
|
||||
{
|
||||
var geometryStr = geometry.ToString(CultureInfo.InvariantCulture);
|
||||
AnimationHelper.DecomposeGeometryStr(geometryStr, out obj._numbersTo);
|
||||
obj._strings = Regex.Split(geometryStr, RegexPatterns.DigitsPattern);
|
||||
obj.UpdateValue();
|
||||
}
|
||||
}
|
||||
|
||||
public Geometry To
|
||||
{
|
||||
get => (Geometry) GetValue(ToProperty);
|
||||
set => SetValue(ToProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty EasingFunctionProperty = DependencyProperty.Register(
|
||||
"EasingFunction", typeof(IEasingFunction), typeof(GeometryAnimation), new PropertyMetadata(default(IEasingFunction)));
|
||||
|
||||
public IEasingFunction EasingFunction
|
||||
{
|
||||
get => (IEasingFunction) GetValue(EasingFunctionProperty);
|
||||
set => SetValue(EasingFunctionProperty, value);
|
||||
}
|
||||
|
||||
public bool IsCumulative
|
||||
{
|
||||
get => (bool) GetValue(IsCumulativeProperty);
|
||||
set => SetValue(IsCumulativeProperty, ValueBoxes.BooleanBox(value));
|
||||
_numbersAccumulator[i] = _numbersTo[i] - _numbersFrom[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GeometryAnimation(Geometry fromValue, Geometry toValue) : this()
|
||||
{
|
||||
From = fromValue;
|
||||
To = toValue;
|
||||
}
|
||||
|
||||
public GeometryAnimation(Geometry fromValue, Geometry toValue, Duration duration) : this()
|
||||
{
|
||||
From = fromValue;
|
||||
To = toValue;
|
||||
Duration = duration;
|
||||
}
|
||||
|
||||
public GeometryAnimation(Geometry fromValue, Geometry toValue, Duration duration, FillBehavior fillBehavior) : this()
|
||||
{
|
||||
From = fromValue;
|
||||
To = toValue;
|
||||
Duration = duration;
|
||||
FillBehavior = fillBehavior;
|
||||
}
|
||||
|
||||
public new GeometryAnimation Clone() => (GeometryAnimation) base.Clone();
|
||||
|
||||
protected override Freezable CreateInstanceCore() => new GeometryAnimation();
|
||||
|
||||
protected override Geometry GetCurrentValueCore(Geometry defaultOriginValue, Geometry defaultDestinationValue, AnimationClock animationClock)
|
||||
{
|
||||
if (_numbersAccumulator == null)
|
||||
{
|
||||
if (_numbersFrom == null)
|
||||
{
|
||||
var geometryStr = defaultOriginValue.ToString(CultureInfo.InvariantCulture);
|
||||
AnimationHelper.DecomposeGeometryStr(geometryStr, out _numbersFrom);
|
||||
}
|
||||
|
||||
if (_numbersTo == null)
|
||||
{
|
||||
var geometryStr = defaultDestinationValue.ToString(CultureInfo.InvariantCulture);
|
||||
AnimationHelper.DecomposeGeometryStr(geometryStr, out _numbersTo);
|
||||
_strings = Regex.Split(geometryStr, RegexPatterns.DigitsPattern);
|
||||
}
|
||||
|
||||
UpdateValue();
|
||||
}
|
||||
|
||||
if (_numbersAccumulator == null) return defaultOriginValue;
|
||||
|
||||
var progress = animationClock.CurrentProgress.Value;
|
||||
|
||||
var easingFunction = EasingFunction;
|
||||
if (easingFunction != null)
|
||||
{
|
||||
progress = easingFunction.Ease(progress);
|
||||
}
|
||||
|
||||
var accumulated = new double[_numbersAccumulator.Length];
|
||||
|
||||
if (IsCumulative)
|
||||
{
|
||||
var currentRepeat = (double) (animationClock.CurrentIteration - 1);
|
||||
|
||||
if (currentRepeat > 0.0)
|
||||
{
|
||||
accumulated = new double[_numbersAccumulator.Length];
|
||||
for (var i = 0; i < _numbersAccumulator.Length; i++)
|
||||
{
|
||||
accumulated[i] = _numbersAccumulator[i] * currentRepeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var numbers = new double[_numbersAccumulator.Length];
|
||||
|
||||
for (var i = 0; i < _numbersAccumulator.Length; i++)
|
||||
{
|
||||
numbers[i] = accumulated[i] + _numbersFrom[i] + _numbersAccumulator[i] * progress;
|
||||
}
|
||||
|
||||
return AnimationHelper.ComposeGeometry(_strings, numbers);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty FromProperty = DependencyProperty.Register(
|
||||
"From", typeof(Geometry), typeof(GeometryAnimation), new PropertyMetadata(default(Geometry), OnFromChanged));
|
||||
|
||||
private static void OnFromChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var obj = (GeometryAnimation) d;
|
||||
if (e.NewValue is Geometry geometry)
|
||||
{
|
||||
AnimationHelper.DecomposeGeometryStr(geometry.ToString(CultureInfo.InvariantCulture), out obj._numbersFrom);
|
||||
obj.UpdateValue();
|
||||
}
|
||||
}
|
||||
|
||||
public Geometry From
|
||||
{
|
||||
get => (Geometry) GetValue(FromProperty);
|
||||
set => SetValue(FromProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ToProperty = DependencyProperty.Register(
|
||||
"To", typeof(Geometry), typeof(GeometryAnimation), new PropertyMetadata(default(Geometry), OnToChanged));
|
||||
|
||||
private static void OnToChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var obj = (GeometryAnimation) d;
|
||||
if (e.NewValue is Geometry geometry)
|
||||
{
|
||||
var geometryStr = geometry.ToString(CultureInfo.InvariantCulture);
|
||||
AnimationHelper.DecomposeGeometryStr(geometryStr, out obj._numbersTo);
|
||||
obj._strings = Regex.Split(geometryStr, RegexPatterns.DigitsPattern);
|
||||
obj.UpdateValue();
|
||||
}
|
||||
}
|
||||
|
||||
public Geometry To
|
||||
{
|
||||
get => (Geometry) GetValue(ToProperty);
|
||||
set => SetValue(ToProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty EasingFunctionProperty = DependencyProperty.Register(
|
||||
"EasingFunction", typeof(IEasingFunction), typeof(GeometryAnimation), new PropertyMetadata(default(IEasingFunction)));
|
||||
|
||||
public IEasingFunction EasingFunction
|
||||
{
|
||||
get => (IEasingFunction) GetValue(EasingFunctionProperty);
|
||||
set => SetValue(EasingFunctionProperty, value);
|
||||
}
|
||||
|
||||
public bool IsCumulative
|
||||
{
|
||||
get => (bool) GetValue(IsCumulativeProperty);
|
||||
set => SetValue(IsCumulativeProperty, ValueBoxes.BooleanBox(value));
|
||||
}
|
||||
}
|
||||
@@ -4,57 +4,56 @@ using System.Threading;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace HandyControl.Media.Animation
|
||||
namespace HandyControl.Media.Animation;
|
||||
|
||||
public abstract class GeometryAnimationBase : AnimationTimeline
|
||||
{
|
||||
public abstract class GeometryAnimationBase : AnimationTimeline
|
||||
protected GeometryAnimationBase()
|
||||
{
|
||||
protected GeometryAnimationBase()
|
||||
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
||||
}
|
||||
|
||||
public new GeometryAnimationBase Clone() => (GeometryAnimationBase) base.Clone();
|
||||
|
||||
public sealed override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
|
||||
{
|
||||
if (defaultOriginValue == null)
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
||||
throw new ArgumentNullException(nameof(defaultOriginValue));
|
||||
}
|
||||
|
||||
public new GeometryAnimationBase Clone() => (GeometryAnimationBase) base.Clone();
|
||||
|
||||
public sealed override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
|
||||
if (defaultDestinationValue == null)
|
||||
{
|
||||
if (defaultOriginValue == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(defaultOriginValue));
|
||||
}
|
||||
if (defaultDestinationValue == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(defaultDestinationValue));
|
||||
}
|
||||
return GetCurrentValue((Geometry) defaultOriginValue, (Geometry) defaultDestinationValue, animationClock);
|
||||
throw new ArgumentNullException(nameof(defaultDestinationValue));
|
||||
}
|
||||
return GetCurrentValue((Geometry) defaultOriginValue, (Geometry) defaultDestinationValue, animationClock);
|
||||
}
|
||||
|
||||
public override Type TargetPropertyType
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return typeof(Geometry);
|
||||
}
|
||||
}
|
||||
|
||||
public Geometry GetCurrentValue(Geometry defaultOriginValue, Geometry defaultDestinationValue, AnimationClock animationClock)
|
||||
public override Type TargetPropertyType
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
if (animationClock == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(animationClock));
|
||||
}
|
||||
return typeof(Geometry);
|
||||
}
|
||||
}
|
||||
|
||||
if (animationClock.CurrentState == ClockState.Stopped)
|
||||
{
|
||||
return defaultDestinationValue;
|
||||
}
|
||||
public Geometry GetCurrentValue(Geometry defaultOriginValue, Geometry defaultDestinationValue, AnimationClock animationClock)
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return GetCurrentValueCore(defaultOriginValue, defaultDestinationValue, animationClock);
|
||||
if (animationClock == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(animationClock));
|
||||
}
|
||||
|
||||
protected abstract Geometry GetCurrentValueCore(Geometry defaultOriginValue, Geometry defaultDestinationValue, AnimationClock animationClock);
|
||||
if (animationClock.CurrentState == ClockState.Stopped)
|
||||
{
|
||||
return defaultDestinationValue;
|
||||
}
|
||||
|
||||
return GetCurrentValueCore(defaultOriginValue, defaultDestinationValue, animationClock);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Geometry GetCurrentValueCore(Geometry defaultOriginValue, Geometry defaultDestinationValue, AnimationClock animationClock);
|
||||
}
|
||||
+429
-430
@@ -11,511 +11,510 @@ using System.Windows.Media.Animation;
|
||||
using HandyControl.Tools;
|
||||
|
||||
// ReSharper disable PossibleInvalidOperationException
|
||||
namespace HandyControl.Media.Animation
|
||||
namespace HandyControl.Media.Animation;
|
||||
|
||||
[ContentProperty("KeyFrames")]
|
||||
public class GeometryAnimationUsingKeyFrames : GeometryAnimationBase, IKeyFrameAnimation, IAddChild
|
||||
{
|
||||
[ContentProperty("KeyFrames")]
|
||||
public class GeometryAnimationUsingKeyFrames : GeometryAnimationBase, IKeyFrameAnimation, IAddChild
|
||||
private string[] _strings;
|
||||
|
||||
public string[] Strings
|
||||
{
|
||||
private string[] _strings;
|
||||
|
||||
public string[] Strings
|
||||
get
|
||||
{
|
||||
get
|
||||
if (_keyFrames == null || _keyFrames.Count == 0)
|
||||
{
|
||||
if (_keyFrames == null || _keyFrames.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _strings ??= Regex.Split(_keyFrames[0].Value.ToString(CultureInfo.InvariantCulture), RegexPatterns.DigitsPattern);
|
||||
}
|
||||
}
|
||||
|
||||
private GeometryKeyFrameCollection _keyFrames;
|
||||
|
||||
private ResolvedKeyFrameEntry[] _sortedResolvedKeyFrames;
|
||||
|
||||
private bool _areKeyTimesValid;
|
||||
|
||||
public GeometryAnimationUsingKeyFrames()
|
||||
{
|
||||
_areKeyTimesValid = true;
|
||||
}
|
||||
|
||||
public new GeometryAnimationUsingKeyFrames Clone() => (GeometryAnimationUsingKeyFrames) base.Clone();
|
||||
|
||||
public new GeometryAnimationUsingKeyFrames CloneCurrentValue() => (GeometryAnimationUsingKeyFrames) base.CloneCurrentValue();
|
||||
|
||||
protected override bool FreezeCore(bool isChecking)
|
||||
{
|
||||
var canFreeze = base.FreezeCore(isChecking);
|
||||
|
||||
canFreeze &= Freeze(_keyFrames, isChecking);
|
||||
|
||||
if (canFreeze & !_areKeyTimesValid)
|
||||
{
|
||||
ResolveKeyTimes();
|
||||
return null;
|
||||
}
|
||||
|
||||
return canFreeze;
|
||||
return _strings ??= Regex.Split(_keyFrames[0].Value.ToString(CultureInfo.InvariantCulture), RegexPatterns.DigitsPattern);
|
||||
}
|
||||
}
|
||||
|
||||
private GeometryKeyFrameCollection _keyFrames;
|
||||
|
||||
private ResolvedKeyFrameEntry[] _sortedResolvedKeyFrames;
|
||||
|
||||
private bool _areKeyTimesValid;
|
||||
|
||||
public GeometryAnimationUsingKeyFrames()
|
||||
{
|
||||
_areKeyTimesValid = true;
|
||||
}
|
||||
|
||||
public new GeometryAnimationUsingKeyFrames Clone() => (GeometryAnimationUsingKeyFrames) base.Clone();
|
||||
|
||||
public new GeometryAnimationUsingKeyFrames CloneCurrentValue() => (GeometryAnimationUsingKeyFrames) base.CloneCurrentValue();
|
||||
|
||||
protected override bool FreezeCore(bool isChecking)
|
||||
{
|
||||
var canFreeze = base.FreezeCore(isChecking);
|
||||
|
||||
canFreeze &= Freeze(_keyFrames, isChecking);
|
||||
|
||||
if (canFreeze & !_areKeyTimesValid)
|
||||
{
|
||||
ResolveKeyTimes();
|
||||
}
|
||||
|
||||
protected override void OnChanged()
|
||||
{
|
||||
_areKeyTimesValid = false;
|
||||
return canFreeze;
|
||||
}
|
||||
|
||||
base.OnChanged();
|
||||
protected override void OnChanged()
|
||||
{
|
||||
_areKeyTimesValid = false;
|
||||
|
||||
base.OnChanged();
|
||||
}
|
||||
|
||||
protected override Freezable CreateInstanceCore() => new GeometryAnimationUsingKeyFrames();
|
||||
|
||||
protected override void CloneCore(Freezable sourceFreezable)
|
||||
{
|
||||
var sourceAnimation = (GeometryAnimationUsingKeyFrames) sourceFreezable;
|
||||
base.CloneCore(sourceFreezable);
|
||||
|
||||
CopyCommon(sourceAnimation, false);
|
||||
}
|
||||
|
||||
protected override void CloneCurrentValueCore(Freezable sourceFreezable)
|
||||
{
|
||||
var sourceAnimation = (GeometryAnimationUsingKeyFrames) sourceFreezable;
|
||||
base.CloneCurrentValueCore(sourceFreezable);
|
||||
|
||||
CopyCommon(sourceAnimation, true);
|
||||
}
|
||||
|
||||
protected override void GetAsFrozenCore(Freezable source)
|
||||
{
|
||||
var sourceAnimation = (GeometryAnimationUsingKeyFrames) source;
|
||||
base.GetAsFrozenCore(source);
|
||||
|
||||
CopyCommon(sourceAnimation, false);
|
||||
}
|
||||
|
||||
protected override void GetCurrentValueAsFrozenCore(Freezable source)
|
||||
{
|
||||
var sourceAnimation = (GeometryAnimationUsingKeyFrames) source;
|
||||
base.GetCurrentValueAsFrozenCore(source);
|
||||
|
||||
CopyCommon(sourceAnimation, true);
|
||||
}
|
||||
|
||||
private void CopyCommon(GeometryAnimationUsingKeyFrames sourceAnimation, bool isCurrentValueClone)
|
||||
{
|
||||
_areKeyTimesValid = sourceAnimation._areKeyTimesValid;
|
||||
|
||||
if (_areKeyTimesValid && sourceAnimation._sortedResolvedKeyFrames != null)
|
||||
{
|
||||
_sortedResolvedKeyFrames = (ResolvedKeyFrameEntry[]) sourceAnimation._sortedResolvedKeyFrames.Clone();
|
||||
}
|
||||
|
||||
protected override Freezable CreateInstanceCore() => new GeometryAnimationUsingKeyFrames();
|
||||
|
||||
protected override void CloneCore(Freezable sourceFreezable)
|
||||
if (sourceAnimation._keyFrames != null)
|
||||
{
|
||||
var sourceAnimation = (GeometryAnimationUsingKeyFrames) sourceFreezable;
|
||||
base.CloneCore(sourceFreezable);
|
||||
|
||||
CopyCommon(sourceAnimation, false);
|
||||
}
|
||||
|
||||
protected override void CloneCurrentValueCore(Freezable sourceFreezable)
|
||||
{
|
||||
var sourceAnimation = (GeometryAnimationUsingKeyFrames) sourceFreezable;
|
||||
base.CloneCurrentValueCore(sourceFreezable);
|
||||
|
||||
CopyCommon(sourceAnimation, true);
|
||||
}
|
||||
|
||||
protected override void GetAsFrozenCore(Freezable source)
|
||||
{
|
||||
var sourceAnimation = (GeometryAnimationUsingKeyFrames) source;
|
||||
base.GetAsFrozenCore(source);
|
||||
|
||||
CopyCommon(sourceAnimation, false);
|
||||
}
|
||||
|
||||
protected override void GetCurrentValueAsFrozenCore(Freezable source)
|
||||
{
|
||||
var sourceAnimation = (GeometryAnimationUsingKeyFrames) source;
|
||||
base.GetCurrentValueAsFrozenCore(source);
|
||||
|
||||
CopyCommon(sourceAnimation, true);
|
||||
}
|
||||
|
||||
private void CopyCommon(GeometryAnimationUsingKeyFrames sourceAnimation, bool isCurrentValueClone)
|
||||
{
|
||||
_areKeyTimesValid = sourceAnimation._areKeyTimesValid;
|
||||
|
||||
if (_areKeyTimesValid && sourceAnimation._sortedResolvedKeyFrames != null)
|
||||
if (isCurrentValueClone)
|
||||
{
|
||||
_sortedResolvedKeyFrames = (ResolvedKeyFrameEntry[]) sourceAnimation._sortedResolvedKeyFrames.Clone();
|
||||
}
|
||||
|
||||
if (sourceAnimation._keyFrames != null)
|
||||
{
|
||||
if (isCurrentValueClone)
|
||||
{
|
||||
_keyFrames = (GeometryKeyFrameCollection) sourceAnimation._keyFrames.CloneCurrentValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
_keyFrames = sourceAnimation._keyFrames.Clone();
|
||||
}
|
||||
|
||||
OnFreezablePropertyChanged(null, _keyFrames);
|
||||
}
|
||||
}
|
||||
|
||||
void IAddChild.AddChild(object child)
|
||||
{
|
||||
WritePreamble();
|
||||
|
||||
if (child == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(child));
|
||||
}
|
||||
|
||||
AddChild(child);
|
||||
|
||||
WritePostscript();
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
protected virtual void AddChild(object child)
|
||||
{
|
||||
if (child is GeometryKeyFrame keyFrame)
|
||||
{
|
||||
KeyFrames.Add(keyFrame);
|
||||
_keyFrames = (GeometryKeyFrameCollection) sourceAnimation._keyFrames.CloneCurrentValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Animation_ChildMustBeKeyFrame", nameof(child));
|
||||
_keyFrames = sourceAnimation._keyFrames.Clone();
|
||||
}
|
||||
|
||||
OnFreezablePropertyChanged(null, _keyFrames);
|
||||
}
|
||||
}
|
||||
|
||||
void IAddChild.AddChild(object child)
|
||||
{
|
||||
WritePreamble();
|
||||
|
||||
if (child == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(child));
|
||||
}
|
||||
|
||||
void IAddChild.AddText(string childText)
|
||||
AddChild(child);
|
||||
|
||||
WritePostscript();
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
protected virtual void AddChild(object child)
|
||||
{
|
||||
if (child is GeometryKeyFrame keyFrame)
|
||||
{
|
||||
if (childText == null)
|
||||
KeyFrames.Add(keyFrame);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Animation_ChildMustBeKeyFrame", nameof(child));
|
||||
}
|
||||
}
|
||||
|
||||
void IAddChild.AddText(string childText)
|
||||
{
|
||||
if (childText == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(childText));
|
||||
}
|
||||
|
||||
AddText(childText);
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
protected virtual void AddText(string childText) => throw new InvalidOperationException("Animation_NoTextChildren");
|
||||
|
||||
protected override Geometry GetCurrentValueCore(Geometry defaultOriginValue, Geometry defaultDestinationValue, AnimationClock animationClock)
|
||||
{
|
||||
if (_keyFrames == null)
|
||||
{
|
||||
return defaultDestinationValue;
|
||||
}
|
||||
|
||||
if (!_areKeyTimesValid)
|
||||
{
|
||||
ResolveKeyTimes();
|
||||
}
|
||||
|
||||
if (_sortedResolvedKeyFrames == null)
|
||||
{
|
||||
return defaultDestinationValue;
|
||||
}
|
||||
|
||||
var currentTime = animationClock.CurrentTime.Value;
|
||||
var keyFrameCount = _sortedResolvedKeyFrames.Length;
|
||||
var maxKeyFrameIndex = keyFrameCount - 1;
|
||||
|
||||
double[] currentIterationValue;
|
||||
|
||||
var currentResolvedKeyFrameIndex = 0;
|
||||
|
||||
while (currentResolvedKeyFrameIndex < keyFrameCount && currentTime > _sortedResolvedKeyFrames[currentResolvedKeyFrameIndex]._resolvedKeyTime)
|
||||
{
|
||||
currentResolvedKeyFrameIndex++;
|
||||
}
|
||||
|
||||
while (currentResolvedKeyFrameIndex < maxKeyFrameIndex && currentTime == _sortedResolvedKeyFrames[currentResolvedKeyFrameIndex + 1]._resolvedKeyTime)
|
||||
{
|
||||
currentResolvedKeyFrameIndex++;
|
||||
}
|
||||
|
||||
if (currentResolvedKeyFrameIndex == keyFrameCount)
|
||||
{
|
||||
currentIterationValue = GetResolvedKeyFrameValue(maxKeyFrameIndex);
|
||||
}
|
||||
else if (currentTime == _sortedResolvedKeyFrames[currentResolvedKeyFrameIndex]._resolvedKeyTime)
|
||||
{
|
||||
currentIterationValue = GetResolvedKeyFrameValue(currentResolvedKeyFrameIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
double currentSegmentProgress;
|
||||
double[] fromValue;
|
||||
|
||||
if (currentResolvedKeyFrameIndex == 0)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(childText));
|
||||
AnimationHelper.DecomposeGeometryStr(defaultOriginValue.ToString(CultureInfo.InvariantCulture), out fromValue);
|
||||
|
||||
currentSegmentProgress = currentTime.TotalMilliseconds / _sortedResolvedKeyFrames[0]._resolvedKeyTime.TotalMilliseconds;
|
||||
}
|
||||
else
|
||||
{
|
||||
var previousResolvedKeyFrameIndex = currentResolvedKeyFrameIndex - 1;
|
||||
var previousResolvedKeyTime = _sortedResolvedKeyFrames[previousResolvedKeyFrameIndex]._resolvedKeyTime;
|
||||
|
||||
fromValue = GetResolvedKeyFrameValue(previousResolvedKeyFrameIndex);
|
||||
|
||||
var segmentCurrentTime = currentTime - previousResolvedKeyTime;
|
||||
var segmentDuration = _sortedResolvedKeyFrames[currentResolvedKeyFrameIndex]._resolvedKeyTime - previousResolvedKeyTime;
|
||||
|
||||
currentSegmentProgress = segmentCurrentTime.TotalMilliseconds / segmentDuration.TotalMilliseconds;
|
||||
}
|
||||
|
||||
AddText(childText);
|
||||
currentIterationValue = GetResolvedKeyFrame(currentResolvedKeyFrameIndex).InterpolateValue(fromValue, currentSegmentProgress);
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
protected virtual void AddText(string childText) => throw new InvalidOperationException("Animation_NoTextChildren");
|
||||
return AnimationHelper.ComposeGeometry(Strings, currentIterationValue);
|
||||
}
|
||||
|
||||
protected override Geometry GetCurrentValueCore(Geometry defaultOriginValue, Geometry defaultDestinationValue, AnimationClock animationClock)
|
||||
protected sealed override Duration GetNaturalDurationCore(Clock clock) => new(LargestTimeSpanKeyTime);
|
||||
|
||||
IList IKeyFrameAnimation.KeyFrames
|
||||
{
|
||||
get => KeyFrames;
|
||||
set => KeyFrames = (GeometryKeyFrameCollection) value;
|
||||
}
|
||||
|
||||
public GeometryKeyFrameCollection KeyFrames
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
if (_keyFrames == null)
|
||||
{
|
||||
return defaultDestinationValue;
|
||||
}
|
||||
|
||||
if (!_areKeyTimesValid)
|
||||
{
|
||||
ResolveKeyTimes();
|
||||
}
|
||||
|
||||
if (_sortedResolvedKeyFrames == null)
|
||||
{
|
||||
return defaultDestinationValue;
|
||||
}
|
||||
|
||||
var currentTime = animationClock.CurrentTime.Value;
|
||||
var keyFrameCount = _sortedResolvedKeyFrames.Length;
|
||||
var maxKeyFrameIndex = keyFrameCount - 1;
|
||||
|
||||
double[] currentIterationValue;
|
||||
|
||||
var currentResolvedKeyFrameIndex = 0;
|
||||
|
||||
while (currentResolvedKeyFrameIndex < keyFrameCount && currentTime > _sortedResolvedKeyFrames[currentResolvedKeyFrameIndex]._resolvedKeyTime)
|
||||
{
|
||||
currentResolvedKeyFrameIndex++;
|
||||
}
|
||||
|
||||
while (currentResolvedKeyFrameIndex < maxKeyFrameIndex && currentTime == _sortedResolvedKeyFrames[currentResolvedKeyFrameIndex + 1]._resolvedKeyTime)
|
||||
{
|
||||
currentResolvedKeyFrameIndex++;
|
||||
}
|
||||
|
||||
if (currentResolvedKeyFrameIndex == keyFrameCount)
|
||||
{
|
||||
currentIterationValue = GetResolvedKeyFrameValue(maxKeyFrameIndex);
|
||||
}
|
||||
else if (currentTime == _sortedResolvedKeyFrames[currentResolvedKeyFrameIndex]._resolvedKeyTime)
|
||||
{
|
||||
currentIterationValue = GetResolvedKeyFrameValue(currentResolvedKeyFrameIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
double currentSegmentProgress;
|
||||
double[] fromValue;
|
||||
|
||||
if (currentResolvedKeyFrameIndex == 0)
|
||||
if (IsFrozen)
|
||||
{
|
||||
AnimationHelper.DecomposeGeometryStr(defaultOriginValue.ToString(CultureInfo.InvariantCulture), out fromValue);
|
||||
|
||||
currentSegmentProgress = currentTime.TotalMilliseconds / _sortedResolvedKeyFrames[0]._resolvedKeyTime.TotalMilliseconds;
|
||||
_keyFrames = GeometryKeyFrameCollection.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
var previousResolvedKeyFrameIndex = currentResolvedKeyFrameIndex - 1;
|
||||
var previousResolvedKeyTime = _sortedResolvedKeyFrames[previousResolvedKeyFrameIndex]._resolvedKeyTime;
|
||||
WritePreamble();
|
||||
|
||||
fromValue = GetResolvedKeyFrameValue(previousResolvedKeyFrameIndex);
|
||||
_keyFrames = new GeometryKeyFrameCollection();
|
||||
|
||||
var segmentCurrentTime = currentTime - previousResolvedKeyTime;
|
||||
var segmentDuration = _sortedResolvedKeyFrames[currentResolvedKeyFrameIndex]._resolvedKeyTime - previousResolvedKeyTime;
|
||||
|
||||
currentSegmentProgress = segmentCurrentTime.TotalMilliseconds / segmentDuration.TotalMilliseconds;
|
||||
}
|
||||
|
||||
currentIterationValue = GetResolvedKeyFrame(currentResolvedKeyFrameIndex).InterpolateValue(fromValue, currentSegmentProgress);
|
||||
}
|
||||
|
||||
return AnimationHelper.ComposeGeometry(Strings, currentIterationValue);
|
||||
}
|
||||
|
||||
protected sealed override Duration GetNaturalDurationCore(Clock clock) => new(LargestTimeSpanKeyTime);
|
||||
|
||||
IList IKeyFrameAnimation.KeyFrames
|
||||
{
|
||||
get => KeyFrames;
|
||||
set => KeyFrames = (GeometryKeyFrameCollection) value;
|
||||
}
|
||||
|
||||
public GeometryKeyFrameCollection KeyFrames
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
if (_keyFrames == null)
|
||||
{
|
||||
if (IsFrozen)
|
||||
{
|
||||
_keyFrames = GeometryKeyFrameCollection.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
WritePreamble();
|
||||
|
||||
_keyFrames = new GeometryKeyFrameCollection();
|
||||
|
||||
OnFreezablePropertyChanged(null, _keyFrames);
|
||||
|
||||
WritePostscript();
|
||||
}
|
||||
}
|
||||
|
||||
return _keyFrames;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
WritePreamble();
|
||||
|
||||
if (value != _keyFrames)
|
||||
{
|
||||
OnFreezablePropertyChanged(_keyFrames, value);
|
||||
_keyFrames = value;
|
||||
OnFreezablePropertyChanged(null, _keyFrames);
|
||||
|
||||
WritePostscript();
|
||||
}
|
||||
}
|
||||
|
||||
return _keyFrames;
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public bool ShouldSerializeKeyFrames()
|
||||
set
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return _keyFrames != null && _keyFrames.Count > 0;
|
||||
}
|
||||
|
||||
private struct KeyTimeBlock
|
||||
{
|
||||
public int BeginIndex;
|
||||
public int EndIndex;
|
||||
}
|
||||
|
||||
private double[] GetResolvedKeyFrameValue(int resolvedKeyFrameIndex) =>
|
||||
GetResolvedKeyFrame(resolvedKeyFrameIndex).Numbers;
|
||||
|
||||
private GeometryKeyFrame GetResolvedKeyFrame(int resolvedKeyFrameIndex) =>
|
||||
_keyFrames[_sortedResolvedKeyFrames[resolvedKeyFrameIndex]._originalKeyFrameIndex];
|
||||
|
||||
private TimeSpan LargestTimeSpanKeyTime
|
||||
{
|
||||
get
|
||||
if (value == null)
|
||||
{
|
||||
var hasTimeSpanKeyTime = false;
|
||||
var largestTimeSpanKeyTime = TimeSpan.Zero;
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
if (_keyFrames != null)
|
||||
{
|
||||
var keyFrameCount = _keyFrames.Count;
|
||||
WritePreamble();
|
||||
|
||||
for (var index = 0; index < keyFrameCount; index++)
|
||||
{
|
||||
var keyTime = _keyFrames[index].KeyTime;
|
||||
if (value != _keyFrames)
|
||||
{
|
||||
OnFreezablePropertyChanged(_keyFrames, value);
|
||||
_keyFrames = value;
|
||||
|
||||
if (keyTime.Type == KeyTimeType.TimeSpan)
|
||||
{
|
||||
hasTimeSpanKeyTime = true;
|
||||
|
||||
if (keyTime.TimeSpan > largestTimeSpanKeyTime)
|
||||
{
|
||||
largestTimeSpanKeyTime = keyTime.TimeSpan;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hasTimeSpanKeyTime ? largestTimeSpanKeyTime : TimeSpan.FromSeconds(1.0);
|
||||
WritePostscript();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ResolveKeyTimes()
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public bool ShouldSerializeKeyFrames()
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return _keyFrames != null && _keyFrames.Count > 0;
|
||||
}
|
||||
|
||||
private struct KeyTimeBlock
|
||||
{
|
||||
public int BeginIndex;
|
||||
public int EndIndex;
|
||||
}
|
||||
|
||||
private double[] GetResolvedKeyFrameValue(int resolvedKeyFrameIndex) =>
|
||||
GetResolvedKeyFrame(resolvedKeyFrameIndex).Numbers;
|
||||
|
||||
private GeometryKeyFrame GetResolvedKeyFrame(int resolvedKeyFrameIndex) =>
|
||||
_keyFrames[_sortedResolvedKeyFrames[resolvedKeyFrameIndex]._originalKeyFrameIndex];
|
||||
|
||||
private TimeSpan LargestTimeSpanKeyTime
|
||||
{
|
||||
get
|
||||
{
|
||||
var keyFrameCount = 0;
|
||||
var hasTimeSpanKeyTime = false;
|
||||
var largestTimeSpanKeyTime = TimeSpan.Zero;
|
||||
|
||||
if (_keyFrames != null)
|
||||
{
|
||||
keyFrameCount = _keyFrames.Count;
|
||||
}
|
||||
var keyFrameCount = _keyFrames.Count;
|
||||
|
||||
if (keyFrameCount == 0)
|
||||
{
|
||||
_sortedResolvedKeyFrames = null;
|
||||
_areKeyTimesValid = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_sortedResolvedKeyFrames = new ResolvedKeyFrameEntry[keyFrameCount];
|
||||
|
||||
var index = 0;
|
||||
|
||||
for (; index < keyFrameCount; index++)
|
||||
{
|
||||
_sortedResolvedKeyFrames[index]._originalKeyFrameIndex = index;
|
||||
}
|
||||
|
||||
var duration = Duration;
|
||||
var calculationDuration = duration.HasTimeSpan ? duration.TimeSpan : LargestTimeSpanKeyTime;
|
||||
var maxKeyFrameIndex = keyFrameCount - 1;
|
||||
var unspecifiedBlocks = new List<KeyTimeBlock>();
|
||||
var hasPacedKeyTimes = false;
|
||||
|
||||
index = 0;
|
||||
while (index < keyFrameCount)
|
||||
{
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
var keyTime = _keyFrames[index].KeyTime;
|
||||
|
||||
switch (keyTime.Type)
|
||||
for (var index = 0; index < keyFrameCount; index++)
|
||||
{
|
||||
case KeyTimeType.Percent:
|
||||
var keyTime = _keyFrames[index].KeyTime;
|
||||
|
||||
_sortedResolvedKeyFrames[index]._resolvedKeyTime = TimeSpan.FromMilliseconds(keyTime.Percent * calculationDuration.TotalMilliseconds);
|
||||
index++;
|
||||
break;
|
||||
if (keyTime.Type == KeyTimeType.TimeSpan)
|
||||
{
|
||||
hasTimeSpanKeyTime = true;
|
||||
|
||||
case KeyTimeType.TimeSpan:
|
||||
|
||||
_sortedResolvedKeyFrames[index]._resolvedKeyTime = keyTime.TimeSpan;
|
||||
|
||||
index++;
|
||||
break;
|
||||
|
||||
case KeyTimeType.Paced:
|
||||
case KeyTimeType.Uniform:
|
||||
|
||||
if (index == maxKeyFrameIndex)
|
||||
if (keyTime.TimeSpan > largestTimeSpanKeyTime)
|
||||
{
|
||||
_sortedResolvedKeyFrames[index]._resolvedKeyTime = calculationDuration;
|
||||
index++;
|
||||
largestTimeSpanKeyTime = keyTime.TimeSpan;
|
||||
}
|
||||
else if (index == 0 && keyTime.Type == KeyTimeType.Paced)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hasTimeSpanKeyTime ? largestTimeSpanKeyTime : TimeSpan.FromSeconds(1.0);
|
||||
}
|
||||
}
|
||||
|
||||
private void ResolveKeyTimes()
|
||||
{
|
||||
var keyFrameCount = 0;
|
||||
|
||||
if (_keyFrames != null)
|
||||
{
|
||||
keyFrameCount = _keyFrames.Count;
|
||||
}
|
||||
|
||||
if (keyFrameCount == 0)
|
||||
{
|
||||
_sortedResolvedKeyFrames = null;
|
||||
_areKeyTimesValid = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_sortedResolvedKeyFrames = new ResolvedKeyFrameEntry[keyFrameCount];
|
||||
|
||||
var index = 0;
|
||||
|
||||
for (; index < keyFrameCount; index++)
|
||||
{
|
||||
_sortedResolvedKeyFrames[index]._originalKeyFrameIndex = index;
|
||||
}
|
||||
|
||||
var duration = Duration;
|
||||
var calculationDuration = duration.HasTimeSpan ? duration.TimeSpan : LargestTimeSpanKeyTime;
|
||||
var maxKeyFrameIndex = keyFrameCount - 1;
|
||||
var unspecifiedBlocks = new List<KeyTimeBlock>();
|
||||
var hasPacedKeyTimes = false;
|
||||
|
||||
index = 0;
|
||||
while (index < keyFrameCount)
|
||||
{
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
var keyTime = _keyFrames[index].KeyTime;
|
||||
|
||||
switch (keyTime.Type)
|
||||
{
|
||||
case KeyTimeType.Percent:
|
||||
|
||||
_sortedResolvedKeyFrames[index]._resolvedKeyTime = TimeSpan.FromMilliseconds(keyTime.Percent * calculationDuration.TotalMilliseconds);
|
||||
index++;
|
||||
break;
|
||||
|
||||
case KeyTimeType.TimeSpan:
|
||||
|
||||
_sortedResolvedKeyFrames[index]._resolvedKeyTime = keyTime.TimeSpan;
|
||||
|
||||
index++;
|
||||
break;
|
||||
|
||||
case KeyTimeType.Paced:
|
||||
case KeyTimeType.Uniform:
|
||||
|
||||
if (index == maxKeyFrameIndex)
|
||||
{
|
||||
_sortedResolvedKeyFrames[index]._resolvedKeyTime = calculationDuration;
|
||||
index++;
|
||||
}
|
||||
else if (index == 0 && keyTime.Type == KeyTimeType.Paced)
|
||||
{
|
||||
_sortedResolvedKeyFrames[index]._resolvedKeyTime = TimeSpan.Zero;
|
||||
index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (keyTime.Type == KeyTimeType.Paced)
|
||||
{
|
||||
_sortedResolvedKeyFrames[index]._resolvedKeyTime = TimeSpan.Zero;
|
||||
index++;
|
||||
hasPacedKeyTimes = true;
|
||||
}
|
||||
else
|
||||
|
||||
var block = new KeyTimeBlock
|
||||
{
|
||||
if (keyTime.Type == KeyTimeType.Paced)
|
||||
BeginIndex = index
|
||||
};
|
||||
|
||||
while (++index < maxKeyFrameIndex)
|
||||
{
|
||||
var type = _keyFrames[index].KeyTime.Type;
|
||||
|
||||
if (type == KeyTimeType.Percent || type == KeyTimeType.TimeSpan)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == KeyTimeType.Paced)
|
||||
{
|
||||
hasPacedKeyTimes = true;
|
||||
}
|
||||
|
||||
var block = new KeyTimeBlock
|
||||
{
|
||||
BeginIndex = index
|
||||
};
|
||||
|
||||
while (++index < maxKeyFrameIndex)
|
||||
{
|
||||
var type = _keyFrames[index].KeyTime.Type;
|
||||
|
||||
if (type == KeyTimeType.Percent || type == KeyTimeType.TimeSpan)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == KeyTimeType.Paced)
|
||||
{
|
||||
hasPacedKeyTimes = true;
|
||||
}
|
||||
}
|
||||
|
||||
block.EndIndex = index;
|
||||
unspecifiedBlocks.Add(block);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
block.EndIndex = index;
|
||||
unspecifiedBlocks.Add(block);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var block in unspecifiedBlocks)
|
||||
{
|
||||
var blockBeginTime = TimeSpan.Zero;
|
||||
|
||||
if (block.BeginIndex > 0)
|
||||
{
|
||||
blockBeginTime = _sortedResolvedKeyFrames[block.BeginIndex - 1]._resolvedKeyTime;
|
||||
}
|
||||
|
||||
long segmentCount = block.EndIndex - block.BeginIndex + 1;
|
||||
var uniformTimeStep = TimeSpan.FromTicks((_sortedResolvedKeyFrames[block.EndIndex]._resolvedKeyTime - blockBeginTime).Ticks / segmentCount);
|
||||
|
||||
index = block.BeginIndex;
|
||||
var resolvedTime = blockBeginTime + uniformTimeStep;
|
||||
|
||||
while (index < block.EndIndex)
|
||||
{
|
||||
_sortedResolvedKeyFrames[index]._resolvedKeyTime = resolvedTime;
|
||||
|
||||
resolvedTime += uniformTimeStep;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasPacedKeyTimes)
|
||||
{
|
||||
ResolvePacedKeyTimes();
|
||||
}
|
||||
|
||||
Array.Sort(_sortedResolvedKeyFrames);
|
||||
|
||||
_areKeyTimesValid = true;
|
||||
}
|
||||
|
||||
private void ResolvePacedKeyTimes()
|
||||
foreach (var block in unspecifiedBlocks)
|
||||
{
|
||||
var index = 1;
|
||||
var maxKeyFrameIndex = _sortedResolvedKeyFrames.Length - 1;
|
||||
var blockBeginTime = TimeSpan.Zero;
|
||||
|
||||
do
|
||||
if (block.BeginIndex > 0)
|
||||
{
|
||||
if (_keyFrames[index].KeyTime.Type == KeyTimeType.Paced)
|
||||
{
|
||||
var firstPacedBlockKeyFrameIndex = index;
|
||||
|
||||
var segmentLengths = new List<double>();
|
||||
var prePacedBlockKeyTime = _sortedResolvedKeyFrames[index - 1]._resolvedKeyTime;
|
||||
var totalLength = 0.0;
|
||||
var prevKeyValue = _keyFrames[index - 1].Numbers;
|
||||
|
||||
do
|
||||
{
|
||||
var currentKeyValue = _keyFrames[index].Numbers;
|
||||
totalLength += Math.Abs(currentKeyValue[0] - prevKeyValue[0]);
|
||||
segmentLengths.Add(totalLength);
|
||||
prevKeyValue = currentKeyValue;
|
||||
index++;
|
||||
}
|
||||
while (index < maxKeyFrameIndex && _keyFrames[index].KeyTime.Type == KeyTimeType.Paced);
|
||||
totalLength += Math.Abs(_keyFrames[index].Numbers[0] - prevKeyValue[0]);
|
||||
|
||||
var pacedBlockDuration = _sortedResolvedKeyFrames[index]._resolvedKeyTime - prePacedBlockKeyTime;
|
||||
|
||||
for (int i = 0, currentKeyFrameIndex = firstPacedBlockKeyFrameIndex; i < segmentLengths.Count; i++, currentKeyFrameIndex++)
|
||||
{
|
||||
_sortedResolvedKeyFrames[currentKeyFrameIndex]._resolvedKeyTime = prePacedBlockKeyTime + TimeSpan.FromMilliseconds(segmentLengths[i] / totalLength * pacedBlockDuration.TotalMilliseconds);
|
||||
}
|
||||
}
|
||||
else
|
||||
blockBeginTime = _sortedResolvedKeyFrames[block.BeginIndex - 1]._resolvedKeyTime;
|
||||
}
|
||||
|
||||
long segmentCount = block.EndIndex - block.BeginIndex + 1;
|
||||
var uniformTimeStep = TimeSpan.FromTicks((_sortedResolvedKeyFrames[block.EndIndex]._resolvedKeyTime - blockBeginTime).Ticks / segmentCount);
|
||||
|
||||
index = block.BeginIndex;
|
||||
var resolvedTime = blockBeginTime + uniformTimeStep;
|
||||
|
||||
while (index < block.EndIndex)
|
||||
{
|
||||
_sortedResolvedKeyFrames[index]._resolvedKeyTime = resolvedTime;
|
||||
|
||||
resolvedTime += uniformTimeStep;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasPacedKeyTimes)
|
||||
{
|
||||
ResolvePacedKeyTimes();
|
||||
}
|
||||
|
||||
Array.Sort(_sortedResolvedKeyFrames);
|
||||
|
||||
_areKeyTimesValid = true;
|
||||
}
|
||||
|
||||
private void ResolvePacedKeyTimes()
|
||||
{
|
||||
var index = 1;
|
||||
var maxKeyFrameIndex = _sortedResolvedKeyFrames.Length - 1;
|
||||
|
||||
do
|
||||
{
|
||||
if (_keyFrames[index].KeyTime.Type == KeyTimeType.Paced)
|
||||
{
|
||||
var firstPacedBlockKeyFrameIndex = index;
|
||||
|
||||
var segmentLengths = new List<double>();
|
||||
var prePacedBlockKeyTime = _sortedResolvedKeyFrames[index - 1]._resolvedKeyTime;
|
||||
var totalLength = 0.0;
|
||||
var prevKeyValue = _keyFrames[index - 1].Numbers;
|
||||
|
||||
do
|
||||
{
|
||||
var currentKeyValue = _keyFrames[index].Numbers;
|
||||
totalLength += Math.Abs(currentKeyValue[0] - prevKeyValue[0]);
|
||||
segmentLengths.Add(totalLength);
|
||||
prevKeyValue = currentKeyValue;
|
||||
index++;
|
||||
}
|
||||
while (index < maxKeyFrameIndex && _keyFrames[index].KeyTime.Type == KeyTimeType.Paced);
|
||||
totalLength += Math.Abs(_keyFrames[index].Numbers[0] - prevKeyValue[0]);
|
||||
|
||||
var pacedBlockDuration = _sortedResolvedKeyFrames[index]._resolvedKeyTime - prePacedBlockKeyTime;
|
||||
|
||||
for (int i = 0, currentKeyFrameIndex = firstPacedBlockKeyFrameIndex; i < segmentLengths.Count; i++, currentKeyFrameIndex++)
|
||||
{
|
||||
_sortedResolvedKeyFrames[currentKeyFrameIndex]._resolvedKeyTime = prePacedBlockKeyTime + TimeSpan.FromMilliseconds(segmentLengths[i] / totalLength * pacedBlockDuration.TotalMilliseconds);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
index++;
|
||||
}
|
||||
while (index < maxKeyFrameIndex);
|
||||
}
|
||||
while (index < maxKeyFrameIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,320 +4,319 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
|
||||
namespace HandyControl.Media.Animation
|
||||
namespace HandyControl.Media.Animation;
|
||||
|
||||
public class GeometryKeyFrameCollection : Freezable, IList
|
||||
{
|
||||
public class GeometryKeyFrameCollection : Freezable, IList
|
||||
private List<GeometryKeyFrame> _keyFrames;
|
||||
|
||||
private static GeometryKeyFrameCollection s_emptyCollection;
|
||||
|
||||
public GeometryKeyFrameCollection()
|
||||
{
|
||||
private List<GeometryKeyFrame> _keyFrames;
|
||||
_keyFrames = new List<GeometryKeyFrame>(2);
|
||||
}
|
||||
|
||||
private static GeometryKeyFrameCollection s_emptyCollection;
|
||||
|
||||
public GeometryKeyFrameCollection()
|
||||
public static GeometryKeyFrameCollection Empty
|
||||
{
|
||||
get
|
||||
{
|
||||
_keyFrames = new List<GeometryKeyFrame>(2);
|
||||
}
|
||||
|
||||
public static GeometryKeyFrameCollection Empty
|
||||
{
|
||||
get
|
||||
if (s_emptyCollection == null)
|
||||
{
|
||||
if (s_emptyCollection == null)
|
||||
var emptyCollection = new GeometryKeyFrameCollection
|
||||
{
|
||||
var emptyCollection = new GeometryKeyFrameCollection
|
||||
{
|
||||
_keyFrames = new List<GeometryKeyFrame>(0)
|
||||
};
|
||||
_keyFrames = new List<GeometryKeyFrame>(0)
|
||||
};
|
||||
|
||||
emptyCollection.Freeze();
|
||||
emptyCollection.Freeze();
|
||||
|
||||
s_emptyCollection = emptyCollection;
|
||||
}
|
||||
|
||||
return s_emptyCollection;
|
||||
}
|
||||
}
|
||||
|
||||
public new GeometryKeyFrameCollection Clone() => (GeometryKeyFrameCollection) base.Clone();
|
||||
|
||||
protected override Freezable CreateInstanceCore() => new GeometryKeyFrameCollection();
|
||||
|
||||
protected override void CloneCore(Freezable sourceFreezable)
|
||||
{
|
||||
var sourceCollection = (GeometryKeyFrameCollection) sourceFreezable;
|
||||
base.CloneCore(sourceFreezable);
|
||||
|
||||
var count = sourceCollection._keyFrames.Count;
|
||||
|
||||
_keyFrames = new List<GeometryKeyFrame>(count);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var keyFrame = (GeometryKeyFrame) sourceCollection._keyFrames[i].Clone();
|
||||
_keyFrames.Add(keyFrame);
|
||||
OnFreezablePropertyChanged(null, keyFrame);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CloneCurrentValueCore(Freezable sourceFreezable)
|
||||
{
|
||||
var sourceCollection = (GeometryKeyFrameCollection) sourceFreezable;
|
||||
base.CloneCurrentValueCore(sourceFreezable);
|
||||
|
||||
var count = sourceCollection._keyFrames.Count;
|
||||
|
||||
_keyFrames = new List<GeometryKeyFrame>(count);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var keyFrame = (GeometryKeyFrame) sourceCollection._keyFrames[i].CloneCurrentValue();
|
||||
_keyFrames.Add(keyFrame);
|
||||
OnFreezablePropertyChanged(null, keyFrame);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void GetAsFrozenCore(Freezable sourceFreezable)
|
||||
{
|
||||
var sourceCollection = (GeometryKeyFrameCollection) sourceFreezable;
|
||||
base.GetAsFrozenCore(sourceFreezable);
|
||||
|
||||
var count = sourceCollection._keyFrames.Count;
|
||||
|
||||
_keyFrames = new List<GeometryKeyFrame>(count);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var keyFrame = (GeometryKeyFrame) sourceCollection._keyFrames[i].GetAsFrozen();
|
||||
_keyFrames.Add(keyFrame);
|
||||
OnFreezablePropertyChanged(null, keyFrame);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable)
|
||||
{
|
||||
var sourceCollection = (GeometryKeyFrameCollection) sourceFreezable;
|
||||
base.GetCurrentValueAsFrozenCore(sourceFreezable);
|
||||
|
||||
var count = sourceCollection._keyFrames.Count;
|
||||
|
||||
_keyFrames = new List<GeometryKeyFrame>(count);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var keyFrame = (GeometryKeyFrame) sourceCollection._keyFrames[i].GetCurrentValueAsFrozen();
|
||||
_keyFrames.Add(keyFrame);
|
||||
OnFreezablePropertyChanged(null, keyFrame);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool FreezeCore(bool isChecking)
|
||||
{
|
||||
var canFreeze = base.FreezeCore(isChecking);
|
||||
|
||||
for (var i = 0; i < _keyFrames.Count && canFreeze; i++)
|
||||
{
|
||||
canFreeze &= Freeze(_keyFrames[i], isChecking);
|
||||
s_emptyCollection = emptyCollection;
|
||||
}
|
||||
|
||||
return canFreeze;
|
||||
return s_emptyCollection;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
public new GeometryKeyFrameCollection Clone() => (GeometryKeyFrameCollection) base.Clone();
|
||||
|
||||
protected override Freezable CreateInstanceCore() => new GeometryKeyFrameCollection();
|
||||
|
||||
protected override void CloneCore(Freezable sourceFreezable)
|
||||
{
|
||||
var sourceCollection = (GeometryKeyFrameCollection) sourceFreezable;
|
||||
base.CloneCore(sourceFreezable);
|
||||
|
||||
var count = sourceCollection._keyFrames.Count;
|
||||
|
||||
_keyFrames = new List<GeometryKeyFrame>(count);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return _keyFrames.GetEnumerator();
|
||||
}
|
||||
|
||||
void ICollection.CopyTo(Array array, int index)
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
((ICollection) _keyFrames).CopyTo(array, index);
|
||||
}
|
||||
|
||||
public void CopyTo(GeometryKeyFrame[] array, int index)
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
_keyFrames.CopyTo(array, index);
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return _keyFrames.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public object SyncRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return ((ICollection) _keyFrames).SyncRoot;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSynchronized
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return IsFrozen || Dispatcher != null;
|
||||
}
|
||||
}
|
||||
|
||||
int IList.Add(object keyFrame) => Add((GeometryKeyFrame) keyFrame);
|
||||
|
||||
public int Add(GeometryKeyFrame keyFrame)
|
||||
{
|
||||
if (keyFrame == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(keyFrame));
|
||||
}
|
||||
|
||||
WritePreamble();
|
||||
|
||||
OnFreezablePropertyChanged(null, keyFrame);
|
||||
var keyFrame = (GeometryKeyFrame) sourceCollection._keyFrames[i].Clone();
|
||||
_keyFrames.Add(keyFrame);
|
||||
|
||||
WritePostscript();
|
||||
|
||||
return _keyFrames.Count - 1;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
WritePreamble();
|
||||
|
||||
if (_keyFrames.Count <= 0) return;
|
||||
|
||||
foreach (var frame in _keyFrames)
|
||||
{
|
||||
OnFreezablePropertyChanged(frame, null);
|
||||
}
|
||||
|
||||
_keyFrames.Clear();
|
||||
|
||||
WritePostscript();
|
||||
}
|
||||
|
||||
bool IList.Contains(object keyFrame) => Contains((GeometryKeyFrame) keyFrame);
|
||||
|
||||
public bool Contains(GeometryKeyFrame keyFrame)
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return _keyFrames.Contains(keyFrame);
|
||||
}
|
||||
|
||||
int IList.IndexOf(object keyFrame) => IndexOf((GeometryKeyFrame) keyFrame);
|
||||
|
||||
public int IndexOf(GeometryKeyFrame keyFrame)
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return _keyFrames.IndexOf(keyFrame);
|
||||
}
|
||||
|
||||
void IList.Insert(int index, object keyFrame) => Insert(index, (GeometryKeyFrame) keyFrame);
|
||||
|
||||
public void Insert(int index, GeometryKeyFrame keyFrame)
|
||||
{
|
||||
if (keyFrame == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(keyFrame));
|
||||
}
|
||||
|
||||
WritePreamble();
|
||||
|
||||
OnFreezablePropertyChanged(null, keyFrame);
|
||||
_keyFrames.Insert(index, keyFrame);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CloneCurrentValueCore(Freezable sourceFreezable)
|
||||
{
|
||||
var sourceCollection = (GeometryKeyFrameCollection) sourceFreezable;
|
||||
base.CloneCurrentValueCore(sourceFreezable);
|
||||
|
||||
var count = sourceCollection._keyFrames.Count;
|
||||
|
||||
_keyFrames = new List<GeometryKeyFrame>(count);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var keyFrame = (GeometryKeyFrame) sourceCollection._keyFrames[i].CloneCurrentValue();
|
||||
_keyFrames.Add(keyFrame);
|
||||
OnFreezablePropertyChanged(null, keyFrame);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void GetAsFrozenCore(Freezable sourceFreezable)
|
||||
{
|
||||
var sourceCollection = (GeometryKeyFrameCollection) sourceFreezable;
|
||||
base.GetAsFrozenCore(sourceFreezable);
|
||||
|
||||
var count = sourceCollection._keyFrames.Count;
|
||||
|
||||
_keyFrames = new List<GeometryKeyFrame>(count);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var keyFrame = (GeometryKeyFrame) sourceCollection._keyFrames[i].GetAsFrozen();
|
||||
_keyFrames.Add(keyFrame);
|
||||
OnFreezablePropertyChanged(null, keyFrame);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable)
|
||||
{
|
||||
var sourceCollection = (GeometryKeyFrameCollection) sourceFreezable;
|
||||
base.GetCurrentValueAsFrozenCore(sourceFreezable);
|
||||
|
||||
var count = sourceCollection._keyFrames.Count;
|
||||
|
||||
_keyFrames = new List<GeometryKeyFrame>(count);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var keyFrame = (GeometryKeyFrame) sourceCollection._keyFrames[i].GetCurrentValueAsFrozen();
|
||||
_keyFrames.Add(keyFrame);
|
||||
OnFreezablePropertyChanged(null, keyFrame);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool FreezeCore(bool isChecking)
|
||||
{
|
||||
var canFreeze = base.FreezeCore(isChecking);
|
||||
|
||||
for (var i = 0; i < _keyFrames.Count && canFreeze; i++)
|
||||
{
|
||||
canFreeze &= Freeze(_keyFrames[i], isChecking);
|
||||
}
|
||||
|
||||
return canFreeze;
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return _keyFrames.GetEnumerator();
|
||||
}
|
||||
|
||||
void ICollection.CopyTo(Array array, int index)
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
((ICollection) _keyFrames).CopyTo(array, index);
|
||||
}
|
||||
|
||||
public void CopyTo(GeometryKeyFrame[] array, int index)
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
_keyFrames.CopyTo(array, index);
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return _keyFrames.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public object SyncRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return ((ICollection) _keyFrames).SyncRoot;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSynchronized
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return IsFrozen || Dispatcher != null;
|
||||
}
|
||||
}
|
||||
|
||||
int IList.Add(object keyFrame) => Add((GeometryKeyFrame) keyFrame);
|
||||
|
||||
public int Add(GeometryKeyFrame keyFrame)
|
||||
{
|
||||
if (keyFrame == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(keyFrame));
|
||||
}
|
||||
|
||||
WritePreamble();
|
||||
|
||||
OnFreezablePropertyChanged(null, keyFrame);
|
||||
_keyFrames.Add(keyFrame);
|
||||
|
||||
WritePostscript();
|
||||
|
||||
return _keyFrames.Count - 1;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
WritePreamble();
|
||||
|
||||
if (_keyFrames.Count <= 0) return;
|
||||
|
||||
foreach (var frame in _keyFrames)
|
||||
{
|
||||
OnFreezablePropertyChanged(frame, null);
|
||||
}
|
||||
|
||||
_keyFrames.Clear();
|
||||
|
||||
WritePostscript();
|
||||
}
|
||||
|
||||
bool IList.Contains(object keyFrame) => Contains((GeometryKeyFrame) keyFrame);
|
||||
|
||||
public bool Contains(GeometryKeyFrame keyFrame)
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return _keyFrames.Contains(keyFrame);
|
||||
}
|
||||
|
||||
int IList.IndexOf(object keyFrame) => IndexOf((GeometryKeyFrame) keyFrame);
|
||||
|
||||
public int IndexOf(GeometryKeyFrame keyFrame)
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return _keyFrames.IndexOf(keyFrame);
|
||||
}
|
||||
|
||||
void IList.Insert(int index, object keyFrame) => Insert(index, (GeometryKeyFrame) keyFrame);
|
||||
|
||||
public void Insert(int index, GeometryKeyFrame keyFrame)
|
||||
{
|
||||
if (keyFrame == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(keyFrame));
|
||||
}
|
||||
|
||||
WritePreamble();
|
||||
|
||||
OnFreezablePropertyChanged(null, keyFrame);
|
||||
_keyFrames.Insert(index, keyFrame);
|
||||
|
||||
WritePostscript();
|
||||
}
|
||||
|
||||
void IList.Remove(object keyFrame) => Remove((GeometryKeyFrame) keyFrame);
|
||||
|
||||
public void Remove(GeometryKeyFrame keyFrame)
|
||||
{
|
||||
WritePreamble();
|
||||
|
||||
if (_keyFrames.Contains(keyFrame))
|
||||
{
|
||||
OnFreezablePropertyChanged(keyFrame, null);
|
||||
_keyFrames.Remove(keyFrame);
|
||||
|
||||
WritePostscript();
|
||||
}
|
||||
}
|
||||
|
||||
void IList.Remove(object keyFrame) => Remove((GeometryKeyFrame) keyFrame);
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
WritePreamble();
|
||||
|
||||
public void Remove(GeometryKeyFrame keyFrame)
|
||||
OnFreezablePropertyChanged(_keyFrames[index], null);
|
||||
_keyFrames.RemoveAt(index);
|
||||
|
||||
WritePostscript();
|
||||
}
|
||||
|
||||
object IList.this[int index]
|
||||
{
|
||||
get => this[index];
|
||||
set => this[index] = (GeometryKeyFrame) value;
|
||||
}
|
||||
|
||||
public GeometryKeyFrame this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return _keyFrames[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(string.Format(CultureInfo.InvariantCulture, "DoubleKeyFrameCollection[{0}]", index));
|
||||
}
|
||||
|
||||
WritePreamble();
|
||||
|
||||
if (_keyFrames.Contains(keyFrame))
|
||||
if (value != _keyFrames[index])
|
||||
{
|
||||
OnFreezablePropertyChanged(keyFrame, null);
|
||||
_keyFrames.Remove(keyFrame);
|
||||
OnFreezablePropertyChanged(_keyFrames[index], value);
|
||||
_keyFrames[index] = value;
|
||||
|
||||
WritePostscript();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
WritePreamble();
|
||||
ReadPreamble();
|
||||
|
||||
OnFreezablePropertyChanged(_keyFrames[index], null);
|
||||
_keyFrames.RemoveAt(index);
|
||||
|
||||
WritePostscript();
|
||||
}
|
||||
|
||||
object IList.this[int index]
|
||||
{
|
||||
get => this[index];
|
||||
set => this[index] = (GeometryKeyFrame) value;
|
||||
}
|
||||
|
||||
public GeometryKeyFrame this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return _keyFrames[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(string.Format(CultureInfo.InvariantCulture, "DoubleKeyFrameCollection[{0}]", index));
|
||||
}
|
||||
|
||||
WritePreamble();
|
||||
|
||||
if (value != _keyFrames[index])
|
||||
{
|
||||
OnFreezablePropertyChanged(_keyFrames[index], value);
|
||||
_keyFrames[index] = value;
|
||||
|
||||
WritePostscript();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return IsFrozen;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFixedSize
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return IsFrozen;
|
||||
}
|
||||
return IsFrozen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFixedSize
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadPreamble();
|
||||
|
||||
return IsFrozen;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,40 +4,39 @@ using System.Windows.Media.Animation;
|
||||
using HandyControl.Expression.Drawing;
|
||||
using HandyControl.Tools;
|
||||
|
||||
namespace HandyControl.Media.Animation
|
||||
namespace HandyControl.Media.Animation;
|
||||
|
||||
public class LinearGeometryKeyFrame : GeometryKeyFrame
|
||||
{
|
||||
public class LinearGeometryKeyFrame : GeometryKeyFrame
|
||||
public LinearGeometryKeyFrame()
|
||||
{
|
||||
public LinearGeometryKeyFrame()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public LinearGeometryKeyFrame(Geometry value) : base(value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public LinearGeometryKeyFrame(Geometry value, KeyTime keyTime) : base(value, keyTime)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override Freezable CreateInstanceCore() => new LinearGeometryKeyFrame();
|
||||
|
||||
protected override double[] InterpolateValueCore(double[] baseValue, double keyFrameProgress)
|
||||
{
|
||||
if (MathHelper.IsVerySmall(keyFrameProgress))
|
||||
{
|
||||
return baseValue;
|
||||
}
|
||||
|
||||
if (MathHelper.AreClose(keyFrameProgress, 1))
|
||||
{
|
||||
return Numbers;
|
||||
}
|
||||
|
||||
return AnimationHelper.InterpolateGeometryValue(baseValue, Numbers, keyFrameProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LinearGeometryKeyFrame(Geometry value) : base(value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public LinearGeometryKeyFrame(Geometry value, KeyTime keyTime) : base(value, keyTime)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override Freezable CreateInstanceCore() => new LinearGeometryKeyFrame();
|
||||
|
||||
protected override double[] InterpolateValueCore(double[] baseValue, double keyFrameProgress)
|
||||
{
|
||||
if (MathHelper.IsVerySmall(keyFrameProgress))
|
||||
{
|
||||
return baseValue;
|
||||
}
|
||||
|
||||
if (MathHelper.AreClose(keyFrameProgress, 1))
|
||||
{
|
||||
return Numbers;
|
||||
}
|
||||
|
||||
return AnimationHelper.InterpolateGeometryValue(baseValue, Numbers, keyFrameProgress);
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,37 @@
|
||||
using System;
|
||||
|
||||
namespace HandyControl.Media.Animation
|
||||
namespace HandyControl.Media.Animation;
|
||||
|
||||
internal struct ResolvedKeyFrameEntry : IComparable
|
||||
{
|
||||
internal struct ResolvedKeyFrameEntry : IComparable
|
||||
internal int _originalKeyFrameIndex;
|
||||
|
||||
internal TimeSpan _resolvedKeyTime;
|
||||
|
||||
public int CompareTo(object other)
|
||||
{
|
||||
internal int _originalKeyFrameIndex;
|
||||
var otherEntry = (ResolvedKeyFrameEntry) other;
|
||||
|
||||
internal TimeSpan _resolvedKeyTime;
|
||||
|
||||
public int CompareTo(object other)
|
||||
if (otherEntry._resolvedKeyTime > _resolvedKeyTime)
|
||||
{
|
||||
var otherEntry = (ResolvedKeyFrameEntry) other;
|
||||
|
||||
if (otherEntry._resolvedKeyTime > _resolvedKeyTime)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (otherEntry._resolvedKeyTime < _resolvedKeyTime)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (otherEntry._originalKeyFrameIndex > _originalKeyFrameIndex)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (otherEntry._originalKeyFrameIndex < _originalKeyFrameIndex)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (otherEntry._resolvedKeyTime < _resolvedKeyTime)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (otherEntry._originalKeyFrameIndex > _originalKeyFrameIndex)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (otherEntry._originalKeyFrameIndex < _originalKeyFrameIndex)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,55 +5,54 @@ using System.Windows.Media.Animation;
|
||||
using HandyControl.Expression.Drawing;
|
||||
using HandyControl.Tools;
|
||||
|
||||
namespace HandyControl.Media.Animation
|
||||
namespace HandyControl.Media.Animation;
|
||||
|
||||
public class SplineGeometryKeyFrame : GeometryKeyFrame
|
||||
{
|
||||
public class SplineGeometryKeyFrame : GeometryKeyFrame
|
||||
public SplineGeometryKeyFrame()
|
||||
{
|
||||
public SplineGeometryKeyFrame()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public SplineGeometryKeyFrame(Geometry value) : base(value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public SplineGeometryKeyFrame(Geometry value, KeyTime keyTime) : base(value, keyTime)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public SplineGeometryKeyFrame(Geometry value, KeyTime keyTime, KeySpline keySpline) : base(value, keyTime)
|
||||
{
|
||||
KeySpline = keySpline ?? throw new ArgumentNullException(nameof(keySpline));
|
||||
}
|
||||
|
||||
protected override Freezable CreateInstanceCore() => new SplineGeometryKeyFrame();
|
||||
|
||||
protected override double[] InterpolateValueCore(double[] baseValue, double keyFrameProgress)
|
||||
{
|
||||
if (MathHelper.IsVerySmall(keyFrameProgress))
|
||||
{
|
||||
return baseValue;
|
||||
}
|
||||
|
||||
if (MathHelper.AreClose(keyFrameProgress, 1))
|
||||
{
|
||||
return Numbers;
|
||||
}
|
||||
|
||||
var splineProgress = KeySpline.GetSplineProgress(keyFrameProgress);
|
||||
return AnimationHelper.InterpolateGeometryValue(baseValue, Numbers, splineProgress);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty KeySplineProperty = DependencyProperty.Register(
|
||||
"KeySpline", typeof(KeySpline), typeof(SplineGeometryKeyFrame), new PropertyMetadata(new KeySpline()));
|
||||
|
||||
public KeySpline KeySpline
|
||||
{
|
||||
get => (KeySpline) GetValue(KeySplineProperty);
|
||||
set => SetValue(KeySplineProperty, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SplineGeometryKeyFrame(Geometry value) : base(value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public SplineGeometryKeyFrame(Geometry value, KeyTime keyTime) : base(value, keyTime)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public SplineGeometryKeyFrame(Geometry value, KeyTime keyTime, KeySpline keySpline) : base(value, keyTime)
|
||||
{
|
||||
KeySpline = keySpline ?? throw new ArgumentNullException(nameof(keySpline));
|
||||
}
|
||||
|
||||
protected override Freezable CreateInstanceCore() => new SplineGeometryKeyFrame();
|
||||
|
||||
protected override double[] InterpolateValueCore(double[] baseValue, double keyFrameProgress)
|
||||
{
|
||||
if (MathHelper.IsVerySmall(keyFrameProgress))
|
||||
{
|
||||
return baseValue;
|
||||
}
|
||||
|
||||
if (MathHelper.AreClose(keyFrameProgress, 1))
|
||||
{
|
||||
return Numbers;
|
||||
}
|
||||
|
||||
var splineProgress = KeySpline.GetSplineProgress(keyFrameProgress);
|
||||
return AnimationHelper.InterpolateGeometryValue(baseValue, Numbers, splineProgress);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty KeySplineProperty = DependencyProperty.Register(
|
||||
"KeySpline", typeof(KeySpline), typeof(SplineGeometryKeyFrame), new PropertyMetadata(new KeySpline()));
|
||||
|
||||
public KeySpline KeySpline
|
||||
{
|
||||
get => (KeySpline) GetValue(KeySplineProperty);
|
||||
set => SetValue(KeySplineProperty, value);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user