@viji-dev/sdk 1.0.0 → 1.0.2
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/README.md +70 -63
- package/bin/viji.js +9 -29
- package/dist/assets/artist-dts-BHUsvSI6.js +613 -0
- package/dist/assets/artist-dts-p5-Cyw8vmy_.js +736 -0
- package/dist/assets/core-CiQx3w0t.js +12 -0
- package/dist/assets/dark-plus-C3mMm8J8.js +1 -0
- package/dist/assets/docs-api-PBLtY4Ni.js +12381 -0
- package/dist/assets/engine-javascript-CXyY7cc8.js +141 -0
- package/dist/assets/essentia-wasm.web-0S-sW98u-CYV1l1zv.js +38 -0
- package/dist/assets/essentia.js-core.es-DnrJE0uR-DOSrF5_G.js +32 -0
- package/dist/assets/glsl-DMyvO4G4.js +1 -0
- package/dist/assets/index-BhFxsauQ.js +215 -0
- package/dist/assets/index-BqhVeA7U.css +1 -0
- package/dist/assets/index-T4TOjvD0.js +1 -0
- package/dist/assets/index-Wz9WqGqz.js +52 -0
- package/dist/assets/index-t24aGwla.js +1 -0
- package/dist/assets/javascript-wDzz0qaB.js +1 -0
- package/dist/assets/shader-uniforms-GdaUkQPK.js +1 -0
- package/dist/assets/typescript-BPQ3VLAy.js +1 -0
- package/dist/assets/viji.worker-CQSJ0SiO-ljtBlcNZ.js +27018 -0
- package/{index.html → dist/index.html} +2 -1
- package/package.json +31 -35
- package/src/cli/commands/build.js +50 -99
- package/src/cli/commands/create.js +49 -46
- package/src/cli/commands/dev.js +30 -97
- package/src/cli/server/dev-server.js +233 -0
- package/src/cli/server/scene-scanner.js +93 -0
- package/src/cli/server/vite-scene-plugin.d.ts +2 -0
- package/src/cli/server/vite-scene-plugin.js +134 -0
- package/src/cli/utils/cli-utils.js +29 -139
- package/src/cli/utils/scene-compiler.js +10 -17
- package/src/templates/scene-templates.js +85 -0
- package/.gitignore +0 -29
- package/eslint.config.js +0 -37
- package/postcss.config.js +0 -6
- package/scenes/audio-visualizer/main.js +0 -287
- package/scenes/core-demo/main.js +0 -532
- package/scenes/demo-scene/main.js +0 -619
- package/scenes/global.d.ts +0 -15
- package/scenes/particle-system/main.js +0 -349
- package/scenes/tsconfig.json +0 -12
- package/scenes/video-mirror/main.ts +0 -436
- package/src/App.css +0 -42
- package/src/App.tsx +0 -279
- package/src/cli/commands/init.js +0 -262
- package/src/components/SDKPage.tsx +0 -337
- package/src/components/core/CoreContainer.tsx +0 -126
- package/src/components/ui/DeviceSelectionList.tsx +0 -137
- package/src/components/ui/FPSCounter.tsx +0 -78
- package/src/components/ui/FileDropzonePanel.tsx +0 -120
- package/src/components/ui/FileListPanel.tsx +0 -285
- package/src/components/ui/InputExpansionPanel.tsx +0 -31
- package/src/components/ui/MediaPlayerControls.tsx +0 -191
- package/src/components/ui/MenuContainer.tsx +0 -71
- package/src/components/ui/ParametersMenu.tsx +0 -797
- package/src/components/ui/ProjectSwitcherMenu.tsx +0 -192
- package/src/components/ui/QuickInputControls.tsx +0 -542
- package/src/components/ui/SDKMenuSystem.tsx +0 -96
- package/src/components/ui/SettingsMenu.tsx +0 -346
- package/src/components/ui/SimpleInputControls.tsx +0 -137
- package/src/index.css +0 -68
- package/src/main.tsx +0 -10
- package/src/scenes-hmr.ts +0 -158
- package/src/services/project-filesystem.ts +0 -436
- package/src/stores/scene-player/index.ts +0 -3
- package/src/stores/scene-player/input-manager.store.ts +0 -1045
- package/src/stores/scene-player/scene-session.store.ts +0 -659
- package/src/styles/globals.css +0 -111
- package/src/templates/minimal-template.js +0 -11
- package/src/utils/debounce.js +0 -34
- package/src/vite-env.d.ts +0 -1
- package/tailwind.config.js +0 -18
- package/tsconfig.app.json +0 -27
- package/tsconfig.json +0 -27
- package/tsconfig.node.json +0 -27
- package/vite.config.ts +0 -54
- /package/{public → dist}/favicon.png +0 -0
package/scenes/core-demo/main.js
DELETED
|
@@ -1,532 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
// Scene Code - Parameter Object API
|
|
3
|
-
console.log('🎯 CORE DEMO SCENE LOADED');
|
|
4
|
-
|
|
5
|
-
// Define parameters using helper functions (returns parameter objects)
|
|
6
|
-
const primaryColor = viji.color('#ff6b6b', {
|
|
7
|
-
label: 'Primary Color',
|
|
8
|
-
description: 'Main color for the animated shapes',
|
|
9
|
-
group: 'colors'
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
const secondaryColor = viji.color('#4ecdc4', {
|
|
13
|
-
label: 'Secondary Color',
|
|
14
|
-
description: 'Secondary color for gradient effects',
|
|
15
|
-
group: 'colors'
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
const shapeSize = viji.slider(50, {
|
|
19
|
-
min: 10,
|
|
20
|
-
max: 150,
|
|
21
|
-
step: 5,
|
|
22
|
-
label: 'Shape Size',
|
|
23
|
-
description: 'Size of the animated shapes',
|
|
24
|
-
group: 'shape'
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
const animationSpeed = viji.slider(1.0, {
|
|
28
|
-
min: 0.1,
|
|
29
|
-
max: 3.0,
|
|
30
|
-
step: 0.1,
|
|
31
|
-
label: 'Animation Speed',
|
|
32
|
-
description: 'Speed of the animation loop',
|
|
33
|
-
group: 'speed'
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
const useGradient = viji.toggle(true, {
|
|
37
|
-
label: 'Use Gradient',
|
|
38
|
-
description: 'Enable gradient fill for shapes',
|
|
39
|
-
group: 'colors'
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const shapeType = viji.select('circle', {
|
|
43
|
-
options: ['circle', 'square', 'triangle'],
|
|
44
|
-
label: 'Shape Type',
|
|
45
|
-
description: 'Type of shape to render',
|
|
46
|
-
group: 'shape'
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const particleCount = viji.number(5, {
|
|
50
|
-
min: 1,
|
|
51
|
-
max: 20,
|
|
52
|
-
step: 1,
|
|
53
|
-
label: 'Particle Count',
|
|
54
|
-
description: 'Number of particles to render',
|
|
55
|
-
group: 'shape'
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const sceneTitle = viji.text('Viji Core Demo', {
|
|
59
|
-
label: 'Scene Title',
|
|
60
|
-
description: 'Title displayed in the scene',
|
|
61
|
-
group: 'text',
|
|
62
|
-
maxLength: 50
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
const rotationSpeed = viji.slider(0, {
|
|
66
|
-
min: -5,
|
|
67
|
-
max: 5,
|
|
68
|
-
step: 0.1,
|
|
69
|
-
label: 'Rotation Speed',
|
|
70
|
-
description: 'Rotation speed for shapes',
|
|
71
|
-
group: 'speed'
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const opacity = viji.slider(0.8, {
|
|
75
|
-
min: 0,
|
|
76
|
-
max: 1,
|
|
77
|
-
step: 0.05,
|
|
78
|
-
label: 'Opacity',
|
|
79
|
-
description: 'Overall opacity of the scene',
|
|
80
|
-
group: 'colors'
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
const enableTrails = viji.toggle(false, {
|
|
84
|
-
label: 'Enable Trails',
|
|
85
|
-
description: 'Show trails behind moving shapes'
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// Interaction Parameters
|
|
89
|
-
const mouseAttraction = viji.toggle(true, {
|
|
90
|
-
label: 'Mouse Attraction',
|
|
91
|
-
description: 'Particles are attracted to mouse cursor',
|
|
92
|
-
group: 'interaction',
|
|
93
|
-
category: 'interaction'
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
const attractionStrength = viji.slider(0.3, {
|
|
97
|
-
min: 0,
|
|
98
|
-
max: 1,
|
|
99
|
-
step: 0.1,
|
|
100
|
-
label: 'Attraction Strength',
|
|
101
|
-
description: 'How strongly particles are attracted to mouse',
|
|
102
|
-
group: 'interaction',
|
|
103
|
-
category: 'interaction'
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
const keyboardControl = viji.toggle(true, {
|
|
107
|
-
label: 'Keyboard Control',
|
|
108
|
-
description: 'Use WASD keys to move particles',
|
|
109
|
-
group: 'interaction',
|
|
110
|
-
category: 'interaction'
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
const touchScale = viji.slider(1.0, {
|
|
114
|
-
min: 0.5,
|
|
115
|
-
max: 2.0,
|
|
116
|
-
step: 0.1,
|
|
117
|
-
label: 'Touch Scale Factor',
|
|
118
|
-
description: 'Scale factor for touch interactions',
|
|
119
|
-
group: 'interaction',
|
|
120
|
-
category: 'interaction'
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
const showInteractionInfo = viji.toggle(true, {
|
|
124
|
-
label: 'Show Interaction Info',
|
|
125
|
-
description: 'Display current interaction state',
|
|
126
|
-
group: 'interaction',
|
|
127
|
-
category: 'interaction'
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
// Audio Parameters
|
|
131
|
-
const audioReactive = viji.toggle(false, {
|
|
132
|
-
label: 'Audio Reactive',
|
|
133
|
-
description: 'Make particles react to audio input',
|
|
134
|
-
group: 'audio',
|
|
135
|
-
category: 'audio'
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
const volumeSensitivity = viji.slider(1.0, {
|
|
139
|
-
min: 0.1,
|
|
140
|
-
max: 5.0,
|
|
141
|
-
step: 0.1,
|
|
142
|
-
label: 'Volume Sensitivity',
|
|
143
|
-
description: 'How sensitive particles are to volume changes',
|
|
144
|
-
group: 'audio',
|
|
145
|
-
category: 'audio'
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
const bassReactivity = viji.slider(1.0, {
|
|
149
|
-
min: 0,
|
|
150
|
-
max: 3.0,
|
|
151
|
-
step: 0.1,
|
|
152
|
-
label: 'Bass Reactivity',
|
|
153
|
-
description: 'How much particles react to bass frequencies',
|
|
154
|
-
group: 'audio',
|
|
155
|
-
category: 'audio'
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
const trebleReactivity = viji.slider(0.5, {
|
|
159
|
-
min: 0,
|
|
160
|
-
max: 2.0,
|
|
161
|
-
step: 0.1,
|
|
162
|
-
label: 'Treble Reactivity',
|
|
163
|
-
description: 'How much particles react to treble frequencies',
|
|
164
|
-
group: 'audio',
|
|
165
|
-
category: 'audio'
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
const showAudioViz = viji.toggle(true, {
|
|
169
|
-
label: 'Show Audio Visualization',
|
|
170
|
-
description: 'Display real-time audio analysis on canvas',
|
|
171
|
-
group: 'audio',
|
|
172
|
-
category: 'audio'
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
// Video Parameters
|
|
176
|
-
const videoReactive = viji.toggle(false, {
|
|
177
|
-
label: 'Video Reactive',
|
|
178
|
-
description: 'Make particles react to video input',
|
|
179
|
-
group: 'video',
|
|
180
|
-
category: 'video'
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
const motionSensitivity = viji.slider(1.0, {
|
|
184
|
-
min: 0.1,
|
|
185
|
-
max: 3.0,
|
|
186
|
-
step: 0.1,
|
|
187
|
-
label: 'Motion Sensitivity',
|
|
188
|
-
description: 'How sensitive particles are to video motion',
|
|
189
|
-
group: 'video',
|
|
190
|
-
category: 'video'
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
const brightnessReactivity = viji.slider(0.5, {
|
|
194
|
-
min: 0,
|
|
195
|
-
max: 2.0,
|
|
196
|
-
step: 0.1,
|
|
197
|
-
label: 'Brightness Reactivity',
|
|
198
|
-
description: 'How much particles react to video brightness',
|
|
199
|
-
group: 'video',
|
|
200
|
-
category: 'video'
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
const showVideoOverlay = viji.toggle(true, {
|
|
204
|
-
label: 'Show Video Overlay',
|
|
205
|
-
description: 'Display current video frame on canvas',
|
|
206
|
-
group: 'video',
|
|
207
|
-
category: 'video'
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
// Render function using parameter object API with interactions, audio, and video
|
|
211
|
-
function render(viji) {
|
|
212
|
-
const ctx = viji.useContext('2d');
|
|
213
|
-
|
|
214
|
-
// Get interaction state
|
|
215
|
-
const mouse = viji.mouse;
|
|
216
|
-
const keyboard = viji.keyboard;
|
|
217
|
-
const touch = viji.touches;
|
|
218
|
-
|
|
219
|
-
// Get audio state
|
|
220
|
-
const audio = viji.audio;
|
|
221
|
-
|
|
222
|
-
// Get video state
|
|
223
|
-
const video = viji.video;
|
|
224
|
-
|
|
225
|
-
// Clear canvas with trails effect if enabled
|
|
226
|
-
if (enableTrails.value) {
|
|
227
|
-
ctx.fillStyle = 'rgba(0, 0, 0, ' + (0.1 * opacity.value) + ')';
|
|
228
|
-
ctx.fillRect(0, 0, viji.width, viji.height);
|
|
229
|
-
} else {
|
|
230
|
-
ctx.fillStyle = '#2c3e35';
|
|
231
|
-
ctx.fillRect(0, 0, viji.width, viji.height);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Draw title using parameter object
|
|
235
|
-
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
|
|
236
|
-
ctx.font = '20px Arial';
|
|
237
|
-
ctx.textAlign = 'center';
|
|
238
|
-
ctx.fillText(sceneTitle.value, viji.width / 2, 30);
|
|
239
|
-
|
|
240
|
-
// Keyboard offset for particle center
|
|
241
|
-
let keyboardOffsetX = 0;
|
|
242
|
-
let keyboardOffsetY = 0;
|
|
243
|
-
|
|
244
|
-
if (keyboardControl.value) {
|
|
245
|
-
const keySpeed = 2;
|
|
246
|
-
if (keyboard.isPressed('w') || keyboard.isPressed('W')) keyboardOffsetY -= keySpeed;
|
|
247
|
-
if (keyboard.isPressed('s') || keyboard.isPressed('S')) keyboardOffsetY += keySpeed;
|
|
248
|
-
if (keyboard.isPressed('a') || keyboard.isPressed('A')) keyboardOffsetX -= keySpeed;
|
|
249
|
-
if (keyboard.isPressed('d') || keyboard.isPressed('D')) keyboardOffsetX += keySpeed;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Animation calculations using parameter objects
|
|
253
|
-
let centerX = viji.width / 2 + keyboardOffsetX;
|
|
254
|
-
let centerY = viji.height / 2 + keyboardOffsetY;
|
|
255
|
-
const time = viji.time * animationSpeed.value;
|
|
256
|
-
|
|
257
|
-
// Audio reactive calculations
|
|
258
|
-
let audioScale = 1;
|
|
259
|
-
let audioColorShift = 0;
|
|
260
|
-
if (audioReactive.value && audio.isConnected) {
|
|
261
|
-
// Scale based on volume (RMS)
|
|
262
|
-
audioScale = 1 + (audio.volume.rms * volumeSensitivity.value);
|
|
263
|
-
|
|
264
|
-
// Color shift based on bass vs treble
|
|
265
|
-
const bassEnergy = (audio.bands.bass + audio.bands.subBass) / 2;
|
|
266
|
-
const trebleEnergy = (audio.bands.treble + audio.bands.presence) / 2;
|
|
267
|
-
audioColorShift = (bassEnergy * bassReactivity.value) - (trebleEnergy * trebleReactivity.value);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Video reactive calculations
|
|
271
|
-
let videoScale = 1;
|
|
272
|
-
let videoBrightness = 0;
|
|
273
|
-
if (videoReactive.value && video.isConnected) {
|
|
274
|
-
// Simple motion detection (placeholder - would use actual frame analysis)
|
|
275
|
-
const motionEnergy = Math.sin(time * 2) * 0.5 + 0.5; // Simulate motion
|
|
276
|
-
videoScale = 1 + (motionEnergy * motionSensitivity.value * 0.3);
|
|
277
|
-
|
|
278
|
-
// Brightness-based effects
|
|
279
|
-
const brightness = Math.sin(time * 1.5) * 0.5 + 0.5; // Simulate brightness
|
|
280
|
-
videoBrightness = brightness * brightnessReactivity.value;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Touch interaction - use primary touch for center offset
|
|
284
|
-
if (touch.points.length > 0) {
|
|
285
|
-
const primaryTouch = touch.points[0];
|
|
286
|
-
centerX = primaryTouch.x * touchScale.value;
|
|
287
|
-
centerY = primaryTouch.y * touchScale.value;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Draw particles
|
|
291
|
-
for (let i = 0; i < particleCount.value; i++) {
|
|
292
|
-
const angle = (i / particleCount.value) * Math.PI * 2 + time;
|
|
293
|
-
const radius = Math.min(viji.width, viji.height) * 0.3;
|
|
294
|
-
|
|
295
|
-
let x = centerX + Math.cos(angle) * radius;
|
|
296
|
-
let y = centerY + Math.sin(angle) * radius;
|
|
297
|
-
|
|
298
|
-
// Mouse attraction
|
|
299
|
-
if (mouseAttraction.value && mouse.isInCanvas) {
|
|
300
|
-
const mouseX = mouse.x;
|
|
301
|
-
const mouseY = mouse.y;
|
|
302
|
-
const distToMouse = Math.sqrt((x - mouseX) ** 2 + (y - mouseY) ** 2);
|
|
303
|
-
|
|
304
|
-
if (distToMouse > 0) {
|
|
305
|
-
const attraction = attractionStrength.value * (1 / (distToMouse * 0.01 + 1));
|
|
306
|
-
x += (mouseX - x) * attraction;
|
|
307
|
-
y += (mouseY - y) * attraction;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// Apply rotation using parameter object
|
|
312
|
-
const rotation = time * rotationSpeed.value;
|
|
313
|
-
|
|
314
|
-
ctx.save();
|
|
315
|
-
ctx.translate(x, y);
|
|
316
|
-
ctx.rotate(rotation);
|
|
317
|
-
|
|
318
|
-
// Set opacity using parameter object
|
|
319
|
-
ctx.globalAlpha = opacity.value;
|
|
320
|
-
|
|
321
|
-
let scaledSize = shapeSize.value * Math.min(viji.width, viji.height) / 1000;
|
|
322
|
-
|
|
323
|
-
// Apply audio scaling
|
|
324
|
-
if (audioReactive.value && audio.isConnected) {
|
|
325
|
-
scaledSize *= audioScale;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Apply video scaling
|
|
329
|
-
if (videoReactive.value && video.isConnected) {
|
|
330
|
-
scaledSize *= videoScale;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Create gradient or solid fill using parameter objects
|
|
334
|
-
if (useGradient.value) {
|
|
335
|
-
const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, scaledSize / 2);
|
|
336
|
-
gradient.addColorStop(0, primaryColor.value);
|
|
337
|
-
gradient.addColorStop(1, secondaryColor.value);
|
|
338
|
-
ctx.fillStyle = gradient;
|
|
339
|
-
} else {
|
|
340
|
-
ctx.fillStyle = primaryColor.value;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// Draw shape based on type using parameter object
|
|
344
|
-
ctx.beginPath();
|
|
345
|
-
switch (shapeType.value) {
|
|
346
|
-
case 'circle':
|
|
347
|
-
ctx.arc(0, 0, scaledSize / 2, 0, Math.PI * 2);
|
|
348
|
-
break;
|
|
349
|
-
case 'square':
|
|
350
|
-
const half = scaledSize / 2;
|
|
351
|
-
ctx.rect(-half, -half, scaledSize, scaledSize);
|
|
352
|
-
break;
|
|
353
|
-
case 'triangle':
|
|
354
|
-
const size = scaledSize;
|
|
355
|
-
ctx.moveTo(0, -size / 2);
|
|
356
|
-
ctx.lineTo(-size / 2, size / 2);
|
|
357
|
-
ctx.lineTo(size / 2, size / 2);
|
|
358
|
-
ctx.closePath();
|
|
359
|
-
break;
|
|
360
|
-
}
|
|
361
|
-
ctx.fill();
|
|
362
|
-
|
|
363
|
-
ctx.restore();
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Draw debug info showing parameter object properties
|
|
367
|
-
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
|
|
368
|
-
ctx.font = '12px monospace';
|
|
369
|
-
ctx.textAlign = 'left';
|
|
370
|
-
ctx.fillText('Frame: ' + viji.frameCount, 10, viji.height - 100);
|
|
371
|
-
ctx.fillText('Time: ' + viji.time.toFixed(2) + 's', 10, viji.height - 85);
|
|
372
|
-
ctx.fillText('FPS: ' + viji.fps, 10, viji.height - 70);
|
|
373
|
-
ctx.fillText('Shape Size: ' + shapeSize.value + ' (min: ' + shapeSize.min + ', max: ' + shapeSize.max + ')', 10, viji.height - 55);
|
|
374
|
-
ctx.fillText('Animation Speed: ' + animationSpeed.value.toFixed(1), 10, viji.height - 40);
|
|
375
|
-
ctx.fillText('Rotation: ' + rotationSpeed.value.toFixed(1), 10, viji.height - 25);
|
|
376
|
-
ctx.fillText('Particle Count: ' + particleCount.value + ' (max: ' + particleCount.max + ')', 10, viji.height - 10);
|
|
377
|
-
|
|
378
|
-
// Draw interaction info
|
|
379
|
-
if (showInteractionInfo.value) {
|
|
380
|
-
ctx.fillStyle = 'rgba(255, 255, 0, 0.8)';
|
|
381
|
-
ctx.font = '12px monospace';
|
|
382
|
-
ctx.textAlign = 'right';
|
|
383
|
-
|
|
384
|
-
let infoY = viji.height - 120;
|
|
385
|
-
|
|
386
|
-
// Mouse info
|
|
387
|
-
ctx.fillText('INTERACTIONS:', viji.width - 10, infoY);
|
|
388
|
-
infoY += 15;
|
|
389
|
-
ctx.fillText('Mouse: ' + (mouse.isInCanvas ? `(${Math.round(mouse.x)}, ${Math.round(mouse.y)})` : 'outside'), viji.width - 10, infoY);
|
|
390
|
-
infoY += 15;
|
|
391
|
-
ctx.fillText('Mouse Buttons: ' + (mouse.leftButton ? 'L' : '-') + (mouse.rightButton ? 'R' : '-') + (mouse.middleButton ? 'M' : '-'), viji.width - 10, infoY);
|
|
392
|
-
infoY += 15;
|
|
393
|
-
|
|
394
|
-
// Keyboard info
|
|
395
|
-
const activeKeys = Array.from(keyboard.activeKeys);
|
|
396
|
-
ctx.fillText('Keys: ' + (activeKeys.length > 0 ? activeKeys.slice(0, 5).join(', ') : 'none'), viji.width - 10, infoY);
|
|
397
|
-
infoY += 15;
|
|
398
|
-
|
|
399
|
-
// Touch info
|
|
400
|
-
ctx.fillText('Touches: ' + touch.points.length, viji.width - 10, infoY);
|
|
401
|
-
infoY += 15;
|
|
402
|
-
|
|
403
|
-
if (touch.points.length > 0) {
|
|
404
|
-
const touch0 = touch.points[0];
|
|
405
|
-
ctx.fillText('Touch 0: (' + Math.round(touch0.x) + ', ' + Math.round(touch0.y) + ')', viji.width - 10, infoY);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// Draw mouse cursor indicator
|
|
410
|
-
if (mouseAttraction.value && mouse.isInCanvas) {
|
|
411
|
-
ctx.strokeStyle = 'rgba(255, 255, 0, 0.8)';
|
|
412
|
-
ctx.lineWidth = 2;
|
|
413
|
-
ctx.setLineDash([5, 5]);
|
|
414
|
-
ctx.beginPath();
|
|
415
|
-
ctx.arc(mouse.x, mouse.y, Math.min(viji.width, viji.height) / 50, 0, Math.PI * 2);
|
|
416
|
-
ctx.stroke();
|
|
417
|
-
ctx.setLineDash([]);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// Draw audio visualization
|
|
421
|
-
if (showAudioViz.value && audio.isConnected) {
|
|
422
|
-
ctx.fillStyle = 'rgba(0, 255, 255, 0.8)';
|
|
423
|
-
ctx.font = '12px monospace';
|
|
424
|
-
ctx.textAlign = 'left';
|
|
425
|
-
|
|
426
|
-
let audioY = 10;
|
|
427
|
-
|
|
428
|
-
// Audio connection status
|
|
429
|
-
ctx.fillText('AUDIO ANALYSIS:', 10, audioY);
|
|
430
|
-
audioY += 15;
|
|
431
|
-
ctx.fillText('Connected: ' + (audio.isConnected ? 'Yes' : 'No'), 10, audioY);
|
|
432
|
-
audioY += 15;
|
|
433
|
-
|
|
434
|
-
// Volume meters
|
|
435
|
-
ctx.fillText('Volume RMS: ' + (audio.volume.rms * 100).toFixed(1) + '%', 10, audioY);
|
|
436
|
-
audioY += 15;
|
|
437
|
-
ctx.fillText('Volume Peak: ' + (audio.volume.peak * 100).toFixed(1) + '%', 10, audioY);
|
|
438
|
-
audioY += 15;
|
|
439
|
-
|
|
440
|
-
// Frequency bands
|
|
441
|
-
ctx.fillText('Bass: ' + (audio.bands.bass * 100).toFixed(1) + '%', 10, audioY);
|
|
442
|
-
audioY += 15;
|
|
443
|
-
ctx.fillText('Mid: ' + (audio.bands.mid * 100).toFixed(1) + '%', 10, audioY);
|
|
444
|
-
audioY += 15;
|
|
445
|
-
ctx.fillText('Treble: ' + (audio.bands.treble * 100).toFixed(1) + '%', 10, audioY);
|
|
446
|
-
audioY += 15;
|
|
447
|
-
|
|
448
|
-
// Visual frequency bars
|
|
449
|
-
const barWidth = 4;
|
|
450
|
-
const barSpacing = 6;
|
|
451
|
-
const barHeight = 60;
|
|
452
|
-
const barStartX = 10;
|
|
453
|
-
const barStartY = audioY + 10;
|
|
454
|
-
|
|
455
|
-
const bands = ['subBass', 'bass', 'lowMid', 'mid', 'highMid', 'presence', 'brilliance', 'treble'];
|
|
456
|
-
|
|
457
|
-
for (let i = 0; i < bands.length; i++) {
|
|
458
|
-
const bandValue = audio.bands[bands[i]] || 0;
|
|
459
|
-
const x = barStartX + i * barSpacing;
|
|
460
|
-
const height = bandValue * barHeight;
|
|
461
|
-
|
|
462
|
-
// Background bar
|
|
463
|
-
ctx.fillStyle = 'rgba(100, 100, 100, 0.3)';
|
|
464
|
-
ctx.fillRect(x, barStartY, barWidth, barHeight);
|
|
465
|
-
|
|
466
|
-
// Value bar with color gradient
|
|
467
|
-
const hue = (i / bands.length) * 360;
|
|
468
|
-
ctx.fillStyle = `hsla(${hue}, 80%, 60%, 0.8)`;
|
|
469
|
-
ctx.fillRect(x, barStartY + barHeight - height, barWidth, height);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// Band labels
|
|
473
|
-
ctx.fillStyle = 'rgba(0, 255, 255, 0.6)';
|
|
474
|
-
ctx.font = '8px monospace';
|
|
475
|
-
ctx.textAlign = 'center';
|
|
476
|
-
for (let i = 0; i < bands.length; i++) {
|
|
477
|
-
const x = barStartX + i * barSpacing + barWidth / 2;
|
|
478
|
-
ctx.fillText(bands[i].substring(0, 3), x, barStartY + barHeight + 12);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// Draw video overlay and info
|
|
483
|
-
if (showVideoOverlay.value && video.isConnected) {
|
|
484
|
-
// ? ACTUAL VIDEO RENDERING: Draw real video frame
|
|
485
|
-
if (video.currentFrame) {
|
|
486
|
-
ctx.save();
|
|
487
|
-
ctx.globalAlpha = 0.7; // Semi-transparent overlay
|
|
488
|
-
|
|
489
|
-
// Calculate video display size (maintain aspect ratio)
|
|
490
|
-
const videoWidth = video.frameWidth;
|
|
491
|
-
const videoHeight = video.frameHeight;
|
|
492
|
-
const displayWidth = 200;
|
|
493
|
-
const displayHeight = (videoHeight / videoWidth) * displayWidth;
|
|
494
|
-
|
|
495
|
-
// Draw the actual video frame from OffscreenCanvas
|
|
496
|
-
try {
|
|
497
|
-
ctx.drawImage(
|
|
498
|
-
video.currentFrame, // OffscreenCanvas from worker
|
|
499
|
-
0, 0, videoWidth, videoHeight, // Source rectangle
|
|
500
|
-
viji.width - displayWidth - 10, 10, displayWidth, displayHeight // Destination rectangle
|
|
501
|
-
);
|
|
502
|
-
} catch (error) {
|
|
503
|
-
// Fallback if direct drawing fails
|
|
504
|
-
console.warn('Could not draw video frame:', error);
|
|
505
|
-
ctx.fillStyle = 'rgba(255, 100, 100, 0.3)';
|
|
506
|
-
ctx.fillRect(viji.width - displayWidth - 10, 10, displayWidth, displayHeight);
|
|
507
|
-
ctx.fillStyle = 'white';
|
|
508
|
-
ctx.font = '12px monospace';
|
|
509
|
-
ctx.textAlign = 'center';
|
|
510
|
-
ctx.fillText('Video Active', viji.width - displayWidth/2 - 10, 30);
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
ctx.restore();
|
|
514
|
-
|
|
515
|
-
// Video info text
|
|
516
|
-
ctx.fillStyle = 'rgba(0, 255, 255, 0.8)';
|
|
517
|
-
ctx.font = '12px monospace';
|
|
518
|
-
ctx.textAlign = 'right';
|
|
519
|
-
|
|
520
|
-
let videoInfoY = 140;
|
|
521
|
-
ctx.fillText('VIDEO ANALYSIS:', viji.width - 10, videoInfoY);
|
|
522
|
-
videoInfoY += 15;
|
|
523
|
-
ctx.fillText('Connected: ' + (video.isConnected ? 'Yes' : 'No'), viji.width - 10, videoInfoY);
|
|
524
|
-
videoInfoY += 15;
|
|
525
|
-
ctx.fillText('Resolution: ' + video.frameWidth + 'x' + video.frameHeight, viji.width - 10, videoInfoY);
|
|
526
|
-
videoInfoY += 15;
|
|
527
|
-
ctx.fillText('Frame Rate: ' + video.frameRate.toFixed(1) + ' FPS', viji.width - 10, videoInfoY);
|
|
528
|
-
videoInfoY += 15;
|
|
529
|
-
ctx.fillText('Motion: ' + (videoReactive.value ? 'Reactive' : 'Static'), viji.width - 10, videoInfoY);
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}
|