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 System;
5using System.Collections.Generic;
6
7namespace osu.Framework.Graphics.Transforms
8{
9 public abstract class Transform
10 {
11 internal ulong TransformID;
12
13 /// <summary>
14 /// Whether this <see cref="Transform"/> has been applied to an <see cref="ITransformable"/>.
15 /// </summary>
16 internal bool Applied;
17
18 /// <summary>
19 /// Whether this <see cref="Transform"/> has been applied completely to an <see cref="ITransformable"/>.
20 /// Used to track whether we still need to apply for targets which allow rewind.
21 /// </summary>
22 internal bool AppliedToEnd;
23
24 /// <summary>
25 /// Whether this <see cref="Transform"/> can be rewound.
26 /// </summary>
27 public bool Rewindable = true;
28
29 public abstract ITransformable TargetTransformable { get; }
30
31 public double StartTime { get; internal set; }
32 public double EndTime { get; internal set; }
33
34 public bool IsLooping { get; internal set; }
35 public double LoopDelay { get; internal set; }
36
37 public abstract string TargetMember { get; }
38
39 /// <summary>
40 /// The name of the grouping this <see cref="Transform"/> belongs to.
41 /// Defaults to <see cref="TargetMember"/>.
42 /// </summary>
43 /// <remarks>
44 /// Transforms in a single group affect the same property (or properties) of a <see cref="Transformable"/>.
45 /// It is assumed that transforms in different groups are independent from each other
46 /// in that they affect different properties, and therefore they can be applied independently
47 /// in any order without affecting the end result.
48 /// </remarks>
49 public virtual string TargetGrouping => TargetMember;
50
51 public abstract void Apply(double time);
52
53 public abstract void ReadIntoStartValue();
54
55 internal bool HasStartValue;
56
57 internal ITransformSequence CompletionTargetSequence;
58
59 internal ITransformSequence AbortTargetSequence;
60
61 public Transform Clone() => (Transform)MemberwiseClone();
62
63 public static readonly IComparer<Transform> COMPARER = new TransformTimeComparer();
64
65 private class TransformTimeComparer : IComparer<Transform>
66 {
67 public int Compare(Transform x, Transform y)
68 {
69 if (x == null) throw new ArgumentNullException(nameof(x));
70 if (y == null) throw new ArgumentNullException(nameof(y));
71
72 int compare = x.StartTime.CompareTo(y.StartTime);
73 if (compare != 0) return compare;
74
75 compare = x.TransformID.CompareTo(y.TransformID);
76
77 return compare;
78 }
79 }
80
81 internal void TriggerComplete() => CompletionTargetSequence?.TransformCompleted();
82
83 internal void TriggerAbort() => AbortTargetSequence?.TransformAborted();
84 }
85
86 public abstract class Transform<TValue> : Transform
87 {
88 public TValue StartValue { get; protected set; }
89 public TValue EndValue { get; protected internal set; }
90 }
91
92 public abstract class Transform<TValue, TEasing, T> : Transform<TValue>
93 where TEasing : IEasingFunction
94 where T : class, ITransformable
95 {
96 public override ITransformable TargetTransformable => Target;
97
98 public T Target { get; internal set; }
99
100 public TEasing Easing { get; internal set; }
101
102 public sealed override void Apply(double time)
103 {
104 Apply(Target, time);
105 Applied = true;
106 }
107
108 public sealed override void ReadIntoStartValue() => ReadIntoStartValue(Target);
109
110 protected abstract void Apply(T d, double time);
111
112 protected abstract void ReadIntoStartValue(T d);
113
114 public override string ToString() => $"{Target.GetType().Name}.{TargetMember} {StartTime:0.000}-{EndTime:0.000}ms {StartValue} -> {EndValue}";
115 }
116
117 public abstract class Transform<TValue, T> : Transform<TValue, DefaultEasingFunction, T>
118 where T : class, ITransformable
119 {
120 }
121}