@wave3d/core 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -6
- package/dist/config/model.d.ts +53 -2
- package/dist/config/model.js +133 -61
- package/dist/config/model.js.map +1 -1
- package/dist/core-loader.d.ts +0 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/presets.d.ts +0 -1
- package/dist/presets.js +105 -0
- package/dist/presets.js.map +1 -1
- package/dist/renderer/WaveGeometry.d.ts +0 -1
- package/dist/renderer/WaveGeometry.js.map +1 -1
- package/dist/renderer/WaveRenderer.d.ts +62 -14
- package/dist/renderer/WaveRenderer.js +126 -17
- package/dist/renderer/WaveRenderer.js.map +1 -1
- package/dist/renderer/heroPalette.d.ts +0 -1
- package/dist/renderer/index.d.ts +2 -2
- package/dist/renderer/index.js +2 -2
- package/dist/renderer/interaction.d.ts +0 -2
- package/dist/renderer/interaction.js +9 -0
- package/dist/renderer/interaction.js.map +1 -1
- package/dist/renderer/palette.d.ts +0 -1
- package/dist/renderer/shaders.js +46 -0
- package/dist/renderer/shaders.js.map +1 -1
- package/dist/shell/createWave.d.ts +0 -1
- package/dist/standalone/wave3d.standalone.js +501 -294
- package/dist/standalone.d.ts +2 -3
- package/dist/standalone.js +2 -2
- package/dist/studio/StudioWaveRenderer.d.ts +4 -1
- package/dist/studio/StudioWaveRenderer.js +10 -3
- package/dist/studio/StudioWaveRenderer.js.map +1 -1
- package/dist/studio/randomize.d.ts +0 -1
- package/dist/studio/thumbnail.d.ts +0 -1
- package/dist/studio/thumbnail.js +5 -1
- package/dist/studio/thumbnail.js.map +1 -1
- package/package.json +5 -3
- package/skills/wave3d/SKILL.md +30 -12
package/README.md
CHANGED
|
@@ -40,9 +40,7 @@ const blob = await handle.snapshot(); // resolves null until running. options: {
|
|
|
40
40
|
```html
|
|
41
41
|
<script type="module">
|
|
42
42
|
import { mountWave } from "https://esm.sh/@wave3d/core/standalone";
|
|
43
|
-
mountWave(document.getElementById("wave"), {
|
|
44
|
-
/* your exported config */
|
|
45
|
-
});
|
|
43
|
+
mountWave(document.getElementById("wave"), {/* your exported config */});
|
|
46
44
|
</script>
|
|
47
45
|
```
|
|
48
46
|
|
|
@@ -76,9 +74,7 @@ createWave(el, {
|
|
|
76
74
|
touch: false, // follow touch pointers too
|
|
77
75
|
bindings: [{ source: "scroll", target: "timeOffset", to: 40 }], // scrub the whole wave with scroll
|
|
78
76
|
},
|
|
79
|
-
waves: [
|
|
80
|
-
/* each wave carries its own `interaction` (hover / press / bindings) */
|
|
81
|
-
],
|
|
77
|
+
waves: [/* each wave carries its own `interaction` (hover / press / bindings) */],
|
|
82
78
|
});
|
|
83
79
|
```
|
|
84
80
|
|
package/dist/config/model.d.ts
CHANGED
|
@@ -26,6 +26,17 @@ type BasicGradientType = "linear" | "radial" | "conic";
|
|
|
26
26
|
type GradientType = BasicGradientType | "mesh";
|
|
27
27
|
type BackgroundMode = "color" | "gradient" | "image";
|
|
28
28
|
type BackgroundImageFit = "cover" | "contain" | "stretch";
|
|
29
|
+
/** How the authored reference frame (FRAME_W × FRAME_H, a 16:9 rectangle centred on cameraTarget)
|
|
30
|
+
* is mapped onto a canvas of a different aspect:
|
|
31
|
+
* - `cover` — fill both axes, crop the overflow. The default and the hero look.
|
|
32
|
+
* - `contain` — fit the whole frame, revealing world beyond it on the long axis.
|
|
33
|
+
* - `width` — always bind on width, so the horizontal composition is identical at every aspect.
|
|
34
|
+
* Above 16:9 this is exactly `cover`; below it, it reveals vertically instead of cropping.
|
|
35
|
+
* - `height` — always bind on height (the mirror of `width`).
|
|
36
|
+
* Pair with `cameraMinVisibleWidth` to land between `cover` and `width`. */
|
|
37
|
+
type CameraFit = "cover" | "contain" | "width" | "height";
|
|
38
|
+
/** Runtime whitelist for {@link CameraFit} (validating imported/serialized configs). */
|
|
39
|
+
declare const CAMERA_FITS: readonly CameraFit[];
|
|
29
40
|
/** What fills the 2D palette texture: the baked hero LUT, our editable stops, or
|
|
30
41
|
* a named built-in map (see PALETTE_MAPS). Any string is allowed for forward-compat. */
|
|
31
42
|
type PaletteSource = "hero" | "stops" | (string & {});
|
|
@@ -137,10 +148,34 @@ interface WaveConfig {
|
|
|
137
148
|
twistFrequency: Vec3;
|
|
138
149
|
twistPower: Vec3;
|
|
139
150
|
twistMotion?: boolean;
|
|
151
|
+
/** Helix: sweep the ribbon around its OWN length axis, `helixTurns` full turns from end to end.
|
|
152
|
+
* The three twists above can't express this — their axes sit 45°/90° off the length, and
|
|
153
|
+
* `expStep` is a monotone falloff, so nothing they do ever repeats. This angle is periodic in the
|
|
154
|
+
* length coordinate instead, which is what makes a helix (and a DNA ladder) reachable.
|
|
155
|
+
* 0 turns still leaves the block inert unless radius or roll is non-zero. */
|
|
156
|
+
helixTurns?: number;
|
|
157
|
+
/** Carries the WHOLE ribbon around the axis at this radius (world units, pre-scale), keeping its
|
|
158
|
+
* orientation. A narrow ribbon then reads as a single strand — two waves half a turn apart in
|
|
159
|
+
* `helixPhase` are the two strands of a double helix. 0 = off. */
|
|
160
|
+
helixRadius?: number;
|
|
161
|
+
/** Rolls the ribbon's own cross-section about the axis as it advances, as a fraction of
|
|
162
|
+
* `helixTurns` (1 = rolls exactly in step, a rigid twisted ribbon). That swings the ribbon's two
|
|
163
|
+
* long edges onto opposite sides of the axis, so ONE wave becomes a ladder whose edges are both
|
|
164
|
+
* strands — pair it with `rungAmount` for the rungs between them. 0 = off. */
|
|
165
|
+
helixRoll?: number;
|
|
166
|
+
/** Phase offset in degrees — where along the turn the ribbon starts. The per-wave knob that puts
|
|
167
|
+
* a second wave on the opposite side of the same helix (180). */
|
|
168
|
+
helixPhase?: number;
|
|
140
169
|
theme?: "solid" | "wireframe";
|
|
141
170
|
lineAmount?: number;
|
|
142
171
|
lineThickness?: number;
|
|
143
172
|
lineDerivativePower?: number;
|
|
173
|
+
/** Wireframe RUNGS: a second line family carved at constant uv.y, so these run ACROSS the ribbon
|
|
174
|
+
* where `lineAmount`'s run along it — the two cross into a ladder. Frequency, like `lineAmount`
|
|
175
|
+
* (rungs ≈ amount / π). 0 = off, and the cross-wise path isn't compiled. */
|
|
176
|
+
rungAmount?: number;
|
|
177
|
+
/** Rung line width, in pixels (screen-space, so it holds at any zoom). */
|
|
178
|
+
rungThickness?: number;
|
|
144
179
|
maxWidth?: number;
|
|
145
180
|
position: Vec3;
|
|
146
181
|
rotation: Vec3;
|
|
@@ -163,7 +198,7 @@ interface WaveConfig {
|
|
|
163
198
|
type InteractionSource = "scroll" | "hover" | "pointerX" | "pointerY" | "pointerSpeed" | "press" | "scrollVelocity" | "appear" | `custom:${string}`;
|
|
164
199
|
/** Per-WAVE params a binding may drive. Single source of truth for WAVE_APPLIERS in
|
|
165
200
|
* renderer/interaction.ts (checked via `satisfies`) and validated by normalizeWaveInteraction. */
|
|
166
|
-
declare const WAVE_TARGET_NAMES: readonly ["displaceAmount", "detailAmount", "twistPowerX", "twistPowerY", "twistPowerZ", "twistFrequencyX", "twistFrequencyY", "twistFrequencyZ", "hueShift", "gradientShift", "colorSaturation", "opacity", "lineThickness", "lineAmount", "fiberStrength", "sheen", "iridescence", "positionX", "positionY"];
|
|
201
|
+
declare const WAVE_TARGET_NAMES: readonly ["displaceAmount", "detailAmount", "twistPowerX", "twistPowerY", "twistPowerZ", "twistFrequencyX", "twistFrequencyY", "twistFrequencyZ", "helixPhase", "helixTurns", "helixRadius", "hueShift", "gradientShift", "colorSaturation", "opacity", "lineThickness", "lineAmount", "fiberStrength", "sheen", "iridescence", "positionX", "positionY"];
|
|
167
202
|
/** A per-wave param a {@link WaveInteractionBinding} can drive. */
|
|
168
203
|
type WaveInteractionTarget = (typeof WAVE_TARGET_NAMES)[number];
|
|
169
204
|
/** SCENE params a binding may drive (post / camera / time — shared, not per wave). Single source of
|
|
@@ -286,6 +321,22 @@ interface SceneConfig {
|
|
|
286
321
|
cameraZoom: number;
|
|
287
322
|
cameraPosition: Vec3;
|
|
288
323
|
cameraTarget: Vec3;
|
|
324
|
+
/** How the authored reference frame maps onto the canvas when their aspects differ.
|
|
325
|
+
* Default `"cover"`. See {@link CameraFit}. */
|
|
326
|
+
cameraFit?: CameraFit;
|
|
327
|
+
/** Floor on how much of the authored frame's WIDTH stays on screen, as a fraction (0..1).
|
|
328
|
+
* A ceiling on zoom applied AFTER {@link cameraFit}, so the two compose instead of fighting:
|
|
329
|
+
* it only ever zooms out, and is inert for fits that already do (`contain`, `width`).
|
|
330
|
+
*
|
|
331
|
+
* This is the narrow-screen crop control. `cameraFit: "cover"` binds on height once the canvas
|
|
332
|
+
* is narrower than the 16:9 reference, so a portrait phone (390×844 @ dpr 2) zooms 2.25× and
|
|
333
|
+
* shows only ~26% of the authored width. `0.6` holds 60% of it on screen; `1` is equivalent to
|
|
334
|
+
* `"width"`. Default 0 (off) — existing configs frame exactly as before.
|
|
335
|
+
*
|
|
336
|
+
* It clamps the BASE zoom, before the cameraZoom multiplier, which makes the fraction read
|
|
337
|
+
* against your own composition rather than the raw constant: `1` shows exactly the horizontal
|
|
338
|
+
* span you see at 16:9 whatever cameraZoom you authored at, and `0.6` shows 60% of that. */
|
|
339
|
+
cameraMinVisibleWidth?: number;
|
|
289
340
|
/** Film grain amount (post pass). */
|
|
290
341
|
grain: number;
|
|
291
342
|
/** Soft-focus / spin blur amount (post pass). */
|
|
@@ -373,5 +424,5 @@ declare function normalizeSceneInteraction(config: StudioConfig): void;
|
|
|
373
424
|
* well as freshly loaded save-states / share links. */
|
|
374
425
|
declare function ensureStudioConfig(input: StudioConfig): StudioConfig;
|
|
375
426
|
//#endregion
|
|
376
|
-
export { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, ColorStop, DEFAULT_LIGHT_POSITION, GradientType, InteractionSource, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PaletteSource, SceneConfig, SceneInteractionBinding, SceneInteractionConfig, SceneInteractionTarget, StudioConfig, Vec2, Vec3, WaveConfig, WaveHoverConfig, WaveInteractionBinding, WaveInteractionConfig, WaveInteractionTarget, WavePressConfig, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeSceneInteraction, normalizeWave, normalizeWaveInteraction, resizeWaves };
|
|
427
|
+
export { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, CAMERA_FITS, CameraFit, ColorStop, DEFAULT_LIGHT_POSITION, GradientType, InteractionSource, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PaletteSource, SceneConfig, SceneInteractionBinding, SceneInteractionConfig, SceneInteractionTarget, StudioConfig, Vec2, Vec3, WaveConfig, WaveHoverConfig, WaveInteractionBinding, WaveInteractionConfig, WaveInteractionTarget, WavePressConfig, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeSceneInteraction, normalizeWave, normalizeWaveInteraction, resizeWaves };
|
|
377
428
|
//# sourceMappingURL=model.d.ts.map
|
package/dist/config/model.js
CHANGED
|
@@ -12,6 +12,13 @@ const MAX_LIGHTS = 8;
|
|
|
12
12
|
const MAX_NOISE_BANDS = 4;
|
|
13
13
|
/** Cap on stacked waves (keeps total geometry bounded — see WaveRenderer segment scaling). */
|
|
14
14
|
const MAX_WAVES = 6;
|
|
15
|
+
/** Runtime whitelist for {@link CameraFit} (validating imported/serialized configs). */
|
|
16
|
+
const CAMERA_FITS = [
|
|
17
|
+
"cover",
|
|
18
|
+
"contain",
|
|
19
|
+
"width",
|
|
20
|
+
"height"
|
|
21
|
+
];
|
|
15
22
|
/** A default light; pass overrides for added fill lights. */
|
|
16
23
|
function createLight(position = {
|
|
17
24
|
x: 300,
|
|
@@ -112,6 +119,9 @@ const WAVE_TARGET_NAMES = [
|
|
|
112
119
|
"twistFrequencyX",
|
|
113
120
|
"twistFrequencyY",
|
|
114
121
|
"twistFrequencyZ",
|
|
122
|
+
"helixPhase",
|
|
123
|
+
"helixTurns",
|
|
124
|
+
"helixRadius",
|
|
115
125
|
"hueShift",
|
|
116
126
|
"gradientShift",
|
|
117
127
|
"colorSaturation",
|
|
@@ -256,10 +266,16 @@ function defaultWave() {
|
|
|
256
266
|
z: 6.33
|
|
257
267
|
},
|
|
258
268
|
twistMotion: false,
|
|
269
|
+
helixTurns: 0,
|
|
270
|
+
helixRadius: 0,
|
|
271
|
+
helixRoll: 0,
|
|
272
|
+
helixPhase: 0,
|
|
259
273
|
theme: "solid",
|
|
260
274
|
lineAmount: 425,
|
|
261
275
|
lineThickness: 1,
|
|
262
276
|
lineDerivativePower: .95,
|
|
277
|
+
rungAmount: 0,
|
|
278
|
+
rungThickness: 1,
|
|
263
279
|
maxWidth: 1232,
|
|
264
280
|
position: {
|
|
265
281
|
x: -24.3,
|
|
@@ -338,6 +354,8 @@ function createDefaultConfig() {
|
|
|
338
354
|
z: 0
|
|
339
355
|
},
|
|
340
356
|
cameraZoom: 1,
|
|
357
|
+
cameraFit: "cover",
|
|
358
|
+
cameraMinVisibleWidth: 0,
|
|
341
359
|
grain: 1.1,
|
|
342
360
|
blur: .02,
|
|
343
361
|
blurSamples: 6,
|
|
@@ -368,7 +386,8 @@ function createDefaultConfig() {
|
|
|
368
386
|
* ColorStop[]; mesh points + texture transform are clamped). */
|
|
369
387
|
function normalizeWaveColour(config) {
|
|
370
388
|
const p = config.palette;
|
|
371
|
-
if (p
|
|
389
|
+
if (!Array.isArray(p) || p.length === 0) config.palette = defaultWave().palette;
|
|
390
|
+
else if (typeof p[0] === "string") config.palette = makeStops(p);
|
|
372
391
|
if (config.gradientType !== "radial" && config.gradientType !== "conic" && config.gradientType !== "mesh" && config.gradientType !== "linear") config.gradientType = "linear";
|
|
373
392
|
const rawMeshPoints = config.meshGradientPoints;
|
|
374
393
|
if (!Array.isArray(rawMeshPoints) || rawMeshPoints.length < 2) config.meshGradientPoints = createDefaultMeshPoints();
|
|
@@ -405,6 +424,8 @@ function normalizeWaveColour(config) {
|
|
|
405
424
|
}
|
|
406
425
|
/** Backfill background styling for states saved before gradient/image backgrounds existed. */
|
|
407
426
|
function normalizeBackground(config) {
|
|
427
|
+
if (typeof config.background !== "string") config.background = "#ffffff";
|
|
428
|
+
if (typeof config.transparentBackground !== "boolean") config.transparentBackground = true;
|
|
408
429
|
if (config.backgroundMode !== "gradient" && config.backgroundMode !== "image" && config.backgroundMode !== "color") config.backgroundMode = "color";
|
|
409
430
|
const palette = config.backgroundPalette;
|
|
410
431
|
if (!palette || palette.length < 2) config.backgroundPalette = makeStops([
|
|
@@ -419,11 +440,11 @@ function normalizeBackground(config) {
|
|
|
419
440
|
if (!Array.isArray(bgMesh) || bgMesh.length < 2) config.backgroundMeshPoints = createDefaultMeshPoints();
|
|
420
441
|
if (!Number.isFinite(config.backgroundMeshSoftness)) config.backgroundMeshSoftness = .62;
|
|
421
442
|
config.backgroundMeshSoftness = clamp01(config.backgroundMeshSoftness);
|
|
422
|
-
if (
|
|
443
|
+
if (!Number.isFinite(config.backgroundGradientAngle)) config.backgroundGradientAngle = 135;
|
|
423
444
|
if (typeof config.backgroundGradientSource !== "string") config.backgroundGradientSource = "stops";
|
|
424
445
|
if (typeof config.backgroundImageSource !== "string") config.backgroundImageSource = "vaporwave";
|
|
425
446
|
if (config.backgroundImageFit !== "contain" && config.backgroundImageFit !== "stretch" && config.backgroundImageFit !== "cover") config.backgroundImageFit = "cover";
|
|
426
|
-
if (
|
|
447
|
+
if (!Number.isFinite(config.backgroundImageZoom)) config.backgroundImageZoom = 1;
|
|
427
448
|
config.backgroundImageZoom = clamp(config.backgroundImageZoom, .1, 8);
|
|
428
449
|
if (!config.backgroundImagePosition) config.backgroundImagePosition = {
|
|
429
450
|
x: 0,
|
|
@@ -446,43 +467,46 @@ function ensureCamera(config) {
|
|
|
446
467
|
y: 0,
|
|
447
468
|
z: 0
|
|
448
469
|
};
|
|
449
|
-
if (
|
|
470
|
+
if (!Number.isFinite(config.cameraZoom)) config.cameraZoom = 1;
|
|
471
|
+
if (!CAMERA_FITS.includes(config.cameraFit)) config.cameraFit = "cover";
|
|
472
|
+
config.cameraMinVisibleWidth = typeof config.cameraMinVisibleWidth === "number" ? clamp(config.cameraMinVisibleWidth, 0, 1) : 0;
|
|
450
473
|
}
|
|
451
474
|
/** Backfill/repair a wave so the renderer can consume it (covers partial wave-model JSON). */
|
|
452
475
|
function normalizeWave(s) {
|
|
453
476
|
normalizeWaveColour(s);
|
|
454
|
-
if (
|
|
455
|
-
if (
|
|
477
|
+
if (!Number.isFinite(s.gradientAngle)) s.gradientAngle = 90;
|
|
478
|
+
if (!Number.isFinite(s.gradientShift)) s.gradientShift = .15;
|
|
456
479
|
if (typeof s.usePaletteTexture !== "boolean") s.usePaletteTexture = true;
|
|
457
480
|
if (typeof s.paletteSource !== "string") s.paletteSource = "hero";
|
|
458
481
|
if (typeof s.paletteEdgeColor !== "string") s.paletteEdgeColor = "#8e9dff";
|
|
459
|
-
if (
|
|
460
|
-
if (
|
|
461
|
-
if (
|
|
462
|
-
if (
|
|
463
|
-
if (
|
|
464
|
-
if (
|
|
465
|
-
if (
|
|
466
|
-
if (
|
|
482
|
+
if (!Number.isFinite(s.paletteEdgeAmount)) s.paletteEdgeAmount = .3;
|
|
483
|
+
if (!Number.isFinite(s.paletteDriftX)) s.paletteDriftX = 0;
|
|
484
|
+
if (!Number.isFinite(s.paletteDriftY)) s.paletteDriftY = 0;
|
|
485
|
+
if (!Number.isFinite(s.hueShift)) s.hueShift = 0;
|
|
486
|
+
if (!Number.isFinite(s.colorContrast)) s.colorContrast = 1;
|
|
487
|
+
if (!Number.isFinite(s.colorSaturation)) s.colorSaturation = 1;
|
|
488
|
+
if (!Number.isFinite(s.fiberCount)) s.fiberCount = 600;
|
|
489
|
+
if (!Number.isFinite(s.fiberStrength)) s.fiberStrength = .2;
|
|
467
490
|
if (!Array.isArray(s.noiseBands)) s.noiseBands = [];
|
|
468
|
-
|
|
469
|
-
if (
|
|
470
|
-
if (
|
|
471
|
-
if (
|
|
472
|
-
if (
|
|
473
|
-
if (
|
|
474
|
-
if (
|
|
475
|
-
if (
|
|
476
|
-
if (
|
|
477
|
-
if (
|
|
491
|
+
normalizeNoiseBands(s);
|
|
492
|
+
if (!Number.isFinite(s.texture)) s.texture = 0;
|
|
493
|
+
if (!Number.isFinite(s.creaseLight)) s.creaseLight = .6;
|
|
494
|
+
if (!Number.isFinite(s.creaseSharpness)) s.creaseSharpness = .589;
|
|
495
|
+
if (!Number.isFinite(s.creaseSoftness)) s.creaseSoftness = 1;
|
|
496
|
+
if (!Number.isFinite(s.sheen)) s.sheen = 0;
|
|
497
|
+
if (!Number.isFinite(s.roundness)) s.roundness = 0;
|
|
498
|
+
if (!Number.isFinite(s.iridescence)) s.iridescence = 0;
|
|
499
|
+
if (!Number.isFinite(s.edgeFade)) s.edgeFade = .04;
|
|
500
|
+
if (!Number.isFinite(s.edgeFeather)) s.edgeFeather = .1;
|
|
501
|
+
if (!Number.isFinite(s.depthTint)) s.depthTint = 0;
|
|
478
502
|
if (typeof s.depthTintColor !== "string") s.depthTintColor = "#0a2540";
|
|
479
503
|
if (!s.displaceFrequency) s.displaceFrequency = {
|
|
480
504
|
x: .003234,
|
|
481
505
|
y: .00799
|
|
482
506
|
};
|
|
483
|
-
if (
|
|
484
|
-
if (
|
|
485
|
-
if (
|
|
507
|
+
if (!Number.isFinite(s.displaceAmount)) s.displaceAmount = 6.051;
|
|
508
|
+
if (!Number.isFinite(s.detailFrequency)) s.detailFrequency = .04;
|
|
509
|
+
if (!Number.isFinite(s.detailAmount)) s.detailAmount = 0;
|
|
486
510
|
if (!s.twistFrequency) s.twistFrequency = {
|
|
487
511
|
x: -.055,
|
|
488
512
|
y: .077,
|
|
@@ -493,11 +517,18 @@ function normalizeWave(s) {
|
|
|
493
517
|
y: 5.85,
|
|
494
518
|
z: 6.33
|
|
495
519
|
};
|
|
520
|
+
if (typeof s.twistMotion !== "boolean") s.twistMotion = false;
|
|
521
|
+
if (!Number.isFinite(s.helixTurns)) s.helixTurns = 0;
|
|
522
|
+
if (!Number.isFinite(s.helixRadius)) s.helixRadius = 0;
|
|
523
|
+
if (!Number.isFinite(s.helixRoll)) s.helixRoll = 0;
|
|
524
|
+
if (!Number.isFinite(s.helixPhase)) s.helixPhase = 0;
|
|
496
525
|
if (typeof s.theme !== "string") s.theme = "solid";
|
|
497
|
-
if (
|
|
498
|
-
if (
|
|
499
|
-
if (
|
|
500
|
-
if (
|
|
526
|
+
if (!Number.isFinite(s.lineAmount)) s.lineAmount = 425;
|
|
527
|
+
if (!Number.isFinite(s.lineThickness)) s.lineThickness = 1;
|
|
528
|
+
if (!Number.isFinite(s.lineDerivativePower)) s.lineDerivativePower = .95;
|
|
529
|
+
if (!Number.isFinite(s.rungAmount)) s.rungAmount = 0;
|
|
530
|
+
if (!Number.isFinite(s.rungThickness)) s.rungThickness = 1;
|
|
531
|
+
if (!Number.isFinite(s.maxWidth)) s.maxWidth = 1232;
|
|
501
532
|
if (!s.position) s.position = {
|
|
502
533
|
x: 0,
|
|
503
534
|
y: 0,
|
|
@@ -514,44 +545,46 @@ function normalizeWave(s) {
|
|
|
514
545
|
z: 7
|
|
515
546
|
};
|
|
516
547
|
if (typeof s.blendMode !== "string") s.blendMode = "squared";
|
|
517
|
-
if (
|
|
518
|
-
if (
|
|
519
|
-
if (
|
|
548
|
+
if (!Number.isFinite(s.speed)) s.speed = .04;
|
|
549
|
+
if (!Number.isFinite(s.opacity)) s.opacity = 1;
|
|
550
|
+
if (!Number.isFinite(s.seed)) s.seed = 0;
|
|
520
551
|
if (s.interaction) normalizeWaveInteraction(s);
|
|
521
552
|
}
|
|
522
553
|
/** Backfill scene-level defaults (background/camera/post/lights/quality/mirror). */
|
|
523
554
|
function ensureSceneDefaults(config) {
|
|
524
555
|
normalizeBackground(config);
|
|
525
556
|
ensureCamera(config);
|
|
526
|
-
if (
|
|
557
|
+
if (!Number.isFinite(config.ambient)) config.ambient = .45;
|
|
527
558
|
if (!Array.isArray(config.lights)) config.lights = [];
|
|
528
|
-
|
|
529
|
-
if (
|
|
530
|
-
if (
|
|
531
|
-
if (
|
|
532
|
-
if (
|
|
533
|
-
if (
|
|
534
|
-
if (
|
|
535
|
-
if (
|
|
536
|
-
if (
|
|
537
|
-
if (
|
|
538
|
-
if (
|
|
539
|
-
if (
|
|
540
|
-
if (
|
|
541
|
-
if (
|
|
542
|
-
if (
|
|
543
|
-
if (
|
|
544
|
-
if (
|
|
545
|
-
if (
|
|
546
|
-
if (
|
|
547
|
-
if (
|
|
548
|
-
if (
|
|
549
|
-
if (
|
|
550
|
-
if (
|
|
551
|
-
if (
|
|
559
|
+
normalizeLights(config);
|
|
560
|
+
if (!Number.isFinite(config.quality)) config.quality = 1;
|
|
561
|
+
if (!Number.isFinite(config.dprMax)) config.dprMax = 2;
|
|
562
|
+
if (!Number.isFinite(config.grain)) config.grain = 1.1;
|
|
563
|
+
if (!Number.isFinite(config.blur)) config.blur = .02;
|
|
564
|
+
if (!Number.isFinite(config.blurSamples)) config.blurSamples = 6;
|
|
565
|
+
if (!Number.isFinite(config.bloomStrength)) config.bloomStrength = 0;
|
|
566
|
+
if (!Number.isFinite(config.bloomRadius)) config.bloomRadius = .4;
|
|
567
|
+
if (!Number.isFinite(config.bloomThreshold)) config.bloomThreshold = .85;
|
|
568
|
+
if (!Number.isFinite(config.dither)) config.dither = 0;
|
|
569
|
+
if (!Number.isFinite(config.ditherScale)) config.ditherScale = 2;
|
|
570
|
+
if (!Number.isFinite(config.ditherSteps)) config.ditherSteps = 4;
|
|
571
|
+
if (!Number.isFinite(config.innerLight)) config.innerLight = 0;
|
|
572
|
+
if (!Number.isFinite(config.innerLightDensity)) config.innerLightDensity = .5;
|
|
573
|
+
if (!Number.isFinite(config.innerLightDecay)) config.innerLightDecay = .95;
|
|
574
|
+
if (!Number.isFinite(config.innerLightX)) config.innerLightX = .5;
|
|
575
|
+
if (!Number.isFinite(config.innerLightY)) config.innerLightY = .15;
|
|
576
|
+
if (!Number.isFinite(config.halftone)) config.halftone = 0;
|
|
577
|
+
if (!Number.isFinite(config.halftoneCell)) config.halftoneCell = 6;
|
|
578
|
+
if (!Number.isFinite(config.halftoneAngle)) config.halftoneAngle = .4;
|
|
579
|
+
if (!Number.isFinite(config.heatmap)) config.heatmap = 0;
|
|
580
|
+
if (!Number.isFinite(config.paperTexture)) config.paperTexture = 0;
|
|
581
|
+
if (!Number.isFinite(config.paperTextureScale)) config.paperTextureScale = 2;
|
|
582
|
+
if (!Number.isFinite(config.halftoneCmyk)) config.halftoneCmyk = 0;
|
|
583
|
+
if (!Number.isFinite(config.halftoneCmykCell)) config.halftoneCmykCell = 6;
|
|
552
584
|
if (typeof config.showCameraRig !== "boolean") config.showCameraRig = false;
|
|
553
585
|
if (typeof config.paused !== "boolean") config.paused = false;
|
|
554
|
-
if (
|
|
586
|
+
if (!Number.isFinite(config.timeOffset)) config.timeOffset = 0;
|
|
587
|
+
if (!Number.isFinite(config.loopSeconds)) config.loopSeconds = 0;
|
|
555
588
|
if (typeof config.mirrorH !== "boolean") config.mirrorH = false;
|
|
556
589
|
if (typeof config.mirrorV !== "boolean") config.mirrorV = false;
|
|
557
590
|
}
|
|
@@ -560,6 +593,45 @@ function clampNumber(v, min, max, dflt) {
|
|
|
560
593
|
const n = Number(v);
|
|
561
594
|
return Number.isFinite(n) ? clamp(n, min, max) : dflt;
|
|
562
595
|
}
|
|
596
|
+
/** Coerce an untrusted field to a finite number, falling back to `dflt`. Deliberately does NOT
|
|
597
|
+
* clamp — it repairs broken values without reinterpreting out-of-range ones that a saved config
|
|
598
|
+
* may legitimately rely on. */
|
|
599
|
+
function num(v, dflt) {
|
|
600
|
+
const n = Number(v);
|
|
601
|
+
return Number.isFinite(n) ? n : dflt;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Repair the ELEMENTS of the scene's `lights` and a wave's `noiseBands`. Both arrays are
|
|
605
|
+
* type-checked where they're backfilled, but their entries never were — and the studio binds every
|
|
606
|
+
* field below directly, so one hand-authored `"lights": [{}]` was enough to make the whole control
|
|
607
|
+
* panel unbuildable. Entries that aren't objects at all are dropped.
|
|
608
|
+
*/
|
|
609
|
+
function normalizeLights(config) {
|
|
610
|
+
config.lights = config.lights.filter((l) => typeof l === "object" && l !== null);
|
|
611
|
+
for (const l of config.lights) {
|
|
612
|
+
if (typeof l.position !== "object" || l.position === null) l.position = { ...DEFAULT_LIGHT_POSITION };
|
|
613
|
+
l.position.x = num(l.position.x, DEFAULT_LIGHT_POSITION.x);
|
|
614
|
+
l.position.y = num(l.position.y, DEFAULT_LIGHT_POSITION.y);
|
|
615
|
+
l.position.z = num(l.position.z, DEFAULT_LIGHT_POSITION.z);
|
|
616
|
+
if (typeof l.color !== "string") l.color = "#ffffff";
|
|
617
|
+
l.intensity = num(l.intensity, 1);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
function normalizeNoiseBands(s) {
|
|
621
|
+
s.noiseBands = s.noiseBands.filter((b) => typeof b === "object" && b !== null);
|
|
622
|
+
const d = createNoiseBand();
|
|
623
|
+
for (const b of s.noiseBands) {
|
|
624
|
+
b.startX = num(b.startX, d.startX);
|
|
625
|
+
b.endX = num(b.endX, d.endX);
|
|
626
|
+
b.startY = num(b.startY, d.startY);
|
|
627
|
+
b.endY = num(b.endY, d.endY);
|
|
628
|
+
b.feather = num(b.feather, d.feather);
|
|
629
|
+
b.strength = num(b.strength, d.strength);
|
|
630
|
+
b.frequency = num(b.frequency, d.frequency);
|
|
631
|
+
b.colorAttenuation = num(b.colorAttenuation, d.colorAttenuation);
|
|
632
|
+
b.parabolaPower = num(b.parabolaPower, d.parabolaPower);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
563
635
|
/** True for a valid interaction source string: a built-in name or a non-empty `custom:<name>`. */
|
|
564
636
|
function isInteractionSource(v) {
|
|
565
637
|
return typeof v === "string" && (INTERACTION_SOURCE_NAMES.includes(v) || v.startsWith("custom:") && v.length > 7);
|
|
@@ -634,6 +706,6 @@ function ensureStudioConfig(input) {
|
|
|
634
706
|
return config;
|
|
635
707
|
}
|
|
636
708
|
//#endregion
|
|
637
|
-
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, normalizeSceneInteraction, normalizeWave, normalizeWaveInteraction, resizeWaves };
|
|
709
|
+
export { CAMERA_FITS, DEFAULT_LIGHT_POSITION, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeSceneInteraction, normalizeWave, normalizeWaveInteraction, resizeWaves };
|
|
638
710
|
|
|
639
711
|
//# sourceMappingURL=model.js.map
|