A game framework written with osu! in mind.
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.
3
4using osuTK;
5using osuTK.Graphics;
6using osu.Framework.Graphics.Colour;
7using osu.Framework.Graphics.Containers;
8using osu.Framework.Graphics.Transforms;
9using System;
10using System.Linq;
11using JetBrains.Annotations;
12using osu.Framework.Bindables;
13using osu.Framework.Graphics.Effects;
14using osu.Framework.Utils;
15
16namespace osu.Framework.Graphics
17{
18 public static class TransformableExtensions
19 {
20 /// <summary>
21 /// Transforms a given property or field member of a given <see cref="ITransformable"/> <typeparamref name="TThis"/> to <paramref name="newValue"/>.
22 /// The value of the given member is smoothly changed over time using the given <paramref name="easing"/> for tweening.
23 /// </summary>
24 /// <typeparam name="TThis">The type of the <see cref="ITransformable"/> to apply the <see cref="Transform{TValue, T}"/> to.</typeparam>
25 /// <typeparam name="TValue">The value type which is being transformed.</typeparam>
26 /// <param name="t">The <see cref="ITransformable"/> to apply the <see cref="Transform{TValue, T}"/> to.</param>
27 /// <param name="propertyOrFieldName">The property or field name of the member ot <typeparamref name="TThis"/> to transform.</param>
28 /// <param name="newValue">The value to transform to.</param>
29 /// <param name="duration">The transform duration.</param>
30 /// <param name="easing">The transform easing to be used for tweening.</param>
31 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
32 public static TransformSequence<TThis> TransformTo<TThis, TValue>(this TThis t, string propertyOrFieldName, TValue newValue, double duration = 0, Easing easing = Easing.None)
33 where TThis : class, ITransformable
34 => t.TransformTo(t.MakeTransform(propertyOrFieldName, newValue, duration, new DefaultEasingFunction(easing)));
35
36 /// <summary>
37 /// Transforms a given property or field member of a given <see cref="ITransformable"/> <typeparamref name="TThis"/> to <paramref name="newValue"/>.
38 /// The value of the given member is smoothly changed over time using the given <paramref name="easing"/> for tweening.
39 /// </summary>
40 /// <typeparam name="TThis">The type of the <see cref="ITransformable"/> to apply the <see cref="Transform{TValue, T}"/> to.</typeparam>
41 /// <typeparam name="TValue">The value type which is being transformed.</typeparam>
42 /// <typeparam name="TEasing">The type of easing.</typeparam>
43 /// <param name="t">The <see cref="ITransformable"/> to apply the <see cref="Transform{TValue, T}"/> to.</param>
44 /// <param name="propertyOrFieldName">The property or field name of the member ot <typeparamref name="TThis"/> to transform.</param>
45 /// <param name="newValue">The value to transform to.</param>
46 /// <param name="duration">The transform duration.</param>
47 /// <param name="easing">The transform easing to be used for tweening.</param>
48 /// <param name="grouping">An optional grouping specification to be used when the same property may be touched by multiple transform types.</param>
49 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
50 public static TransformSequence<TThis> TransformTo<TThis, TValue, TEasing>(this TThis t, string propertyOrFieldName, TValue newValue, double duration, in TEasing easing, string grouping = null)
51 where TThis : class, ITransformable
52 where TEasing : IEasingFunction
53 => t.TransformTo(t.MakeTransform(propertyOrFieldName, newValue, duration, easing, grouping));
54
55 /// <summary>
56 /// Applies a <see cref="Transform"/> to a given <see cref="ITransformable"/>.
57 /// </summary>
58 /// <typeparam name="TThis">The type of the <see cref="ITransformable"/> to apply the <see cref="Transform"/> to.</typeparam>
59 /// <param name="t">The <see cref="ITransformable"/> to apply the <see cref="Transform{TValue, T}"/> to.</param>
60 /// <param name="transform">The transform to use.</param>
61 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
62 public static TransformSequence<TThis> TransformTo<TThis>(this TThis t, Transform transform) where TThis : class, ITransformable
63 {
64 var result = new TransformSequence<TThis>(t);
65 result.Add(transform);
66 t.AddTransform(transform);
67 return result;
68 }
69
70 /// <summary>
71 /// Creates a <see cref="Transform{TValue, T}"/> for smoothly changing <paramref name="propertyOrFieldName"/>
72 /// over time using the given <paramref name="easing"/> for tweening.
73 /// <see cref="PopulateTransform{TValue, DefaultEasingFunction, TThis}"/> is invoked as part of this method.
74 /// </summary>
75 /// <typeparam name="TThis">The type of the <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> can be applied to.</typeparam>
76 /// <typeparam name="TValue">The value type which is being transformed.</typeparam>
77 /// <param name="t">The <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> will be applied to.</param>
78 /// <param name="propertyOrFieldName">The property or field name of the member ot <typeparamref name="TThis"/> to transform.</param>
79 /// <param name="newValue">The value to transform to.</param>
80 /// <param name="duration">The transform duration.</param>
81 /// <param name="easing">The transform easing to be used for tweening.</param>
82 /// <param name="grouping">An optional grouping specification to be used when the same property may be touched by multiple transform types.</param>
83 /// <returns>The resulting <see cref="Transform{TValue, T}"/>.</returns>
84 public static Transform<TValue, DefaultEasingFunction, TThis> MakeTransform<TThis, TValue>(this TThis t, string propertyOrFieldName, TValue newValue, double duration = 0,
85 Easing easing = Easing.None, string grouping = null)
86 where TThis : class, ITransformable
87 => t.MakeTransform(propertyOrFieldName, newValue, duration, new DefaultEasingFunction(easing), grouping);
88
89 /// <summary>
90 /// Creates a <see cref="Transform{TValue, T}"/> for smoothly changing <paramref name="propertyOrFieldName"/>
91 /// over time using the given <paramref name="easing"/> for tweening.
92 /// <see cref="PopulateTransform{TValue, TEasing, TThis}"/> is invoked as part of this method.
93 /// </summary>
94 /// <typeparam name="TThis">The type of the <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> can be applied to.</typeparam>
95 /// <typeparam name="TValue">The value type which is being transformed.</typeparam>
96 /// <typeparam name="TEasing">The type of easing.</typeparam>
97 /// <param name="t">The <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> will be applied to.</param>
98 /// <param name="propertyOrFieldName">The property or field name of the member ot <typeparamref name="TThis"/> to transform.</param>
99 /// <param name="newValue">The value to transform to.</param>
100 /// <param name="duration">The transform duration.</param>
101 /// <param name="easing">The transform easing to be used for tweening.</param>
102 /// <param name="grouping">An optional grouping specification to be used when the same property may be touched by multiple transform types.</param>
103 /// <returns>The resulting <see cref="Transform{TValue, T}"/>.</returns>
104 public static Transform<TValue, TEasing, TThis> MakeTransform<TThis, TEasing, TValue>(this TThis t, string propertyOrFieldName, TValue newValue, double duration, in TEasing easing, string grouping = null)
105 where TThis : class, ITransformable
106 where TEasing : IEasingFunction
107 => t.PopulateTransform(new TransformCustom<TValue, TEasing, TThis>(propertyOrFieldName, grouping), newValue, duration, easing);
108
109 /// <summary>
110 /// Populates a newly created <see cref="Transform{TValue, T}"/> with necessary values.
111 /// All <see cref="Transform{TValue, T}"/>s must be populated by this method prior to being used.
112 /// </summary>
113 /// <typeparam name="TThis">The type of the <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> can be applied to.</typeparam>
114 /// <typeparam name="TValue">The value type which is being transformed.</typeparam>
115 /// <param name="t">The <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> will be applied to.</param>
116 /// <param name="transform">The transform to populate.</param>
117 /// <param name="newValue">The value to transform to.</param>
118 /// <param name="duration">The transform duration.</param>
119 /// <param name="easing">The transform easing to be used for tweening.</param>
120 /// <returns>The populated <paramref name="transform"/>.</returns>
121 public static Transform<TValue, DefaultEasingFunction, TThis> PopulateTransform<TValue, TThis>(this TThis t, Transform<TValue, DefaultEasingFunction, TThis> transform, TValue newValue,
122 double duration = 0, Easing easing = Easing.None)
123 where TThis : class, ITransformable
124 => t.PopulateTransform(transform, newValue, duration, new DefaultEasingFunction(easing));
125
126 /// <summary>
127 /// Populates a newly created <see cref="Transform{TValue, T}"/> with necessary values.
128 /// All <see cref="Transform{TValue, T}"/>s must be populated by this method prior to being used.
129 /// </summary>
130 /// <typeparam name="TThis">The type of the <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> can be applied to.</typeparam>
131 /// <typeparam name="TValue">The value type which is being transformed.</typeparam>
132 /// <typeparam name="TEasing">The type of easing.</typeparam>
133 /// <param name="t">The <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> will be applied to.</param>
134 /// <param name="transform">The transform to populate.</param>
135 /// <param name="newValue">The value to transform to.</param>
136 /// <param name="duration">The transform duration.</param>
137 /// <param name="easing">The transform easing to be used for tweening.</param>
138 /// <returns>The populated <paramref name="transform"/>.</returns>
139 public static Transform<TValue, TEasing, TThis> PopulateTransform<TValue, TEasing, TThis>(this TThis t, Transform<TValue, TEasing, TThis> transform, TValue newValue, double duration,
140 in TEasing easing)
141 where TThis : class, ITransformable
142 where TEasing : IEasingFunction
143 {
144 if (duration < 0)
145 throw new ArgumentOutOfRangeException(nameof(duration), $"{nameof(duration)} must be positive.");
146
147 if (transform.Target != null)
148 throw new InvalidOperationException($"May not {nameof(PopulateTransform)} the same {nameof(Transform<TValue, TThis>)} more than once.");
149
150 transform.Target = t;
151
152 double startTime = t.TransformStartTime;
153
154 transform.StartTime = startTime;
155 transform.EndTime = startTime + duration;
156 transform.EndValue = newValue;
157 transform.Easing = easing;
158
159 return transform;
160 }
161
162 /// <summary>
163 /// Applies <paramref name="childGenerators"/> via TransformSequence.Append(IEnumerable{Generator})/>.
164 /// </summary>
165 /// <typeparam name="T">The type of the <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> can be applied to.</typeparam>
166 /// <param name="transformable">The <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> will be applied to.</param>
167 /// <param name="childGenerators">The optional Generators for <see cref="TransformSequence{T}"/>s to be appended.</param>
168 /// <returns>This <see cref="TransformSequence{T}"/>.</returns>
169 public static TransformSequence<T> Animate<T>(this T transformable, params TransformSequence<T>.Generator[] childGenerators) where T : class, ITransformable =>
170 transformable.Delay(0, childGenerators);
171
172 /// <summary>
173 /// Advances the start time of future appended <see cref="TransformSequence{T}"/>s by <paramref name="delay"/> milliseconds.
174 /// Then, <paramref name="childGenerators"/> are appended via TransformSequence.Append(IEnumerable{Generator})/>.
175 /// </summary>
176 /// <typeparam name="T">The type of the <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> can be applied to.</typeparam>
177 /// <param name="transformable">The <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> will be applied to.</param>
178 /// <param name="delay">The delay to advance the start time by.</param>
179 /// <param name="childGenerators">The optional Generators for <see cref="TransformSequence{T}"/>s to be appended.</param>
180 /// <returns>This <see cref="TransformSequence{T}"/>.</returns>
181 public static TransformSequence<T> Delay<T>(this T transformable, double delay, params TransformSequence<T>.Generator[] childGenerators) where T : class, ITransformable =>
182 new TransformSequence<T>(transformable).Delay(delay, childGenerators);
183
184 /// <summary>
185 /// Returns a <see cref="TransformSequence{T}"/> which waits for all existing transforms to finish.
186 /// </summary>
187 /// <returns>A <see cref="TransformSequence{T}"/> which has a delay waiting for all transforms to be completed.</returns>
188 public static TransformSequence<T> DelayUntilTransformsFinished<T>(this T transformable)
189 where T : Transformable =>
190 transformable.Delay(Math.Max(0, transformable.LatestTransformEndTime - transformable.Time.Current));
191
192 /// <summary>
193 /// Append a looping <see cref="TransformSequence{T}"/> to this <see cref="TransformSequence{T}"/>.
194 /// All <see cref="Transform"/>s generated by <paramref name="childGenerators"/> are appended to
195 /// this <see cref="TransformSequence{T}"/> and then repeated <paramref name="numIters"/> times
196 /// with <paramref name="pause"/> milliseconds between iterations.
197 /// </summary>
198 /// <typeparam name="T">The type of the <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> can be applied to.</typeparam>
199 /// <param name="transformable">The <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> will be applied to.</param>
200 /// <param name="pause">The pause between iterations in milliseconds.</param>
201 /// <param name="numIters">The number of iterations.</param>
202 /// <param name="childGenerators">The functions to generate the <see cref="TransformSequence{T}"/>s to be looped.</param>
203 /// <returns>This <see cref="TransformSequence{T}"/>.</returns>
204 public static TransformSequence<T> Loop<T>(this T transformable, double pause, int numIters, params TransformSequence<T>.Generator[] childGenerators)
205 where T : class, ITransformable =>
206 transformable.Delay(0).Loop(pause, numIters, childGenerators);
207
208 /// <summary>
209 /// Append a looping <see cref="TransformSequence{T}"/> to this <see cref="TransformSequence{T}"/>.
210 /// All <see cref="Transform"/>s generated by <paramref name="childGenerators"/> are appended to
211 /// this <see cref="TransformSequence{T}"/> and then repeated indefinitely with <paramref name="pause"/>
212 /// milliseconds between iterations.
213 /// </summary>
214 /// <typeparam name="T">The type of the <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> can be applied to.</typeparam>
215 /// <param name="transformable">The <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> will be applied to.</param>
216 /// <param name="pause">The pause between iterations in milliseconds.</param>
217 /// <param name="childGenerators">The functions to generate the <see cref="TransformSequence{T}"/>s to be looped.</param>
218 /// <returns>This <see cref="TransformSequence{T}"/>.</returns>
219 public static TransformSequence<T> Loop<T>(this T transformable, double pause, params TransformSequence<T>.Generator[] childGenerators)
220 where T : class, ITransformable =>
221 transformable.Delay(0).Loop(pause, childGenerators);
222
223 /// <summary>
224 /// Append a looping <see cref="TransformSequence{T}"/> to this <see cref="TransformSequence{T}"/>.
225 /// All <see cref="Transform"/>s generated by <paramref name="childGenerators"/> are appended to
226 /// this <see cref="TransformSequence{T}"/> and then repeated indefinitely.
227 /// milliseconds between iterations.
228 /// </summary>
229 /// <typeparam name="T">The type of the <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> can be applied to.</typeparam>
230 /// <param name="transformable">The <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> will be applied to.</param>
231 /// <param name="childGenerators">The functions to generate the <see cref="TransformSequence{T}"/>s to be looped.</param>
232 /// <returns>This <see cref="TransformSequence{T}"/>.</returns>
233 public static TransformSequence<T> Loop<T>(this T transformable, params TransformSequence<T>.Generator[] childGenerators)
234 where T : class, ITransformable =>
235 transformable.Delay(0).Loop(childGenerators);
236
237 /// <summary>
238 /// Append a looping <see cref="TransformSequence{T}"/> to this <see cref="TransformSequence{T}"/> to repeat indefinitely with <paramref name="pause"/>
239 /// milliseconds between iterations.
240 /// </summary>
241 /// <typeparam name="T">The type of the <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> can be applied to.</typeparam>
242 /// <param name="transformable">The <see cref="ITransformable"/> the <see cref="Transform{TValue, T}"/> will be applied to.</param>
243 /// <param name="pause">The pause between iterations in milliseconds.</param>
244 /// <returns>This <see cref="TransformSequence{T}"/>.</returns>
245 public static TransformSequence<T> Loop<T>(this T transformable, double pause = 0)
246 where T : class, ITransformable =>
247 transformable.Delay(0).Loop(pause);
248
249 /// <summary>
250 /// Rotate over one full rotation with provided parameters.
251 /// </summary>
252 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
253 public static TransformSequence<T> Spin<T>(this T drawable, double revolutionDuration, RotationDirection direction, float startRotation = 0)
254 where T : Drawable
255 => drawable.Delay(0).Spin(revolutionDuration, direction, startRotation);
256
257 /// <summary>
258 /// Rotate <paramref name="numRevolutions"/> times with provided parameters.
259 /// </summary>
260 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
261 public static TransformSequence<T> Spin<T>(this T drawable, double revolutionDuration, RotationDirection direction, float startRotation, int numRevolutions)
262 where T : Drawable
263 => drawable.Delay(0).Spin(revolutionDuration, direction, startRotation, numRevolutions);
264
265 #region Easing
266
267 /// <summary>
268 /// Smoothly adjusts <see cref="Drawable.Alpha"/> to 1 over time.
269 /// </summary>
270 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
271 public static TransformSequence<T> FadeIn<T>(this T drawable, double duration = 0, Easing easing = Easing.None)
272 where T : Drawable
273 => drawable.FadeIn(duration, new DefaultEasingFunction(easing));
274
275 /// <summary>
276 /// Smoothly adjusts <see cref="Drawable.Alpha"/> from 0 to 1 over time.
277 /// </summary>
278 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
279 public static TransformSequence<T> FadeInFromZero<T>(this T drawable, double duration = 0, Easing easing = Easing.None)
280 where T : Drawable
281 => drawable.FadeInFromZero(duration, new DefaultEasingFunction(easing));
282
283 /// <summary>
284 /// Smoothly adjusts <see cref="Drawable.Alpha"/> to 0 over time.
285 /// </summary>
286 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
287 public static TransformSequence<T> FadeOut<T>(this T drawable, double duration = 0, Easing easing = Easing.None)
288 where T : Drawable
289 => drawable.FadeOut(duration, new DefaultEasingFunction(easing));
290
291 /// <summary>
292 /// Smoothly adjusts <see cref="Drawable.Alpha"/> from 1 to 0 over time.
293 /// </summary>
294 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
295 public static TransformSequence<T> FadeOutFromOne<T>(this T drawable, double duration = 0, Easing easing = Easing.None)
296 where T : Drawable
297 => drawable.FadeOutFromOne(duration, new DefaultEasingFunction(easing));
298
299 /// <summary>
300 /// Smoothly adjusts <see cref="Drawable.Alpha"/> over time.
301 /// </summary>
302 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
303 public static TransformSequence<T> FadeTo<T>(this T drawable, float newAlpha, double duration = 0, Easing easing = Easing.None)
304 where T : Drawable
305 => drawable.FadeTo(newAlpha, duration, new DefaultEasingFunction(easing));
306
307 /// <summary>
308 /// Smoothly adjusts <see cref="Drawable.Colour"/> over time.
309 /// </summary>
310 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
311 public static TransformSequence<T> FadeColour<T>(this T drawable, ColourInfo newColour, double duration = 0, Easing easing = Easing.None)
312 where T : Drawable
313 => drawable.FadeColour(newColour, duration, new DefaultEasingFunction(easing));
314
315 /// <summary>
316 /// Instantaneously flashes <see cref="Drawable.Colour"/>, then smoothly changes it back over time.
317 /// </summary>
318 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
319 public static TransformSequence<T> FlashColour<T>(this T drawable, ColourInfo flashColour, double duration, Easing easing = Easing.None)
320 where T : Drawable
321 => drawable.FlashColour(flashColour, duration, new DefaultEasingFunction(easing));
322
323 /// <summary>
324 /// Smoothly adjusts <see cref="Drawable.Rotation"/> over time.
325 /// </summary>
326 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
327 public static TransformSequence<T> RotateTo<T>(this T drawable, float newRotation, double duration = 0, Easing easing = Easing.None)
328 where T : Drawable
329 => drawable.RotateTo(newRotation, duration, new DefaultEasingFunction(easing));
330
331 /// <summary>
332 /// Smoothly adjusts <see cref="Drawable.Scale"/> over time.
333 /// </summary>
334 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
335 public static TransformSequence<T> ScaleTo<T>(this T drawable, float newScale, double duration = 0, Easing easing = Easing.None)
336 where T : Drawable
337 => drawable.ScaleTo(newScale, duration, new DefaultEasingFunction(easing));
338
339 /// <summary>
340 /// Smoothly adjusts <see cref="Drawable.Scale"/> over time.
341 /// </summary>
342 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
343 public static TransformSequence<T> ScaleTo<T>(this T drawable, Vector2 newScale, double duration = 0, Easing easing = Easing.None)
344 where T : Drawable
345 => drawable.ScaleTo(newScale, duration, new DefaultEasingFunction(easing));
346
347 /// <summary>
348 /// Smoothly adjusts <see cref="Drawable.Size"/> over time.
349 /// </summary>
350 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
351 public static TransformSequence<T> ResizeTo<T>(this T drawable, float newSize, double duration = 0, Easing easing = Easing.None)
352 where T : Drawable
353 => drawable.ResizeTo(newSize, duration, new DefaultEasingFunction(easing));
354
355 /// <summary>
356 /// Smoothly adjusts <see cref="Drawable.Size"/> over time.
357 /// </summary>
358 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
359 public static TransformSequence<T> ResizeTo<T>(this T drawable, Vector2 newSize, double duration = 0, Easing easing = Easing.None)
360 where T : Drawable
361 => drawable.ResizeTo(newSize, duration, new DefaultEasingFunction(easing));
362
363 /// <summary>
364 /// Smoothly adjusts <see cref="Drawable.Width"/> over time.
365 /// </summary>
366 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
367 public static TransformSequence<T> ResizeWidthTo<T>(this T drawable, float newWidth, double duration = 0, Easing easing = Easing.None)
368 where T : Drawable
369 => drawable.ResizeWidthTo(newWidth, duration, new DefaultEasingFunction(easing));
370
371 /// <summary>
372 /// Smoothly adjusts <see cref="Drawable.Height"/> over time.
373 /// </summary>
374 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
375 public static TransformSequence<T> ResizeHeightTo<T>(this T drawable, float newHeight, double duration = 0, Easing easing = Easing.None)
376 where T : Drawable
377 => drawable.ResizeHeightTo(newHeight, duration, new DefaultEasingFunction(easing));
378
379 /// <summary>
380 /// Smoothly adjusts <see cref="Drawable.Position"/> over time.
381 /// </summary>
382 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
383 public static TransformSequence<T> MoveTo<T>(this T drawable, Vector2 newPosition, double duration = 0, Easing easing = Easing.None)
384 where T : Drawable
385 => drawable.MoveTo(newPosition, duration, new DefaultEasingFunction(easing));
386
387 /// <summary>
388 /// Smoothly adjusts <see cref="Drawable.X"/> or <see cref="Drawable.Y"/> over time.
389 /// </summary>
390 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
391 public static TransformSequence<T> MoveTo<T>(this T drawable, Direction direction, float destination, double duration = 0, Easing easing = Easing.None)
392 where T : Drawable
393 => drawable.MoveTo(direction, destination, duration, new DefaultEasingFunction(easing));
394
395 /// <summary>
396 /// Smoothly adjusts <see cref="Drawable.X"/> over time.
397 /// </summary>
398 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
399 public static TransformSequence<T> MoveToX<T>(this T drawable, float destination, double duration = 0, Easing easing = Easing.None)
400 where T : Drawable
401 => drawable.MoveToX(destination, duration, new DefaultEasingFunction(easing));
402
403 /// <summary>
404 /// Smoothly adjusts <see cref="Drawable.Y"/> over time.
405 /// </summary>
406 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
407 public static TransformSequence<T> MoveToY<T>(this T drawable, float destination, double duration = 0, Easing easing = Easing.None)
408 where T : Drawable
409 => drawable.MoveToY(destination, duration, new DefaultEasingFunction(easing));
410
411 /// <summary>
412 /// Smoothly adjusts <see cref="Drawable.Position"/> by an offset to its final value over time.
413 /// </summary>
414 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
415 public static TransformSequence<T> MoveToOffset<T>(this T drawable, Vector2 offset, double duration = 0, Easing easing = Easing.None)
416 where T : Drawable
417 => drawable.MoveToOffset(offset, duration, new DefaultEasingFunction(easing));
418
419 /// <summary>
420 /// Smoothly adjusts <see cref="IContainer.RelativeChildSize"/> over time.
421 /// </summary>
422 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
423 public static TransformSequence<T> TransformRelativeChildSizeTo<T>(this T container, Vector2 newSize, double duration = 0, Easing easing = Easing.None)
424 where T : class, IContainer
425 => container.TransformRelativeChildSizeTo(newSize, duration, new DefaultEasingFunction(easing));
426
427 /// <summary>
428 /// Smoothly adjusts <see cref="IContainer.RelativeChildOffset"/> over time.
429 /// </summary>
430 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
431 public static TransformSequence<T> TransformRelativeChildOffsetTo<T>(this T container, Vector2 newOffset, double duration = 0, Easing easing = Easing.None)
432 where T : class, IContainer
433 => container.TransformRelativeChildOffsetTo(newOffset, duration, new DefaultEasingFunction(easing));
434
435 /// <summary>
436 /// Smoothly adjusts <see cref="IBufferedContainer.BlurSigma"/> over time.
437 /// </summary>
438 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
439 public static TransformSequence<T> BlurTo<T>(this T bufferedContainer, Vector2 newBlurSigma, double duration = 0, Easing easing = Easing.None)
440 where T : class, IBufferedContainer
441 => bufferedContainer.BlurTo(newBlurSigma, duration, new DefaultEasingFunction(easing));
442
443 /// <summary>
444 /// Smoothly adjusts <see cref="IFillFlowContainer.Spacing"/> over time.
445 /// </summary>
446 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
447 public static TransformSequence<T> TransformSpacingTo<T>(this T flowContainer, Vector2 newSpacing, double duration = 0, Easing easing = Easing.None)
448 where T : class, IFillFlowContainer
449 => flowContainer.TransformSpacingTo(newSpacing, duration, new DefaultEasingFunction(easing));
450
451 /// <summary>
452 /// Smoothly adjusts the alpha channel of the colour of <see cref="IContainer.EdgeEffect"/> over time.
453 /// </summary>
454 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
455 public static TransformSequence<T> FadeEdgeEffectTo<T>(this T container, float newAlpha, double duration = 0, Easing easing = Easing.None)
456 where T : class, IContainer
457 => container.FadeEdgeEffectTo(newAlpha, duration, new DefaultEasingFunction(easing));
458
459 /// <summary>
460 /// Smoothly adjusts the colour of <see cref="IContainer.EdgeEffect"/> over time.
461 /// </summary>
462 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
463 public static TransformSequence<T> FadeEdgeEffectTo<T>(this T container, Color4 newColour, double duration = 0, Easing easing = Easing.None)
464 where T : class, IContainer
465 => container.FadeEdgeEffectTo(newColour, duration, new DefaultEasingFunction(easing));
466
467 /// <summary>
468 /// Smoothly adjusts all parameters of <see cref="IContainer.EdgeEffect"/> over time.
469 /// </summary>
470 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
471 public static TransformSequence<T> TweenEdgeEffectTo<T>(this T container, EdgeEffectParameters newParameters, double duration = 0, Easing easing = Easing.None)
472 where T : class, IContainer
473 => container.TweenEdgeEffectTo(newParameters, duration, new DefaultEasingFunction(easing));
474
475 /// <summary>
476 /// Smoothly adjusts the value of a <see cref="Bindable{TValue}"/> over time.
477 /// </summary>
478 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
479 public static TransformSequence<T> TransformBindableTo<T, TValue>(this T drawable, [NotNull] Bindable<TValue> bindable, TValue newValue, double duration = 0, Easing easing = Easing.None)
480 where T : class, ITransformable
481 => drawable.TransformBindableTo(bindable, newValue, duration, new DefaultEasingFunction(easing));
482
483 #endregion
484
485 #region Generic Easing
486
487 /// <summary>
488 /// Smoothly adjusts <see cref="Drawable.Alpha"/> to 1 over time.
489 /// </summary>
490 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
491 public static TransformSequence<T> FadeIn<T, TEasing>(this T drawable, double duration, in TEasing easing)
492 where T : Drawable
493 where TEasing : IEasingFunction
494 => drawable.FadeTo(1, duration, easing);
495
496 /// <summary>
497 /// Smoothly adjusts <see cref="Drawable.Alpha"/> from 0 to 1 over time.
498 /// </summary>
499 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
500 public static TransformSequence<T> FadeInFromZero<T, TEasing>(this T drawable, double duration, in TEasing easing)
501 where T : Drawable
502 where TEasing : IEasingFunction
503 => drawable.FadeTo(0).FadeIn(duration, easing);
504
505 /// <summary>
506 /// Smoothly adjusts <see cref="Drawable.Alpha"/> to 0 over time.
507 /// </summary>
508 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
509 public static TransformSequence<T> FadeOut<T, TEasing>(this T drawable, double duration, in TEasing easing)
510 where T : Drawable
511 where TEasing : IEasingFunction
512 => drawable.FadeTo(0, duration, easing);
513
514 /// <summary>
515 /// Smoothly adjusts <see cref="Drawable.Alpha"/> from 1 to 0 over time.
516 /// </summary>
517 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
518 public static TransformSequence<T> FadeOutFromOne<T, TEasing>(this T drawable, double duration, in TEasing easing)
519 where T : Drawable
520 where TEasing : IEasingFunction
521 => drawable.FadeTo(1).FadeOut(duration, easing);
522
523 /// <summary>
524 /// Smoothly adjusts <see cref="Drawable.Alpha"/> over time.
525 /// </summary>
526 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
527 public static TransformSequence<T> FadeTo<T, TEasing>(this T drawable, float newAlpha, double duration, in TEasing easing)
528 where T : Drawable
529 where TEasing : IEasingFunction
530 => drawable.TransformTo(nameof(drawable.Alpha), newAlpha, duration, easing);
531
532 /// <summary>
533 /// Smoothly adjusts <see cref="Drawable.Colour"/> over time.
534 /// </summary>
535 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
536 public static TransformSequence<T> FadeColour<T, TEasing>(this T drawable, ColourInfo newColour, double duration, in TEasing easing)
537 where T : Drawable
538 where TEasing : IEasingFunction
539 => drawable.TransformTo(nameof(drawable.Colour), newColour, duration, easing);
540
541 /// <summary>
542 /// Instantaneously flashes <see cref="Drawable.Colour"/>, then smoothly changes it back over time.
543 /// </summary>
544 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
545 public static TransformSequence<T> FlashColour<T, TEasing>(this T drawable, ColourInfo flashColour, double duration, in TEasing easing)
546 where T : Drawable
547 where TEasing : IEasingFunction
548 {
549 ColourInfo endValue = (drawable.Transforms.LastOrDefault(t => t.TargetMember == nameof(drawable.Colour)) as Transform<ColourInfo>)?.EndValue ?? drawable.Colour;
550 return drawable.FadeColour(flashColour).FadeColour(endValue, duration, easing);
551 }
552
553 /// <summary>
554 /// Smoothly adjusts <see cref="Drawable.Rotation"/> over time.
555 /// </summary>
556 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
557 public static TransformSequence<T> RotateTo<T, TEasing>(this T drawable, float newRotation, double duration, in TEasing easing)
558 where T : Drawable
559 where TEasing : IEasingFunction
560 => drawable.TransformTo(nameof(drawable.Rotation), newRotation, duration, easing);
561
562 /// <summary>
563 /// Smoothly adjusts <see cref="Drawable.Scale"/> over time.
564 /// </summary>
565 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
566 public static TransformSequence<T> ScaleTo<T, TEasing>(this T drawable, float newScale, double duration, in TEasing easing)
567 where T : Drawable
568 where TEasing : IEasingFunction
569 => drawable.ScaleTo(new Vector2(newScale), duration, easing);
570
571 /// <summary>
572 /// Smoothly adjusts <see cref="Drawable.Scale"/> over time.
573 /// </summary>
574 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
575 public static TransformSequence<T> ScaleTo<T, TEasing>(this T drawable, Vector2 newScale, double duration, in TEasing easing)
576 where T : Drawable
577 where TEasing : IEasingFunction
578 => drawable.TransformTo(nameof(drawable.Scale), newScale, duration, easing);
579
580 /// <summary>
581 /// Smoothly adjusts <see cref="Drawable.Size"/> over time.
582 /// </summary>
583 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
584 public static TransformSequence<T> ResizeTo<T, TEasing>(this T drawable, float newSize, double duration, in TEasing easing)
585 where T : Drawable
586 where TEasing : IEasingFunction
587 => drawable.ResizeTo(new Vector2(newSize), duration, easing);
588
589 /// <summary>
590 /// Smoothly adjusts <see cref="Drawable.Size"/> over time.
591 /// </summary>
592 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
593 public static TransformSequence<T> ResizeTo<T, TEasing>(this T drawable, Vector2 newSize, double duration, in TEasing easing)
594 where T : Drawable
595 where TEasing : IEasingFunction
596 => drawable.TransformTo(nameof(drawable.Size), newSize, duration, easing);
597
598 /// <summary>
599 /// Smoothly adjusts <see cref="Drawable.Width"/> over time.
600 /// </summary>
601 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
602 public static TransformSequence<T> ResizeWidthTo<T, TEasing>(this T drawable, float newWidth, double duration, in TEasing easing)
603 where T : Drawable
604 where TEasing : IEasingFunction
605 => drawable.TransformTo(nameof(drawable.Width), newWidth, duration, easing, nameof(drawable.Size));
606
607 /// <summary>
608 /// Smoothly adjusts <see cref="Drawable.Height"/> over time.
609 /// </summary>
610 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
611 public static TransformSequence<T> ResizeHeightTo<T, TEasing>(this T drawable, float newHeight, double duration, in TEasing easing)
612 where T : Drawable
613 where TEasing : IEasingFunction
614 => drawable.TransformTo(nameof(drawable.Height), newHeight, duration, easing, nameof(drawable.Size));
615
616 /// <summary>
617 /// Smoothly adjusts <see cref="Drawable.Position"/> over time.
618 /// </summary>
619 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
620 public static TransformSequence<T> MoveTo<T, TEasing>(this T drawable, Vector2 newPosition, double duration, in TEasing easing)
621 where T : Drawable
622 where TEasing : IEasingFunction
623 => drawable.TransformTo(nameof(drawable.Position), newPosition, duration, easing);
624
625 /// <summary>
626 /// Smoothly adjusts <see cref="Drawable.X"/> or <see cref="Drawable.Y"/> over time.
627 /// </summary>
628 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
629 public static TransformSequence<T> MoveTo<T, TEasing>(this T drawable, Direction direction, float destination, double duration, in TEasing easing)
630 where T : Drawable
631 where TEasing : IEasingFunction
632 {
633 switch (direction)
634 {
635 case Direction.Horizontal:
636 return drawable.MoveToX(destination, duration, easing);
637
638 case Direction.Vertical:
639 return drawable.MoveToY(destination, duration, easing);
640 }
641
642 throw new InvalidOperationException($"Invalid direction ({direction}) passed to {nameof(MoveTo)}.");
643 }
644
645 /// <summary>
646 /// Smoothly adjusts <see cref="Drawable.X"/> over time.
647 /// </summary>
648 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
649 public static TransformSequence<T> MoveToX<T, TEasing>(this T drawable, float destination, double duration, in TEasing easing)
650 where T : Drawable
651 where TEasing : IEasingFunction
652 => drawable.TransformTo(nameof(drawable.X), destination, duration, easing, nameof(drawable.Position));
653
654 /// <summary>
655 /// Smoothly adjusts <see cref="Drawable.Y"/> over time.
656 /// </summary>
657 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
658 public static TransformSequence<T> MoveToY<T, TEasing>(this T drawable, float destination, double duration, in TEasing easing)
659 where T : Drawable
660 where TEasing : IEasingFunction
661 => drawable.TransformTo(nameof(drawable.Y), destination, duration, easing, nameof(drawable.Position));
662
663 /// <summary>
664 /// Smoothly adjusts <see cref="Drawable.Position"/> by an offset to its final value over time.
665 /// </summary>
666 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
667 public static TransformSequence<T> MoveToOffset<T, TEasing>(this T drawable, Vector2 offset, double duration, in TEasing easing)
668 where T : Drawable
669 where TEasing : IEasingFunction
670 => drawable.TransformTo(drawable.PopulateTransform(new PositionOffsetTransform<TEasing>(offset), default, duration, easing));
671
672 /// <summary>
673 /// Smoothly adjusts <see cref="IContainer.RelativeChildSize"/> over time.
674 /// </summary>
675 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
676 public static TransformSequence<T> TransformRelativeChildSizeTo<T, TEasing>(this T container, Vector2 newSize, double duration, in TEasing easing)
677 where T : class, IContainer
678 where TEasing : IEasingFunction
679 => container.TransformTo(nameof(container.RelativeChildSize), newSize, duration, easing);
680
681 /// <summary>
682 /// Smoothly adjusts <see cref="IContainer.RelativeChildOffset"/> over time.
683 /// </summary>
684 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
685 public static TransformSequence<T> TransformRelativeChildOffsetTo<T, TEasing>(this T container, Vector2 newOffset, double duration, in TEasing easing)
686 where T : class, IContainer
687 where TEasing : IEasingFunction
688 => container.TransformTo(nameof(container.RelativeChildOffset), newOffset, duration, easing);
689
690 /// <summary>
691 /// Smoothly adjusts <see cref="IBufferedContainer.BlurSigma"/> over time.
692 /// </summary>
693 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
694 public static TransformSequence<T> BlurTo<T, TEasing>(this T bufferedContainer, Vector2 newBlurSigma, double duration, in TEasing easing)
695 where T : class, IBufferedContainer
696 where TEasing : IEasingFunction
697 => bufferedContainer.TransformTo(nameof(bufferedContainer.BlurSigma), newBlurSigma, duration, easing);
698
699 /// <summary>
700 /// Smoothly adjusts <see cref="IFillFlowContainer.Spacing"/> over time.
701 /// </summary>
702 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
703 public static TransformSequence<T> TransformSpacingTo<T, TEasing>(this T flowContainer, Vector2 newSpacing, double duration, in TEasing easing)
704 where T : class, IFillFlowContainer
705 where TEasing : IEasingFunction
706 => flowContainer.TransformTo(nameof(flowContainer.Spacing), newSpacing, duration, easing);
707
708 /// <summary>
709 /// Smoothly adjusts the alpha channel of the colour of <see cref="IContainer.EdgeEffect"/> over time.
710 /// </summary>
711 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
712 public static TransformSequence<T> FadeEdgeEffectTo<T, TEasing>(this T container, float newAlpha, double duration, in TEasing easing)
713 where T : class, IContainer
714 where TEasing : IEasingFunction
715 {
716 Color4 targetColour = container.EdgeEffect.Colour;
717 targetColour.A = newAlpha;
718 return container.FadeEdgeEffectTo(targetColour, duration, easing);
719 }
720
721 /// <summary>
722 /// Smoothly adjusts the colour of <see cref="IContainer.EdgeEffect"/> over time.
723 /// </summary>
724 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
725 public static TransformSequence<T> FadeEdgeEffectTo<T, TEasing>(this T container, Color4 newColour, double duration, in TEasing easing)
726 where T : class, IContainer
727 where TEasing : IEasingFunction
728 {
729 var effect = container.EdgeEffect;
730 effect.Colour = newColour;
731 return container.TweenEdgeEffectTo(effect, duration, easing);
732 }
733
734 /// <summary>
735 /// Smoothly adjusts all parameters of <see cref="IContainer.EdgeEffect"/> over time.
736 /// </summary>
737 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
738 public static TransformSequence<T> TweenEdgeEffectTo<T, TEasing>(this T container, EdgeEffectParameters newParameters, double duration, in TEasing easing)
739 where T : class, IContainer
740 where TEasing : IEasingFunction
741 => container.TransformTo(nameof(container.EdgeEffect), newParameters, duration, easing);
742
743 /// <summary>
744 /// Smoothly adjusts the value of a <see cref="Bindable{TValue}"/> over time.
745 /// </summary>
746 /// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
747 public static TransformSequence<T> TransformBindableTo<T, TValue, TEasing>(this T drawable, [NotNull] Bindable<TValue> bindable, TValue newValue, double duration, in TEasing easing)
748 where T : class, ITransformable
749 where TEasing : IEasingFunction
750 => drawable.TransformTo(drawable.PopulateTransform(new TransformBindable<TValue, TEasing, T>(bindable), newValue, duration, easing));
751
752 #endregion
753
754 private class PositionOffsetTransform<TEasing> : Transform<Vector2, TEasing, Drawable>
755 where TEasing : IEasingFunction
756 {
757 private readonly Vector2 offset;
758
759 public override string TargetMember => nameof(Drawable.Position);
760
761 public PositionOffsetTransform(Vector2 offset)
762 {
763 this.offset = offset;
764 }
765
766 private Vector2 positionAt(double time)
767 {
768 if (time < StartTime) return StartValue;
769 if (time >= EndTime) return EndValue;
770
771 return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
772 }
773
774 protected override void Apply(Drawable d, double time) => d.Position = positionAt(time);
775
776 protected override void ReadIntoStartValue(Drawable d)
777 {
778 StartValue = d.Position;
779 EndValue = d.Position + offset;
780 }
781 }
782 }
783}