@wave3d/core 0.8.0 → 0.10.0
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 +3 -1
- package/dist/config/model.d.ts +48 -2
- package/dist/config/model.js +8 -1
- package/dist/config/model.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/renderer/WaveRenderer.d.ts +142 -10
- package/dist/renderer/WaveRenderer.js +209 -53
- package/dist/renderer/WaveRenderer.js.map +1 -1
- package/dist/renderer/WaveRendererGPU.js +291 -0
- package/dist/renderer/WaveRendererGPU.js.map +1 -0
- package/dist/renderer/gpu-loader.js +2 -0
- package/dist/renderer/index.d.ts +3 -2
- package/dist/renderer/interaction.d.ts +29 -0
- package/dist/renderer/interaction.js +77 -31
- package/dist/renderer/interaction.js.map +1 -1
- package/dist/renderer/interactionGates.js +59 -0
- package/dist/renderer/interactionGates.js.map +1 -0
- package/dist/renderer/particleField.d.ts +1 -59
- package/dist/renderer/particleField.js +49 -29
- package/dist/renderer/particleField.js.map +1 -1
- package/dist/renderer/particleFieldGPU.js +141 -0
- package/dist/renderer/particleFieldGPU.js.map +1 -0
- package/dist/renderer/tilt.d.ts +17 -0
- package/dist/renderer/tilt.js +124 -0
- package/dist/renderer/tilt.js.map +1 -0
- package/dist/renderer/tsl/color.js +129 -0
- package/dist/renderer/tsl/color.js.map +1 -0
- package/dist/renderer/tsl/noise.js +83 -0
- package/dist/renderer/tsl/noise.js.map +1 -0
- package/dist/renderer/tsl/packedArray.js +76 -0
- package/dist/renderer/tsl/packedArray.js.map +1 -0
- package/dist/renderer/tsl/particleMaterial.js +133 -0
- package/dist/renderer/tsl/particleMaterial.js.map +1 -0
- package/dist/renderer/tsl/particleUniforms.js +44 -0
- package/dist/renderer/tsl/particleUniforms.js.map +1 -0
- package/dist/renderer/tsl/pointerField.js +73 -0
- package/dist/renderer/tsl/pointerField.js.map +1 -0
- package/dist/renderer/tsl/post.js +63 -0
- package/dist/renderer/tsl/post.js.map +1 -0
- package/dist/renderer/tsl/postChain.js +56 -0
- package/dist/renderer/tsl/postChain.js.map +1 -0
- package/dist/renderer/tsl/postEffects.js +221 -0
- package/dist/renderer/tsl/postEffects.js.map +1 -0
- package/dist/renderer/tsl/types.js +28 -0
- package/dist/renderer/tsl/types.js.map +1 -0
- package/dist/renderer/tsl/uniforms.js +152 -0
- package/dist/renderer/tsl/uniforms.js.map +1 -0
- package/dist/renderer/tsl/waveMaterial.js +185 -0
- package/dist/renderer/tsl/waveMaterial.js.map +1 -0
- package/dist/renderer/tsl/waveShape.js +108 -0
- package/dist/renderer/tsl/waveShape.js.map +1 -0
- package/dist/shell/createWave.d.ts +29 -0
- package/dist/shell/createWave.js +39 -11
- package/dist/shell/createWave.js.map +1 -1
- package/dist/shell/probe.js +18 -1
- package/dist/shell/probe.js.map +1 -1
- package/dist/standalone/wave3d.standalone.js +1811 -1594
- package/dist/standalone/wave3d.standalone.webgpu.js +36647 -0
- package/dist/standalone.d.ts +10 -3
- package/dist/standalone.js +10 -3
- package/dist/standalone.js.map +1 -1
- package/dist/studio/StudioWaveRenderer.d.ts +4 -0
- package/dist/studio/StudioWaveRenderer.js +9 -0
- package/dist/studio/StudioWaveRenderer.js.map +1 -1
- package/dist/studio/StudioWaveRendererGPU.js +18 -0
- package/dist/studio/StudioWaveRendererGPU.js.map +1 -0
- package/dist/studio/index.d.ts +12 -2
- package/dist/studio/index.js +14 -1
- package/dist/studio/index.js.map +1 -0
- package/package.json +12 -3
- package/skills/wave3d/SKILL.md +29 -2
package/dist/standalone.d.ts
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, CAMERA_FITS, CameraFit, ColorStop, DEFAULT_LIGHT_POSITION, GradientType, InteractionSource, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PARTICLE_SHAPES, PaletteSource, ParticleShape, ParticlesConfig, SceneConfig, SceneInteractionBinding, SceneInteractionConfig, SceneInteractionTarget, StudioConfig, Vec2, Vec3, WaveConfig, WaveHoverConfig, WaveInteractionBinding, WaveInteractionConfig, WaveInteractionTarget, WavePressConfig, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeParticles, normalizeSceneInteraction, normalizeWave, normalizeWaveInteraction, resizeWaves } from "./config/model.js";
|
|
1
|
+
import { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, CAMERA_FITS, CameraFit, ColorStop, DEFAULT_LIGHT_POSITION, GradientType, InteractionSource, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PARTICLE_SHAPES, PaletteSource, ParticleShape, ParticlesConfig, SceneConfig, SceneInteractionBinding, SceneInteractionConfig, SceneInteractionTarget, StudioConfig, TiltConfig, Vec2, Vec3, WaveConfig, WaveHoverConfig, WaveInteractionBinding, WaveInteractionConfig, WaveInteractionTarget, WavePressConfig, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeParticles, normalizeSceneInteraction, normalizeWave, normalizeWaveInteraction, resizeWaves } from "./config/model.js";
|
|
2
2
|
import { WaveRenderer } from "./renderer/WaveRenderer.js";
|
|
3
3
|
import { FallbackReason, SnapshotOptions, WaveHandle, WaveOptions, WaveState } from "./shell/createWave.js";
|
|
4
4
|
import { PRESETS } from "./presets.js";
|
|
5
5
|
//#region src/standalone.d.ts
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* {@link createWaveImpl} with the engine already bundled in (synchronous upgrade).
|
|
8
|
+
*
|
|
9
|
+
* WebGL only. This artifact is emitted with code splitting disabled — the studio inlines it as a
|
|
10
|
+
* single Blob into exported embed HTML — so a dynamic import here would be INLINED rather than
|
|
11
|
+
* split out, taking the whole node system with it (419 KB gzipped against 197 KB). Builds that
|
|
12
|
+
* want the TSL backend use `standalone.webgpu.ts`, which is a separate artifact.
|
|
13
|
+
*/
|
|
7
14
|
declare function createWave(container: HTMLElement, config?: Partial<StudioConfig>, options?: WaveOptions): WaveHandle;
|
|
8
15
|
/** The drop-in embed contract: an alias of {@link createWave}. */
|
|
9
16
|
declare const mountWave: typeof createWave;
|
|
10
17
|
//#endregion
|
|
11
|
-
export { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, CAMERA_FITS, CameraFit, ColorStop, DEFAULT_LIGHT_POSITION, type FallbackReason, GradientType, InteractionSource, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PARTICLE_SHAPES, PRESETS, PaletteSource, ParticleShape, ParticlesConfig, SceneConfig, SceneInteractionBinding, SceneInteractionConfig, SceneInteractionTarget, type SnapshotOptions, StudioConfig, Vec2, Vec3, WaveConfig, type WaveHandle, WaveHoverConfig, WaveInteractionBinding, WaveInteractionConfig, WaveInteractionTarget, type WaveOptions, WavePressConfig, WaveRenderer, type WaveState, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, createWave, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, mountWave, normalizeBackground, normalizeParticles, normalizeSceneInteraction, normalizeWave, normalizeWaveInteraction, resizeWaves };
|
|
18
|
+
export { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, CAMERA_FITS, CameraFit, ColorStop, DEFAULT_LIGHT_POSITION, type FallbackReason, GradientType, InteractionSource, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PARTICLE_SHAPES, PRESETS, PaletteSource, ParticleShape, ParticlesConfig, SceneConfig, SceneInteractionBinding, SceneInteractionConfig, SceneInteractionTarget, type SnapshotOptions, StudioConfig, TiltConfig, Vec2, Vec3, WaveConfig, type WaveHandle, WaveHoverConfig, WaveInteractionBinding, WaveInteractionConfig, WaveInteractionTarget, type WaveOptions, WavePressConfig, WaveRenderer, type WaveState, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, createWave, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, mountWave, normalizeBackground, normalizeParticles, normalizeSceneInteraction, normalizeWave, normalizeWaveInteraction, resizeWaves };
|
|
12
19
|
//# sourceMappingURL=standalone.d.ts.map
|
package/dist/standalone.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { CAMERA_FITS, DEFAULT_LIGHT_POSITION, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, PARTICLE_SHAPES, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeParticles, normalizeSceneInteraction, normalizeWave, normalizeWaveInteraction, resizeWaves } from "./config/model.js";
|
|
2
|
-
import { createWaveImpl } from "./shell/createWave.js";
|
|
2
|
+
import { createWaveImpl, noGpuBackend } from "./shell/createWave.js";
|
|
3
3
|
import { WaveRenderer } from "./renderer/WaveRenderer.js";
|
|
4
4
|
import { PRESETS } from "./presets.js";
|
|
5
5
|
import { core_loader_exports } from "./core-loader.js";
|
|
6
6
|
//#region src/standalone.ts
|
|
7
7
|
const loadCore = () => Promise.resolve(core_loader_exports);
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* {@link createWaveImpl} with the engine already bundled in (synchronous upgrade).
|
|
10
|
+
*
|
|
11
|
+
* WebGL only. This artifact is emitted with code splitting disabled — the studio inlines it as a
|
|
12
|
+
* single Blob into exported embed HTML — so a dynamic import here would be INLINED rather than
|
|
13
|
+
* split out, taking the whole node system with it (419 KB gzipped against 197 KB). Builds that
|
|
14
|
+
* want the TSL backend use `standalone.webgpu.ts`, which is a separate artifact.
|
|
15
|
+
*/
|
|
9
16
|
function createWave(container, config = {}, options = {}) {
|
|
10
|
-
return createWaveImpl(loadCore, container, config, options);
|
|
17
|
+
return createWaveImpl(loadCore, noGpuBackend, container, config, options);
|
|
11
18
|
}
|
|
12
19
|
/** The drop-in embed contract: an alias of {@link createWave}. */
|
|
13
20
|
const mountWave = createWave;
|
package/dist/standalone.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standalone.js","names":["core"],"sources":["../src/standalone.ts"],"sourcesContent":["// The single-file CDN / standalone build entry. Unlike the `.` shell — which fetches the engine on\n// demand — this statically imports the engine (three bundled in) and pre-binds createWave/mountWave\n// with a synchronous loadCore, so a plain <script type=\"module\"> from a CDN upgrades with no extra\n// network round-trip. This is also the runtime the studio inlines into its exported embed HTML.\nimport * as core from \"./core-loader\";\nimport { createWaveImpl } from \"./shell/createWave\";\nimport type { WaveOptions, WaveHandle } from \"./shell/createWave\";\nimport type { StudioConfig } from \"./config/model\";\n\n// Synchronous core — the engine is already bundled into this file, so there is no dynamic import\n// (which is exactly why the standalone stays a single file: createWaveImpl never references the\n// public createWave's `import(\"./core-loader\")` default).\nconst loadCore = (): Promise<typeof core> => Promise.resolve(core);\n\n
|
|
1
|
+
{"version":3,"file":"standalone.js","names":["core"],"sources":["../src/standalone.ts"],"sourcesContent":["// The single-file CDN / standalone build entry. Unlike the `.` shell — which fetches the engine on\n// demand — this statically imports the engine (three bundled in) and pre-binds createWave/mountWave\n// with a synchronous loadCore, so a plain <script type=\"module\"> from a CDN upgrades with no extra\n// network round-trip. This is also the runtime the studio inlines into its exported embed HTML.\nimport * as core from \"./core-loader\";\nimport { createWaveImpl, noGpuBackend } from \"./shell/createWave\";\nimport type { WaveOptions, WaveHandle } from \"./shell/createWave\";\nimport type { StudioConfig } from \"./config/model\";\n\n// Synchronous core — the engine is already bundled into this file, so there is no dynamic import\n// (which is exactly why the standalone stays a single file: createWaveImpl never references the\n// public createWave's `import(\"./core-loader\")` default).\nconst loadCore = (): Promise<typeof core> => Promise.resolve(core);\n\n/**\n * {@link createWaveImpl} with the engine already bundled in (synchronous upgrade).\n *\n * WebGL only. This artifact is emitted with code splitting disabled — the studio inlines it as a\n * single Blob into exported embed HTML — so a dynamic import here would be INLINED rather than\n * split out, taking the whole node system with it (419 KB gzipped against 197 KB). Builds that\n * want the TSL backend use `standalone.webgpu.ts`, which is a separate artifact.\n */\nexport function createWave(\n container: HTMLElement,\n config: Partial<StudioConfig> = {},\n options: WaveOptions = {},\n): WaveHandle {\n return createWaveImpl(loadCore, noGpuBackend, container, config, options);\n}\n\n/** The drop-in embed contract: an alias of {@link createWave}. */\nexport const mountWave = createWave;\n\n// CDN users get the raw engine, presets, and full model directly too.\nexport { WaveRenderer } from \"./renderer/WaveRenderer\";\nexport { PRESETS } from \"./presets\";\nexport * from \"./config/model\";\nexport type {\n WaveOptions,\n WaveHandle,\n WaveState,\n FallbackReason,\n SnapshotOptions,\n} from \"./shell/createWave\";\n"],"mappings":";;;;;;AAYA,MAAM,iBAAuC,QAAQ,QAAQA,mBAAI;;;;;;;;;AAUjE,SAAgB,WACd,WACA,SAAgC,CAAC,GACjC,UAAuB,CAAC,GACZ;CACZ,OAAO,eAAe,UAAU,cAAc,WAAW,QAAQ,OAAO;AAC1E;;AAGA,MAAa,YAAY"}
|
|
@@ -195,6 +195,10 @@ declare class StudioWaveRenderer extends WaveRenderer {
|
|
|
195
195
|
private pushLightUniforms;
|
|
196
196
|
protected isCameraExternallyDriven(): boolean;
|
|
197
197
|
protected onAfterRefresh(): void;
|
|
198
|
+
/** The interaction runtime is fetched lazily, so the controller usually arrives AFTER the refresh
|
|
199
|
+
* that asked for it — this is the studio's second chance to hand it the scroll preview. Without
|
|
200
|
+
* it, adding the first scroll binding leaves the slider dead until the next edit. */
|
|
201
|
+
protected onInteractionReady(): void;
|
|
198
202
|
protected onAfterRenderFrame(): void;
|
|
199
203
|
protected onAfterResize(): void;
|
|
200
204
|
protected applyCameraFromConfig(): void;
|
|
@@ -888,6 +888,15 @@ var StudioWaveRenderer = class extends WaveRenderer {
|
|
|
888
888
|
else if (this.editMode === "wave") this.syncWaveHelpers();
|
|
889
889
|
if (this.interaction) this.interaction.scrollOverride = this.scrollPreview;
|
|
890
890
|
}
|
|
891
|
+
/** The interaction runtime is fetched lazily, so the controller usually arrives AFTER the refresh
|
|
892
|
+
* that asked for it — this is the studio's second chance to hand it the scroll preview. Without
|
|
893
|
+
* it, adding the first scroll binding leaves the slider dead until the next edit. */
|
|
894
|
+
onInteractionReady() {
|
|
895
|
+
if (!this.interaction) return;
|
|
896
|
+
this.interaction.scrollOverride = this.scrollPreview;
|
|
897
|
+
this.interaction.snapScroll();
|
|
898
|
+
this.renderOnce();
|
|
899
|
+
}
|
|
891
900
|
onAfterRenderFrame() {
|
|
892
901
|
if (this.overlay && this.editing && !this.capturing && this.overlay.children.length > 0) {
|
|
893
902
|
const helpers = this.editMode === "wave" ? this.waveHelpers : this.lightHelpers;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StudioWaveRenderer.js","names":[],"sources":["../../src/studio/StudioWaveRenderer.ts"],"sourcesContent":["// The studio's editor-enabled renderer: WaveRenderer + orbit/zoom/pan, the light & wave drag\n// gizmos, and the camera-rig minimap. Lives in @wave3d/core/studio so the drop-in shell and the\n// standalone build (which never edit) don't pay for ~1,000 lines of editor code. Every member here\n// moved verbatim out of WaveRenderer; the base exposes exactly five protected hook points the\n// overrides below plug into.\nimport * as THREE from \"three\";\nimport type { OrbitControls } from \"three/addons/controls/OrbitControls.js\";\nimport type { TransformControls } from \"three/addons/controls/TransformControls.js\";\nimport { WaveRenderer, hexToLinearVec3 } from \"../renderer/WaveRenderer\";\nimport { createLight, DEFAULT_LIGHT_POSITION, MAX_LIGHTS } from \"../config/model\";\nimport type { LightConfig } from \"../config/model\";\nimport { roundTo } from \"../util/math\";\n\n// The minimap's fixed 3/4 vantage direction.\nconst MINIMAP_VANTAGE = new THREE.Vector3(0.85, 0.6, 1).normalize();\n\nexport class StudioWaveRenderer extends WaveRenderer {\n /** Set while the panel drives the camera, so orbit's 'change' doesn't re-refresh the\n * panel mid-drag (the panel already knows the new value). */\n private suppressCameraChange = false;\n\n /** Studio scroll-preview value (null = live). Persisted here so it survives controller\n * recreation — a scroll binding added after the preview was set still responds (see\n * onAfterRefresh). */\n private scrollPreview: number | null = null;\n\n // Same for the minimap, which renders every frame while the camera rig is open.\n private readonly miniBox = new THREE.Box3();\n private readonly miniSphere = new THREE.Sphere();\n private readonly miniTmpA = new THREE.Vector3();\n private readonly miniTmpB = new THREE.Vector3();\n private readonly miniPrevColor = new THREE.Color();\n private readonly miniSize = new THREE.Vector2();\n // Per-wave blend/transparent stash for the minimap's forced-opaque draw (index-parallel to waves).\n private readonly miniBlendPrev: THREE.Blending[] = [];\n private readonly miniTransPrev: boolean[] = [];\n\n // --- Camera-rig minimap (corner inset: the wave + a little camera/light marker) ---\n private cameraRigOn = false;\n private cameraRigCollapsed = false;\n private minimapCamera?: THREE.PerspectiveCamera;\n private camMarker?: THREE.Group;\n /** Gold markers in the minimap, one per rig light (positions/colours tracked live). */\n private minimapLights: THREE.Mesh[] = [];\n /** Shown in the rig when no light has been added yet, so the light is always visible there.\n * Matches where \"drag in 3D\" creates the first light, so the marker doesn't jump when added. */\n private readonly defaultRigLight: LightConfig = createLight({ ...DEFAULT_LIGHT_POSITION }, 1);\n private minimapBtn?: HTMLButtonElement;\n\n // --- Camera controls (orbit/zoom/pan) + light-editing gizmo ---\n private readonly overlay = new THREE.Scene();\n private readonly raycaster = new THREE.Raycaster();\n private orbit?: OrbitControls;\n private transform?: TransformControls;\n /** Whether the main view orbit/zoom/pan is on (studio); off for the embed. */\n private mainOrbitOn = false;\n private lightHelpers: THREE.Mesh[] = [];\n /** Which 3D-editing gizmo is active: none, dragging lights, or dragging the wave/waves. */\n private editMode: \"none\" | \"light\" | \"wave\" = \"none\";\n private selectedLight = 0;\n /** Wave/wave drag handles: index 0 = the whole-wave box (moves config.position); 1..N =\n * per-wave spheres (move each layer's offset), shown only when there's >1 wave. */\n private waveHelpers: THREE.Mesh[] = [];\n private selectedWave = 0;\n /** Gizmo operation: \"translate\" moves the handle, \"rotate\" spins the whole wave. */\n private gizmoMode: \"translate\" | \"rotate\" = \"translate\";\n /** Active free screen-plane drag of a handle (grab anywhere on the marker, camera locked). */\n private dragState?: { helper: THREE.Mesh; offset: THREE.Vector3 };\n private readonly dragPlane = new THREE.Plane();\n /** Active left-drag camera pan in edit mode (the press missed every handle → move the view). */\n private panState?: { lastNdc: THREE.Vector2 };\n /** Camera snapshot taken when entering a 3D-edit mode and restored verbatim on exit, so the\n * view returns exactly where it was (position + ortho zoom + up) rather than snapping to the\n * authored hero framing. */\n private returnCamera: {\n pos: THREE.Vector3;\n target: THREE.Vector3;\n zoom: number;\n up: THREE.Vector3;\n } | null = null;\n\n /** Set by the panel: fired after a gizmo drag/selection so sliders can refresh. */\n onLightsChanged?: (selected: number) => void;\n /** Set by the panel: fired after orbit moves the camera so sliders can refresh. */\n onCameraChanged?: () => void;\n /** Set by the panel: fired after a wave/wave gizmo drag/selection so the position and\n * per-wave offset sliders can refresh. */\n onWaveChanged?: () => void;\n\n // ---------------- 3D editing (draggable gizmo: lights or wave/waves) ----------------\n\n /** True while any drag-in-3D gizmo owns the camera (light or wave). */\n private get editing(): boolean {\n return this.editMode !== \"none\";\n }\n\n isLightEditMode(): boolean {\n return this.editMode === \"light\";\n }\n\n isWaveEditMode(): boolean {\n return this.editMode === \"wave\";\n }\n\n /** Toggle 3D light editing: show draggable light handles; off restores the prior view. */\n async setLightEditMode(on: boolean): Promise<void> {\n await this.setEditMode(on ? \"light\" : \"none\");\n }\n\n /** Toggle 3D wave/wave editing: drag the whole wave (and each wave when there's >1). */\n async setWaveEditMode(on: boolean): Promise<void> {\n await this.setEditMode(on ? \"wave\" : \"none\");\n }\n\n /** Enter/leave/switch a 3D-edit mode. Modes are mutually exclusive — turning one on turns\n * the other off. The camera is snapshotted on the first entry and restored on the final exit\n * (so light↔wave switches keep the same return view). */\n private async setEditMode(mode: \"none\" | \"light\" | \"wave\"): Promise<void> {\n if (mode === this.editMode) return;\n const prev = this.editMode;\n // Tear down the previous mode's handles + gizmo.\n if (prev !== \"none\") {\n if (this.transform) this.transform.enabled = false;\n this.transform?.detach();\n if (prev === \"light\") this.clearLightHelpers();\n else this.clearWaveHelpers();\n }\n this.editMode = mode;\n // Leaving LIGHT mode undoes its transient 3/4 framing (back to the pre-edit camera). Wave\n // editing never moves the camera, so there's nothing to restore when leaving it.\n if (prev === \"light\") this.restoreReturnCamera();\n if (mode === \"none\") {\n if (this.orbit) this.orbit.enabled = this.mainOrbitOn; // keep main-view orbit on\n this.setOrbitForEdit(false); // restore left-drag camera pan\n this.dragState = undefined;\n this.renderOnce();\n return;\n }\n await this.ensureGizmo();\n if (this.editMode !== mode) return; // toggled away while controls lazy-loaded\n if (this.orbit) this.orbit.enabled = true;\n this.setOrbitForEdit(true); // left-drag on a handle moves it; on empty space it pans\n this.gizmoMode = \"translate\"; // each mode entry starts in move mode (rotate is wave-only)\n this.transform?.setMode(\"translate\");\n this.transform?.setSpace(\"world\"); // reset to world-space translate on every entry\n if (this.transform) this.transform.enabled = true;\n if (mode === \"light\") {\n // Light editing reframes to a 3/4 working angle, so snapshot the current view first and\n // restore it on exit (the framing is transient, not part of the authored composition).\n this.captureReturnCamera();\n this.syncLightHelpers();\n this.frameEditCamera();\n this.selectLight(Math.min(this.selectedLight, Math.max(0, this.lightHelpers.length - 1)));\n } else {\n // Wave editing leaves the camera exactly where it is — no reframing, no zoom — so the view\n // stays put on enter AND exit; you pan/rotate/zoom normally to reach a handle and drag it.\n this.syncWaveHelpers();\n this.selectWaveHandle(Math.min(this.selectedWave, Math.max(0, this.waveHelpers.length - 1)));\n }\n this.renderOnce();\n }\n\n /** In edit mode, take PAN off OrbitControls' left button so left-drag is free to grab handles;\n * onPointerDown/Move pan the camera manually when a left-press misses every handle (so panning\n * never fights the object drag). Right-drag still rotates the camera; scroll/middle zooms. */\n private setOrbitForEdit(editing: boolean): void {\n if (!this.orbit) return;\n this.orbit.mouseButtons = editing\n ? { MIDDLE: THREE.MOUSE.DOLLY, RIGHT: THREE.MOUSE.ROTATE }\n : { LEFT: THREE.MOUSE.PAN, MIDDLE: THREE.MOUSE.DOLLY, RIGHT: THREE.MOUSE.ROTATE };\n }\n\n /** Switch the wave-edit gizmo between moving handles and rotating the whole wave. Rotate\n * targets the whole-wave box (config.rotation), so selecting it makes the intent obvious. */\n setGizmoMode(mode: \"translate\" | \"rotate\"): void {\n this.gizmoMode = mode;\n this.transform?.setMode(mode);\n // Rotate in LOCAL space so the gizmo rings reorient with the wave — a visual read-out of its\n // current rotation; translate stays in world space so the arrows track the world axes.\n this.transform?.setSpace(mode === \"rotate\" ? \"local\" : \"world\");\n if (mode === \"rotate\" && this.editMode === \"wave\") {\n const waveIdx = this.waveHelpers.findIndex((h) => h.userData.kind === \"wave\");\n if (waveIdx >= 0) this.selectWaveHandle(waveIdx);\n }\n if (!this.running) this.renderOnce();\n }\n\n getGizmoMode(): \"translate\" | \"rotate\" {\n return this.gizmoMode;\n }\n\n /** Snapshot the live camera so leaving edit mode returns exactly here (incl. ortho zoom). */\n private captureReturnCamera(): void {\n this.returnCamera = {\n pos: this.camera.position.clone(),\n target: this.orbit\n ? this.orbit.target.clone()\n : this.camera.getWorldDirection(new THREE.Vector3()).add(this.camera.position),\n zoom: this.camera.zoom,\n up: this.camera.up.clone(),\n };\n }\n\n /** Restore the snapshot from captureReturnCamera (falls back to the authored hero camera). */\n private restoreReturnCamera(): void {\n const s = this.returnCamera;\n this.returnCamera = null;\n if (!s) {\n this.restoreHeroCamera();\n return;\n }\n this.camera.position.copy(s.pos);\n this.camera.up.copy(s.up);\n this.camera.zoom = s.zoom;\n this.camera.updateProjectionMatrix();\n if (this.orbit) {\n this.orbit.target.copy(s.target);\n this.orbit.update(); // fires onControlsChange → writes the restored view back to config\n } else {\n this.camera.lookAt(s.target);\n }\n }\n\n /** Turn on mouse/trackpad orbit + zoom + pan + arrow-key orbit (studio only). */\n async enableOrbit(): Promise<void> {\n this.mainOrbitOn = true;\n this.renderer.domElement.style.cursor = \"move\"; // 4-way move arrows: left-drag pans the view\n window.addEventListener(\"keydown\", this.onKeyDown);\n await this.ensureOrbit();\n if (this.orbit && !this.editing) this.orbit.enabled = true;\n }\n\n /** Arrow keys orbit the camera around the target (←/→ azimuth, ↑/↓ elevation). */\n private onKeyDown = (e: KeyboardEvent): void => {\n if (!this.mainOrbitOn || !this.orbit || this.editing) return;\n const t = e.target instanceof HTMLElement ? e.target : null;\n if (t && (t.closest(\"#panel\") || /^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName))) return; // let the panel keep arrows\n const step = e.shiftKey ? 0.015 : 0.05;\n let az = 0;\n let pol = 0;\n if (e.key === \"ArrowLeft\") az = -step;\n else if (e.key === \"ArrowRight\") az = step;\n else if (e.key === \"ArrowUp\") pol = -step;\n else if (e.key === \"ArrowDown\") pol = step;\n else return;\n e.preventDefault();\n const offset = this.camera.position.clone().sub(this.orbit.target);\n const sph = new THREE.Spherical().setFromVector3(offset);\n sph.theta += az;\n sph.phi = THREE.MathUtils.clamp(sph.phi + pol, 0.05, Math.PI - 0.05);\n offset.setFromSpherical(sph);\n this.camera.position.copy(this.orbit.target).add(offset);\n this.orbit.update(); // fires 'change' → writes camera to config + renders\n };\n\n /** Reset the camera to the straight-on hero framing at the configured distance. */\n resetView(): void {\n this.camera.position.copy(this.homeCamPos);\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(this.homeCamTarget);\n if (this.orbit) {\n this.orbit.target.copy(this.homeCamTarget);\n this.orbit.update();\n }\n this.writeCameraToConfig();\n this.onCameraChanged?.();\n if (!this.running) this.renderOnce();\n }\n\n /** Dolly/aim the camera so the whole wave fills the viewport (keeps the view angle).\n * Fits the geometry box's actual *projected* screen extent — tighter than a bounding\n * sphere for a flat, diagonal ribbon. */\n fitToView(): void {\n const box = new THREE.Box3();\n for (const s of this.waves) {\n s.mesh.updateWorldMatrix(true, false);\n if (!s.mesh.geometry.boundingBox) s.mesh.geometry.computeBoundingBox();\n const bb = s.mesh.geometry.boundingBox;\n if (bb) box.union(bb.clone().applyMatrix4(s.mesh.matrixWorld));\n }\n if (box.isEmpty()) return;\n\n const center = box.getCenter(new THREE.Vector3());\n\n // Aim at the centre, then measure how much of the viewport the box spans (in NDC).\n if (this.orbit) this.orbit.target.copy(center);\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(center);\n this.camera.updateMatrixWorld(true);\n const c = box.min,\n m = box.max;\n let frac = 0;\n const v = new THREE.Vector3();\n for (let i = 0; i < 8; i++) {\n v.set(i & 1 ? m.x : c.x, i & 2 ? m.y : c.y, i & 4 ? m.z : c.z).project(this.camera);\n frac = Math.max(frac, Math.abs(v.x), Math.abs(v.y)); // |ndc| 0→1 = half-viewport\n }\n // Overfill slightly (>1): the folded geometry's bounding box has empty diagonal\n // corners, so filling past the box edges lets the actual ribbon fill the frame.\n // Ortho: framing is the zoom, not the distance.\n const target = 1.18;\n this.config.cameraZoom = ((this.config.cameraZoom ?? 1) * target) / Math.max(0.001, frac);\n this.applyZoom();\n if (this.orbit) this.orbit.update();\n this.writeCameraToConfig();\n this.onCameraChanged?.();\n if (!this.running) this.renderOnce();\n }\n\n /** Dolly the camera to a distance from the orbit target (or set z when no orbit). */\n setCameraDistance(d: number): void {\n if (this.orbit) {\n const dir = this.camera.position.clone().sub(this.orbit.target);\n const len = dir.length() || 1;\n this.camera.position.copy(this.orbit.target).addScaledVector(dir.multiplyScalar(1 / len), d);\n this.orbit.update();\n } else {\n this.camera.position.z = d;\n }\n this.writeCameraToConfig();\n if (!this.running) this.renderOnce();\n }\n\n /** The current look-at target (orbit's if present, else from config). */\n private camTarget(): THREE.Vector3 {\n if (this.orbit) return this.orbit.target;\n const t = this.config.cameraTarget;\n return new THREE.Vector3(t.x, t.y, t.z);\n }\n\n /** Read the camera as orbit values for the panel (angles in degrees). */\n getCameraOrbit(): {\n azimuth: number;\n elevation: number;\n distance: number;\n panX: number;\n panY: number;\n } {\n const t = this.camTarget();\n const sph = new THREE.Spherical().setFromVector3(this.camera.position.clone().sub(t));\n return {\n azimuth: THREE.MathUtils.radToDeg(sph.theta),\n elevation: 90 - THREE.MathUtils.radToDeg(sph.phi),\n distance: sph.radius,\n panX: t.x,\n panY: t.y,\n };\n }\n\n /** Place the camera at azimuth/elevation (degrees) + distance around the target. */\n setCameraOrbit(azimuthDeg: number, elevationDeg: number, distance: number): void {\n const target = this.camTarget();\n const sph = new THREE.Spherical(\n Math.max(0.01, distance),\n THREE.MathUtils.degToRad(90 - elevationDeg),\n THREE.MathUtils.degToRad(azimuthDeg),\n );\n sph.makeSafe();\n this.suppressCameraChange = true;\n this.camera.position.copy(target).add(new THREE.Vector3().setFromSpherical(sph));\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(target);\n if (this.orbit) this.orbit.update();\n this.suppressCameraChange = false;\n this.writeCameraToConfig();\n if (!this.running) this.renderOnce();\n }\n\n /** Roll the camera around its view axis (degrees) — tilts the composition without\n * moving the camera. Applied after positioning; reset by any orbit interaction. */\n rollView(deg: number): void {\n this.camera.rotateZ(THREE.MathUtils.degToRad(deg));\n this.camera.updateMatrixWorld();\n if (!this.running) this.renderOnce();\n }\n\n /** Pan: move the look-at target (and camera with it) to (x, y) in world units. */\n setCameraTarget(x: number, y: number): void {\n const target = this.camTarget();\n const delta = new THREE.Vector3(x - target.x, y - target.y, 0);\n this.suppressCameraChange = true;\n this.camera.position.add(delta);\n if (this.orbit) this.orbit.target.add(delta);\n else this.config.cameraTarget = { x, y, z: target.z };\n this.camera.lookAt(this.camTarget());\n if (this.orbit) this.orbit.update();\n this.suppressCameraChange = false;\n this.writeCameraToConfig();\n if (!this.running) this.renderOnce();\n }\n\n /** Ortho zoom MULTIPLIER (the camera has no real fov). 1 = the responsive base framing (the hero crop). */\n getZoom(): number {\n return this.config.cameraZoom ?? 1;\n }\n\n setZoom(zoom: number): void {\n this.config.cameraZoom = THREE.MathUtils.clamp(zoom, 0.1, 6);\n this.applyZoom();\n if (!this.running) this.renderOnce();\n }\n\n /** Re-apply the responsive framing after config.cameraFit / cameraMinVisibleWidth were edited in\n * place (the panel binds the live config object, so there is no setConfig to hook). Embeds get\n * this for free — handle.set() → setConfig → applyCameraFromConfig → applyZoom. */\n refreshFraming(): void {\n this.applyZoom();\n if (!this.running) this.renderOnce();\n }\n\n /** Studio-only scroll preview (the studio page doesn't scroll): override the `scroll` source with\n * a fixed 0..1 value, or pass null to return to the live container-progress read. NEVER touches\n * config. No-op until interaction is enabled (the controller exists). */\n setScrollPreview(v: number | null): void {\n this.scrollPreview = v;\n if (this.interaction) {\n this.interaction.scrollOverride = v;\n // Apply the scrub immediately, whether or not the loop is \"running\". The studio page never\n // really scrolls, so this is a manual scrub that must reflect the instant you drag the slider —\n // we can't defer to the render loop to pick up scrollOverride, because the browser fully\n // SUSPENDS requestAnimationFrame whenever the studio tab isn't the foreground tab, so a\n // \"running\" loop is frozen and the slider looks dead. (The old `!this.running` guard only ever\n // covered the explicitly-paused case, missing every background-tab / occluded-window case.)\n // snapScroll() resolves just the scroll bindings, leaving live pointer/press state untouched.\n this.interaction.snapScroll();\n this.renderOnce();\n }\n // No controller yet → nothing to scrub; onAfterRefresh re-applies scrollPreview once one exists.\n }\n\n /** Feed a live scroll position (0..1) from the studio scroll-test overlay — a real scrollable\n * surface the user drags/wheels. Unlike setScrollPreview's instant snap (built for the slider and\n * a possibly-frozen loop), this leaves the RUNNING render loop's update() to smooth the bindings\n * and derive `scrollVelocity` from the real scroll delta, so the wave reacts exactly as it would on\n * a scrolling page (velocity included). Falls back to a snapped frame when the loop isn't running\n * (paused / reduced-motion), so the preview still tracks. Like setScrollPreview it NEVER touches config. */\n setScrollTestProgress(v: number): void {\n this.scrollPreview = v;\n if (!this.interaction) return;\n this.interaction.scrollOverride = v;\n if (!this.running) {\n this.interaction.snapScroll();\n this.renderOnce();\n }\n }\n\n duplicateOffset(): { x: number; y: number; z: number } {\n this.camera.updateMatrixWorld();\n const worldW = (this.camera.right - this.camera.left) / this.camera.zoom; // visible world span\n const worldH = (this.camera.top - this.camera.bottom) / this.camera.zoom;\n const right = new THREE.Vector3().setFromMatrixColumn(this.camera.matrixWorld, 0).normalize();\n const up = new THREE.Vector3().setFromMatrixColumn(this.camera.matrixWorld, 1).normalize();\n // Screen-left ~40% + screen-down ~15% of the frame: enough to clearly separate the copy, small\n // enough that a few successive adds cascade diagonally and stay on-screen before running off.\n const off = right.multiplyScalar(-0.4 * worldW).add(up.multiplyScalar(-0.15 * worldH));\n return { x: off.x, y: off.y, z: 0 };\n }\n\n /** Toggle the corner camera-rig minimap (studio aid; off in the embed). */\n setCameraRig(on: boolean): void {\n this.cameraRigOn = on;\n if (on) this.ensureMinimap();\n if (this.minimapBtn) this.minimapBtn.style.display = on ? \"\" : \"none\";\n if (on) this.positionMinimapBtn();\n if (!this.running) this.renderOnce();\n }\n\n /** Corner rectangle (logical px) for the minimap viewport. */\n private minimapRect(): { x: number; y: number; size: number; pad: number } {\n const sz = this.renderer.getSize(new THREE.Vector2());\n const size = Math.round(Math.min(sz.x, sz.y) * 0.27);\n const pad = Math.round(size * 0.06);\n return { x: sz.x - size - pad, y: pad, size, pad };\n }\n\n /** Place the collapse button at the minimap's top-right (or bottom corner when collapsed). */\n private positionMinimapBtn(): void {\n const b = this.minimapBtn;\n if (!b) return;\n const { size, pad } = this.minimapRect();\n // minimapRect() is in renderer BUFFER px (= the export size); the canvas is CSS-scaled to\n // fill the container, so convert to on-screen px or the button detaches from the minimap.\n const canvas = this.renderer.domElement;\n const buf = this.renderer.getSize(new THREE.Vector2());\n const sx = buf.x > 0 ? canvas.clientWidth / buf.x : 1;\n const sy = buf.y > 0 ? canvas.clientHeight / buf.y : 1;\n b.style.right = pad * sx + \"px\";\n b.style.bottom = (this.cameraRigCollapsed ? pad * sy : (pad + size) * sy - 22) + \"px\";\n b.textContent = this.cameraRigCollapsed ? \"▴ camera\" : \"▾ camera\";\n }\n\n /** Build the minimap's fixed 3rd-person camera + the camera/light markers (once). */\n private ensureMinimap(): void {\n if (this.minimapCamera) return;\n // Pose/near/far are recomputed every frame by frameMinimap() to fit the current scene\n // (the wave sits in a ×10 ortho world, so a fixed vantage can't frame it).\n this.minimapCamera = new THREE.PerspectiveCamera(42, 1, 1, 10000);\n\n // A little camera (body + lens) marking where the main camera views the wave from.\n const marker = new THREE.Group();\n marker.add(\n new THREE.Mesh(\n new THREE.BoxGeometry(3.4, 2.4, 4.2),\n new THREE.MeshBasicMaterial({ color: 0x2a2f3d }),\n ),\n );\n const lens = new THREE.Mesh(\n new THREE.ConeGeometry(1.3, 2.4, 18),\n new THREE.MeshBasicMaterial({ color: 0x6ea8fe }),\n );\n lens.rotation.x = -Math.PI / 2; // cone points -Z (the camera's forward)\n lens.position.z = -2.7;\n marker.add(lens);\n marker.visible = false;\n this.scene.add(marker);\n this.camMarker = marker;\n\n // Collapse/expand toggle overlaid on the minimap corner.\n const btn = document.createElement(\"button\");\n btn.style.cssText =\n \"position:absolute;z-index:30;padding:2px 8px;border-radius:5px;cursor:pointer;\" +\n \"font:11px ui-sans-serif,system-ui,-apple-system,sans-serif;color:#cdd0d6;\" +\n \"background:rgba(18,18,26,0.85);border:1px solid rgba(255,255,255,0.16);\";\n btn.addEventListener(\"click\", () => {\n this.cameraRigCollapsed = !this.cameraRigCollapsed;\n this.positionMinimapBtn();\n this.renderOnce();\n });\n this.container.appendChild(btn);\n this.minimapBtn = btn;\n this.positionMinimapBtn();\n }\n\n /** The lights the rig should show: the configured lights, or a single default-position\n * marker when none has been added yet — so the light is always visible in the rig. */\n private rigLights(): LightConfig[] {\n const lights = this.config.lights ?? [];\n return lights.length ? lights : [this.defaultRigLight];\n }\n\n /** Reconcile the minimap's light markers with the rig lights (count, position, colour). */\n private syncMinimapLights(visible: boolean): void {\n const lights = this.rigLights();\n while (this.minimapLights.length < lights.length) {\n const m = new THREE.Mesh(\n new THREE.SphereGeometry(2.2, 16, 12),\n new THREE.MeshBasicMaterial({ color: 0xffd24a }),\n );\n m.visible = false;\n this.scene.add(m);\n this.minimapLights.push(m);\n }\n while (this.minimapLights.length > lights.length) {\n const m = this.minimapLights.pop();\n if (!m) break;\n this.scene.remove(m);\n m.geometry.dispose();\n (m.material as THREE.Material).dispose();\n }\n lights.forEach((l, i) => {\n const m = this.minimapLights[i];\n m.position.set(l.position.x, l.position.y, l.position.z);\n (m.material as THREE.MeshBasicMaterial).color.set(l.color); // track the light's colour\n m.visible = visible;\n });\n }\n\n /** Fit the minimap's 3rd-person camera to the wave (+ lights) and size/place the camera\n * proxy, so the rig reads at any scene scale. The main camera is orthographic — its literal\n * distance is arbitrary — so the proxy sits a fixed multiple of the scene radius back along\n * the true view direction rather than at the far-away ortho position. */\n private frameMinimap(): void {\n const cam = this.minimapCamera;\n const marker = this.camMarker;\n if (!cam || !marker) return;\n const box = this.miniBox.setFromObject(this.group);\n if (box.isEmpty()) return; // geometry not built yet\n for (const l of this.rigLights()) {\n box.expandByPoint(this.miniTmpA.set(l.position.x, l.position.y, l.position.z));\n }\n const subject = box.getBoundingSphere(this.miniSphere);\n const radius = Math.max(subject.radius, 1);\n\n // Point the little camera at the wave from its real view direction, kept a sane distance\n // away (using the ortho camera's actual z would push it thousands of units off and dwarf\n // the wave).\n const viewDir = this.camera.getWorldDirection(this.miniTmpA); // points toward the wave\n const markerPos = this.miniTmpB.copy(subject.center).addScaledVector(viewDir, -radius * 1.5);\n marker.position.copy(markerPos);\n marker.quaternion.copy(this.camera.quaternion);\n marker.scale.setScalar(radius * 0.05);\n for (const m of this.minimapLights) m.scale.setScalar(radius * 0.045);\n\n // Frame the whole rig (wave + proxy) from a fixed 3/4 vantage. (`rig` reuses the sphere\n // behind `subject`, which has no readers past this point.)\n box.expandByPoint(markerPos);\n const rig = box.getBoundingSphere(this.miniSphere);\n const frameR = Math.max(rig.radius, 1);\n cam.position.copy(rig.center).addScaledVector(MINIMAP_VANTAGE, frameR * 2.9);\n cam.near = Math.max(1, frameR * 0.02);\n cam.far = frameR * 10;\n cam.up.set(0, 1, 0);\n cam.lookAt(rig.center);\n cam.updateProjectionMatrix();\n }\n\n /** Draw the camera-rig minimap into a corner viewport (called after the main render). */\n private renderMinimap(): void {\n if (!this.cameraRigOn || this.cameraRigCollapsed || !this.mainOrbitOn || this.capturing) return;\n if (!this.minimapCamera || !this.camMarker) return;\n // setViewport/setScissor take LOGICAL (CSS) pixels — three applies pixelRatio itself.\n const { x, y, size } = this.minimapRect();\n\n this.camMarker.visible = true;\n this.syncMinimapLights(true);\n this.frameMinimap();\n\n const r = this.renderer;\n const prevColor = this.miniPrevColor;\n r.getClearColor(prevColor);\n const prevAlpha = r.getClearAlpha();\n r.autoClear = false;\n r.setRenderTarget(null); // draw to the screen — NOT a leftover composer buffer\n r.setScissorTest(true);\n r.setViewport(x, y, size, size);\n r.setScissor(x, y, size, size);\n r.setClearColor(0x12121a, 0.92);\n r.clear(true, true);\n // scene.background (the wave's page colour / image / gradient) fills ANY camera's view, so\n // hide it while drawing the minimap — otherwise it covers the 3rd-person wave.\n const prevBg = this.scene.background;\n this.scene.background = null;\n // The wave's own blend mode (additive for the neon / Spider-Man presets) makes it vanish on\n // the dark minimap backdrop; force opaque normal blending just for this draw so the shape\n // always reads. The main render already happened, so we restore immediately after.\n for (let i = 0; i < this.waves.length; i++) {\n const m = this.waves[i].material;\n this.miniBlendPrev[i] = m.blending;\n this.miniTransPrev[i] = m.transparent;\n m.blending = THREE.NormalBlending;\n m.transparent = false;\n }\n r.render(this.scene, this.minimapCamera);\n for (let i = 0; i < this.waves.length; i++) {\n const m = this.waves[i].material;\n m.blending = this.miniBlendPrev[i];\n m.transparent = this.miniTransPrev[i];\n }\n this.scene.background = prevBg;\n r.setScissorTest(false);\n const full = this.renderer.getSize(this.miniSize);\n r.setViewport(0, 0, full.x, full.y);\n r.setClearColor(prevColor, prevAlpha);\n r.autoClear = true;\n\n this.camMarker.visible = false;\n for (const m of this.minimapLights) m.visible = false;\n }\n\n private async ensureOrbit(): Promise<void> {\n if (this.orbit) return;\n const { OrbitControls } = await import(\"three/addons/controls/OrbitControls.js\");\n if (this.orbit) return; // a concurrent call already set it up\n this.orbit = new OrbitControls(this.camera, this.renderer.domElement);\n this.orbit.enableDamping = false;\n this.orbit.enabled = false; // enabled by enableOrbit() / light-edit\n this.orbit.screenSpacePanning = true;\n this.orbit.zoomToCursor = true;\n this.orbit.minDistance = 12;\n this.orbit.maxDistance = 600;\n // Left drag PANS (moves the view around); right drag ROTATES — swapped from the\n // OrbitControls default so the primary drag moves the scene rather than orbiting it.\n this.orbit.mouseButtons = {\n LEFT: THREE.MOUSE.PAN,\n MIDDLE: THREE.MOUSE.DOLLY,\n RIGHT: THREE.MOUSE.ROTATE,\n };\n this.orbit.target.set(\n this.config.cameraTarget.x,\n this.config.cameraTarget.y,\n this.config.cameraTarget.z,\n );\n this.orbit.update();\n this.orbit.addEventListener(\"change\", this.onControlsChange);\n // Cursor feedback by drag type: left-drag pans → 4-way move arrows; right-drag rotates →\n // grab/closed-hand. Idle stays on the move arrows (the primary drag pans). OrbitControls\n // doesn't expose the button, so we read it from pointerdown directly.\n this.renderer.domElement.addEventListener(\"pointerdown\", this.onCursorDown);\n window.addEventListener(\"pointerup\", this.onCursorUp);\n }\n\n private onCursorDown = (e: PointerEvent): void => {\n if (this.mainOrbitOn) {\n this.renderer.domElement.style.cursor = e.button === 2 ? \"grabbing\" : \"move\";\n }\n };\n\n private onCursorUp = (): void => {\n if (this.mainOrbitOn) this.renderer.domElement.style.cursor = \"move\";\n };\n\n private async ensureGizmo(): Promise<void> {\n await this.ensureOrbit();\n if (this.transform) return;\n const { TransformControls } = await import(\"three/addons/controls/TransformControls.js\");\n if (this.transform) return; // a concurrent call already set it up\n this.transform = new TransformControls(this.camera, this.renderer.domElement);\n this.transform.setMode(\"translate\");\n this.transform.addEventListener(\"dragging-changed\", (e) => {\n if (this.orbit) this.orbit.enabled = !(e as unknown as { value: boolean }).value;\n });\n this.transform.addEventListener(\"objectChange\", this.onGizmoMoved);\n // (onGizmoMoved routes to the light- or wave-drag handler based on the active mode.)\n this.transform.addEventListener(\"change\", this.onControlsChange);\n const tc = this.transform as unknown as { getHelper?: () => THREE.Object3D };\n this.overlay.add(tc.getHelper ? tc.getHelper() : (this.transform as unknown as THREE.Object3D));\n\n this.renderer.domElement.addEventListener(\"pointerdown\", this.onPointerDown);\n this.renderer.domElement.addEventListener(\"pointermove\", this.onPointerMove);\n this.renderer.domElement.addEventListener(\"pointerup\", this.onPointerUp);\n this.renderer.domElement.addEventListener(\"pointercancel\", this.onPointerUp);\n }\n\n private onControlsChange = (): void => {\n // Orbit/zoom/pan moved the camera → capture it so exports match the view. Wave editing keeps\n // the live view (persist it); light editing uses a transient 3/4 working view (don't persist).\n if (\n this.orbit &&\n this.orbit.enabled &&\n this.editMode !== \"light\" &&\n !this.suppressCameraChange\n ) {\n this.writeCameraToConfig();\n this.onCameraChanged?.();\n }\n if (!this.running) this.renderOnce();\n };\n\n /** Persist the live camera (position/target/distance) into the config. */\n private writeCameraToConfig(): void {\n const p = this.camera.position;\n this.config.cameraPosition = {\n x: roundTo(p.x, 3),\n y: roundTo(p.y, 3),\n z: roundTo(p.z, 3),\n };\n // Capture the LIVE ortho zoom (mouse-scroll changes camera.zoom directly) back into\n // config.cameraZoom — the user multiplier — by inverting applyZoom's responsive base factor.\n // Without this, scroll-zoom changed the view but was never saved/exported, so a framing tuned\n // at a scrolled zoom didn't reproduce (its pan made sense only at that zoom). It reads the\n // factor through baseFrameZoom so it tracks cameraFit / cameraMinVisibleWidth: inverting a\n // hardcoded COVER while the scene rendered under another fit would bake the ratio between the\n // two into cameraZoom, and the framing would drift a little on every orbit.\n const base = this.baseFrameZoom(\n this.camera.right - this.camera.left,\n this.camera.top - this.camera.bottom,\n );\n // Divide interactionZoom back out too: a live scroll→cameraZoom reaction multiplies camera.zoom\n // for the preview, but only the authored zoom belongs in the persisted (and exported) config.\n if (base > 0) {\n this.config.cameraZoom = roundTo(this.camera.zoom / base / (this.interactionZoom || 1), 3);\n }\n if (this.orbit) {\n const t = this.orbit.target;\n this.config.cameraTarget = {\n x: roundTo(t.x, 3),\n y: roundTo(t.y, 3),\n z: roundTo(t.z, 3),\n };\n this.config.cameraDistance = roundTo(p.distanceTo(this.orbit.target), 3);\n }\n }\n\n /** Pointer position in normalized device coords (-1..1), from a canvas-relative event. */\n private pointerNdc(ev: PointerEvent): THREE.Vector2 {\n const rect = this.renderer.domElement.getBoundingClientRect();\n return new THREE.Vector2(\n ((ev.clientX - rect.left) / rect.width) * 2 - 1,\n -((ev.clientY - rect.top) / rect.height) * 2 + 1,\n );\n }\n\n private onPointerDown = (ev: PointerEvent): void => {\n if (!this.editing || !this.transform) return;\n if (ev.button !== 0) return; // only left-drag moves objects; right-drag rotates the camera\n if (this.transform.dragging || this.transform.axis) return; // on a gizmo handle → let it move\n this.raycaster.setFromCamera(this.pointerNdc(ev), this.camera);\n const helpers = this.editMode === \"wave\" ? this.waveHelpers : this.lightHelpers;\n const hit = this.raycaster.intersectObjects(helpers, false)[0];\n if (!hit) {\n // Missed every handle → pan the view (the tool's normal left-drag). OrbitControls' LEFT is\n // unmapped in edit mode, so onPointerMove pans manually without fighting the object drag.\n if (this.orbit) {\n this.panState = { lastNdc: this.pointerNdc(ev) };\n this.renderer.domElement.setPointerCapture?.(ev.pointerId);\n }\n return;\n }\n const idx = helpers.indexOf(hit.object as THREE.Mesh);\n if (idx < 0) return;\n if (this.editMode === \"wave\") this.selectWaveHandle(idx);\n else this.selectLight(idx);\n // Free screen-plane drag: the WHOLE marker is grabbable (not just the thin gizmo arrows) and\n // the camera stays locked. Rotate mode uses the gizmo's rings instead, so skip it there.\n if (this.gizmoMode !== \"translate\") return;\n const helper = helpers[idx];\n const normal = this.camera.getWorldDirection(new THREE.Vector3());\n this.dragPlane.setFromNormalAndCoplanarPoint(normal, helper.position);\n const grab = new THREE.Vector3();\n if (!this.raycaster.ray.intersectPlane(this.dragPlane, grab)) return;\n this.dragState = { helper, offset: helper.position.clone().sub(grab) };\n if (this.orbit) this.orbit.enabled = false; // lock the camera for the whole drag\n this.renderer.domElement.setPointerCapture?.(ev.pointerId);\n };\n\n private onPointerMove = (ev: PointerEvent): void => {\n if (this.panState) {\n // Ortho pan: unproject the pointer delta into world units (auto-handles zoom/aspect/dpr),\n // then shift camera + orbit target together so the grabbed point tracks the cursor.\n const ndc = this.pointerNdc(ev);\n const before = new THREE.Vector3(this.panState.lastNdc.x, this.panState.lastNdc.y, 0);\n const after = new THREE.Vector3(ndc.x, ndc.y, 0);\n const delta = before.unproject(this.camera).sub(after.unproject(this.camera)); // opposite the cursor\n this.camera.position.add(delta);\n this.panState.lastNdc = ndc;\n if (this.orbit) {\n this.orbit.target.add(delta);\n this.orbit.update(); // fires 'change' → persists the new framing + renders\n } else {\n this.camera.updateProjectionMatrix();\n if (!this.running) this.renderOnce();\n }\n return;\n }\n if (!this.dragState) return;\n this.raycaster.setFromCamera(this.pointerNdc(ev), this.camera);\n const p = new THREE.Vector3();\n if (!this.raycaster.ray.intersectPlane(this.dragPlane, p)) return;\n this.dragState.helper.position.copy(p.add(this.dragState.offset));\n this.onGizmoMoved(); // write the new position into config + uniforms\n if (!this.running) this.renderOnce();\n };\n\n private onPointerUp = (ev: PointerEvent): void => {\n if (this.panState) {\n this.panState = undefined;\n this.renderer.domElement.releasePointerCapture?.(ev.pointerId);\n return;\n }\n if (!this.dragState) return;\n this.dragState = undefined;\n if (this.orbit) this.orbit.enabled = true; // still editing → keep right-drag camera rotate\n this.renderer.domElement.releasePointerCapture?.(ev.pointerId);\n };\n\n private selectLight(i: number): void {\n this.selectedLight = i;\n const h = this.lightHelpers[i];\n if (h && this.transform) this.transform.attach(h);\n else this.transform?.detach();\n this.onLightsChanged?.(i);\n if (!this.running) this.renderOnce();\n }\n\n private selectWaveHandle(i: number): void {\n this.selectedWave = i;\n const h = this.waveHelpers[i];\n if (h && this.transform) this.transform.attach(h);\n else this.transform?.detach();\n this.onWaveChanged?.();\n if (!this.running) this.renderOnce();\n }\n\n /** Gizmo drag → route to the active mode's writer. */\n private onGizmoMoved = (): void => {\n if (this.editMode === \"wave\") this.onWaveGizmoMoved();\n else this.onLightGizmoMoved();\n };\n\n /** Light gizmo drag → write the moved handle back into the config + uniforms. */\n private onLightGizmoMoved(): void {\n const h = this.lightHelpers[this.selectedLight];\n const light = this.config.lights?.[this.selectedLight];\n if (!h || !light) return;\n light.position.x = roundTo(h.position.x, 2);\n light.position.y = roundTo(h.position.y, 2);\n light.position.z = roundTo(h.position.z, 2);\n this.pushLightUniforms();\n this.onLightsChanged?.(this.selectedLight);\n }\n\n /** Wave gizmo drag → the whole-wave box writes config.position (and the wave handles\n * follow it); a per-wave sphere writes that layer's offset (relative to config.position). */\n private onWaveGizmoMoved(): void {\n const h = this.waveHelpers[this.selectedWave];\n if (!h) return;\n const wave = this.config.waves[h.userData.index as number];\n if (!wave) return;\n // Mutate the wave's vectors IN PLACE (don't reassign a new object): the panel's Transform\n // sliders hold a reference to these objects, so replacing them would leave the sliders\n // reading the stale old object even though the wave moved.\n if (this.gizmoMode === \"rotate\") {\n wave.rotation.x = roundTo(THREE.MathUtils.radToDeg(h.rotation.x), 2);\n wave.rotation.y = roundTo(THREE.MathUtils.radToDeg(h.rotation.y), 2);\n wave.rotation.z = roundTo(THREE.MathUtils.radToDeg(h.rotation.z), 2);\n } else {\n wave.position.x = roundTo(h.position.x, 2);\n wave.position.y = roundTo(h.position.y, 2);\n wave.position.z = roundTo(h.position.z, 2);\n }\n this.pushWaveTransforms();\n this.onWaveChanged?.();\n }\n\n /** Reconcile the helper spheres with config.lights (count, position, colour). */\n private syncLightHelpers(): void {\n const lights = this.config.lights ?? [];\n while (this.lightHelpers.length < lights.length) {\n const mesh = new THREE.Mesh(\n new THREE.SphereGeometry(0.28, 16, 12),\n new THREE.MeshBasicMaterial({ color: 0xffffff, depthTest: false, transparent: true }),\n );\n mesh.renderOrder = 999;\n this.overlay.add(mesh);\n this.lightHelpers.push(mesh);\n }\n while (this.lightHelpers.length > lights.length) {\n const mesh = this.lightHelpers.pop();\n if (!mesh) break;\n this.overlay.remove(mesh);\n mesh.geometry.dispose();\n (mesh.material as THREE.Material).dispose();\n }\n lights.forEach((l, i) => {\n const h = this.lightHelpers[i];\n h.position.set(l.position.x, l.position.y, l.position.z);\n (h.material as THREE.MeshBasicMaterial).color.set(l.color);\n });\n if (this.selectedLight >= this.lightHelpers.length) {\n this.selectedLight = Math.max(0, this.lightHelpers.length - 1);\n }\n const sel = this.lightHelpers[this.selectedLight];\n if (sel && this.transform && this.transform.object !== sel) this.transform.attach(sel);\n if (!sel) this.transform?.detach();\n }\n\n private clearLightHelpers(): void {\n for (const mesh of this.lightHelpers) {\n this.overlay.remove(mesh);\n mesh.geometry.dispose();\n (mesh.material as THREE.Material).dispose();\n }\n this.lightHelpers = [];\n }\n\n /** Reconcile the wave drag handles with config: one box handle per wave, sitting at that\n * wave's absolute position (and oriented to its rotation so the rotate gizmo starts there). */\n private syncWaveHelpers(): void {\n if (this.transform?.dragging) return; // don't yank a handle out from under an active drag\n const waves = this.config.waves ?? [];\n const wantTotal = waves.length;\n if (this.waveHelpers.length !== wantTotal) {\n this.clearWaveHelpers();\n for (let i = 0; i < wantTotal; i++) {\n const box = new THREE.Mesh(\n new THREE.BoxGeometry(0.55, 0.55, 0.55),\n new THREE.MeshBasicMaterial({ color: 0x39d0ff, depthTest: false, transparent: true }),\n );\n box.renderOrder = 999;\n box.userData = { kind: \"wave\", index: i };\n this.overlay.add(box);\n this.waveHelpers.push(box);\n }\n }\n for (const h of this.waveHelpers) {\n const sc = waves[h.userData.index as number];\n if (!sc) continue;\n h.position.set(sc.position.x, sc.position.y, sc.position.z);\n h.rotation.set(\n THREE.MathUtils.degToRad(sc.rotation.x),\n THREE.MathUtils.degToRad(sc.rotation.y),\n THREE.MathUtils.degToRad(sc.rotation.z),\n );\n }\n if (this.selectedWave >= this.waveHelpers.length) this.selectedWave = 0;\n const sel = this.waveHelpers[this.selectedWave];\n if (sel && this.transform && this.transform.object !== sel) this.transform.attach(sel);\n if (!sel) this.transform?.detach();\n }\n\n private clearWaveHelpers(): void {\n for (const mesh of this.waveHelpers) {\n this.overlay.remove(mesh);\n mesh.geometry.dispose();\n (mesh.material as THREE.Material).dispose();\n }\n this.waveHelpers = [];\n }\n\n /** Reposition just the wave MESHES from each wave's absolute transform — the transform\n * subset of refresh(), used live during a wave gizmo drag so the ribbon follows without a\n * full uniform re-push or a helper resync. */\n private pushWaveTransforms(): void {\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n wave.mesh.scale.set(sc.scale.x, sc.scale.y, sc.scale.z);\n wave.mesh.rotation.set(\n THREE.MathUtils.degToRad(sc.rotation.x),\n THREE.MathUtils.degToRad(sc.rotation.y),\n THREE.MathUtils.degToRad(sc.rotation.z),\n );\n wave.mesh.position.set(sc.position.x, sc.position.y, sc.position.z);\n });\n if (!this.running) this.renderOnce();\n }\n\n /** Pull the camera back to a 3/4 angle that frames the edit target: the origin wave + all\n * lights (light mode), or a region around config.position + the handles (wave mode). */\n private frameEditCamera(): void {\n const box = new THREE.Box3();\n if (this.editMode === \"wave\") {\n // The wave can sit far from the origin (some presets push position to the hundreds), so\n // frame around it — a ~200-unit margin shows a good chunk of the ×10-scaled ribbon.\n const target = this.config.waves[this.selectedWave] ?? this.config.waves[0];\n const c = new THREE.Vector3(target.position.x, target.position.y, target.position.z);\n box.expandByPoint(c.clone().addScalar(200));\n box.expandByPoint(c.clone().addScalar(-200));\n for (const h of this.waveHelpers) box.expandByPoint(h.position);\n } else {\n // The baked + scaled wave spans ~±25 units; frame that plus any lights.\n box.expandByPoint(new THREE.Vector3(25, 25, 25));\n box.expandByPoint(new THREE.Vector3(-25, -25, -25));\n for (const l of this.config.lights ?? []) {\n box.expandByPoint(new THREE.Vector3(l.position.x, l.position.y, l.position.z));\n }\n }\n const sphere = box.getBoundingSphere(new THREE.Sphere());\n const radius = Math.max(sphere.radius, 2);\n const dir = new THREE.Vector3(0.45, 0.35, 1).normalize();\n this.camera.position.copy(sphere.center).addScaledVector(dir, radius * 3 + 200);\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(sphere.center);\n // Ortho: frame by zoom (frustum is in px), not distance.\n this.camera.zoom = (this.camera.right - this.camera.left) / Math.max(1, radius * 2.6);\n this.camera.updateProjectionMatrix();\n if (this.orbit) {\n this.orbit.target.copy(sphere.center);\n this.orbit.update();\n }\n }\n\n /** Restore the authored camera (from config) — used when leaving light-edit. */\n private restoreHeroCamera(): void {\n const p = this.config.cameraPosition;\n const t = this.config.cameraTarget;\n this.camera.position.set(p.x, p.y, p.z);\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(t.x, t.y, t.z);\n if (this.orbit) {\n this.orbit.target.set(t.x, t.y, t.z);\n this.orbit.update();\n }\n }\n\n /** Push only the light uniforms (used live during a gizmo drag). */\n private pushLightUniforms(): void {\n const lights = this.config.lights ?? [];\n for (const wave of this.waves) {\n const u = wave.material.uniforms;\n u.uNumLights.value = Math.min(lights.length, MAX_LIGHTS);\n const lPos = u.uLightPos.value as THREE.Vector3[];\n const lCol = u.uLightColor.value as THREE.Vector3[];\n const lInt = u.uLightIntensity.value as number[];\n for (let li = 0; li < MAX_LIGHTS; li++) {\n const light = lights[li];\n if (light) {\n lPos[li].set(light.position.x, light.position.y, light.position.z);\n hexToLinearVec3(light.color, lCol[li]);\n lInt[li] = light.intensity;\n } else {\n lInt[li] = 0;\n }\n }\n }\n if (!this.running) this.renderOnce();\n }\n\n // ---- Hook overrides: plug the editor behavior into the base render pipeline (5 hook points) ----\n\n protected override isCameraExternallyDriven(): boolean {\n return !!this.orbit || this.editing;\n }\n\n protected override onAfterRefresh(): void {\n if (this.editMode === \"light\") this.syncLightHelpers();\n else if (this.editMode === \"wave\") this.syncWaveHelpers();\n // refresh() may have just (re)created the interaction controller via syncInteraction(); re-apply\n // the scroll preview so a freshly-added scroll binding responds immediately instead of being\n // stuck on the (frozen, in-studio) live scroll.\n if (this.interaction) this.interaction.scrollOverride = this.scrollPreview;\n }\n\n protected override onAfterRenderFrame(): void {\n // Draw the light/wave gizmo helpers on top, crisp (not through the post pass), and never into\n // exports. Guarded on `overlay` because the base constructor renders a first frame before this\n // subclass's own fields are initialized (base-ctor-calls-virtual-hook order).\n if (this.overlay && this.editing && !this.capturing && this.overlay.children.length > 0) {\n const helpers = this.editMode === \"wave\" ? this.waveHelpers : this.lightHelpers;\n for (const h of helpers) {\n h.scale.setScalar(Math.max(0.1, this.camera.position.distanceTo(h.position) * 0.09));\n }\n this.renderer.autoClear = false;\n this.renderer.setRenderTarget(null); // draw to the screen, not a leftover composer buffer\n this.renderer.render(this.overlay, this.camera);\n this.renderer.autoClear = true;\n }\n this.renderMinimap();\n }\n\n protected override onAfterResize(): void {\n if (this.cameraRigOn) this.positionMinimapBtn();\n }\n\n protected override applyCameraFromConfig(): void {\n if (this.editing) return; // a 3D-edit gizmo owns the camera; don't fight it\n const p = this.config.cameraPosition;\n const tg = this.config.cameraTarget;\n this.suppressCameraChange = true;\n this.camera.position.set(p.x, p.y, p.z);\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(tg.x, tg.y, tg.z);\n if (this.orbit) {\n this.orbit.target.set(tg.x, tg.y, tg.z);\n this.orbit.update();\n }\n this.applyZoom();\n this.suppressCameraChange = false;\n this.onCameraChanged?.(); // keep the panel's camera sliders in sync\n if (!this.running) this.renderOnce();\n }\n\n override dispose(): void {\n window.removeEventListener(\"keydown\", this.onKeyDown);\n this.renderer.domElement.removeEventListener(\"pointerdown\", this.onPointerDown);\n this.renderer.domElement.removeEventListener(\"pointerdown\", this.onCursorDown);\n window.removeEventListener(\"pointerup\", this.onCursorUp);\n this.transform?.detach();\n this.transform?.dispose();\n this.orbit?.dispose();\n this.minimapBtn?.remove();\n this.clearLightHelpers();\n this.clearWaveHelpers();\n for (const m of this.minimapLights) {\n m.geometry.dispose();\n (m.material as THREE.Material).dispose();\n }\n super.dispose();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"StudioWaveRenderer.js","names":[],"sources":["../../src/studio/StudioWaveRenderer.ts"],"sourcesContent":["// The studio's editor-enabled renderer: WaveRenderer + orbit/zoom/pan, the light & wave drag\n// gizmos, and the camera-rig minimap. Lives in @wave3d/core/studio so the drop-in shell and the\n// standalone build (which never edit) don't pay for ~1,000 lines of editor code. Every member here\n// moved verbatim out of WaveRenderer; the base exposes exactly five protected hook points the\n// overrides below plug into.\nimport * as THREE from \"three\";\nimport type { OrbitControls } from \"three/addons/controls/OrbitControls.js\";\nimport type { TransformControls } from \"three/addons/controls/TransformControls.js\";\nimport { WaveRenderer, hexToLinearVec3 } from \"../renderer/WaveRenderer\";\nimport { createLight, DEFAULT_LIGHT_POSITION, MAX_LIGHTS } from \"../config/model\";\nimport type { LightConfig } from \"../config/model\";\nimport { roundTo } from \"../util/math\";\n\n// The minimap's fixed 3/4 vantage direction.\nconst MINIMAP_VANTAGE = new THREE.Vector3(0.85, 0.6, 1).normalize();\n\nexport class StudioWaveRenderer extends WaveRenderer {\n /** Set while the panel drives the camera, so orbit's 'change' doesn't re-refresh the\n * panel mid-drag (the panel already knows the new value). */\n private suppressCameraChange = false;\n\n /** Studio scroll-preview value (null = live). Persisted here so it survives controller\n * recreation — a scroll binding added after the preview was set still responds (see\n * onAfterRefresh). */\n private scrollPreview: number | null = null;\n\n // Same for the minimap, which renders every frame while the camera rig is open.\n private readonly miniBox = new THREE.Box3();\n private readonly miniSphere = new THREE.Sphere();\n private readonly miniTmpA = new THREE.Vector3();\n private readonly miniTmpB = new THREE.Vector3();\n private readonly miniPrevColor = new THREE.Color();\n private readonly miniSize = new THREE.Vector2();\n // Per-wave blend/transparent stash for the minimap's forced-opaque draw (index-parallel to waves).\n private readonly miniBlendPrev: THREE.Blending[] = [];\n private readonly miniTransPrev: boolean[] = [];\n\n // --- Camera-rig minimap (corner inset: the wave + a little camera/light marker) ---\n private cameraRigOn = false;\n private cameraRigCollapsed = false;\n private minimapCamera?: THREE.PerspectiveCamera;\n private camMarker?: THREE.Group;\n /** Gold markers in the minimap, one per rig light (positions/colours tracked live). */\n private minimapLights: THREE.Mesh[] = [];\n /** Shown in the rig when no light has been added yet, so the light is always visible there.\n * Matches where \"drag in 3D\" creates the first light, so the marker doesn't jump when added. */\n private readonly defaultRigLight: LightConfig = createLight({ ...DEFAULT_LIGHT_POSITION }, 1);\n private minimapBtn?: HTMLButtonElement;\n\n // --- Camera controls (orbit/zoom/pan) + light-editing gizmo ---\n private readonly overlay = new THREE.Scene();\n private readonly raycaster = new THREE.Raycaster();\n private orbit?: OrbitControls;\n private transform?: TransformControls;\n /** Whether the main view orbit/zoom/pan is on (studio); off for the embed. */\n private mainOrbitOn = false;\n private lightHelpers: THREE.Mesh[] = [];\n /** Which 3D-editing gizmo is active: none, dragging lights, or dragging the wave/waves. */\n private editMode: \"none\" | \"light\" | \"wave\" = \"none\";\n private selectedLight = 0;\n /** Wave/wave drag handles: index 0 = the whole-wave box (moves config.position); 1..N =\n * per-wave spheres (move each layer's offset), shown only when there's >1 wave. */\n private waveHelpers: THREE.Mesh[] = [];\n private selectedWave = 0;\n /** Gizmo operation: \"translate\" moves the handle, \"rotate\" spins the whole wave. */\n private gizmoMode: \"translate\" | \"rotate\" = \"translate\";\n /** Active free screen-plane drag of a handle (grab anywhere on the marker, camera locked). */\n private dragState?: { helper: THREE.Mesh; offset: THREE.Vector3 };\n private readonly dragPlane = new THREE.Plane();\n /** Active left-drag camera pan in edit mode (the press missed every handle → move the view). */\n private panState?: { lastNdc: THREE.Vector2 };\n /** Camera snapshot taken when entering a 3D-edit mode and restored verbatim on exit, so the\n * view returns exactly where it was (position + ortho zoom + up) rather than snapping to the\n * authored hero framing. */\n private returnCamera: {\n pos: THREE.Vector3;\n target: THREE.Vector3;\n zoom: number;\n up: THREE.Vector3;\n } | null = null;\n\n /** Set by the panel: fired after a gizmo drag/selection so sliders can refresh. */\n onLightsChanged?: (selected: number) => void;\n /** Set by the panel: fired after orbit moves the camera so sliders can refresh. */\n onCameraChanged?: () => void;\n /** Set by the panel: fired after a wave/wave gizmo drag/selection so the position and\n * per-wave offset sliders can refresh. */\n onWaveChanged?: () => void;\n\n // ---------------- 3D editing (draggable gizmo: lights or wave/waves) ----------------\n\n /** True while any drag-in-3D gizmo owns the camera (light or wave). */\n private get editing(): boolean {\n return this.editMode !== \"none\";\n }\n\n isLightEditMode(): boolean {\n return this.editMode === \"light\";\n }\n\n isWaveEditMode(): boolean {\n return this.editMode === \"wave\";\n }\n\n /** Toggle 3D light editing: show draggable light handles; off restores the prior view. */\n async setLightEditMode(on: boolean): Promise<void> {\n await this.setEditMode(on ? \"light\" : \"none\");\n }\n\n /** Toggle 3D wave/wave editing: drag the whole wave (and each wave when there's >1). */\n async setWaveEditMode(on: boolean): Promise<void> {\n await this.setEditMode(on ? \"wave\" : \"none\");\n }\n\n /** Enter/leave/switch a 3D-edit mode. Modes are mutually exclusive — turning one on turns\n * the other off. The camera is snapshotted on the first entry and restored on the final exit\n * (so light↔wave switches keep the same return view). */\n private async setEditMode(mode: \"none\" | \"light\" | \"wave\"): Promise<void> {\n if (mode === this.editMode) return;\n const prev = this.editMode;\n // Tear down the previous mode's handles + gizmo.\n if (prev !== \"none\") {\n if (this.transform) this.transform.enabled = false;\n this.transform?.detach();\n if (prev === \"light\") this.clearLightHelpers();\n else this.clearWaveHelpers();\n }\n this.editMode = mode;\n // Leaving LIGHT mode undoes its transient 3/4 framing (back to the pre-edit camera). Wave\n // editing never moves the camera, so there's nothing to restore when leaving it.\n if (prev === \"light\") this.restoreReturnCamera();\n if (mode === \"none\") {\n if (this.orbit) this.orbit.enabled = this.mainOrbitOn; // keep main-view orbit on\n this.setOrbitForEdit(false); // restore left-drag camera pan\n this.dragState = undefined;\n this.renderOnce();\n return;\n }\n await this.ensureGizmo();\n if (this.editMode !== mode) return; // toggled away while controls lazy-loaded\n if (this.orbit) this.orbit.enabled = true;\n this.setOrbitForEdit(true); // left-drag on a handle moves it; on empty space it pans\n this.gizmoMode = \"translate\"; // each mode entry starts in move mode (rotate is wave-only)\n this.transform?.setMode(\"translate\");\n this.transform?.setSpace(\"world\"); // reset to world-space translate on every entry\n if (this.transform) this.transform.enabled = true;\n if (mode === \"light\") {\n // Light editing reframes to a 3/4 working angle, so snapshot the current view first and\n // restore it on exit (the framing is transient, not part of the authored composition).\n this.captureReturnCamera();\n this.syncLightHelpers();\n this.frameEditCamera();\n this.selectLight(Math.min(this.selectedLight, Math.max(0, this.lightHelpers.length - 1)));\n } else {\n // Wave editing leaves the camera exactly where it is — no reframing, no zoom — so the view\n // stays put on enter AND exit; you pan/rotate/zoom normally to reach a handle and drag it.\n this.syncWaveHelpers();\n this.selectWaveHandle(Math.min(this.selectedWave, Math.max(0, this.waveHelpers.length - 1)));\n }\n this.renderOnce();\n }\n\n /** In edit mode, take PAN off OrbitControls' left button so left-drag is free to grab handles;\n * onPointerDown/Move pan the camera manually when a left-press misses every handle (so panning\n * never fights the object drag). Right-drag still rotates the camera; scroll/middle zooms. */\n private setOrbitForEdit(editing: boolean): void {\n if (!this.orbit) return;\n this.orbit.mouseButtons = editing\n ? { MIDDLE: THREE.MOUSE.DOLLY, RIGHT: THREE.MOUSE.ROTATE }\n : { LEFT: THREE.MOUSE.PAN, MIDDLE: THREE.MOUSE.DOLLY, RIGHT: THREE.MOUSE.ROTATE };\n }\n\n /** Switch the wave-edit gizmo between moving handles and rotating the whole wave. Rotate\n * targets the whole-wave box (config.rotation), so selecting it makes the intent obvious. */\n setGizmoMode(mode: \"translate\" | \"rotate\"): void {\n this.gizmoMode = mode;\n this.transform?.setMode(mode);\n // Rotate in LOCAL space so the gizmo rings reorient with the wave — a visual read-out of its\n // current rotation; translate stays in world space so the arrows track the world axes.\n this.transform?.setSpace(mode === \"rotate\" ? \"local\" : \"world\");\n if (mode === \"rotate\" && this.editMode === \"wave\") {\n const waveIdx = this.waveHelpers.findIndex((h) => h.userData.kind === \"wave\");\n if (waveIdx >= 0) this.selectWaveHandle(waveIdx);\n }\n if (!this.running) this.renderOnce();\n }\n\n getGizmoMode(): \"translate\" | \"rotate\" {\n return this.gizmoMode;\n }\n\n /** Snapshot the live camera so leaving edit mode returns exactly here (incl. ortho zoom). */\n private captureReturnCamera(): void {\n this.returnCamera = {\n pos: this.camera.position.clone(),\n target: this.orbit\n ? this.orbit.target.clone()\n : this.camera.getWorldDirection(new THREE.Vector3()).add(this.camera.position),\n zoom: this.camera.zoom,\n up: this.camera.up.clone(),\n };\n }\n\n /** Restore the snapshot from captureReturnCamera (falls back to the authored hero camera). */\n private restoreReturnCamera(): void {\n const s = this.returnCamera;\n this.returnCamera = null;\n if (!s) {\n this.restoreHeroCamera();\n return;\n }\n this.camera.position.copy(s.pos);\n this.camera.up.copy(s.up);\n this.camera.zoom = s.zoom;\n this.camera.updateProjectionMatrix();\n if (this.orbit) {\n this.orbit.target.copy(s.target);\n this.orbit.update(); // fires onControlsChange → writes the restored view back to config\n } else {\n this.camera.lookAt(s.target);\n }\n }\n\n /** Turn on mouse/trackpad orbit + zoom + pan + arrow-key orbit (studio only). */\n async enableOrbit(): Promise<void> {\n this.mainOrbitOn = true;\n this.renderer.domElement.style.cursor = \"move\"; // 4-way move arrows: left-drag pans the view\n window.addEventListener(\"keydown\", this.onKeyDown);\n await this.ensureOrbit();\n if (this.orbit && !this.editing) this.orbit.enabled = true;\n }\n\n /** Arrow keys orbit the camera around the target (←/→ azimuth, ↑/↓ elevation). */\n private onKeyDown = (e: KeyboardEvent): void => {\n if (!this.mainOrbitOn || !this.orbit || this.editing) return;\n const t = e.target instanceof HTMLElement ? e.target : null;\n if (t && (t.closest(\"#panel\") || /^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName))) return; // let the panel keep arrows\n const step = e.shiftKey ? 0.015 : 0.05;\n let az = 0;\n let pol = 0;\n if (e.key === \"ArrowLeft\") az = -step;\n else if (e.key === \"ArrowRight\") az = step;\n else if (e.key === \"ArrowUp\") pol = -step;\n else if (e.key === \"ArrowDown\") pol = step;\n else return;\n e.preventDefault();\n const offset = this.camera.position.clone().sub(this.orbit.target);\n const sph = new THREE.Spherical().setFromVector3(offset);\n sph.theta += az;\n sph.phi = THREE.MathUtils.clamp(sph.phi + pol, 0.05, Math.PI - 0.05);\n offset.setFromSpherical(sph);\n this.camera.position.copy(this.orbit.target).add(offset);\n this.orbit.update(); // fires 'change' → writes camera to config + renders\n };\n\n /** Reset the camera to the straight-on hero framing at the configured distance. */\n resetView(): void {\n this.camera.position.copy(this.homeCamPos);\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(this.homeCamTarget);\n if (this.orbit) {\n this.orbit.target.copy(this.homeCamTarget);\n this.orbit.update();\n }\n this.writeCameraToConfig();\n this.onCameraChanged?.();\n if (!this.running) this.renderOnce();\n }\n\n /** Dolly/aim the camera so the whole wave fills the viewport (keeps the view angle).\n * Fits the geometry box's actual *projected* screen extent — tighter than a bounding\n * sphere for a flat, diagonal ribbon. */\n fitToView(): void {\n const box = new THREE.Box3();\n for (const s of this.waves) {\n s.mesh.updateWorldMatrix(true, false);\n if (!s.mesh.geometry.boundingBox) s.mesh.geometry.computeBoundingBox();\n const bb = s.mesh.geometry.boundingBox;\n if (bb) box.union(bb.clone().applyMatrix4(s.mesh.matrixWorld));\n }\n if (box.isEmpty()) return;\n\n const center = box.getCenter(new THREE.Vector3());\n\n // Aim at the centre, then measure how much of the viewport the box spans (in NDC).\n if (this.orbit) this.orbit.target.copy(center);\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(center);\n this.camera.updateMatrixWorld(true);\n const c = box.min,\n m = box.max;\n let frac = 0;\n const v = new THREE.Vector3();\n for (let i = 0; i < 8; i++) {\n v.set(i & 1 ? m.x : c.x, i & 2 ? m.y : c.y, i & 4 ? m.z : c.z).project(this.camera);\n frac = Math.max(frac, Math.abs(v.x), Math.abs(v.y)); // |ndc| 0→1 = half-viewport\n }\n // Overfill slightly (>1): the folded geometry's bounding box has empty diagonal\n // corners, so filling past the box edges lets the actual ribbon fill the frame.\n // Ortho: framing is the zoom, not the distance.\n const target = 1.18;\n this.config.cameraZoom = ((this.config.cameraZoom ?? 1) * target) / Math.max(0.001, frac);\n this.applyZoom();\n if (this.orbit) this.orbit.update();\n this.writeCameraToConfig();\n this.onCameraChanged?.();\n if (!this.running) this.renderOnce();\n }\n\n /** Dolly the camera to a distance from the orbit target (or set z when no orbit). */\n setCameraDistance(d: number): void {\n if (this.orbit) {\n const dir = this.camera.position.clone().sub(this.orbit.target);\n const len = dir.length() || 1;\n this.camera.position.copy(this.orbit.target).addScaledVector(dir.multiplyScalar(1 / len), d);\n this.orbit.update();\n } else {\n this.camera.position.z = d;\n }\n this.writeCameraToConfig();\n if (!this.running) this.renderOnce();\n }\n\n /** The current look-at target (orbit's if present, else from config). */\n private camTarget(): THREE.Vector3 {\n if (this.orbit) return this.orbit.target;\n const t = this.config.cameraTarget;\n return new THREE.Vector3(t.x, t.y, t.z);\n }\n\n /** Read the camera as orbit values for the panel (angles in degrees). */\n getCameraOrbit(): {\n azimuth: number;\n elevation: number;\n distance: number;\n panX: number;\n panY: number;\n } {\n const t = this.camTarget();\n const sph = new THREE.Spherical().setFromVector3(this.camera.position.clone().sub(t));\n return {\n azimuth: THREE.MathUtils.radToDeg(sph.theta),\n elevation: 90 - THREE.MathUtils.radToDeg(sph.phi),\n distance: sph.radius,\n panX: t.x,\n panY: t.y,\n };\n }\n\n /** Place the camera at azimuth/elevation (degrees) + distance around the target. */\n setCameraOrbit(azimuthDeg: number, elevationDeg: number, distance: number): void {\n const target = this.camTarget();\n const sph = new THREE.Spherical(\n Math.max(0.01, distance),\n THREE.MathUtils.degToRad(90 - elevationDeg),\n THREE.MathUtils.degToRad(azimuthDeg),\n );\n sph.makeSafe();\n this.suppressCameraChange = true;\n this.camera.position.copy(target).add(new THREE.Vector3().setFromSpherical(sph));\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(target);\n if (this.orbit) this.orbit.update();\n this.suppressCameraChange = false;\n this.writeCameraToConfig();\n if (!this.running) this.renderOnce();\n }\n\n /** Roll the camera around its view axis (degrees) — tilts the composition without\n * moving the camera. Applied after positioning; reset by any orbit interaction. */\n rollView(deg: number): void {\n this.camera.rotateZ(THREE.MathUtils.degToRad(deg));\n this.camera.updateMatrixWorld();\n if (!this.running) this.renderOnce();\n }\n\n /** Pan: move the look-at target (and camera with it) to (x, y) in world units. */\n setCameraTarget(x: number, y: number): void {\n const target = this.camTarget();\n const delta = new THREE.Vector3(x - target.x, y - target.y, 0);\n this.suppressCameraChange = true;\n this.camera.position.add(delta);\n if (this.orbit) this.orbit.target.add(delta);\n else this.config.cameraTarget = { x, y, z: target.z };\n this.camera.lookAt(this.camTarget());\n if (this.orbit) this.orbit.update();\n this.suppressCameraChange = false;\n this.writeCameraToConfig();\n if (!this.running) this.renderOnce();\n }\n\n /** Ortho zoom MULTIPLIER (the camera has no real fov). 1 = the responsive base framing (the hero crop). */\n getZoom(): number {\n return this.config.cameraZoom ?? 1;\n }\n\n setZoom(zoom: number): void {\n this.config.cameraZoom = THREE.MathUtils.clamp(zoom, 0.1, 6);\n this.applyZoom();\n if (!this.running) this.renderOnce();\n }\n\n /** Re-apply the responsive framing after config.cameraFit / cameraMinVisibleWidth were edited in\n * place (the panel binds the live config object, so there is no setConfig to hook). Embeds get\n * this for free — handle.set() → setConfig → applyCameraFromConfig → applyZoom. */\n refreshFraming(): void {\n this.applyZoom();\n if (!this.running) this.renderOnce();\n }\n\n /** Studio-only scroll preview (the studio page doesn't scroll): override the `scroll` source with\n * a fixed 0..1 value, or pass null to return to the live container-progress read. NEVER touches\n * config. No-op until interaction is enabled (the controller exists). */\n setScrollPreview(v: number | null): void {\n this.scrollPreview = v;\n if (this.interaction) {\n this.interaction.scrollOverride = v;\n // Apply the scrub immediately, whether or not the loop is \"running\". The studio page never\n // really scrolls, so this is a manual scrub that must reflect the instant you drag the slider —\n // we can't defer to the render loop to pick up scrollOverride, because the browser fully\n // SUSPENDS requestAnimationFrame whenever the studio tab isn't the foreground tab, so a\n // \"running\" loop is frozen and the slider looks dead. (The old `!this.running` guard only ever\n // covered the explicitly-paused case, missing every background-tab / occluded-window case.)\n // snapScroll() resolves just the scroll bindings, leaving live pointer/press state untouched.\n this.interaction.snapScroll();\n this.renderOnce();\n }\n // No controller yet → nothing to scrub; onInteractionReady re-applies scrollPreview when the\n // interaction chunk lands (it is fetched lazily, so it can arrive after this call).\n }\n\n /** Feed a live scroll position (0..1) from the studio scroll-test overlay — a real scrollable\n * surface the user drags/wheels. Unlike setScrollPreview's instant snap (built for the slider and\n * a possibly-frozen loop), this leaves the RUNNING render loop's update() to smooth the bindings\n * and derive `scrollVelocity` from the real scroll delta, so the wave reacts exactly as it would on\n * a scrolling page (velocity included). Falls back to a snapped frame when the loop isn't running\n * (paused / reduced-motion), so the preview still tracks. Like setScrollPreview it NEVER touches config. */\n setScrollTestProgress(v: number): void {\n this.scrollPreview = v;\n if (!this.interaction) return;\n this.interaction.scrollOverride = v;\n if (!this.running) {\n this.interaction.snapScroll();\n this.renderOnce();\n }\n }\n\n duplicateOffset(): { x: number; y: number; z: number } {\n this.camera.updateMatrixWorld();\n const worldW = (this.camera.right - this.camera.left) / this.camera.zoom; // visible world span\n const worldH = (this.camera.top - this.camera.bottom) / this.camera.zoom;\n const right = new THREE.Vector3().setFromMatrixColumn(this.camera.matrixWorld, 0).normalize();\n const up = new THREE.Vector3().setFromMatrixColumn(this.camera.matrixWorld, 1).normalize();\n // Screen-left ~40% + screen-down ~15% of the frame: enough to clearly separate the copy, small\n // enough that a few successive adds cascade diagonally and stay on-screen before running off.\n const off = right.multiplyScalar(-0.4 * worldW).add(up.multiplyScalar(-0.15 * worldH));\n return { x: off.x, y: off.y, z: 0 };\n }\n\n /** Toggle the corner camera-rig minimap (studio aid; off in the embed). */\n setCameraRig(on: boolean): void {\n this.cameraRigOn = on;\n if (on) this.ensureMinimap();\n if (this.minimapBtn) this.minimapBtn.style.display = on ? \"\" : \"none\";\n if (on) this.positionMinimapBtn();\n if (!this.running) this.renderOnce();\n }\n\n /** Corner rectangle (logical px) for the minimap viewport. */\n private minimapRect(): { x: number; y: number; size: number; pad: number } {\n const sz = this.renderer.getSize(new THREE.Vector2());\n const size = Math.round(Math.min(sz.x, sz.y) * 0.27);\n const pad = Math.round(size * 0.06);\n return { x: sz.x - size - pad, y: pad, size, pad };\n }\n\n /** Place the collapse button at the minimap's top-right (or bottom corner when collapsed). */\n private positionMinimapBtn(): void {\n const b = this.minimapBtn;\n if (!b) return;\n const { size, pad } = this.minimapRect();\n // minimapRect() is in renderer BUFFER px (= the export size); the canvas is CSS-scaled to\n // fill the container, so convert to on-screen px or the button detaches from the minimap.\n const canvas = this.renderer.domElement;\n const buf = this.renderer.getSize(new THREE.Vector2());\n const sx = buf.x > 0 ? canvas.clientWidth / buf.x : 1;\n const sy = buf.y > 0 ? canvas.clientHeight / buf.y : 1;\n b.style.right = pad * sx + \"px\";\n b.style.bottom = (this.cameraRigCollapsed ? pad * sy : (pad + size) * sy - 22) + \"px\";\n b.textContent = this.cameraRigCollapsed ? \"▴ camera\" : \"▾ camera\";\n }\n\n /** Build the minimap's fixed 3rd-person camera + the camera/light markers (once). */\n private ensureMinimap(): void {\n if (this.minimapCamera) return;\n // Pose/near/far are recomputed every frame by frameMinimap() to fit the current scene\n // (the wave sits in a ×10 ortho world, so a fixed vantage can't frame it).\n this.minimapCamera = new THREE.PerspectiveCamera(42, 1, 1, 10000);\n\n // A little camera (body + lens) marking where the main camera views the wave from.\n const marker = new THREE.Group();\n marker.add(\n new THREE.Mesh(\n new THREE.BoxGeometry(3.4, 2.4, 4.2),\n new THREE.MeshBasicMaterial({ color: 0x2a2f3d }),\n ),\n );\n const lens = new THREE.Mesh(\n new THREE.ConeGeometry(1.3, 2.4, 18),\n new THREE.MeshBasicMaterial({ color: 0x6ea8fe }),\n );\n lens.rotation.x = -Math.PI / 2; // cone points -Z (the camera's forward)\n lens.position.z = -2.7;\n marker.add(lens);\n marker.visible = false;\n this.scene.add(marker);\n this.camMarker = marker;\n\n // Collapse/expand toggle overlaid on the minimap corner.\n const btn = document.createElement(\"button\");\n btn.style.cssText =\n \"position:absolute;z-index:30;padding:2px 8px;border-radius:5px;cursor:pointer;\" +\n \"font:11px ui-sans-serif,system-ui,-apple-system,sans-serif;color:#cdd0d6;\" +\n \"background:rgba(18,18,26,0.85);border:1px solid rgba(255,255,255,0.16);\";\n btn.addEventListener(\"click\", () => {\n this.cameraRigCollapsed = !this.cameraRigCollapsed;\n this.positionMinimapBtn();\n this.renderOnce();\n });\n this.container.appendChild(btn);\n this.minimapBtn = btn;\n this.positionMinimapBtn();\n }\n\n /** The lights the rig should show: the configured lights, or a single default-position\n * marker when none has been added yet — so the light is always visible in the rig. */\n private rigLights(): LightConfig[] {\n const lights = this.config.lights ?? [];\n return lights.length ? lights : [this.defaultRigLight];\n }\n\n /** Reconcile the minimap's light markers with the rig lights (count, position, colour). */\n private syncMinimapLights(visible: boolean): void {\n const lights = this.rigLights();\n while (this.minimapLights.length < lights.length) {\n const m = new THREE.Mesh(\n new THREE.SphereGeometry(2.2, 16, 12),\n new THREE.MeshBasicMaterial({ color: 0xffd24a }),\n );\n m.visible = false;\n this.scene.add(m);\n this.minimapLights.push(m);\n }\n while (this.minimapLights.length > lights.length) {\n const m = this.minimapLights.pop();\n if (!m) break;\n this.scene.remove(m);\n m.geometry.dispose();\n (m.material as THREE.Material).dispose();\n }\n lights.forEach((l, i) => {\n const m = this.minimapLights[i];\n m.position.set(l.position.x, l.position.y, l.position.z);\n (m.material as THREE.MeshBasicMaterial).color.set(l.color); // track the light's colour\n m.visible = visible;\n });\n }\n\n /** Fit the minimap's 3rd-person camera to the wave (+ lights) and size/place the camera\n * proxy, so the rig reads at any scene scale. The main camera is orthographic — its literal\n * distance is arbitrary — so the proxy sits a fixed multiple of the scene radius back along\n * the true view direction rather than at the far-away ortho position. */\n private frameMinimap(): void {\n const cam = this.minimapCamera;\n const marker = this.camMarker;\n if (!cam || !marker) return;\n const box = this.miniBox.setFromObject(this.group);\n if (box.isEmpty()) return; // geometry not built yet\n for (const l of this.rigLights()) {\n box.expandByPoint(this.miniTmpA.set(l.position.x, l.position.y, l.position.z));\n }\n const subject = box.getBoundingSphere(this.miniSphere);\n const radius = Math.max(subject.radius, 1);\n\n // Point the little camera at the wave from its real view direction, kept a sane distance\n // away (using the ortho camera's actual z would push it thousands of units off and dwarf\n // the wave).\n const viewDir = this.camera.getWorldDirection(this.miniTmpA); // points toward the wave\n const markerPos = this.miniTmpB.copy(subject.center).addScaledVector(viewDir, -radius * 1.5);\n marker.position.copy(markerPos);\n marker.quaternion.copy(this.camera.quaternion);\n marker.scale.setScalar(radius * 0.05);\n for (const m of this.minimapLights) m.scale.setScalar(radius * 0.045);\n\n // Frame the whole rig (wave + proxy) from a fixed 3/4 vantage. (`rig` reuses the sphere\n // behind `subject`, which has no readers past this point.)\n box.expandByPoint(markerPos);\n const rig = box.getBoundingSphere(this.miniSphere);\n const frameR = Math.max(rig.radius, 1);\n cam.position.copy(rig.center).addScaledVector(MINIMAP_VANTAGE, frameR * 2.9);\n cam.near = Math.max(1, frameR * 0.02);\n cam.far = frameR * 10;\n cam.up.set(0, 1, 0);\n cam.lookAt(rig.center);\n cam.updateProjectionMatrix();\n }\n\n /** Draw the camera-rig minimap into a corner viewport (called after the main render). */\n private renderMinimap(): void {\n if (!this.cameraRigOn || this.cameraRigCollapsed || !this.mainOrbitOn || this.capturing) return;\n if (!this.minimapCamera || !this.camMarker) return;\n // setViewport/setScissor take LOGICAL (CSS) pixels — three applies pixelRatio itself.\n const { x, y, size } = this.minimapRect();\n\n this.camMarker.visible = true;\n this.syncMinimapLights(true);\n this.frameMinimap();\n\n const r = this.renderer;\n const prevColor = this.miniPrevColor;\n r.getClearColor(prevColor);\n const prevAlpha = r.getClearAlpha();\n r.autoClear = false;\n r.setRenderTarget(null); // draw to the screen — NOT a leftover composer buffer\n r.setScissorTest(true);\n r.setViewport(x, y, size, size);\n r.setScissor(x, y, size, size);\n r.setClearColor(0x12121a, 0.92);\n r.clear(true, true);\n // scene.background (the wave's page colour / image / gradient) fills ANY camera's view, so\n // hide it while drawing the minimap — otherwise it covers the 3rd-person wave.\n const prevBg = this.scene.background;\n this.scene.background = null;\n // The wave's own blend mode (additive for the neon / Spider-Man presets) makes it vanish on\n // the dark minimap backdrop; force opaque normal blending just for this draw so the shape\n // always reads. The main render already happened, so we restore immediately after.\n for (let i = 0; i < this.waves.length; i++) {\n const m = this.waves[i].material;\n this.miniBlendPrev[i] = m.blending;\n this.miniTransPrev[i] = m.transparent;\n m.blending = THREE.NormalBlending;\n m.transparent = false;\n }\n r.render(this.scene, this.minimapCamera);\n for (let i = 0; i < this.waves.length; i++) {\n const m = this.waves[i].material;\n m.blending = this.miniBlendPrev[i];\n m.transparent = this.miniTransPrev[i];\n }\n this.scene.background = prevBg;\n r.setScissorTest(false);\n const full = this.renderer.getSize(this.miniSize);\n r.setViewport(0, 0, full.x, full.y);\n r.setClearColor(prevColor, prevAlpha);\n r.autoClear = true;\n\n this.camMarker.visible = false;\n for (const m of this.minimapLights) m.visible = false;\n }\n\n private async ensureOrbit(): Promise<void> {\n if (this.orbit) return;\n const { OrbitControls } = await import(\"three/addons/controls/OrbitControls.js\");\n if (this.orbit) return; // a concurrent call already set it up\n this.orbit = new OrbitControls(this.camera, this.renderer.domElement);\n this.orbit.enableDamping = false;\n this.orbit.enabled = false; // enabled by enableOrbit() / light-edit\n this.orbit.screenSpacePanning = true;\n this.orbit.zoomToCursor = true;\n this.orbit.minDistance = 12;\n this.orbit.maxDistance = 600;\n // Left drag PANS (moves the view around); right drag ROTATES — swapped from the\n // OrbitControls default so the primary drag moves the scene rather than orbiting it.\n this.orbit.mouseButtons = {\n LEFT: THREE.MOUSE.PAN,\n MIDDLE: THREE.MOUSE.DOLLY,\n RIGHT: THREE.MOUSE.ROTATE,\n };\n this.orbit.target.set(\n this.config.cameraTarget.x,\n this.config.cameraTarget.y,\n this.config.cameraTarget.z,\n );\n this.orbit.update();\n this.orbit.addEventListener(\"change\", this.onControlsChange);\n // Cursor feedback by drag type: left-drag pans → 4-way move arrows; right-drag rotates →\n // grab/closed-hand. Idle stays on the move arrows (the primary drag pans). OrbitControls\n // doesn't expose the button, so we read it from pointerdown directly.\n this.renderer.domElement.addEventListener(\"pointerdown\", this.onCursorDown);\n window.addEventListener(\"pointerup\", this.onCursorUp);\n }\n\n private onCursorDown = (e: PointerEvent): void => {\n if (this.mainOrbitOn) {\n this.renderer.domElement.style.cursor = e.button === 2 ? \"grabbing\" : \"move\";\n }\n };\n\n private onCursorUp = (): void => {\n if (this.mainOrbitOn) this.renderer.domElement.style.cursor = \"move\";\n };\n\n private async ensureGizmo(): Promise<void> {\n await this.ensureOrbit();\n if (this.transform) return;\n const { TransformControls } = await import(\"three/addons/controls/TransformControls.js\");\n if (this.transform) return; // a concurrent call already set it up\n this.transform = new TransformControls(this.camera, this.renderer.domElement);\n this.transform.setMode(\"translate\");\n this.transform.addEventListener(\"dragging-changed\", (e) => {\n if (this.orbit) this.orbit.enabled = !(e as unknown as { value: boolean }).value;\n });\n this.transform.addEventListener(\"objectChange\", this.onGizmoMoved);\n // (onGizmoMoved routes to the light- or wave-drag handler based on the active mode.)\n this.transform.addEventListener(\"change\", this.onControlsChange);\n const tc = this.transform as unknown as { getHelper?: () => THREE.Object3D };\n this.overlay.add(tc.getHelper ? tc.getHelper() : (this.transform as unknown as THREE.Object3D));\n\n this.renderer.domElement.addEventListener(\"pointerdown\", this.onPointerDown);\n this.renderer.domElement.addEventListener(\"pointermove\", this.onPointerMove);\n this.renderer.domElement.addEventListener(\"pointerup\", this.onPointerUp);\n this.renderer.domElement.addEventListener(\"pointercancel\", this.onPointerUp);\n }\n\n private onControlsChange = (): void => {\n // Orbit/zoom/pan moved the camera → capture it so exports match the view. Wave editing keeps\n // the live view (persist it); light editing uses a transient 3/4 working view (don't persist).\n if (\n this.orbit &&\n this.orbit.enabled &&\n this.editMode !== \"light\" &&\n !this.suppressCameraChange\n ) {\n this.writeCameraToConfig();\n this.onCameraChanged?.();\n }\n if (!this.running) this.renderOnce();\n };\n\n /** Persist the live camera (position/target/distance) into the config. */\n private writeCameraToConfig(): void {\n const p = this.camera.position;\n this.config.cameraPosition = {\n x: roundTo(p.x, 3),\n y: roundTo(p.y, 3),\n z: roundTo(p.z, 3),\n };\n // Capture the LIVE ortho zoom (mouse-scroll changes camera.zoom directly) back into\n // config.cameraZoom — the user multiplier — by inverting applyZoom's responsive base factor.\n // Without this, scroll-zoom changed the view but was never saved/exported, so a framing tuned\n // at a scrolled zoom didn't reproduce (its pan made sense only at that zoom). It reads the\n // factor through baseFrameZoom so it tracks cameraFit / cameraMinVisibleWidth: inverting a\n // hardcoded COVER while the scene rendered under another fit would bake the ratio between the\n // two into cameraZoom, and the framing would drift a little on every orbit.\n const base = this.baseFrameZoom(\n this.camera.right - this.camera.left,\n this.camera.top - this.camera.bottom,\n );\n // Divide interactionZoom back out too: a live scroll→cameraZoom reaction multiplies camera.zoom\n // for the preview, but only the authored zoom belongs in the persisted (and exported) config.\n if (base > 0) {\n this.config.cameraZoom = roundTo(this.camera.zoom / base / (this.interactionZoom || 1), 3);\n }\n if (this.orbit) {\n const t = this.orbit.target;\n this.config.cameraTarget = {\n x: roundTo(t.x, 3),\n y: roundTo(t.y, 3),\n z: roundTo(t.z, 3),\n };\n this.config.cameraDistance = roundTo(p.distanceTo(this.orbit.target), 3);\n }\n }\n\n /** Pointer position in normalized device coords (-1..1), from a canvas-relative event. */\n private pointerNdc(ev: PointerEvent): THREE.Vector2 {\n const rect = this.renderer.domElement.getBoundingClientRect();\n return new THREE.Vector2(\n ((ev.clientX - rect.left) / rect.width) * 2 - 1,\n -((ev.clientY - rect.top) / rect.height) * 2 + 1,\n );\n }\n\n private onPointerDown = (ev: PointerEvent): void => {\n if (!this.editing || !this.transform) return;\n if (ev.button !== 0) return; // only left-drag moves objects; right-drag rotates the camera\n if (this.transform.dragging || this.transform.axis) return; // on a gizmo handle → let it move\n this.raycaster.setFromCamera(this.pointerNdc(ev), this.camera);\n const helpers = this.editMode === \"wave\" ? this.waveHelpers : this.lightHelpers;\n const hit = this.raycaster.intersectObjects(helpers, false)[0];\n if (!hit) {\n // Missed every handle → pan the view (the tool's normal left-drag). OrbitControls' LEFT is\n // unmapped in edit mode, so onPointerMove pans manually without fighting the object drag.\n if (this.orbit) {\n this.panState = { lastNdc: this.pointerNdc(ev) };\n this.renderer.domElement.setPointerCapture?.(ev.pointerId);\n }\n return;\n }\n const idx = helpers.indexOf(hit.object as THREE.Mesh);\n if (idx < 0) return;\n if (this.editMode === \"wave\") this.selectWaveHandle(idx);\n else this.selectLight(idx);\n // Free screen-plane drag: the WHOLE marker is grabbable (not just the thin gizmo arrows) and\n // the camera stays locked. Rotate mode uses the gizmo's rings instead, so skip it there.\n if (this.gizmoMode !== \"translate\") return;\n const helper = helpers[idx];\n const normal = this.camera.getWorldDirection(new THREE.Vector3());\n this.dragPlane.setFromNormalAndCoplanarPoint(normal, helper.position);\n const grab = new THREE.Vector3();\n if (!this.raycaster.ray.intersectPlane(this.dragPlane, grab)) return;\n this.dragState = { helper, offset: helper.position.clone().sub(grab) };\n if (this.orbit) this.orbit.enabled = false; // lock the camera for the whole drag\n this.renderer.domElement.setPointerCapture?.(ev.pointerId);\n };\n\n private onPointerMove = (ev: PointerEvent): void => {\n if (this.panState) {\n // Ortho pan: unproject the pointer delta into world units (auto-handles zoom/aspect/dpr),\n // then shift camera + orbit target together so the grabbed point tracks the cursor.\n const ndc = this.pointerNdc(ev);\n const before = new THREE.Vector3(this.panState.lastNdc.x, this.panState.lastNdc.y, 0);\n const after = new THREE.Vector3(ndc.x, ndc.y, 0);\n const delta = before.unproject(this.camera).sub(after.unproject(this.camera)); // opposite the cursor\n this.camera.position.add(delta);\n this.panState.lastNdc = ndc;\n if (this.orbit) {\n this.orbit.target.add(delta);\n this.orbit.update(); // fires 'change' → persists the new framing + renders\n } else {\n this.camera.updateProjectionMatrix();\n if (!this.running) this.renderOnce();\n }\n return;\n }\n if (!this.dragState) return;\n this.raycaster.setFromCamera(this.pointerNdc(ev), this.camera);\n const p = new THREE.Vector3();\n if (!this.raycaster.ray.intersectPlane(this.dragPlane, p)) return;\n this.dragState.helper.position.copy(p.add(this.dragState.offset));\n this.onGizmoMoved(); // write the new position into config + uniforms\n if (!this.running) this.renderOnce();\n };\n\n private onPointerUp = (ev: PointerEvent): void => {\n if (this.panState) {\n this.panState = undefined;\n this.renderer.domElement.releasePointerCapture?.(ev.pointerId);\n return;\n }\n if (!this.dragState) return;\n this.dragState = undefined;\n if (this.orbit) this.orbit.enabled = true; // still editing → keep right-drag camera rotate\n this.renderer.domElement.releasePointerCapture?.(ev.pointerId);\n };\n\n private selectLight(i: number): void {\n this.selectedLight = i;\n const h = this.lightHelpers[i];\n if (h && this.transform) this.transform.attach(h);\n else this.transform?.detach();\n this.onLightsChanged?.(i);\n if (!this.running) this.renderOnce();\n }\n\n private selectWaveHandle(i: number): void {\n this.selectedWave = i;\n const h = this.waveHelpers[i];\n if (h && this.transform) this.transform.attach(h);\n else this.transform?.detach();\n this.onWaveChanged?.();\n if (!this.running) this.renderOnce();\n }\n\n /** Gizmo drag → route to the active mode's writer. */\n private onGizmoMoved = (): void => {\n if (this.editMode === \"wave\") this.onWaveGizmoMoved();\n else this.onLightGizmoMoved();\n };\n\n /** Light gizmo drag → write the moved handle back into the config + uniforms. */\n private onLightGizmoMoved(): void {\n const h = this.lightHelpers[this.selectedLight];\n const light = this.config.lights?.[this.selectedLight];\n if (!h || !light) return;\n light.position.x = roundTo(h.position.x, 2);\n light.position.y = roundTo(h.position.y, 2);\n light.position.z = roundTo(h.position.z, 2);\n this.pushLightUniforms();\n this.onLightsChanged?.(this.selectedLight);\n }\n\n /** Wave gizmo drag → the whole-wave box writes config.position (and the wave handles\n * follow it); a per-wave sphere writes that layer's offset (relative to config.position). */\n private onWaveGizmoMoved(): void {\n const h = this.waveHelpers[this.selectedWave];\n if (!h) return;\n const wave = this.config.waves[h.userData.index as number];\n if (!wave) return;\n // Mutate the wave's vectors IN PLACE (don't reassign a new object): the panel's Transform\n // sliders hold a reference to these objects, so replacing them would leave the sliders\n // reading the stale old object even though the wave moved.\n if (this.gizmoMode === \"rotate\") {\n wave.rotation.x = roundTo(THREE.MathUtils.radToDeg(h.rotation.x), 2);\n wave.rotation.y = roundTo(THREE.MathUtils.radToDeg(h.rotation.y), 2);\n wave.rotation.z = roundTo(THREE.MathUtils.radToDeg(h.rotation.z), 2);\n } else {\n wave.position.x = roundTo(h.position.x, 2);\n wave.position.y = roundTo(h.position.y, 2);\n wave.position.z = roundTo(h.position.z, 2);\n }\n this.pushWaveTransforms();\n this.onWaveChanged?.();\n }\n\n /** Reconcile the helper spheres with config.lights (count, position, colour). */\n private syncLightHelpers(): void {\n const lights = this.config.lights ?? [];\n while (this.lightHelpers.length < lights.length) {\n const mesh = new THREE.Mesh(\n new THREE.SphereGeometry(0.28, 16, 12),\n new THREE.MeshBasicMaterial({ color: 0xffffff, depthTest: false, transparent: true }),\n );\n mesh.renderOrder = 999;\n this.overlay.add(mesh);\n this.lightHelpers.push(mesh);\n }\n while (this.lightHelpers.length > lights.length) {\n const mesh = this.lightHelpers.pop();\n if (!mesh) break;\n this.overlay.remove(mesh);\n mesh.geometry.dispose();\n (mesh.material as THREE.Material).dispose();\n }\n lights.forEach((l, i) => {\n const h = this.lightHelpers[i];\n h.position.set(l.position.x, l.position.y, l.position.z);\n (h.material as THREE.MeshBasicMaterial).color.set(l.color);\n });\n if (this.selectedLight >= this.lightHelpers.length) {\n this.selectedLight = Math.max(0, this.lightHelpers.length - 1);\n }\n const sel = this.lightHelpers[this.selectedLight];\n if (sel && this.transform && this.transform.object !== sel) this.transform.attach(sel);\n if (!sel) this.transform?.detach();\n }\n\n private clearLightHelpers(): void {\n for (const mesh of this.lightHelpers) {\n this.overlay.remove(mesh);\n mesh.geometry.dispose();\n (mesh.material as THREE.Material).dispose();\n }\n this.lightHelpers = [];\n }\n\n /** Reconcile the wave drag handles with config: one box handle per wave, sitting at that\n * wave's absolute position (and oriented to its rotation so the rotate gizmo starts there). */\n private syncWaveHelpers(): void {\n if (this.transform?.dragging) return; // don't yank a handle out from under an active drag\n const waves = this.config.waves ?? [];\n const wantTotal = waves.length;\n if (this.waveHelpers.length !== wantTotal) {\n this.clearWaveHelpers();\n for (let i = 0; i < wantTotal; i++) {\n const box = new THREE.Mesh(\n new THREE.BoxGeometry(0.55, 0.55, 0.55),\n new THREE.MeshBasicMaterial({ color: 0x39d0ff, depthTest: false, transparent: true }),\n );\n box.renderOrder = 999;\n box.userData = { kind: \"wave\", index: i };\n this.overlay.add(box);\n this.waveHelpers.push(box);\n }\n }\n for (const h of this.waveHelpers) {\n const sc = waves[h.userData.index as number];\n if (!sc) continue;\n h.position.set(sc.position.x, sc.position.y, sc.position.z);\n h.rotation.set(\n THREE.MathUtils.degToRad(sc.rotation.x),\n THREE.MathUtils.degToRad(sc.rotation.y),\n THREE.MathUtils.degToRad(sc.rotation.z),\n );\n }\n if (this.selectedWave >= this.waveHelpers.length) this.selectedWave = 0;\n const sel = this.waveHelpers[this.selectedWave];\n if (sel && this.transform && this.transform.object !== sel) this.transform.attach(sel);\n if (!sel) this.transform?.detach();\n }\n\n private clearWaveHelpers(): void {\n for (const mesh of this.waveHelpers) {\n this.overlay.remove(mesh);\n mesh.geometry.dispose();\n (mesh.material as THREE.Material).dispose();\n }\n this.waveHelpers = [];\n }\n\n /** Reposition just the wave MESHES from each wave's absolute transform — the transform\n * subset of refresh(), used live during a wave gizmo drag so the ribbon follows without a\n * full uniform re-push or a helper resync. */\n private pushWaveTransforms(): void {\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n wave.mesh.scale.set(sc.scale.x, sc.scale.y, sc.scale.z);\n wave.mesh.rotation.set(\n THREE.MathUtils.degToRad(sc.rotation.x),\n THREE.MathUtils.degToRad(sc.rotation.y),\n THREE.MathUtils.degToRad(sc.rotation.z),\n );\n wave.mesh.position.set(sc.position.x, sc.position.y, sc.position.z);\n });\n if (!this.running) this.renderOnce();\n }\n\n /** Pull the camera back to a 3/4 angle that frames the edit target: the origin wave + all\n * lights (light mode), or a region around config.position + the handles (wave mode). */\n private frameEditCamera(): void {\n const box = new THREE.Box3();\n if (this.editMode === \"wave\") {\n // The wave can sit far from the origin (some presets push position to the hundreds), so\n // frame around it — a ~200-unit margin shows a good chunk of the ×10-scaled ribbon.\n const target = this.config.waves[this.selectedWave] ?? this.config.waves[0];\n const c = new THREE.Vector3(target.position.x, target.position.y, target.position.z);\n box.expandByPoint(c.clone().addScalar(200));\n box.expandByPoint(c.clone().addScalar(-200));\n for (const h of this.waveHelpers) box.expandByPoint(h.position);\n } else {\n // The baked + scaled wave spans ~±25 units; frame that plus any lights.\n box.expandByPoint(new THREE.Vector3(25, 25, 25));\n box.expandByPoint(new THREE.Vector3(-25, -25, -25));\n for (const l of this.config.lights ?? []) {\n box.expandByPoint(new THREE.Vector3(l.position.x, l.position.y, l.position.z));\n }\n }\n const sphere = box.getBoundingSphere(new THREE.Sphere());\n const radius = Math.max(sphere.radius, 2);\n const dir = new THREE.Vector3(0.45, 0.35, 1).normalize();\n this.camera.position.copy(sphere.center).addScaledVector(dir, radius * 3 + 200);\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(sphere.center);\n // Ortho: frame by zoom (frustum is in px), not distance.\n this.camera.zoom = (this.camera.right - this.camera.left) / Math.max(1, radius * 2.6);\n this.camera.updateProjectionMatrix();\n if (this.orbit) {\n this.orbit.target.copy(sphere.center);\n this.orbit.update();\n }\n }\n\n /** Restore the authored camera (from config) — used when leaving light-edit. */\n private restoreHeroCamera(): void {\n const p = this.config.cameraPosition;\n const t = this.config.cameraTarget;\n this.camera.position.set(p.x, p.y, p.z);\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(t.x, t.y, t.z);\n if (this.orbit) {\n this.orbit.target.set(t.x, t.y, t.z);\n this.orbit.update();\n }\n }\n\n /** Push only the light uniforms (used live during a gizmo drag). */\n private pushLightUniforms(): void {\n const lights = this.config.lights ?? [];\n for (const wave of this.waves) {\n const u = wave.material.uniforms;\n u.uNumLights.value = Math.min(lights.length, MAX_LIGHTS);\n const lPos = u.uLightPos.value as THREE.Vector3[];\n const lCol = u.uLightColor.value as THREE.Vector3[];\n const lInt = u.uLightIntensity.value as number[];\n for (let li = 0; li < MAX_LIGHTS; li++) {\n const light = lights[li];\n if (light) {\n lPos[li].set(light.position.x, light.position.y, light.position.z);\n hexToLinearVec3(light.color, lCol[li]);\n lInt[li] = light.intensity;\n } else {\n lInt[li] = 0;\n }\n }\n }\n if (!this.running) this.renderOnce();\n }\n\n // ---- Hook overrides: plug the editor behavior into the base render pipeline (5 hook points) ----\n\n protected override isCameraExternallyDriven(): boolean {\n return !!this.orbit || this.editing;\n }\n\n protected override onAfterRefresh(): void {\n if (this.editMode === \"light\") this.syncLightHelpers();\n else if (this.editMode === \"wave\") this.syncWaveHelpers();\n // refresh() may have just (re)created the interaction controller via syncInteraction(); re-apply\n // the scroll preview so a freshly-added scroll binding responds immediately instead of being\n // stuck on the (frozen, in-studio) live scroll.\n if (this.interaction) this.interaction.scrollOverride = this.scrollPreview;\n }\n\n /** The interaction runtime is fetched lazily, so the controller usually arrives AFTER the refresh\n * that asked for it — this is the studio's second chance to hand it the scroll preview. Without\n * it, adding the first scroll binding leaves the slider dead until the next edit. */\n protected override onInteractionReady(): void {\n if (!this.interaction) return;\n this.interaction.scrollOverride = this.scrollPreview;\n this.interaction.snapScroll();\n this.renderOnce();\n }\n\n protected override onAfterRenderFrame(): void {\n // Draw the light/wave gizmo helpers on top, crisp (not through the post pass), and never into\n // exports. Guarded on `overlay` because the base constructor renders a first frame before this\n // subclass's own fields are initialized (base-ctor-calls-virtual-hook order).\n if (this.overlay && this.editing && !this.capturing && this.overlay.children.length > 0) {\n const helpers = this.editMode === \"wave\" ? this.waveHelpers : this.lightHelpers;\n for (const h of helpers) {\n h.scale.setScalar(Math.max(0.1, this.camera.position.distanceTo(h.position) * 0.09));\n }\n this.renderer.autoClear = false;\n this.renderer.setRenderTarget(null); // draw to the screen, not a leftover composer buffer\n this.renderer.render(this.overlay, this.camera);\n this.renderer.autoClear = true;\n }\n this.renderMinimap();\n }\n\n protected override onAfterResize(): void {\n if (this.cameraRigOn) this.positionMinimapBtn();\n }\n\n protected override applyCameraFromConfig(): void {\n if (this.editing) return; // a 3D-edit gizmo owns the camera; don't fight it\n const p = this.config.cameraPosition;\n const tg = this.config.cameraTarget;\n this.suppressCameraChange = true;\n this.camera.position.set(p.x, p.y, p.z);\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(tg.x, tg.y, tg.z);\n if (this.orbit) {\n this.orbit.target.set(tg.x, tg.y, tg.z);\n this.orbit.update();\n }\n this.applyZoom();\n this.suppressCameraChange = false;\n this.onCameraChanged?.(); // keep the panel's camera sliders in sync\n if (!this.running) this.renderOnce();\n }\n\n override dispose(): void {\n window.removeEventListener(\"keydown\", this.onKeyDown);\n this.renderer.domElement.removeEventListener(\"pointerdown\", this.onPointerDown);\n this.renderer.domElement.removeEventListener(\"pointerdown\", this.onCursorDown);\n window.removeEventListener(\"pointerup\", this.onCursorUp);\n this.transform?.detach();\n this.transform?.dispose();\n this.orbit?.dispose();\n this.minimapBtn?.remove();\n this.clearLightHelpers();\n this.clearWaveHelpers();\n for (const m of this.minimapLights) {\n m.geometry.dispose();\n (m.material as THREE.Material).dispose();\n }\n super.dispose();\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { StudioWaveRenderer } from "./StudioWaveRenderer.js";
|
|
2
|
+
import { withTslBackend } from "../renderer/WaveRendererGPU.js";
|
|
3
|
+
//#region src/studio/StudioWaveRendererGPU.ts
|
|
4
|
+
/**
|
|
5
|
+
* The studio editor on the TSL/WebGPU backend: {@link StudioWaveRenderer} with the GPU backend
|
|
6
|
+
* layered on top via {@link withTslBackend}. The composition is safe because the two classes
|
|
7
|
+
* override disjoint base hook sets (editor: camera/overlay hooks; backend: renderer/material/post
|
|
8
|
+
* hooks) — see the mixin's doc comment.
|
|
9
|
+
*
|
|
10
|
+
* This module transitively imports `three/webgpu` (~197 KB gzipped), so it must only ever be
|
|
11
|
+
* reached through the dynamic import in `loadStudioWaveRendererGPU` (./index.ts). A static import
|
|
12
|
+
* from any eager module fails `scripts/check-webgpu-chunk.mjs`.
|
|
13
|
+
*/
|
|
14
|
+
const StudioWaveRendererGPU = withTslBackend(StudioWaveRenderer);
|
|
15
|
+
//#endregion
|
|
16
|
+
export { StudioWaveRendererGPU };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=StudioWaveRendererGPU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StudioWaveRendererGPU.js","names":[],"sources":["../../src/studio/StudioWaveRendererGPU.ts"],"sourcesContent":["/**\n * The studio editor on the TSL/WebGPU backend: {@link StudioWaveRenderer} with the GPU backend\n * layered on top via {@link withTslBackend}. The composition is safe because the two classes\n * override disjoint base hook sets (editor: camera/overlay hooks; backend: renderer/material/post\n * hooks) — see the mixin's doc comment.\n *\n * This module transitively imports `three/webgpu` (~197 KB gzipped), so it must only ever be\n * reached through the dynamic import in `loadStudioWaveRendererGPU` (./index.ts). A static import\n * from any eager module fails `scripts/check-webgpu-chunk.mjs`.\n */\nimport { withTslBackend } from \"../renderer/WaveRendererGPU\";\nimport { StudioWaveRenderer } from \"./StudioWaveRenderer\";\n\nexport const StudioWaveRendererGPU = withTslBackend(StudioWaveRenderer);\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAa,wBAAwB,eAAe,kBAAkB"}
|
package/dist/studio/index.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import { randomizeBackground, randomizeColor, randomizeConfig, randomizeFinish, randomizeGlobal, randomizeGradient, randomizeLights, randomizePostFx, randomizeSpine, randomizeTransform, randomizeTwist, randomizeWave } from "./randomize.js";
|
|
2
1
|
import { StudioWaveRenderer } from "./StudioWaveRenderer.js";
|
|
2
|
+
import { randomizeBackground, randomizeColor, randomizeConfig, randomizeFinish, randomizeGlobal, randomizeGradient, randomizeLights, randomizePostFx, randomizeSpine, randomizeTransform, randomizeTwist, randomizeWave } from "./randomize.js";
|
|
3
3
|
import { createThumbHost, prepThumbConfig, renderThumbFrame } from "./thumbnail.js";
|
|
4
|
-
|
|
4
|
+
//#region src/studio/index.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Fetch the TSL/WebGPU studio renderer — a drop-in for {@link StudioWaveRenderer} (construct, then
|
|
7
|
+
* `await renderer.init()` before the first draw). Behind a dynamic import so `three/webgpu`
|
|
8
|
+
* (~197 KB gzipped) never enters the eager studio graph; the boundary is enforced by
|
|
9
|
+
* `scripts/check-webgpu-chunk.mjs`.
|
|
10
|
+
*/
|
|
11
|
+
declare function loadStudioWaveRendererGPU(): Promise<typeof StudioWaveRenderer>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { StudioWaveRenderer, createThumbHost, loadStudioWaveRendererGPU, prepThumbConfig, randomizeBackground, randomizeColor, randomizeConfig, randomizeFinish, randomizeGlobal, randomizeGradient, randomizeLights, randomizePostFx, randomizeSpine, randomizeTransform, randomizeTwist, randomizeWave, renderThumbFrame };
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/studio/index.js
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import { randomizeBackground, randomizeColor, randomizeConfig, randomizeFinish, randomizeGlobal, randomizeGradient, randomizeLights, randomizePostFx, randomizeSpine, randomizeTransform, randomizeTwist, randomizeWave } from "./randomize.js";
|
|
2
2
|
import { StudioWaveRenderer } from "./StudioWaveRenderer.js";
|
|
3
3
|
import { createThumbHost, prepThumbConfig, renderThumbFrame } from "./thumbnail.js";
|
|
4
|
-
|
|
4
|
+
//#region src/studio/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* Fetch the TSL/WebGPU studio renderer — a drop-in for {@link StudioWaveRenderer} (construct, then
|
|
7
|
+
* `await renderer.init()` before the first draw). Behind a dynamic import so `three/webgpu`
|
|
8
|
+
* (~197 KB gzipped) never enters the eager studio graph; the boundary is enforced by
|
|
9
|
+
* `scripts/check-webgpu-chunk.mjs`.
|
|
10
|
+
*/
|
|
11
|
+
async function loadStudioWaveRendererGPU() {
|
|
12
|
+
return (await import("./StudioWaveRendererGPU.js")).StudioWaveRendererGPU;
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { StudioWaveRenderer, createThumbHost, loadStudioWaveRendererGPU, prepThumbConfig, randomizeBackground, randomizeColor, randomizeConfig, randomizeFinish, randomizeGlobal, randomizeGradient, randomizeLights, randomizePostFx, randomizeSpine, randomizeTransform, randomizeTwist, randomizeWave, renderThumbFrame };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/studio/index.ts"],"sourcesContent":["// The `@wave3d/core/studio` entry: studio-facing helpers layered on the renderer core.\nimport type { StudioWaveRenderer } from \"./StudioWaveRenderer\";\n\nexport * from \"./randomize\";\nexport { StudioWaveRenderer } from \"./StudioWaveRenderer\";\nexport { createThumbHost, prepThumbConfig, renderThumbFrame } from \"./thumbnail\";\n\n/**\n * Fetch the TSL/WebGPU studio renderer — a drop-in for {@link StudioWaveRenderer} (construct, then\n * `await renderer.init()` before the first draw). Behind a dynamic import so `three/webgpu`\n * (~197 KB gzipped) never enters the eager studio graph; the boundary is enforced by\n * `scripts/check-webgpu-chunk.mjs`.\n */\nexport async function loadStudioWaveRendererGPU(): Promise<typeof StudioWaveRenderer> {\n return (await import(\"./StudioWaveRendererGPU\")).StudioWaveRendererGPU;\n}\n"],"mappings":";;;;;;;;;;AAaA,eAAsB,4BAAgE;CACpF,QAAQ,MAAM,OAAO,8BAAA,CAA4B;AACnD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wave3d/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Framework-agnostic 3D gradient-wave renderer + config model (the engine behind Wave Studio)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"animation",
|
|
@@ -48,6 +48,10 @@
|
|
|
48
48
|
"./standalone": {
|
|
49
49
|
"types": "./dist/standalone.d.ts",
|
|
50
50
|
"default": "./dist/standalone/wave3d.standalone.js"
|
|
51
|
+
},
|
|
52
|
+
"./standalone-webgpu": {
|
|
53
|
+
"types": "./dist/standalone.webgpu.d.ts",
|
|
54
|
+
"default": "./dist/standalone/wave3d.standalone.webgpu.js"
|
|
51
55
|
}
|
|
52
56
|
},
|
|
53
57
|
"publishConfig": {
|
|
@@ -56,6 +60,7 @@
|
|
|
56
60
|
"devDependencies": {
|
|
57
61
|
"@tanstack/intent": "^0.3.6",
|
|
58
62
|
"@types/three": "^0.185.0",
|
|
63
|
+
"playwright": "^1.62.1",
|
|
59
64
|
"three": "^0.185.0",
|
|
60
65
|
"vite": "^8.1.0",
|
|
61
66
|
"vitest": "^4.1.10"
|
|
@@ -71,10 +76,14 @@
|
|
|
71
76
|
},
|
|
72
77
|
"scripts": {
|
|
73
78
|
"build": "tsdown && pnpm build:standalone",
|
|
74
|
-
"build:standalone": "vite build --config vite.standalone.config.ts",
|
|
79
|
+
"build:standalone": "vite build --config vite.standalone.config.ts && WAVE3D_STANDALONE=webgpu vite build --config vite.standalone.config.ts",
|
|
75
80
|
"skill:stale": "intent stale skills",
|
|
76
81
|
"skill:validate": "intent validate skills",
|
|
77
82
|
"test": "vitest run",
|
|
78
|
-
"typecheck": "tsc --noEmit"
|
|
83
|
+
"typecheck": "tsc --noEmit",
|
|
84
|
+
"parity": "node parity/run.mjs",
|
|
85
|
+
"parity:capture": "node parity/run.mjs --capture",
|
|
86
|
+
"parity:serve": "vite --config parity/vite.config.ts",
|
|
87
|
+
"parity:math": "node parity/math.mjs"
|
|
79
88
|
}
|
|
80
89
|
}
|