@wave3d/core 0.2.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -0
- package/dist/config/model.d.ts +138 -1
- package/dist/config/model.js +144 -1
- package/dist/config/model.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -2
- package/dist/presets.js +15 -0
- package/dist/presets.js.map +1 -1
- package/dist/renderer/WaveRenderer.d.ts +59 -0
- package/dist/renderer/WaveRenderer.js +422 -8
- package/dist/renderer/WaveRenderer.js.map +1 -1
- package/dist/renderer/interaction.d.ts +119 -0
- package/dist/renderer/interaction.js +474 -0
- package/dist/renderer/interaction.js.map +1 -0
- package/dist/renderer/palette.js +14 -0
- package/dist/renderer/palette.js.map +1 -1
- package/dist/renderer/shaders.js +313 -1
- package/dist/renderer/shaders.js.map +1 -1
- package/dist/shell/createWave.d.ts +9 -1
- package/dist/shell/createWave.js +7 -1
- package/dist/shell/createWave.js.map +1 -1
- package/dist/shell/poster.d.ts +12 -0
- package/dist/shell/poster.js +4 -3
- package/dist/shell/poster.js.map +1 -1
- package/dist/standalone/wave3d.standalone.js +1016 -223
- package/dist/standalone.d.ts +2 -2
- package/dist/standalone.js +2 -2
- package/dist/studio/StudioWaveRenderer.d.ts +15 -0
- package/dist/studio/StudioWaveRenderer.js +32 -1
- package/dist/studio/StudioWaveRenderer.js.map +1 -1
- package/dist/studio/index.d.ts +2 -2
- package/dist/studio/index.js +2 -2
- package/dist/studio/randomize.d.ts +7 -4
- package/dist/studio/randomize.js +52 -5
- package/dist/studio/randomize.js.map +1 -1
- package/package.json +1 -1
- package/skills/wave3d/SKILL.md +60 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interaction.js","names":[],"sources":["../../src/renderer/interaction.ts"],"sourcesContent":["// The optional interactivity runtime for the wave renderer: a per-wave pointer field (localized\n// cursor effects) + per-wave and scene input→param bindings, driven by ONE shared cursor/scroll.\n// It lives in renderer/ so it stays below the shell/studio/index layers (depcruise); it may import\n// only `three`, ../config/model, and ../util/math.\n//\n// Split of responsibility with WaveRenderer: this controller owns ALL input + smoothing (the one\n// cursor's position / presence / press / velocity, scroll progress + velocity, the `appear` latch,\n// custom inputs, click ripples, and every binding's smoothed 0..1 source value — keyed by binding\n// identity so scene + per-wave binding lists all get their own smoothing). The renderer reads\n// sample() / bindingValue() once per frame and writes uniforms — the pointer field per wave, and\n// bindings via the WAVE_APPLIERS / SCENE_APPLIERS tables. Bindings NEVER mutate `config`.\nimport * as THREE from \"three\";\nimport { clamp01 } from \"../util/math\";\nimport type {\n InteractionSource,\n SceneInteractionBinding,\n SceneInteractionTarget,\n StudioConfig,\n WaveConfig,\n WaveInteractionBinding,\n WaveInteractionTarget,\n} from \"../config/model\";\n\n/** Click-ripple ring-buffer size. MUST match the `[4]` array sizes in shaders.ts (POINTER_RIPPLES). */\nexport const RIPPLE_SLOTS = 4;\nconst RIPPLE_LIFETIME = 1.5; // seconds a ripple lives (crest travels out + fades by then)\nconst VELOCITY_TAU = 0.08; // pointer-velocity smoothing time constant (seconds)\nconst POINTER_SPEED_REF = 4.0; // NDC/s that normalizes pointerSpeed to 1.0\nconst SCROLL_VELOCITY_REF = 2.0; // progress/s that normalizes scrollVelocity to 1.0\nconst SCROLL_VELOCITY_TAU = 0.15; // scroll-velocity smoothing (seconds)\nconst DEFAULT_POINTER_TAU = 0.12; // pointer-follow smoothing default (seconds)\nconst DEFAULT_BINDING_TAU = 0.25; // per-binding source smoothing default (seconds)\nconst POINTER_SPRING_ZETA = 0.7; // pointer-field damping ratio (<1 → slight overshoot = \"weight\")\nconst MIN_POINTER_TAU = 0.02; // floor before smoothing→spring frequency (omega = 1/tau)\nconst SPRING_MAX_STEP = 1 / 120; // substep the spring below this dt so it stays stable after a stall\nconst SPRING_MAX_SUBSTEPS = 6;\n\ntype AnyBinding = WaveInteractionBinding | SceneInteractionBinding;\n\n/** Frame-rate-independent exponential smoothing factor for time constant `tau` (seconds). */\nfunction alpha(tau: number, dt: number): number {\n return tau > 0 ? 1 - Math.exp(-dt / tau) : 1;\n}\n\n/**\n * Advance a damped spring (`pos`/`vel`) toward `target` by `dt`, using semi-implicit (symplectic)\n * Euler. `omega` is the natural angular frequency (≈ 1/response-time), `zeta` the damping ratio\n * (<1 underdamped → overshoots and settles; 1 critical; >1 sluggish). Unlike a first-order lag this\n * carries momentum, so motion has weight and settles instead of creeping to a dead stop. Substeps\n * when `dt` spikes (e.g. the tab was backgrounded) so a stiff spring can't blow up; ~1 step at 60fps.\n */\nfunction springVec2(\n pos: THREE.Vector2,\n vel: THREE.Vector2,\n target: THREE.Vector2,\n omega: number,\n zeta: number,\n dt: number,\n): void {\n if (dt <= 0) return;\n const steps =\n dt > SPRING_MAX_STEP ? Math.min(Math.ceil(dt / SPRING_MAX_STEP), SPRING_MAX_SUBSTEPS) : 1;\n const h = dt / steps;\n const k = omega * omega;\n const c = 2 * zeta * omega;\n for (let s = 0; s < steps; s++) {\n vel.x += (k * (target.x - pos.x) - c * vel.x) * h;\n vel.y += (k * (target.y - pos.y) - c * vel.y) * h;\n pos.x += vel.x * h;\n pos.y += vel.y * h;\n }\n}\n\n// ---- Binding applier tables -----------------------------------------------------------------\n\n/** What a wave-scoped applier writes into: one wave's uniforms + its mesh transform. */\nexport interface WaveApplyArgs {\n u: Record<string, THREE.IUniform>;\n mesh: THREE.Object3D;\n}\n/** What a scene-scoped applier writes into: the post-pass uniforms + a small out-param the renderer\n * seeds (0 / 1) each frame and reads back (the interaction time-offset + zoom multiplier). */\nexport interface SceneApplyArgs {\n post: Record<string, THREE.IUniform>;\n out: { timeOffset: number; zoom: number };\n}\ninterface WaveApplier {\n base(w: WaveConfig): number;\n apply(value: number, a: WaveApplyArgs): void;\n}\ninterface SceneApplier {\n base(c: StudioConfig): number;\n apply(value: number, a: SceneApplyArgs): void;\n}\n\nconst waveApplier = (\n base: (w: WaveConfig) => number,\n apply: (value: number, a: WaveApplyArgs) => void,\n): WaveApplier => ({ base, apply });\nconst sceneApplier = (\n base: (c: StudioConfig) => number,\n apply: (value: number, a: SceneApplyArgs) => void,\n): SceneApplier => ({ base, apply });\n\n/**\n * Per-wave binding targets → (how to read the authored base value, how to write the modulated one).\n * Each base() mirrors the exact fallback refresh() uses, so a binding at rest (from omitted, source\n * 0) writes the same value the renderer already had — no visible jump. This object is the runtime\n * source of truth for {@link WaveInteractionTarget} (enforced by `satisfies`).\n */\nexport const WAVE_APPLIERS = {\n displaceAmount: waveApplier(\n (w) => w.displaceAmount,\n (v, a) => {\n a.u.uDispAmount.value = v;\n },\n ),\n detailAmount: waveApplier(\n (w) => w.detailAmount ?? 0,\n (v, a) => {\n a.u.uDetailAmount.value = v;\n },\n ),\n twistPowerX: waveApplier(\n (w) => w.twistPower.x,\n (v, a) => {\n a.u.uTwPowX.value = v;\n },\n ),\n twistPowerY: waveApplier(\n (w) => w.twistPower.y,\n (v, a) => {\n a.u.uTwPowY.value = v;\n },\n ),\n twistPowerZ: waveApplier(\n (w) => w.twistPower.z,\n (v, a) => {\n a.u.uTwPowZ.value = v;\n },\n ),\n twistFrequencyX: waveApplier(\n (w) => w.twistFrequency.x,\n (v, a) => {\n a.u.uTwFreqX.value = v;\n },\n ),\n twistFrequencyY: waveApplier(\n (w) => w.twistFrequency.y,\n (v, a) => {\n a.u.uTwFreqY.value = v;\n },\n ),\n twistFrequencyZ: waveApplier(\n (w) => w.twistFrequency.z,\n (v, a) => {\n a.u.uTwFreqZ.value = v;\n },\n ),\n hueShift: waveApplier(\n (w) => w.hueShift,\n (v, a) => {\n a.u.uHueShift.value = v;\n },\n ),\n gradientShift: waveApplier(\n (w) => w.gradientShift ?? 0,\n (v, a) => {\n a.u.uGradShift.value = v;\n },\n ),\n colorSaturation: waveApplier(\n (w) => w.colorSaturation,\n (v, a) => {\n a.u.uSaturation.value = v;\n },\n ),\n opacity: waveApplier(\n (w) => w.opacity,\n (v, a) => {\n a.u.uOpacity.value = v;\n },\n ),\n lineThickness: waveApplier(\n (w) => w.lineThickness ?? 1,\n (v, a) => {\n a.u.uLineThickness.value = v;\n },\n ),\n lineAmount: waveApplier(\n (w) => w.lineAmount ?? 425,\n (v, a) => {\n a.u.uLineAmount.value = v;\n },\n ),\n fiberStrength: waveApplier(\n (w) => w.fiberStrength,\n (v, a) => {\n a.u.uFiberStrength.value = v;\n },\n ),\n sheen: waveApplier(\n (w) => w.sheen ?? 1,\n (v, a) => {\n a.u.uSheen.value = v;\n },\n ),\n iridescence: waveApplier(\n (w) => w.iridescence ?? 0,\n (v, a) => {\n a.u.uIridescence.value = v;\n },\n ),\n positionX: waveApplier(\n (w) => w.position.x,\n (v, a) => {\n a.mesh.position.x = v;\n },\n ),\n positionY: waveApplier(\n (w) => w.position.y,\n (v, a) => {\n a.mesh.position.y = v;\n },\n ),\n} satisfies Record<WaveInteractionTarget, WaveApplier>;\n\n/** Scene-level binding targets. base() mirrors updateTime() / applyZoom() / applyPost() fallbacks. */\nexport const SCENE_APPLIERS = {\n timeOffset: sceneApplier(\n (c) => c.timeOffset ?? 0,\n (v, a) => {\n a.out.timeOffset = v;\n },\n ),\n cameraZoom: sceneApplier(\n (c) => c.cameraZoom ?? 1,\n (v, a) => {\n a.out.zoom = v;\n },\n ),\n blur: sceneApplier(\n (c) => c.blur,\n (v, a) => {\n a.post.uBlurAmount.value = v;\n },\n ),\n grain: sceneApplier(\n (c) => c.grain,\n (v, a) => {\n a.post.uGrainAmount.value = v;\n },\n ),\n} satisfies Record<SceneInteractionTarget, SceneApplier>;\n\n// ---- Active-state predicates (keyed off config only, so input never triggers a recompile) ----\n\n/** The global master switch: only `scene.interaction.enabled === false` turns the whole layer off. */\nfunction notDisabled(cfg: StudioConfig): boolean {\n return cfg.interaction?.enabled !== false;\n}\n\n/** Whether a wave has a pointer field (hover effects, or a click ripple). */\nfunction waveHasPointerField(w: WaveConfig): boolean {\n const it = w.interaction;\n return !!it && (!!it.hover || (it.press?.ripple ?? 0) > 0);\n}\n\n/** Whether this wave has an active pointer field → its POINTER_FX shader path compiles. */\nexport function wavePointerFxActive(cfg: StudioConfig, w: WaveConfig): boolean {\n return notDisabled(cfg) && waveHasPointerField(w);\n}\n\n/** Whether this wave has active click ripples → its nested POINTER_RIPPLES path compiles. */\nexport function waveRipplesActive(cfg: StudioConfig, w: WaveConfig): boolean {\n return notDisabled(cfg) && (w.interaction?.press?.ripple ?? 0) > 0;\n}\n\n/** Whether ANY wave has a pointer field (so the renderer bothers writing the shared pointer uniforms). */\nexport function anyPointerFxActive(cfg: StudioConfig): boolean {\n return notDisabled(cfg) && cfg.waves.some(waveHasPointerField);\n}\n\n/** Whether the interaction layer should run at all (any wave interaction, or any scene binding). */\nexport function interactionActive(cfg: StudioConfig): boolean {\n if (!notDisabled(cfg)) return false;\n if ((cfg.interaction?.bindings?.length ?? 0) > 0) return true;\n return cfg.waves.some((w) => {\n const it = w.interaction;\n return !!it && (!!it.hover || (it.press?.ripple ?? 0) > 0 || (it.bindings?.length ?? 0) > 0);\n });\n}\n\n// ---- Sample shape + the controller ----------------------------------------------------------\n\ninterface RippleSlot {\n origin: THREE.Vector2; // NDC\n age: number; // seconds since spawn\n amp: number; // 0..1 decay envelope (0 = free slot)\n}\ninterface RippleState extends RippleSlot {\n active: boolean;\n}\n\n/** A per-frame snapshot of the pointer-field state. Fields are LIVE references into the controller's\n * state — read them synchronously each frame; don't retain them. */\nexport interface InteractionSample {\n /** Smoothed pointer position, NDC (-1..1). */\n ndc: THREE.Vector2;\n /** Smoothed pointer presence 0..1 (→ uPointerActive). */\n presence: number;\n /** Click-ripple ring buffer (amp = shared 0..1 envelope; 0 = free slot). */\n ripples: readonly RippleSlot[];\n}\n\n/** A wave's own smoothed pointer-field state — trails the shared cursor at the wave's own rate. */\nexport interface PointerField {\n /** Smoothed pointer position for this wave, NDC (-1..1). */\n ndc: THREE.Vector2;\n /** Spring velocity of `ndc` (NDC/s) — internal spring state, not read by the renderer. */\n vel: THREE.Vector2;\n /** Smoothed pointer presence 0..1 for this wave. */\n presence: number;\n}\n\n/**\n * Owns the one cursor's input + scroll + press/appear/custom and all smoothing. Constructed by the\n * renderer when {@link interactionActive} first turns true, disposed when it turns false. All\n * listeners are passive and container-scoped (the poster overlay passes events through).\n */\nexport class InteractionController {\n /** Studio-only scroll preview: when non-null, overrides the computed scroll progress. */\n scrollOverride: number | null = null;\n\n private readonly ndc = new THREE.Vector2();\n private readonly ndcTarget = new THREE.Vector2();\n private readonly ndcPrev = new THREE.Vector2();\n private readonly velNdc = new THREE.Vector2();\n private presence = 0;\n private presenceTarget = 0;\n private press = 0;\n private pressTarget = 0;\n private pointerSpeed = 0;\n private scroll = 0;\n private scrollPrev = 0;\n private scrollVel = 0;\n private appearLatched = false;\n private readonly customInputs = new Map<string, number>();\n private readonly ripples: RippleState[] = [];\n // Per-wave pointer-field state (index-parallel to config.waves); each trails the cursor at its own\n // hover smoothing. Grown/shrunk in update().\n private readonly fields: PointerField[] = [];\n // Per-binding smoothing state, keyed by binding-object identity (covers scene + every wave list).\n private readonly bindingState = new Map<\n AnyBinding,\n { value: number; source: InteractionSource }\n >();\n // Scratch set reused by updateBindings every frame (cleared, never reallocated).\n private readonly seenBindings = new Set<AnyBinding>();\n private readonly out: InteractionSample;\n\n constructor(\n private readonly container: HTMLElement,\n private readonly cfg: () => StudioConfig | undefined,\n ) {\n for (let i = 0; i < RIPPLE_SLOTS; i++) {\n this.ripples.push({ origin: new THREE.Vector2(), age: 0, amp: 0, active: false });\n }\n this.out = { ndc: this.ndc, presence: 0, ripples: this.ripples };\n const opts = { passive: true } as const;\n container.addEventListener(\"pointerenter\", this.onPointerEnter, opts);\n container.addEventListener(\"pointermove\", this.onPointerMove, opts);\n container.addEventListener(\"pointerleave\", this.onPointerLeave, opts);\n container.addEventListener(\"pointercancel\", this.onPointerCancel, opts);\n container.addEventListener(\"pointerdown\", this.onPointerDown, opts);\n container.addEventListener(\"pointerup\", this.onPointerUp, opts);\n }\n\n /** Ignore coarse (touch) pointers unless the scene opts in with interaction.touch. */\n private ignore(e: PointerEvent): boolean {\n return e.pointerType === \"touch\" && this.cfg()?.interaction?.touch !== true;\n }\n\n private setNdcTarget(e: PointerEvent): void {\n const rect = this.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n this.ndcTarget.set(\n ((e.clientX - rect.left) / rect.width) * 2 - 1,\n -(((e.clientY - rect.top) / rect.height) * 2 - 1),\n );\n }\n\n private onPointerEnter = (e: PointerEvent): void => {\n if (this.ignore(e)) return;\n this.presenceTarget = 1;\n this.setNdcTarget(e);\n };\n private onPointerMove = (e: PointerEvent): void => {\n if (this.ignore(e)) return;\n if (e.pointerType === \"touch\" && this.pressTarget < 0.5) return; // touch: only track while down\n this.presenceTarget = 1;\n this.setNdcTarget(e);\n };\n private onPointerLeave = (e: PointerEvent): void => {\n if (this.ignore(e)) return;\n this.presenceTarget = 0;\n this.ndcTarget.set(0, 0); // relax toward centre → pointerX/Y rest at 0.5\n };\n private onPointerCancel = (e: PointerEvent): void => {\n if (this.ignore(e)) return;\n this.pressTarget = 0;\n this.presenceTarget = 0;\n this.ndcTarget.set(0, 0);\n };\n private onPointerDown = (e: PointerEvent): void => {\n if (this.ignore(e)) return;\n this.pressTarget = 1;\n this.presenceTarget = 1;\n this.setNdcTarget(e);\n // Spawn a ripple only if some wave actually wants ripples (else it is wasted state).\n const cfg = this.cfg();\n if (cfg && cfg.waves.some((w) => (w.interaction?.press?.ripple ?? 0) > 0)) this.spawnRipple();\n };\n private onPointerUp = (e: PointerEvent): void => {\n if (this.ignore(e)) return;\n this.pressTarget = 0;\n if (e.pointerType === \"touch\") {\n this.presenceTarget = 0; // touch has no hover — presence ends with the touch\n this.ndcTarget.set(0, 0);\n }\n };\n\n /** Spawn a normalized ripple (envelope 0..1) at the click NDC; per-wave amplitude scales it in the\n * shader. Reuses a free slot or evicts the oldest. */\n private spawnRipple(): void {\n let slot = this.ripples.find((r) => !r.active);\n if (!slot) {\n slot = this.ripples[0];\n for (const r of this.ripples) if (r.age > slot.age) slot = r;\n }\n slot.origin.copy(this.ndcTarget);\n slot.age = 0;\n slot.amp = 1;\n slot.active = true;\n }\n\n /** Advance all smoothed state by `dt` seconds. Called from the render loop with the same delta. */\n update(dt: number): void {\n const cfg = this.cfg();\n if (!cfg) return;\n const d = Math.max(dt, 0);\n // The SHARED pointer state feeds binding sources (hover / pointerX-Y / pointerSpeed / press) at a\n // fixed baseline; each wave's FIELD trails at its own hover smoothing further below.\n const kPointer = alpha(DEFAULT_POINTER_TAU, d);\n\n // Pointer position + presence + press.\n this.ndcPrev.copy(this.ndc);\n this.ndc.lerp(this.ndcTarget, kPointer);\n this.presence += (this.presenceTarget - this.presence) * kPointer;\n this.press += (this.pressTarget - this.press) * kPointer;\n\n // Velocity (own tau) from the smoothed-position delta.\n if (d > 1e-5) {\n const kv = alpha(VELOCITY_TAU, d);\n this.velNdc.x += ((this.ndc.x - this.ndcPrev.x) / d - this.velNdc.x) * kv;\n this.velNdc.y += ((this.ndc.y - this.ndcPrev.y) / d - this.velNdc.y) * kv;\n }\n this.pointerSpeed = this.presence * clamp01(this.velNdc.length() / POINTER_SPEED_REF);\n\n // Per-wave pointer FIELD: each wave's position is a damped SPRING toward the raw cursor target,\n // so a stack trails the cursor at different rates (parallax) and — because the spring is slightly\n // underdamped — carries a little weight: it overshoots and settles instead of creeping to a dead\n // stop. Presence stays a plain ramp (a spring there could dip below 0 and invert the effect).\n // A wave's hover `smoothing` sets the spring frequency (omega = 1/tau).\n const waves = cfg.waves;\n if (this.fields.length > waves.length) this.fields.length = waves.length;\n for (let i = 0; i < waves.length; i++) {\n let f = this.fields[i];\n if (!f) {\n f = {\n ndc: this.ndcTarget.clone(),\n vel: new THREE.Vector2(),\n presence: this.presenceTarget,\n };\n this.fields[i] = f;\n }\n const tau = Math.max(\n waves[i].interaction?.hover?.smoothing ?? DEFAULT_POINTER_TAU,\n MIN_POINTER_TAU,\n );\n springVec2(f.ndc, f.vel, this.ndcTarget, 1 / tau, POINTER_SPRING_ZETA, d);\n f.presence += (this.presenceTarget - f.presence) * alpha(tau, d);\n }\n\n // Scroll progress + velocity.\n const rawScroll = this.scrollOverride ?? this.computeScroll();\n if (d > 1e-5) {\n const sv = Math.abs(rawScroll - this.scrollPrev) / d;\n this.scrollVel += (sv - this.scrollVel) * alpha(SCROLL_VELOCITY_TAU, d);\n }\n this.scrollPrev = rawScroll;\n this.scroll = rawScroll;\n\n // Appear latch: the render loop is visibility-gated, so the first update() IS first-visible.\n this.appearLatched = true;\n\n // Ripples: age + quadratic-decay envelope.\n for (const r of this.ripples) {\n if (!r.active) continue;\n r.age += d;\n const env = Math.max(0, 1 - r.age / RIPPLE_LIFETIME);\n r.amp = env * env;\n if (r.amp <= 0) r.active = false;\n }\n\n this.updateBindings(cfg, d);\n }\n\n // Indexed loops + a reused scratch set (no per-frame closure/array/Set) — this runs every frame.\n private updateBindings(cfg: StudioConfig, dt: number): void {\n const seen = this.seenBindings;\n seen.clear();\n const sceneBindings = cfg.interaction?.bindings;\n if (sceneBindings) {\n for (let i = 0; i < sceneBindings.length; i++) this.advanceBinding(sceneBindings[i], dt);\n }\n for (let w = 0; w < cfg.waves.length; w++) {\n const bindings = cfg.waves[w].interaction?.bindings;\n if (!bindings) continue;\n for (let i = 0; i < bindings.length; i++) this.advanceBinding(bindings[i], dt);\n }\n // Prune state for bindings that no longer exist (edited/removed slots). advanceBinding puts every\n // seen binding in the map, so map ⊇ seen — equal sizes means nothing is stale to walk for.\n if (this.bindingState.size > seen.size) {\n for (const key of this.bindingState.keys()) if (!seen.has(key)) this.bindingState.delete(key);\n }\n }\n\n /** Advance one binding's smoothed source value by `dt` and mark it live in `seenBindings`. */\n private advanceBinding(b: AnyBinding, dt: number): void {\n this.seenBindings.add(b);\n const raw = this.rawSource(b.source);\n let st = this.bindingState.get(b);\n // (Re)initialise on first sight or when the slot's source changed (studio edit): `appear`\n // ramps from 0 (entrance), every other source snaps to its current value.\n if (!st || st.source !== b.source) {\n st = { value: b.source === \"appear\" ? 0 : raw, source: b.source };\n this.bindingState.set(b, st);\n }\n st.value += (raw - st.value) * alpha(b.smoothing ?? DEFAULT_BINDING_TAU, dt);\n }\n\n /** The current smoothed 0..1 value of a binding's source (0 if the binding is unknown). */\n bindingValue(b: AnyBinding): number {\n return this.bindingState.get(b)?.value ?? 0;\n }\n\n /** The current raw (un-per-binding-smoothed) 0..1 value of a source signal. */\n private rawSource(source: InteractionSource): number {\n switch (source) {\n case \"scroll\":\n return this.scroll;\n case \"hover\":\n return this.presence;\n case \"pointerX\":\n return (this.ndc.x + 1) * 0.5;\n case \"pointerY\":\n return (this.ndc.y + 1) * 0.5;\n case \"pointerSpeed\":\n return this.pointerSpeed;\n case \"press\":\n return this.press;\n case \"scrollVelocity\":\n return clamp01(this.scrollVel / SCROLL_VELOCITY_REF);\n case \"appear\":\n return this.appearLatched ? 1 : 0;\n default:\n // custom:<name> — fed by setInput(name, value).\n return this.customInputs.get(source.slice(\"custom:\".length)) ?? 0;\n }\n }\n\n /** Container progress through the viewport: 0 as it enters from below, 1 once scrolled past. */\n private computeScroll(): number {\n const rect = this.container.getBoundingClientRect();\n const vh = window.innerHeight || document.documentElement.clientHeight || 1;\n return clamp01((vh - rect.top) / (vh + rect.height));\n }\n\n /** The shared pointer-field state + ripples for the renderer (live references — read synchronously). */\n sample(): InteractionSample {\n this.out.presence = this.presence;\n return this.out;\n }\n\n /** This wave's smoothed pointer-field state (it trails the cursor at its own hover smoothing), or\n * null if the wave hasn't been advanced by update() yet (treat as rest). */\n fieldFor(waveIdx: number): PointerField | null {\n return this.fields[waveIdx] ?? null;\n }\n\n /** Velocity-driven agitation drive 0..1 (how fast the cursor is moving, presence-gated). The\n * renderer scales each wave's hover `agitate` by this, so the churn tracks the gesture instead\n * of buzzing at a constant rate whenever the cursor is merely present. */\n pointerFlux(): number {\n return this.pointerSpeed;\n }\n\n /** Smoothed pointer velocity, NDC/s (direction + speed of the drag). The renderer feeds it to the\n * drag-wake shader so the trailing trough forms behind the motion. Live reference — read per frame. */\n pointerVelocity(): THREE.Vector2 {\n return this.velNdc;\n }\n\n /** Feed a `custom:<name>` input (developer API; staged/forwarded by the shell). */\n setInput(name: string, value: number): void {\n if (typeof name !== \"string\" || !Number.isFinite(value)) return;\n this.customInputs.set(name, value);\n }\n\n /**\n * Collapse to the settled resting state for the single frame drawn when the loop stops (paused /\n * reduced-motion / offscreen): presence / velocity / press / pointerSpeed → 0, ripples cleared,\n * scroll → its current raw value, pointer → centre, and `appear` → 1 (reduced-motion users must\n * see the FINAL entered state). Custom inputs KEEP their last explicit values. Each binding snaps\n * to its settled source so the one settled frame shows the final look.\n */\n settle(): void {\n this.presence = this.presenceTarget = 0;\n this.press = this.pressTarget = 0;\n this.pointerSpeed = 0;\n this.velNdc.set(0, 0);\n this.ndc.set(0, 0);\n this.ndcTarget.set(0, 0);\n this.ndcPrev.set(0, 0);\n for (const f of this.fields) {\n f.ndc.set(0, 0);\n f.vel.set(0, 0);\n f.presence = 0;\n }\n for (const r of this.ripples) {\n r.age = 0;\n r.amp = 0;\n r.active = false;\n }\n const rawScroll = this.scrollOverride ?? this.computeScroll();\n this.scroll = this.scrollPrev = rawScroll;\n this.scrollVel = 0;\n this.appearLatched = true;\n const cfg = this.cfg();\n if (cfg) {\n this.bindingState.clear();\n const snap = (b: AnyBinding): void => {\n this.bindingState.set(b, { value: this.rawSource(b.source), source: b.source });\n };\n for (const b of cfg.interaction?.bindings ?? []) snap(b);\n for (const w of cfg.waves) for (const b of w.interaction?.bindings ?? []) snap(b);\n }\n }\n\n /**\n * Snap scroll progress + the scroll-sourced bindings to the current override at once, leaving\n * every other input (pointer / press / appear / velocity / custom) advancing live. Used by the\n * studio scroll preview: the studio page never really scrolls, so dragging the preview slider is a\n * manual scrub that must reflect the instant you move it — not on the next animation frame, which\n * the browser fully suspends whenever the tab isn't foreground. Unlike settle() (which collapses\n * ALL input to rest for a paused still frame), this touches only the scroll signal.\n */\n snapScroll(): void {\n const raw = this.scrollOverride ?? this.computeScroll();\n this.scroll = this.scrollPrev = raw;\n this.scrollVel = 0; // a static scrub has no velocity\n const cfg = this.cfg();\n if (!cfg) return;\n const snap = (b: AnyBinding): void => {\n if (b.source === \"scroll\" || b.source === \"scrollVelocity\") {\n this.bindingState.set(b, { value: this.rawSource(b.source), source: b.source });\n }\n };\n for (const b of cfg.interaction?.bindings ?? []) snap(b);\n for (const w of cfg.waves) for (const b of w.interaction?.bindings ?? []) snap(b);\n }\n\n dispose(): void {\n const c = this.container;\n c.removeEventListener(\"pointerenter\", this.onPointerEnter);\n c.removeEventListener(\"pointermove\", this.onPointerMove);\n c.removeEventListener(\"pointerleave\", this.onPointerLeave);\n c.removeEventListener(\"pointercancel\", this.onPointerCancel);\n c.removeEventListener(\"pointerdown\", this.onPointerDown);\n c.removeEventListener(\"pointerup\", this.onPointerUp);\n this.customInputs.clear();\n this.bindingState.clear();\n }\n}\n"],"mappings":"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"}
|
package/dist/renderer/palette.js
CHANGED
|
@@ -411,6 +411,20 @@ const PALETTE_MAPS = {
|
|
|
411
411
|
edgeColor: "#e4312b",
|
|
412
412
|
edgeAmount: .22
|
|
413
413
|
},
|
|
414
|
+
spain: {
|
|
415
|
+
label: "Spain",
|
|
416
|
+
kind: "gradient",
|
|
417
|
+
stops: mk([
|
|
418
|
+
["#aa151b", 0],
|
|
419
|
+
["#aa151b", .24],
|
|
420
|
+
["#f1bf00", .34],
|
|
421
|
+
["#f1bf00", .66],
|
|
422
|
+
["#aa151b", .76],
|
|
423
|
+
["#aa151b", 1]
|
|
424
|
+
]),
|
|
425
|
+
edgeColor: "#7a0f14",
|
|
426
|
+
edgeAmount: .22
|
|
427
|
+
},
|
|
414
428
|
grandLine: {
|
|
415
429
|
label: "Grand Line",
|
|
416
430
|
kind: "gradient",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"palette.js","names":[],"sources":["../../src/renderer/palette.ts"],"sourcesContent":["import * as THREE from \"three\";\nimport { clamp01 } from \"../util/math\";\nimport { createDefaultMeshPoints } from \"../config/model\";\nimport type {\n BackgroundImageFit,\n BasicGradientType,\n ColorStop,\n MeshGradientPoint,\n} from \"../config/model\";\n\n/**\n * Bakes the gradient stops into a 2D palette texture, sampled in the shader as\n * `texture2D(u_paletteTexture, vec2(uv.x, uv.y))`. The texture is a real image, so\n * colour can vary independently along BOTH axes:\n * - X (uv.x, along the length): the gradient stops.\n * - Y (uv.y, across the width): an \"edge tint\" blended toward both long edges\n */\n\nconst TEX_W = 256; // resolution along the gradient (length)\nconst TEX_H = 64; // resolution across the width\n\nfunction smoothstep(value: number): number {\n return value * value * (3 - 2 * value);\n}\n\n/** \"#rrggbb\" → \"rgba(r,g,b,a)\" for canvas fills with alpha. */\nfunction hexToRgba(hex: string, alpha: number): string {\n const c = new THREE.Color(hex); // parses many formats; .r/.g/.b are linear…\n // …but we want the original sRGB bytes for the canvas, so re-encode.\n const srgb = c.clone().convertLinearToSRGB();\n const r = Math.round(srgb.r * 255);\n const g = Math.round(srgb.g * 255);\n const b = Math.round(srgb.b * 255);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n\nexport interface PaletteTextureOptions {\n stops: ColorStop[];\n /** Cross-width edge tint colour (e.g. periwinkle). */\n edgeColor: string;\n /** 0 = flat 1-D gradient (no 2nd axis); higher = stronger cool edges. */\n edgeAmount: number;\n}\n\n/** Draw the palette into a 2D canvas. */\nexport function buildPaletteCanvas(opts: PaletteTextureOptions): HTMLCanvasElement {\n const canvas = document.createElement(\"canvas\");\n canvas.width = TEX_W;\n canvas.height = TEX_H;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return canvas;\n\n // X axis: the main gradient (stops sorted by position).\n const stops = [...opts.stops].sort((a, b) => a.pos - b.pos);\n const grad = ctx.createLinearGradient(0, 0, TEX_W, 0);\n if (stops.length === 0) {\n grad.addColorStop(0, \"#ffffff\");\n } else {\n for (const s of stops) grad.addColorStop(clamp01(s.pos), s.color);\n }\n ctx.fillStyle = grad;\n ctx.fillRect(0, 0, TEX_W, TEX_H);\n\n // Y axis: blend the edge colour toward both long edges (V-shaped alpha), 0 in the\n // middle — a genuine second dimension of colour.\n const a = clamp01(opts.edgeAmount);\n if (a > 0.001) {\n const eg = ctx.createLinearGradient(0, 0, 0, TEX_H);\n eg.addColorStop(0, hexToRgba(opts.edgeColor, a));\n eg.addColorStop(0.5, hexToRgba(opts.edgeColor, 0));\n eg.addColorStop(1, hexToRgba(opts.edgeColor, a));\n ctx.fillStyle = eg;\n ctx.fillRect(0, 0, TEX_W, TEX_H);\n }\n\n return canvas;\n}\n\n/** A small string that changes whenever the texture would need rebuilding. */\nexport function paletteSignature(opts: PaletteTextureOptions): string {\n const s = opts.stops.map((x) => `${x.color}@${x.pos.toFixed(3)}`).join(\",\");\n return `${s}|${opts.edgeColor}|${opts.edgeAmount.toFixed(3)}`;\n}\n\n/** Sampling config shared by every palette-texture source (canvas, image, LUT, video):\n * sRGB (the GPU linearizes on sample), linear filtering, clamped edges, no mipmaps. */\nexport function configurePaletteTexture<T extends THREE.Texture>(tex: T): T {\n tex.colorSpace = THREE.SRGBColorSpace;\n tex.minFilter = THREE.LinearFilter;\n tex.magFilter = THREE.LinearFilter;\n tex.wrapS = THREE.ClampToEdgeWrapping;\n tex.wrapT = THREE.ClampToEdgeWrapping;\n tex.generateMipmaps = false;\n return tex;\n}\n\n/** Wrap a canvas as a sampling-ready CanvasTexture. */\nexport function canvasToTexture(canvas: HTMLCanvasElement): THREE.CanvasTexture {\n return configurePaletteTexture(new THREE.CanvasTexture(canvas));\n}\n\n/** Build a ready-to-use CanvasTexture from gradient stops + edge tint. */\nexport function buildPaletteTexture(opts: PaletteTextureOptions): THREE.CanvasTexture {\n return canvasToTexture(buildPaletteCanvas(opts));\n}\n\nexport interface BackgroundGradientOptions {\n stops: ColorStop[];\n type: BasicGradientType;\n angle: number;\n width: number;\n height: number;\n}\n\n/** Draw an export-ready linear, radial, or conic background gradient. */\nexport function buildBackgroundGradientCanvas(opts: BackgroundGradientOptions): HTMLCanvasElement {\n const canvas = document.createElement(\"canvas\");\n canvas.width = Math.max(1, Math.round(opts.width));\n canvas.height = Math.max(1, Math.round(opts.height));\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return canvas;\n\n const { width, height } = canvas;\n const cx = width / 2;\n const cy = height / 2;\n const angle = (opts.angle * Math.PI) / 180;\n let gradient: CanvasGradient;\n if (opts.type === \"radial\") {\n gradient = ctx.createRadialGradient(cx, cy, 0, cx, cy, Math.hypot(cx, cy));\n } else if (opts.type === \"conic\") {\n gradient = ctx.createConicGradient(angle, cx, cy);\n } else {\n const radius = Math.abs(Math.sin(angle)) * cx + Math.abs(Math.cos(angle)) * cy;\n const dx = Math.sin(angle) * radius;\n const dy = -Math.cos(angle) * radius;\n gradient = ctx.createLinearGradient(cx - dx, cy - dy, cx + dx, cy + dy);\n }\n\n const stops = [...opts.stops].sort((a, b) => a.pos - b.pos);\n if (stops.length === 0) gradient.addColorStop(0, \"#ffffff\");\n else for (const stop of stops) gradient.addColorStop(clamp01(stop.pos), stop.color);\n ctx.fillStyle = gradient;\n ctx.fillRect(0, 0, width, height);\n return canvas;\n}\n\n/** Linear (0–1) → sRGB byte (0–255). */\nfunction linearToSrgbByte(linear: number): number {\n const c = clamp01(linear);\n const srgb = c <= 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;\n return Math.round(clamp01(srgb) * 255);\n}\n\n/**\n * The single source of truth for mesh-gradient rendering — shared by the background\n * (buildBackgroundMeshCanvas) and the on-canvas MeshGradientEditor preview, and mirroring the\n * wave's meshGradient shader. An inverse-distance-weighted blend of coloured points, computed in\n * LINEAR RGB with y measured UP (point.y = 1 is the top). Returns an ImageData to putImageData\n * onto any 2D context.\n */\nexport function renderMeshGradient(\n points: MeshGradientPoint[],\n softness: number,\n width: number,\n height: number,\n): ImageData {\n const w = Math.max(1, Math.round(width));\n const h = Math.max(1, Math.round(height));\n const image = new ImageData(w, h);\n const data = image.data;\n const pts = points.length >= 2 ? points : createDefaultMeshPoints();\n const colors = pts.map((p) => new THREE.Color(p.color)); // .r/.g/.b are LINEAR\n const exponent = 4.8 + (1.35 - 4.8) * clamp01(softness);\n for (let py = 0; py < h; py++) {\n const y = 1 - py / Math.max(1, h - 1);\n for (let px = 0; px < w; px++) {\n const x = px / Math.max(1, w - 1);\n let r = 0;\n let g = 0;\n let b = 0;\n let weightSum = 0;\n for (let i = 0; i < pts.length; i++) {\n const influence = Math.max(pts[i].influence, 0.05);\n const distance = Math.hypot(x - pts[i].x, y - pts[i].y) / influence;\n const weight = 1 / (Math.pow(Math.max(distance, 0.012), exponent) + 0.002);\n r += colors[i].r * weight;\n g += colors[i].g * weight;\n b += colors[i].b * weight;\n weightSum += weight;\n }\n const iw = Math.max(weightSum, 0.0001);\n const off = (py * w + px) * 4;\n data[off] = linearToSrgbByte(r / iw);\n data[off + 1] = linearToSrgbByte(g / iw);\n data[off + 2] = linearToSrgbByte(b / iw);\n data[off + 3] = 255;\n }\n }\n return image;\n}\n\n/**\n * Draw a mesh-gradient background. Mesh gradients are low-frequency, so we render at a small\n * internal size (capped ~320 px) via renderMeshGradient and scale up smoothly.\n */\nexport function buildBackgroundMeshCanvas(\n points: MeshGradientPoint[],\n softness: number,\n width: number,\n height: number,\n): HTMLCanvasElement {\n const canvas = document.createElement(\"canvas\");\n canvas.width = Math.max(1, Math.round(width));\n canvas.height = Math.max(1, Math.round(height));\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return canvas;\n\n const scale = Math.min(1, 320 / Math.max(canvas.width, canvas.height));\n const lw = Math.max(2, Math.round(canvas.width * scale));\n const lh = Math.max(2, Math.round(canvas.height * scale));\n const buf = document.createElement(\"canvas\");\n buf.width = lw;\n buf.height = lh;\n const bctx = buf.getContext(\"2d\");\n if (!bctx) return canvas;\n bctx.putImageData(renderMeshGradient(points, softness, lw, lh), 0, 0);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = \"high\";\n ctx.drawImage(buf, 0, 0, lw, lh, 0, 0, canvas.width, canvas.height);\n return canvas;\n}\n\n/** Fit a built-in canvas or uploaded image into a background-sized canvas. */\nexport function buildBackgroundImageCanvas(\n source: CanvasImageSource,\n sourceWidth: number,\n sourceHeight: number,\n width: number,\n height: number,\n fit: BackgroundImageFit,\n matte: string,\n zoom = 1,\n positionX = 0,\n positionY = 0,\n): HTMLCanvasElement {\n const canvas = document.createElement(\"canvas\");\n canvas.width = Math.max(1, Math.round(width));\n canvas.height = Math.max(1, Math.round(height));\n drawBackgroundMediaFrame(\n canvas,\n source,\n sourceWidth,\n sourceHeight,\n fit,\n matte,\n zoom,\n positionX,\n positionY,\n );\n return canvas;\n}\n\n/** Draw one image or video frame into an existing fitted background canvas. */\nexport function drawBackgroundMediaFrame(\n canvas: HTMLCanvasElement,\n source: CanvasImageSource,\n sourceWidth: number,\n sourceHeight: number,\n fit: BackgroundImageFit,\n matte: string,\n zoom = 1,\n positionX = 0,\n positionY = 0,\n): void {\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n ctx.fillStyle = matte;\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n const safeZoom = Math.max(0.1, zoom);\n const baseWidth =\n fit === \"stretch\"\n ? canvas.width\n : sourceWidth *\n (fit === \"contain\"\n ? Math.min(canvas.width / sourceWidth, canvas.height / sourceHeight)\n : Math.max(canvas.width / sourceWidth, canvas.height / sourceHeight));\n const baseHeight =\n fit === \"stretch\"\n ? canvas.height\n : sourceHeight *\n (fit === \"contain\"\n ? Math.min(canvas.width / sourceWidth, canvas.height / sourceHeight)\n : Math.max(canvas.width / sourceWidth, canvas.height / sourceHeight));\n const drawWidth = baseWidth * safeZoom;\n const drawHeight = baseHeight * safeZoom;\n const offsetX = (positionX / 100) * canvas.width;\n const offsetY = (positionY / 100) * canvas.height;\n ctx.drawImage(\n source,\n (canvas.width - drawWidth) / 2 + offsetX,\n (canvas.height - drawHeight) / 2 + offsetY,\n drawWidth,\n drawHeight,\n );\n}\n\n// ---- Built-in palette maps (pick via config.paletteSource) ----\n\nexport interface PaletteMapDef {\n label: string;\n /** \"gradient\" = a color-stop preset (1-D ramp + edge tint, reproducible with stops);\n * \"image\" = a true 2-D texture (build()) that the stops can't reproduce. */\n kind: \"gradient\" | \"image\";\n stops?: ColorStop[];\n edgeColor?: string;\n edgeAmount?: number;\n build?: (resolution?: number) => HTMLCanvasElement;\n}\n\nconst mk = (pairs: Array<[string, number]>): ColorStop[] =>\n pairs.map(([color, pos]) => ({ color, pos }));\n\n// ---- A genuine 2-D image map (procedural nebula): organic colour patches that vary in\n// BOTH axes via domain-warped value noise — the kind of thing flat stops can't make. ----\n\nfunction valueNoise2D(seed: number): (x: number, y: number) => number {\n const hash = (x: number, y: number): number => {\n let h = (x * 374761393 + y * 668265263 + seed * 1442695041) | 0;\n h = Math.imul(h ^ (h >>> 13), 1274126177);\n return ((h ^ (h >>> 16)) >>> 0) / 4294967295;\n };\n return (x, y) => {\n const xi = Math.floor(x);\n const yi = Math.floor(y);\n const u = smoothstep(x - xi);\n const v = smoothstep(y - yi);\n const a = hash(xi, yi);\n const b = hash(xi + 1, yi);\n const c = hash(xi, yi + 1);\n const d = hash(xi + 1, yi + 1);\n return a * (1 - u) * (1 - v) + b * u * (1 - v) + c * (1 - u) * v + d * u * v;\n };\n}\n\nfunction fbm(n: (x: number, y: number) => number, x: number, y: number, oct: number): number {\n let s = 0;\n let amp = 0.5;\n let f = 1;\n let tot = 0;\n for (let i = 0; i < oct; i++) {\n s += amp * n(x * f, y * f);\n tot += amp;\n f *= 2;\n amp *= 0.5;\n }\n return s / tot;\n}\n\nconst nebulaCache = new Map<number, HTMLCanvasElement>();\nfunction buildNebulaCanvas(resolution = 220): HTMLCanvasElement {\n const N = Math.max(64, Math.min(1280, Math.round(resolution)));\n const cached = nebulaCache.get(N);\n if (cached) return cached;\n const cv = document.createElement(\"canvas\");\n cv.width = N;\n cv.height = N;\n const ctx = cv.getContext(\"2d\");\n if (!ctx) return cv;\n const img = ctx.createImageData(N, N);\n const field = valueNoise2D(11);\n const warp = valueNoise2D(91);\n const bloomN = valueNoise2D(47);\n const COLORS: Array<[number, [number, number, number]]> = [\n [0.0, [34, 26, 92]], // deep indigo\n [0.24, [104, 48, 196]], // violet\n [0.46, [226, 70, 158]], // magenta\n [0.64, [255, 122, 92]], // coral\n [0.82, [255, 202, 110]], // gold\n [1.0, [70, 196, 188]], // teal\n ];\n const ramp = (t: number): [number, number, number] => {\n t = clamp01(t);\n for (let i = 1; i < COLORS.length; i++) {\n if (t <= COLORS[i][0]) {\n const [p0, c0] = COLORS[i - 1];\n const [p1, c1] = COLORS[i];\n const k = (t - p0) / (p1 - p0);\n return [\n c0[0] + (c1[0] - c0[0]) * k,\n c0[1] + (c1[1] - c0[1]) * k,\n c0[2] + (c1[2] - c0[2]) * k,\n ];\n }\n }\n return COLORS[COLORS.length - 1][1];\n };\n const S = 3.0;\n for (let y = 0; y < N; y++) {\n for (let x = 0; x < N; x++) {\n const u = (x / N) * S;\n const v = (y / N) * S;\n const wx = u + 0.7 * warp(u * 0.7 + 5, v * 0.7);\n const wy = v + 0.7 * warp(u * 0.7, v * 0.7 + 9);\n const f = fbm(field, wx, wy, 4);\n let [r, g, b] = ramp(f);\n const bloom = Math.max(0, fbm(bloomN, wx * 1.6 + 3, wy * 1.6, 3) - 0.55) * 1.7;\n r = r * (1 - bloom) + 70 * bloom;\n g = g * (1 - bloom) + 210 * bloom;\n b = b * (1 - bloom) + 200 * bloom;\n const i = (y * N + x) * 4;\n img.data[i] = r;\n img.data[i + 1] = g;\n img.data[i + 2] = b;\n img.data[i + 3] = 255;\n }\n }\n ctx.putImageData(img, 0, 0);\n nebulaCache.set(N, cv);\n return cv;\n}\n\nconst imageMapCache = new Map<string, HTMLCanvasElement>();\n\nfunction cachedImageMap(\n id: string,\n width: number,\n height: number,\n paint: (ctx: CanvasRenderingContext2D, width: number, height: number) => void,\n): HTMLCanvasElement {\n const cacheKey = `${id}|${width}x${height}`;\n const cached = imageMapCache.get(cacheKey);\n if (cached) return cached;\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n if (ctx) paint(ctx, width, height);\n imageMapCache.set(cacheKey, canvas);\n return canvas;\n}\n\nfunction buildVaporwaveCanvas(resolution = 240): HTMLCanvasElement {\n const width = Math.max(120, Math.min(2048, Math.round(resolution)));\n const height = Math.round((width * 2) / 3);\n return cachedImageMap(\"vaporwave\", width, height, (ctx, canvasWidth, canvasHeight) => {\n const unit = canvasWidth / 240;\n const sky = ctx.createLinearGradient(0, 0, 0, canvasHeight);\n sky.addColorStop(0, \"#120638\");\n sky.addColorStop(0.56, \"#8e2de2\");\n sky.addColorStop(1, \"#ff2fa8\");\n ctx.fillStyle = sky;\n ctx.fillRect(0, 0, canvasWidth, canvasHeight);\n\n const sunY = canvasHeight * 0.48;\n const sunRadius = canvasHeight * 0.28;\n const sun = ctx.createLinearGradient(0, sunY - sunRadius, 0, sunY + sunRadius);\n sun.addColorStop(0, \"#fff66d\");\n sun.addColorStop(1, \"#ff5cbe\");\n ctx.fillStyle = sun;\n ctx.beginPath();\n ctx.arc(canvasWidth / 2, sunY, sunRadius, 0, Math.PI * 2);\n ctx.fill();\n\n ctx.fillStyle = \"#3b126c\";\n for (let y = sunY + 3 * unit; y < sunY + sunRadius; y += 9 * unit) {\n ctx.fillRect(canvasWidth / 2 - sunRadius, y, sunRadius * 2, 4 * unit);\n }\n\n const horizon = canvasHeight * 0.72;\n ctx.fillStyle = \"#09051f\";\n ctx.fillRect(0, horizon, canvasWidth, canvasHeight - horizon);\n ctx.strokeStyle = \"#19e3ff\";\n ctx.lineWidth = Math.max(1, unit);\n ctx.globalAlpha = 0.72;\n for (let x = -canvasWidth; x <= canvasWidth * 2; x += canvasWidth / 12) {\n ctx.beginPath();\n ctx.moveTo(canvasWidth / 2, horizon);\n ctx.lineTo(x, canvasHeight);\n ctx.stroke();\n }\n for (let row = 0; row < 7; row++) {\n const t = row / 6;\n const y = horizon + (canvasHeight - horizon) * t * t;\n ctx.beginPath();\n ctx.moveTo(0, y);\n ctx.lineTo(canvasWidth, y);\n ctx.stroke();\n }\n ctx.globalAlpha = 1;\n });\n}\n\nfunction buildKaleidoscopeCanvas(resolution = 220): HTMLCanvasElement {\n const size = Math.max(110, Math.min(2048, Math.round(resolution)));\n return cachedImageMap(\"kaleidoscope\", size, size, (ctx, width, height) => {\n const unit = width / 220;\n const colors = [\"#00e5ff\", \"#6c3bff\", \"#ff2ca8\", \"#ff7a00\", \"#ffe600\", \"#16f7a6\"];\n const cx = width / 2;\n const cy = height / 2;\n const radius = Math.hypot(width, height);\n const slices = 24;\n for (let i = 0; i < slices; i++) {\n const a0 = (i / slices) * Math.PI * 2;\n const a1 = ((i + 1) / slices) * Math.PI * 2;\n ctx.fillStyle = colors[(i * 5) % colors.length];\n ctx.beginPath();\n ctx.moveTo(cx, cy);\n ctx.lineTo(cx + Math.cos(a0) * radius, cy + Math.sin(a0) * radius);\n ctx.lineTo(cx + Math.cos(a1) * radius, cy + Math.sin(a1) * radius);\n ctx.closePath();\n ctx.fill();\n }\n for (let ring = 1; ring <= 4; ring++) {\n ctx.strokeStyle = ring % 2 ? \"rgba(255,255,255,.55)\" : \"rgba(8,5,35,.6)\";\n ctx.lineWidth = 5 * unit;\n ctx.beginPath();\n ctx.arc(cx, cy, ring * 25 * unit, 0, Math.PI * 2);\n ctx.stroke();\n }\n });\n}\n\n/** Named palette maps. \"gradient\" kinds are stop presets; \"image\" kinds are true 2-D maps. */\nexport const PALETTE_MAPS: Record<string, PaletteMapDef> = {\n palestine: {\n label: \"Palestine\",\n kind: \"gradient\",\n stops: mk([\n [\"#000000\", 0],\n [\"#f7f7f2\", 0.34],\n [\"#149954\", 0.67],\n [\"#e4312b\", 1],\n ]),\n edgeColor: \"#e4312b\",\n edgeAmount: 0.22,\n },\n grandLine: {\n label: \"Grand Line\",\n kind: \"gradient\",\n stops: mk([\n [\"#071b33\", 0],\n [\"#087ea4\", 0.22],\n [\"#45d4c5\", 0.4],\n [\"#f4d35e\", 0.58],\n [\"#f2b84b\", 0.72],\n [\"#d62828\", 0.86],\n [\"#18130f\", 1],\n ]),\n edgeColor: \"#061426\",\n edgeAmount: 0.28,\n },\n vaporwave: { label: \"Vaporwave Sunset\", kind: \"image\", build: buildVaporwaveCanvas },\n kaleidoscope: { label: \"Kaleidoscope\", kind: \"image\", build: buildKaleidoscopeCanvas },\n nebula: { label: \"Nebula (2D)\", kind: \"image\", build: buildNebulaCanvas },\n sunset: {\n label: \"Sunset\",\n kind: \"gradient\",\n stops: mk([\n [\"#3b1c6b\", 0],\n [\"#8b2fa0\", 0.3],\n [\"#e0457a\", 0.56],\n [\"#ff7a3d\", 0.8],\n [\"#ffd166\", 1],\n ]),\n edgeColor: \"#2a1a6b\",\n edgeAmount: 0.3,\n },\n aurora: {\n label: \"Aurora\",\n kind: \"gradient\",\n stops: mk([\n [\"#0b3d4f\", 0],\n [\"#1fb89e\", 0.3],\n [\"#5ee0a0\", 0.52],\n [\"#4d8ef0\", 0.76],\n [\"#9b5de5\", 1],\n ]),\n edgeColor: \"#0a2540\",\n edgeAmount: 0.35,\n },\n ocean: {\n label: \"Ocean\",\n kind: \"gradient\",\n stops: mk([\n [\"#0a1f4d\", 0],\n [\"#1f6fb8\", 0.36],\n [\"#2bd0d0\", 0.66],\n [\"#a8f0e2\", 1],\n ]),\n edgeColor: \"#061233\",\n edgeAmount: 0.4,\n },\n ember: {\n label: \"Ember\",\n kind: \"gradient\",\n stops: mk([\n [\"#2a0707\", 0],\n [\"#a81e1e\", 0.34],\n [\"#ff5a2e\", 0.64],\n [\"#ffd24a\", 1],\n ]),\n edgeColor: \"#150404\",\n edgeAmount: 0.28,\n },\n iris: {\n label: \"Iris\",\n kind: \"gradient\",\n stops: mk([\n [\"#2e1065\", 0],\n [\"#7c3aed\", 0.34],\n [\"#db2777\", 0.64],\n [\"#f5b8f0\", 1],\n ]),\n edgeColor: \"#1a0840\",\n edgeAmount: 0.35,\n },\n mono: {\n label: \"Mono\",\n kind: \"gradient\",\n stops: mk([\n [\"#16161e\", 0],\n [\"#6b7280\", 0.4],\n [\"#c2c8d2\", 0.72],\n [\"#f6f8fb\", 1],\n ]),\n edgeColor: \"#0a0a12\",\n edgeAmount: 0.2,\n },\n};\n\n/** The canvas for a named map (its 2-D image, or its stops+edge gradient). */\nexport function paletteMapCanvas(def: PaletteMapDef, resolution?: number): HTMLCanvasElement {\n if (def.build) return def.build(resolution);\n return buildPaletteCanvas({\n stops: def.stops ?? [],\n edgeColor: def.edgeColor ?? \"#8e9dff\",\n edgeAmount: def.edgeAmount ?? 0,\n });\n}\n\n/** Load an arbitrary image (URL or object-URL) as a palette texture — \"bring your own map\". */\nexport function loadPaletteImage(url: string): THREE.Texture {\n return configurePaletteTexture(new THREE.TextureLoader().load(url));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"palette.js","names":[],"sources":["../../src/renderer/palette.ts"],"sourcesContent":["import * as THREE from \"three\";\nimport { clamp01 } from \"../util/math\";\nimport { createDefaultMeshPoints } from \"../config/model\";\nimport type {\n BackgroundImageFit,\n BasicGradientType,\n ColorStop,\n MeshGradientPoint,\n} from \"../config/model\";\n\n/**\n * Bakes the gradient stops into a 2D palette texture, sampled in the shader as\n * `texture2D(u_paletteTexture, vec2(uv.x, uv.y))`. The texture is a real image, so\n * colour can vary independently along BOTH axes:\n * - X (uv.x, along the length): the gradient stops.\n * - Y (uv.y, across the width): an \"edge tint\" blended toward both long edges\n */\n\nconst TEX_W = 256; // resolution along the gradient (length)\nconst TEX_H = 64; // resolution across the width\n\nfunction smoothstep(value: number): number {\n return value * value * (3 - 2 * value);\n}\n\n/** \"#rrggbb\" → \"rgba(r,g,b,a)\" for canvas fills with alpha. */\nfunction hexToRgba(hex: string, alpha: number): string {\n const c = new THREE.Color(hex); // parses many formats; .r/.g/.b are linear…\n // …but we want the original sRGB bytes for the canvas, so re-encode.\n const srgb = c.clone().convertLinearToSRGB();\n const r = Math.round(srgb.r * 255);\n const g = Math.round(srgb.g * 255);\n const b = Math.round(srgb.b * 255);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n\nexport interface PaletteTextureOptions {\n stops: ColorStop[];\n /** Cross-width edge tint colour (e.g. periwinkle). */\n edgeColor: string;\n /** 0 = flat 1-D gradient (no 2nd axis); higher = stronger cool edges. */\n edgeAmount: number;\n}\n\n/** Draw the palette into a 2D canvas. */\nexport function buildPaletteCanvas(opts: PaletteTextureOptions): HTMLCanvasElement {\n const canvas = document.createElement(\"canvas\");\n canvas.width = TEX_W;\n canvas.height = TEX_H;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return canvas;\n\n // X axis: the main gradient (stops sorted by position).\n const stops = [...opts.stops].sort((a, b) => a.pos - b.pos);\n const grad = ctx.createLinearGradient(0, 0, TEX_W, 0);\n if (stops.length === 0) {\n grad.addColorStop(0, \"#ffffff\");\n } else {\n for (const s of stops) grad.addColorStop(clamp01(s.pos), s.color);\n }\n ctx.fillStyle = grad;\n ctx.fillRect(0, 0, TEX_W, TEX_H);\n\n // Y axis: blend the edge colour toward both long edges (V-shaped alpha), 0 in the\n // middle — a genuine second dimension of colour.\n const a = clamp01(opts.edgeAmount);\n if (a > 0.001) {\n const eg = ctx.createLinearGradient(0, 0, 0, TEX_H);\n eg.addColorStop(0, hexToRgba(opts.edgeColor, a));\n eg.addColorStop(0.5, hexToRgba(opts.edgeColor, 0));\n eg.addColorStop(1, hexToRgba(opts.edgeColor, a));\n ctx.fillStyle = eg;\n ctx.fillRect(0, 0, TEX_W, TEX_H);\n }\n\n return canvas;\n}\n\n/** A small string that changes whenever the texture would need rebuilding. */\nexport function paletteSignature(opts: PaletteTextureOptions): string {\n const s = opts.stops.map((x) => `${x.color}@${x.pos.toFixed(3)}`).join(\",\");\n return `${s}|${opts.edgeColor}|${opts.edgeAmount.toFixed(3)}`;\n}\n\n/** Sampling config shared by every palette-texture source (canvas, image, LUT, video):\n * sRGB (the GPU linearizes on sample), linear filtering, clamped edges, no mipmaps. */\nexport function configurePaletteTexture<T extends THREE.Texture>(tex: T): T {\n tex.colorSpace = THREE.SRGBColorSpace;\n tex.minFilter = THREE.LinearFilter;\n tex.magFilter = THREE.LinearFilter;\n tex.wrapS = THREE.ClampToEdgeWrapping;\n tex.wrapT = THREE.ClampToEdgeWrapping;\n tex.generateMipmaps = false;\n return tex;\n}\n\n/** Wrap a canvas as a sampling-ready CanvasTexture. */\nexport function canvasToTexture(canvas: HTMLCanvasElement): THREE.CanvasTexture {\n return configurePaletteTexture(new THREE.CanvasTexture(canvas));\n}\n\n/** Build a ready-to-use CanvasTexture from gradient stops + edge tint. */\nexport function buildPaletteTexture(opts: PaletteTextureOptions): THREE.CanvasTexture {\n return canvasToTexture(buildPaletteCanvas(opts));\n}\n\nexport interface BackgroundGradientOptions {\n stops: ColorStop[];\n type: BasicGradientType;\n angle: number;\n width: number;\n height: number;\n}\n\n/** Draw an export-ready linear, radial, or conic background gradient. */\nexport function buildBackgroundGradientCanvas(opts: BackgroundGradientOptions): HTMLCanvasElement {\n const canvas = document.createElement(\"canvas\");\n canvas.width = Math.max(1, Math.round(opts.width));\n canvas.height = Math.max(1, Math.round(opts.height));\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return canvas;\n\n const { width, height } = canvas;\n const cx = width / 2;\n const cy = height / 2;\n const angle = (opts.angle * Math.PI) / 180;\n let gradient: CanvasGradient;\n if (opts.type === \"radial\") {\n gradient = ctx.createRadialGradient(cx, cy, 0, cx, cy, Math.hypot(cx, cy));\n } else if (opts.type === \"conic\") {\n gradient = ctx.createConicGradient(angle, cx, cy);\n } else {\n const radius = Math.abs(Math.sin(angle)) * cx + Math.abs(Math.cos(angle)) * cy;\n const dx = Math.sin(angle) * radius;\n const dy = -Math.cos(angle) * radius;\n gradient = ctx.createLinearGradient(cx - dx, cy - dy, cx + dx, cy + dy);\n }\n\n const stops = [...opts.stops].sort((a, b) => a.pos - b.pos);\n if (stops.length === 0) gradient.addColorStop(0, \"#ffffff\");\n else for (const stop of stops) gradient.addColorStop(clamp01(stop.pos), stop.color);\n ctx.fillStyle = gradient;\n ctx.fillRect(0, 0, width, height);\n return canvas;\n}\n\n/** Linear (0–1) → sRGB byte (0–255). */\nfunction linearToSrgbByte(linear: number): number {\n const c = clamp01(linear);\n const srgb = c <= 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;\n return Math.round(clamp01(srgb) * 255);\n}\n\n/**\n * The single source of truth for mesh-gradient rendering — shared by the background\n * (buildBackgroundMeshCanvas) and the on-canvas MeshGradientEditor preview, and mirroring the\n * wave's meshGradient shader. An inverse-distance-weighted blend of coloured points, computed in\n * LINEAR RGB with y measured UP (point.y = 1 is the top). Returns an ImageData to putImageData\n * onto any 2D context.\n */\nexport function renderMeshGradient(\n points: MeshGradientPoint[],\n softness: number,\n width: number,\n height: number,\n): ImageData {\n const w = Math.max(1, Math.round(width));\n const h = Math.max(1, Math.round(height));\n const image = new ImageData(w, h);\n const data = image.data;\n const pts = points.length >= 2 ? points : createDefaultMeshPoints();\n const colors = pts.map((p) => new THREE.Color(p.color)); // .r/.g/.b are LINEAR\n const exponent = 4.8 + (1.35 - 4.8) * clamp01(softness);\n for (let py = 0; py < h; py++) {\n const y = 1 - py / Math.max(1, h - 1);\n for (let px = 0; px < w; px++) {\n const x = px / Math.max(1, w - 1);\n let r = 0;\n let g = 0;\n let b = 0;\n let weightSum = 0;\n for (let i = 0; i < pts.length; i++) {\n const influence = Math.max(pts[i].influence, 0.05);\n const distance = Math.hypot(x - pts[i].x, y - pts[i].y) / influence;\n const weight = 1 / (Math.pow(Math.max(distance, 0.012), exponent) + 0.002);\n r += colors[i].r * weight;\n g += colors[i].g * weight;\n b += colors[i].b * weight;\n weightSum += weight;\n }\n const iw = Math.max(weightSum, 0.0001);\n const off = (py * w + px) * 4;\n data[off] = linearToSrgbByte(r / iw);\n data[off + 1] = linearToSrgbByte(g / iw);\n data[off + 2] = linearToSrgbByte(b / iw);\n data[off + 3] = 255;\n }\n }\n return image;\n}\n\n/**\n * Draw a mesh-gradient background. Mesh gradients are low-frequency, so we render at a small\n * internal size (capped ~320 px) via renderMeshGradient and scale up smoothly.\n */\nexport function buildBackgroundMeshCanvas(\n points: MeshGradientPoint[],\n softness: number,\n width: number,\n height: number,\n): HTMLCanvasElement {\n const canvas = document.createElement(\"canvas\");\n canvas.width = Math.max(1, Math.round(width));\n canvas.height = Math.max(1, Math.round(height));\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return canvas;\n\n const scale = Math.min(1, 320 / Math.max(canvas.width, canvas.height));\n const lw = Math.max(2, Math.round(canvas.width * scale));\n const lh = Math.max(2, Math.round(canvas.height * scale));\n const buf = document.createElement(\"canvas\");\n buf.width = lw;\n buf.height = lh;\n const bctx = buf.getContext(\"2d\");\n if (!bctx) return canvas;\n bctx.putImageData(renderMeshGradient(points, softness, lw, lh), 0, 0);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = \"high\";\n ctx.drawImage(buf, 0, 0, lw, lh, 0, 0, canvas.width, canvas.height);\n return canvas;\n}\n\n/** Fit a built-in canvas or uploaded image into a background-sized canvas. */\nexport function buildBackgroundImageCanvas(\n source: CanvasImageSource,\n sourceWidth: number,\n sourceHeight: number,\n width: number,\n height: number,\n fit: BackgroundImageFit,\n matte: string,\n zoom = 1,\n positionX = 0,\n positionY = 0,\n): HTMLCanvasElement {\n const canvas = document.createElement(\"canvas\");\n canvas.width = Math.max(1, Math.round(width));\n canvas.height = Math.max(1, Math.round(height));\n drawBackgroundMediaFrame(\n canvas,\n source,\n sourceWidth,\n sourceHeight,\n fit,\n matte,\n zoom,\n positionX,\n positionY,\n );\n return canvas;\n}\n\n/** Draw one image or video frame into an existing fitted background canvas. */\nexport function drawBackgroundMediaFrame(\n canvas: HTMLCanvasElement,\n source: CanvasImageSource,\n sourceWidth: number,\n sourceHeight: number,\n fit: BackgroundImageFit,\n matte: string,\n zoom = 1,\n positionX = 0,\n positionY = 0,\n): void {\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n ctx.fillStyle = matte;\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n const safeZoom = Math.max(0.1, zoom);\n const baseWidth =\n fit === \"stretch\"\n ? canvas.width\n : sourceWidth *\n (fit === \"contain\"\n ? Math.min(canvas.width / sourceWidth, canvas.height / sourceHeight)\n : Math.max(canvas.width / sourceWidth, canvas.height / sourceHeight));\n const baseHeight =\n fit === \"stretch\"\n ? canvas.height\n : sourceHeight *\n (fit === \"contain\"\n ? Math.min(canvas.width / sourceWidth, canvas.height / sourceHeight)\n : Math.max(canvas.width / sourceWidth, canvas.height / sourceHeight));\n const drawWidth = baseWidth * safeZoom;\n const drawHeight = baseHeight * safeZoom;\n const offsetX = (positionX / 100) * canvas.width;\n const offsetY = (positionY / 100) * canvas.height;\n ctx.drawImage(\n source,\n (canvas.width - drawWidth) / 2 + offsetX,\n (canvas.height - drawHeight) / 2 + offsetY,\n drawWidth,\n drawHeight,\n );\n}\n\n// ---- Built-in palette maps (pick via config.paletteSource) ----\n\nexport interface PaletteMapDef {\n label: string;\n /** \"gradient\" = a color-stop preset (1-D ramp + edge tint, reproducible with stops);\n * \"image\" = a true 2-D texture (build()) that the stops can't reproduce. */\n kind: \"gradient\" | \"image\";\n stops?: ColorStop[];\n edgeColor?: string;\n edgeAmount?: number;\n build?: (resolution?: number) => HTMLCanvasElement;\n}\n\nconst mk = (pairs: Array<[string, number]>): ColorStop[] =>\n pairs.map(([color, pos]) => ({ color, pos }));\n\n// ---- A genuine 2-D image map (procedural nebula): organic colour patches that vary in\n// BOTH axes via domain-warped value noise — the kind of thing flat stops can't make. ----\n\nfunction valueNoise2D(seed: number): (x: number, y: number) => number {\n const hash = (x: number, y: number): number => {\n let h = (x * 374761393 + y * 668265263 + seed * 1442695041) | 0;\n h = Math.imul(h ^ (h >>> 13), 1274126177);\n return ((h ^ (h >>> 16)) >>> 0) / 4294967295;\n };\n return (x, y) => {\n const xi = Math.floor(x);\n const yi = Math.floor(y);\n const u = smoothstep(x - xi);\n const v = smoothstep(y - yi);\n const a = hash(xi, yi);\n const b = hash(xi + 1, yi);\n const c = hash(xi, yi + 1);\n const d = hash(xi + 1, yi + 1);\n return a * (1 - u) * (1 - v) + b * u * (1 - v) + c * (1 - u) * v + d * u * v;\n };\n}\n\nfunction fbm(n: (x: number, y: number) => number, x: number, y: number, oct: number): number {\n let s = 0;\n let amp = 0.5;\n let f = 1;\n let tot = 0;\n for (let i = 0; i < oct; i++) {\n s += amp * n(x * f, y * f);\n tot += amp;\n f *= 2;\n amp *= 0.5;\n }\n return s / tot;\n}\n\nconst nebulaCache = new Map<number, HTMLCanvasElement>();\nfunction buildNebulaCanvas(resolution = 220): HTMLCanvasElement {\n const N = Math.max(64, Math.min(1280, Math.round(resolution)));\n const cached = nebulaCache.get(N);\n if (cached) return cached;\n const cv = document.createElement(\"canvas\");\n cv.width = N;\n cv.height = N;\n const ctx = cv.getContext(\"2d\");\n if (!ctx) return cv;\n const img = ctx.createImageData(N, N);\n const field = valueNoise2D(11);\n const warp = valueNoise2D(91);\n const bloomN = valueNoise2D(47);\n const COLORS: Array<[number, [number, number, number]]> = [\n [0.0, [34, 26, 92]], // deep indigo\n [0.24, [104, 48, 196]], // violet\n [0.46, [226, 70, 158]], // magenta\n [0.64, [255, 122, 92]], // coral\n [0.82, [255, 202, 110]], // gold\n [1.0, [70, 196, 188]], // teal\n ];\n const ramp = (t: number): [number, number, number] => {\n t = clamp01(t);\n for (let i = 1; i < COLORS.length; i++) {\n if (t <= COLORS[i][0]) {\n const [p0, c0] = COLORS[i - 1];\n const [p1, c1] = COLORS[i];\n const k = (t - p0) / (p1 - p0);\n return [\n c0[0] + (c1[0] - c0[0]) * k,\n c0[1] + (c1[1] - c0[1]) * k,\n c0[2] + (c1[2] - c0[2]) * k,\n ];\n }\n }\n return COLORS[COLORS.length - 1][1];\n };\n const S = 3.0;\n for (let y = 0; y < N; y++) {\n for (let x = 0; x < N; x++) {\n const u = (x / N) * S;\n const v = (y / N) * S;\n const wx = u + 0.7 * warp(u * 0.7 + 5, v * 0.7);\n const wy = v + 0.7 * warp(u * 0.7, v * 0.7 + 9);\n const f = fbm(field, wx, wy, 4);\n let [r, g, b] = ramp(f);\n const bloom = Math.max(0, fbm(bloomN, wx * 1.6 + 3, wy * 1.6, 3) - 0.55) * 1.7;\n r = r * (1 - bloom) + 70 * bloom;\n g = g * (1 - bloom) + 210 * bloom;\n b = b * (1 - bloom) + 200 * bloom;\n const i = (y * N + x) * 4;\n img.data[i] = r;\n img.data[i + 1] = g;\n img.data[i + 2] = b;\n img.data[i + 3] = 255;\n }\n }\n ctx.putImageData(img, 0, 0);\n nebulaCache.set(N, cv);\n return cv;\n}\n\nconst imageMapCache = new Map<string, HTMLCanvasElement>();\n\nfunction cachedImageMap(\n id: string,\n width: number,\n height: number,\n paint: (ctx: CanvasRenderingContext2D, width: number, height: number) => void,\n): HTMLCanvasElement {\n const cacheKey = `${id}|${width}x${height}`;\n const cached = imageMapCache.get(cacheKey);\n if (cached) return cached;\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n if (ctx) paint(ctx, width, height);\n imageMapCache.set(cacheKey, canvas);\n return canvas;\n}\n\nfunction buildVaporwaveCanvas(resolution = 240): HTMLCanvasElement {\n const width = Math.max(120, Math.min(2048, Math.round(resolution)));\n const height = Math.round((width * 2) / 3);\n return cachedImageMap(\"vaporwave\", width, height, (ctx, canvasWidth, canvasHeight) => {\n const unit = canvasWidth / 240;\n const sky = ctx.createLinearGradient(0, 0, 0, canvasHeight);\n sky.addColorStop(0, \"#120638\");\n sky.addColorStop(0.56, \"#8e2de2\");\n sky.addColorStop(1, \"#ff2fa8\");\n ctx.fillStyle = sky;\n ctx.fillRect(0, 0, canvasWidth, canvasHeight);\n\n const sunY = canvasHeight * 0.48;\n const sunRadius = canvasHeight * 0.28;\n const sun = ctx.createLinearGradient(0, sunY - sunRadius, 0, sunY + sunRadius);\n sun.addColorStop(0, \"#fff66d\");\n sun.addColorStop(1, \"#ff5cbe\");\n ctx.fillStyle = sun;\n ctx.beginPath();\n ctx.arc(canvasWidth / 2, sunY, sunRadius, 0, Math.PI * 2);\n ctx.fill();\n\n ctx.fillStyle = \"#3b126c\";\n for (let y = sunY + 3 * unit; y < sunY + sunRadius; y += 9 * unit) {\n ctx.fillRect(canvasWidth / 2 - sunRadius, y, sunRadius * 2, 4 * unit);\n }\n\n const horizon = canvasHeight * 0.72;\n ctx.fillStyle = \"#09051f\";\n ctx.fillRect(0, horizon, canvasWidth, canvasHeight - horizon);\n ctx.strokeStyle = \"#19e3ff\";\n ctx.lineWidth = Math.max(1, unit);\n ctx.globalAlpha = 0.72;\n for (let x = -canvasWidth; x <= canvasWidth * 2; x += canvasWidth / 12) {\n ctx.beginPath();\n ctx.moveTo(canvasWidth / 2, horizon);\n ctx.lineTo(x, canvasHeight);\n ctx.stroke();\n }\n for (let row = 0; row < 7; row++) {\n const t = row / 6;\n const y = horizon + (canvasHeight - horizon) * t * t;\n ctx.beginPath();\n ctx.moveTo(0, y);\n ctx.lineTo(canvasWidth, y);\n ctx.stroke();\n }\n ctx.globalAlpha = 1;\n });\n}\n\nfunction buildKaleidoscopeCanvas(resolution = 220): HTMLCanvasElement {\n const size = Math.max(110, Math.min(2048, Math.round(resolution)));\n return cachedImageMap(\"kaleidoscope\", size, size, (ctx, width, height) => {\n const unit = width / 220;\n const colors = [\"#00e5ff\", \"#6c3bff\", \"#ff2ca8\", \"#ff7a00\", \"#ffe600\", \"#16f7a6\"];\n const cx = width / 2;\n const cy = height / 2;\n const radius = Math.hypot(width, height);\n const slices = 24;\n for (let i = 0; i < slices; i++) {\n const a0 = (i / slices) * Math.PI * 2;\n const a1 = ((i + 1) / slices) * Math.PI * 2;\n ctx.fillStyle = colors[(i * 5) % colors.length];\n ctx.beginPath();\n ctx.moveTo(cx, cy);\n ctx.lineTo(cx + Math.cos(a0) * radius, cy + Math.sin(a0) * radius);\n ctx.lineTo(cx + Math.cos(a1) * radius, cy + Math.sin(a1) * radius);\n ctx.closePath();\n ctx.fill();\n }\n for (let ring = 1; ring <= 4; ring++) {\n ctx.strokeStyle = ring % 2 ? \"rgba(255,255,255,.55)\" : \"rgba(8,5,35,.6)\";\n ctx.lineWidth = 5 * unit;\n ctx.beginPath();\n ctx.arc(cx, cy, ring * 25 * unit, 0, Math.PI * 2);\n ctx.stroke();\n }\n });\n}\n\n/** Named palette maps. \"gradient\" kinds are stop presets; \"image\" kinds are true 2-D maps. */\nexport const PALETTE_MAPS: Record<string, PaletteMapDef> = {\n palestine: {\n label: \"Palestine\",\n kind: \"gradient\",\n stops: mk([\n [\"#000000\", 0],\n [\"#f7f7f2\", 0.34],\n [\"#149954\", 0.67],\n [\"#e4312b\", 1],\n ]),\n edgeColor: \"#e4312b\",\n edgeAmount: 0.22,\n },\n spain: {\n label: \"Spain\",\n kind: \"gradient\",\n stops: mk([\n [\"#aa151b\", 0], // crimson — top stripe\n [\"#aa151b\", 0.24], // hold the red band\n [\"#f1bf00\", 0.34], // into gold\n [\"#f1bf00\", 0.66], // hold the wide gold band (middle 50%)\n [\"#aa151b\", 0.76], // back to red\n [\"#aa151b\", 1], // crimson — bottom stripe\n ]),\n edgeColor: \"#7a0f14\", // deep oxblood edge tint (mirrors Palestine's red edge)\n edgeAmount: 0.22,\n },\n grandLine: {\n label: \"Grand Line\",\n kind: \"gradient\",\n stops: mk([\n [\"#071b33\", 0],\n [\"#087ea4\", 0.22],\n [\"#45d4c5\", 0.4],\n [\"#f4d35e\", 0.58],\n [\"#f2b84b\", 0.72],\n [\"#d62828\", 0.86],\n [\"#18130f\", 1],\n ]),\n edgeColor: \"#061426\",\n edgeAmount: 0.28,\n },\n vaporwave: { label: \"Vaporwave Sunset\", kind: \"image\", build: buildVaporwaveCanvas },\n kaleidoscope: { label: \"Kaleidoscope\", kind: \"image\", build: buildKaleidoscopeCanvas },\n nebula: { label: \"Nebula (2D)\", kind: \"image\", build: buildNebulaCanvas },\n sunset: {\n label: \"Sunset\",\n kind: \"gradient\",\n stops: mk([\n [\"#3b1c6b\", 0],\n [\"#8b2fa0\", 0.3],\n [\"#e0457a\", 0.56],\n [\"#ff7a3d\", 0.8],\n [\"#ffd166\", 1],\n ]),\n edgeColor: \"#2a1a6b\",\n edgeAmount: 0.3,\n },\n aurora: {\n label: \"Aurora\",\n kind: \"gradient\",\n stops: mk([\n [\"#0b3d4f\", 0],\n [\"#1fb89e\", 0.3],\n [\"#5ee0a0\", 0.52],\n [\"#4d8ef0\", 0.76],\n [\"#9b5de5\", 1],\n ]),\n edgeColor: \"#0a2540\",\n edgeAmount: 0.35,\n },\n ocean: {\n label: \"Ocean\",\n kind: \"gradient\",\n stops: mk([\n [\"#0a1f4d\", 0],\n [\"#1f6fb8\", 0.36],\n [\"#2bd0d0\", 0.66],\n [\"#a8f0e2\", 1],\n ]),\n edgeColor: \"#061233\",\n edgeAmount: 0.4,\n },\n ember: {\n label: \"Ember\",\n kind: \"gradient\",\n stops: mk([\n [\"#2a0707\", 0],\n [\"#a81e1e\", 0.34],\n [\"#ff5a2e\", 0.64],\n [\"#ffd24a\", 1],\n ]),\n edgeColor: \"#150404\",\n edgeAmount: 0.28,\n },\n iris: {\n label: \"Iris\",\n kind: \"gradient\",\n stops: mk([\n [\"#2e1065\", 0],\n [\"#7c3aed\", 0.34],\n [\"#db2777\", 0.64],\n [\"#f5b8f0\", 1],\n ]),\n edgeColor: \"#1a0840\",\n edgeAmount: 0.35,\n },\n mono: {\n label: \"Mono\",\n kind: \"gradient\",\n stops: mk([\n [\"#16161e\", 0],\n [\"#6b7280\", 0.4],\n [\"#c2c8d2\", 0.72],\n [\"#f6f8fb\", 1],\n ]),\n edgeColor: \"#0a0a12\",\n edgeAmount: 0.2,\n },\n};\n\n/** The canvas for a named map (its 2-D image, or its stops+edge gradient). */\nexport function paletteMapCanvas(def: PaletteMapDef, resolution?: number): HTMLCanvasElement {\n if (def.build) return def.build(resolution);\n return buildPaletteCanvas({\n stops: def.stops ?? [],\n edgeColor: def.edgeColor ?? \"#8e9dff\",\n edgeAmount: def.edgeAmount ?? 0,\n });\n}\n\n/** Load an arbitrary image (URL or object-URL) as a palette texture — \"bring your own map\". */\nexport function loadPaletteImage(url: string): THREE.Texture {\n return configurePaletteTexture(new THREE.TextureLoader().load(url));\n}\n"],"mappings":"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"}
|