@wave3d/core 0.1.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/LICENSE +21 -0
- package/README.md +60 -0
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/config/model.d.ts +240 -0
- package/dist/config/model.js +496 -0
- package/dist/config/model.js.map +1 -0
- package/dist/core-loader.d.ts +10 -0
- package/dist/core-loader.js +12 -0
- package/dist/core-loader.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/presets.d.ts +8 -0
- package/dist/presets.js +544 -0
- package/dist/presets.js.map +1 -0
- package/dist/renderer/WaveGeometry.d.ts +31 -0
- package/dist/renderer/WaveGeometry.js +92 -0
- package/dist/renderer/WaveGeometry.js.map +1 -0
- package/dist/renderer/WaveRenderer.d.ts +232 -0
- package/dist/renderer/WaveRenderer.js +1118 -0
- package/dist/renderer/WaveRenderer.js.map +1 -0
- package/dist/renderer/heroPalette.d.ts +10 -0
- package/dist/renderer/heroPalette.js +34 -0
- package/dist/renderer/heroPalette.js.map +1 -0
- package/dist/renderer/index.d.ts +4 -0
- package/dist/renderer/index.js +4 -0
- package/dist/renderer/palette.d.ts +67 -0
- package/dist/renderer/palette.js +535 -0
- package/dist/renderer/palette.js.map +1 -0
- package/dist/renderer/shaders.js +545 -0
- package/dist/renderer/shaders.js.map +1 -0
- package/dist/shell/createWave.d.ts +58 -0
- package/dist/shell/createWave.js +161 -0
- package/dist/shell/createWave.js.map +1 -0
- package/dist/shell/poster.js +64 -0
- package/dist/shell/poster.js.map +1 -0
- package/dist/shell/probe.js +34 -0
- package/dist/shell/probe.js.map +1 -0
- package/dist/standalone/wave3d.standalone.js +13507 -0
- package/dist/standalone.d.ts +13 -0
- package/dist/standalone.js +17 -0
- package/dist/standalone.js.map +1 -0
- package/dist/studio/StudioWaveRenderer.d.ts +187 -0
- package/dist/studio/StudioWaveRenderer.js +905 -0
- package/dist/studio/StudioWaveRenderer.js.map +1 -0
- package/dist/studio/index.d.ts +3 -0
- package/dist/studio/index.js +3 -0
- package/dist/studio/randomize.d.ts +28 -0
- package/dist/studio/randomize.js +264 -0
- package/dist/studio/randomize.js.map +1 -0
- package/dist/util/base64.js +14 -0
- package/dist/util/base64.js.map +1 -0
- package/dist/util/math.js +18 -0
- package/dist/util/math.js.map +1 -0
- package/package.json +76 -0
- package/skills/wave3d/SKILL.md +158 -0
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
import { clamp, clamp01 } from "../util/math.js";
|
|
2
|
+
//#region src/config/model.ts
|
|
3
|
+
/**
|
|
4
|
+
* Configuration schema for the wave: a flat sheet displaced by noise (X/Z frequency
|
|
5
|
+
* + amount) then twisted by three axis-rotations (twistFrequency + twistPower per
|
|
6
|
+
* axis), then scaled / rotated / positioned. Plain JSON — doubles as the save-state
|
|
7
|
+
* format.
|
|
8
|
+
*/
|
|
9
|
+
const MAX_COLORS = 8;
|
|
10
|
+
const MAX_MESH_POINTS = 8;
|
|
11
|
+
const MAX_LIGHTS = 8;
|
|
12
|
+
const MAX_NOISE_BANDS = 4;
|
|
13
|
+
/** Cap on stacked waves (keeps total geometry bounded — see WaveRenderer segment scaling). */
|
|
14
|
+
const MAX_WAVES = 6;
|
|
15
|
+
/** A default light; pass overrides for added fill lights. */
|
|
16
|
+
function createLight(position = {
|
|
17
|
+
x: 300,
|
|
18
|
+
y: 500,
|
|
19
|
+
z: 800
|
|
20
|
+
}, intensity = 1) {
|
|
21
|
+
return {
|
|
22
|
+
position: { ...position },
|
|
23
|
+
color: "#ffffff",
|
|
24
|
+
intensity
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Where the first light lands when you engage lights from an empty scene. Shared by the
|
|
28
|
+
* "drag in 3D" control and the camera-rig minimap, which previews a light marker here so the
|
|
29
|
+
* rig always shows the light — even before one has been explicitly added. */
|
|
30
|
+
const DEFAULT_LIGHT_POSITION = {
|
|
31
|
+
x: 800,
|
|
32
|
+
y: 900,
|
|
33
|
+
z: 1100
|
|
34
|
+
};
|
|
35
|
+
/** A default band: a strong, coarse streak region over the first half. */
|
|
36
|
+
function createNoiseBand() {
|
|
37
|
+
return {
|
|
38
|
+
startX: 0,
|
|
39
|
+
endX: .5,
|
|
40
|
+
startY: 0,
|
|
41
|
+
endY: 1,
|
|
42
|
+
feather: .3,
|
|
43
|
+
strength: 1,
|
|
44
|
+
frequency: 220,
|
|
45
|
+
colorAttenuation: 0,
|
|
46
|
+
parabolaPower: 2
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Build evenly-spaced stops from a plain list of colours. */
|
|
50
|
+
function makeStops(colors) {
|
|
51
|
+
const n = colors.length;
|
|
52
|
+
return colors.map((color, i) => ({
|
|
53
|
+
color,
|
|
54
|
+
pos: n > 1 ? i / (n - 1) : 0
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
/** A balanced iOS-style field shown the first time Mesh is selected. */
|
|
58
|
+
function createDefaultMeshPoints() {
|
|
59
|
+
return [
|
|
60
|
+
{
|
|
61
|
+
color: "#5e5ce6",
|
|
62
|
+
x: .08,
|
|
63
|
+
y: .12,
|
|
64
|
+
influence: .78
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
color: "#64d2ff",
|
|
68
|
+
x: .88,
|
|
69
|
+
y: .08,
|
|
70
|
+
influence: .72
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
color: "#ff375f",
|
|
74
|
+
x: .12,
|
|
75
|
+
y: .88,
|
|
76
|
+
influence: .72
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
color: "#ff9f0a",
|
|
80
|
+
x: .9,
|
|
81
|
+
y: .86,
|
|
82
|
+
influence: .78
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
color: "#bf5af2",
|
|
86
|
+
x: .5,
|
|
87
|
+
y: .48,
|
|
88
|
+
influence: .58
|
|
89
|
+
}
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
/** Spread a base wave into `count` overlapping waves — each with a slightly varied hue, width,
|
|
93
|
+
* speed, phase, vertical offset and roll so a stack reads as one composition. `count === 1`
|
|
94
|
+
* returns the base unchanged. Used to author multi-wave presets. */
|
|
95
|
+
function makeWaveSpread(base, count) {
|
|
96
|
+
if (count <= 1) return [structuredClone(base)];
|
|
97
|
+
const out = [];
|
|
98
|
+
for (let i = 0; i < count; i++) {
|
|
99
|
+
const f = i / (count - 1);
|
|
100
|
+
const w = structuredClone(base);
|
|
101
|
+
w.opacity = 1 - f * .3;
|
|
102
|
+
w.hueShift = base.hueShift + i * 18;
|
|
103
|
+
w.scale = {
|
|
104
|
+
x: base.scale.x,
|
|
105
|
+
y: base.scale.y * (1 - f * .2),
|
|
106
|
+
z: base.scale.z
|
|
107
|
+
};
|
|
108
|
+
w.speed = base.speed * (1 + f * .15);
|
|
109
|
+
w.seed = i * 3.3;
|
|
110
|
+
w.position = {
|
|
111
|
+
x: base.position.x,
|
|
112
|
+
y: base.position.y + (f - .5) * 1.5,
|
|
113
|
+
z: base.position.z - i * .8
|
|
114
|
+
};
|
|
115
|
+
w.rotation = {
|
|
116
|
+
x: base.rotation.x,
|
|
117
|
+
y: base.rotation.y,
|
|
118
|
+
z: base.rotation.z + i * 20
|
|
119
|
+
};
|
|
120
|
+
out.push(w);
|
|
121
|
+
}
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
/** The hero wave (a single complete wave) — the base for the default config and most presets. */
|
|
125
|
+
function defaultWave() {
|
|
126
|
+
return {
|
|
127
|
+
palette: [
|
|
128
|
+
{
|
|
129
|
+
color: "#8e9dff",
|
|
130
|
+
pos: 0
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
color: "#c98fd0",
|
|
134
|
+
pos: .14
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
color: "#ff9326",
|
|
138
|
+
pos: .3
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
color: "#fd8108",
|
|
142
|
+
pos: .52
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
color: "#fb7a36",
|
|
146
|
+
pos: .64
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
color: "#d24ecc",
|
|
150
|
+
pos: .78
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
color: "#e95cae",
|
|
154
|
+
pos: .9
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
color: "#9b6ae0",
|
|
158
|
+
pos: 1
|
|
159
|
+
}
|
|
160
|
+
],
|
|
161
|
+
gradientType: "linear",
|
|
162
|
+
gradientAngle: 90,
|
|
163
|
+
gradientShift: .15,
|
|
164
|
+
meshGradientPoints: createDefaultMeshPoints(),
|
|
165
|
+
meshGradientSoftness: .62,
|
|
166
|
+
usePaletteTexture: true,
|
|
167
|
+
paletteSource: "hero",
|
|
168
|
+
paletteTextureScale: {
|
|
169
|
+
x: 1,
|
|
170
|
+
y: 1
|
|
171
|
+
},
|
|
172
|
+
paletteTextureOffset: {
|
|
173
|
+
x: 0,
|
|
174
|
+
y: 0
|
|
175
|
+
},
|
|
176
|
+
paletteTextureRotation: 0,
|
|
177
|
+
paletteDriftX: 0,
|
|
178
|
+
paletteDriftY: 0,
|
|
179
|
+
paletteEdgeColor: "#8e9dff",
|
|
180
|
+
paletteEdgeAmount: .3,
|
|
181
|
+
hueShift: -1.81,
|
|
182
|
+
colorContrast: 1,
|
|
183
|
+
colorSaturation: 1.15,
|
|
184
|
+
fiberCount: 600,
|
|
185
|
+
fiberStrength: .2,
|
|
186
|
+
noiseBands: [],
|
|
187
|
+
texture: 0,
|
|
188
|
+
creaseLight: .6,
|
|
189
|
+
creaseSharpness: .589,
|
|
190
|
+
creaseSoftness: 1,
|
|
191
|
+
sheen: 0,
|
|
192
|
+
roundness: 0,
|
|
193
|
+
iridescence: 0,
|
|
194
|
+
edgeFade: .04,
|
|
195
|
+
edgeFeather: .1,
|
|
196
|
+
depthTint: 0,
|
|
197
|
+
depthTintColor: "#0a2540",
|
|
198
|
+
displaceFrequency: {
|
|
199
|
+
x: .003234,
|
|
200
|
+
y: .00799
|
|
201
|
+
},
|
|
202
|
+
displaceAmount: 6.051,
|
|
203
|
+
detailFrequency: .04,
|
|
204
|
+
detailAmount: 0,
|
|
205
|
+
twistFrequency: {
|
|
206
|
+
x: -.055,
|
|
207
|
+
y: .077,
|
|
208
|
+
z: -.518
|
|
209
|
+
},
|
|
210
|
+
twistPower: {
|
|
211
|
+
x: 3.95,
|
|
212
|
+
y: 5.85,
|
|
213
|
+
z: 6.33
|
|
214
|
+
},
|
|
215
|
+
twistMotion: false,
|
|
216
|
+
theme: "solid",
|
|
217
|
+
lineAmount: 425,
|
|
218
|
+
lineThickness: 1,
|
|
219
|
+
lineDerivativePower: .95,
|
|
220
|
+
maxWidth: 1232,
|
|
221
|
+
position: {
|
|
222
|
+
x: -24.3,
|
|
223
|
+
y: -56.4,
|
|
224
|
+
z: -11.1
|
|
225
|
+
},
|
|
226
|
+
rotation: {
|
|
227
|
+
x: -9.14,
|
|
228
|
+
y: -16.25,
|
|
229
|
+
z: -161.32
|
|
230
|
+
},
|
|
231
|
+
scale: {
|
|
232
|
+
x: 10,
|
|
233
|
+
y: 10,
|
|
234
|
+
z: 7
|
|
235
|
+
},
|
|
236
|
+
blendMode: "squared",
|
|
237
|
+
speed: .04,
|
|
238
|
+
opacity: 1,
|
|
239
|
+
seed: 0
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
/** A fresh default wave (the hero wave as one complete wave). */
|
|
243
|
+
function makeWave() {
|
|
244
|
+
return defaultWave();
|
|
245
|
+
}
|
|
246
|
+
/** Resize `waves` to match `waveCount`. New waves CLONE the last one (inherit every
|
|
247
|
+
* property of the preceding wave); extras are dropped. */
|
|
248
|
+
function resizeWaves(config) {
|
|
249
|
+
const target = Math.max(1, Math.round(config.waveCount) || 1);
|
|
250
|
+
if (!Array.isArray(config.waves) || config.waves.length === 0) config.waves = [makeWave()];
|
|
251
|
+
while (config.waves.length < target) config.waves.push(structuredClone(config.waves[config.waves.length - 1]));
|
|
252
|
+
while (config.waves.length > target) config.waves.pop();
|
|
253
|
+
config.waveCount = config.waves.length;
|
|
254
|
+
}
|
|
255
|
+
/** The default studio config: the hero wave + its scene, in the canonical wave model. */
|
|
256
|
+
function createDefaultConfig() {
|
|
257
|
+
return {
|
|
258
|
+
background: "#ffffff",
|
|
259
|
+
transparentBackground: true,
|
|
260
|
+
backgroundMode: "color",
|
|
261
|
+
backgroundPalette: makeStops([
|
|
262
|
+
"#0a2540",
|
|
263
|
+
"#425466",
|
|
264
|
+
"#7a73ff",
|
|
265
|
+
"#f6f9fc"
|
|
266
|
+
]),
|
|
267
|
+
backgroundGradientType: "linear",
|
|
268
|
+
backgroundGradientAngle: 135,
|
|
269
|
+
backgroundGradientSource: "stops",
|
|
270
|
+
backgroundMeshPoints: createDefaultMeshPoints(),
|
|
271
|
+
backgroundMeshSoftness: .62,
|
|
272
|
+
backgroundImageSource: "vaporwave",
|
|
273
|
+
backgroundImageFit: "cover",
|
|
274
|
+
backgroundImageZoom: 1,
|
|
275
|
+
backgroundImagePosition: {
|
|
276
|
+
x: 0,
|
|
277
|
+
y: 0
|
|
278
|
+
},
|
|
279
|
+
waveCount: 1,
|
|
280
|
+
quality: 1,
|
|
281
|
+
dprMax: 2,
|
|
282
|
+
paused: false,
|
|
283
|
+
timeOffset: 0,
|
|
284
|
+
introRamp: true,
|
|
285
|
+
showCameraRig: false,
|
|
286
|
+
cameraDistance: 5001,
|
|
287
|
+
cameraPosition: {
|
|
288
|
+
x: 100,
|
|
289
|
+
y: 0,
|
|
290
|
+
z: 5e3
|
|
291
|
+
},
|
|
292
|
+
cameraTarget: {
|
|
293
|
+
x: -44,
|
|
294
|
+
y: -250,
|
|
295
|
+
z: 0
|
|
296
|
+
},
|
|
297
|
+
cameraZoom: 1,
|
|
298
|
+
grain: 1.1,
|
|
299
|
+
blur: .02,
|
|
300
|
+
blurSamples: 6,
|
|
301
|
+
ambient: .45,
|
|
302
|
+
lights: [],
|
|
303
|
+
mirrorH: false,
|
|
304
|
+
mirrorV: false,
|
|
305
|
+
waves: [defaultWave()]
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
/** Clamp/backfill a single wave's colour + palette fields (legacy `string[]` palettes become
|
|
309
|
+
* ColorStop[]; mesh points + texture transform are clamped). */
|
|
310
|
+
function normalizeWaveColour(config) {
|
|
311
|
+
const p = config.palette;
|
|
312
|
+
if (p.length > 0 && typeof p[0] === "string") config.palette = makeStops(p);
|
|
313
|
+
if (config.gradientType !== "radial" && config.gradientType !== "conic" && config.gradientType !== "mesh" && config.gradientType !== "linear") config.gradientType = "linear";
|
|
314
|
+
const rawMeshPoints = config.meshGradientPoints;
|
|
315
|
+
if (!Array.isArray(rawMeshPoints) || rawMeshPoints.length < 2) config.meshGradientPoints = createDefaultMeshPoints();
|
|
316
|
+
else {
|
|
317
|
+
const defaults = createDefaultMeshPoints();
|
|
318
|
+
config.meshGradientPoints = rawMeshPoints.slice(0, 8).map((point, index) => {
|
|
319
|
+
const fallback = defaults[index] ?? defaults[defaults.length - 1];
|
|
320
|
+
const x = Number(point.x);
|
|
321
|
+
const y = Number(point.y);
|
|
322
|
+
const influence = Number(point.influence);
|
|
323
|
+
return {
|
|
324
|
+
color: typeof point.color === "string" ? point.color : fallback.color,
|
|
325
|
+
x: clamp01(Number.isFinite(x) ? x : fallback.x),
|
|
326
|
+
y: clamp01(Number.isFinite(y) ? y : fallback.y),
|
|
327
|
+
influence: clamp(Number.isFinite(influence) ? influence : fallback.influence, .15, 1.5)
|
|
328
|
+
};
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
if (!Number.isFinite(config.meshGradientSoftness)) config.meshGradientSoftness = .62;
|
|
332
|
+
config.meshGradientSoftness = clamp01(config.meshGradientSoftness);
|
|
333
|
+
if (!config.paletteTextureScale) config.paletteTextureScale = {
|
|
334
|
+
x: 1,
|
|
335
|
+
y: 1
|
|
336
|
+
};
|
|
337
|
+
if (!config.paletteTextureOffset) config.paletteTextureOffset = {
|
|
338
|
+
x: 0,
|
|
339
|
+
y: 0
|
|
340
|
+
};
|
|
341
|
+
config.paletteTextureScale.x = clamp(Number(config.paletteTextureScale.x) || 1, .1, 8);
|
|
342
|
+
config.paletteTextureScale.y = clamp(Number(config.paletteTextureScale.y) || 1, .1, 8);
|
|
343
|
+
config.paletteTextureOffset.x = clamp(Number(config.paletteTextureOffset.x) || 0, -4, 4);
|
|
344
|
+
config.paletteTextureOffset.y = clamp(Number(config.paletteTextureOffset.y) || 0, -4, 4);
|
|
345
|
+
config.paletteTextureRotation = clamp(Number(config.paletteTextureRotation) || 0, -180, 180);
|
|
346
|
+
}
|
|
347
|
+
/** Backfill background styling for states saved before gradient/image backgrounds existed. */
|
|
348
|
+
function normalizeBackground(config) {
|
|
349
|
+
if (config.backgroundMode !== "gradient" && config.backgroundMode !== "image" && config.backgroundMode !== "color") config.backgroundMode = "color";
|
|
350
|
+
const palette = config.backgroundPalette;
|
|
351
|
+
if (!palette || palette.length < 2) config.backgroundPalette = makeStops([
|
|
352
|
+
"#0a2540",
|
|
353
|
+
"#425466",
|
|
354
|
+
"#7a73ff",
|
|
355
|
+
"#f6f9fc"
|
|
356
|
+
]);
|
|
357
|
+
else if (typeof palette[0] === "string") config.backgroundPalette = makeStops(palette);
|
|
358
|
+
if (config.backgroundGradientType !== "radial" && config.backgroundGradientType !== "conic" && config.backgroundGradientType !== "mesh" && config.backgroundGradientType !== "linear") config.backgroundGradientType = "linear";
|
|
359
|
+
const bgMesh = config.backgroundMeshPoints;
|
|
360
|
+
if (!Array.isArray(bgMesh) || bgMesh.length < 2) config.backgroundMeshPoints = createDefaultMeshPoints();
|
|
361
|
+
if (!Number.isFinite(config.backgroundMeshSoftness)) config.backgroundMeshSoftness = .62;
|
|
362
|
+
config.backgroundMeshSoftness = clamp01(config.backgroundMeshSoftness);
|
|
363
|
+
if (typeof config.backgroundGradientAngle !== "number") config.backgroundGradientAngle = 135;
|
|
364
|
+
if (typeof config.backgroundGradientSource !== "string") config.backgroundGradientSource = "stops";
|
|
365
|
+
if (typeof config.backgroundImageSource !== "string") config.backgroundImageSource = "vaporwave";
|
|
366
|
+
if (config.backgroundImageFit !== "contain" && config.backgroundImageFit !== "stretch" && config.backgroundImageFit !== "cover") config.backgroundImageFit = "cover";
|
|
367
|
+
if (typeof config.backgroundImageZoom !== "number") config.backgroundImageZoom = 1;
|
|
368
|
+
config.backgroundImageZoom = clamp(config.backgroundImageZoom, .1, 8);
|
|
369
|
+
if (!config.backgroundImagePosition) config.backgroundImagePosition = {
|
|
370
|
+
x: 0,
|
|
371
|
+
y: 0
|
|
372
|
+
};
|
|
373
|
+
if (typeof config.backgroundImagePosition.x !== "number") config.backgroundImagePosition.x = 0;
|
|
374
|
+
if (typeof config.backgroundImagePosition.y !== "number") config.backgroundImagePosition.y = 0;
|
|
375
|
+
config.backgroundImagePosition.x = clamp(config.backgroundImagePosition.x, -100, 100);
|
|
376
|
+
config.backgroundImagePosition.y = clamp(config.backgroundImagePosition.y, -100, 100);
|
|
377
|
+
}
|
|
378
|
+
/** Backfill camera position/target for states saved before they existed. */
|
|
379
|
+
function ensureCamera(config) {
|
|
380
|
+
if (!config.cameraPosition) config.cameraPosition = {
|
|
381
|
+
x: 0,
|
|
382
|
+
y: 0,
|
|
383
|
+
z: config.cameraDistance ?? 62
|
|
384
|
+
};
|
|
385
|
+
if (!config.cameraTarget) config.cameraTarget = {
|
|
386
|
+
x: 0,
|
|
387
|
+
y: 0,
|
|
388
|
+
z: 0
|
|
389
|
+
};
|
|
390
|
+
if (typeof config.cameraZoom !== "number") config.cameraZoom = 1;
|
|
391
|
+
}
|
|
392
|
+
/** Backfill/repair a wave so the renderer can consume it (covers partial wave-model JSON). */
|
|
393
|
+
function normalizeWave(s) {
|
|
394
|
+
normalizeWaveColour(s);
|
|
395
|
+
if (typeof s.gradientAngle !== "number") s.gradientAngle = 90;
|
|
396
|
+
if (typeof s.gradientShift !== "number") s.gradientShift = .15;
|
|
397
|
+
if (typeof s.usePaletteTexture !== "boolean") s.usePaletteTexture = true;
|
|
398
|
+
if (typeof s.paletteSource !== "string") s.paletteSource = "hero";
|
|
399
|
+
if (typeof s.paletteEdgeColor !== "string") s.paletteEdgeColor = "#8e9dff";
|
|
400
|
+
if (typeof s.paletteEdgeAmount !== "number") s.paletteEdgeAmount = .3;
|
|
401
|
+
if (typeof s.paletteDriftX !== "number") s.paletteDriftX = 0;
|
|
402
|
+
if (typeof s.paletteDriftY !== "number") s.paletteDriftY = 0;
|
|
403
|
+
if (typeof s.hueShift !== "number") s.hueShift = 0;
|
|
404
|
+
if (typeof s.colorContrast !== "number") s.colorContrast = 1;
|
|
405
|
+
if (typeof s.colorSaturation !== "number") s.colorSaturation = 1;
|
|
406
|
+
if (typeof s.fiberCount !== "number") s.fiberCount = 600;
|
|
407
|
+
if (typeof s.fiberStrength !== "number") s.fiberStrength = .2;
|
|
408
|
+
if (!Array.isArray(s.noiseBands)) s.noiseBands = [];
|
|
409
|
+
if (typeof s.texture !== "number") s.texture = 0;
|
|
410
|
+
if (typeof s.creaseLight !== "number") s.creaseLight = .6;
|
|
411
|
+
if (typeof s.creaseSharpness !== "number") s.creaseSharpness = .589;
|
|
412
|
+
if (typeof s.creaseSoftness !== "number") s.creaseSoftness = 1;
|
|
413
|
+
if (typeof s.sheen !== "number") s.sheen = 0;
|
|
414
|
+
if (typeof s.roundness !== "number") s.roundness = 0;
|
|
415
|
+
if (typeof s.iridescence !== "number") s.iridescence = 0;
|
|
416
|
+
if (typeof s.edgeFade !== "number") s.edgeFade = .04;
|
|
417
|
+
if (typeof s.edgeFeather !== "number") s.edgeFeather = .1;
|
|
418
|
+
if (typeof s.depthTint !== "number") s.depthTint = 0;
|
|
419
|
+
if (typeof s.depthTintColor !== "string") s.depthTintColor = "#0a2540";
|
|
420
|
+
if (!s.displaceFrequency) s.displaceFrequency = {
|
|
421
|
+
x: .003234,
|
|
422
|
+
y: .00799
|
|
423
|
+
};
|
|
424
|
+
if (typeof s.displaceAmount !== "number") s.displaceAmount = 6.051;
|
|
425
|
+
if (typeof s.detailFrequency !== "number") s.detailFrequency = .04;
|
|
426
|
+
if (typeof s.detailAmount !== "number") s.detailAmount = 0;
|
|
427
|
+
if (!s.twistFrequency) s.twistFrequency = {
|
|
428
|
+
x: -.055,
|
|
429
|
+
y: .077,
|
|
430
|
+
z: -.518
|
|
431
|
+
};
|
|
432
|
+
if (!s.twistPower) s.twistPower = {
|
|
433
|
+
x: 3.95,
|
|
434
|
+
y: 5.85,
|
|
435
|
+
z: 6.33
|
|
436
|
+
};
|
|
437
|
+
if (typeof s.theme !== "string") s.theme = "solid";
|
|
438
|
+
if (typeof s.lineAmount !== "number") s.lineAmount = 425;
|
|
439
|
+
if (typeof s.lineThickness !== "number") s.lineThickness = 1;
|
|
440
|
+
if (typeof s.lineDerivativePower !== "number") s.lineDerivativePower = .95;
|
|
441
|
+
if (typeof s.maxWidth !== "number") s.maxWidth = 1232;
|
|
442
|
+
if (!s.position) s.position = {
|
|
443
|
+
x: 0,
|
|
444
|
+
y: 0,
|
|
445
|
+
z: 0
|
|
446
|
+
};
|
|
447
|
+
if (!s.rotation) s.rotation = {
|
|
448
|
+
x: 0,
|
|
449
|
+
y: 0,
|
|
450
|
+
z: 0
|
|
451
|
+
};
|
|
452
|
+
if (!s.scale) s.scale = {
|
|
453
|
+
x: 10,
|
|
454
|
+
y: 10,
|
|
455
|
+
z: 7
|
|
456
|
+
};
|
|
457
|
+
if (typeof s.blendMode !== "string") s.blendMode = "squared";
|
|
458
|
+
if (typeof s.speed !== "number") s.speed = .04;
|
|
459
|
+
if (typeof s.opacity !== "number") s.opacity = 1;
|
|
460
|
+
if (typeof s.seed !== "number") s.seed = 0;
|
|
461
|
+
}
|
|
462
|
+
/** Backfill scene-level defaults (background/camera/post/lights/quality/mirror). */
|
|
463
|
+
function ensureSceneDefaults(config) {
|
|
464
|
+
normalizeBackground(config);
|
|
465
|
+
ensureCamera(config);
|
|
466
|
+
if (typeof config.ambient !== "number") config.ambient = .45;
|
|
467
|
+
if (!Array.isArray(config.lights)) config.lights = [];
|
|
468
|
+
if (typeof config.quality !== "number") config.quality = 1;
|
|
469
|
+
if (typeof config.dprMax !== "number") config.dprMax = 2;
|
|
470
|
+
if (typeof config.grain !== "number") config.grain = 1.1;
|
|
471
|
+
if (typeof config.blur !== "number") config.blur = .02;
|
|
472
|
+
if (typeof config.blurSamples !== "number") config.blurSamples = 6;
|
|
473
|
+
if (typeof config.bloomStrength !== "number") config.bloomStrength = 0;
|
|
474
|
+
if (typeof config.bloomRadius !== "number") config.bloomRadius = .4;
|
|
475
|
+
if (typeof config.bloomThreshold !== "number") config.bloomThreshold = .85;
|
|
476
|
+
if (typeof config.showCameraRig !== "boolean") config.showCameraRig = false;
|
|
477
|
+
if (typeof config.paused !== "boolean") config.paused = false;
|
|
478
|
+
if (typeof config.loopSeconds !== "number") config.loopSeconds = 0;
|
|
479
|
+
if (typeof config.mirrorH !== "boolean") config.mirrorH = false;
|
|
480
|
+
if (typeof config.mirrorV !== "boolean") config.mirrorV = false;
|
|
481
|
+
}
|
|
482
|
+
/** Normalize an ingested config to the wave model: backfill the scene + every wave, and drop in
|
|
483
|
+
* a default wave if none are present. Idempotent, so it is safe on the renderer's own config as
|
|
484
|
+
* well as freshly loaded save-states / share links. */
|
|
485
|
+
function ensureStudioConfig(input) {
|
|
486
|
+
const config = input;
|
|
487
|
+
ensureSceneDefaults(config);
|
|
488
|
+
if (!Array.isArray(config.waves) || config.waves.length === 0) config.waves = [makeWave()];
|
|
489
|
+
config.waves.forEach(normalizeWave);
|
|
490
|
+
config.waveCount = config.waves.length;
|
|
491
|
+
return config;
|
|
492
|
+
}
|
|
493
|
+
//#endregion
|
|
494
|
+
export { DEFAULT_LIGHT_POSITION, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeWave, resizeWaves };
|
|
495
|
+
|
|
496
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","names":[],"sources":["../../src/config/model.ts"],"sourcesContent":["/**\n * Configuration schema for the wave: a flat sheet displaced by noise (X/Z frequency\n * + amount) then twisted by three axis-rotations (twistFrequency + twistPower per\n * axis), then scaled / rotated / positioned. Plain JSON — doubles as the save-state\n * format.\n */\n\nimport { clamp, clamp01 } from \"../util/math\";\n\nexport const MAX_COLORS = 8;\nexport const MAX_MESH_POINTS = 8;\nexport const MAX_LIGHTS = 8;\nexport const MAX_NOISE_BANDS = 4;\n/** Cap on stacked waves (keeps total geometry bounded — see WaveRenderer segment scaling). */\nexport const MAX_WAVES = 6;\n\nexport interface Vec2 {\n x: number;\n y: number;\n}\n\nexport interface Vec3 {\n x: number;\n y: number;\n z: number;\n}\n\n// \"squared\" = the hero material blend: SrcColor × Zero (framebuffer = fragColor²), which\n// deepens the colours — the faithful default. \"normal\"/\"additive\"/\"multiply\" are authoring\n// overrides (\"multiply\" darkens where waves/background overlap).\nexport type BlendMode = \"squared\" | \"normal\" | \"additive\" | \"multiply\";\n\n/** How the palette is mapped across the surface. */\nexport type BasicGradientType = \"linear\" | \"radial\" | \"conic\";\nexport type GradientType = BasicGradientType | \"mesh\";\n\nexport type BackgroundMode = \"color\" | \"gradient\" | \"image\";\nexport type BackgroundImageFit = \"cover\" | \"contain\" | \"stretch\";\n\n/** What fills the 2D palette texture: the baked hero LUT, our editable stops, or\n * a named built-in map (see PALETTE_MAPS). Any string is allowed for forward-compat. */\nexport type PaletteSource = \"hero\" | \"stops\" | (string & {});\n\n/** A positionable light. `position` lives in the same 3D space as the wave. */\nexport interface LightConfig {\n position: Vec3;\n color: string;\n intensity: number;\n}\n\n/** A default light; pass overrides for added fill lights. */\nexport function createLight(\n position: Vec3 = { x: 300, y: 500, z: 800 },\n intensity = 1,\n): LightConfig {\n return { position: { ...position }, color: \"#ffffff\", intensity };\n}\n\n/** Where the first light lands when you engage lights from an empty scene. Shared by the\n * \"drag in 3D\" control and the camera-rig minimap, which previews a light marker here so the\n * rig always shows the light — even before one has been explicitly added. */\nexport const DEFAULT_LIGHT_POSITION: Vec3 = { x: 800, y: 900, z: 1100 };\n\n/**\n * A noise band: inside a rectangular uv region (startX..endX along the length,\n * startY..endY across the width, softened by `feather`), the fiber streaks are\n * overridden — strength, frequency (density), colourAttenuation (how much the local\n * colour suppresses them), and the end-weighting parabolaPower. Lets the fibers vary\n * per region instead of uniform.\n */\nexport interface NoiseBand {\n startX: number;\n endX: number;\n startY: number;\n endY: number;\n feather: number;\n strength: number;\n frequency: number;\n colorAttenuation: number;\n parabolaPower: number;\n}\n\n/** A default band: a strong, coarse streak region over the first half. */\nexport function createNoiseBand(): NoiseBand {\n return {\n startX: 0.0,\n endX: 0.5,\n startY: 0.0,\n endY: 1.0,\n feather: 0.3,\n strength: 1.0,\n frequency: 220,\n colorAttenuation: 0.0,\n parabolaPower: 2.0,\n };\n}\n\n/** One gradient stop: a colour at a normalized position (0–1) across the width. */\nexport interface ColorStop {\n color: string;\n pos: number;\n}\n\n/** One colour influence point in the 2D mesh-gradient field. */\nexport interface MeshGradientPoint {\n color: string;\n /** Horizontal UV position (0–1). */\n x: number;\n /** Vertical UV position (0–1). */\n y: number;\n /** Relative reach of this point's colour field. */\n influence: number;\n}\n\n/** Build evenly-spaced stops from a plain list of colours. */\nexport function makeStops(colors: string[]): ColorStop[] {\n const n = colors.length;\n return colors.map((color, i) => ({ color, pos: n > 1 ? i / (n - 1) : 0 }));\n}\n\n/** A balanced iOS-style field shown the first time Mesh is selected. */\nexport function createDefaultMeshPoints(): MeshGradientPoint[] {\n return [\n { color: \"#5e5ce6\", x: 0.08, y: 0.12, influence: 0.78 },\n { color: \"#64d2ff\", x: 0.88, y: 0.08, influence: 0.72 },\n { color: \"#ff375f\", x: 0.12, y: 0.88, influence: 0.72 },\n { color: \"#ff9f0a\", x: 0.9, y: 0.86, influence: 0.78 },\n { color: \"#bf5af2\", x: 0.5, y: 0.48, influence: 0.58 },\n ];\n}\n\n/**\n * A single wave: a COMPLETE, self-contained wave — its own shape, twist, colour, finish,\n * transform and blend. Stacking waves composites independent waves; there is no shared\n * \"base wave\" any more, so nothing is duplicated between a global section and the waves.\n * Field names mirror the legacy top-level wave fields so migration + the per-section helpers\n * (normalizeWaveColour, randomize*) map 1:1.\n */\nexport interface WaveConfig {\n // Colour & gradient\n palette: ColorStop[];\n gradientType: GradientType;\n gradientAngle: number;\n gradientShift: number;\n meshGradientPoints: MeshGradientPoint[];\n meshGradientSoftness: number;\n usePaletteTexture: boolean;\n paletteSource: PaletteSource;\n paletteImageUrl?: string;\n paletteVideoUrl?: string;\n paletteTextureScale: Vec2;\n paletteTextureOffset: Vec2;\n paletteTextureRotation: number;\n /** Palette-offset drift per second (animates colour independently of the geometry; 0 = static).\n * Applies to any texture palette (not mesh / procedural stops). */\n paletteDriftX: number;\n paletteDriftY: number;\n paletteEdgeColor: string;\n paletteEdgeAmount: number;\n hueShift: number;\n colorContrast: number;\n colorSaturation: number;\n // Surface finish\n fiberCount: number;\n fiberStrength: number;\n noiseBands: NoiseBand[];\n texture: number;\n creaseLight: number;\n creaseSharpness: number;\n creaseSoftness: number;\n sheen: number;\n roundness: number;\n /** Thin-film / holographic hue response that shifts with view angle (0 = off). */\n iridescence: number;\n edgeFade: number;\n /** Softness of the ribbon's long edges (smoothstep width across uv.y). 0.1 = the original\n * hardcoded value; smaller = razor-crisp graphic ribbons, larger = soft vapor. */\n edgeFeather: number;\n /** Depth tint (solid theme): fade far fragments toward depthTintColor for atmospheric\n * separation in multi-wave stacks (0 = off). */\n depthTint: number;\n depthTintColor: string;\n // Displacement + twist (the wave shape)\n displaceFrequency: Vec2;\n displaceAmount: number;\n /** Optional 2nd displacement octave: finer ripples riding on the broad swell (amount 0 = off). */\n detailFrequency: number;\n detailAmount: number;\n twistFrequency: Vec3;\n twistPower: Vec3;\n twistMotion?: boolean;\n // Material (\"solid\" surface vs \"wireframe\" line shader)\n theme?: \"solid\" | \"wireframe\";\n lineAmount?: number;\n lineThickness?: number;\n lineDerivativePower?: number;\n maxWidth?: number;\n // Transform (absolute — no shared base to offset from)\n position: Vec3;\n rotation: Vec3;\n scale: Vec3;\n // Compositing\n blendMode: BlendMode;\n /** Absolute animation speed for this wave (legacy global speed × per-layer multiplier). */\n speed: number;\n /** Overall opacity of this wave. */\n opacity: number;\n /** Phase/seed so waves don't move in lockstep. */\n seed: number;\n}\n\n/**\n * Scene-level settings shared by every wave: output/background/camera/lights, the post-fx\n * pass (grain/blur), playback, quality, and the whole-composition mirror. Everything that\n * describes an individual wave lives on WaveConfig instead.\n */\nexport interface SceneConfig {\n background: string;\n transparentBackground: boolean;\n backgroundMode: BackgroundMode;\n backgroundPalette: ColorStop[];\n backgroundGradientType: GradientType;\n backgroundGradientAngle: number;\n backgroundGradientSource: PaletteSource;\n backgroundMeshPoints: MeshGradientPoint[];\n backgroundMeshSoftness: number;\n backgroundImageSource: PaletteSource;\n backgroundImageUrl?: string;\n backgroundVideoUrl?: string;\n backgroundImageFit: BackgroundImageFit;\n backgroundImageZoom: number;\n backgroundImagePosition: Vec2;\n /** Number of stacked waves (kept in sync with waves.length). */\n waveCount: number;\n quality: number;\n dprMax: number;\n paused: boolean;\n /** Noise phase offset — scrubs the noise pattern to pick a still frame. */\n timeOffset?: number;\n /** Seamless-loop period in seconds (0 = off). When >0, the motion is mapped onto a circle in\n * noise space so it repeats exactly every `loopSeconds` — scene-level so a multi-wave stack\n * shares one period and the whole composite loops. */\n loopSeconds?: number;\n introRamp?: boolean;\n showCameraRig: boolean;\n cameraDistance: number;\n cameraZoom: number;\n cameraPosition: Vec3;\n cameraTarget: Vec3;\n /** Film grain amount (post pass). */\n grain: number;\n /** Soft-focus / spin blur amount (post pass). */\n blur: number;\n blurSamples?: number;\n /** Bloom (post pass, UnrealBloomPass). strength 0 removes the pass entirely, so cost and pixels\n * are identical to bloom-off; radius/threshold only take effect once strength > 0. */\n bloomStrength?: number;\n bloomRadius?: number;\n bloomThreshold?: number;\n /** Base ambient light level (0–1). */\n ambient: number;\n lights: LightConfig[];\n /** Mirror the whole composition on screen (world-space flip). */\n mirrorH: boolean;\n mirrorV: boolean;\n}\n\n/** The full save-state: scene settings + one or more complete waves. */\nexport interface StudioConfig extends SceneConfig {\n waves: WaveConfig[];\n}\n\n/** Spread a base wave into `count` overlapping waves — each with a slightly varied hue, width,\n * speed, phase, vertical offset and roll so a stack reads as one composition. `count === 1`\n * returns the base unchanged. Used to author multi-wave presets. */\nexport function makeWaveSpread(base: WaveConfig, count: number): WaveConfig[] {\n if (count <= 1) return [structuredClone(base)];\n const out: WaveConfig[] = [];\n for (let i = 0; i < count; i++) {\n const f = i / (count - 1);\n const w = structuredClone(base);\n w.opacity = 1.0 - f * 0.3;\n w.hueShift = base.hueShift + i * 18;\n w.scale = { x: base.scale.x, y: base.scale.y * (1 - f * 0.2), z: base.scale.z };\n w.speed = base.speed * (1 + f * 0.15);\n w.seed = i * 3.3;\n w.position = {\n x: base.position.x,\n y: base.position.y + (f - 0.5) * 1.5,\n z: base.position.z - i * 0.8,\n };\n w.rotation = { x: base.rotation.x, y: base.rotation.y, z: base.rotation.z + i * 20 };\n out.push(w);\n }\n return out;\n}\n\n/** The hero wave (a single complete wave) — the base for the default config and most presets. */\nfunction defaultWave(): WaveConfig {\n return {\n // The hero palette: a periwinkle tip/edge, a dominant orange core, then coral → magenta →\n // pink, with a violet twist tip. gradientShift warps it to mimic a baked 2D palette texture.\n palette: [\n { color: \"#8e9dff\", pos: 0 }, // periwinkle (blue tip/edge)\n { color: \"#c98fd0\", pos: 0.14 }, // lavender transition\n { color: \"#ff9326\", pos: 0.3 }, // orange (rising)\n { color: \"#fd8108\", pos: 0.52 }, // orange core\n { color: \"#fb7a36\", pos: 0.64 }, // orange-coral (keeps orange dominant)\n { color: \"#d24ecc\", pos: 0.78 }, // true magenta (hue ~303, not pink)\n { color: \"#e95cae\", pos: 0.9 }, // pink-magenta\n { color: \"#9b6ae0\", pos: 1.0 }, // violet (twist tip)\n ],\n gradientType: \"linear\",\n gradientAngle: 90, // 90° = the gradient runs ALONG the length (uv.x)\n gradientShift: 0.15,\n meshGradientPoints: createDefaultMeshPoints(),\n meshGradientSoftness: 0.62,\n usePaletteTexture: true, // default to the baked hero LUT\n paletteSource: \"hero\",\n paletteTextureScale: { x: 1, y: 1 },\n paletteTextureOffset: { x: 0, y: 0 },\n paletteTextureRotation: 0,\n paletteDriftX: 0,\n paletteDriftY: 0,\n paletteEdgeColor: \"#8e9dff\",\n paletteEdgeAmount: 0.3,\n hueShift: -1.81, // hero colorHueShift ≈ -1.81°\n colorContrast: 1.0,\n colorSaturation: 1.15,\n // Hero fibers: the surfaceColor fragment hardcodes freq 600 / strength 0.2; the line* fields\n // feed the wireframe theme (unused by the solid hero).\n fiberCount: 600,\n fiberStrength: 0.2,\n noiseBands: [],\n texture: 0,\n creaseLight: 0.6,\n creaseSharpness: 0.589,\n creaseSoftness: 1.0,\n // sheen 0 + roundness 0: the ortho crop makes crease low, so the hero look comes from the\n // SrcColor² blend + the palette, not the derivative white-lift.\n sheen: 0.0,\n roundness: 0.0,\n iridescence: 0,\n edgeFade: 0.04,\n edgeFeather: 0.1, // the original hardcoded ribbon-edge softness\n depthTint: 0,\n depthTintColor: \"#0a2540\",\n // Hero deformation on the native 400-unit folded() geometry.\n displaceFrequency: { x: 0.003234, y: 0.00799 },\n displaceAmount: 6.051,\n detailFrequency: 0.04, // finer than the base swell; only bites once detailAmount > 0\n detailAmount: 0,\n // Small twist frequencies + high powers — a gentle twist; the drama is the ortho crop.\n twistFrequency: { x: -0.055, y: 0.077, z: -0.518 },\n twistPower: { x: 3.95, y: 5.85, z: 6.33 },\n twistMotion: false,\n theme: \"solid\",\n lineAmount: 425, // wireframe-theme line params (defaults)\n lineThickness: 1,\n lineDerivativePower: 0.95,\n maxWidth: 1232,\n // Hero mesh transform at FULL scale (the ortho camera frames in pixels).\n position: { x: -24.3, y: -56.4, z: -11.1 },\n rotation: { x: -9.14, y: -16.25, z: -161.32 },\n scale: { x: 10, y: 10, z: 7 },\n blendMode: \"squared\", // the hero squaring blend (SrcColor²)\n speed: 0.04, // hero speed: 4e-5 vs ms-time ≈ 0.04/s\n opacity: 1,\n seed: 0,\n };\n}\n\n/** A fresh default wave (the hero wave as one complete wave). */\nexport function makeWave(): WaveConfig {\n return defaultWave();\n}\n\n/** Resize `waves` to match `waveCount`. New waves CLONE the last one (inherit every\n * property of the preceding wave); extras are dropped. */\nexport function resizeWaves(config: StudioConfig): void {\n const target = Math.max(1, Math.round(config.waveCount) || 1);\n if (!Array.isArray(config.waves) || config.waves.length === 0) {\n config.waves = [makeWave()];\n }\n while (config.waves.length < target) {\n config.waves.push(structuredClone(config.waves[config.waves.length - 1]));\n }\n while (config.waves.length > target) config.waves.pop();\n config.waveCount = config.waves.length;\n}\n\n/** The default studio config: the hero wave + its scene, in the canonical wave model. */\nexport function createDefaultConfig(): StudioConfig {\n return {\n background: \"#ffffff\",\n transparentBackground: true,\n backgroundMode: \"color\",\n backgroundPalette: makeStops([\"#0a2540\", \"#425466\", \"#7a73ff\", \"#f6f9fc\"]),\n backgroundGradientType: \"linear\",\n backgroundGradientAngle: 135,\n backgroundGradientSource: \"stops\",\n backgroundMeshPoints: createDefaultMeshPoints(),\n backgroundMeshSoftness: 0.62,\n backgroundImageSource: \"vaporwave\",\n backgroundImageFit: \"cover\",\n backgroundImageZoom: 1,\n backgroundImagePosition: { x: 0, y: 0 },\n waveCount: 1,\n quality: 1,\n dprMax: 2,\n paused: false,\n timeOffset: 0, // noise phase (scrub to pick a still)\n introRamp: true, // ease the animation in over ~1s on load (skipped in dev; see WaveRenderer.updateTime)\n showCameraRig: false,\n // The hero camera: ORTHOGRAPHIC at (100,0,5000) looking at the origin. The mesh is ×10 so\n // the wave overflows the frame and only the twist shows. cameraZoom is a user multiplier on\n // the responsive base zoom (1 = the hero crop); cameraTarget pans the look-at to the twist.\n cameraDistance: 5001,\n cameraPosition: { x: 100, y: 0, z: 5000 },\n cameraTarget: { x: -44, y: -250, z: 0 },\n cameraZoom: 1.0,\n // Post (one pass over the whole composite): hero grain 1.1, blur 0.02.\n grain: 1.1,\n blur: 0.02,\n blurSamples: 6,\n ambient: 0.45,\n lights: [], // hero has no lights — colour is the palette + the SrcColor² blend\n mirrorH: false,\n mirrorV: false,\n waves: [defaultWave()],\n };\n}\n\n/** Clamp/backfill a single wave's colour + palette fields (legacy `string[]` palettes become\n * ColorStop[]; mesh points + texture transform are clamped). */\nfunction normalizeWaveColour(config: WaveConfig): void {\n const p = config.palette as unknown as Array<string | ColorStop>;\n if (p.length > 0 && typeof p[0] === \"string\") {\n config.palette = makeStops(p as string[]);\n }\n if (\n config.gradientType !== \"radial\" &&\n config.gradientType !== \"conic\" &&\n config.gradientType !== \"mesh\" &&\n config.gradientType !== \"linear\"\n ) {\n config.gradientType = \"linear\";\n }\n const rawMeshPoints = config.meshGradientPoints as MeshGradientPoint[] | undefined;\n if (!Array.isArray(rawMeshPoints) || rawMeshPoints.length < 2) {\n config.meshGradientPoints = createDefaultMeshPoints();\n } else {\n const defaults = createDefaultMeshPoints();\n config.meshGradientPoints = rawMeshPoints.slice(0, MAX_MESH_POINTS).map((point, index) => {\n const fallback = defaults[index] ?? defaults[defaults.length - 1];\n const x = Number(point.x);\n const y = Number(point.y);\n const influence = Number(point.influence);\n return {\n color: typeof point.color === \"string\" ? point.color : fallback.color,\n x: clamp01(Number.isFinite(x) ? x : fallback.x),\n y: clamp01(Number.isFinite(y) ? y : fallback.y),\n influence: clamp(Number.isFinite(influence) ? influence : fallback.influence, 0.15, 1.5),\n };\n });\n }\n if (!Number.isFinite(config.meshGradientSoftness)) config.meshGradientSoftness = 0.62;\n config.meshGradientSoftness = clamp01(config.meshGradientSoftness);\n if (!config.paletteTextureScale) config.paletteTextureScale = { x: 1, y: 1 };\n if (!config.paletteTextureOffset) config.paletteTextureOffset = { x: 0, y: 0 };\n config.paletteTextureScale.x = clamp(Number(config.paletteTextureScale.x) || 1, 0.1, 8);\n config.paletteTextureScale.y = clamp(Number(config.paletteTextureScale.y) || 1, 0.1, 8);\n config.paletteTextureOffset.x = clamp(Number(config.paletteTextureOffset.x) || 0, -4, 4);\n config.paletteTextureOffset.y = clamp(Number(config.paletteTextureOffset.y) || 0, -4, 4);\n config.paletteTextureRotation = clamp(Number(config.paletteTextureRotation) || 0, -180, 180);\n}\n\n/** Backfill background styling for states saved before gradient/image backgrounds existed. */\nexport function normalizeBackground(config: StudioConfig): void {\n if (\n config.backgroundMode !== \"gradient\" &&\n config.backgroundMode !== \"image\" &&\n config.backgroundMode !== \"color\"\n ) {\n config.backgroundMode = \"color\";\n }\n const palette = config.backgroundPalette as unknown as Array<string | ColorStop> | undefined;\n if (!palette || palette.length < 2) {\n config.backgroundPalette = makeStops([\"#0a2540\", \"#425466\", \"#7a73ff\", \"#f6f9fc\"]);\n } else if (typeof palette[0] === \"string\") {\n config.backgroundPalette = makeStops(palette as string[]);\n }\n if (\n config.backgroundGradientType !== \"radial\" &&\n config.backgroundGradientType !== \"conic\" &&\n config.backgroundGradientType !== \"mesh\" &&\n config.backgroundGradientType !== \"linear\"\n ) {\n config.backgroundGradientType = \"linear\";\n }\n const bgMesh = config.backgroundMeshPoints as MeshGradientPoint[] | undefined;\n if (!Array.isArray(bgMesh) || bgMesh.length < 2) {\n config.backgroundMeshPoints = createDefaultMeshPoints();\n }\n if (!Number.isFinite(config.backgroundMeshSoftness)) config.backgroundMeshSoftness = 0.62;\n config.backgroundMeshSoftness = clamp01(config.backgroundMeshSoftness);\n if (typeof config.backgroundGradientAngle !== \"number\") config.backgroundGradientAngle = 135;\n if (typeof config.backgroundGradientSource !== \"string\")\n config.backgroundGradientSource = \"stops\";\n if (typeof config.backgroundImageSource !== \"string\") config.backgroundImageSource = \"vaporwave\";\n if (\n config.backgroundImageFit !== \"contain\" &&\n config.backgroundImageFit !== \"stretch\" &&\n config.backgroundImageFit !== \"cover\"\n ) {\n config.backgroundImageFit = \"cover\";\n }\n if (typeof config.backgroundImageZoom !== \"number\") config.backgroundImageZoom = 1;\n config.backgroundImageZoom = clamp(config.backgroundImageZoom, 0.1, 8);\n if (!config.backgroundImagePosition) config.backgroundImagePosition = { x: 0, y: 0 };\n if (typeof config.backgroundImagePosition.x !== \"number\") config.backgroundImagePosition.x = 0;\n if (typeof config.backgroundImagePosition.y !== \"number\") config.backgroundImagePosition.y = 0;\n config.backgroundImagePosition.x = clamp(config.backgroundImagePosition.x, -100, 100);\n config.backgroundImagePosition.y = clamp(config.backgroundImagePosition.y, -100, 100);\n}\n\n/** Backfill camera position/target for states saved before they existed. */\nexport function ensureCamera(config: StudioConfig): void {\n if (!config.cameraPosition)\n config.cameraPosition = { x: 0, y: 0, z: config.cameraDistance ?? 62 };\n if (!config.cameraTarget) config.cameraTarget = { x: 0, y: 0, z: 0 };\n if (typeof config.cameraZoom !== \"number\") config.cameraZoom = 1;\n}\n\n/** Backfill/repair a wave so the renderer can consume it (covers partial wave-model JSON). */\nexport function normalizeWave(s: WaveConfig): void {\n normalizeWaveColour(s);\n if (typeof s.gradientAngle !== \"number\") s.gradientAngle = 90;\n if (typeof s.gradientShift !== \"number\") s.gradientShift = 0.15;\n if (typeof s.usePaletteTexture !== \"boolean\") s.usePaletteTexture = true;\n if (typeof s.paletteSource !== \"string\") s.paletteSource = \"hero\";\n if (typeof s.paletteEdgeColor !== \"string\") s.paletteEdgeColor = \"#8e9dff\";\n if (typeof s.paletteEdgeAmount !== \"number\") s.paletteEdgeAmount = 0.3;\n if (typeof s.paletteDriftX !== \"number\") s.paletteDriftX = 0;\n if (typeof s.paletteDriftY !== \"number\") s.paletteDriftY = 0;\n if (typeof s.hueShift !== \"number\") s.hueShift = 0;\n if (typeof s.colorContrast !== \"number\") s.colorContrast = 1;\n if (typeof s.colorSaturation !== \"number\") s.colorSaturation = 1;\n if (typeof s.fiberCount !== \"number\") s.fiberCount = 600;\n if (typeof s.fiberStrength !== \"number\") s.fiberStrength = 0.2;\n if (!Array.isArray(s.noiseBands)) s.noiseBands = [];\n if (typeof s.texture !== \"number\") s.texture = 0;\n if (typeof s.creaseLight !== \"number\") s.creaseLight = 0.6;\n if (typeof s.creaseSharpness !== \"number\") s.creaseSharpness = 0.589;\n if (typeof s.creaseSoftness !== \"number\") s.creaseSoftness = 1;\n if (typeof s.sheen !== \"number\") s.sheen = 0;\n if (typeof s.roundness !== \"number\") s.roundness = 0;\n if (typeof s.iridescence !== \"number\") s.iridescence = 0;\n if (typeof s.edgeFade !== \"number\") s.edgeFade = 0.04;\n if (typeof s.edgeFeather !== \"number\") s.edgeFeather = 0.1;\n if (typeof s.depthTint !== \"number\") s.depthTint = 0;\n if (typeof s.depthTintColor !== \"string\") s.depthTintColor = \"#0a2540\";\n if (!s.displaceFrequency) s.displaceFrequency = { x: 0.003234, y: 0.00799 };\n if (typeof s.displaceAmount !== \"number\") s.displaceAmount = 6.051;\n if (typeof s.detailFrequency !== \"number\") s.detailFrequency = 0.04;\n if (typeof s.detailAmount !== \"number\") s.detailAmount = 0;\n if (!s.twistFrequency) s.twistFrequency = { x: -0.055, y: 0.077, z: -0.518 };\n if (!s.twistPower) s.twistPower = { x: 3.95, y: 5.85, z: 6.33 };\n if (typeof s.theme !== \"string\") s.theme = \"solid\";\n if (typeof s.lineAmount !== \"number\") s.lineAmount = 425;\n if (typeof s.lineThickness !== \"number\") s.lineThickness = 1;\n if (typeof s.lineDerivativePower !== \"number\") s.lineDerivativePower = 0.95;\n if (typeof s.maxWidth !== \"number\") s.maxWidth = 1232;\n if (!s.position) s.position = { x: 0, y: 0, z: 0 };\n if (!s.rotation) s.rotation = { x: 0, y: 0, z: 0 };\n if (!s.scale) s.scale = { x: 10, y: 10, z: 7 };\n if (typeof s.blendMode !== \"string\") s.blendMode = \"squared\";\n if (typeof s.speed !== \"number\") s.speed = 0.04;\n if (typeof s.opacity !== \"number\") s.opacity = 1;\n if (typeof s.seed !== \"number\") s.seed = 0;\n}\n\n/** Backfill scene-level defaults (background/camera/post/lights/quality/mirror). */\nexport function ensureSceneDefaults(config: StudioConfig): void {\n normalizeBackground(config);\n ensureCamera(config);\n if (typeof config.ambient !== \"number\") config.ambient = 0.45;\n if (!Array.isArray(config.lights)) config.lights = [];\n if (typeof config.quality !== \"number\") config.quality = 1;\n if (typeof config.dprMax !== \"number\") config.dprMax = 2;\n if (typeof config.grain !== \"number\") config.grain = 1.1;\n if (typeof config.blur !== \"number\") config.blur = 0.02;\n if (typeof config.blurSamples !== \"number\") config.blurSamples = 6;\n if (typeof config.bloomStrength !== \"number\") config.bloomStrength = 0;\n if (typeof config.bloomRadius !== \"number\") config.bloomRadius = 0.4;\n if (typeof config.bloomThreshold !== \"number\") config.bloomThreshold = 0.85;\n if (typeof config.showCameraRig !== \"boolean\") config.showCameraRig = false;\n if (typeof config.paused !== \"boolean\") config.paused = false;\n if (typeof config.loopSeconds !== \"number\") config.loopSeconds = 0;\n if (typeof config.mirrorH !== \"boolean\") config.mirrorH = false;\n if (typeof config.mirrorV !== \"boolean\") config.mirrorV = false;\n}\n\n/** Normalize an ingested config to the wave model: backfill the scene + every wave, and drop in\n * a default wave if none are present. Idempotent, so it is safe on the renderer's own config as\n * well as freshly loaded save-states / share links. */\nexport function ensureStudioConfig(input: StudioConfig): StudioConfig {\n const config = input;\n ensureSceneDefaults(config);\n if (!Array.isArray(config.waves) || config.waves.length === 0) {\n config.waves = [makeWave()];\n }\n config.waves.forEach(normalizeWave);\n config.waveCount = config.waves.length;\n return config;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createDefaultConfig } from "./config/model.js";
|
|
2
|
+
import { WaveRenderer } from "./renderer/WaveRenderer.js";
|
|
3
|
+
|
|
4
|
+
//#region src/core-loader.d.ts
|
|
5
|
+
declare namespace core_loader_d_exports {
|
|
6
|
+
export { WaveRenderer, createDefaultConfig };
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { WaveRenderer, core_loader_d_exports, createDefaultConfig };
|
|
10
|
+
//# sourceMappingURL=core-loader.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { createDefaultConfig } from "./config/model.js";
|
|
3
|
+
import { WaveRenderer } from "./renderer/WaveRenderer.js";
|
|
4
|
+
//#region src/core-loader.ts
|
|
5
|
+
var core_loader_exports = /* @__PURE__ */ __exportAll({
|
|
6
|
+
WaveRenderer: () => WaveRenderer,
|
|
7
|
+
createDefaultConfig: () => createDefaultConfig
|
|
8
|
+
});
|
|
9
|
+
//#endregion
|
|
10
|
+
export { WaveRenderer, core_loader_exports, createDefaultConfig };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=core-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-loader.js","names":[],"sources":["../src/core-loader.ts"],"sourcesContent":["// The dynamic-import target for the shell. The `.` entry's `createWave` reaches the heavy renderer\n// (and, through it, three.js) via `import(\"./core-loader\")`, so a bundler code-splits three out of\n// the initial load — the drop-in component ships a tiny shell and fetches the engine only when a\n// wave actually upgrades. The standalone/CDN build imports this statically instead (three bundled).\nexport { WaveRenderer } from \"./renderer/WaveRenderer\";\nexport { createDefaultConfig } from \"./config/model\";\n"],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, ColorStop, DEFAULT_LIGHT_POSITION, GradientType, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PaletteSource, SceneConfig, StudioConfig, Vec2, Vec3, WaveConfig, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeWave, resizeWaves } from "./config/model.js";
|
|
2
|
+
import { WaveRenderer, WaveRendererOptions } from "./renderer/WaveRenderer.js";
|
|
3
|
+
import { FallbackReason, WaveHandle, WaveOptions, WaveState, createWave, mountWave } from "./shell/createWave.js";
|
|
4
|
+
export { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, ColorStop, DEFAULT_LIGHT_POSITION, type FallbackReason, GradientType, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PaletteSource, SceneConfig, StudioConfig, Vec2, Vec3, WaveConfig, type WaveHandle, type WaveOptions, type WaveRenderer, type WaveRendererOptions, type WaveState, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, createWave, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, mountWave, normalizeBackground, normalizeWave, resizeWaves };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { DEFAULT_LIGHT_POSITION, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeWave, resizeWaves } from "./config/model.js";
|
|
2
|
+
import { createWave, mountWave } from "./shell/createWave.js";
|
|
3
|
+
export { DEFAULT_LIGHT_POSITION, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, createWave, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, mountWave, normalizeBackground, normalizeWave, resizeWaves };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { StudioConfig } from "./config/model.js";
|
|
2
|
+
|
|
3
|
+
//#region src/presets.d.ts
|
|
4
|
+
/** Presets: each a complete studio config (scene + one or more waves) in the wave model. */
|
|
5
|
+
declare const PRESETS: Record<string, () => StudioConfig>;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { PRESETS };
|
|
8
|
+
//# sourceMappingURL=presets.d.ts.map
|