UNPKG

2.83 kBJavaScriptView Raw
1/*!
2 * (C) Ionic http://ionicframework.com - MIT License
3 */
4/**
5 * Based on:
6 * https://stackoverflow.com/questions/7348009/y-coordinate-for-a-given-x-cubic-bezier
7 * https://math.stackexchange.com/questions/26846/is-there-an-explicit-form-for-cubic-b%C3%A9zier-curves
8 * TODO: Reduce rounding error
9 */
10/**
11 * EXPERIMENTAL
12 * Given a cubic-bezier curve, get the x value (time) given
13 * the y value (progression).
14 * Ex: cubic-bezier(0.32, 0.72, 0, 1);
15 * P0: (0, 0)
16 * P1: (0.32, 0.72)
17 * P2: (0, 1)
18 * P3: (1, 1)
19 *
20 * If you give a cubic bezier curve that never reaches the
21 * provided progression, this function will return an empty array.
22 */
23const getTimeGivenProgression = (p0, p1, p2, p3, progression) => {
24 return solveCubicBezier(p0[1], p1[1], p2[1], p3[1], progression).map(tValue => {
25 return solveCubicParametricEquation(p0[0], p1[0], p2[0], p3[0], tValue);
26 });
27};
28/**
29 * Solve a cubic equation in one dimension (time)
30 */
31const solveCubicParametricEquation = (p0, p1, p2, p3, t) => {
32 const partA = (3 * p1) * Math.pow(t - 1, 2);
33 const partB = (-3 * p2 * t) + (3 * p2) + (p3 * t);
34 const partC = p0 * Math.pow(t - 1, 3);
35 return t * (partA + (t * partB)) - partC;
36};
37/**
38 * Find the `t` value for a cubic bezier using Cardano's formula
39 */
40const solveCubicBezier = (p0, p1, p2, p3, refPoint) => {
41 p0 -= refPoint;
42 p1 -= refPoint;
43 p2 -= refPoint;
44 p3 -= refPoint;
45 const roots = solveCubicEquation(p3 - 3 * p2 + 3 * p1 - p0, 3 * p2 - 6 * p1 + 3 * p0, 3 * p1 - 3 * p0, p0);
46 return roots.filter(root => root >= 0 && root <= 1);
47};
48const solveQuadraticEquation = (a, b, c) => {
49 const discriminant = b * b - 4 * a * c;
50 if (discriminant < 0) {
51 return [];
52 }
53 else {
54 return [
55 (-b + Math.sqrt(discriminant)) / (2 * a),
56 (-b - Math.sqrt(discriminant)) / (2 * a)
57 ];
58 }
59};
60const solveCubicEquation = (a, b, c, d) => {
61 if (a === 0) {
62 return solveQuadraticEquation(b, c, d);
63 }
64 b /= a;
65 c /= a;
66 d /= a;
67 const p = (3 * c - b * b) / 3;
68 const q = (2 * b * b * b - 9 * b * c + 27 * d) / 27;
69 if (p === 0) {
70 return [Math.pow(-q, 1 / 3)];
71 }
72 else if (q === 0) {
73 return [Math.sqrt(-p), -Math.sqrt(-p)];
74 }
75 const discriminant = Math.pow(q / 2, 2) + Math.pow(p / 3, 3);
76 if (discriminant === 0) {
77 return [Math.pow(q / 2, 1 / 2) - b / 3];
78 }
79 else if (discriminant > 0) {
80 return [Math.pow(-(q / 2) + Math.sqrt(discriminant), 1 / 3) - Math.pow((q / 2) + Math.sqrt(discriminant), 1 / 3) - b / 3];
81 }
82 const r = Math.sqrt(Math.pow(-(p / 3), 3));
83 const phi = Math.acos(-(q / (2 * Math.sqrt(Math.pow(-(p / 3), 3)))));
84 const s = 2 * Math.pow(r, 1 / 3);
85 return [
86 s * Math.cos(phi / 3) - b / 3,
87 s * Math.cos((phi + 2 * Math.PI) / 3) - b / 3,
88 s * Math.cos((phi + 4 * Math.PI) / 3) - b / 3
89 ];
90};
91
92export { getTimeGivenProgression as g };