@johnhenry/math 0.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 +165 -0
- package/dist/Bivector4.d.ts +52 -0
- package/dist/Bivector4.d.ts.map +1 -0
- package/dist/Bivector4.js +105 -0
- package/dist/Bivector4.js.map +1 -0
- package/dist/CellGraph.d.ts +259 -0
- package/dist/CellGraph.d.ts.map +1 -0
- package/dist/CellGraph.js +637 -0
- package/dist/CellGraph.js.map +1 -0
- package/dist/Clifford4Internal.d.ts +35 -0
- package/dist/Clifford4Internal.d.ts.map +1 -0
- package/dist/Clifford4Internal.js +86 -0
- package/dist/Clifford4Internal.js.map +1 -0
- package/dist/Combinatorics.d.ts +61 -0
- package/dist/Combinatorics.d.ts.map +1 -0
- package/dist/Combinatorics.js +186 -0
- package/dist/Combinatorics.js.map +1 -0
- package/dist/CombinatoricsGenerators.d.ts +44 -0
- package/dist/CombinatoricsGenerators.d.ts.map +1 -0
- package/dist/CombinatoricsGenerators.js +148 -0
- package/dist/CombinatoricsGenerators.js.map +1 -0
- package/dist/ComplexMath.d.ts +46 -0
- package/dist/ComplexMath.d.ts.map +1 -0
- package/dist/ComplexMath.js +98 -0
- package/dist/ComplexMath.js.map +1 -0
- package/dist/ComplexNumber.d.ts +174 -0
- package/dist/ComplexNumber.d.ts.map +1 -0
- package/dist/ComplexNumber.js +443 -0
- package/dist/ComplexNumber.js.map +1 -0
- package/dist/Cycle.d.ts +42 -0
- package/dist/Cycle.d.ts.map +1 -0
- package/dist/Cycle.js +120 -0
- package/dist/Cycle.js.map +1 -0
- package/dist/Decimal.d.ts +45 -0
- package/dist/Decimal.d.ts.map +1 -0
- package/dist/Decimal.js +135 -0
- package/dist/Decimal.js.map +1 -0
- package/dist/Distributions.d.ts +55 -0
- package/dist/Distributions.d.ts.map +1 -0
- package/dist/Distributions.js +222 -0
- package/dist/Distributions.js.map +1 -0
- package/dist/DualNumber.d.ts +42 -0
- package/dist/DualNumber.d.ts.map +1 -0
- package/dist/DualNumber.js +91 -0
- package/dist/DualNumber.js.map +1 -0
- package/dist/Environment.d.ts +44 -0
- package/dist/Environment.d.ts.map +1 -0
- package/dist/Environment.js +129 -0
- package/dist/Environment.js.map +1 -0
- package/dist/Expression.d.ts +19 -0
- package/dist/Expression.d.ts.map +1 -0
- package/dist/Expression.js +43 -0
- package/dist/Expression.js.map +1 -0
- package/dist/FFT.d.ts +21 -0
- package/dist/FFT.d.ts.map +1 -0
- package/dist/FFT.js +136 -0
- package/dist/FFT.js.map +1 -0
- package/dist/GeometricAlgebra4.d.ts +73 -0
- package/dist/GeometricAlgebra4.d.ts.map +1 -0
- package/dist/GeometricAlgebra4.js +115 -0
- package/dist/GeometricAlgebra4.js.map +1 -0
- package/dist/Geometry.d.ts +41 -0
- package/dist/Geometry.d.ts.map +1 -0
- package/dist/Geometry.js +167 -0
- package/dist/Geometry.js.map +1 -0
- package/dist/Graph.d.ts +72 -0
- package/dist/Graph.d.ts.map +1 -0
- package/dist/Graph.js +337 -0
- package/dist/Graph.js.map +1 -0
- package/dist/Graph3DUtils.d.ts +80 -0
- package/dist/Graph3DUtils.d.ts.map +1 -0
- package/dist/Graph3DUtils.js +156 -0
- package/dist/Graph3DUtils.js.map +1 -0
- package/dist/GraphUtils.d.ts +67 -0
- package/dist/GraphUtils.d.ts.map +1 -0
- package/dist/GraphUtils.js +72 -0
- package/dist/GraphUtils.js.map +1 -0
- package/dist/GroupTheory.d.ts +73 -0
- package/dist/GroupTheory.d.ts.map +1 -0
- package/dist/GroupTheory.js +194 -0
- package/dist/GroupTheory.js.map +1 -0
- package/dist/IntUtils.d.ts +33 -0
- package/dist/IntUtils.d.ts.map +1 -0
- package/dist/IntUtils.js +185 -0
- package/dist/IntUtils.js.map +1 -0
- package/dist/Interval.d.ts +44 -0
- package/dist/Interval.d.ts.map +1 -0
- package/dist/Interval.js +165 -0
- package/dist/Interval.js.map +1 -0
- package/dist/Lattice.d.ts +48 -0
- package/dist/Lattice.d.ts.map +1 -0
- package/dist/Lattice.js +65 -0
- package/dist/Lattice.js.map +1 -0
- package/dist/Logic.d.ts +22 -0
- package/dist/Logic.d.ts.map +1 -0
- package/dist/Logic.js +34 -0
- package/dist/Logic.js.map +1 -0
- package/dist/MatrixMath.d.ts +71 -0
- package/dist/MatrixMath.d.ts.map +1 -0
- package/dist/MatrixMath.js +302 -0
- package/dist/MatrixMath.js.map +1 -0
- package/dist/NumberTheory.d.ts +49 -0
- package/dist/NumberTheory.d.ts.map +1 -0
- package/dist/NumberTheory.js +230 -0
- package/dist/NumberTheory.js.map +1 -0
- package/dist/Numerical.d.ts +85 -0
- package/dist/Numerical.d.ts.map +1 -0
- package/dist/Numerical.js +343 -0
- package/dist/Numerical.js.map +1 -0
- package/dist/Permutation.d.ts +43 -0
- package/dist/Permutation.d.ts.map +1 -0
- package/dist/Permutation.js +142 -0
- package/dist/Permutation.js.map +1 -0
- package/dist/Polygon.d.ts +64 -0
- package/dist/Polygon.d.ts.map +1 -0
- package/dist/Polygon.js +221 -0
- package/dist/Polygon.js.map +1 -0
- package/dist/PolynomialRing.d.ts +74 -0
- package/dist/PolynomialRing.d.ts.map +1 -0
- package/dist/PolynomialRing.js +231 -0
- package/dist/PolynomialRing.js.map +1 -0
- package/dist/Quaternion.d.ts +37 -0
- package/dist/Quaternion.d.ts.map +1 -0
- package/dist/Quaternion.js +106 -0
- package/dist/Quaternion.js.map +1 -0
- package/dist/Rational.d.ts +34 -0
- package/dist/Rational.d.ts.map +1 -0
- package/dist/Rational.js +136 -0
- package/dist/Rational.js.map +1 -0
- package/dist/RealMath.d.ts +112 -0
- package/dist/RealMath.d.ts.map +1 -0
- package/dist/RealMath.js +262 -0
- package/dist/RealMath.js.map +1 -0
- package/dist/Rotor4.d.ts +180 -0
- package/dist/Rotor4.d.ts.map +1 -0
- package/dist/Rotor4.js +388 -0
- package/dist/Rotor4.js.map +1 -0
- package/dist/SpecialFunctions.d.ts +32 -0
- package/dist/SpecialFunctions.d.ts.map +1 -0
- package/dist/SpecialFunctions.js +161 -0
- package/dist/SpecialFunctions.js.map +1 -0
- package/dist/SpecialOperator.d.ts +21 -0
- package/dist/SpecialOperator.d.ts.map +1 -0
- package/dist/SpecialOperator.js +26 -0
- package/dist/SpecialOperator.js.map +1 -0
- package/dist/Statistics.d.ts +50 -0
- package/dist/Statistics.d.ts.map +1 -0
- package/dist/Statistics.js +131 -0
- package/dist/Statistics.js.map +1 -0
- package/dist/StringEvaluator.d.ts +48 -0
- package/dist/StringEvaluator.d.ts.map +1 -0
- package/dist/StringEvaluator.js +190 -0
- package/dist/StringEvaluator.js.map +1 -0
- package/dist/Structure.d.ts +130 -0
- package/dist/Structure.d.ts.map +1 -0
- package/dist/Structure.js +394 -0
- package/dist/Structure.js.map +1 -0
- package/dist/Symbolic.d.ts +501 -0
- package/dist/Symbolic.d.ts.map +1 -0
- package/dist/Symbolic.js +4077 -0
- package/dist/Symbolic.js.map +1 -0
- package/dist/Type.d.ts +31 -0
- package/dist/Type.d.ts.map +1 -0
- package/dist/Type.js +47 -0
- package/dist/Type.js.map +1 -0
- package/dist/Utilities.d.ts +27 -0
- package/dist/Utilities.d.ts.map +1 -0
- package/dist/Utilities.js +47 -0
- package/dist/Utilities.js.map +1 -0
- package/dist/Vec4.d.ts +39 -0
- package/dist/Vec4.d.ts.map +1 -0
- package/dist/Vec4.js +82 -0
- package/dist/Vec4.js.map +1 -0
- package/dist/Vector.d.ts +83 -0
- package/dist/Vector.d.ts.map +1 -0
- package/dist/Vector.js +173 -0
- package/dist/Vector.js.map +1 -0
- package/dist/VectorCalculus.d.ts +42 -0
- package/dist/VectorCalculus.d.ts.map +1 -0
- package/dist/VectorCalculus.js +82 -0
- package/dist/VectorCalculus.js.map +1 -0
- package/dist/VectorUtils.d.ts +172 -0
- package/dist/VectorUtils.d.ts.map +1 -0
- package/dist/VectorUtils.js +706 -0
- package/dist/VectorUtils.js.map +1 -0
- package/dist/differentiation-rules.d.ts +21 -0
- package/dist/differentiation-rules.d.ts.map +1 -0
- package/dist/differentiation-rules.js +52 -0
- package/dist/differentiation-rules.js.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
- package/src/Bivector4.ts +136 -0
- package/src/CellGraph.ts +669 -0
- package/src/Clifford4Internal.ts +90 -0
- package/src/Combinatorics.ts +183 -0
- package/src/CombinatoricsGenerators.ts +144 -0
- package/src/ComplexMath.ts +162 -0
- package/src/ComplexNumber.ts +491 -0
- package/src/Cycle.ts +126 -0
- package/src/Decimal.ts +145 -0
- package/src/Distributions.ts +269 -0
- package/src/DualNumber.ts +105 -0
- package/src/Environment.ts +127 -0
- package/src/Expression.ts +48 -0
- package/src/FFT.ts +146 -0
- package/src/GeometricAlgebra4.ts +157 -0
- package/src/Geometry.ts +173 -0
- package/src/Graph.ts +337 -0
- package/src/Graph3DUtils.ts +269 -0
- package/src/GraphUtils.ts +159 -0
- package/src/GroupTheory.ts +214 -0
- package/src/IntUtils.ts +200 -0
- package/src/Interval.ts +171 -0
- package/src/Lattice.ts +87 -0
- package/src/Logic.ts +38 -0
- package/src/MatrixMath.ts +326 -0
- package/src/NumberTheory.ts +228 -0
- package/src/Numerical.ts +390 -0
- package/src/Permutation.ts +148 -0
- package/src/Polygon.ts +229 -0
- package/src/PolynomialRing.ts +242 -0
- package/src/Quaternion.ts +125 -0
- package/src/Rational.ts +148 -0
- package/src/RealMath.ts +346 -0
- package/src/Rotor4.ts +440 -0
- package/src/SpecialFunctions.ts +152 -0
- package/src/SpecialOperator.ts +28 -0
- package/src/Statistics.ts +170 -0
- package/src/StringEvaluator.ts +208 -0
- package/src/Structure.ts +481 -0
- package/src/Symbolic.ts +4313 -0
- package/src/Type.ts +48 -0
- package/src/Utilities.ts +49 -0
- package/src/Vec4.ts +98 -0
- package/src/Vector.ts +189 -0
- package/src/VectorCalculus.ts +87 -0
- package/src/VectorUtils.ts +797 -0
- package/src/differentiation-rules.ts +73 -0
- package/src/index.ts +140 -0
package/src/FFT.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { ComplexNumber } from "./ComplexNumber.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* FFT — the discrete Fourier transform and its fast (Cooley-Tukey) radix-2
|
|
5
|
+
* implementation, plus the inverse transform and FFT-based convolution. Builds
|
|
6
|
+
* on {@link ComplexNumber}. Inputs may be complex numbers or plain reals.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
type Signal = ReadonlyArray<ComplexNumber | number>;
|
|
10
|
+
|
|
11
|
+
const toParts = (input: Signal): { re: number[]; im: number[] } => {
|
|
12
|
+
const re: number[] = [];
|
|
13
|
+
const im: number[] = [];
|
|
14
|
+
for (const x of input) {
|
|
15
|
+
if (x instanceof ComplexNumber) {
|
|
16
|
+
re.push(x.value);
|
|
17
|
+
im.push(x.iValue);
|
|
18
|
+
} else {
|
|
19
|
+
re.push(x);
|
|
20
|
+
im.push(0);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return { re, im };
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const toComplex = (re: number[], im: number[]): ComplexNumber[] => re.map((r, i) => new ComplexNumber(r, im[i]));
|
|
27
|
+
|
|
28
|
+
const isPowerOfTwo = (n: number): boolean => n > 0 && (n & (n - 1)) === 0;
|
|
29
|
+
|
|
30
|
+
/** In-place iterative radix-2 Cooley-Tukey. `invert` runs the inverse transform. */
|
|
31
|
+
function transform(re: number[], im: number[], invert: boolean): void {
|
|
32
|
+
const n = re.length;
|
|
33
|
+
// bit-reversal permutation
|
|
34
|
+
for (let i = 1, j = 0; i < n; i++) {
|
|
35
|
+
let bit = n >> 1;
|
|
36
|
+
for (; j & bit; bit >>= 1) j ^= bit;
|
|
37
|
+
j ^= bit;
|
|
38
|
+
if (i < j) {
|
|
39
|
+
[re[i], re[j]] = [re[j], re[i]];
|
|
40
|
+
[im[i], im[j]] = [im[j], im[i]];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (let len = 2; len <= n; len <<= 1) {
|
|
44
|
+
const ang = ((2 * Math.PI) / len) * (invert ? 1 : -1);
|
|
45
|
+
const wlenR = Math.cos(ang);
|
|
46
|
+
const wlenI = Math.sin(ang);
|
|
47
|
+
for (let i = 0; i < n; i += len) {
|
|
48
|
+
let wr = 1;
|
|
49
|
+
let wi = 0;
|
|
50
|
+
for (let j = 0; j < len / 2; j++) {
|
|
51
|
+
const uR = re[i + j];
|
|
52
|
+
const uI = im[i + j];
|
|
53
|
+
const vR = re[i + j + len / 2] * wr - im[i + j + len / 2] * wi;
|
|
54
|
+
const vI = re[i + j + len / 2] * wi + im[i + j + len / 2] * wr;
|
|
55
|
+
re[i + j] = uR + vR;
|
|
56
|
+
im[i + j] = uI + vI;
|
|
57
|
+
re[i + j + len / 2] = uR - vR;
|
|
58
|
+
im[i + j + len / 2] = uI - vI;
|
|
59
|
+
const nwr = wr * wlenR - wi * wlenI;
|
|
60
|
+
wi = wr * wlenI + wi * wlenR;
|
|
61
|
+
wr = nwr;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (invert)
|
|
66
|
+
for (let i = 0; i < n; i++) {
|
|
67
|
+
re[i] /= n;
|
|
68
|
+
im[i] /= n;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export class FFT {
|
|
73
|
+
/** Fast Fourier transform (input length must be a power of two). */
|
|
74
|
+
static fft(input: Signal): ComplexNumber[] {
|
|
75
|
+
const { re, im } = toParts(input);
|
|
76
|
+
if (!isPowerOfTwo(re.length)) throw new Error("FFT input length must be a power of two (see fftPadded).");
|
|
77
|
+
transform(re, im, false);
|
|
78
|
+
return toComplex(re, im);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Inverse fast Fourier transform. */
|
|
82
|
+
static ifft(input: Signal): ComplexNumber[] {
|
|
83
|
+
const { re, im } = toParts(input);
|
|
84
|
+
if (!isPowerOfTwo(re.length)) throw new Error("IFFT input length must be a power of two.");
|
|
85
|
+
transform(re, im, true);
|
|
86
|
+
return toComplex(re, im);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** FFT with the input zero-padded up to the next power of two. */
|
|
90
|
+
static fftPadded(input: Signal): ComplexNumber[] {
|
|
91
|
+
const { re, im } = toParts(input);
|
|
92
|
+
let n = 1;
|
|
93
|
+
while (n < re.length) n <<= 1;
|
|
94
|
+
while (re.length < n) {
|
|
95
|
+
re.push(0);
|
|
96
|
+
im.push(0);
|
|
97
|
+
}
|
|
98
|
+
transform(re, im, false);
|
|
99
|
+
return toComplex(re, im);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Direct O(n²) discrete Fourier transform for any length (reference implementation). */
|
|
103
|
+
static dft(input: Signal): ComplexNumber[] {
|
|
104
|
+
const { re, im } = toParts(input);
|
|
105
|
+
const n = re.length;
|
|
106
|
+
const out: ComplexNumber[] = [];
|
|
107
|
+
for (let k = 0; k < n; k++) {
|
|
108
|
+
let sumR = 0;
|
|
109
|
+
let sumI = 0;
|
|
110
|
+
for (let t = 0; t < n; t++) {
|
|
111
|
+
const ang = (-2 * Math.PI * k * t) / n;
|
|
112
|
+
sumR += re[t] * Math.cos(ang) - im[t] * Math.sin(ang);
|
|
113
|
+
sumI += re[t] * Math.sin(ang) + im[t] * Math.cos(ang);
|
|
114
|
+
}
|
|
115
|
+
out.push(new ComplexNumber(sumR, sumI));
|
|
116
|
+
}
|
|
117
|
+
return out;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Linear convolution of two real (or complex) signals, via the FFT. */
|
|
121
|
+
static convolve(a: Signal, b: Signal): ComplexNumber[] {
|
|
122
|
+
const resultLen = a.length + b.length - 1;
|
|
123
|
+
let n = 1;
|
|
124
|
+
while (n < resultLen) n <<= 1;
|
|
125
|
+
const pad = (s: Signal) => {
|
|
126
|
+
const { re, im } = toParts(s);
|
|
127
|
+
while (re.length < n) {
|
|
128
|
+
re.push(0);
|
|
129
|
+
im.push(0);
|
|
130
|
+
}
|
|
131
|
+
return { re, im };
|
|
132
|
+
};
|
|
133
|
+
const A = pad(a);
|
|
134
|
+
const B = pad(b);
|
|
135
|
+
transform(A.re, A.im, false);
|
|
136
|
+
transform(B.re, B.im, false);
|
|
137
|
+
const re = new Array(n);
|
|
138
|
+
const im = new Array(n);
|
|
139
|
+
for (let i = 0; i < n; i++) {
|
|
140
|
+
re[i] = A.re[i] * B.re[i] - A.im[i] * B.im[i];
|
|
141
|
+
im[i] = A.re[i] * B.im[i] + A.im[i] * B.re[i];
|
|
142
|
+
}
|
|
143
|
+
transform(re, im, true);
|
|
144
|
+
return toComplex(re.slice(0, resultLen), im.slice(0, resultLen));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GeometricAlgebra4 — the products that combine {@link Vec4} and
|
|
3
|
+
* {@link Bivector4} objects, mirroring how {@link ComplexMath} and
|
|
4
|
+
* {@link RealMath} hold the free-function operations for their respective
|
|
5
|
+
* types. {@link Rotor4} is built on top of these.
|
|
6
|
+
*
|
|
7
|
+
* Scope note: this module only implements the specific products the physics
|
|
8
|
+
* and rendering layers of a 4D engine actually need (vector·vector,
|
|
9
|
+
* vector∧vector, bivector*bivector, and the Hodge dual of a bivector) rather
|
|
10
|
+
* than a fully generic arbitrary-grade multivector type — Cl(4,0) has 16
|
|
11
|
+
* basis blades across grades 0–4, but rotors only ever touch grades 0, 2, 4,
|
|
12
|
+
* and nothing in this package needs grade-1 (vector) or grade-3 (trivector)
|
|
13
|
+
* results from a product.
|
|
14
|
+
*/
|
|
15
|
+
import { Bivector4 } from "./Bivector4.ts";
|
|
16
|
+
import {
|
|
17
|
+
E1,
|
|
18
|
+
E2,
|
|
19
|
+
E3,
|
|
20
|
+
E4,
|
|
21
|
+
E12,
|
|
22
|
+
E13,
|
|
23
|
+
E14,
|
|
24
|
+
E23,
|
|
25
|
+
E24,
|
|
26
|
+
E34,
|
|
27
|
+
geometricProductMV,
|
|
28
|
+
type Multivector4,
|
|
29
|
+
mvZero,
|
|
30
|
+
PSEUDOSCALAR4,
|
|
31
|
+
wedgeProductMV,
|
|
32
|
+
} from "./Clifford4Internal.ts";
|
|
33
|
+
import { Vec4 } from "./Vec4.ts";
|
|
34
|
+
|
|
35
|
+
function vecToMV(v: Vec4): Multivector4 {
|
|
36
|
+
const mv = mvZero();
|
|
37
|
+
mv[E1] = v.x;
|
|
38
|
+
mv[E2] = v.y;
|
|
39
|
+
mv[E3] = v.z;
|
|
40
|
+
mv[E4] = v.w;
|
|
41
|
+
return mv;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function bivectorToMV(b: Bivector4): Multivector4 {
|
|
45
|
+
const mv = mvZero();
|
|
46
|
+
mv[E12] = b.xy;
|
|
47
|
+
mv[E13] = b.xz;
|
|
48
|
+
mv[E14] = b.xw;
|
|
49
|
+
mv[E23] = b.yz;
|
|
50
|
+
mv[E24] = b.yw;
|
|
51
|
+
mv[E34] = b.zw;
|
|
52
|
+
return mv;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function bivectorFromMV(mv: Multivector4): Bivector4 {
|
|
56
|
+
return new Bivector4(
|
|
57
|
+
mv[E12] as number,
|
|
58
|
+
mv[E13] as number,
|
|
59
|
+
mv[E14] as number,
|
|
60
|
+
mv[E23] as number,
|
|
61
|
+
mv[E24] as number,
|
|
62
|
+
mv[E34] as number,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The Euclidean inner product `a · b` (a scalar). Equivalent to `a.dot(b)`. */
|
|
67
|
+
export function dotProduct(a: Vec4, b: Vec4): number {
|
|
68
|
+
return a.dot(b);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The outer (wedge) product `a ∧ b` — the oriented 2-plane spanned by `a` and `b`. */
|
|
72
|
+
export function wedgeProduct(a: Vec4, b: Vec4): Bivector4 {
|
|
73
|
+
return bivectorFromMV(wedgeProductMV(vecToMV(a), vecToMV(b)));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface VectorGeometricProduct {
|
|
77
|
+
readonly scalar: number;
|
|
78
|
+
readonly bivector: Bivector4;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** The full geometric product `a * b = a·b + a∧b` of two vectors. */
|
|
82
|
+
export function geometricProductVectors(a: Vec4, b: Vec4): VectorGeometricProduct {
|
|
83
|
+
const mv = geometricProductMV(vecToMV(a), vecToMV(b));
|
|
84
|
+
return { scalar: mv[0] as number, bivector: bivectorFromMV(mv) };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface BivectorGeometricProduct {
|
|
88
|
+
readonly scalar: number;
|
|
89
|
+
readonly bivector: Bivector4;
|
|
90
|
+
readonly pseudoscalar: number;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The full geometric product `A * B` of two bivectors: `⟨AB⟩₀ + ⟨AB⟩₂ + ⟨AB⟩₄`.
|
|
95
|
+
* This is the operation that makes 4D rotors an 8-component (1 + 6 + 1)
|
|
96
|
+
* object — the grade-4 (pseudoscalar) term has no 3D analogue, since in 3D
|
|
97
|
+
* the product of two bivectors never produces a grade-4 term (3D has no
|
|
98
|
+
* grade above 3). It's exactly this term that lets a 4D rotor encode a
|
|
99
|
+
* *double rotation*: simultaneous rotation in two independent orthogonal
|
|
100
|
+
* planes at once.
|
|
101
|
+
*/
|
|
102
|
+
export function geometricProductBivectors(a: Bivector4, b: Bivector4): BivectorGeometricProduct {
|
|
103
|
+
const mv = geometricProductMV(bivectorToMV(a), bivectorToMV(b));
|
|
104
|
+
return {
|
|
105
|
+
scalar: mv[0] as number,
|
|
106
|
+
bivector: bivectorFromMV(mv),
|
|
107
|
+
pseudoscalar: mv[PSEUDOSCALAR4] as number,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Hodge dual of a bivector in 4D: maps each basis 2-plane to its orthogonal
|
|
113
|
+
* complement 2-plane (e.g. `dual(e12) = ±e34`), scaled by the pseudoscalar.
|
|
114
|
+
* Unlike 3D (where the dual of a bivector is a vector), the 4D dual of a
|
|
115
|
+
* bivector is again a bivector — this is a 4D-specific fact worth remembering
|
|
116
|
+
* when porting 3D geometric intuition.
|
|
117
|
+
*/
|
|
118
|
+
export function dual(b: Bivector4): Bivector4 {
|
|
119
|
+
// Multiplying by the inverse pseudoscalar (I⁻¹ = -I in Cl(4,0), since
|
|
120
|
+
// I*I = +1... actually for Cl(4,0), I^2 = (e1e2e3e4)^2 = +1, so I⁻¹ = I)
|
|
121
|
+
// implements the Hodge dual via the geometric product with I.
|
|
122
|
+
const I = mvZero();
|
|
123
|
+
I[PSEUDOSCALAR4] = 1;
|
|
124
|
+
const mv = geometricProductMV(bivectorToMV(b), I);
|
|
125
|
+
return bivectorFromMV(mv);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The commutator product `A × B = ½(AB − BA)` of two bivectors -- distinct
|
|
130
|
+
* from {@link geometricProductBivectors}'s full `AB`. This is the operation
|
|
131
|
+
* n-dimensional rigid body dynamics needs for the generalized Euler's
|
|
132
|
+
* equation `L_t(ω) = I(ω_t) − ω × I(ω) = τ` (angular momentum, inertia
|
|
133
|
+
* tensor, and torque are all bivectors in 4D; this `×` is the commutator,
|
|
134
|
+
* not the 3D cross product it specializes to in 3D).
|
|
135
|
+
*/
|
|
136
|
+
export function commutatorProduct(a: Bivector4, b: Bivector4): Bivector4 {
|
|
137
|
+
const ab = geometricProductMV(bivectorToMV(a), bivectorToMV(b));
|
|
138
|
+
const ba = geometricProductMV(bivectorToMV(b), bivectorToMV(a));
|
|
139
|
+
const commutator = mvZero();
|
|
140
|
+
for (let i = 0; i < commutator.length; i++) commutator[i] = ((ab[i] as number) - (ba[i] as number)) / 2;
|
|
141
|
+
return bivectorFromMV(commutator);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* The left contraction `v⌋B` of a vector into a bivector, producing a
|
|
146
|
+
* vector -- the grade-1 part of the geometric product `vB` (the geometric
|
|
147
|
+
* product of a grade-1 and a grade-2 element splits into grade 1 [this] and
|
|
148
|
+
* grade 3, and this package doesn't otherwise need grade-3/trivector
|
|
149
|
+
* results, so only the grade-1 part is extracted here). Needed for a rigid
|
|
150
|
+
* body's point velocity: `v_point = v + r⌋ω`, where `v` is linear velocity
|
|
151
|
+
* (Vec4), `ω` is angular velocity (Bivector4), and `r` is the point's offset
|
|
152
|
+
* from the body's center of mass.
|
|
153
|
+
*/
|
|
154
|
+
export function leftContraction(v: Vec4, b: Bivector4): Vec4 {
|
|
155
|
+
const mv = geometricProductMV(vecToMV(v), bivectorToMV(b));
|
|
156
|
+
return new Vec4(mv[E1] as number, mv[E2] as number, mv[E3] as number, mv[E4] as number);
|
|
157
|
+
}
|
package/src/Geometry.ts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Geometry — computational geometry on 2D points (`[x, y]` tuples): convex hull,
|
|
3
|
+
* orientation and intersection predicates, point-in-polygon, distances, polygon
|
|
4
|
+
* area/centroid, and 2D affine transforms via homogeneous matrices. Complements
|
|
5
|
+
* the {@link Polygon} class with lower-level, allocation-free primitives.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type Point = [number, number];
|
|
9
|
+
|
|
10
|
+
export class Geometry {
|
|
11
|
+
/** The signed area of the triangle `(a, b, c)` (>0 CCW, <0 CW, 0 collinear). */
|
|
12
|
+
static orientation(a: Point, b: Point, c: Point): number {
|
|
13
|
+
return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static distance(a: Point, b: Point): number {
|
|
17
|
+
return Math.hypot(a[0] - b[0], a[1] - b[1]);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Distance from point `p` to the segment `a–b`. */
|
|
21
|
+
static distancePointToSegment(p: Point, a: Point, b: Point): number {
|
|
22
|
+
const dx = b[0] - a[0];
|
|
23
|
+
const dy = b[1] - a[1];
|
|
24
|
+
const len2 = dx * dx + dy * dy;
|
|
25
|
+
if (len2 === 0) return Geometry.distance(p, a);
|
|
26
|
+
let t = ((p[0] - a[0]) * dx + (p[1] - a[1]) * dy) / len2;
|
|
27
|
+
t = Math.max(0, Math.min(1, t));
|
|
28
|
+
return Geometry.distance(p, [a[0] + t * dx, a[1] + t * dy]);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The convex hull (counter-clockwise) via Andrew's monotone chain. */
|
|
32
|
+
static convexHull(points: readonly Point[]): Point[] {
|
|
33
|
+
const pts = [...points].sort((p, q) => p[0] - q[0] || p[1] - q[1]);
|
|
34
|
+
if (pts.length <= 2) return pts;
|
|
35
|
+
const cross = (o: Point, a: Point, b: Point) => Geometry.orientation(o, a, b);
|
|
36
|
+
|
|
37
|
+
const lower: Point[] = [];
|
|
38
|
+
for (const p of pts) {
|
|
39
|
+
while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], p) <= 0) lower.pop();
|
|
40
|
+
lower.push(p);
|
|
41
|
+
}
|
|
42
|
+
const upper: Point[] = [];
|
|
43
|
+
for (let i = pts.length - 1; i >= 0; i--) {
|
|
44
|
+
const p = pts[i];
|
|
45
|
+
while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], p) <= 0) upper.pop();
|
|
46
|
+
upper.push(p);
|
|
47
|
+
}
|
|
48
|
+
lower.pop();
|
|
49
|
+
upper.pop();
|
|
50
|
+
return lower.concat(upper);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Whether segments `p1–p2` and `p3–p4` intersect (endpoints/collinear included). */
|
|
54
|
+
static segmentsIntersect(p1: Point, p2: Point, p3: Point, p4: Point): boolean {
|
|
55
|
+
const d1 = Geometry.orientation(p3, p4, p1);
|
|
56
|
+
const d2 = Geometry.orientation(p3, p4, p2);
|
|
57
|
+
const d3 = Geometry.orientation(p1, p2, p3);
|
|
58
|
+
const d4 = Geometry.orientation(p1, p2, p4);
|
|
59
|
+
const onSeg = (a: Point, b: Point, p: Point) =>
|
|
60
|
+
Math.min(a[0], b[0]) <= p[0] &&
|
|
61
|
+
p[0] <= Math.max(a[0], b[0]) &&
|
|
62
|
+
Math.min(a[1], b[1]) <= p[1] &&
|
|
63
|
+
p[1] <= Math.max(a[1], b[1]);
|
|
64
|
+
if (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) return true;
|
|
65
|
+
if (d1 === 0 && onSeg(p3, p4, p1)) return true;
|
|
66
|
+
if (d2 === 0 && onSeg(p3, p4, p2)) return true;
|
|
67
|
+
if (d3 === 0 && onSeg(p1, p2, p3)) return true;
|
|
68
|
+
if (d4 === 0 && onSeg(p1, p2, p4)) return true;
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The shoelace area of a polygon given by its vertices. */
|
|
73
|
+
static polygonArea(polygon: readonly Point[]): number {
|
|
74
|
+
let sum = 0;
|
|
75
|
+
for (let i = 0; i < polygon.length; i++) {
|
|
76
|
+
const a = polygon[i];
|
|
77
|
+
const b = polygon[(i + 1) % polygon.length];
|
|
78
|
+
sum += a[0] * b[1] - b[0] * a[1];
|
|
79
|
+
}
|
|
80
|
+
return Math.abs(sum) / 2;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The centroid of a set of points (the simple average). */
|
|
84
|
+
static centroid(points: readonly Point[]): Point {
|
|
85
|
+
const n = points.length || 1;
|
|
86
|
+
let sx = 0;
|
|
87
|
+
let sy = 0;
|
|
88
|
+
for (const [x, y] of points) {
|
|
89
|
+
sx += x;
|
|
90
|
+
sy += y;
|
|
91
|
+
}
|
|
92
|
+
return [sx / n, sy / n];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Point-in-polygon by the even-odd ray-casting rule. */
|
|
96
|
+
static pointInPolygon(point: Point, polygon: readonly Point[]): boolean {
|
|
97
|
+
const [px, py] = point;
|
|
98
|
+
let inside = false;
|
|
99
|
+
const n = polygon.length;
|
|
100
|
+
for (let i = 0, j = n - 1; i < n; j = i++) {
|
|
101
|
+
const [xi, yi] = polygon[i];
|
|
102
|
+
const [xj, yj] = polygon[j];
|
|
103
|
+
if (yi > py !== yj > py && px < ((xj - xi) * (py - yi)) / (yj - yi) + xi) inside = !inside;
|
|
104
|
+
}
|
|
105
|
+
return inside;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Transform2D — 2D affine transforms as 3×3 homogeneous matrices. Compose them
|
|
111
|
+
* with {@link Transform2D.multiply} and apply them with {@link Transform2D.apply}.
|
|
112
|
+
*/
|
|
113
|
+
export class Transform2D {
|
|
114
|
+
readonly m: number[][];
|
|
115
|
+
|
|
116
|
+
constructor(m: number[][]) {
|
|
117
|
+
this.m = m;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static identity(): Transform2D {
|
|
121
|
+
return new Transform2D([
|
|
122
|
+
[1, 0, 0],
|
|
123
|
+
[0, 1, 0],
|
|
124
|
+
[0, 0, 1],
|
|
125
|
+
]);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
static translation(tx: number, ty: number): Transform2D {
|
|
129
|
+
return new Transform2D([
|
|
130
|
+
[1, 0, tx],
|
|
131
|
+
[0, 1, ty],
|
|
132
|
+
[0, 0, 1],
|
|
133
|
+
]);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
static scaling(sx: number, sy: number = sx): Transform2D {
|
|
137
|
+
return new Transform2D([
|
|
138
|
+
[sx, 0, 0],
|
|
139
|
+
[0, sy, 0],
|
|
140
|
+
[0, 0, 1],
|
|
141
|
+
]);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
static rotation(theta: number): Transform2D {
|
|
145
|
+
const c = Math.cos(theta);
|
|
146
|
+
const s = Math.sin(theta);
|
|
147
|
+
return new Transform2D([
|
|
148
|
+
[c, -s, 0],
|
|
149
|
+
[s, c, 0],
|
|
150
|
+
[0, 0, 1],
|
|
151
|
+
]);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Matrix product `this · other` (apply `other` first, then `this`). */
|
|
155
|
+
multiply(other: Transform2D): Transform2D {
|
|
156
|
+
const a = this.m;
|
|
157
|
+
const b = other.m;
|
|
158
|
+
const out = [
|
|
159
|
+
[0, 0, 0],
|
|
160
|
+
[0, 0, 0],
|
|
161
|
+
[0, 0, 0],
|
|
162
|
+
];
|
|
163
|
+
for (let i = 0; i < 3; i++) for (let j = 0; j < 3; j++) for (let k = 0; k < 3; k++) out[i][j] += a[i][k] * b[k][j];
|
|
164
|
+
return new Transform2D(out);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Apply the transform to a point. */
|
|
168
|
+
apply(p: Point): Point {
|
|
169
|
+
const [x, y] = p;
|
|
170
|
+
const m = this.m;
|
|
171
|
+
return [m[0][0] * x + m[0][1] * y + m[0][2], m[1][0] * x + m[1][1] * y + m[1][2]];
|
|
172
|
+
}
|
|
173
|
+
}
|