vireglass 2.3.0 → 3.0.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 +37 -6
- package/dist/{adapters-DugC6Qri.d.cts → adapters--bXcGvhW.d.cts} +5 -3
- package/dist/{adapters-BfeAopGO.d.ts → adapters-BlMhc6pI.d.ts} +5 -3
- package/dist/{chunk-MMPK6DCK.js → chunk-CPSIY2DZ.js} +58 -8
- package/dist/chunk-CPSIY2DZ.js.map +1 -0
- package/dist/{chunk-PM2SWCSF.js → chunk-RWUR3OV6.js} +26 -6
- package/dist/chunk-RWUR3OV6.js.map +1 -0
- package/dist/{chunk-CTTZNZFV.js → chunk-VLDD655U.js} +3 -3
- package/dist/{chunk-CTTZNZFV.js.map → chunk-VLDD655U.js.map} +1 -1
- package/dist/{chunk-H5QTBF67.js → chunk-YAN2OK4H.js} +31 -38
- package/dist/chunk-YAN2OK4H.js.map +1 -0
- package/dist/{chunk-SLRPHDV7.js → chunk-Z4FKYY47.js} +3 -3
- package/dist/{chunk-SLRPHDV7.js.map → chunk-Z4FKYY47.js.map} +1 -1
- package/dist/dom.cjs +180 -55
- package/dist/dom.cjs.map +1 -1
- package/dist/dom.d.cts +72 -25
- package/dist/dom.d.ts +72 -25
- package/dist/dom.js +116 -54
- package/dist/dom.js.map +1 -1
- package/dist/{geometry-RhKd7CM5.d.cts → geometry-ip9WSlZv.d.cts} +1 -1
- package/dist/{geometry-GvXtt-_S.d.ts → geometry-pabkYV7w.d.ts} +1 -1
- package/dist/{group-model-z4N27qsC.d.ts → group-model-CQiI5cCe.d.ts} +1 -1
- package/dist/{group-model-DwtixGhG.d.cts → group-model-DX2_W9J_.d.cts} +1 -1
- package/dist/index.cjs +114 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -8
- package/dist/index.d.ts +13 -8
- package/dist/index.js +5 -5
- package/dist/law.cjs +56 -6
- package/dist/law.cjs.map +1 -1
- package/dist/law.d.cts +54 -4
- package/dist/law.d.ts +54 -4
- package/dist/law.js +1 -1
- package/dist/{material-CnUmdKg1.d.ts → material-D9m1EN47.d.cts} +9 -6
- package/dist/{material-CnUmdKg1.d.cts → material-D9m1EN47.d.ts} +9 -6
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/reference.d.cts +2 -2
- package/dist/reference.d.ts +2 -2
- package/dist/reference.js +2 -2
- package/dist/web/index.cjs +112 -40
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.d.cts +3 -3
- package/dist/web/index.d.ts +3 -3
- package/dist/web/index.js +4 -4
- package/docs/dom.md +49 -3
- package/docs/law.md +1 -1
- package/package.json +1 -1
- package/src/adapters.ts +3 -1
- package/src/dom/body.ts +93 -39
- package/src/dom/index.ts +49 -20
- package/src/dom/probe.ts +1 -1
- package/src/dom/rim.ts +63 -28
- package/src/dom/style-hold.ts +8 -0
- package/src/law.ts +54 -4
- package/src/lens-shader.ts +26 -33
- package/src/material.ts +10 -7
- package/src/targets/glsl.ts +27 -2
- package/dist/chunk-H5QTBF67.js.map +0 -1
- package/dist/chunk-MMPK6DCK.js.map +0 -1
- package/dist/chunk-PM2SWCSF.js.map +0 -1
package/README.md
CHANGED
|
@@ -165,6 +165,35 @@ your choreography; the material only knows how two silhouettes join.
|
|
|
165
165
|
| `--vireglass-radius`, `--vireglass-radius-min` | for concentric children (§11) |
|
|
166
166
|
| `--vireglass-ink`, `--vireglass-tint`, `--vireglass-body-density` | the model's raw numbers |
|
|
167
167
|
|
|
168
|
+
### Telling the glass what is behind it
|
|
169
|
+
|
|
170
|
+
A host that knows its backdrop — an image, a gradient, a video's average colour — supplies it as a
|
|
171
|
+
`BackdropSample` and changes it whenever the backdrop changes, without re-attaching:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
import { attachGlass, statsFromSamples } from 'vireglass/dom';
|
|
175
|
+
|
|
176
|
+
const cover = statsFromSamples([{ r: 198, g: 92, b: 40 }, { r: 210, g: 120, b: 60 }]); // 0..255
|
|
177
|
+
const glass = attachGlass(el, { material, sample: cover });
|
|
178
|
+
glass.setSample(statsFromSamples([{ r: 20, g: 30, b: 60 }])); // next track
|
|
179
|
+
glass.setSample(null); // back to probing declared backgrounds
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The sample drives the body, the rim's colour and strength, the ink polarity and the shadow.
|
|
183
|
+
`statsFromSamples` turns a handful of colours into one; a hand-written object with `luma`,
|
|
184
|
+
`busy`, `lo`, `hi`, `r`, `g`, `b` (0..1) works as well.
|
|
185
|
+
|
|
186
|
+
### Body and rim
|
|
187
|
+
|
|
188
|
+
The body is drawn as the element's `background-color` on the refracting path too, composed from the
|
|
189
|
+
same law the shaders apply (tint, matte lift, the medium's pull, concentration of light), so over a
|
|
190
|
+
dark surround it reads lighter than the surround. `--vireglass-body-color` carries the same colour.
|
|
191
|
+
Pass `body: false` if the host paints the background itself.
|
|
192
|
+
|
|
193
|
+
The rim is a thin light line round the whole contour, brighter toward the key light, over a dark
|
|
194
|
+
edge. Its strength follows the lightness around the element (faint over dark, plain over bright)
|
|
195
|
+
and the medium's reflectance; it is no longer white at full strength everywhere.
|
|
196
|
+
|
|
168
197
|
The three system accessibility settings are read from the browser's own media queries and
|
|
169
198
|
followed while the page is open — reduced transparency frosts the glass, increased contrast takes
|
|
170
199
|
the element by its silhouette, reduced motion holds it still. A system setting outranks the
|
|
@@ -185,8 +214,8 @@ Three things to know before you reach for it:
|
|
|
185
214
|
same optics — detected by measurement, never by user-agent string.
|
|
186
215
|
- **There is no pixel probe, so adaptation reads declared styles.** The grid under the element is
|
|
187
216
|
sampled through `elementsFromPoint` and the background stack is composited. That is exact for
|
|
188
|
-
colour-defined surfaces and blind to images, video and canvas —
|
|
189
|
-
since your app already knows its cover-art accent.
|
|
217
|
+
colour-defined surfaces and blind to images, video and canvas — tell it what is there instead,
|
|
218
|
+
since your app already knows its cover-art accent (below).
|
|
190
219
|
|
|
191
220
|
## Android / React Native
|
|
192
221
|
|
|
@@ -225,9 +254,11 @@ described by a plane, so density rises as a whole, driven by the lightness range
|
|
|
225
254
|
real glass does with roughness: it stops being a window.
|
|
226
255
|
|
|
227
256
|
**Presence.** Over a uniform backdrop there is nothing to refract, and the glass honestly
|
|
228
|
-
disappears. Correct for a piece of background, wrong for a control. `presence`
|
|
229
|
-
|
|
230
|
-
dark,
|
|
257
|
+
disappears. Correct for a piece of background, wrong for a control. `presence` is a floor on the
|
|
258
|
+
body's density, tinted toward light and fading as the backdrop nears the light pole, so there is no
|
|
259
|
+
veil or seam near white: the reference body is lighter than its surroundings from dark video to sky, whatever the glyph on
|
|
260
|
+
it. What sends it to the ink's own pole is the ink's legibility over texture, a system setting
|
|
261
|
+
(reduced transparency, increased contrast), or a density past `BODY.inkPoleTo`.
|
|
231
262
|
|
|
232
263
|
Ink polarity is decided by the **cost of holding it**, not by contrast: how much body density
|
|
233
264
|
it would take to keep light ink legible. Above the threshold the element would stop being glass
|
|
@@ -321,7 +352,7 @@ timestamp so the claims can be checked. No Apple code, assets or footage is incl
|
|
|
321
352
|
distributed with this package.
|
|
322
353
|
|
|
323
354
|
Every calibrated number lives in `src/law.ts` with its provenance, and `check:law` names the ones
|
|
324
|
-
that have none: **
|
|
355
|
+
that have none: **51 of 130** today. See [`docs/law.md`](docs/law.md).
|
|
325
356
|
|
|
326
357
|
## License
|
|
327
358
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { V as VireGlassGeometry } from './geometry-
|
|
2
|
-
import { V as VireGlassOptics, o as VireGlassDebugMode } from './material-
|
|
1
|
+
import { V as VireGlassGeometry } from './geometry-ip9WSlZv.cjs';
|
|
2
|
+
import { V as VireGlassOptics, o as VireGlassDebugMode } from './material-D9m1EN47.cjs';
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/** A second shape joined to the element by a smooth union: the geometry that merges and growth
|
|
5
|
+
* transitions are built on (a capsule drawn into a drop, then grown into its menu). Offsets from
|
|
6
|
+
* the element's centre and sizes in dp. */
|
|
5
7
|
type VireGlassMorph = {
|
|
6
8
|
offsetX: number;
|
|
7
9
|
offsetY: number;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { V as VireGlassGeometry } from './geometry-
|
|
2
|
-
import { V as VireGlassOptics, o as VireGlassDebugMode } from './material-
|
|
1
|
+
import { V as VireGlassGeometry } from './geometry-pabkYV7w.js';
|
|
2
|
+
import { V as VireGlassOptics, o as VireGlassDebugMode } from './material-D9m1EN47.js';
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/** A second shape joined to the element by a smooth union: the geometry that merges and growth
|
|
5
|
+
* transitions are built on (a capsule drawn into a drop, then grown into its menu). Offsets from
|
|
6
|
+
* the element's centre and sizes in dp. */
|
|
5
7
|
type VireGlassMorph = {
|
|
6
8
|
offsetX: number;
|
|
7
9
|
offsetY: number;
|
|
@@ -23,7 +23,47 @@ var RIM = {
|
|
|
23
23
|
*/
|
|
24
24
|
darkEdge: 0.22,
|
|
25
25
|
/** Width of the hairline, CSS px. "About a point, not a band and not a bevel" (§2). */
|
|
26
|
-
widthPx: 1
|
|
26
|
+
widthPx: 1,
|
|
27
|
+
/**
|
|
28
|
+
* How strongly the line is drawn, by the lightness around the element. The rim is a reflection
|
|
29
|
+
* of the surroundings, so it is faint over a dark one and plain over a bright one, not white at
|
|
30
|
+
* full strength everywhere (§2, §6).
|
|
31
|
+
*
|
|
32
|
+
* MEASURED off the controls of A 2:17 (over dark video), M 2:52 (over sky) and A 2:18 (over
|
|
33
|
+
* grass), as the brightest rim bin's OKLab lightness over the surround's: 0.24 to 0.27 over dark,
|
|
34
|
+
* 0.25 over sky, 0.37 over grass. Through the DOM path onto the same backdrops these land within
|
|
35
|
+
* 0.08 of the smooth rows (docs/dom.md); a line white at full strength reads 0.70 to 0.80 over the
|
|
36
|
+
* dark frames.
|
|
37
|
+
*/
|
|
38
|
+
strengthDark: 0.37,
|
|
39
|
+
strengthLight: 0.95,
|
|
40
|
+
/**
|
|
41
|
+
* The surround lightness between which the strength moves from one to the other. UNMEASURED: the
|
|
42
|
+
* two ends are, and the controls in between were not looked for.
|
|
43
|
+
*/
|
|
44
|
+
ambientFrom: 0.15,
|
|
45
|
+
ambientTo: 0.45,
|
|
46
|
+
/**
|
|
47
|
+
* The share of the line's width, from the silhouette inward, that the light line leaves to the
|
|
48
|
+
* dark edge. UNMEASURED: without it the line covers the dark edge and an element over a bright
|
|
49
|
+
* flat backdrop loses the one mark that holds it there.
|
|
50
|
+
*/
|
|
51
|
+
lineInset: 0.3,
|
|
52
|
+
/**
|
|
53
|
+
* The share of the peak that the line keeps along the whole contour, away from the key light.
|
|
54
|
+
* MEASURED: the reference line goes all the way round. Its dimmest bin sits 0.06 to 0.12 of
|
|
55
|
+
* lightness over the surround on the dark frames, against a peak of 0.24 to 0.27 (A 2:17).
|
|
56
|
+
*/
|
|
57
|
+
contour: 0.3,
|
|
58
|
+
/** How far the line's colour is lifted from the surroundings' toward white. UNMEASURED: the
|
|
59
|
+
* lens always had 0.75 here, and this is that figure with a name. */
|
|
60
|
+
whiteShare: 0.75,
|
|
61
|
+
/**
|
|
62
|
+
* The reflectance (`fresnelStrength`) at which the line is drawn at the strengths above: that
|
|
63
|
+
* of ordinary glass, index 1.5. A denser medium does not draw it brighter than they say; a
|
|
64
|
+
* thinner one draws it fainter. UNMEASURED.
|
|
65
|
+
*/
|
|
66
|
+
reflectanceRef: 0.7
|
|
27
67
|
};
|
|
28
68
|
var BODY = {
|
|
29
69
|
/** Lightness ceiling for the body when legibility is loose. */
|
|
@@ -37,10 +77,14 @@ var BODY = {
|
|
|
37
77
|
busyEdge: 0.75,
|
|
38
78
|
/** Fine texture the body has to stop being a window for. */
|
|
39
79
|
groundSpan: 0.06,
|
|
40
|
-
/**
|
|
41
|
-
*
|
|
42
|
-
|
|
43
|
-
|
|
80
|
+
/**
|
|
81
|
+
* The spread under the element at which the ink's legibility requirement starts to bind, and the
|
|
82
|
+
* spread where it binds fully. Over a flat backdrop polarity flips before the ink drowns, so
|
|
83
|
+
* the body does not darken under a light glyph there; over texture it does. UNMEASURED: the
|
|
84
|
+
* reference shows a lighter body under a white glyph over sky, nothing about texture.
|
|
85
|
+
*/
|
|
86
|
+
inkBusyFrom: 0.02,
|
|
87
|
+
inkBusyTo: 0.12,
|
|
44
88
|
/** Ceiling on any single demand, so one requirement cannot make the glass opaque on its own. */
|
|
45
89
|
maxDemand: 0.92,
|
|
46
90
|
/**
|
|
@@ -49,7 +93,13 @@ var BODY = {
|
|
|
49
93
|
* separation is the ceiling, and glass at `maxDemand` has stopped being a window. Better to
|
|
50
94
|
* separate as far as transparency allows and no further. UNMEASURED.
|
|
51
95
|
*/
|
|
52
|
-
presenceDemand: 0.5
|
|
96
|
+
presenceDemand: 0.5,
|
|
97
|
+
/**
|
|
98
|
+
* The density at which the body is wholly the ink's own pole. Between `presenceDemand` and this
|
|
99
|
+
* the body moves from the light a thin body carries to the pole: a density no presence floor
|
|
100
|
+
* reaches is one the ink's legibility, or a system setting, has asked for. UNMEASURED.
|
|
101
|
+
*/
|
|
102
|
+
inkPoleTo: 0.8
|
|
53
103
|
};
|
|
54
104
|
var SHADOW = {
|
|
55
105
|
/**
|
|
@@ -459,5 +509,5 @@ var SCROLL_EDGE = {
|
|
|
459
509
|
};
|
|
460
510
|
|
|
461
511
|
export { ACCENT, ACCESSIBILITY, BEVEL, BODY, DERIVE, DISPERSION, EPSILON, INK, LENS, MEDIUM, MORPH, RIM, SCALE, SCROLL_EDGE, SHADOW, SHADOW_DENSITY, SHADOW_GAP, SIZE, SPECTRAL, SPRING, TOUCH };
|
|
462
|
-
//# sourceMappingURL=chunk-
|
|
463
|
-
//# sourceMappingURL=chunk-
|
|
512
|
+
//# sourceMappingURL=chunk-CPSIY2DZ.js.map
|
|
513
|
+
//# sourceMappingURL=chunk-CPSIY2DZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/law.ts"],"names":[],"mappings":"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","file":"chunk-CPSIY2DZ.js","sourcesContent":["/**\n * THE LAW: every calibrated number the material obeys, in one place.\n *\n * A value here is written ONCE and read by everything that needs it — the shader text interpolates\n * it, the JS twins import it. The alternative is what this file replaces: the same number typed\n * into a GLSL string and again into a TypeScript function, drifting apart the first time one side\n * is tuned. `VG_FALLOFF` already worked this way; the rest now does too.\n *\n * Each entry carries where it came from. `§N` is a section of `docs/reference.md`, which is itself\n * drawn from Apple's public sessions; `M 2:32` and the like are the timestamps those sections cite.\n * A number with no provenance is marked as such, and that marking is a debt, not a decoration.\n *\n * Structural constants — 0, 1, 0.5, 2, π — are not laws and do not belong here.\n */\n\n/** Rim optics: the silhouette's light (§2). */\nexport const RIM = {\n /**\n * The key-light lobe's exponent. Two opposing arcs come out of one expression, which is why\n * they are opposed rather than placed: `pow(max(facing, 0), n) + 0.45 * pow(max(-facing, 0), n)`.\n *\n * MEASURED off two close-ups of real end caps, frames/crops/cap158-left and cap158-right. The\n * bright arc's full width at half its peak is 53 degrees in the first and 30 in the second; this\n * exponent's own width is `2·acos(0.5^(1/n))`, so they imply 6 and 20. It was 3, whose arc is 75\n * degrees wide — broader than either measurement, and the error is in the same direction in both.\n *\n * 6 is the conservative end: it reproduces the wider crop exactly, and a broader arc is the more\n * forgiving error. One exponent cannot serve both, and that is not noise — a specular lobe's\n * width depends on the light's angular size as well as the surface, and those are two different\n * scenes. The model has no term for the light's size.\n */\n lobeExponent: 6,\n /** The far arc, weaker than the one facing the light (§2). */\n opposingArc: 0.45,\n /**\n * The dark edge along the whole silhouette, as a multiplicative darkening. iOS 27 made it a\n * layer of its own, and it does not rule out the highlight sitting on top of it (§2).\n */\n darkEdge: 0.22,\n /** Width of the hairline, CSS px. \"About a point, not a band and not a bevel\" (§2). */\n widthPx: 1,\n /**\n * How strongly the line is drawn, by the lightness around the element. The rim is a reflection\n * of the surroundings, so it is faint over a dark one and plain over a bright one, not white at\n * full strength everywhere (§2, §6).\n *\n * MEASURED off the controls of A 2:17 (over dark video), M 2:52 (over sky) and A 2:18 (over\n * grass), as the brightest rim bin's OKLab lightness over the surround's: 0.24 to 0.27 over dark,\n * 0.25 over sky, 0.37 over grass. Through the DOM path onto the same backdrops these land within\n * 0.08 of the smooth rows (docs/dom.md); a line white at full strength reads 0.70 to 0.80 over the\n * dark frames.\n */\n strengthDark: 0.37,\n strengthLight: 0.95,\n /**\n * The surround lightness between which the strength moves from one to the other. UNMEASURED: the\n * two ends are, and the controls in between were not looked for.\n */\n ambientFrom: 0.15,\n ambientTo: 0.45,\n /**\n * The share of the line's width, from the silhouette inward, that the light line leaves to the\n * dark edge. UNMEASURED: without it the line covers the dark edge and an element over a bright\n * flat backdrop loses the one mark that holds it there.\n */\n lineInset: 0.3,\n /**\n * The share of the peak that the line keeps along the whole contour, away from the key light.\n * MEASURED: the reference line goes all the way round. Its dimmest bin sits 0.06 to 0.12 of\n * lightness over the surround on the dark frames, against a peak of 0.24 to 0.27 (A 2:17).\n */\n contour: 0.3,\n /** How far the line's colour is lifted from the surroundings' toward white. UNMEASURED: the\n * lens always had 0.75 here, and this is that figure with a name. */\n whiteShare: 0.75,\n /**\n * The reflectance (`fresnelStrength`) at which the line is drawn at the strengths above: that\n * of ordinary glass, index 1.5. A denser medium does not draw it brighter than they say; a\n * thinner one draws it fainter. UNMEASURED.\n */\n reflectanceRef: 0.7,\n} as const;\n\n/** How the medium stands between the backdrop and the eye (§3). */\nexport const BODY = {\n /** Lightness ceiling for the body when legibility is loose. */\n capLoose: 0.62,\n /** …and when it is strict. The model is calibrated at these two, not in between (E-43). */\n capTight: 0.38,\n /** The two ends the body tints toward. Glass has no colour of its own (HIG \"Color\"). */\n tintDark: 0.07,\n tintLight: 0.94,\n /** How far the backdrop's spread reaches past its mean when deciding what the ink needs. */\n busyEdge: 0.75,\n /** Fine texture the body has to stop being a window for. */\n groundSpan: 0.06,\n /**\n * The spread under the element at which the ink's legibility requirement starts to bind, and the\n * spread where it binds fully. Over a flat backdrop polarity flips before the ink drowns, so\n * the body does not darken under a light glyph there; over texture it does. UNMEASURED: the\n * reference shows a lighter body under a white glyph over sky, nothing about texture.\n */\n inkBusyFrom: 0.02,\n inkBusyTo: 0.12,\n /** Ceiling on any single demand, so one requirement cannot make the glass opaque on its own. */\n maxDemand: 0.92,\n /**\n * And a tighter one for presence specifically. It is a FLOOR on visibility, not a licence to go\n * opaque: wherever the body's tint sits close to the backdrop, the density that would deliver the\n * separation is the ceiling, and glass at `maxDemand` has stopped being a window. Better to\n * separate as far as transparency allows and no further. UNMEASURED.\n */\n presenceDemand: 0.5,\n /**\n * The density at which the body is wholly the ink's own pole. Between `presenceDemand` and this\n * the body moves from the light a thin body carries to the pole: a density no presence floor\n * reaches is one the ink's legibility, or a system setting, has asked for. UNMEASURED.\n */\n inkPoleTo: 0.8,\n} as const;\n\n/** Shadow (§4). The density law lives in `geometry.ts`; these are its measured ends. */\nexport const SHADOW = {\n /**\n * Measured off reference frames 711–723: 4.0% over a flat light backdrop, 19.9% over text.\n *\n * REPRODUCED independently off frames/verify/m-707 and m-711 — the same capsule in the same scene\n * at the same scale, once below the text over plain background and once scrolled up over it.\n * Comparing the rows under the capsule against the same rows far to either side, so the\n * background's own gradient cancels: 3.5% and 19.2%, peaking about 140 px below the capsule.\n * Two passes, different frames and a different method, agreeing to within 0.7 of a point.\n */\n alphaFlat: 0.04,\n alphaBusy: 0.199,\n} as const;\n\n/** Dispersion: the channels refract at different indices (§1, Cauchy). */\nexport const DISPERSION = {\n /** Red bends less than the base index, blue more — and not symmetrically. */\n redShift: -0.4,\n blueShift: 0.6,\n} as const;\n\n/** Response to a finger (§5). The springs and decay live in `touch-response.ts`; these shape the\n * field the finger deforms. */\nexport const TOUCH = {\n /**\n * The element grows under pressure (M 3:51; HIG: interactive \"expands\").\n *\n * CHECKED, not measured: across frames/knob/switch-press, a 4x5 sheet of one switch being\n * pressed, the control's extent runs 764 px in the three settled frames and 787 to 822 under the\n * finger — 3 to 8 per cent. This value sits inside that, which is a consistency check and not a\n * derivation: the measurement cannot separate the knob stretching from the track growing, and the\n * settled frames agreeing to the pixel is the only part of it that is tight.\n */\n pressGrow: 0.06,\n /** The ridge around the contact blob: material displaced from under the finger ends up\n * somewhere. Without it the shape balloons, and a dense medium does not do that. */\n ridge: 0.42,\n /** How far press pulls the field toward the finger. */\n pressPull: 0.16,\n /** Wavelength and decay of the ripple, as fractions of its span. Short and quick: in a dense\n * medium ripples are frequent and small; long shallow swells are water. */\n waveLength: 0.17,\n waveDecay: 0.7,\n /** The ripple's span, as a multiple of the contact radius. It has to reach the far edge, or it\n * reads as jitter under the finger rather than a wave across the surface. */\n waveSpan: 2,\n /**\n * Contact radius as a fraction of the element's half-size. UNMEASURED: §5 says the response has\n * to be visible UNDER the finger, which is why the blob is wide, but gives no figure.\n */\n radiusOfHalfSize: 1.1,\n /**\n * Wave impulse as a fraction of the travel limit. UNMEASURED: the core carries the ripple's\n * decay and frequency, but nothing says how hard a finger strikes it.\n */\n waveOfTravel: 0.18,\n /** The lift-off ring is weaker than the one from touching down (§5). UNMEASURED ratio. */\n releaseWave: 0.6,\n /**\n * How far a glow reaches past the element it started on, as a multiple of that element's\n * half-size. UNMEASURED: §5 says the glow spreads \"onto any Liquid Glass elements nearby\" and\n * names no distance. This is the reach at which a neighbour in a tab bar still lights and one\n * across the screen does not.\n */\n glowReach: 3,\n /**\n * How much of an element's \"active\" state a touch alone raises — the fraction the glow and the\n * ink lift ride on when nothing else is driving them. UNMEASURED.\n *\n * It was a private constant on the Android surface whose comment said it matched the web's, and\n * nothing enforced that: the web renderer takes `active` from the host as a field and has no\n * constant to match. Two renderers agreeing by a comment is exactly what this file is for.\n */\n activeOnTouch: 0.3,\n} as const;\n\n/** The bevel's profile — spherical, like a cap: `t / sqrt(1 - t² · k)`, clamped. A plain `t²` kept\n * the slope near zero through most of the bevel and then shot up at the edge, so the optics\n * bunched into a ring and the element read as a puck. */\nexport const BEVEL = {\n sphere: 0.94,\n floor: 0.02,\n slopeMax: 3.2,\n} as const;\n\n/** Where two shapes meet head-on their normals cancel exactly and the blend lands on zero. */\nexport const EPSILON = 1e-5;\n\n/** What the medium does to light passing through it (§3). */\nexport const MEDIUM = {\n /** The lightness content under the glass is pulled toward, and how hard. The medium both removes\n * light and mixes scattered light back in: over a light backdrop the body darkens, over a dark\n * one it lightens. A layer that only ADDED light pushed the body the wrong way over light\n * backdrops (M 2:35). */\n luma: 0.4,\n pull: 0.07,\n /** Ambient light reaching the body on top of that pull. */\n ambientSpill: 0.01,\n /** Glass concentrates light: the body is a touch lighter than what lies beneath it (M 2:29). */\n concentrate: 0.01,\n /** Scattered light on a frosted element is an ADDITION over the backdrop, not a fraction of the\n * way to the tint — a fraction goes to zero once the backdrop reaches the tint's lightness and\n * darkens past it (docs/benchmarks.md). */\n matteLift: 0.1,\n /** How far scattering reaches, as a fraction of the gather radius, over a busy backdrop under\n * ink. Set against the reference: structure under the capsule fades by a factor of nine or ten\n * there, not thirty (M 11:47). */\n scatterMax: 0.2,\n scatterBase: 0.1,\n /** The response to structure saturates early: what competes with the ink is not the AREA of\n * foreign text but the fact of it — a line under a tile measures about 0.12 of busy. */\n structureGain: 20,\n /** Tint density in the flat middle; the bevel multiplies it by the edge density. */\n bodyDensityFlat: 0.19,\n} as const;\n\n/** The lens itself (§1). */\nexport const LENS = {\n /** Backdrop compression at the rim goes to infinity at the silhouette, so the radius correction\n * is capped. */\n footprintMax: 1.8,\n /** Same reason, for the profile's slope. */\n slopeMax: 40,\n /** Every thin film on glass sits around this index. */\n filmIor: 1.35,\n /** Reference channel wavelengths in nm — red, green, blue — for diffraction and interference. */\n lambdaR: 610,\n lambdaG: 550,\n lambdaB: 460,\n} as const;\n\n/** The shadow's shape in the gap under the element (§4). */\nexport const SHADOW_GAP = {\n /** How much weaker the shadow is right at the outline than under the middle of the gap, and what\n * fraction of the offset it takes to reach full depth. In the reference the shadow's minimum\n * sits BELOW the rim, not at it. */\n light: 0.76,\n reach: 0.2,\n /** Fraction of the ambient hue the shadow carries. It has to stay a shadow, not a coloured blob. */\n tint: 1,\n} as const;\n\n/** Ink drawn inside the material (§5). */\nexport const INK = {\n /** Depth beneath the surface, dp. The normal displaces it by exactly this much at the rim. */\n depth: 4,\n /** Defocus under a finger, as a FRACTION of the touch blob rather than a length: the blob\n * already arrives in device units, so this follows both screen density and element size. At\n * rest it is zero and the sample stays a single one. */\n defocus: 0.08,\n} as const;\n\n/** The spectral edge (§1): dispersion, diffraction and interference are one cause — a dependence\n * on wavelength — and one hue multiplier, normalised to its own mean. They colour the reflection\n * without brightening it. */\nexport const SPECTRAL = {\n /** How far into the bevel the fringes reach, as a fraction of it measured from the silhouette.\n * Diffraction is an EDGE effect; past this the bevel is plain glass. */\n diffractionOnset: 0.45,\n /**\n * The same window for interference, which had none. §2 puts the iridescence on the opposing ARC —\n * \"the opposing arc carries an iridescent fringe\" — and a fringe is a thing at an edge.\n *\n * Without it the term applies wherever the surface angle puts it, and across the inner bevel the\n * angle barely changes, so a film that should read as a fringe reads as a flat colour filter.\n * Measured on a 680x64 bar with the default preset: a multiplier of 1.68, 1.26, 0.05 held CONSTANT\n * from a third of the way through the bevel to its inner edge — red boosted by two thirds and blue\n * removed outright. UNMEASURED window; wider than diffraction's because interference reaches\n * further in than the fringes do.\n */\n iridescenceOnset: 0.25,\n /** Fringes across the bevel. Denser as the bevel sharpens, which is what the phase term does. */\n diffractionFringes: 4,\n /** The channel the hue is measured against, nm. Green is the eye's own reference. */\n referenceLambda: 550,\n} as const;\n\n/** Tinting (§7): a colour that is a property of the medium, not a fill over it. */\nexport const ACCENT = {\n /** The tone the colour takes over a dark backdrop and over a light one. \"A range of tones mapped\n * to content brightness underneath\", rather than one flat colour. */\n deep: 0.88,\n light: 1.08,\n /** How much of the medium the colour occupies by default. Short of one, because the content has\n * to keep coming through: a fill \"breaks the visual character of Liquid Glass\" (@17:03). */\n amount: 0.8,\n} as const;\n\n/**\n * The system accessibility settings (§9). They are modifiers on the material's layers, so their\n * calibration belongs in the law next to the layers they move — not as private constants in the\n * module that happens to apply them.\n */\nexport const ACCESSIBILITY = {\n /** Frost floor under reduced transparency: the glass gets \"frostier\" (§9). */\n frostMinDp: 14,\n /**\n * Body floor under reduced transparency. B @10:07 is the stricter statement of the two — a bar\n * \"gets a background when reduce transparency is enabled\" — so this is a background, not a\n * thicker haze. UNMEASURED: neither source gives a figure.\n */\n obscureMin: 0.82,\n /** Under increased contrast the element is \"predominantly black or white\" (§9): the body goes to\n * whichever pole its ink already points at, at a density that leaves it reading as that pole. */\n contrastDensity: 0.94,\n /** …and separates from the backdrop on its own, whatever is behind it. */\n contrastPresence: 0.5,\n /**\n * The contrasting border §9 asks for, in units of lightness away from the body's own pole. The\n * rim stops taking its colour from the environment and takes it from the element instead — an\n * ambient hairline is not a contrasting border. UNMEASURED.\n */\n contrastRim: 0.85,\n /**\n * Press that survives reduced motion. §9 \"decreases the intensity of some effects and disables\n * any elastic properties\": the spring and the ripple are elastic and go to zero, the press is an\n * ease that carries the element's light, so it is damped rather than removed — an element that\n * stops answering a finger altogether is not reduced motion, it is no feedback. UNMEASURED.\n */\n stillPress: 0.6,\n} as const;\n\n/**\n * Morphing (§5). Apple's design leads name their own model for it out loud (S 1:25:30, fireside\n * chat with the Apple design team): \"we also had a refresher from biology class. It's called\n * mitosis and meiosis. When these things are coming together or materialization and\n * dematerialization and morphing.\"\n *\n * Which is the right physics and not a metaphor: a dividing cell is one body throughout. It never\n * has a moment of being two bodies that happen to touch, and it never pops a second body into\n * existence beside the first. The neck is continuous from start to finish, and it is the neck that\n * carries the whole transition.\n */\nexport const MORPH = {\n /**\n * Width of the neck between two lobes, as a fraction of the smaller one's half-size. On a large\n * element the bridge has to be wider or the seam keeps a sharp corner, which a liquid does not\n * have. UNMEASURED: the reference names the behaviour and no figure.\n */\n neck: 0.35,\n /**\n * How far past merely touching a completed fusion goes, as a multiple of the bridge width that\n * just closes the gap. At exactly the bridge width the two surfaces meet at a point, which is two\n * shapes kissing rather than one body.\n *\n * MEASURED, frames/morph/s03, the one panel where the necks are still attached. Apple's\n * silhouette there: lobes 72 and 82 px with their centres 92 px apart — 15 px of space between\n * their surfaces — joined by a neck 28 px tall. Rendering the same geometry through this model\n * gives 29.0. The measurement discriminates: 1.2 gives 23.5, 1.4 gives 33.5, and at 1.0 the neck\n * closes to nothing.\n */\n fuse: 1.3,\n} as const;\n\n/**\n * CAUSE TO EFFECT. The normalisations that turn what the medium IS — index, thickness, bevel,\n * roughness, film — into what a renderer needs. They lived as literals inside `optics.ts`, half of\n * them not even named, which meant the heart of the model was the one part of it the law could not\n * see.\n *\n * Almost all of them are stylisations and say so. Literal physical values put every effect at the\n * threshold of visibility: real glass reflects four per cent at normal incidence, and a rim drawn\n * at four per cent is not a rim. Each is monotonic in its own cause, so \"denser medium, brighter\n * rim\" holds whatever the constant is; what no one has measured is where on the scale Apple's\n * material sits. Hence UNMEASURED on nearly all of it — that is the honest state of this file, and\n * the gate now says so out loud instead of the number hiding in a module.\n */\nexport const DERIVE = {\n /** Physical 4% reflectance into the render's working range. UNMEASURED. */\n fresnelGain: 17.5,\n /** Schlick's exponent. A constant of the approximation, not a knob. */\n fresnelExponent: 5,\n /** The index at which ray bending is at full strength, measured from air. UNMEASURED. */\n iorFullBend: 0.6,\n /** Magnification behind a plane-parallel plate, per dp of thickness. UNMEASURED. */\n magnifyPerDp: 0.006,\n /** In ordinary glasses the Abbe number falls as the index rises, so dispersion grows with it.\n * UNMEASURED slope. */\n dispersionPerIor: 1.1,\n /** Index spread between the red and blue channels. The physical value (~0.01 at an Abbe number\n * of 55) is at the threshold of visibility. UNMEASURED. */\n iorSpreadPerDispersion: 0.045,\n /** Beer–Lambert absorption per dp of path. UNMEASURED. */\n absorbPerDp: 0.017,\n /** Ceiling on how much denser the bevel may read than the body. The rim's ray travels further\n * through the medium; past this the rim is milk rather than glass. UNMEASURED. */\n edgeDensityMax: 4,\n /**\n * Frost ceiling, dp. Deliberately low: blur is a SUPPORTING device here, and past about a dozen\n * dp letters under the glass stop being letters. Separating content from ink is the body's job.\n *\n * UNMEASURED, and an attempt is recorded here so the next one does not repeat it.\n * frames/verify/m-711 has the same paragraph sharp beside the capsule and behind it, which looks\n * like the measurement: match the blur that takes the sharp patch's detail down to the glassed\n * one's. It gives 2 to 3 px at that frame's scale, and the figure is not trustworthy. Detail is\n * lost to three things at once there — the frost, the magnification (`refractionScale` lowers the\n * text's spatial frequency without blurring anything), and the body veiling its contrast — and\n * the method cannot separate them. What it does bound is the total: the text behind the glass\n * keeps 68% of the detail it has beside it.\n *\n * A clean measurement needs a frame where the glass does not magnify, or a feature of known size\n * behind it to normalise the magnification out.\n */\n blurMaxDp: 12,\n /** Highlight width from a smooth surface and from a fully rough one, as a specular exponent.\n * UNMEASURED. */\n specularPowerSmooth: 160,\n specularPowerRough: 8,\n /** How much roughness damps the highlight's brightness. UNMEASURED. */\n specularRoughDamping: 0.6,\n /**\n * The medium's own hue by density. Water absorbs red and skews cool, ordinary glass is nearly\n * neutral with a faint green, dense high-index media skew warm. Glass has no colour of its own\n * (HIG \"Color\"), so this is never set by hand — it is tied to the index.\n */\n hueCool: [0.86, 1.0, 1.08],\n hueNeutral: [1.0, 1.02, 0.99],\n hueWarm: [1.08, 1.0, 0.88],\n /** The index range the hue sweeps across, and where it starts. UNMEASURED. */\n hueFromIor: 1.2,\n hueSpan: 0.55,\n /** Body lightness follows reflectance: the denser the medium, the more of its surroundings it\n * returns. UNMEASURED base and slope. */\n bodyLiftBase: 0.34,\n bodyLiftPerF0: 2.4,\n /**\n * How far past the bevel the rim gathers its surroundings, as a multiple of the bevel. At a\n * grazing angle the eye receives the vicinity rather than what is under the glass, and without\n * this, glass on an empty black background has no source at all. UNMEASURED.\n */\n gatherPerBevel: 4,\n /** Floor and ceiling on that radius, dp. Unbounded, a wide bevel gathers from fifty dp away and\n * a READABLE copy of the neighbourhood appears inside the glass — eight samples cannot blur a\n * disc that large (E-33, E-37). */\n gatherMinDp: 4,\n gatherMaxDp: 28,\n /** The body's own density from absorption through its thickness. Small: the body only moves\n * toward the tint under the legibility requirement, otherwise it dims the content. UNMEASURED. */\n bodyDensity: 0.05,\n /**\n * Rim light on the physical scale rather than the rim highlight's. `fresnelGain` saturates at one\n * for dense media, and applying it here too would raise body lightness harder than the glass had\n * just separated it from the ink — eating its own legibility work. UNMEASURED.\n */\n edgeLightGain: 6,\n /**\n * Iridescence rides the REFLECTED ray, so it is tied to Fresnel and has no knob of its own.\n *\n * MEASURED against how coloured a real rim is. In frames/crops the brightest rim pixels have a\n * channel spread — (max − min) / max — of 13% in cap158-left and 20% in cap158-right, against\n * their own backdrops' 14.6% and 14.3%. So the rim is no more coloured than the scene it stands\n * in: §2's \"iridescent fringe\" adds a few points at most, and in one of the two crops it adds\n * nothing measurable.\n *\n * At 1.6 this put ordinary glass at 0.945 iridescence and the spectral multiplier reached a 97%\n * channel spread — red boosted by two thirds and blue removed. Rendered over a coloured page that\n * is a neon tube around every control, which is what sent me to measure it.\n */\n iridescencePerFresnel: 0.042,\n /** Edge diffraction shares dispersion's λ-dependence, at the stylised amplitude: at the physical\n * one the fringes are not visible at all. MEASURED with the same crops and the same target as\n * iridescence — on its own it was reaching a 93% channel spread. */\n diffractionPerDispersion: 0.05,\n /** Multiple internal reflection — the denser the medium, the more light it circulates and the\n * more it is tinted by its surroundings. UNMEASURED slope. */\n colorPickupPerFresnel: 0.9,\n /**\n * The ceiling, which keeps it a medium rather than a fill.\n *\n * MEASURED, frames/concentric/shadow-zoom: the same sidebar twice, over a yellow cover and over a\n * pink one, so the difference between the panels is the pickup with the sidebar's own colour\n * subtracted out. Sampling a band of rows clear of the text, the bar's hue offset against the\n * cover's own reaches 0.31 of it at the edge nearest the content in the yellow panel and 0.28 in\n * the pink one. It was 0.42, which nothing measured.\n *\n * Reality is not uniform: the bleed falls to nothing about ninety pixels in, and both panels show\n * the same falloff. The DOM path has one backdrop sample per element and cannot reproduce that,\n * so it applies the ceiling across the whole body — right at the edge, too strong in the middle.\n * That limit is in docs/dom.md rather than papered over with a lower number.\n */\n colorPickupMax: 0.3,\n} as const;\n\n/**\n * SIZE AND SHADOW. What changes when an element gets bigger, and how dense its shadow reads over\n * what is behind it. 219 @6:36: as glass \"flexes and morphs to larger sizes, it simulates a\n * thicker material with deeper shadows and more pronounced lensing and refraction\".\n */\nexport const SIZE = {\n /** Half-size at which bevel and thickness are specified as given; they grow as its square root\n * from there. UNMEASURED reference size. */\n referenceDp: 24,\n /** Floor and ceiling on that growth. UNMEASURED. */\n gainMin: 0.8,\n gainMax: 2.4,\n /** A bevel wider than this fraction of the half-size breaks the SDF — the roundings converge in\n * the middle. A property of the geometry, not a taste. */\n maxBevelFraction: 0.65,\n} as const;\n\n/**\n * Shadow density by what is behind the element. 219 @11:47, verbatim: it \"increases the opacity of\n * its shadow when it is over text… lowers the opacity of its shadow when it is over a solid light\n * background.\" About content BEHIND the element, so one value per element rather than a field over\n * the shadow's area. Endpoints checked against the reference by the depth of the dip under the\n * element (docs/benchmarks.md).\n */\nexport const SHADOW_DENSITY = {\n /** Over a flat background. */\n flat: 0.8,\n /** How fast it rises with structure behind the element, and how far it may rise. */\n busyGain: 6,\n busyRise: 1.2,\n /** Quantisation of the emitted value: a shadow that changes on every pixel of scroll re-lays-out\n * the native view every frame. */\n step: 0.05,\n} as const;\n\n/**\n * THE MEDIUM'S RESPONSE TO A FINGER (§5). Part of the material, exactly as `ior` is: keeping these\n * with the consumer would mean web and Android ended up with two different glasses under one name.\n *\n * Press is viscous — it dents shallow and releases slowly. Drag is thick: the body noticeably lags\n * the finger, travel is short, and on release it snaps back fast with almost no overshoot, because\n * a wide springy overshoot would be liquid jelly rather than dense glass.\n */\nexport const SPRING = {\n /** Stiffness and damping while the finger is down. UNMEASURED. */\n holdStiffness: 260,\n holdDamping: 46,\n /** …and after it lifts. Stiffer, and damped hard: a dense medium snaps back without ringing. */\n releaseStiffness: 420,\n releaseDamping: 34,\n /** Time constants for the press dent, in and out, seconds. UNMEASURED. */\n pressAttack: 0.07,\n pressRelease: 0.16,\n /** How fast the touch point catches up to the finger. Not instant: an instant jump tears the\n * deformation, and on rapid taps that reads as jitter. */\n pointFollow: 0.045,\n /** …and how fast the element registers as being touched at all, which is what the glow rides. */\n activeFollow: 0.09,\n /** The ripple decays within a quarter second rather than oscillating: in a viscous medium ripples\n * are short and weak. UNMEASURED. */\n waveDecay: 0.22,\n waveTurnsPerSecond: 3,\n /** Ceilings on the accumulated ripple, as multiples of a single impulse. Impulses add up rather\n * than restart — restarting cuts a running wave mid-period, which is what jolts on rapid taps. */\n waveCapOnGrab: 1.6,\n waveCapOnRelease: 2,\n} as const;\n\n/** The user's clear-to-tinted preference (§3). */\nexport const SCALE = {\n /** Where on the scale the material stays exactly as it is by default. */\n default: 0.35,\n /** Body density at the fully tinted end: content under the glass has to be hidden. UNMEASURED. */\n tintedDensity: 0.9,\n} as const;\n\n/**\n * The scroll edge (§10). 219 @9:16: as content scrolls under a glass element \"the effect gently\n * DISSOLVES the content into the background, lifting the glass visually above the moving content,\n * and allowing floating elements like titles to always remain clear.\"\n *\n * 356 @11:32 says what it is not, and the first implementation here did exactly that: \"they don't\n * block or darken like overlays. They simply clarify where UI and content meet.\"\n */\nexport const SCROLL_EDGE = {\n /** How far the content has to slide under the panel for the effect to fully engage, dp. */\n engageDp: 24,\n /**\n * How far the blur reaches BELOW the bar's lower lip, as a multiple of the bar's own height.\n *\n * MEASURED, frames/scroll-edge/z576 at 320x413. High-frequency energy (mean |laplacian| per row,\n * which a brightness gradient in the content cannot fake) reads 1.4 through the zone under the\n * bar and 14 in the sharp content below — a factor of ten. The bar's lip is at y≈45 and the zone\n * runs to y≈130, so the reach is about 1.9 times the bar's own height.\n *\n * The same frame confirms 219 @9:22 in passing: the floating title sits inside that zone at 32,\n * twenty times its surroundings. Titles stay crisp while everything behind them dissolves, which\n * is why the effect belongs behind the glass in the stack rather than over the content.\n */\n reachOfBarHeight: 1.9,\n /** The blur the content dissolves into at the edge, dp. This is the effect itself — the content\n * goes out of focus into the background rather than being covered by anything. UNMEASURED. */\n dissolveBlurDp: 10,\n /**\n * Over dark content the glass turns dark and 219 @9:33 switches the effect \"to apply a subtle\n * dimming instead\". Dimming is a reduction in luminance: dark content under dark glass merges\n * with it, and pushing the content down is what lets the glass sit above it. UNMEASURED.\n */\n dimAlpha: 0.1,\n /** A pinned view under the panel (column headers) gets a flat band instead of a gradual fade\n * (219 @9:41) — \"a stronger, more opaque boundary\" (356 @12:12). UNMEASURED. */\n hardBlurDp: 14,\n hardAlpha: 0.18,\n} as const;\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { VG_SDF, VG_FALLOFF } from './chunk-
|
|
2
|
-
import { MEDIUM, SHADOW_GAP, INK } from './chunk-
|
|
1
|
+
import { VG_SDF, VG_FALLOFF } from './chunk-YAN2OK4H.js';
|
|
2
|
+
import { MEDIUM, SHADOW_GAP, INK } from './chunk-CPSIY2DZ.js';
|
|
3
3
|
|
|
4
4
|
// src/surface-shader.ts
|
|
5
5
|
var SURFACE_SHADER = `
|
|
@@ -332,6 +332,26 @@ function convertEvalCalls(src) {
|
|
|
332
332
|
out += src.slice(cursor);
|
|
333
333
|
return out;
|
|
334
334
|
}
|
|
335
|
+
function stripComments(src) {
|
|
336
|
+
let out = "";
|
|
337
|
+
let i = 0;
|
|
338
|
+
while (i < src.length) {
|
|
339
|
+
if (src.startsWith("//", i)) {
|
|
340
|
+
const end = src.indexOf("\n", i);
|
|
341
|
+
i = end < 0 ? src.length : end;
|
|
342
|
+
} else if (src.startsWith("/*", i)) {
|
|
343
|
+
const end = src.indexOf("*/", i + 2);
|
|
344
|
+
if (end < 0) throw new Error("vireglass/targets/glsl: unterminated block comment");
|
|
345
|
+
const stop = end + 2;
|
|
346
|
+
out += src.slice(i, stop).replace(/[^\n]/g, "");
|
|
347
|
+
i = stop;
|
|
348
|
+
} else {
|
|
349
|
+
out += src[i];
|
|
350
|
+
i += 1;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return out;
|
|
354
|
+
}
|
|
335
355
|
function convertReturns(mainBody) {
|
|
336
356
|
return mainBody.replace(/\breturn\s+([^;]+);/g, "fragColor = $1; return;");
|
|
337
357
|
}
|
|
@@ -362,7 +382,7 @@ function addPrologue(src) {
|
|
|
362
382
|
return "#version 300 es\nprecision highp float;\n\nout vec4 fragColor;\nuniform vec2 u_resolution;\n\n" + src;
|
|
363
383
|
}
|
|
364
384
|
function toGLSL(shaderSource) {
|
|
365
|
-
let out = shaderSource;
|
|
385
|
+
let out = stripComments(shaderSource);
|
|
366
386
|
out = convertEntryPoint(out);
|
|
367
387
|
out = convertShaderUniforms(out);
|
|
368
388
|
out = convertEvalCalls(out);
|
|
@@ -372,6 +392,6 @@ function toGLSL(shaderSource) {
|
|
|
372
392
|
return out;
|
|
373
393
|
}
|
|
374
394
|
|
|
375
|
-
export { SURFACE_SHADER, addPrologue, convertEntryPoint, convertEvalCalls, convertHalfTypes, convertReturns, convertShaderUniforms, convertVecTypes, toGLSL };
|
|
376
|
-
//# sourceMappingURL=chunk-
|
|
377
|
-
//# sourceMappingURL=chunk-
|
|
395
|
+
export { SURFACE_SHADER, addPrologue, convertEntryPoint, convertEvalCalls, convertHalfTypes, convertReturns, convertShaderUniforms, convertVecTypes, stripComments, toGLSL };
|
|
396
|
+
//# sourceMappingURL=chunk-RWUR3OV6.js.map
|
|
397
|
+
//# sourceMappingURL=chunk-RWUR3OV6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/surface-shader.ts","../src/targets/glsl.ts"],"names":[],"mappings":"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","file":"chunk-RWUR3OV6.js","sourcesContent":["import { INK, MEDIUM, SHADOW_GAP } from './law';\nimport { VG_FALLOFF, VG_SDF } from './sdf';\n\n/** Order matches `DEBUG_MODES` in `material.ts`: the index goes straight into `u_debug`. */\nexport const SURFACE_SHADER = `\nuniform shader u_icon;\n/** App-colored content ON the glass — cover art, a thumbnail. A separate layer from the ink\n * mask: that one is single-channel and colored by polarity, this one carries its own color\n * as-is. */\nuniform shader u_overlay;\n\nuniform float2 u_center;\nuniform float2 u_halfSize;\nuniform float u_corner;\nuniform float u_bevel;\nuniform float u_thickness;\nuniform float2 u_morphOffset;\nuniform float2 u_morphHalf;\nuniform float u_morphCorner;\nuniform float u_morphK;\nuniform float2 u_morph2Offset;\nuniform float2 u_morph2Half;\nuniform float u_morph2Corner;\n\nuniform float u_press;\nuniform float u_active;\n/* 0 — the element sinks in under the finger, 1 — it rises into glass (reference §5). */\nuniform float u_lift;\n\nuniform float u_edgeDensity;\nuniform float u_dispersion;\nuniform float u_refraction;\nuniform float4 u_tint;\nuniform float u_shadow;\nuniform float u_shadowReach;\nuniform float3 u_ambient;\nuniform float u_presence;\nuniform float u_progress;\nuniform float u_appear;\nuniform float u_debug;\n\nuniform float u_iconOn;\nuniform float u_overlayOn;\nuniform float u_iconScale;\nuniform float4 u_inkIdle;\nuniform float4 u_inkActive;\nuniform float2 u_touch;\nuniform float2 u_pull;\nuniform float u_touchPress;\nuniform float u_touchRadius;\nuniform float2 u_wave;\n\n${VG_SDF}\n\nconst float VG_FALLOFF = ${VG_FALLOFF};\n\n// Tint density in the flat middle; at the bevel it's multiplied by u_edgeDensity.\nconst float VG_BODY_DENSITY = ${MEDIUM.bodyDensityFlat};\n/* Fraction of the ambient hue in the shadow. The shadow has to stay a shadow, not a colored\n * blob. */\nconst float VG_SHADOW_TINT = ${SHADOW_GAP.tint}.0;\n// How much weaker the shadow is right at the outline than under the middle of the gap, and what\n// fraction of the offset it takes to reach full depth. In the reference the shadow's minimum\n// sits below the rim, not right at it.\nconst float VG_GAP_LIGHT = ${SHADOW_GAP.light};\nconst float VG_GAP_REACH = ${SHADOW_GAP.reach};\n/* Depth of the ink beneath the surface, dp. The normal displaces it by exactly this much right\n * at the rim. */\nconst float VG_INK_DEPTH = ${INK.depth}.0;\n/* Ink defocus under the finger (reference §6): a glyph loses its edge and drowns in milk. Given\n * as a FRACTION of the touch blob, not a number: the blob already arrives in device units, so\n * the radius automatically follows both screen density and the element's size. At rest it's\n * zero, and the sample stays a single one. */\nconst float VG_INK_DEFOCUS = ${INK.defocus};\n\nhalf4 vgPack(half3 c, float a) { return half4(c * half(a), half(a)); }\n\n// What bleeds into the shadow is the COLOR of the surroundings, not its brightness: the hue's own\n// lightness is subtracted out of it, otherwise the shadow pales and loses depth instead of\n// warming up. Strength is set by how much light there is around: over a nearly black backdrop\n// there's nothing to bleed into the shadow.\n// JS twin in shadow-tint.ts, which the DOM renderer reads: same derivation, same reason.\nfloat3 vgShadowTint(float3 ambient) {\n float peak = max(max(ambient.r, ambient.g), max(ambient.b, 0.001));\n float3 hue = ambient / peak;\n float lumaWeights = dot(ambient, float3(0.2126, 0.7152, 0.0722));\n return (hue - dot(hue, float3(0.2126, 0.7152, 0.0722))) * lumaWeights;\n}\n\nhalf3 vgHeat(float v) {\n float x = clamp(v, 0.0, 1.0);\n return half3(half(clamp(x * 2.2 - 0.2, 0.0, 1.0)),\n half(clamp(1.0 - abs(x - 0.5) * 2.2, 0.0, 1.0)),\n half(clamp(1.2 - x * 2.4, 0.0, 1.0)));\n}\n\nhalf4 main(float2 xy) {\n float2 p = vgTouchWarp(xy - u_center, u_touch, u_pull, u_touchPress, u_touchRadius, u_wave.x, u_wave.y);\n\n // Inverse deformation: stretch A along the pull vector, compression 1/sqrt(A) across it. This\n // exact law mirrors the transform of the live backdrop under the canvas, otherwise the two\n // would drift apart. There's NO motion geometry here — no pull, no press-driven bulge. All of\n // that is done by a single wrapper transform, the same one that carries the native lens: the\n // shader doesn't touch it, and two pipelines driving one motion would drift apart by a frame,\n // making the layers visible as separate.\n\n float halfMin = max(min(u_halfSize.x, u_halfSize.y), 1.0);\n float bevel = max(u_bevel, 1.0);\n\n float sd = vgScene(p, u_halfSize, u_corner, u_morphOffset, u_morphHalf, u_morphCorner, u_morphK,\n u_morph2Offset, u_morph2Half, u_morph2Corner);\n float t = vgBevelT(sd, bevel);\n float2 n = vgSceneNormal(p, u_halfSize, u_corner, u_morphOffset, u_morphHalf, u_morphCorner, u_morphK,\n u_morph2Offset, u_morph2Half, u_morph2Corner);\n float3 N = normalize(float3(n * vgBevelSlope(t), 1.0));\n\n // Progress is an active state turned into a field: the played fraction shines and glows\n // exactly as much as an active element shines whole. This does NOT touch ink: recoloring text\n // as the track advances would mean changing it mid-word.\n float lit = max(u_active, vgProgress(p, u_halfSize, u_progress));\n\n // The highlight and the rim are computed by the lens: reflection is a function of the\n // surroundings, and only the lens can see them.\n\n if (u_debug > 0.5) {\n float inMask = 1.0 - smoothstep(-1.0, 1.0, sd);\n if (u_debug < 1.5) {\n float band = abs(fract(sd / (halfMin * 0.22)) - 0.5) * 2.0;\n half3 c = sd < 0.0 ? half3(0.20, 0.62, 1.0) : half3(1.0, 0.42, 0.22);\n return vgPack(c * half(0.25 + band * 0.75), 1.0);\n }\n if (u_debug < 2.5) { return vgPack(half3(1.0), inMask); }\n if (u_debug < 3.5) { return vgPack(vgHeat(t), inMask); }\n // Fresnel now lives in the lens; here we show its SHAPE — how grazing the view is.\n if (u_debug < 4.5) { return vgPack(vgHeat(1.0 - clamp(N.z, 0.0, 1.0)), inMask); }\n if (u_debug < 5.5) {\n float push = pow(t, VG_FALLOFF) * u_refraction;\n return vgPack(vgHeat(push), inMask);\n }\n if (u_debug < 6.5) { return half4(0.0); }\n if (u_debug < 7.5) { return half4(0.0); }\n // Dispersion is computed by the lens; here is the field it grows along.\n if (u_debug < 8.5) { return vgPack(vgHeat(u_dispersion * t * t), inMask); }\n if (u_debug < 9.5) { return vgPack(half3(N * 0.5 + 0.5), inMask); }\n // spectral and adapt are shown by the LENS — the surface has to get out of the way.\n return half4(0.0);\n }\n\n // The shadow and the halo live OUTSIDE the shape: inside, their place is taken by the glass\n // itself. Separation from the content rests entirely on the shadow — without it the surface\n // would sit ON the picture, not above it. The shadow only lives at the rim and outside it: at\n // sd < −1 the outside multiplier is already zero. Computing it deep inside the shape would be\n // a wasted FULL SDF evaluation for every such pixel, and the body covers almost the whole\n // area. The branch here is on the coordinate, but only the threads right at the rim actually\n // diverge.\n float shade = 0.0;\n float halo = 0.0;\n if (sd > -1.0) {\n float outside = smoothstep(-1.0, 1.0, sd);\n // Under the finger a button moves TOWARD the backdrop, and the shadow tightens: it's exactly\n // the gap between the two. A control rising into glass (a switch knob, a slider handle —\n // reference §5) moves AWAY from it, and then the shadow spreads out instead. Only the app\n // knows which one applies, so the direction arrives as a fraction: 0 is sinking in, the way\n // every element behaved before this rule existed.\n float reach = u_shadowReach * (1.0 + mix(-0.25, 0.55, u_lift) * u_press);\n // The shadow is soft and wide, shifted downward (M 11:58): it's what holds the element's\n // separation from the content.\n float sdDrop = vgScene(p - float2(0.0, reach * 0.35), u_halfSize, u_corner,\n u_morphOffset, u_morphHalf, u_morphCorner, u_morphK,\n u_morph2Offset, u_morph2Half, u_morph2Corner);\n float amb = 1.0 - smoothstep(-reach * 0.3, reach, sdDrop);\n // THE GAP UNDER A RAISED ELEMENT IS LIT: right at the outline the shadow is WEAKER than\n // further down. Otherwise the minimum sits right against the silhouette — both parts of the\n // shadow are monotonic in distance.\n float gap = smoothstep(0.0, max(reach * VG_GAP_REACH, 1.0), max(sd, 0.0));\n shade = amb * amb * 0.13 * mix(VG_GAP_LIGHT, 1.0, gap) * outside * u_shadow * u_appear;\n halo = 1.0 - smoothstep(0.0, reach * 0.30, max(sd, 0.0));\n halo = halo * halo * lit * 0.10 * outside * u_appear;\n }\n\n if (sd > 1.0) {\n // Ambient light bleeds into the shadow (219 @8:22): it isn't a black hole, it's tinted by\n // whatever lies nearby. Otherwise an element over colored content would hang over a gray\n // blob.\n half3 spill = max(half3(half(halo)) + half3(vgShadowTint(u_ambient)) * half(VG_SHADOW_TINT * shade * (1.0 - halo)), half3(0.0));\n return half4(spill, half(halo + shade * (1.0 - halo)));\n }\n\n float density = u_tint.w;\n float inner = clamp(-sd / halfMin, 0.0, 1.0);\n half3 col = half3(u_tint.rgb);\n\n // The tint is LIGHT and weak, not dark: dark glass over dark content disappears, and has to be\n // held together with a heavy rim — which makes the surface read as a chrome bead. Tint density\n // at the bevel is a separate value: physically the bevel bends light harder, but it doesn't\n // get hazier. Coupled together, the two produced a milky ring around the whole outline.\n float body = mix(VG_BODY_DENSITY, VG_BODY_DENSITY * u_edgeDensity, smoothstep(0.10, 0.62, t)) * density;\n float vignette = smoothstep(0.15, 1.0, inner) * 0.19 * density;\n // There's NO density boost on press here. It was meant as \"the element stands out more under\n // the finger,\" but it pulls the body toward the tint, and the tint depends on polarity: over a\n // light backdrop (dark polarity) press ended up DARKENING the element. Presence is shown\n // instead by the vgTouchWarp field's deformation and the highlight blooming below — neither\n // cares about the sign of polarity.\n float a = max(body, vignette);\n\n // Active state does NOT touch the body's color. Mixing in a fixed gray here flipped the\n // effect's sign depending on the backdrop: over dark the element lightened, over light it\n // darkened, for the exact same state. Active state is shown instead by the highlight and rim\n // lighting above: neither cares about the backdrop.\n\n // THE ICON SITS BENEATH THE SURFACE, it isn't stuck on top of it: it used to be mixed in last,\n // over the highlight, and read as a flat sticker on volumetric glass. At the rim the normal\n // displaces it, like everything seen through the glass, while the highlight sits ON TOP — it\n // lives on the surface itself.\n //\n // There is NO shadow under the icon here. It used to be made from the difference of two offset\n // mask samples and produced a second outline along the edge — the icon's borders looked\n // ragged.\n // THE INK'S SHIFT IS SET BY ITS OWN DEPTH, NOT BY BEVEL WIDTH. The ink sits right at the\n // surface, and it's displaced by exactly the thin layer of glass above it — not by how wide a\n // bevel this particular piece of glass happens to have. It used to be a fraction of the bevel:\n // on thin glass the shift came out to 4 dp and everything lined up, but on thick glass it was\n // 8, and cover art inset from the edge by 6 stretched toward the rim and spilled past its\n // bounds. The ceiling is absolute: ink has one depth regardless of the glass.\n float2 inkShift = n * (min(u_bevel * 0.5, VG_INK_DEPTH) * t);\n float2 inkUv = u_center + p - inkShift;\n\n // INK LOSES SHARPNESS UNDER THE FINGER (reference §6): in the reference, a glyph under\n // pressure drowns in milk rather than simply getting lighter. This branch is uniform across\n // the whole element: press arrives as a uniform, so defocus costs nothing while the element\n // isn't being touched.\n float defocus = VG_INK_DEFOCUS * u_touchRadius * u_touchPress;\n float2 inkDx = float2(defocus, 0.0);\n float2 inkDy = float2(0.0, defocus);\n\n half4 ink;\n half4 over;\n if (defocus > 0.01) {\n // A cross around the center with double weight in the middle: the ink mask is\n // high-contrast, and four samples are enough for a stroke to stop holding its edge.\n ink = (u_icon.eval((inkUv - inkDx) * u_iconScale)\n + u_icon.eval((inkUv + inkDx) * u_iconScale)\n + u_icon.eval((inkUv - inkDy) * u_iconScale)\n + u_icon.eval((inkUv + inkDy) * u_iconScale)\n + u_icon.eval(inkUv * u_iconScale) * 2.0) * half(u_iconOn / 6.0);\n // Colored content lives in the same plane as the ink and blurs together with it: different\n // sharpness for two layers in one plane would read as a defect, not a press.\n over = (u_overlay.eval((inkUv - inkDx) * u_iconScale)\n + u_overlay.eval((inkUv + inkDx) * u_iconScale)\n + u_overlay.eval((inkUv - inkDy) * u_iconScale)\n + u_overlay.eval((inkUv + inkDy) * u_iconScale)\n + u_overlay.eval(inkUv * u_iconScale) * 2.0) * half(u_overlayOn * u_appear / 6.0);\n } else {\n ink = u_icon.eval(inkUv * u_iconScale) * half(u_iconOn);\n over = u_overlay.eval(inkUv * u_iconScale) * half(u_overlayOn * u_appear);\n }\n\n // The backing under the ink is held by the LENS (body density from the probe), not the\n // surface: legibility is a property of the material, and computing it here a second time\n // would give two different answers to one question.\n\n // Layers are composited premultiplied-over: in straight alpha, a nearly transparent dark body\n // used to pull the color of semi-transparent ink and the finger's light toward itself — letter\n // edges grayed out, pressing darkened things.\n half3 pm = col * half(a);\n half inkA = ink.g * half(mix(0.82, 1.0, u_active) * u_appear);\n half3 inkCol = mix(half3(u_inkIdle.rgb), half3(u_inkActive.rgb), half(u_active));\n pm = pm * (1.0 - inkA) + inkCol * inkA;\n a = a + float(inkA) * (1.0 - a);\n\n // COLORED CONTENT LIVES IN THE SAME PLACE AS THE INK — inside the material, at the same\n // coordinate. Otherwise deformation would carry them apart: on press, the title and artist\n // name would shake together with the surface while the cover art stayed put, because it used\n // to be a separate layer on top of the glass. Polarity doesn't touch it — it has its own\n // color, and there's nothing to swap it for.\n pm = pm * (1.0 - over.a) + over.rgb * over.a;\n a = a + float(over.a) * (1.0 - a);\n\n // Light from the finger is gathered by the LENS: it's a concentration of the surroundings, not\n // the glass's own whiteness. The surface is left with only what the glass spills outward — the\n // halo above.\n\n float mask = 1.0 - smoothstep(-1.0, 1.0, sd);\n pm = clamp(pm, half3(0.0), half3(1.0)) * half(mask);\n a *= mask;\n pm = max(pm + half3(vgShadowTint(u_ambient)) * half(VG_SHADOW_TINT * shade * (1.0 - a)), half3(0.0));\n return half4(pm, half(a + shade * (1.0 - a)));\n}\n`;\n","// WebGL2 target. Translates the same AGSL text (SDF + body, see `targets/agsl.ts`) into GLSL\n// ES 3.0. Each conversion rule is its own function with its own test in\n// `__tests__/targets.test.ts`. Three spots are NOT mechanical: the coordinate origin, units of\n// measure (settled in the web renderer's own setup, not here), and sampler filtering (also the\n// renderer's concern). What's here is text conversion only.\n\n/**\n * The sampler-size uniform's name, for translating `NAME.eval(coord)` → `texture(NAME, coord / SIZE)`.\n * A new uniform — there's no equivalent in AGSL: `.eval()` has the size implicitly known to the\n * shader, while GLSL's `texture()` needs a normalized coordinate. One per `uniform shader`/\n * `uniform sampler2D`, its name derived from the sampler's name rather than hardcoded.\n */\nfunction sizeUniformName(sampler: string): string {\n return sampler.startsWith('u_') ? `${sampler}Size` : `u_${sampler}Size`;\n}\n\n/** float2/float3/float4 → vec2/vec3/vec4. */\nexport function convertVecTypes(src: string): string {\n return src.replace(/\\bfloat([234])\\b/g, 'vec$1');\n}\n\n/** half/half2/half3/half4 → float/vec2/vec3/vec4, including half(x) → float(x) casts. */\nexport function convertHalfTypes(src: string): string {\n return src.replace(/\\bhalf([234])\\b/g, 'vec$1').replace(/\\bhalf\\b/g, 'float');\n}\n\n/** `uniform shader NAME;` → `uniform sampler2D NAME;` + a new `uniform vec2 <sizeUniform>;`. */\nexport function convertShaderUniforms(src: string): string {\n return src.replace(/uniform\\s+shader\\s+(\\w+)\\s*;/g, (_match, name: string) => {\n return `uniform sampler2D ${name};\\nuniform vec2 ${sizeUniformName(name)};`;\n });\n}\n\n/**\n * `NAME.eval(coord)` → `texture(NAME, (coord) / <sizeUniform>)` for each declared\n * `uniform sampler2D`. The bounds of `coord` are found by scanning parens, not with a regex: the\n * arguments are nested calls (`content.eval(vgInContent(s + dR - e))`) with commas and\n * parentheses inside.\n */\nexport function convertEvalCalls(src: string): string {\n const samplers = new Set<string>();\n const declRe = /uniform\\s+sampler2D\\s+(\\w+)\\s*;/g;\n for (let m = declRe.exec(src); m; m = declRe.exec(src)) samplers.add(m[1]);\n if (samplers.size === 0) return src;\n\n const callRe = /(\\w+)\\.eval\\(/g;\n let out = '';\n let cursor = 0;\n for (let m = callRe.exec(src); m; m = callRe.exec(src)) {\n const name = m[1];\n if (!samplers.has(name)) continue;\n const argStart = m.index + m[0].length;\n let depth = 1;\n let i = argStart;\n while (i < src.length && depth > 0) {\n if (src[i] === '(') depth += 1;\n else if (src[i] === ')') depth -= 1;\n i += 1;\n }\n const args = src.slice(argStart, i - 1);\n out += src.slice(cursor, m.index);\n out += `texture(${name}, (${args}) / ${sizeUniformName(name)})`;\n cursor = i;\n callRe.lastIndex = i;\n }\n out += src.slice(cursor);\n return out;\n}\n\n/** Comments are prose, and the rewrites here are regexes over raw text: a comment mentioning\n * \"return\" became one, and a brace in a comment ended the entry point early. Newlines inside\n * block comments survive, so a compile error still points at the source line. */\nexport function stripComments(src: string): string {\n let out = '';\n let i = 0;\n while (i < src.length) {\n if (src.startsWith('//', i)) {\n const end = src.indexOf('\\n', i);\n i = end < 0 ? src.length : end;\n } else if (src.startsWith('/*', i)) {\n const end = src.indexOf('*/', i + 2);\n if (end < 0) throw new Error('vireglass/targets/glsl: unterminated block comment');\n const stop = end + 2;\n out += src.slice(i, stop).replace(/[^\\n]/g, '');\n i = stop;\n } else {\n out += src[i];\n i += 1;\n }\n }\n return out;\n}\n\n/**\n * Early `return <expression>;` inside `main` → `fragColor = <expression>; return;`. Takes ONLY\n * the body of main (see `convertEntryPoint`) — ordinary `return`s in helper functions live\n * outside this text and are untouched. Expressions never contain a `;` of their own (shaders have\n * none — the only `;` lives inside `for(...)`, which isn't a return), so `[^;]+` is safe once\n * comments are gone (`stripComments`).\n */\nexport function convertReturns(mainBody: string): string {\n return mainBody.replace(/\\breturn\\s+([^;]+);/g, 'fragColor = $1; return;');\n}\n\nconst ENTRY_RE = /half4\\s+main\\s*\\(\\s*float2\\s+(\\w+)\\s*\\)\\s*\\{/;\n\n/**\n * `half4 main(float2 xy) { ... }` → `void main() { vec2 xy = <Y-flipped coordinate>; ...; }`.\n * AGSL gives `xy` in screen pixels, top-left origin, y down; `gl_FragCoord` is bottom-left\n * origin, y up. The flip via `u_resolution.y - gl_FragCoord.y` restores the original convention\n * (both coordinate systems center the pixel at `+0.5`, so no X shift or constant is needed).\n * `out vec4 fragColor` and `uniform vec2 u_resolution` are declared in the prologue (`addPrologue`).\n *\n * `xy` and `.eval()` are flipped together — but only inside a shader THIS function translates. A\n * `VireGlassBackdropPass` (`web/renderer.ts`) that reads its own previous frame from an FBO is raw\n * `gl_FragCoord`, unflipped; reusing AGSL-style helpers there needs an explicit flip of its own.\n */\nexport function convertEntryPoint(src: string): string {\n const match = ENTRY_RE.exec(src);\n if (!match) {\n throw new Error('vireglass/targets/glsl: entry point \"half4 main(float2 xy)\" not found');\n }\n const bodyStart = match.index + match[0].length;\n let depth = 1;\n let i = bodyStart;\n while (i < src.length && depth > 0) {\n if (src[i] === '{') depth += 1;\n else if (src[i] === '}') depth -= 1;\n i += 1;\n }\n const bodyEnd = i - 1;\n const param = match[1];\n const body = convertReturns(src.slice(bodyStart, bodyEnd));\n const head = src.slice(0, match.index);\n const tail = src.slice(i);\n return (\n `${head}void main() {\\n` +\n ` vec2 ${param} = vec2(gl_FragCoord.x, u_resolution.y - gl_FragCoord.y);\\n` +\n `${body}}${tail}`\n );\n}\n\n/** `#version 300 es` + `precision highp float;` — no equivalent in AGSL, WebGL2 requires them as\n * the file's first lines. `out vec4 fragColor` and `uniform vec2 u_resolution` ride along here\n * too: both new, with no counterpart in the source, needed by exactly one entry point\n * (`convertEntryPoint`). */\nexport function addPrologue(src: string): string {\n return (\n '#version 300 es\\n' +\n 'precision highp float;\\n\\n' +\n 'out vec4 fragColor;\\n' +\n 'uniform vec2 u_resolution;\\n\\n' +\n src\n );\n}\n\n/** Full translation AGSL/SkSL → GLSL ES 3.0. Order matters: the entry point is found by its\n * literal AGSL signature BEFORE type conversion, otherwise `half4 main(float2 xy)` can't be\n * found. */\nexport function toGLSL(shaderSource: string): string {\n let out = stripComments(shaderSource);\n out = convertEntryPoint(out);\n out = convertShaderUniforms(out);\n out = convertEvalCalls(out);\n out = convertVecTypes(out);\n out = convertHalfTypes(out);\n out = addPrologue(out);\n return out;\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CONFIRMATIONS, FLIP_LUMA, RETURN_LUMA } from './chunk-JNRZNMJJ.js';
|
|
2
|
-
import { SCALE, SCROLL_EDGE, ACCENT, ACCESSIBILITY, BODY, SPRING } from './chunk-
|
|
2
|
+
import { SCALE, SCROLL_EDGE, ACCENT, ACCESSIBILITY, BODY, SPRING } from './chunk-CPSIY2DZ.js';
|
|
3
3
|
|
|
4
4
|
// src/accent.ts
|
|
5
5
|
var clamp01 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
@@ -356,5 +356,5 @@ function createGroupState(sink) {
|
|
|
356
356
|
}
|
|
357
357
|
|
|
358
358
|
export { GLASS_SCALE_DEFAULT, GRADIENT, NO_ACCESSIBILITY, SCROLL_EDGE_ENGAGE_DP, SETTLE, SMOOTH, accentAmount, accentTone, aggregate, applyAccessibility, applyAppear, applyGlassScale, concentricFit, concentricInset, concentricRadius, contrastRimLuma, createDeform, createGroupState, elasticAllowed, probeValuesAt, raiseIntoGlass, resolveShape, scrollEdgeReachDp, scrollEdgeStrength, scrollEdgeStyle, shadowTint, smoothPlane };
|
|
359
|
-
//# sourceMappingURL=chunk-
|
|
360
|
-
//# sourceMappingURL=chunk-
|
|
359
|
+
//# sourceMappingURL=chunk-VLDD655U.js.map
|
|
360
|
+
//# sourceMappingURL=chunk-VLDD655U.js.map
|