@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,854 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VIB3+ Figma Plugin Integration
|
|
3
|
+
*
|
|
4
|
+
* Generates a complete Figma plugin package that renders VIB3+ 4D visualizations
|
|
5
|
+
* and exports them as image fills, effects, or standalone frames within Figma.
|
|
6
|
+
*
|
|
7
|
+
* The plugin uses an off-screen canvas with WebGL to render VIB3 shaders,
|
|
8
|
+
* then transfers the pixel data to Figma via the Plugin API.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* const pkg = Vib3FigmaPlugin.generatePackage();
|
|
12
|
+
* // Write all files to disk, then publish via Figma Developer Console
|
|
13
|
+
*
|
|
14
|
+
* @module Vib3FigmaPlugin
|
|
15
|
+
* @version 1.0.0
|
|
16
|
+
* @license All Rights Reserved - Clear Seas Solutions LLC
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Default render dimensions for plugin output.
|
|
21
|
+
* @constant {Object}
|
|
22
|
+
*/
|
|
23
|
+
const DEFAULT_RENDER_CONFIG = {
|
|
24
|
+
width: 1024,
|
|
25
|
+
height: 1024,
|
|
26
|
+
pixelRatio: 2,
|
|
27
|
+
format: 'PNG'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* VIB3+ systems available in the plugin.
|
|
32
|
+
* @constant {string[]}
|
|
33
|
+
*/
|
|
34
|
+
const AVAILABLE_SYSTEMS = ['quantum', 'faceted', 'holographic'];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Base geometry names for the UI selector.
|
|
38
|
+
* @constant {string[]}
|
|
39
|
+
*/
|
|
40
|
+
const BASE_GEOMETRIES = [
|
|
41
|
+
'Tetrahedron', 'Hypercube', 'Sphere', 'Torus',
|
|
42
|
+
'Klein Bottle', 'Fractal', 'Wave', 'Crystal'
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Core type names for the UI selector.
|
|
47
|
+
* @constant {string[]}
|
|
48
|
+
*/
|
|
49
|
+
const CORE_TYPES = ['Base', 'Hypersphere', 'Hypertetrahedron'];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Figma plugin integration for VIB3+ visualization engine.
|
|
53
|
+
*
|
|
54
|
+
* Generates all necessary files for a Figma plugin:
|
|
55
|
+
* - manifest.json (plugin metadata and permissions)
|
|
56
|
+
* - code.js (plugin sandbox code using Figma Plugin API)
|
|
57
|
+
* - ui.html (plugin UI with parameter controls and preview)
|
|
58
|
+
*
|
|
59
|
+
* @class
|
|
60
|
+
*/
|
|
61
|
+
export class Vib3FigmaPlugin {
|
|
62
|
+
/**
|
|
63
|
+
* Generates the Figma plugin manifest.json content.
|
|
64
|
+
*
|
|
65
|
+
* Configures the plugin with:
|
|
66
|
+
* - Plugin name, ID, and API version
|
|
67
|
+
* - Edit permission for creating/modifying fills
|
|
68
|
+
* - UI entry point and dimensions
|
|
69
|
+
* - Network access for loading VIB3 core assets
|
|
70
|
+
*
|
|
71
|
+
* @param {object} [options={}] - Manifest customization options
|
|
72
|
+
* @param {string} [options.name='VIB3+ 4D Visualizer'] - Plugin display name
|
|
73
|
+
* @param {string} [options.id='vib3-figma-plugin'] - Plugin identifier
|
|
74
|
+
* @param {string} [options.description] - Plugin description text
|
|
75
|
+
* @returns {object} Figma plugin manifest object
|
|
76
|
+
* @example
|
|
77
|
+
* const manifest = Vib3FigmaPlugin.generatePluginManifest({ name: 'My VIB3 Plugin' });
|
|
78
|
+
* fs.writeFileSync('manifest.json', JSON.stringify(manifest, null, 2));
|
|
79
|
+
*/
|
|
80
|
+
static generatePluginManifest(options = {}) {
|
|
81
|
+
return {
|
|
82
|
+
name: options.name || 'VIB3+ 4D Visualizer',
|
|
83
|
+
id: options.id || 'vib3-figma-plugin',
|
|
84
|
+
api: '1.0.0',
|
|
85
|
+
main: 'code.js',
|
|
86
|
+
ui: 'ui.html',
|
|
87
|
+
editorType: ['figma'],
|
|
88
|
+
capabilities: [],
|
|
89
|
+
enableProposedApi: false,
|
|
90
|
+
permissions: ['currentuser'],
|
|
91
|
+
networkAccess: {
|
|
92
|
+
allowedDomains: ['none'],
|
|
93
|
+
reasoning: 'VIB3 renders entirely client-side using embedded shaders.'
|
|
94
|
+
},
|
|
95
|
+
description: options.description ||
|
|
96
|
+
'Generate stunning 4D visualizations with quantum, faceted, and holographic rendering systems. ' +
|
|
97
|
+
'Supports 24 geometry variants with full 6D rotation control.',
|
|
98
|
+
documentAccess: 'dynamic-page',
|
|
99
|
+
containsWidget: false
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Generates the main plugin sandbox code (code.js).
|
|
105
|
+
*
|
|
106
|
+
* This code runs in the Figma plugin sandbox and:
|
|
107
|
+
* - Opens the plugin UI at a configurable size
|
|
108
|
+
* - Receives rendered pixel data from the UI iframe
|
|
109
|
+
* - Creates Figma rectangles with the visualization as an image fill
|
|
110
|
+
* - Supports batch generation of multiple geometry variants
|
|
111
|
+
* - Handles error reporting back to the UI
|
|
112
|
+
*
|
|
113
|
+
* @param {object} [options={}] - Code generation options
|
|
114
|
+
* @param {number} [options.uiWidth=480] - Plugin UI panel width
|
|
115
|
+
* @param {number} [options.uiHeight=640] - Plugin UI panel height
|
|
116
|
+
* @returns {string} Complete plugin sandbox code
|
|
117
|
+
*/
|
|
118
|
+
static generatePluginCode(options = {}) {
|
|
119
|
+
const uiWidth = options.uiWidth || 480;
|
|
120
|
+
const uiHeight = options.uiHeight || 640;
|
|
121
|
+
|
|
122
|
+
return `/**
|
|
123
|
+
* VIB3+ Figma Plugin - Sandbox Code
|
|
124
|
+
* Runs in the Figma plugin sandbox (no DOM access).
|
|
125
|
+
* Communicates with ui.html via figma.ui.postMessage / figma.ui.onmessage.
|
|
126
|
+
*/
|
|
127
|
+
|
|
128
|
+
figma.showUI(__html__, {
|
|
129
|
+
width: ${uiWidth},
|
|
130
|
+
height: ${uiHeight},
|
|
131
|
+
title: 'VIB3+ 4D Visualizer'
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Handle messages from the plugin UI.
|
|
136
|
+
*/
|
|
137
|
+
figma.ui.onmessage = async (msg) => {
|
|
138
|
+
try {
|
|
139
|
+
switch (msg.type) {
|
|
140
|
+
case 'render-complete':
|
|
141
|
+
await handleRenderComplete(msg);
|
|
142
|
+
break;
|
|
143
|
+
|
|
144
|
+
case 'batch-render':
|
|
145
|
+
await handleBatchRender(msg);
|
|
146
|
+
break;
|
|
147
|
+
|
|
148
|
+
case 'apply-fill':
|
|
149
|
+
await handleApplyFill(msg);
|
|
150
|
+
break;
|
|
151
|
+
|
|
152
|
+
case 'cancel':
|
|
153
|
+
figma.closePlugin();
|
|
154
|
+
break;
|
|
155
|
+
|
|
156
|
+
default:
|
|
157
|
+
console.warn('Unknown message type:', msg.type);
|
|
158
|
+
}
|
|
159
|
+
} catch (err) {
|
|
160
|
+
figma.ui.postMessage({
|
|
161
|
+
type: 'error',
|
|
162
|
+
message: err.message || 'An unknown error occurred'
|
|
163
|
+
});
|
|
164
|
+
figma.notify('VIB3+ Error: ' + (err.message || 'Unknown error'), { error: true });
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Create a Figma rectangle with the rendered VIB3 visualization as an image fill.
|
|
170
|
+
*
|
|
171
|
+
* @param {object} msg - Message containing imageData (Uint8Array), width, height, name
|
|
172
|
+
*/
|
|
173
|
+
async function handleRenderComplete(msg) {
|
|
174
|
+
const { imageData, width, height, name } = msg;
|
|
175
|
+
|
|
176
|
+
if (!imageData || !width || !height) {
|
|
177
|
+
throw new Error('Invalid render data: missing imageData, width, or height');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Create image from raw pixel data
|
|
181
|
+
const image = figma.createImage(new Uint8Array(imageData));
|
|
182
|
+
|
|
183
|
+
// Create rectangle node
|
|
184
|
+
const rect = figma.createRectangle();
|
|
185
|
+
rect.name = name || 'VIB3+ Visualization';
|
|
186
|
+
rect.resize(width, height);
|
|
187
|
+
|
|
188
|
+
// Apply image as fill
|
|
189
|
+
rect.fills = [{
|
|
190
|
+
type: 'IMAGE',
|
|
191
|
+
scaleMode: 'FILL',
|
|
192
|
+
imageHash: image.hash
|
|
193
|
+
}];
|
|
194
|
+
|
|
195
|
+
// Position in viewport center
|
|
196
|
+
const viewCenter = figma.viewport.center;
|
|
197
|
+
rect.x = viewCenter.x - width / 2;
|
|
198
|
+
rect.y = viewCenter.y - height / 2;
|
|
199
|
+
|
|
200
|
+
// Select the new node
|
|
201
|
+
figma.currentPage.selection = [rect];
|
|
202
|
+
figma.viewport.scrollAndZoomIntoView([rect]);
|
|
203
|
+
|
|
204
|
+
figma.notify('VIB3+ visualization created!');
|
|
205
|
+
figma.ui.postMessage({ type: 'render-inserted', nodeId: rect.id });
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Generate multiple geometry variants and arrange them in a grid.
|
|
210
|
+
*
|
|
211
|
+
* @param {object} msg - Message containing params, count, columns, cellSize
|
|
212
|
+
*/
|
|
213
|
+
async function handleBatchRender(msg) {
|
|
214
|
+
const { renders, columns, cellSize, name } = msg;
|
|
215
|
+
|
|
216
|
+
if (!renders || !Array.isArray(renders) || renders.length === 0) {
|
|
217
|
+
throw new Error('Invalid batch render data');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const cols = columns || 4;
|
|
221
|
+
const size = cellSize || 512;
|
|
222
|
+
const groupName = name || 'VIB3+ Batch';
|
|
223
|
+
|
|
224
|
+
const nodes = [];
|
|
225
|
+
const viewCenter = figma.viewport.center;
|
|
226
|
+
const totalCols = Math.min(cols, renders.length);
|
|
227
|
+
const totalRows = Math.ceil(renders.length / cols);
|
|
228
|
+
const startX = viewCenter.x - (totalCols * (size + 20)) / 2;
|
|
229
|
+
const startY = viewCenter.y - (totalRows * (size + 20)) / 2;
|
|
230
|
+
|
|
231
|
+
for (let i = 0; i < renders.length; i++) {
|
|
232
|
+
const render = renders[i];
|
|
233
|
+
const col = i % cols;
|
|
234
|
+
const row = Math.floor(i / cols);
|
|
235
|
+
|
|
236
|
+
const image = figma.createImage(new Uint8Array(render.imageData));
|
|
237
|
+
|
|
238
|
+
const rect = figma.createRectangle();
|
|
239
|
+
rect.name = render.name || 'VIB3+ Variant ' + i;
|
|
240
|
+
rect.resize(size, size);
|
|
241
|
+
rect.fills = [{
|
|
242
|
+
type: 'IMAGE',
|
|
243
|
+
scaleMode: 'FILL',
|
|
244
|
+
imageHash: image.hash
|
|
245
|
+
}];
|
|
246
|
+
|
|
247
|
+
rect.x = startX + col * (size + 20);
|
|
248
|
+
rect.y = startY + row * (size + 20);
|
|
249
|
+
|
|
250
|
+
// Add rounded corners
|
|
251
|
+
rect.cornerRadius = 8;
|
|
252
|
+
|
|
253
|
+
nodes.push(rect);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Group all nodes
|
|
257
|
+
if (nodes.length > 1) {
|
|
258
|
+
const group = figma.group(nodes, figma.currentPage);
|
|
259
|
+
group.name = groupName;
|
|
260
|
+
figma.currentPage.selection = [group];
|
|
261
|
+
figma.viewport.scrollAndZoomIntoView([group]);
|
|
262
|
+
} else if (nodes.length === 1) {
|
|
263
|
+
figma.currentPage.selection = nodes;
|
|
264
|
+
figma.viewport.scrollAndZoomIntoView(nodes);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
figma.notify('VIB3+ batch: ' + renders.length + ' visualizations created!');
|
|
268
|
+
figma.ui.postMessage({ type: 'batch-complete', count: renders.length });
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Apply a VIB3 visualization as fill to the currently selected node.
|
|
273
|
+
*
|
|
274
|
+
* @param {object} msg - Message containing imageData, width, height
|
|
275
|
+
*/
|
|
276
|
+
async function handleApplyFill(msg) {
|
|
277
|
+
const { imageData } = msg;
|
|
278
|
+
const selection = figma.currentPage.selection;
|
|
279
|
+
|
|
280
|
+
if (selection.length === 0) {
|
|
281
|
+
figma.notify('Please select a shape first.', { error: true });
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const image = figma.createImage(new Uint8Array(imageData));
|
|
286
|
+
|
|
287
|
+
let appliedCount = 0;
|
|
288
|
+
for (const node of selection) {
|
|
289
|
+
if ('fills' in node) {
|
|
290
|
+
node.fills = [{
|
|
291
|
+
type: 'IMAGE',
|
|
292
|
+
scaleMode: 'FILL',
|
|
293
|
+
imageHash: image.hash
|
|
294
|
+
}];
|
|
295
|
+
appliedCount++;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (appliedCount > 0) {
|
|
300
|
+
figma.notify('VIB3+ fill applied to ' + appliedCount + ' shape(s)!');
|
|
301
|
+
} else {
|
|
302
|
+
figma.notify('Selected nodes do not support fills.', { error: true });
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
figma.ui.postMessage({ type: 'fill-applied', count: appliedCount });
|
|
306
|
+
}
|
|
307
|
+
`;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Generates the plugin UI HTML file.
|
|
312
|
+
*
|
|
313
|
+
* The UI provides:
|
|
314
|
+
* - System selector (Quantum / Faceted / Holographic)
|
|
315
|
+
* - Geometry variant grid (24 options)
|
|
316
|
+
* - Parameter sliders (hue, saturation, intensity, speed, etc.)
|
|
317
|
+
* - 6D rotation controls
|
|
318
|
+
* - Live WebGL preview canvas
|
|
319
|
+
* - Export buttons (Create New, Apply to Selection, Batch Generate)
|
|
320
|
+
* - Render size configuration
|
|
321
|
+
*
|
|
322
|
+
* The UI renders VIB3 shaders on an off-screen WebGL canvas and
|
|
323
|
+
* sends pixel data to the plugin sandbox via parent.postMessage.
|
|
324
|
+
*
|
|
325
|
+
* @param {object} [options={}] - UI customization options
|
|
326
|
+
* @param {number} [options.previewSize=256] - Preview canvas size in pixels
|
|
327
|
+
* @returns {string} Complete HTML string for the plugin UI
|
|
328
|
+
*/
|
|
329
|
+
static generatePluginUI(options = {}) {
|
|
330
|
+
const previewSize = options.previewSize || 256;
|
|
331
|
+
|
|
332
|
+
return `<!DOCTYPE html>
|
|
333
|
+
<html lang="en">
|
|
334
|
+
<head>
|
|
335
|
+
<meta charset="UTF-8">
|
|
336
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
337
|
+
<title>VIB3+ 4D Visualizer</title>
|
|
338
|
+
<style>
|
|
339
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
340
|
+
body {
|
|
341
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
|
342
|
+
font-size: 11px;
|
|
343
|
+
color: #e0e0e0;
|
|
344
|
+
background: #1e1e2e;
|
|
345
|
+
overflow-y: auto;
|
|
346
|
+
padding: 12px;
|
|
347
|
+
}
|
|
348
|
+
h2 { font-size: 13px; margin-bottom: 8px; color: #64ffb4; text-transform: uppercase; letter-spacing: 1px; }
|
|
349
|
+
h3 { font-size: 11px; margin-bottom: 6px; color: #aaa; text-transform: uppercase; letter-spacing: 0.5px; }
|
|
350
|
+
.section { margin-bottom: 16px; }
|
|
351
|
+
.preview-container {
|
|
352
|
+
display: flex; justify-content: center; margin-bottom: 12px;
|
|
353
|
+
}
|
|
354
|
+
#preview-canvas {
|
|
355
|
+
width: ${previewSize}px; height: ${previewSize}px;
|
|
356
|
+
border-radius: 8px; border: 1px solid #333;
|
|
357
|
+
background: #000;
|
|
358
|
+
}
|
|
359
|
+
.system-tabs {
|
|
360
|
+
display: flex; gap: 4px; margin-bottom: 12px;
|
|
361
|
+
}
|
|
362
|
+
.system-tab {
|
|
363
|
+
flex: 1; padding: 6px 0; text-align: center;
|
|
364
|
+
background: #2a2a3e; border: 1px solid #3a3a5e; border-radius: 4px;
|
|
365
|
+
color: #888; cursor: pointer; font-size: 10px; text-transform: uppercase;
|
|
366
|
+
transition: all 0.2s;
|
|
367
|
+
}
|
|
368
|
+
.system-tab.active { background: #3a3a6e; color: #64ffb4; border-color: #64ffb4; }
|
|
369
|
+
.system-tab:hover:not(.active) { background: #333358; color: #ccc; }
|
|
370
|
+
.geometry-grid {
|
|
371
|
+
display: grid; grid-template-columns: repeat(8, 1fr); gap: 3px; margin-bottom: 12px;
|
|
372
|
+
}
|
|
373
|
+
.geom-btn {
|
|
374
|
+
padding: 4px 0; text-align: center; font-size: 9px;
|
|
375
|
+
background: #2a2a3e; border: 1px solid #3a3a5e; border-radius: 3px;
|
|
376
|
+
color: #888; cursor: pointer; transition: all 0.15s;
|
|
377
|
+
}
|
|
378
|
+
.geom-btn.active { background: #3a3a6e; color: #64ffb4; border-color: #64ffb4; }
|
|
379
|
+
.geom-btn:hover:not(.active) { background: #333358; color: #ccc; }
|
|
380
|
+
.slider-row {
|
|
381
|
+
display: flex; align-items: center; margin-bottom: 6px;
|
|
382
|
+
}
|
|
383
|
+
.slider-label { width: 80px; color: #999; font-size: 10px; }
|
|
384
|
+
.slider-input {
|
|
385
|
+
flex: 1; -webkit-appearance: none; appearance: none;
|
|
386
|
+
height: 4px; background: #3a3a5e; border-radius: 2px; outline: none;
|
|
387
|
+
}
|
|
388
|
+
.slider-input::-webkit-slider-thumb {
|
|
389
|
+
-webkit-appearance: none; width: 12px; height: 12px;
|
|
390
|
+
background: #64ffb4; border-radius: 50%; cursor: pointer;
|
|
391
|
+
}
|
|
392
|
+
.slider-value { width: 40px; text-align: right; color: #64ffb4; font-size: 10px; font-family: monospace; }
|
|
393
|
+
.action-buttons {
|
|
394
|
+
display: flex; gap: 6px; margin-top: 12px;
|
|
395
|
+
}
|
|
396
|
+
.btn {
|
|
397
|
+
flex: 1; padding: 8px 0; text-align: center;
|
|
398
|
+
border: 1px solid #64ffb4; border-radius: 4px;
|
|
399
|
+
color: #64ffb4; background: transparent;
|
|
400
|
+
cursor: pointer; font-size: 10px; text-transform: uppercase;
|
|
401
|
+
letter-spacing: 0.5px; transition: all 0.2s;
|
|
402
|
+
}
|
|
403
|
+
.btn:hover { background: #64ffb420; }
|
|
404
|
+
.btn.primary { background: #64ffb4; color: #1e1e2e; font-weight: 600; }
|
|
405
|
+
.btn.primary:hover { background: #7affcc; }
|
|
406
|
+
.size-row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
|
|
407
|
+
.size-input {
|
|
408
|
+
width: 60px; padding: 4px 6px; background: #2a2a3e; border: 1px solid #3a3a5e;
|
|
409
|
+
border-radius: 3px; color: #e0e0e0; font-size: 10px; text-align: center;
|
|
410
|
+
}
|
|
411
|
+
.status { color: #888; font-size: 10px; text-align: center; margin-top: 8px; min-height: 14px; }
|
|
412
|
+
.core-label {
|
|
413
|
+
grid-column: span 8; font-size: 9px; color: #666;
|
|
414
|
+
text-transform: uppercase; letter-spacing: 0.5px; padding: 2px 0;
|
|
415
|
+
}
|
|
416
|
+
</style>
|
|
417
|
+
</head>
|
|
418
|
+
<body>
|
|
419
|
+
<h2>VIB3+ 4D Visualizer</h2>
|
|
420
|
+
|
|
421
|
+
<!-- Preview -->
|
|
422
|
+
<div class="preview-container">
|
|
423
|
+
<canvas id="preview-canvas" width="${previewSize * 2}" height="${previewSize * 2}"></canvas>
|
|
424
|
+
</div>
|
|
425
|
+
|
|
426
|
+
<!-- System Selection -->
|
|
427
|
+
<div class="section">
|
|
428
|
+
<div class="system-tabs">
|
|
429
|
+
<div class="system-tab active" data-system="quantum">Quantum</div>
|
|
430
|
+
<div class="system-tab" data-system="faceted">Faceted</div>
|
|
431
|
+
<div class="system-tab" data-system="holographic">Holographic</div>
|
|
432
|
+
</div>
|
|
433
|
+
</div>
|
|
434
|
+
|
|
435
|
+
<!-- Geometry Selection -->
|
|
436
|
+
<div class="section">
|
|
437
|
+
<h3>Geometry</h3>
|
|
438
|
+
<div class="geometry-grid" id="geometry-grid"></div>
|
|
439
|
+
</div>
|
|
440
|
+
|
|
441
|
+
<!-- Parameters -->
|
|
442
|
+
<div class="section">
|
|
443
|
+
<h3>Parameters</h3>
|
|
444
|
+
<div id="param-sliders"></div>
|
|
445
|
+
</div>
|
|
446
|
+
|
|
447
|
+
<!-- 6D Rotation -->
|
|
448
|
+
<div class="section">
|
|
449
|
+
<h3>6D Rotation</h3>
|
|
450
|
+
<div id="rotation-sliders"></div>
|
|
451
|
+
</div>
|
|
452
|
+
|
|
453
|
+
<!-- Render Size -->
|
|
454
|
+
<div class="section">
|
|
455
|
+
<h3>Output Size</h3>
|
|
456
|
+
<div class="size-row">
|
|
457
|
+
<span class="slider-label">Width</span>
|
|
458
|
+
<input class="size-input" id="render-width" type="number" value="1024" min="64" max="4096" step="64">
|
|
459
|
+
<span style="color:#666">x</span>
|
|
460
|
+
<input class="size-input" id="render-height" type="number" value="1024" min="64" max="4096" step="64">
|
|
461
|
+
<span class="slider-label">Height</span>
|
|
462
|
+
</div>
|
|
463
|
+
</div>
|
|
464
|
+
|
|
465
|
+
<!-- Action Buttons -->
|
|
466
|
+
<div class="action-buttons">
|
|
467
|
+
<button class="btn primary" id="btn-create">Create New</button>
|
|
468
|
+
<button class="btn" id="btn-apply">Apply Fill</button>
|
|
469
|
+
<button class="btn" id="btn-batch">Batch (24)</button>
|
|
470
|
+
</div>
|
|
471
|
+
<div class="status" id="status"></div>
|
|
472
|
+
|
|
473
|
+
<script>
|
|
474
|
+
(function() {
|
|
475
|
+
// ---- State ----
|
|
476
|
+
const state = {
|
|
477
|
+
system: 'quantum',
|
|
478
|
+
geometry: 0,
|
|
479
|
+
hue: 200, saturation: 0.8, intensity: 0.5, speed: 1.0,
|
|
480
|
+
gridDensity: 15, morphFactor: 1.0, chaos: 0.2, dimension: 3.5,
|
|
481
|
+
rot4dXY: 0, rot4dXZ: 0, rot4dYZ: 0, rot4dXW: 0, rot4dYW: 0, rot4dZW: 0
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
// ---- Parameter Definitions ----
|
|
485
|
+
const paramDefs = [
|
|
486
|
+
{ key: 'hue', label: 'Hue', min: 0, max: 360, step: 1, format: (v) => v + '\\u00b0' },
|
|
487
|
+
{ key: 'saturation', label: 'Saturation', min: 0, max: 1, step: 0.01, format: (v) => (v * 100).toFixed(0) + '%' },
|
|
488
|
+
{ key: 'intensity', label: 'Intensity', min: 0, max: 1, step: 0.01, format: (v) => (v * 100).toFixed(0) + '%' },
|
|
489
|
+
{ key: 'speed', label: 'Speed', min: 0.1, max: 3, step: 0.1, format: (v) => v.toFixed(1) + 'x' },
|
|
490
|
+
{ key: 'gridDensity', label: 'Density', min: 4, max: 100, step: 1, format: (v) => v.toFixed(0) },
|
|
491
|
+
{ key: 'morphFactor', label: 'Morph', min: 0, max: 2, step: 0.01, format: (v) => v.toFixed(2) },
|
|
492
|
+
{ key: 'chaos', label: 'Chaos', min: 0, max: 1, step: 0.01, format: (v) => (v * 100).toFixed(0) + '%' },
|
|
493
|
+
{ key: 'dimension', label: 'Dimension', min: 3.0, max: 4.5, step: 0.01, format: (v) => v.toFixed(2) + 'D' }
|
|
494
|
+
];
|
|
495
|
+
|
|
496
|
+
const rotDefs = [
|
|
497
|
+
{ key: 'rot4dXY', label: 'XY Plane', min: -6.28, max: 6.28, step: 0.01 },
|
|
498
|
+
{ key: 'rot4dXZ', label: 'XZ Plane', min: -6.28, max: 6.28, step: 0.01 },
|
|
499
|
+
{ key: 'rot4dYZ', label: 'YZ Plane', min: -6.28, max: 6.28, step: 0.01 },
|
|
500
|
+
{ key: 'rot4dXW', label: 'XW Hyper', min: -2, max: 2, step: 0.01 },
|
|
501
|
+
{ key: 'rot4dYW', label: 'YW Hyper', min: -2, max: 2, step: 0.01 },
|
|
502
|
+
{ key: 'rot4dZW', label: 'ZW Hyper', min: -2, max: 2, step: 0.01 }
|
|
503
|
+
];
|
|
504
|
+
|
|
505
|
+
const baseNames = ['Tet', 'Cube', 'Sph', 'Tor', 'Kln', 'Frc', 'Wav', 'Cry'];
|
|
506
|
+
const coreLabels = ['Base', 'Hypersphere Core', 'Hypertetrahedron Core'];
|
|
507
|
+
|
|
508
|
+
// ---- Build UI ----
|
|
509
|
+
function buildGeometryGrid() {
|
|
510
|
+
const grid = document.getElementById('geometry-grid');
|
|
511
|
+
grid.innerHTML = '';
|
|
512
|
+
for (let core = 0; core < 3; core++) {
|
|
513
|
+
const label = document.createElement('div');
|
|
514
|
+
label.className = 'core-label';
|
|
515
|
+
label.textContent = coreLabels[core];
|
|
516
|
+
grid.appendChild(label);
|
|
517
|
+
for (let base = 0; base < 8; base++) {
|
|
518
|
+
const idx = core * 8 + base;
|
|
519
|
+
const btn = document.createElement('div');
|
|
520
|
+
btn.className = 'geom-btn' + (idx === state.geometry ? ' active' : '');
|
|
521
|
+
btn.textContent = baseNames[base];
|
|
522
|
+
btn.title = coreLabels[core] + ' - ' + baseNames[base] + ' (' + idx + ')';
|
|
523
|
+
btn.addEventListener('click', () => {
|
|
524
|
+
state.geometry = idx;
|
|
525
|
+
document.querySelectorAll('.geom-btn').forEach((b, i) => {
|
|
526
|
+
b.classList.toggle('active', i === idx);
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
grid.appendChild(btn);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function buildSliders(container, defs) {
|
|
535
|
+
const el = document.getElementById(container);
|
|
536
|
+
el.innerHTML = '';
|
|
537
|
+
defs.forEach(def => {
|
|
538
|
+
const row = document.createElement('div');
|
|
539
|
+
row.className = 'slider-row';
|
|
540
|
+
const fmt = def.format || ((v) => v.toFixed(2));
|
|
541
|
+
row.innerHTML =
|
|
542
|
+
'<span class="slider-label">' + def.label + '</span>' +
|
|
543
|
+
'<input class="slider-input" type="range" min="' + def.min + '" max="' + def.max +
|
|
544
|
+
'" step="' + def.step + '" value="' + state[def.key] + '" data-key="' + def.key + '">' +
|
|
545
|
+
'<span class="slider-value" id="val-' + def.key + '">' + fmt(state[def.key]) + '</span>';
|
|
546
|
+
const slider = row.querySelector('input');
|
|
547
|
+
slider.addEventListener('input', () => {
|
|
548
|
+
const v = parseFloat(slider.value);
|
|
549
|
+
state[def.key] = v;
|
|
550
|
+
document.getElementById('val-' + def.key).textContent = fmt(v);
|
|
551
|
+
});
|
|
552
|
+
el.appendChild(row);
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
buildGeometryGrid();
|
|
557
|
+
buildSliders('param-sliders', paramDefs);
|
|
558
|
+
buildSliders('rotation-sliders', rotDefs);
|
|
559
|
+
|
|
560
|
+
// ---- System Tabs ----
|
|
561
|
+
document.querySelectorAll('.system-tab').forEach(tab => {
|
|
562
|
+
tab.addEventListener('click', () => {
|
|
563
|
+
state.system = tab.dataset.system;
|
|
564
|
+
document.querySelectorAll('.system-tab').forEach(t => t.classList.remove('active'));
|
|
565
|
+
tab.classList.add('active');
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
// ---- Render to Canvas ----
|
|
570
|
+
function renderToCanvas(canvas, width, height) {
|
|
571
|
+
canvas.width = width;
|
|
572
|
+
canvas.height = height;
|
|
573
|
+
|
|
574
|
+
const gl = canvas.getContext('webgl2') || canvas.getContext('webgl');
|
|
575
|
+
if (!gl) {
|
|
576
|
+
setStatus('WebGL not available');
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Minimal full-screen quad vertex shader
|
|
581
|
+
const vsSource = 'attribute vec2 a_position;void main(){gl_Position=vec4(a_position,0.0,1.0);}';
|
|
582
|
+
|
|
583
|
+
// Faceted-style fragment shader (works for all systems as preview)
|
|
584
|
+
const fsSource = \`precision highp float;
|
|
585
|
+
uniform float u_time;uniform vec2 u_resolution;uniform float u_geometry;
|
|
586
|
+
uniform float u_rot4dXY;uniform float u_rot4dXZ;uniform float u_rot4dYZ;
|
|
587
|
+
uniform float u_rot4dXW;uniform float u_rot4dYW;uniform float u_rot4dZW;
|
|
588
|
+
uniform float u_dimension;uniform float u_gridDensity;uniform float u_morphFactor;
|
|
589
|
+
uniform float u_chaos;uniform float u_hue;uniform float u_intensity;
|
|
590
|
+
uniform float u_saturation;uniform float u_speed;
|
|
591
|
+
|
|
592
|
+
mat4 rotXY(float a){float c=cos(a),s=sin(a);return mat4(c,-s,0,0,s,c,0,0,0,0,1,0,0,0,0,1);}
|
|
593
|
+
mat4 rotXW(float a){float c=cos(a),s=sin(a);return mat4(c,0,0,-s,0,1,0,0,0,0,1,0,s,0,0,c);}
|
|
594
|
+
mat4 rotYW(float a){float c=cos(a),s=sin(a);return mat4(1,0,0,0,0,c,0,-s,0,0,1,0,0,s,0,c);}
|
|
595
|
+
mat4 rotZW(float a){float c=cos(a),s=sin(a);return mat4(1,0,0,0,0,1,0,0,0,0,c,-s,0,0,s,c);}
|
|
596
|
+
|
|
597
|
+
vec3 hsl2rgb(float h,float s,float l){
|
|
598
|
+
float c=(1.0-abs(2.0*l-1.0))*s;float hp=h*6.0;
|
|
599
|
+
float x=c*(1.0-abs(mod(hp,2.0)-1.0));float m=l-c*0.5;vec3 rgb;
|
|
600
|
+
if(hp<1.0)rgb=vec3(c,x,0);else if(hp<2.0)rgb=vec3(x,c,0);
|
|
601
|
+
else if(hp<3.0)rgb=vec3(0,c,x);else if(hp<4.0)rgb=vec3(0,x,c);
|
|
602
|
+
else if(hp<5.0)rgb=vec3(x,0,c);else rgb=vec3(c,0,x);
|
|
603
|
+
return rgb+m;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
void main(){
|
|
607
|
+
vec2 uv=(gl_FragCoord.xy-u_resolution*0.5)/min(u_resolution.x,u_resolution.y);
|
|
608
|
+
float t=u_time*0.001*u_speed;
|
|
609
|
+
vec4 p=vec4(uv*3.0,sin(t),cos(t*0.7));
|
|
610
|
+
p=rotXY(u_rot4dXY)*p;p=rotXW(u_rot4dXW)*p;p=rotYW(u_rot4dYW)*p;p=rotZW(u_rot4dZW)*p;
|
|
611
|
+
float w=1.0/(u_dimension-p.w);vec3 proj=p.xyz*w;
|
|
612
|
+
float gs=u_gridDensity*0.1;vec3 g=fract(proj*gs);
|
|
613
|
+
float base=mod(u_geometry,8.0);float core=floor(u_geometry/8.0);
|
|
614
|
+
float pattern=0.0;
|
|
615
|
+
if(base<1.0){vec3 q=g-0.5;pattern=1.0-smoothstep(0.0,0.15,length(q));}
|
|
616
|
+
else if(base<2.0){vec3 e=min(g,1.0-g);pattern=1.0-smoothstep(0.0,0.04,min(min(e.x,e.y),e.z));}
|
|
617
|
+
else if(base<3.0){pattern=1.0-smoothstep(0.1,0.3,length(g-0.5));}
|
|
618
|
+
else if(base<4.0){float r=length(g.xy-0.5);pattern=1.0-smoothstep(0.0,0.05,abs(r-0.3));}
|
|
619
|
+
else if(base<5.0){float u2=atan(g.y-0.5,g.x-0.5);pattern=sin(u2*4.0+t)*0.5+0.5;}
|
|
620
|
+
else if(base<6.0){vec3 c2=abs(g*2.0-1.0);float d=length(max(c2-0.3,0.0));for(int i=0;i<3;i++){c2=abs(c2*2.0-1.0);d=min(d,length(max(c2-0.3,0.0))/pow(2.0,float(i+1)));}pattern=1.0-smoothstep(0.0,0.05,d);}
|
|
621
|
+
else if(base<7.0){pattern=(sin(proj.x*gs*2.0+t*2.0)+sin(proj.y*gs*1.8+t*1.5))*0.25+0.5;}
|
|
622
|
+
else{vec3 c2=g-0.5;float oct=max(max(abs(c2.x)+abs(c2.y),abs(c2.y)+abs(c2.z)),abs(c2.x)+abs(c2.z));pattern=1.0-smoothstep(0.3,0.4,oct);}
|
|
623
|
+
|
|
624
|
+
if(core>0.5){pattern*=(1.0+sin(length(proj)*3.0+t)*0.3);}
|
|
625
|
+
pattern*=u_morphFactor;
|
|
626
|
+
pattern+=sin(proj.x*7.0)*cos(proj.y*11.0)*u_chaos*0.3;
|
|
627
|
+
float pi2=pow(clamp(pattern,0.0,1.0),1.5)*u_intensity;
|
|
628
|
+
vec3 col=hsl2rgb(u_hue,u_saturation,0.3+pi2*0.5);
|
|
629
|
+
gl_FragColor=vec4(col*pi2*2.0,1.0);
|
|
630
|
+
}\`;
|
|
631
|
+
|
|
632
|
+
// Compile shaders
|
|
633
|
+
function createShader(type, source) {
|
|
634
|
+
const shader = gl.createShader(type);
|
|
635
|
+
gl.shaderSource(shader, source);
|
|
636
|
+
gl.compileShader(shader);
|
|
637
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
638
|
+
console.error('Shader error:', gl.getShaderInfoLog(shader));
|
|
639
|
+
gl.deleteShader(shader);
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
return shader;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const vs = createShader(gl.VERTEX_SHADER, vsSource);
|
|
646
|
+
const fs = createShader(gl.FRAGMENT_SHADER, fsSource);
|
|
647
|
+
if (!vs || !fs) return null;
|
|
648
|
+
|
|
649
|
+
const prog = gl.createProgram();
|
|
650
|
+
gl.attachShader(prog, vs);
|
|
651
|
+
gl.attachShader(prog, fs);
|
|
652
|
+
gl.linkProgram(prog);
|
|
653
|
+
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
|
|
654
|
+
console.error('Program link error');
|
|
655
|
+
return null;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// Fullscreen quad
|
|
659
|
+
const buf = gl.createBuffer();
|
|
660
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
|
661
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1,1,-1,-1,1,1,1]), gl.STATIC_DRAW);
|
|
662
|
+
const posLoc = gl.getAttribLocation(prog, 'a_position');
|
|
663
|
+
gl.enableVertexAttribArray(posLoc);
|
|
664
|
+
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
|
|
665
|
+
|
|
666
|
+
gl.useProgram(prog);
|
|
667
|
+
|
|
668
|
+
// Set uniforms
|
|
669
|
+
gl.uniform2f(gl.getUniformLocation(prog, 'u_resolution'), width, height);
|
|
670
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_time'), performance.now());
|
|
671
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_geometry'), state.geometry);
|
|
672
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_gridDensity'), state.gridDensity);
|
|
673
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_morphFactor'), state.morphFactor);
|
|
674
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_chaos'), state.chaos);
|
|
675
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_speed'), state.speed);
|
|
676
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_hue'), state.hue / 360.0);
|
|
677
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_intensity'), state.intensity);
|
|
678
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_saturation'), state.saturation);
|
|
679
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_dimension'), state.dimension);
|
|
680
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_rot4dXY'), state.rot4dXY);
|
|
681
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_rot4dXZ'), state.rot4dXZ);
|
|
682
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_rot4dYZ'), state.rot4dYZ);
|
|
683
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_rot4dXW'), state.rot4dXW);
|
|
684
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_rot4dYW'), state.rot4dYW);
|
|
685
|
+
gl.uniform1f(gl.getUniformLocation(prog, 'u_rot4dZW'), state.rot4dZW);
|
|
686
|
+
|
|
687
|
+
gl.viewport(0, 0, width, height);
|
|
688
|
+
gl.clearColor(0, 0, 0, 1);
|
|
689
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
690
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
691
|
+
|
|
692
|
+
// Read pixels
|
|
693
|
+
const pixels = new Uint8Array(width * height * 4);
|
|
694
|
+
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
|
|
695
|
+
|
|
696
|
+
// Cleanup
|
|
697
|
+
gl.deleteProgram(prog);
|
|
698
|
+
gl.deleteShader(vs);
|
|
699
|
+
gl.deleteShader(fs);
|
|
700
|
+
gl.deleteBuffer(buf);
|
|
701
|
+
|
|
702
|
+
return pixels;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// ---- Preview Loop ----
|
|
706
|
+
const previewCanvas = document.getElementById('preview-canvas');
|
|
707
|
+
let animFrame;
|
|
708
|
+
function previewLoop() {
|
|
709
|
+
renderToCanvas(previewCanvas, previewCanvas.width, previewCanvas.height);
|
|
710
|
+
animFrame = requestAnimationFrame(previewLoop);
|
|
711
|
+
}
|
|
712
|
+
previewLoop();
|
|
713
|
+
|
|
714
|
+
// ---- Status ----
|
|
715
|
+
function setStatus(msg) {
|
|
716
|
+
document.getElementById('status').textContent = msg;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// ---- Actions ----
|
|
720
|
+
document.getElementById('btn-create').addEventListener('click', () => {
|
|
721
|
+
setStatus('Rendering...');
|
|
722
|
+
const w = parseInt(document.getElementById('render-width').value) || 1024;
|
|
723
|
+
const h = parseInt(document.getElementById('render-height').value) || 1024;
|
|
724
|
+
const offscreen = document.createElement('canvas');
|
|
725
|
+
const pixels = renderToCanvas(offscreen, w, h);
|
|
726
|
+
if (pixels) {
|
|
727
|
+
parent.postMessage({
|
|
728
|
+
pluginMessage: {
|
|
729
|
+
type: 'render-complete',
|
|
730
|
+
imageData: Array.from(pixels),
|
|
731
|
+
width: w, height: h,
|
|
732
|
+
name: 'VIB3+ ' + state.system + ' G' + state.geometry
|
|
733
|
+
}
|
|
734
|
+
}, '*');
|
|
735
|
+
setStatus('Sent to Figma!');
|
|
736
|
+
} else {
|
|
737
|
+
setStatus('Render failed.');
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
document.getElementById('btn-apply').addEventListener('click', () => {
|
|
742
|
+
setStatus('Rendering for fill...');
|
|
743
|
+
const w = parseInt(document.getElementById('render-width').value) || 1024;
|
|
744
|
+
const h = parseInt(document.getElementById('render-height').value) || 1024;
|
|
745
|
+
const offscreen = document.createElement('canvas');
|
|
746
|
+
const pixels = renderToCanvas(offscreen, w, h);
|
|
747
|
+
if (pixels) {
|
|
748
|
+
parent.postMessage({
|
|
749
|
+
pluginMessage: {
|
|
750
|
+
type: 'apply-fill',
|
|
751
|
+
imageData: Array.from(pixels),
|
|
752
|
+
width: w, height: h
|
|
753
|
+
}
|
|
754
|
+
}, '*');
|
|
755
|
+
setStatus('Fill applied!');
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
document.getElementById('btn-batch').addEventListener('click', () => {
|
|
760
|
+
setStatus('Batch rendering 24 variants...');
|
|
761
|
+
const size = 512;
|
|
762
|
+
const offscreen = document.createElement('canvas');
|
|
763
|
+
const renders = [];
|
|
764
|
+
const baseNames = ['Tetrahedron','Hypercube','Sphere','Torus','Klein','Fractal','Wave','Crystal'];
|
|
765
|
+
const coreNames = ['Base','Hyper-S','Hyper-T'];
|
|
766
|
+
const origGeom = state.geometry;
|
|
767
|
+
|
|
768
|
+
for (let i = 0; i < 24; i++) {
|
|
769
|
+
state.geometry = i;
|
|
770
|
+
const pixels = renderToCanvas(offscreen, size, size);
|
|
771
|
+
if (pixels) {
|
|
772
|
+
renders.push({
|
|
773
|
+
imageData: Array.from(pixels),
|
|
774
|
+
name: coreNames[Math.floor(i / 8)] + ' ' + baseNames[i % 8]
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
state.geometry = origGeom;
|
|
779
|
+
|
|
780
|
+
if (renders.length > 0) {
|
|
781
|
+
parent.postMessage({
|
|
782
|
+
pluginMessage: {
|
|
783
|
+
type: 'batch-render',
|
|
784
|
+
renders: renders,
|
|
785
|
+
columns: 8,
|
|
786
|
+
cellSize: size,
|
|
787
|
+
name: 'VIB3+ ' + state.system + ' All Geometries'
|
|
788
|
+
}
|
|
789
|
+
}, '*');
|
|
790
|
+
setStatus('Batch sent! ' + renders.length + ' variants.');
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
// ---- Messages from plugin code ----
|
|
795
|
+
window.onmessage = (event) => {
|
|
796
|
+
const msg = event.data.pluginMessage;
|
|
797
|
+
if (!msg) return;
|
|
798
|
+
if (msg.type === 'error') setStatus('Error: ' + msg.message);
|
|
799
|
+
if (msg.type === 'render-inserted') setStatus('Visualization created!');
|
|
800
|
+
if (msg.type === 'batch-complete') setStatus('Batch complete: ' + msg.count + ' items.');
|
|
801
|
+
if (msg.type === 'fill-applied') setStatus('Fill applied to ' + msg.count + ' shape(s).');
|
|
802
|
+
};
|
|
803
|
+
})();
|
|
804
|
+
<\/script>
|
|
805
|
+
</body>
|
|
806
|
+
</html>`;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* Generates a complete Figma plugin package ready for deployment.
|
|
811
|
+
*
|
|
812
|
+
* @param {object} [options={}] - Package customization options
|
|
813
|
+
* @returns {Object.<string, string|object>} Map of file path to file content
|
|
814
|
+
* @example
|
|
815
|
+
* const pkg = Vib3FigmaPlugin.generatePackage();
|
|
816
|
+
* // Write manifest.json, code.js, and ui.html to your plugin directory
|
|
817
|
+
*/
|
|
818
|
+
static generatePackage(options = {}) {
|
|
819
|
+
return {
|
|
820
|
+
'manifest.json': Vib3FigmaPlugin.generatePluginManifest(options),
|
|
821
|
+
'code.js': Vib3FigmaPlugin.generatePluginCode(options),
|
|
822
|
+
'ui.html': Vib3FigmaPlugin.generatePluginUI(options)
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Returns the default render configuration.
|
|
828
|
+
* @returns {Object} Default width, height, pixelRatio, format
|
|
829
|
+
*/
|
|
830
|
+
static getDefaultRenderConfig() {
|
|
831
|
+
return { ...DEFAULT_RENDER_CONFIG };
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Returns available system names.
|
|
836
|
+
* @returns {string[]} Array of system names
|
|
837
|
+
*/
|
|
838
|
+
static getAvailableSystems() {
|
|
839
|
+
return [...AVAILABLE_SYSTEMS];
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Returns geometry name for a given index.
|
|
844
|
+
* @param {number} index - Geometry index (0-23)
|
|
845
|
+
* @returns {string} Human-readable geometry name
|
|
846
|
+
*/
|
|
847
|
+
static getGeometryName(index) {
|
|
848
|
+
const coreIndex = Math.floor(index / 8);
|
|
849
|
+
const baseIndex = index % 8;
|
|
850
|
+
const coreName = CORE_TYPES[coreIndex] || 'Unknown';
|
|
851
|
+
const baseName = BASE_GEOMETRIES[baseIndex] || 'Unknown';
|
|
852
|
+
return coreIndex === 0 ? baseName : `${coreName} (${baseName})`;
|
|
853
|
+
}
|
|
854
|
+
}
|