using System.Globalization; using System.Text.RegularExpressions; using System.Windows; using System.Windows.Media; using System.Windows.Media.Animation; using HandyControl.Data; using HandyControl.Tools; // ReSharper disable PossibleInvalidOperationException namespace HandyControl.Media.Animation; public class GeometryAnimation : GeometryAnimationBase { private string[] _strings; private double[] _numbersFrom; private double[] _numbersTo; private double[] _numbersAccumulator; public GeometryAnimation() { } 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( nameof(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( nameof(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( nameof(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)); } }