1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | export var NEWTON_ITERATIONS = 4;
|
8 | export var NEWTON_MIN_SLOPE = 0.001;
|
9 | export var SUBDIVISION_PRECISION = 0.0000001;
|
10 | export var SUBDIVISION_MAX_ITERATIONS = 10;
|
11 | export var kSplineTableSize = 11;
|
12 | export var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
|
13 | export var float32ArraySupported = typeof Float32Array === 'function';
|
14 | export var A = function A(aA1, aA2) {
|
15 | return 1.0 - 3.0 * aA2 + 3.0 * aA1;
|
16 | };
|
17 | export var B = function B(aA1, aA2) {
|
18 | return 3.0 * aA2 - 6.0 * aA1;
|
19 | };
|
20 | export var C = function C(aA1) {
|
21 | return 3.0 * aA1;
|
22 | };
|
23 |
|
24 | export var calcBezier = function calcBezier(aT, aA1, aA2) {
|
25 | return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
|
26 | };
|
27 |
|
28 | export var getSlope = function getSlope(aT, aA1, aA2) {
|
29 | return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
|
30 | };
|
31 | export var binarySubdivide = function binarySubdivide(aX, aA, aB, mX1, mX2) {
|
32 | var currentX,
|
33 | currentT,
|
34 | i = 0;
|
35 |
|
36 | do {
|
37 | currentT = aA + (aB - aA) / 2.0;
|
38 | currentX = calcBezier(currentT, mX1, mX2) - aX;
|
39 | if (currentX > 0.0) aB = currentT;else aA = currentT;
|
40 | } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
|
41 |
|
42 | return currentT;
|
43 | };
|
44 | export var newtonRaphsonIterate = function newtonRaphsonIterate(aX, aGuessT, mX1, mX2) {
|
45 | for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
|
46 | var currentSlope = getSlope(aGuessT, mX1, mX2);
|
47 | if (currentSlope === 0.0) return aGuessT;
|
48 | var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
|
49 | aGuessT -= currentX / currentSlope;
|
50 | }
|
51 |
|
52 | return aGuessT;
|
53 | };
|
54 | export var bezier = function bezier(mX1, mY1, mX2, mY2) {
|
55 | if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) throw new Error('bezier x values must be in [0, 1] range');
|
56 | if (mX1 === mY1 && mX2 === mY2) return function (t) {
|
57 | return t;
|
58 | };
|
59 |
|
60 | var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
|
61 |
|
62 | for (var i = 0; i < kSplineTableSize; ++i) {
|
63 | sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
|
64 | }
|
65 |
|
66 | var getTForX = function getTForX(aX) {
|
67 | var intervalStart = 0.0;
|
68 | var currentSample = 1;
|
69 | var lastSample = kSplineTableSize - 1;
|
70 |
|
71 | for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
|
72 | intervalStart += kSampleStepSize;
|
73 | }
|
74 |
|
75 | --currentSample;
|
76 |
|
77 | var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]);
|
78 | var guessForT = intervalStart + dist * kSampleStepSize;
|
79 | var initialSlope = getSlope(guessForT, mX1, mX2);
|
80 | if (initialSlope >= NEWTON_MIN_SLOPE) return newtonRaphsonIterate(aX, guessForT, mX1, mX2);else if (initialSlope === 0.0) return guessForT;else {
|
81 | return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
|
82 | }
|
83 | };
|
84 |
|
85 | return function (t) {
|
86 |
|
87 | if (t === 0 || t === 1) return t;
|
88 | return calcBezier(getTForX(t), mY1, mY2);
|
89 | };
|
90 | }; |
\ | No newline at end of file |