glmaths 0.0.1

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.
@@ -0,0 +1,617 @@
1
+ import { describe, it } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import glmaths, { Vec2, vec2, Vec3, Mat2, Mat2x3, Mat3, Mat4 } from '../dist/esm/glmaths'
4
+
5
+ function closeTo(actual: number, expected: number, numDigits = 5) {
6
+ const pass = Math.abs(actual - expected) < Math.pow(10, -numDigits) / 2
7
+ assert.ok(pass, `expected ${actual} to be close to ${expected}`)
8
+ }
9
+
10
+ const EPSILON = glmaths.EPSILON
11
+
12
+ describe('Vec2', () => {
13
+ describe('constructor', () => {
14
+ it('creates a zero vector by default', () => {
15
+ const v = vec2()
16
+ assert.strictEqual(v[0], 0)
17
+ assert.strictEqual(v[1], 0)
18
+ })
19
+ it('creates a vector with given values', () => {
20
+ const v = vec2(3, 4)
21
+ assert.strictEqual(v[0], 3)
22
+ assert.strictEqual(v[1], 4)
23
+ })
24
+ it('extends Float32Array with length 2', () => {
25
+ const v = vec2()
26
+ assert.ok(v instanceof Float32Array)
27
+ assert.strictEqual(v.length, 2)
28
+ })
29
+ })
30
+
31
+ describe('x/y getters and setters', () => {
32
+ it('gets x and y', () => {
33
+ const v = vec2(5, 6)
34
+ assert.strictEqual(v.x, 5)
35
+ assert.strictEqual(v.y, 6)
36
+ })
37
+ it('sets x and y', () => {
38
+ const v = vec2()
39
+ v.x = 10
40
+ v.y = 20
41
+ assert.strictEqual(v[0], 10)
42
+ assert.strictEqual(v[1], 20)
43
+ })
44
+ })
45
+
46
+ describe('static constants', () => {
47
+ it('ZERO returns (0,0)', () => {
48
+ const v = Vec2.ZERO
49
+ assert.strictEqual(v[0], 0)
50
+ assert.strictEqual(v[1], 0)
51
+ })
52
+ it('ONE returns (1,1)', () => {
53
+ const v = Vec2.ONE
54
+ assert.strictEqual(v[0], 1)
55
+ assert.strictEqual(v[1], 1)
56
+ })
57
+ })
58
+
59
+ describe('plus / add', () => {
60
+ it('adds two vectors in-place', () => {
61
+ const a = vec2(1, 2)
62
+ const b = vec2(3, 4)
63
+ const result = a.plus(b)
64
+ assert.strictEqual(result[0], 4)
65
+ assert.strictEqual(result[1], 6)
66
+ })
67
+ it('adds a scalar to both components', () => {
68
+ const a = vec2(1, 2)
69
+ const r = a.plus(5)
70
+ assert.strictEqual(r[0], 6)
71
+ assert.strictEqual(r[1], 7)
72
+ })
73
+ it('writes to out parameter without modifying this', () => {
74
+ const a = vec2(1, 2)
75
+ const b = vec2(3, 4)
76
+ const out = vec2()
77
+ const result = a.plus(b, out)
78
+ assert.strictEqual(result, out)
79
+ assert.strictEqual(out[0], 4)
80
+ assert.strictEqual(out[1], 6)
81
+ assert.strictEqual(a[0], 1)
82
+ assert.strictEqual(a[1], 2)
83
+ })
84
+ it('add alias works', () => {
85
+ const a = vec2(1, 2)
86
+ const b = vec2(3, 4)
87
+ const out = vec2()
88
+ a.add(b, out)
89
+ assert.strictEqual(out[0], 4)
90
+ assert.strictEqual(out[1], 6)
91
+ })
92
+ })
93
+
94
+ describe('minus / sub / subtract', () => {
95
+ it('subtracts two vectors', () => {
96
+ const a = vec2(5, 7)
97
+ const b = vec2(2, 3)
98
+ const out = vec2()
99
+ a.minus(b, out)
100
+ assert.strictEqual(out[0], 3)
101
+ assert.strictEqual(out[1], 4)
102
+ })
103
+ it('subtracts a scalar', () => {
104
+ const a = vec2(5, 7)
105
+ const r = a.minus(2)
106
+ assert.strictEqual(r[0], 3)
107
+ assert.strictEqual(r[1], 5)
108
+ })
109
+ })
110
+
111
+ describe('mult / mul / multiply / times', () => {
112
+ it('multiplies component-wise', () => {
113
+ const a = vec2(2, 3)
114
+ const b = vec2(4, 5)
115
+ const out = vec2()
116
+ a.mult(b, out)
117
+ assert.strictEqual(out[0], 8)
118
+ assert.strictEqual(out[1], 15)
119
+ })
120
+ it('multiplies by scalar', () => {
121
+ const a = vec2(2, 3)
122
+ const r = a.mult(3)
123
+ assert.strictEqual(r[0], 6)
124
+ assert.strictEqual(r[1], 9)
125
+ })
126
+ })
127
+
128
+ describe('div / divide', () => {
129
+ it('divides component-wise', () => {
130
+ const a = vec2(10, 20)
131
+ const b = vec2(2, 5)
132
+ const out = vec2()
133
+ a.div(b, out)
134
+ assert.strictEqual(out[0], 5)
135
+ assert.strictEqual(out[1], 4)
136
+ })
137
+ it('divides by scalar', () => {
138
+ const a = vec2(10, 20)
139
+ const r = a.div(2)
140
+ assert.strictEqual(r[0], 5)
141
+ assert.strictEqual(r[1], 10)
142
+ })
143
+ })
144
+
145
+ describe('invDiv', () => {
146
+ it('divides scalar by vector components', () => {
147
+ const a = vec2(2, 4)
148
+ const out = vec2()
149
+ a.invDiv(8, out)
150
+ assert.strictEqual(out[0], 4)
151
+ assert.strictEqual(out[1], 2)
152
+ })
153
+ })
154
+
155
+ describe('scale', () => {
156
+ it('scales by a number', () => {
157
+ const a = vec2(2, 3)
158
+ const r = a.scale(3)
159
+ assert.strictEqual(r[0], 6)
160
+ assert.strictEqual(r[1], 9)
161
+ })
162
+ it('scales by a vector', () => {
163
+ const a = vec2(2, 3)
164
+ const r = a.scale(vec2(4, 5))
165
+ assert.strictEqual(r[0], 8)
166
+ assert.strictEqual(r[1], 15)
167
+ })
168
+ })
169
+
170
+ describe('negate', () => {
171
+ it('negates components', () => {
172
+ const a = vec2(3, -4)
173
+ const r = a.negate()
174
+ assert.strictEqual(r[0], -3)
175
+ assert.strictEqual(r[1], 4)
176
+ })
177
+ })
178
+
179
+ describe('normalize', () => {
180
+ it('normalizes a vector to unit length', () => {
181
+ const a = vec2(3, 4)
182
+ const r = a.normalize()
183
+ closeTo(r[0], 0.6)
184
+ closeTo(r[1], 0.8)
185
+ closeTo(r.len(), 1)
186
+ })
187
+ it('handles zero vector', () => {
188
+ const a = vec2(0, 0)
189
+ a.normalize()
190
+ assert.strictEqual(a[0], 0)
191
+ assert.strictEqual(a[1], 0)
192
+ })
193
+ })
194
+
195
+ describe('len / squaredLength', () => {
196
+ it('calculates length', () => {
197
+ const a = vec2(3, 4)
198
+ closeTo(a.len(), 5)
199
+ })
200
+ it('calculates squared length', () => {
201
+ const a = vec2(3, 4)
202
+ assert.strictEqual(a.squaredLength(), 25)
203
+ })
204
+ it('sqrLen alias works', () => {
205
+ const a = vec2(3, 4)
206
+ assert.strictEqual(a.sqrLen(), 25)
207
+ })
208
+ })
209
+
210
+ describe('floor / round / ceil', () => {
211
+ it('floors components', () => {
212
+ const a = vec2(1.7, 2.3)
213
+ const r = a.floor()
214
+ assert.strictEqual(r[0], 1)
215
+ assert.strictEqual(r[1], 2)
216
+ })
217
+ it('rounds components', () => {
218
+ const a = vec2(1.4, 2.6)
219
+ const r = a.round()
220
+ assert.strictEqual(r[0], 1)
221
+ assert.strictEqual(r[1], 3)
222
+ })
223
+ it('ceils components', () => {
224
+ const a = vec2(1.1, 2.9)
225
+ const r = a.ceil()
226
+ assert.strictEqual(r[0], 2)
227
+ assert.strictEqual(r[1], 3)
228
+ })
229
+ })
230
+
231
+ describe('equals / exactEquals', () => {
232
+ it('returns true for equal vectors', () => {
233
+ const a = vec2(1, 2)
234
+ const b = vec2(1, 2)
235
+ assert.strictEqual(a.exactEquals(b), true)
236
+ assert.strictEqual(a.equals(b), true)
237
+ })
238
+ it('returns false for different vectors', () => {
239
+ const a = vec2(1, 2)
240
+ const b = vec2(1, 3)
241
+ assert.strictEqual(a.exactEquals(b), false)
242
+ assert.strictEqual(a.equals(b), false)
243
+ })
244
+ it('equals allows epsilon difference', () => {
245
+ const a = vec2(1, 2)
246
+ const b = vec2(1 + EPSILON * 0.5, 2)
247
+ assert.strictEqual(a.exactEquals(b), false)
248
+ assert.strictEqual(a.equals(b), true)
249
+ })
250
+ })
251
+
252
+ describe('toString', () => {
253
+ it('returns string representation', () => {
254
+ const a = vec2(1, 2)
255
+ assert.strictEqual(a.toString(), 'vec2(1, 2)')
256
+ })
257
+ })
258
+
259
+ describe('clone', () => {
260
+ it('creates an independent copy', () => {
261
+ const a = vec2(1, 2)
262
+ const b = a.clone()
263
+ assert.strictEqual(b[0], 1)
264
+ assert.strictEqual(b[1], 2)
265
+ b[0] = 99
266
+ assert.strictEqual(a[0], 1)
267
+ })
268
+ })
269
+
270
+ describe('static methods', () => {
271
+ it('dot product', () => {
272
+ const a = vec2(1, 2)
273
+ const b = vec2(3, 4)
274
+ assert.strictEqual(Vec2.dot(a, b), 11)
275
+ })
276
+ it('cross product returns Vec3 with z = determinant', () => {
277
+ const a = vec2(1, 0)
278
+ const b = vec2(0, 1)
279
+ const out = Vec2.cross(a, b)
280
+ assert.strictEqual(out[0], 0)
281
+ assert.strictEqual(out[1], 0)
282
+ assert.strictEqual(out[2], 1)
283
+ })
284
+ it('cross product of parallel vectors is zero', () => {
285
+ const a = vec2(2, 0)
286
+ const b = vec2(5, 0)
287
+ const out = Vec2.cross(a, b)
288
+ assert.strictEqual(out[2], 0)
289
+ })
290
+ it('distance', () => {
291
+ const a = vec2(0, 0)
292
+ const b = vec2(3, 4)
293
+ closeTo(Vec2.distance(a, b), 5)
294
+ })
295
+ it('squaredDistance', () => {
296
+ const a = vec2(0, 0)
297
+ const b = vec2(3, 4)
298
+ assert.strictEqual(Vec2.squaredDistance(a, b), 25)
299
+ })
300
+ it('lerp', () => {
301
+ const a = vec2(0, 0)
302
+ const b = vec2(10, 20)
303
+ const out = Vec2.lerp(a, b, 0.5)
304
+ assert.strictEqual(out[0], 5)
305
+ assert.strictEqual(out[1], 10)
306
+ })
307
+ it('lerp at t=0 returns a', () => {
308
+ const a = vec2(1, 2)
309
+ const b = vec2(10, 20)
310
+ const out = Vec2.lerp(a, b, 0)
311
+ assert.strictEqual(out[0], 1)
312
+ assert.strictEqual(out[1], 2)
313
+ })
314
+ it('lerp at t=1 returns b', () => {
315
+ const a = vec2(1, 2)
316
+ const b = vec2(10, 20)
317
+ const out = Vec2.lerp(a, b, 1)
318
+ assert.strictEqual(out[0], 10)
319
+ assert.strictEqual(out[1], 20)
320
+ })
321
+ it('max', () => {
322
+ const a = vec2(1, 5)
323
+ const b = vec2(3, 2)
324
+ const out = Vec2.max(a, b)
325
+ assert.strictEqual(out[0], 3)
326
+ assert.strictEqual(out[1], 5)
327
+ })
328
+ it('min', () => {
329
+ const a = vec2(1, 5)
330
+ const b = vec2(3, 2)
331
+ const out = Vec2.min(a, b)
332
+ assert.strictEqual(out[0], 1)
333
+ assert.strictEqual(out[1], 2)
334
+ })
335
+ it('angle between two vectors', () => {
336
+ const a = vec2(1, 0)
337
+ const b = vec2(0, 1)
338
+ closeTo(Vec2.angle(a, b), Math.PI / 2)
339
+ })
340
+ it('angle between parallel vectors is 0', () => {
341
+ const a = vec2(1, 0)
342
+ const b = vec2(5, 0)
343
+ closeTo(Vec2.angle(a, b), 0)
344
+ })
345
+ it('rotate by 90 degrees', () => {
346
+ const v = vec2(1, 0)
347
+ const out = vec2()
348
+ v.rotate(Math.PI / 2, Vec2.ZERO, out)
349
+ closeTo(out[0], 0)
350
+ closeTo(out[1], 1)
351
+ })
352
+ it('rotate with origin', () => {
353
+ const v = vec2(2, 0)
354
+ const origin = vec2(1, 0)
355
+ const out = vec2()
356
+ v.rotate(Math.PI / 2, origin, out)
357
+ closeTo(out[0], 1)
358
+ closeTo(out[1], 1)
359
+ })
360
+ it('rotate by 180 degrees', () => {
361
+ const v = vec2(1, 0)
362
+ const out = vec2()
363
+ v.rotate(Math.PI, Vec2.ZERO, out)
364
+ closeTo(out[0], -1)
365
+ closeTo(out[1], 0)
366
+ })
367
+ })
368
+
369
+ describe('factory function', () => {
370
+ it('creates Vec2 with vec2()', () => {
371
+ const v = vec2(3, 4)
372
+ assert.ok(v instanceof Vec2)
373
+ assert.strictEqual(v[0], 3)
374
+ assert.strictEqual(v[1], 4)
375
+ })
376
+ })
377
+
378
+ describe('unaryMinus / unaryPlus', () => {
379
+ it('unaryMinus negates', () => {
380
+ const a = vec2(3, -4)
381
+ const out = vec2()
382
+ a.unaryMinus(out)
383
+ assert.strictEqual(out[0], -3)
384
+ assert.strictEqual(out[1], 4)
385
+ })
386
+ it('unaryPlus returns this', () => {
387
+ const a = vec2(3, -4)
388
+ const r = a.unaryPlus()
389
+ assert.strictEqual(r[0], 3)
390
+ assert.strictEqual(r[1], -4)
391
+ })
392
+ })
393
+
394
+ describe('scaleAndAdd', () => {
395
+ it('adds scaled vector', () => {
396
+ const a = vec2(1, 2)
397
+ const b = vec2(3, 4)
398
+ const out = vec2()
399
+ a.scaleAndAdd(b, 2, out)
400
+ closeTo(out[0], 7)
401
+ closeTo(out[1], 10)
402
+ })
403
+ it('static version', () => {
404
+ const out = Vec2.scaleAndAdd(vec2(1, 2), vec2(3, 4), 0.5)
405
+ closeTo(out[0], 2.5)
406
+ closeTo(out[1], 4)
407
+ })
408
+ })
409
+
410
+ describe('abs', () => {
411
+ it('absolute value of components', () => {
412
+ const v = vec2(-3, -4)
413
+ const out = vec2()
414
+ v.abs(out)
415
+ assert.strictEqual(out[0], 3)
416
+ assert.strictEqual(out[1], 4)
417
+ })
418
+ })
419
+
420
+ describe('transformMat2', () => {
421
+ it('transforms by 2x2 matrix', () => {
422
+ const v = vec2(1, 0)
423
+ const m = new Mat2(0, 1, -1, 0) // 90 degree rotation
424
+ const out = vec2()
425
+ v.transformMat2(m, out)
426
+ closeTo(out[0], 0)
427
+ closeTo(out[1], 1)
428
+ })
429
+ })
430
+
431
+ describe('transformMat2x3', () => {
432
+ it('transforms by 2x3 affine matrix', () => {
433
+ const v = vec2(1, 0)
434
+ const m = Mat2x3.fromTranslation(vec2(10, 20))
435
+ const out = vec2()
436
+ v.transformMat2x3(m, out)
437
+ closeTo(out[0], 11)
438
+ closeTo(out[1], 20)
439
+ })
440
+ })
441
+
442
+ describe('transformMat3', () => {
443
+ it('transforms by 3x3 matrix', () => {
444
+ const v = vec2(1, 0)
445
+ const m = Mat3.fromTranslation(vec2(5, 10))
446
+ const out = vec2()
447
+ v.transformMat3(m, out)
448
+ closeTo(out[0], 6)
449
+ closeTo(out[1], 10)
450
+ })
451
+ })
452
+
453
+ describe('transformMat4', () => {
454
+ it('transforms by 4x4 matrix', () => {
455
+ const v = vec2(1, 2)
456
+ const m = Mat4.fromTranslation(new Vec3(10, 20, 0))
457
+ const out = vec2()
458
+ v.transformMat4(m, out)
459
+ closeTo(out[0], 11)
460
+ closeTo(out[1], 22)
461
+ })
462
+ })
463
+
464
+ describe('reflect', () => {
465
+ it('reflects a vector', () => {
466
+ const I = vec2(1, -1)
467
+ const N = vec2(0, 1)
468
+ const out = Vec2.reflect(I, N)
469
+ closeTo(out[0], 1)
470
+ closeTo(out[1], 1)
471
+ })
472
+ })
473
+
474
+ describe('GLSL functions', () => {
475
+ it('clamp', () => {
476
+ const v = vec2(-1, 5)
477
+ const out = vec2()
478
+ v.clamp(0, 1, out)
479
+ assert.strictEqual(out[0], 0)
480
+ assert.strictEqual(out[1], 1)
481
+ })
482
+ it('clamp with vectors', () => {
483
+ const v = vec2(-1, 5)
484
+ const out = Vec2.clamp(v, vec2(0, 0), vec2(2, 3))
485
+ assert.strictEqual(out[0], 0)
486
+ assert.strictEqual(out[1], 3)
487
+ })
488
+ it('mix', () => {
489
+ const a = vec2(0, 0)
490
+ const b = vec2(10, 20)
491
+ const out = vec2()
492
+ a.mix(b, 0.5, out)
493
+ closeTo(out[0], 5)
494
+ closeTo(out[1], 10)
495
+ })
496
+ it('static mix', () => {
497
+ const out = Vec2.mix(vec2(0, 0), vec2(10, 20), 0.25)
498
+ closeTo(out[0], 2.5)
499
+ closeTo(out[1], 5)
500
+ })
501
+ it('step', () => {
502
+ const v = vec2(0.3, 0.7)
503
+ const out = vec2()
504
+ v.step(0.5, out)
505
+ assert.strictEqual(out[0], 0)
506
+ assert.strictEqual(out[1], 1)
507
+ })
508
+ it('smoothstep', () => {
509
+ const v = vec2(0.5, 1.5)
510
+ const out = vec2()
511
+ v.smoothstep(0, 1, out)
512
+ closeTo(out[0], 0.5)
513
+ closeTo(out[1], 1)
514
+ })
515
+ it('fract', () => {
516
+ const v = vec2(1.7, 2.3)
517
+ const out = vec2()
518
+ v.fract(out)
519
+ closeTo(out[0], 0.7)
520
+ closeTo(out[1], 0.3)
521
+ })
522
+ it('sign', () => {
523
+ const v = vec2(-5, 3)
524
+ const out = vec2()
525
+ v.sign(out)
526
+ assert.strictEqual(out[0], -1)
527
+ assert.strictEqual(out[1], 1)
528
+ })
529
+ it('saturate', () => {
530
+ const v = vec2(-0.5, 1.5)
531
+ const out = vec2()
532
+ v.saturate(out)
533
+ assert.strictEqual(out[0], 0)
534
+ assert.strictEqual(out[1], 1)
535
+ })
536
+ })
537
+ describe('operators', () => {
538
+ it('+', () => {
539
+ const a = vec2(0, 1)
540
+ const b = vec2(2, 3)
541
+ const c = a + b
542
+ assert.strictEqual(c.x, 2)
543
+ assert.strictEqual(c.y, 4)
544
+ let d = vec2(4, 5)
545
+ d += vec2(8, 8)
546
+ assert.strictEqual(d.x, 12)
547
+ assert.strictEqual(d.y, 13)
548
+ })
549
+ it('-', () => {
550
+ const a = vec2(0, 1)
551
+ const b = vec2(2, 3)
552
+ let c = a - b
553
+ assert.strictEqual(c.x, -2)
554
+ assert.strictEqual(c.y, -2)
555
+ c -= vec2(10, 20)
556
+ assert.strictEqual(c.x, -12)
557
+ assert.strictEqual(c.y, -22)
558
+ })
559
+ it('*', () => {
560
+ const a = vec2(0, 1)
561
+ const b = vec2(2, 3)
562
+ let c = a * b
563
+ assert.strictEqual(c.x, 0)
564
+ assert.strictEqual(c.y, 3)
565
+ c *= vec2(10)
566
+ assert.strictEqual(c.x, 0)
567
+ assert.strictEqual(c.y, 30)
568
+ })
569
+ it('/', () => {
570
+ const a = vec2(0, 1)
571
+ const b = vec2(2, 3)
572
+ let c = a / b
573
+ assert.strictEqual(c.x, 0)
574
+ closeTo(c.y, 1 / 3)
575
+ c /= 1 / 3
576
+ assert.strictEqual(c.x, 0)
577
+ closeTo(c.y, 1)
578
+ })
579
+ it('%', () => {
580
+ const a = vec2(0, 3)
581
+ const b = vec2(2, 2)
582
+ const c = a % b
583
+ assert.strictEqual(c.x, 0)
584
+ assert.strictEqual(c.y, 1)
585
+ let d = vec2(2, 5)
586
+ d %= b
587
+ assert.strictEqual(d.x, 0)
588
+ assert.strictEqual(d.y, 1)
589
+ })
590
+ it('==', () => {
591
+ const a = vec2(0, 3)
592
+ assert.strictEqual(a == a, true)
593
+ assert.strictEqual(vec2(0, 3) == vec2(0, 3), true)
594
+ assert.strictEqual(vec2(0, 3.0000005) == vec2(0, 3), true)
595
+ assert.strictEqual(vec2(0, 30) == vec2(0, 3), false)
596
+
597
+ assert.strictEqual(a != a, false)
598
+ assert.strictEqual(vec2(0, 3) != vec2(0, 3), false)
599
+ assert.strictEqual(vec2(0, 3.0000005) != vec2(0, 3), false)
600
+ assert.strictEqual(vec2(0, 30) != vec2(0, 3), true)
601
+ })
602
+ it('===', () => {
603
+ const a = vec2(0, 3)
604
+ assert.strictEqual(a === a, true)
605
+ assert.strictEqual(vec2(0, 3) === vec2(0, 3), true)
606
+ assert.strictEqual(vec2(3, 0) === vec2(0, 3), false)
607
+ assert.strictEqual(vec2(0, 3.0000005) === vec2(0, 3), false)
608
+ assert.strictEqual(vec2(0, 30) === vec2(0, 3), false)
609
+
610
+ assert.strictEqual(a !== a, false)
611
+ assert.strictEqual(vec2(0, 3) !== vec2(0, 3), false)
612
+ assert.strictEqual(vec2(3, 0) !== vec2(0, 3), true)
613
+ assert.strictEqual(vec2(0, 3.0000005) !== vec2(0, 3), true)
614
+ assert.strictEqual(vec2(0, 30) !== vec2(0, 3), true)
615
+ })
616
+ })
617
+ })