@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.
- package/README.md +15 -21
- package/build/clamp.d.ts +5 -5
- package/build/clamp.js +5 -5
- 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 +158 -171
- package/build/interpolation.d.ts.map +1 -1
- package/build/interpolation.js +162 -181
- package/build/interpolation.js.map +1 -1
- package/build/matrices/arithmetic.d.ts +132 -132
- package/build/matrices/arithmetic.d.ts.map +1 -1
- package/build/matrices/arithmetic.js +194 -226
- package/build/matrices/arithmetic.js.map +1 -1
- package/build/matrices/asserts.d.ts +30 -408
- package/build/matrices/asserts.d.ts.map +1 -1
- package/build/matrices/asserts.js +98 -542
- package/build/matrices/asserts.js.map +1 -1
- package/build/matrices/core.d.ts +117 -46
- package/build/matrices/core.d.ts.map +1 -1
- package/build/matrices/core.js +127 -103
- package/build/matrices/core.js.map +1 -1
- package/build/matrices/decompositions.d.ts +95 -96
- package/build/matrices/decompositions.d.ts.map +1 -1
- package/build/matrices/decompositions.js +119 -160
- package/build/matrices/decompositions.js.map +1 -1
- package/build/matrices/index.d.ts +0 -1
- package/build/matrices/index.d.ts.map +1 -1
- package/build/matrices/index.js +0 -3
- package/build/matrices/index.js.map +1 -1
- package/build/matrices/linear-algebra.d.ts +45 -8
- package/build/matrices/linear-algebra.d.ts.map +1 -1
- package/build/matrices/linear-algebra.js +76 -32
- package/build/matrices/linear-algebra.js.map +1 -1
- package/build/matrices/normalization.d.ts +29 -8
- 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 +116 -148
- package/build/matrices/transformations.d.ts.map +1 -1
- package/build/matrices/transformations.js +69 -91
- package/build/matrices/transformations.js.map +1 -1
- package/build/matrices/types.d.ts +32 -60
- 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 +29 -38
- package/build/quaternions/asserts.d.ts.map +1 -1
- package/build/quaternions/asserts.js +61 -77
- package/build/quaternions/asserts.js.map +1 -1
- package/build/quaternions/conversions.d.ts +26 -26
- package/build/quaternions/conversions.d.ts.map +1 -1
- package/build/quaternions/conversions.js +24 -24
- package/build/quaternions/core.d.ts +70 -69
- package/build/quaternions/core.d.ts.map +1 -1
- package/build/quaternions/core.js +71 -71
- package/build/quaternions/core.js.map +1 -1
- package/build/quaternions/index.d.ts +0 -1
- package/build/quaternions/index.d.ts.map +1 -1
- package/build/quaternions/index.js +0 -2
- package/build/quaternions/index.js.map +1 -1
- package/build/quaternions/interpolation.d.ts +16 -16
- package/build/quaternions/interpolation.d.ts.map +1 -1
- package/build/quaternions/interpolation.js +21 -21
- package/build/quaternions/interpolation.js.map +1 -1
- package/build/quaternions/predefined.d.ts +10 -10
- package/build/quaternions/predefined.d.ts.map +1 -1
- package/build/quaternions/predefined.js +9 -9
- package/build/quaternions/types.d.ts +23 -12
- 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 +50 -220
- package/build/vectors/asserts.d.ts.map +1 -1
- package/build/vectors/asserts.js +141 -327
- package/build/vectors/asserts.js.map +1 -1
- package/build/vectors/core.d.ts +182 -229
- package/build/vectors/core.d.ts.map +1 -1
- package/build/vectors/core.js +234 -288
- package/build/vectors/core.js.map +1 -1
- package/build/vectors/index.d.ts +0 -1
- package/build/vectors/index.d.ts.map +1 -1
- package/build/vectors/index.js +0 -2
- package/build/vectors/index.js.map +1 -1
- package/build/vectors/interpolation.d.ts +40 -40
- package/build/vectors/interpolation.d.ts.map +1 -1
- package/build/vectors/interpolation.js +75 -86
- package/build/vectors/interpolation.js.map +1 -1
- package/build/vectors/predefined.d.ts +31 -7
- package/build/vectors/predefined.d.ts.map +1 -1
- package/build/vectors/predefined.js +30 -6
- 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 +3 -2
- package/build/matrices/_exports.d.ts +0 -13
- package/build/matrices/_exports.d.ts.map +0 -1
- package/build/matrices/_exports.js +0 -13
- package/build/matrices/_exports.js.map +0 -1
- package/build/quaternions/_exports.d.ts +0 -11
- package/build/quaternions/_exports.d.ts.map +0 -1
- package/build/quaternions/_exports.js +0 -11
- package/build/quaternions/_exports.js.map +0 -1
- package/build/vectors/_exports.d.ts +0 -10
- package/build/vectors/_exports.d.ts.map +0 -1
- package/build/vectors/_exports.js +0 -10
- package/build/vectors/_exports.js.map +0 -1
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,11 +13,11 @@ import { AssertVector, AssertVectors, AssertVectorValue, VectorError, AssertVect
|
|
|
13
13
|
* @returns A new vector with identical components
|
|
14
14
|
*
|
|
15
15
|
* @example
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
162
|
-
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
|
|
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
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
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
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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
|
-
|
|
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
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
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
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
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
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
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
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
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
|
-
|
|
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
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
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
|
-
|
|
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
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
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
|
-
|
|
715
|
-
|
|
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
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
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
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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);
|