1 /**
2  * Type generic easing functions.
3  */
4 module bettercmath.easings;
5 
6 import bettercmath.cmath;
7 
8 // Easings based on https://easings.net/
9 // and this implementation: https://github.com/warrenm/AHEasing/
10 
11 private T squared(T)(const T p)
12 {
13     return p * p;
14 }
15 
16 // Modeled after the line y = x
17 T linear(T)(const T p)
18 {
19 	return p;
20 }
21 
22 // Modeled after the parabola y = x^2
23 T easeInQuadratic(T)(const T p)
24 {
25 	return p * p;
26 }
27 alias easeInQuad = easeInQuadratic;
28 
29 // Modeled after the parabola y = -x^2 + 2x
30 T easeOutQuadratic(T)(const T p)
31 {
32 	return -(p * (p - 2));
33 }
34 alias easeOutQuad = easeOutQuadratic;
35 
36 // Modeled after the piecewise quadratic
37 // y = (1/2)((2x)^2)             ; [0, 0.5)
38 // y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1]
39 T easeInOutQuadratic(T)(const T p)
40 {
41 	if(p < 0.5)
42 	{
43 		return 2 * p * p;
44 	}
45 	else
46 	{
47 		return (-2 * p * p) + (4 * p) - 1;
48 	}
49 }
50 alias easeInOutQuad = easeInOutQuadratic;
51 
52 // Modeled after the cubic y = x^3
53 T easeInCubic(T)(const T p)
54 {
55 	return p * p * p;
56 }
57 
58 // Modeled after the cubic y = (x - 1)^3 + 1
59 T easeOutCubic(T)(const T p)
60 {
61 	auto f = (p - 1);
62 	return f * f * f + 1;
63 }
64 
65 // Modeled after the piecewise cubic
66 // y = (1/2)((2x)^3)       ; [0, 0.5)
67 // y = (1/2)((2x-2)^3 + 2) ; [0.5, 1]
68 T easeInOutCubic(T)(const T p)
69 {
70 	if(p < 0.5)
71 	{
72 		return 4 * p * p * p;
73 	}
74 	else
75 	{
76 		auto f = ((2 * p) - 2);
77 		return 0.5 * f * f * f + 1;
78 	}
79 }
80 
81 // Modeled after the quartic x^4
82 T easeInQuartic(T)(const T p)
83 {
84 	return p * p * p * p;
85 }
86 alias easeInQuart = easeInQuartic;
87 
88 // Modeled after the quartic y = 1 - (x - 1)^4
89 T easeOutQuartic(T)(const T p)
90 {
91 	auto f = (p - 1);
92 	return f * f * f * (1 - p) + 1;
93 }
94 alias easeOutQuart = easeOutQuartic;
95 
96 // Modeled after the piecewise quartic
97 // y = (1/2)((2x)^4)        ; [0, 0.5)
98 // y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1]
99 T easeInOutQuartic(T)(const T p) 
100 {
101 	if(p < 0.5)
102 	{
103 		return 8 * p * p * p * p;
104 	}
105 	else
106 	{
107 		auto f = (p - 1);
108 		return -8 * f * f * f * f + 1;
109 	}
110 }
111 alias easeInOutQuart = easeInOutQuartic;
112 
113 // Modeled after the quintic y = x^5
114 T easeInQuintic(T)(const T p) 
115 {
116 	return p * p * p * p * p;
117 }
118 alias easeInQuint = easeInQuintic;
119 
120 // Modeled after the quintic y = (x - 1)^5 + 1
121 T easeOutQuintic(T)(const T p) 
122 {
123 	auto f = (p - 1);
124 	return f * f * f * f * f + 1;
125 }
126 alias easeOutQuint = easeOutQuintic;
127 
128 // Modeled after the piecewise quintic
129 // y = (1/2)((2x)^5)       ; [0, 0.5)
130 // y = (1/2)((2x-2)^5 + 2) ; [0.5, 1]
131 T easeInOutQuintic(T)(const T p) 
132 {
133 	if(p < 0.5)
134 	{
135 		return 16 * p * p * p * p * p;
136 	}
137 	else
138 	{
139 		auto f = ((2 * p) - 2);
140 		return  0.5 * f * f * f * f * f + 1;
141 	}
142 }
143 alias easeInOutQuint = easeInOutQuintic;
144 
145 // Modeled after quarter-cycle of sine wave
146 T easeInSine(T)(const T p)
147 {
148 	return sin((p - 1) * PI_2!T) + 1;
149 }
150 
151 // Modeled after quarter-cycle of sine wave (different phase)
152 T easeOutSine(T)(const T p)
153 {
154 	return sin(p * PI_2!T);
155 }
156 
157 // Modeled after half sine wave
158 T easeInOutSine(T)(const T p)
159 {
160 	return 0.5 * (1 - cos(p * PI!T));
161 }
162 
163 // Modeled after shifted quadrant IV of unit circle
164 T easeInCircular(T)(const T p)
165 {
166 	return 1 - sqrt(1 - (p * p));
167 }
168 alias easeInCirc = easeInCircular;
169 
170 // Modeled after shifted quadrant II of unit circle
171 T easeOutCircular(T)(const T p)
172 {
173 	return sqrt((2 - p) * p);
174 }
175 alias easeOutCirc = easeOutCircular;
176 
177 // Modeled after the piecewise circular function
178 // y = (1/2)(1 - sqrt(1 - 4x^2))           ; [0, 0.5)
179 // y = (1/2)(sqrt(-(2x - 3)*(2x - 1)) + 1) ; [0.5, 1]
180 T easeInOutCircular(T)(const T p)
181 {
182 	if(p < 0.5)
183 	{
184 		return 0.5 * (1 - sqrt(1 - 4 * (p * p)));
185 	}
186 	else
187 	{
188 		return 0.5 * (sqrt(-((2 * p) - 3) * ((2 * p) - 1)) + 1);
189 	}
190 }
191 alias easeInOutCirc = easeInOutCircular;
192 
193 // Modeled after the exponential function y = 2^(10(x - 1))
194 T easeInExponential(T)(const T p)
195 {
196 	return (p == 0.0) ? p : squared(10 * (p - 1));
197 }
198 alias easeInExpo = easeInExponential;
199 
200 // Modeled after the exponential function y = -2^(-10x) + 1
201 T easeOutExponential(T)(const T p)
202 {
203 	return (p == 1.0) ? p : 1 - squared(-10 * p);
204 }
205 alias easeOutExpo = easeOutExponential;
206 
207 // Modeled after the piecewise exponential
208 // y = (1/2)2^(10(2x - 1))         ; [0,0.5)
209 // y = -(1/2)*2^(-10(2x - 1))) + 1 ; [0.5,1]
210 T easeInOutExponential(T)(const T p)
211 {
212 	if(p == 0.0 || p == 1.0) return p;
213 	
214 	if(p < 0.5)
215 	{
216 		return 0.5 * squared((20 * p) - 10);
217 	}
218 	else
219 	{
220 		return -0.5 * squared((-20 * p) + 10) + 1;
221 	}
222 }
223 alias easeInOutExpo = easeInOutExponential;
224 
225 // Modeled after the damped sine wave y = sin(13pi/2*x)*pow(2, 10 * (x - 1))
226 T easeInElastic(T)(const T p)
227 {
228 	return sin(13 * PI_2!T * p) * squared(10 * (p - 1));
229 }
230 
231 // Modeled after the damped sine wave y = sin(-13pi/2*(x + 1))*pow(2, -10x) + 1
232 T easeOutElastic(T)(const T p)
233 {
234 	return sin(-13 * PI_2!T * (p + 1)) * squared(-10 * p) + 1;
235 }
236 
237 // Modeled after the piecewise exponentially-damped sine wave:
238 // y = (1/2)*sin(13pi/2*(2*x))*pow(2, 10 * ((2*x) - 1))      ; [0,0.5)
239 // y = (1/2)*(sin(-13pi/2*((2x-1)+1))*pow(2,-10(2*x-1)) + 2) ; [0.5, 1]
240 T easeInOutElastic(T)(const T p)
241 {
242 	if(p < 0.5)
243 	{
244 		return 0.5 * sin(13 * PI_2!T * (2 * p)) * squared(10 * ((2 * p) - 1));
245 	}
246 	else
247 	{
248 		return 0.5 * (sin(-13 * PI_2!T * ((2 * p - 1) + 1)) * squared(-10 * (2 * p - 1)) + 2);
249 	}
250 }
251 
252 // Modeled after the overshooting cubic y = x^3-x*sin(x*pi)
253 T easeInBack(T)(const T p)
254 {
255 	return p * p * p - p * sin(p * PI!T);
256 }
257 
258 // Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi))
259 T easeOutBack(T)(const T p)
260 {
261 	auto f = (1 - p);
262 	return 1 - (f * f * f - f * sin(f * PI!T));
263 }
264 
265 // Modeled after the piecewise overshooting cubic function:
266 // y = (1/2)*((2x)^3-(2x)*sin(2*x*pi))           ; [0, 0.5)
267 // y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1]
268 T easeInOutBack(T)(const T p)
269 {
270 	if(p < 0.5)
271 	{
272 		auto f = 2 * p;
273 		return 0.5 * (f * f * f - f * sin(f * PI!T));
274 	}
275 	else
276 	{
277 		auto f = (1 - (2*p - 1));
278 		return 0.5 * (1 - (f * f * f - f * sin(f * PI!T))) + 0.5;
279 	}
280 }
281 
282 T easeInBounce(T)(const T p)
283 {
284 	return 1 - easeOutBounce(1 - p);
285 }
286 
287 T easeOutBounce(T)(const T p)
288 {
289 	if(p < 4/11.0)
290 	{
291 		return (121 * p * p)/16.0;
292 	}
293 	else if(p < 8/11.0)
294 	{
295 		return (363/40.0 * p * p) - (99/10.0 * p) + 17/5.0;
296 	}
297 	else if(p < 9/10.0)
298 	{
299 		return (4356/361.0 * p * p) - (35442/1805.0 * p) + 16061/1805.0;
300 	}
301 	else
302 	{
303 		return (54/5.0 * p * p) - (513/25.0 * p) + 268/25.0;
304 	}
305 }
306 
307 T easeInOutBounce(T)(const T p)
308 {
309 	if(p < 0.5)
310 	{
311 		return 0.5 * easeInBounce(p*2);
312 	}
313 	else
314 	{
315 		return 0.5 * easeOutBounce(p * 2 - 1) + 0.5;
316 	}
317 }
318 
319 /// Alias for the easing functions' type.
320 alias EasingFunction(T) = T function(const T);
321 
322 /**
323  * Collection of all easings instanciated with the same type.
324  */
325 template Easing(T)
326 {
327     alias linear = .linear!T;
328 
329     alias easeInQuadratic = .easeInQuadratic!T;
330     alias easeInQuad = easeInQuadratic;
331     alias easeOutQuadratic = .easeOutQuadratic!T;
332     alias easeOutQuad = easeOutQuadratic;
333     alias easeInOutQuadratic = .easeInOutQuadratic!T;
334     alias easeInOutQuad = easeInOutQuadratic;
335 
336     alias easeInCubic = .easeInCubic!T;
337     alias easeOutCubic = .easeOutCubic!T;
338     alias easeInOutCubic = .easeInOutCubic!T;
339 
340     alias easeInQuartic = .easeInQuartic!T;
341     alias easeInQuart = easeInQuartic;
342     alias easeOutQuartic = .easeOutQuartic!T;
343     alias easeOutQuart = easeOutQuartic;
344     alias easeInOutQuartic = .easeInOutQuartic!T;
345     alias easeInOutQuart = easeInOutQuartic;
346 
347     alias easeInQuintic = .easeInQuintic!T;
348     alias easeInQuint= easeInQuintic;
349     alias easeOutQuintic = .easeOutQuintic!T;
350     alias easeOutQuint= easeOutQuintic;
351     alias easeInOutQuintic = .easeInOutQuintic!T;
352     alias easeInOutQuint= easeInOutQuintic;
353 
354     alias easeInSine = .easeInSine!T;
355     alias easeOutSine = .easeOutSine!T;
356     alias easeInOutSine = .easeInOutSine!T;
357 
358     alias easeInCircular = .easeInCircular!T;
359     alias easeInCirc = easeInCircular;
360     alias easeOutCircular = .easeOutCircular!T;
361     alias easeOutCirc = easeOutCircular;
362     alias easeInOutCircular = .easeInOutCircular!T;
363     alias easeInOutCirc = easeInOutCircular;
364 
365     alias easeInExponential = .easeInExponential!T;
366     alias easeInExpo = easeInExponential;
367     alias easeOutExponential = .easeOutExponential!T;
368     alias easeOutExpo = easeOutExponential;
369     alias easeInOutExponential = .easeInOutExponential!T;
370     alias easeInOutExpo = easeInOutExponential;
371 
372     alias easeInElastic = .easeInElastic!T;
373     alias easeOutElastic = .easeOutElastic!T;
374     alias easeInOutElastic = .easeInOutElastic!T;
375 
376     alias easeInBack = .easeInBack!T;
377     alias easeOutBack = .easeOutBack!T;
378     alias easeInOutBack = .easeInOutBack!T;
379 
380     alias easeInBounce = .easeInBounce!T;
381     alias easeOutBounce = .easeOutBounce!T;
382     alias easeInOutBounce = .easeInOutBounce!T;
383 
384     /// Get an easing function by name.
385     /// This is useful for templates where the easing function
386     /// will be passed by name, for example for configuring an
387     /// animation in compile time.
388     EasingFunction!T named(string name)()
389     {
390         mixin("return &" ~ name ~ ";");
391     }
392 }