├── .gitignore ├── .travis.yml ├── LICENCE ├── README.md ├── dub.json └── source └── easing ├── functions.d └── package.d /.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | *.o 5 | *.obj 6 | 7 | *.a 8 | *.selections.json 9 | __*__ 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: d 2 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 tanitta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | easing 2 | ==== 3 | 4 | [![Dub version](https://img.shields.io/dub/v/easing.svg)](https://code.dlang.org/packages/easing) 5 | [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/tanitta/easing/blob/master/LICENSE) 6 | [![Build Status](https://travis-ci.org/tanitta/easing.svg?branch=master)](https://travis-ci.org/tanitta/easing) 7 | [![Dub downloads](https://img.shields.io/dub/dt/easing.svg)](https://code.dlang.org/packages/easing) 8 | 9 | ## Description 10 | 11 | easing is a library that add flavor to motion in D programming language. 12 | 13 | ## Usage 14 | ``` 15 | import easing; 16 | auto output = input.linear; 17 | // | 18 | // easing function -+ 19 | 20 | ``` 21 | 22 | If you would like to call with custom range, 23 | 24 | ``` 25 | import easing; 26 | auto output = input.map!linear(0.0, 10.0, 0.0, 1.0); 27 | // | | | | | 28 | // easing function -----+ | | | | 29 | // min of input ---------------+ | | | 30 | // max of input --------------------+ | | 31 | // min of output -------------------------+ | 32 | // max of output ------------------------------+ 33 | ``` 34 | 35 | Some functions have option. 36 | 37 | ``` 38 | auto output = input.easeQuadBezier(0.6, 0.7); 39 | auto output = input.easeCubicBezier(0.0, 1.0, 1.0, 0.0); 40 | 41 | auto output = input.easeInBack(1.5); 42 | ``` 43 | 44 | ``` 45 | auto output = input.map!easeInBack(0.0, 10.0, 0.0, 1.0, 1.5); 46 | ``` 47 | 48 | ## Easing functions 49 | 50 | - linear 51 | - easeInSine 52 | - easeOutSine 53 | - easeInOutSine 54 | - easeInCubic 55 | - easeOutCubic 56 | - easeInOutCubic 57 | - easeInQuint 58 | - easeOutQuint 59 | - easeInOutQuint 60 | - easeInCirc 61 | - easeOutCirc 62 | - easeInOutCirc 63 | - easeInElastic 64 | - easeOutElastic 65 | - easeInOutElastic 66 | - easeInQuad 67 | - easeOutQuad 68 | - easeInOutQuad 69 | - easeInQuart 70 | - easeOutQuart 71 | - easeInOutQuart 72 | - easeInExpo 73 | - easeOutExpo 74 | - easeInOutExpo 75 | - easeInBack 76 | - easeOutBack 77 | - easeInOutBack 78 | - easeInBounce 79 | - easeOutBounce 80 | - easeInOutBounce 81 | - easeCubicBezier 82 | - easeQuadBezier 83 | 84 | Please look at [http://easings.net/](http://easings.net/) regarding details of implemented functions. 85 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "easing", 3 | "description": "A library that add flavor to motion in D programming language.", 4 | "copyright": "Copyright © 2016, tanitta", 5 | "authors": ["tanitta"], 6 | "license": "MIT", 7 | "targetType": "library", 8 | "dependencies": { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /source/easing/functions.d: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /source/easing/package.d: -------------------------------------------------------------------------------- 1 | module easing; 2 | 3 | public import easing.functions; 4 | 5 | pure T map(alias F = linear, T, Options...)( in T input, in T minIn, in T maxIn, in T minOut, in T maxOut, Options options){ 6 | T inputUnit = (input - minIn) / (maxIn - minIn); 7 | T outputUnit = F(inputUnit, options); 8 | return outputUnit * (maxOut - minOut) + minOut; 9 | } 10 | 11 | // map should ease with custom range 12 | unittest{ 13 | assert(0.0.map!linear(0.0, 10.0, 0.0, 1.0) == 0.0); 14 | assert(5.0.map!linear(0.0, 10.0, 0.0, 1.0) == 0.5); 15 | assert(10.0.map!linear(0.0, 10.0, 0.0, 1.0) == 1.0); 16 | 17 | assert(0.0.map!(Quad.easeIn)(0.0, 10.0, 0.0, 1.0) == 0.0); 18 | assert(5.0.map!(Quad.easeIn)(0.0, 10.0, 0.0, 1.0) == 0.25); 19 | assert(10.0.map!(Quad.easeIn)(0.0, 10.0, 0.0, 1.0) == 1.0); 20 | 21 | assert(0.0.map!easeInQuad(0.0, 10.0, 0.0, 1.0) == 0.0.map!(Quad.easeIn)(0.0, 10.0, 0.0, 1.0)); 22 | } 23 | 24 | // map should accept optional args 25 | version(unittest){ 26 | T multiArgsFunction(T)( T time, T option1, T option2){return option2;} 27 | } 28 | 29 | unittest{ 30 | assert(__traits(compiles, { 31 | 0.0.map!multiArgsFunction(0.0, 10.0, 0.0, 1.0, 0.5, 2.0); 32 | })); 33 | assert(0.0.map!multiArgsFunction(0.0, 10.0, 0.0, 1.0, 0.5, 2.0) == 2.0); 34 | } 35 | --------------------------------------------------------------------------------