@benev/math 0.0.0-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/license +23 -0
- package/package.json +41 -0
- package/readme.md +3 -0
- package/s/concepts/angles.ts +74 -0
- package/s/concepts/circular.ts +72 -0
- package/s/concepts/noise.ts +23 -0
- package/s/concepts/quat.ts +131 -0
- package/s/concepts/randy.ts +116 -0
- package/s/concepts/scalar.ts +224 -0
- package/s/concepts/spline.ts +88 -0
- package/s/concepts/vec2.ts +392 -0
- package/s/concepts/vec3.ts +439 -0
- package/s/concepts/vec4.ts +74 -0
- package/s/index.ts +12 -0
- package/x/concepts/angles.d.ts +29 -0
- package/x/concepts/angles.js +62 -0
- package/x/concepts/angles.js.map +1 -0
- package/x/concepts/circular.d.ts +17 -0
- package/x/concepts/circular.js +70 -0
- package/x/concepts/circular.js.map +1 -0
- package/x/concepts/noise.d.ts +9 -0
- package/x/concepts/noise.js +20 -0
- package/x/concepts/noise.js.map +1 -0
- package/x/concepts/quat.d.ts +35 -0
- package/x/concepts/quat.js +110 -0
- package/x/concepts/quat.js.map +1 -0
- package/x/concepts/randy.d.ts +39 -0
- package/x/concepts/randy.js +95 -0
- package/x/concepts/randy.js.map +1 -0
- package/x/concepts/scalar.d.ts +51 -0
- package/x/concepts/scalar.js +219 -0
- package/x/concepts/scalar.js.map +1 -0
- package/x/concepts/spline.d.ts +9 -0
- package/x/concepts/spline.js +59 -0
- package/x/concepts/spline.js.map +1 -0
- package/x/concepts/vec2.d.ts +114 -0
- package/x/concepts/vec2.js +314 -0
- package/x/concepts/vec2.js.map +1 -0
- package/x/concepts/vec3.d.ts +117 -0
- package/x/concepts/vec3.js +357 -0
- package/x/concepts/vec3.js.map +1 -0
- package/x/concepts/vec4.d.ts +21 -0
- package/x/concepts/vec4.js +62 -0
- package/x/concepts/vec4.js.map +1 -0
- package/x/importmap.json +9 -0
- package/x/index.d.ts +10 -0
- package/x/index.js +11 -0
- package/x/index.js.map +1 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
|
|
2
|
+
import {Scalar} from "./scalar.js"
|
|
3
|
+
import {Vec2Array} from "./vec2.js"
|
|
4
|
+
|
|
5
|
+
/** resolve a number within a linear spline. */
|
|
6
|
+
export function linear(x: number, points: Vec2Array[]): number {
|
|
7
|
+
if (points.length < 2)
|
|
8
|
+
throw new Error("need at least two points, come on")
|
|
9
|
+
|
|
10
|
+
const [first] = points.at(0)!
|
|
11
|
+
const [last] = points.at(-1)!
|
|
12
|
+
|
|
13
|
+
x = Scalar.clamp(x, first, last)
|
|
14
|
+
|
|
15
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
16
|
+
const [x0, y0] = points[i]
|
|
17
|
+
const [x1, y1] = points[i + 1]
|
|
18
|
+
|
|
19
|
+
if (x >= x0 && x <= x1) {
|
|
20
|
+
const t = (x - x0) / (x1 - x0)
|
|
21
|
+
return y0 + t * (y1 - y0)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
throw new Error("x is out of bounds, what are you even doing")
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** resolve a number within a catmull-rom spline, that's all smooth-like. */
|
|
29
|
+
export function catmullRom(x: number, points: Vec2Array[]) {
|
|
30
|
+
if (points.length < 4)
|
|
31
|
+
throw new Error("need at least four points for this magic")
|
|
32
|
+
|
|
33
|
+
x = Scalar.clamp(x)
|
|
34
|
+
|
|
35
|
+
// find the segment where 'x' fits
|
|
36
|
+
for (let i = 1; i < points.length - 2; i++) {
|
|
37
|
+
const [x1, ] = points[i]
|
|
38
|
+
const [x2, ] = points[i + 1]
|
|
39
|
+
|
|
40
|
+
if (x >= x1 && x <= x2) {
|
|
41
|
+
const t = (x - x1) / (x2 - x1)
|
|
42
|
+
return helpers.catmullRom(t, points[i - 1], points[i], points[i + 1], points[i + 2])
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
throw new Error("x is out of bounds, try again")
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const ez = {
|
|
50
|
+
|
|
51
|
+
/** simple linear spline where the control points are equally-spaced based on their array indices (x is expected to be between 0 and 1). */
|
|
52
|
+
linear(x: number, points: number[]) {
|
|
53
|
+
if (points.length < 2)
|
|
54
|
+
throw new Error("need at least two points, come on")
|
|
55
|
+
|
|
56
|
+
const points2 = points.map(
|
|
57
|
+
(p, index): Vec2Array =>
|
|
58
|
+
[Scalar.clamp(index / (points.length - 1)), p]
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return linear(Scalar.clamp(x), points2)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
namespace helpers {
|
|
66
|
+
|
|
67
|
+
/** internal big-brain maths for the catmull-rom implementation */
|
|
68
|
+
export function catmullRom(
|
|
69
|
+
t: number,
|
|
70
|
+
[,p0]: Vec2Array,
|
|
71
|
+
[,p1]: Vec2Array,
|
|
72
|
+
[,p2]: Vec2Array,
|
|
73
|
+
[,p3]: Vec2Array,
|
|
74
|
+
) {
|
|
75
|
+
|
|
76
|
+
const t2 = t * t
|
|
77
|
+
const t3 = t2 * t
|
|
78
|
+
|
|
79
|
+
// coefficients for the cubic polynomial (Catmull-Rom)
|
|
80
|
+
const a = -0.5 * p0 + 1.5 * p1 - 1.5 * p2 + 0.5 * p3
|
|
81
|
+
const b = p0 - 2.5 * p1 + 2 * p2 - 0.5 * p3
|
|
82
|
+
const c = -0.5 * p0 + 0.5 * p2
|
|
83
|
+
const d = p1
|
|
84
|
+
|
|
85
|
+
return a * t3 + b * t2 + c * t + d
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
|
|
2
|
+
import {Scalar} from "./scalar.js"
|
|
3
|
+
|
|
4
|
+
export type Vec2Array = [number, number]
|
|
5
|
+
export type Xy = {x: number, y: number}
|
|
6
|
+
|
|
7
|
+
/** https://github.com/microsoft/TypeScript/issues/5863 */
|
|
8
|
+
type TsHack<T> = {new(...a: ConstructorParameters<typeof Vec2>): T}
|
|
9
|
+
|
|
10
|
+
export class Vec2 implements Xy {
|
|
11
|
+
constructor(
|
|
12
|
+
public x: number,
|
|
13
|
+
public y: number,
|
|
14
|
+
) {}
|
|
15
|
+
|
|
16
|
+
///////////////////////////////////////////////////////////////////////
|
|
17
|
+
|
|
18
|
+
static new<T extends Vec2>(this: TsHack<T>, x: number, y: number): T {
|
|
19
|
+
return new this(x, y)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static zero<T extends Vec2>(this: TsHack<T>) {
|
|
23
|
+
return new this(0, 0)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static all<T extends Vec2>(this: TsHack<T>, value: number) {
|
|
27
|
+
return new this(value, value)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static array<T extends Vec2>(this: TsHack<T>, v: Vec2Array) {
|
|
31
|
+
return new this(...v)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static import<T extends Vec2>(this: TsHack<T>, {x, y}: Xy) {
|
|
35
|
+
return new this(x, y)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static from(v: Vec2Array | Xy) {
|
|
39
|
+
return Array.isArray(v)
|
|
40
|
+
? this.array(v)
|
|
41
|
+
: this.import(v)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static magnitudeSquared(x: number, y: number) {
|
|
45
|
+
return (x * x) + (y * y)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static magnitude(x: number, y: number) {
|
|
49
|
+
return Math.sqrt(this.magnitudeSquared(x, y))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static average(...vectors: Xy[]) {
|
|
53
|
+
return this.zero()
|
|
54
|
+
.add(...vectors)
|
|
55
|
+
.divideBy(vectors.length)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static min(...vecs: Vec2[]) {
|
|
59
|
+
return new Vec2(
|
|
60
|
+
Math.min(...vecs.map(v => v.x)),
|
|
61
|
+
Math.min(...vecs.map(v => v.y)),
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static max(...vecs: Vec2[]) {
|
|
66
|
+
return new Vec2(
|
|
67
|
+
Math.max(...vecs.map(v => v.x)),
|
|
68
|
+
Math.max(...vecs.map(v => v.y)),
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
///////////////////////////////////////////////////////////////////////
|
|
73
|
+
|
|
74
|
+
clone() {
|
|
75
|
+
return new Vec2(this.x, this.y)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
array(): Vec2Array {
|
|
79
|
+
return [this.x, this.y]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
toString() {
|
|
83
|
+
return `(Vec2 x${this.x.toFixed(2)}, y${this.y.toFixed(2)})`
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** mutator */
|
|
87
|
+
set_(x: number, y: number) {
|
|
88
|
+
this.x = x
|
|
89
|
+
this.y = y
|
|
90
|
+
return this
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** mutator */
|
|
94
|
+
set({x, y}: Xy) {
|
|
95
|
+
this.x = x
|
|
96
|
+
this.y = y
|
|
97
|
+
return this
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
///////////////////////////////////////////////////////////////////////
|
|
101
|
+
|
|
102
|
+
magnitudeSquared() {
|
|
103
|
+
return Vec2.magnitudeSquared(this.x, this.y)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
magnitude() {
|
|
107
|
+
return Vec2.magnitude(this.x, this.y)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
rotation() {
|
|
111
|
+
return Math.atan2(this.y, this.x)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
equals_(x: number, y: number) {
|
|
115
|
+
return (
|
|
116
|
+
this.x === x &&
|
|
117
|
+
this.y === y
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
equals(...vecs: Xy[]) {
|
|
122
|
+
return vecs.every(({x, y}) => this.equals_(x, y))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
dot_(x: number, y: number) {
|
|
126
|
+
return (this.x * x) + (this.y * y)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
dot({x, y}: Xy) {
|
|
130
|
+
return this.dot_(x, y)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
distanceSquared_(x: number, y: number) {
|
|
134
|
+
x = this.x - x
|
|
135
|
+
y = this.y - y
|
|
136
|
+
return (x * x) + (y * y)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
distanceSquared({x, y}: Xy) {
|
|
140
|
+
return this.distanceSquared_(x, y)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
distance_(x: number, y: number) {
|
|
144
|
+
return Math.sqrt(this.distanceSquared_(x, y))
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
distance({x, y}: Xy) {
|
|
148
|
+
return this.distance_(x, y)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
angleBetween_(x: number, y: number) {
|
|
152
|
+
const dot = this.dot_(x, y)
|
|
153
|
+
const magnitudes = this.magnitude() * Vec2.magnitude(x, y)
|
|
154
|
+
return Math.acos(dot / magnitudes)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
angleBetween({x, y}: Xy) {
|
|
158
|
+
return this.angleBetween_(x, y)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
///////////////////////////////////////////////////////////////////////
|
|
162
|
+
|
|
163
|
+
/** mutator */
|
|
164
|
+
normalize() {
|
|
165
|
+
return this.divideBy(this.magnitude())
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** mutator */
|
|
169
|
+
half() {
|
|
170
|
+
return this.divideBy(2)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** mutator */
|
|
174
|
+
double() {
|
|
175
|
+
return this.multiplyBy(2)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** mutator */
|
|
179
|
+
abs() {
|
|
180
|
+
return this.map(x => Math.abs(x))
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** mutator */
|
|
184
|
+
rotate(radians: number) {
|
|
185
|
+
const {x, y} = this
|
|
186
|
+
this.x = (x * Math.cos(radians)) - (y * Math.sin(radians))
|
|
187
|
+
this.y = (x * Math.sin(radians)) + (y * Math.cos(radians))
|
|
188
|
+
return this
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** mutator */
|
|
192
|
+
perpendicular() {
|
|
193
|
+
const {x, y} = this
|
|
194
|
+
this.x = -y
|
|
195
|
+
this.y = x
|
|
196
|
+
return this
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** mutator */
|
|
200
|
+
clampMagnitude(max: number) {
|
|
201
|
+
const mag = this.magnitude()
|
|
202
|
+
if (mag > max)
|
|
203
|
+
this.normalize().multiplyBy(max)
|
|
204
|
+
return this
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** mutator */
|
|
208
|
+
floor() {
|
|
209
|
+
this.x = Math.floor(this.x)
|
|
210
|
+
this.y = Math.floor(this.y)
|
|
211
|
+
return this
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** mutator */
|
|
215
|
+
ceil() {
|
|
216
|
+
this.x = Math.ceil(this.x)
|
|
217
|
+
this.y = Math.ceil(this.y)
|
|
218
|
+
return this
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** mutator */
|
|
222
|
+
round() {
|
|
223
|
+
this.x = Math.round(this.x)
|
|
224
|
+
this.y = Math.round(this.y)
|
|
225
|
+
return this
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** mutator */
|
|
229
|
+
map(fn: (a: number, index: number) => number) {
|
|
230
|
+
this.x = fn(this.x, 0)
|
|
231
|
+
this.y = fn(this.y, 1)
|
|
232
|
+
return this
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** mutator */
|
|
236
|
+
clamp(min: number, max: number) {
|
|
237
|
+
const clamp = (val: number) => Math.max(min, Math.min(max, val))
|
|
238
|
+
return this.map(clamp)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** mutator */
|
|
242
|
+
negate() {
|
|
243
|
+
return this.map(a => a * -1)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** mutator */
|
|
247
|
+
addBy(delta: number) {
|
|
248
|
+
this.x += delta
|
|
249
|
+
this.y += delta
|
|
250
|
+
return this
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** mutator */
|
|
254
|
+
subtractBy(delta: number) {
|
|
255
|
+
this.x -= delta
|
|
256
|
+
this.y -= delta
|
|
257
|
+
return this
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** mutator */
|
|
261
|
+
multiplyBy(coefficient: number) {
|
|
262
|
+
this.x *= coefficient
|
|
263
|
+
this.y *= coefficient
|
|
264
|
+
return this
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** mutator */
|
|
268
|
+
divideBy(divisor: number) {
|
|
269
|
+
if (divisor === 0) return this
|
|
270
|
+
this.x /= divisor
|
|
271
|
+
this.y /= divisor
|
|
272
|
+
return this
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
///////////////////////////////////////////////////////////////////////
|
|
276
|
+
|
|
277
|
+
/** mutator */
|
|
278
|
+
add_(x: number, y: number) {
|
|
279
|
+
this.x += x
|
|
280
|
+
this.y += y
|
|
281
|
+
return this
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** mutator */
|
|
285
|
+
add(...vecs: Xy[]) {
|
|
286
|
+
for (const {x, y} of vecs) this.add_(x, y)
|
|
287
|
+
return this
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** mutator */
|
|
291
|
+
subtract_(x: number, y: number) {
|
|
292
|
+
this.x -= x
|
|
293
|
+
this.y -= y
|
|
294
|
+
return this
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** mutator */
|
|
298
|
+
subtract(...vecs: Xy[]) {
|
|
299
|
+
for (const {x, y} of vecs) this.subtract_(x, y)
|
|
300
|
+
return this
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** mutator */
|
|
304
|
+
multiply_(x: number, y: number) {
|
|
305
|
+
this.x *= x
|
|
306
|
+
this.y *= y
|
|
307
|
+
return this
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** mutator */
|
|
311
|
+
multiply(...vecs: Xy[]) {
|
|
312
|
+
for (const {x, y} of vecs) this.multiply_(x, y)
|
|
313
|
+
return this
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** mutator */
|
|
317
|
+
divide_(x: number, y: number) {
|
|
318
|
+
this.x /= x
|
|
319
|
+
this.y /= y
|
|
320
|
+
return this
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/** mutator */
|
|
324
|
+
divide(...vecs: Xy[]) {
|
|
325
|
+
for (const {x, y} of vecs) this.divide_(x, y)
|
|
326
|
+
return this
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** mutator */
|
|
330
|
+
lerp_(x: number, y: number, fraction: number) {
|
|
331
|
+
this.x += (x - this.x) * fraction
|
|
332
|
+
this.y += (y - this.y) * fraction
|
|
333
|
+
return this
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** mutator */
|
|
337
|
+
lerp({x, y}: Xy, fraction: number) {
|
|
338
|
+
return this.lerp_(x, y, fraction)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
approach_(x: number, y: number, speed: number, deltaTime: number, speedLimit?: number) {
|
|
342
|
+
this.x = Scalar.approach(this.x, x, speed, deltaTime, speedLimit)
|
|
343
|
+
this.y = Scalar.approach(this.y, y, speed, deltaTime, speedLimit)
|
|
344
|
+
return this
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
approach({x, y}: Xy, speed: number, deltaTime: number, speedLimit?: number) {
|
|
348
|
+
return this.approach_(x, y, speed, deltaTime, speedLimit)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/** mutator */
|
|
352
|
+
reflect_(x: number, y: number) {
|
|
353
|
+
const dot = 2 * this.dot_(x, y)
|
|
354
|
+
this.x -= dot * x
|
|
355
|
+
this.y -= dot * y
|
|
356
|
+
return this
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/** mutator */
|
|
360
|
+
reflect({x, y}: Xy) {
|
|
361
|
+
return this.reflect_(x, y)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/** mutator */
|
|
365
|
+
rotateAroundPoint_(x: number, y: number, radians: number) {
|
|
366
|
+
const dx = this.x - x
|
|
367
|
+
const dy = this.y - y
|
|
368
|
+
const cos = Math.cos(radians)
|
|
369
|
+
const sin = Math.sin(radians)
|
|
370
|
+
this.x = cos * dx - sin * dy + x
|
|
371
|
+
this.y = sin * dx + cos * dy + y
|
|
372
|
+
return this
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** mutator */
|
|
376
|
+
rotateAroundPoint({x, y}: Vec2, radians: number) {
|
|
377
|
+
return this.rotateAroundPoint_(x, y, radians)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** mutator */
|
|
381
|
+
smooth_(x: number, y: number, smoothing: number) {
|
|
382
|
+
this.x = Scalar.smooth(this.x, x, smoothing)
|
|
383
|
+
this.y = Scalar.smooth(this.y, y, smoothing)
|
|
384
|
+
return this
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** mutator */
|
|
388
|
+
smooth({x, y}: Xy, smoothing: number) {
|
|
389
|
+
return this.smooth_(x, y, smoothing)
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|