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