@flighthq/geometry 0.3.0 → 0.3.1-next.1041.35b950c
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/dist/aabb.d.ts +1 -0
- package/dist/aabb.d.ts.map +1 -1
- package/dist/aabb.js +58 -38
- package/dist/aabb.js.map +1 -1
- package/dist/boundingSphere.d.ts.map +1 -1
- package/dist/boundingSphere.js +3 -2
- package/dist/boundingSphere.js.map +1 -1
- package/dist/capsule.d.ts +6 -5
- package/dist/capsule.d.ts.map +1 -1
- package/dist/capsule.js +38 -15
- package/dist/capsule.js.map +1 -1
- package/dist/contract.d.ts +1 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +1 -0
- package/dist/contract.js.map +1 -1
- package/dist/enableGeometryPoolGuards.d.ts +4 -0
- package/dist/enableGeometryPoolGuards.d.ts.map +1 -0
- package/dist/enableGeometryPoolGuards.js +32 -0
- package/dist/enableGeometryPoolGuards.js.map +1 -0
- package/dist/frustum.d.ts +2 -1
- package/dist/frustum.d.ts.map +1 -1
- package/dist/frustum.js +4 -1
- package/dist/frustum.js.map +1 -1
- package/dist/geometryPoolGuards.d.ts +6 -0
- package/dist/geometryPoolGuards.d.ts.map +1 -0
- package/dist/geometryPoolGuards.js +7 -0
- package/dist/geometryPoolGuards.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/matrix.d.ts +12 -9
- package/dist/matrix.d.ts.map +1 -1
- package/dist/matrix.js +103 -106
- package/dist/matrix.js.map +1 -1
- package/dist/matrix3.d.ts +17 -2
- package/dist/matrix3.d.ts.map +1 -1
- package/dist/matrix3.js +88 -44
- package/dist/matrix3.js.map +1 -1
- package/dist/matrix3Pool.d.ts.map +1 -1
- package/dist/matrix3Pool.js +6 -0
- package/dist/matrix3Pool.js.map +1 -1
- package/dist/matrix4.d.ts +23 -10
- package/dist/matrix4.d.ts.map +1 -1
- package/dist/matrix4.js +74 -53
- package/dist/matrix4.js.map +1 -1
- package/dist/matrix4Pool.d.ts.map +1 -1
- package/dist/matrix4Pool.js +6 -0
- package/dist/matrix4Pool.js.map +1 -1
- package/dist/matrixPool.d.ts.map +1 -1
- package/dist/matrixPool.js +6 -0
- package/dist/matrixPool.js.map +1 -1
- package/dist/obb.d.ts +4 -2
- package/dist/obb.d.ts.map +1 -1
- package/dist/obb.js +164 -88
- package/dist/obb.js.map +1 -1
- package/dist/quaternion.d.ts +7 -4
- package/dist/quaternion.d.ts.map +1 -1
- package/dist/quaternion.js +40 -25
- package/dist/quaternion.js.map +1 -1
- package/dist/quaternionPool.d.ts.map +1 -1
- package/dist/quaternionPool.js +6 -0
- package/dist/quaternionPool.js.map +1 -1
- package/dist/ray3d.d.ts +5 -4
- package/dist/ray3d.d.ts.map +1 -1
- package/dist/ray3d.js +23 -8
- package/dist/ray3d.js.map +1 -1
- package/dist/rectangle.d.ts.map +1 -1
- package/dist/rectangle.js +66 -43
- package/dist/rectangle.js.map +1 -1
- package/dist/rectanglePool.d.ts.map +1 -1
- package/dist/rectanglePool.js +6 -0
- package/dist/rectanglePool.js.map +1 -1
- package/dist/transform2d.d.ts.map +1 -1
- package/dist/transform2d.js +5 -2
- package/dist/transform2d.js.map +1 -1
- package/dist/typedarray.js +3 -6
- package/dist/typedarray.js.map +1 -1
- package/dist/vector2.d.ts.map +1 -1
- package/dist/vector2.js +44 -26
- package/dist/vector2.js.map +1 -1
- package/dist/vector2Pool.d.ts.map +1 -1
- package/dist/vector2Pool.js +6 -0
- package/dist/vector2Pool.js.map +1 -1
- package/dist/vector3.d.ts +2 -2
- package/dist/vector3.d.ts.map +1 -1
- package/dist/vector3.js +55 -37
- package/dist/vector3.js.map +1 -1
- package/dist/vector3Pool.d.ts.map +1 -1
- package/dist/vector3Pool.js +6 -0
- package/dist/vector3Pool.js.map +1 -1
- package/dist/vector4.d.ts +2 -2
- package/dist/vector4.d.ts.map +1 -1
- package/dist/vector4.js +66 -48
- package/dist/vector4.js.map +1 -1
- package/dist/vector4Pool.d.ts.map +1 -1
- package/dist/vector4Pool.js +6 -0
- package/dist/vector4Pool.js.map +1 -1
- package/package.json +4 -3
- package/src/aabb.test.ts +18 -0
- package/src/boundingSphere.test.ts +39 -0
- package/src/capsule.test.ts +158 -0
- package/src/enableGeometryPoolGuards.test.ts +121 -0
- package/src/frustum.test.ts +38 -0
- package/src/geometryPoolGuards.test.ts +16 -0
- package/src/matrix.test.ts +52 -6
- package/src/matrix3.test.ts +46 -6
- package/src/matrix3Pool.test.ts +11 -0
- package/src/matrix4.test.ts +553 -135
- package/src/matrix4Pool.test.ts +11 -0
- package/src/matrixPool.test.ts +11 -0
- package/src/obb.test.ts +277 -0
- package/src/plane.test.ts +20 -0
- package/src/quaternion.test.ts +222 -4
- package/src/quaternionPool.test.ts +12 -0
- package/src/ray3d.test.ts +204 -0
- package/src/rectangle.test.ts +58 -14
- package/src/rectanglePool.test.ts +11 -0
- package/src/transform2d.test.ts +38 -0
- package/src/vector2.test.ts +170 -0
- package/src/vector2Pool.test.ts +11 -0
- package/src/vector3.test.ts +210 -0
- package/src/vector3Pool.test.ts +11 -0
- package/src/vector4.test.ts +231 -0
- package/src/vector4Pool.test.ts +11 -0
package/src/vector2.test.ts
CHANGED
|
@@ -96,6 +96,27 @@ describe('addVector2', () => {
|
|
|
96
96
|
});
|
|
97
97
|
|
|
98
98
|
describe('clampVector2', () => {
|
|
99
|
+
it('clamps each component to its own bounds, below, above, and not at all', () => {
|
|
100
|
+
// Every component gets a DIFFERENT min and max, so a component reading a neighbour's bound
|
|
101
|
+
// comes out as a wrong number instead of the right one by luck. Each component is then run
|
|
102
|
+
// through all three outcomes — under its floor, over its ceiling, and already in range.
|
|
103
|
+
const min = createVector2(-1, -2);
|
|
104
|
+
const max = createVector2(10, 20);
|
|
105
|
+
const out = createVector2();
|
|
106
|
+
|
|
107
|
+
clampVector2(out, createVector2(-5, -6), min, max);
|
|
108
|
+
expect(out.x).toBe(-1);
|
|
109
|
+
expect(out.y).toBe(-2);
|
|
110
|
+
|
|
111
|
+
clampVector2(out, createVector2(100, 200), min, max);
|
|
112
|
+
expect(out.x).toBe(10);
|
|
113
|
+
expect(out.y).toBe(20);
|
|
114
|
+
|
|
115
|
+
clampVector2(out, createVector2(0, 1), min, max);
|
|
116
|
+
expect(out.x).toBe(0);
|
|
117
|
+
expect(out.y).toBe(1);
|
|
118
|
+
});
|
|
119
|
+
|
|
99
120
|
it('clamps each component independently', () => {
|
|
100
121
|
const out = createVector2();
|
|
101
122
|
const value = createVector2(5, -2);
|
|
@@ -116,12 +137,28 @@ describe('clampVector2', () => {
|
|
|
116
137
|
expect(out.y).toBe(2);
|
|
117
138
|
});
|
|
118
139
|
|
|
140
|
+
it('clamps x low and y high', () => {
|
|
141
|
+
const out = createVector2();
|
|
142
|
+
clampVector2(out, createVector2(-2, 5), createVector2(0, 0), createVector2(3, 3));
|
|
143
|
+
expect(out).toMatchObject({ x: 0, y: 3 });
|
|
144
|
+
});
|
|
145
|
+
|
|
119
146
|
it('supports out === value', () => {
|
|
120
147
|
const v = createVector2(5, -1);
|
|
121
148
|
clampVector2(v, v, createVector2(0, 0), createVector2(3, 3));
|
|
122
149
|
expect(v.x).toBe(3);
|
|
123
150
|
expect(v.y).toBe(0);
|
|
124
151
|
});
|
|
152
|
+
|
|
153
|
+
it('supports out === min or max', () => {
|
|
154
|
+
const min = createVector2(0, 0);
|
|
155
|
+
clampVector2(min, createVector2(5, -2), min, createVector2(3, 3));
|
|
156
|
+
expect(min).toMatchObject({ x: 3, y: 0 });
|
|
157
|
+
|
|
158
|
+
const max = createVector2(3, 3);
|
|
159
|
+
clampVector2(max, createVector2(5, -2), createVector2(0, 0), max);
|
|
160
|
+
expect(max).toMatchObject({ x: 3, y: 0 });
|
|
161
|
+
});
|
|
125
162
|
});
|
|
126
163
|
|
|
127
164
|
describe('cloneVector2', () => {
|
|
@@ -195,6 +232,21 @@ describe('createVector2FromPolar', () => {
|
|
|
195
232
|
});
|
|
196
233
|
|
|
197
234
|
describe('divideVector2', () => {
|
|
235
|
+
it('gives 0 for a zero divisor on each component in turn', () => {
|
|
236
|
+
// A zero divisor is answered per component, not for the whole vector, so each component is
|
|
237
|
+
// zeroed on its own while the others still divide normally.
|
|
238
|
+
const source = createVector2(6, 8);
|
|
239
|
+
const out = createVector2();
|
|
240
|
+
|
|
241
|
+
divideVector2(out, source, createVector2(0, 4));
|
|
242
|
+
expect(out.x).toBe(0);
|
|
243
|
+
expect(out.y).toBe(2);
|
|
244
|
+
|
|
245
|
+
divideVector2(out, source, createVector2(2, 0));
|
|
246
|
+
expect(out.x).toBe(3);
|
|
247
|
+
expect(out.y).toBe(0);
|
|
248
|
+
});
|
|
249
|
+
|
|
198
250
|
it('divides component-wise', () => {
|
|
199
251
|
const out = createVector2();
|
|
200
252
|
divideVector2(out, createVector2(6, 8), createVector2(2, 4));
|
|
@@ -209,15 +261,32 @@ describe('divideVector2', () => {
|
|
|
209
261
|
expect(out.y).toBe(2);
|
|
210
262
|
});
|
|
211
263
|
|
|
264
|
+
it('produces 0 for a zero y divisor', () => {
|
|
265
|
+
const out = createVector2();
|
|
266
|
+
divideVector2(out, createVector2(6, 8), createVector2(2, 0));
|
|
267
|
+
expect(out).toMatchObject({ x: 3, y: 0 });
|
|
268
|
+
});
|
|
269
|
+
|
|
212
270
|
it('supports out === source', () => {
|
|
213
271
|
const v = createVector2(6, 8);
|
|
214
272
|
divideVector2(v, v, createVector2(2, 4));
|
|
215
273
|
expect(v.x).toBe(3);
|
|
216
274
|
expect(v.y).toBe(2);
|
|
217
275
|
});
|
|
276
|
+
|
|
277
|
+
it('supports out === divisor', () => {
|
|
278
|
+
const divisor = createVector2(2, 4);
|
|
279
|
+
divideVector2(divisor, createVector2(6, 8), divisor);
|
|
280
|
+
expect(divisor).toMatchObject({ x: 3, y: 2 });
|
|
281
|
+
});
|
|
218
282
|
});
|
|
219
283
|
|
|
220
284
|
describe('equalsVector2', () => {
|
|
285
|
+
it('returns false when either vector is absent', () => {
|
|
286
|
+
expect(equalsVector2(pt, null)).toBe(false);
|
|
287
|
+
expect(equalsVector2(undefined, pt)).toBe(false);
|
|
288
|
+
});
|
|
289
|
+
|
|
221
290
|
it('returns true if vectors are identical, false otherwise', () => {
|
|
222
291
|
expect(equalsVector2(pt, pt2)).toBe(true);
|
|
223
292
|
pt.x = 1;
|
|
@@ -234,6 +303,34 @@ describe('equalsVector2', () => {
|
|
|
234
303
|
pt2.x = 1;
|
|
235
304
|
expect(equalsVector2(pt, pt2)).toBe(true);
|
|
236
305
|
});
|
|
306
|
+
|
|
307
|
+
it('compares every component, not just the first', () => {
|
|
308
|
+
// Two hand-written comparisons in a row: one left reading the wrong field still agrees with
|
|
309
|
+
// the right answer on every pair that differs in more than one place, so each differs alone.
|
|
310
|
+
const a = createVector2(1, 2);
|
|
311
|
+
expect(equalsVector2(a, createVector2(9, 2))).toBe(false);
|
|
312
|
+
expect(equalsVector2(a, createVector2(1, 9))).toBe(false);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('returns false when either side is missing', () => {
|
|
316
|
+
// The signature accepts null and undefined, so absence is an answerable input rather than
|
|
317
|
+
// misuse: nothing is equal to a vector, including another absence.
|
|
318
|
+
const v = createVector2(1, 2);
|
|
319
|
+
expect(equalsVector2(v, null)).toBe(false);
|
|
320
|
+
expect(equalsVector2(null, v)).toBe(false);
|
|
321
|
+
expect(equalsVector2(v, undefined)).toBe(false);
|
|
322
|
+
expect(equalsVector2(undefined, v)).toBe(false);
|
|
323
|
+
expect(equalsVector2(null, null)).toBe(false);
|
|
324
|
+
expect(equalsVector2(undefined, undefined)).toBe(false);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('holds a vector equal to itself even with a NaN component', () => {
|
|
328
|
+
// Comparing field by field would say a vector carrying NaN differs from itself, and a caller
|
|
329
|
+
// watching for changes would then see one every time it looked.
|
|
330
|
+
const notANumber = createVector2(NaN, 2);
|
|
331
|
+
expect(equalsVector2(notANumber, notANumber)).toBe(true);
|
|
332
|
+
expect(equalsVector2(notANumber, createVector2(NaN, 2))).toBe(false);
|
|
333
|
+
});
|
|
237
334
|
});
|
|
238
335
|
|
|
239
336
|
describe('getVector2AngleBetween', () => {
|
|
@@ -446,6 +543,23 @@ describe('interpolateVector2', () => {
|
|
|
446
543
|
});
|
|
447
544
|
|
|
448
545
|
describe('maxVector2', () => {
|
|
546
|
+
it('takes each component from whichever side wins, and gives the same answer either way round', () => {
|
|
547
|
+
// The winner alternates between the two arguments component by component, and all 4 numbers
|
|
548
|
+
// are distinct, so a line reading the wrong component or the wrong side cannot land on the
|
|
549
|
+
// right answer anyway. Running it again with the arguments swapped must not change it.
|
|
550
|
+
const a = createVector2(1, 5);
|
|
551
|
+
const b = createVector2(3, 0);
|
|
552
|
+
const out = createVector2();
|
|
553
|
+
|
|
554
|
+
maxVector2(out, a, b);
|
|
555
|
+
expect(out.x).toBe(3);
|
|
556
|
+
expect(out.y).toBe(5);
|
|
557
|
+
|
|
558
|
+
maxVector2(out, b, a);
|
|
559
|
+
expect(out.x).toBe(3);
|
|
560
|
+
expect(out.y).toBe(5);
|
|
561
|
+
});
|
|
562
|
+
|
|
449
563
|
it('returns the component-wise maximum', () => {
|
|
450
564
|
const out = createVector2();
|
|
451
565
|
maxVector2(out, createVector2(1, 5), createVector2(3, 2));
|
|
@@ -460,9 +574,39 @@ describe('maxVector2', () => {
|
|
|
460
574
|
expect(a.x).toBe(3);
|
|
461
575
|
expect(a.y).toBe(5);
|
|
462
576
|
});
|
|
577
|
+
|
|
578
|
+
it('supports out === b', () => {
|
|
579
|
+
const a = createVector2(1, 5);
|
|
580
|
+
const b = createVector2(3, 2);
|
|
581
|
+
maxVector2(b, a, b);
|
|
582
|
+
expect(b).toMatchObject({ x: 3, y: 5 });
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
it('selects a for x and b for y', () => {
|
|
586
|
+
const out = createVector2();
|
|
587
|
+
maxVector2(out, createVector2(5, 1), createVector2(2, 3));
|
|
588
|
+
expect(out).toMatchObject({ x: 5, y: 3 });
|
|
589
|
+
});
|
|
463
590
|
});
|
|
464
591
|
|
|
465
592
|
describe('minVector2', () => {
|
|
593
|
+
it('takes each component from whichever side wins, and gives the same answer either way round', () => {
|
|
594
|
+
// The winner alternates between the two arguments component by component, and all 4 numbers
|
|
595
|
+
// are distinct, so a line reading the wrong component or the wrong side cannot land on the
|
|
596
|
+
// right answer anyway. Running it again with the arguments swapped must not change it.
|
|
597
|
+
const a = createVector2(1, 5);
|
|
598
|
+
const b = createVector2(3, 0);
|
|
599
|
+
const out = createVector2();
|
|
600
|
+
|
|
601
|
+
minVector2(out, a, b);
|
|
602
|
+
expect(out.x).toBe(1);
|
|
603
|
+
expect(out.y).toBe(0);
|
|
604
|
+
|
|
605
|
+
minVector2(out, b, a);
|
|
606
|
+
expect(out.x).toBe(1);
|
|
607
|
+
expect(out.y).toBe(0);
|
|
608
|
+
});
|
|
609
|
+
|
|
466
610
|
it('returns the component-wise minimum', () => {
|
|
467
611
|
const out = createVector2();
|
|
468
612
|
minVector2(out, createVector2(1, 5), createVector2(3, 2));
|
|
@@ -477,6 +621,19 @@ describe('minVector2', () => {
|
|
|
477
621
|
expect(a.x).toBe(1);
|
|
478
622
|
expect(a.y).toBe(2);
|
|
479
623
|
});
|
|
624
|
+
|
|
625
|
+
it('supports out === b', () => {
|
|
626
|
+
const a = createVector2(1, 5);
|
|
627
|
+
const b = createVector2(3, 2);
|
|
628
|
+
minVector2(b, a, b);
|
|
629
|
+
expect(b).toMatchObject({ x: 1, y: 2 });
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
it('selects b for x and a for y', () => {
|
|
633
|
+
const out = createVector2();
|
|
634
|
+
minVector2(out, createVector2(5, 1), createVector2(2, 3));
|
|
635
|
+
expect(out).toMatchObject({ x: 2, y: 1 });
|
|
636
|
+
});
|
|
480
637
|
});
|
|
481
638
|
|
|
482
639
|
describe('multiplyVector2', () => {
|
|
@@ -493,6 +650,12 @@ describe('multiplyVector2', () => {
|
|
|
493
650
|
expect(a.x).toBe(8);
|
|
494
651
|
expect(a.y).toBe(15);
|
|
495
652
|
});
|
|
653
|
+
|
|
654
|
+
it('supports out === b', () => {
|
|
655
|
+
const b = createVector2(4, 5);
|
|
656
|
+
multiplyVector2(b, createVector2(2, 3), b);
|
|
657
|
+
expect(b).toMatchObject({ x: 8, y: 15 });
|
|
658
|
+
});
|
|
496
659
|
});
|
|
497
660
|
|
|
498
661
|
describe('nearEqualsVector2', () => {
|
|
@@ -646,6 +809,13 @@ describe('reflectVector2', () => {
|
|
|
646
809
|
expect(v.x).toBeCloseTo(-1, 6);
|
|
647
810
|
expect(v.y).toBeCloseTo(-1, 6);
|
|
648
811
|
});
|
|
812
|
+
|
|
813
|
+
it('supports out === normal', () => {
|
|
814
|
+
const normal = createVector2(1, 0);
|
|
815
|
+
reflectVector2(normal, createVector2(1, -1), normal);
|
|
816
|
+
expect(normal.x).toBeCloseTo(-1, 6);
|
|
817
|
+
expect(normal.y).toBeCloseTo(-1, 6);
|
|
818
|
+
});
|
|
649
819
|
});
|
|
650
820
|
|
|
651
821
|
describe('scaleVector2', () => {
|
package/src/vector2Pool.test.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { attachEntityBinding, getEntityBinding } from '@flighthq/entity/contract';
|
|
1
2
|
import type { Vector2 } from '@flighthq/types/contract';
|
|
2
3
|
|
|
3
4
|
import { acquireEmptyVector2, acquireVector2, clearVector2Pool, releaseVector2 } from './vector2Pool';
|
|
@@ -59,6 +60,16 @@ describe('clearVector2Pool', () => {
|
|
|
59
60
|
});
|
|
60
61
|
|
|
61
62
|
describe('releaseVector2', () => {
|
|
63
|
+
it('detaches the previous owner binding before the vector is reused', () => {
|
|
64
|
+
const v = acquireVector2();
|
|
65
|
+
attachEntityBinding(v, { owner: 'first' });
|
|
66
|
+
releaseVector2(v);
|
|
67
|
+
|
|
68
|
+
const reused = acquireVector2();
|
|
69
|
+
expect(reused).toBe(v);
|
|
70
|
+
expect(getEntityBinding(reused)).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
|
|
62
73
|
it('handles null safely', () => {
|
|
63
74
|
expect(() => releaseVector2(null as unknown as Vector2)).not.toThrow();
|
|
64
75
|
});
|
package/src/vector3.test.ts
CHANGED
|
@@ -88,6 +88,30 @@ describe('addVector3', () => {
|
|
|
88
88
|
});
|
|
89
89
|
|
|
90
90
|
describe('clampVector3', () => {
|
|
91
|
+
it('clamps each component to its own bounds, below, above, and not at all', () => {
|
|
92
|
+
// Every component gets a DIFFERENT min and max, so a component reading a neighbour's bound
|
|
93
|
+
// comes out as a wrong number instead of the right one by luck. Each component is then run
|
|
94
|
+
// through all three outcomes — under its floor, over its ceiling, and already in range.
|
|
95
|
+
const min = createVector3(-1, -2, -3);
|
|
96
|
+
const max = createVector3(10, 20, 30);
|
|
97
|
+
const out = createVector3();
|
|
98
|
+
|
|
99
|
+
clampVector3(out, createVector3(-5, -6, -7), min, max);
|
|
100
|
+
expect(out.x).toBe(-1);
|
|
101
|
+
expect(out.y).toBe(-2);
|
|
102
|
+
expect(out.z).toBe(-3);
|
|
103
|
+
|
|
104
|
+
clampVector3(out, createVector3(100, 200, 300), min, max);
|
|
105
|
+
expect(out.x).toBe(10);
|
|
106
|
+
expect(out.y).toBe(20);
|
|
107
|
+
expect(out.z).toBe(30);
|
|
108
|
+
|
|
109
|
+
clampVector3(out, createVector3(0, 1, 2), min, max);
|
|
110
|
+
expect(out.x).toBe(0);
|
|
111
|
+
expect(out.y).toBe(1);
|
|
112
|
+
expect(out.z).toBe(2);
|
|
113
|
+
});
|
|
114
|
+
|
|
91
115
|
it('clamps each component independently', () => {
|
|
92
116
|
const out = createVector3();
|
|
93
117
|
clampVector3(out, createVector3(5, -2, 1), createVector3(0, 0, 0), createVector3(3, 3, 3));
|
|
@@ -103,6 +127,25 @@ describe('clampVector3', () => {
|
|
|
103
127
|
expect(v.y).toBe(0);
|
|
104
128
|
expect(v.z).toBe(3);
|
|
105
129
|
});
|
|
130
|
+
|
|
131
|
+
it('supports out === min or max', () => {
|
|
132
|
+
const min = createVector3(0, 0, 0);
|
|
133
|
+
clampVector3(min, createVector3(5, -2, 1), min, createVector3(3, 3, 3));
|
|
134
|
+
expect(min).toMatchObject({ x: 3, y: 0, z: 1 });
|
|
135
|
+
|
|
136
|
+
const max = createVector3(3, 3, 3);
|
|
137
|
+
clampVector3(max, createVector3(5, -2, 1), createVector3(0, 0, 0), max);
|
|
138
|
+
expect(max).toMatchObject({ x: 3, y: 0, z: 1 });
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('covers every clamp outcome on every component', () => {
|
|
142
|
+
const out = createVector3();
|
|
143
|
+
clampVector3(out, createVector3(-1, 2, 5), createVector3(0, 0, 0), createVector3(3, 3, 3));
|
|
144
|
+
expect(out).toMatchObject({ x: 0, y: 2, z: 3 });
|
|
145
|
+
|
|
146
|
+
clampVector3(out, createVector3(2, 5, -1), createVector3(0, 0, 0), createVector3(3, 3, 3));
|
|
147
|
+
expect(out).toMatchObject({ x: 2, y: 3, z: 0 });
|
|
148
|
+
});
|
|
106
149
|
});
|
|
107
150
|
|
|
108
151
|
describe('cloneVector3', () => {
|
|
@@ -211,6 +254,28 @@ describe('crossVector3', () => {
|
|
|
211
254
|
});
|
|
212
255
|
|
|
213
256
|
describe('divideVector3', () => {
|
|
257
|
+
it('gives 0 for a zero divisor on each component in turn', () => {
|
|
258
|
+
// A zero divisor is answered per component, not for the whole vector, so each component is
|
|
259
|
+
// zeroed on its own while the others still divide normally.
|
|
260
|
+
const source = createVector3(6, 8, 9);
|
|
261
|
+
const out = createVector3();
|
|
262
|
+
|
|
263
|
+
divideVector3(out, source, createVector3(0, 4, 3));
|
|
264
|
+
expect(out.x).toBe(0);
|
|
265
|
+
expect(out.y).toBe(2);
|
|
266
|
+
expect(out.z).toBe(3);
|
|
267
|
+
|
|
268
|
+
divideVector3(out, source, createVector3(2, 0, 3));
|
|
269
|
+
expect(out.x).toBe(3);
|
|
270
|
+
expect(out.y).toBe(0);
|
|
271
|
+
expect(out.z).toBe(3);
|
|
272
|
+
|
|
273
|
+
divideVector3(out, source, createVector3(2, 4, 0));
|
|
274
|
+
expect(out.x).toBe(3);
|
|
275
|
+
expect(out.y).toBe(2);
|
|
276
|
+
expect(out.z).toBe(0);
|
|
277
|
+
});
|
|
278
|
+
|
|
214
279
|
it('divides component-wise', () => {
|
|
215
280
|
const out = createVector3();
|
|
216
281
|
divideVector3(out, createVector3(6, 8, 9), createVector3(2, 4, 3));
|
|
@@ -234,9 +299,26 @@ describe('divideVector3', () => {
|
|
|
234
299
|
expect(v.y).toBe(2);
|
|
235
300
|
expect(v.z).toBe(3);
|
|
236
301
|
});
|
|
302
|
+
|
|
303
|
+
it('supports out === divisor', () => {
|
|
304
|
+
const divisor = createVector3(2, 4, 3);
|
|
305
|
+
divideVector3(divisor, createVector3(6, 8, 9), divisor);
|
|
306
|
+
expect(divisor).toMatchObject({ x: 3, y: 2, z: 3 });
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('produces 0 for a zero y divisor', () => {
|
|
310
|
+
const out = createVector3();
|
|
311
|
+
divideVector3(out, createVector3(6, 8, 9), createVector3(2, 0, 3));
|
|
312
|
+
expect(out).toMatchObject({ x: 3, y: 0, z: 3 });
|
|
313
|
+
});
|
|
237
314
|
});
|
|
238
315
|
|
|
239
316
|
describe('equalsVector3', () => {
|
|
317
|
+
it('returns false when either vector is absent', () => {
|
|
318
|
+
expect(equalsVector3(createVector3(), null)).toBe(false);
|
|
319
|
+
expect(equalsVector3(undefined, createVector3())).toBe(false);
|
|
320
|
+
});
|
|
321
|
+
|
|
240
322
|
it('returns true if vectors are equal', () => {
|
|
241
323
|
const a = createVector3(1, 2, 3);
|
|
242
324
|
const b = createVector3(1, 2, 3);
|
|
@@ -248,6 +330,35 @@ describe('equalsVector3', () => {
|
|
|
248
330
|
const b = createVector3(4, 5, 6);
|
|
249
331
|
expect(equalsVector3(a, b)).toBe(false);
|
|
250
332
|
});
|
|
333
|
+
|
|
334
|
+
it('compares every component, not just the first', () => {
|
|
335
|
+
// Three hand-written comparisons in a row: one left reading the wrong field agrees with the
|
|
336
|
+
// right answer on every pair that differs in more than one place, so each differs alone here.
|
|
337
|
+
const a = createVector3(1, 2, 3);
|
|
338
|
+
expect(equalsVector3(a, createVector3(9, 2, 3))).toBe(false);
|
|
339
|
+
expect(equalsVector3(a, createVector3(1, 9, 3))).toBe(false);
|
|
340
|
+
expect(equalsVector3(a, createVector3(1, 2, 9))).toBe(false);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('returns false when either side is missing', () => {
|
|
344
|
+
// The signature accepts null and undefined, so absence is an answerable input rather than
|
|
345
|
+
// misuse: nothing is equal to a vector, including another absence.
|
|
346
|
+
const v = createVector3(1, 2, 3);
|
|
347
|
+
expect(equalsVector3(v, null)).toBe(false);
|
|
348
|
+
expect(equalsVector3(null, v)).toBe(false);
|
|
349
|
+
expect(equalsVector3(v, undefined)).toBe(false);
|
|
350
|
+
expect(equalsVector3(undefined, v)).toBe(false);
|
|
351
|
+
expect(equalsVector3(null, null)).toBe(false);
|
|
352
|
+
expect(equalsVector3(undefined, undefined)).toBe(false);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('holds a vector equal to itself even with a NaN component', () => {
|
|
356
|
+
// Comparing field by field would say a vector carrying NaN differs from itself, and a caller
|
|
357
|
+
// watching for changes would then see one every time it looked.
|
|
358
|
+
const notANumber = createVector3(NaN, 2, 3);
|
|
359
|
+
expect(equalsVector3(notANumber, notANumber)).toBe(true);
|
|
360
|
+
expect(equalsVector3(notANumber, createVector3(NaN, 2, 3))).toBe(false);
|
|
361
|
+
});
|
|
251
362
|
});
|
|
252
363
|
|
|
253
364
|
describe('getVector3AngleBetween', () => {
|
|
@@ -340,6 +451,14 @@ describe('getVector3Spherical', () => {
|
|
|
340
451
|
expect(sph.y).toBe(0);
|
|
341
452
|
expect(sph.z).toBe(0);
|
|
342
453
|
});
|
|
454
|
+
|
|
455
|
+
it('supports out === source', () => {
|
|
456
|
+
const vector = createVector3(1, 0, 0);
|
|
457
|
+
getVector3Spherical(vector, vector);
|
|
458
|
+
expect(vector.x).toBeCloseTo(1, 6);
|
|
459
|
+
expect(vector.y).toBeCloseTo(Math.PI / 2, 6);
|
|
460
|
+
expect(vector.z).toBeCloseTo(0, 6);
|
|
461
|
+
});
|
|
343
462
|
});
|
|
344
463
|
|
|
345
464
|
describe('interpolateVector3', () => {
|
|
@@ -373,9 +492,35 @@ describe('interpolateVector3', () => {
|
|
|
373
492
|
expect(a.y).toBe(2);
|
|
374
493
|
expect(a.z).toBe(3);
|
|
375
494
|
});
|
|
495
|
+
|
|
496
|
+
it('supports out === b', () => {
|
|
497
|
+
const a = createVector3(0, 0, 0);
|
|
498
|
+
const b = createVector3(2, 4, 6);
|
|
499
|
+
interpolateVector3(b, a, b, 0.5);
|
|
500
|
+
expect(b).toMatchObject({ x: 1, y: 2, z: 3 });
|
|
501
|
+
});
|
|
376
502
|
});
|
|
377
503
|
|
|
378
504
|
describe('maxVector3', () => {
|
|
505
|
+
it('takes each component from whichever side wins, and gives the same answer either way round', () => {
|
|
506
|
+
// The winner alternates between the two arguments component by component, and all 6 numbers
|
|
507
|
+
// are distinct, so a line reading the wrong component or the wrong side cannot land on the
|
|
508
|
+
// right answer anyway. Running it again with the arguments swapped must not change it.
|
|
509
|
+
const a = createVector3(1, 5, 2);
|
|
510
|
+
const b = createVector3(3, 0, 7);
|
|
511
|
+
const out = createVector3();
|
|
512
|
+
|
|
513
|
+
maxVector3(out, a, b);
|
|
514
|
+
expect(out.x).toBe(3);
|
|
515
|
+
expect(out.y).toBe(5);
|
|
516
|
+
expect(out.z).toBe(7);
|
|
517
|
+
|
|
518
|
+
maxVector3(out, b, a);
|
|
519
|
+
expect(out.x).toBe(3);
|
|
520
|
+
expect(out.y).toBe(5);
|
|
521
|
+
expect(out.z).toBe(7);
|
|
522
|
+
});
|
|
523
|
+
|
|
379
524
|
it('returns the component-wise maximum', () => {
|
|
380
525
|
const out = createVector3();
|
|
381
526
|
maxVector3(out, createVector3(1, 5, 2), createVector3(3, 2, 7));
|
|
@@ -383,9 +528,44 @@ describe('maxVector3', () => {
|
|
|
383
528
|
expect(out.y).toBe(5);
|
|
384
529
|
expect(out.z).toBe(7);
|
|
385
530
|
});
|
|
531
|
+
|
|
532
|
+
it('supports out === a or b', () => {
|
|
533
|
+
const a = createVector3(1, 5, 2);
|
|
534
|
+
maxVector3(a, a, createVector3(3, 2, 7));
|
|
535
|
+
expect(a).toMatchObject({ x: 3, y: 5, z: 7 });
|
|
536
|
+
|
|
537
|
+
const b = createVector3(3, 2, 7);
|
|
538
|
+
maxVector3(b, createVector3(1, 5, 2), b);
|
|
539
|
+
expect(b).toMatchObject({ x: 3, y: 5, z: 7 });
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
it('selects the complementary component outcomes', () => {
|
|
543
|
+
const out = createVector3();
|
|
544
|
+
maxVector3(out, createVector3(5, 1, 7), createVector3(2, 3, 4));
|
|
545
|
+
expect(out).toMatchObject({ x: 5, y: 3, z: 7 });
|
|
546
|
+
});
|
|
386
547
|
});
|
|
387
548
|
|
|
388
549
|
describe('minVector3', () => {
|
|
550
|
+
it('takes each component from whichever side wins, and gives the same answer either way round', () => {
|
|
551
|
+
// The winner alternates between the two arguments component by component, and all 6 numbers
|
|
552
|
+
// are distinct, so a line reading the wrong component or the wrong side cannot land on the
|
|
553
|
+
// right answer anyway. Running it again with the arguments swapped must not change it.
|
|
554
|
+
const a = createVector3(1, 5, 2);
|
|
555
|
+
const b = createVector3(3, 0, 7);
|
|
556
|
+
const out = createVector3();
|
|
557
|
+
|
|
558
|
+
minVector3(out, a, b);
|
|
559
|
+
expect(out.x).toBe(1);
|
|
560
|
+
expect(out.y).toBe(0);
|
|
561
|
+
expect(out.z).toBe(2);
|
|
562
|
+
|
|
563
|
+
minVector3(out, b, a);
|
|
564
|
+
expect(out.x).toBe(1);
|
|
565
|
+
expect(out.y).toBe(0);
|
|
566
|
+
expect(out.z).toBe(2);
|
|
567
|
+
});
|
|
568
|
+
|
|
389
569
|
it('returns the component-wise minimum', () => {
|
|
390
570
|
const out = createVector3();
|
|
391
571
|
minVector3(out, createVector3(1, 5, 2), createVector3(3, 2, 7));
|
|
@@ -393,6 +573,22 @@ describe('minVector3', () => {
|
|
|
393
573
|
expect(out.y).toBe(2);
|
|
394
574
|
expect(out.z).toBe(2);
|
|
395
575
|
});
|
|
576
|
+
|
|
577
|
+
it('supports out === a or b', () => {
|
|
578
|
+
const a = createVector3(1, 5, 2);
|
|
579
|
+
minVector3(a, a, createVector3(3, 2, 7));
|
|
580
|
+
expect(a).toMatchObject({ x: 1, y: 2, z: 2 });
|
|
581
|
+
|
|
582
|
+
const b = createVector3(3, 2, 7);
|
|
583
|
+
minVector3(b, createVector3(1, 5, 2), b);
|
|
584
|
+
expect(b).toMatchObject({ x: 1, y: 2, z: 2 });
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
it('selects the complementary component outcomes', () => {
|
|
588
|
+
const out = createVector3();
|
|
589
|
+
minVector3(out, createVector3(5, 1, 7), createVector3(2, 3, 4));
|
|
590
|
+
expect(out).toMatchObject({ x: 2, y: 1, z: 4 });
|
|
591
|
+
});
|
|
396
592
|
});
|
|
397
593
|
|
|
398
594
|
describe('multiplyVector3', () => {
|
|
@@ -411,6 +607,12 @@ describe('multiplyVector3', () => {
|
|
|
411
607
|
expect(a.y).toBe(18);
|
|
412
608
|
expect(a.z).toBe(28);
|
|
413
609
|
});
|
|
610
|
+
|
|
611
|
+
it('supports out === b', () => {
|
|
612
|
+
const b = createVector3(5, 6, 7);
|
|
613
|
+
multiplyVector3(b, createVector3(2, 3, 4), b);
|
|
614
|
+
expect(b).toMatchObject({ x: 10, y: 18, z: 28 });
|
|
615
|
+
});
|
|
414
616
|
});
|
|
415
617
|
|
|
416
618
|
describe('nearEqualsVector3', () => {
|
|
@@ -567,6 +769,14 @@ describe('reflectVector3', () => {
|
|
|
567
769
|
expect(v.x).toBeCloseTo(-1, 6);
|
|
568
770
|
expect(v.y).toBeCloseTo(-1, 6);
|
|
569
771
|
});
|
|
772
|
+
|
|
773
|
+
it('supports out === normal', () => {
|
|
774
|
+
const normal = createVector3(1, 0, 0);
|
|
775
|
+
reflectVector3(normal, createVector3(1, -1, 0), normal);
|
|
776
|
+
expect(normal.x).toBeCloseTo(-1, 6);
|
|
777
|
+
expect(normal.y).toBeCloseTo(-1, 6);
|
|
778
|
+
expect(normal.z).toBeCloseTo(0, 6);
|
|
779
|
+
});
|
|
570
780
|
});
|
|
571
781
|
|
|
572
782
|
describe('scaleVector3', () => {
|
package/src/vector3Pool.test.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { attachEntityBinding, getEntityBinding } from '@flighthq/entity/contract';
|
|
1
2
|
import type { Vector3 } from '@flighthq/types/contract';
|
|
2
3
|
|
|
3
4
|
import { acquireEmptyVector3, acquireVector3, clearVector3Pool, releaseVector3 } from './vector3Pool';
|
|
@@ -62,6 +63,16 @@ describe('clearVector3Pool', () => {
|
|
|
62
63
|
});
|
|
63
64
|
|
|
64
65
|
describe('releaseVector3', () => {
|
|
66
|
+
it('detaches the previous owner binding before the vector is reused', () => {
|
|
67
|
+
const v = acquireVector3();
|
|
68
|
+
attachEntityBinding(v, { owner: 'first' });
|
|
69
|
+
releaseVector3(v);
|
|
70
|
+
|
|
71
|
+
const reused = acquireVector3();
|
|
72
|
+
expect(reused).toBe(v);
|
|
73
|
+
expect(getEntityBinding(reused)).toBeNull();
|
|
74
|
+
});
|
|
75
|
+
|
|
65
76
|
it('handles null safely', () => {
|
|
66
77
|
expect(() => releaseVector3(null as unknown as Vector3)).not.toThrow();
|
|
67
78
|
});
|