@wave3d/core 0.9.0 → 0.11.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/dist/config/model.d.ts +209 -6
- package/dist/config/model.js +85 -3
- package/dist/config/model.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/presets.js +295 -0
- package/dist/presets.js.map +1 -1
- package/dist/renderer/WaveGeometry.js +21 -0
- package/dist/renderer/WaveGeometry.js.map +1 -1
- package/dist/renderer/WaveRenderer.d.ts +62 -0
- package/dist/renderer/WaveRenderer.js +349 -10
- package/dist/renderer/WaveRenderer.js.map +1 -1
- package/dist/renderer/WaveRendererGPU.js +32 -2
- package/dist/renderer/WaveRendererGPU.js.map +1 -1
- package/dist/renderer/interaction.js +12 -0
- package/dist/renderer/interaction.js.map +1 -1
- package/dist/renderer/particleField.js +26 -2
- package/dist/renderer/particleField.js.map +1 -1
- package/dist/renderer/particleFieldGPU.js +6 -0
- package/dist/renderer/particleFieldGPU.js.map +1 -1
- package/dist/renderer/shaders.js +748 -13
- package/dist/renderer/shaders.js.map +1 -1
- package/dist/renderer/tsl/dissolve.js +56 -0
- package/dist/renderer/tsl/dissolve.js.map +1 -0
- package/dist/renderer/tsl/particleMaterial.js +46 -8
- package/dist/renderer/tsl/particleMaterial.js.map +1 -1
- package/dist/renderer/tsl/uniforms.js +40 -1
- package/dist/renderer/tsl/uniforms.js.map +1 -1
- package/dist/renderer/tsl/waveMaterial.js +275 -26
- package/dist/renderer/tsl/waveMaterial.js.map +1 -1
- package/dist/renderer/tsl/waveShape.js +31 -10
- package/dist/renderer/tsl/waveShape.js.map +1 -1
- package/dist/renderer/wavePath.js +189 -0
- package/dist/renderer/wavePath.js.map +1 -0
- package/dist/shell/createWave.d.ts +23 -3
- package/dist/shell/createWave.js +5 -4
- package/dist/shell/createWave.js.map +1 -1
- package/dist/shell/probe.d.ts +28 -0
- package/dist/shell/probe.js +58 -11
- package/dist/shell/probe.js.map +1 -1
- package/dist/standalone/wave3d.standalone.js +3401 -2108
- package/dist/standalone/wave3d.standalone.webgpu.js +7372 -5848
- package/dist/standalone.d.ts +2 -2
- package/dist/standalone.js +2 -2
- package/dist/studio/StudioWaveRenderer.d.ts +115 -8
- package/dist/studio/StudioWaveRenderer.js +588 -14
- package/dist/studio/StudioWaveRenderer.js.map +1 -1
- package/dist/studio/index.d.ts +2 -2
- package/dist/studio/index.js.map +1 -1
- package/dist/studio/randomize.js +0 -1
- package/dist/studio/randomize.js.map +1 -1
- package/package.json +1 -1
- package/skills/wave3d/SKILL.md +142 -5
|
@@ -1 +1 @@
|
|
|
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/** \"Post FX\" folder: enable a single random effect (the rest off) with random shape params — one\n * post effect reads cleaner than a stack, so this stays tasteful. */\nexport function randomizePostFx(c: StudioConfig): void {\n c.dither = 0;\n c.halftone = 0;\n c.halftoneCmyk = 0;\n c.heatmap = 0;\n c.paperTexture = 0;\n c.innerLight = 0;\n switch (pick([\"dither\", \"halftone\", \"cmyk\", \"heatmap\", \"paper\", \"light\"])) {\n case \"dither\":\n c.dither = r2(rand(0.6, 1));\n c.ditherScale = Math.round(rand(1, 5));\n c.ditherSteps = Math.round(rand(2, 6));\n break;\n case \"halftone\":\n c.halftone = r2(rand(0.7, 1));\n c.halftoneCell = r2(rand(4, 12));\n c.halftoneAngle = r2(rand(0, 1.2));\n break;\n case \"cmyk\":\n c.halftoneCmyk = r2(rand(0.7, 1));\n c.halftoneCmykCell = r2(rand(4, 10));\n break;\n case \"heatmap\":\n c.heatmap = r2(rand(0.6, 1));\n break;\n case \"paper\":\n c.paperTexture = r2(rand(0.4, 0.9));\n c.paperTextureScale = r2(rand(1, 4));\n break;\n case \"light\":\n c.innerLight = r2(rand(0.4, 0.9));\n c.innerLightDensity = r2(rand(0.3, 0.8));\n c.innerLightDecay = r3(rand(0.9, 0.98));\n c.innerLightX = r2(rand(0.2, 0.8));\n c.innerLightY = r2(rand(0, 0.4));\n break;\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/** \"Post FX\" folder: enable a single random effect (the rest off) with random shape params — one\n * post effect reads cleaner than a stack, so this stays tasteful. */\nexport function randomizePostFx(c: StudioConfig): void {\n c.dither = 0;\n c.halftone = 0;\n c.halftoneCmyk = 0;\n c.heatmap = 0;\n c.paperTexture = 0;\n c.innerLight = 0;\n switch (pick([\"dither\", \"halftone\", \"cmyk\", \"heatmap\", \"paper\", \"light\"])) {\n case \"dither\":\n c.dither = r2(rand(0.6, 1));\n c.ditherScale = Math.round(rand(1, 5));\n c.ditherSteps = Math.round(rand(2, 6));\n break;\n case \"halftone\":\n c.halftone = r2(rand(0.7, 1));\n c.halftoneCell = r2(rand(4, 12));\n c.halftoneAngle = r2(rand(0, 1.2));\n break;\n case \"cmyk\":\n c.halftoneCmyk = r2(rand(0.7, 1));\n c.halftoneCmykCell = r2(rand(4, 10));\n break;\n case \"heatmap\":\n c.heatmap = r2(rand(0.6, 1));\n break;\n case \"paper\":\n c.paperTexture = r2(rand(0.4, 0.9));\n c.paperTextureScale = r2(rand(1, 4));\n break;\n case \"light\":\n c.innerLight = r2(rand(0.4, 0.9));\n c.innerLightDensity = r2(rand(0.3, 0.8));\n c.innerLightDecay = r3(rand(0.9, 0.98));\n c.innerLightX = r2(rand(0.2, 0.8));\n c.innerLightY = r2(rand(0, 0.4));\n break;\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.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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"}
|
package/package.json
CHANGED
package/skills/wave3d/SKILL.md
CHANGED
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
metadata:
|
|
11
11
|
type: core
|
|
12
12
|
library: "@wave3d/core"
|
|
13
|
-
library_version: "0.
|
|
13
|
+
library_version: "0.11.0"
|
|
14
14
|
sources:
|
|
15
15
|
- "wave3d/wave3d:README.md"
|
|
16
16
|
- "wave3d/wave3d:packages/core/src/config/model.ts"
|
|
@@ -109,9 +109,134 @@ a repeating helix:
|
|
|
109
109
|
- `helixRoll` rolls the ribbon's own cross-section in step (1 = rigid twisted ribbon), swinging its
|
|
110
110
|
two long edges onto opposite sides of the axis, so **one wave becomes a ladder whose edges are
|
|
111
111
|
both strands**. Add `rungAmount` (wireframe theme) for the rungs between them.
|
|
112
|
+
- `radialCone` (on the radial fan, not the helix) lifts the fan out of its own plane as it spreads,
|
|
113
|
+
turning the flat plume into a TRUMPET whose combed strands run down the slant into the throat. A
|
|
114
|
+
helix carries the ribbon around an axis but its WIDTH never follows the slant, so this is the only
|
|
115
|
+
route to a cone mouth.
|
|
116
|
+
- `radialSwirl` (degrees) lets the fan's ANGLE advance along the band as well as across it. Radius
|
|
117
|
+
already grows with uv.y, so angle growing with it too is what curls a straight arm into a SPIRAL
|
|
118
|
+
one wrapping the throat. Narrow the `radialArc` and a few of these, posed a quarter-turn apart,
|
|
119
|
+
read as one vortex.
|
|
112
120
|
- Both are off at 0, and the helix code path isn't compiled unless `helixRadius` or `helixRoll` is
|
|
113
121
|
non-zero — a wave without one renders byte-identically to before.
|
|
114
122
|
|
|
123
|
+
**Which shape control to reach for.** They overlap less than they look:
|
|
124
|
+
|
|
125
|
+
| want | use | why not the other |
|
|
126
|
+
| --------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
127
|
+
| a coil, a spring, a double helix | `helix*` | it carries the ribbon around an axis WITHOUT banking it, and it is animatable — `helixPhase` / `helixTurns` / `helixRadius` are binding targets, so scroll can spin it |
|
|
128
|
+
| a fan, a plume, a trumpet | `radial*` | it remaps the ribbon's WIDTH to an angle; a path only moves the centreline, so it can never splay a sheet into a sector |
|
|
129
|
+
| anything else: an S-curve, a knot, a ring, a throat, a shape you draw | `path` | the others bend a centreline that stays put |
|
|
130
|
+
|
|
131
|
+
A path SWEEPS with a transported frame, so it banks into its curves like a
|
|
132
|
+
rollercoaster track; a helix TRANSLATES around an axis, so the ribbon keeps facing the same way. Same
|
|
133
|
+
coil, different object — which is why both stay.
|
|
134
|
+
|
|
135
|
+
**`path` — the ribbon's centreline, and the one shape control the others cannot substitute for.**
|
|
136
|
+
Everything else deforms a ribbon whose centreline is fixed: the twists rotate it, the helix carries it
|
|
137
|
+
around an axis, the radial fan splays it. So none of them can make a ribbon that changes direction
|
|
138
|
+
more than once, crosses itself, or is wide here and narrow there. A path can, because it IS the
|
|
139
|
+
centreline.
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
path: [
|
|
143
|
+
{ x: -200, y: 0, z: -8 },
|
|
144
|
+
{ x: 0, y: 120, z: 32, width: 0.3, twist: 45 }, // a throat, and a quarter turn of the section
|
|
145
|
+
{ x: 200, y: 0, z: -8 },
|
|
146
|
+
];
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
- Points are where the centreline goes, in the wave's LOCAL space. The un-pathed ribbon runs
|
|
150
|
+
x −200 → +200 along the plane z = −8 (the folded geometry's centre), so a straight path there —
|
|
151
|
+
`straightPath()` — reproduces any wave exactly, twists and all. Absent ⇒ that straight ribbon.
|
|
152
|
+
- `width` per point is what a separate "pinch" knob would be, and `twist` (degrees) rotates the
|
|
153
|
+
cross-section. Both interpolate smoothly between points.
|
|
154
|
+
- A path whose LAST point sits on its FIRST is a closed ring. That is the whole declaration — there
|
|
155
|
+
is no `closed` flag, because the points already say it.
|
|
156
|
+
- `arcPath(turns)` builds one: `arcPath(1)` is a closed ring whose radius comes from the ribbon's own
|
|
157
|
+
length, `arcPath(0.35)` an open curve.
|
|
158
|
+
- It is swept by ARC LENGTH with a parallel-transported frame, so the strand comb stays even wherever
|
|
159
|
+
the points are dragged and the ribbon never flips through an inflection.
|
|
160
|
+
|
|
161
|
+
**In the studio, double-click a ribbon to shape it.** The primary gesture is dragging the RIBBON
|
|
162
|
+
itself: it moves like putty, with the push falling off smoothly along the length so only the part you
|
|
163
|
+
grabbed follows (hold Shift for a fingertip-tight push instead of a palm). The path densifies itself
|
|
164
|
+
as you sculpt, so there is always something local to move. Control points are still there for
|
|
165
|
+
precision — drag a handle, double-click a point to remove it, double-click the ribbon to insert one —
|
|
166
|
+
and Escape leaves. The gestures are listed on a bar while the mode is on, and the cursor says what is
|
|
167
|
+
under it (grab on the ribbon, pointer on a handle). The wave takes a straight path on entry, so nothing moves until you move it.
|
|
168
|
+
|
|
169
|
+
**Disintegration (`WaveConfig.dissolve`).** A front sweeps across the wave and eats it away chunk by
|
|
170
|
+
chunk, so the surface CRUMBLES rather than fading. Absent ⇒ intact and byte-identical.
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
dissolve: { amount: 0.3, axis: "screenX", reverse: true, band: 0.55, scale: 150, blocky: 0.75, dust: 1 }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- `amount` is the whole animation: 0 whole, 1 gone, whatever the `band` (fray width). It is a binding
|
|
177
|
+
target, so `{ source: "scroll", target: "dissolveAmount", to: 1 }` snaps the wave as the page moves.
|
|
178
|
+
- `axis` `"length"` / `"width"` ride the ribbon's own uv; **`"screenX"` / `"screenY"` are a straight
|
|
179
|
+
line on the canvas**, which is what makes a multi-wave STACK crumble as one object — give every
|
|
180
|
+
wave the same axis and amount and they share one edge. `reverse` flips which end goes first.
|
|
181
|
+
- `scale` sets how finely the sheet is diced and `blocky` the chunk character (0 organic tatters,
|
|
182
|
+
1 hard quantized cells).
|
|
183
|
+
- `dust` pins the wave's own `particles` field to that same front: each mote is the chunk that just
|
|
184
|
+
left, so the dust and the holes are ONE event. Under a screen axis the debris also blows along the
|
|
185
|
+
sweep instead of radiating in every direction. Pair with `shape: "square"` and `blend: "normal"`
|
|
186
|
+
for hard dark debris — `blend` defaults to `"additive"`, which can only brighten and so is
|
|
187
|
+
invisible on a pale page.
|
|
188
|
+
|
|
189
|
+
**Which way the stripes run is the biggest single decision.** `lineAmount` carves at constant uv.x, so
|
|
190
|
+
each strand runs the ribbon's LENGTH; `rungAmount` carves at constant uv.y, so each crosses its WIDTH.
|
|
191
|
+
On a twisting ribbon the two look nothing alike: along-stripes just follow the flow, while
|
|
192
|
+
across-stripes ARE the twist made visible — they fan wide where the sheet faces the camera, pinch to a
|
|
193
|
+
dense line where it turns edge-on, and sweep as nested arcs through a fold. For a striped-ribbon look,
|
|
194
|
+
reach for rungs first and set `lineThickness: 0` to switch the lengthwise family off. `rungThickness`
|
|
195
|
+
is in PIXELS, so it must come DOWN as `rungAmount` goes up or the strands merge into solid; and past
|
|
196
|
+
~300-400 rungs the periods go sub-pixel in a small embed, where a pixel-width stripe stops being
|
|
197
|
+
stable (it falls back to the strands' analytic duty cycle, which is a flat tone, not detail).
|
|
198
|
+
|
|
199
|
+
**A wireframe strand is a MASK, not a material — unless you light it.** Its colour comes from its uv
|
|
200
|
+
alone, so it holds one tone wherever the surface turns, which is why a dense wireframe reads as
|
|
201
|
+
hatching however it is posed. `lineLight` (0..1) shades it with the same derivative normal and scene
|
|
202
|
+
`lights` the solid theme uses, AND gives each strand a round cross-section so a specular travels along
|
|
203
|
+
one strand and not its neighbour — which is what separates combed thread from print. One knob, not
|
|
204
|
+
three: shading a flat stripe barely reads, so the rounding is not optional to it. Default 0.
|
|
205
|
+
|
|
206
|
+
**`lineDerivativePower` decides whether a close-framed wave has any ink.** It scales strand thickness
|
|
207
|
+
by the screen-space uv derivative, which is right for a ribbon seen whole (strands thicken where the
|
|
208
|
+
surface turns away, which reads as light on a curve). But the bigger a sheet gets on screen the
|
|
209
|
+
SMALLER its uv derivative, so framed close the strands thin to pale grey exactly where you want them
|
|
210
|
+
black. Set it to 0 and `lineThickness` alone is the duty cycle, crisp at any zoom.
|
|
211
|
+
|
|
212
|
+
**Wireframe ink (`theme: "wireframe"`).** The strand is a soft ramp by default, so `lineThickness`
|
|
213
|
+
alone goes from pale hairlines to flat solid without passing through dense ink. `lineSharpness`
|
|
214
|
+
(0..1) steepens it, which turns `lineThickness` into a DUTY CYCLE and gives engraved black strands
|
|
215
|
+
with the background still showing between them; `lineDepthFade` (default 1) turns off the recede
|
|
216
|
+
into the background colour, which a deep or stacked composition needs.
|
|
217
|
+
|
|
218
|
+
**`lineGapColor` is the one to know about** — it decides what sits BETWEEN the strands, which is most
|
|
219
|
+
of what a wireframe looks like. Absent, the gaps take the page background, so the wave is a window
|
|
220
|
+
onto the page (dark strands, paper between) and, being opaque, the near fold HIDES the far one, which
|
|
221
|
+
is what makes a stack read solid. Give it a dark colour and the ribbon becomes its own body: bright
|
|
222
|
+
combed lines on a dark ground. Set it to `"transparent"` (or an 8-digit hex) and the gaps go clear, so
|
|
223
|
+
stacked folds show through each other — airier, at the cost of that occlusion. Note that `lineAmount` counts strands across the whole of uv.x, which the
|
|
224
|
+
radial fan maps to `radialArc`, so a narrow arc needs a proportionally SMALLER count or the strands
|
|
225
|
+
go sub-pixel and average to grey. `edgeFeather` now applies to the wireframe too — without it a
|
|
226
|
+
ribbon ends at a flat end-cap that reads as a straight cut across the strands.
|
|
227
|
+
|
|
228
|
+
**Glass (`theme: "glass"`) is a lens over whatever is behind it — so put something there.** The
|
|
229
|
+
sheet draws opaque and samples the frame behind it, bent along its own normal: over a flat page it is
|
|
230
|
+
a silvered ribbon with a bright rim and nothing more; a gradient, an image or another wave behind it
|
|
231
|
+
is what makes it read. The palette only sets the HUE of what gets through — `glassTint` (default
|
|
232
|
+
0.12) and `glassDensity` decide how much — and `blendMode`, `lights`, `fiber*` and noise bands are
|
|
233
|
+
not read at all. The knobs: `glassStrength` (bend at the silhouette, px), `glassChroma` (dispersion),
|
|
234
|
+
`glassRim` / `glassRimPower`, `glassSpec`, `glassIrid` + `glassFilmNm`, `glassFrost` (scatter; under
|
|
235
|
+
~0.1 it is sub-pixel and skipped), `glassCaustic`, `glassLayerGain` (a fold over itself thickens),
|
|
236
|
+
`glassFusion` (two close sheets bend along one merged silhouette), and the liquid trio `glassRipple`
|
|
237
|
+
/ `glassRippleScale` / `glassFlow`. `glassRipple`, `glassStrength` and `glassTint` are binding
|
|
238
|
+
targets.
|
|
239
|
+
|
|
115
240
|
**React flat props** are a shortcut mapped onto `waves[0]` and the scene:
|
|
116
241
|
`palette` (`string[]` | `ColorStop[]`), `fiberCount`, `fiberStrength`, `sheen`, `iridescence`,
|
|
117
242
|
`displaceAmount`, `speed`, `opacity`, `blendMode`, `theme` → the first wave; `background`,
|
|
@@ -200,9 +325,10 @@ Only `blur` and `grain` are bindable from an interaction input; the others are a
|
|
|
200
325
|
|
|
201
326
|
## Presets
|
|
202
327
|
|
|
203
|
-
|
|
204
|
-
**Wireframe**, **Neon Dark Multistrand**, **Mesh Gradient**, **Solar Bloom**, **
|
|
205
|
-
**Aurora**, **Palestine**, **Spain**, **Vaporwave Sunset**,
|
|
328
|
+
18 built-in presets (`@wave3d/core/presets`): **Hero**, **Wave 2**, **Wave 3**, **Wave 4**,
|
|
329
|
+
**Wireframe**, **Neon Dark Multistrand**, **Mesh Gradient**, **Solar Bloom**, **Latte Ring**,
|
|
330
|
+
**Particle Zoo**, **Holographic**, **Aurora**, **Palestine**, **Spain**, **Vaporwave Sunset**,
|
|
331
|
+
**Corkscrew**, **Disintegration**, **Kaleidoscope**.
|
|
206
332
|
|
|
207
333
|
- React: `preset="Hero"` (a **string** lazy-imports the presets chunk) or
|
|
208
334
|
`preset={() => PRESETS["Hero"]()}` (a **function** is tree-shakeable — bundles only that preset).
|
|
@@ -227,7 +353,18 @@ The shell shows a poster immediately, then crossfades to the live wave.
|
|
|
227
353
|
`@wave3d/vite/client` for React / `createWave`. Re-snapshots over HMR; `vite build` just uses the
|
|
228
354
|
committed file.
|
|
229
355
|
- **`onFallback(reason)`** fires when the shell keeps the poster instead of upgrading; reasons:
|
|
230
|
-
`"no-webgl" | "reduced-motion" | "save-data" | "context-lost" |
|
|
356
|
+
`"no-webgl" | "software-renderer" | "reduced-motion" | "save-data" | "context-lost" |
|
|
357
|
+
"load-error"`.
|
|
358
|
+
- **`webgl="auto"` upgrades only onto a GPU.** A software rasteriser (SwiftShader, llvmpipe) keeps
|
|
359
|
+
the poster and reports `"software-renderer"` — it can run the wave, at roughly 2 fps with seconds
|
|
360
|
+
of blocked main thread, which is worse for the page than the still it already has. Use
|
|
361
|
+
`webgl="force"` if you want the live render anyway. `probeWebGL()` and `isSoftwareRenderer(gl)`
|
|
362
|
+
are exported for pages that need the same answer for their own reasons; note that an unreadable
|
|
363
|
+
renderer string counts as HARDWARE, because the extension is hidden under some privacy settings
|
|
364
|
+
and guessing "software" there downgrades people silently.
|
|
365
|
+
- **`paused` does not decline the upgrade.** It stops FRAMES: the engine is still fetched, the
|
|
366
|
+
renderer still builds, ready still fires and the poster is still swapped for a static canvas.
|
|
367
|
+
`webgl="off"` is the one that keeps the poster and builds nothing.
|
|
231
368
|
- **`onStateChange(state)`**: `"poster" → "loading" → "running"`, or `"fallback"`.
|
|
232
369
|
|
|
233
370
|
## Performance
|