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;
5
6namespace osu.Framework.Graphics.Transforms
7{
8 /// <summary>
9 /// An easing function corresponding to one of the <see cref="Easing"/> types.
10 /// </summary>
11 public readonly struct DefaultEasingFunction : IEasingFunction
12 {
13 private const double elastic_const = 2 * Math.PI / .3;
14 private const double elastic_const2 = .3 / 4;
15
16 private const double back_const = 1.70158;
17 private const double back_const2 = back_const * 1.525;
18
19 private const double bounce_const = 1 / 2.75;
20
21 // constants used to fix expo and elastic curves to start/end at 0/1
22 private static readonly double expo_offset = Math.Pow(2, -10);
23 private static readonly double elastic_offset_full = Math.Pow(2, -11);
24 private static readonly double elastic_offset_half = Math.Pow(2, -10) * Math.Sin((.5 - elastic_const2) * elastic_const);
25 private static readonly double elastic_offset_quarter = Math.Pow(2, -10) * Math.Sin((.25 - elastic_const2) * elastic_const);
26 private static readonly double in_out_elastic_offset = Math.Pow(2, -10) * Math.Sin((1 - elastic_const2 * 1.5) * elastic_const / 1.5);
27
28 private readonly Easing easing;
29
30 /// <summary>
31 /// Creates a new <see cref="DefaultEasingFunction"/> for an <see cref="Easing"/>.
32 /// </summary>
33 /// <param name="easing">The <see cref="Easing"/> type.</param>
34 public DefaultEasingFunction(Easing easing)
35 {
36 this.easing = easing;
37 }
38
39 public double ApplyEasing(double time)
40 {
41 switch (easing)
42 {
43 default:
44 return time;
45
46 case Easing.In:
47 case Easing.InQuad:
48 return time * time;
49
50 case Easing.Out:
51 case Easing.OutQuad:
52 return time * (2 - time);
53
54 case Easing.InOutQuad:
55 if (time < .5) return time * time * 2;
56
57 return --time * time * -2 + 1;
58
59 case Easing.InCubic:
60 return time * time * time;
61
62 case Easing.OutCubic:
63 return --time * time * time + 1;
64
65 case Easing.InOutCubic:
66 if (time < .5) return time * time * time * 4;
67
68 return --time * time * time * 4 + 1;
69
70 case Easing.InQuart:
71 return time * time * time * time;
72
73 case Easing.OutQuart:
74 return 1 - --time * time * time * time;
75
76 case Easing.InOutQuart:
77 if (time < .5) return time * time * time * time * 8;
78
79 return --time * time * time * time * -8 + 1;
80
81 case Easing.InQuint:
82 return time * time * time * time * time;
83
84 case Easing.OutQuint:
85 return --time * time * time * time * time + 1;
86
87 case Easing.InOutQuint:
88 if (time < .5) return time * time * time * time * time * 16;
89
90 return --time * time * time * time * time * 16 + 1;
91
92 case Easing.InSine:
93 return 1 - Math.Cos(time * Math.PI * .5);
94
95 case Easing.OutSine:
96 return Math.Sin(time * Math.PI * .5);
97
98 case Easing.InOutSine:
99 return .5 - .5 * Math.Cos(Math.PI * time);
100
101 case Easing.InExpo:
102 return Math.Pow(2, 10 * (time - 1)) + expo_offset * (time - 1);
103
104 case Easing.OutExpo:
105 return -Math.Pow(2, -10 * time) + 1 + expo_offset * time;
106
107 case Easing.InOutExpo:
108 if (time < .5) return .5 * (Math.Pow(2, 20 * time - 10) + expo_offset * (2 * time - 1));
109
110 return 1 - .5 * (Math.Pow(2, -20 * time + 10) + expo_offset * (-2 * time + 1));
111
112 case Easing.InCirc:
113 return 1 - Math.Sqrt(1 - time * time);
114
115 case Easing.OutCirc:
116 return Math.Sqrt(1 - --time * time);
117
118 case Easing.InOutCirc:
119 if ((time *= 2) < 1) return .5 - .5 * Math.Sqrt(1 - time * time);
120
121 return .5 * Math.Sqrt(1 - (time -= 2) * time) + .5;
122
123 case Easing.InElastic:
124 return -Math.Pow(2, -10 + 10 * time) * Math.Sin((1 - elastic_const2 - time) * elastic_const) + elastic_offset_full * (1 - time);
125
126 case Easing.OutElastic:
127 return Math.Pow(2, -10 * time) * Math.Sin((time - elastic_const2) * elastic_const) + 1 - elastic_offset_full * time;
128
129 case Easing.OutElasticHalf:
130 return Math.Pow(2, -10 * time) * Math.Sin((.5 * time - elastic_const2) * elastic_const) + 1 - elastic_offset_half * time;
131
132 case Easing.OutElasticQuarter:
133 return Math.Pow(2, -10 * time) * Math.Sin((.25 * time - elastic_const2) * elastic_const) + 1 - elastic_offset_quarter * time;
134
135 case Easing.InOutElastic:
136 if ((time *= 2) < 1)
137 {
138 return -.5 * (Math.Pow(2, -10 + 10 * time) * Math.Sin((1 - elastic_const2 * 1.5 - time) * elastic_const / 1.5)
139 - in_out_elastic_offset * (1 - time));
140 }
141
142 return .5 * (Math.Pow(2, -10 * --time) * Math.Sin((time - elastic_const2 * 1.5) * elastic_const / 1.5)
143 - in_out_elastic_offset * time) + 1;
144
145 case Easing.InBack:
146 return time * time * ((back_const + 1) * time - back_const);
147
148 case Easing.OutBack:
149 return --time * time * ((back_const + 1) * time + back_const) + 1;
150
151 case Easing.InOutBack:
152 if ((time *= 2) < 1) return .5 * time * time * ((back_const2 + 1) * time - back_const2);
153
154 return .5 * ((time -= 2) * time * ((back_const2 + 1) * time + back_const2) + 2);
155
156 case Easing.InBounce:
157 time = 1 - time;
158 if (time < bounce_const)
159 return 1 - 7.5625 * time * time;
160 if (time < 2 * bounce_const)
161 return 1 - (7.5625 * (time -= 1.5 * bounce_const) * time + .75);
162 if (time < 2.5 * bounce_const)
163 return 1 - (7.5625 * (time -= 2.25 * bounce_const) * time + .9375);
164
165 return 1 - (7.5625 * (time -= 2.625 * bounce_const) * time + .984375);
166
167 case Easing.OutBounce:
168 if (time < bounce_const)
169 return 7.5625 * time * time;
170 if (time < 2 * bounce_const)
171 return 7.5625 * (time -= 1.5 * bounce_const) * time + .75;
172 if (time < 2.5 * bounce_const)
173 return 7.5625 * (time -= 2.25 * bounce_const) * time + .9375;
174
175 return 7.5625 * (time -= 2.625 * bounce_const) * time + .984375;
176
177 case Easing.InOutBounce:
178 if (time < .5) return .5 - .5 * new DefaultEasingFunction(Easing.OutBounce).ApplyEasing(1 - time * 2);
179
180 return new DefaultEasingFunction(Easing.OutBounce).ApplyEasing((time - .5) * 2) * .5 + .5;
181
182 case Easing.OutPow10:
183 return --time * Math.Pow(time, 10) + 1;
184 }
185 }
186 }
187}