vireglass 2.0.0 → 2.2.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 -1
- package/dist/{chunk-JF3FK254.js → chunk-2XJJO7SC.js} +4 -4
- package/dist/{chunk-JF3FK254.js.map → chunk-2XJJO7SC.js.map} +1 -1
- package/dist/{chunk-3PGYF2NK.js → chunk-CTTZNZFV.js} +3 -3
- package/dist/{chunk-3PGYF2NK.js.map → chunk-CTTZNZFV.js.map} +1 -1
- package/dist/{chunk-IQOBZGCF.js → chunk-H5QTBF67.js} +30 -32
- package/dist/chunk-H5QTBF67.js.map +1 -0
- package/dist/{chunk-TTOKBARR.js → chunk-MMPK6DCK.js} +10 -3
- package/dist/chunk-MMPK6DCK.js.map +1 -0
- package/dist/{chunk-P2ZOODXD.js → chunk-SLRPHDV7.js} +3 -3
- package/dist/{chunk-P2ZOODXD.js.map → chunk-SLRPHDV7.js.map} +1 -1
- package/dist/dom.cjs +79 -52
- package/dist/dom.cjs.map +1 -1
- package/dist/dom.d.cts +27 -4
- package/dist/dom.d.ts +27 -4
- package/dist/dom.js +48 -26
- package/dist/dom.js.map +1 -1
- package/dist/{group-model-8u4GKrUu.d.cts → group-model-DwtixGhG.d.cts} +15 -6
- package/dist/{group-model-vYSmEVia.d.ts → group-model-z4N27qsC.d.ts} +15 -6
- package/dist/index.cjs +28 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -5
- package/dist/law.cjs +8 -1
- package/dist/law.cjs.map +1 -1
- package/dist/law.d.cts +7 -0
- package/dist/law.d.ts +7 -0
- package/dist/law.js +1 -1
- package/dist/reference.js +2 -2
- package/dist/web/index.cjs +1 -2
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.js +4 -4
- package/docs/law.md +2 -2
- package/package.json +11 -9
- package/src/dom/body.ts +24 -9
- package/src/dom/displacement.ts +7 -2
- package/src/dom/index.ts +52 -11
- package/src/dom/probe.ts +27 -3
- package/src/law.ts +7 -0
- package/src/sdf.ts +44 -36
- package/dist/chunk-IQOBZGCF.js.map +0 -1
- package/dist/chunk-TTOKBARR.js.map +0 -1
package/dist/dom.cjs
CHANGED
|
@@ -60,7 +60,14 @@ var BODY = {
|
|
|
60
60
|
darkSideFrom: 0.42,
|
|
61
61
|
darkSideTo: 0.58,
|
|
62
62
|
/** Ceiling on any single demand, so one requirement cannot make the glass opaque on its own. */
|
|
63
|
-
maxDemand: 0.92
|
|
63
|
+
maxDemand: 0.92,
|
|
64
|
+
/**
|
|
65
|
+
* And a tighter one for presence specifically. It is a FLOOR on visibility, not a licence to go
|
|
66
|
+
* opaque: wherever the body's tint sits close to the backdrop, the density that would deliver the
|
|
67
|
+
* separation is the ceiling, and glass at `maxDemand` has stopped being a window. Better to
|
|
68
|
+
* separate as far as transparency allows and no further. UNMEASURED.
|
|
69
|
+
*/
|
|
70
|
+
presenceDemand: 0.5
|
|
64
71
|
};
|
|
65
72
|
var SHADOW = {
|
|
66
73
|
/**
|
|
@@ -438,36 +445,34 @@ function smin(a, b, k) {
|
|
|
438
445
|
const h = Math.min(Math.max(0.5 + 0.5 * (b - a) / k, 0), 1);
|
|
439
446
|
return b + (a - b) * h - k * h * (1 - h);
|
|
440
447
|
}
|
|
441
|
-
function sceneDistance(x, y, w, h, r, smoothing = 0,
|
|
442
|
-
|
|
443
|
-
if (smoothing <= 0
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
const dc = sdfRoundedRect(x - c.offsetX, y - c.offsetY, c.width, c.height, c.cornerRadius);
|
|
448
|
-
return smin(ab, dc, smoothing);
|
|
449
|
-
}
|
|
450
|
-
function sceneGradient(x, y, w, h, r, smoothing = 0, b, c) {
|
|
451
|
-
const na = sdfRoundedRectGradient(x, y, w, h, r);
|
|
452
|
-
if (smoothing <= 0 || !b || b.width <= 0) return na;
|
|
453
|
-
const bx = x - b.offsetX;
|
|
454
|
-
const by = y - b.offsetY;
|
|
455
|
-
const da = sdfRoundedRect(x, y, w, h, r);
|
|
456
|
-
const db = sdfRoundedRect(bx, by, b.width, b.height, b.cornerRadius);
|
|
457
|
-
const hb = Math.min(Math.max(0.5 + 0.5 * (db - da) / smoothing, 0), 1);
|
|
458
|
-
const nb = sdfRoundedRectGradient(bx, by, b.width, b.height, b.cornerRadius);
|
|
459
|
-
let gx = nb[0] + (na[0] - nb[0]) * hb;
|
|
460
|
-
let gy = nb[1] + (na[1] - nb[1]) * hb;
|
|
461
|
-
if (c && c.width > 0) {
|
|
462
|
-
const cx = x - c.offsetX;
|
|
463
|
-
const cy = y - c.offsetY;
|
|
464
|
-
const dab = smin(da, db, smoothing);
|
|
465
|
-
const dc = sdfRoundedRect(cx, cy, c.width, c.height, c.cornerRadius);
|
|
466
|
-
const hc = Math.min(Math.max(0.5 + 0.5 * (dc - dab) / smoothing, 0), 1);
|
|
467
|
-
const nc = sdfRoundedRectGradient(cx, cy, c.width, c.height, c.cornerRadius);
|
|
468
|
-
gx = nc[0] + (gx - nc[0]) * hc;
|
|
469
|
-
gy = nc[1] + (gy - nc[1]) * hc;
|
|
448
|
+
function sceneDistance(x, y, w, h, r, smoothing = 0, ...shapes) {
|
|
449
|
+
let d = sdfRoundedRect(x, y, w, h, r);
|
|
450
|
+
if (smoothing <= 0) return d;
|
|
451
|
+
for (const s of shapes) {
|
|
452
|
+
if (!s || s.width <= 0) continue;
|
|
453
|
+
d = smin(d, sdfRoundedRect(x - s.offsetX, y - s.offsetY, s.width, s.height, s.cornerRadius), smoothing);
|
|
470
454
|
}
|
|
455
|
+
return d;
|
|
456
|
+
}
|
|
457
|
+
function sceneGradient(x, y, w, h, r, smoothing = 0, ...shapes) {
|
|
458
|
+
let d = sdfRoundedRect(x, y, w, h, r);
|
|
459
|
+
let [gx, gy] = sdfRoundedRectGradient(x, y, w, h, r);
|
|
460
|
+
let blended = false;
|
|
461
|
+
if (smoothing > 0) {
|
|
462
|
+
for (const s of shapes) {
|
|
463
|
+
if (!s || s.width <= 0) continue;
|
|
464
|
+
const sx = x - s.offsetX;
|
|
465
|
+
const sy = y - s.offsetY;
|
|
466
|
+
const ds = sdfRoundedRect(sx, sy, s.width, s.height, s.cornerRadius);
|
|
467
|
+
const weight = Math.min(Math.max(0.5 + 0.5 * (ds - d) / smoothing, 0), 1);
|
|
468
|
+
const [nx, ny] = sdfRoundedRectGradient(sx, sy, s.width, s.height, s.cornerRadius);
|
|
469
|
+
gx = nx + (gx - nx) * weight;
|
|
470
|
+
gy = ny + (gy - ny) * weight;
|
|
471
|
+
d = smin(d, ds, smoothing);
|
|
472
|
+
blended = true;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
if (!blended) return [gx, gy];
|
|
471
476
|
gx += EPSILON;
|
|
472
477
|
gy += EPSILON;
|
|
473
478
|
const len = Math.hypot(gx, gy) || 1;
|
|
@@ -523,6 +528,7 @@ function renderDisplacementPixels(optics, geometry, dpr, state = {}) {
|
|
|
523
528
|
const rawScale = optics.refraction * thicknessDp(geometry, optics) * SCALE_GAIN;
|
|
524
529
|
const scale = Math.min(rawScale, MAX_SCALE);
|
|
525
530
|
const smoothing = state.smoothing ?? 0;
|
|
531
|
+
const shapes = [state.morph, state.morph2, ...state.morphs ?? []];
|
|
526
532
|
const data = new Uint8ClampedArray(width * height * 4);
|
|
527
533
|
const halfW = geometry.width / 2;
|
|
528
534
|
const halfH = geometry.height / 2;
|
|
@@ -532,7 +538,7 @@ function renderDisplacementPixels(optics, geometry, dpr, state = {}) {
|
|
|
532
538
|
const x = (px + 0.5) / density - halfW;
|
|
533
539
|
const i = (py * width + px) * 4;
|
|
534
540
|
const [wx, wy] = state.touch ? touchWarp(x, y, state.touch) : [x, y];
|
|
535
|
-
const sd = sceneDistance(wx, wy, geometry.width, geometry.height, geometry.cornerRadius, smoothing,
|
|
541
|
+
const sd = sceneDistance(wx, wy, geometry.width, geometry.height, geometry.cornerRadius, smoothing, ...shapes);
|
|
536
542
|
const t = Math.min(Math.max((sd + bevel) / bevel, 0), 1);
|
|
537
543
|
if (t <= 0 || scale <= 0) {
|
|
538
544
|
data[i] = 128;
|
|
@@ -541,7 +547,7 @@ function renderDisplacementPixels(optics, geometry, dpr, state = {}) {
|
|
|
541
547
|
data[i + 3] = 255;
|
|
542
548
|
continue;
|
|
543
549
|
}
|
|
544
|
-
const [gx, gy] = sceneGradient(wx, wy, geometry.width, geometry.height, geometry.cornerRadius, smoothing,
|
|
550
|
+
const [gx, gy] = sceneGradient(wx, wy, geometry.width, geometry.height, geometry.cornerRadius, smoothing, ...shapes);
|
|
545
551
|
const magnitude = bevelProfile(t) * (scale / 2);
|
|
546
552
|
const dx = -gx * magnitude;
|
|
547
553
|
const dy = -gy * magnitude;
|
|
@@ -930,10 +936,18 @@ function sampleColorAt(el, x, y) {
|
|
|
930
936
|
}
|
|
931
937
|
const composited = compositeLayers(layers);
|
|
932
938
|
if (composited) return composited;
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
939
|
+
return canvasColor(el);
|
|
940
|
+
}
|
|
941
|
+
function canvasColor(el) {
|
|
942
|
+
const doc = el.ownerDocument;
|
|
943
|
+
for (const node of [doc.documentElement, doc.body]) {
|
|
944
|
+
if (!node) continue;
|
|
945
|
+
const bg = parseColor(getComputedStyle(node).backgroundColor);
|
|
946
|
+
if (bg && bg.a > 1e-3) return bg.a >= 0.999 ? bg : compositeLayers([bg, WHITE]);
|
|
947
|
+
}
|
|
948
|
+
return WHITE;
|
|
936
949
|
}
|
|
950
|
+
var WHITE = { r: 255, g: 255, b: 255, a: 1 };
|
|
937
951
|
function probeBackdrop(el, opts = {}) {
|
|
938
952
|
const cols = opts.cols ?? GRID_COLS;
|
|
939
953
|
const rows = opts.rows ?? GRID_ROWS;
|
|
@@ -1047,15 +1061,17 @@ function resolveBody(optics, sample, ink) {
|
|
|
1047
1061
|
function withPresence(body, optics, sample) {
|
|
1048
1062
|
if (optics.presence <= 0) return body;
|
|
1049
1063
|
const mean = clamp3(sample.luma, 0, 1);
|
|
1050
|
-
const
|
|
1051
|
-
const
|
|
1052
|
-
const separation = Math.abs(
|
|
1064
|
+
const above = Math.abs(body.tintLuma - mean) > 1e-4 ? body.tintLuma > mean : mean < 0.5;
|
|
1065
|
+
const pole = above ? TINT_LIGHT : TINT_DARK;
|
|
1066
|
+
const separation = Math.min(optics.presence, Math.abs(pole - mean));
|
|
1053
1067
|
const reached = Math.abs(body.tintLuma - mean) * body.density;
|
|
1054
1068
|
if (reached >= separation) return body;
|
|
1055
|
-
const needed = separation / Math.max(Math.abs(
|
|
1069
|
+
const needed = separation / Math.max(Math.abs(pole - mean), 1e-4);
|
|
1056
1070
|
return {
|
|
1057
|
-
density: Math.max(body.density, Math.min(needed,
|
|
1058
|
-
|
|
1071
|
+
density: Math.max(body.density, Math.min(needed, BODY.presenceDemand)),
|
|
1072
|
+
// Left where the tint law put it, unless it landed exactly on the backdrop — which is the
|
|
1073
|
+
// case that used to be created here rather than avoided.
|
|
1074
|
+
tintLuma: Math.abs(body.tintLuma - mean) > 1e-4 ? body.tintLuma : pole
|
|
1059
1075
|
};
|
|
1060
1076
|
}
|
|
1061
1077
|
|
|
@@ -1603,15 +1619,22 @@ function waveImpulse(g) {
|
|
|
1603
1619
|
return halfMinDp(g) * MAX_STRETCH * WAVE_OF_TRAVEL;
|
|
1604
1620
|
}
|
|
1605
1621
|
var STYLE_ATTR = "data-vireglass-styles";
|
|
1606
|
-
function
|
|
1607
|
-
|
|
1622
|
+
function hostFor(el) {
|
|
1623
|
+
const root = el.getRootNode();
|
|
1624
|
+
return root instanceof ShadowRoot ? root : el.ownerDocument ?? document;
|
|
1625
|
+
}
|
|
1626
|
+
function styleParent(host) {
|
|
1627
|
+
return host instanceof ShadowRoot ? host : host.head;
|
|
1628
|
+
}
|
|
1629
|
+
function ensureRimStyles(host) {
|
|
1630
|
+
if (host.querySelector(`style[${STYLE_ATTR}]`)) return;
|
|
1608
1631
|
const style = document.createElement("style");
|
|
1609
1632
|
style.setAttribute(STYLE_ATTR, "");
|
|
1610
1633
|
style.textContent = `[${GLASS_ATTR}]::after{content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;padding:var(--vireglass-rim-width,1px);background:var(--vireglass-rim,none);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask-composite:exclude}[${GLASS_ATTR}]::before{content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;background:var(--vireglass-glow,none);opacity:var(--vireglass-glow-opacity,0)}`;
|
|
1611
|
-
|
|
1634
|
+
styleParent(host).appendChild(style);
|
|
1612
1635
|
}
|
|
1613
|
-
function getFilterHost() {
|
|
1614
|
-
const existing =
|
|
1636
|
+
function getFilterHost(host) {
|
|
1637
|
+
const existing = host.querySelector(`svg[${FILTER_HOST_ATTR}]`);
|
|
1615
1638
|
if (existing) return existing;
|
|
1616
1639
|
const svg = document.createElementNS(SVG_NS, "svg");
|
|
1617
1640
|
svg.setAttribute(FILTER_HOST_ATTR, "");
|
|
@@ -1621,7 +1644,7 @@ function getFilterHost() {
|
|
|
1621
1644
|
svg.style.position = "absolute";
|
|
1622
1645
|
svg.style.pointerEvents = "none";
|
|
1623
1646
|
svg.appendChild(document.createElementNS(SVG_NS, "defs"));
|
|
1624
|
-
|
|
1647
|
+
(host instanceof ShadowRoot ? host : host.body).appendChild(svg);
|
|
1625
1648
|
return svg;
|
|
1626
1649
|
}
|
|
1627
1650
|
function buildFilterElement(id, mapUrl, optics, scale, spectral) {
|
|
@@ -1659,6 +1682,7 @@ function buildFilterElement(id, mapUrl, optics, scale, spectral) {
|
|
|
1659
1682
|
pass.setAttribute("xChannelSelector", "R");
|
|
1660
1683
|
pass.setAttribute("yChannelSelector", "G");
|
|
1661
1684
|
pass.setAttribute("result", `${name}raw`);
|
|
1685
|
+
pass.setAttribute("data-vireglass-ratio", String(scale > 0 ? chScale / scale : 1));
|
|
1662
1686
|
const only = document.createElementNS(SVG_NS, "feColorMatrix");
|
|
1663
1687
|
only.setAttribute("in", `${name}raw`);
|
|
1664
1688
|
only.setAttribute("type", "matrix");
|
|
@@ -1765,7 +1789,8 @@ function attachGlass(el, opts = {}) {
|
|
|
1765
1789
|
apply();
|
|
1766
1790
|
});
|
|
1767
1791
|
warnIfNested(el);
|
|
1768
|
-
|
|
1792
|
+
const host = hostFor(el);
|
|
1793
|
+
ensureRimStyles(host);
|
|
1769
1794
|
el.setAttribute(GLASS_ATTR, "");
|
|
1770
1795
|
const positionWasStatic = getComputedStyle(el).position === "static";
|
|
1771
1796
|
if (positionWasStatic) el.style.setProperty("position", "relative");
|
|
@@ -1777,7 +1802,7 @@ function attachGlass(el, opts = {}) {
|
|
|
1777
1802
|
}
|
|
1778
1803
|
function morphState() {
|
|
1779
1804
|
if (!morph) return {};
|
|
1780
|
-
return { smoothing: morph.smoothing, morph: morph.shape, morph2: morph.shape2 };
|
|
1805
|
+
return { smoothing: morph.smoothing, morph: morph.shape, morph2: morph.shape2, morphs: morph.shapes };
|
|
1781
1806
|
}
|
|
1782
1807
|
function morphKey() {
|
|
1783
1808
|
if (!morph) return "0";
|
|
@@ -1793,7 +1818,7 @@ function attachGlass(el, opts = {}) {
|
|
|
1793
1818
|
const key = `a${appear.toFixed(3)}|m${morphKey()}|${geometry.width}x${geometry.height}r${geometry.cornerRadius}@${devicePixelRatio()}:${opticsNow.refraction}:${opticsNow.refractionScale}:${opticsNow.bevelDp}:${opticsNow.blur}`;
|
|
1794
1819
|
if (key !== mapKey || !filterEl) {
|
|
1795
1820
|
const map = cached(`d|${key}`, () => buildDisplacementMap(opticsNow, geometry, devicePixelRatio(), morphState()));
|
|
1796
|
-
const defs = getFilterHost().querySelector("defs");
|
|
1821
|
+
const defs = getFilterHost(host).querySelector("defs");
|
|
1797
1822
|
const hue = hasSpectralEdge(opticsNow) ? cached(`h|${key}`, () => buildSpectralMap(opticsNow, geometry, devicePixelRatio())) : null;
|
|
1798
1823
|
const next = buildFilterElement(id, map.url, opticsNow, map.scale, hue);
|
|
1799
1824
|
if (filterEl?.parentNode) defs.replaceChild(next, filterEl);
|
|
@@ -1859,9 +1884,11 @@ function attachGlass(el, opts = {}) {
|
|
|
1859
1884
|
});
|
|
1860
1885
|
if (filterEl) {
|
|
1861
1886
|
const feImage = filterEl.querySelector("feImage");
|
|
1862
|
-
const feDisplace = filterEl.querySelector("feDisplacementMap");
|
|
1863
1887
|
feImage?.setAttribute("href", map.url);
|
|
1864
|
-
|
|
1888
|
+
for (const pass of filterEl.querySelectorAll("feDisplacementMap")) {
|
|
1889
|
+
const ratio = Number(pass.getAttribute("data-vireglass-ratio") ?? 1);
|
|
1890
|
+
pass.setAttribute("scale", String(map.scale * (Number.isFinite(ratio) ? ratio : 1)));
|
|
1891
|
+
}
|
|
1865
1892
|
}
|
|
1866
1893
|
mapKey = "";
|
|
1867
1894
|
paintGlow(sampleOut.touchX, sampleOut.touchY, sampleOut.active * base.edgeLight, geometry);
|