@pawells/math-extended 1.1.1 → 3.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/README.md +15 -21
- package/build/angles.d.ts +28 -18
- package/build/angles.d.ts.map +1 -1
- package/build/angles.js +28 -18
- package/build/angles.js.map +1 -1
- package/build/clamp.d.ts +2 -0
- package/build/clamp.d.ts.map +1 -1
- package/build/clamp.js +2 -0
- package/build/clamp.js.map +1 -1
- package/build/core.d.ts +23 -0
- package/build/core.d.ts.map +1 -0
- package/build/core.js +25 -0
- package/build/core.js.map +1 -0
- package/build/index.d.ts +1 -4
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -6
- package/build/index.js.map +1 -1
- package/build/interpolation.d.ts +62 -13
- package/build/interpolation.d.ts.map +1 -1
- package/build/interpolation.js +66 -23
- package/build/interpolation.js.map +1 -1
- package/build/matrices/arithmetic.d.ts +65 -76
- package/build/matrices/arithmetic.d.ts.map +1 -1
- package/build/matrices/arithmetic.js +65 -107
- package/build/matrices/arithmetic.js.map +1 -1
- package/build/matrices/asserts.d.ts +26 -273
- package/build/matrices/asserts.d.ts.map +1 -1
- package/build/matrices/asserts.js +106 -350
- package/build/matrices/asserts.js.map +1 -1
- package/build/matrices/core.d.ts +150 -79
- package/build/matrices/core.d.ts.map +1 -1
- package/build/matrices/core.js +128 -104
- package/build/matrices/core.js.map +1 -1
- package/build/matrices/decompositions.d.ts +41 -44
- package/build/matrices/decompositions.d.ts.map +1 -1
- package/build/matrices/decompositions.js +51 -94
- package/build/matrices/decompositions.js.map +1 -1
- package/build/matrices/index.d.ts +2 -2
- package/build/matrices/index.d.ts.map +1 -1
- package/build/matrices/index.js +2 -2
- package/build/matrices/index.js.map +1 -1
- package/build/matrices/linear-algebra.d.ts +56 -19
- package/build/matrices/linear-algebra.d.ts.map +1 -1
- package/build/matrices/linear-algebra.js +80 -36
- package/build/matrices/linear-algebra.js.map +1 -1
- package/build/matrices/normalization.d.ts +36 -15
- package/build/matrices/normalization.d.ts.map +1 -1
- package/build/matrices/normalization.js +32 -23
- package/build/matrices/normalization.js.map +1 -1
- package/build/matrices/transformations.d.ts +39 -52
- package/build/matrices/transformations.d.ts.map +1 -1
- package/build/matrices/transformations.js +8 -11
- package/build/matrices/transformations.js.map +1 -1
- package/build/matrices/types.d.ts +39 -67
- package/build/matrices/types.d.ts.map +1 -1
- package/build/matrices/types.js +63 -1
- package/build/matrices/types.js.map +1 -1
- package/build/quaternions/asserts.d.ts +114 -15
- package/build/quaternions/asserts.d.ts.map +1 -1
- package/build/quaternions/asserts.js +189 -51
- package/build/quaternions/asserts.js.map +1 -1
- package/build/quaternions/conversions.d.ts +18 -8
- package/build/quaternions/conversions.d.ts.map +1 -1
- package/build/quaternions/conversions.js +14 -4
- package/build/quaternions/conversions.js.map +1 -1
- package/build/quaternions/core.d.ts +31 -2
- package/build/quaternions/core.d.ts.map +1 -1
- package/build/quaternions/core.js +32 -4
- package/build/quaternions/core.js.map +1 -1
- package/build/quaternions/index.d.ts +2 -2
- package/build/quaternions/index.d.ts.map +1 -1
- package/build/quaternions/index.js +3 -2
- package/build/quaternions/index.js.map +1 -1
- package/build/quaternions/interpolation.d.ts +7 -1
- package/build/quaternions/interpolation.d.ts.map +1 -1
- package/build/quaternions/interpolation.js +12 -6
- package/build/quaternions/interpolation.js.map +1 -1
- package/build/quaternions/predefined.d.ts +7 -1
- package/build/quaternions/predefined.d.ts.map +1 -1
- package/build/quaternions/predefined.js +6 -0
- package/build/quaternions/predefined.js.map +1 -1
- package/build/quaternions/types.d.ts +24 -13
- package/build/quaternions/types.d.ts.map +1 -1
- package/build/quaternions/types.js +51 -1
- package/build/quaternions/types.js.map +1 -1
- package/build/random.d.ts +66 -20
- package/build/random.d.ts.map +1 -1
- package/build/random.js +73 -20
- package/build/random.js.map +1 -1
- package/build/vectors/asserts.d.ts +33 -99
- package/build/vectors/asserts.d.ts.map +1 -1
- package/build/vectors/asserts.js +145 -181
- package/build/vectors/asserts.js.map +1 -1
- package/build/vectors/core.d.ts +76 -44
- package/build/vectors/core.d.ts.map +1 -1
- package/build/vectors/core.js +128 -119
- package/build/vectors/core.js.map +1 -1
- package/build/vectors/index.d.ts.map +1 -1
- package/build/vectors/index.js +1 -0
- package/build/vectors/index.js.map +1 -1
- package/build/vectors/interpolation.d.ts +13 -1
- package/build/vectors/interpolation.d.ts.map +1 -1
- package/build/vectors/interpolation.js +48 -47
- package/build/vectors/interpolation.js.map +1 -1
- package/build/vectors/predefined.d.ts +73 -25
- package/build/vectors/predefined.d.ts.map +1 -1
- package/build/vectors/predefined.js +62 -18
- package/build/vectors/predefined.js.map +1 -1
- package/build/vectors/types.d.ts +26 -4
- package/build/vectors/types.d.ts.map +1 -1
- package/build/vectors/types.js +50 -1
- package/build/vectors/types.js.map +1 -1
- package/package.json +4 -3
package/build/vectors/core.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Provides a comprehensive set of vector operations with type safety and error checking.
|
|
4
4
|
*/
|
|
5
5
|
import { Clamp } from '../clamp.js';
|
|
6
|
-
import { AssertVector,
|
|
6
|
+
import { AssertVector, AssertVector2, AssertVector3, AssertVectorNonZero, AssertVectorSameSize, ValidateVectorSameSize, VectorError } from './asserts.js';
|
|
7
7
|
/**
|
|
8
8
|
* Creates a deep copy of a vector.
|
|
9
9
|
* Essential for avoiding mutations when performing operations that should preserve the original vector.
|
|
@@ -13,9 +13,11 @@ import { AssertVector, AssertVectors, AssertVectorValue, VectorError, AssertVect
|
|
|
13
13
|
* @returns A new vector with identical components
|
|
14
14
|
*
|
|
15
15
|
* @example
|
|
16
|
+
* ```typescript
|
|
16
17
|
* const original = [1, 2, 3];
|
|
17
18
|
* const copy = VectorClone(original);
|
|
18
19
|
* copy[0] = 10; // original remains unchanged
|
|
20
|
+
* ```
|
|
19
21
|
*/
|
|
20
22
|
export function VectorClone(vector) {
|
|
21
23
|
AssertVector(vector);
|
|
@@ -32,20 +34,21 @@ export function VectorClone(vector) {
|
|
|
32
34
|
* @returns True if vectors are equal within tolerance, false otherwise
|
|
33
35
|
*
|
|
34
36
|
* @example
|
|
37
|
+
* ```typescript
|
|
35
38
|
* const a = [1.0001, 2.0001];
|
|
36
39
|
* const b = [1.0002, 2.0002];
|
|
37
40
|
* const exactlyEqual = VectorEquals(a, b); // false
|
|
38
41
|
* const approximatelyEqual = VectorEquals(a, b, 0.001); // true
|
|
42
|
+
* ```
|
|
39
43
|
*/
|
|
40
44
|
export function VectorEquals(a, b, tolerance = 0) {
|
|
41
|
-
|
|
42
|
-
if (a.length !== b.length)
|
|
45
|
+
if (!ValidateVectorSameSize([a, b]))
|
|
43
46
|
return false;
|
|
47
|
+
AssertVector(a);
|
|
48
|
+
AssertVector(b);
|
|
44
49
|
for (let i = 0; i < a.length; i++) {
|
|
45
50
|
const av = a[i];
|
|
46
|
-
AssertVectorValue(av, {});
|
|
47
51
|
const bv = b[i];
|
|
48
|
-
AssertVectorValue(bv, {});
|
|
49
52
|
if (tolerance !== 0) {
|
|
50
53
|
if (Math.abs(av - bv) > tolerance)
|
|
51
54
|
return false;
|
|
@@ -64,16 +67,17 @@ export function VectorEquals(a, b, tolerance = 0) {
|
|
|
64
67
|
* @returns String representation of the vector
|
|
65
68
|
*
|
|
66
69
|
* @example
|
|
70
|
+
* ```typescript
|
|
67
71
|
* const vec = [1, 2, 3];
|
|
68
72
|
* const parens = VectorToString(vec, 'parens'); // "(1, 2, 3)"
|
|
69
73
|
* const brackets = VectorToString(vec, 'brackets'); // "[1, 2, 3]"
|
|
74
|
+
* ```
|
|
70
75
|
*/
|
|
71
76
|
export function VectorToString(vector, style = 'parens') {
|
|
72
77
|
AssertVector(vector);
|
|
73
78
|
const components = vector.map((v) => v.toString()).join(', ');
|
|
74
|
-
if (style === 'parens')
|
|
79
|
+
if (style === 'parens')
|
|
75
80
|
return `(${components})`;
|
|
76
|
-
}
|
|
77
81
|
return `[${components}]`;
|
|
78
82
|
}
|
|
79
83
|
/**
|
|
@@ -86,18 +90,20 @@ export function VectorToString(vector, style = 'parens') {
|
|
|
86
90
|
* @returns New vector where each component is the sum of corresponding components
|
|
87
91
|
*
|
|
88
92
|
* @example
|
|
93
|
+
* ```typescript
|
|
89
94
|
* const position = [10, 20, 30];
|
|
90
95
|
* const velocity = [1, -2, 0.5];
|
|
91
96
|
* const newPosition = VectorAdd(position, velocity); // [11, 18, 30.5]
|
|
97
|
+
* ```
|
|
92
98
|
*/
|
|
93
99
|
export function VectorAdd(a, b) {
|
|
94
|
-
|
|
100
|
+
AssertVector(a);
|
|
101
|
+
AssertVector(b);
|
|
102
|
+
AssertVectorSameSize([a, b]);
|
|
95
103
|
const result = [];
|
|
96
104
|
for (let i = 0; i < a.length; i++) {
|
|
97
105
|
const av = a[i];
|
|
98
|
-
AssertVectorValue(av, {});
|
|
99
106
|
const bv = b[i];
|
|
100
|
-
AssertVectorValue(bv, {});
|
|
101
107
|
result.push(av + bv);
|
|
102
108
|
}
|
|
103
109
|
return result;
|
|
@@ -112,18 +118,20 @@ export function VectorAdd(a, b) {
|
|
|
112
118
|
* @returns New vector where each component is the difference of corresponding components
|
|
113
119
|
*
|
|
114
120
|
* @example
|
|
121
|
+
* ```typescript
|
|
115
122
|
* const target = [100, 50, 0];
|
|
116
123
|
* const current = [80, 30, 0];
|
|
117
124
|
* const direction = VectorSubtract(target, current); // [20, 20, 0]
|
|
125
|
+
* ```
|
|
118
126
|
*/
|
|
119
127
|
export function VectorSubtract(a, b) {
|
|
120
|
-
|
|
128
|
+
AssertVector(a);
|
|
129
|
+
AssertVector(b);
|
|
130
|
+
AssertVectorSameSize([a, b]);
|
|
121
131
|
const result = [];
|
|
122
132
|
for (let i = 0; i < a.length; i++) {
|
|
123
133
|
const av = a[i];
|
|
124
|
-
AssertVectorValue(av, {}, { index: i });
|
|
125
134
|
const bv = b[i];
|
|
126
|
-
AssertVectorValue(bv, {}, { index: i });
|
|
127
135
|
result.push(av - bv);
|
|
128
136
|
}
|
|
129
137
|
return result;
|
|
@@ -138,28 +146,28 @@ export function VectorSubtract(a, b) {
|
|
|
138
146
|
* @returns New vector with multiplied components
|
|
139
147
|
*
|
|
140
148
|
* @example
|
|
149
|
+
* ```typescript
|
|
141
150
|
* const velocity = [10, 5, 0];
|
|
142
151
|
* const scaled = VectorMultiply(velocity, 2); // [20, 10, 0] - scalar multiplication
|
|
143
152
|
* const factors = [1, -1, 0.5];
|
|
144
153
|
* const componentWise = VectorMultiply(velocity, factors); // [10, -5, 0] - component-wise
|
|
154
|
+
* ```
|
|
145
155
|
*/
|
|
146
156
|
export function VectorMultiply(a, b) {
|
|
157
|
+
AssertVector(a);
|
|
147
158
|
const result = [];
|
|
148
159
|
if (Array.isArray(b)) {
|
|
149
|
-
|
|
150
|
-
|
|
160
|
+
AssertVector(b);
|
|
161
|
+
AssertVectorSameSize([a, b]);
|
|
151
162
|
for (let i = 0; i < a.length; i++) {
|
|
152
163
|
const av = a[i];
|
|
153
|
-
AssertVectorValue(av, {});
|
|
154
164
|
const bv = b[i];
|
|
155
|
-
AssertVectorValue(bv, {});
|
|
156
165
|
const prod = av * bv;
|
|
157
166
|
result.push(Object.is(prod, -0) ? 0 : prod);
|
|
158
167
|
}
|
|
159
168
|
}
|
|
160
169
|
else if (typeof b === 'number') {
|
|
161
170
|
for (const av of a) {
|
|
162
|
-
AssertVectorValue(av, {});
|
|
163
171
|
const prod = av * b;
|
|
164
172
|
result.push(Object.is(prod, -0) ? 0 : prod);
|
|
165
173
|
}
|
|
@@ -175,9 +183,11 @@ export function VectorMultiply(a, b) {
|
|
|
175
183
|
* @returns The straight-line distance between the two points represented by the vectors
|
|
176
184
|
*
|
|
177
185
|
* @example
|
|
186
|
+
* ```typescript
|
|
178
187
|
* const pointA = [0, 0, 0];
|
|
179
188
|
* const pointB = [3, 4, 0];
|
|
180
189
|
* const distance = VectorDistance(pointA, pointB); // 5.0 (3-4-5 triangle)
|
|
190
|
+
* ```
|
|
181
191
|
*/
|
|
182
192
|
export function VectorDistance(a, b) {
|
|
183
193
|
return Math.sqrt(VectorDistanceSquared(a, b));
|
|
@@ -192,18 +202,20 @@ export function VectorDistance(a, b) {
|
|
|
192
202
|
* @returns The squared distance between vectors
|
|
193
203
|
*
|
|
194
204
|
* @example
|
|
205
|
+
* ```typescript
|
|
195
206
|
* const pointA = [1, 1];
|
|
196
207
|
* const pointB = [4, 5];
|
|
197
208
|
* const distSq = VectorDistanceSquared(pointA, pointB); // 25 (faster than distance comparison)
|
|
209
|
+
* ```
|
|
198
210
|
*/
|
|
199
211
|
export function VectorDistanceSquared(a, b) {
|
|
200
|
-
|
|
212
|
+
AssertVector(a);
|
|
213
|
+
AssertVector(b);
|
|
214
|
+
AssertVectorSameSize([a, b]);
|
|
201
215
|
let sum = 0;
|
|
202
216
|
for (let i = 0; i < a.length; i++) {
|
|
203
217
|
const av = a[i];
|
|
204
|
-
AssertVectorValue(av, {});
|
|
205
218
|
const bv = b[i];
|
|
206
|
-
AssertVectorValue(bv, {});
|
|
207
219
|
const diff = bv - av;
|
|
208
220
|
sum += diff * diff;
|
|
209
221
|
}
|
|
@@ -219,21 +231,22 @@ export function VectorDistanceSquared(a, b) {
|
|
|
219
231
|
* @returns The dot product (scalar value)
|
|
220
232
|
*
|
|
221
233
|
* @example
|
|
234
|
+
* ```typescript
|
|
222
235
|
* const forward = [0, 0, 1];
|
|
223
236
|
* const direction = [0, 0, 2];
|
|
224
237
|
* const dot = VectorDot(forward, direction); // 2 (same direction)
|
|
225
|
-
*
|
|
226
238
|
* const perpendicular = [1, 0, 0];
|
|
227
239
|
* const dotPerp = VectorDot(forward, perpendicular); // 0 (perpendicular)
|
|
240
|
+
* ```
|
|
228
241
|
*/
|
|
229
242
|
export function VectorDot(a, b) {
|
|
230
|
-
|
|
243
|
+
AssertVector(a);
|
|
244
|
+
AssertVector(b);
|
|
245
|
+
AssertVectorSameSize([a, b]);
|
|
231
246
|
let dotProduct = 0;
|
|
232
247
|
for (let i = 0; i < a.length; i++) {
|
|
233
248
|
const av = a[i];
|
|
234
|
-
AssertVectorValue(av, {});
|
|
235
249
|
const bv = b[i];
|
|
236
|
-
AssertVectorValue(bv, {});
|
|
237
250
|
dotProduct += av * bv;
|
|
238
251
|
}
|
|
239
252
|
return dotProduct;
|
|
@@ -249,11 +262,12 @@ export function VectorDot(a, b) {
|
|
|
249
262
|
* @throws {VectorError} If the vector is zero or has infinite magnitude
|
|
250
263
|
*
|
|
251
264
|
* @example
|
|
265
|
+
* ```typescript
|
|
252
266
|
* const vector = [3, 4, 0];
|
|
253
267
|
* const normalized = VectorNormalize(vector); // [0.6, 0.8, 0] (magnitude = 1)
|
|
254
|
-
*
|
|
255
268
|
* const direction = [10, 0, 0];
|
|
256
269
|
* const unitDirection = VectorNormalize(direction); // [1, 0, 0]
|
|
270
|
+
* ```
|
|
257
271
|
*/
|
|
258
272
|
export function VectorNormalize(a) {
|
|
259
273
|
AssertVector(a);
|
|
@@ -265,7 +279,6 @@ export function VectorNormalize(a) {
|
|
|
265
279
|
const result = VectorClone(a);
|
|
266
280
|
for (let i = 0; i < a.length; i++) {
|
|
267
281
|
const av = a[i];
|
|
268
|
-
AssertVectorValue(av, {});
|
|
269
282
|
result[i] = av / magnitude;
|
|
270
283
|
}
|
|
271
284
|
return result;
|
|
@@ -278,19 +291,18 @@ export function VectorNormalize(a) {
|
|
|
278
291
|
* @returns The magnitude (length) of the vector
|
|
279
292
|
*
|
|
280
293
|
* @example
|
|
294
|
+
* ```typescript
|
|
281
295
|
* const velocity = [3, 4, 0];
|
|
282
296
|
* const speed = VectorMagnitude(velocity); // 5.0
|
|
283
|
-
*
|
|
284
297
|
* const unitVector = [1, 0, 0];
|
|
285
298
|
* const unitLength = VectorMagnitude(unitVector); // 1.0
|
|
299
|
+
* ```
|
|
286
300
|
*/
|
|
287
301
|
export function VectorMagnitude(a) {
|
|
288
302
|
AssertVector(a);
|
|
289
303
|
let sum = 0;
|
|
290
|
-
for (const av of a)
|
|
291
|
-
AssertVectorValue(av, {});
|
|
304
|
+
for (const av of a)
|
|
292
305
|
sum += av * av;
|
|
293
|
-
}
|
|
294
306
|
return Math.sqrt(sum);
|
|
295
307
|
}
|
|
296
308
|
/**
|
|
@@ -302,19 +314,18 @@ export function VectorMagnitude(a) {
|
|
|
302
314
|
* @returns New vector with absolute values of all components
|
|
303
315
|
*
|
|
304
316
|
* @example
|
|
317
|
+
* ```typescript
|
|
305
318
|
* const vector = [-3, 4, -2];
|
|
306
319
|
* const absolute = VectorAbs(vector); // [3, 4, 2]
|
|
307
|
-
*
|
|
308
320
|
* const mixed = [1.5, -2.7, 0];
|
|
309
321
|
* const absValues = VectorAbs(mixed); // [1.5, 2.7, 0]
|
|
322
|
+
* ```
|
|
310
323
|
*/
|
|
311
324
|
export function VectorAbs(a) {
|
|
312
325
|
AssertVector(a);
|
|
313
326
|
const result = [];
|
|
314
|
-
for (const av of a)
|
|
315
|
-
AssertVectorValue(av, {});
|
|
327
|
+
for (const av of a)
|
|
316
328
|
result.push(Math.abs(av));
|
|
317
|
-
}
|
|
318
329
|
return result;
|
|
319
330
|
}
|
|
320
331
|
/**
|
|
@@ -325,11 +336,12 @@ export function VectorAbs(a) {
|
|
|
325
336
|
* @returns True if all components are zero, false otherwise
|
|
326
337
|
*
|
|
327
338
|
* @example
|
|
339
|
+
* ```typescript
|
|
328
340
|
* const zero = [0, 0, 0];
|
|
329
341
|
* const isZero = VectorIsZero(zero); // true
|
|
330
|
-
*
|
|
331
342
|
* const notZero = [0, 0.001, 0];
|
|
332
343
|
* const isNotZero = VectorIsZero(notZero); // false
|
|
344
|
+
* ```
|
|
333
345
|
*/
|
|
334
346
|
export function VectorIsZero(vector) {
|
|
335
347
|
return vector.every((v) => v === 0);
|
|
@@ -345,16 +357,19 @@ export function VectorIsZero(vector) {
|
|
|
345
357
|
* @throws {VectorError} If either vector is zero
|
|
346
358
|
*
|
|
347
359
|
* @example
|
|
360
|
+
* ```typescript
|
|
348
361
|
* const right = [1, 0, 0];
|
|
349
362
|
* const up = [0, 1, 0];
|
|
350
363
|
* const angle = VectorAngle(right, up); // π/2 (90 degrees)
|
|
351
|
-
*
|
|
352
364
|
* const forward = [0, 0, 1];
|
|
353
365
|
* const backward = [0, 0, -1];
|
|
354
366
|
* const oppositeAngle = VectorAngle(forward, backward); // π (180 degrees)
|
|
367
|
+
* ```
|
|
355
368
|
*/
|
|
356
369
|
export function VectorAngle(a, b) {
|
|
357
|
-
|
|
370
|
+
AssertVector(a);
|
|
371
|
+
AssertVector(b);
|
|
372
|
+
AssertVectorSameSize([a, b]);
|
|
358
373
|
if (VectorIsZero(a) || VectorIsZero(b))
|
|
359
374
|
throw new VectorError('Cannot Calculate Angle with Zero Vectors');
|
|
360
375
|
const dot = VectorDot(a, b);
|
|
@@ -362,6 +377,32 @@ export function VectorAngle(a, b) {
|
|
|
362
377
|
const cosTheta = Clamp(dot / magProduct, -1, 1);
|
|
363
378
|
return Math.acos(cosTheta);
|
|
364
379
|
}
|
|
380
|
+
/**
|
|
381
|
+
* Limits (clamps) the magnitude of a vector to a maximum value.
|
|
382
|
+
* If the vector's magnitude exceeds the maximum, scales it down proportionally while preserving direction.
|
|
383
|
+
* If the magnitude is below the maximum, the vector is returned unchanged.
|
|
384
|
+
*
|
|
385
|
+
* @param vector - The input vector to limit
|
|
386
|
+
* @param maxMagnitude - The maximum allowed magnitude (must be >= 0)
|
|
387
|
+
* @returns A new vector with magnitude limited to maxMagnitude
|
|
388
|
+
* @throws {VectorError} If vector is invalid or maxMagnitude is invalid
|
|
389
|
+
*
|
|
390
|
+
* @example
|
|
391
|
+
* ```typescript
|
|
392
|
+
* const v = [3, 4]; // magnitude 5
|
|
393
|
+
* VectorLimit(v, 3); // [1.8, 2.4] — magnitude now 3
|
|
394
|
+
* ```
|
|
395
|
+
*/
|
|
396
|
+
export function VectorLimit(vector, maxMagnitude) {
|
|
397
|
+
AssertVector(vector);
|
|
398
|
+
if (maxMagnitude < 0)
|
|
399
|
+
throw new VectorError(`maxMagnitude must be non-negative: ${maxMagnitude}`);
|
|
400
|
+
const mag = VectorMagnitude(vector);
|
|
401
|
+
if (mag <= maxMagnitude || mag === 0)
|
|
402
|
+
return VectorClone(vector);
|
|
403
|
+
const scale = maxMagnitude / mag;
|
|
404
|
+
return VectorMultiply(vector, scale);
|
|
405
|
+
}
|
|
365
406
|
/**
|
|
366
407
|
* Rotates a 2D vector by the specified angle in radians.
|
|
367
408
|
* Essential for 2D transformations, sprite rotations, and directional calculations.
|
|
@@ -371,9 +412,11 @@ export function VectorAngle(a, b) {
|
|
|
371
412
|
* @returns New rotated 2D vector
|
|
372
413
|
*
|
|
373
414
|
* @example
|
|
415
|
+
* ```typescript
|
|
374
416
|
* const right = [1, 0];
|
|
375
417
|
* const rotated90 = Vector2Rotate(right, Math.PI / 2); // [0, 1] (up)
|
|
376
418
|
* const rotated180 = Vector2Rotate(right, Math.PI); // [-1, 0] (left)
|
|
419
|
+
* ```
|
|
377
420
|
*/
|
|
378
421
|
export function Vector2Rotate(vector, radians) {
|
|
379
422
|
AssertVector2(vector);
|
|
@@ -393,9 +436,11 @@ export function Vector2Rotate(vector, radians) {
|
|
|
393
436
|
* @returns Unit vector pointing in the specified direction
|
|
394
437
|
*
|
|
395
438
|
* @example
|
|
439
|
+
* ```typescript
|
|
396
440
|
* const right = Vector2FromAngle(0); // [1, 0]
|
|
397
441
|
* const up = Vector2FromAngle(Math.PI / 2); // [0, 1]
|
|
398
442
|
* const diagonal = Vector2FromAngle(Math.PI / 4); // [0.707, 0.707]
|
|
443
|
+
* ```
|
|
399
444
|
*/
|
|
400
445
|
export function Vector2FromAngle(radians) {
|
|
401
446
|
return [Math.cos(radians), Math.sin(radians)];
|
|
@@ -410,10 +455,12 @@ export function Vector2FromAngle(radians) {
|
|
|
410
455
|
* @returns Scalar cross product (positive = counterclockwise, negative = clockwise)
|
|
411
456
|
*
|
|
412
457
|
* @example
|
|
458
|
+
* ```typescript
|
|
413
459
|
* const right = [1, 0];
|
|
414
460
|
* const up = [0, 1];
|
|
415
461
|
* const cross = Vector2Cross(right, up); // 1 (counterclockwise)
|
|
416
462
|
* const crossReverse = Vector2Cross(up, right); // -1 (clockwise)
|
|
463
|
+
* ```
|
|
417
464
|
*/
|
|
418
465
|
export function Vector2Cross(a, b) {
|
|
419
466
|
AssertVector2(a);
|
|
@@ -431,9 +478,11 @@ export function Vector2Cross(a, b) {
|
|
|
431
478
|
* @throws {VectorError} If vector b is zero
|
|
432
479
|
*
|
|
433
480
|
* @example
|
|
481
|
+
* ```typescript
|
|
434
482
|
* const force = [5, 3, 0];
|
|
435
483
|
* const surface = [1, 0, 0];
|
|
436
484
|
* const perpendicular = Vector3Reject(force, surface); // [0, 3, 0]
|
|
485
|
+
* ```
|
|
437
486
|
*/
|
|
438
487
|
export function Vector3Reject(a, b) {
|
|
439
488
|
AssertVector3(a);
|
|
@@ -455,22 +504,24 @@ export function Vector3Reject(a, b) {
|
|
|
455
504
|
* @throws {VectorError} If vector b is zero
|
|
456
505
|
*
|
|
457
506
|
* @example
|
|
507
|
+
* ```typescript
|
|
458
508
|
* const force = [5, 3, 0];
|
|
459
509
|
* const surface = [1, 0, 0];
|
|
460
510
|
* const parallel = VectorProject(force, surface); // [5, 0, 0]
|
|
511
|
+
* ```
|
|
461
512
|
*/
|
|
462
513
|
export function VectorProject(a, b) {
|
|
463
|
-
|
|
514
|
+
AssertVector(a);
|
|
515
|
+
AssertVector(b);
|
|
516
|
+
AssertVectorSameSize([a, b]);
|
|
464
517
|
if (VectorIsZero(b))
|
|
465
518
|
throw new VectorError('Cannot project onto a zero vector');
|
|
466
519
|
const dot = VectorDot(a, b);
|
|
467
520
|
const magSquared = VectorDot(b, b);
|
|
468
521
|
const scalar = dot / magSquared;
|
|
469
522
|
const result = [];
|
|
470
|
-
for (const bv of b)
|
|
471
|
-
AssertVectorValue(bv, {});
|
|
523
|
+
for (const bv of b)
|
|
472
524
|
result.push(scalar * bv);
|
|
473
|
-
}
|
|
474
525
|
return result;
|
|
475
526
|
}
|
|
476
527
|
/**
|
|
@@ -484,9 +535,11 @@ export function VectorProject(a, b) {
|
|
|
484
535
|
* @throws {VectorError} If the normal is a zero vector
|
|
485
536
|
*
|
|
486
537
|
* @example
|
|
538
|
+
* ```typescript
|
|
487
539
|
* const incoming = [1, -1, 0];
|
|
488
540
|
* const normal = [0, 1, 0]; // surface normal (upward)
|
|
489
541
|
* const reflected = Vector3Reflect(incoming, normal); // [1, 1, 0]
|
|
542
|
+
* ```
|
|
490
543
|
*/
|
|
491
544
|
export function Vector3Reflect(incident, normal) {
|
|
492
545
|
AssertVector3(incident);
|
|
@@ -506,11 +559,12 @@ export function Vector3Reflect(incident, normal) {
|
|
|
506
559
|
* @returns Vector perpendicular to both a and b (following right-hand rule)
|
|
507
560
|
*
|
|
508
561
|
* @example
|
|
562
|
+
* ```typescript
|
|
509
563
|
* const right = [1, 0, 0];
|
|
510
564
|
* const forward = [0, 0, 1];
|
|
511
565
|
* const up = Vector3Cross(right, forward); // [0, 1, 0]
|
|
512
|
-
*
|
|
513
566
|
* const normal = Vector3Cross([1, 0, 0], [0, 1, 0]); // [0, 0, 1]
|
|
567
|
+
* ```
|
|
514
568
|
*/
|
|
515
569
|
export function Vector3Cross(a, b) {
|
|
516
570
|
AssertVector3(a);
|
|
@@ -531,9 +585,11 @@ export function Vector3Cross(a, b) {
|
|
|
531
585
|
* @returns Magnitude of the cross product
|
|
532
586
|
*
|
|
533
587
|
* @example
|
|
588
|
+
* ```typescript
|
|
534
589
|
* const side1 = [3, 0, 0];
|
|
535
590
|
* const side2 = [0, 4, 0];
|
|
536
591
|
* const area = VectorCrossMagnitude(side1, side2); // 12 (area of rectangle)
|
|
592
|
+
* ```
|
|
537
593
|
*/
|
|
538
594
|
export function VectorCrossMagnitude(a, b) {
|
|
539
595
|
AssertVector3(a);
|
|
@@ -552,10 +608,12 @@ export function VectorCrossMagnitude(a, b) {
|
|
|
552
608
|
* @returns Signed volume (positive = right-handed orientation)
|
|
553
609
|
*
|
|
554
610
|
* @example
|
|
611
|
+
* ```typescript
|
|
555
612
|
* const x = [1, 0, 0];
|
|
556
613
|
* const y = [0, 1, 0];
|
|
557
614
|
* const z = [0, 0, 1];
|
|
558
615
|
* const volume = Vector3ScalarTripleProduct(x, y, z); // 1 (unit cube)
|
|
616
|
+
* ```
|
|
559
617
|
*/
|
|
560
618
|
export function Vector3ScalarTripleProduct(a, b, c) {
|
|
561
619
|
AssertVector3(a);
|
|
@@ -575,10 +633,12 @@ export function Vector3ScalarTripleProduct(a, b, c) {
|
|
|
575
633
|
* @returns Vector result of a × (b × c)
|
|
576
634
|
*
|
|
577
635
|
* @example
|
|
636
|
+
* ```typescript
|
|
578
637
|
* const a = [1, 0, 0];
|
|
579
638
|
* const b = [0, 1, 0];
|
|
580
639
|
* const c = [0, 0, 1];
|
|
581
640
|
* const result = Vector3TripleProduct(a, b, c); // [0, 0, 0]
|
|
641
|
+
* ```
|
|
582
642
|
*/
|
|
583
643
|
export function Vector3TripleProduct(a, b, c) {
|
|
584
644
|
AssertVector3(a);
|
|
@@ -598,20 +658,22 @@ export function Vector3TripleProduct(a, b, c) {
|
|
|
598
658
|
* @returns The reflected vector
|
|
599
659
|
*
|
|
600
660
|
* @example
|
|
661
|
+
* ```typescript
|
|
601
662
|
* const incoming = [1, -1, 0];
|
|
602
663
|
* const wall = [0, 1, 0]; // vertical wall normal
|
|
603
664
|
* const bounced = VectorReflect(incoming, wall); // [1, 1, 0]
|
|
665
|
+
* ```
|
|
604
666
|
*/
|
|
605
667
|
export function VectorReflect(incident, normal) {
|
|
606
|
-
|
|
668
|
+
AssertVector(incident);
|
|
669
|
+
AssertVector(normal);
|
|
670
|
+
AssertVectorSameSize([incident, normal]);
|
|
607
671
|
const normalizedNormal = VectorNormalize(normal);
|
|
608
672
|
const dot = VectorDot(incident, normalizedNormal);
|
|
609
673
|
const result = [];
|
|
610
674
|
for (let i = 0; i < incident.length; i++) {
|
|
611
675
|
const iv = incident[i];
|
|
612
|
-
AssertVectorValue(iv, {});
|
|
613
676
|
const nnv = normalizedNormal[i];
|
|
614
|
-
AssertVectorValue(nnv, {});
|
|
615
677
|
result.push(iv - (2 * dot * nnv));
|
|
616
678
|
}
|
|
617
679
|
return result;
|
|
@@ -626,24 +688,18 @@ export function VectorReflect(incident, normal) {
|
|
|
626
688
|
* @returns Vector with all components negated
|
|
627
689
|
*
|
|
628
690
|
* @example
|
|
691
|
+
* ```typescript
|
|
629
692
|
* const forward = [0, 0, 1];
|
|
630
693
|
* const backward = VectorNegate(forward); // [0, 0, -1]
|
|
631
|
-
*
|
|
632
694
|
* const velocity = [5, -3, 2];
|
|
633
695
|
* const opposite = VectorNegate(velocity); // [-5, 3, -2]
|
|
696
|
+
* ```
|
|
634
697
|
*/
|
|
635
698
|
export function VectorNegate(a) {
|
|
699
|
+
AssertVector(a);
|
|
636
700
|
const result = [];
|
|
637
|
-
for (const av of a)
|
|
638
|
-
|
|
639
|
-
// Special handling for zero to avoid negative zero (-0)
|
|
640
|
-
if (av === 0) {
|
|
641
|
-
result.push(0);
|
|
642
|
-
}
|
|
643
|
-
else {
|
|
644
|
-
result.push(-1 * av);
|
|
645
|
-
}
|
|
646
|
-
}
|
|
701
|
+
for (const av of a)
|
|
702
|
+
result.push(av === 0 ? 0 : -1 * av);
|
|
647
703
|
return result;
|
|
648
704
|
}
|
|
649
705
|
/**
|
|
@@ -657,21 +713,22 @@ export function VectorNegate(a) {
|
|
|
657
713
|
* @throws {VectorError} If any divisor component is zero
|
|
658
714
|
*
|
|
659
715
|
* @example
|
|
716
|
+
* ```typescript
|
|
660
717
|
* const velocity = [20, 10, 0];
|
|
661
718
|
* const halved = VectorDivide(velocity, 2); // [10, 5, 0] - scalar division
|
|
662
719
|
* const factors = [2, 5, 1];
|
|
663
720
|
* const componentWise = VectorDivide(velocity, factors); // [10, 2, 0] - component-wise
|
|
721
|
+
* ```
|
|
664
722
|
*/
|
|
665
723
|
export function VectorDivide(a, b) {
|
|
724
|
+
AssertVector(a);
|
|
666
725
|
const result = [];
|
|
667
726
|
if (Array.isArray(b)) {
|
|
668
|
-
|
|
669
|
-
|
|
727
|
+
AssertVector(b);
|
|
728
|
+
AssertVectorSameSize([a, b]);
|
|
670
729
|
for (let i = 0; i < a.length; i++) {
|
|
671
730
|
const av = a[i];
|
|
672
|
-
AssertVectorValue(av, {});
|
|
673
731
|
const bv = b[i];
|
|
674
|
-
AssertVectorValue(bv, {});
|
|
675
732
|
if (bv === 0)
|
|
676
733
|
throw new VectorError(`Division by zero at component [${i}]`);
|
|
677
734
|
const quot = av / bv;
|
|
@@ -682,7 +739,6 @@ export function VectorDivide(a, b) {
|
|
|
682
739
|
if (b === 0)
|
|
683
740
|
throw new VectorError('Division by zero scalar');
|
|
684
741
|
for (const av of a) {
|
|
685
|
-
AssertVectorValue(av, {});
|
|
686
742
|
const quot = av / b;
|
|
687
743
|
result.push(Object.is(quot, -0) ? 0 : quot);
|
|
688
744
|
}
|
|
@@ -701,75 +757,25 @@ export function VectorDivide(a, b) {
|
|
|
701
757
|
* @returns New vector with each component clamped between min and max
|
|
702
758
|
*
|
|
703
759
|
* @example
|
|
760
|
+
* ```typescript
|
|
704
761
|
* const v = [5, -3, 12, 0];
|
|
705
762
|
* VectorClamp(v, 0, 10); // [5, 0, 10, 0] - scalar bounds
|
|
706
|
-
*
|
|
707
763
|
* const mins = [0, -5, 0, -1];
|
|
708
764
|
* const maxs = [10, 5, 8, 1];
|
|
709
765
|
* VectorClamp(v, mins, maxs); // [5, -3, 8, 0] - per-component bounds
|
|
766
|
+
* ```
|
|
710
767
|
*/
|
|
711
768
|
export function VectorClamp(a, min, max) {
|
|
712
769
|
AssertVector(a);
|
|
713
770
|
const result = [];
|
|
714
771
|
for (let i = 0; i < a.length; i++) {
|
|
715
772
|
const av = a[i];
|
|
716
|
-
AssertVectorValue(av, {});
|
|
717
773
|
const minV = Array.isArray(min) ? min[i] : min;
|
|
718
774
|
const maxV = Array.isArray(max) ? max[i] : max;
|
|
719
775
|
result.push(Math.max(minV, Math.min(av, maxV)));
|
|
720
776
|
}
|
|
721
777
|
return result;
|
|
722
778
|
}
|
|
723
|
-
/**
|
|
724
|
-
* Limits the magnitude of a vector to a maximum value.
|
|
725
|
-
* If the vector's magnitude exceeds the limit, scales it down proportionally.
|
|
726
|
-
* Preserves direction while constraining magnitude.
|
|
727
|
-
*
|
|
728
|
-
* @template T - The vector type extending TVector
|
|
729
|
-
* @param a - Vector to limit
|
|
730
|
-
* @param max - Maximum allowed magnitude
|
|
731
|
-
* @returns Vector with magnitude limited to max
|
|
732
|
-
* @throws {VectorError} If max is negative
|
|
733
|
-
*
|
|
734
|
-
* @example
|
|
735
|
-
* const velocity = [15, 20, 0]; // magnitude ≈ 25
|
|
736
|
-
* const limited = VectorLimit(velocity, 10); // magnitude = 10, same direction
|
|
737
|
-
*
|
|
738
|
-
* const small = [1, 1, 0]; // magnitude ≈ 1.414
|
|
739
|
-
* const unchanged = VectorLimit(small, 5); // unchanged since already under limit
|
|
740
|
-
*/
|
|
741
|
-
export function VectorLimit(a, max) {
|
|
742
|
-
if (max < 0)
|
|
743
|
-
throw new VectorError('Maximum magnitude cannot be negative');
|
|
744
|
-
const magnitude = VectorMagnitude(a);
|
|
745
|
-
if (magnitude <= max || magnitude === 0)
|
|
746
|
-
return VectorClone(a);
|
|
747
|
-
const scaleFactor = max / magnitude;
|
|
748
|
-
return VectorMultiply(a, scaleFactor);
|
|
749
|
-
}
|
|
750
|
-
/**
|
|
751
|
-
* Validates if the input is a properly formatted vector.
|
|
752
|
-
* Performs comprehensive validation without throwing errors.
|
|
753
|
-
* Useful for input validation and defensive programming.
|
|
754
|
-
*
|
|
755
|
-
* @param vector - Input to validate
|
|
756
|
-
* @returns True if input is a valid vector, false otherwise
|
|
757
|
-
*
|
|
758
|
-
* @example
|
|
759
|
-
* const valid = VectorIsValid([1, 2, 3]); // true
|
|
760
|
-
* const invalid = VectorIsValid("not a vector"); // false
|
|
761
|
-
* const nullVector = VectorIsValid(null); // false
|
|
762
|
-
* const emptyArray = VectorIsValid([]); // depends on implementation
|
|
763
|
-
*/
|
|
764
|
-
export function VectorIsValid(vector) {
|
|
765
|
-
try {
|
|
766
|
-
AssertVector(vector);
|
|
767
|
-
return true;
|
|
768
|
-
}
|
|
769
|
-
catch {
|
|
770
|
-
return false;
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
779
|
/**
|
|
774
780
|
* Performs Gram-Schmidt orthogonalization on a set of vectors.
|
|
775
781
|
* Converts a set of linearly independent vectors into an orthogonal (or orthonormal) set.
|
|
@@ -782,9 +788,11 @@ export function VectorIsValid(vector) {
|
|
|
782
788
|
* @throws {VectorError} If vectors are linearly dependent or invalid
|
|
783
789
|
*
|
|
784
790
|
* @example
|
|
791
|
+
* ```typescript
|
|
785
792
|
* const vectors = [[1, 1, 0], [1, 0, 1], [0, 1, 1]];
|
|
786
793
|
* const orthogonal = VectorGramSchmidt(vectors); // Orthogonal set
|
|
787
794
|
* const orthonormal = VectorGramSchmidt(vectors, true); // Orthonormal set
|
|
795
|
+
* ```
|
|
788
796
|
*/
|
|
789
797
|
export function VectorGramSchmidt(vectors, normalize = false) {
|
|
790
798
|
if (vectors.length === 0)
|
|
@@ -797,21 +805,22 @@ export function VectorGramSchmidt(vectors, normalize = false) {
|
|
|
797
805
|
AssertVector(vector);
|
|
798
806
|
if (vector.length !== dimension)
|
|
799
807
|
throw new VectorError(`GramSchmidt: Vector at index ${i} has different dimension than first vector. Expected ${dimension}, got ${vector.length}`);
|
|
800
|
-
|
|
801
|
-
throw new VectorError(`GramSchmidt: Vector at index ${i} is a zero vector. Cannot orthogonalize zero vectors.`);
|
|
808
|
+
AssertVectorNonZero(vector, `GramSchmidt vector at index ${i}`);
|
|
802
809
|
}
|
|
803
810
|
const result = [];
|
|
804
811
|
for (const [i, currentVector] of vectors.entries()) {
|
|
805
812
|
AssertVector(currentVector);
|
|
806
|
-
|
|
813
|
+
// Start with a clone; TVectorResult<T> is dimensionally equivalent to T at runtime
|
|
814
|
+
const orthogonalBase = VectorClone(currentVector);
|
|
815
|
+
let orthogonalVector = orthogonalBase;
|
|
807
816
|
for (let j = 0; j < i; j++) {
|
|
808
817
|
const previousVector = result[j];
|
|
809
818
|
AssertVector(previousVector);
|
|
819
|
+
// Project current onto previous: TVectorResult<T> shares dimensionality with T
|
|
810
820
|
const projection = VectorProject(currentVector, previousVector);
|
|
811
821
|
orthogonalVector = VectorSubtract(orthogonalVector, projection);
|
|
812
822
|
}
|
|
813
|
-
|
|
814
|
-
throw new VectorError(`GramSchmidt: Vector at index ${i} is linearly dependent on previous vectors. Cannot orthogonalize linearly dependent vectors.`);
|
|
823
|
+
AssertVectorNonZero(orthogonalVector, `GramSchmidt orthogonalized vector at index ${i}`);
|
|
815
824
|
if (normalize)
|
|
816
825
|
orthogonalVector = VectorNormalize(orthogonalVector);
|
|
817
826
|
result.push(orthogonalVector);
|