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