@pawells/math-extended 1.0.4 → 1.1.1
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 +5 -5
- package/build/angles.d.ts +33 -3
- package/build/angles.d.ts.map +1 -1
- package/build/angles.js +57 -35
- package/build/angles.js.map +1 -1
- package/build/interpolation.d.ts +378 -63
- package/build/interpolation.d.ts.map +1 -1
- package/build/interpolation.js +380 -63
- package/build/interpolation.js.map +1 -1
- package/build/matrices/asserts.d.ts +12 -15
- package/build/matrices/asserts.d.ts.map +1 -1
- package/build/matrices/asserts.js +12 -15
- package/build/matrices/asserts.js.map +1 -1
- package/build/matrices/core.d.ts +11 -12
- package/build/matrices/core.d.ts.map +1 -1
- package/build/matrices/core.js +11 -12
- package/build/matrices/core.js.map +1 -1
- package/build/matrices/decompositions.d.ts +23 -24
- package/build/matrices/decompositions.d.ts.map +1 -1
- package/build/matrices/decompositions.js +85 -141
- package/build/matrices/decompositions.js.map +1 -1
- package/build/matrices/linear-algebra.d.ts +6 -1
- package/build/matrices/linear-algebra.d.ts.map +1 -1
- package/build/matrices/linear-algebra.js +54 -19
- package/build/matrices/linear-algebra.js.map +1 -1
- package/build/matrices/transformations.d.ts +9 -12
- package/build/matrices/transformations.d.ts.map +1 -1
- package/build/matrices/transformations.js +9 -12
- package/build/matrices/transformations.js.map +1 -1
- package/build/quaternions/core.d.ts +6 -2
- package/build/quaternions/core.d.ts.map +1 -1
- package/build/quaternions/core.js +17 -10
- package/build/quaternions/core.js.map +1 -1
- package/build/random.d.ts +10 -2
- package/build/random.d.ts.map +1 -1
- package/build/random.js +25 -17
- package/build/random.js.map +1 -1
- package/build/vectors/core.d.ts.map +1 -1
- package/build/vectors/core.js +5 -9
- package/build/vectors/core.js.map +1 -1
- package/package.json +11 -14
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@pawells/math-extended)
|
|
4
4
|
[](https://github.com/PhillipAWells/math-extended/releases)
|
|
5
5
|
[](https://github.com/PhillipAWells/math-extended/actions/workflows/ci.yml)
|
|
6
|
-
[](https://nodejs.org)
|
|
7
7
|
[](./LICENSE)
|
|
8
8
|
[](https://github.com/sponsors/PhillipAWells)
|
|
9
9
|
|
|
@@ -88,7 +88,7 @@ QuaternionUtils.QuaternionSLERP(q1, q2, 0.5);
|
|
|
88
88
|
|--------|-------------|
|
|
89
89
|
| `RandomInt(min, max)` | Random integer in `[min, max]` |
|
|
90
90
|
| `RandomFloat(min, max)` | Random float in `[min, max)` |
|
|
91
|
-
| `RandomBool(probability?)` | Random boolean with optional probability |
|
|
91
|
+
| `RandomBool(probability?)` | Random boolean with optional probability; throws `RangeError` if probability is outside `[0, 1]` |
|
|
92
92
|
| `RandomNormal(mean?, stdDev?)` | Normal-distributed random number |
|
|
93
93
|
| `RandomChoice(array)` | Random element from an array |
|
|
94
94
|
| `RandomSample(array, count)` | `count` unique random elements |
|
|
@@ -226,7 +226,7 @@ Matrices are `number[][]` arrays (`IMatrix`, `IMatrix1`–`IMatrix4`). All opera
|
|
|
226
226
|
|
|
227
227
|
| Export | Description |
|
|
228
228
|
|--------|-------------|
|
|
229
|
-
| `MatrixLU(m)` | LU decomposition `{ L, U }` |
|
|
229
|
+
| `MatrixLU(m)` | LU decomposition with partial pivoting `{ L, U, P }` |
|
|
230
230
|
| `MatrixQR(m)` | QR decomposition `{ Q, R }` |
|
|
231
231
|
| `MatrixCholesky(m)` | Cholesky decomposition `L` |
|
|
232
232
|
| `MatrixEigen(m)` | Eigenvalue decomposition `{ eigenvalues, eigenvectors }` |
|
|
@@ -328,14 +328,14 @@ yarn watch # Watch mode
|
|
|
328
328
|
yarn typecheck # Type check without building
|
|
329
329
|
yarn lint # ESLint
|
|
330
330
|
yarn lint:fix # ESLint with auto-fix
|
|
331
|
-
yarn test # Run tests (
|
|
331
|
+
yarn test # Run tests (1080 tests)
|
|
332
332
|
yarn test:ui # Interactive Vitest UI
|
|
333
333
|
yarn test:coverage # Tests with coverage report
|
|
334
334
|
```
|
|
335
335
|
|
|
336
336
|
## Requirements
|
|
337
337
|
|
|
338
|
-
- Node.js >=
|
|
338
|
+
- Node.js >= 22.0.0
|
|
339
339
|
|
|
340
340
|
## License
|
|
341
341
|
|
package/build/angles.d.ts
CHANGED
|
@@ -2,30 +2,60 @@
|
|
|
2
2
|
* Converts degrees to radians
|
|
3
3
|
* @param degrees - Angle in degrees
|
|
4
4
|
* @returns Angle in radians
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* DegreesToRadians(180) // Math.PI
|
|
8
|
+
* DegreesToRadians(90) // Math.PI / 2
|
|
9
|
+
* DegreesToRadians(0) // 0
|
|
5
10
|
*/
|
|
6
11
|
export declare function DegreesToRadians(degrees: number): number;
|
|
7
12
|
/**
|
|
8
13
|
* Converts radians to degrees
|
|
9
14
|
* @param radians - Angle in radians
|
|
10
15
|
* @returns Angle in degrees
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* RadiansToDegrees(Math.PI) // 180
|
|
19
|
+
* RadiansToDegrees(Math.PI / 2) // 90
|
|
20
|
+
* RadiansToDegrees(0) // 0
|
|
11
21
|
*/
|
|
12
22
|
export declare function RadiansToDegrees(radians: number): number;
|
|
13
23
|
/**
|
|
14
24
|
* Formats an angle in radians to a string representation in terms of π
|
|
15
25
|
* @param radians - Angle in radians
|
|
16
26
|
* @returns String representation of the angle
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* FormatRadians(0) // '0'
|
|
30
|
+
* FormatRadians(Math.PI) // 'π'
|
|
31
|
+
* FormatRadians(Math.PI / 2) // 'π/2'
|
|
32
|
+
* FormatRadians(Math.PI / 4) // 'π/4'
|
|
33
|
+
* FormatRadians(-Math.PI) // '-π'
|
|
17
34
|
*/
|
|
18
35
|
export declare function FormatRadians(radians: number): string;
|
|
19
36
|
/**
|
|
20
37
|
* Normalizes an angle in radians to be between 0 and 2π
|
|
21
38
|
* @param radians - Angle in radians
|
|
22
|
-
* @returns Normalized angle in radians
|
|
39
|
+
* @returns Normalized angle in radians in the range [0, 2π)
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* NormalizeRadians(3 * Math.PI) // Math.PI (wraps around)
|
|
43
|
+
* NormalizeRadians(-Math.PI) // Math.PI (negative angles normalized)
|
|
44
|
+
* NormalizeRadians(0) // 0
|
|
23
45
|
*/
|
|
24
46
|
export declare function NormalizeRadians(radians: number): number;
|
|
25
47
|
/**
|
|
26
|
-
* Normalizes an angle in degrees to be between 0 and 360
|
|
48
|
+
* Normalizes an angle in degrees to be between 0 and 360.
|
|
49
|
+
* Applies a floating-point epsilon cleanup so values within `1e-10` of 0 or 360
|
|
50
|
+
* are snapped to exactly 0, matching the behaviour of `NormalizeRadians`.
|
|
27
51
|
* @param degrees - Angle in degrees
|
|
28
|
-
* @returns Normalized angle in degrees
|
|
52
|
+
* @returns Normalized angle in degrees in the range [0°, 360°)
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* NormalizeDegrees(450) // 90 (wraps around 360°)
|
|
56
|
+
* NormalizeDegrees(-90) // 270 (negative angles normalized)
|
|
57
|
+
* NormalizeDegrees(0) // 0
|
|
58
|
+
* NormalizeDegrees(360 - 1e-11) // 0 (boundary epsilon snap)
|
|
29
59
|
*/
|
|
30
60
|
export declare function NormalizeDegrees(degrees: number): number;
|
|
31
61
|
//# sourceMappingURL=angles.d.ts.map
|
package/build/angles.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"angles.d.ts","sourceRoot":"","sources":["../src/angles.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"angles.d.ts","sourceRoot":"","sources":["../src/angles.ts"],"names":[],"mappings":"AAMA;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAuBrD;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMxD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKxD"}
|
package/build/angles.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
const DEGREES_PER_HALF_REVOLUTION = 180;
|
|
2
2
|
const DEGREES_PER_FULL_REVOLUTION = 360;
|
|
3
|
-
const ANGLE_FRACTION_DENOMINATOR_3 = 3;
|
|
4
|
-
const ANGLE_FRACTION_DENOMINATOR_6 = 6;
|
|
5
|
-
const ANGLE_FRACTION_QUARTER = 0.25;
|
|
6
|
-
const ANGLE_FRACTION_THREE_QUARTERS = 0.75;
|
|
7
3
|
const ANGLE_FRACTION_TOLERANCE = 0.0001;
|
|
8
4
|
const ANGLE_MAX_DENOMINATOR = 12;
|
|
5
|
+
const NORMALIZE_EPSILON = 1e-10; // Epsilon for floating-point boundary cleanup
|
|
9
6
|
/**
|
|
10
7
|
* Converts degrees to radians
|
|
11
8
|
* @param degrees - Angle in degrees
|
|
12
9
|
* @returns Angle in radians
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* DegreesToRadians(180) // Math.PI
|
|
13
|
+
* DegreesToRadians(90) // Math.PI / 2
|
|
14
|
+
* DegreesToRadians(0) // 0
|
|
13
15
|
*/
|
|
14
16
|
export function DegreesToRadians(degrees) {
|
|
15
17
|
return (degrees * Math.PI) / DEGREES_PER_HALF_REVOLUTION;
|
|
@@ -18,6 +20,11 @@ export function DegreesToRadians(degrees) {
|
|
|
18
20
|
* Converts radians to degrees
|
|
19
21
|
* @param radians - Angle in radians
|
|
20
22
|
* @returns Angle in degrees
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* RadiansToDegrees(Math.PI) // 180
|
|
26
|
+
* RadiansToDegrees(Math.PI / 2) // 90
|
|
27
|
+
* RadiansToDegrees(0) // 0
|
|
21
28
|
*/
|
|
22
29
|
export function RadiansToDegrees(radians) {
|
|
23
30
|
return (radians * DEGREES_PER_HALF_REVOLUTION) / Math.PI;
|
|
@@ -26,6 +33,13 @@ export function RadiansToDegrees(radians) {
|
|
|
26
33
|
* Formats an angle in radians to a string representation in terms of π
|
|
27
34
|
* @param radians - Angle in radians
|
|
28
35
|
* @returns String representation of the angle
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* FormatRadians(0) // '0'
|
|
39
|
+
* FormatRadians(Math.PI) // 'π'
|
|
40
|
+
* FormatRadians(Math.PI / 2) // 'π/2'
|
|
41
|
+
* FormatRadians(Math.PI / 4) // 'π/4'
|
|
42
|
+
* FormatRadians(-Math.PI) // '-π'
|
|
29
43
|
*/
|
|
30
44
|
export function FormatRadians(radians) {
|
|
31
45
|
const r = radians / Math.PI;
|
|
@@ -35,32 +49,19 @@ export function FormatRadians(radians) {
|
|
|
35
49
|
return 'π';
|
|
36
50
|
if (r === -1)
|
|
37
51
|
return '-π';
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return '-' + fraction.str;
|
|
52
|
-
}
|
|
53
|
-
// Try to find a simple fraction representation
|
|
54
|
-
const tolerance = ANGLE_FRACTION_TOLERANCE;
|
|
55
|
-
for (let denominator = 2; denominator <= ANGLE_MAX_DENOMINATOR; denominator++) {
|
|
56
|
-
for (let numerator = 1; numerator < denominator; numerator++) {
|
|
57
|
-
const frac = numerator / denominator;
|
|
58
|
-
if (Math.abs(r - frac) < tolerance) {
|
|
59
|
-
return `${numerator === 1 ? '' : numerator}π/${denominator}`;
|
|
60
|
-
}
|
|
61
|
-
if (Math.abs(r + frac) < tolerance) {
|
|
62
|
-
return `-${numerator === 1 ? '' : numerator}π/${denominator}`;
|
|
63
|
-
}
|
|
52
|
+
const absR = Math.abs(r);
|
|
53
|
+
const sign = r < 0 ? '-' : '';
|
|
54
|
+
// Try to express |r| as n/d for d in [2, ANGLE_MAX_DENOMINATOR].
|
|
55
|
+
// Iterating from the smallest denominator first ensures reduced fractions
|
|
56
|
+
// are returned (e.g., π/2 is found at d=2 before 2π/4 would be at d=4).
|
|
57
|
+
for (let d = 2; d <= ANGLE_MAX_DENOMINATOR; d++) {
|
|
58
|
+
const n = Math.round(absR * d);
|
|
59
|
+
if (n === 0)
|
|
60
|
+
continue; // zero already handled
|
|
61
|
+
if (n % d === 0)
|
|
62
|
+
continue; // integer multiple of π — fall through to fallback
|
|
63
|
+
if (Math.abs(absR - n / d) < ANGLE_FRACTION_TOLERANCE) {
|
|
64
|
+
return n === 1 ? `${sign}π/${d}` : `${sign}${n}π/${d}`;
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
return `${r}π`;
|
|
@@ -68,18 +69,39 @@ export function FormatRadians(radians) {
|
|
|
68
69
|
/**
|
|
69
70
|
* Normalizes an angle in radians to be between 0 and 2π
|
|
70
71
|
* @param radians - Angle in radians
|
|
71
|
-
* @returns Normalized angle in radians
|
|
72
|
+
* @returns Normalized angle in radians in the range [0, 2π)
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* NormalizeRadians(3 * Math.PI) // Math.PI (wraps around)
|
|
76
|
+
* NormalizeRadians(-Math.PI) // Math.PI (negative angles normalized)
|
|
77
|
+
* NormalizeRadians(0) // 0
|
|
72
78
|
*/
|
|
73
79
|
export function NormalizeRadians(radians) {
|
|
74
80
|
const twoPi = 2 * Math.PI;
|
|
75
|
-
|
|
81
|
+
const result = ((radians % twoPi) + twoPi) % twoPi;
|
|
82
|
+
// Epsilon cleanup for floating-point precision at boundaries
|
|
83
|
+
if (result < NORMALIZE_EPSILON || result > twoPi - NORMALIZE_EPSILON)
|
|
84
|
+
return 0;
|
|
85
|
+
return result;
|
|
76
86
|
}
|
|
77
87
|
/**
|
|
78
|
-
* Normalizes an angle in degrees to be between 0 and 360
|
|
88
|
+
* Normalizes an angle in degrees to be between 0 and 360.
|
|
89
|
+
* Applies a floating-point epsilon cleanup so values within `1e-10` of 0 or 360
|
|
90
|
+
* are snapped to exactly 0, matching the behaviour of `NormalizeRadians`.
|
|
79
91
|
* @param degrees - Angle in degrees
|
|
80
|
-
* @returns Normalized angle in degrees
|
|
92
|
+
* @returns Normalized angle in degrees in the range [0°, 360°)
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* NormalizeDegrees(450) // 90 (wraps around 360°)
|
|
96
|
+
* NormalizeDegrees(-90) // 270 (negative angles normalized)
|
|
97
|
+
* NormalizeDegrees(0) // 0
|
|
98
|
+
* NormalizeDegrees(360 - 1e-11) // 0 (boundary epsilon snap)
|
|
81
99
|
*/
|
|
82
100
|
export function NormalizeDegrees(degrees) {
|
|
83
|
-
|
|
101
|
+
const result = ((degrees % DEGREES_PER_FULL_REVOLUTION) + DEGREES_PER_FULL_REVOLUTION) % DEGREES_PER_FULL_REVOLUTION;
|
|
102
|
+
// Epsilon cleanup for floating-point precision at boundaries (mirrors NormalizeRadians)
|
|
103
|
+
if (result < NORMALIZE_EPSILON || result > DEGREES_PER_FULL_REVOLUTION - NORMALIZE_EPSILON)
|
|
104
|
+
return 0;
|
|
105
|
+
return result;
|
|
84
106
|
}
|
|
85
107
|
//# sourceMappingURL=angles.js.map
|
package/build/angles.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"angles.js","sourceRoot":"","sources":["../src/angles.ts"],"names":[],"mappings":"AAAA,MAAM,2BAA2B,GAAG,GAAG,CAAC;AACxC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AACxC,MAAM,
|
|
1
|
+
{"version":3,"file":"angles.js","sourceRoot":"","sources":["../src/angles.ts"],"names":[],"mappings":"AAAA,MAAM,2BAA2B,GAAG,GAAG,CAAC;AACxC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AACxC,MAAM,wBAAwB,GAAG,MAAM,CAAC;AACxC,MAAM,qBAAqB,GAAG,EAAE,CAAC;AACjC,MAAM,iBAAiB,GAAG,KAAK,CAAC,CAAC,8CAA8C;AAE/E;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC/C,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,2BAA2B,CAAC;AAC1D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC/C,OAAO,CAAC,OAAO,GAAG,2BAA2B,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe;IAC5C,MAAM,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC;IAE5B,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACxB,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACxB,IAAI,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAE1B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACzB,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAE9B,iEAAiE;IACjE,0EAA0E;IAC1E,wEAAwE;IACxE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,qBAAqB,EAAE,CAAC,EAAE,EAAE,CAAC;QACjD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC;YAAE,SAAS,CAAQ,uBAAuB;QACrD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,SAAS,CAAI,mDAAmD;QACjF,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,wBAAwB,EAAE,CAAC;YACvD,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACxD,CAAC;IACF,CAAC;IAED,OAAO,GAAG,CAAC,GAAG,CAAC;AAChB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC/C,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC;IACnD,6DAA6D;IAC7D,IAAI,MAAM,GAAG,iBAAiB,IAAI,MAAM,GAAG,KAAK,GAAG,iBAAiB;QAAE,OAAO,CAAC,CAAC;IAC/E,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC/C,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,GAAG,2BAA2B,CAAC,GAAG,2BAA2B,CAAC,GAAG,2BAA2B,CAAC;IACrH,wFAAwF;IACxF,IAAI,MAAM,GAAG,iBAAiB,IAAI,MAAM,GAAG,2BAA2B,GAAG,iBAAiB;QAAE,OAAO,CAAC,CAAC;IACrG,OAAO,MAAM,CAAC;AACf,CAAC"}
|