1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", { value: true });
|
4 | exports.bezier = void 0;
|
5 | const NEWTON_ITERATIONS = 4;
|
6 | const NEWTON_MIN_SLOPE = 0.001;
|
7 | const SUBDIVISION_PRECISION = 0.0000001;
|
8 | const SUBDIVISION_MAX_ITERATIONS = 10;
|
9 | const kSplineTableSize = 11;
|
10 | const kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
|
11 | const float32ArraySupported = typeof Float32Array === 'function';
|
12 | function a(aA1, aA2) {
|
13 | return 1.0 - 3.0 * aA2 + 3.0 * aA1;
|
14 | }
|
15 | function b(aA1, aA2) {
|
16 | return 3.0 * aA2 - 6.0 * aA1;
|
17 | }
|
18 | function c(aA1) {
|
19 | return 3.0 * aA1;
|
20 | }
|
21 |
|
22 | function calcBezier(aT, aA1, aA2) {
|
23 | return ((a(aA1, aA2) * aT + b(aA1, aA2)) * aT + c(aA1)) * aT;
|
24 | }
|
25 |
|
26 | function getSlope(aT, aA1, aA2) {
|
27 | return 3.0 * a(aA1, aA2) * aT * aT + 2.0 * b(aA1, aA2) * aT + c(aA1);
|
28 | }
|
29 | function binarySubdivide({ aX, _aA, _aB, mX1, mX2, }) {
|
30 | let currentX;
|
31 | let currentT;
|
32 | let i = 0;
|
33 | let aA = _aA;
|
34 | let aB = _aB;
|
35 | do {
|
36 | currentT = aA + (aB - aA) / 2.0;
|
37 | currentX = calcBezier(currentT, mX1, mX2) - aX;
|
38 | if (currentX > 0.0) {
|
39 | aB = currentT;
|
40 | }
|
41 | else {
|
42 | aA = currentT;
|
43 | }
|
44 | } while (Math.abs(currentX) > SUBDIVISION_PRECISION &&
|
45 | ++i < SUBDIVISION_MAX_ITERATIONS);
|
46 | return currentT;
|
47 | }
|
48 | function newtonRaphsonIterate(aX, _aGuessT, mX1, mX2) {
|
49 | let aGuessT = _aGuessT;
|
50 | for (let i = 0; i < NEWTON_ITERATIONS; ++i) {
|
51 | const currentSlope = getSlope(aGuessT, mX1, mX2);
|
52 | if (currentSlope === 0.0) {
|
53 | return aGuessT;
|
54 | }
|
55 | const currentX = calcBezier(aGuessT, mX1, mX2) - aX;
|
56 | aGuessT -= currentX / currentSlope;
|
57 | }
|
58 | return aGuessT;
|
59 | }
|
60 | function bezier(mX1, mY1, mX2, mY2) {
|
61 | if (!(mX1 >= 0 && mX1 <= 1 && mX2 >= 0 && mX2 <= 1)) {
|
62 | throw new Error('bezier x values must be in [0, 1] range');
|
63 | }
|
64 |
|
65 | const sampleValues = float32ArraySupported
|
66 | ? new Float32Array(kSplineTableSize)
|
67 | : new Array(kSplineTableSize);
|
68 | if (mX1 !== mY1 || mX2 !== mY2) {
|
69 | for (let i = 0; i < kSplineTableSize; ++i) {
|
70 | sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
|
71 | }
|
72 | }
|
73 | function getTForX(aX) {
|
74 | let intervalStart = 0.0;
|
75 | let currentSample = 1;
|
76 | const lastSample = kSplineTableSize - 1;
|
77 | for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
|
78 | intervalStart += kSampleStepSize;
|
79 | }
|
80 | --currentSample;
|
81 |
|
82 | const dist = (aX - sampleValues[currentSample]) /
|
83 | (sampleValues[currentSample + 1] - sampleValues[currentSample]);
|
84 | const guessForT = intervalStart + dist * kSampleStepSize;
|
85 | const initialSlope = getSlope(guessForT, mX1, mX2);
|
86 | if (initialSlope >= NEWTON_MIN_SLOPE) {
|
87 | return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
|
88 | }
|
89 | if (initialSlope === 0.0) {
|
90 | return guessForT;
|
91 | }
|
92 | return binarySubdivide({
|
93 | aX,
|
94 | _aA: intervalStart,
|
95 | _aB: intervalStart + kSampleStepSize,
|
96 | mX1,
|
97 | mX2,
|
98 | });
|
99 | }
|
100 | return function (x) {
|
101 | if (mX1 === mY1 && mX2 === mY2) {
|
102 | return x;
|
103 | }
|
104 |
|
105 | if (x === 0) {
|
106 | return 0;
|
107 | }
|
108 | if (x === 1) {
|
109 | return 1;
|
110 | }
|
111 | return calcBezier(getTForX(x), mY1, mY2);
|
112 | };
|
113 | }
|
114 | exports.bezier = bezier;
|