@deck.gl/extensions 9.4.0-alpha.2 → 9.4.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clip/clip-extension.d.ts +3 -0
- package/dist/clip/clip-extension.d.ts.map +1 -1
- package/dist/clip/clip-extension.js +7 -0
- package/dist/clip/clip-extension.js.map +1 -1
- package/dist/dist.dev.js +2195 -142
- package/dist/fill-style/fill-style-extension.d.ts +38 -6
- package/dist/fill-style/fill-style-extension.d.ts.map +1 -1
- package/dist/fill-style/fill-style-extension.js +88 -10
- package/dist/fill-style/fill-style-extension.js.map +1 -1
- package/dist/fill-style/procedural-pattern.d.ts +65 -0
- package/dist/fill-style/procedural-pattern.d.ts.map +1 -0
- package/dist/fill-style/procedural-pattern.js +135 -0
- package/dist/fill-style/procedural-pattern.js.map +1 -0
- package/dist/fill-style/shader-module.d.ts +13 -4
- package/dist/fill-style/shader-module.d.ts.map +1 -1
- package/dist/fill-style/shader-module.js +212 -21
- package/dist/fill-style/shader-module.js.map +1 -1
- package/dist/index.cjs +3484 -215
- package/dist/index.cjs.map +3 -4
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/mask/shader-module.d.ts +1 -0
- package/dist/mask/shader-module.d.ts.map +1 -1
- package/dist/path-style/path-style-extension.d.ts +49 -6
- package/dist/path-style/path-style-extension.d.ts.map +1 -1
- package/dist/path-style/path-style-extension.js +237 -37
- package/dist/path-style/path-style-extension.js.map +1 -1
- package/dist/path-style/shaders.glsl.d.ts +30 -1
- package/dist/path-style/shaders.glsl.d.ts.map +1 -1
- package/dist/path-style/shaders.glsl.js +136 -21
- package/dist/path-style/shaders.glsl.js.map +1 -1
- package/dist/terrain/shader-module.d.ts +17 -2
- package/dist/terrain/shader-module.d.ts.map +1 -1
- package/dist/utils/debug.d.ts.map +1 -1
- package/dist.min.js +1251 -96
- package/package.json +6 -6
- package/src/clip/clip-extension.ts +15 -1
- package/src/fill-style/fill-style-extension.ts +135 -13
- package/src/fill-style/procedural-pattern.ts +203 -0
- package/src/fill-style/shader-module.ts +233 -23
- package/src/index.ts +10 -1
- package/src/path-style/path-style-extension.ts +350 -44
- package/src/path-style/shaders.glsl.ts +268 -27
package/dist/dist.dev.js
CHANGED
|
@@ -13,7 +13,11 @@ var __exports__ = (() => {
|
|
|
13
13
|
var __getProtoOf = Object.getPrototypeOf;
|
|
14
14
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
15
15
|
var __commonJS = (cb, mod) => function __require() {
|
|
16
|
-
|
|
16
|
+
try {
|
|
17
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
18
|
+
} catch (e) {
|
|
19
|
+
throw mod = 0, e;
|
|
20
|
+
}
|
|
17
21
|
};
|
|
18
22
|
var __export = (target, all) => {
|
|
19
23
|
for (var name in all)
|
|
@@ -918,8 +922,7 @@ void main() {
|
|
|
918
922
|
*/
|
|
919
923
|
_updateCategoryBitMask(params, extension) {
|
|
920
924
|
const { categorySize } = extension.opts;
|
|
921
|
-
if (!categorySize)
|
|
922
|
-
return;
|
|
925
|
+
if (!categorySize) return;
|
|
923
926
|
const { filterCategories } = this.props;
|
|
924
927
|
const categoryBitMask = new Uint32Array([0, 0, 0, 0]);
|
|
925
928
|
const categoryFilters = categorySize === 1 ? [filterCategories] : filterCategories;
|
|
@@ -1173,10 +1176,10 @@ void main() {
|
|
|
1173
1176
|
out[2] = a[2] * b;
|
|
1174
1177
|
return out;
|
|
1175
1178
|
}
|
|
1176
|
-
function scaleAndAdd(out, a, b,
|
|
1177
|
-
out[0] = a[0] + b[0] *
|
|
1178
|
-
out[1] = a[1] + b[1] *
|
|
1179
|
-
out[2] = a[2] + b[2] *
|
|
1179
|
+
function scaleAndAdd(out, a, b, scale3) {
|
|
1180
|
+
out[0] = a[0] + b[0] * scale3;
|
|
1181
|
+
out[1] = a[1] + b[1] * scale3;
|
|
1182
|
+
out[2] = a[2] + b[2] * scale3;
|
|
1180
1183
|
return out;
|
|
1181
1184
|
}
|
|
1182
1185
|
function distance(a, b) {
|
|
@@ -1213,13 +1216,13 @@ void main() {
|
|
|
1213
1216
|
const x = a[0];
|
|
1214
1217
|
const y = a[1];
|
|
1215
1218
|
const z = a[2];
|
|
1216
|
-
let
|
|
1217
|
-
if (
|
|
1218
|
-
|
|
1219
|
+
let len3 = x * x + y * y + z * z;
|
|
1220
|
+
if (len3 > 0) {
|
|
1221
|
+
len3 = 1 / Math.sqrt(len3);
|
|
1219
1222
|
}
|
|
1220
|
-
out[0] = a[0] *
|
|
1221
|
-
out[1] = a[1] *
|
|
1222
|
-
out[2] = a[2] *
|
|
1223
|
+
out[0] = a[0] * len3;
|
|
1224
|
+
out[1] = a[1] * len3;
|
|
1225
|
+
out[2] = a[2] * len3;
|
|
1223
1226
|
return out;
|
|
1224
1227
|
}
|
|
1225
1228
|
function dot(a, b) {
|
|
@@ -1280,14 +1283,14 @@ void main() {
|
|
|
1280
1283
|
out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;
|
|
1281
1284
|
return out;
|
|
1282
1285
|
}
|
|
1283
|
-
function random(out,
|
|
1284
|
-
|
|
1286
|
+
function random(out, scale3) {
|
|
1287
|
+
scale3 = scale3 === void 0 ? 1 : scale3;
|
|
1285
1288
|
const r = RANDOM() * 2 * Math.PI;
|
|
1286
1289
|
const z = RANDOM() * 2 - 1;
|
|
1287
|
-
const zScale = Math.sqrt(1 - z * z) *
|
|
1290
|
+
const zScale = Math.sqrt(1 - z * z) * scale3;
|
|
1288
1291
|
out[0] = Math.cos(r) * zScale;
|
|
1289
1292
|
out[1] = Math.sin(r) * zScale;
|
|
1290
|
-
out[2] = z *
|
|
1293
|
+
out[2] = z * scale3;
|
|
1291
1294
|
return out;
|
|
1292
1295
|
}
|
|
1293
1296
|
function transformMat4(out, a, m) {
|
|
@@ -1417,7 +1420,7 @@ void main() {
|
|
|
1417
1420
|
var sqrDist = squaredDistance;
|
|
1418
1421
|
var len = length;
|
|
1419
1422
|
var sqrLen = squaredLength;
|
|
1420
|
-
var forEach = function() {
|
|
1423
|
+
var forEach = (function() {
|
|
1421
1424
|
const vec = create();
|
|
1422
1425
|
return function(a, stride, offset, count, fn, arg) {
|
|
1423
1426
|
let i;
|
|
@@ -1444,7 +1447,373 @@ void main() {
|
|
|
1444
1447
|
}
|
|
1445
1448
|
return a;
|
|
1446
1449
|
};
|
|
1447
|
-
}();
|
|
1450
|
+
})();
|
|
1451
|
+
|
|
1452
|
+
// ../../node_modules/@math.gl/core/dist/gl-matrix/vec4.js
|
|
1453
|
+
var vec4_exports = {};
|
|
1454
|
+
__export(vec4_exports, {
|
|
1455
|
+
add: () => add2,
|
|
1456
|
+
ceil: () => ceil2,
|
|
1457
|
+
clone: () => clone2,
|
|
1458
|
+
copy: () => copy2,
|
|
1459
|
+
create: () => create2,
|
|
1460
|
+
cross: () => cross2,
|
|
1461
|
+
dist: () => dist2,
|
|
1462
|
+
distance: () => distance2,
|
|
1463
|
+
div: () => div2,
|
|
1464
|
+
divide: () => divide2,
|
|
1465
|
+
dot: () => dot2,
|
|
1466
|
+
equals: () => equals3,
|
|
1467
|
+
exactEquals: () => exactEquals2,
|
|
1468
|
+
floor: () => floor2,
|
|
1469
|
+
forEach: () => forEach2,
|
|
1470
|
+
fromValues: () => fromValues2,
|
|
1471
|
+
inverse: () => inverse2,
|
|
1472
|
+
len: () => len2,
|
|
1473
|
+
length: () => length2,
|
|
1474
|
+
lerp: () => lerp2,
|
|
1475
|
+
max: () => max2,
|
|
1476
|
+
min: () => min2,
|
|
1477
|
+
mul: () => mul2,
|
|
1478
|
+
multiply: () => multiply2,
|
|
1479
|
+
negate: () => negate2,
|
|
1480
|
+
normalize: () => normalize2,
|
|
1481
|
+
random: () => random2,
|
|
1482
|
+
round: () => round3,
|
|
1483
|
+
scale: () => scale2,
|
|
1484
|
+
scaleAndAdd: () => scaleAndAdd2,
|
|
1485
|
+
set: () => set2,
|
|
1486
|
+
sqrDist: () => sqrDist2,
|
|
1487
|
+
sqrLen: () => sqrLen2,
|
|
1488
|
+
squaredDistance: () => squaredDistance2,
|
|
1489
|
+
squaredLength: () => squaredLength2,
|
|
1490
|
+
str: () => str2,
|
|
1491
|
+
sub: () => sub2,
|
|
1492
|
+
subtract: () => subtract2,
|
|
1493
|
+
transformMat4: () => transformMat42,
|
|
1494
|
+
transformQuat: () => transformQuat2,
|
|
1495
|
+
zero: () => zero2
|
|
1496
|
+
});
|
|
1497
|
+
function create2() {
|
|
1498
|
+
const out = new ARRAY_TYPE(4);
|
|
1499
|
+
if (ARRAY_TYPE != Float32Array) {
|
|
1500
|
+
out[0] = 0;
|
|
1501
|
+
out[1] = 0;
|
|
1502
|
+
out[2] = 0;
|
|
1503
|
+
out[3] = 0;
|
|
1504
|
+
}
|
|
1505
|
+
return out;
|
|
1506
|
+
}
|
|
1507
|
+
function clone2(a) {
|
|
1508
|
+
const out = new ARRAY_TYPE(4);
|
|
1509
|
+
out[0] = a[0];
|
|
1510
|
+
out[1] = a[1];
|
|
1511
|
+
out[2] = a[2];
|
|
1512
|
+
out[3] = a[3];
|
|
1513
|
+
return out;
|
|
1514
|
+
}
|
|
1515
|
+
function fromValues2(x, y, z, w) {
|
|
1516
|
+
const out = new ARRAY_TYPE(4);
|
|
1517
|
+
out[0] = x;
|
|
1518
|
+
out[1] = y;
|
|
1519
|
+
out[2] = z;
|
|
1520
|
+
out[3] = w;
|
|
1521
|
+
return out;
|
|
1522
|
+
}
|
|
1523
|
+
function copy2(out, a) {
|
|
1524
|
+
out[0] = a[0];
|
|
1525
|
+
out[1] = a[1];
|
|
1526
|
+
out[2] = a[2];
|
|
1527
|
+
out[3] = a[3];
|
|
1528
|
+
return out;
|
|
1529
|
+
}
|
|
1530
|
+
function set2(out, x, y, z, w) {
|
|
1531
|
+
out[0] = x;
|
|
1532
|
+
out[1] = y;
|
|
1533
|
+
out[2] = z;
|
|
1534
|
+
out[3] = w;
|
|
1535
|
+
return out;
|
|
1536
|
+
}
|
|
1537
|
+
function add2(out, a, b) {
|
|
1538
|
+
out[0] = a[0] + b[0];
|
|
1539
|
+
out[1] = a[1] + b[1];
|
|
1540
|
+
out[2] = a[2] + b[2];
|
|
1541
|
+
out[3] = a[3] + b[3];
|
|
1542
|
+
return out;
|
|
1543
|
+
}
|
|
1544
|
+
function subtract2(out, a, b) {
|
|
1545
|
+
out[0] = a[0] - b[0];
|
|
1546
|
+
out[1] = a[1] - b[1];
|
|
1547
|
+
out[2] = a[2] - b[2];
|
|
1548
|
+
out[3] = a[3] - b[3];
|
|
1549
|
+
return out;
|
|
1550
|
+
}
|
|
1551
|
+
function multiply2(out, a, b) {
|
|
1552
|
+
out[0] = a[0] * b[0];
|
|
1553
|
+
out[1] = a[1] * b[1];
|
|
1554
|
+
out[2] = a[2] * b[2];
|
|
1555
|
+
out[3] = a[3] * b[3];
|
|
1556
|
+
return out;
|
|
1557
|
+
}
|
|
1558
|
+
function divide2(out, a, b) {
|
|
1559
|
+
out[0] = a[0] / b[0];
|
|
1560
|
+
out[1] = a[1] / b[1];
|
|
1561
|
+
out[2] = a[2] / b[2];
|
|
1562
|
+
out[3] = a[3] / b[3];
|
|
1563
|
+
return out;
|
|
1564
|
+
}
|
|
1565
|
+
function ceil2(out, a) {
|
|
1566
|
+
out[0] = Math.ceil(a[0]);
|
|
1567
|
+
out[1] = Math.ceil(a[1]);
|
|
1568
|
+
out[2] = Math.ceil(a[2]);
|
|
1569
|
+
out[3] = Math.ceil(a[3]);
|
|
1570
|
+
return out;
|
|
1571
|
+
}
|
|
1572
|
+
function floor2(out, a) {
|
|
1573
|
+
out[0] = Math.floor(a[0]);
|
|
1574
|
+
out[1] = Math.floor(a[1]);
|
|
1575
|
+
out[2] = Math.floor(a[2]);
|
|
1576
|
+
out[3] = Math.floor(a[3]);
|
|
1577
|
+
return out;
|
|
1578
|
+
}
|
|
1579
|
+
function min2(out, a, b) {
|
|
1580
|
+
out[0] = Math.min(a[0], b[0]);
|
|
1581
|
+
out[1] = Math.min(a[1], b[1]);
|
|
1582
|
+
out[2] = Math.min(a[2], b[2]);
|
|
1583
|
+
out[3] = Math.min(a[3], b[3]);
|
|
1584
|
+
return out;
|
|
1585
|
+
}
|
|
1586
|
+
function max2(out, a, b) {
|
|
1587
|
+
out[0] = Math.max(a[0], b[0]);
|
|
1588
|
+
out[1] = Math.max(a[1], b[1]);
|
|
1589
|
+
out[2] = Math.max(a[2], b[2]);
|
|
1590
|
+
out[3] = Math.max(a[3], b[3]);
|
|
1591
|
+
return out;
|
|
1592
|
+
}
|
|
1593
|
+
function round3(out, a) {
|
|
1594
|
+
out[0] = round(a[0]);
|
|
1595
|
+
out[1] = round(a[1]);
|
|
1596
|
+
out[2] = round(a[2]);
|
|
1597
|
+
out[3] = round(a[3]);
|
|
1598
|
+
return out;
|
|
1599
|
+
}
|
|
1600
|
+
function scale2(out, a, b) {
|
|
1601
|
+
out[0] = a[0] * b;
|
|
1602
|
+
out[1] = a[1] * b;
|
|
1603
|
+
out[2] = a[2] * b;
|
|
1604
|
+
out[3] = a[3] * b;
|
|
1605
|
+
return out;
|
|
1606
|
+
}
|
|
1607
|
+
function scaleAndAdd2(out, a, b, scale3) {
|
|
1608
|
+
out[0] = a[0] + b[0] * scale3;
|
|
1609
|
+
out[1] = a[1] + b[1] * scale3;
|
|
1610
|
+
out[2] = a[2] + b[2] * scale3;
|
|
1611
|
+
out[3] = a[3] + b[3] * scale3;
|
|
1612
|
+
return out;
|
|
1613
|
+
}
|
|
1614
|
+
function distance2(a, b) {
|
|
1615
|
+
const x = b[0] - a[0];
|
|
1616
|
+
const y = b[1] - a[1];
|
|
1617
|
+
const z = b[2] - a[2];
|
|
1618
|
+
const w = b[3] - a[3];
|
|
1619
|
+
return Math.sqrt(x * x + y * y + z * z + w * w);
|
|
1620
|
+
}
|
|
1621
|
+
function squaredDistance2(a, b) {
|
|
1622
|
+
const x = b[0] - a[0];
|
|
1623
|
+
const y = b[1] - a[1];
|
|
1624
|
+
const z = b[2] - a[2];
|
|
1625
|
+
const w = b[3] - a[3];
|
|
1626
|
+
return x * x + y * y + z * z + w * w;
|
|
1627
|
+
}
|
|
1628
|
+
function length2(a) {
|
|
1629
|
+
const x = a[0];
|
|
1630
|
+
const y = a[1];
|
|
1631
|
+
const z = a[2];
|
|
1632
|
+
const w = a[3];
|
|
1633
|
+
return Math.sqrt(x * x + y * y + z * z + w * w);
|
|
1634
|
+
}
|
|
1635
|
+
function squaredLength2(a) {
|
|
1636
|
+
const x = a[0];
|
|
1637
|
+
const y = a[1];
|
|
1638
|
+
const z = a[2];
|
|
1639
|
+
const w = a[3];
|
|
1640
|
+
return x * x + y * y + z * z + w * w;
|
|
1641
|
+
}
|
|
1642
|
+
function negate2(out, a) {
|
|
1643
|
+
out[0] = -a[0];
|
|
1644
|
+
out[1] = -a[1];
|
|
1645
|
+
out[2] = -a[2];
|
|
1646
|
+
out[3] = -a[3];
|
|
1647
|
+
return out;
|
|
1648
|
+
}
|
|
1649
|
+
function inverse2(out, a) {
|
|
1650
|
+
out[0] = 1 / a[0];
|
|
1651
|
+
out[1] = 1 / a[1];
|
|
1652
|
+
out[2] = 1 / a[2];
|
|
1653
|
+
out[3] = 1 / a[3];
|
|
1654
|
+
return out;
|
|
1655
|
+
}
|
|
1656
|
+
function normalize2(out, a) {
|
|
1657
|
+
const x = a[0];
|
|
1658
|
+
const y = a[1];
|
|
1659
|
+
const z = a[2];
|
|
1660
|
+
const w = a[3];
|
|
1661
|
+
let len3 = x * x + y * y + z * z + w * w;
|
|
1662
|
+
if (len3 > 0) {
|
|
1663
|
+
len3 = 1 / Math.sqrt(len3);
|
|
1664
|
+
}
|
|
1665
|
+
out[0] = x * len3;
|
|
1666
|
+
out[1] = y * len3;
|
|
1667
|
+
out[2] = z * len3;
|
|
1668
|
+
out[3] = w * len3;
|
|
1669
|
+
return out;
|
|
1670
|
+
}
|
|
1671
|
+
function dot2(a, b) {
|
|
1672
|
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
|
|
1673
|
+
}
|
|
1674
|
+
function cross2(out, u, v, w) {
|
|
1675
|
+
const A = v[0] * w[1] - v[1] * w[0];
|
|
1676
|
+
const B = v[0] * w[2] - v[2] * w[0];
|
|
1677
|
+
const C = v[0] * w[3] - v[3] * w[0];
|
|
1678
|
+
const D = v[1] * w[2] - v[2] * w[1];
|
|
1679
|
+
const E = v[1] * w[3] - v[3] * w[1];
|
|
1680
|
+
const F = v[2] * w[3] - v[3] * w[2];
|
|
1681
|
+
const G = u[0];
|
|
1682
|
+
const H = u[1];
|
|
1683
|
+
const I = u[2];
|
|
1684
|
+
const J = u[3];
|
|
1685
|
+
out[0] = H * F - I * E + J * D;
|
|
1686
|
+
out[1] = -(G * F) + I * C - J * B;
|
|
1687
|
+
out[2] = G * E - H * C + J * A;
|
|
1688
|
+
out[3] = -(G * D) + H * B - I * A;
|
|
1689
|
+
return out;
|
|
1690
|
+
}
|
|
1691
|
+
function lerp2(out, a, b, t) {
|
|
1692
|
+
const ax = a[0];
|
|
1693
|
+
const ay = a[1];
|
|
1694
|
+
const az = a[2];
|
|
1695
|
+
const aw = a[3];
|
|
1696
|
+
out[0] = ax + t * (b[0] - ax);
|
|
1697
|
+
out[1] = ay + t * (b[1] - ay);
|
|
1698
|
+
out[2] = az + t * (b[2] - az);
|
|
1699
|
+
out[3] = aw + t * (b[3] - aw);
|
|
1700
|
+
return out;
|
|
1701
|
+
}
|
|
1702
|
+
function random2(out, scale3) {
|
|
1703
|
+
scale3 = scale3 === void 0 ? 1 : scale3;
|
|
1704
|
+
let v1;
|
|
1705
|
+
let v2;
|
|
1706
|
+
let v3;
|
|
1707
|
+
let v4;
|
|
1708
|
+
let s1;
|
|
1709
|
+
let s2;
|
|
1710
|
+
do {
|
|
1711
|
+
v1 = RANDOM() * 2 - 1;
|
|
1712
|
+
v2 = RANDOM() * 2 - 1;
|
|
1713
|
+
s1 = v1 * v1 + v2 * v2;
|
|
1714
|
+
} while (s1 >= 1);
|
|
1715
|
+
do {
|
|
1716
|
+
v3 = RANDOM() * 2 - 1;
|
|
1717
|
+
v4 = RANDOM() * 2 - 1;
|
|
1718
|
+
s2 = v3 * v3 + v4 * v4;
|
|
1719
|
+
} while (s2 >= 1);
|
|
1720
|
+
const d = Math.sqrt((1 - s1) / s2);
|
|
1721
|
+
out[0] = scale3 * v1;
|
|
1722
|
+
out[1] = scale3 * v2;
|
|
1723
|
+
out[2] = scale3 * v3 * d;
|
|
1724
|
+
out[3] = scale3 * v4 * d;
|
|
1725
|
+
return out;
|
|
1726
|
+
}
|
|
1727
|
+
function transformMat42(out, a, m) {
|
|
1728
|
+
const x = a[0];
|
|
1729
|
+
const y = a[1];
|
|
1730
|
+
const z = a[2];
|
|
1731
|
+
const w = a[3];
|
|
1732
|
+
out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w;
|
|
1733
|
+
out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
|
|
1734
|
+
out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
|
|
1735
|
+
out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w;
|
|
1736
|
+
return out;
|
|
1737
|
+
}
|
|
1738
|
+
function transformQuat2(out, a, q) {
|
|
1739
|
+
const x = a[0];
|
|
1740
|
+
const y = a[1];
|
|
1741
|
+
const z = a[2];
|
|
1742
|
+
const qx = q[0];
|
|
1743
|
+
const qy = q[1];
|
|
1744
|
+
const qz = q[2];
|
|
1745
|
+
const qw = q[3];
|
|
1746
|
+
const ix = qw * x + qy * z - qz * y;
|
|
1747
|
+
const iy = qw * y + qz * x - qx * z;
|
|
1748
|
+
const iz = qw * z + qx * y - qy * x;
|
|
1749
|
+
const iw = -qx * x - qy * y - qz * z;
|
|
1750
|
+
out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy;
|
|
1751
|
+
out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz;
|
|
1752
|
+
out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx;
|
|
1753
|
+
out[3] = a[3];
|
|
1754
|
+
return out;
|
|
1755
|
+
}
|
|
1756
|
+
function zero2(out) {
|
|
1757
|
+
out[0] = 0;
|
|
1758
|
+
out[1] = 0;
|
|
1759
|
+
out[2] = 0;
|
|
1760
|
+
out[3] = 0;
|
|
1761
|
+
return out;
|
|
1762
|
+
}
|
|
1763
|
+
function str2(a) {
|
|
1764
|
+
return `vec4(${a[0]}, ${a[1]}, ${a[2]}, ${a[3]})`;
|
|
1765
|
+
}
|
|
1766
|
+
function exactEquals2(a, b) {
|
|
1767
|
+
return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
|
|
1768
|
+
}
|
|
1769
|
+
function equals3(a, b) {
|
|
1770
|
+
const a0 = a[0];
|
|
1771
|
+
const a1 = a[1];
|
|
1772
|
+
const a2 = a[2];
|
|
1773
|
+
const a3 = a[3];
|
|
1774
|
+
const b0 = b[0];
|
|
1775
|
+
const b1 = b[1];
|
|
1776
|
+
const b2 = b[2];
|
|
1777
|
+
const b3 = b[3];
|
|
1778
|
+
return Math.abs(a0 - b0) <= EPSILON * Math.max(1, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1, Math.abs(a3), Math.abs(b3));
|
|
1779
|
+
}
|
|
1780
|
+
var sub2 = subtract2;
|
|
1781
|
+
var mul2 = multiply2;
|
|
1782
|
+
var div2 = divide2;
|
|
1783
|
+
var dist2 = distance2;
|
|
1784
|
+
var sqrDist2 = squaredDistance2;
|
|
1785
|
+
var len2 = length2;
|
|
1786
|
+
var sqrLen2 = squaredLength2;
|
|
1787
|
+
var forEach2 = (function() {
|
|
1788
|
+
const vec = create2();
|
|
1789
|
+
return function(a, stride, offset, count, fn, arg) {
|
|
1790
|
+
let i;
|
|
1791
|
+
let l;
|
|
1792
|
+
if (!stride) {
|
|
1793
|
+
stride = 4;
|
|
1794
|
+
}
|
|
1795
|
+
if (!offset) {
|
|
1796
|
+
offset = 0;
|
|
1797
|
+
}
|
|
1798
|
+
if (count) {
|
|
1799
|
+
l = Math.min(count * stride + offset, a.length);
|
|
1800
|
+
} else {
|
|
1801
|
+
l = a.length;
|
|
1802
|
+
}
|
|
1803
|
+
for (i = offset; i < l; i += stride) {
|
|
1804
|
+
vec[0] = a[i];
|
|
1805
|
+
vec[1] = a[i + 1];
|
|
1806
|
+
vec[2] = a[i + 2];
|
|
1807
|
+
vec[3] = a[i + 3];
|
|
1808
|
+
fn(vec, vec, arg);
|
|
1809
|
+
a[i] = vec[0];
|
|
1810
|
+
a[i + 1] = vec[1];
|
|
1811
|
+
a[i + 2] = vec[2];
|
|
1812
|
+
a[i + 3] = vec[3];
|
|
1813
|
+
}
|
|
1814
|
+
return a;
|
|
1815
|
+
};
|
|
1816
|
+
})();
|
|
1448
1817
|
|
|
1449
1818
|
// ../../node_modules/@luma.gl/shadertools/dist/modules/math/fp64/fp64-utils.js
|
|
1450
1819
|
function fp64ify(a, out = [], startIndex = 0) {
|
|
@@ -1673,6 +2042,462 @@ vec2 sqrt_fp64(vec2 a) {
|
|
|
1673
2042
|
`
|
|
1674
2043
|
);
|
|
1675
2044
|
|
|
2045
|
+
// ../../node_modules/@luma.gl/shadertools/dist/modules/math/fp64/fp64-arithmetic-wgsl-integer.js
|
|
2046
|
+
var fp64arithmeticWGSLInteger = (
|
|
2047
|
+
/* wgsl */
|
|
2048
|
+
`struct Fp64F32Bits {
|
|
2049
|
+
sign: u32,
|
|
2050
|
+
baseExponent: i32,
|
|
2051
|
+
significand: u32,
|
|
2052
|
+
isZero: bool,
|
|
2053
|
+
isInf: bool,
|
|
2054
|
+
isNan: bool,
|
|
2055
|
+
};
|
|
2056
|
+
|
|
2057
|
+
// Decode an f32 as (-1)^sign * significand * 2^baseExponent.
|
|
2058
|
+
fn fp64_decode_f32_bits(bits: u32) -> Fp64F32Bits {
|
|
2059
|
+
let sign = bits >> 31u;
|
|
2060
|
+
let exponentBits = (bits >> 23u) & 0xffu;
|
|
2061
|
+
let fraction = bits & 0x7fffffu;
|
|
2062
|
+
|
|
2063
|
+
if (exponentBits == 0xffu) {
|
|
2064
|
+
return Fp64F32Bits(sign, 0, 0u, false, fraction == 0u, fraction != 0u);
|
|
2065
|
+
}
|
|
2066
|
+
if (exponentBits == 0u) {
|
|
2067
|
+
return Fp64F32Bits(sign, -149, fraction, fraction == 0u, false, false);
|
|
2068
|
+
}
|
|
2069
|
+
return Fp64F32Bits(sign, i32(exponentBits) - 150, 0x800000u | fraction, false, false, false);
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
fn fp64_f32_magnitude_compare(aBits: u32, bBits: u32) -> i32 {
|
|
2073
|
+
let aMagnitude = aBits & 0x7fffffffu;
|
|
2074
|
+
let bMagnitude = bBits & 0x7fffffffu;
|
|
2075
|
+
if (aMagnitude == bMagnitude) {
|
|
2076
|
+
return 0;
|
|
2077
|
+
}
|
|
2078
|
+
return select(-1, 1, aMagnitude > bMagnitude);
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
fn fp64_make_residual_f32_bits(
|
|
2082
|
+
exactSign: u32,
|
|
2083
|
+
exactMagnitude: vec2u,
|
|
2084
|
+
exactBaseExponent: i32,
|
|
2085
|
+
highBits: u32
|
|
2086
|
+
) -> u32 {
|
|
2087
|
+
if (fp64_u64_is_zero(exactMagnitude)) {
|
|
2088
|
+
return 0u;
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
let high = fp64_decode_f32_bits(highBits);
|
|
2092
|
+
if (high.isInf || high.isNan) {
|
|
2093
|
+
return exactSign << 31u;
|
|
2094
|
+
}
|
|
2095
|
+
if (high.isZero) {
|
|
2096
|
+
return fp64_make_f32_bits_from_u64(exactSign, exactMagnitude, exactBaseExponent);
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
let commonBaseExponent = min(exactBaseExponent, high.baseExponent);
|
|
2100
|
+
let exactShift = exactBaseExponent - commonBaseExponent;
|
|
2101
|
+
let highShift = high.baseExponent - commonBaseExponent;
|
|
2102
|
+
|
|
2103
|
+
// A normal two-sum/two-product residual never needs a shift this large.
|
|
2104
|
+
// This guard gives deterministic underflow behavior outside that contract.
|
|
2105
|
+
if (exactShift >= 64 || highShift >= 64) {
|
|
2106
|
+
return exactSign << 31u;
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
let exactAligned = fp64_u64_shift_left(exactMagnitude, u32(exactShift));
|
|
2110
|
+
let highAligned = fp64_u64_shift_left(vec2u(0u, high.significand), u32(highShift));
|
|
2111
|
+
let comparison = fp64_u64_compare(exactAligned, highAligned);
|
|
2112
|
+
if (comparison == 0) {
|
|
2113
|
+
return 0u;
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
var residualSign = exactSign;
|
|
2117
|
+
var residualMagnitude: vec2u;
|
|
2118
|
+
if (comparison > 0) {
|
|
2119
|
+
residualMagnitude = fp64_u64_sub(exactAligned, highAligned);
|
|
2120
|
+
} else {
|
|
2121
|
+
residualSign = exactSign ^ 1u;
|
|
2122
|
+
residualMagnitude = fp64_u64_sub(highAligned, exactAligned);
|
|
2123
|
+
}
|
|
2124
|
+
return fp64_make_f32_bits_from_u64(
|
|
2125
|
+
residualSign,
|
|
2126
|
+
residualMagnitude,
|
|
2127
|
+
commonBaseExponent
|
|
2128
|
+
);
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
fn fp64_split_accumulator_bits(
|
|
2132
|
+
sign: u32,
|
|
2133
|
+
magnitude: vec2u,
|
|
2134
|
+
baseExponent: i32
|
|
2135
|
+
) -> vec2u {
|
|
2136
|
+
let highBits = fp64_make_f32_bits_from_u64(sign, magnitude, baseExponent);
|
|
2137
|
+
let lowBits = fp64_make_residual_f32_bits(sign, magnitude, baseExponent, highBits);
|
|
2138
|
+
return vec2u(highBits, lowBits);
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
fn fp64_two_sum_integer_bits(aBits: u32, bBits: u32) -> vec2u {
|
|
2142
|
+
let a = fp64_decode_f32_bits(aBits);
|
|
2143
|
+
let b = fp64_decode_f32_bits(bBits);
|
|
2144
|
+
|
|
2145
|
+
if (a.isNan || b.isNan) {
|
|
2146
|
+
return vec2u(0x7fc00000u, 0u);
|
|
2147
|
+
}
|
|
2148
|
+
if (a.isInf || b.isInf) {
|
|
2149
|
+
if (a.isInf && b.isInf && a.sign != b.sign) {
|
|
2150
|
+
return vec2u(0x7fc00000u, 0u);
|
|
2151
|
+
}
|
|
2152
|
+
return select(vec2u(bBits, 0u), vec2u(aBits, 0u), a.isInf);
|
|
2153
|
+
}
|
|
2154
|
+
if (a.isZero && b.isZero) {
|
|
2155
|
+
return vec2u((a.sign & b.sign) << 31u, 0u);
|
|
2156
|
+
}
|
|
2157
|
+
if (a.isZero) {
|
|
2158
|
+
return vec2u(bBits, 0u);
|
|
2159
|
+
}
|
|
2160
|
+
if (b.isZero) {
|
|
2161
|
+
return vec2u(aBits, 0u);
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
let exponentDifference = select(
|
|
2165
|
+
b.baseExponent - a.baseExponent,
|
|
2166
|
+
a.baseExponent - b.baseExponent,
|
|
2167
|
+
a.baseExponent >= b.baseExponent
|
|
2168
|
+
);
|
|
2169
|
+
|
|
2170
|
+
// Beyond half an ulp, rounding cannot change the larger operand. Returning
|
|
2171
|
+
// the smaller operand intact also avoids an unbounded integer alignment.
|
|
2172
|
+
// At a power-of-two boundary the spacing below the larger operand is half
|
|
2173
|
+
// the spacing above it, so an opposite-sign gap-25 operand can still change
|
|
2174
|
+
// the rounded high limb. Gap 26 is the first universally safe early-out.
|
|
2175
|
+
if (exponentDifference > 25) {
|
|
2176
|
+
if (fp64_f32_magnitude_compare(aBits, bBits) >= 0) {
|
|
2177
|
+
return vec2u(aBits, bBits);
|
|
2178
|
+
}
|
|
2179
|
+
return vec2u(bBits, aBits);
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
let commonBaseExponent = min(a.baseExponent, b.baseExponent);
|
|
2183
|
+
let aMagnitude = fp64_u64_shift_left(
|
|
2184
|
+
vec2u(0u, a.significand),
|
|
2185
|
+
u32(a.baseExponent - commonBaseExponent)
|
|
2186
|
+
);
|
|
2187
|
+
let bMagnitude = fp64_u64_shift_left(
|
|
2188
|
+
vec2u(0u, b.significand),
|
|
2189
|
+
u32(b.baseExponent - commonBaseExponent)
|
|
2190
|
+
);
|
|
2191
|
+
|
|
2192
|
+
var resultSign = a.sign;
|
|
2193
|
+
var resultMagnitude: vec2u;
|
|
2194
|
+
if (a.sign == b.sign) {
|
|
2195
|
+
resultMagnitude = fp64_u64_add(aMagnitude, bMagnitude);
|
|
2196
|
+
} else {
|
|
2197
|
+
let comparison = fp64_u64_compare(aMagnitude, bMagnitude);
|
|
2198
|
+
if (comparison == 0) {
|
|
2199
|
+
return vec2u(0u, 0u);
|
|
2200
|
+
}
|
|
2201
|
+
if (comparison > 0) {
|
|
2202
|
+
resultMagnitude = fp64_u64_sub(aMagnitude, bMagnitude);
|
|
2203
|
+
} else {
|
|
2204
|
+
resultSign = b.sign;
|
|
2205
|
+
resultMagnitude = fp64_u64_sub(bMagnitude, aMagnitude);
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
return fp64_split_accumulator_bits(resultSign, resultMagnitude, commonBaseExponent);
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
fn fp64_two_sum_integer(a: f32, b: f32) -> vec2f {
|
|
2213
|
+
let resultBits = fp64_two_sum_integer_bits(bitcast<u32>(a), bitcast<u32>(b));
|
|
2214
|
+
return vec2f(bitcast<f32>(resultBits.x), bitcast<f32>(resultBits.y));
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
fn fp64_multiply_significands(a: u32, b: u32) -> vec2u {
|
|
2218
|
+
let aLow = a & 0xffffu;
|
|
2219
|
+
let aHigh = a >> 16u;
|
|
2220
|
+
let bLow = b & 0xffffu;
|
|
2221
|
+
let bHigh = b >> 16u;
|
|
2222
|
+
let lowProduct = aLow * bLow;
|
|
2223
|
+
let crossProduct = aLow * bHigh + aHigh * bLow;
|
|
2224
|
+
let highProduct = aHigh * bHigh;
|
|
2225
|
+
|
|
2226
|
+
var result = vec2u(0u, lowProduct);
|
|
2227
|
+
result = fp64_u64_add(
|
|
2228
|
+
result,
|
|
2229
|
+
fp64_u64_shift_left(vec2u(0u, crossProduct), 16u)
|
|
2230
|
+
);
|
|
2231
|
+
result = fp64_u64_add(result, vec2u(highProduct, 0u));
|
|
2232
|
+
return result;
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
fn fp64_two_prod_integer_bits(aBits: u32, bBits: u32) -> vec2u {
|
|
2236
|
+
let a = fp64_decode_f32_bits(aBits);
|
|
2237
|
+
let b = fp64_decode_f32_bits(bBits);
|
|
2238
|
+
let resultSign = a.sign ^ b.sign;
|
|
2239
|
+
|
|
2240
|
+
if (a.isNan || b.isNan || ((a.isZero || b.isZero) && (a.isInf || b.isInf))) {
|
|
2241
|
+
return vec2u(0x7fc00000u, 0u);
|
|
2242
|
+
}
|
|
2243
|
+
if (a.isInf || b.isInf) {
|
|
2244
|
+
return vec2u((resultSign << 31u) | 0x7f800000u, resultSign << 31u);
|
|
2245
|
+
}
|
|
2246
|
+
if (a.isZero || b.isZero) {
|
|
2247
|
+
return vec2u(resultSign << 31u, resultSign << 31u);
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
let magnitude = fp64_multiply_significands(a.significand, b.significand);
|
|
2251
|
+
return fp64_split_accumulator_bits(
|
|
2252
|
+
resultSign,
|
|
2253
|
+
magnitude,
|
|
2254
|
+
a.baseExponent + b.baseExponent
|
|
2255
|
+
);
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
fn fp64_two_prod_integer(a: f32, b: f32) -> vec2f {
|
|
2259
|
+
let resultBits = fp64_two_prod_integer_bits(bitcast<u32>(a), bitcast<u32>(b));
|
|
2260
|
+
return vec2f(bitcast<f32>(resultBits.x), bitcast<f32>(resultBits.y));
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
fn fp64_round_add_integer(a: f32, b: f32) -> f32 {
|
|
2264
|
+
return fp64_two_sum_integer(a, b).x;
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
fn fp64_round_mul_integer(a: f32, b: f32) -> f32 {
|
|
2268
|
+
return fp64_two_prod_integer(a, b).x;
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
#ifndef LUMA_FP64_PREDICATE_ONLY
|
|
2272
|
+
fn fp64_f32_finite_exponent(value: Fp64F32Bits) -> i32 {
|
|
2273
|
+
let mostSignificantBit = 31u - countLeadingZeros(value.significand);
|
|
2274
|
+
return value.baseExponent + i32(mostSignificantBit);
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
fn fp64_scale_f32_integer(value: f32, exponent: i32) -> f32 {
|
|
2278
|
+
let decoded = fp64_decode_f32_bits(bitcast<u32>(value));
|
|
2279
|
+
if (decoded.isZero || decoded.isInf || decoded.isNan) {
|
|
2280
|
+
return value;
|
|
2281
|
+
}
|
|
2282
|
+
let resultBits = fp64_make_f32_bits_from_u64(
|
|
2283
|
+
decoded.sign,
|
|
2284
|
+
vec2u(0u, decoded.significand),
|
|
2285
|
+
decoded.baseExponent + exponent
|
|
2286
|
+
);
|
|
2287
|
+
return bitcast<f32>(resultBits);
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
// Divide normalized significands so the hardware operation cannot overflow,
|
|
2291
|
+
// underflow, or flush a subnormal result. Reapply the exponent with integer
|
|
2292
|
+
// packing, which also produces subnormal correction limbs without relying on
|
|
2293
|
+
// floating-point arithmetic to preserve them.
|
|
2294
|
+
fn fp64_divide_f32_integer(aValue: f32, bValue: f32) -> f32 {
|
|
2295
|
+
let a = fp64_decode_f32_bits(bitcast<u32>(aValue));
|
|
2296
|
+
let b = fp64_decode_f32_bits(bitcast<u32>(bValue));
|
|
2297
|
+
if (a.isZero || b.isZero || a.isInf || b.isInf || a.isNan || b.isNan) {
|
|
2298
|
+
return aValue / bValue;
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
let aMostSignificantBit = 31u - countLeadingZeros(a.significand);
|
|
2302
|
+
let bMostSignificantBit = 31u - countLeadingZeros(b.significand);
|
|
2303
|
+
let normalizedABits = fp64_make_f32_bits_from_u64(
|
|
2304
|
+
a.sign,
|
|
2305
|
+
vec2u(0u, a.significand),
|
|
2306
|
+
-i32(aMostSignificantBit)
|
|
2307
|
+
);
|
|
2308
|
+
let normalizedBBits = fp64_make_f32_bits_from_u64(
|
|
2309
|
+
b.sign,
|
|
2310
|
+
vec2u(0u, b.significand),
|
|
2311
|
+
-i32(bMostSignificantBit)
|
|
2312
|
+
);
|
|
2313
|
+
let normalizedQuotient = bitcast<f32>(normalizedABits) / bitcast<f32>(normalizedBBits);
|
|
2314
|
+
let quotient = fp64_decode_f32_bits(bitcast<u32>(normalizedQuotient));
|
|
2315
|
+
let exponentShift =
|
|
2316
|
+
a.baseExponent + i32(aMostSignificantBit) -
|
|
2317
|
+
b.baseExponent - i32(bMostSignificantBit);
|
|
2318
|
+
let quotientBits = fp64_make_f32_bits_from_u64(
|
|
2319
|
+
quotient.sign,
|
|
2320
|
+
vec2u(0u, quotient.significand),
|
|
2321
|
+
quotient.baseExponent + exponentShift
|
|
2322
|
+
);
|
|
2323
|
+
return bitcast<f32>(quotientBits);
|
|
2324
|
+
}
|
|
2325
|
+
#endif
|
|
2326
|
+
|
|
2327
|
+
#ifndef LUMA_FP64_PREDICATE_ONLY
|
|
2328
|
+
fn split(a: f32) -> vec2f {
|
|
2329
|
+
let aBits = bitcast<u32>(a);
|
|
2330
|
+
let decoded = fp64_decode_f32_bits(aBits);
|
|
2331
|
+
if (decoded.isZero || decoded.isInf || decoded.isNan) {
|
|
2332
|
+
return vec2f(a, 0.0);
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
var roundedHigh = decoded.significand >> 12u;
|
|
2336
|
+
let remainder = decoded.significand & 0xfffu;
|
|
2337
|
+
if (remainder > 0x800u || (remainder == 0x800u && (roundedHigh & 1u) == 1u)) {
|
|
2338
|
+
roundedHigh = roundedHigh + 1u;
|
|
2339
|
+
}
|
|
2340
|
+
var highMagnitude = vec2u(0u, roundedHigh << 12u);
|
|
2341
|
+
var highBits = fp64_make_f32_bits_from_u64(
|
|
2342
|
+
decoded.sign,
|
|
2343
|
+
highMagnitude,
|
|
2344
|
+
decoded.baseExponent
|
|
2345
|
+
);
|
|
2346
|
+
// Rounding the high limb of a maximum-exponent value can overflow even
|
|
2347
|
+
// though the original value is finite. Truncate only in that boundary case
|
|
2348
|
+
// so split remains an exact finite decomposition.
|
|
2349
|
+
if (fp64_decode_f32_bits(highBits).isInf) {
|
|
2350
|
+
roundedHigh = decoded.significand >> 12u;
|
|
2351
|
+
highMagnitude = vec2u(0u, roundedHigh << 12u);
|
|
2352
|
+
highBits = fp64_make_f32_bits_from_u64(
|
|
2353
|
+
decoded.sign,
|
|
2354
|
+
highMagnitude,
|
|
2355
|
+
decoded.baseExponent
|
|
2356
|
+
);
|
|
2357
|
+
}
|
|
2358
|
+
let lowBits = fp64_make_residual_f32_bits(
|
|
2359
|
+
decoded.sign,
|
|
2360
|
+
vec2u(0u, decoded.significand),
|
|
2361
|
+
decoded.baseExponent,
|
|
2362
|
+
highBits
|
|
2363
|
+
);
|
|
2364
|
+
return vec2f(bitcast<f32>(highBits), bitcast<f32>(lowBits));
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
fn split2(a: vec2f) -> vec2f {
|
|
2368
|
+
var result = split(a.x);
|
|
2369
|
+
result.y = fp64_round_add_integer(result.y, a.y);
|
|
2370
|
+
return result;
|
|
2371
|
+
}
|
|
2372
|
+
#endif
|
|
2373
|
+
|
|
2374
|
+
#ifndef LUMA_FP64_PREDICATE_ONLY
|
|
2375
|
+
fn quickTwoSum(a: f32, b: f32) -> vec2f {
|
|
2376
|
+
return fp64_two_sum_integer(a, b);
|
|
2377
|
+
}
|
|
2378
|
+
#endif
|
|
2379
|
+
|
|
2380
|
+
fn twoSum(a: f32, b: f32) -> vec2f {
|
|
2381
|
+
return fp64_two_sum_integer(a, b);
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2384
|
+
fn twoSub(a: f32, b: f32) -> vec2f {
|
|
2385
|
+
let bBits = bitcast<u32>(b) ^ 0x80000000u;
|
|
2386
|
+
let resultBits = fp64_two_sum_integer_bits(bitcast<u32>(a), bBits);
|
|
2387
|
+
return vec2f(bitcast<f32>(resultBits.x), bitcast<f32>(resultBits.y));
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
#ifndef LUMA_FP64_PREDICATE_ONLY
|
|
2391
|
+
fn twoSqr(a: f32) -> vec2f {
|
|
2392
|
+
return fp64_two_prod_integer(a, a);
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
fn twoProd(a: f32, b: f32) -> vec2f {
|
|
2396
|
+
return fp64_two_prod_integer(a, b);
|
|
2397
|
+
}
|
|
2398
|
+
#endif
|
|
2399
|
+
|
|
2400
|
+
fn sum_fp64(a: vec2f, b: vec2f) -> vec2f {
|
|
2401
|
+
var sum = fp64_two_sum_integer(a.x, b.x);
|
|
2402
|
+
let lowSum = fp64_two_sum_integer(a.y, b.y);
|
|
2403
|
+
sum.y = fp64_round_add_integer(sum.y, lowSum.x);
|
|
2404
|
+
sum = fp64_two_sum_integer(sum.x, sum.y);
|
|
2405
|
+
sum.y = fp64_round_add_integer(sum.y, lowSum.y);
|
|
2406
|
+
return fp64_two_sum_integer(sum.x, sum.y);
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
fn sub_fp64(a: vec2f, b: vec2f) -> vec2f {
|
|
2410
|
+
let negatedB = vec2f(
|
|
2411
|
+
bitcast<f32>(bitcast<u32>(b.x) ^ 0x80000000u),
|
|
2412
|
+
bitcast<f32>(bitcast<u32>(b.y) ^ 0x80000000u)
|
|
2413
|
+
);
|
|
2414
|
+
return sum_fp64(a, negatedB);
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
fn mul_fp64(a: vec2f, b: vec2f) -> vec2f {
|
|
2418
|
+
var product = fp64_two_prod_integer(a.x, b.x);
|
|
2419
|
+
let crossProduct1 = fp64_round_mul_integer(a.x, b.y);
|
|
2420
|
+
product.y = fp64_round_add_integer(product.y, crossProduct1);
|
|
2421
|
+
product = fp64_two_sum_integer(product.x, product.y);
|
|
2422
|
+
let crossProduct2 = fp64_round_mul_integer(a.y, b.x);
|
|
2423
|
+
product.y = fp64_round_add_integer(product.y, crossProduct2);
|
|
2424
|
+
return fp64_two_sum_integer(product.x, product.y);
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
#ifndef LUMA_FP64_PREDICATE_ONLY
|
|
2428
|
+
fn fp64_scale_fp64_integer(value: vec2f, exponent: i32) -> vec2f {
|
|
2429
|
+
let high = fp64_scale_f32_integer(value.x, exponent);
|
|
2430
|
+
let low = fp64_scale_f32_integer(value.y, exponent);
|
|
2431
|
+
return sum_fp64(vec2f(high, 0.0), vec2f(low, 0.0));
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
fn fp64_div_fp64_normalized(a: vec2f, b: vec2f) -> vec2f {
|
|
2435
|
+
let quotientHigh = fp64_divide_f32_integer(a.x, b.x);
|
|
2436
|
+
var quotient = vec2f(quotientHigh, 0.0);
|
|
2437
|
+
|
|
2438
|
+
let remainder = sub_fp64(a, mul_fp64(b, quotient));
|
|
2439
|
+
let quotientLow = fp64_divide_f32_integer(remainder.x, b.x);
|
|
2440
|
+
quotient = sum_fp64(quotient, vec2f(quotientLow, 0.0));
|
|
2441
|
+
|
|
2442
|
+
let secondRemainder = sub_fp64(a, mul_fp64(b, quotient));
|
|
2443
|
+
let correction = fp64_divide_f32_integer(secondRemainder.x, b.x);
|
|
2444
|
+
return sum_fp64(quotient, vec2f(correction, 0.0));
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
fn div_fp64(a: vec2f, b: vec2f) -> vec2f {
|
|
2448
|
+
let decodedA = fp64_decode_f32_bits(bitcast<u32>(a.x));
|
|
2449
|
+
let decodedB = fp64_decode_f32_bits(bitcast<u32>(b.x));
|
|
2450
|
+
if (
|
|
2451
|
+
decodedA.isZero || decodedB.isZero ||
|
|
2452
|
+
decodedA.isInf || decodedB.isInf ||
|
|
2453
|
+
decodedA.isNan || decodedB.isNan
|
|
2454
|
+
) {
|
|
2455
|
+
return fp64_div_fp64_normalized(a, b);
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2458
|
+
let exponentA = fp64_f32_finite_exponent(decodedA);
|
|
2459
|
+
let exponentB = fp64_f32_finite_exponent(decodedB);
|
|
2460
|
+
// Correct the quotient near unity so b * q and the remainder stay clear of
|
|
2461
|
+
// both f32 underflow and overflow. The exponent difference is applied once.
|
|
2462
|
+
let normalizedA = fp64_scale_fp64_integer(a, -exponentA);
|
|
2463
|
+
let normalizedB = fp64_scale_fp64_integer(b, -exponentB);
|
|
2464
|
+
let normalizedQuotient = fp64_div_fp64_normalized(normalizedA, normalizedB);
|
|
2465
|
+
return fp64_scale_fp64_integer(normalizedQuotient, exponentA - exponentB);
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
fn fp64_sqrt_fp64_normalized(a: vec2f) -> vec2f {
|
|
2469
|
+
let estimate = sqrt(a.x);
|
|
2470
|
+
let difference = sub_fp64(a, fp64_two_prod_integer(estimate, estimate)).x;
|
|
2471
|
+
let denominator = fp64_round_add_integer(estimate, estimate);
|
|
2472
|
+
let correction = fp64_divide_f32_integer(difference, denominator);
|
|
2473
|
+
return sum_fp64(vec2f(estimate, 0.0), vec2f(correction, 0.0));
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
fn sqrt_fp64(a: vec2f) -> vec2f {
|
|
2477
|
+
let decoded = fp64_decode_f32_bits(bitcast<u32>(a.x));
|
|
2478
|
+
let decodedLow = fp64_decode_f32_bits(bitcast<u32>(a.y));
|
|
2479
|
+
if (decoded.isZero && decodedLow.isZero) {
|
|
2480
|
+
return vec2f(0.0, 0.0);
|
|
2481
|
+
}
|
|
2482
|
+
if (decoded.sign == 1u) {
|
|
2483
|
+
let nanValue = fp64_nan(a.x);
|
|
2484
|
+
return vec2f(nanValue, nanValue);
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2487
|
+
if (decoded.isInf || decoded.isNan) {
|
|
2488
|
+
return fp64_sqrt_fp64_normalized(a);
|
|
2489
|
+
}
|
|
2490
|
+
let exponent = fp64_f32_finite_exponent(decoded);
|
|
2491
|
+
// An even scale lets the final square-root rescale use an integer exponent.
|
|
2492
|
+
let evenExponent = exponent - (exponent & 1);
|
|
2493
|
+
let normalizedA = fp64_scale_fp64_integer(a, -evenExponent);
|
|
2494
|
+
let normalizedRoot = fp64_sqrt_fp64_normalized(normalizedA);
|
|
2495
|
+
return fp64_scale_fp64_integer(normalizedRoot, evenExponent / 2);
|
|
2496
|
+
}
|
|
2497
|
+
#endif
|
|
2498
|
+
`
|
|
2499
|
+
);
|
|
2500
|
+
|
|
1676
2501
|
// ../../node_modules/@luma.gl/shadertools/dist/modules/math/fp64/fp64-arithmetic-wgsl.js
|
|
1677
2502
|
var fp64arithmeticWGSL = (
|
|
1678
2503
|
/* wgsl */
|
|
@@ -1683,6 +2508,7 @@ vec2 sqrt_fp64(vec2 a) {
|
|
|
1683
2508
|
|
|
1684
2509
|
@group(0) @binding(auto) var<uniform> fp64arithmetic : Fp64ArithmeticUniforms;
|
|
1685
2510
|
|
|
2511
|
+
#ifndef LUMA_FP64_F32_INPUT_ONLY
|
|
1686
2512
|
struct Fp64Bits {
|
|
1687
2513
|
sign: u32,
|
|
1688
2514
|
exponent: i32,
|
|
@@ -1691,11 +2517,14 @@ struct Fp64Bits {
|
|
|
1691
2517
|
isInf: bool,
|
|
1692
2518
|
isNan: bool,
|
|
1693
2519
|
};
|
|
2520
|
+
#endif
|
|
1694
2521
|
|
|
2522
|
+
#ifndef LUMA_FP64_PREDICATE_ONLY
|
|
1695
2523
|
fn fp64_nan(seed: f32) -> f32 {
|
|
1696
2524
|
let nanBits = 0x7fc00000u | select(0u, 1u, seed < 0.0);
|
|
1697
2525
|
return bitcast<f32>(nanBits);
|
|
1698
2526
|
}
|
|
2527
|
+
#endif
|
|
1699
2528
|
|
|
1700
2529
|
fn fp64_u64_is_zero(value: vec2u) -> bool {
|
|
1701
2530
|
return value.x == 0u && value.y == 0u;
|
|
@@ -1783,6 +2612,7 @@ fn fp64_u64_has_bits_below(value: vec2u, bitCount: u32) -> bool {
|
|
|
1783
2612
|
return (value.y & lowMask) != 0u;
|
|
1784
2613
|
}
|
|
1785
2614
|
|
|
2615
|
+
#ifndef LUMA_FP64_F32_INPUT_ONLY
|
|
1786
2616
|
fn fp64_u64_shift_right_sticky(value: vec2u, shift: u32) -> vec2u {
|
|
1787
2617
|
var shifted = fp64_u64_shift_right(value, shift);
|
|
1788
2618
|
if (fp64_u64_has_bits_below(value, shift)) {
|
|
@@ -1790,6 +2620,7 @@ fn fp64_u64_shift_right_sticky(value: vec2u, shift: u32) -> vec2u {
|
|
|
1790
2620
|
}
|
|
1791
2621
|
return shifted;
|
|
1792
2622
|
}
|
|
2623
|
+
#endif
|
|
1793
2624
|
|
|
1794
2625
|
fn fp64_u64_count_leading_zeros(value: vec2u) -> u32 {
|
|
1795
2626
|
if (value.x != 0u) {
|
|
@@ -1813,6 +2644,22 @@ fn fp64_round_shift_right_to_u32(value: vec2u, shift: u32) -> u32 {
|
|
|
1813
2644
|
return rounded;
|
|
1814
2645
|
}
|
|
1815
2646
|
|
|
2647
|
+
#ifndef LUMA_FP64_F32_INPUT_ONLY
|
|
2648
|
+
fn fp64_round_shift_right(value: vec2u, shift: u32) -> vec2u {
|
|
2649
|
+
if (shift == 0u) {
|
|
2650
|
+
return value;
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2653
|
+
var rounded = fp64_u64_shift_right(value, shift);
|
|
2654
|
+
let guard = fp64_u64_get_bit(value, shift - 1u);
|
|
2655
|
+
let hasTrailingBits = fp64_u64_has_bits_below(value, shift - 1u);
|
|
2656
|
+
if (guard && (hasTrailingBits || (rounded.y & 1u) == 1u)) {
|
|
2657
|
+
rounded = fp64_u64_add(rounded, vec2u(0u, 1u));
|
|
2658
|
+
}
|
|
2659
|
+
return rounded;
|
|
2660
|
+
}
|
|
2661
|
+
#endif
|
|
2662
|
+
|
|
1816
2663
|
fn fp64_make_f32_bits_from_u64(sign: u32, significand: vec2u, baseExponent: i32) -> u32 {
|
|
1817
2664
|
if (fp64_u64_is_zero(significand)) {
|
|
1818
2665
|
return sign << 31u;
|
|
@@ -1860,6 +2707,7 @@ fn fp64_make_f32_bits_from_u64(sign: u32, significand: vec2u, baseExponent: i32)
|
|
|
1860
2707
|
return (sign << 31u) | mantissa;
|
|
1861
2708
|
}
|
|
1862
2709
|
|
|
2710
|
+
#ifndef LUMA_FP64_F32_INPUT_ONLY
|
|
1863
2711
|
fn fp64_decode_bits(bits: vec2u) -> Fp64Bits {
|
|
1864
2712
|
let sign = bits.x >> 31u;
|
|
1865
2713
|
let exponentBits = (bits.x >> 20u) & 0x7ffu;
|
|
@@ -1886,6 +2734,261 @@ fn fp64_finite_magnitude_compare(a: Fp64Bits, b: Fp64Bits) -> i32 {
|
|
|
1886
2734
|
}
|
|
1887
2735
|
return fp64_u64_compare(a.significand, b.significand);
|
|
1888
2736
|
}
|
|
2737
|
+
#endif
|
|
2738
|
+
|
|
2739
|
+
#ifndef LUMA_FP64_F32_INPUT_ONLY
|
|
2740
|
+
struct Fp64RawF32Bits {
|
|
2741
|
+
sign: u32,
|
|
2742
|
+
baseExponent: i32,
|
|
2743
|
+
significand: u32,
|
|
2744
|
+
isZero: bool,
|
|
2745
|
+
isInf: bool,
|
|
2746
|
+
isNan: bool,
|
|
2747
|
+
};
|
|
2748
|
+
|
|
2749
|
+
// Decode an f32 as (-1)^sign * significand * 2^baseExponent. This shared
|
|
2750
|
+
// integer representation lets normalization remain independent of the
|
|
2751
|
+
// selected double-single arithmetic implementation.
|
|
2752
|
+
fn fp64_decode_raw_f32_bits(bits: u32) -> Fp64RawF32Bits {
|
|
2753
|
+
let sign = bits >> 31u;
|
|
2754
|
+
let exponentBits = (bits >> 23u) & 0xffu;
|
|
2755
|
+
let fraction = bits & 0x7fffffu;
|
|
2756
|
+
|
|
2757
|
+
if (exponentBits == 0xffu) {
|
|
2758
|
+
return Fp64RawF32Bits(sign, 0, 0u, false, fraction == 0u, fraction != 0u);
|
|
2759
|
+
}
|
|
2760
|
+
if (exponentBits == 0u) {
|
|
2761
|
+
return Fp64RawF32Bits(sign, -149, fraction, fraction == 0u, false, false);
|
|
2762
|
+
}
|
|
2763
|
+
return Fp64RawF32Bits(
|
|
2764
|
+
sign,
|
|
2765
|
+
i32(exponentBits) - 150,
|
|
2766
|
+
0x800000u | fraction,
|
|
2767
|
+
false,
|
|
2768
|
+
false,
|
|
2769
|
+
false
|
|
2770
|
+
);
|
|
2771
|
+
}
|
|
2772
|
+
|
|
2773
|
+
fn fp64_raw_f32_magnitude_compare(aBits: u32, bBits: u32) -> i32 {
|
|
2774
|
+
let aMagnitude = aBits & 0x7fffffffu;
|
|
2775
|
+
let bMagnitude = bBits & 0x7fffffffu;
|
|
2776
|
+
if (aMagnitude == bMagnitude) {
|
|
2777
|
+
return 0;
|
|
2778
|
+
}
|
|
2779
|
+
return select(-1, 1, aMagnitude > bMagnitude);
|
|
2780
|
+
}
|
|
2781
|
+
|
|
2782
|
+
fn fp64_make_raw_residual_f32_bits(
|
|
2783
|
+
exactSign: u32,
|
|
2784
|
+
exactMagnitude: vec2u,
|
|
2785
|
+
exactBaseExponent: i32,
|
|
2786
|
+
highBits: u32
|
|
2787
|
+
) -> u32 {
|
|
2788
|
+
if (fp64_u64_is_zero(exactMagnitude)) {
|
|
2789
|
+
return 0u;
|
|
2790
|
+
}
|
|
2791
|
+
|
|
2792
|
+
let high = fp64_decode_raw_f32_bits(highBits);
|
|
2793
|
+
if (high.isInf || high.isNan) {
|
|
2794
|
+
return 0u;
|
|
2795
|
+
}
|
|
2796
|
+
if (high.isZero) {
|
|
2797
|
+
return fp64_make_f32_bits_from_u64(exactSign, exactMagnitude, exactBaseExponent);
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
let commonBaseExponent = min(exactBaseExponent, high.baseExponent);
|
|
2801
|
+
let exactShift = exactBaseExponent - commonBaseExponent;
|
|
2802
|
+
let highShift = high.baseExponent - commonBaseExponent;
|
|
2803
|
+
if (exactShift >= 64 || highShift >= 64) {
|
|
2804
|
+
return 0u;
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
let exactAligned = fp64_u64_shift_left(exactMagnitude, u32(exactShift));
|
|
2808
|
+
let highAligned = fp64_u64_shift_left(vec2u(0u, high.significand), u32(highShift));
|
|
2809
|
+
let comparison = fp64_u64_compare(exactAligned, highAligned);
|
|
2810
|
+
if (comparison == 0) {
|
|
2811
|
+
return 0u;
|
|
2812
|
+
}
|
|
2813
|
+
|
|
2814
|
+
var residualSign = exactSign;
|
|
2815
|
+
var residualMagnitude: vec2u;
|
|
2816
|
+
if (comparison > 0) {
|
|
2817
|
+
residualMagnitude = fp64_u64_sub(exactAligned, highAligned);
|
|
2818
|
+
} else {
|
|
2819
|
+
residualSign = exactSign ^ 1u;
|
|
2820
|
+
residualMagnitude = fp64_u64_sub(highAligned, exactAligned);
|
|
2821
|
+
}
|
|
2822
|
+
return fp64_make_f32_bits_from_u64(
|
|
2823
|
+
residualSign,
|
|
2824
|
+
residualMagnitude,
|
|
2825
|
+
commonBaseExponent
|
|
2826
|
+
);
|
|
2827
|
+
}
|
|
2828
|
+
|
|
2829
|
+
fn fp64_split_raw_accumulator_bits(
|
|
2830
|
+
sign: u32,
|
|
2831
|
+
magnitude: vec2u,
|
|
2832
|
+
baseExponent: i32
|
|
2833
|
+
) -> vec2u {
|
|
2834
|
+
if (fp64_u64_is_zero(magnitude)) {
|
|
2835
|
+
return vec2u(0u);
|
|
2836
|
+
}
|
|
2837
|
+
let highBits = fp64_make_f32_bits_from_u64(sign, magnitude, baseExponent);
|
|
2838
|
+
let rawLowBits = fp64_make_raw_residual_f32_bits(sign, magnitude, baseExponent, highBits);
|
|
2839
|
+
let lowBits = select(rawLowBits, 0u, (rawLowBits & 0x7fffffffu) == 0u);
|
|
2840
|
+
if ((highBits & 0x7fffffffu) == 0u && (lowBits & 0x7fffffffu) == 0u) {
|
|
2841
|
+
return vec2u(0u);
|
|
2842
|
+
}
|
|
2843
|
+
return vec2u(highBits, lowBits);
|
|
2844
|
+
}
|
|
2845
|
+
#endif
|
|
2846
|
+
|
|
2847
|
+
#ifndef LUMA_FP64_F32_INPUT_ONLY
|
|
2848
|
+
// Round an arithmetic accumulator to binary64 before splitting it. The
|
|
2849
|
+
// aligned add/subtract paths retain three guard bits plus a sticky bit, which
|
|
2850
|
+
// is sufficient for round-to-nearest-even at the binary64 boundary.
|
|
2851
|
+
fn fp64_split_binary64_accumulator_bits(
|
|
2852
|
+
sign: u32,
|
|
2853
|
+
magnitude: vec2u,
|
|
2854
|
+
baseExponent: i32
|
|
2855
|
+
) -> vec2u {
|
|
2856
|
+
if (fp64_u64_is_zero(magnitude)) {
|
|
2857
|
+
return vec2u(0u);
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
let mostSignificantBit = 63u - fp64_u64_count_leading_zeros(magnitude);
|
|
2861
|
+
let exponent = baseExponent + i32(mostSignificantBit);
|
|
2862
|
+
if (exponent > 1023) {
|
|
2863
|
+
return vec2u((sign << 31u) | 0x7f800000u, 0u);
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2866
|
+
var roundedMagnitude = magnitude;
|
|
2867
|
+
var roundedBaseExponent = baseExponent;
|
|
2868
|
+
if (exponent >= -1022) {
|
|
2869
|
+
if (mostSignificantBit > 52u) {
|
|
2870
|
+
let shift = mostSignificantBit - 52u;
|
|
2871
|
+
roundedMagnitude = fp64_round_shift_right(magnitude, shift);
|
|
2872
|
+
roundedBaseExponent = baseExponent + i32(shift);
|
|
2873
|
+
}
|
|
2874
|
+
} else {
|
|
2875
|
+
let shift = -1074 - baseExponent;
|
|
2876
|
+
if (shift > 0) {
|
|
2877
|
+
roundedMagnitude = fp64_round_shift_right(magnitude, u32(shift));
|
|
2878
|
+
roundedBaseExponent = -1074;
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
if (fp64_u64_is_zero(roundedMagnitude)) {
|
|
2883
|
+
return vec2u(0u);
|
|
2884
|
+
}
|
|
2885
|
+
return fp64_split_raw_accumulator_bits(sign, roundedMagnitude, roundedBaseExponent);
|
|
2886
|
+
}
|
|
2887
|
+
#endif
|
|
2888
|
+
|
|
2889
|
+
#ifndef LUMA_FP64_PREDICATE_ONLY
|
|
2890
|
+
fn fp64_add_raw_f32_bits(aBits: u32, bBits: u32) -> vec2u {
|
|
2891
|
+
let a = fp64_decode_raw_f32_bits(aBits);
|
|
2892
|
+
let b = fp64_decode_raw_f32_bits(bBits);
|
|
2893
|
+
|
|
2894
|
+
if (a.isNan || b.isNan) {
|
|
2895
|
+
return vec2u(0x7fc00000u, 0u);
|
|
2896
|
+
}
|
|
2897
|
+
if (a.isInf || b.isInf) {
|
|
2898
|
+
if (a.isInf && b.isInf && a.sign != b.sign) {
|
|
2899
|
+
return vec2u(0x7fc00000u, 0u);
|
|
2900
|
+
}
|
|
2901
|
+
return select(vec2u(bBits, 0u), vec2u(aBits, 0u), a.isInf);
|
|
2902
|
+
}
|
|
2903
|
+
if (a.isZero && b.isZero) {
|
|
2904
|
+
return vec2u(0u);
|
|
2905
|
+
}
|
|
2906
|
+
if (a.isZero) {
|
|
2907
|
+
return vec2u(bBits, 0u);
|
|
2908
|
+
}
|
|
2909
|
+
if (b.isZero) {
|
|
2910
|
+
return vec2u(aBits, 0u);
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2913
|
+
let exponentDifference = abs(a.baseExponent - b.baseExponent);
|
|
2914
|
+
if (exponentDifference > 25) {
|
|
2915
|
+
if (fp64_raw_f32_magnitude_compare(aBits, bBits) >= 0) {
|
|
2916
|
+
return vec2u(aBits, bBits);
|
|
2917
|
+
}
|
|
2918
|
+
return vec2u(bBits, aBits);
|
|
2919
|
+
}
|
|
2920
|
+
|
|
2921
|
+
let commonBaseExponent = min(a.baseExponent, b.baseExponent);
|
|
2922
|
+
let aMagnitude = fp64_u64_shift_left(
|
|
2923
|
+
vec2u(0u, a.significand),
|
|
2924
|
+
u32(a.baseExponent - commonBaseExponent)
|
|
2925
|
+
);
|
|
2926
|
+
let bMagnitude = fp64_u64_shift_left(
|
|
2927
|
+
vec2u(0u, b.significand),
|
|
2928
|
+
u32(b.baseExponent - commonBaseExponent)
|
|
2929
|
+
);
|
|
2930
|
+
|
|
2931
|
+
var resultSign = a.sign;
|
|
2932
|
+
var resultMagnitude: vec2u;
|
|
2933
|
+
if (a.sign == b.sign) {
|
|
2934
|
+
resultMagnitude = fp64_u64_add(aMagnitude, bMagnitude);
|
|
2935
|
+
} else {
|
|
2936
|
+
let comparison = fp64_u64_compare(aMagnitude, bMagnitude);
|
|
2937
|
+
if (comparison == 0) {
|
|
2938
|
+
return vec2u(0u);
|
|
2939
|
+
}
|
|
2940
|
+
if (comparison > 0) {
|
|
2941
|
+
resultMagnitude = fp64_u64_sub(aMagnitude, bMagnitude);
|
|
2942
|
+
} else {
|
|
2943
|
+
resultSign = b.sign;
|
|
2944
|
+
resultMagnitude = fp64_u64_sub(bMagnitude, aMagnitude);
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
|
|
2948
|
+
return fp64_split_raw_accumulator_bits(
|
|
2949
|
+
resultSign,
|
|
2950
|
+
resultMagnitude,
|
|
2951
|
+
commonBaseExponent
|
|
2952
|
+
);
|
|
2953
|
+
}
|
|
2954
|
+
#endif
|
|
2955
|
+
|
|
2956
|
+
#ifndef LUMA_FP64_F32_INPUT_ONLY
|
|
2957
|
+
fn fp64_add_aligned_magnitudes_to_fp64_bits(
|
|
2958
|
+
sign: u32,
|
|
2959
|
+
larger: Fp64Bits,
|
|
2960
|
+
smaller: Fp64Bits
|
|
2961
|
+
) -> vec2u {
|
|
2962
|
+
let largeSignificand = fp64_u64_shift_left(larger.significand, 3u);
|
|
2963
|
+
let smallSignificand = fp64_u64_shift_right_sticky(
|
|
2964
|
+
fp64_u64_shift_left(smaller.significand, 3u),
|
|
2965
|
+
u32(larger.exponent - smaller.exponent)
|
|
2966
|
+
);
|
|
2967
|
+
let resultSignificand = fp64_u64_add(largeSignificand, smallSignificand);
|
|
2968
|
+
return fp64_split_binary64_accumulator_bits(
|
|
2969
|
+
sign,
|
|
2970
|
+
resultSignificand,
|
|
2971
|
+
larger.exponent - 55
|
|
2972
|
+
);
|
|
2973
|
+
}
|
|
2974
|
+
|
|
2975
|
+
fn fp64_sub_aligned_magnitudes_to_fp64_bits(
|
|
2976
|
+
sign: u32,
|
|
2977
|
+
larger: Fp64Bits,
|
|
2978
|
+
smaller: Fp64Bits
|
|
2979
|
+
) -> vec2u {
|
|
2980
|
+
let largeSignificand = fp64_u64_shift_left(larger.significand, 3u);
|
|
2981
|
+
let smallSignificand = fp64_u64_shift_right_sticky(
|
|
2982
|
+
fp64_u64_shift_left(smaller.significand, 3u),
|
|
2983
|
+
u32(larger.exponent - smaller.exponent)
|
|
2984
|
+
);
|
|
2985
|
+
let resultSignificand = fp64_u64_sub(largeSignificand, smallSignificand);
|
|
2986
|
+
return fp64_split_binary64_accumulator_bits(
|
|
2987
|
+
sign,
|
|
2988
|
+
resultSignificand,
|
|
2989
|
+
larger.exponent - 55
|
|
2990
|
+
);
|
|
2991
|
+
}
|
|
1889
2992
|
|
|
1890
2993
|
fn fp64_add_aligned_magnitudes_to_f32_bits(sign: u32, larger: Fp64Bits, smaller: Fp64Bits) -> u32 {
|
|
1891
2994
|
let largeSignificand = fp64_u64_shift_left(larger.significand, 3u);
|
|
@@ -1907,54 +3010,106 @@ fn fp64_sub_aligned_magnitudes_to_f32_bits(sign: u32, larger: Fp64Bits, smaller:
|
|
|
1907
3010
|
return fp64_make_f32_bits_from_u64(sign, resultSignificand, larger.exponent - 55);
|
|
1908
3011
|
}
|
|
1909
3012
|
|
|
1910
|
-
// Subtract two raw binary64 values and round the exact result once to f32.
|
|
1911
|
-
// The input words are canonical high/low words: .x contains sign/exponent/high
|
|
1912
|
-
// fraction bits, and .y contains the low 32 fraction bits.
|
|
1913
|
-
fn sub_fp64u32_to_f32_bits(aBits: vec2u, bBits: vec2u) -> u32 {
|
|
3013
|
+
// Subtract two raw binary64 values and round the exact result once to f32.
|
|
3014
|
+
// The input words are canonical high/low words: .x contains sign/exponent/high
|
|
3015
|
+
// fraction bits, and .y contains the low 32 fraction bits.
|
|
3016
|
+
fn sub_fp64u32_to_f32_bits(aBits: vec2u, bBits: vec2u) -> u32 {
|
|
3017
|
+
let a = fp64_decode_bits(aBits);
|
|
3018
|
+
let b = fp64_decode_bits(bBits);
|
|
3019
|
+
let bSubtractionSign = b.sign ^ 1u;
|
|
3020
|
+
|
|
3021
|
+
if (a.isNan || b.isNan) {
|
|
3022
|
+
return 0x7fc00000u;
|
|
3023
|
+
}
|
|
3024
|
+
if (a.isInf && b.isInf) {
|
|
3025
|
+
if (a.sign == bSubtractionSign) {
|
|
3026
|
+
return (a.sign << 31u) | 0x7f800000u;
|
|
3027
|
+
}
|
|
3028
|
+
return 0x7fc00000u;
|
|
3029
|
+
}
|
|
3030
|
+
if (a.isInf) {
|
|
3031
|
+
return (a.sign << 31u) | 0x7f800000u;
|
|
3032
|
+
}
|
|
3033
|
+
if (b.isInf) {
|
|
3034
|
+
return (bSubtractionSign << 31u) | 0x7f800000u;
|
|
3035
|
+
}
|
|
3036
|
+
if (a.isZero && b.isZero) {
|
|
3037
|
+
return select(0u, 0x80000000u, a.sign == 1u && b.sign == 0u);
|
|
3038
|
+
}
|
|
3039
|
+
|
|
3040
|
+
let magnitudeComparison = fp64_finite_magnitude_compare(a, b);
|
|
3041
|
+
if (a.sign == bSubtractionSign) {
|
|
3042
|
+
if (magnitudeComparison >= 0) {
|
|
3043
|
+
return fp64_add_aligned_magnitudes_to_f32_bits(a.sign, a, b);
|
|
3044
|
+
}
|
|
3045
|
+
return fp64_add_aligned_magnitudes_to_f32_bits(a.sign, b, a);
|
|
3046
|
+
}
|
|
3047
|
+
|
|
3048
|
+
if (magnitudeComparison == 0) {
|
|
3049
|
+
return 0u;
|
|
3050
|
+
}
|
|
3051
|
+
if (magnitudeComparison > 0) {
|
|
3052
|
+
return fp64_sub_aligned_magnitudes_to_f32_bits(a.sign, a, b);
|
|
3053
|
+
}
|
|
3054
|
+
return fp64_sub_aligned_magnitudes_to_f32_bits(bSubtractionSign, b, a);
|
|
3055
|
+
}
|
|
3056
|
+
|
|
3057
|
+
fn sub_fp64u32_to_f32(aBits: vec2u, bBits: vec2u) -> f32 {
|
|
3058
|
+
return bitcast<f32>(sub_fp64u32_to_f32_bits(aBits, bBits));
|
|
3059
|
+
}
|
|
3060
|
+
|
|
3061
|
+
// Subtract two raw binary64 values, round once to binary64, then split the
|
|
3062
|
+
// result into normalized f32 limbs. Finite results must fit within the f32
|
|
3063
|
+
// exponent range; larger magnitudes map to infinity and smaller magnitudes
|
|
3064
|
+
// map to zero. The input words use canonical high/low word order.
|
|
3065
|
+
fn sub_fp64u32_to_fp64_bits(aBits: vec2u, bBits: vec2u) -> vec2u {
|
|
1914
3066
|
let a = fp64_decode_bits(aBits);
|
|
1915
3067
|
let b = fp64_decode_bits(bBits);
|
|
1916
3068
|
let bSubtractionSign = b.sign ^ 1u;
|
|
1917
3069
|
|
|
1918
3070
|
if (a.isNan || b.isNan) {
|
|
1919
|
-
return 0x7fc00000u;
|
|
3071
|
+
return vec2u(0x7fc00000u, 0u);
|
|
1920
3072
|
}
|
|
1921
3073
|
if (a.isInf && b.isInf) {
|
|
1922
3074
|
if (a.sign == bSubtractionSign) {
|
|
1923
|
-
return (a.sign << 31u) | 0x7f800000u;
|
|
3075
|
+
return vec2u((a.sign << 31u) | 0x7f800000u, 0u);
|
|
1924
3076
|
}
|
|
1925
|
-
return 0x7fc00000u;
|
|
3077
|
+
return vec2u(0x7fc00000u, 0u);
|
|
1926
3078
|
}
|
|
1927
3079
|
if (a.isInf) {
|
|
1928
|
-
return (a.sign << 31u) | 0x7f800000u;
|
|
3080
|
+
return vec2u((a.sign << 31u) | 0x7f800000u, 0u);
|
|
1929
3081
|
}
|
|
1930
3082
|
if (b.isInf) {
|
|
1931
|
-
return (bSubtractionSign << 31u) | 0x7f800000u;
|
|
3083
|
+
return vec2u((bSubtractionSign << 31u) | 0x7f800000u, 0u);
|
|
1932
3084
|
}
|
|
1933
3085
|
if (a.isZero && b.isZero) {
|
|
1934
|
-
return
|
|
3086
|
+
return vec2u(0u);
|
|
1935
3087
|
}
|
|
1936
3088
|
|
|
1937
3089
|
let magnitudeComparison = fp64_finite_magnitude_compare(a, b);
|
|
1938
3090
|
if (a.sign == bSubtractionSign) {
|
|
1939
3091
|
if (magnitudeComparison >= 0) {
|
|
1940
|
-
return
|
|
3092
|
+
return fp64_add_aligned_magnitudes_to_fp64_bits(a.sign, a, b);
|
|
1941
3093
|
}
|
|
1942
|
-
return
|
|
3094
|
+
return fp64_add_aligned_magnitudes_to_fp64_bits(a.sign, b, a);
|
|
1943
3095
|
}
|
|
1944
3096
|
|
|
1945
3097
|
if (magnitudeComparison == 0) {
|
|
1946
|
-
return 0u;
|
|
3098
|
+
return vec2u(0u);
|
|
1947
3099
|
}
|
|
1948
3100
|
if (magnitudeComparison > 0) {
|
|
1949
|
-
return
|
|
3101
|
+
return fp64_sub_aligned_magnitudes_to_fp64_bits(a.sign, a, b);
|
|
1950
3102
|
}
|
|
1951
|
-
return
|
|
3103
|
+
return fp64_sub_aligned_magnitudes_to_fp64_bits(bSubtractionSign, b, a);
|
|
1952
3104
|
}
|
|
1953
3105
|
|
|
1954
|
-
fn
|
|
1955
|
-
|
|
3106
|
+
fn sub_fp64u32_to_fp64(aBits: vec2u, bBits: vec2u) -> vec2f {
|
|
3107
|
+
let resultBits = sub_fp64u32_to_fp64_bits(aBits, bBits);
|
|
3108
|
+
return vec2f(bitcast<f32>(resultBits.x), bitcast<f32>(resultBits.y));
|
|
1956
3109
|
}
|
|
3110
|
+
#endif
|
|
1957
3111
|
|
|
3112
|
+
#ifndef LUMA_FP64_PREDICATE_ONLY
|
|
1958
3113
|
fn fp64_runtime_zero() -> f32 {
|
|
1959
3114
|
return fp64arithmetic.ONE * 0.0;
|
|
1960
3115
|
}
|
|
@@ -1966,7 +3121,11 @@ fn prevent_fp64_optimization(value: f32) -> f32 {
|
|
|
1966
3121
|
return value;
|
|
1967
3122
|
#endif
|
|
1968
3123
|
}
|
|
3124
|
+
#endif
|
|
1969
3125
|
|
|
3126
|
+
#ifdef LUMA_FP64_INTEGER_ARITHMETIC
|
|
3127
|
+
${fp64arithmeticWGSLInteger}
|
|
3128
|
+
#else
|
|
1970
3129
|
fn split(a: f32) -> vec2f {
|
|
1971
3130
|
let splitValue = prevent_fp64_optimization(fp64arithmetic.SPLIT + fp64_runtime_zero());
|
|
1972
3131
|
let t = prevent_fp64_optimization(a * splitValue);
|
|
@@ -2117,6 +3276,7 @@ fn mul_fp64(a: vec2f, b: vec2f) -> vec2f {
|
|
|
2117
3276
|
return prod;
|
|
2118
3277
|
}
|
|
2119
3278
|
|
|
3279
|
+
#ifndef LUMA_FP64_PREDICATE_ONLY
|
|
2120
3280
|
fn div_fp64(a: vec2f, b: vec2f) -> vec2f {
|
|
2121
3281
|
let xn = prevent_fp64_optimization(1.0 / b.x);
|
|
2122
3282
|
let yn = mul_fp64(a, vec2f(xn, fp64_runtime_zero()));
|
|
@@ -2149,6 +3309,97 @@ fn sqrt_fp64(a: vec2f) -> vec2f {
|
|
|
2149
3309
|
return sum_fp64(vec2f(yn, 0.0), prod);
|
|
2150
3310
|
#endif
|
|
2151
3311
|
}
|
|
3312
|
+
#endif
|
|
3313
|
+
#endif
|
|
3314
|
+
|
|
3315
|
+
#ifndef LUMA_FP64_PREDICATE_ONLY
|
|
3316
|
+
fn fp64_f32_bits_is_nan(bits: u32) -> bool {
|
|
3317
|
+
return (bits & 0x7fffffffu) > 0x7f800000u;
|
|
3318
|
+
}
|
|
3319
|
+
|
|
3320
|
+
fn fp64_f32_bits_is_inf(bits: u32) -> bool {
|
|
3321
|
+
return (bits & 0x7fffffffu) == 0x7f800000u;
|
|
3322
|
+
}
|
|
3323
|
+
|
|
3324
|
+
fn fp64_compare_f32_bits(aBits: u32, bBits: u32) -> i32 {
|
|
3325
|
+
let aMagnitude = aBits & 0x7fffffffu;
|
|
3326
|
+
let bMagnitude = bBits & 0x7fffffffu;
|
|
3327
|
+
if (aMagnitude == 0u && bMagnitude == 0u) {
|
|
3328
|
+
return 0;
|
|
3329
|
+
}
|
|
3330
|
+
let aSign = aBits >> 31u;
|
|
3331
|
+
let bSign = bBits >> 31u;
|
|
3332
|
+
if (aSign != bSign) {
|
|
3333
|
+
return select(1, -1, aSign == 1u);
|
|
3334
|
+
}
|
|
3335
|
+
if (aMagnitude == bMagnitude) {
|
|
3336
|
+
return 0;
|
|
3337
|
+
}
|
|
3338
|
+
let magnitudeComparison = select(-1, 1, aMagnitude > bMagnitude);
|
|
3339
|
+
return select(magnitudeComparison, -magnitudeComparison, aSign == 1u);
|
|
3340
|
+
}
|
|
3341
|
+
|
|
3342
|
+
// Normalize an arbitrary pair of finite f32 limbs with integer accumulation.
|
|
3343
|
+
// This is independent of LUMA_FP64_INTEGER_ARITHMETIC and canonicalizes every
|
|
3344
|
+
// representation of zero to vec2f(+0.0, +0.0).
|
|
3345
|
+
fn normalize_fp64(value: vec2f) -> vec2f {
|
|
3346
|
+
let resultBits = fp64_add_raw_f32_bits(bitcast<u32>(value.x), bitcast<u32>(value.y));
|
|
3347
|
+
return vec2f(bitcast<f32>(resultBits.x), bitcast<f32>(resultBits.y));
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
fn is_nan_fp64(value: vec2f) -> bool {
|
|
3351
|
+
let normalized = normalize_fp64(value);
|
|
3352
|
+
return fp64_f32_bits_is_nan(bitcast<u32>(normalized.x)) ||
|
|
3353
|
+
fp64_f32_bits_is_nan(bitcast<u32>(normalized.y));
|
|
3354
|
+
}
|
|
3355
|
+
|
|
3356
|
+
fn is_finite_fp64(value: vec2f) -> bool {
|
|
3357
|
+
let normalized = normalize_fp64(value);
|
|
3358
|
+
let highBits = bitcast<u32>(normalized.x);
|
|
3359
|
+
let lowBits = bitcast<u32>(normalized.y);
|
|
3360
|
+
return !fp64_f32_bits_is_nan(highBits) && !fp64_f32_bits_is_nan(lowBits) &&
|
|
3361
|
+
!fp64_f32_bits_is_inf(highBits) && !fp64_f32_bits_is_inf(lowBits);
|
|
3362
|
+
}
|
|
3363
|
+
|
|
3364
|
+
// Returns -1, 0, or 1. NaN is unordered and returns 0; call is_nan_fp64 or
|
|
3365
|
+
// is_finite_fp64 first when 0 must mean a finite zero.
|
|
3366
|
+
fn sign_fp64(value: vec2f) -> i32 {
|
|
3367
|
+
let normalized = normalize_fp64(value);
|
|
3368
|
+
let highBits = bitcast<u32>(normalized.x);
|
|
3369
|
+
let lowBits = bitcast<u32>(normalized.y);
|
|
3370
|
+
if (fp64_f32_bits_is_nan(highBits) || fp64_f32_bits_is_nan(lowBits)) {
|
|
3371
|
+
return 0;
|
|
3372
|
+
}
|
|
3373
|
+
if ((highBits & 0x7fffffffu) != 0u) {
|
|
3374
|
+
return select(1, -1, (highBits >> 31u) == 1u);
|
|
3375
|
+
}
|
|
3376
|
+
if ((lowBits & 0x7fffffffu) != 0u) {
|
|
3377
|
+
return select(1, -1, (lowBits >> 31u) == 1u);
|
|
3378
|
+
}
|
|
3379
|
+
return 0;
|
|
3380
|
+
}
|
|
3381
|
+
|
|
3382
|
+
// Compares double-single values and returns -1, 0, or 1. NaN is unordered
|
|
3383
|
+
// and returns 0; callers that require equality semantics must first check
|
|
3384
|
+
// is_nan_fp64 or is_finite_fp64.
|
|
3385
|
+
fn compare_fp64(a: vec2f, b: vec2f) -> i32 {
|
|
3386
|
+
let normalizedA = normalize_fp64(a);
|
|
3387
|
+
let normalizedB = normalize_fp64(b);
|
|
3388
|
+
let aHighBits = bitcast<u32>(normalizedA.x);
|
|
3389
|
+
let aLowBits = bitcast<u32>(normalizedA.y);
|
|
3390
|
+
let bHighBits = bitcast<u32>(normalizedB.x);
|
|
3391
|
+
let bLowBits = bitcast<u32>(normalizedB.y);
|
|
3392
|
+
if (fp64_f32_bits_is_nan(aHighBits) || fp64_f32_bits_is_nan(aLowBits) ||
|
|
3393
|
+
fp64_f32_bits_is_nan(bHighBits) || fp64_f32_bits_is_nan(bLowBits)) {
|
|
3394
|
+
return 0;
|
|
3395
|
+
}
|
|
3396
|
+
let highComparison = fp64_compare_f32_bits(aHighBits, bHighBits);
|
|
3397
|
+
if (highComparison != 0) {
|
|
3398
|
+
return highComparison;
|
|
3399
|
+
}
|
|
3400
|
+
return fp64_compare_f32_bits(aLowBits, bLowBits);
|
|
3401
|
+
}
|
|
3402
|
+
#endif
|
|
2152
3403
|
`
|
|
2153
3404
|
);
|
|
2154
3405
|
|
|
@@ -2972,14 +4223,14 @@ vec4 project_position_to_clipspace(
|
|
|
2972
4223
|
var getMemoizedUniforms = (0, import_core5._memoize)(calculateUniforms);
|
|
2973
4224
|
function getUniforms2(opts) {
|
|
2974
4225
|
if (opts && "viewport" in opts) {
|
|
2975
|
-
const { viewProjectionMatrix, scale:
|
|
2976
|
-
return getMemoizedUniforms({ viewProjectionMatrix, scale:
|
|
4226
|
+
const { viewProjectionMatrix, scale: scale3 } = opts.viewport;
|
|
4227
|
+
return getMemoizedUniforms({ viewProjectionMatrix, scale: scale3 });
|
|
2977
4228
|
}
|
|
2978
4229
|
return {};
|
|
2979
4230
|
}
|
|
2980
4231
|
function calculateUniforms({
|
|
2981
4232
|
viewProjectionMatrix,
|
|
2982
|
-
scale:
|
|
4233
|
+
scale: scale3
|
|
2983
4234
|
}) {
|
|
2984
4235
|
const glViewProjectionMatrixFP64 = fp64ifyMatrix42(viewProjectionMatrix);
|
|
2985
4236
|
const viewProjectionMatrix64High = new Float32Array(16);
|
|
@@ -2993,7 +4244,7 @@ vec4 project_position_to_clipspace(
|
|
|
2993
4244
|
}
|
|
2994
4245
|
}
|
|
2995
4246
|
return {
|
|
2996
|
-
scale: fp64ify2(
|
|
4247
|
+
scale: fp64ify2(scale3),
|
|
2997
4248
|
viewProjectionMatrix: [...viewProjectionMatrix64High],
|
|
2998
4249
|
viewProjectionMatrix64Low: [...viewProjectionMatrix64Low]
|
|
2999
4250
|
};
|
|
@@ -3026,27 +4277,121 @@ vec4 project_position_to_clipspace(
|
|
|
3026
4277
|
"vs:#decl": `
|
|
3027
4278
|
in vec2 instanceDashArrays;
|
|
3028
4279
|
#ifdef HIGH_PRECISION_DASH
|
|
3029
|
-
in
|
|
4280
|
+
// [distance from the start of the path, total length of the path], in common space.
|
|
4281
|
+
in vec2 instanceDashOffsets;
|
|
3030
4282
|
#endif
|
|
3031
4283
|
out vec2 vDashArray;
|
|
3032
4284
|
out float vDashOffset;
|
|
4285
|
+
out float vDashPathLength;
|
|
4286
|
+
|
|
4287
|
+
// Also declared in the fragment stage. The vertex stage needs dashAlignMode so that it can
|
|
4288
|
+
// reduce the dash phase modulo the same period the fragment stage will test against, and
|
|
4289
|
+
// dashUnits to scale the dash array into that period's units.
|
|
4290
|
+
layout(std140) uniform pathStyleUniforms {
|
|
4291
|
+
float dashAlignMode;
|
|
4292
|
+
bool dashGapPickable;
|
|
4293
|
+
highp int dashUnits;
|
|
4294
|
+
} pathStyle;
|
|
3033
4295
|
`,
|
|
3034
4296
|
"vs:#main-end": `
|
|
3035
|
-
|
|
4297
|
+
// How many screen pixels one unit of vPathPosition.y covers. The two extrusion branches of
|
|
4298
|
+
// the path layer leave this in different units, and both differences have to be undone here
|
|
4299
|
+
// for a billboarded path to dash like a flat one.
|
|
4300
|
+
//
|
|
4301
|
+
// Flat paths extrude in common space, so \`width\` is in common units and a unit of
|
|
4302
|
+
// vPathPosition.y is width.x * project.scale pixels.
|
|
4303
|
+
//
|
|
4304
|
+
// Billboarded paths extrude in clip space, so \`width\` is already in pixels at this point -
|
|
4305
|
+
// the clip-space conversion happens inside getLineJoinOffset. That conversion also folds in
|
|
4306
|
+
// project.focalDistance, which scales the half-width the segment delta is divided by but not
|
|
4307
|
+
// the delta itself, so a unit of vPathPosition.y ends up focalDistance pixels wider than the
|
|
4308
|
+
// stroke it is supposed to be relative to.
|
|
4309
|
+
float dashWidthPixels = path.billboard
|
|
4310
|
+
? width.x * project.focalDistance
|
|
4311
|
+
: width.x * project.scale;
|
|
4312
|
+
|
|
4313
|
+
// The actual half stroke width on screen, which is what 'widths' is relative to. It differs
|
|
4314
|
+
// from dashWidthPixels by exactly the spurious focalDistance above, so expressing 'widths'
|
|
4315
|
+
// as a ratio of the two corrects the billboard case onto the flat one - getDashArray is
|
|
4316
|
+
// documented relative to the stroke, and flat paths already honor that.
|
|
4317
|
+
float strokeHalfWidthPixels = path.billboard ? width.x : width.x * project.scale;
|
|
4318
|
+
|
|
4319
|
+
// Everything reduces to "how many screen pixels is one dash unit", divided through by the
|
|
4320
|
+
// pixels per unit of vPathPosition.y, so the fragment shader needs no notion of units at all.
|
|
4321
|
+
// Keep the cases in sync with DASH_UNITS in path-style-extension.ts.
|
|
4322
|
+
float dashUnitPixels = strokeHalfWidthPixels;
|
|
4323
|
+
if (pathStyle.dashUnits == 1) {
|
|
4324
|
+
dashUnitPixels = 1.0;
|
|
4325
|
+
} else if (pathStyle.dashUnits == 2) {
|
|
4326
|
+
dashUnitPixels = project_size_to_pixel(1.0);
|
|
4327
|
+
} else if (pathStyle.dashUnits == 3) {
|
|
4328
|
+
dashUnitPixels = project.scale;
|
|
4329
|
+
}
|
|
4330
|
+
vDashArray = instanceDashArrays * (dashUnitPixels / dashWidthPixels);
|
|
4331
|
+
|
|
3036
4332
|
#ifdef HIGH_PRECISION_DASH
|
|
3037
|
-
|
|
4333
|
+
// instanceDashOffsets accumulates common-space distance on the CPU. Convert it to pixels, then
|
|
4334
|
+
// divide by the pixel half-width so both extrusion branches use the same dash coordinate.
|
|
4335
|
+
vec2 dashOffsetAndLength = (instanceDashOffsets * project.scale) / dashWidthPixels;
|
|
4336
|
+
vDashPathLength = dashOffsetAndLength.y;
|
|
4337
|
+
|
|
4338
|
+
// Reduce the phase into the first period here rather than in the fragment shader. On a long
|
|
4339
|
+
// path at high zoom the raw offset reaches into the millions, and adding the much smaller
|
|
4340
|
+
// vPathPosition.y to it in fp32 loses the latter entirely, freezing the pattern mid-segment.
|
|
4341
|
+
// The dash function is periodic, so this discards nothing - but it has to be reduced modulo
|
|
4342
|
+
// whichever period the fragment stage will actually test against, hence dashAlignMode here.
|
|
4343
|
+
float dashUnitLength = vDashArray.x + vDashArray.y;
|
|
4344
|
+
float dashPeriod = pathStyle.dashAlignMode == 0.0
|
|
4345
|
+
? dashUnitLength
|
|
4346
|
+
: vDashPathLength / max(round(vDashPathLength / max(dashUnitLength, 0.0001)), 1.0);
|
|
4347
|
+
vDashOffset = dashPeriod > 0.0 ? mod(dashOffsetAndLength.x, dashPeriod) : 0.0;
|
|
3038
4348
|
#else
|
|
3039
4349
|
vDashOffset = 0.0;
|
|
4350
|
+
vDashPathLength = 0.0;
|
|
3040
4351
|
#endif
|
|
3041
4352
|
`,
|
|
3042
4353
|
"fs:#decl": `
|
|
3043
4354
|
layout(std140) uniform pathStyleUniforms {
|
|
3044
4355
|
float dashAlignMode;
|
|
3045
4356
|
bool dashGapPickable;
|
|
4357
|
+
highp int dashUnits;
|
|
3046
4358
|
} pathStyle;
|
|
3047
4359
|
|
|
3048
4360
|
in vec2 vDashArray;
|
|
3049
4361
|
in float vDashOffset;
|
|
4362
|
+
in float vDashPathLength;
|
|
4363
|
+
|
|
4364
|
+
// Integral of the dash square wave from 0 to position, i.e. how much solid stroke lies before it.
|
|
4365
|
+
float dashPatternIntegral(float position, float solidLength, float unitLength) {
|
|
4366
|
+
return floor(position / unitLength) * solidLength +
|
|
4367
|
+
min(mod(position, unitLength), solidLength);
|
|
4368
|
+
}
|
|
4369
|
+
|
|
4370
|
+
// Fraction of the filter-width interval centered on position that is covered by solid stroke.
|
|
4371
|
+
//
|
|
4372
|
+
// Testing the dash pattern with a single comparison per fragment aliases as soon as one
|
|
4373
|
+
// period approaches one pixel: the stroke breaks into moire or, when the phase happens to
|
|
4374
|
+
// land inside a dash, reads as solid. Integrating the wave over the fragment footprint
|
|
4375
|
+
// instead is the closed form of what a mipmapped dash texture approximates, and it degrades
|
|
4376
|
+
// the way one wants - once a period drops below a pixel the result converges on the duty
|
|
4377
|
+
// cycle solidLength / unitLength, so the stroke fades to a uniformly lighter line.
|
|
4378
|
+
//
|
|
4379
|
+
// position is reduced into the first period first. The integral satisfies
|
|
4380
|
+
// F(position + periodIndex * unitLength) = F(position) + periodIndex * solidLength,
|
|
4381
|
+
// so the same periodIndex cancels out of the
|
|
4382
|
+
// difference below, and keeping the arguments small avoids subtracting two large nearly
|
|
4383
|
+
// equal numbers in fp32 on long paths.
|
|
4384
|
+
float dashPatternCoverage(
|
|
4385
|
+
float position,
|
|
4386
|
+
float solidLength,
|
|
4387
|
+
float unitLength,
|
|
4388
|
+
float filterWidth
|
|
4389
|
+
) {
|
|
4390
|
+
float halfFilter = 0.5 * filterWidth;
|
|
4391
|
+
float reducedPosition = mod(position, unitLength);
|
|
4392
|
+
return (dashPatternIntegral(reducedPosition + halfFilter, solidLength, unitLength) -
|
|
4393
|
+
dashPatternIntegral(reducedPosition - halfFilter, solidLength, unitLength)) / filterWidth;
|
|
4394
|
+
}
|
|
3050
4395
|
`,
|
|
3051
4396
|
// if given position is in the gap part of the dashed line
|
|
3052
4397
|
// dashArray.x: solid stroke length, relative to width
|
|
@@ -3057,41 +4402,130 @@ in float vDashOffset;
|
|
|
3057
4402
|
// 1 - stretch to fit, draw half dash at each end for nicer joints
|
|
3058
4403
|
// o-- ---- ---- ---- --o-- --o-- ---- --o
|
|
3059
4404
|
"fs:#main-start": `
|
|
4405
|
+
float dashCoverage = 1.0;
|
|
4406
|
+
bool shouldDiscardDash = false;
|
|
4407
|
+
bool inRoundedDashGap = false;
|
|
4408
|
+
float roundedDashResolvedCoverage = 1.0;
|
|
4409
|
+
float roundedDashSubPixelBlend = 0.0;
|
|
4410
|
+
float roundedDashDutyCycle = 1.0;
|
|
4411
|
+
|
|
3060
4412
|
float solidLength = vDashArray.x;
|
|
3061
4413
|
float gapLength = vDashArray.y;
|
|
3062
4414
|
float unitLength = solidLength + gapLength;
|
|
3063
4415
|
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
if (unitLength > 0.0) {
|
|
4416
|
+
if (unitLength > 0.0 && gapLength > 0.0) {
|
|
4417
|
+
float offset;
|
|
3067
4418
|
if (pathStyle.dashAlignMode == 0.0) {
|
|
3068
4419
|
offset = vDashOffset;
|
|
3069
4420
|
} else {
|
|
3070
|
-
|
|
4421
|
+
// Justified: stretch the period so a whole number of them spans the run, and start
|
|
4422
|
+
// half a dash in so both ends finish on a joint. Rounding up to at least one period
|
|
4423
|
+
// matters - a run shorter than half a period used to round to zero, which made
|
|
4424
|
+
// unitLength infinite and rendered it solid.
|
|
4425
|
+
#ifdef HIGH_PRECISION_DASH
|
|
4426
|
+
// Justify across the entire path rather than each segment separately, so the gaps stay
|
|
4427
|
+
// even instead of being stretched by a different amount on every segment. vDashOffset
|
|
4428
|
+
// already carries the distance to the start of this segment, reduced modulo this same
|
|
4429
|
+
// period in the vertex shader.
|
|
4430
|
+
unitLength = vDashPathLength / max(round(vDashPathLength / unitLength), 1.0);
|
|
4431
|
+
#else
|
|
4432
|
+
unitLength = vPathLength / max(round(vPathLength / unitLength), 1.0);
|
|
4433
|
+
#endif
|
|
4434
|
+
// A short segment or path can make the fitted period shorter than the requested dash.
|
|
4435
|
+
// Bound the solid interval to that period so coverage and duty cycle remain valid.
|
|
4436
|
+
solidLength = min(solidLength, unitLength);
|
|
3071
4437
|
offset = solidLength / 2.0;
|
|
4438
|
+
#ifdef HIGH_PRECISION_DASH
|
|
4439
|
+
offset += vDashOffset;
|
|
4440
|
+
#endif
|
|
3072
4441
|
}
|
|
3073
4442
|
|
|
3074
|
-
float
|
|
4443
|
+
float alongPath = vPathPosition.y + offset;
|
|
4444
|
+
float unitOffset = mod(alongPath, unitLength);
|
|
4445
|
+
float filterWidth = max(fwidth(alongPath), 0.0001);
|
|
3075
4446
|
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
4447
|
+
// Picking stays a hard in-or-out test. A blended picking colour decodes to the wrong
|
|
4448
|
+
// index, and dashGapPickable is defined in terms of whole gaps rather than coverage.
|
|
4449
|
+
if (bool(picking.isActive)) {
|
|
4450
|
+
bool inGap = unitOffset > solidLength;
|
|
4451
|
+
// Only measure the rounded end-cap distance after the hard interval test has found a
|
|
4452
|
+
// gap. Inside a solid interval the longitudinal distance is negative; taking its
|
|
4453
|
+
// vector length would turn it positive and incorrectly discard the middle of a dash.
|
|
4454
|
+
if (inGap && path.capType > 0.5) {
|
|
4455
|
+
inGap = length(vec2(
|
|
3084
4456
|
min(unitOffset - solidLength, unitLength - unitOffset),
|
|
3085
4457
|
vPathPosition.x
|
|
3086
|
-
));
|
|
3087
|
-
if (distToEnd > 1.0) {
|
|
3088
|
-
if (!(pathStyle.dashGapPickable && bool(picking.isActive))) {
|
|
3089
|
-
discard;
|
|
3090
|
-
}
|
|
3091
|
-
}
|
|
4458
|
+
)) > 1.0;
|
|
3092
4459
|
}
|
|
4460
|
+
if (inGap && !pathStyle.dashGapPickable) {
|
|
4461
|
+
shouldDiscardDash = true;
|
|
4462
|
+
}
|
|
4463
|
+
} else if (path.capType <= 0.5) {
|
|
4464
|
+
dashCoverage = dashPatternCoverage(alongPath, solidLength, unitLength, filterWidth);
|
|
4465
|
+
} else {
|
|
4466
|
+
// Rounded caps: the dash end is an arc, so resolve the 2D distance to the nearer solid
|
|
4467
|
+
// end rather than the 1D position along the path. Only filter fragments in the gap;
|
|
4468
|
+
// PathLayer already antialiases the ordinary sides of the solid stroke.
|
|
4469
|
+
float distanceAlongGap = min(unitOffset - solidLength, unitLength - unitOffset);
|
|
4470
|
+
// Clamp before taking the derivative so the distance stays continuous across the
|
|
4471
|
+
// wrapped period boundary. Evaluate fwidth for every fragment in the quad; derivatives
|
|
4472
|
+
// are undefined inside the non-uniform gap branch below.
|
|
4473
|
+
float distanceToEnd = length(vec2(max(distanceAlongGap, 0.0), vPathPosition.x));
|
|
4474
|
+
float capEdgePixels = (1.0 - distanceToEnd) / max(fwidth(distanceToEnd), 1e-6);
|
|
4475
|
+
if (distanceAlongGap > 0.0) {
|
|
4476
|
+
inRoundedDashGap = true;
|
|
4477
|
+
roundedDashResolvedCoverage = smoothedge(0.0, capEdgePixels);
|
|
4478
|
+
dashCoverage = roundedDashResolvedCoverage;
|
|
4479
|
+
}
|
|
4480
|
+
// That smoothstep resolves one dash end at a time, so it stops meaning anything once a
|
|
4481
|
+
// whole period fits inside the filter footprint. Start fading only at that boundary;
|
|
4482
|
+
// blending resolvable periods would attenuate the solid body of every rounded dash.
|
|
4483
|
+
float subPixelBlend = smoothstep(unitLength, 2.0 * unitLength, filterWidth);
|
|
4484
|
+
roundedDashSubPixelBlend = subPixelBlend;
|
|
4485
|
+
// At sub-pixel scale, preserve the area of the repeated capsule rather than falling
|
|
4486
|
+
// back to the rectangular duty cycle. Each scanline contains the solid body plus the
|
|
4487
|
+
// two circular cap intrusions, capped when neighboring caps overlap across the gap.
|
|
4488
|
+
float boundedSolidLength = min(solidLength, unitLength);
|
|
4489
|
+
float effectiveGap = max(unitLength - boundedSolidLength, 0.0);
|
|
4490
|
+
float capSpan = 2.0 * sqrt(max(1.0 - vPathPosition.x * vPathPosition.x, 0.0));
|
|
4491
|
+
float roundedDutyCycle = clamp(
|
|
4492
|
+
(boundedSolidLength + min(effectiveGap, capSpan)) / unitLength,
|
|
4493
|
+
0.0,
|
|
4494
|
+
1.0
|
|
4495
|
+
);
|
|
4496
|
+
roundedDashDutyCycle = roundedDutyCycle;
|
|
4497
|
+
dashCoverage = mix(dashCoverage, roundedDutyCycle, subPixelBlend);
|
|
3093
4498
|
}
|
|
4499
|
+
|
|
4500
|
+
dashCoverage = clamp(dashCoverage, 0.0, 1.0);
|
|
4501
|
+
// Fully transparent fragments would still write depth and occlude whatever is behind.
|
|
4502
|
+
shouldDiscardDash = shouldDiscardDash || dashCoverage <= 0.0;
|
|
4503
|
+
}
|
|
4504
|
+
`,
|
|
4505
|
+
"fs:#main-end": `
|
|
4506
|
+
// PathLayer computes analytic-edge derivatives in its fragment body. A discard in
|
|
4507
|
+
// #main-start can remove helper invocations and make those derivatives undefined, so all
|
|
4508
|
+
// dash-related termination is deferred until the layer has completed that work.
|
|
4509
|
+
#ifdef ANTIALIASING
|
|
4510
|
+
if (inRoundedDashGap) {
|
|
4511
|
+
// The rounded dash cap and PathLayer silhouette are two geometric constraints on the
|
|
4512
|
+
// same fragment. Intersect their coverage instead of multiplying two edge ramps; at the
|
|
4513
|
+
// cap/body shoulder both ramps are half covered and multiplication would create a dark
|
|
4514
|
+
// quarter-covered notch. Keep the sub-pixel duty cycle separable from the path silhouette.
|
|
4515
|
+
float pathCoverage = smoothedge(0.0, edgePixels);
|
|
4516
|
+
float resolvedCapMultiplier =
|
|
4517
|
+
min(pathCoverage, roundedDashResolvedCoverage) / max(pathCoverage, 1e-6);
|
|
4518
|
+
dashCoverage = mix(
|
|
4519
|
+
resolvedCapMultiplier,
|
|
4520
|
+
roundedDashDutyCycle,
|
|
4521
|
+
roundedDashSubPixelBlend
|
|
4522
|
+
);
|
|
4523
|
+
}
|
|
4524
|
+
#endif
|
|
4525
|
+
if (shouldDiscardDash) {
|
|
4526
|
+
discard;
|
|
3094
4527
|
}
|
|
4528
|
+
fragColor.a *= dashCoverage;
|
|
3095
4529
|
`
|
|
3096
4530
|
}
|
|
3097
4531
|
};
|
|
@@ -3313,13 +4747,42 @@ in float instanceOffsets;
|
|
|
3313
4747
|
vPathPosition.x = (vPathPosition.x + offsetDir) * offsetWidth - offsetDir;
|
|
3314
4748
|
vPathPosition.y *= offsetWidth;
|
|
3315
4749
|
vPathLength *= offsetWidth;
|
|
4750
|
+
#ifdef DASH_ENABLED
|
|
4751
|
+
// DECKGL_FILTER_SIZE above widened the stroke by offsetWidth, so these rescalings restore
|
|
4752
|
+
// units of the original half-width. The dash block merges ahead of this one.
|
|
4753
|
+
if (pathStyle.dashUnits != 0) {
|
|
4754
|
+
// Absolute dash units must not inherit the artificial width used to build offset
|
|
4755
|
+
// geometry. Relative 'widths' units intentionally remain tied to the original stroke.
|
|
4756
|
+
vDashArray *= offsetWidth;
|
|
4757
|
+
}
|
|
4758
|
+
vPathBounds *= offsetWidth;
|
|
4759
|
+
#ifdef HIGH_PRECISION_DASH
|
|
4760
|
+
vDashPathLength *= offsetWidth;
|
|
4761
|
+
// The dash block reduced the CPU path offset modulo a period expressed in widened-width
|
|
4762
|
+
// units. Multiplying that remainder cannot recover the original phase after wrapping, so
|
|
4763
|
+
// redo the reduction with both the restored path offset and restored justified period.
|
|
4764
|
+
float restoredDashUnitLength = vDashArray.x + vDashArray.y;
|
|
4765
|
+
float restoredDashPeriod = pathStyle.dashAlignMode == 0.0
|
|
4766
|
+
? restoredDashUnitLength
|
|
4767
|
+
: vDashPathLength /
|
|
4768
|
+
max(round(vDashPathLength / max(restoredDashUnitLength, 0.0001)), 1.0);
|
|
4769
|
+
vDashOffset = restoredDashPeriod > 0.0
|
|
4770
|
+
? mod(dashOffsetAndLength.x * offsetWidth, restoredDashPeriod)
|
|
4771
|
+
: 0.0;
|
|
4772
|
+
#else
|
|
4773
|
+
vDashOffset *= offsetWidth;
|
|
4774
|
+
#endif
|
|
4775
|
+
#endif
|
|
3316
4776
|
`,
|
|
3317
|
-
"fs:#main-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
4777
|
+
"fs:#main-end": `
|
|
4778
|
+
#ifndef ANTIALIASING
|
|
4779
|
+
// With analytic antialiasing, PathLayer evaluates this boundary using the remapped
|
|
4780
|
+
// vPathPosition and retains the complete centered coverage ramp. The hard clip remains for
|
|
4781
|
+
// the original non-AA path.
|
|
4782
|
+
if (abs(vPathPosition.x) > 1.0) {
|
|
3321
4783
|
discard;
|
|
3322
4784
|
}
|
|
4785
|
+
#endif
|
|
3323
4786
|
`
|
|
3324
4787
|
}
|
|
3325
4788
|
};
|
|
@@ -3329,15 +4792,73 @@ in float instanceOffsets;
|
|
|
3329
4792
|
getDashArray: { type: "accessor", value: [0, 0] },
|
|
3330
4793
|
getOffset: { type: "accessor", value: 0 },
|
|
3331
4794
|
dashJustified: false,
|
|
3332
|
-
dashGapPickable: false
|
|
4795
|
+
dashGapPickable: false,
|
|
4796
|
+
dashUnits: "widths"
|
|
3333
4797
|
};
|
|
4798
|
+
var DASH_UNITS = {
|
|
4799
|
+
widths: 0,
|
|
4800
|
+
pixels: 1,
|
|
4801
|
+
meters: 2,
|
|
4802
|
+
common: 3
|
|
4803
|
+
};
|
|
4804
|
+
var PATH_STYLE_ATTRIBUTES = ["instanceDashArrays", "instanceDashOffsets", "instanceOffsets"];
|
|
4805
|
+
function dashMetricsProjectionPropsChanged({
|
|
4806
|
+
props,
|
|
4807
|
+
oldProps
|
|
4808
|
+
}) {
|
|
4809
|
+
return !(0, import_core7._deepEqual)(props.modelMatrix, oldProps.modelMatrix, 2) || props.coordinateSystem !== oldProps.coordinateSystem || !(0, import_core7._deepEqual)(props.coordinateOrigin, oldProps.coordinateOrigin, 1);
|
|
4810
|
+
}
|
|
4811
|
+
function projectRenderedPathPosition(layer, position, projectUniforms) {
|
|
4812
|
+
const viewport = layer.context.viewport;
|
|
4813
|
+
if (!(viewport instanceof import_core7.WebMercatorViewport) || viewport.zoom < 12) {
|
|
4814
|
+
return layer.projectPosition(position, { autoOffset: false });
|
|
4815
|
+
}
|
|
4816
|
+
const positionWorld = vec4_exports.transformMat4(
|
|
4817
|
+
[],
|
|
4818
|
+
[position[0], position[1], position[2] || 0, 1],
|
|
4819
|
+
projectUniforms.modelMatrix
|
|
4820
|
+
);
|
|
4821
|
+
const coordinateSystem = layer.props.coordinateSystem === "default" ? "lnglat" : layer.props.coordinateSystem;
|
|
4822
|
+
if (coordinateSystem === "lnglat") {
|
|
4823
|
+
if (Math.abs(positionWorld[1] - projectUniforms.coordinateOrigin[1]) > 0.25) {
|
|
4824
|
+
let longitude = positionWorld[0];
|
|
4825
|
+
if (projectUniforms.wrapLongitude) {
|
|
4826
|
+
longitude = ((longitude + 180) % 360 + 360) % 360 - 180;
|
|
4827
|
+
}
|
|
4828
|
+
const commonPosition = viewport.projectPosition([longitude, positionWorld[1], 0]);
|
|
4829
|
+
return [
|
|
4830
|
+
commonPosition[0] - projectUniforms.commonOrigin[0],
|
|
4831
|
+
commonPosition[1] - projectUniforms.commonOrigin[1],
|
|
4832
|
+
positionWorld[2] * projectUniforms.commonUnitsPerMeter[2]
|
|
4833
|
+
];
|
|
4834
|
+
}
|
|
4835
|
+
vec3_exports.sub(positionWorld, positionWorld, projectUniforms.coordinateOrigin);
|
|
4836
|
+
} else if (coordinateSystem === "cartesian") {
|
|
4837
|
+
vec3_exports.sub(positionWorld, positionWorld, projectUniforms.coordinateOrigin);
|
|
4838
|
+
}
|
|
4839
|
+
const deltaLatitude = positionWorld[1];
|
|
4840
|
+
const scale3 = vec3_exports.scaleAndAdd(
|
|
4841
|
+
[],
|
|
4842
|
+
projectUniforms.commonUnitsPerWorldUnit,
|
|
4843
|
+
projectUniforms.commonUnitsPerWorldUnit2,
|
|
4844
|
+
deltaLatitude
|
|
4845
|
+
);
|
|
4846
|
+
return vec3_exports.multiply([], positionWorld, scale3);
|
|
4847
|
+
}
|
|
3334
4848
|
var PathStyleExtension = class extends import_core7.LayerExtension {
|
|
3335
4849
|
constructor({
|
|
3336
4850
|
dash = false,
|
|
3337
4851
|
offset = false,
|
|
4852
|
+
dashMode,
|
|
3338
4853
|
highPrecisionDash = false
|
|
3339
4854
|
} = {}) {
|
|
3340
|
-
|
|
4855
|
+
const resolvedDashMode = dashMode ?? (highPrecisionDash ? "path" : "segment");
|
|
4856
|
+
super({
|
|
4857
|
+
dash: dash || highPrecisionDash || dashMode !== void 0,
|
|
4858
|
+
offset,
|
|
4859
|
+
dashMode: resolvedDashMode,
|
|
4860
|
+
highPrecisionDash: resolvedDashMode === "path"
|
|
4861
|
+
});
|
|
3341
4862
|
}
|
|
3342
4863
|
getLayerType(layer) {
|
|
3343
4864
|
if ("pathTesselator" in layer.state) {
|
|
@@ -3352,6 +4873,59 @@ in float instanceOffsets;
|
|
|
3352
4873
|
}
|
|
3353
4874
|
return null;
|
|
3354
4875
|
}
|
|
4876
|
+
synchronizeAttributes(layer) {
|
|
4877
|
+
const attributeManager = layer.getAttributeManager();
|
|
4878
|
+
const layerType = this.getLayerType(layer);
|
|
4879
|
+
if (!attributeManager || !layerType) {
|
|
4880
|
+
return false;
|
|
4881
|
+
}
|
|
4882
|
+
const attributes = attributeManager.getAttributes();
|
|
4883
|
+
const desiredAttributes = /* @__PURE__ */ new Set();
|
|
4884
|
+
if (this.opts.dash) {
|
|
4885
|
+
desiredAttributes.add("instanceDashArrays");
|
|
4886
|
+
}
|
|
4887
|
+
if (layerType === "path" && this.opts.dash && this.opts.dashMode === "path") {
|
|
4888
|
+
desiredAttributes.add("instanceDashOffsets");
|
|
4889
|
+
}
|
|
4890
|
+
if (layerType === "path" && this.opts.offset) {
|
|
4891
|
+
desiredAttributes.add("instanceOffsets");
|
|
4892
|
+
}
|
|
4893
|
+
let topologyChanged = false;
|
|
4894
|
+
for (const attributeName of PATH_STYLE_ATTRIBUTES) {
|
|
4895
|
+
if (attributes[attributeName] && !desiredAttributes.has(attributeName)) {
|
|
4896
|
+
attributeManager.remove([attributeName]);
|
|
4897
|
+
topologyChanged = true;
|
|
4898
|
+
}
|
|
4899
|
+
}
|
|
4900
|
+
if (desiredAttributes.has("instanceDashArrays") && !attributes.instanceDashArrays) {
|
|
4901
|
+
attributeManager.addInstanced({
|
|
4902
|
+
instanceDashArrays: { size: 2, accessor: "getDashArray" }
|
|
4903
|
+
});
|
|
4904
|
+
topologyChanged = true;
|
|
4905
|
+
}
|
|
4906
|
+
if (desiredAttributes.has("instanceDashOffsets") && !attributes.instanceDashOffsets) {
|
|
4907
|
+
attributeManager.addInstanced({
|
|
4908
|
+
instanceDashOffsets: {
|
|
4909
|
+
// [distance from the start of the path, total length of the path]
|
|
4910
|
+
size: 2,
|
|
4911
|
+
// Recalculate from rendered geometry when getPath changes. A binary getPath buffer
|
|
4912
|
+
// contains positions, not dash metrics, so keep it as an update trigger rather than
|
|
4913
|
+
// binding it directly to this attribute.
|
|
4914
|
+
accessor: ["getPath"],
|
|
4915
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
4916
|
+
update: this.calculateDashMetrics
|
|
4917
|
+
}
|
|
4918
|
+
});
|
|
4919
|
+
topologyChanged = true;
|
|
4920
|
+
}
|
|
4921
|
+
if (desiredAttributes.has("instanceOffsets") && !attributes.instanceOffsets) {
|
|
4922
|
+
attributeManager.addInstanced({
|
|
4923
|
+
instanceOffsets: { size: 1, accessor: "getOffset" }
|
|
4924
|
+
});
|
|
4925
|
+
topologyChanged = true;
|
|
4926
|
+
}
|
|
4927
|
+
return topologyChanged;
|
|
4928
|
+
}
|
|
3355
4929
|
isEnabled(layer) {
|
|
3356
4930
|
return this.getLayerType(layer) !== null;
|
|
3357
4931
|
}
|
|
@@ -3378,57 +4952,54 @@ in float instanceOffsets;
|
|
|
3378
4952
|
const defines = {};
|
|
3379
4953
|
if (extension.opts.dash) {
|
|
3380
4954
|
result = (0, import_core7._mergeShaders)(result, dashShaders);
|
|
3381
|
-
|
|
4955
|
+
defines.DASH_ENABLED = true;
|
|
4956
|
+
if (extension.opts.dashMode === "path") {
|
|
3382
4957
|
defines.HIGH_PRECISION_DASH = true;
|
|
3383
4958
|
}
|
|
3384
4959
|
}
|
|
3385
4960
|
if (extension.opts.offset) {
|
|
3386
4961
|
result = (0, import_core7._mergeShaders)(result, offsetShaders);
|
|
4962
|
+
defines.PATH_STYLE_OFFSET = true;
|
|
3387
4963
|
}
|
|
3388
4964
|
const { inject: inject6 } = result;
|
|
3389
4965
|
const pathStyle = {
|
|
3390
4966
|
name: "pathStyle",
|
|
3391
|
-
inject: inject6
|
|
3392
|
-
uniformTypes: {
|
|
3393
|
-
dashAlignMode: "f32",
|
|
3394
|
-
dashGapPickable: "i32"
|
|
3395
|
-
}
|
|
4967
|
+
inject: inject6
|
|
3396
4968
|
};
|
|
4969
|
+
if (extension.opts.dash) {
|
|
4970
|
+
pathStyle.uniformTypes = {
|
|
4971
|
+
dashAlignMode: "f32",
|
|
4972
|
+
dashGapPickable: "i32",
|
|
4973
|
+
dashUnits: "i32"
|
|
4974
|
+
};
|
|
4975
|
+
}
|
|
3397
4976
|
return {
|
|
3398
4977
|
modules: [pathStyle],
|
|
3399
4978
|
defines
|
|
3400
4979
|
};
|
|
3401
4980
|
}
|
|
3402
4981
|
initializeState(context, extension) {
|
|
3403
|
-
|
|
3404
|
-
const layerType = extension.getLayerType(this);
|
|
3405
|
-
if (!attributeManager || !layerType) {
|
|
3406
|
-
return;
|
|
3407
|
-
}
|
|
3408
|
-
if (extension.opts.dash) {
|
|
3409
|
-
attributeManager.addInstanced({
|
|
3410
|
-
instanceDashArrays: { size: 2, accessor: "getDashArray" },
|
|
3411
|
-
...layerType === "path" && extension.opts.highPrecisionDash ? {
|
|
3412
|
-
instanceDashOffsets: {
|
|
3413
|
-
size: 1,
|
|
3414
|
-
accessor: "getPath",
|
|
3415
|
-
transform: extension.getDashOffsets.bind(this)
|
|
3416
|
-
}
|
|
3417
|
-
} : {}
|
|
3418
|
-
});
|
|
3419
|
-
}
|
|
3420
|
-
if (layerType === "path" && extension.opts.offset) {
|
|
3421
|
-
attributeManager.addInstanced({
|
|
3422
|
-
instanceOffsets: { size: 1, accessor: "getOffset" }
|
|
3423
|
-
});
|
|
3424
|
-
}
|
|
4982
|
+
extension.synchronizeAttributes(this);
|
|
3425
4983
|
}
|
|
3426
4984
|
updateState(params, extension) {
|
|
3427
4985
|
if (!extension.isEnabled(this)) {
|
|
3428
4986
|
return;
|
|
3429
4987
|
}
|
|
4988
|
+
if (params.changeFlags.extensionsChanged) {
|
|
4989
|
+
const topologyChanged = extension.synchronizeAttributes(this);
|
|
4990
|
+
const attributeManager = this.getAttributeManager();
|
|
4991
|
+
if (attributeManager && topologyChanged) {
|
|
4992
|
+
attributeManager.invalidateAll();
|
|
4993
|
+
for (const model of this.getModels()) {
|
|
4994
|
+
model.setBufferLayout(attributeManager.getBufferLayouts(model));
|
|
4995
|
+
}
|
|
4996
|
+
}
|
|
4997
|
+
}
|
|
3430
4998
|
if (extension.opts.dash) {
|
|
3431
4999
|
const layerType = extension.getLayerType(this);
|
|
5000
|
+
if (layerType === "path" && extension.opts.dashMode === "path" && dashMetricsProjectionPropsChanged(params)) {
|
|
5001
|
+
this.getAttributeManager()?.invalidate("instanceDashOffsets");
|
|
5002
|
+
}
|
|
3432
5003
|
if (layerType === "scatterplot" || layerType === "textBackground") {
|
|
3433
5004
|
const pathStyleProps = {
|
|
3434
5005
|
dashGapPickable: Boolean(this.props.dashGapPickable)
|
|
@@ -3437,12 +5008,101 @@ in float instanceOffsets;
|
|
|
3437
5008
|
} else {
|
|
3438
5009
|
const pathStyleProps = {
|
|
3439
5010
|
dashAlignMode: this.props.dashJustified ? 1 : 0,
|
|
3440
|
-
dashGapPickable: Boolean(this.props.dashGapPickable)
|
|
5011
|
+
dashGapPickable: Boolean(this.props.dashGapPickable),
|
|
5012
|
+
dashUnits: DASH_UNITS[this.props.dashUnits ?? "widths"]
|
|
3441
5013
|
};
|
|
3442
5014
|
this.setShaderModuleProps({ pathStyle: pathStyleProps });
|
|
3443
5015
|
}
|
|
3444
5016
|
}
|
|
3445
5017
|
}
|
|
5018
|
+
calculateDashMetrics(attribute, { startRow, endRow }) {
|
|
5019
|
+
const pathTesselator = this.state.pathTesselator;
|
|
5020
|
+
const vertexStarts = pathTesselator?.vertexStarts;
|
|
5021
|
+
const instanceCount = pathTesselator?.instanceCount;
|
|
5022
|
+
const packedPositions = pathTesselator?.get("positions");
|
|
5023
|
+
if (!vertexStarts || instanceCount === void 0 || typeof pathTesselator?.getPathSegmentIndices !== "function") {
|
|
5024
|
+
throw new Error("PathStyleExtension requires PathLayer tessellation data for path dashes.");
|
|
5025
|
+
}
|
|
5026
|
+
const binaryPath = this.props.data?.attributes?.getPath;
|
|
5027
|
+
const binaryValue = ArrayBuffer.isView(binaryPath) ? binaryPath : binaryPath?.value;
|
|
5028
|
+
const hasReadableBinaryPath = ArrayBuffer.isView(binaryValue);
|
|
5029
|
+
const usePackedPositions = packedPositions && (!binaryPath || pathTesselator.normalize || pathTesselator.opts?.isWebGPU);
|
|
5030
|
+
if (binaryPath && !hasReadableBinaryPath || !usePackedPositions && !hasReadableBinaryPath) {
|
|
5031
|
+
throw new Error(
|
|
5032
|
+
"PathStyleExtension cannot calculate whole-path dash metrics from GPU-only getPath data; supply data.attributes.instanceDashOffsets."
|
|
5033
|
+
);
|
|
5034
|
+
}
|
|
5035
|
+
const output = attribute.value;
|
|
5036
|
+
const metricSize = attribute.size;
|
|
5037
|
+
const firstRow = Math.max(0, startRow);
|
|
5038
|
+
const lastRow = Math.min(endRow, vertexStarts.length - 1);
|
|
5039
|
+
let binarySize = 3;
|
|
5040
|
+
let binaryStride = 3;
|
|
5041
|
+
let binaryOffset = 0;
|
|
5042
|
+
if (!usePackedPositions) {
|
|
5043
|
+
const bytesPerElement = binaryValue.BYTES_PER_ELEMENT;
|
|
5044
|
+
binarySize = binaryPath?.size || (this.props.positionFormat === "XY" ? 2 : 3);
|
|
5045
|
+
binaryStride = binaryPath?.stride ? binaryPath.stride / bytesPerElement : binarySize;
|
|
5046
|
+
binaryOffset = binaryPath?.offset ? binaryPath.offset / bytesPerElement : 0;
|
|
5047
|
+
}
|
|
5048
|
+
const sourcePosition = new Array(3);
|
|
5049
|
+
const projectUniforms = import_core7.project.getUniforms({
|
|
5050
|
+
viewport: this.context.viewport,
|
|
5051
|
+
modelMatrix: this.props.modelMatrix,
|
|
5052
|
+
coordinateSystem: this.props.coordinateSystem,
|
|
5053
|
+
coordinateOrigin: this.props.coordinateOrigin,
|
|
5054
|
+
// PathLayer normalizes/cuts wrapped paths on the CPU; its shader never wraps again.
|
|
5055
|
+
autoWrapLongitude: this.wrapLongitude
|
|
5056
|
+
});
|
|
5057
|
+
const readPosition = (vertexIndex) => {
|
|
5058
|
+
if (usePackedPositions) {
|
|
5059
|
+
sourcePosition[0] = packedPositions[vertexIndex * 3];
|
|
5060
|
+
sourcePosition[1] = packedPositions[vertexIndex * 3 + 1];
|
|
5061
|
+
sourcePosition[2] = packedPositions[vertexIndex * 3 + 2];
|
|
5062
|
+
} else {
|
|
5063
|
+
const sourceIndex = binaryOffset + vertexIndex * binaryStride;
|
|
5064
|
+
sourcePosition[0] = binaryValue[sourceIndex];
|
|
5065
|
+
sourcePosition[1] = binaryValue[sourceIndex + 1];
|
|
5066
|
+
sourcePosition[2] = binarySize === 3 ? binaryValue[sourceIndex + 2] : 0;
|
|
5067
|
+
}
|
|
5068
|
+
return sourcePosition;
|
|
5069
|
+
};
|
|
5070
|
+
attribute.startIndices = vertexStarts;
|
|
5071
|
+
for (let row = firstRow; row < lastRow; row++) {
|
|
5072
|
+
const rowStart = vertexStarts[row];
|
|
5073
|
+
const rowEnd = Math.min(vertexStarts[row + 1] ?? instanceCount, instanceCount);
|
|
5074
|
+
output.fill(0, rowStart * metricSize, rowEnd * metricSize);
|
|
5075
|
+
const validSegments = pathTesselator.getPathSegmentIndices(row);
|
|
5076
|
+
let phase = 0;
|
|
5077
|
+
let previousSegment = -2;
|
|
5078
|
+
let previousEnd = null;
|
|
5079
|
+
for (const segmentIndex of validSegments) {
|
|
5080
|
+
const outputIndex = segmentIndex * metricSize;
|
|
5081
|
+
output[outputIndex] = phase;
|
|
5082
|
+
const start = segmentIndex === previousSegment + 1 && previousEnd ? previousEnd : projectRenderedPathPosition(this, readPosition(segmentIndex), projectUniforms);
|
|
5083
|
+
const end = projectRenderedPathPosition(
|
|
5084
|
+
this,
|
|
5085
|
+
readPosition(segmentIndex + 1),
|
|
5086
|
+
projectUniforms
|
|
5087
|
+
);
|
|
5088
|
+
phase += vec3_exports.dist(start, end);
|
|
5089
|
+
previousSegment = segmentIndex;
|
|
5090
|
+
previousEnd = end;
|
|
5091
|
+
}
|
|
5092
|
+
if (metricSize > 1) {
|
|
5093
|
+
for (let vertexIndex = rowStart; vertexIndex < rowEnd; vertexIndex++) {
|
|
5094
|
+
output[vertexIndex * metricSize + 1] = phase;
|
|
5095
|
+
}
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
5098
|
+
}
|
|
5099
|
+
/**
|
|
5100
|
+
* Calculates the distance from the start of a path to each rendered segment.
|
|
5101
|
+
*
|
|
5102
|
+
* The final entry is zero because PathLayer reserves the final vertex as invalid padding.
|
|
5103
|
+
* This scalar return shape is retained for compatibility; path-mode rendering uses an
|
|
5104
|
+
* internal attribute containing both each segment offset and the total path length.
|
|
5105
|
+
*/
|
|
3446
5106
|
getDashOffsets(path) {
|
|
3447
5107
|
const result = [0];
|
|
3448
5108
|
const positionSize = this.props.positionFormat === "XY" ? 2 : 3;
|
|
@@ -3452,7 +5112,7 @@ in float instanceOffsets;
|
|
|
3452
5112
|
let prevP;
|
|
3453
5113
|
for (let i = 0; i < geometrySize - 1; i++) {
|
|
3454
5114
|
p = isNested ? path[i] : path.slice(i * positionSize, i * positionSize + positionSize);
|
|
3455
|
-
p = this.projectPosition(p);
|
|
5115
|
+
p = this.projectPosition(p, { autoOffset: false });
|
|
3456
5116
|
if (i > 0) {
|
|
3457
5117
|
result[i] = result[i - 1] + vec3_exports.dist(prevP, p);
|
|
3458
5118
|
}
|
|
@@ -3470,12 +5130,26 @@ in float instanceOffsets;
|
|
|
3470
5130
|
|
|
3471
5131
|
// src/fill-style/shader-module.ts
|
|
3472
5132
|
var import_core9 = __toESM(require_core(), 1);
|
|
5133
|
+
var FILL_UV_SCALE = 512 / 4e7;
|
|
5134
|
+
function getPatternUnitScale(sizeUnits, viewport) {
|
|
5135
|
+
switch (sizeUnits) {
|
|
5136
|
+
case "pixels":
|
|
5137
|
+
return 2 ** -Math.round(viewport.zoom);
|
|
5138
|
+
case "common":
|
|
5139
|
+
return 1;
|
|
5140
|
+
default:
|
|
5141
|
+
return FILL_UV_SCALE;
|
|
5142
|
+
}
|
|
5143
|
+
}
|
|
3473
5144
|
var uniformBlock3 = (
|
|
3474
5145
|
/* glsl */
|
|
3475
5146
|
`layout(std140) uniform fillUniforms {
|
|
3476
5147
|
vec2 patternTextureSize;
|
|
5148
|
+
float patternUnitScale;
|
|
3477
5149
|
bool patternEnabled;
|
|
3478
5150
|
bool patternMask;
|
|
5151
|
+
bool procedural;
|
|
5152
|
+
bool flipY;
|
|
3479
5153
|
vec2 uvCoordinateOrigin;
|
|
3480
5154
|
vec2 uvCoordinateOrigin64Low;
|
|
3481
5155
|
} fill;
|
|
@@ -3484,13 +5158,20 @@ in float instanceOffsets;
|
|
|
3484
5158
|
var patternVs = (
|
|
3485
5159
|
/* glsl */
|
|
3486
5160
|
`
|
|
5161
|
+
uniform sampler2D fill_patternTexture;
|
|
5162
|
+
|
|
3487
5163
|
in vec4 fillPatternFrames;
|
|
3488
5164
|
in float fillPatternScales;
|
|
3489
5165
|
in vec2 fillPatternOffsets;
|
|
5166
|
+
in vec4 fillPatternBackgroundColors;
|
|
3490
5167
|
|
|
3491
5168
|
out vec2 fill_uv;
|
|
3492
5169
|
out vec4 fill_patternBounds;
|
|
3493
5170
|
out vec4 fill_patternPlacement;
|
|
5171
|
+
out vec4 fill_backgroundColor;
|
|
5172
|
+
out float fill_patternCoordinateScale;
|
|
5173
|
+
flat out vec4 fill_patternParams0;
|
|
5174
|
+
flat out vec4 fill_patternParams1;
|
|
3494
5175
|
`
|
|
3495
5176
|
);
|
|
3496
5177
|
var vs3 = `
|
|
@@ -3504,9 +5185,136 @@ uniform sampler2D fill_patternTexture;
|
|
|
3504
5185
|
|
|
3505
5186
|
in vec4 fill_patternBounds;
|
|
3506
5187
|
in vec4 fill_patternPlacement;
|
|
5188
|
+
in vec4 fill_backgroundColor;
|
|
3507
5189
|
in vec2 fill_uv;
|
|
5190
|
+
in float fill_patternCoordinateScale;
|
|
5191
|
+
flat in vec4 fill_patternParams0;
|
|
5192
|
+
flat in vec4 fill_patternParams1;
|
|
5193
|
+
|
|
5194
|
+
const float FILL_PATTERN_HATCH = 1.0;
|
|
5195
|
+
const float FILL_PATTERN_CROSS_HATCH = 2.0;
|
|
5196
|
+
const float FILL_PATTERN_DOTS = 3.0;
|
|
5197
|
+
|
|
5198
|
+
float fill_getPatternCoordinate(vec2 axis, float period, float offset) {
|
|
5199
|
+
float coordinateScale = max(abs(fill_patternCoordinateScale), 1.0e-20);
|
|
5200
|
+
vec2 coordinateOrigin = fill.uvCoordinateOrigin;
|
|
5201
|
+
vec2 coordinateOffset = fill.uvCoordinateOrigin64Low + fill_uv;
|
|
5202
|
+
float yDirection = fill.flipY ? 1.0 : -1.0;
|
|
5203
|
+
coordinateOrigin.y *= yDirection;
|
|
5204
|
+
coordinateOffset.y *= yDirection;
|
|
5205
|
+
|
|
5206
|
+
// Reduce the high part before adding the local offset to retain precision in large coordinates.
|
|
5207
|
+
float highPart = mod(dot(coordinateOrigin, axis) / coordinateScale, period);
|
|
5208
|
+
float lowPart = dot(coordinateOffset, axis) / coordinateScale;
|
|
5209
|
+
return mod(highPart + lowPart + offset * period, period);
|
|
5210
|
+
}
|
|
5211
|
+
|
|
5212
|
+
vec2 fill_getPatternCoords(
|
|
5213
|
+
vec2 axis0,
|
|
5214
|
+
vec2 axis1,
|
|
5215
|
+
vec2 period,
|
|
5216
|
+
vec2 offset
|
|
5217
|
+
) {
|
|
5218
|
+
return vec2(
|
|
5219
|
+
fill_getPatternCoordinate(axis0, period.x, offset.x),
|
|
5220
|
+
fill_getPatternCoordinate(axis1, period.y, offset.y)
|
|
5221
|
+
);
|
|
5222
|
+
}
|
|
5223
|
+
|
|
5224
|
+
float fill_getScreenSpaceAlpha(float signedDistance) {
|
|
5225
|
+
float distanceDerivative = fwidth(signedDistance);
|
|
5226
|
+
if (distanceDerivative == 0.0) {
|
|
5227
|
+
return signedDistance <= 0.0 ? 1.0 : 0.0;
|
|
5228
|
+
}
|
|
5229
|
+
// Normalize the SDF to screen pixels so the coverage ramp is independent of pattern units.
|
|
5230
|
+
float distanceInPixels = signedDistance / distanceDerivative;
|
|
5231
|
+
return 1.0 - smoothstep(-0.5, 0.5, distanceInPixels);
|
|
5232
|
+
}
|
|
5233
|
+
|
|
5234
|
+
float fill_getLineAlpha(float position, float center, float period, float strokeWidth) {
|
|
5235
|
+
float distanceToLine = abs(mod(position - center + period * 0.5, period) - period * 0.5);
|
|
5236
|
+
return fill_getScreenSpaceAlpha(distanceToLine - strokeWidth * 0.5);
|
|
5237
|
+
}
|
|
5238
|
+
|
|
5239
|
+
float fill_getHatchAlpha(float angle, float strokeWidth, vec2 gaps, bool alternating) {
|
|
5240
|
+
vec2 lineDirection = vec2(cos(angle), sin(angle));
|
|
5241
|
+
vec2 lineNormal = vec2(-lineDirection.y, lineDirection.x);
|
|
5242
|
+
float period = alternating
|
|
5243
|
+
? strokeWidth * 2.0 + gaps.x + gaps.y
|
|
5244
|
+
: strokeWidth + gaps.x;
|
|
5245
|
+
float offset = dot(fill_patternPlacement.xy, lineNormal);
|
|
5246
|
+
float position = fill_getPatternCoordinate(lineNormal, period, offset);
|
|
5247
|
+
float alpha = fill_getLineAlpha(position, 0.0, period, strokeWidth);
|
|
5248
|
+
if (alternating) {
|
|
5249
|
+
alpha = max(
|
|
5250
|
+
alpha,
|
|
5251
|
+
fill_getLineAlpha(position, strokeWidth + gaps.x, period, strokeWidth)
|
|
5252
|
+
);
|
|
5253
|
+
}
|
|
5254
|
+
return alpha;
|
|
5255
|
+
}
|
|
5256
|
+
|
|
5257
|
+
float fill_getDotAlpha(float angle, float skew, float radius, float gap) {
|
|
5258
|
+
vec2 axis0 = vec2(cos(angle), sin(angle));
|
|
5259
|
+
float axis1Angle = angle + 1.5707963267948966 - skew;
|
|
5260
|
+
vec2 axis1 = vec2(cos(axis1Angle), sin(axis1Angle));
|
|
5261
|
+
float determinant = axis0.x * axis1.y - axis0.y * axis1.x;
|
|
5262
|
+
|
|
5263
|
+
// The reciprocal basis maps common coordinates into the skewed dot lattice.
|
|
5264
|
+
vec2 reciprocal0 = vec2(axis1.y, -axis1.x) / determinant;
|
|
5265
|
+
vec2 reciprocal1 = vec2(-axis0.y, axis0.x) / determinant;
|
|
5266
|
+
float period = radius * 2.0 + gap;
|
|
5267
|
+
vec2 latticeCoords = fill_getPatternCoords(
|
|
5268
|
+
reciprocal0,
|
|
5269
|
+
reciprocal1,
|
|
5270
|
+
vec2(period),
|
|
5271
|
+
fill_patternPlacement.xy
|
|
5272
|
+
);
|
|
5273
|
+
vec2 cellOffset = mod(latticeCoords + period * 0.5, period) - period * 0.5;
|
|
5274
|
+
float distanceToDotSquared = 1.0e20;
|
|
5275
|
+
// In a skewed lattice the closest dot is not necessarily in the independently wrapped cell.
|
|
5276
|
+
// Include adjacent cells to keep the repeated distance field continuous at cell boundaries.
|
|
5277
|
+
for (int x = -1; x <= 1; x++) {
|
|
5278
|
+
for (int y = -1; y <= 1; y++) {
|
|
5279
|
+
vec2 neighborOffset = cellOffset + vec2(float(x), float(y)) * period;
|
|
5280
|
+
vec2 dotOffset = axis0 * neighborOffset.x + axis1 * neighborOffset.y;
|
|
5281
|
+
distanceToDotSquared = min(distanceToDotSquared, dot(dotOffset, dotOffset));
|
|
5282
|
+
}
|
|
5283
|
+
}
|
|
5284
|
+
float distanceToDot = sqrt(distanceToDotSquared);
|
|
5285
|
+
return fill_getScreenSpaceAlpha(distanceToDot - radius);
|
|
5286
|
+
}
|
|
5287
|
+
|
|
5288
|
+
float fill_getProceduralPatternAlpha() {
|
|
5289
|
+
float patternType = fill_patternParams0.x;
|
|
5290
|
+
float size = fill_patternParams0.y;
|
|
5291
|
+
vec2 gaps = fill_patternParams0.zw;
|
|
5292
|
+
vec2 angles = fill_patternParams1.xy;
|
|
5293
|
+
float elementCount = fill_patternParams1.z;
|
|
5294
|
+
|
|
5295
|
+
if (patternType == FILL_PATTERN_HATCH) {
|
|
5296
|
+
return fill_getHatchAlpha(angles.x, size, gaps, elementCount == 2.0);
|
|
5297
|
+
}
|
|
5298
|
+
if (patternType == FILL_PATTERN_CROSS_HATCH) {
|
|
5299
|
+
return max(
|
|
5300
|
+
fill_getHatchAlpha(angles.x, size, gaps, false),
|
|
5301
|
+
fill_getHatchAlpha(angles.y, size, gaps, false)
|
|
5302
|
+
);
|
|
5303
|
+
}
|
|
5304
|
+
if (patternType == FILL_PATTERN_DOTS) {
|
|
5305
|
+
return fill_getDotAlpha(angles.x, angles.y, size, gaps.x);
|
|
5306
|
+
}
|
|
5307
|
+
return 1.0;
|
|
5308
|
+
}
|
|
3508
5309
|
|
|
3509
|
-
|
|
5310
|
+
// Draw the pattern over the background fill (Porter-Duff source-over)
|
|
5311
|
+
vec4 fill_blendOverBackground(vec4 pattern, vec4 background) {
|
|
5312
|
+
if (background.a == 0.0 || layer.opacity == 0.0) return pattern;
|
|
5313
|
+
float patternAlpha = pattern.a / layer.opacity;
|
|
5314
|
+
float blendedAlpha = patternAlpha + background.a * (1.0 - patternAlpha);
|
|
5315
|
+
vec3 blendedRGB = mix(background.rgb, pattern.rgb, patternAlpha / blendedAlpha);
|
|
5316
|
+
return vec4(blendedRGB, blendedAlpha * layer.opacity);
|
|
5317
|
+
}
|
|
3510
5318
|
`
|
|
3511
5319
|
);
|
|
3512
5320
|
var fs3 = `
|
|
@@ -3517,16 +5325,32 @@ ${patternFs}
|
|
|
3517
5325
|
"vs:DECKGL_FILTER_GL_POSITION": (
|
|
3518
5326
|
/* glsl */
|
|
3519
5327
|
`
|
|
3520
|
-
fill_uv = geometry.position.xy;
|
|
3521
|
-
`
|
|
3522
|
-
),
|
|
3523
|
-
"vs:DECKGL_FILTER_COLOR": (
|
|
3524
|
-
/* glsl */
|
|
3525
|
-
`
|
|
3526
5328
|
if (fill.patternEnabled) {
|
|
3527
|
-
fill_patternBounds = fillPatternFrames / vec4(fill.patternTextureSize, fill.patternTextureSize);
|
|
3528
5329
|
fill_patternPlacement.xy = fillPatternOffsets;
|
|
3529
|
-
|
|
5330
|
+
fill_backgroundColor = fillPatternBackgroundColors;
|
|
5331
|
+
if (fill.procedural) {
|
|
5332
|
+
fill_uv = geometry.position.xy;
|
|
5333
|
+
fill_patternCoordinateScale = fill.patternUnitScale * fillPatternScales;
|
|
5334
|
+
int texelIndex = int(fillPatternFrames.x) * 4;
|
|
5335
|
+
fill_patternParams0 = vec4(
|
|
5336
|
+
texelFetch(fill_patternTexture, ivec2(texelIndex, 0), 0).rg,
|
|
5337
|
+
texelFetch(fill_patternTexture, ivec2(texelIndex + 1, 0), 0).rg
|
|
5338
|
+
);
|
|
5339
|
+
fill_patternParams1 = vec4(
|
|
5340
|
+
texelFetch(fill_patternTexture, ivec2(texelIndex + 2, 0), 0).rg,
|
|
5341
|
+
texelFetch(fill_patternTexture, ivec2(texelIndex + 3, 0), 0).rg
|
|
5342
|
+
);
|
|
5343
|
+
} else {
|
|
5344
|
+
fill_patternBounds = fillPatternFrames / vec4(fill.patternTextureSize, fill.patternTextureSize);
|
|
5345
|
+
vec2 patternFrameCommon = fill.patternUnitScale * fillPatternScales * fillPatternFrames.zw;
|
|
5346
|
+
// Reduce the coordinate origin to within one tile before adding the vertex position. The
|
|
5347
|
+
// origin is large in common space, and fp32 cannot carry the sum at full precision.
|
|
5348
|
+
vec2 origin = mod(fill.uvCoordinateOrigin, patternFrameCommon) + fill.uvCoordinateOrigin64Low;
|
|
5349
|
+
fill_uv = (origin + geometry.position.xy) / patternFrameCommon;
|
|
5350
|
+
// Pattern atlases use top-left coordinates, so reverse common-space Y in bottom-left views.
|
|
5351
|
+
fill_uv.y *= fill.flipY ? 1.0 : -1.0;
|
|
5352
|
+
fill_uv += fillPatternOffsets;
|
|
5353
|
+
}
|
|
3530
5354
|
}
|
|
3531
5355
|
`
|
|
3532
5356
|
),
|
|
@@ -3534,17 +5358,25 @@ ${patternFs}
|
|
|
3534
5358
|
/* glsl */
|
|
3535
5359
|
`
|
|
3536
5360
|
if (fill.patternEnabled) {
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
5361
|
+
if (fill.procedural) {
|
|
5362
|
+
color.a *= fill_getProceduralPatternAlpha();
|
|
5363
|
+
} else {
|
|
5364
|
+
vec2 patternUV = fract(fill_uv);
|
|
5365
|
+
vec2 texCoords = fill_patternBounds.xy + fill_patternBounds.zw * patternUV;
|
|
5366
|
+
|
|
5367
|
+
// Tiling is emulated by wrapping the coordinate, so texCoords jumps from the end of the
|
|
5368
|
+
// frame back to its start once per tile, leading to the wrong mip level being selected,
|
|
5369
|
+
// which leads to artifacts at pattern edges. fill_uv is continuous across the primitive,
|
|
5370
|
+
// meaning that the correct mip level is always selected
|
|
5371
|
+
vec4 grad = fill_patternBounds.zwzw * vec4(dFdx(fill_uv), dFdy(fill_uv));
|
|
5372
|
+
vec4 patternColor = textureGrad(fill_patternTexture, texCoords, grad.xy, grad.zw);
|
|
5373
|
+
|
|
5374
|
+
color.a *= patternColor.a;
|
|
5375
|
+
if (!fill.patternMask) {
|
|
5376
|
+
color.rgb = patternColor.rgb;
|
|
5377
|
+
}
|
|
3547
5378
|
}
|
|
5379
|
+
color = fill_blendOverBackground(color, fill_backgroundColor);
|
|
3548
5380
|
}
|
|
3549
5381
|
`
|
|
3550
5382
|
)
|
|
@@ -3560,17 +5392,28 @@ ${patternFs}
|
|
|
3560
5392
|
uniforms.patternTextureSize = [fillPatternTexture.width, fillPatternTexture.height];
|
|
3561
5393
|
}
|
|
3562
5394
|
if ("project" in opts) {
|
|
3563
|
-
const {
|
|
5395
|
+
const {
|
|
5396
|
+
fillPatternMask = true,
|
|
5397
|
+
fillPatternEnabled = true,
|
|
5398
|
+
fillPatternSizeUnits = "meters",
|
|
5399
|
+
fillPatternCommonFrame = null,
|
|
5400
|
+
procedural = false
|
|
5401
|
+
} = opts;
|
|
3564
5402
|
const projectUniforms = import_core9.project.getUniforms(opts.project);
|
|
3565
5403
|
const { commonOrigin: coordinateOriginCommon } = projectUniforms;
|
|
3566
|
-
const
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
5404
|
+
const unitScale = getPatternUnitScale(fillPatternSizeUnits, opts.project.viewport);
|
|
5405
|
+
const origin = [coordinateOriginCommon[0], coordinateOriginCommon[1]];
|
|
5406
|
+
if (fillPatternCommonFrame) {
|
|
5407
|
+
origin[0] %= unitScale * fillPatternCommonFrame[0];
|
|
5408
|
+
origin[1] %= unitScale * fillPatternCommonFrame[1];
|
|
5409
|
+
}
|
|
5410
|
+
uniforms.uvCoordinateOrigin = origin;
|
|
5411
|
+
uniforms.uvCoordinateOrigin64Low = [(0, import_core9.fp64LowPart)(origin[0]), (0, import_core9.fp64LowPart)(origin[1])];
|
|
5412
|
+
uniforms.patternUnitScale = unitScale;
|
|
3572
5413
|
uniforms.patternMask = fillPatternMask;
|
|
3573
5414
|
uniforms.patternEnabled = fillPatternEnabled;
|
|
5415
|
+
uniforms.procedural = procedural;
|
|
5416
|
+
uniforms.flipY = opts.project.viewport?.flipY ?? false;
|
|
3574
5417
|
}
|
|
3575
5418
|
return uniforms;
|
|
3576
5419
|
}
|
|
@@ -3583,31 +5426,172 @@ ${patternFs}
|
|
|
3583
5426
|
getUniforms: getPatternUniforms,
|
|
3584
5427
|
uniformTypes: {
|
|
3585
5428
|
patternTextureSize: "vec2<f32>",
|
|
5429
|
+
patternUnitScale: "f32",
|
|
3586
5430
|
patternEnabled: "i32",
|
|
3587
5431
|
patternMask: "i32",
|
|
5432
|
+
procedural: "i32",
|
|
5433
|
+
flipY: "i32",
|
|
3588
5434
|
uvCoordinateOrigin: "vec2<f32>",
|
|
3589
5435
|
uvCoordinateOrigin64Low: "vec2<f32>"
|
|
3590
5436
|
}
|
|
3591
5437
|
};
|
|
3592
5438
|
|
|
5439
|
+
// src/fill-style/procedural-pattern.ts
|
|
5440
|
+
var PROCEDURAL_PATTERN_TEXTURE_FORMAT = "rg32float";
|
|
5441
|
+
var PROCEDURAL_PATTERN_COMPONENTS_PER_TEXEL = 2;
|
|
5442
|
+
var PROCEDURAL_PATTERN_TEXELS_PER_PATTERN = 4;
|
|
5443
|
+
var PROCEDURAL_PATTERN_TYPES = {
|
|
5444
|
+
none: 0,
|
|
5445
|
+
hatch: 1,
|
|
5446
|
+
crossHatch: 2,
|
|
5447
|
+
dots: 3
|
|
5448
|
+
};
|
|
5449
|
+
var PROCEDURAL_PATTERN_OFFSETS = {
|
|
5450
|
+
type: 0,
|
|
5451
|
+
size: 1,
|
|
5452
|
+
gap0: 2,
|
|
5453
|
+
gap1: 3,
|
|
5454
|
+
angle0: 4,
|
|
5455
|
+
angle1: 5,
|
|
5456
|
+
/** Number of alternating gaps, hatch directions, or dot-grid axes, depending on type. */
|
|
5457
|
+
elementCount: 6
|
|
5458
|
+
};
|
|
5459
|
+
var COMPONENTS_PER_PATTERN = PROCEDURAL_PATTERN_COMPONENTS_PER_TEXEL * PROCEDURAL_PATTERN_TEXELS_PER_PATTERN;
|
|
5460
|
+
var DEGREES_TO_RADIANS2 = Math.PI / 180;
|
|
5461
|
+
var DEFAULT_SIZE = 1;
|
|
5462
|
+
var DEFAULT_GAP = 1;
|
|
5463
|
+
var DEFAULT_HATCH_ANGLE = 0;
|
|
5464
|
+
var DEFAULT_CROSS_HATCH_ANGLES = [45, 135];
|
|
5465
|
+
var DEFAULT_DOT_ANGLE = 0;
|
|
5466
|
+
var DEFAULT_DOT_SKEW = 0;
|
|
5467
|
+
function packProceduralPatterns(mapping) {
|
|
5468
|
+
const entries = Object.entries(mapping);
|
|
5469
|
+
const data = new Float32Array((entries.length + 1) * COMPONENTS_PER_PATTERN);
|
|
5470
|
+
const patternIndices = /* @__PURE__ */ new Map();
|
|
5471
|
+
entries.forEach(([id, config2], entryIndex) => {
|
|
5472
|
+
const patternIndex = entryIndex + 1;
|
|
5473
|
+
const offset = patternIndex * COMPONENTS_PER_PATTERN;
|
|
5474
|
+
patternIndices.set(id, patternIndex);
|
|
5475
|
+
switch (config2.type) {
|
|
5476
|
+
case "hatch": {
|
|
5477
|
+
const gap = config2.gap === void 0 ? DEFAULT_GAP : config2.gap;
|
|
5478
|
+
const strokeWidth = config2.strokeWidth === void 0 ? DEFAULT_SIZE : config2.strokeWidth;
|
|
5479
|
+
const angle2 = config2.angle === void 0 ? DEFAULT_HATCH_ANGLE : config2.angle;
|
|
5480
|
+
if (Array.isArray(gap) && gap.length !== 2) {
|
|
5481
|
+
throw new Error(`${id}.gap must be a number or a two-element array`);
|
|
5482
|
+
}
|
|
5483
|
+
const gaps = Array.isArray(gap) ? gap : [gap, gap];
|
|
5484
|
+
validatePositive(strokeWidth, `${id}.strokeWidth`);
|
|
5485
|
+
validateNonNegative(gaps[0], `${id}.gap[0]`);
|
|
5486
|
+
validateNonNegative(gaps[1], `${id}.gap[1]`);
|
|
5487
|
+
validateAngle(angle2, `${id}.angle`);
|
|
5488
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.type] = PROCEDURAL_PATTERN_TYPES.hatch;
|
|
5489
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.size] = strokeWidth;
|
|
5490
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.gap0] = gaps[0];
|
|
5491
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.gap1] = gaps[1];
|
|
5492
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.angle0] = angle2 * DEGREES_TO_RADIANS2;
|
|
5493
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.elementCount] = Array.isArray(gap) ? 2 : 1;
|
|
5494
|
+
break;
|
|
5495
|
+
}
|
|
5496
|
+
case "cross-hatch": {
|
|
5497
|
+
const angles = config2.angles === void 0 ? DEFAULT_CROSS_HATCH_ANGLES : config2.angles;
|
|
5498
|
+
const strokeWidth = config2.strokeWidth === void 0 ? DEFAULT_SIZE : config2.strokeWidth;
|
|
5499
|
+
const gap = config2.gap === void 0 ? DEFAULT_GAP : config2.gap;
|
|
5500
|
+
if (!Array.isArray(angles) || angles.length !== 2) {
|
|
5501
|
+
throw new Error(`${id}.angles must be a two-element array`);
|
|
5502
|
+
}
|
|
5503
|
+
validatePositive(strokeWidth, `${id}.strokeWidth`);
|
|
5504
|
+
validateNonNegative(gap, `${id}.gap`);
|
|
5505
|
+
validateAngle(angles[0], `${id}.angles[0]`);
|
|
5506
|
+
validateAngle(angles[1], `${id}.angles[1]`);
|
|
5507
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.type] = PROCEDURAL_PATTERN_TYPES.crossHatch;
|
|
5508
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.size] = strokeWidth;
|
|
5509
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.gap0] = gap;
|
|
5510
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.gap1] = gap;
|
|
5511
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.angle0] = angles[0] * DEGREES_TO_RADIANS2;
|
|
5512
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.angle1] = angles[1] * DEGREES_TO_RADIANS2;
|
|
5513
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.elementCount] = 2;
|
|
5514
|
+
break;
|
|
5515
|
+
}
|
|
5516
|
+
case "dots": {
|
|
5517
|
+
const radius = config2.radius === void 0 ? DEFAULT_SIZE : config2.radius;
|
|
5518
|
+
const gap = config2.gap === void 0 ? DEFAULT_GAP : config2.gap;
|
|
5519
|
+
const angle2 = config2.angle === void 0 ? DEFAULT_DOT_ANGLE : config2.angle;
|
|
5520
|
+
const skew = config2.skew === void 0 ? DEFAULT_DOT_SKEW : config2.skew;
|
|
5521
|
+
validatePositive(radius, `${id}.radius`);
|
|
5522
|
+
validateNonNegative(gap, `${id}.gap`);
|
|
5523
|
+
validateAngle(angle2, `${id}.angle`);
|
|
5524
|
+
validateSkew(skew, `${id}.skew`);
|
|
5525
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.type] = PROCEDURAL_PATTERN_TYPES.dots;
|
|
5526
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.size] = radius;
|
|
5527
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.gap0] = gap;
|
|
5528
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.gap1] = gap;
|
|
5529
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.angle0] = angle2 * DEGREES_TO_RADIANS2;
|
|
5530
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.angle1] = skew * DEGREES_TO_RADIANS2;
|
|
5531
|
+
data[offset + PROCEDURAL_PATTERN_OFFSETS.elementCount] = 2;
|
|
5532
|
+
break;
|
|
5533
|
+
}
|
|
5534
|
+
default:
|
|
5535
|
+
throw new Error(`${id}.type is not a supported procedural pattern type`);
|
|
5536
|
+
}
|
|
5537
|
+
});
|
|
5538
|
+
return {
|
|
5539
|
+
data,
|
|
5540
|
+
width: data.length / PROCEDURAL_PATTERN_COMPONENTS_PER_TEXEL,
|
|
5541
|
+
height: 1,
|
|
5542
|
+
patternIndices
|
|
5543
|
+
};
|
|
5544
|
+
}
|
|
5545
|
+
function validatePositive(value, name) {
|
|
5546
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
5547
|
+
throw new Error(`${name} must be a finite number greater than 0`);
|
|
5548
|
+
}
|
|
5549
|
+
}
|
|
5550
|
+
function validateNonNegative(value, name) {
|
|
5551
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
5552
|
+
throw new Error(`${name} must be a finite number greater than or equal to 0`);
|
|
5553
|
+
}
|
|
5554
|
+
}
|
|
5555
|
+
function validateAngle(value, name) {
|
|
5556
|
+
if (!Number.isFinite(value)) {
|
|
5557
|
+
throw new Error(`${name} must be a finite number`);
|
|
5558
|
+
}
|
|
5559
|
+
}
|
|
5560
|
+
function validateSkew(value, name) {
|
|
5561
|
+
if (!Number.isFinite(value) || value <= -90 || value >= 90) {
|
|
5562
|
+
throw new Error(`${name} must be a finite number greater than -90 and less than 90`);
|
|
5563
|
+
}
|
|
5564
|
+
}
|
|
5565
|
+
|
|
3593
5566
|
// src/fill-style/fill-style-extension.ts
|
|
5567
|
+
function leastCommonMultiple(a, b) {
|
|
5568
|
+
if (!Number.isInteger(b) || b <= 0) return 0;
|
|
5569
|
+
if (a === 0) return b;
|
|
5570
|
+
let [x, y] = [a, b];
|
|
5571
|
+
while (y > 0) [x, y] = [y, x % y];
|
|
5572
|
+
return a / x * b;
|
|
5573
|
+
}
|
|
3594
5574
|
var defaultProps4 = {
|
|
3595
5575
|
fillPatternEnabled: true,
|
|
3596
5576
|
fillPatternAtlas: {
|
|
3597
5577
|
type: "image",
|
|
3598
5578
|
value: null,
|
|
3599
|
-
async: true
|
|
3600
|
-
parameters: { lodMaxClamp: 0 }
|
|
5579
|
+
async: true
|
|
3601
5580
|
},
|
|
3602
5581
|
fillPatternMapping: { type: "object", value: {}, async: true },
|
|
3603
5582
|
fillPatternMask: true,
|
|
5583
|
+
fillPatternSizeUnits: "meters",
|
|
3604
5584
|
getFillPattern: { type: "accessor", value: (d) => d.pattern },
|
|
3605
5585
|
getFillPatternScale: { type: "accessor", value: 1 },
|
|
3606
|
-
getFillPatternOffset: { type: "accessor", value: [0, 0] }
|
|
5586
|
+
getFillPatternOffset: { type: "accessor", value: [0, 0] },
|
|
5587
|
+
getFillPatternBackgroundColor: { type: "accessor", value: [0, 0, 0, 0] }
|
|
3607
5588
|
};
|
|
3608
5589
|
var FillStyleExtension = class extends import_core10.LayerExtension {
|
|
3609
|
-
constructor({
|
|
3610
|
-
|
|
5590
|
+
constructor({
|
|
5591
|
+
pattern = false,
|
|
5592
|
+
proceduralPattern = false
|
|
5593
|
+
} = {}) {
|
|
5594
|
+
super({ pattern: pattern || proceduralPattern, proceduralPattern });
|
|
3611
5595
|
}
|
|
3612
5596
|
isEnabled(layer) {
|
|
3613
5597
|
return layer.getAttributeManager() !== null && !("pathTesselator" in layer.state);
|
|
@@ -3631,7 +5615,7 @@ ${patternFs}
|
|
|
3631
5615
|
size: 4,
|
|
3632
5616
|
stepMode: "dynamic",
|
|
3633
5617
|
accessor: "getFillPattern",
|
|
3634
|
-
transform: extension.getPatternFrame.bind(this)
|
|
5618
|
+
transform: extension.opts.proceduralPattern ? extension.getProceduralPatternIndex.bind(this) : extension.getPatternFrame.bind(this)
|
|
3635
5619
|
},
|
|
3636
5620
|
fillPatternScales: {
|
|
3637
5621
|
size: 1,
|
|
@@ -3643,6 +5627,13 @@ ${patternFs}
|
|
|
3643
5627
|
size: 2,
|
|
3644
5628
|
stepMode: "dynamic",
|
|
3645
5629
|
accessor: "getFillPatternOffset"
|
|
5630
|
+
},
|
|
5631
|
+
fillPatternBackgroundColors: {
|
|
5632
|
+
size: 4,
|
|
5633
|
+
type: "unorm8",
|
|
5634
|
+
stepMode: "dynamic",
|
|
5635
|
+
accessor: "getFillPatternBackgroundColor",
|
|
5636
|
+
defaultValue: [0, 0, 0, 255]
|
|
3646
5637
|
}
|
|
3647
5638
|
});
|
|
3648
5639
|
}
|
|
@@ -3651,7 +5642,8 @@ ${patternFs}
|
|
|
3651
5642
|
data: new Uint8Array(4),
|
|
3652
5643
|
width: 1,
|
|
3653
5644
|
height: 1
|
|
3654
|
-
})
|
|
5645
|
+
}),
|
|
5646
|
+
...extension.opts.proceduralPattern && extension.createProceduralPatternTexture.call(this, {})
|
|
3655
5647
|
});
|
|
3656
5648
|
}
|
|
3657
5649
|
updateState({ props, oldProps }, extension) {
|
|
@@ -3659,30 +5651,84 @@ ${patternFs}
|
|
|
3659
5651
|
return;
|
|
3660
5652
|
}
|
|
3661
5653
|
if (props.fillPatternMapping && props.fillPatternMapping !== oldProps.fillPatternMapping) {
|
|
5654
|
+
if (extension.opts.proceduralPattern && typeof props.fillPatternMapping !== "string") {
|
|
5655
|
+
const oldTexture = this.state.proceduralPatternTexture;
|
|
5656
|
+
this.setState(
|
|
5657
|
+
extension.createProceduralPatternTexture.call(
|
|
5658
|
+
this,
|
|
5659
|
+
props.fillPatternMapping
|
|
5660
|
+
)
|
|
5661
|
+
);
|
|
5662
|
+
oldTexture?.delete();
|
|
5663
|
+
}
|
|
3662
5664
|
this.getAttributeManager().invalidate("getFillPattern");
|
|
3663
5665
|
}
|
|
5666
|
+
if (props.fillPatternMapping !== oldProps.fillPatternMapping || props.getFillPatternScale !== oldProps.getFillPatternScale) {
|
|
5667
|
+
this.setState({ commonFrame: extension.getCommonFrame(props) });
|
|
5668
|
+
}
|
|
3664
5669
|
}
|
|
3665
5670
|
draw(params, extension) {
|
|
3666
5671
|
if (!extension.isEnabled(this)) {
|
|
3667
5672
|
return;
|
|
3668
5673
|
}
|
|
3669
|
-
const { fillPatternAtlas, fillPatternEnabled, fillPatternMask } = this.props;
|
|
5674
|
+
const { fillPatternAtlas, fillPatternEnabled, fillPatternMask, fillPatternSizeUnits } = this.props;
|
|
3670
5675
|
const fillProps = {
|
|
3671
5676
|
project: params.shaderModuleProps.project,
|
|
3672
5677
|
fillPatternEnabled,
|
|
3673
5678
|
fillPatternMask,
|
|
3674
|
-
|
|
5679
|
+
fillPatternSizeUnits,
|
|
5680
|
+
procedural: extension.opts.proceduralPattern,
|
|
5681
|
+
fillPatternTexture: extension.opts.proceduralPattern ? this.state.proceduralPatternTexture || this.state.emptyTexture : fillPatternAtlas || this.state.emptyTexture,
|
|
5682
|
+
fillPatternCommonFrame: this.state.commonFrame
|
|
3675
5683
|
};
|
|
3676
5684
|
this.setShaderModuleProps({ fill: fillProps });
|
|
3677
5685
|
}
|
|
3678
5686
|
finalizeState() {
|
|
3679
5687
|
const emptyTexture = this.state.emptyTexture;
|
|
3680
5688
|
emptyTexture?.delete();
|
|
5689
|
+
const proceduralPatternTexture = this.state.proceduralPatternTexture;
|
|
5690
|
+
proceduralPatternTexture?.delete();
|
|
3681
5691
|
}
|
|
3682
5692
|
getPatternFrame(name) {
|
|
3683
5693
|
const { fillPatternMapping } = this.getCurrentLayer().props;
|
|
3684
5694
|
const def = fillPatternMapping && fillPatternMapping[name];
|
|
3685
|
-
return def ? [def.x, def.y, def.width, def.height] : [0, 0, 0, 0];
|
|
5695
|
+
return def && "x" in def ? [def.x, def.y, def.width, def.height] : [0, 0, 0, 0];
|
|
5696
|
+
}
|
|
5697
|
+
/**
|
|
5698
|
+
* Returns common frame into which all patterns fit as integer multiples
|
|
5699
|
+
*/
|
|
5700
|
+
getCommonFrame(props) {
|
|
5701
|
+
const { fillPatternMapping, getFillPatternScale: scale3 } = props;
|
|
5702
|
+
if (typeof scale3 !== "number" || !(scale3 > 0) || typeof fillPatternMapping !== "object") {
|
|
5703
|
+
return null;
|
|
5704
|
+
}
|
|
5705
|
+
let width = 0;
|
|
5706
|
+
let height = 0;
|
|
5707
|
+
for (const name in fillPatternMapping) {
|
|
5708
|
+
const frame = fillPatternMapping[name];
|
|
5709
|
+
if (!("width" in frame)) return null;
|
|
5710
|
+
width = leastCommonMultiple(width, frame.width);
|
|
5711
|
+
height = leastCommonMultiple(height, frame.height);
|
|
5712
|
+
if (!width || !height) return null;
|
|
5713
|
+
}
|
|
5714
|
+
return width ? [scale3 * width, scale3 * height] : null;
|
|
5715
|
+
}
|
|
5716
|
+
getProceduralPatternIndex(name) {
|
|
5717
|
+
const patternIndices = this.state.proceduralPatternIndices;
|
|
5718
|
+
return [patternIndices?.get(name) ?? 0, 0, 0, 0];
|
|
5719
|
+
}
|
|
5720
|
+
createProceduralPatternTexture(mapping) {
|
|
5721
|
+
const packedPatterns = packProceduralPatterns(mapping);
|
|
5722
|
+
return {
|
|
5723
|
+
proceduralPatternIndices: packedPatterns.patternIndices,
|
|
5724
|
+
proceduralPatternTexture: this.context.device.createTexture({
|
|
5725
|
+
data: packedPatterns.data,
|
|
5726
|
+
width: packedPatterns.width,
|
|
5727
|
+
height: packedPatterns.height,
|
|
5728
|
+
format: PROCEDURAL_PATTERN_TEXTURE_FORMAT,
|
|
5729
|
+
sampler: { minFilter: "nearest", magFilter: "nearest" }
|
|
5730
|
+
})
|
|
5731
|
+
};
|
|
3686
5732
|
}
|
|
3687
5733
|
};
|
|
3688
5734
|
FillStyleExtension.defaultProps = defaultProps4;
|
|
@@ -3779,6 +5825,9 @@ in vec2 clip_commonPosition;
|
|
|
3779
5825
|
clipByInstance = Boolean(this.props.clipByInstance);
|
|
3780
5826
|
}
|
|
3781
5827
|
this.state.clipByInstance = clipByInstance;
|
|
5828
|
+
if (this.context.device.type === "webgpu") {
|
|
5829
|
+
return {};
|
|
5830
|
+
}
|
|
3782
5831
|
return clipByInstance ? {
|
|
3783
5832
|
modules: [shaderModuleVs],
|
|
3784
5833
|
inject: injectionVs
|
|
@@ -3803,6 +5852,10 @@ in vec2 clip_commonPosition;
|
|
|
3803
5852
|
Math.max(corner0[1], corner1[1])
|
|
3804
5853
|
];
|
|
3805
5854
|
}
|
|
5855
|
+
if (this.context.device.type === "webgpu") {
|
|
5856
|
+
clipProps.enabled = true;
|
|
5857
|
+
clipProps.mode = this.state.clipByInstance ? "instance" : "geometry";
|
|
5858
|
+
}
|
|
3806
5859
|
this.setShaderModuleProps({ clip: clipProps });
|
|
3807
5860
|
}
|
|
3808
5861
|
};
|
|
@@ -4428,12 +6481,12 @@ in vec2 mask_texCoords;
|
|
|
4428
6481
|
if (zoom === void 0) {
|
|
4429
6482
|
width = width - border * 2;
|
|
4430
6483
|
height = height - border * 2;
|
|
4431
|
-
const
|
|
4432
|
-
zoom = Math.min(Math.log2(
|
|
6484
|
+
const scale3 = Math.min(width / (bounds[2] - bounds[0]), height / (bounds[3] - bounds[1]));
|
|
6485
|
+
zoom = Math.min(Math.log2(scale3), 20);
|
|
4433
6486
|
} else if (!width || !height) {
|
|
4434
|
-
const
|
|
4435
|
-
width = Math.round(Math.abs(bounds[2] - bounds[0]) *
|
|
4436
|
-
height = Math.round(Math.abs(bounds[3] - bounds[1]) *
|
|
6487
|
+
const scale3 = 2 ** zoom;
|
|
6488
|
+
width = Math.round(Math.abs(bounds[2] - bounds[0]) * scale3);
|
|
6489
|
+
height = Math.round(Math.abs(bounds[3] - bounds[1]) * scale3);
|
|
4437
6490
|
const maxSize = MAX_VIEWPORT_SIZE - border * 2;
|
|
4438
6491
|
if (width > maxSize || height > maxSize) {
|
|
4439
6492
|
const r = maxSize / Math.max(width, height);
|
|
@@ -6023,9 +8076,9 @@ if ((terrain.mode == TERRAIN_MODE_USE_COVER) || (terrain.mode == TERRAIN_MODE_US
|
|
|
6023
8076
|
}
|
|
6024
8077
|
this.bounds = bounds;
|
|
6025
8078
|
this.lastViewport = viewport;
|
|
6026
|
-
const
|
|
6027
|
-
const pixelWidth = (bounds[2] - bounds[0]) *
|
|
6028
|
-
const pixelHeight = (bounds[3] - bounds[1]) *
|
|
8079
|
+
const scale3 = viewport.scale;
|
|
8080
|
+
const pixelWidth = (bounds[2] - bounds[0]) * scale3;
|
|
8081
|
+
const pixelHeight = (bounds[3] - bounds[1]) * scale3;
|
|
6029
8082
|
const centerMerc = viewport.isGeospatial ? lngLatToMercatorCommon([
|
|
6030
8083
|
viewport.longitude ?? 0,
|
|
6031
8084
|
viewport.latitude ?? 0
|