@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,777 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VIB3+ AI Preset Generator
|
|
3
|
+
* Uses rule-based NLP keyword extraction and optional MCP/LLM integration
|
|
4
|
+
* to generate creative parameter presets from natural-language descriptions.
|
|
5
|
+
*
|
|
6
|
+
* The generator works in three tiers:
|
|
7
|
+
* 1. Built-in keyword-to-parameter vocabulary (no external API needed)
|
|
8
|
+
* 2. Algorithmic random generation with optional theme constraints
|
|
9
|
+
* 3. MCP protocol integration for full LLM-powered generation
|
|
10
|
+
*
|
|
11
|
+
* Also provides preset mutation and crossbreeding for evolutionary exploration.
|
|
12
|
+
*
|
|
13
|
+
* @module advanced/AIPresetGenerator
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A VIB3 preset -- a complete set of visualization parameters.
|
|
18
|
+
* @typedef {Object} VIB3Preset
|
|
19
|
+
* @property {string} name - Human-readable name
|
|
20
|
+
* @property {string} description - How this preset was generated
|
|
21
|
+
* @property {number} geometry - Geometry index (0-23)
|
|
22
|
+
* @property {number} rot4dXY - XY rotation (0 - 2*PI)
|
|
23
|
+
* @property {number} rot4dXZ - XZ rotation
|
|
24
|
+
* @property {number} rot4dYZ - YZ rotation
|
|
25
|
+
* @property {number} rot4dXW - XW rotation
|
|
26
|
+
* @property {number} rot4dYW - YW rotation
|
|
27
|
+
* @property {number} rot4dZW - ZW rotation
|
|
28
|
+
* @property {number} gridDensity - Grid density (4-100)
|
|
29
|
+
* @property {number} morphFactor - Morph factor (0-2)
|
|
30
|
+
* @property {number} chaos - Chaos level (0-1)
|
|
31
|
+
* @property {number} speed - Animation speed (0.1-3)
|
|
32
|
+
* @property {number} hue - Color hue (0-360)
|
|
33
|
+
* @property {number} intensity - Brightness (0-1)
|
|
34
|
+
* @property {number} saturation - Color saturation (0-1)
|
|
35
|
+
* @property {number} dimension - Projection distance (3.0-4.5)
|
|
36
|
+
* @property {string} [system] - Optional target system (quantum|faceted|holographic)
|
|
37
|
+
* @property {number} timestamp - Creation timestamp (ms)
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/** Parameter ranges used by the generator. */
|
|
41
|
+
const PARAM_RANGES = {
|
|
42
|
+
geometry: { min: 0, max: 23, step: 1, type: 'int' },
|
|
43
|
+
rot4dXY: { min: 0, max: Math.PI * 2, step: null, type: 'float' },
|
|
44
|
+
rot4dXZ: { min: 0, max: Math.PI * 2, step: null, type: 'float' },
|
|
45
|
+
rot4dYZ: { min: 0, max: Math.PI * 2, step: null, type: 'float' },
|
|
46
|
+
rot4dXW: { min: 0, max: Math.PI * 2, step: null, type: 'float' },
|
|
47
|
+
rot4dYW: { min: 0, max: Math.PI * 2, step: null, type: 'float' },
|
|
48
|
+
rot4dZW: { min: 0, max: Math.PI * 2, step: null, type: 'float' },
|
|
49
|
+
gridDensity: { min: 4, max: 100, step: 1, type: 'float' },
|
|
50
|
+
morphFactor: { min: 0, max: 2, step: null, type: 'float' },
|
|
51
|
+
chaos: { min: 0, max: 1, step: null, type: 'float' },
|
|
52
|
+
speed: { min: 0.1, max: 3, step: null, type: 'float' },
|
|
53
|
+
hue: { min: 0, max: 360, step: null, type: 'float' },
|
|
54
|
+
intensity: { min: 0, max: 1, step: null, type: 'float' },
|
|
55
|
+
saturation: { min: 0, max: 1, step: null, type: 'float' },
|
|
56
|
+
dimension: { min: 3.0, max: 4.5, step: null, type: 'float' }
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Style vocabulary: maps descriptive keywords to partial parameter overrides.
|
|
61
|
+
* Values are expressed as fractions of the parameter range (0-1) unless
|
|
62
|
+
* marked as absolute.
|
|
63
|
+
*/
|
|
64
|
+
const STYLE_VOCABULARY = {
|
|
65
|
+
// Energy / Motion
|
|
66
|
+
energetic: { speed: [0.7, 1.0], chaos: [0.4, 0.7], intensity: [0.7, 1.0], hue: [0, 60] },
|
|
67
|
+
calm: { speed: [0.05, 0.25], chaos: [0.0, 0.15], intensity: [0.3, 0.6], hue: [180, 240] },
|
|
68
|
+
frantic: { speed: [0.85, 1.0], chaos: [0.7, 1.0], morphFactor: [0.6, 1.0], gridDensity: [0.5, 0.8] },
|
|
69
|
+
slow: { speed: [0.0, 0.15], chaos: [0.0, 0.1] },
|
|
70
|
+
fast: { speed: [0.7, 1.0], chaos: [0.2, 0.5] },
|
|
71
|
+
gentle: { speed: [0.05, 0.2], chaos: [0.0, 0.1], intensity: [0.2, 0.5] },
|
|
72
|
+
aggressive: { speed: [0.8, 1.0], chaos: [0.6, 1.0], intensity: [0.8, 1.0] },
|
|
73
|
+
pulsing: { speed: [0.4, 0.6], morphFactor: [0.5, 1.0], chaos: [0.2, 0.4] },
|
|
74
|
+
flowing: { speed: [0.2, 0.45], chaos: [0.1, 0.3], morphFactor: [0.3, 0.7] },
|
|
75
|
+
chaotic: { chaos: [0.7, 1.0], speed: [0.5, 0.8] },
|
|
76
|
+
orderly: { chaos: [0.0, 0.1], gridDensity: [0.4, 0.7] },
|
|
77
|
+
vibrating: { speed: [0.6, 0.8], morphFactor: [0.4, 0.8], chaos: [0.3, 0.5] },
|
|
78
|
+
|
|
79
|
+
// Color / Light
|
|
80
|
+
dark: { intensity: [0.05, 0.25], saturation: [0.2, 0.5], hue: [240, 300] },
|
|
81
|
+
bright: { intensity: [0.7, 1.0], saturation: [0.6, 1.0] },
|
|
82
|
+
neon: { intensity: [0.8, 1.0], saturation: [0.9, 1.0], hue: [280, 340] },
|
|
83
|
+
pastel: { intensity: [0.5, 0.7], saturation: [0.2, 0.4] },
|
|
84
|
+
monochrome: { saturation: [0.0, 0.05], intensity: [0.4, 0.8] },
|
|
85
|
+
warm: { hue: [0, 60], saturation: [0.5, 0.8], intensity: [0.5, 0.8] },
|
|
86
|
+
cool: { hue: [180, 270], saturation: [0.4, 0.7], intensity: [0.4, 0.7] },
|
|
87
|
+
fiery: { hue: [0, 30], saturation: [0.8, 1.0], intensity: [0.7, 1.0], speed: [0.5, 0.8] },
|
|
88
|
+
icy: { hue: [190, 220], saturation: [0.3, 0.6], intensity: [0.5, 0.8], speed: [0.1, 0.3] },
|
|
89
|
+
golden: { hue: [40, 55], saturation: [0.7, 0.9], intensity: [0.6, 0.85] },
|
|
90
|
+
sunset: { hue: [10, 45], saturation: [0.6, 0.9], intensity: [0.5, 0.8] },
|
|
91
|
+
ocean: { hue: [180, 220], saturation: [0.5, 0.8], speed: [0.2, 0.4] },
|
|
92
|
+
forest: { hue: [100, 150], saturation: [0.4, 0.7], intensity: [0.3, 0.6] },
|
|
93
|
+
electric: { intensity: [0.8, 1.0], saturation: [0.8, 1.0], hue: [220, 280] },
|
|
94
|
+
muted: { saturation: [0.1, 0.3], intensity: [0.3, 0.5] },
|
|
95
|
+
vivid: { saturation: [0.8, 1.0], intensity: [0.7, 1.0] },
|
|
96
|
+
purple: { hue: [270, 310], saturation: [0.6, 0.9] },
|
|
97
|
+
red: { hue: [350, 370], saturation: [0.7, 1.0] },
|
|
98
|
+
blue: { hue: [200, 250], saturation: [0.5, 0.8] },
|
|
99
|
+
green: { hue: [100, 150], saturation: [0.5, 0.8] },
|
|
100
|
+
pink: { hue: [320, 350], saturation: [0.5, 0.8], intensity: [0.6, 0.9] },
|
|
101
|
+
|
|
102
|
+
// Mood / Theme
|
|
103
|
+
psychedelic: { chaos: [0.5, 0.9], saturation: [0.8, 1.0], speed: [0.4, 0.7], morphFactor: [0.6, 1.0] },
|
|
104
|
+
zen: { chaos: [0.0, 0.05], speed: [0.05, 0.15], intensity: [0.3, 0.5], saturation: [0.2, 0.4] },
|
|
105
|
+
dreamy: { chaos: [0.1, 0.3], speed: [0.1, 0.3], intensity: [0.4, 0.6], morphFactor: [0.3, 0.6] },
|
|
106
|
+
nightmare: { chaos: [0.6, 0.9], intensity: [0.1, 0.3], hue: [280, 340], speed: [0.3, 0.6] },
|
|
107
|
+
ethereal: { intensity: [0.3, 0.5], saturation: [0.3, 0.6], chaos: [0.1, 0.25], speed: [0.1, 0.25] },
|
|
108
|
+
cosmic: { dimension: [0.6, 1.0], chaos: [0.3, 0.6], hue: [240, 300], intensity: [0.5, 0.8] },
|
|
109
|
+
organic: { morphFactor: [0.4, 0.8], chaos: [0.2, 0.4], speed: [0.2, 0.4] },
|
|
110
|
+
mechanical: { chaos: [0.0, 0.1], gridDensity: [0.6, 0.9], morphFactor: [0.0, 0.2] },
|
|
111
|
+
abstract: { chaos: [0.3, 0.6], morphFactor: [0.4, 0.8], dimension: [0.3, 0.7] },
|
|
112
|
+
minimal: { gridDensity: [0.0, 0.2], chaos: [0.0, 0.1], intensity: [0.3, 0.5] },
|
|
113
|
+
complex: { gridDensity: [0.7, 1.0], chaos: [0.3, 0.6], morphFactor: [0.5, 0.8] },
|
|
114
|
+
retro: { saturation: [0.4, 0.6], hue: [20, 60], gridDensity: [0.3, 0.5] },
|
|
115
|
+
futuristic: { saturation: [0.6, 0.9], hue: [180, 260], gridDensity: [0.5, 0.8] },
|
|
116
|
+
alien: { hue: [100, 160], chaos: [0.4, 0.7], morphFactor: [0.5, 0.9], dimension: [0.5, 0.9] },
|
|
117
|
+
underwater: { hue: [170, 210], speed: [0.15, 0.35], chaos: [0.15, 0.35] },
|
|
118
|
+
space: { hue: [230, 280], intensity: [0.3, 0.6], dimension: [0.7, 1.0] },
|
|
119
|
+
hypnotic: { speed: [0.3, 0.5], morphFactor: [0.5, 0.8], chaos: [0.15, 0.3] },
|
|
120
|
+
glitch: { chaos: [0.7, 1.0], speed: [0.6, 0.9], gridDensity: [0.5, 0.8] },
|
|
121
|
+
serene: { speed: [0.05, 0.2], chaos: [0.0, 0.1], intensity: [0.3, 0.5], hue: [160, 220] },
|
|
122
|
+
|
|
123
|
+
// Geometry hints
|
|
124
|
+
crystal: { _geometryBase: 7, gridDensity: [0.4, 0.7], chaos: [0.0, 0.15] },
|
|
125
|
+
fractal: { _geometryBase: 5, chaos: [0.3, 0.6], gridDensity: [0.5, 0.8] },
|
|
126
|
+
toroidal: { _geometryBase: 3, morphFactor: [0.3, 0.6] },
|
|
127
|
+
torus: { _geometryBase: 3 },
|
|
128
|
+
cube: { _geometryBase: 1 },
|
|
129
|
+
tesseract: { _geometryBase: 1, dimension: [0.5, 0.8] },
|
|
130
|
+
sphere: { _geometryBase: 2 },
|
|
131
|
+
wave: { _geometryBase: 6, speed: [0.3, 0.6] },
|
|
132
|
+
klein: { _geometryBase: 4, dimension: [0.4, 0.7] },
|
|
133
|
+
simplex: { _geometryBase: 0 },
|
|
134
|
+
tetrahedron: { _geometryBase: 0 },
|
|
135
|
+
|
|
136
|
+
// Core type hints
|
|
137
|
+
hypersphere: { _coreType: 1 },
|
|
138
|
+
hypertetra: { _coreType: 2 },
|
|
139
|
+
base: { _coreType: 0 },
|
|
140
|
+
|
|
141
|
+
// System hints
|
|
142
|
+
quantum: { _system: 'quantum' },
|
|
143
|
+
faceted: { _system: 'faceted' },
|
|
144
|
+
holographic: { _system: 'holographic' },
|
|
145
|
+
hologram: { _system: 'holographic' }
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Named theme presets for the random generator.
|
|
150
|
+
*/
|
|
151
|
+
const THEMES = {
|
|
152
|
+
deepSpace: {
|
|
153
|
+
hue: [220, 280], intensity: [0.2, 0.5], saturation: [0.4, 0.7],
|
|
154
|
+
speed: [0.1, 0.3], chaos: [0.1, 0.3], dimension: [0.6, 1.0],
|
|
155
|
+
gridDensity: [0.3, 0.6]
|
|
156
|
+
},
|
|
157
|
+
lavaLamp: {
|
|
158
|
+
hue: [0, 40], intensity: [0.6, 0.9], saturation: [0.7, 1.0],
|
|
159
|
+
speed: [0.15, 0.35], chaos: [0.2, 0.4], morphFactor: [0.5, 0.9]
|
|
160
|
+
},
|
|
161
|
+
arctic: {
|
|
162
|
+
hue: [190, 220], intensity: [0.5, 0.8], saturation: [0.2, 0.5],
|
|
163
|
+
speed: [0.05, 0.2], chaos: [0.0, 0.1], gridDensity: [0.2, 0.4]
|
|
164
|
+
},
|
|
165
|
+
acid: {
|
|
166
|
+
hue: [80, 160], intensity: [0.7, 1.0], saturation: [0.8, 1.0],
|
|
167
|
+
speed: [0.5, 0.9], chaos: [0.5, 0.9], morphFactor: [0.6, 1.0]
|
|
168
|
+
},
|
|
169
|
+
meditation: {
|
|
170
|
+
hue: [240, 280], intensity: [0.2, 0.4], saturation: [0.3, 0.5],
|
|
171
|
+
speed: [0.03, 0.12], chaos: [0.0, 0.05], morphFactor: [0.1, 0.3]
|
|
172
|
+
},
|
|
173
|
+
storm: {
|
|
174
|
+
hue: [200, 260], intensity: [0.3, 0.7], saturation: [0.4, 0.7],
|
|
175
|
+
speed: [0.6, 1.0], chaos: [0.5, 0.8], gridDensity: [0.5, 0.8]
|
|
176
|
+
},
|
|
177
|
+
synthwave: {
|
|
178
|
+
hue: [280, 340], intensity: [0.6, 0.9], saturation: [0.7, 1.0],
|
|
179
|
+
speed: [0.3, 0.5], chaos: [0.1, 0.3], gridDensity: [0.4, 0.7]
|
|
180
|
+
},
|
|
181
|
+
nature: {
|
|
182
|
+
hue: [80, 150], intensity: [0.4, 0.7], saturation: [0.4, 0.7],
|
|
183
|
+
speed: [0.1, 0.3], chaos: [0.1, 0.3], morphFactor: [0.2, 0.5]
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
export class AIPresetGenerator {
|
|
188
|
+
/**
|
|
189
|
+
* @param {Object} engine - VIB3Engine instance (optional, used for reading current state)
|
|
190
|
+
*/
|
|
191
|
+
constructor(engine) {
|
|
192
|
+
/** @type {Object|null} */
|
|
193
|
+
this.engine = engine;
|
|
194
|
+
|
|
195
|
+
/** @type {string|null} MCP server endpoint URL */
|
|
196
|
+
this.mcpEndpoint = null;
|
|
197
|
+
|
|
198
|
+
/** @type {VIB3Preset[]} History of generated presets */
|
|
199
|
+
this.presetHistory = [];
|
|
200
|
+
|
|
201
|
+
/** @type {number} Maximum history entries */
|
|
202
|
+
this.maxHistory = 100;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// -----------------------------------------------------------------------
|
|
206
|
+
// Text Description -> Preset
|
|
207
|
+
// -----------------------------------------------------------------------
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Generate a VIB3 preset from a natural-language text description.
|
|
211
|
+
* Uses the built-in style vocabulary to extract keywords and map them
|
|
212
|
+
* to parameter ranges. No external API required.
|
|
213
|
+
*
|
|
214
|
+
* @param {string} description - e.g. "calm ocean with gentle waves"
|
|
215
|
+
* @returns {Promise<VIB3Preset>}
|
|
216
|
+
*/
|
|
217
|
+
async generateFromDescription(description) {
|
|
218
|
+
if (!description || typeof description !== 'string') {
|
|
219
|
+
throw new Error('Description must be a non-empty string.');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const normalized = description.toLowerCase().trim();
|
|
223
|
+
const tokens = this._tokenize(normalized);
|
|
224
|
+
const matchedStyles = this._matchKeywords(tokens);
|
|
225
|
+
|
|
226
|
+
// Build parameter constraints from matched styles
|
|
227
|
+
const constraints = {};
|
|
228
|
+
let geometryBase = null;
|
|
229
|
+
let coreType = null;
|
|
230
|
+
let system = null;
|
|
231
|
+
|
|
232
|
+
for (const style of matchedStyles) {
|
|
233
|
+
const mapping = STYLE_VOCABULARY[style];
|
|
234
|
+
if (!mapping) continue;
|
|
235
|
+
|
|
236
|
+
// Handle special keys
|
|
237
|
+
if (mapping._geometryBase !== undefined && geometryBase === null) {
|
|
238
|
+
geometryBase = mapping._geometryBase;
|
|
239
|
+
}
|
|
240
|
+
if (mapping._coreType !== undefined && coreType === null) {
|
|
241
|
+
coreType = mapping._coreType;
|
|
242
|
+
}
|
|
243
|
+
if (mapping._system !== undefined && system === null) {
|
|
244
|
+
system = mapping._system;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Merge parameter constraints (later matches override earlier)
|
|
248
|
+
for (const [param, range] of Object.entries(mapping)) {
|
|
249
|
+
if (param.startsWith('_')) continue;
|
|
250
|
+
constraints[param] = range;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Generate parameter values from constraints
|
|
255
|
+
const preset = this._generateFromConstraints(constraints, geometryBase, coreType);
|
|
256
|
+
|
|
257
|
+
preset.name = this._generatePresetName(matchedStyles, description);
|
|
258
|
+
preset.description = `Generated from: "${description}"`;
|
|
259
|
+
if (system) {
|
|
260
|
+
preset.system = system;
|
|
261
|
+
}
|
|
262
|
+
preset.timestamp = Date.now();
|
|
263
|
+
|
|
264
|
+
// Store in history
|
|
265
|
+
this._addToHistory(preset);
|
|
266
|
+
|
|
267
|
+
return preset;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Get the comprehensive style vocabulary mapping.
|
|
272
|
+
* @returns {Object} Keyword-to-parameter mapping dictionary
|
|
273
|
+
*/
|
|
274
|
+
getStyleMapping() {
|
|
275
|
+
return { ...STYLE_VOCABULARY };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Get available theme names for random generation.
|
|
280
|
+
* @returns {string[]}
|
|
281
|
+
*/
|
|
282
|
+
getAvailableThemes() {
|
|
283
|
+
return Object.keys(THEMES);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// -----------------------------------------------------------------------
|
|
287
|
+
// MCP / LLM Integration
|
|
288
|
+
// -----------------------------------------------------------------------
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Set the MCP endpoint for LLM-based generation.
|
|
292
|
+
* @param {string} endpoint - URL of the MCP server
|
|
293
|
+
*/
|
|
294
|
+
setMCPEndpoint(endpoint) {
|
|
295
|
+
this.mcpEndpoint = endpoint;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Generate a preset via MCP protocol communicating with an LLM.
|
|
300
|
+
* Sends the current visualization state plus the user prompt,
|
|
301
|
+
* and expects a structured parameter response.
|
|
302
|
+
*
|
|
303
|
+
* @param {string} prompt - User's creative prompt
|
|
304
|
+
* @returns {Promise<VIB3Preset>}
|
|
305
|
+
* @throws {Error} If MCP endpoint is not configured or request fails
|
|
306
|
+
*/
|
|
307
|
+
async generateViaMCP(prompt) {
|
|
308
|
+
if (!this.mcpEndpoint) {
|
|
309
|
+
throw new Error('MCP endpoint not configured. Call setMCPEndpoint() first.');
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Gather current state
|
|
313
|
+
const currentState = this._getCurrentState();
|
|
314
|
+
|
|
315
|
+
const requestBody = {
|
|
316
|
+
jsonrpc: '2.0',
|
|
317
|
+
id: Date.now(),
|
|
318
|
+
method: 'tools/call',
|
|
319
|
+
params: {
|
|
320
|
+
name: 'generate_preset',
|
|
321
|
+
arguments: {
|
|
322
|
+
prompt,
|
|
323
|
+
currentState,
|
|
324
|
+
parameterRanges: PARAM_RANGES,
|
|
325
|
+
availableStyles: Object.keys(STYLE_VOCABULARY)
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const response = await fetch(this.mcpEndpoint, {
|
|
331
|
+
method: 'POST',
|
|
332
|
+
headers: { 'Content-Type': 'application/json' },
|
|
333
|
+
body: JSON.stringify(requestBody)
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
if (!response.ok) {
|
|
337
|
+
throw new Error(`MCP request failed: ${response.status} ${response.statusText}`);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const result = await response.json();
|
|
341
|
+
|
|
342
|
+
if (result.error) {
|
|
343
|
+
throw new Error(`MCP error: ${result.error.message || JSON.stringify(result.error)}`);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Parse LLM response into a preset
|
|
347
|
+
const llmOutput = result.result;
|
|
348
|
+
const preset = this._parseLLMResponse(llmOutput);
|
|
349
|
+
|
|
350
|
+
preset.name = preset.name || `LLM: ${prompt.substring(0, 30)}`;
|
|
351
|
+
preset.description = `LLM generated from: "${prompt}"`;
|
|
352
|
+
preset.timestamp = Date.now();
|
|
353
|
+
|
|
354
|
+
this._addToHistory(preset);
|
|
355
|
+
|
|
356
|
+
return preset;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// -----------------------------------------------------------------------
|
|
360
|
+
// Random Generation
|
|
361
|
+
// -----------------------------------------------------------------------
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Generate a random creative preset with optional theme constraint.
|
|
365
|
+
*
|
|
366
|
+
* @param {string|null} [theme=null] - Theme name (e.g. 'deepSpace', 'lavaLamp')
|
|
367
|
+
* or null for fully random.
|
|
368
|
+
* @returns {VIB3Preset}
|
|
369
|
+
*/
|
|
370
|
+
generateRandom(theme = null) {
|
|
371
|
+
let constraints = {};
|
|
372
|
+
|
|
373
|
+
if (theme && THEMES[theme]) {
|
|
374
|
+
constraints = { ...THEMES[theme] };
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Random geometry
|
|
378
|
+
const geometryBase = Math.floor(Math.random() * 8);
|
|
379
|
+
const coreType = Math.floor(Math.random() * 3);
|
|
380
|
+
|
|
381
|
+
const preset = this._generateFromConstraints(constraints, geometryBase, coreType);
|
|
382
|
+
|
|
383
|
+
preset.name = theme
|
|
384
|
+
? `${this._capitalize(theme)} #${Math.floor(Math.random() * 1000)}`
|
|
385
|
+
: `Random #${Math.floor(Math.random() * 10000)}`;
|
|
386
|
+
preset.description = theme ? `Random preset with ${theme} theme` : 'Fully random preset';
|
|
387
|
+
preset.timestamp = Date.now();
|
|
388
|
+
|
|
389
|
+
this._addToHistory(preset);
|
|
390
|
+
|
|
391
|
+
return preset;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// -----------------------------------------------------------------------
|
|
395
|
+
// Mutation & Crossbreeding
|
|
396
|
+
// -----------------------------------------------------------------------
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Mutate an existing preset by randomly varying parameters while
|
|
400
|
+
* maintaining overall coherence.
|
|
401
|
+
*
|
|
402
|
+
* @param {VIB3Preset} preset - Source preset
|
|
403
|
+
* @param {number} [intensity=0.3] - Mutation intensity (0 = no change, 1 = fully random)
|
|
404
|
+
* @returns {VIB3Preset} New mutated preset
|
|
405
|
+
*/
|
|
406
|
+
mutate(preset, intensity = 0.3) {
|
|
407
|
+
const clamped = Math.max(0, Math.min(1, intensity));
|
|
408
|
+
const result = { ...preset };
|
|
409
|
+
|
|
410
|
+
for (const [param, range] of Object.entries(PARAM_RANGES)) {
|
|
411
|
+
if (preset[param] === undefined) continue;
|
|
412
|
+
|
|
413
|
+
const currentNorm = (preset[param] - range.min) / (range.max - range.min);
|
|
414
|
+
|
|
415
|
+
// Apply gaussian-like mutation
|
|
416
|
+
const mutation = (Math.random() - 0.5) * 2.0 * clamped;
|
|
417
|
+
let newNorm = currentNorm + mutation;
|
|
418
|
+
newNorm = Math.max(0, Math.min(1, newNorm));
|
|
419
|
+
|
|
420
|
+
let newValue = range.min + newNorm * (range.max - range.min);
|
|
421
|
+
|
|
422
|
+
if (range.type === 'int') {
|
|
423
|
+
newValue = Math.round(newValue);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
result[param] = newValue;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
result.name = `Mutated: ${preset.name || 'Unnamed'} (${Math.round(clamped * 100)}%)`;
|
|
430
|
+
result.description = `Mutated from "${preset.name || 'unnamed'}" at ${Math.round(clamped * 100)}% intensity`;
|
|
431
|
+
result.timestamp = Date.now();
|
|
432
|
+
|
|
433
|
+
this._addToHistory(result);
|
|
434
|
+
|
|
435
|
+
return result;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Crossbreed two presets by blending their parameters.
|
|
440
|
+
*
|
|
441
|
+
* @param {VIB3Preset} presetA - Parent A
|
|
442
|
+
* @param {VIB3Preset} presetB - Parent B
|
|
443
|
+
* @param {number} [ratio=0.5] - Blend ratio (0 = all A, 1 = all B)
|
|
444
|
+
* @returns {VIB3Preset} Offspring preset
|
|
445
|
+
*/
|
|
446
|
+
crossbreed(presetA, presetB, ratio = 0.5) {
|
|
447
|
+
const r = Math.max(0, Math.min(1, ratio));
|
|
448
|
+
const result = {};
|
|
449
|
+
|
|
450
|
+
for (const [param, range] of Object.entries(PARAM_RANGES)) {
|
|
451
|
+
const valA = presetA[param] !== undefined ? presetA[param] : this._randomInRange(range);
|
|
452
|
+
const valB = presetB[param] !== undefined ? presetB[param] : this._randomInRange(range);
|
|
453
|
+
|
|
454
|
+
// For geometry, pick one parent based on ratio
|
|
455
|
+
if (range.type === 'int') {
|
|
456
|
+
result[param] = Math.random() < r
|
|
457
|
+
? Math.round(valB)
|
|
458
|
+
: Math.round(valA);
|
|
459
|
+
} else {
|
|
460
|
+
// Linear interpolation
|
|
461
|
+
result[param] = valA * (1.0 - r) + valB * r;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
result.name = `Crossbred: ${(presetA.name || 'A').substring(0, 15)} x ${(presetB.name || 'B').substring(0, 15)}`;
|
|
466
|
+
result.description = `Crossbred at ${Math.round(r * 100)}% ratio`;
|
|
467
|
+
result.timestamp = Date.now();
|
|
468
|
+
|
|
469
|
+
this._addToHistory(result);
|
|
470
|
+
|
|
471
|
+
return result;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// -----------------------------------------------------------------------
|
|
475
|
+
// History
|
|
476
|
+
// -----------------------------------------------------------------------
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Export the full preset generation history.
|
|
480
|
+
* @returns {VIB3Preset[]}
|
|
481
|
+
*/
|
|
482
|
+
exportHistory() {
|
|
483
|
+
return [...this.presetHistory];
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Clear the preset history.
|
|
488
|
+
*/
|
|
489
|
+
clearHistory() {
|
|
490
|
+
this.presetHistory = [];
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Get the most recently generated preset.
|
|
495
|
+
* @returns {VIB3Preset|null}
|
|
496
|
+
*/
|
|
497
|
+
getLastPreset() {
|
|
498
|
+
return this.presetHistory.length > 0
|
|
499
|
+
? this.presetHistory[this.presetHistory.length - 1]
|
|
500
|
+
: null;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// -----------------------------------------------------------------------
|
|
504
|
+
// Internal Helpers
|
|
505
|
+
// -----------------------------------------------------------------------
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Tokenize a description string into individual keywords.
|
|
509
|
+
* @param {string} text - Lowercased description
|
|
510
|
+
* @returns {string[]}
|
|
511
|
+
* @private
|
|
512
|
+
*/
|
|
513
|
+
_tokenize(text) {
|
|
514
|
+
// Remove punctuation and split on whitespace
|
|
515
|
+
return text
|
|
516
|
+
.replace(/[^a-z0-9\s-]/g, ' ')
|
|
517
|
+
.split(/\s+/)
|
|
518
|
+
.filter(t => t.length > 1);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Match tokens against the style vocabulary.
|
|
523
|
+
* Also attempts partial/fuzzy matching for common suffixes.
|
|
524
|
+
*
|
|
525
|
+
* @param {string[]} tokens
|
|
526
|
+
* @returns {string[]} Matched style keywords
|
|
527
|
+
* @private
|
|
528
|
+
*/
|
|
529
|
+
_matchKeywords(tokens) {
|
|
530
|
+
const matched = [];
|
|
531
|
+
const vocabKeys = Object.keys(STYLE_VOCABULARY);
|
|
532
|
+
|
|
533
|
+
for (const token of tokens) {
|
|
534
|
+
// Direct match
|
|
535
|
+
if (STYLE_VOCABULARY[token]) {
|
|
536
|
+
matched.push(token);
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Try common variations
|
|
541
|
+
const variations = [
|
|
542
|
+
token,
|
|
543
|
+
token.replace(/s$/, ''), // plurals
|
|
544
|
+
token.replace(/ing$/, ''), // gerunds
|
|
545
|
+
token.replace(/ed$/, ''), // past tense
|
|
546
|
+
token.replace(/ly$/, ''), // adverbs -> adjectives
|
|
547
|
+
token.replace(/ish$/, ''), // bluish -> blue
|
|
548
|
+
token.replace(/ness$/, ''), // darkness -> dark
|
|
549
|
+
token.replace(/y$/, ''), // wavy -> wav
|
|
550
|
+
token + 'al', // cosmic -> cosmical
|
|
551
|
+
];
|
|
552
|
+
|
|
553
|
+
for (const variant of variations) {
|
|
554
|
+
if (STYLE_VOCABULARY[variant]) {
|
|
555
|
+
matched.push(variant);
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Substring match (e.g. "psychedelia" matches "psychedelic")
|
|
561
|
+
if (!matched.includes(token)) {
|
|
562
|
+
for (const key of vocabKeys) {
|
|
563
|
+
if (key.startsWith(token) || token.startsWith(key)) {
|
|
564
|
+
if (!matched.includes(key)) {
|
|
565
|
+
matched.push(key);
|
|
566
|
+
}
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return matched;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Generate a preset from parameter constraints.
|
|
578
|
+
* Constraints are expressed as [low, high] normalized fractions of the
|
|
579
|
+
* parameter range.
|
|
580
|
+
*
|
|
581
|
+
* @param {Object} constraints - Param name -> [low, high] (0-1 fractions)
|
|
582
|
+
* @param {number|null} geometryBase - Forced base geometry (0-7) or null
|
|
583
|
+
* @param {number|null} coreType - Forced core type (0-2) or null
|
|
584
|
+
* @returns {VIB3Preset}
|
|
585
|
+
* @private
|
|
586
|
+
*/
|
|
587
|
+
_generateFromConstraints(constraints, geometryBase, coreType) {
|
|
588
|
+
const preset = {};
|
|
589
|
+
|
|
590
|
+
for (const [param, range] of Object.entries(PARAM_RANGES)) {
|
|
591
|
+
if (param === 'geometry') continue; // Handle separately
|
|
592
|
+
|
|
593
|
+
if (constraints[param]) {
|
|
594
|
+
const [lo, hi] = constraints[param];
|
|
595
|
+
const norm = lo + Math.random() * (hi - lo);
|
|
596
|
+
preset[param] = range.min + norm * (range.max - range.min);
|
|
597
|
+
} else {
|
|
598
|
+
preset[param] = this._randomInRange(range);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (range.type === 'int') {
|
|
602
|
+
preset[param] = Math.round(preset[param]);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// Geometry index = coreType * 8 + baseGeometry
|
|
607
|
+
const base = geometryBase !== null ? geometryBase : Math.floor(Math.random() * 8);
|
|
608
|
+
const core = coreType !== null ? coreType : Math.floor(Math.random() * 3);
|
|
609
|
+
preset.geometry = core * 8 + base;
|
|
610
|
+
|
|
611
|
+
// Handle hue wrapping (values like [350, 370] should wrap around 360)
|
|
612
|
+
if (preset.hue > 360) {
|
|
613
|
+
preset.hue = preset.hue - 360;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
return preset;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Generate a random value within a parameter range.
|
|
621
|
+
* @param {{min: number, max: number, type: string}} range
|
|
622
|
+
* @returns {number}
|
|
623
|
+
* @private
|
|
624
|
+
*/
|
|
625
|
+
_randomInRange(range) {
|
|
626
|
+
const val = range.min + Math.random() * (range.max - range.min);
|
|
627
|
+
return range.type === 'int' ? Math.round(val) : val;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Generate a human-readable name from matched styles.
|
|
632
|
+
* @param {string[]} styles
|
|
633
|
+
* @param {string} description
|
|
634
|
+
* @returns {string}
|
|
635
|
+
* @private
|
|
636
|
+
*/
|
|
637
|
+
_generatePresetName(styles, description) {
|
|
638
|
+
if (styles.length === 0) {
|
|
639
|
+
// Take first few words of description
|
|
640
|
+
const words = description.split(/\s+/).slice(0, 3);
|
|
641
|
+
return words.map(w => this._capitalize(w)).join(' ');
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Combine up to 3 matched styles into a name
|
|
645
|
+
const nameStyles = styles.slice(0, 3);
|
|
646
|
+
return nameStyles.map(s => this._capitalize(s)).join(' ');
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Capitalize first letter of a string.
|
|
651
|
+
* @param {string} str
|
|
652
|
+
* @returns {string}
|
|
653
|
+
* @private
|
|
654
|
+
*/
|
|
655
|
+
_capitalize(str) {
|
|
656
|
+
if (!str) return '';
|
|
657
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Get the current engine state (if engine is available).
|
|
662
|
+
* @returns {Object}
|
|
663
|
+
* @private
|
|
664
|
+
*/
|
|
665
|
+
_getCurrentState() {
|
|
666
|
+
if (!this.engine || typeof this.engine.getParameter !== 'function') {
|
|
667
|
+
return {};
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
const state = {};
|
|
671
|
+
for (const param of Object.keys(PARAM_RANGES)) {
|
|
672
|
+
try {
|
|
673
|
+
state[param] = this.engine.getParameter(param);
|
|
674
|
+
} catch (_e) {
|
|
675
|
+
// Parameter may not exist
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
return state;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Parse an LLM response into a VIB3Preset. The LLM is expected to return
|
|
684
|
+
* either a JSON object with parameter keys or a content array with text.
|
|
685
|
+
*
|
|
686
|
+
* @param {Object} llmOutput
|
|
687
|
+
* @returns {VIB3Preset}
|
|
688
|
+
* @private
|
|
689
|
+
*/
|
|
690
|
+
_parseLLMResponse(llmOutput) {
|
|
691
|
+
const preset = {};
|
|
692
|
+
|
|
693
|
+
// Try direct parameter extraction
|
|
694
|
+
let paramSource = llmOutput;
|
|
695
|
+
|
|
696
|
+
// Handle MCP content array format
|
|
697
|
+
if (llmOutput && llmOutput.content && Array.isArray(llmOutput.content)) {
|
|
698
|
+
for (const block of llmOutput.content) {
|
|
699
|
+
if (block.type === 'text') {
|
|
700
|
+
try {
|
|
701
|
+
// Try to parse JSON from text
|
|
702
|
+
const jsonMatch = block.text.match(/\{[\s\S]*\}/);
|
|
703
|
+
if (jsonMatch) {
|
|
704
|
+
paramSource = JSON.parse(jsonMatch[0]);
|
|
705
|
+
}
|
|
706
|
+
} catch (_e) {
|
|
707
|
+
// Not valid JSON, try keyword extraction
|
|
708
|
+
return this._generateFromConstraints(
|
|
709
|
+
this._extractConstraintsFromText(block.text),
|
|
710
|
+
null,
|
|
711
|
+
null
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Map known parameters from the source
|
|
719
|
+
for (const [param, range] of Object.entries(PARAM_RANGES)) {
|
|
720
|
+
if (paramSource[param] !== undefined) {
|
|
721
|
+
let val = parseFloat(paramSource[param]);
|
|
722
|
+
if (!isNaN(val)) {
|
|
723
|
+
val = Math.max(range.min, Math.min(range.max, val));
|
|
724
|
+
preset[param] = range.type === 'int' ? Math.round(val) : val;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// Fill any missing parameters with defaults
|
|
730
|
+
for (const [param, range] of Object.entries(PARAM_RANGES)) {
|
|
731
|
+
if (preset[param] === undefined) {
|
|
732
|
+
preset[param] = this._randomInRange(range);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
if (paramSource.name) preset.name = String(paramSource.name);
|
|
737
|
+
if (paramSource.system) preset.system = String(paramSource.system);
|
|
738
|
+
|
|
739
|
+
return preset;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Extract parameter constraints from unstructured text (fallback).
|
|
744
|
+
* @param {string} text
|
|
745
|
+
* @returns {Object}
|
|
746
|
+
* @private
|
|
747
|
+
*/
|
|
748
|
+
_extractConstraintsFromText(text) {
|
|
749
|
+
const tokens = this._tokenize(text.toLowerCase());
|
|
750
|
+
const matched = this._matchKeywords(tokens);
|
|
751
|
+
const constraints = {};
|
|
752
|
+
|
|
753
|
+
for (const style of matched) {
|
|
754
|
+
const mapping = STYLE_VOCABULARY[style];
|
|
755
|
+
if (!mapping) continue;
|
|
756
|
+
for (const [param, range] of Object.entries(mapping)) {
|
|
757
|
+
if (!param.startsWith('_')) {
|
|
758
|
+
constraints[param] = range;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
return constraints;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Add a preset to history, trimming if needed.
|
|
768
|
+
* @param {VIB3Preset} preset
|
|
769
|
+
* @private
|
|
770
|
+
*/
|
|
771
|
+
_addToHistory(preset) {
|
|
772
|
+
this.presetHistory.push(preset);
|
|
773
|
+
if (this.presetHistory.length > this.maxHistory) {
|
|
774
|
+
this.presetHistory = this.presetHistory.slice(-this.maxHistory);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|