bezier.ts 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. export function cubicBezier(p1x : number, p1y : number, p2x : number, p2y : number):(x: number)=> number {
  2. const ZERO_LIMIT = 1e-6;
  3. // Calculate the polynomial coefficients,
  4. // implicit first and last control points are (0,0) and (1,1).
  5. const ax = 3 * p1x - 3 * p2x + 1;
  6. const bx = 3 * p2x - 6 * p1x;
  7. const cx = 3 * p1x;
  8. const ay = 3 * p1y - 3 * p2y + 1;
  9. const by = 3 * p2y - 6 * p1y;
  10. const cy = 3 * p1y;
  11. function sampleCurveDerivativeX(t : number) : number {
  12. // `ax t^3 + bx t^2 + cx t` expanded using Horner's rule
  13. return (3 * ax * t + 2 * bx) * t + cx;
  14. }
  15. function sampleCurveX(t : number) : number {
  16. return ((ax * t + bx) * t + cx) * t;
  17. }
  18. function sampleCurveY(t : number) : number {
  19. return ((ay * t + by) * t + cy) * t;
  20. }
  21. // Given an x value, find a parametric value it came from.
  22. function solveCurveX(x : number) : number {
  23. let t2 = x;
  24. let derivative : number;
  25. let x2 : number;
  26. // https://trac.webkit.org/browser/trunk/Source/WebCore/platform/animation
  27. // first try a few iterations of Newton's method -- normally very fast.
  28. // http://en.wikipedia.org/wikiNewton's_method
  29. for (let i = 0; i < 8; i++) {
  30. // f(t) - x = 0
  31. x2 = sampleCurveX(t2) - x;
  32. if (Math.abs(x2) < ZERO_LIMIT) {
  33. return t2;
  34. }
  35. derivative = sampleCurveDerivativeX(t2);
  36. // == 0, failure
  37. /* istanbul ignore if */
  38. if (Math.abs(derivative) < ZERO_LIMIT) {
  39. break;
  40. }
  41. t2 -= x2 / derivative;
  42. }
  43. // Fall back to the bisection method for reliability.
  44. // bisection
  45. // http://en.wikipedia.org/wiki/Bisection_method
  46. let t1 = 1;
  47. /* istanbul ignore next */
  48. let t0 = 0;
  49. /* istanbul ignore next */
  50. t2 = x;
  51. /* istanbul ignore next */
  52. while (t1 > t0) {
  53. x2 = sampleCurveX(t2) - x;
  54. if (Math.abs(x2) < ZERO_LIMIT) {
  55. return t2;
  56. }
  57. if (x2 > 0) {
  58. t1 = t2;
  59. } else {
  60. t0 = t2;
  61. }
  62. t2 = (t1 + t0) / 2;
  63. }
  64. // Failure
  65. return t2;
  66. }
  67. return function (x : number) : number {
  68. return sampleCurveY(solveCurveX(x));
  69. }
  70. // return solve;
  71. }