1 module easing.functions;
2 
3 pure T linear(T)(in T time){
4     return time;
5 }
6 
7 pure T linearS(T)(in T[] args...)
8 in{
9     assert(args.length >= 1);
10 }body{
11     return linear!T(args[0]);
12 }
13 
14 unittest{
15     assert(0.0.linear == 0.0);
16     assert(1.0.linear == 1.0);
17 }
18 
19 unittest{
20     assert([1.0].linearS == 1.0);
21     assert(1.0.linearS == 1.0);
22 }
23 
24 private mixin template AddFunctionAcceptingSlice(string Name){
25     mixin(q"/
26         static pure T /"~Name~q"/S(T)(in T[] args...)
27         in{
28             assert(args.length >= 1);
29         }body{
30             return /"~Name~q"/!T(args[0]);
31         }
32     /");
33 }
34 
35 unittest{
36     assert(1.0.easeInOutSineS == 1.0);
37 }
38 
39 struct Sine{
40     @disable this();
41 
42     import std.math;
43 
44     static pure T easeIn(T)(in T time){
45         return -cos(time * (PI/T(2))) + T(1);
46     }
47 
48     static pure T easeOut(T)(in T time){
49         return sin(time * (PI/T(2)));
50     }
51 
52     static pure T easeInOut(T)(in T time){
53         return T(-0.5) * (cos(PI*time) - T(1));
54     }
55 
56     mixin AddFunctionAcceptingSlice!("easeIn");
57     mixin AddFunctionAcceptingSlice!("easeOut");
58     mixin AddFunctionAcceptingSlice!("easeInOut");
59 }
60 
61 alias easeInSine = Sine.easeIn;
62 alias easeOutSine = Sine.easeOut;
63 alias easeInOutSine = Sine.easeInOut;
64 alias easeInSineS = Sine.easeInS;
65 alias easeOutSineS = Sine.easeOutS;
66 alias easeInOutSineS = Sine.easeInOutS;
67 
68 unittest{
69     assert(0.0.easeInSine == 0.0);
70     assert(1.0.easeInSine == 1.0);
71 
72     assert(0.0.easeOutSine == 0.0);
73     assert(1.0.easeOutSine == 1.0);
74 
75     assert(0.0.easeInOutSine == 0.0);
76     assert(1.0.easeInOutSine == 1.0);
77 }
78 
79 
80 struct Qubic{
81     @disable this();
82 
83     static pure T easeIn(T)(in T time){
84         return time^^T(3.0);
85     }
86 
87     static pure T easeOut(T)(in T time){
88         return (time-T(1))^^T(3) + T(1);
89     }
90 
91     static pure T easeInOut(T)(in T time){
92         immutable t2 = time * T(2);
93         if (t2 < T(1)){
94             return T(0.5)*t2^^T(3);
95         }else{
96             immutable t2m2 = t2-T(2);
97             return T(0.5)*(t2m2^^T(3) + T(2));
98         }
99     }
100 
101     mixin AddFunctionAcceptingSlice!("easeIn");
102     mixin AddFunctionAcceptingSlice!("easeOut");
103     mixin AddFunctionAcceptingSlice!("easeInOut");
104 }
105 
106 alias easeInQubic = Qubic.easeIn;
107 alias easeOutQubic = Qubic.easeOut;
108 alias easeInOutQubic = Qubic.easeInOut;
109 alias easeInQubicS = Qubic.easeInS;
110 alias easeOutQubicS = Qubic.easeOutS;
111 alias easeInOutQubicS = Qubic.easeInOutS;
112 
113 unittest{
114     assert(0.0.easeInQubic == 0.0);
115     assert(1.0.easeInQubic == 1.0);
116 
117     assert(0.0.easeOutQubic == 0.0);
118     assert(1.0.easeOutQubic == 1.0);
119 
120     assert(0.0.easeInOutQubic == 0.0);
121     assert(1.0.easeInOutQubic == 1.0);
122 }
123 
124 
125 struct Quint{
126     @disable this();
127 
128     static pure T easeIn(T)(in T time){
129         return time^^T(5);
130     }
131 
132     static pure T easeOut(T)(in T time){
133         immutable tm1 = time-T(1);
134         return (tm1^^5 + T(1));
135     }
136 
137     static pure T easeInOut(T)(in T time){
138         immutable t2 = time * T(2);
139         if (t2 < 1){
140             return T(0.5)*t2^^T(5);
141         }else {
142             immutable t2m2 = t2-T(2);
143             return T(0.5)*(t2m2^^T(5) + T(2));
144         }
145     }
146 
147     mixin AddFunctionAcceptingSlice!("easeIn");
148     mixin AddFunctionAcceptingSlice!("easeOut");
149     mixin AddFunctionAcceptingSlice!("easeInOut");
150 }
151 
152 alias easeInQuint = Quint.easeIn;
153 alias easeOutQuint = Quint.easeOut;
154 alias easeInOutQuint = Quint.easeInOut;
155 alias easeInQuintS = Quint.easeInS;
156 alias easeOutQuintS = Quint.easeOutS;
157 alias easeInOutQuintS = Quint.easeInOutS;
158 
159 unittest{
160     assert(0.0.easeInQuint == 0.0);
161     assert(1.0.easeInQuint == 1.0);
162 
163     assert(0.0.easeOutQuint == 0.0);
164     assert(1.0.easeOutQuint == 1.0);
165 
166     assert(0.0.easeInOutQuint == 0.0);
167     assert(1.0.easeInOutQuint == 1.0);
168 }
169 
170 
171 struct Circ{
172     @disable this();
173 
174     import std.math;
175     static pure T easeIn(T)(in T time){
176         return -(sqrt(T(1) - time*time) - T(1));
177     }
178 
179     static pure T easeOut(T)(in T time){
180         T duration = T(1);
181         
182         immutable tm1 = time - T(1);
183         return sqrt(T(1) - tm1^^T(2));
184     }
185 
186     static pure T easeInOut(T)(in T time){
187         immutable t2 = time * T(2);
188         if (t2 < T(1)) {
189             return -T(0.5) * (sqrt(T(1) - t2^^2) - T(1));
190         }else{
191             immutable t2m2 = t2 - T(2);
192             return T(0.5) * (sqrt(T(1) - t2m2^^2) + T(1));
193         }
194     }
195 
196     mixin AddFunctionAcceptingSlice!("easeIn");
197     mixin AddFunctionAcceptingSlice!("easeOut");
198     mixin AddFunctionAcceptingSlice!("easeInOut");
199 }
200 
201 alias easeInCirc = Circ.easeIn;
202 alias easeOutCirc = Circ.easeOut;
203 alias easeInOutCirc = Circ.easeInOut;
204 alias easeInCircS = Circ.easeInS;
205 alias easeOutCircS = Circ.easeOutS;
206 alias easeInOutCircS = Circ.easeInOutS;
207 
208 unittest{
209     assert(0.0.easeInCirc == 0.0);
210     assert(1.0.easeInCirc == 1.0);
211 
212     assert(0.0.easeOutCirc == 0.0);
213     assert(1.0.easeOutCirc == 1.0);
214 
215     assert(0.0.easeInOutCirc == 0.0);
216     assert(1.0.easeInOutCirc == 1.0);
217 }
218 
219 
220 struct Elastic{
221     @disable this();
222 
223     import std.math;
224 
225     static pure T easeIn(T)(in T time){
226         if (time==T(0)) return T(0);
227         if (time==T(1)) return T(1);
228         immutable p = T(0.3);
229         immutable a = T(1);
230         immutable s = p/T(4);
231         immutable tm1 = time-T(1);
232         T postFix =a*pow(T(2),T(10)*tm1);
233         return -(postFix * sin((tm1-s)*(T(2)*T(PI))/p ));
234     }
235 
236     static pure T easeOut(T)(in T time){
237         if (time==T(0)) return T(0);
238         if (time==T(1)) return T(1);
239         immutable p = T(0.3);
240         immutable a = T(1);
241         immutable s = p/T(4);
242         return (a*pow(T(2),T(-10)*time) * sin( (time-s)*(T(2)*T(PI))/p ) + T(1));
243     }
244 
245     static pure T easeInOut(T)(in T time){
246         if (time==T(0)) return T(0);
247         immutable t2 = time * T(2);
248         if (t2==T(2)) return T(1);
249         T p=T(0.3*1.5);
250         T a=T(1);
251         T s=p/T(4);
252 
253         immutable t2m1 = t2 - T(1);
254         if (t2 < T(1)) {
255             T postFix =a*pow(T(2),T(10)*(t2m1)); // postIncrement is evil
256             return T(-0.5)*(postFix* sin( (t2m1-s)*(T(2)*T(PI))/p ));
257         }
258         T postFix =  a*pow(T(2),T(-10)*(t2m1)); // postIncrement is evil
259         return postFix * sin( (t2m1-s)*(T(2)*T(PI))/p )*T(0.5) + T(1);
260     }
261 
262     mixin AddFunctionAcceptingSlice!("easeIn");
263     mixin AddFunctionAcceptingSlice!("easeOut");
264     mixin AddFunctionAcceptingSlice!("easeInOut");
265 }
266 
267 alias easeInElastic = Elastic.easeIn;
268 alias easeOutElastic = Elastic.easeOut;
269 alias easeInOutElastic = Elastic.easeInOut;
270 alias easeInElasticS = Elastic.easeInS;
271 alias easeOutElasticS = Elastic.easeOutS;
272 alias easeInOutElasticS = Elastic.easeInOutS;
273 
274 unittest{
275     assert(0.0.easeInElastic == 0.0);
276     assert(1.0.easeInElastic == 1.0);
277 
278     assert(0.0.easeOutElastic == 0.0);
279     assert(1.0.easeOutElastic == 1.0);
280 
281     assert(0.0.easeInOutElastic == 0.0);
282     assert(1.0.easeInOutElastic == 1.0);
283 }
284 
285 
286 struct Quad{
287     @disable this();
288 
289     static pure T easeIn(T)(in T time){
290         return time^^2;
291     }
292 
293     static pure T easeOut(T)(in T time){
294         return -time * (time-T(2));
295     }
296 
297     static pure T easeInOut(T)(in T time){
298         if (time < T(0.5)){
299             return easeIn(time*T(2)) * T(0.5);
300         }else{
301             return easeOut(time*T(2)-T(1)) * T(0.5) + T(0.5);
302         }
303     }
304 
305     mixin AddFunctionAcceptingSlice!("easeIn");
306     mixin AddFunctionAcceptingSlice!("easeOut");
307     mixin AddFunctionAcceptingSlice!("easeInOut");
308 }
309 
310 alias easeInQuad = Quad.easeIn;
311 alias easeOutQuad = Quad.easeOut;
312 alias easeInOutQuad = Quad.easeInOut;
313 alias easeInQuadS = Quad.easeInS;
314 alias easeOutQuadS = Quad.easeOutS;
315 alias easeInOutQuadS = Quad.easeInOutS;
316 
317 unittest{
318     assert(0.0.easeInQuad == 0.0);
319     assert(1.0.easeInQuad == 1.0);
320 
321     assert(0.0.easeOutQuad == 0.0);
322     assert(1.0.easeOutQuad == 1.0);
323 
324     assert(0.0.easeInOutQuad == 0.0);
325     assert(1.0.easeInOutQuad == 1.0);
326 }
327 
328 
329 struct Quart{
330     @disable this();
331 
332     static pure T easeIn(T)(in T time){
333         return time^^4;
334     }
335 
336     static pure T easeOut(T)(in T time){
337         immutable tm1 = time - T(1);
338         return -(tm1^^4- T(1));
339     }
340 
341     static pure T easeInOut(T)(in T time){
342         immutable t2 = time * T(2);
343         if (t2 < T(1)){
344             return T(0.5)*t2^^4;
345         }else{
346             immutable t2m2 = t2 - T(2);
347             return T(-0.5) * (t2m2^^4 - T(2));
348         }
349     }
350 
351     mixin AddFunctionAcceptingSlice!("easeIn");
352     mixin AddFunctionAcceptingSlice!("easeOut");
353     mixin AddFunctionAcceptingSlice!("easeInOut");
354 }
355 
356 alias easeInQuart = Quart.easeIn;
357 alias easeOutQuart = Quart.easeOut;
358 alias easeInOutQuart = Quart.easeInOut;
359 alias easeInQuartS = Quart.easeInS;
360 alias easeOutQuartS = Quart.easeOutS;
361 alias easeInOutQuartS = Quart.easeInOutS;
362 
363 unittest{
364     assert(0.0.easeInQuart == 0.0);
365     assert(1.0.easeInQuart == 1.0);
366 
367     assert(0.0.easeOutQuart == 0.0);
368     assert(1.0.easeOutQuart == 1.0);
369 
370     assert(0.0.easeInOutQuart == 0.0);
371     assert(1.0.easeInOutQuart == 1.0);
372 }
373 
374 
375 struct Expo{
376     @disable this();
377 
378     import std.math;
379 
380     static pure T easeIn(T)(in T time){
381         if(time==T(0)){
382             return T(0);
383         }else{
384             return pow(T(2), T(10) * (time- T(1))) + T(0);
385         }
386     }
387 
388     static pure T easeOut(T)(in T time){
389         if(time==T(1)){
390             return T(1);
391         }else{
392             return -pow(T(2), T(-10) * time) + T(1);
393         }
394     }
395 
396     static pure T easeInOut(T)(in T time){
397         if (time==T(0)) return T(0);
398         if (time==T(1)) return T(1);
399         immutable t2 = time*T(2);
400         if (t2 < T(1)) {
401             return T(0.5) * pow(T(2), T(10) * (t2 - T(1)));
402         }
403         immutable t2m1 = t2;
404         return T(0.5) * (-pow(T(2), T(-10) * t2m1) + T(2));
405     }
406 
407     mixin AddFunctionAcceptingSlice!("easeIn");
408     mixin AddFunctionAcceptingSlice!("easeOut");
409     mixin AddFunctionAcceptingSlice!("easeInOut");
410 }
411 
412 alias easeInExpo = Expo.easeIn;
413 alias easeOutExpo = Expo.easeOut;
414 alias easeInOutExpo = Expo.easeInOut;
415 alias easeInExpoS = Expo.easeInS;
416 alias easeOutExpoS = Expo.easeOutS;
417 alias easeInOutExpoS = Expo.easeInOutS;
418 
419 unittest{
420     assert(0.0.easeInExpo == 0.0);
421     assert(1.0.easeInExpo == 1.0);
422 
423     assert(0.0.easeOutExpo == 0.0);
424     assert(1.0.easeOutExpo == 1.0);
425 
426     assert(0.0.easeInOutExpo == 0.0);
427     assert(1.0.easeInOutExpo == 1.0);
428 }
429 
430 
431 struct Back{
432     @disable this();
433 
434     static pure T easeIn(T)(in T time, in T s = 1.70158){
435         return time^^2*((s+T(1))*time - s);
436     }
437 
438     static pure T easeInS(T)(in T[] args ...)in{
439         assert(args.length >= 1);
440     }body{
441         immutable time = args[0];
442         immutable s = (args.length>1)?args[1]:1.70158;
443         return easeIn!(T)(time, s);
444     }
445 
446     static pure T easeOut(T)(in T time, in T s = 1.70158){
447         immutable tm1 = time-T(1);
448         return (tm1^^2*((s+T(1))*tm1 + s) + T(1));
449     }
450 
451     static pure T easeOutS(T)(in T[] args ...)in{
452         assert(args.length >= 1);
453     }body{
454         immutable time = args[0];
455         immutable s = (args.length>1)?args[1]:1.70158;
456         return easeOut!(T)(time, s);
457     }
458 
459     // static pure T easeInS(T)(in T[] args ...){
460     //
461     // }
462 
463     static pure T easeInOut(T)(in T time, in T s = 1.70158){
464         immutable sc = s * T(1.525f);
465         immutable t2 = time * T(2);
466         if (t2 < T(1)){
467             return T(0.5)*(t2^^2*((sc+T(1))*t2 - sc));
468         }
469         immutable t2m2 = t2 - T(2);
470         return T(0.5)*(t2m2^^2*((sc+T(1))*t2m2 + sc) + T(2));
471     }
472 
473     static pure T easeInOutS(T)(in T[] args ...)in{
474         assert(args.length >= 1);
475     }body{
476         immutable time = args[0];
477         immutable s = (args.length>1)?args[1]:1.70158;
478         return easeInOut!(T)(time, s);
479     }
480 }
481 
482 alias easeInBack = Back.easeIn;
483 alias easeOutBack = Back.easeOut;
484 alias easeInOutBack = Back.easeInOut;
485 alias easeInBackS = Back.easeInS;
486 alias easeOutBackS = Back.easeOutS;
487 alias easeInOutBackS = Back.easeInOutS;
488 
489 unittest{
490     import std.math;
491     
492     assert(approxEqual(0.0.easeInBack, 0.0));
493     assert(approxEqual(1.0.easeInBack, 1.0));
494 
495     assert(approxEqual(0.0.easeOutBack, 0.0));
496     assert(approxEqual(1.0.easeOutBack, 1.0));
497 
498     assert(approxEqual(0.0.easeInOutBack, 0.0));
499     assert(approxEqual(1.0.easeInOutBack, 1.0));
500 }
501 
502 
503 struct Bounce{
504     @disable this();
505 
506     static pure T easeIn(T)(in T time){
507         return T(1)- easeOut(T(1)-time);
508     }
509 
510     static pure T easeOut(T)(in T time){
511         if ((time) < (T(1.0/2.75))) {
512             return (T(7.5625)*time^^2);
513         } else if (time < T(2.0/2.75)) {
514             immutable postFix = time - T(1.5/2.75);
515             return (T(7.5625)*postFix^^2+ T(0.75));
516         } else if (time < T(2.5/2.75)) {
517             immutable postFix = time - T(2.25/2.75);
518             return (T(7.5625)*postFix^^2+ T(0.9375));
519         } else {
520             immutable postFix = time-T(2.625/2.75);
521             return (T(7.5625)*postFix^^2+ T(0.984375));
522         }
523     }
524 
525     static pure T easeInOut(T)(in T time){
526         if (time < T(0.5)){
527             return easeIn(time*T(2)) * T(0.5);
528         }else{ 
529             return easeOut(time*T(2)-T(1)) * T(0.5) + T(0.5);
530         }
531     }
532 
533     mixin AddFunctionAcceptingSlice!("easeIn");
534     mixin AddFunctionAcceptingSlice!("easeOut");
535     mixin AddFunctionAcceptingSlice!("easeInOut");
536 }
537 
538 alias easeInBounce = Bounce.easeIn;
539 alias easeOutBounce = Bounce.easeOut;
540 alias easeInOutBounce = Bounce.easeInOut;
541 alias easeInBounceS = Bounce.easeInS;
542 alias easeOutBounceS = Bounce.easeOutS;
543 alias easeInOutBounceS = Bounce.easeInOutS;
544 
545 unittest{
546     import std.math;
547     assert(approxEqual(0.0.easeInBounce, 0.0));
548     assert(approxEqual(1.0.easeInBounce, 1.0));
549 
550     assert(approxEqual(0.0.easeOutBounce, 0.0));
551     assert(approxEqual(1.0.easeOutBounce, 1.0));
552 
553     assert(approxEqual(0.0.easeInOutBounce, 0.0));
554     assert(approxEqual(1.0.easeInOutBounce, 1.0));
555 }
556 
557 
558 struct Bezier{
559     @disable this();
560     
561     static pure T cubic(T)(in T time, in T x1, in T y1, in T x2, in T y2, in T error = T(0.0001))
562     in{
563         assert(T(0)<=time && time<=T(1));
564         assert(T(0)<=x1 && x1<=T(1));
565         assert(T(0)<=x2 && x2<=T(1));
566     }body{
567         import std.math;
568         
569         T t = time;
570         auto dt = T(0);
571         
572         do {
573             dt = -(cubicParametricF(t, x1, x2) - time) / cubicParametricDF(t, x1, x2);
574             if(dt.isNaN)break;
575             t += dt;
576         } while (dt.fabs > error);
577         
578         return cubicParametricF(t, y1, y2);
579     }
580 
581     static pure T cubicS(T)(in T[] args ...)in{
582         assert(args.length >= 5);
583     }body{
584         immutable time = args[0];
585         immutable x1= args[1];
586         immutable y1= args[2];
587         immutable x2= args[3];
588         immutable y2= args[4];
589         immutable error = (args.length>5)?args[5]:T(0.0001);
590         return cubic!T(time, x1, y1, x2, y2, error);
591     }
592     
593     static pure T quad(T)(in T time, in T x, in T y, in T error = T(0.0001))
594     in{
595         assert(T(0)<=time && time<=T(1));
596         assert(T(0)<=x && x<=T(1));
597     }body{
598         import std.math;
599         
600         T t = time;
601         auto dt = T(0);
602         
603         do {
604             dt = -(quadParametricF(t, x) - time) / quadParametricDF(t, x);
605             if(dt.isNaN)break;
606             t += dt;
607         } while (dt.fabs > error);
608         
609         return quadParametricF(t, y);
610     }
611 
612     static pure T quadS(T)(in T[] args ...)in{
613         assert(args.length >= 3);
614     }body{
615         immutable time = args[0];
616         immutable x = args[1];
617         immutable y = args[2];
618         immutable error = (args.length>3)?args[3]:T(0.0001);
619         return quad!T(time, x, y, error);
620     }
621     
622     private{
623         static pure T cubicParametricF(T)(in T t, in T x1, in T x2){
624             return T(3) * (T(1) - t)^^2 * t * x1 + T(3) * (T(1) - t) * t^^2 * x2 + t^^3;
625         }
626         
627         static pure T cubicParametricDF(T)(in T t, in T x1, in T x2){
628             return T(-6) * (T(1)-t) * t * x1 +
629                 T(3) * (T(1)-t) * (T(1) - t) * x1 +
630                 T(-3) * t^^2 * x2 +
631                 T(6) * (T(1) - t) * t * x2 + 
632                 T(3) * t^^2;
633         }
634         
635         static pure T quadParametricF(T)(in T t, in T x){
636             return T(2) * (T(1)-t) * t * x + t^^2;
637         }
638         
639         static pure T quadParametricDF(T)(in T t, in T x){
640             return T(2) * (T(1)-t) * x - T(2) * t * (T(1) - x);
641         }
642     }
643 }
644 
645 alias easeCubicBezier = Bezier.cubic;
646 alias easeQuadBezier = Bezier.quad;
647 alias easeCubicBezierS = Bezier.cubicS;
648 alias easeQuadBezierS = Bezier.quadS;
649 
650 unittest{
651     import std.math;
652     assert(approxEqual(0.0.easeCubicBezier(0.0, 0.0, 1.0, 1.0), 0.0));
653     assert(approxEqual(0.5.easeCubicBezier(0.0, 0.0, 1.0, 1.0), 0.5));
654     assert(approxEqual(1.0.easeCubicBezier(0.0, 0.0, 1.0, 1.0), 1.0));
655     
656     assert(approxEqual(0.0.easeCubicBezier(0.17, 0.67, 0.83, 0.67), 0.0));
657     assert(approxEqual(1.0.easeCubicBezier(0.17, 0.67, 0.83, 0.67), 1.0));
658     
659     assert(approxEqual(0.0.easeQuadBezier(0.5, 0.5), 0.0));
660     assert(approxEqual(0.5.easeQuadBezier(0.5, 0.5), 0.5));
661     assert(approxEqual(1.0.easeQuadBezier(0.5, 0.5), 1.0));
662     
663     assert(approxEqual(0.0.easeQuadBezier(0.6, 0.7), 0.0));
664     assert(approxEqual(1.0.easeQuadBezier(0.6, 0.7), 1.0));
665 }
666 
667 unittest{
668     import std.math;
669     assert(approxEqual(1.0.easeQuadBezierS(0.6, 0.7), 1.0));
670     assert(approxEqual(1.0.easeCubicBezierS(0.0, 0.0, 1.0, 1.0), 1.0));
671     assert(approxEqual(1.0.easeCubicBezierS(0.0, 0.0, 1.0, 1.0, 0.00001), 1.0));
672     assert(approxEqual(easeCubicBezierS([1.0, 0.0, 0.0, 1.0, 1.0]), 1.0));
673     assert(approxEqual([1.0, 0.0, 0.0, 1.0, 1.0].easeCubicBezierS, 1.0));
674 }
675