@wave3d/core 0.2.1 → 0.3.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.
Files changed (40) hide show
  1. package/README.md +47 -0
  2. package/dist/config/model.d.ts +104 -1
  3. package/dist/config/model.js +111 -1
  4. package/dist/config/model.js.map +1 -1
  5. package/dist/index.d.ts +3 -2
  6. package/dist/index.js +2 -2
  7. package/dist/presets.js +15 -0
  8. package/dist/presets.js.map +1 -1
  9. package/dist/renderer/WaveRenderer.d.ts +34 -0
  10. package/dist/renderer/WaveRenderer.js +232 -7
  11. package/dist/renderer/WaveRenderer.js.map +1 -1
  12. package/dist/renderer/interaction.d.ts +119 -0
  13. package/dist/renderer/interaction.js +474 -0
  14. package/dist/renderer/interaction.js.map +1 -0
  15. package/dist/renderer/palette.js +14 -0
  16. package/dist/renderer/palette.js.map +1 -1
  17. package/dist/renderer/shaders.js +105 -0
  18. package/dist/renderer/shaders.js.map +1 -1
  19. package/dist/shell/createWave.d.ts +9 -1
  20. package/dist/shell/createWave.js +7 -1
  21. package/dist/shell/createWave.js.map +1 -1
  22. package/dist/shell/poster.d.ts +12 -0
  23. package/dist/shell/poster.js +4 -3
  24. package/dist/shell/poster.js.map +1 -1
  25. package/dist/standalone/wave3d.standalone.js +864 -219
  26. package/dist/standalone.d.ts +2 -2
  27. package/dist/standalone.js +2 -2
  28. package/dist/studio/StudioWaveRenderer.d.ts +15 -0
  29. package/dist/studio/StudioWaveRenderer.js +32 -1
  30. package/dist/studio/StudioWaveRenderer.js.map +1 -1
  31. package/dist/studio/index.d.ts +2 -1
  32. package/dist/studio/index.js +2 -1
  33. package/dist/studio/randomize.d.ts +3 -3
  34. package/dist/studio/randomize.js +4 -4
  35. package/dist/studio/randomize.js.map +1 -1
  36. package/dist/studio/thumbnail.d.ts +14 -0
  37. package/dist/studio/thumbnail.js +32 -0
  38. package/dist/studio/thumbnail.js.map +1 -0
  39. package/package.json +1 -1
  40. package/skills/wave3d/SKILL.md +20 -2
@@ -1,4 +1,4 @@
1
- import { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, ColorStop, DEFAULT_LIGHT_POSITION, GradientType, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PaletteSource, SceneConfig, StudioConfig, Vec2, Vec3, WaveConfig, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeWave, resizeWaves } from "./config/model.js";
1
+ import { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, ColorStop, DEFAULT_LIGHT_POSITION, GradientType, InteractionSource, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PaletteSource, SceneConfig, SceneInteractionBinding, SceneInteractionConfig, SceneInteractionTarget, StudioConfig, Vec2, Vec3, WaveConfig, WaveHoverConfig, WaveInteractionBinding, WaveInteractionConfig, WaveInteractionTarget, WavePressConfig, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, 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";
@@ -9,5 +9,5 @@ declare function createWave(container: HTMLElement, config?: Partial<StudioConfi
9
9
  /** The drop-in embed contract: an alias of {@link createWave}. */
10
10
  declare const mountWave: typeof createWave;
11
11
  //#endregion
12
- export { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, ColorStop, DEFAULT_LIGHT_POSITION, type FallbackReason, GradientType, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PRESETS, PaletteSource, SceneConfig, type SnapshotOptions, StudioConfig, Vec2, Vec3, WaveConfig, type WaveHandle, type WaveOptions, WaveRenderer, type WaveState, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, createWave, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, mountWave, normalizeBackground, normalizeWave, resizeWaves };
12
+ export { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, ColorStop, DEFAULT_LIGHT_POSITION, type FallbackReason, GradientType, InteractionSource, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PRESETS, PaletteSource, 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, normalizeSceneInteraction, normalizeWave, normalizeWaveInteraction, resizeWaves };
13
13
  //# sourceMappingURL=standalone.d.ts.map
@@ -1,4 +1,4 @@
1
- import { DEFAULT_LIGHT_POSITION, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeWave, resizeWaves } from "./config/model.js";
1
+ import { DEFAULT_LIGHT_POSITION, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeSceneInteraction, normalizeWave, normalizeWaveInteraction, resizeWaves } from "./config/model.js";
2
2
  import { createWaveImpl } from "./shell/createWave.js";
3
3
  import { WaveRenderer } from "./renderer/WaveRenderer.js";
4
4
  import { PRESETS } from "./presets.js";
@@ -12,6 +12,6 @@ function createWave(container, config = {}, options = {}) {
12
12
  /** The drop-in embed contract: an alias of {@link createWave}. */
13
13
  const mountWave = createWave;
14
14
  //#endregion
15
- export { DEFAULT_LIGHT_POSITION, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, PRESETS, WaveRenderer, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, createWave, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, mountWave, normalizeBackground, normalizeWave, resizeWaves };
15
+ export { DEFAULT_LIGHT_POSITION, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, PRESETS, WaveRenderer, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, createWave, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, mountWave, normalizeBackground, normalizeSceneInteraction, normalizeWave, normalizeWaveInteraction, resizeWaves };
16
16
 
17
17
  //# sourceMappingURL=standalone.js.map
@@ -5,6 +5,10 @@ declare class StudioWaveRenderer extends WaveRenderer {
5
5
  /** Set while the panel drives the camera, so orbit's 'change' doesn't re-refresh the
6
6
  * panel mid-drag (the panel already knows the new value). */
7
7
  private suppressCameraChange;
8
+ /** Studio scroll-preview value (null = live). Persisted here so it survives controller
9
+ * recreation — a scroll binding added after the preview was set still responds (see
10
+ * onAfterRefresh). */
11
+ private scrollPreview;
8
12
  private readonly miniBox;
9
13
  private readonly miniSphere;
10
14
  private readonly miniTmpA;
@@ -111,6 +115,17 @@ declare class StudioWaveRenderer extends WaveRenderer {
111
115
  /** Ortho zoom MULTIPLIER (the camera has no real fov). 1 = the responsive base framing (the hero crop). */
112
116
  getZoom(): number;
113
117
  setZoom(zoom: number): void;
118
+ /** Studio-only scroll preview (the studio page doesn't scroll): override the `scroll` source with
119
+ * a fixed 0..1 value, or pass null to return to the live container-progress read. NEVER touches
120
+ * config. No-op until interaction is enabled (the controller exists). */
121
+ setScrollPreview(v: number | null): void;
122
+ /** Feed a live scroll position (0..1) from the studio scroll-test overlay — a real scrollable
123
+ * surface the user drags/wheels. Unlike setScrollPreview's instant snap (built for the slider and
124
+ * a possibly-frozen loop), this leaves the RUNNING render loop's update() to smooth the bindings
125
+ * and derive `scrollVelocity` from the real scroll delta, so the wave reacts exactly as it would on
126
+ * a scrolling page (velocity included). Falls back to a snapped frame when the loop isn't running
127
+ * (paused / reduced-motion), so the preview still tracks. Like setScrollPreview it NEVER touches config. */
128
+ setScrollTestProgress(v: number): void;
114
129
  duplicateOffset(): {
115
130
  x: number;
116
131
  y: number;
@@ -8,6 +8,10 @@ var StudioWaveRenderer = class extends WaveRenderer {
8
8
  /** Set while the panel drives the camera, so orbit's 'change' doesn't re-refresh the
9
9
  * panel mid-drag (the panel already knows the new value). */
10
10
  suppressCameraChange = false;
11
+ /** Studio scroll-preview value (null = live). Persisted here so it survives controller
12
+ * recreation — a scroll binding added after the preview was set still responds (see
13
+ * onAfterRefresh). */
14
+ scrollPreview = null;
11
15
  miniBox = new THREE.Box3();
12
16
  miniSphere = new THREE.Sphere();
13
17
  miniTmpA = new THREE.Vector3();
@@ -323,6 +327,32 @@ var StudioWaveRenderer = class extends WaveRenderer {
323
327
  this.applyZoom();
324
328
  if (!this.running) this.renderOnce();
325
329
  }
330
+ /** Studio-only scroll preview (the studio page doesn't scroll): override the `scroll` source with
331
+ * a fixed 0..1 value, or pass null to return to the live container-progress read. NEVER touches
332
+ * config. No-op until interaction is enabled (the controller exists). */
333
+ setScrollPreview(v) {
334
+ this.scrollPreview = v;
335
+ if (this.interaction) {
336
+ this.interaction.scrollOverride = v;
337
+ this.interaction.snapScroll();
338
+ this.renderOnce();
339
+ }
340
+ }
341
+ /** Feed a live scroll position (0..1) from the studio scroll-test overlay — a real scrollable
342
+ * surface the user drags/wheels. Unlike setScrollPreview's instant snap (built for the slider and
343
+ * a possibly-frozen loop), this leaves the RUNNING render loop's update() to smooth the bindings
344
+ * and derive `scrollVelocity` from the real scroll delta, so the wave reacts exactly as it would on
345
+ * a scrolling page (velocity included). Falls back to a snapped frame when the loop isn't running
346
+ * (paused / reduced-motion), so the preview still tracks. Like setScrollPreview it NEVER touches config. */
347
+ setScrollTestProgress(v) {
348
+ this.scrollPreview = v;
349
+ if (!this.interaction) return;
350
+ this.interaction.scrollOverride = v;
351
+ if (!this.running) {
352
+ this.interaction.snapScroll();
353
+ this.renderOnce();
354
+ }
355
+ }
326
356
  duplicateOffset() {
327
357
  this.camera.updateMatrixWorld();
328
358
  const worldW = (this.camera.right - this.camera.left) / this.camera.zoom;
@@ -557,7 +587,7 @@ var StudioWaveRenderer = class extends WaveRenderer {
557
587
  z: roundTo(p.z, 3)
558
588
  };
559
589
  const cover = Math.max((this.camera.right - this.camera.left) / FRAME_W, (this.camera.top - this.camera.bottom) / 750);
560
- if (cover > 0) this.config.cameraZoom = roundTo(this.camera.zoom / cover, 3);
590
+ if (cover > 0) this.config.cameraZoom = roundTo(this.camera.zoom / cover / (this.interactionZoom || 1), 3);
561
591
  if (this.orbit) {
562
592
  const t = this.orbit.target;
563
593
  this.config.cameraTarget = {
@@ -849,6 +879,7 @@ var StudioWaveRenderer = class extends WaveRenderer {
849
879
  onAfterRefresh() {
850
880
  if (this.editMode === "light") this.syncLightHelpers();
851
881
  else if (this.editMode === "wave") this.syncWaveHelpers();
882
+ if (this.interaction) this.interaction.scrollOverride = this.scrollPreview;
852
883
  }
853
884
  onAfterRenderFrame() {
854
885
  if (this.overlay && this.editing && !this.capturing && this.overlay.children.length > 0) {
@@ -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, FRAME_W, FRAME_H, 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 // 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 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 COVER\n // factor. Without this, scroll-zoom changed the view but was never saved/exported, so a\n // framing tuned at a scrolled zoom didn't reproduce (its pan made sense only at that zoom).\n const cover = Math.max(\n (this.camera.right - this.camera.left) / FRAME_W,\n (this.camera.top - this.camera.bottom) / FRAME_H,\n );\n if (cover > 0) this.config.cameraZoom = roundTo(this.camera.zoom / cover, 3);\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 }\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, FRAME_W, FRAME_H, 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 /** 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 COVER\n // factor. Without this, scroll-zoom changed the view but was never saved/exported, so a\n // framing tuned at a scrolled zoom didn't reproduce (its pan made sense only at that zoom).\n const cover = Math.max(\n (this.camera.right - this.camera.left) / FRAME_W,\n (this.camera.top - this.camera.bottom) / FRAME_H,\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 (cover > 0) {\n this.config.cameraZoom = roundTo(this.camera.zoom / cover / (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,3 +1,4 @@
1
1
  import { randomizeBackground, randomizeColor, randomizeConfig, randomizeFinish, randomizeGlobal, randomizeGradient, randomizeLights, randomizeSpine, randomizeTransform, randomizeTwist, randomizeWave } from "./randomize.js";
2
2
  import { StudioWaveRenderer } from "./StudioWaveRenderer.js";
3
- export { StudioWaveRenderer, randomizeBackground, randomizeColor, randomizeConfig, randomizeFinish, randomizeGlobal, randomizeGradient, randomizeLights, randomizeSpine, randomizeTransform, randomizeTwist, randomizeWave };
3
+ import { createThumbHost, prepThumbConfig, renderThumbFrame } from "./thumbnail.js";
4
+ export { StudioWaveRenderer, createThumbHost, prepThumbConfig, randomizeBackground, randomizeColor, randomizeConfig, randomizeFinish, randomizeGlobal, randomizeGradient, randomizeLights, randomizeSpine, randomizeTransform, randomizeTwist, randomizeWave, renderThumbFrame };
@@ -1,3 +1,4 @@
1
1
  import { randomizeBackground, randomizeColor, randomizeConfig, randomizeFinish, randomizeGlobal, randomizeGradient, randomizeLights, randomizeSpine, randomizeTransform, randomizeTwist, randomizeWave } from "./randomize.js";
2
2
  import { StudioWaveRenderer } from "./StudioWaveRenderer.js";
3
- export { StudioWaveRenderer, randomizeBackground, randomizeColor, randomizeConfig, randomizeFinish, randomizeGlobal, randomizeGradient, randomizeLights, randomizeSpine, randomizeTransform, randomizeTwist, randomizeWave };
3
+ import { createThumbHost, prepThumbConfig, renderThumbFrame } from "./thumbnail.js";
4
+ export { StudioWaveRenderer, createThumbHost, prepThumbConfig, randomizeBackground, randomizeColor, randomizeConfig, randomizeFinish, randomizeGlobal, randomizeGradient, randomizeLights, randomizeSpine, randomizeTransform, randomizeTwist, randomizeWave, renderThumbFrame };
@@ -1,16 +1,16 @@
1
1
  import { StudioConfig, WaveConfig } from "../config/model.js";
2
2
 
3
3
  //#region src/studio/randomize.d.ts
4
- /** "Randomize All": keep the scene (background, camera, lights, quality) and randomize the
4
+ /** "Tasteful Randomize": keep the scene (background, camera, lights, quality) and randomize the
5
5
  * post-fx plus every wave independently — so a multi-wave stack becomes visibly varied.
6
6
  * The camera is deliberately left alone so the result always lands in view. */
7
7
  declare function randomizeConfig(base: StudioConfig): StudioConfig;
8
8
  /** Randomize a whole wave: colour, shape (displacement/twist), transform, finish + the
9
- * compositing knobs (speed/opacity/seed/blend). Used by each wave's 🎲 and by "Randomize All". */
9
+ * compositing knobs (speed/opacity/seed/blend). Used by each wave's 🎲 and by "Tasteful Randomize". */
10
10
  declare function randomizeWave(s: WaveConfig): void;
11
11
  declare function randomizeGradient(c: WaveConfig): void;
12
12
  /** "Background" folder: a fresh random gradient (linear/radial/conic) or mesh backdrop. Left out
13
- * of "Randomize All", which deliberately preserves the background. */
13
+ * of "Tasteful Randomize", which deliberately preserves the background. */
14
14
  declare function randomizeBackground(c: StudioConfig): void;
15
15
  /** "Color" folder: hue / contrast / saturation grading. */
16
16
  declare function randomizeColor(c: WaveConfig): void;
@@ -1,7 +1,7 @@
1
1
  import { clamp, roundTo } from "../util/math.js";
2
2
  //#region src/studio/randomize.ts
3
3
  /**
4
- * The "Randomize" helpers: `randomizeConfig` ("Randomize All") plus the per-section randomizers
4
+ * The "Randomize" helpers: `randomizeConfig` ("Tasteful Randomize") plus the per-section randomizers
5
5
  * the studio wires to each folder's 🎲. Studio-facing (not part of the renderer core), exposed via
6
6
  * the `@wave3d/core/studio` entry.
7
7
  */
@@ -48,7 +48,7 @@ function rand(min, max) {
48
48
  function pick(arr) {
49
49
  return arr[Math.floor(Math.random() * arr.length)];
50
50
  }
51
- /** "Randomize All": keep the scene (background, camera, lights, quality) and randomize the
51
+ /** "Tasteful Randomize": keep the scene (background, camera, lights, quality) and randomize the
52
52
  * post-fx plus every wave independently — so a multi-wave stack becomes visibly varied.
53
53
  * The camera is deliberately left alone so the result always lands in view. */
54
54
  function randomizeConfig(base) {
@@ -59,7 +59,7 @@ function randomizeConfig(base) {
59
59
  return cfg;
60
60
  }
61
61
  /** Randomize a whole wave: colour, shape (displacement/twist), transform, finish + the
62
- * compositing knobs (speed/opacity/seed/blend). Used by each wave's 🎲 and by "Randomize All". */
62
+ * compositing knobs (speed/opacity/seed/blend). Used by each wave's 🎲 and by "Tasteful Randomize". */
63
63
  function randomizeWave(s) {
64
64
  randomizeGradient(s);
65
65
  randomizeColor(s);
@@ -150,7 +150,7 @@ function randomizeGradient(c) {
150
150
  c.paletteDriftY = 0;
151
151
  }
152
152
  /** "Background" folder: a fresh random gradient (linear/radial/conic) or mesh backdrop. Left out
153
- * of "Randomize All", which deliberately preserves the background. */
153
+ * of "Tasteful Randomize", which deliberately preserves the background. */
154
154
  function randomizeBackground(c) {
155
155
  const colors = pick(RANDOM_PALETTES);
156
156
  c.transparentBackground = false;