@vib3code/sdk 2.0.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/CHANGELOG.md +118 -0
- package/DOCS/BLUEPRINT_EXECUTION_PLAN_2026-01-07.md +34 -0
- package/DOCS/CI_TESTING.md +38 -0
- package/DOCS/CLI_ONBOARDING.md +75 -0
- package/DOCS/CONTROL_REFERENCE.md +64 -0
- package/DOCS/DEV_TRACK_ANALYSIS.md +77 -0
- package/DOCS/DEV_TRACK_PLAN_2026-01-07.md +42 -0
- package/DOCS/DEV_TRACK_SESSION_2026-01-31.md +220 -0
- package/DOCS/ENV_SETUP.md +189 -0
- package/DOCS/EXPORT_FORMATS.md +417 -0
- package/DOCS/GPU_DISPOSAL_GUIDE.md +21 -0
- package/DOCS/LICENSING_TIERS.md +275 -0
- package/DOCS/MASTER_PLAN_2026-01-31.md +570 -0
- package/DOCS/OBS_SETUP_GUIDE.md +98 -0
- package/DOCS/PROJECT_SETUP.md +66 -0
- package/DOCS/RENDERER_LIFECYCLE.md +40 -0
- package/DOCS/REPO_MANIFEST.md +121 -0
- package/DOCS/SESSION_014_PLAN.md +195 -0
- package/DOCS/SESSION_LOG_2026-01-07.md +56 -0
- package/DOCS/STRATEGIC_BLUEPRINT_2026-01-07.md +72 -0
- package/DOCS/SYSTEM_AUDIT_2026-01-30.md +738 -0
- package/DOCS/SYSTEM_INVENTORY.md +520 -0
- package/DOCS/TELEMETRY_EXPORTS.md +34 -0
- package/DOCS/WEBGPU_STATUS.md +38 -0
- package/DOCS/XR_BENCHMARKS.md +608 -0
- package/LICENSE +21 -0
- package/README.md +426 -0
- package/docs/.nojekyll +0 -0
- package/docs/01-dissolution_of_euclidean_hegemony.html +346 -0
- package/docs/02-hyperspatial_ego_death.html +346 -0
- package/docs/03-post_cartesian_sublime.html +346 -0
- package/docs/04-crystalline_void_meditation.html +346 -0
- package/docs/05-quantum_decoherence_ballet.html +346 -0
- package/docs/06-dissolution_of_euclidean_hegemony.html +346 -0
- package/docs/07-hyperspatial_ego_death.html +346 -0
- package/docs/08-post_cartesian_sublime.html +346 -0
- package/docs/09-crystalline_void_meditation.html +346 -0
- package/docs/10-quantum_decoherence_ballet.html +346 -0
- package/docs/11-dissolution_of_euclidean_hegemony.html +346 -0
- package/docs/12-hyperspatial_ego_death.html +346 -0
- package/docs/13-post_cartesian_sublime.html +346 -0
- package/docs/index.html +794 -0
- package/docs/test-hub.html +441 -0
- package/docs/url-state.js +102 -0
- package/docs/vib3-exports/01-quantum-quantum-tetrahedron-lattice.html +489 -0
- package/docs/vib3-exports/02-quantum-quantum-hypersphere-matrix.html +489 -0
- package/docs/vib3-exports/03-quantum-quantum-hypertetra-fractal.html +489 -0
- package/docs/vib3-exports/04-faceted-faceted-crystal-structure.html +407 -0
- package/docs/vib3-exports/05-faceted-faceted-klein-bottle.html +407 -0
- package/docs/vib3-exports/06-faceted-faceted-hypertetra-torus.html +407 -0
- package/docs/vib3-exports/07-holographic-holographic-wave-field.html +457 -0
- package/docs/vib3-exports/08-holographic-holographic-hypersphere-sphere.html +457 -0
- package/docs/vib3-exports/09-holographic-holographic-hypertetra-crystal.html +457 -0
- package/docs/vib3-exports/index.html +238 -0
- package/docs/webgpu-live.html +702 -0
- package/package.json +367 -0
- package/src/advanced/AIPresetGenerator.js +777 -0
- package/src/advanced/MIDIController.js +703 -0
- package/src/advanced/OffscreenWorker.js +1051 -0
- package/src/advanced/WebGPUCompute.js +1051 -0
- package/src/advanced/WebXRRenderer.js +680 -0
- package/src/agent/cli/AgentCLI.js +615 -0
- package/src/agent/cli/index.js +14 -0
- package/src/agent/index.js +73 -0
- package/src/agent/mcp/MCPServer.js +950 -0
- package/src/agent/mcp/index.js +9 -0
- package/src/agent/mcp/tools.js +548 -0
- package/src/agent/telemetry/EventStream.js +669 -0
- package/src/agent/telemetry/Instrumentation.js +618 -0
- package/src/agent/telemetry/TelemetryExporters.js +427 -0
- package/src/agent/telemetry/TelemetryService.js +464 -0
- package/src/agent/telemetry/index.js +52 -0
- package/src/benchmarks/BenchmarkRunner.js +381 -0
- package/src/benchmarks/MetricsCollector.js +299 -0
- package/src/benchmarks/index.js +9 -0
- package/src/benchmarks/scenes.js +259 -0
- package/src/cli/index.js +675 -0
- package/src/config/ApiConfig.js +88 -0
- package/src/core/CanvasManager.js +217 -0
- package/src/core/ErrorReporter.js +117 -0
- package/src/core/ParameterMapper.js +333 -0
- package/src/core/Parameters.js +396 -0
- package/src/core/RendererContracts.js +200 -0
- package/src/core/UnifiedResourceManager.js +370 -0
- package/src/core/VIB3Engine.js +636 -0
- package/src/core/renderers/FacetedRendererAdapter.js +32 -0
- package/src/core/renderers/HolographicRendererAdapter.js +29 -0
- package/src/core/renderers/QuantumRendererAdapter.js +29 -0
- package/src/core/renderers/RendererLifecycleManager.js +63 -0
- package/src/creative/ColorPresetsSystem.js +980 -0
- package/src/creative/ParameterTimeline.js +1061 -0
- package/src/creative/PostProcessingPipeline.js +1113 -0
- package/src/creative/TransitionAnimator.js +683 -0
- package/src/export/CSSExporter.js +226 -0
- package/src/export/CardGeneratorBase.js +279 -0
- package/src/export/ExportManager.js +580 -0
- package/src/export/FacetedCardGenerator.js +279 -0
- package/src/export/HolographicCardGenerator.js +543 -0
- package/src/export/LottieExporter.js +552 -0
- package/src/export/QuantumCardGenerator.js +315 -0
- package/src/export/SVGExporter.js +519 -0
- package/src/export/ShaderExporter.js +903 -0
- package/src/export/TradingCardGenerator.js +3055 -0
- package/src/export/TradingCardManager.js +181 -0
- package/src/export/VIB3PackageExporter.js +559 -0
- package/src/export/index.js +14 -0
- package/src/export/systems/TradingCardSystemFaceted.js +494 -0
- package/src/export/systems/TradingCardSystemHolographic.js +452 -0
- package/src/export/systems/TradingCardSystemQuantum.js +411 -0
- package/src/faceted/FacetedSystem.js +963 -0
- package/src/features/CollectionManager.js +433 -0
- package/src/gallery/CollectionManager.js +240 -0
- package/src/gallery/GallerySystem.js +485 -0
- package/src/geometry/GeometryFactory.js +314 -0
- package/src/geometry/GeometryLibrary.js +72 -0
- package/src/geometry/buffers/BufferBuilder.js +338 -0
- package/src/geometry/buffers/index.js +18 -0
- package/src/geometry/generators/Crystal.js +420 -0
- package/src/geometry/generators/Fractal.js +298 -0
- package/src/geometry/generators/KleinBottle.js +197 -0
- package/src/geometry/generators/Sphere.js +192 -0
- package/src/geometry/generators/Tesseract.js +160 -0
- package/src/geometry/generators/Tetrahedron.js +225 -0
- package/src/geometry/generators/Torus.js +304 -0
- package/src/geometry/generators/Wave.js +341 -0
- package/src/geometry/index.js +142 -0
- package/src/geometry/warp/HypersphereCore.js +211 -0
- package/src/geometry/warp/HypertetraCore.js +386 -0
- package/src/geometry/warp/index.js +57 -0
- package/src/holograms/HolographicVisualizer.js +1073 -0
- package/src/holograms/RealHolographicSystem.js +966 -0
- package/src/holograms/variantRegistry.js +69 -0
- package/src/integrations/FigmaPlugin.js +854 -0
- package/src/integrations/OBSMode.js +754 -0
- package/src/integrations/ThreeJsPackage.js +660 -0
- package/src/integrations/TouchDesignerExport.js +552 -0
- package/src/integrations/frameworks/Vib3React.js +591 -0
- package/src/integrations/frameworks/Vib3Svelte.js +654 -0
- package/src/integrations/frameworks/Vib3Vue.js +628 -0
- package/src/llm/LLMParameterInterface.js +240 -0
- package/src/llm/LLMParameterUI.js +577 -0
- package/src/math/Mat4x4.js +708 -0
- package/src/math/Projection.js +341 -0
- package/src/math/Rotor4D.js +637 -0
- package/src/math/Vec4.js +476 -0
- package/src/math/constants.js +164 -0
- package/src/math/index.js +68 -0
- package/src/math/projections.js +54 -0
- package/src/math/rotations.js +196 -0
- package/src/quantum/QuantumEngine.js +906 -0
- package/src/quantum/QuantumVisualizer.js +1103 -0
- package/src/reactivity/ReactivityConfig.js +499 -0
- package/src/reactivity/ReactivityManager.js +586 -0
- package/src/reactivity/SpatialInputSystem.js +1783 -0
- package/src/reactivity/index.js +93 -0
- package/src/render/CommandBuffer.js +465 -0
- package/src/render/MultiCanvasBridge.js +340 -0
- package/src/render/RenderCommand.js +514 -0
- package/src/render/RenderResourceRegistry.js +523 -0
- package/src/render/RenderState.js +552 -0
- package/src/render/RenderTarget.js +512 -0
- package/src/render/ShaderLoader.js +253 -0
- package/src/render/ShaderProgram.js +599 -0
- package/src/render/UnifiedRenderBridge.js +496 -0
- package/src/render/backends/WebGLBackend.js +1108 -0
- package/src/render/backends/WebGPUBackend.js +1409 -0
- package/src/render/commands/CommandBufferExecutor.js +607 -0
- package/src/render/commands/RenderCommandBuffer.js +661 -0
- package/src/render/commands/index.js +17 -0
- package/src/render/index.js +367 -0
- package/src/scene/Disposable.js +498 -0
- package/src/scene/MemoryPool.js +618 -0
- package/src/scene/Node4D.js +697 -0
- package/src/scene/ResourceManager.js +599 -0
- package/src/scene/Scene4D.js +540 -0
- package/src/scene/index.js +98 -0
- package/src/schemas/error.schema.json +84 -0
- package/src/schemas/extension.schema.json +88 -0
- package/src/schemas/index.js +214 -0
- package/src/schemas/parameters.schema.json +142 -0
- package/src/schemas/tool-pack.schema.json +44 -0
- package/src/schemas/tool-response.schema.json +127 -0
- package/src/shaders/common/fullscreen.vert.glsl +5 -0
- package/src/shaders/common/fullscreen.vert.wgsl +17 -0
- package/src/shaders/common/geometry24.glsl +65 -0
- package/src/shaders/common/geometry24.wgsl +54 -0
- package/src/shaders/common/rotation4d.glsl +85 -0
- package/src/shaders/common/rotation4d.wgsl +86 -0
- package/src/shaders/common/uniforms.glsl +44 -0
- package/src/shaders/common/uniforms.wgsl +48 -0
- package/src/shaders/faceted/faceted.frag.glsl +129 -0
- package/src/shaders/faceted/faceted.frag.wgsl +164 -0
- package/src/shaders/holographic/holographic.frag.glsl +406 -0
- package/src/shaders/holographic/holographic.frag.wgsl +185 -0
- package/src/shaders/quantum/quantum.frag.glsl +513 -0
- package/src/shaders/quantum/quantum.frag.wgsl +361 -0
- package/src/testing/ParallelTestFramework.js +519 -0
- package/src/testing/__snapshots__/exportFormats.test.js.snap +24 -0
- package/src/testing/exportFormats.test.js +8 -0
- package/src/testing/projections.test.js +14 -0
- package/src/testing/rotations.test.js +37 -0
- package/src/ui/InteractivityMenu.js +516 -0
- package/src/ui/StatusManager.js +96 -0
- package/src/ui/adaptive/renderers/webgpu/BufferLayout.ts +252 -0
- package/src/ui/adaptive/renderers/webgpu/PolytopeInstanceBuffer.ts +144 -0
- package/src/ui/adaptive/renderers/webgpu/TripleBufferedUniform.ts +170 -0
- package/src/ui/adaptive/renderers/webgpu/WebGPURenderer.ts +735 -0
- package/src/ui/adaptive/renderers/webgpu/index.ts +112 -0
- package/src/variations/VariationManager.js +431 -0
- package/src/viewer/AudioReactivity.js +505 -0
- package/src/viewer/CardBending.js +481 -0
- package/src/viewer/GalleryUI.js +832 -0
- package/src/viewer/ReactivityManager.js +590 -0
- package/src/viewer/TradingCardExporter.js +600 -0
- package/src/viewer/ViewerPortal.js +374 -0
- package/src/viewer/index.js +12 -0
- package/src/wasm/WasmLoader.js +296 -0
- package/src/wasm/index.js +132 -0
- package/tools/agentic/mcpTools.js +88 -0
- package/tools/cli/agent-cli.js +92 -0
- package/tools/export/formats.js +24 -0
- package/tools/math/rotation-baseline.mjs +64 -0
- package/tools/shader-sync-verify.js +937 -0
- package/tools/telemetry/manifestPipeline.js +141 -0
- package/tools/telemetry/telemetryEvents.js +35 -0
- package/types/adaptive-sdk.d.ts +185 -0
- package/types/advanced/AIPresetGenerator.d.ts +81 -0
- package/types/advanced/MIDIController.d.ts +100 -0
- package/types/advanced/OffscreenWorker.d.ts +82 -0
- package/types/advanced/WebGPUCompute.d.ts +52 -0
- package/types/advanced/WebXRRenderer.d.ts +77 -0
- package/types/advanced/index.d.ts +46 -0
- package/types/core/ErrorReporter.d.ts +50 -0
- package/types/core/VIB3Engine.d.ts +204 -0
- package/types/creative/ColorPresetsSystem.d.ts +91 -0
- package/types/creative/ParameterTimeline.d.ts +74 -0
- package/types/creative/PostProcessingPipeline.d.ts +109 -0
- package/types/creative/TransitionAnimator.d.ts +71 -0
- package/types/creative/index.d.ts +35 -0
- package/types/integrations/FigmaPlugin.d.ts +46 -0
- package/types/integrations/OBSMode.d.ts +74 -0
- package/types/integrations/ThreeJsPackage.d.ts +62 -0
- package/types/integrations/TouchDesignerExport.d.ts +36 -0
- package/types/integrations/Vib3React.d.ts +74 -0
- package/types/integrations/Vib3Svelte.d.ts +63 -0
- package/types/integrations/Vib3Vue.d.ts +55 -0
- package/types/integrations/index.d.ts +52 -0
- package/types/reactivity/SpatialInputSystem.d.ts +173 -0
- package/types/reactivity/index.d.ts +394 -0
- package/types/render/CommandBuffer.d.ts +169 -0
- package/types/render/RenderCommand.d.ts +312 -0
- package/types/render/RenderState.d.ts +279 -0
- package/types/render/RenderTarget.d.ts +254 -0
- package/types/render/ShaderProgram.d.ts +277 -0
- package/types/render/UnifiedRenderBridge.d.ts +143 -0
- package/types/render/WebGLBackend.d.ts +168 -0
- package/types/render/WebGPUBackend.d.ts +186 -0
- package/types/render/index.d.ts +141 -0
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node4D - Base class for 4D scene graph nodes
|
|
3
|
+
*
|
|
4
|
+
* Provides hierarchical transform management with parent-child relationships.
|
|
5
|
+
* Supports both local and world transforms in 4D space.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Vec4 } from '../math/Vec4.js';
|
|
9
|
+
import { Mat4x4 } from '../math/Mat4x4.js';
|
|
10
|
+
import { Rotor4D } from '../math/Rotor4D.js';
|
|
11
|
+
|
|
12
|
+
let nodeIdCounter = 0;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generate unique node ID
|
|
16
|
+
* @returns {string}
|
|
17
|
+
*/
|
|
18
|
+
function generateNodeId() {
|
|
19
|
+
return `node_${++nodeIdCounter}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Node4D class - Base scene graph node
|
|
24
|
+
*/
|
|
25
|
+
export class Node4D {
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} [name] - Optional node name
|
|
28
|
+
*/
|
|
29
|
+
constructor(name = '') {
|
|
30
|
+
/** @type {string} Unique identifier */
|
|
31
|
+
this.id = generateNodeId();
|
|
32
|
+
|
|
33
|
+
/** @type {string} Human-readable name */
|
|
34
|
+
this.name = name || this.id;
|
|
35
|
+
|
|
36
|
+
/** @type {Node4D|null} Parent node */
|
|
37
|
+
this._parent = null;
|
|
38
|
+
|
|
39
|
+
/** @type {Node4D[]} Child nodes */
|
|
40
|
+
this._children = [];
|
|
41
|
+
|
|
42
|
+
/** @type {Vec4} Local position */
|
|
43
|
+
this._position = Vec4.zero();
|
|
44
|
+
|
|
45
|
+
/** @type {Rotor4D} Local rotation */
|
|
46
|
+
this._rotation = Rotor4D.identity();
|
|
47
|
+
|
|
48
|
+
/** @type {Vec4} Local scale (uniform in each dimension) */
|
|
49
|
+
this._scale = new Vec4(1, 1, 1, 1);
|
|
50
|
+
|
|
51
|
+
/** @type {Mat4x4} Local transform matrix (cached) */
|
|
52
|
+
this._localMatrix = Mat4x4.identity();
|
|
53
|
+
|
|
54
|
+
/** @type {Mat4x4} World transform matrix (cached) */
|
|
55
|
+
this._worldMatrix = Mat4x4.identity();
|
|
56
|
+
|
|
57
|
+
/** @type {boolean} Whether local matrix needs recalculation */
|
|
58
|
+
this._localDirty = true;
|
|
59
|
+
|
|
60
|
+
/** @type {boolean} Whether world matrix needs recalculation */
|
|
61
|
+
this._worldDirty = true;
|
|
62
|
+
|
|
63
|
+
/** @type {boolean} Whether node is visible */
|
|
64
|
+
this.visible = true;
|
|
65
|
+
|
|
66
|
+
/** @type {boolean} Whether node is enabled for updates */
|
|
67
|
+
this.enabled = true;
|
|
68
|
+
|
|
69
|
+
/** @type {Map<string, any>} User data storage */
|
|
70
|
+
this.userData = new Map();
|
|
71
|
+
|
|
72
|
+
/** @type {string[]} Tags for filtering */
|
|
73
|
+
this.tags = [];
|
|
74
|
+
|
|
75
|
+
/** @type {number} Layer mask for rendering/physics */
|
|
76
|
+
this.layerMask = 1;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ==================== Hierarchy ====================
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get parent node
|
|
83
|
+
* @returns {Node4D|null}
|
|
84
|
+
*/
|
|
85
|
+
get parent() {
|
|
86
|
+
return this._parent;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Set parent node
|
|
91
|
+
* @param {Node4D|null} newParent
|
|
92
|
+
*/
|
|
93
|
+
set parent(newParent) {
|
|
94
|
+
if (this._parent === newParent) return;
|
|
95
|
+
|
|
96
|
+
// Remove from current parent
|
|
97
|
+
if (this._parent) {
|
|
98
|
+
const idx = this._parent._children.indexOf(this);
|
|
99
|
+
if (idx !== -1) {
|
|
100
|
+
this._parent._children.splice(idx, 1);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Set new parent
|
|
105
|
+
this._parent = newParent;
|
|
106
|
+
|
|
107
|
+
// Add to new parent's children
|
|
108
|
+
if (newParent) {
|
|
109
|
+
newParent._children.push(this);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Mark world transform as dirty
|
|
113
|
+
this._markWorldDirty();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get children array (read-only copy)
|
|
118
|
+
* @returns {Node4D[]}
|
|
119
|
+
*/
|
|
120
|
+
get children() {
|
|
121
|
+
return [...this._children];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get number of children
|
|
126
|
+
* @returns {number}
|
|
127
|
+
*/
|
|
128
|
+
get childCount() {
|
|
129
|
+
return this._children.length;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Add a child node
|
|
134
|
+
* @param {Node4D} child
|
|
135
|
+
* @returns {this}
|
|
136
|
+
*/
|
|
137
|
+
addChild(child) {
|
|
138
|
+
if (child._parent === this) return this;
|
|
139
|
+
if (child === this) throw new Error('Cannot add node as child of itself');
|
|
140
|
+
if (this.isDescendantOf(child)) throw new Error('Cannot create circular hierarchy');
|
|
141
|
+
|
|
142
|
+
child.parent = this;
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Remove a child node
|
|
148
|
+
* @param {Node4D} child
|
|
149
|
+
* @returns {boolean} True if child was removed
|
|
150
|
+
*/
|
|
151
|
+
removeChild(child) {
|
|
152
|
+
if (child._parent !== this) return false;
|
|
153
|
+
child.parent = null;
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Remove all children
|
|
159
|
+
* @returns {this}
|
|
160
|
+
*/
|
|
161
|
+
removeAllChildren() {
|
|
162
|
+
for (const child of [...this._children]) {
|
|
163
|
+
child.parent = null;
|
|
164
|
+
}
|
|
165
|
+
return this;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get child by index
|
|
170
|
+
* @param {number} index
|
|
171
|
+
* @returns {Node4D|undefined}
|
|
172
|
+
*/
|
|
173
|
+
getChildAt(index) {
|
|
174
|
+
return this._children[index];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get child by name
|
|
179
|
+
* @param {string} name
|
|
180
|
+
* @returns {Node4D|undefined}
|
|
181
|
+
*/
|
|
182
|
+
getChildByName(name) {
|
|
183
|
+
return this._children.find(c => c.name === name);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Find descendant by name (recursive)
|
|
188
|
+
* @param {string} name
|
|
189
|
+
* @returns {Node4D|undefined}
|
|
190
|
+
*/
|
|
191
|
+
findByName(name) {
|
|
192
|
+
if (this.name === name) return this;
|
|
193
|
+
for (const child of this._children) {
|
|
194
|
+
const found = child.findByName(name);
|
|
195
|
+
if (found) return found;
|
|
196
|
+
}
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Find all descendants with tag
|
|
202
|
+
* @param {string} tag
|
|
203
|
+
* @returns {Node4D[]}
|
|
204
|
+
*/
|
|
205
|
+
findByTag(tag) {
|
|
206
|
+
const results = [];
|
|
207
|
+
if (this.tags.includes(tag)) results.push(this);
|
|
208
|
+
for (const child of this._children) {
|
|
209
|
+
results.push(...child.findByTag(tag));
|
|
210
|
+
}
|
|
211
|
+
return results;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Check if this node is descendant of another
|
|
216
|
+
* @param {Node4D} ancestor
|
|
217
|
+
* @returns {boolean}
|
|
218
|
+
*/
|
|
219
|
+
isDescendantOf(ancestor) {
|
|
220
|
+
let current = this._parent;
|
|
221
|
+
while (current) {
|
|
222
|
+
if (current === ancestor) return true;
|
|
223
|
+
current = current._parent;
|
|
224
|
+
}
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get root node of hierarchy
|
|
230
|
+
* @returns {Node4D}
|
|
231
|
+
*/
|
|
232
|
+
getRoot() {
|
|
233
|
+
let current = this;
|
|
234
|
+
while (current._parent) {
|
|
235
|
+
current = current._parent;
|
|
236
|
+
}
|
|
237
|
+
return current;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Get depth in hierarchy (root = 0)
|
|
242
|
+
* @returns {number}
|
|
243
|
+
*/
|
|
244
|
+
getDepth() {
|
|
245
|
+
let depth = 0;
|
|
246
|
+
let current = this._parent;
|
|
247
|
+
while (current) {
|
|
248
|
+
depth++;
|
|
249
|
+
current = current._parent;
|
|
250
|
+
}
|
|
251
|
+
return depth;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ==================== Transform ====================
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Get local position
|
|
258
|
+
* @returns {Vec4}
|
|
259
|
+
*/
|
|
260
|
+
get position() {
|
|
261
|
+
return this._position;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Set local position
|
|
266
|
+
* @param {Vec4} value
|
|
267
|
+
*/
|
|
268
|
+
set position(value) {
|
|
269
|
+
this._position = value;
|
|
270
|
+
this._markLocalDirty();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Get local rotation
|
|
275
|
+
* @returns {Rotor4D}
|
|
276
|
+
*/
|
|
277
|
+
get rotation() {
|
|
278
|
+
return this._rotation;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Set local rotation
|
|
283
|
+
* @param {Rotor4D} value
|
|
284
|
+
*/
|
|
285
|
+
set rotation(value) {
|
|
286
|
+
this._rotation = value;
|
|
287
|
+
this._markLocalDirty();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Get local scale
|
|
292
|
+
* @returns {Vec4}
|
|
293
|
+
*/
|
|
294
|
+
get scale() {
|
|
295
|
+
return this._scale;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Set local scale
|
|
300
|
+
* @param {Vec4} value
|
|
301
|
+
*/
|
|
302
|
+
set scale(value) {
|
|
303
|
+
this._scale = value;
|
|
304
|
+
this._markLocalDirty();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Set position components directly
|
|
309
|
+
* @param {number} x
|
|
310
|
+
* @param {number} y
|
|
311
|
+
* @param {number} z
|
|
312
|
+
* @param {number} w
|
|
313
|
+
* @returns {this}
|
|
314
|
+
*/
|
|
315
|
+
setPosition(x, y, z, w) {
|
|
316
|
+
this._position = new Vec4(x, y, z, w);
|
|
317
|
+
this._markLocalDirty();
|
|
318
|
+
return this;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Set uniform scale
|
|
323
|
+
* @param {number} s
|
|
324
|
+
* @returns {this}
|
|
325
|
+
*/
|
|
326
|
+
setUniformScale(s) {
|
|
327
|
+
this._scale = new Vec4(s, s, s, s);
|
|
328
|
+
this._markLocalDirty();
|
|
329
|
+
return this;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Translate by offset
|
|
334
|
+
* @param {Vec4} offset
|
|
335
|
+
* @returns {this}
|
|
336
|
+
*/
|
|
337
|
+
translate(offset) {
|
|
338
|
+
this._position = this._position.add(offset);
|
|
339
|
+
this._markLocalDirty();
|
|
340
|
+
return this;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Rotate by rotor
|
|
345
|
+
* @param {Rotor4D} rotor
|
|
346
|
+
* @returns {this}
|
|
347
|
+
*/
|
|
348
|
+
rotate(rotor) {
|
|
349
|
+
this._rotation = rotor.multiply(this._rotation).normalize();
|
|
350
|
+
this._markLocalDirty();
|
|
351
|
+
return this;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Rotate around a plane
|
|
356
|
+
* @param {string} plane - 'XY', 'XZ', 'YZ', 'XW', 'YW', 'ZW'
|
|
357
|
+
* @param {number} angle - Radians
|
|
358
|
+
* @returns {this}
|
|
359
|
+
*/
|
|
360
|
+
rotateOnPlane(plane, angle) {
|
|
361
|
+
const rotor = Rotor4D.fromPlaneAngle(plane, angle);
|
|
362
|
+
return this.rotate(rotor);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Get local transform matrix
|
|
367
|
+
* @returns {Mat4x4}
|
|
368
|
+
*/
|
|
369
|
+
get localMatrix() {
|
|
370
|
+
if (this._localDirty) {
|
|
371
|
+
this._updateLocalMatrix();
|
|
372
|
+
}
|
|
373
|
+
return this._localMatrix;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Get world transform matrix
|
|
378
|
+
* @returns {Mat4x4}
|
|
379
|
+
*/
|
|
380
|
+
get worldMatrix() {
|
|
381
|
+
if (this._worldDirty) {
|
|
382
|
+
this._updateWorldMatrix();
|
|
383
|
+
}
|
|
384
|
+
return this._worldMatrix;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Get world position
|
|
389
|
+
* @returns {Vec4}
|
|
390
|
+
*/
|
|
391
|
+
get worldPosition() {
|
|
392
|
+
const wm = this.worldMatrix;
|
|
393
|
+
return new Vec4(wm.get(0, 3), wm.get(1, 3), wm.get(2, 3), wm.get(3, 3));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Set world position (adjusts local position)
|
|
398
|
+
* @param {Vec4} worldPos
|
|
399
|
+
* @returns {this}
|
|
400
|
+
*/
|
|
401
|
+
setWorldPosition(worldPos) {
|
|
402
|
+
if (this._parent) {
|
|
403
|
+
const parentWorldInv = this._parent.worldMatrix.inverse();
|
|
404
|
+
this._position = parentWorldInv.multiplyVec4(worldPos);
|
|
405
|
+
} else {
|
|
406
|
+
this._position = worldPos;
|
|
407
|
+
}
|
|
408
|
+
this._markLocalDirty();
|
|
409
|
+
return this;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Transform a point from local to world space
|
|
414
|
+
* Note: In 4D, we can't use homogeneous coordinates for translation
|
|
415
|
+
* since W is a spatial dimension. We apply rotation/scale via the
|
|
416
|
+
* rotation part of the matrix, then add world position separately.
|
|
417
|
+
* @param {Vec4} localPoint
|
|
418
|
+
* @returns {Vec4}
|
|
419
|
+
*/
|
|
420
|
+
localToWorld(localPoint) {
|
|
421
|
+
// Get local rotation/scale only (exclude translation)
|
|
422
|
+
// by using the rotation rotor directly
|
|
423
|
+
const scaledPoint = new Vec4(
|
|
424
|
+
localPoint.x * this._scale.x,
|
|
425
|
+
localPoint.y * this._scale.y,
|
|
426
|
+
localPoint.z * this._scale.z,
|
|
427
|
+
localPoint.w * this._scale.w
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
// Apply rotation
|
|
431
|
+
const rotated = this._rotation.rotate(scaledPoint);
|
|
432
|
+
|
|
433
|
+
// Add local position
|
|
434
|
+
const localResult = rotated.add(this._position);
|
|
435
|
+
|
|
436
|
+
// If has parent, transform through parent
|
|
437
|
+
if (this._parent) {
|
|
438
|
+
return this._parent.localToWorld(localResult);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return localResult;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Transform a point from world to local space
|
|
446
|
+
* @param {Vec4} worldPoint
|
|
447
|
+
* @returns {Vec4}
|
|
448
|
+
*/
|
|
449
|
+
worldToLocal(worldPoint) {
|
|
450
|
+
return this.worldMatrix.inverse().multiplyVec4(worldPoint);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Look at a target position (aligns +Z toward target)
|
|
455
|
+
* Note: In 4D this is more complex - this is a simplified version
|
|
456
|
+
* @param {Vec4} target
|
|
457
|
+
* @returns {this}
|
|
458
|
+
*/
|
|
459
|
+
lookAt(target) {
|
|
460
|
+
const direction = target.sub(this.worldPosition).normalize();
|
|
461
|
+
// Simplified: rotate to align with direction
|
|
462
|
+
// Full 4D lookAt would require specifying an "up" and "ana" vector
|
|
463
|
+
const forward = new Vec4(0, 0, 1, 0);
|
|
464
|
+
const dot = forward.dot(direction);
|
|
465
|
+
if (Math.abs(dot) < 0.9999) {
|
|
466
|
+
const angle = Math.acos(dot);
|
|
467
|
+
// Use XZ and YZ planes for 3D-like rotation
|
|
468
|
+
const rotX = Math.atan2(direction.y, direction.z);
|
|
469
|
+
const rotY = Math.atan2(direction.x, direction.z);
|
|
470
|
+
this._rotation = Rotor4D.fromEuler6(rotX, rotY, 0, 0, 0, 0);
|
|
471
|
+
this._markLocalDirty();
|
|
472
|
+
}
|
|
473
|
+
return this;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// ==================== Internal ====================
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Mark local matrix as dirty
|
|
480
|
+
* @private
|
|
481
|
+
*/
|
|
482
|
+
_markLocalDirty() {
|
|
483
|
+
this._localDirty = true;
|
|
484
|
+
this._markWorldDirty();
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Mark world matrix as dirty (cascades to children)
|
|
489
|
+
* @private
|
|
490
|
+
*/
|
|
491
|
+
_markWorldDirty() {
|
|
492
|
+
this._worldDirty = true;
|
|
493
|
+
for (const child of this._children) {
|
|
494
|
+
child._markWorldDirty();
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Update local transform matrix
|
|
500
|
+
* @private
|
|
501
|
+
*/
|
|
502
|
+
_updateLocalMatrix() {
|
|
503
|
+
// Start with identity
|
|
504
|
+
this._localMatrix = Mat4x4.identity();
|
|
505
|
+
|
|
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
|
+
this._localDirty = false;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Update world transform matrix
|
|
531
|
+
* @private
|
|
532
|
+
*/
|
|
533
|
+
_updateWorldMatrix() {
|
|
534
|
+
if (this._localDirty) {
|
|
535
|
+
this._updateLocalMatrix();
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (this._parent) {
|
|
539
|
+
this._worldMatrix = this._parent.worldMatrix.multiply(this._localMatrix);
|
|
540
|
+
} else {
|
|
541
|
+
this._worldMatrix = this._localMatrix.clone();
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
this._worldDirty = false;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// ==================== Traversal ====================
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Traverse hierarchy depth-first
|
|
551
|
+
* @param {function(Node4D): boolean|void} callback - Return false to stop
|
|
552
|
+
* @returns {boolean} True if completed, false if stopped
|
|
553
|
+
*/
|
|
554
|
+
traverse(callback) {
|
|
555
|
+
if (callback(this) === false) return false;
|
|
556
|
+
for (const child of this._children) {
|
|
557
|
+
if (!child.traverse(callback)) return false;
|
|
558
|
+
}
|
|
559
|
+
return true;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Traverse hierarchy breadth-first
|
|
564
|
+
* @param {function(Node4D): boolean|void} callback
|
|
565
|
+
* @returns {boolean}
|
|
566
|
+
*/
|
|
567
|
+
traverseBreadthFirst(callback) {
|
|
568
|
+
const queue = [this];
|
|
569
|
+
while (queue.length > 0) {
|
|
570
|
+
const node = queue.shift();
|
|
571
|
+
if (callback(node) === false) return false;
|
|
572
|
+
queue.push(...node._children);
|
|
573
|
+
}
|
|
574
|
+
return true;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Traverse only visible nodes
|
|
579
|
+
* @param {function(Node4D): void} callback
|
|
580
|
+
*/
|
|
581
|
+
traverseVisible(callback) {
|
|
582
|
+
if (!this.visible) return;
|
|
583
|
+
callback(this);
|
|
584
|
+
for (const child of this._children) {
|
|
585
|
+
child.traverseVisible(callback);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Get all descendants as flat array
|
|
591
|
+
* @returns {Node4D[]}
|
|
592
|
+
*/
|
|
593
|
+
getDescendants() {
|
|
594
|
+
const result = [];
|
|
595
|
+
this.traverse(node => {
|
|
596
|
+
if (node !== this) result.push(node);
|
|
597
|
+
});
|
|
598
|
+
return result;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// ==================== Lifecycle ====================
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Clone this node (without children)
|
|
605
|
+
* @returns {Node4D}
|
|
606
|
+
*/
|
|
607
|
+
clone() {
|
|
608
|
+
const cloned = new Node4D(this.name + '_clone');
|
|
609
|
+
cloned._position = this._position.clone();
|
|
610
|
+
cloned._rotation = this._rotation.clone();
|
|
611
|
+
cloned._scale = this._scale.clone();
|
|
612
|
+
cloned.visible = this.visible;
|
|
613
|
+
cloned.enabled = this.enabled;
|
|
614
|
+
cloned.tags = [...this.tags];
|
|
615
|
+
cloned.layerMask = this.layerMask;
|
|
616
|
+
return cloned;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Clone this node with all descendants
|
|
621
|
+
* @returns {Node4D}
|
|
622
|
+
*/
|
|
623
|
+
cloneDeep() {
|
|
624
|
+
const cloned = this.clone();
|
|
625
|
+
for (const child of this._children) {
|
|
626
|
+
cloned.addChild(child.cloneDeep());
|
|
627
|
+
}
|
|
628
|
+
return cloned;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Dispose this node and remove from parent
|
|
633
|
+
*/
|
|
634
|
+
dispose() {
|
|
635
|
+
this.removeAllChildren();
|
|
636
|
+
if (this._parent) {
|
|
637
|
+
this._parent.removeChild(this);
|
|
638
|
+
}
|
|
639
|
+
this.userData.clear();
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Dispose this node and all descendants
|
|
644
|
+
*/
|
|
645
|
+
disposeDeep() {
|
|
646
|
+
for (const child of [...this._children]) {
|
|
647
|
+
child.disposeDeep();
|
|
648
|
+
}
|
|
649
|
+
this.dispose();
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// ==================== Serialization ====================
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Serialize node to plain object
|
|
656
|
+
* @returns {object}
|
|
657
|
+
*/
|
|
658
|
+
toJSON() {
|
|
659
|
+
return {
|
|
660
|
+
id: this.id,
|
|
661
|
+
name: this.name,
|
|
662
|
+
position: [this._position.x, this._position.y, this._position.z, this._position.w],
|
|
663
|
+
rotation: this._rotation.toArray(),
|
|
664
|
+
scale: [this._scale.x, this._scale.y, this._scale.z, this._scale.w],
|
|
665
|
+
visible: this.visible,
|
|
666
|
+
enabled: this.enabled,
|
|
667
|
+
tags: this.tags,
|
|
668
|
+
layerMask: this.layerMask,
|
|
669
|
+
children: this._children.map(c => c.toJSON())
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Create node from plain object
|
|
675
|
+
* @param {object} json
|
|
676
|
+
* @returns {Node4D}
|
|
677
|
+
*/
|
|
678
|
+
static fromJSON(json) {
|
|
679
|
+
const node = new Node4D(json.name);
|
|
680
|
+
node._position = new Vec4(...json.position);
|
|
681
|
+
node._rotation = Rotor4D.fromArray(json.rotation);
|
|
682
|
+
node._scale = new Vec4(...json.scale);
|
|
683
|
+
node.visible = json.visible;
|
|
684
|
+
node.enabled = json.enabled;
|
|
685
|
+
node.tags = json.tags || [];
|
|
686
|
+
node.layerMask = json.layerMask || 1;
|
|
687
|
+
node._markLocalDirty();
|
|
688
|
+
|
|
689
|
+
for (const childJson of (json.children || [])) {
|
|
690
|
+
node.addChild(Node4D.fromJSON(childJson));
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
return node;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
export default Node4D;
|