@vib3code/sdk 2.0.1 → 2.0.3-canary.3349130
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 +36 -0
- package/DOCS/AGENT_HARNESS_ARCHITECTURE.md +243 -0
- package/DOCS/CLI_ONBOARDING.md +1 -1
- package/DOCS/CROSS_SITE_DESIGN_PATTERNS.md +117 -0
- package/DOCS/EPIC_SCROLL_EVENTS.md +773 -0
- package/DOCS/HANDOFF_LANDING_PAGE.md +154 -0
- package/DOCS/HANDOFF_SDK_DEVELOPMENT.md +493 -0
- package/DOCS/MULTIVIZ_CHOREOGRAPHY_PATTERNS.md +937 -0
- package/DOCS/PRODUCT_STRATEGY.md +63 -0
- package/DOCS/README.md +103 -0
- package/DOCS/REFERENCE_SCROLL_ANALYSIS.md +97 -0
- package/DOCS/ROADMAP.md +111 -0
- package/DOCS/SCROLL_TIMELINE_v3.md +269 -0
- package/DOCS/SITE_REFACTOR_PLAN.md +100 -0
- package/DOCS/STATUS.md +24 -0
- package/DOCS/SYSTEM_INVENTORY.md +33 -30
- package/DOCS/VISUAL_ANALYSIS_CLICKERSS.md +85 -0
- package/DOCS/VISUAL_ANALYSIS_FACETAD.md +133 -0
- package/DOCS/VISUAL_ANALYSIS_SIMONE.md +95 -0
- package/DOCS/VISUAL_ANALYSIS_TABLESIDE.md +86 -0
- package/DOCS/{BLUEPRINT_EXECUTION_PLAN_2026-01-07.md → archive/BLUEPRINT_EXECUTION_PLAN_2026-01-07.md} +1 -1
- package/DOCS/{DEV_TRACK_ANALYSIS.md → archive/DEV_TRACK_ANALYSIS.md} +3 -0
- package/DOCS/{SYSTEM_AUDIT_2026-01-30.md → archive/SYSTEM_AUDIT_2026-01-30.md} +3 -0
- package/DOCS/{DEV_TRACK_SESSION_2026-01-31.md → dev-tracks/DEV_TRACK_SESSION_2026-01-31.md} +1 -1
- package/DOCS/dev-tracks/DEV_TRACK_SESSION_2026-02-06.md +231 -0
- package/DOCS/dev-tracks/DEV_TRACK_SESSION_2026-02-13.md +127 -0
- package/DOCS/dev-tracks/DEV_TRACK_SESSION_2026-02-15.md +142 -0
- package/DOCS/dev-tracks/README.md +10 -0
- package/README.md +26 -13
- package/cpp/CMakeLists.txt +236 -0
- package/cpp/bindings/embind.cpp +269 -0
- package/cpp/build.sh +129 -0
- package/cpp/geometry/Crystal.cpp +103 -0
- package/cpp/geometry/Fractal.cpp +136 -0
- package/cpp/geometry/GeometryGenerator.cpp +262 -0
- package/cpp/geometry/KleinBottle.cpp +71 -0
- package/cpp/geometry/Sphere.cpp +134 -0
- package/cpp/geometry/Tesseract.cpp +94 -0
- package/cpp/geometry/Tetrahedron.cpp +83 -0
- package/cpp/geometry/Torus.cpp +65 -0
- package/cpp/geometry/WarpFunctions.cpp +238 -0
- package/cpp/geometry/Wave.cpp +85 -0
- package/cpp/include/vib3_ffi.h +238 -0
- package/cpp/math/Mat4x4.cpp +409 -0
- package/cpp/math/Mat4x4.hpp +209 -0
- package/cpp/math/Projection.cpp +142 -0
- package/cpp/math/Projection.hpp +148 -0
- package/cpp/math/Rotor4D.cpp +322 -0
- package/cpp/math/Rotor4D.hpp +204 -0
- package/cpp/math/Vec4.cpp +303 -0
- package/cpp/math/Vec4.hpp +225 -0
- package/cpp/src/vib3_ffi.cpp +607 -0
- package/cpp/tests/Geometry_test.cpp +213 -0
- package/cpp/tests/Mat4x4_test.cpp +494 -0
- package/cpp/tests/Projection_test.cpp +298 -0
- package/cpp/tests/Rotor4D_test.cpp +423 -0
- package/cpp/tests/Vec4_test.cpp +489 -0
- package/package.json +40 -27
- package/src/agent/index.js +1 -3
- package/src/agent/mcp/MCPServer.js +1024 -7
- package/src/agent/mcp/index.js +1 -1
- package/src/agent/mcp/stdio-server.js +264 -0
- package/src/agent/mcp/tools.js +454 -0
- package/src/cli/index.js +374 -44
- package/src/core/CanvasManager.js +97 -204
- package/src/core/ErrorReporter.js +1 -1
- package/src/core/Parameters.js +1 -1
- package/src/core/VIB3Engine.js +93 -4
- package/src/core/VitalitySystem.js +53 -0
- package/src/core/index.js +18 -0
- package/src/core/renderers/FacetedRendererAdapter.js +10 -9
- package/src/core/renderers/HolographicRendererAdapter.js +13 -9
- package/src/core/renderers/QuantumRendererAdapter.js +11 -7
- package/src/creative/AestheticMapper.js +628 -0
- package/src/creative/ChoreographyPlayer.js +481 -0
- package/src/creative/index.js +11 -0
- package/src/export/TradingCardManager.js +3 -4
- package/src/export/index.js +11 -1
- package/src/faceted/FacetedSystem.js +241 -388
- package/src/games/glyph-war/GlyphWarVisualizer.js +641 -0
- package/src/holograms/HolographicVisualizer.js +29 -12
- package/src/holograms/RealHolographicSystem.js +194 -43
- package/src/math/index.js +7 -7
- package/src/polychora/PolychoraSystem.js +77 -0
- package/src/quantum/QuantumEngine.js +103 -66
- package/src/quantum/QuantumVisualizer.js +7 -2
- package/src/reactivity/index.js +3 -5
- package/src/render/LayerPresetManager.js +372 -0
- package/src/render/LayerReactivityBridge.js +344 -0
- package/src/render/LayerRelationshipGraph.js +610 -0
- package/src/render/MultiCanvasBridge.js +148 -25
- package/src/render/UnifiedRenderBridge.js +3 -0
- package/src/render/index.js +27 -2
- package/src/scene/index.js +4 -4
- package/src/shaders/faceted/faceted.frag.glsl +220 -80
- package/src/shaders/faceted/faceted.frag.wgsl +138 -97
- package/src/shaders/holographic/holographic.frag.glsl +28 -9
- package/src/shaders/holographic/holographic.frag.wgsl +107 -38
- package/src/shaders/quantum/quantum.frag.glsl +1 -0
- package/src/shaders/quantum/quantum.frag.wgsl +1 -1
- package/src/testing/ParallelTestFramework.js +2 -2
- package/src/viewer/GalleryUI.js +17 -0
- package/src/viewer/ViewerPortal.js +2 -2
- package/src/viewer/index.js +1 -1
- package/tools/headless-renderer.js +258 -0
- package/tools/shader-sync-verify.js +8 -4
- package/tools/site-analysis/all-reports.json +32 -0
- package/tools/site-analysis/combined-analysis.md +50 -0
- package/tools/site-analyzer.mjs +779 -0
- package/tools/visual-catalog/capture.js +276 -0
- package/tools/visual-catalog/composite.js +138 -0
- package/types/adaptive-sdk.d.ts +204 -5
- package/types/agent/cli.d.ts +78 -0
- package/types/agent/index.d.ts +18 -0
- package/types/agent/mcp.d.ts +87 -0
- package/types/agent/telemetry.d.ts +190 -0
- package/types/core/VIB3Engine.d.ts +26 -0
- package/types/core/index.d.ts +261 -0
- package/types/creative/AestheticMapper.d.ts +72 -0
- package/types/creative/ChoreographyPlayer.d.ts +96 -0
- package/types/creative/index.d.ts +17 -0
- package/types/export/index.d.ts +243 -0
- package/types/geometry/index.d.ts +164 -0
- package/types/math/index.d.ts +214 -0
- package/types/render/LayerPresetManager.d.ts +78 -0
- package/types/render/LayerReactivityBridge.d.ts +85 -0
- package/types/render/LayerRelationshipGraph.d.ts +174 -0
- package/types/render/index.d.ts +3 -0
- package/types/scene/index.d.ts +204 -0
- package/types/systems/index.d.ts +244 -0
- package/types/variations/index.d.ts +62 -0
- package/types/viewer/index.d.ts +225 -0
- /package/DOCS/{DEV_TRACK_PLAN_2026-01-07.md → archive/DEV_TRACK_PLAN_2026-01-07.md} +0 -0
- /package/DOCS/{SESSION_014_PLAN.md → archive/SESSION_014_PLAN.md} +0 -0
- /package/DOCS/{SESSION_LOG_2026-01-07.md → archive/SESSION_LOG_2026-01-07.md} +0 -0
- /package/DOCS/{STRATEGIC_BLUEPRINT_2026-01-07.md → archive/STRATEGIC_BLUEPRINT_2026-01-07.md} +0 -0
- /package/src/viewer/{ReactivityManager.js → ViewerInputHandler.js} +0 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LayerReactivityBridge — Connect audio/tilt/input reactivity to layer relationship modulation
|
|
3
|
+
*
|
|
4
|
+
* Instead of reactivity inputs only affecting the keystone parameters (which all layers see),
|
|
5
|
+
* this bridge lets reactivity inputs *modulate the relationship configurations themselves*.
|
|
6
|
+
*
|
|
7
|
+
* For example:
|
|
8
|
+
* - Audio bass can increase the `gain` on reactive relationships (layers react more to beats)
|
|
9
|
+
* - Device tilt can shift the `hueAngle` on harmonic relationships (tilt changes color harmony)
|
|
10
|
+
* - Click intensity can spike the `lerpRate` on chase relationships (layers snap to attention)
|
|
11
|
+
*
|
|
12
|
+
* This creates a second level of dynamics: the relationships between layers change in real-time
|
|
13
|
+
* based on user input, not just the parameters themselves.
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* const bridge = new LayerReactivityBridge(graph, presetManager);
|
|
17
|
+
* bridge.addModulation('audio', 'bass', 'shadow', 'gain', { scale: 2.0, baseline: 1.5 });
|
|
18
|
+
* bridge.addModulation('tilt', 'gamma', 'highlight', 'hueAngle', { scale: 50, baseline: 137.508 });
|
|
19
|
+
* bridge.update({ audio: { bass: 0.8, mid: 0.3 }, tilt: { gamma: 15 } });
|
|
20
|
+
*
|
|
21
|
+
* Pre-built profiles:
|
|
22
|
+
* bridge.loadModulationProfile('audioStorm');
|
|
23
|
+
* bridge.loadModulationProfile('tiltHarmonic');
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { PRESET_REGISTRY } from './LayerRelationshipGraph.js';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {object} ModulationMapping
|
|
30
|
+
* @property {string} source - Input source category ('audio', 'tilt', 'mouse', 'click', 'custom')
|
|
31
|
+
* @property {string} channel - Input channel within source ('bass', 'mid', 'high', 'alpha', 'beta', 'gamma', 'x', 'y')
|
|
32
|
+
* @property {string} layerName - Target layer to modulate
|
|
33
|
+
* @property {string} configKey - Relationship config key to modulate ('opacity', 'gain', 'hueAngle', 'lerpRate', etc.)
|
|
34
|
+
* @property {number} scale - How much the input affects the config value
|
|
35
|
+
* @property {number} baseline - Default value when input is 0
|
|
36
|
+
* @property {number} [min] - Clamp minimum
|
|
37
|
+
* @property {number} [max] - Clamp maximum
|
|
38
|
+
* @property {string} [mode='add'] - 'add' (baseline + input*scale) or 'multiply' (baseline * (1 + input*scale))
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Pre-built modulation profiles that configure multiple mappings at once.
|
|
43
|
+
*/
|
|
44
|
+
const MODULATION_PROFILES = {
|
|
45
|
+
/** Audio drives reactive gain and chase speed across all layers */
|
|
46
|
+
audioStorm: [
|
|
47
|
+
{ source: 'audio', channel: 'bass', layerName: 'background', configKey: 'gain', scale: 3.0, baseline: 1.5, min: 0.5, max: 8.0, mode: 'add' },
|
|
48
|
+
{ source: 'audio', channel: 'bass', layerName: 'shadow', configKey: 'gain', scale: 4.0, baseline: 2.0, min: 0.5, max: 10.0, mode: 'add' },
|
|
49
|
+
{ source: 'audio', channel: 'mid', layerName: 'highlight', configKey: 'gain', scale: 3.0, baseline: 3.0, min: 1.0, max: 8.0, mode: 'add' },
|
|
50
|
+
{ source: 'audio', channel: 'high', layerName: 'accent', configKey: 'gain', scale: 5.0, baseline: 4.0, min: 1.0, max: 12.0, mode: 'add' },
|
|
51
|
+
{ source: 'audio', channel: 'bass', layerName: 'accent', configKey: 'opacity', scale: 0.4, baseline: 0.3, min: 0.1, max: 1.0, mode: 'add' }
|
|
52
|
+
],
|
|
53
|
+
|
|
54
|
+
/** Tilt shifts harmonic hue angles and density ratios */
|
|
55
|
+
tiltHarmonic: [
|
|
56
|
+
{ source: 'tilt', channel: 'gamma', layerName: 'shadow', configKey: 'hueAngle', scale: 3.0, baseline: 137.508, min: 0, max: 360, mode: 'add' },
|
|
57
|
+
{ source: 'tilt', channel: 'beta', layerName: 'highlight', configKey: 'densityHarmonic', scale: 0.05, baseline: 2.0, min: 0.5, max: 5.0, mode: 'add' },
|
|
58
|
+
{ source: 'tilt', channel: 'gamma', layerName: 'accent', configKey: 'hueAngle', scale: -2.0, baseline: 52.524, min: 0, max: 360, mode: 'add' },
|
|
59
|
+
{ source: 'tilt', channel: 'alpha', layerName: 'background', configKey: 'speedScale', scale: 0.01, baseline: 0.2, min: 0.05, max: 1.0, mode: 'add' }
|
|
60
|
+
],
|
|
61
|
+
|
|
62
|
+
/** Mouse position drives chase lerp rate and echo density */
|
|
63
|
+
mouseChase: [
|
|
64
|
+
{ source: 'mouse', channel: 'x', layerName: 'shadow', configKey: 'lerpRate', scale: 0.2, baseline: 0.05, min: 0.01, max: 0.5, mode: 'add' },
|
|
65
|
+
{ source: 'mouse', channel: 'y', layerName: 'accent', configKey: 'lerpRate', scale: 0.3, baseline: 0.1, min: 0.01, max: 0.8, mode: 'add' },
|
|
66
|
+
{ source: 'mouse', channel: 'x', layerName: 'highlight', configKey: 'densityScale', scale: 1.5, baseline: 1.0, min: 0.2, max: 3.0, mode: 'add' }
|
|
67
|
+
],
|
|
68
|
+
|
|
69
|
+
/** Audio beats pulse layer opacities */
|
|
70
|
+
audioPulse: [
|
|
71
|
+
{ source: 'audio', channel: 'bass', layerName: 'background', configKey: 'opacity', scale: 0.3, baseline: 0.2, min: 0.05, max: 0.8, mode: 'add' },
|
|
72
|
+
{ source: 'audio', channel: 'mid', layerName: 'shadow', configKey: 'opacity', scale: 0.3, baseline: 0.4, min: 0.1, max: 0.9, mode: 'add' },
|
|
73
|
+
{ source: 'audio', channel: 'high', layerName: 'highlight', configKey: 'opacity', scale: 0.3, baseline: 0.6, min: 0.2, max: 1.0, mode: 'add' },
|
|
74
|
+
{ source: 'audio', channel: 'bass', layerName: 'accent', configKey: 'opacity', scale: 0.5, baseline: 0.3, min: 0.1, max: 1.0, mode: 'add' }
|
|
75
|
+
],
|
|
76
|
+
|
|
77
|
+
/** Combined: audio drives gains, tilt drives hue shifts */
|
|
78
|
+
fullReactive: [
|
|
79
|
+
// Audio → gain/opacity
|
|
80
|
+
{ source: 'audio', channel: 'bass', layerName: 'shadow', configKey: 'gain', scale: 3.0, baseline: 2.0, min: 0.5, max: 8.0, mode: 'add' },
|
|
81
|
+
{ source: 'audio', channel: 'mid', layerName: 'highlight', configKey: 'gain', scale: 2.5, baseline: 3.0, min: 1.0, max: 8.0, mode: 'add' },
|
|
82
|
+
{ source: 'audio', channel: 'high', layerName: 'accent', configKey: 'gain', scale: 4.0, baseline: 4.0, min: 1.0, max: 12.0, mode: 'add' },
|
|
83
|
+
{ source: 'audio', channel: 'bass', layerName: 'accent', configKey: 'opacity', scale: 0.4, baseline: 0.35, min: 0.1, max: 1.0, mode: 'add' },
|
|
84
|
+
// Tilt → hue/speed
|
|
85
|
+
{ source: 'tilt', channel: 'gamma', layerName: 'shadow', configKey: 'hueAngle', scale: 2.0, baseline: 137.508, min: 0, max: 360, mode: 'add' },
|
|
86
|
+
{ source: 'tilt', channel: 'beta', layerName: 'highlight', configKey: 'speedRatio', scale: 0.02, baseline: 0.5, min: 0.1, max: 2.0, mode: 'add' },
|
|
87
|
+
// Mouse → chase
|
|
88
|
+
{ source: 'mouse', channel: 'x', layerName: 'accent', configKey: 'lerpRate', scale: 0.2, baseline: 0.1, min: 0.01, max: 0.5, mode: 'add' }
|
|
89
|
+
]
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export class LayerReactivityBridge {
|
|
93
|
+
/**
|
|
94
|
+
* @param {import('./LayerRelationshipGraph.js').LayerRelationshipGraph} graph - Live graph
|
|
95
|
+
* @param {import('./LayerPresetManager.js').LayerPresetManager} [presetManager] - Optional preset manager for save/restore
|
|
96
|
+
*/
|
|
97
|
+
constructor(graph, presetManager = null) {
|
|
98
|
+
/** @type {import('./LayerRelationshipGraph.js').LayerRelationshipGraph} */
|
|
99
|
+
this._graph = graph;
|
|
100
|
+
|
|
101
|
+
/** @type {import('./LayerPresetManager.js').LayerPresetManager|null} */
|
|
102
|
+
this._presetManager = presetManager;
|
|
103
|
+
|
|
104
|
+
/** @type {ModulationMapping[]} Active modulation mappings */
|
|
105
|
+
this._mappings = [];
|
|
106
|
+
|
|
107
|
+
/** @type {string|null} Active modulation profile name */
|
|
108
|
+
this._activeProfile = null;
|
|
109
|
+
|
|
110
|
+
/** @type {boolean} Whether modulation is active */
|
|
111
|
+
this._active = false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ========================================================================
|
|
115
|
+
// Modulation Mappings
|
|
116
|
+
// ========================================================================
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Add a single modulation mapping.
|
|
120
|
+
*
|
|
121
|
+
* @param {string} source - Input source ('audio', 'tilt', 'mouse', 'click', 'custom')
|
|
122
|
+
* @param {string} channel - Channel within source ('bass', 'mid', 'high', 'gamma', 'x', etc.)
|
|
123
|
+
* @param {string} layerName - Target layer
|
|
124
|
+
* @param {string} configKey - Relationship config key to modulate
|
|
125
|
+
* @param {object} [options]
|
|
126
|
+
* @param {number} [options.scale=1.0]
|
|
127
|
+
* @param {number} [options.baseline=0]
|
|
128
|
+
* @param {number} [options.min] - Clamp minimum
|
|
129
|
+
* @param {number} [options.max] - Clamp maximum
|
|
130
|
+
* @param {string} [options.mode='add'] - 'add' or 'multiply'
|
|
131
|
+
*/
|
|
132
|
+
addModulation(source, channel, layerName, configKey, options = {}) {
|
|
133
|
+
this._mappings.push({
|
|
134
|
+
source,
|
|
135
|
+
channel,
|
|
136
|
+
layerName,
|
|
137
|
+
configKey,
|
|
138
|
+
scale: options.scale ?? 1.0,
|
|
139
|
+
baseline: options.baseline ?? 0,
|
|
140
|
+
min: options.min,
|
|
141
|
+
max: options.max,
|
|
142
|
+
mode: options.mode || 'add'
|
|
143
|
+
});
|
|
144
|
+
this._activeProfile = null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Remove all modulation mappings for a specific layer.
|
|
149
|
+
*
|
|
150
|
+
* @param {string} layerName
|
|
151
|
+
* @returns {number} Number of mappings removed
|
|
152
|
+
*/
|
|
153
|
+
removeModulationsForLayer(layerName) {
|
|
154
|
+
const before = this._mappings.length;
|
|
155
|
+
this._mappings = this._mappings.filter(m => m.layerName !== layerName);
|
|
156
|
+
return before - this._mappings.length;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Remove all modulation mappings.
|
|
161
|
+
*/
|
|
162
|
+
clearModulations() {
|
|
163
|
+
this._mappings = [];
|
|
164
|
+
this._activeProfile = null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Load a pre-built modulation profile.
|
|
169
|
+
*
|
|
170
|
+
* @param {string} profileName - One of: audioStorm, tiltHarmonic, mouseChase, audioPulse, fullReactive
|
|
171
|
+
* @returns {boolean} True if loaded
|
|
172
|
+
*/
|
|
173
|
+
loadModulationProfile(profileName) {
|
|
174
|
+
const profile = MODULATION_PROFILES[profileName];
|
|
175
|
+
if (!profile) return false;
|
|
176
|
+
|
|
177
|
+
this._mappings = profile.map(m => ({ ...m }));
|
|
178
|
+
this._activeProfile = profileName;
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get available modulation profile names.
|
|
184
|
+
*
|
|
185
|
+
* @returns {string[]}
|
|
186
|
+
*/
|
|
187
|
+
static get profileNames() {
|
|
188
|
+
return Object.keys(MODULATION_PROFILES);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get the active modulation profile name.
|
|
193
|
+
*
|
|
194
|
+
* @returns {string|null}
|
|
195
|
+
*/
|
|
196
|
+
get activeProfile() {
|
|
197
|
+
return this._activeProfile;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Get all current modulation mappings.
|
|
202
|
+
*
|
|
203
|
+
* @returns {ModulationMapping[]}
|
|
204
|
+
*/
|
|
205
|
+
get mappings() {
|
|
206
|
+
return [...this._mappings];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ========================================================================
|
|
210
|
+
// Activation
|
|
211
|
+
// ========================================================================
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Enable modulation processing.
|
|
215
|
+
*/
|
|
216
|
+
activate() {
|
|
217
|
+
this._active = true;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Disable modulation processing.
|
|
222
|
+
*/
|
|
223
|
+
deactivate() {
|
|
224
|
+
this._active = false;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Check if modulation is active.
|
|
229
|
+
*
|
|
230
|
+
* @returns {boolean}
|
|
231
|
+
*/
|
|
232
|
+
get isActive() {
|
|
233
|
+
return this._active;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ========================================================================
|
|
237
|
+
// Update (called each frame or on input change)
|
|
238
|
+
// ========================================================================
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Process input state and apply modulations to the layer relationship graph.
|
|
242
|
+
*
|
|
243
|
+
* Call this from the render loop or when input state changes.
|
|
244
|
+
*
|
|
245
|
+
* @param {object} inputState - Current input values
|
|
246
|
+
* @param {object} [inputState.audio] - { bass, mid, high, energy }
|
|
247
|
+
* @param {object} [inputState.tilt] - { alpha, beta, gamma }
|
|
248
|
+
* @param {object} [inputState.mouse] - { x, y, velocityX, velocityY }
|
|
249
|
+
* @param {object} [inputState.click] - { intensity }
|
|
250
|
+
* @param {object} [inputState.custom] - Custom named channels
|
|
251
|
+
* @returns {object} Map of layerName → applied config overrides
|
|
252
|
+
*/
|
|
253
|
+
update(inputState) {
|
|
254
|
+
if (!this._active || this._mappings.length === 0) return {};
|
|
255
|
+
|
|
256
|
+
// Group mappings by layer to batch-apply
|
|
257
|
+
const overridesByLayer = {};
|
|
258
|
+
|
|
259
|
+
for (const mapping of this._mappings) {
|
|
260
|
+
const sourceData = inputState[mapping.source];
|
|
261
|
+
if (!sourceData) continue;
|
|
262
|
+
|
|
263
|
+
const inputValue = sourceData[mapping.channel];
|
|
264
|
+
if (inputValue === undefined || inputValue === null) continue;
|
|
265
|
+
|
|
266
|
+
// Compute modulated value
|
|
267
|
+
let value;
|
|
268
|
+
if (mapping.mode === 'multiply') {
|
|
269
|
+
value = mapping.baseline * (1 + inputValue * mapping.scale);
|
|
270
|
+
} else {
|
|
271
|
+
value = mapping.baseline + inputValue * mapping.scale;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Clamp
|
|
275
|
+
if (mapping.min !== undefined) value = Math.max(mapping.min, value);
|
|
276
|
+
if (mapping.max !== undefined) value = Math.min(mapping.max, value);
|
|
277
|
+
|
|
278
|
+
// Accumulate overrides per layer
|
|
279
|
+
if (!overridesByLayer[mapping.layerName]) {
|
|
280
|
+
overridesByLayer[mapping.layerName] = {};
|
|
281
|
+
}
|
|
282
|
+
overridesByLayer[mapping.layerName][mapping.configKey] = value;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Apply overrides to the graph via tune
|
|
286
|
+
for (const [layerName, overrides] of Object.entries(overridesByLayer)) {
|
|
287
|
+
const graphConfig = this._graph.exportConfig();
|
|
288
|
+
const currentRel = graphConfig.relationships[layerName];
|
|
289
|
+
|
|
290
|
+
if (currentRel && currentRel.preset) {
|
|
291
|
+
const factory = PRESET_REGISTRY[currentRel.preset];
|
|
292
|
+
if (factory) {
|
|
293
|
+
const newConfig = { ...(currentRel.config || {}), ...overrides };
|
|
294
|
+
this._graph.setRelationship(layerName, {
|
|
295
|
+
preset: currentRel.preset,
|
|
296
|
+
config: newConfig
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return overridesByLayer;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ========================================================================
|
|
306
|
+
// Serialization
|
|
307
|
+
// ========================================================================
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Export the current modulation configuration.
|
|
311
|
+
*
|
|
312
|
+
* @returns {object}
|
|
313
|
+
*/
|
|
314
|
+
exportConfig() {
|
|
315
|
+
return {
|
|
316
|
+
profile: this._activeProfile,
|
|
317
|
+
mappings: this._mappings.map(m => ({ ...m })),
|
|
318
|
+
active: this._active
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Import a modulation configuration.
|
|
324
|
+
*
|
|
325
|
+
* @param {object} config
|
|
326
|
+
*/
|
|
327
|
+
importConfig(config) {
|
|
328
|
+
if (!config) return;
|
|
329
|
+
|
|
330
|
+
if (config.profile && MODULATION_PROFILES[config.profile]) {
|
|
331
|
+
this.loadModulationProfile(config.profile);
|
|
332
|
+
} else if (Array.isArray(config.mappings)) {
|
|
333
|
+
this._mappings = config.mappings.map(m => ({ ...m }));
|
|
334
|
+
this._activeProfile = null;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (config.active !== undefined) {
|
|
338
|
+
this._active = config.active;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export { MODULATION_PROFILES };
|
|
344
|
+
export default LayerReactivityBridge;
|