@flighthq/geometry 0.1.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/dist/aabb.d.ts +88 -0
- package/dist/aabb.d.ts.map +1 -0
- package/dist/aabb.js +230 -0
- package/dist/aabb.js.map +1 -0
- package/dist/boundingSphere.d.ts +62 -0
- package/dist/boundingSphere.d.ts.map +1 -0
- package/dist/boundingSphere.js +188 -0
- package/dist/boundingSphere.js.map +1 -0
- package/dist/capsule.d.ts +38 -0
- package/dist/capsule.d.ts.map +1 -0
- package/dist/capsule.js +215 -0
- package/dist/capsule.js.map +1 -0
- package/dist/frustum.d.ts +49 -0
- package/dist/frustum.d.ts.map +1 -0
- package/dist/frustum.js +149 -0
- package/dist/frustum.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/matrix.d.ts +152 -0
- package/dist/matrix.d.ts.map +1 -0
- package/dist/matrix.js +488 -0
- package/dist/matrix.js.map +1 -0
- package/dist/matrix3.d.ts +63 -0
- package/dist/matrix3.d.ts.map +1 -0
- package/dist/matrix3.js +408 -0
- package/dist/matrix3.js.map +1 -0
- package/dist/matrix3Pool.d.ts +6 -0
- package/dist/matrix3Pool.d.ts.map +1 -0
- package/dist/matrix3Pool.js +26 -0
- package/dist/matrix3Pool.js.map +1 -0
- package/dist/matrix4.d.ts +238 -0
- package/dist/matrix4.d.ts.map +1 -0
- package/dist/matrix4.js +931 -0
- package/dist/matrix4.js.map +1 -0
- package/dist/matrix4Pool.d.ts +6 -0
- package/dist/matrix4Pool.d.ts.map +1 -0
- package/dist/matrix4Pool.js +26 -0
- package/dist/matrix4Pool.js.map +1 -0
- package/dist/matrixPool.d.ts +6 -0
- package/dist/matrixPool.d.ts.map +1 -0
- package/dist/matrixPool.js +26 -0
- package/dist/matrixPool.js.map +1 -0
- package/dist/obb.d.ts +44 -0
- package/dist/obb.d.ts.map +1 -0
- package/dist/obb.js +248 -0
- package/dist/obb.js.map +1 -0
- package/dist/plane.d.ts +66 -0
- package/dist/plane.d.ts.map +1 -0
- package/dist/plane.js +140 -0
- package/dist/plane.js.map +1 -0
- package/dist/quaternion.d.ts +128 -0
- package/dist/quaternion.d.ts.map +1 -0
- package/dist/quaternion.js +508 -0
- package/dist/quaternion.js.map +1 -0
- package/dist/quaternionPool.d.ts +6 -0
- package/dist/quaternionPool.d.ts.map +1 -0
- package/dist/quaternionPool.js +29 -0
- package/dist/quaternionPool.js.map +1 -0
- package/dist/ray3d.d.ts +69 -0
- package/dist/ray3d.d.ts.map +1 -0
- package/dist/ray3d.js +264 -0
- package/dist/ray3d.js.map +1 -0
- package/dist/rectangle.d.ts +56 -0
- package/dist/rectangle.d.ts.map +1 -0
- package/dist/rectangle.js +235 -0
- package/dist/rectangle.js.map +1 -0
- package/dist/rectanglePool.d.ts +6 -0
- package/dist/rectanglePool.d.ts.map +1 -0
- package/dist/rectanglePool.js +29 -0
- package/dist/rectanglePool.js.map +1 -0
- package/dist/typedarray.d.ts +4 -0
- package/dist/typedarray.d.ts.map +1 -0
- package/dist/typedarray.js +25 -0
- package/dist/typedarray.js.map +1 -0
- package/dist/vector2.d.ts +130 -0
- package/dist/vector2.d.ts.map +1 -0
- package/dist/vector2.js +250 -0
- package/dist/vector2.js.map +1 -0
- package/dist/vector2Pool.d.ts +6 -0
- package/dist/vector2Pool.d.ts.map +1 -0
- package/dist/vector2Pool.js +20 -0
- package/dist/vector2Pool.js.map +1 -0
- package/dist/vector3.d.ts +220 -0
- package/dist/vector3.d.ts.map +1 -0
- package/dist/vector3.js +380 -0
- package/dist/vector3.js.map +1 -0
- package/dist/vector3Pool.d.ts +6 -0
- package/dist/vector3Pool.d.ts.map +1 -0
- package/dist/vector3Pool.js +28 -0
- package/dist/vector3Pool.js.map +1 -0
- package/dist/vector4.d.ts +180 -0
- package/dist/vector4.d.ts.map +1 -0
- package/dist/vector4.js +333 -0
- package/dist/vector4.js.map +1 -0
- package/dist/vector4Pool.d.ts +6 -0
- package/dist/vector4Pool.d.ts.map +1 -0
- package/dist/vector4Pool.js +29 -0
- package/dist/vector4Pool.js.map +1 -0
- package/package.json +38 -0
- package/src/aabb.test.ts +341 -0
- package/src/boundingSphere.test.ts +247 -0
- package/src/capsule.test.ts +157 -0
- package/src/frustum.test.ts +160 -0
- package/src/matrix.test.ts +1111 -0
- package/src/matrix3.test.ts +752 -0
- package/src/matrix3Pool.test.ts +73 -0
- package/src/matrix4.test.ts +1627 -0
- package/src/matrix4Pool.test.ts +71 -0
- package/src/matrixPool.test.ts +69 -0
- package/src/obb.test.ts +183 -0
- package/src/plane.test.ts +225 -0
- package/src/quaternion.test.ts +531 -0
- package/src/quaternionPool.test.ts +64 -0
- package/src/ray3d.test.ts +297 -0
- package/src/rectangle.test.ts +982 -0
- package/src/rectanglePool.test.ts +71 -0
- package/src/typedarray.test.ts +118 -0
- package/src/vector2.test.ts +958 -0
- package/src/vector2Pool.test.ts +65 -0
- package/src/vector3.test.ts +749 -0
- package/src/vector3Pool.test.ts +68 -0
- package/src/vector4.test.ts +660 -0
- package/src/vector4Pool.test.ts +71 -0
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
import {
|
|
2
|
+
addVector3,
|
|
3
|
+
clampVector3,
|
|
4
|
+
cloneVector3,
|
|
5
|
+
copyVector3,
|
|
6
|
+
createVector3,
|
|
7
|
+
createVector3FromSpherical,
|
|
8
|
+
crossVector3,
|
|
9
|
+
divideVector3,
|
|
10
|
+
equalsVector3,
|
|
11
|
+
getVector3AngleBetween,
|
|
12
|
+
getVector3Distance,
|
|
13
|
+
getVector3DistanceSquared,
|
|
14
|
+
getVector3Dot,
|
|
15
|
+
getVector3Length,
|
|
16
|
+
getVector3LengthSquared,
|
|
17
|
+
getVector3Spherical,
|
|
18
|
+
interpolateVector3,
|
|
19
|
+
maxVector3,
|
|
20
|
+
minVector3,
|
|
21
|
+
multiplyVector3,
|
|
22
|
+
nearEqualsVector3,
|
|
23
|
+
negateVector3,
|
|
24
|
+
normalizeVector3,
|
|
25
|
+
offsetVector3,
|
|
26
|
+
projectVector3,
|
|
27
|
+
reflectVector3,
|
|
28
|
+
scaleVector3,
|
|
29
|
+
setVector3,
|
|
30
|
+
setVector3FromFloat32Array,
|
|
31
|
+
setVector3FromSpherical,
|
|
32
|
+
setVector3FromVector4,
|
|
33
|
+
subtractVector3,
|
|
34
|
+
transformVector3ByMatrix3,
|
|
35
|
+
VECTOR3_X_AXIS,
|
|
36
|
+
VECTOR3_Y_AXIS,
|
|
37
|
+
VECTOR3_Z_AXIS,
|
|
38
|
+
writeVector3ToFloat32Array,
|
|
39
|
+
} from '@flighthq/geometry';
|
|
40
|
+
import type { Vector3 } from '@flighthq/types';
|
|
41
|
+
|
|
42
|
+
describe('addVector3', () => {
|
|
43
|
+
it('returns a new vector when no target is passed', () => {
|
|
44
|
+
const a = createVector3(1, 2, 3);
|
|
45
|
+
const b = createVector3(4, 5, 6);
|
|
46
|
+
const result = createVector3();
|
|
47
|
+
addVector3(result, a, b);
|
|
48
|
+
expect(result.x).toBe(5);
|
|
49
|
+
expect(result.y).toBe(7);
|
|
50
|
+
expect(result.z).toBe(9);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('modifies target when same object is passed as target', () => {
|
|
54
|
+
const a = createVector3(1, 2, 3);
|
|
55
|
+
addVector3(a, a, a); // passing the same object as both source and target
|
|
56
|
+
expect(a.x).toBe(2);
|
|
57
|
+
expect(a.y).toBe(4);
|
|
58
|
+
expect(a.z).toBe(6);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('supports out === a', () => {
|
|
62
|
+
const a = createVector3(1, 2, 3);
|
|
63
|
+
const b = createVector3(4, 5, 6);
|
|
64
|
+
addVector3(a, a, b);
|
|
65
|
+
expect(a.x).toBe(5);
|
|
66
|
+
expect(a.y).toBe(7);
|
|
67
|
+
expect(a.z).toBe(9);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('supports out === b', () => {
|
|
71
|
+
const a = createVector3(1, 2, 3);
|
|
72
|
+
const b = createVector3(4, 5, 6);
|
|
73
|
+
addVector3(b, a, b);
|
|
74
|
+
expect(b.x).toBe(5);
|
|
75
|
+
expect(b.y).toBe(7);
|
|
76
|
+
expect(b.z).toBe(9);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('allows vector-like objects', () => {
|
|
80
|
+
const a = { x: 1, y: 2, z: 3 };
|
|
81
|
+
const b = { x: 4, y: 5, z: 6 };
|
|
82
|
+
const result = { x: 0, y: 0, z: 0 };
|
|
83
|
+
addVector3(result, a, b);
|
|
84
|
+
expect(result.x).toBe(5);
|
|
85
|
+
expect(result.y).toBe(7);
|
|
86
|
+
expect(result.z).toBe(9);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('clampVector3', () => {
|
|
91
|
+
it('clamps each component independently', () => {
|
|
92
|
+
const out = createVector3();
|
|
93
|
+
clampVector3(out, createVector3(5, -2, 1), createVector3(0, 0, 0), createVector3(3, 3, 3));
|
|
94
|
+
expect(out.x).toBe(3);
|
|
95
|
+
expect(out.y).toBe(0);
|
|
96
|
+
expect(out.z).toBe(1);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('supports out === value', () => {
|
|
100
|
+
const v = createVector3(5, -1, 7);
|
|
101
|
+
clampVector3(v, v, createVector3(0, 0, 0), createVector3(3, 3, 3));
|
|
102
|
+
expect(v.x).toBe(3);
|
|
103
|
+
expect(v.y).toBe(0);
|
|
104
|
+
expect(v.z).toBe(3);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('cloneVector3', () => {
|
|
109
|
+
it('creates a new independent vector', () => {
|
|
110
|
+
const original = createVector3(1, 2, 3);
|
|
111
|
+
const cloned: Vector3 = cloneVector3(original);
|
|
112
|
+
expect(cloned).not.toBe(original); // ensures a new instance
|
|
113
|
+
expect(cloned).not.toBeNull();
|
|
114
|
+
expect(cloned.x).toBe(1);
|
|
115
|
+
expect(cloned.y).toBe(2);
|
|
116
|
+
expect(cloned.z).toBe(3);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('allows vector-like objects', () => {
|
|
120
|
+
const original = { x: 1, y: 2, z: 3 };
|
|
121
|
+
const cloned: Vector3 = cloneVector3(original);
|
|
122
|
+
expect(cloned).not.toBe(original); // ensures a new instance
|
|
123
|
+
expect(cloned).not.toBeNull();
|
|
124
|
+
expect(cloned.x).toBe(1);
|
|
125
|
+
expect(cloned.y).toBe(2);
|
|
126
|
+
expect(cloned.z).toBe(3);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('copyVector3', () => {
|
|
131
|
+
it('copies values from source to target', () => {
|
|
132
|
+
const source = createVector3(1, 2, 3);
|
|
133
|
+
const target = createVector3();
|
|
134
|
+
copyVector3(target, source);
|
|
135
|
+
expect(target.x).toBe(1);
|
|
136
|
+
expect(target.y).toBe(2);
|
|
137
|
+
expect(target.z).toBe(3);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('does not affect source when same object is used for input and output', () => {
|
|
141
|
+
const vector = createVector3(1, 2, 3);
|
|
142
|
+
copyVector3(vector, vector);
|
|
143
|
+
expect(vector.x).toBe(1);
|
|
144
|
+
expect(vector.y).toBe(2);
|
|
145
|
+
expect(vector.z).toBe(3);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe('createVector3', () => {
|
|
150
|
+
it('creates a createVector3 with default values', () => {
|
|
151
|
+
const v = createVector3();
|
|
152
|
+
expect(v.x).toBe(0);
|
|
153
|
+
expect(v.y).toBe(0);
|
|
154
|
+
expect(v.z).toBe(0);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('creates a createVector3 with specified values', () => {
|
|
158
|
+
const v = createVector3(1, 2, 3);
|
|
159
|
+
expect(v.x).toBe(1);
|
|
160
|
+
expect(v.y).toBe(2);
|
|
161
|
+
expect(v.z).toBe(3);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('createVector3FromSpherical', () => {
|
|
166
|
+
it('produces the correct cartesian point', () => {
|
|
167
|
+
const v = createVector3FromSpherical(1, Math.PI / 2, 0);
|
|
168
|
+
expect(v.x).toBeCloseTo(1, 6);
|
|
169
|
+
expect(v.y).toBeCloseTo(0, 6);
|
|
170
|
+
expect(v.z).toBeCloseTo(0, 6);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('round-trips with getVector3Spherical', () => {
|
|
174
|
+
const v = createVector3FromSpherical(2, Math.PI / 3, Math.PI / 4);
|
|
175
|
+
const sph = createVector3();
|
|
176
|
+
getVector3Spherical(sph, v);
|
|
177
|
+
expect(sph.x).toBeCloseTo(2, 5);
|
|
178
|
+
expect(sph.y).toBeCloseTo(Math.PI / 3, 5);
|
|
179
|
+
expect(sph.z).toBeCloseTo(Math.PI / 4, 5);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe('crossVector3', () => {
|
|
184
|
+
it('returns the cross product of two vectors', () => {
|
|
185
|
+
const a = createVector3(1, 0, 0);
|
|
186
|
+
const b = createVector3(0, 1, 0);
|
|
187
|
+
const result = createVector3();
|
|
188
|
+
crossVector3(result, a, b);
|
|
189
|
+
expect(result.x).toBe(0);
|
|
190
|
+
expect(result.y).toBe(0);
|
|
191
|
+
expect(result.z).toBe(1);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('modifies target when same object is passed as target', () => {
|
|
195
|
+
const a = createVector3(1, 0, 0);
|
|
196
|
+
const b = createVector3(0, 1, 0);
|
|
197
|
+
crossVector3(a, a, b); // passing the same object as both source and target
|
|
198
|
+
expect(a.x).toBe(0);
|
|
199
|
+
expect(a.y).toBe(0);
|
|
200
|
+
expect(a.z).toBe(1);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('supports out === other', () => {
|
|
204
|
+
const a = createVector3(1, 0, 0);
|
|
205
|
+
const b = createVector3(0, 1, 0);
|
|
206
|
+
crossVector3(b, a, b);
|
|
207
|
+
expect(b.x).toBe(0);
|
|
208
|
+
expect(b.y).toBe(0);
|
|
209
|
+
expect(b.z).toBe(1);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe('divideVector3', () => {
|
|
214
|
+
it('divides component-wise', () => {
|
|
215
|
+
const out = createVector3();
|
|
216
|
+
divideVector3(out, createVector3(6, 8, 9), createVector3(2, 4, 3));
|
|
217
|
+
expect(out.x).toBe(3);
|
|
218
|
+
expect(out.y).toBe(2);
|
|
219
|
+
expect(out.z).toBe(3);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('produces 0 for zero divisor components', () => {
|
|
223
|
+
const out = createVector3();
|
|
224
|
+
divideVector3(out, createVector3(6, 8, 9), createVector3(0, 4, 0));
|
|
225
|
+
expect(out.x).toBe(0);
|
|
226
|
+
expect(out.y).toBe(2);
|
|
227
|
+
expect(out.z).toBe(0);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('supports out === source', () => {
|
|
231
|
+
const v = createVector3(6, 8, 9);
|
|
232
|
+
divideVector3(v, v, createVector3(2, 4, 3));
|
|
233
|
+
expect(v.x).toBe(3);
|
|
234
|
+
expect(v.y).toBe(2);
|
|
235
|
+
expect(v.z).toBe(3);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
describe('equalsVector3', () => {
|
|
240
|
+
it('returns true if vectors are equal', () => {
|
|
241
|
+
const a = createVector3(1, 2, 3);
|
|
242
|
+
const b = createVector3(1, 2, 3);
|
|
243
|
+
expect(equalsVector3(a, b)).toBe(true);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('returns false if vectors are not equal', () => {
|
|
247
|
+
const a = createVector3(1, 2, 3);
|
|
248
|
+
const b = createVector3(4, 5, 6);
|
|
249
|
+
expect(equalsVector3(a, b)).toBe(false);
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
describe('getVector3AngleBetween', () => {
|
|
254
|
+
it('returns 0 for identical vectors', () => {
|
|
255
|
+
const a = createVector3(1, 0, 0);
|
|
256
|
+
expect(getVector3AngleBetween(a, a)).toBeCloseTo(0);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('returns PI/2 for perpendicular vectors', () => {
|
|
260
|
+
const a = createVector3(1, 0, 0);
|
|
261
|
+
const b = createVector3(0, 1, 0);
|
|
262
|
+
expect(getVector3AngleBetween(a, b)).toBeCloseTo(Math.PI / 2);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('returns PI for opposite vectors', () => {
|
|
266
|
+
const a = createVector3(1, 0, 0);
|
|
267
|
+
const b = createVector3(-1, 0, 0);
|
|
268
|
+
expect(getVector3AngleBetween(a, b)).toBeCloseTo(Math.PI);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('returns NaN for a zero-length vector', () => {
|
|
272
|
+
const a = createVector3(0, 0, 0);
|
|
273
|
+
const b = createVector3(1, 0, 0);
|
|
274
|
+
expect(getVector3AngleBetween(a, b)).toBeNaN();
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
describe('getVector3Distance', () => {
|
|
279
|
+
it('returns the distance between two vectors', () => {
|
|
280
|
+
const a = createVector3(1, 1, 1);
|
|
281
|
+
const b = createVector3(4, 5, 6);
|
|
282
|
+
expect(getVector3Distance(a, b)).toBeCloseTo(7.071068, 5);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe('getVector3DistanceSquared', () => {
|
|
287
|
+
it('returns the squared distance between two vectors', () => {
|
|
288
|
+
const a = createVector3(1, 1, 1);
|
|
289
|
+
const b = createVector3(4, 5, 6);
|
|
290
|
+
expect(getVector3DistanceSquared(a, b)).toBe(50);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
describe('getVector3Dot', () => {
|
|
295
|
+
it('returns the dot product of two vectors', () => {
|
|
296
|
+
const a = createVector3(1, 2, 3);
|
|
297
|
+
const b = createVector3(4, 5, 6);
|
|
298
|
+
expect(getVector3Dot(a, b)).toBe(32); // 1*4 + 2*5 + 3*6
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe('getVector3Length', () => {
|
|
303
|
+
it('returns the length of the vector', () => {
|
|
304
|
+
const v = createVector3(3, 4, 0);
|
|
305
|
+
expect(getVector3Length(v)).toBe(5);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('allows a vector-like object', () => {
|
|
309
|
+
const v = { x: 3, y: 4, z: 0 };
|
|
310
|
+
expect(getVector3Length(v)).toBe(5);
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
describe('getVector3LengthSquared', () => {
|
|
315
|
+
it('returns the squared length of the vector', () => {
|
|
316
|
+
const v = createVector3(3, 4, 0);
|
|
317
|
+
expect(getVector3LengthSquared(v)).toBe(25);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('allows a vector-like object', () => {
|
|
321
|
+
const v = { x: 3, y: 4, z: 0 };
|
|
322
|
+
expect(getVector3LengthSquared(v)).toBe(25);
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
describe('getVector3Spherical', () => {
|
|
327
|
+
it('returns radius, theta, phi for a known point', () => {
|
|
328
|
+
const v = createVector3(1, 0, 0);
|
|
329
|
+
const sph = createVector3();
|
|
330
|
+
getVector3Spherical(sph, v);
|
|
331
|
+
expect(sph.x).toBeCloseTo(1, 6); // radius
|
|
332
|
+
expect(sph.y).toBeCloseTo(Math.PI / 2, 6); // theta (from +Y)
|
|
333
|
+
expect(sph.z).toBeCloseTo(0, 6); // phi
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('handles zero vector', () => {
|
|
337
|
+
const sph = createVector3();
|
|
338
|
+
getVector3Spherical(sph, createVector3(0, 0, 0));
|
|
339
|
+
expect(sph.x).toBe(0);
|
|
340
|
+
expect(sph.y).toBe(0);
|
|
341
|
+
expect(sph.z).toBe(0);
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
describe('interpolateVector3', () => {
|
|
346
|
+
it('returns a at t=0 and b at t=1', () => {
|
|
347
|
+
const out = createVector3();
|
|
348
|
+
const a = createVector3(1, 2, 3);
|
|
349
|
+
const b = createVector3(4, 5, 6);
|
|
350
|
+
interpolateVector3(out, a, b, 0);
|
|
351
|
+
expect(out.x).toBe(1);
|
|
352
|
+
expect(out.y).toBe(2);
|
|
353
|
+
expect(out.z).toBe(3);
|
|
354
|
+
interpolateVector3(out, a, b, 1);
|
|
355
|
+
expect(out.x).toBe(4);
|
|
356
|
+
expect(out.y).toBe(5);
|
|
357
|
+
expect(out.z).toBe(6);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('interpolates midpoint at t=0.5', () => {
|
|
361
|
+
const out = createVector3();
|
|
362
|
+
interpolateVector3(out, createVector3(0, 0, 0), createVector3(2, 4, 6), 0.5);
|
|
363
|
+
expect(out.x).toBe(1);
|
|
364
|
+
expect(out.y).toBe(2);
|
|
365
|
+
expect(out.z).toBe(3);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it('supports out === a', () => {
|
|
369
|
+
const a = createVector3(0, 0, 0);
|
|
370
|
+
const b = createVector3(2, 4, 6);
|
|
371
|
+
interpolateVector3(a, a, b, 0.5);
|
|
372
|
+
expect(a.x).toBe(1);
|
|
373
|
+
expect(a.y).toBe(2);
|
|
374
|
+
expect(a.z).toBe(3);
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
describe('maxVector3', () => {
|
|
379
|
+
it('returns the component-wise maximum', () => {
|
|
380
|
+
const out = createVector3();
|
|
381
|
+
maxVector3(out, createVector3(1, 5, 2), createVector3(3, 2, 7));
|
|
382
|
+
expect(out.x).toBe(3);
|
|
383
|
+
expect(out.y).toBe(5);
|
|
384
|
+
expect(out.z).toBe(7);
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
describe('minVector3', () => {
|
|
389
|
+
it('returns the component-wise minimum', () => {
|
|
390
|
+
const out = createVector3();
|
|
391
|
+
minVector3(out, createVector3(1, 5, 2), createVector3(3, 2, 7));
|
|
392
|
+
expect(out.x).toBe(1);
|
|
393
|
+
expect(out.y).toBe(2);
|
|
394
|
+
expect(out.z).toBe(2);
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
describe('multiplyVector3', () => {
|
|
399
|
+
it('multiplies component-wise (Hadamard)', () => {
|
|
400
|
+
const out = createVector3();
|
|
401
|
+
multiplyVector3(out, createVector3(2, 3, 4), createVector3(5, 6, 7));
|
|
402
|
+
expect(out.x).toBe(10);
|
|
403
|
+
expect(out.y).toBe(18);
|
|
404
|
+
expect(out.z).toBe(28);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('supports out === a', () => {
|
|
408
|
+
const a = createVector3(2, 3, 4);
|
|
409
|
+
multiplyVector3(a, a, createVector3(5, 6, 7));
|
|
410
|
+
expect(a.x).toBe(10);
|
|
411
|
+
expect(a.y).toBe(18);
|
|
412
|
+
expect(a.z).toBe(28);
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
describe('nearEqualsVector3', () => {
|
|
417
|
+
it('returns true for identical vectors', () => {
|
|
418
|
+
const a = createVector3(1, 2, 3);
|
|
419
|
+
expect(nearEqualsVector3(a, a)).toBe(true);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('returns true when difference is within default tolerance', () => {
|
|
423
|
+
const a = createVector3(1, 2, 3);
|
|
424
|
+
const b = createVector3(1 + 1e-7, 2, 3);
|
|
425
|
+
expect(nearEqualsVector3(a, b)).toBe(true);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('returns false when difference exceeds default tolerance', () => {
|
|
429
|
+
const a = createVector3(1, 2, 3);
|
|
430
|
+
const b = createVector3(1 + 1e-5, 2, 3);
|
|
431
|
+
expect(nearEqualsVector3(a, b)).toBe(false);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('respects a custom tolerance', () => {
|
|
435
|
+
const a = createVector3(1, 2, 3);
|
|
436
|
+
const b = createVector3(1.05, 2, 3);
|
|
437
|
+
expect(nearEqualsVector3(a, b, 0.1)).toBe(true);
|
|
438
|
+
expect(nearEqualsVector3(a, b, 0.01)).toBe(false);
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
describe('negateVector3', () => {
|
|
443
|
+
it('inverts the values of the vector components', () => {
|
|
444
|
+
const v = createVector3(1, -2, 3);
|
|
445
|
+
const result = createVector3();
|
|
446
|
+
negateVector3(result, v);
|
|
447
|
+
expect(result.x).toBe(-1);
|
|
448
|
+
expect(result.y).toBe(2);
|
|
449
|
+
expect(result.z).toBe(-3);
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it('supports out === source', () => {
|
|
453
|
+
const v = createVector3(1, -2, 3);
|
|
454
|
+
negateVector3(v, v);
|
|
455
|
+
expect(v.x).toBe(-1);
|
|
456
|
+
expect(v.y).toBe(2);
|
|
457
|
+
expect(v.z).toBe(-3);
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
describe('normalizeVector3', () => {
|
|
462
|
+
it('normalizes the vector', () => {
|
|
463
|
+
const v = createVector3(3, 4, 0);
|
|
464
|
+
const result = createVector3();
|
|
465
|
+
const length = normalizeVector3(result, v);
|
|
466
|
+
expect(result.x).toBeCloseTo(0.6, 5);
|
|
467
|
+
expect(result.y).toBeCloseTo(0.8, 5);
|
|
468
|
+
expect(result.z).toBe(0);
|
|
469
|
+
expect(length).toBe(5);
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
it('supports out === source', () => {
|
|
473
|
+
const v = createVector3(3, 4, 0);
|
|
474
|
+
const length = normalizeVector3(v, v);
|
|
475
|
+
expect(v.x).toBeCloseTo(0.6, 5);
|
|
476
|
+
expect(v.y).toBeCloseTo(0.8, 5);
|
|
477
|
+
expect(v.z).toBe(0);
|
|
478
|
+
expect(length).toBe(5);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
it('writes zero to out for a zero-length source', () => {
|
|
482
|
+
const v = createVector3(0, 0, 0);
|
|
483
|
+
const result = createVector3(1, 2, 3);
|
|
484
|
+
const length = normalizeVector3(result, v);
|
|
485
|
+
expect(result.x).toBe(0);
|
|
486
|
+
expect(result.y).toBe(0);
|
|
487
|
+
expect(result.z).toBe(0);
|
|
488
|
+
expect(length).toBe(0);
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
describe('offsetVector3', () => {
|
|
493
|
+
it('offsets each component by a scalar per axis', () => {
|
|
494
|
+
const v = createVector3(1, 2, 3);
|
|
495
|
+
const result = createVector3();
|
|
496
|
+
offsetVector3(result, v, 10, 20, 30);
|
|
497
|
+
expect(result.x).toBe(11);
|
|
498
|
+
expect(result.y).toBe(22);
|
|
499
|
+
expect(result.z).toBe(33);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
it('works with negative deltas', () => {
|
|
503
|
+
const v = createVector3(1, 2, 3);
|
|
504
|
+
const result = createVector3();
|
|
505
|
+
offsetVector3(result, v, -5, -10, -15);
|
|
506
|
+
expect(result.x).toBe(-4);
|
|
507
|
+
expect(result.y).toBe(-8);
|
|
508
|
+
expect(result.z).toBe(-12);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it('supports out === source', () => {
|
|
512
|
+
const v = createVector3(1, 2, 3);
|
|
513
|
+
offsetVector3(v, v, 3, 4, 5);
|
|
514
|
+
expect(v.x).toBe(4);
|
|
515
|
+
expect(v.y).toBe(6);
|
|
516
|
+
expect(v.z).toBe(8);
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
describe('projectVector3', () => {
|
|
521
|
+
it('divides x and y by z to produce a 2D point', () => {
|
|
522
|
+
const v = createVector3(4, 6, 2);
|
|
523
|
+
const out = { x: 0, y: 0 };
|
|
524
|
+
projectVector3(out, v);
|
|
525
|
+
expect(out.x).toBe(2);
|
|
526
|
+
expect(out.y).toBe(3);
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
it('returns the original xy when z is 1', () => {
|
|
530
|
+
const v = createVector3(5, 7, 1);
|
|
531
|
+
const out = { x: 0, y: 0 };
|
|
532
|
+
projectVector3(out, v);
|
|
533
|
+
expect(out.x).toBe(5);
|
|
534
|
+
expect(out.y).toBe(7);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it('supports out === source', () => {
|
|
538
|
+
const v = createVector3(4, 6, 2);
|
|
539
|
+
projectVector3(v, v);
|
|
540
|
+
expect(v.x).toBe(2);
|
|
541
|
+
expect(v.y).toBe(3);
|
|
542
|
+
expect(v.z).toBe(2);
|
|
543
|
+
});
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
describe('reflectVector3', () => {
|
|
547
|
+
it('reflects incident vector about the x-axis normal', () => {
|
|
548
|
+
const out = createVector3();
|
|
549
|
+
// Reflect (1, -1, 0) about normal (1, 0, 0): result should be (-1, -1, 0)
|
|
550
|
+
reflectVector3(out, createVector3(1, -1, 0), createVector3(1, 0, 0));
|
|
551
|
+
expect(out.x).toBeCloseTo(-1, 6);
|
|
552
|
+
expect(out.y).toBeCloseTo(-1, 6);
|
|
553
|
+
expect(out.z).toBeCloseTo(0, 6);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it('reflecting a downward vector about upward normal gives upward', () => {
|
|
557
|
+
const out = createVector3();
|
|
558
|
+
reflectVector3(out, createVector3(0, -1, 0), createVector3(0, 1, 0));
|
|
559
|
+
expect(out.x).toBeCloseTo(0, 6);
|
|
560
|
+
expect(out.y).toBeCloseTo(1, 6);
|
|
561
|
+
expect(out.z).toBeCloseTo(0, 6);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it('supports out === incident', () => {
|
|
565
|
+
const v = createVector3(1, -1, 0);
|
|
566
|
+
reflectVector3(v, v, createVector3(1, 0, 0));
|
|
567
|
+
expect(v.x).toBeCloseTo(-1, 6);
|
|
568
|
+
expect(v.y).toBeCloseTo(-1, 6);
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
describe('scaleVector3', () => {
|
|
573
|
+
it('scales the vector by a scalar', () => {
|
|
574
|
+
const v = createVector3(1, 1, 1);
|
|
575
|
+
const result = createVector3();
|
|
576
|
+
scaleVector3(result, v, 2);
|
|
577
|
+
expect(result.x).toBe(2);
|
|
578
|
+
expect(result.y).toBe(2);
|
|
579
|
+
expect(result.z).toBe(2);
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
it('supports out === source', () => {
|
|
583
|
+
const v = createVector3(1, 1, 1);
|
|
584
|
+
scaleVector3(v, v, 2);
|
|
585
|
+
expect(v.x).toBe(2);
|
|
586
|
+
expect(v.y).toBe(2);
|
|
587
|
+
expect(v.z).toBe(2);
|
|
588
|
+
});
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
describe('setVector3', () => {
|
|
592
|
+
it('sets the values of the vector', () => {
|
|
593
|
+
const v = createVector3();
|
|
594
|
+
setVector3(v, 5, 10, 15);
|
|
595
|
+
expect(v.x).toBe(5);
|
|
596
|
+
expect(v.y).toBe(10);
|
|
597
|
+
expect(v.z).toBe(15);
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
it('modifies target when same object is passed as target', () => {
|
|
601
|
+
const v = createVector3(1, 2, 3);
|
|
602
|
+
setVector3(v, 5, 10, 15);
|
|
603
|
+
expect(v.x).toBe(5);
|
|
604
|
+
expect(v.y).toBe(10);
|
|
605
|
+
expect(v.z).toBe(15);
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
describe('setVector3FromFloat32Array', () => {
|
|
610
|
+
it('reads x/y/z from the array at offset', () => {
|
|
611
|
+
const arr = new Float32Array([0, 1, 2, 3, 4]);
|
|
612
|
+
const out = createVector3();
|
|
613
|
+
setVector3FromFloat32Array(out, 1, arr);
|
|
614
|
+
expect(out.x).toBe(1);
|
|
615
|
+
expect(out.y).toBe(2);
|
|
616
|
+
expect(out.z).toBe(3);
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
describe('setVector3FromSpherical', () => {
|
|
621
|
+
it('produces the north pole at theta=0', () => {
|
|
622
|
+
const out = createVector3();
|
|
623
|
+
setVector3FromSpherical(out, 5, 0, 0);
|
|
624
|
+
expect(out.x).toBeCloseTo(0, 6);
|
|
625
|
+
expect(out.y).toBeCloseTo(5, 6);
|
|
626
|
+
expect(out.z).toBeCloseTo(0, 6);
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
describe('setVector3FromVector4', () => {
|
|
631
|
+
it('copies x, y, z and drops w', () => {
|
|
632
|
+
const src = { x: 1, y: 2, z: 3, w: 99 };
|
|
633
|
+
const out = createVector3();
|
|
634
|
+
setVector3FromVector4(out, src);
|
|
635
|
+
expect(out.x).toBe(1);
|
|
636
|
+
expect(out.y).toBe(2);
|
|
637
|
+
expect(out.z).toBe(3);
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
it('does not perform a perspective divide', () => {
|
|
641
|
+
const src = { x: 2, y: 4, z: 6, w: 2 };
|
|
642
|
+
const out = createVector3();
|
|
643
|
+
setVector3FromVector4(out, src);
|
|
644
|
+
// Should NOT divide by w
|
|
645
|
+
expect(out.x).toBe(2);
|
|
646
|
+
expect(out.y).toBe(4);
|
|
647
|
+
expect(out.z).toBe(6);
|
|
648
|
+
});
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
describe('subtractVector3', () => {
|
|
652
|
+
it('returns a new vector when no target is passed', () => {
|
|
653
|
+
const a = createVector3(4, 5, 6);
|
|
654
|
+
const b = createVector3(1, 2, 3);
|
|
655
|
+
const result = createVector3();
|
|
656
|
+
subtractVector3(result, a, b);
|
|
657
|
+
expect(result.x).toBe(3);
|
|
658
|
+
expect(result.y).toBe(3);
|
|
659
|
+
expect(result.z).toBe(3);
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
it('supports out === source', () => {
|
|
663
|
+
const a = createVector3(4, 5, 6);
|
|
664
|
+
const b = createVector3(1, 2, 3);
|
|
665
|
+
subtractVector3(a, a, b);
|
|
666
|
+
expect(a.x).toBe(3);
|
|
667
|
+
expect(a.y).toBe(3);
|
|
668
|
+
expect(a.z).toBe(3);
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
it('supports out === other', () => {
|
|
672
|
+
const a = createVector3(4, 5, 6);
|
|
673
|
+
const b = createVector3(1, 2, 3);
|
|
674
|
+
subtractVector3(b, a, b);
|
|
675
|
+
expect(b.x).toBe(3);
|
|
676
|
+
expect(b.y).toBe(3);
|
|
677
|
+
expect(b.z).toBe(3);
|
|
678
|
+
});
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
describe('transformVector3ByMatrix3', () => {
|
|
682
|
+
it('transforms by an identity matrix', () => {
|
|
683
|
+
const identity = { m: new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1]) };
|
|
684
|
+
const out = createVector3();
|
|
685
|
+
transformVector3ByMatrix3(out, createVector3(1, 2, 3), identity);
|
|
686
|
+
expect(out.x).toBeCloseTo(1, 6);
|
|
687
|
+
expect(out.y).toBeCloseTo(2, 6);
|
|
688
|
+
expect(out.z).toBeCloseTo(3, 6);
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
it('transforms by a scale matrix', () => {
|
|
692
|
+
const scale = { m: new Float32Array([2, 0, 0, 0, 3, 0, 0, 0, 4]) };
|
|
693
|
+
const out = createVector3();
|
|
694
|
+
transformVector3ByMatrix3(out, createVector3(1, 1, 1), scale);
|
|
695
|
+
expect(out.x).toBeCloseTo(2, 6);
|
|
696
|
+
expect(out.y).toBeCloseTo(3, 6);
|
|
697
|
+
expect(out.z).toBeCloseTo(4, 6);
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
it('supports out === source', () => {
|
|
701
|
+
const identity = { m: new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1]) };
|
|
702
|
+
const v = createVector3(1, 2, 3);
|
|
703
|
+
transformVector3ByMatrix3(v, v, identity);
|
|
704
|
+
expect(v.x).toBe(1);
|
|
705
|
+
expect(v.y).toBe(2);
|
|
706
|
+
expect(v.z).toBe(3);
|
|
707
|
+
});
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
describe('writeVector3ToFloat32Array', () => {
|
|
711
|
+
it('writes x/y/z to the array at offset', () => {
|
|
712
|
+
const arr = new Float32Array(5);
|
|
713
|
+
writeVector3ToFloat32Array(arr, 1, createVector3(1, 2, 3));
|
|
714
|
+
expect(arr[0]).toBe(0);
|
|
715
|
+
expect(arr[1]).toBe(1);
|
|
716
|
+
expect(arr[2]).toBe(2);
|
|
717
|
+
expect(arr[3]).toBe(3);
|
|
718
|
+
});
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
describe('X_AXIS', () => {
|
|
722
|
+
it('returns the unit vector along the X-axis', () => {
|
|
723
|
+
const xAxis: Vector3 = VECTOR3_X_AXIS;
|
|
724
|
+
expect(xAxis).not.toBeNull();
|
|
725
|
+
expect(xAxis.x).toBe(1);
|
|
726
|
+
expect(xAxis.y).toBe(0);
|
|
727
|
+
expect(xAxis.z).toBe(0);
|
|
728
|
+
});
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
describe('Y_AXIS', () => {
|
|
732
|
+
it('returns the unit vector along the Y-axis', () => {
|
|
733
|
+
const yAxis: Vector3 = VECTOR3_Y_AXIS;
|
|
734
|
+
expect(yAxis).not.toBeNull();
|
|
735
|
+
expect(yAxis.x).toBe(0);
|
|
736
|
+
expect(yAxis.y).toBe(1);
|
|
737
|
+
expect(yAxis.z).toBe(0);
|
|
738
|
+
});
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
describe('Z_AXIS', () => {
|
|
742
|
+
it('returns the unit vector along the Z-axis', () => {
|
|
743
|
+
const zAxis: Vector3 = VECTOR3_Z_AXIS;
|
|
744
|
+
expect(zAxis).not.toBeNull();
|
|
745
|
+
expect(zAxis.x).toBe(0);
|
|
746
|
+
expect(zAxis.y).toBe(0);
|
|
747
|
+
expect(zAxis.z).toBe(1);
|
|
748
|
+
});
|
|
749
|
+
});
|