@vectojs/core 1.38.1 → 1.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-QVTCKWDO.mjs → chunk-7PYD5UDX.mjs} +93 -43
- package/dist/{chunk-ZYYP2M5D.js → chunk-KRUDTGNR.js} +168 -76
- package/dist/{chunk-SRXU3ZXK.js → chunk-POPXUPKR.js} +141 -91
- package/dist/{chunk-LWEMD34D.mjs → chunk-RUM2KEDI.mjs} +119 -27
- package/dist/index.js +686 -395
- package/dist/index.mjs +372 -81
- package/dist/renderer/CanvasRenderer.d.ts +6 -0
- package/dist/renderer/SVGRenderer.d.ts +17 -2
- package/dist/renderer/WebGPUParticleSystemManager.d.ts +2 -0
- package/dist/renderer.js +2 -2
- package/dist/renderer.mjs +1 -1
- package/dist/text/MSDFTextEntity.d.ts +0 -1
- package/dist/text.js +2 -2
- package/dist/text.mjs +1 -1
- package/dist/tree/Entity.d.ts +8 -2
- package/dist/tree/Scene.d.ts +106 -0
- package/dist/tree/scene/A11yProjectionManager.d.ts +12 -0
- package/dist/tree/scene/HitTester.d.ts +14 -2
- package/dist/tree/scene/WasmBackendFacade.d.ts +17 -0
- package/dist/tree/scene/keyboard.d.ts +56 -0
- package/dist/wasm/backend.d.ts +7 -7
- package/dist/wasm/vectojs_core.wasm +0 -0
- package/package.json +5 -5
|
@@ -398,6 +398,18 @@ var Entity = class {
|
|
|
398
398
|
}
|
|
399
399
|
/** Attach a single child (the O(1) common path). See {@link add}. */
|
|
400
400
|
_addOne(child) {
|
|
401
|
+
if (child === this) {
|
|
402
|
+
throw new Error(`Entity.add(): cannot add entity "${child.id}" under itself.`);
|
|
403
|
+
}
|
|
404
|
+
let ancestor = this.parent;
|
|
405
|
+
while (ancestor) {
|
|
406
|
+
if (ancestor === child) {
|
|
407
|
+
throw new Error(
|
|
408
|
+
`Entity.add(): cannot add entity "${child.id}" under "${this.id}" \u2014 an entity cannot be added under its own descendant.`
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
ancestor = ancestor.parent;
|
|
412
|
+
}
|
|
401
413
|
if (child.parent) child.parent.remove(child);
|
|
402
414
|
child.parent = this;
|
|
403
415
|
this.children.push(child);
|
|
@@ -485,6 +497,19 @@ var Entity = class {
|
|
|
485
497
|
* @example entity.animate({ x: 400, opacity: 0 }, 500);
|
|
486
498
|
*/
|
|
487
499
|
animate(targetProps, durationMs) {
|
|
500
|
+
if (!Number.isFinite(durationMs) || durationMs <= 0) {
|
|
501
|
+
for (const key in targetProps) {
|
|
502
|
+
const end = targetProps[key];
|
|
503
|
+
if (typeof end !== "number") continue;
|
|
504
|
+
if (ANIMATABLE_PROPS.has(key)) {
|
|
505
|
+
this._applyAnimated(key, end);
|
|
506
|
+
} else {
|
|
507
|
+
this[key] = end;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
this.scene?.markDirty({ entity: this.id, reason: "animation-start" });
|
|
511
|
+
return this;
|
|
512
|
+
}
|
|
488
513
|
(this.animations ??= []).push({
|
|
489
514
|
target: targetProps,
|
|
490
515
|
duration: durationMs,
|
|
@@ -566,6 +591,11 @@ var Entity = class {
|
|
|
566
591
|
const from = this._currentOf(prop);
|
|
567
592
|
const driver = isTweenConfig(cfg) ? new TweenDriver(from, to, cfg) : new SpringDriver(from, to, cfg === "spring" ? {} : cfg);
|
|
568
593
|
(this._drivers ??= /* @__PURE__ */ new Map()).set(prop, driver);
|
|
594
|
+
const s = this.scene;
|
|
595
|
+
if (s && this._driversTickedFrame === s.currentFrame && s._updateWalkDt !== null) {
|
|
596
|
+
driver.tick(s._updateWalkDt);
|
|
597
|
+
this._applyDriverTick(prop, driver);
|
|
598
|
+
}
|
|
569
599
|
this.scene?.markDirty({ entity: this.id, reason: "driver-added" });
|
|
570
600
|
this.scene?._registerActiveDriverEntity(this);
|
|
571
601
|
}
|
|
@@ -652,10 +682,6 @@ var Entity = class {
|
|
|
652
682
|
this._applyAnimated(prop, driver.value);
|
|
653
683
|
}
|
|
654
684
|
}
|
|
655
|
-
/** Internal: true if this entity currently has any active property driver. */
|
|
656
|
-
_hasActiveDrivers() {
|
|
657
|
-
return !!this._drivers && this._drivers.size > 0;
|
|
658
|
-
}
|
|
659
685
|
/**
|
|
660
686
|
* Advance the entity's internal state for one frame.
|
|
661
687
|
*
|
|
@@ -1024,6 +1050,14 @@ var Entity = class {
|
|
|
1024
1050
|
* Accumulated world rotation: this entity's own `rotation` plus
|
|
1025
1051
|
* that of every ancestor.
|
|
1026
1052
|
*
|
|
1053
|
+
* Valid only under positive scales: the sum models the composed matrix
|
|
1054
|
+
* `T*S*R` correctly while every ancestor's `scaleX`/`scaleY` is positive,
|
|
1055
|
+
* but a mirrored (negative-scale) ancestor flips handedness, which an
|
|
1056
|
+
* additive sum cannot represent — the result is then off by the mirror.
|
|
1057
|
+
* For mirror-safe rotation, derive the angle from
|
|
1058
|
+
* {@link getWorldTransform}'s matrix (e.g. `atan2(b, a)`), as SVGEntity's
|
|
1059
|
+
* signed-scale handling already does.
|
|
1060
|
+
*
|
|
1027
1061
|
* @returns The accumulated world rotation in radians.
|
|
1028
1062
|
*/
|
|
1029
1063
|
getWorldRotation() {
|
|
@@ -1242,7 +1276,6 @@ var MSDFTextEntity = class extends Entity {
|
|
|
1242
1276
|
// long-lived shared atlas image would retain the whole entity.
|
|
1243
1277
|
atlasDecodeTarget = null;
|
|
1244
1278
|
atlasDecodeHandler = null;
|
|
1245
|
-
rgbColorCache = /* @__PURE__ */ new Map();
|
|
1246
1279
|
fontStringCache = [];
|
|
1247
1280
|
layoutResult = null;
|
|
1248
1281
|
/**
|
|
@@ -1429,7 +1462,12 @@ var MSDFTextEntity = class extends Entity {
|
|
|
1429
1462
|
shaped.push(String.fromCodePoint(res.codePoints[i]));
|
|
1430
1463
|
}
|
|
1431
1464
|
const sourceWithoutNewlines = this.text.replace(/\n/g, "");
|
|
1432
|
-
if (this.textAlign !== "left" ||
|
|
1465
|
+
if (this.textAlign !== "left" || // The layout worker breaks lines on \n only, so a \r survives as a real
|
|
1466
|
+
// glyph — a phantom ~1em advance at every CRLF line end — yet would
|
|
1467
|
+
// still compare equal below after \n is stripped from both sides. The
|
|
1468
|
+
// contract promises the coarse branch whenever the reply cannot
|
|
1469
|
+
// reproduce the source exactly, and that includes any \r (#692).
|
|
1470
|
+
this.text.includes("\r") || shaped.join("") !== sourceWithoutNewlines || res.yCoords.length !== res.codePoints.length) {
|
|
1433
1471
|
this.projectionLines = [];
|
|
1434
1472
|
return;
|
|
1435
1473
|
}
|
|
@@ -1438,9 +1476,19 @@ var MSDFTextEntity = class extends Entity {
|
|
|
1438
1476
|
const desc = metrics?.descender ?? -0.2;
|
|
1439
1477
|
const actualLineHeight = this.lineHeight ?? this.fontSize * (asc - desc);
|
|
1440
1478
|
const baseline = asc * this.fontSize;
|
|
1441
|
-
const
|
|
1442
|
-
|
|
1443
|
-
|
|
1479
|
+
const srcStart = [];
|
|
1480
|
+
const srcEnd = [];
|
|
1481
|
+
{
|
|
1482
|
+
let cursor = 0;
|
|
1483
|
+
while (cursor < this.text.length) {
|
|
1484
|
+
const code = this.text.codePointAt(cursor);
|
|
1485
|
+
const width = code > 65535 ? 2 : 1;
|
|
1486
|
+
if (code !== 10) {
|
|
1487
|
+
srcStart.push(cursor);
|
|
1488
|
+
srcEnd.push(cursor + width);
|
|
1489
|
+
}
|
|
1490
|
+
cursor += width;
|
|
1491
|
+
}
|
|
1444
1492
|
}
|
|
1445
1493
|
const glyphsByLine = /* @__PURE__ */ new Map();
|
|
1446
1494
|
let maxIdx = -1;
|
|
@@ -1456,8 +1504,8 @@ var MSDFTextEntity = class extends Entity {
|
|
|
1456
1504
|
let previousEnd = 0;
|
|
1457
1505
|
for (let i = 0; i <= maxIdx; i++) {
|
|
1458
1506
|
const glyphs = glyphsByLine.get(i) ?? [];
|
|
1459
|
-
const start = glyphs.length > 0 ?
|
|
1460
|
-
const end = glyphs.length > 0 ?
|
|
1507
|
+
const start = glyphs.length > 0 ? srcStart[glyphs[0]] : previousEnd;
|
|
1508
|
+
const end = glyphs.length > 0 ? srcEnd[glyphs[glyphs.length - 1]] : start;
|
|
1461
1509
|
starts.push(start);
|
|
1462
1510
|
ends.push(end);
|
|
1463
1511
|
previousEnd = Math.max(previousEnd, end);
|
|
@@ -1510,7 +1558,6 @@ var MSDFTextEntity = class extends Entity {
|
|
|
1510
1558
|
const code = this.layoutResult.codePoints[i];
|
|
1511
1559
|
const nodeX = this.layoutResult.xCoords[i];
|
|
1512
1560
|
const nodeY = this.layoutResult.yCoords[i];
|
|
1513
|
-
const packedStyle = this.layoutResult.packedStyles[i];
|
|
1514
1561
|
const def = this.font.getGlyph(code);
|
|
1515
1562
|
if (!def || !def.atlasBounds || !def.planeBounds) continue;
|
|
1516
1563
|
const { atlasBounds: ab, planeBounds: pb } = def;
|
|
@@ -1524,15 +1571,6 @@ var MSDFTextEntity = class extends Entity {
|
|
|
1524
1571
|
const glyphH = (pb.top - pb.bottom) * this.fontSize * worldScaleY;
|
|
1525
1572
|
const v0 = this.font.data.atlas.yOrigin === "bottom" ? 1 - ab.top / ah : ab.top / ah;
|
|
1526
1573
|
const v1 = this.font.data.atlas.yOrigin === "bottom" ? 1 - ab.bottom / ah : ab.bottom / ah;
|
|
1527
|
-
const colorVal = packedStyle >>> 8;
|
|
1528
|
-
let runColor = this.rgbColorCache.get(colorVal);
|
|
1529
|
-
if (!runColor) {
|
|
1530
|
-
const r = colorVal >> 16 & 255;
|
|
1531
|
-
const g = colorVal >> 8 & 255;
|
|
1532
|
-
const b = colorVal & 255;
|
|
1533
|
-
runColor = `rgb(${r},${g},${b})`;
|
|
1534
|
-
this.rgbColorCache.set(colorVal, runColor);
|
|
1535
|
-
}
|
|
1536
1574
|
scene.pointRenderer.addGlyph(
|
|
1537
1575
|
glyphX,
|
|
1538
1576
|
glyphY,
|
|
@@ -1542,7 +1580,7 @@ var MSDFTextEntity = class extends Entity {
|
|
|
1542
1580
|
v0,
|
|
1543
1581
|
ab.right / aw,
|
|
1544
1582
|
v1,
|
|
1545
|
-
|
|
1583
|
+
this.color,
|
|
1546
1584
|
worldOpacity,
|
|
1547
1585
|
worldRot
|
|
1548
1586
|
);
|
|
@@ -1562,16 +1600,7 @@ var MSDFTextEntity = class extends Entity {
|
|
|
1562
1600
|
const nodeY = this.layoutResult.yCoords[i];
|
|
1563
1601
|
const packedStyle = this.layoutResult.packedStyles[i];
|
|
1564
1602
|
const fontString = this.fontStringCache[packedStyle & 3];
|
|
1565
|
-
|
|
1566
|
-
let runColor = this.rgbColorCache.get(colorVal);
|
|
1567
|
-
if (!runColor) {
|
|
1568
|
-
const r = colorVal >> 16 & 255;
|
|
1569
|
-
const g = colorVal >> 8 & 255;
|
|
1570
|
-
const b = colorVal & 255;
|
|
1571
|
-
runColor = `rgb(${r},${g},${b})`;
|
|
1572
|
-
this.rgbColorCache.set(colorVal, runColor);
|
|
1573
|
-
}
|
|
1574
|
-
renderer.fillText(String.fromCodePoint(code), nodeX, nodeY, fontString, runColor);
|
|
1603
|
+
renderer.fillText(String.fromCodePoint(code), nodeX, nodeY, fontString, this.color);
|
|
1575
1604
|
}
|
|
1576
1605
|
}
|
|
1577
1606
|
destroy() {
|
|
@@ -1586,11 +1615,32 @@ var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
|
1586
1615
|
function isSvgWhitespace(ch) {
|
|
1587
1616
|
return ch === " " || ch === " " || ch === "\n" || ch === "\r";
|
|
1588
1617
|
}
|
|
1618
|
+
function findSvgTagEnd(source, from) {
|
|
1619
|
+
let quote = null;
|
|
1620
|
+
for (let i = from; i < source.length; i++) {
|
|
1621
|
+
const ch = source[i];
|
|
1622
|
+
if (quote !== null) {
|
|
1623
|
+
if (ch === quote) quote = null;
|
|
1624
|
+
} else if (ch === '"' || ch === "'") {
|
|
1625
|
+
quote = ch;
|
|
1626
|
+
} else if (ch === ">") {
|
|
1627
|
+
return i;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
return -1;
|
|
1631
|
+
}
|
|
1632
|
+
function isPercentDimension(value) {
|
|
1633
|
+
return value.trim().endsWith("%");
|
|
1634
|
+
}
|
|
1635
|
+
function viewBoxDimensions(value) {
|
|
1636
|
+
const parts = value.split(/[\s,]+/).map(parseFloat);
|
|
1637
|
+
return parts.length === 4 && parts.every(Number.isFinite) ? { width: parts[2], height: parts[3] } : null;
|
|
1638
|
+
}
|
|
1589
1639
|
function readSvgAttribute(source, name) {
|
|
1590
1640
|
const lowerSource = source.toLowerCase();
|
|
1591
1641
|
const svgStart = lowerSource.indexOf("<svg");
|
|
1592
1642
|
if (svgStart < 0) return null;
|
|
1593
|
-
const tagEnd = source
|
|
1643
|
+
const tagEnd = findSvgTagEnd(source, svgStart + 4);
|
|
1594
1644
|
if (tagEnd < 0) return null;
|
|
1595
1645
|
const tag = source.slice(svgStart + 4, tagEnd);
|
|
1596
1646
|
const lowerTag = tag.toLowerCase();
|
|
@@ -1662,14 +1712,14 @@ var SVGEntity = class extends Entity {
|
|
|
1662
1712
|
const wAttr = svgEl.getAttribute("width");
|
|
1663
1713
|
const hAttr = svgEl.getAttribute("height");
|
|
1664
1714
|
const vbAttr = svgEl.getAttribute("viewBox");
|
|
1665
|
-
if (wAttr && hAttr) {
|
|
1715
|
+
if (wAttr && hAttr && !isPercentDimension(wAttr) && !isPercentDimension(hAttr)) {
|
|
1666
1716
|
width = parseFloat(wAttr) || 100;
|
|
1667
1717
|
height = parseFloat(hAttr) || 100;
|
|
1668
1718
|
} else if (vbAttr) {
|
|
1669
|
-
const
|
|
1670
|
-
if (
|
|
1671
|
-
width =
|
|
1672
|
-
height =
|
|
1719
|
+
const dims = viewBoxDimensions(vbAttr);
|
|
1720
|
+
if (dims) {
|
|
1721
|
+
width = dims.width;
|
|
1722
|
+
height = dims.height;
|
|
1673
1723
|
}
|
|
1674
1724
|
}
|
|
1675
1725
|
}
|
|
@@ -1681,14 +1731,14 @@ var SVGEntity = class extends Entity {
|
|
|
1681
1731
|
const wAttr = readSvgAttribute(this.svgSource, "width");
|
|
1682
1732
|
const hAttr = readSvgAttribute(this.svgSource, "height");
|
|
1683
1733
|
const vbAttr = readSvgAttribute(this.svgSource, "viewBox");
|
|
1684
|
-
if (wAttr && hAttr) {
|
|
1734
|
+
if (wAttr && hAttr && !isPercentDimension(wAttr) && !isPercentDimension(hAttr)) {
|
|
1685
1735
|
width = parseFloat(wAttr) || 100;
|
|
1686
1736
|
height = parseFloat(hAttr) || 100;
|
|
1687
1737
|
} else if (vbAttr) {
|
|
1688
|
-
const
|
|
1689
|
-
if (
|
|
1690
|
-
width =
|
|
1691
|
-
height =
|
|
1738
|
+
const dims = viewBoxDimensions(vbAttr);
|
|
1739
|
+
if (dims) {
|
|
1740
|
+
width = dims.width;
|
|
1741
|
+
height = dims.height;
|
|
1692
1742
|
}
|
|
1693
1743
|
}
|
|
1694
1744
|
}
|