@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.
- package/README.md +47 -0
- package/dist/config/model.d.ts +104 -1
- package/dist/config/model.js +111 -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 +34 -0
- package/dist/renderer/WaveRenderer.js +232 -7
- 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 +105 -0
- 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 +864 -219
- 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 -1
- package/dist/studio/index.js +2 -1
- package/dist/studio/randomize.d.ts +3 -3
- package/dist/studio/randomize.js +4 -4
- package/dist/studio/randomize.js.map +1 -1
- package/dist/studio/thumbnail.d.ts +14 -0
- package/dist/studio/thumbnail.js +32 -0
- package/dist/studio/thumbnail.js.map +1 -0
- package/package.json +1 -1
- package/skills/wave3d/SKILL.md +20 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"randomize.js","names":[],"sources":["../../src/studio/randomize.ts"],"sourcesContent":["/**\n * The \"Randomize\" helpers: `randomizeConfig` (\"Randomize All\") plus the per-section randomizers\n * the studio wires to each folder's 🎲. Studio-facing (not part of the renderer core), exposed via\n * the `@wave3d/core/studio` entry.\n */\nimport { clamp, roundTo } from \"../util/math\";\nimport type {\n StudioConfig,\n WaveConfig,\n MeshGradientPoint,\n ColorStop,\n GradientType,\n} from \"../config/model\";\n\nconst RANDOM_PALETTES: string[][] = [\n [\"#9bb0ff\", \"#ffb14e\", \"#ff6aa8\", \"#b15cff\", \"#6f7bff\"],\n [\"#ffd166\", \"#ff6b6b\", \"#c44dff\", \"#4d79ff\", \"#22d3ee\"],\n [\"#f97316\", \"#ec4899\", \"#8b5cf6\", \"#3b82f6\", \"#06b6d4\"],\n [\"#00f5d4\", \"#00bbf9\", \"#9b5de5\", \"#f15bb5\", \"#fee440\"],\n [\"#7c3aed\", \"#db2777\", \"#f59e0b\", \"#10b981\", \"#06b6d4\"],\n];\n\nfunction rand(min: number, max: number): number {\n return min + Math.random() * (max - min);\n}\n\nfunction pick<T>(arr: T[]): T {\n return arr[Math.floor(Math.random() * arr.length)];\n}\n\n/** \"Randomize All\": keep the scene (background, camera, lights, quality) and randomize the\n * post-fx plus every wave independently — so a multi-wave stack becomes visibly varied.\n * The camera is deliberately left alone so the result always lands in view. */\nexport function randomizeConfig(base: StudioConfig): StudioConfig {\n const cfg = structuredClone(base);\n cfg.grain = r2(rand(0, 1.5));\n cfg.blur = r3(rand(0, 0.02));\n for (const s of cfg.waves) randomizeWave(s);\n return cfg;\n}\n\n/** Randomize a whole wave: colour, shape (displacement/twist), transform, finish + the\n * compositing knobs (speed/opacity/seed/blend). Used by each wave's 🎲 and by \"Randomize All\". */\nexport function randomizeWave(s: WaveConfig): void {\n randomizeGradient(s);\n randomizeColor(s);\n randomizeSpine(s);\n randomizeTransform(s);\n randomizeTwist(s);\n randomizeFinish(s);\n s.speed = r2(rand(0.02, 0.15));\n s.opacity = r2(rand(0.6, 1));\n s.seed = r2(rand(0, 12));\n // Bias to the vivid squared blend; occasionally normal/additive (skip multiply — it muddies\n // the wave on light/transparent backgrounds).\n s.blendMode = pick([\"squared\", \"squared\", \"normal\", \"additive\"]);\n}\n\n// ---- Per-section randomizers (each mutates only its own fields, in place) ----\n\nfunction r2(x: number): number {\n return roundTo(x, 2);\n}\nfunction r3(x: number): number {\n return roundTo(x, 3);\n}\n\nconst LIGHT_TINTS = [\"#ffffff\", \"#ffffff\", \"#fff0e0\", \"#e2e8ff\", \"#ffe2f0\", \"#e2fff4\", \"#fff6cc\"];\n\n/** Random sorted positions in [0,1] with the ends pinned to 0 and 1. */\nfunction randomSortedPositions(n: number): number[] {\n if (n <= 1) return [0];\n if (n === 2) return [0, 1];\n const inner = Array.from({ length: n - 2 }, () => r2(rand(0.06, 0.94))).sort((a, b) => a - b);\n return [0, ...inner, 1];\n}\n\nfunction randomMeshPoints(colors: string[]): MeshGradientPoint[] {\n return colors.map((color) => ({\n color,\n x: r2(rand(0.08, 0.92)),\n y: r2(rand(0.08, 0.92)),\n influence: r2(rand(0.5, 0.95)),\n }));\n}\n\nfunction randomStops(colors: string[]): ColorStop[] {\n const positions = randomSortedPositions(colors.length);\n return colors.map((color, i) => ({ color, pos: positions[i] }));\n}\n\nexport function randomizeGradient(c: WaveConfig): void {\n const colors = pick(RANDOM_PALETTES);\n const count = clamp(Math.round(rand(3, colors.length)), 3, colors.length);\n const chosen = colors.slice(0, count);\n c.palette = randomStops(chosen);\n // Bias toward linear, occasionally radial/conic/mesh for variety.\n c.gradientType = pick([\n \"linear\",\n \"linear\",\n \"linear\",\n \"radial\",\n \"conic\",\n \"mesh\",\n ] as GradientType[]);\n c.gradientAngle = Math.round(rand(0, 180));\n c.gradientShift = r2(rand(0, 0.4)); // 2D warp\n // Also refresh the mesh field + edge tint so the whole colour section changes regardless of the\n // active source (both are inert unless gradientType is \"mesh\" / the source is \"stops\").\n c.meshGradientPoints = randomMeshPoints(colors);\n c.meshGradientSoftness = r2(rand(0.45, 0.85));\n c.paletteEdgeColor = pick(chosen);\n c.paletteEdgeAmount = r2(rand(0, 0.5));\n // Colour engine: mostly the editable stops (as a 2D texture or procedurally), occasionally the\n // baked hero LUT. Clear any loaded custom image/video so the picked source actually shows.\n c.paletteImageUrl = undefined;\n c.paletteVideoUrl = undefined;\n const engine = pick([\"stops-tex\", \"stops-tex\", \"procedural\", \"hero\"]);\n c.usePaletteTexture = engine !== \"procedural\";\n c.paletteSource = engine === \"hero\" ? \"hero\" : \"stops\";\n // Occasional slow colour flow along the ribbon (inert on mesh / procedural palettes).\n c.paletteDriftX = rand(0, 1) < 0.25 ? r2(rand(-0.25, 0.25)) : 0;\n c.paletteDriftY = 0;\n}\n\n/** \"Background\" folder: a fresh random gradient (linear/radial/conic) or mesh backdrop. Left out\n * of \"Randomize All\", which deliberately preserves the background. */\nexport function randomizeBackground(c: StudioConfig): void {\n const colors = pick(RANDOM_PALETTES);\n c.transparentBackground = false;\n c.backgroundMode = \"gradient\";\n c.backgroundGradientType = pick([\"linear\", \"radial\", \"conic\", \"mesh\"] as GradientType[]);\n c.backgroundGradientAngle = Math.round(rand(0, 360));\n c.background = colors[0]; // matte fallback (shown only if a gradient ever fails to cover)\n if (c.backgroundGradientType === \"mesh\") {\n c.backgroundMeshPoints = randomMeshPoints(colors);\n c.backgroundMeshSoftness = r2(rand(0.45, 0.85));\n } else {\n c.backgroundGradientSource = \"stops\";\n c.backgroundPalette = randomStops(colors);\n }\n}\n\n/** \"Color\" folder: hue / contrast / saturation grading. */\nexport function randomizeColor(c: WaveConfig): void {\n c.hueShift = Math.round(rand(0, 360));\n c.colorContrast = r2(rand(0.9, 1.3));\n c.colorSaturation = r2(rand(0.8, 1.35));\n}\n\nexport function randomizeSpine(c: WaveConfig): void {\n // Wider frequency spread than before so a re-roll is actually visible (the old range barely\n // moved). Amount takes either sign so the ribbon folds either way.\n c.displaceFrequency = { x: r3(rand(0.002, 0.016)), y: r3(rand(0.004, 0.02)) };\n c.displaceAmount = r2(rand(3, 10)) * pick([1, -1]);\n // Occasionally layer a finer second octave for a richer, two-scale shape.\n if (rand(0, 1) < 0.35) {\n c.detailFrequency = r3(rand(0.03, 0.08));\n c.detailAmount = r2(rand(0.8, 2.5)) * pick([1, -1]);\n } else {\n c.detailAmount = 0;\n }\n}\n\nexport function randomizeTransform(c: WaveConfig): void {\n c.rotation = { x: r2(rand(-20, 5)), y: r2(rand(-25, 10)), z: r2(rand(-170, -150)) };\n // Full scale (×10) — the mesh lives in the tens, not fractions.\n const s = r2(rand(6, 14));\n c.scale = { x: s, y: s, z: r2(s * rand(0.6, 0.8)) };\n // z is modest — the ortho camera barely shows depth, and a big z can clip the near/far planes.\n c.position = { x: r2(rand(-60, 60)), y: r2(rand(-60, 60)), z: r2(rand(-20, 20)) };\n}\n\nexport function randomizeTwist(c: WaveConfig): void {\n c.twistFrequency = { x: r3(rand(-0.5, 0.5)), y: r3(rand(-0.3, 0.5)), z: r3(rand(-1.6, -0.6)) };\n c.twistPower = { x: r2(rand(2, 6)), y: r2(rand(2, 6)), z: r2(rand(2, 7)) };\n c.twistMotion = rand(0, 1) < 0.25; // occasionally enable the breathing wobble\n}\n\n/** A wave's surface finish: fibers/texture + roundness/crease/edge. Grain & blur are scene post-fx\n * (see randomizeGlobal), so they're not touched here. */\nexport function randomizeFinish(c: WaveConfig): void {\n c.fiberCount = Math.round(rand(200, 900));\n c.fiberStrength = r2(rand(0.1, 0.35));\n c.texture = r2(rand(0, 0.35));\n c.roundness = r2(rand(0.3, 0.8)); // rounded-solid shading\n c.sheen = r2(rand(0.2, 0.9));\n c.iridescence = rand(0, 1) < 0.35 ? r2(rand(0.15, 0.6)) : 0; // occasional thin-film sheen\n c.creaseLight = r2(rand(0.4, 1.0)); // crease strength (where streaks appear)\n c.creaseSharpness = r2(rand(0.45, 0.8));\n c.creaseSoftness = r2(rand(0.8, 1.2));\n c.edgeFade = r2(rand(0, 0.08));\n c.edgeFeather = r2(rand(0.03, 0.22)); // crisp graphic ↔ soft vapor edges\n c.depthTint = rand(0, 1) < 0.3 ? r2(rand(0.2, 0.6)) : 0; // occasional atmospheric depth fade\n // Wireframe line params (inert unless theme is \"wireframe\") — randomized too so a wireframe\n // wave's Finish 🎲 refreshes its whole look, not just the solid-shader knobs.\n c.lineAmount = Math.round(rand(200, 900));\n c.lineThickness = r2(rand(0.5, 2));\n c.lineDerivativePower = r2(rand(0.4, 1.2));\n c.maxWidth = Math.round(rand(400, 1600));\n c.theme = rand(0, 1) < 0.2 ? \"wireframe\" : \"solid\"; // occasionally flip the material\n}\n\nexport function randomizeLights(c: StudioConfig): void {\n c.ambient = r2(rand(0.25, 0.6));\n for (const l of c.lights) {\n l.position = { x: r2(rand(-1000, 1000)), y: r2(rand(-500, 1000)), z: r2(rand(-500, 1200)) };\n l.intensity = r2(rand(0.5, 1.6));\n l.color = pick(LIGHT_TINTS);\n }\n}\n\n/** Scene knobs: the post-fx (grain/blur) + camera framing (zoom). */\nexport function randomizeGlobal(c: StudioConfig): void {\n c.grain = r2(rand(0, 1.5));\n c.blur = r3(rand(0, 0.02));\n // Ortho: vary the zoom (framing), keep the camera pose/target — distance doesn't size it.\n c.cameraZoom = r2(rand(1.2, 2.6));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"randomize.js","names":[],"sources":["../../src/studio/randomize.ts"],"sourcesContent":["/**\n * The \"Randomize\" helpers: `randomizeConfig` (\"Tasteful Randomize\") plus the per-section randomizers\n * the studio wires to each folder's 🎲. Studio-facing (not part of the renderer core), exposed via\n * the `@wave3d/core/studio` entry.\n */\nimport { clamp, roundTo } from \"../util/math\";\nimport type {\n StudioConfig,\n WaveConfig,\n MeshGradientPoint,\n ColorStop,\n GradientType,\n} from \"../config/model\";\n\nconst RANDOM_PALETTES: string[][] = [\n [\"#9bb0ff\", \"#ffb14e\", \"#ff6aa8\", \"#b15cff\", \"#6f7bff\"],\n [\"#ffd166\", \"#ff6b6b\", \"#c44dff\", \"#4d79ff\", \"#22d3ee\"],\n [\"#f97316\", \"#ec4899\", \"#8b5cf6\", \"#3b82f6\", \"#06b6d4\"],\n [\"#00f5d4\", \"#00bbf9\", \"#9b5de5\", \"#f15bb5\", \"#fee440\"],\n [\"#7c3aed\", \"#db2777\", \"#f59e0b\", \"#10b981\", \"#06b6d4\"],\n];\n\nfunction rand(min: number, max: number): number {\n return min + Math.random() * (max - min);\n}\n\nfunction pick<T>(arr: T[]): T {\n return arr[Math.floor(Math.random() * arr.length)];\n}\n\n/** \"Tasteful Randomize\": keep the scene (background, camera, lights, quality) and randomize the\n * post-fx plus every wave independently — so a multi-wave stack becomes visibly varied.\n * The camera is deliberately left alone so the result always lands in view. */\nexport function randomizeConfig(base: StudioConfig): StudioConfig {\n const cfg = structuredClone(base);\n cfg.grain = r2(rand(0, 1.5));\n cfg.blur = r3(rand(0, 0.02));\n for (const s of cfg.waves) randomizeWave(s);\n return cfg;\n}\n\n/** Randomize a whole wave: colour, shape (displacement/twist), transform, finish + the\n * compositing knobs (speed/opacity/seed/blend). Used by each wave's 🎲 and by \"Tasteful Randomize\". */\nexport function randomizeWave(s: WaveConfig): void {\n randomizeGradient(s);\n randomizeColor(s);\n randomizeSpine(s);\n randomizeTransform(s);\n randomizeTwist(s);\n randomizeFinish(s);\n s.speed = r2(rand(0.02, 0.15));\n s.opacity = r2(rand(0.6, 1));\n s.seed = r2(rand(0, 12));\n // Bias to the vivid squared blend; occasionally normal/additive (skip multiply — it muddies\n // the wave on light/transparent backgrounds).\n s.blendMode = pick([\"squared\", \"squared\", \"normal\", \"additive\"]);\n}\n\n// ---- Per-section randomizers (each mutates only its own fields, in place) ----\n\nfunction r2(x: number): number {\n return roundTo(x, 2);\n}\nfunction r3(x: number): number {\n return roundTo(x, 3);\n}\n\nconst LIGHT_TINTS = [\"#ffffff\", \"#ffffff\", \"#fff0e0\", \"#e2e8ff\", \"#ffe2f0\", \"#e2fff4\", \"#fff6cc\"];\n\n/** Random sorted positions in [0,1] with the ends pinned to 0 and 1. */\nfunction randomSortedPositions(n: number): number[] {\n if (n <= 1) return [0];\n if (n === 2) return [0, 1];\n const inner = Array.from({ length: n - 2 }, () => r2(rand(0.06, 0.94))).sort((a, b) => a - b);\n return [0, ...inner, 1];\n}\n\nfunction randomMeshPoints(colors: string[]): MeshGradientPoint[] {\n return colors.map((color) => ({\n color,\n x: r2(rand(0.08, 0.92)),\n y: r2(rand(0.08, 0.92)),\n influence: r2(rand(0.5, 0.95)),\n }));\n}\n\nfunction randomStops(colors: string[]): ColorStop[] {\n const positions = randomSortedPositions(colors.length);\n return colors.map((color, i) => ({ color, pos: positions[i] }));\n}\n\nexport function randomizeGradient(c: WaveConfig): void {\n const colors = pick(RANDOM_PALETTES);\n const count = clamp(Math.round(rand(3, colors.length)), 3, colors.length);\n const chosen = colors.slice(0, count);\n c.palette = randomStops(chosen);\n // Bias toward linear, occasionally radial/conic/mesh for variety.\n c.gradientType = pick([\n \"linear\",\n \"linear\",\n \"linear\",\n \"radial\",\n \"conic\",\n \"mesh\",\n ] as GradientType[]);\n c.gradientAngle = Math.round(rand(0, 180));\n c.gradientShift = r2(rand(0, 0.4)); // 2D warp\n // Also refresh the mesh field + edge tint so the whole colour section changes regardless of the\n // active source (both are inert unless gradientType is \"mesh\" / the source is \"stops\").\n c.meshGradientPoints = randomMeshPoints(colors);\n c.meshGradientSoftness = r2(rand(0.45, 0.85));\n c.paletteEdgeColor = pick(chosen);\n c.paletteEdgeAmount = r2(rand(0, 0.5));\n // Colour engine: mostly the editable stops (as a 2D texture or procedurally), occasionally the\n // baked hero LUT. Clear any loaded custom image/video so the picked source actually shows.\n c.paletteImageUrl = undefined;\n c.paletteVideoUrl = undefined;\n const engine = pick([\"stops-tex\", \"stops-tex\", \"procedural\", \"hero\"]);\n c.usePaletteTexture = engine !== \"procedural\";\n c.paletteSource = engine === \"hero\" ? \"hero\" : \"stops\";\n // Occasional slow colour flow along the ribbon (inert on mesh / procedural palettes).\n c.paletteDriftX = rand(0, 1) < 0.25 ? r2(rand(-0.25, 0.25)) : 0;\n c.paletteDriftY = 0;\n}\n\n/** \"Background\" folder: a fresh random gradient (linear/radial/conic) or mesh backdrop. Left out\n * of \"Tasteful Randomize\", which deliberately preserves the background. */\nexport function randomizeBackground(c: StudioConfig): void {\n const colors = pick(RANDOM_PALETTES);\n c.transparentBackground = false;\n c.backgroundMode = \"gradient\";\n c.backgroundGradientType = pick([\"linear\", \"radial\", \"conic\", \"mesh\"] as GradientType[]);\n c.backgroundGradientAngle = Math.round(rand(0, 360));\n c.background = colors[0]; // matte fallback (shown only if a gradient ever fails to cover)\n if (c.backgroundGradientType === \"mesh\") {\n c.backgroundMeshPoints = randomMeshPoints(colors);\n c.backgroundMeshSoftness = r2(rand(0.45, 0.85));\n } else {\n c.backgroundGradientSource = \"stops\";\n c.backgroundPalette = randomStops(colors);\n }\n}\n\n/** \"Color\" folder: hue / contrast / saturation grading. */\nexport function randomizeColor(c: WaveConfig): void {\n c.hueShift = Math.round(rand(0, 360));\n c.colorContrast = r2(rand(0.9, 1.3));\n c.colorSaturation = r2(rand(0.8, 1.35));\n}\n\nexport function randomizeSpine(c: WaveConfig): void {\n // Wider frequency spread than before so a re-roll is actually visible (the old range barely\n // moved). Amount takes either sign so the ribbon folds either way.\n c.displaceFrequency = { x: r3(rand(0.002, 0.016)), y: r3(rand(0.004, 0.02)) };\n c.displaceAmount = r2(rand(3, 10)) * pick([1, -1]);\n // Occasionally layer a finer second octave for a richer, two-scale shape.\n if (rand(0, 1) < 0.35) {\n c.detailFrequency = r3(rand(0.03, 0.08));\n c.detailAmount = r2(rand(0.8, 2.5)) * pick([1, -1]);\n } else {\n c.detailAmount = 0;\n }\n}\n\nexport function randomizeTransform(c: WaveConfig): void {\n c.rotation = { x: r2(rand(-20, 5)), y: r2(rand(-25, 10)), z: r2(rand(-170, -150)) };\n // Full scale (×10) — the mesh lives in the tens, not fractions.\n const s = r2(rand(6, 14));\n c.scale = { x: s, y: s, z: r2(s * rand(0.6, 0.8)) };\n // z is modest — the ortho camera barely shows depth, and a big z can clip the near/far planes.\n c.position = { x: r2(rand(-60, 60)), y: r2(rand(-60, 60)), z: r2(rand(-20, 20)) };\n}\n\nexport function randomizeTwist(c: WaveConfig): void {\n c.twistFrequency = { x: r3(rand(-0.5, 0.5)), y: r3(rand(-0.3, 0.5)), z: r3(rand(-1.6, -0.6)) };\n c.twistPower = { x: r2(rand(2, 6)), y: r2(rand(2, 6)), z: r2(rand(2, 7)) };\n c.twistMotion = rand(0, 1) < 0.25; // occasionally enable the breathing wobble\n}\n\n/** A wave's surface finish: fibers/texture + roundness/crease/edge. Grain & blur are scene post-fx\n * (see randomizeGlobal), so they're not touched here. */\nexport function randomizeFinish(c: WaveConfig): void {\n c.fiberCount = Math.round(rand(200, 900));\n c.fiberStrength = r2(rand(0.1, 0.35));\n c.texture = r2(rand(0, 0.35));\n c.roundness = r2(rand(0.3, 0.8)); // rounded-solid shading\n c.sheen = r2(rand(0.2, 0.9));\n c.iridescence = rand(0, 1) < 0.35 ? r2(rand(0.15, 0.6)) : 0; // occasional thin-film sheen\n c.creaseLight = r2(rand(0.4, 1.0)); // crease strength (where streaks appear)\n c.creaseSharpness = r2(rand(0.45, 0.8));\n c.creaseSoftness = r2(rand(0.8, 1.2));\n c.edgeFade = r2(rand(0, 0.08));\n c.edgeFeather = r2(rand(0.03, 0.22)); // crisp graphic ↔ soft vapor edges\n c.depthTint = rand(0, 1) < 0.3 ? r2(rand(0.2, 0.6)) : 0; // occasional atmospheric depth fade\n // Wireframe line params (inert unless theme is \"wireframe\") — randomized too so a wireframe\n // wave's Finish 🎲 refreshes its whole look, not just the solid-shader knobs.\n c.lineAmount = Math.round(rand(200, 900));\n c.lineThickness = r2(rand(0.5, 2));\n c.lineDerivativePower = r2(rand(0.4, 1.2));\n c.maxWidth = Math.round(rand(400, 1600));\n c.theme = rand(0, 1) < 0.2 ? \"wireframe\" : \"solid\"; // occasionally flip the material\n}\n\nexport function randomizeLights(c: StudioConfig): void {\n c.ambient = r2(rand(0.25, 0.6));\n for (const l of c.lights) {\n l.position = { x: r2(rand(-1000, 1000)), y: r2(rand(-500, 1000)), z: r2(rand(-500, 1200)) };\n l.intensity = r2(rand(0.5, 1.6));\n l.color = pick(LIGHT_TINTS);\n }\n}\n\n/** Scene knobs: the post-fx (grain/blur) + camera framing (zoom). */\nexport function randomizeGlobal(c: StudioConfig): void {\n c.grain = r2(rand(0, 1.5));\n c.blur = r3(rand(0, 0.02));\n // Ortho: vary the zoom (framing), keep the camera pose/target — distance doesn't size it.\n c.cameraZoom = r2(rand(1.2, 2.6));\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { StudioConfig } from "../config/model.js";
|
|
2
|
+
import { WaveRenderer } from "../renderer/WaveRenderer.js";
|
|
3
|
+
|
|
4
|
+
//#region src/studio/thumbnail.d.ts
|
|
5
|
+
/** A hidden host div that is in layout (so clientWidth/Height are real) but off-screen. */
|
|
6
|
+
declare function createThumbHost(width: number, height: number): HTMLDivElement;
|
|
7
|
+
/** Mutate `cfg` for a thumbnail still: static frame, opaque, white page behind solid themes
|
|
8
|
+
* (wireframe keys its between-line colour off the dark page background, so keep it). */
|
|
9
|
+
declare function prepThumbConfig(cfg: StudioConfig): void;
|
|
10
|
+
/** Render the current config to a fresh 2D canvas (null if the WebGL canvas is missing). */
|
|
11
|
+
declare function renderThumbFrame(renderer: WaveRenderer, host: HTMLElement): HTMLCanvasElement | null;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { createThumbHost, prepThumbConfig, renderThumbFrame };
|
|
14
|
+
//# sourceMappingURL=thumbnail.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region src/studio/thumbnail.ts
|
|
2
|
+
/** A hidden host div that is in layout (so clientWidth/Height are real) but off-screen. */
|
|
3
|
+
function createThumbHost(width, height) {
|
|
4
|
+
const host = document.createElement("div");
|
|
5
|
+
host.style.cssText = `position:fixed;left:-10000px;top:0;width:${width}px;height:${height}px;opacity:0;pointer-events:none;`;
|
|
6
|
+
document.body.appendChild(host);
|
|
7
|
+
return host;
|
|
8
|
+
}
|
|
9
|
+
/** Mutate `cfg` for a thumbnail still: static frame, opaque, white page behind solid themes
|
|
10
|
+
* (wireframe keys its between-line colour off the dark page background, so keep it). */
|
|
11
|
+
function prepThumbConfig(cfg) {
|
|
12
|
+
cfg.paused = true;
|
|
13
|
+
cfg.transparentBackground = false;
|
|
14
|
+
if (cfg.waves[0]?.theme !== "wireframe") cfg.background = "#ffffff";
|
|
15
|
+
}
|
|
16
|
+
/** Render the current config to a fresh 2D canvas (null if the WebGL canvas is missing). */
|
|
17
|
+
function renderThumbFrame(renderer, host) {
|
|
18
|
+
renderer.resize();
|
|
19
|
+
renderer.renderOnce();
|
|
20
|
+
renderer.renderOnce();
|
|
21
|
+
const gl = host.querySelector("canvas");
|
|
22
|
+
if (!gl) return null;
|
|
23
|
+
const out = document.createElement("canvas");
|
|
24
|
+
out.width = gl.width;
|
|
25
|
+
out.height = gl.height;
|
|
26
|
+
out.getContext("2d")?.drawImage(gl, 0, 0);
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { createThumbHost, prepThumbConfig, renderThumbFrame };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=thumbnail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thumbnail.js","names":[],"sources":["../../src/studio/thumbnail.ts"],"sourcesContent":["/**\n * Offscreen thumbnail rendering: turn a config into a still frame with one hidden, reused\n * WaveRenderer. Used by the studio's preset + history thumbnails and by the wave gallery grid.\n */\nimport type { WaveRenderer } from \"../renderer/WaveRenderer\";\nimport type { StudioConfig } from \"../config/model\";\n\n/** A hidden host div that is in layout (so clientWidth/Height are real) but off-screen. */\nexport function createThumbHost(width: number, height: number): HTMLDivElement {\n const host = document.createElement(\"div\");\n host.style.cssText = `position:fixed;left:-10000px;top:0;width:${width}px;height:${height}px;opacity:0;pointer-events:none;`;\n document.body.appendChild(host);\n return host;\n}\n\n/** Mutate `cfg` for a thumbnail still: static frame, opaque, white page behind solid themes\n * (wireframe keys its between-line colour off the dark page background, so keep it). */\nexport function prepThumbConfig(cfg: StudioConfig): void {\n cfg.paused = true;\n cfg.transparentBackground = false;\n if (cfg.waves[0]?.theme !== \"wireframe\") cfg.background = \"#ffffff\";\n}\n\n/** Render the current config to a fresh 2D canvas (null if the WebGL canvas is missing). */\nexport function renderThumbFrame(\n renderer: WaveRenderer,\n host: HTMLElement,\n): HTMLCanvasElement | null {\n renderer.resize();\n renderer.renderOnce();\n renderer.renderOnce(); // 2nd pass so any shader recompile (theme/blend variant) is applied\n const gl = host.querySelector(\"canvas\");\n if (!gl) return null;\n // Copy to a 2D canvas before encoding (reliable read of the WebGL drawing buffer).\n const out = document.createElement(\"canvas\");\n out.width = gl.width;\n out.height = gl.height;\n out.getContext(\"2d\")?.drawImage(gl, 0, 0);\n return out;\n}\n"],"mappings":";;AAQA,SAAgB,gBAAgB,OAAe,QAAgC;CAC7E,MAAM,OAAO,SAAS,cAAc,KAAK;CACzC,KAAK,MAAM,UAAU,4CAA4C,MAAM,YAAY,OAAO;CAC1F,SAAS,KAAK,YAAY,IAAI;CAC9B,OAAO;AACT;;;AAIA,SAAgB,gBAAgB,KAAyB;CACvD,IAAI,SAAS;CACb,IAAI,wBAAwB;CAC5B,IAAI,IAAI,MAAM,EAAE,EAAE,UAAU,aAAa,IAAI,aAAa;AAC5D;;AAGA,SAAgB,iBACd,UACA,MAC0B;CAC1B,SAAS,OAAO;CAChB,SAAS,WAAW;CACpB,SAAS,WAAW;CACpB,MAAM,KAAK,KAAK,cAAc,QAAQ;CACtC,IAAI,CAAC,IAAI,OAAO;CAEhB,MAAM,MAAM,SAAS,cAAc,QAAQ;CAC3C,IAAI,QAAQ,GAAG;CACf,IAAI,SAAS,GAAG;CAChB,IAAI,WAAW,IAAI,CAAC,EAAE,UAAU,IAAI,GAAG,CAAC;CACxC,OAAO;AACT"}
|
package/package.json
CHANGED
package/skills/wave3d/SKILL.md
CHANGED
|
@@ -109,11 +109,29 @@ Omitted fields fall back to `createDefaultConfig()`.
|
|
|
109
109
|
The studio's **Export code** button generates a ready-made snippet (a minimal config diff + a
|
|
110
110
|
poster) for every entry — the fastest way to hand a designed wave to a developer.
|
|
111
111
|
|
|
112
|
+
## Interactivity (optional)
|
|
113
|
+
|
|
114
|
+
Interactivity is **per wave** and **off by default / additive** — omit it and the wave renders
|
|
115
|
+
byte-for-byte as before. Each `WaveConfig.interaction` has three parts: **`hover`** (the cursor-follow
|
|
116
|
+
field — `agitate` churn, `push` ± repel/attract dome, `wake` drag-trough, `thin`, `hueShift`,
|
|
117
|
+
`lighten`), **`press`** (`ripple` rings from a click/tap), and **`bindings`** that smoothly drive
|
|
118
|
+
that wave's params from an input.
|
|
119
|
+
Sources: `scroll`, `hover`, `pointerX` / `pointerY`, `pointerSpeed`, `press`, `scrollVelocity`,
|
|
120
|
+
`appear`, or a developer-fed `custom:*` (via `handle.setInteractionInput(name, value)` /
|
|
121
|
+
`renderer.setInteractionInput`). Each binding rests at the authored value and moves toward `to` as
|
|
122
|
+
its input rises 0→1 — `{ source: "hover", target: "displaceAmount", to: 12 }` grows the folds on
|
|
123
|
+
hover. Each wave's `hover.smoothing` sets its own cursor-follow lag (vary it across a stack for a
|
|
124
|
+
parallax drag). **Shared inputs** (one cursor + scroll: `radius`, `touch`) and **scene-param
|
|
125
|
+
bindings** (`timeOffset`, `cameraZoom`, `blur`, `grain` — e.g. `scroll → timeOffset` scrubs the whole
|
|
126
|
+
wave with the page) live on `SceneConfig.interaction`. In React the flat `interaction` prop targets
|
|
127
|
+
the first wave; the studio authors it per wave (Hover / Click & touch / Bindings) plus a global
|
|
128
|
+
Interaction folder for the shared inputs and a scroll preview.
|
|
129
|
+
|
|
112
130
|
## Presets
|
|
113
131
|
|
|
114
|
-
|
|
132
|
+
14 built-in presets (`@wave3d/core/presets`): **Hero**, **Wave 2**, **Wave 3**, **Wave 4**,
|
|
115
133
|
**Wireframe**, **Neon Dark Multistrand**, **Mesh Gradient**, **Solar Bloom**, **Holographic**,
|
|
116
|
-
**Aurora**, **Palestine**, **Vaporwave Sunset**, **Kaleidoscope**.
|
|
134
|
+
**Aurora**, **Palestine**, **Spain**, **Vaporwave Sunset**, **Kaleidoscope**.
|
|
117
135
|
|
|
118
136
|
- React: `preset="Hero"` (a **string** lazy-imports the presets chunk) or
|
|
119
137
|
`preset={() => PRESETS["Hero"]()}` (a **function** is tree-shakeable — bundles only that preset).
|