// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osuTK; using osuTK.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Transforms; using System; using System.Linq; using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Graphics.Effects; using osu.Framework.Utils; namespace osu.Framework.Graphics { public static class TransformableExtensions { /// /// Transforms a given property or field member of a given to . /// The value of the given member is smoothly changed over time using the given for tweening. /// /// The type of the to apply the to. /// The value type which is being transformed. /// The to apply the to. /// The property or field name of the member ot to transform. /// The value to transform to. /// The transform duration. /// The transform easing to be used for tweening. /// A to which further transforms can be added. public static TransformSequence TransformTo(this TThis t, string propertyOrFieldName, TValue newValue, double duration = 0, Easing easing = Easing.None) where TThis : class, ITransformable => t.TransformTo(t.MakeTransform(propertyOrFieldName, newValue, duration, new DefaultEasingFunction(easing))); /// /// Transforms a given property or field member of a given to . /// The value of the given member is smoothly changed over time using the given for tweening. /// /// The type of the to apply the to. /// The value type which is being transformed. /// The type of easing. /// The to apply the to. /// The property or field name of the member ot to transform. /// The value to transform to. /// The transform duration. /// The transform easing to be used for tweening. /// An optional grouping specification to be used when the same property may be touched by multiple transform types. /// A to which further transforms can be added. public static TransformSequence TransformTo(this TThis t, string propertyOrFieldName, TValue newValue, double duration, in TEasing easing, string grouping = null) where TThis : class, ITransformable where TEasing : IEasingFunction => t.TransformTo(t.MakeTransform(propertyOrFieldName, newValue, duration, easing, grouping)); /// /// Applies a to a given . /// /// The type of the to apply the to. /// The to apply the to. /// The transform to use. /// A to which further transforms can be added. public static TransformSequence TransformTo(this TThis t, Transform transform) where TThis : class, ITransformable { var result = new TransformSequence(t); result.Add(transform); t.AddTransform(transform); return result; } /// /// Creates a for smoothly changing /// over time using the given for tweening. /// is invoked as part of this method. /// /// The type of the the can be applied to. /// The value type which is being transformed. /// The the will be applied to. /// The property or field name of the member ot to transform. /// The value to transform to. /// The transform duration. /// The transform easing to be used for tweening. /// An optional grouping specification to be used when the same property may be touched by multiple transform types. /// The resulting . public static Transform MakeTransform(this TThis t, string propertyOrFieldName, TValue newValue, double duration = 0, Easing easing = Easing.None, string grouping = null) where TThis : class, ITransformable => t.MakeTransform(propertyOrFieldName, newValue, duration, new DefaultEasingFunction(easing), grouping); /// /// Creates a for smoothly changing /// over time using the given for tweening. /// is invoked as part of this method. /// /// The type of the the can be applied to. /// The value type which is being transformed. /// The type of easing. /// The the will be applied to. /// The property or field name of the member ot to transform. /// The value to transform to. /// The transform duration. /// The transform easing to be used for tweening. /// An optional grouping specification to be used when the same property may be touched by multiple transform types. /// The resulting . public static Transform MakeTransform(this TThis t, string propertyOrFieldName, TValue newValue, double duration, in TEasing easing, string grouping = null) where TThis : class, ITransformable where TEasing : IEasingFunction => t.PopulateTransform(new TransformCustom(propertyOrFieldName, grouping), newValue, duration, easing); /// /// Populates a newly created with necessary values. /// All s must be populated by this method prior to being used. /// /// The type of the the can be applied to. /// The value type which is being transformed. /// The the will be applied to. /// The transform to populate. /// The value to transform to. /// The transform duration. /// The transform easing to be used for tweening. /// The populated . public static Transform PopulateTransform(this TThis t, Transform transform, TValue newValue, double duration = 0, Easing easing = Easing.None) where TThis : class, ITransformable => t.PopulateTransform(transform, newValue, duration, new DefaultEasingFunction(easing)); /// /// Populates a newly created with necessary values. /// All s must be populated by this method prior to being used. /// /// The type of the the can be applied to. /// The value type which is being transformed. /// The type of easing. /// The the will be applied to. /// The transform to populate. /// The value to transform to. /// The transform duration. /// The transform easing to be used for tweening. /// The populated . public static Transform PopulateTransform(this TThis t, Transform transform, TValue newValue, double duration, in TEasing easing) where TThis : class, ITransformable where TEasing : IEasingFunction { if (duration < 0) throw new ArgumentOutOfRangeException(nameof(duration), $"{nameof(duration)} must be positive."); if (transform.Target != null) throw new InvalidOperationException($"May not {nameof(PopulateTransform)} the same {nameof(Transform)} more than once."); transform.Target = t; double startTime = t.TransformStartTime; transform.StartTime = startTime; transform.EndTime = startTime + duration; transform.EndValue = newValue; transform.Easing = easing; return transform; } /// /// Applies via TransformSequence.Append(IEnumerable{Generator})/>. /// /// The type of the the can be applied to. /// The the will be applied to. /// The optional Generators for s to be appended. /// This . public static TransformSequence Animate(this T transformable, params TransformSequence.Generator[] childGenerators) where T : class, ITransformable => transformable.Delay(0, childGenerators); /// /// Advances the start time of future appended s by milliseconds. /// Then, are appended via TransformSequence.Append(IEnumerable{Generator})/>. /// /// The type of the the can be applied to. /// The the will be applied to. /// The delay to advance the start time by. /// The optional Generators for s to be appended. /// This . public static TransformSequence Delay(this T transformable, double delay, params TransformSequence.Generator[] childGenerators) where T : class, ITransformable => new TransformSequence(transformable).Delay(delay, childGenerators); /// /// Returns a which waits for all existing transforms to finish. /// /// A which has a delay waiting for all transforms to be completed. public static TransformSequence DelayUntilTransformsFinished(this T transformable) where T : Transformable => transformable.Delay(Math.Max(0, transformable.LatestTransformEndTime - transformable.Time.Current)); /// /// Append a looping to this . /// All s generated by are appended to /// this and then repeated times /// with milliseconds between iterations. /// /// The type of the the can be applied to. /// The the will be applied to. /// The pause between iterations in milliseconds. /// The number of iterations. /// The functions to generate the s to be looped. /// This . public static TransformSequence Loop(this T transformable, double pause, int numIters, params TransformSequence.Generator[] childGenerators) where T : class, ITransformable => transformable.Delay(0).Loop(pause, numIters, childGenerators); /// /// Append a looping to this . /// All s generated by are appended to /// this and then repeated indefinitely with /// milliseconds between iterations. /// /// The type of the the can be applied to. /// The the will be applied to. /// The pause between iterations in milliseconds. /// The functions to generate the s to be looped. /// This . public static TransformSequence Loop(this T transformable, double pause, params TransformSequence.Generator[] childGenerators) where T : class, ITransformable => transformable.Delay(0).Loop(pause, childGenerators); /// /// Append a looping to this . /// All s generated by are appended to /// this and then repeated indefinitely. /// milliseconds between iterations. /// /// The type of the the can be applied to. /// The the will be applied to. /// The functions to generate the s to be looped. /// This . public static TransformSequence Loop(this T transformable, params TransformSequence.Generator[] childGenerators) where T : class, ITransformable => transformable.Delay(0).Loop(childGenerators); /// /// Append a looping to this to repeat indefinitely with /// milliseconds between iterations. /// /// The type of the the can be applied to. /// The the will be applied to. /// The pause between iterations in milliseconds. /// This . public static TransformSequence Loop(this T transformable, double pause = 0) where T : class, ITransformable => transformable.Delay(0).Loop(pause); /// /// Rotate over one full rotation with provided parameters. /// /// A to which further transforms can be added. public static TransformSequence Spin(this T drawable, double revolutionDuration, RotationDirection direction, float startRotation = 0) where T : Drawable => drawable.Delay(0).Spin(revolutionDuration, direction, startRotation); /// /// Rotate times with provided parameters. /// /// A to which further transforms can be added. public static TransformSequence Spin(this T drawable, double revolutionDuration, RotationDirection direction, float startRotation, int numRevolutions) where T : Drawable => drawable.Delay(0).Spin(revolutionDuration, direction, startRotation, numRevolutions); #region Easing /// /// Smoothly adjusts to 1 over time. /// /// A to which further transforms can be added. public static TransformSequence FadeIn(this T drawable, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.FadeIn(duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts from 0 to 1 over time. /// /// A to which further transforms can be added. public static TransformSequence FadeInFromZero(this T drawable, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.FadeInFromZero(duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts to 0 over time. /// /// A to which further transforms can be added. public static TransformSequence FadeOut(this T drawable, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.FadeOut(duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts from 1 to 0 over time. /// /// A to which further transforms can be added. public static TransformSequence FadeOutFromOne(this T drawable, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.FadeOutFromOne(duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence FadeTo(this T drawable, float newAlpha, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.FadeTo(newAlpha, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence FadeColour(this T drawable, ColourInfo newColour, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.FadeColour(newColour, duration, new DefaultEasingFunction(easing)); /// /// Instantaneously flashes , then smoothly changes it back over time. /// /// A to which further transforms can be added. public static TransformSequence FlashColour(this T drawable, ColourInfo flashColour, double duration, Easing easing = Easing.None) where T : Drawable => drawable.FlashColour(flashColour, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence RotateTo(this T drawable, float newRotation, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.RotateTo(newRotation, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence ScaleTo(this T drawable, float newScale, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.ScaleTo(newScale, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence ScaleTo(this T drawable, Vector2 newScale, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.ScaleTo(newScale, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence ResizeTo(this T drawable, float newSize, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.ResizeTo(newSize, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence ResizeTo(this T drawable, Vector2 newSize, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.ResizeTo(newSize, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence ResizeWidthTo(this T drawable, float newWidth, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.ResizeWidthTo(newWidth, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence ResizeHeightTo(this T drawable, float newHeight, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.ResizeHeightTo(newHeight, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence MoveTo(this T drawable, Vector2 newPosition, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.MoveTo(newPosition, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts or over time. /// /// A to which further transforms can be added. public static TransformSequence MoveTo(this T drawable, Direction direction, float destination, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.MoveTo(direction, destination, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence MoveToX(this T drawable, float destination, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.MoveToX(destination, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence MoveToY(this T drawable, float destination, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.MoveToY(destination, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts by an offset to its final value over time. /// /// A to which further transforms can be added. public static TransformSequence MoveToOffset(this T drawable, Vector2 offset, double duration = 0, Easing easing = Easing.None) where T : Drawable => drawable.MoveToOffset(offset, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence TransformRelativeChildSizeTo(this T container, Vector2 newSize, double duration = 0, Easing easing = Easing.None) where T : class, IContainer => container.TransformRelativeChildSizeTo(newSize, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence TransformRelativeChildOffsetTo(this T container, Vector2 newOffset, double duration = 0, Easing easing = Easing.None) where T : class, IContainer => container.TransformRelativeChildOffsetTo(newOffset, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence BlurTo(this T bufferedContainer, Vector2 newBlurSigma, double duration = 0, Easing easing = Easing.None) where T : class, IBufferedContainer => bufferedContainer.BlurTo(newBlurSigma, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence TransformSpacingTo(this T flowContainer, Vector2 newSpacing, double duration = 0, Easing easing = Easing.None) where T : class, IFillFlowContainer => flowContainer.TransformSpacingTo(newSpacing, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts the alpha channel of the colour of over time. /// /// A to which further transforms can be added. public static TransformSequence FadeEdgeEffectTo(this T container, float newAlpha, double duration = 0, Easing easing = Easing.None) where T : class, IContainer => container.FadeEdgeEffectTo(newAlpha, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts the colour of over time. /// /// A to which further transforms can be added. public static TransformSequence FadeEdgeEffectTo(this T container, Color4 newColour, double duration = 0, Easing easing = Easing.None) where T : class, IContainer => container.FadeEdgeEffectTo(newColour, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts all parameters of over time. /// /// A to which further transforms can be added. public static TransformSequence TweenEdgeEffectTo(this T container, EdgeEffectParameters newParameters, double duration = 0, Easing easing = Easing.None) where T : class, IContainer => container.TweenEdgeEffectTo(newParameters, duration, new DefaultEasingFunction(easing)); /// /// Smoothly adjusts the value of a over time. /// /// A to which further transforms can be added. public static TransformSequence TransformBindableTo(this T drawable, [NotNull] Bindable bindable, TValue newValue, double duration = 0, Easing easing = Easing.None) where T : class, ITransformable => drawable.TransformBindableTo(bindable, newValue, duration, new DefaultEasingFunction(easing)); #endregion #region Generic Easing /// /// Smoothly adjusts to 1 over time. /// /// A to which further transforms can be added. public static TransformSequence FadeIn(this T drawable, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.FadeTo(1, duration, easing); /// /// Smoothly adjusts from 0 to 1 over time. /// /// A to which further transforms can be added. public static TransformSequence FadeInFromZero(this T drawable, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.FadeTo(0).FadeIn(duration, easing); /// /// Smoothly adjusts to 0 over time. /// /// A to which further transforms can be added. public static TransformSequence FadeOut(this T drawable, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.FadeTo(0, duration, easing); /// /// Smoothly adjusts from 1 to 0 over time. /// /// A to which further transforms can be added. public static TransformSequence FadeOutFromOne(this T drawable, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.FadeTo(1).FadeOut(duration, easing); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence FadeTo(this T drawable, float newAlpha, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.TransformTo(nameof(drawable.Alpha), newAlpha, duration, easing); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence FadeColour(this T drawable, ColourInfo newColour, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.TransformTo(nameof(drawable.Colour), newColour, duration, easing); /// /// Instantaneously flashes , then smoothly changes it back over time. /// /// A to which further transforms can be added. public static TransformSequence FlashColour(this T drawable, ColourInfo flashColour, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction { ColourInfo endValue = (drawable.Transforms.LastOrDefault(t => t.TargetMember == nameof(drawable.Colour)) as Transform)?.EndValue ?? drawable.Colour; return drawable.FadeColour(flashColour).FadeColour(endValue, duration, easing); } /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence RotateTo(this T drawable, float newRotation, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.TransformTo(nameof(drawable.Rotation), newRotation, duration, easing); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence ScaleTo(this T drawable, float newScale, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.ScaleTo(new Vector2(newScale), duration, easing); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence ScaleTo(this T drawable, Vector2 newScale, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.TransformTo(nameof(drawable.Scale), newScale, duration, easing); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence ResizeTo(this T drawable, float newSize, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.ResizeTo(new Vector2(newSize), duration, easing); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence ResizeTo(this T drawable, Vector2 newSize, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.TransformTo(nameof(drawable.Size), newSize, duration, easing); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence ResizeWidthTo(this T drawable, float newWidth, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.TransformTo(nameof(drawable.Width), newWidth, duration, easing, nameof(drawable.Size)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence ResizeHeightTo(this T drawable, float newHeight, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.TransformTo(nameof(drawable.Height), newHeight, duration, easing, nameof(drawable.Size)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence MoveTo(this T drawable, Vector2 newPosition, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.TransformTo(nameof(drawable.Position), newPosition, duration, easing); /// /// Smoothly adjusts or over time. /// /// A to which further transforms can be added. public static TransformSequence MoveTo(this T drawable, Direction direction, float destination, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction { switch (direction) { case Direction.Horizontal: return drawable.MoveToX(destination, duration, easing); case Direction.Vertical: return drawable.MoveToY(destination, duration, easing); } throw new InvalidOperationException($"Invalid direction ({direction}) passed to {nameof(MoveTo)}."); } /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence MoveToX(this T drawable, float destination, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.TransformTo(nameof(drawable.X), destination, duration, easing, nameof(drawable.Position)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence MoveToY(this T drawable, float destination, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.TransformTo(nameof(drawable.Y), destination, duration, easing, nameof(drawable.Position)); /// /// Smoothly adjusts by an offset to its final value over time. /// /// A to which further transforms can be added. public static TransformSequence MoveToOffset(this T drawable, Vector2 offset, double duration, in TEasing easing) where T : Drawable where TEasing : IEasingFunction => drawable.TransformTo(drawable.PopulateTransform(new PositionOffsetTransform(offset), default, duration, easing)); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence TransformRelativeChildSizeTo(this T container, Vector2 newSize, double duration, in TEasing easing) where T : class, IContainer where TEasing : IEasingFunction => container.TransformTo(nameof(container.RelativeChildSize), newSize, duration, easing); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence TransformRelativeChildOffsetTo(this T container, Vector2 newOffset, double duration, in TEasing easing) where T : class, IContainer where TEasing : IEasingFunction => container.TransformTo(nameof(container.RelativeChildOffset), newOffset, duration, easing); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence BlurTo(this T bufferedContainer, Vector2 newBlurSigma, double duration, in TEasing easing) where T : class, IBufferedContainer where TEasing : IEasingFunction => bufferedContainer.TransformTo(nameof(bufferedContainer.BlurSigma), newBlurSigma, duration, easing); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. public static TransformSequence TransformSpacingTo(this T flowContainer, Vector2 newSpacing, double duration, in TEasing easing) where T : class, IFillFlowContainer where TEasing : IEasingFunction => flowContainer.TransformTo(nameof(flowContainer.Spacing), newSpacing, duration, easing); /// /// Smoothly adjusts the alpha channel of the colour of over time. /// /// A to which further transforms can be added. public static TransformSequence FadeEdgeEffectTo(this T container, float newAlpha, double duration, in TEasing easing) where T : class, IContainer where TEasing : IEasingFunction { Color4 targetColour = container.EdgeEffect.Colour; targetColour.A = newAlpha; return container.FadeEdgeEffectTo(targetColour, duration, easing); } /// /// Smoothly adjusts the colour of over time. /// /// A to which further transforms can be added. public static TransformSequence FadeEdgeEffectTo(this T container, Color4 newColour, double duration, in TEasing easing) where T : class, IContainer where TEasing : IEasingFunction { var effect = container.EdgeEffect; effect.Colour = newColour; return container.TweenEdgeEffectTo(effect, duration, easing); } /// /// Smoothly adjusts all parameters of over time. /// /// A to which further transforms can be added. public static TransformSequence TweenEdgeEffectTo(this T container, EdgeEffectParameters newParameters, double duration, in TEasing easing) where T : class, IContainer where TEasing : IEasingFunction => container.TransformTo(nameof(container.EdgeEffect), newParameters, duration, easing); /// /// Smoothly adjusts the value of a over time. /// /// A to which further transforms can be added. public static TransformSequence TransformBindableTo(this T drawable, [NotNull] Bindable bindable, TValue newValue, double duration, in TEasing easing) where T : class, ITransformable where TEasing : IEasingFunction => drawable.TransformTo(drawable.PopulateTransform(new TransformBindable(bindable), newValue, duration, easing)); #endregion private class PositionOffsetTransform : Transform where TEasing : IEasingFunction { private readonly Vector2 offset; public override string TargetMember => nameof(Drawable.Position); public PositionOffsetTransform(Vector2 offset) { this.offset = offset; } private Vector2 positionAt(double time) { if (time < StartTime) return StartValue; if (time >= EndTime) return EndValue; return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing); } protected override void Apply(Drawable d, double time) => d.Position = positionAt(time); protected override void ReadIntoStartValue(Drawable d) { StartValue = d.Position; EndValue = d.Position + offset; } } } }