Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/plugins/default-human-emulator/Bezier.ts
1030 views
1
import IPoint from '@secret-agent/interfaces/IPoint';
2
import curveLength from './curveLength';
3
4
// class extract from https://github.com/Pomax/bezierjs
5
6
export default class Bezier {
7
private readonly points: IPoint[];
8
private readonly derivativePoints: IPoint[][];
9
constructor(...points: IPoint[]) {
10
this.points = points;
11
this.derivativePoints = [];
12
13
let prev = points;
14
for (let i = points.length; i > 1; i -= 1) {
15
const list: IPoint[] = [];
16
const c = i - 1;
17
18
for (let j = 0; j < c; j += 1) {
19
list.push({
20
x: c * (prev[j + 1].x - prev[j].x),
21
y: c * (prev[j + 1].y - prev[j].y),
22
});
23
}
24
25
this.derivativePoints.push(list);
26
prev = list;
27
}
28
}
29
30
toString(): string {
31
const points = this.points.map(p => `${p.x}/${p.y}`).join(', ');
32
return `[${points}]`;
33
}
34
35
length() {
36
return curveLength(t => Bezier.compute(t, this.derivativePoints[0]));
37
}
38
39
getLookupTable(steps = 100): IPoint[] {
40
if (Number.isNaN(steps)) steps = 100;
41
// We want a range from 0 to 1 inclusive, so
42
// we decrement and then use <= rather than <:
43
steps -= 1;
44
45
const table: IPointWithT[] = [];
46
for (let i = 0; i < steps; i += 1) {
47
const t = i / (steps - 1);
48
const p = Bezier.compute(t, this.points);
49
p.t = t;
50
51
table.push(p);
52
}
53
54
return table;
55
}
56
57
static compute(t: number, points: IPointWithT[]): IPointWithT {
58
// shortcuts
59
if (t === 0) {
60
points[0].t = 0;
61
return points[0];
62
}
63
64
const order = points.length - 1;
65
66
if (t === 1) {
67
points[order].t = 1;
68
return points[order];
69
}
70
71
const mt = 1 - t;
72
73
if (order === 0) {
74
points[0].t = t;
75
return points[0];
76
} // linear?
77
78
if (order === 1) {
79
return {
80
x: mt * points[0].x + t * points[1].x,
81
y: mt * points[0].y + t * points[1].y,
82
t,
83
};
84
} // quadratic/cubic curve?
85
86
if (order < 4) {
87
const mt2 = mt * mt;
88
const t2 = t * t;
89
let a: number;
90
let b: number;
91
let c: number;
92
let d = 0;
93
94
if (order === 2) {
95
points = [
96
points[0],
97
points[1],
98
points[2],
99
{
100
x: 0,
101
y: 0,
102
},
103
];
104
a = mt2;
105
b = mt * t * 2;
106
c = t2;
107
} else if (order === 3) {
108
a = mt2 * mt;
109
b = mt2 * t * 3;
110
c = mt * t2 * 3;
111
d = t * t2;
112
}
113
114
return {
115
x: a * points[0].x + b * points[1].x + c * points[2].x + d * points[3].x,
116
y: a * points[0].y + b * points[1].y + c * points[2].y + d * points[3].y,
117
t,
118
};
119
} // higher order curves: use de Casteljau's computation
120
121
// copy list
122
const dCpts = points.map(x => {
123
return { ...x };
124
});
125
126
while (dCpts.length > 1) {
127
for (let i = 0; i < dCpts.length - 1; i += 1) {
128
dCpts[i] = {
129
x: dCpts[i].x + (dCpts[i + 1].x - dCpts[i].x) * t,
130
y: dCpts[i].y + (dCpts[i + 1].y - dCpts[i].y) * t,
131
};
132
}
133
134
dCpts.splice(dCpts.length - 1, 1);
135
}
136
137
dCpts[0].t = t;
138
return dCpts[0];
139
}
140
}
141
142
interface IPointWithT extends IPoint {
143
t?: number;
144
}
145
146