@vib3code/sdk 2.0.3-canary.b52c293 → 2.0.3-canary.bd4d4a5
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/package.json +2 -4
- package/src/geometry/warp/HypersphereCore.js +53 -24
- package/src/math/Mat4x4.js +86 -72
- package/src/math/Rotor4D.js +31 -1
- package/src/scene/Node4D.js +74 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vib3code/sdk",
|
|
3
|
-
"version": "2.0.3-canary.
|
|
3
|
+
"version": "2.0.3-canary.bd4d4a5",
|
|
4
4
|
"description": "VIB3+ 4D Visualization SDK - Unified engine with 6D rotation, MCP agentic integration, and cross-platform support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/core/VIB3Engine.js",
|
|
@@ -9,9 +9,7 @@
|
|
|
9
9
|
"vib3": "./src/cli/index.js",
|
|
10
10
|
"vib3-mcp": "./src/agent/mcp/stdio-server.js"
|
|
11
11
|
},
|
|
12
|
-
"sideEffects":
|
|
13
|
-
"./src/geometry/builders/*.js"
|
|
14
|
-
],
|
|
12
|
+
"sideEffects": false,
|
|
15
13
|
"exports": {
|
|
16
14
|
".": {
|
|
17
15
|
"types": "./types/adaptive-sdk.d.ts",
|
|
@@ -16,13 +16,14 @@ import { Vec4 } from '../../math/Vec4.js';
|
|
|
16
16
|
* @param {number} radius - Hypersphere radius
|
|
17
17
|
* @returns {Vec4} Point on hypersphere
|
|
18
18
|
*/
|
|
19
|
-
export function projectToHypersphere(point, radius = 1) {
|
|
19
|
+
export function projectToHypersphere(point, radius = 1, target = null) {
|
|
20
20
|
const len = point.length();
|
|
21
21
|
if (len < 0.0001) {
|
|
22
22
|
// Handle origin - project to north pole
|
|
23
|
+
if (target) return target.set(0, 0, 0, radius);
|
|
23
24
|
return new Vec4(0, 0, 0, radius);
|
|
24
25
|
}
|
|
25
|
-
return point.scale(radius / len);
|
|
26
|
+
return point.scale(radius / len, target);
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
/**
|
|
@@ -30,9 +31,10 @@ export function projectToHypersphere(point, radius = 1) {
|
|
|
30
31
|
* Maps all of 3D space onto the 4D hypersphere
|
|
31
32
|
* @param {Vec4} point - Input point (uses x, y, z)
|
|
32
33
|
* @param {number} radius - Hypersphere radius
|
|
34
|
+
* @param {Vec4} [target=null] - Optional target vector
|
|
33
35
|
* @returns {Vec4} Point on hypersphere
|
|
34
36
|
*/
|
|
35
|
-
export function stereographicToHypersphere(point, radius = 1) {
|
|
37
|
+
export function stereographicToHypersphere(point, radius = 1, target = null) {
|
|
36
38
|
const x = point.x;
|
|
37
39
|
const y = point.y;
|
|
38
40
|
const z = point.z;
|
|
@@ -40,6 +42,15 @@ export function stereographicToHypersphere(point, radius = 1) {
|
|
|
40
42
|
const sumSq = x * x + y * y + z * z;
|
|
41
43
|
const denom = sumSq + 1;
|
|
42
44
|
|
|
45
|
+
if (target) {
|
|
46
|
+
return target.set(
|
|
47
|
+
(2 * x) / denom * radius,
|
|
48
|
+
(2 * y) / denom * radius,
|
|
49
|
+
(2 * z) / denom * radius,
|
|
50
|
+
(sumSq - 1) / denom * radius
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
43
54
|
return new Vec4(
|
|
44
55
|
(2 * x) / denom * radius,
|
|
45
56
|
(2 * y) / denom * radius,
|
|
@@ -56,12 +67,22 @@ export function stereographicToHypersphere(point, radius = 1) {
|
|
|
56
67
|
* @param {number} phi - Azimuth on base S² (0 to 2π)
|
|
57
68
|
* @param {number} psi - Fiber angle (0 to 2π)
|
|
58
69
|
* @param {number} radius - Hypersphere radius
|
|
70
|
+
* @param {Vec4} [target=null] - Optional target vector
|
|
59
71
|
* @returns {Vec4} Point on hypersphere
|
|
60
72
|
*/
|
|
61
|
-
export function hopfFibration(theta, phi, psi, radius = 1) {
|
|
73
|
+
export function hopfFibration(theta, phi, psi, radius = 1, target = null) {
|
|
62
74
|
const cosTheta2 = Math.cos(theta / 2);
|
|
63
75
|
const sinTheta2 = Math.sin(theta / 2);
|
|
64
76
|
|
|
77
|
+
if (target) {
|
|
78
|
+
return target.set(
|
|
79
|
+
cosTheta2 * Math.cos((phi + psi) / 2) * radius,
|
|
80
|
+
cosTheta2 * Math.sin((phi + psi) / 2) * radius,
|
|
81
|
+
sinTheta2 * Math.cos((phi - psi) / 2) * radius,
|
|
82
|
+
sinTheta2 * Math.sin((phi - psi) / 2) * radius
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
65
86
|
return new Vec4(
|
|
66
87
|
cosTheta2 * Math.cos((phi + psi) / 2) * radius,
|
|
67
88
|
cosTheta2 * Math.sin((phi + psi) / 2) * radius,
|
|
@@ -78,8 +99,9 @@ export function hopfFibration(theta, phi, psi, radius = 1) {
|
|
|
78
99
|
* @returns {Vec4[]} Warped vertices
|
|
79
100
|
*/
|
|
80
101
|
export function warpRadial(vertices, radius = 1, blendFactor = 1) {
|
|
102
|
+
const onSphere = new Vec4();
|
|
81
103
|
return vertices.map(v => {
|
|
82
|
-
|
|
104
|
+
projectToHypersphere(v, radius, onSphere);
|
|
83
105
|
return v.lerp(onSphere, blendFactor);
|
|
84
106
|
});
|
|
85
107
|
}
|
|
@@ -93,8 +115,9 @@ export function warpRadial(vertices, radius = 1, blendFactor = 1) {
|
|
|
93
115
|
* @returns {Vec4[]} Warped vertices
|
|
94
116
|
*/
|
|
95
117
|
export function warpStereographic(vertices, radius = 1, scale = 1) {
|
|
118
|
+
const scaled = new Vec4();
|
|
96
119
|
return vertices.map(v => {
|
|
97
|
-
|
|
120
|
+
v.scale(scale, scaled);
|
|
98
121
|
return stereographicToHypersphere(scaled, radius);
|
|
99
122
|
});
|
|
100
123
|
}
|
|
@@ -146,25 +169,31 @@ export function warpHypersphereCore(geometry, options = {}) {
|
|
|
146
169
|
twist = 1
|
|
147
170
|
} = options;
|
|
148
171
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
172
|
+
const temp = new Vec4();
|
|
173
|
+
const warpedVertices = geometry.vertices.map(v => {
|
|
174
|
+
// Combined scaling and warping to minimize allocations
|
|
175
|
+
const result = v.scale(scale);
|
|
176
|
+
|
|
177
|
+
if (method === 'stereographic') {
|
|
178
|
+
stereographicToHypersphere(result, radius, result);
|
|
179
|
+
} else if (method === 'hopf') {
|
|
180
|
+
const r = result.length();
|
|
181
|
+
if (r < 0.0001) {
|
|
182
|
+
result.set(0, 0, 0, radius);
|
|
183
|
+
} else {
|
|
184
|
+
const theta = Math.acos(result.z / r);
|
|
185
|
+
const phi = Math.atan2(result.y, result.x);
|
|
186
|
+
const psi = result.w * twist + phi * 0.5;
|
|
187
|
+
hopfFibration(theta, phi, psi, radius, result);
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
// Radial (default)
|
|
191
|
+
projectToHypersphere(result, radius, temp);
|
|
192
|
+
result.lerp(temp, blend, result);
|
|
193
|
+
}
|
|
162
194
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
warpedVertices = warpRadial(scaledVertices, radius, blend);
|
|
166
|
-
break;
|
|
167
|
-
}
|
|
195
|
+
return result;
|
|
196
|
+
});
|
|
168
197
|
|
|
169
198
|
return {
|
|
170
199
|
...geometry,
|
package/src/math/Mat4x4.js
CHANGED
|
@@ -18,6 +18,12 @@
|
|
|
18
18
|
import { Vec4 } from './Vec4.js';
|
|
19
19
|
|
|
20
20
|
export class Mat4x4 {
|
|
21
|
+
/**
|
|
22
|
+
* Internal token to skip initialization during construction
|
|
23
|
+
* @private
|
|
24
|
+
*/
|
|
25
|
+
static UNINITIALIZED = {};
|
|
26
|
+
|
|
21
27
|
/**
|
|
22
28
|
* Create a new 4x4 matrix
|
|
23
29
|
* Default is identity matrix
|
|
@@ -26,6 +32,8 @@ export class Mat4x4 {
|
|
|
26
32
|
constructor(elements = null) {
|
|
27
33
|
this.data = new Float32Array(16);
|
|
28
34
|
|
|
35
|
+
if (elements === Mat4x4.UNINITIALIZED) return;
|
|
36
|
+
|
|
29
37
|
if (elements) {
|
|
30
38
|
if (elements.length !== 16) {
|
|
31
39
|
throw new Error('Mat4x4 requires exactly 16 elements');
|
|
@@ -45,12 +53,7 @@ export class Mat4x4 {
|
|
|
45
53
|
* @returns {Mat4x4}
|
|
46
54
|
*/
|
|
47
55
|
static identity() {
|
|
48
|
-
return new Mat4x4(
|
|
49
|
-
1, 0, 0, 0,
|
|
50
|
-
0, 1, 0, 0,
|
|
51
|
-
0, 0, 1, 0,
|
|
52
|
-
0, 0, 0, 1
|
|
53
|
-
]);
|
|
56
|
+
return new Mat4x4();
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
/**
|
|
@@ -58,7 +61,7 @@ export class Mat4x4 {
|
|
|
58
61
|
* @returns {Mat4x4}
|
|
59
62
|
*/
|
|
60
63
|
static zero() {
|
|
61
|
-
return new Mat4x4(
|
|
64
|
+
return new Mat4x4(Mat4x4.UNINITIALIZED);
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
/**
|
|
@@ -161,7 +164,7 @@ export class Mat4x4 {
|
|
|
161
164
|
* @returns {Mat4x4} New matrix = this * m
|
|
162
165
|
*/
|
|
163
166
|
multiply(m, target = null) {
|
|
164
|
-
const out = target || new Mat4x4();
|
|
167
|
+
const out = target || new Mat4x4(Mat4x4.UNINITIALIZED);
|
|
165
168
|
const r = out.data;
|
|
166
169
|
const a = this.data;
|
|
167
170
|
const b = m.data;
|
|
@@ -321,7 +324,7 @@ export class Mat4x4 {
|
|
|
321
324
|
* @returns {Mat4x4} New matrix
|
|
322
325
|
*/
|
|
323
326
|
add(m, target = null) {
|
|
324
|
-
const out = target || new Mat4x4();
|
|
327
|
+
const out = target || new Mat4x4(Mat4x4.UNINITIALIZED);
|
|
325
328
|
const r = out.data;
|
|
326
329
|
const a = this.data;
|
|
327
330
|
const b = m.data;
|
|
@@ -339,7 +342,7 @@ export class Mat4x4 {
|
|
|
339
342
|
* @returns {Mat4x4} New matrix
|
|
340
343
|
*/
|
|
341
344
|
scale(s, target = null) {
|
|
342
|
-
const out = target || new Mat4x4();
|
|
345
|
+
const out = target || new Mat4x4(Mat4x4.UNINITIALIZED);
|
|
343
346
|
const r = out.data;
|
|
344
347
|
const a = this.data;
|
|
345
348
|
|
|
@@ -355,12 +358,13 @@ export class Mat4x4 {
|
|
|
355
358
|
*/
|
|
356
359
|
transpose() {
|
|
357
360
|
const m = this.data;
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
]
|
|
361
|
+
const out = new Mat4x4(Mat4x4.UNINITIALIZED);
|
|
362
|
+
const r = out.data;
|
|
363
|
+
r[0] = m[0]; r[4] = m[1]; r[8] = m[2]; r[12] = m[3];
|
|
364
|
+
r[1] = m[4]; r[5] = m[5]; r[9] = m[6]; r[13] = m[7];
|
|
365
|
+
r[2] = m[8]; r[6] = m[9]; r[10] = m[10]; r[14] = m[11];
|
|
366
|
+
r[3] = m[12]; r[7] = m[13]; r[11] = m[14]; r[15] = m[15];
|
|
367
|
+
return out;
|
|
364
368
|
}
|
|
365
369
|
|
|
366
370
|
/**
|
|
@@ -415,7 +419,8 @@ export class Mat4x4 {
|
|
|
415
419
|
*/
|
|
416
420
|
inverse() {
|
|
417
421
|
const m = this.data;
|
|
418
|
-
const
|
|
422
|
+
const out = new Mat4x4(Mat4x4.UNINITIALIZED);
|
|
423
|
+
const inv = out.data;
|
|
419
424
|
|
|
420
425
|
inv[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] +
|
|
421
426
|
m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10];
|
|
@@ -476,7 +481,7 @@ export class Mat4x4 {
|
|
|
476
481
|
inv[i] *= invDet;
|
|
477
482
|
}
|
|
478
483
|
|
|
479
|
-
return
|
|
484
|
+
return out;
|
|
480
485
|
}
|
|
481
486
|
|
|
482
487
|
/**
|
|
@@ -689,12 +694,13 @@ export class Mat4x4 {
|
|
|
689
694
|
static rotationXY(angle) {
|
|
690
695
|
const c = Math.cos(angle);
|
|
691
696
|
const s = Math.sin(angle);
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
]
|
|
697
|
+
const out = new Mat4x4(Mat4x4.UNINITIALIZED);
|
|
698
|
+
const r = out.data;
|
|
699
|
+
r[0] = c; r[1] = s;
|
|
700
|
+
r[4] = -s; r[5] = c;
|
|
701
|
+
r[10] = 1;
|
|
702
|
+
r[15] = 1;
|
|
703
|
+
return out;
|
|
698
704
|
}
|
|
699
705
|
|
|
700
706
|
/**
|
|
@@ -705,12 +711,13 @@ export class Mat4x4 {
|
|
|
705
711
|
static rotationXZ(angle) {
|
|
706
712
|
const c = Math.cos(angle);
|
|
707
713
|
const s = Math.sin(angle);
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
]
|
|
714
|
+
const out = new Mat4x4(Mat4x4.UNINITIALIZED);
|
|
715
|
+
const r = out.data;
|
|
716
|
+
r[0] = c; r[2] = -s;
|
|
717
|
+
r[5] = 1;
|
|
718
|
+
r[8] = s; r[10] = c;
|
|
719
|
+
r[15] = 1;
|
|
720
|
+
return out;
|
|
714
721
|
}
|
|
715
722
|
|
|
716
723
|
/**
|
|
@@ -721,12 +728,13 @@ export class Mat4x4 {
|
|
|
721
728
|
static rotationYZ(angle) {
|
|
722
729
|
const c = Math.cos(angle);
|
|
723
730
|
const s = Math.sin(angle);
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
]
|
|
731
|
+
const out = new Mat4x4(Mat4x4.UNINITIALIZED);
|
|
732
|
+
const r = out.data;
|
|
733
|
+
r[0] = 1;
|
|
734
|
+
r[5] = c; r[6] = s;
|
|
735
|
+
r[9] = -s; r[10] = c;
|
|
736
|
+
r[15] = 1;
|
|
737
|
+
return out;
|
|
730
738
|
}
|
|
731
739
|
|
|
732
740
|
/**
|
|
@@ -738,12 +746,13 @@ export class Mat4x4 {
|
|
|
738
746
|
static rotationXW(angle) {
|
|
739
747
|
const c = Math.cos(angle);
|
|
740
748
|
const s = Math.sin(angle);
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
]
|
|
749
|
+
const out = new Mat4x4(Mat4x4.UNINITIALIZED);
|
|
750
|
+
const r = out.data;
|
|
751
|
+
r[0] = c; r[3] = s;
|
|
752
|
+
r[5] = 1;
|
|
753
|
+
r[10] = 1;
|
|
754
|
+
r[12] = -s; r[15] = c;
|
|
755
|
+
return out;
|
|
747
756
|
}
|
|
748
757
|
|
|
749
758
|
/**
|
|
@@ -754,12 +763,13 @@ export class Mat4x4 {
|
|
|
754
763
|
static rotationYW(angle) {
|
|
755
764
|
const c = Math.cos(angle);
|
|
756
765
|
const s = Math.sin(angle);
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
]
|
|
766
|
+
const out = new Mat4x4(Mat4x4.UNINITIALIZED);
|
|
767
|
+
const r = out.data;
|
|
768
|
+
r[0] = 1;
|
|
769
|
+
r[5] = c; r[7] = s;
|
|
770
|
+
r[10] = 1;
|
|
771
|
+
r[13] = -s; r[15] = c;
|
|
772
|
+
return out;
|
|
763
773
|
}
|
|
764
774
|
|
|
765
775
|
/**
|
|
@@ -770,12 +780,13 @@ export class Mat4x4 {
|
|
|
770
780
|
static rotationZW(angle) {
|
|
771
781
|
const c = Math.cos(angle);
|
|
772
782
|
const s = Math.sin(angle);
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
]
|
|
783
|
+
const out = new Mat4x4(Mat4x4.UNINITIALIZED);
|
|
784
|
+
const r = out.data;
|
|
785
|
+
r[0] = 1;
|
|
786
|
+
r[5] = 1;
|
|
787
|
+
r[10] = c; r[11] = s;
|
|
788
|
+
r[14] = -s; r[15] = c;
|
|
789
|
+
return out;
|
|
779
790
|
}
|
|
780
791
|
|
|
781
792
|
/**
|
|
@@ -847,12 +858,13 @@ export class Mat4x4 {
|
|
|
847
858
|
* @returns {Mat4x4}
|
|
848
859
|
*/
|
|
849
860
|
static uniformScale(s) {
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
]
|
|
861
|
+
const out = new Mat4x4(Mat4x4.UNINITIALIZED);
|
|
862
|
+
const r = out.data;
|
|
863
|
+
r[0] = s;
|
|
864
|
+
r[5] = s;
|
|
865
|
+
r[10] = s;
|
|
866
|
+
r[15] = s;
|
|
867
|
+
return out;
|
|
856
868
|
}
|
|
857
869
|
|
|
858
870
|
/**
|
|
@@ -864,12 +876,13 @@ export class Mat4x4 {
|
|
|
864
876
|
* @returns {Mat4x4}
|
|
865
877
|
*/
|
|
866
878
|
static scale(sx, sy, sz, sw = 1) {
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
]
|
|
879
|
+
const out = new Mat4x4(Mat4x4.UNINITIALIZED);
|
|
880
|
+
const r = out.data;
|
|
881
|
+
r[0] = sx;
|
|
882
|
+
r[5] = sy;
|
|
883
|
+
r[10] = sz;
|
|
884
|
+
r[15] = sw;
|
|
885
|
+
return out;
|
|
873
886
|
}
|
|
874
887
|
|
|
875
888
|
/**
|
|
@@ -885,12 +898,13 @@ export class Mat4x4 {
|
|
|
885
898
|
static translation(tx, ty, tz, tw = 0) {
|
|
886
899
|
// For true 4D translation, you need 5D homogeneous coordinates
|
|
887
900
|
// This is a placeholder that adds the translation to the W column
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
]
|
|
901
|
+
const out = new Mat4x4(Mat4x4.UNINITIALIZED);
|
|
902
|
+
const r = out.data;
|
|
903
|
+
r[0] = 1;
|
|
904
|
+
r[5] = 1;
|
|
905
|
+
r[10] = 1;
|
|
906
|
+
r[12] = tx; r[13] = ty; r[14] = tz; r[15] = 1 + tw;
|
|
907
|
+
return out;
|
|
894
908
|
}
|
|
895
909
|
}
|
|
896
910
|
|
package/src/math/Rotor4D.js
CHANGED
|
@@ -429,9 +429,10 @@ export class Rotor4D {
|
|
|
429
429
|
|
|
430
430
|
/**
|
|
431
431
|
* Convert rotor to 4x4 rotation matrix (column-major for WebGL)
|
|
432
|
+
* @param {Float32Array|Array} [target] - Optional target array to write into
|
|
432
433
|
* @returns {Float32Array} 16-element array in column-major order
|
|
433
434
|
*/
|
|
434
|
-
toMatrix() {
|
|
435
|
+
toMatrix(target = null) {
|
|
435
436
|
// Normalize first for numerical stability
|
|
436
437
|
const n = this.norm();
|
|
437
438
|
const invN = n > 1e-10 ? 1 / n : 1;
|
|
@@ -495,6 +496,35 @@ export class Rotor4D {
|
|
|
495
496
|
// Formula derived from sandwich product R v R†
|
|
496
497
|
// Diagonal: s² minus bivectors containing that axis, plus others
|
|
497
498
|
// Off-diagonal: 2*s*bivector terms for single-plane contributions
|
|
499
|
+
|
|
500
|
+
if (target) {
|
|
501
|
+
// Column 0 (transformed X axis)
|
|
502
|
+
target[0] = s2 - xy2 - xz2 + yz2 - xw2 + yw2 + zw2 - xyzw2;
|
|
503
|
+
target[1] = sxy + xzyz + xwyw - zwxyzw;
|
|
504
|
+
target[2] = sxz - xyyz + xwzw + ywxyzw;
|
|
505
|
+
target[3] = sxw - xyyw - xzzw - yzxyzw;
|
|
506
|
+
|
|
507
|
+
// Column 1 (transformed Y axis)
|
|
508
|
+
target[4] = -sxy + xzyz + xwyw + zwxyzw;
|
|
509
|
+
target[5] = s2 - xy2 + xz2 - yz2 + xw2 - yw2 + zw2 - xyzw2;
|
|
510
|
+
target[6] = syz + xyxz + ywzw - xwxyzw;
|
|
511
|
+
target[7] = syw + xyxw - yzzw + xzxyzw;
|
|
512
|
+
|
|
513
|
+
// Column 2 (transformed Z axis)
|
|
514
|
+
target[8] = -sxz - xyyz + xwzw - ywxyzw;
|
|
515
|
+
target[9] = -syz + xyxz + ywzw + xwxyzw;
|
|
516
|
+
target[10] = s2 + xy2 - xz2 - yz2 + xw2 + yw2 - zw2 - xyzw2;
|
|
517
|
+
target[11] = szw + xzxw + yzyw - xyxyzw;
|
|
518
|
+
|
|
519
|
+
// Column 3 (transformed W axis)
|
|
520
|
+
target[12] = -sxw - xyyw - xzzw + yzxyzw;
|
|
521
|
+
target[13] = -syw + xyxw - yzzw - xzxyzw;
|
|
522
|
+
target[14] = -szw + xzxw + yzyw + xyxyzw;
|
|
523
|
+
target[15] = s2 + xy2 + xz2 + yz2 - xw2 - yw2 - zw2 - xyzw2;
|
|
524
|
+
|
|
525
|
+
return target;
|
|
526
|
+
}
|
|
527
|
+
|
|
498
528
|
return new Float32Array([
|
|
499
529
|
// Column 0 (transformed X axis)
|
|
500
530
|
s2 - xy2 - xz2 + yz2 - xw2 + yw2 + zw2 - xyzw2,
|
package/src/scene/Node4D.js
CHANGED
|
@@ -500,29 +500,74 @@ export class Node4D {
|
|
|
500
500
|
* @private
|
|
501
501
|
*/
|
|
502
502
|
_updateLocalMatrix() {
|
|
503
|
-
//
|
|
504
|
-
this._localMatrix
|
|
503
|
+
// Ensure matrix exists
|
|
504
|
+
if (!this._localMatrix) {
|
|
505
|
+
this._localMatrix = new Mat4x4();
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const m = this._localMatrix.data;
|
|
509
|
+
const s = this._scale;
|
|
510
|
+
const p = this._position;
|
|
511
|
+
|
|
512
|
+
// 1. Write rotation directly to local matrix (No allocation)
|
|
513
|
+
this._rotation.toMatrix(m);
|
|
514
|
+
|
|
515
|
+
// 2. Apply scale (Diagonal matrix multiplication on the right)
|
|
516
|
+
// M = M * S
|
|
517
|
+
// Columns of M are scaled by s.x, s.y, s.z, s.w
|
|
518
|
+
|
|
519
|
+
// Col 0
|
|
520
|
+
m[0] *= s.x; m[1] *= s.x; m[2] *= s.x; m[3] *= s.x;
|
|
521
|
+
// Col 1
|
|
522
|
+
m[4] *= s.y; m[5] *= s.y; m[6] *= s.y; m[7] *= s.y;
|
|
523
|
+
// Col 2
|
|
524
|
+
m[8] *= s.z; m[9] *= s.z; m[10] *= s.z; m[11] *= s.z;
|
|
525
|
+
// Col 3
|
|
526
|
+
m[12] *= s.w; m[13] *= s.w; m[14] *= s.w; m[15] *= s.w;
|
|
527
|
+
|
|
528
|
+
// 3. Apply translation (Matrix multiplication on the left)
|
|
529
|
+
// M = T * M
|
|
530
|
+
// T is standard 3D translation:
|
|
531
|
+
// [ 1 0 0 px ]
|
|
532
|
+
// [ 0 1 0 py ]
|
|
533
|
+
// [ 0 0 1 pz ]
|
|
534
|
+
// [ 0 0 0 1 ]
|
|
535
|
+
//
|
|
536
|
+
// Row 0 += px * Row 3
|
|
537
|
+
// Row 1 += py * Row 3
|
|
538
|
+
// Row 2 += pz * Row 3
|
|
539
|
+
|
|
540
|
+
const px = p.x;
|
|
541
|
+
const py = p.y;
|
|
542
|
+
const pz = p.z;
|
|
543
|
+
|
|
544
|
+
// Row 3 elements of M (which are used in the calculation)
|
|
545
|
+
const m3 = m[3];
|
|
546
|
+
const m7 = m[7];
|
|
547
|
+
const m11 = m[11];
|
|
548
|
+
const m15 = m[15];
|
|
549
|
+
|
|
550
|
+
if (px !== 0) {
|
|
551
|
+
m[0] += px * m3;
|
|
552
|
+
m[4] += px * m7;
|
|
553
|
+
m[8] += px * m11;
|
|
554
|
+
m[12] += px * m15;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (py !== 0) {
|
|
558
|
+
m[1] += py * m3;
|
|
559
|
+
m[5] += py * m7;
|
|
560
|
+
m[9] += py * m11;
|
|
561
|
+
m[13] += py * m15;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
if (pz !== 0) {
|
|
565
|
+
m[2] += pz * m3;
|
|
566
|
+
m[6] += pz * m7;
|
|
567
|
+
m[10] += pz * m11;
|
|
568
|
+
m[14] += pz * m15;
|
|
569
|
+
}
|
|
505
570
|
|
|
506
|
-
// Apply scale
|
|
507
|
-
const scaleMatrix = Mat4x4.identity();
|
|
508
|
-
scaleMatrix.set(0, 0, this._scale.x);
|
|
509
|
-
scaleMatrix.set(1, 1, this._scale.y);
|
|
510
|
-
scaleMatrix.set(2, 2, this._scale.z);
|
|
511
|
-
scaleMatrix.set(3, 3, this._scale.w);
|
|
512
|
-
|
|
513
|
-
// Apply rotation (toMatrix returns Float32Array, wrap in Mat4x4)
|
|
514
|
-
const rotationMatrix = new Mat4x4(this._rotation.toMatrix());
|
|
515
|
-
|
|
516
|
-
// Apply translation (in 4D, translation is stored in last column, keep [3,3]=1)
|
|
517
|
-
const translationMatrix = Mat4x4.identity();
|
|
518
|
-
translationMatrix.set(0, 3, this._position.x);
|
|
519
|
-
translationMatrix.set(1, 3, this._position.y);
|
|
520
|
-
translationMatrix.set(2, 3, this._position.z);
|
|
521
|
-
// Note: position.w is the 4th spatial coordinate, handled separately
|
|
522
|
-
// Matrix[3,3] must remain 1 for proper transformation
|
|
523
|
-
|
|
524
|
-
// Compose: T * R * S
|
|
525
|
-
this._localMatrix = translationMatrix.multiply(rotationMatrix).multiply(scaleMatrix);
|
|
526
571
|
this._localDirty = false;
|
|
527
572
|
}
|
|
528
573
|
|
|
@@ -535,10 +580,15 @@ export class Node4D {
|
|
|
535
580
|
this._updateLocalMatrix();
|
|
536
581
|
}
|
|
537
582
|
|
|
583
|
+
// Ensure matrix exists
|
|
584
|
+
if (!this._worldMatrix) {
|
|
585
|
+
this._worldMatrix = new Mat4x4();
|
|
586
|
+
}
|
|
587
|
+
|
|
538
588
|
if (this._parent) {
|
|
539
|
-
this.
|
|
589
|
+
this._parent.worldMatrix.multiply(this._localMatrix, this._worldMatrix);
|
|
540
590
|
} else {
|
|
541
|
-
this._worldMatrix
|
|
591
|
+
this._worldMatrix.copy(this._localMatrix);
|
|
542
592
|
}
|
|
543
593
|
|
|
544
594
|
this._worldDirty = false;
|