using System; namespace Cryville.Common { public class PropertyTweener { readonly Func _getter; readonly Action _setter; readonly Tweener _tweener; public PropertyTweener(Func getter, Action setter, Tweener tweener) { _getter = getter; _setter = setter; _tweener = tweener; var initialValue = getter(); _tweener.Start(initialValue, initialValue, float.Epsilon); } public PropertyTweener Start(T endValue, float duration) { _tweener.Start(_getter(), endValue, duration); return this; } public void Advance(float deltaTime) { if (!_tweener.Advance(deltaTime, out var value)) return; _setter(value); } } public delegate T Addition(T a, T b); public delegate T Multiplication(float k, T b); public delegate float EasingFunction(float t); public class Tweener { readonly Addition _addition; readonly Multiplication _multiplication; public Tweener(Addition addition, Multiplication multiplication) { _addition = addition; _multiplication = multiplication; } public EasingFunction EasingFunction { get; set; } = EasingFunctions.Linear; public Tweener With(EasingFunction easing) { EasingFunction = easing; return this; } T _startValue = default; T _endValue = default; float _duration = float.Epsilon; float _time; bool _endOfTween; public Tweener Start(T startValue, T endValue, float duration) { _startValue = startValue; _endValue = endValue; _duration = duration; _time = 0; _endOfTween = false; return this; } public bool Advance(float deltaTime, out T value) { if (_endOfTween) { value = _endValue; return false; } if (_time >= _duration) { value = _endValue; _endOfTween = true; return true; } _time += deltaTime; var ratio = EasingFunction(System.Math.Clamp(_time / _duration, 0, 1)); value = _addition(_multiplication(1 - ratio, _startValue), _multiplication(ratio, _endValue)); return true; } public Tweener Boxed() { return new Tweener((a, b) => _addition((T)a, (T)b), (k, v) => _multiplication(k, (T)v)); } } public static class Tweeners { public static Tweener Byte => new((a, b) => (byte)(a + b), (k, v) => (byte)(k * v)); public static Tweener SByte => new((a, b) => (sbyte)(a + b), (k, v) => (sbyte)(k * v)); public static Tweener Int16 => new((a, b) => (short)(a + b), (k, v) => (short)(k * v)); public static Tweener UInt16 => new((a, b) => (ushort)(a + b), (k, v) => (ushort)(k * v)); public static Tweener Int32 => new((a, b) => a + b, (k, v) => (int)(k * v)); public static Tweener UInt32 => new((a, b) => a + b, (k, v) => (uint)(k * v)); public static Tweener Int64 => new((a, b) => a + b, (k, v) => (long)(k * v)); public static Tweener UInt64 => new((a, b) => a + b, (k, v) => (ulong)(k * v)); public static Tweener IntPtr => new((a, b) => new IntPtr((long)a + (long)b), (k, v) => new IntPtr((long)(k * (long)v))); public static Tweener UIntPtr => new((a, b) => new UIntPtr((ulong)a + (ulong)b), (k, v) => new UIntPtr((ulong)(k * (ulong)v))); public static Tweener Float => new((a, b) => a + b, (k, v) => k * v); public static Tweener Double => new((a, b) => a + b, (k, v) => k * v); } public static class EasingFunctions { public static float Linear(float x) => x; public static float InQuad(float x) => x * x; public static float InCubic(float x) => x * x * x; public static float InSine(float x) => 1 - OutSine(1 - x); public static float OutQuad(float x) => 1 - InQuad(1 - x); public static float OutCubic(float x) => 1 - InCubic(1 - x); public static float OutSine(float x) => MathF.Sin(x * MathF.PI / 2); } }