···29 /// <param name="easing">The transform easing to be used for tweening.</param>
30 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
31 public static TransformSequence<TThis> TransformTo<TThis, TValue>(this TThis t, string propertyOrFieldName, TValue newValue, double duration = 0, Easing easing = Easing.None)
32- where TThis : class, ITransformable =>
33- t.TransformTo(t.MakeTransform(propertyOrFieldName, newValue, duration, easing));
0000000000000000003435 /// <summary>
36 /// Applies a <see cref="Transform"/> to a given <see cref="ITransformable"/>.
···50 /// <summary>
51 /// Creates a <see cref="Transform{TValue, T}"/> for smoothly changing <paramref name="propertyOrFieldName"/>
52 /// over time using the given <paramref name="easing"/> for tweening.
53- /// <see cref="PopulateTransform{TValue, TThis}(TThis, Transform{TValue, TThis}, TValue, double, Easing)"/>
54- /// is invoked as part of this method.
0000000000000000055 /// </summary>
56 /// <typeparam name="TThis">The type of the <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> can be applied to.</typeparam>
57 /// <typeparam name="TValue">The value type which is being transformed.</typeparam>
058 /// <param name="t">The <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> will be applied to.</param>
59 /// <param name="propertyOrFieldName">The property or field name of the member ot <typeparamref name="TThis"/> to transform.</param>
60 /// <param name="newValue">The value to transform to.</param>
61 /// <param name="duration">The transform duration.</param>
62 /// <param name="easing">The transform easing to be used for tweening.</param>
63 /// <returns>The resulting <see cref="Transform{TValue, T}"/>.</returns>
64- public static Transform<TValue, TThis> MakeTransform<TThis, TValue>(this TThis t, string propertyOrFieldName, TValue newValue, double duration = 0, Easing easing = Easing.None)
65- where TThis : class, ITransformable =>
66- t.PopulateTransform(new TransformCustom<TValue, TThis>(propertyOrFieldName), newValue, duration, easing);
06768 /// <summary>
69 /// Populates a newly created <see cref="Transform{TValue, T}"/> with necessary values.
···77 /// <param name="duration">The transform duration.</param>
78 /// <param name="easing">The transform easing to be used for tweening.</param>
79 /// <returns>The populated <paramref name="transform"/>.</returns>
80- public static Transform<TValue, TThis> PopulateTransform<TValue, TThis>(this TThis t, Transform<TValue, TThis> transform, TValue newValue, double duration = 0, Easing easing = Easing.None)
081 where TThis : class, ITransformable
000000000000000000082 {
83 if (duration < 0)
84 throw new ArgumentOutOfRangeException(nameof(duration), $"{nameof(duration)} must be positive.");
···415 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
416 public static TransformSequence<T> TransformBindableTo<T, TValue>(this T drawable, [NotNull] Bindable<TValue> bindable, TValue newValue, double duration = 0, Easing easing = Easing.None)
417 where T : class, ITransformable =>
418- drawable.TransformTo(drawable.PopulateTransform(new TransformBindable<TValue, T>(bindable), newValue, duration, easing));
419 }
420}
···29 /// <param name="easing">The transform easing to be used for tweening.</param>
30 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
31 public static TransformSequence<TThis> TransformTo<TThis, TValue>(this TThis t, string propertyOrFieldName, TValue newValue, double duration = 0, Easing easing = Easing.None)
32+ where TThis : class, ITransformable
33+ => t.TransformTo(t.MakeTransform(propertyOrFieldName, newValue, duration, new DefaultEasingFunction(easing)));
34+35+ /// <summary>
36+ /// Transforms a given property or field member of a given <see cref="ITransformable"/> <typeparamref name="TThis"/> to <paramref name="newValue"/>.
37+ /// The value of the given member is smoothly changed over time using the given <paramref name="easing"/> for tweening.
38+ /// </summary>
39+ /// <typeparam name="TThis">The type of the <see cref="ITransformable"/> to apply the <see cref="Transform{TValue, T}"/> to.</typeparam>
40+ /// <typeparam name="TValue">The value type which is being transformed.</typeparam>
41+ /// <typeparam name="TEasing">The type of easing.</typeparam>
42+ /// <param name="t">The <see cref="ITransformable"/> to apply the <see cref="Transform{TValue, T}"/> to.</param>
43+ /// <param name="propertyOrFieldName">The property or field name of the member ot <typeparamref name="TThis"/> to transform.</param>
44+ /// <param name="newValue">The value to transform to.</param>
45+ /// <param name="duration">The transform duration.</param>
46+ /// <param name="easing">The transform easing to be used for tweening.</param>
47+ /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
48+ public static TransformSequence<TThis> TransformTo<TThis, TValue, TEasing>(this TThis t, string propertyOrFieldName, TValue newValue, double duration, TEasing easing)
49+ where TThis : class, ITransformable
50+ where TEasing : IEasingFunction
51+ => t.TransformTo(t.MakeTransform(propertyOrFieldName, newValue, duration, easing));
5253 /// <summary>
54 /// Applies a <see cref="Transform"/> to a given <see cref="ITransformable"/>.
···68 /// <summary>
69 /// Creates a <see cref="Transform{TValue, T}"/> for smoothly changing <paramref name="propertyOrFieldName"/>
70 /// over time using the given <paramref name="easing"/> for tweening.
71+ /// <see cref="PopulateTransform{TValue, DefaultEasingFunction, TThis}"/> is invoked as part of this method.
72+ /// </summary>
73+ /// <typeparam name="TThis">The type of the <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> can be applied to.</typeparam>
74+ /// <typeparam name="TValue">The value type which is being transformed.</typeparam>
75+ /// <param name="t">The <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> will be applied to.</param>
76+ /// <param name="propertyOrFieldName">The property or field name of the member ot <typeparamref name="TThis"/> to transform.</param>
77+ /// <param name="newValue">The value to transform to.</param>
78+ /// <param name="duration">The transform duration.</param>
79+ /// <param name="easing">The transform easing to be used for tweening.</param>
80+ /// <returns>The resulting <see cref="Transform{TValue, T}"/>.</returns>
81+ public static Transform<TValue, DefaultEasingFunction, TThis> MakeTransform<TThis, TValue>(this TThis t, string propertyOrFieldName, TValue newValue, double duration = 0,
82+ Easing easing = Easing.None)
83+ where TThis : class, ITransformable
84+ => t.MakeTransform(propertyOrFieldName, newValue, duration, new DefaultEasingFunction(easing));
85+86+ /// <summary>
87+ /// Creates a <see cref="Transform{TValue, T}"/> for smoothly changing <paramref name="propertyOrFieldName"/>
88+ /// over time using the given <paramref name="easing"/> for tweening.
89+ /// <see cref="PopulateTransform{TValue, TEasing, TThis}"/> is invoked as part of this method.
90 /// </summary>
91 /// <typeparam name="TThis">The type of the <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> can be applied to.</typeparam>
92 /// <typeparam name="TValue">The value type which is being transformed.</typeparam>
93+ /// <typeparam name="TEasing">The type of easing.</typeparam>
94 /// <param name="t">The <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> will be applied to.</param>
95 /// <param name="propertyOrFieldName">The property or field name of the member ot <typeparamref name="TThis"/> to transform.</param>
96 /// <param name="newValue">The value to transform to.</param>
97 /// <param name="duration">The transform duration.</param>
98 /// <param name="easing">The transform easing to be used for tweening.</param>
99 /// <returns>The resulting <see cref="Transform{TValue, T}"/>.</returns>
100+ public static Transform<TValue, TEasing, TThis> MakeTransform<TThis, TEasing, TValue>(this TThis t, string propertyOrFieldName, TValue newValue, double duration, TEasing easing)
101+ where TThis : class, ITransformable
102+ where TEasing : IEasingFunction
103+ => t.PopulateTransform(new TransformCustom<TValue, TEasing, TThis>(propertyOrFieldName), newValue, duration, easing);
104105 /// <summary>
106 /// Populates a newly created <see cref="Transform{TValue, T}"/> with necessary values.
···114 /// <param name="duration">The transform duration.</param>
115 /// <param name="easing">The transform easing to be used for tweening.</param>
116 /// <returns>The populated <paramref name="transform"/>.</returns>
117+ public static Transform<TValue, DefaultEasingFunction, TThis> PopulateTransform<TValue, TThis>(this TThis t, Transform<TValue, DefaultEasingFunction, TThis> transform, TValue newValue,
118+ double duration = 0, Easing easing = Easing.None)
119 where TThis : class, ITransformable
120+ => t.PopulateTransform(transform, newValue, duration, new DefaultEasingFunction(easing));
121+122+ /// <summary>
123+ /// Populates a newly created <see cref="Transform{TValue, T}"/> with necessary values.
124+ /// All <see cref="Transform{TValue, T}"/>s must be populated by this method prior to being used.
125+ /// </summary>
126+ /// <typeparam name="TThis">The type of the <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> can be applied to.</typeparam>
127+ /// <typeparam name="TValue">The value type which is being transformed.</typeparam>
128+ /// <typeparam name="TEasing">The type of easing.</typeparam>
129+ /// <param name="t">The <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> will be applied to.</param>
130+ /// <param name="transform">The transform to populate.</param>
131+ /// <param name="newValue">The value to transform to.</param>
132+ /// <param name="duration">The transform duration.</param>
133+ /// <param name="easing">The transform easing to be used for tweening.</param>
134+ /// <returns>The populated <paramref name="transform"/>.</returns>
135+ public static Transform<TValue, TEasing, TThis> PopulateTransform<TValue, TEasing, TThis>(this TThis t, Transform<TValue, TEasing, TThis> transform, TValue newValue, double duration,
136+ TEasing easing)
137+ where TThis : class, ITransformable
138+ where TEasing : IEasingFunction
139 {
140 if (duration < 0)
141 throw new ArgumentOutOfRangeException(nameof(duration), $"{nameof(duration)} must be positive.");
···472 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
473 public static TransformSequence<T> TransformBindableTo<T, TValue>(this T drawable, [NotNull] Bindable<TValue> bindable, TValue newValue, double duration = 0, Easing easing = Easing.None)
474 where T : class, ITransformable =>
475+ drawable.TransformTo(drawable.PopulateTransform(new TransformBindable<TValue, DefaultEasingFunction, T>(bindable), newValue, duration, new DefaultEasingFunction(easing)));
476 }
477}
+9-3
osu.Framework/Graphics/Transforms/Transform.cs
···26 /// </summary>
27 public bool Rewindable = true;
2829- public Easing Easing;
30-31 public abstract ITransformable TargetTransformable { get; }
3233 public double StartTime { get; internal set; }
···75 public TValue EndValue { get; protected internal set; }
76 }
7778- public abstract class Transform<TValue, T> : Transform<TValue>
079 where T : class, ITransformable
80 {
81 public override ITransformable TargetTransformable => Target;
8283 public T Target { get; internal set; }
008485 public sealed override void Apply(double time)
86 {
···95 protected abstract void ReadIntoStartValue(T d);
9697 public override string ToString() => $"{Target.GetType().Name}.{TargetMember} {StartTime}-{EndTime}ms {StartValue} -> {EndValue}";
0000098 }
99}
···26 /// </summary>
27 public bool Rewindable = true;
280029 public abstract ITransformable TargetTransformable { get; }
3031 public double StartTime { get; internal set; }
···73 public TValue EndValue { get; protected internal set; }
74 }
7576+ public abstract class Transform<TValue, TEasing, T> : Transform<TValue>
77+ where TEasing : IEasingFunction
78 where T : class, ITransformable
79 {
80 public override ITransformable TargetTransformable => Target;
8182 public T Target { get; internal set; }
83+84+ public TEasing Easing { get; internal set; }
8586 public sealed override void Apply(double time)
87 {
···96 protected abstract void ReadIntoStartValue(T d);
9798 public override string ToString() => $"{Target.GetType().Name}.{TargetMember} {StartTime}-{EndTime}ms {StartValue} -> {EndValue}";
99+ }
100+101+ public abstract class Transform<TValue, T> : Transform<TValue, DefaultEasingFunction, T>
102+ where T : class, ITransformable
103+ {
104 }
105}
···15 /// A transform which operates on arbitrary fields or properties of a given target.
16 /// </summary>
17 /// <typeparam name="TValue">The type of the field or property to operate upon.</typeparam>
018 /// <typeparam name="T">The type of the target to operate upon.</typeparam>
19- internal class TransformCustom<TValue, T> : Transform<TValue, T> where T : class, ITransformable
0020 {
21 private delegate TValue ReadFunc(T transformable);
22···141 private static Accessor getAccessor(string propertyOrFieldName) => accessors.GetOrAdd(propertyOrFieldName, key => findAccessor(typeof(T), key));
142143 private readonly Accessor accessor;
144- private readonly InterpolationFunc<TValue> interpolationFunc;
145146 /// <summary>
147 /// Creates a new instance operating on a property or field of <typeparamref name="T"/>. The property or field is
···175 protected override void Apply(T d, double time) => accessor.Write(d, valueAt(time));
176177 protected override void ReadIntoStartValue(T d) => StartValue = accessor.Read(d);
000000000178 }
179}
···15 /// A transform which operates on arbitrary fields or properties of a given target.
16 /// </summary>
17 /// <typeparam name="TValue">The type of the field or property to operate upon.</typeparam>
18+ /// <typeparam name="TEasing">The type of easing.</typeparam>
19 /// <typeparam name="T">The type of the target to operate upon.</typeparam>
20+ internal class TransformCustom<TValue, TEasing, T> : Transform<TValue, TEasing, T>
21+ where T : class, ITransformable
22+ where TEasing : IEasingFunction
23 {
24 private delegate TValue ReadFunc(T transformable);
25···144 private static Accessor getAccessor(string propertyOrFieldName) => accessors.GetOrAdd(propertyOrFieldName, key => findAccessor(typeof(T), key));
145146 private readonly Accessor accessor;
147+ private readonly InterpolationFunc<TValue, TEasing> interpolationFunc;
148149 /// <summary>
150 /// Creates a new instance operating on a property or field of <typeparamref name="T"/>. The property or field is
···178 protected override void Apply(T d, double time) => accessor.Write(d, valueAt(time));
179180 protected override void ReadIntoStartValue(T d) => StartValue = accessor.Read(d);
181+ }
182+183+ internal class TransformCustom<TValue, T> : TransformCustom<TValue, DefaultEasingFunction, T>
184+ where T : class, ITransformable
185+ {
186+ public TransformCustom(string propertyOrFieldName)
187+ : base(propertyOrFieldName)
188+ {
189+ }
190 }
191}
···412413 /// <summary>
414 /// Adds to this object a <see cref="Transform"/> which was previously populated using this object via
415- /// <see cref="TransformableExtensions.PopulateTransform{TValue, TThis}(TThis, Transform{TValue, TThis}, TValue, double, Easing)"/>.
416 /// Added <see cref="Transform"/>s are immediately applied, and therefore have an immediate effect on this object if the current time of this
417 /// object falls within <see cref="Transform.StartTime"/> and <see cref="Transform.EndTime"/>.
418 /// If <see cref="Clock"/> is null, e.g. because this object has just been constructed, then the given transform will be finished instantaneously.
···412413 /// <summary>
414 /// Adds to this object a <see cref="Transform"/> which was previously populated using this object via
415+ /// <see cref="TransformableExtensions.PopulateTransform{TValue, TEasing, TThis}"/>.
416 /// Added <see cref="Transform"/>s are immediately applied, and therefore have an immediate effect on this object if the current time of this
417 /// object falls within <see cref="Transform.StartTime"/> and <see cref="Transform.EndTime"/>.
418 /// If <see cref="Clock"/> is null, e.g. because this object has just been constructed, then the given transform will be finished instantaneously.
+3-3
osu.Framework/Utils/IInterpolable.cs
···1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
34-using osu.Framework.Graphics;
56namespace osu.Framework.Utils
7{
···22 /// <param name="endValue">The <typeparamref name="TValue"/> at <paramref name="time"/> = <paramref name="endTime"/>.</param>
23 /// <param name="startTime">The start time.</param>
24 /// <param name="endTime">The end time.</param>
25- /// <param name="easingType">The easing to use.</param>
26 /// <returns>The interpolated value.</returns>
27- TValue ValueAt(double time, TValue startValue, TValue endValue, double startTime, double endTime, Easing easingType = Easing.None);
28 }
29}
···1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
34+using osu.Framework.Graphics.Transforms;
56namespace osu.Framework.Utils
7{
···22 /// <param name="endValue">The <typeparamref name="TValue"/> at <paramref name="time"/> = <paramref name="endTime"/>.</param>
23 /// <param name="startTime">The start time.</param>
24 /// <param name="endTime">The end time.</param>
25+ /// <param name="easing">The easing function to use.</param>
26 /// <returns>The interpolated value.</returns>
27+ TValue ValueAt<TEasing>(double time, TValue startValue, TValue endValue, double startTime, double endTime, in TEasing easing) where TEasing : IEasingFunction;
28 }
29}
+8-7
osu.Framework/Utils/Interpolation.cs
···330331 public static TValue ValueAt<TValue, TEasing>(double time, TValue startValue, TValue endValue, double startTime, double endTime, in TEasing easing)
332 where TEasing : IEasingFunction
333- => GenericInterpolation<TValue>.FUNCTION(time, startValue, endValue, startTime, endTime, easing);
334335 #endregion
336···341 where TEasing : IEasingFunction
342 => easing.ApplyEasing(time);
343344- private static class GenericInterpolation<TValue>
0345 {
346- public static readonly InterpolationFunc<TValue> FUNCTION;
347348 static GenericInterpolation()
349 {
350 const string interpolation_method = nameof(Interpolation.ValueAt);
351352- var parameters = typeof(InterpolationFunc<TValue>)
353- .GetMethod(nameof(InterpolationFunc<TValue>.Invoke))
354 ?.GetParameters().Select(p => p.ParameterType).ToArray();
355356 MethodInfo valueAtMethod = typeof(Interpolation).GetMethod(interpolation_method, parameters);
357358 if (valueAtMethod != null)
359- FUNCTION = (InterpolationFunc<TValue>)valueAtMethod.CreateDelegate(typeof(InterpolationFunc<TValue>));
360 else
361 {
362 var typeRef = FormatterServices.GetSafeUninitializedObject(typeof(TValue)) as IInterpolable<TValue>;
···370 }
371 }
372373- public delegate TValue InterpolationFunc<TValue>(double time, TValue startValue, TValue endValue, double startTime, double endTime, Easing easingType);
374}
···330331 public static TValue ValueAt<TValue, TEasing>(double time, TValue startValue, TValue endValue, double startTime, double endTime, in TEasing easing)
332 where TEasing : IEasingFunction
333+ => GenericInterpolation<TValue, TEasing>.FUNCTION(time, startValue, endValue, startTime, endTime, easing);
334335 #endregion
336···341 where TEasing : IEasingFunction
342 => easing.ApplyEasing(time);
343344+ private static class GenericInterpolation<TValue, TEasing>
345+ where TEasing : IEasingFunction
346 {
347+ public static readonly InterpolationFunc<TValue, TEasing> FUNCTION;
348349 static GenericInterpolation()
350 {
351 const string interpolation_method = nameof(Interpolation.ValueAt);
352353+ var parameters = typeof(InterpolationFunc<TValue, TEasing>)
354+ .GetMethod(nameof(InterpolationFunc<TValue, TEasing>.Invoke))
355 ?.GetParameters().Select(p => p.ParameterType).ToArray();
356357 MethodInfo valueAtMethod = typeof(Interpolation).GetMethod(interpolation_method, parameters);
358359 if (valueAtMethod != null)
360+ FUNCTION = (InterpolationFunc<TValue, TEasing>)valueAtMethod.CreateDelegate(typeof(InterpolationFunc<TValue, TEasing>));
361 else
362 {
363 var typeRef = FormatterServices.GetSafeUninitializedObject(typeof(TValue)) as IInterpolable<TValue>;
···371 }
372 }
373374+ public delegate TValue InterpolationFunc<TValue, TEasing>(double time, TValue startValue, TValue endValue, double startTime, double endTime, in TEasing easingType) where TEasing : IEasingFunction;
375}