@weasel-js/core 1.0.1 → 1.0.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/CHANGELOG.md +330 -0
- package/dist/{DrawCommand-DCmiNkBj.d.ts → DrawCommand-BbPrSTra.d.ts} +24 -0
- package/dist/{chunk-Z3KNFTTS.js → chunk-3JYFAQIB.js} +3 -3
- package/dist/chunk-3JYFAQIB.js.map +1 -0
- package/dist/{chunk-UB6A6L77.js → chunk-3KGMJMOS.js} +3 -3
- package/dist/{chunk-UB6A6L77.js.map → chunk-3KGMJMOS.js.map} +1 -1
- package/dist/{chunk-IO5Z75X4.js → chunk-HGF5X6MZ.js} +2 -2
- package/dist/chunk-HGF5X6MZ.js.map +1 -0
- package/dist/{chunk-775XXAHR.js → chunk-JOSUVS6C.js} +2 -2
- package/dist/{chunk-775XXAHR.js.map → chunk-JOSUVS6C.js.map} +1 -1
- package/dist/{chunk-FVNWK7U3.js → chunk-SMCZRSYB.js} +1677 -1371
- package/dist/chunk-SMCZRSYB.js.map +1 -0
- package/dist/{chunk-DCIU3SRK.js → chunk-TR3HZN34.js} +79 -151
- package/dist/chunk-TR3HZN34.js.map +1 -0
- package/dist/{chunk-6SHZHXXR.js → chunk-U45SODC2.js} +2 -2
- package/dist/{chunk-6SHZHXXR.js.map → chunk-U45SODC2.js.map} +1 -1
- package/dist/clipboard.d.ts +5 -2
- package/dist/clipboard.js +1 -2
- package/dist/clone.d.ts +2 -2
- package/dist/clone.js +0 -1
- package/dist/{geometry-DVeZ2i-c.d.ts → geometry-5X6TrAkX.d.ts} +1 -1
- package/dist/{grid-XcFQVS0f.d.ts → grid-sfilv5HP.d.ts} +7 -2
- package/dist/index.d.ts +695 -49
- package/dist/index.js +7 -8
- package/dist/insert.d.ts +6 -3
- package/dist/insert.js +1 -2
- package/dist/insert.js.map +1 -1
- package/dist/move.d.ts +26 -5
- package/dist/move.js +2 -3
- package/dist/move.js.map +1 -1
- package/dist/{options-wAvCnOZd.d.ts → options-CbsIgQci.d.ts} +3 -1
- package/dist/patterns-builtin.d.ts +8 -0
- package/dist/patterns-builtin.js +1 -2
- package/dist/{pointSnapToGrid-BuDZWBrL.d.ts → pointSnapToGrid-C3wowsQy.d.ts} +9 -3
- package/dist/{index-DPI8DV0A.d.ts → registry-CxnkXbG8.d.ts} +225 -350
- package/dist/renderer.d.ts +36 -4
- package/dist/renderer.js +7 -8
- package/dist/resize.d.ts +12 -5
- package/dist/resize.js +2 -3
- package/dist/routing.d.ts +106 -7
- package/dist/routing.js +1 -2
- package/dist/{types-BJ8_cyT7.d.ts → types-BHK2dkMu.d.ts} +43 -1
- package/dist/{types-BhJJ2OCM.d.ts → types-ByZxoD6P.d.ts} +1 -1
- package/dist/{types-B_-khFM0.d.ts → types-DpQ0cWYC.d.ts} +32 -0
- package/dist/{types-Kh_osAq9.d.ts → types-vqxpCFVp.d.ts} +31 -0
- package/dist/{viewToMat3-BvaWCN99.d.ts → viewToMat3-CQW_YqDw.d.ts} +1 -1
- package/package.json +6 -6
- package/dist/chunk-DCIU3SRK.js.map +0 -1
- package/dist/chunk-FVNWK7U3.js.map +0 -1
- package/dist/chunk-IO5Z75X4.js.map +0 -1
- package/dist/chunk-PZ5AY32C.js +0 -9
- package/dist/chunk-PZ5AY32C.js.map +0 -1
- package/dist/chunk-Z3KNFTTS.js.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { ActionDisabledReason,
|
|
2
|
-
import { scratchKey, createTransformOp, snap, getScratch, createReparentOp, setScratch } from './chunk-
|
|
3
|
-
import { fixedCornerOf, DEFAULT_RESIZE_BEHAVIORS, CORNER_ANCHORS, cornerPoint } from './chunk-
|
|
4
|
-
import { AUTO_POSE_DESCRIPTOR, RECT_POSE_DESCRIPTOR, resolveUnit, pathPoseDescriptor, polygonFromPoints, starPath, regularPolygonPath, linePath, ellipsePath, rectPath, strokeHitTest, withinPxRadius, boundsOfPath, PATH_M, PATH_L, PATH_Z, PATH_Q, PATH_C, DEFAULT_FLATTEN_TOLERANCE, flattenQuadratic, flattenCubic, PathBuilder, pointInPath, transformPath, translatePath, pxExtent, remapRotatedLeaf, withinPxBox, flattenCubicWithArcLen, flattenQuadraticWithArcLen, PATH_CMD_LENGTHS, pointToGridCell } from './chunk-
|
|
5
|
-
import { dispatchApplyBatch, dlog, dwarn, usePointerContext, PointerContextProvider, applyOpsTo, extractWeaselClipboardFromSvg, parseWeaselClipboardText, materializePaste, WEASEL_CLIPBOARD_MIME, WEASEL_CLIPBOARD_MIME_WEB, sniffWeaselClipboardText } from './chunk-
|
|
1
|
+
import { ActionDisabledReason, useOptionalDepRegistry, DepRegistryProvider, useActionsRegistry, ActionsProvider, useDepRegistry, reportRouteConflicts, buildDepsFromRequires, useDepSource, actionBindings } from './chunk-TR3HZN34.js';
|
|
2
|
+
import { scratchKey, createTransformOp, snap, getScratch, createReparentOp, setScratch } from './chunk-3KGMJMOS.js';
|
|
3
|
+
import { fixedCornerOf, DEFAULT_RESIZE_BEHAVIORS, CORNER_ANCHORS, cornerPoint } from './chunk-3JYFAQIB.js';
|
|
4
|
+
import { AUTO_POSE_DESCRIPTOR, RECT_POSE_DESCRIPTOR, resolveUnit, pathPoseDescriptor, polygonFromPoints, starPath, regularPolygonPath, linePath, ellipsePath, rectPath, strokeHitTest, withinPxRadius, boundsOfPath, PATH_M, PATH_L, PATH_Z, PATH_Q, PATH_C, DEFAULT_FLATTEN_TOLERANCE, flattenQuadratic, flattenCubic, PathBuilder, pointInPath, transformPath, translatePath, pxExtent, remapRotatedLeaf, withinPxBox, flattenCubicWithArcLen, flattenQuadraticWithArcLen, PATH_CMD_LENGTHS, pointToGridCell } from './chunk-U45SODC2.js';
|
|
5
|
+
import { dispatchApplyBatch, dlog, dwarn, usePointerContext, PointerContextProvider, applyOpsTo, extractWeaselClipboardFromSvg, parseWeaselClipboardText, materializePaste, WEASEL_CLIPBOARD_MIME, WEASEL_CLIPBOARD_MIME_WEB, sniffWeaselClipboardText } from './chunk-JOSUVS6C.js';
|
|
6
6
|
import { createSetSelectionOp } from './chunk-BHVYVFGV.js';
|
|
7
7
|
import { registerOpFactory, createDeleteOp, createInsertOp, rebuildOp } from './chunk-GVCNT7UH.js';
|
|
8
|
-
import { getTexture, chunks, dots, crosshatch, hatch } from './chunk-
|
|
8
|
+
import { getTexture, chunks, dots, crosshatch, hatch } from './chunk-HGF5X6MZ.js';
|
|
9
9
|
import { createContext, createElement, forwardRef, useMemo, useRef, useState, useCallback, useImperativeHandle, useEffect, useSyncExternalStore, useContext, useReducer, useLayoutEffect } from 'react';
|
|
10
10
|
import { subscribeGlyphReady, DEFAULT_BAKE_BUDGET, TEXT_VERT_SRC, TEXT_FRAG_SRC, TEXT_SDF_UNIFORMS, TEXT_SDF_ATTRIBUTES, TEXT_FRAG_R8_SRC, markAllFontsNotUploaded, resetBakeBudget, glyphGeneration, syncDynamicPageTexture, ensureFontTexture, dynamicPageTextureId, textureCacheKey, resolveFontVariant, resolveGlyphFallback, glyphOutline } from '@weasel-js/font';
|
|
11
11
|
export { canQueryLocalFonts, canQueryLocalFonts as canQueryLocalFonts2, enableLocalFontOutlines, enableLocalFontOutlines as enableLocalFontOutlines2, hasFontOutlines, hasFontOutlines as hasFontOutlines2, isCanvasFont, isCanvasFont as isCanvasFont2, listFontOutlines, listFontOutlines as listFontOutlines2, outlineStatus, outlineStatus as outlineStatus2, registerCanvasFont, registerCanvasFont as registerCanvasFont2, registerFont, registerFont as registerFont2, registerFontOutlines, registerFontOutlines as registerFontOutlines2, subscribeGlyphReady, subscribeGlyphReady as subscribeGlyphReady2, unregisterCanvasFont, unregisterCanvasFont as unregisterCanvasFont2, unregisterFontOutlines, unregisterFontOutlines as unregisterFontOutlines2 } from '@weasel-js/font';
|
|
@@ -13,13 +13,13 @@ import earcut from 'earcut';
|
|
|
13
13
|
import { applyToPoint, rotateAboutPoint, boxToBox, pointInPolygon, segmentsCross } from '@weasel-js/geom';
|
|
14
14
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
15
15
|
import polygonClipping from 'polygon-clipping';
|
|
16
|
-
import { matchSpec, parseTargetSpec, mimeMatchesGlob } from '@weasel-js/gestures';
|
|
16
|
+
import { matchSpec, RESERVED_ID_PREFIXES, RESERVED_ID_NAMES, parseTargetSpec, mimeMatchesGlob } from '@weasel-js/gestures';
|
|
17
17
|
import { NORMAL, IMPLICIT_TAGS } from '@weasel-js/modes';
|
|
18
18
|
import { createHistory } from '@weasel-js/history';
|
|
19
19
|
export * from '@weasel-js/history';
|
|
20
20
|
|
|
21
21
|
// src/version.ts
|
|
22
|
-
var VERSION = "1.0.
|
|
22
|
+
var VERSION = "1.0.3" ;
|
|
23
23
|
|
|
24
24
|
// src/features/grid/roundToCell.ts
|
|
25
25
|
function roundToCell(value, cellSize) {
|
|
@@ -399,6 +399,8 @@ var GLMeshCache = class {
|
|
|
399
399
|
gl;
|
|
400
400
|
aPositionLoc;
|
|
401
401
|
map = /* @__PURE__ */ new WeakMap();
|
|
402
|
+
/** Meshes this context has drawn at least once; see `uploadRecurring`. */
|
|
403
|
+
seen = /* @__PURE__ */ new WeakSet();
|
|
402
404
|
finalizer;
|
|
403
405
|
pendingDeletes = [];
|
|
404
406
|
/** Transient resources allocated this frame; freed at end of render(). */
|
|
@@ -423,6 +425,19 @@ var GLMeshCache = class {
|
|
|
423
425
|
this.transientThisFrame.push({ vao: handle.vao, vbo, ibo });
|
|
424
426
|
return handle;
|
|
425
427
|
}
|
|
428
|
+
/**
|
|
429
|
+
* Upload a Mesh that may or may not recur across frames. Its first sight in
|
|
430
|
+
* *this* context takes `uploadTransient`; every later one takes the
|
|
431
|
+
* persistent handle. One transient upload to find out is cheaper than
|
|
432
|
+
* stranding a persistent VAO on a mesh that never returns, whose release
|
|
433
|
+
* would wait on GC. The transient upload does not populate the persistent
|
|
434
|
+
* map, so steady-state reuse begins on the third frame.
|
|
435
|
+
*/
|
|
436
|
+
uploadRecurring(mesh) {
|
|
437
|
+
if (this.seen.has(mesh)) return this.handleFor(mesh);
|
|
438
|
+
this.seen.add(mesh);
|
|
439
|
+
return this.uploadTransient(mesh);
|
|
440
|
+
}
|
|
426
441
|
/**
|
|
427
442
|
* Free all transient resources allocated since the last call. Called by
|
|
428
443
|
* the renderer at the end of each `render()`. Safe under context loss.
|
|
@@ -1532,1269 +1547,1269 @@ function getMesh(path) {
|
|
|
1532
1547
|
cache.set(path, mesh);
|
|
1533
1548
|
return mesh;
|
|
1534
1549
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
}
|
|
1541
|
-
function extractRect(p) {
|
|
1542
|
-
const { x, y, width: w, height: h } = p;
|
|
1543
|
-
return {
|
|
1544
|
-
points: [x, y, x + w, y, x + w, y + h, x, y + h],
|
|
1545
|
-
closed: true,
|
|
1546
|
-
anchorA: new Uint32Array([0, 1, 2, 3]),
|
|
1547
|
-
anchorB: new Uint32Array([0, 1, 2, 3]),
|
|
1548
|
-
anchorT: new Float32Array([0, 0, 0, 0])
|
|
1549
|
-
};
|
|
1550
|
+
var UNDERLINE_OFFSET = 0.1;
|
|
1551
|
+
var STRIKETHROUGH_OFFSET = -0.3;
|
|
1552
|
+
var DECORATION_THICKNESS = 0.05;
|
|
1553
|
+
function fillKey(p) {
|
|
1554
|
+
if ("color" in p) return `s:${p.color}:${p.opacity ?? 1}`;
|
|
1555
|
+
return `nx:${Math.random()}`;
|
|
1550
1556
|
}
|
|
1551
|
-
function
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
let anchorCounter = 0;
|
|
1556
|
-
let pts = null;
|
|
1557
|
-
let aA = null;
|
|
1558
|
-
let aB = null;
|
|
1559
|
-
let aT = null;
|
|
1560
|
-
let current = null;
|
|
1561
|
-
let coordIdx = 0;
|
|
1562
|
-
let prevX = 0;
|
|
1563
|
-
let prevY = 0;
|
|
1564
|
-
let prevAnchor = -1;
|
|
1565
|
-
const beginContour = () => {
|
|
1566
|
-
pts = [];
|
|
1567
|
-
aA = [];
|
|
1568
|
-
aB = [];
|
|
1569
|
-
aT = [];
|
|
1570
|
-
const next = { points: pts, closed: false };
|
|
1571
|
-
out.push(next);
|
|
1572
|
-
return next;
|
|
1573
|
-
};
|
|
1574
|
-
const dropDuplicateClosingPoint = () => {
|
|
1575
|
-
if (!pts || !aA || !aB || !aT) return;
|
|
1576
|
-
const n = pts.length / 2;
|
|
1577
|
-
if (n < 2) return;
|
|
1578
|
-
if (pts[0] !== pts[(n - 1) * 2] || pts[1] !== pts[(n - 1) * 2 + 1]) return;
|
|
1579
|
-
pts.length -= 2;
|
|
1580
|
-
aA.length -= 1;
|
|
1581
|
-
aB.length -= 1;
|
|
1582
|
-
aT.length -= 1;
|
|
1583
|
-
};
|
|
1584
|
-
const commit = (target) => {
|
|
1585
|
-
if (!pts || !aA || !aB || !aT) return;
|
|
1586
|
-
target.anchorA = new Uint32Array(aA);
|
|
1587
|
-
target.anchorB = new Uint32Array(aB);
|
|
1588
|
-
target.anchorT = new Float32Array(aT);
|
|
1589
|
-
};
|
|
1590
|
-
for (let cmdIdx = 0; cmdIdx < commands.length; cmdIdx++) {
|
|
1591
|
-
const cmd = commands[cmdIdx];
|
|
1592
|
-
switch (cmd) {
|
|
1593
|
-
case PATH_M: {
|
|
1594
|
-
if (current) commit(current);
|
|
1595
|
-
current = beginContour();
|
|
1596
|
-
prevX = coords[coordIdx];
|
|
1597
|
-
prevY = coords[coordIdx + 1];
|
|
1598
|
-
pts.push(prevX, prevY);
|
|
1599
|
-
aA.push(anchorCounter);
|
|
1600
|
-
aB.push(anchorCounter);
|
|
1601
|
-
aT.push(0);
|
|
1602
|
-
prevAnchor = anchorCounter;
|
|
1603
|
-
anchorCounter++;
|
|
1604
|
-
coordIdx += 2;
|
|
1605
|
-
break;
|
|
1606
|
-
}
|
|
1607
|
-
case PATH_L: {
|
|
1608
|
-
prevX = coords[coordIdx];
|
|
1609
|
-
prevY = coords[coordIdx + 1];
|
|
1610
|
-
pts.push(prevX, prevY);
|
|
1611
|
-
aA.push(anchorCounter);
|
|
1612
|
-
aB.push(anchorCounter);
|
|
1613
|
-
aT.push(0);
|
|
1614
|
-
prevAnchor = anchorCounter;
|
|
1615
|
-
anchorCounter++;
|
|
1616
|
-
coordIdx += 2;
|
|
1617
|
-
break;
|
|
1618
|
-
}
|
|
1619
|
-
case PATH_Q: {
|
|
1620
|
-
const cx = coords[coordIdx], cy = coords[coordIdx + 1];
|
|
1621
|
-
const ex = coords[coordIdx + 2], ey = coords[coordIdx + 3];
|
|
1622
|
-
const targetAnchor = anchorCounter;
|
|
1623
|
-
const segStart = pts.length / 2;
|
|
1624
|
-
const arcAccum = [];
|
|
1625
|
-
const total = flattenQuadraticWithArcLen(prevX, prevY, cx, cy, ex, ey, tolerance, pts, arcAccum);
|
|
1626
|
-
for (let k = 0; k < arcAccum.length; k++) {
|
|
1627
|
-
aA.push(prevAnchor);
|
|
1628
|
-
aB.push(targetAnchor);
|
|
1629
|
-
aT.push(total > 0 ? arcAccum[k] / total : 0);
|
|
1630
|
-
}
|
|
1631
|
-
const lastIdx = segStart + arcAccum.length - 1;
|
|
1632
|
-
aA[lastIdx] = targetAnchor;
|
|
1633
|
-
aB[lastIdx] = targetAnchor;
|
|
1634
|
-
aT[lastIdx] = 0;
|
|
1635
|
-
prevX = ex;
|
|
1636
|
-
prevY = ey;
|
|
1637
|
-
prevAnchor = targetAnchor;
|
|
1638
|
-
anchorCounter++;
|
|
1639
|
-
coordIdx += 4;
|
|
1640
|
-
break;
|
|
1641
|
-
}
|
|
1642
|
-
case PATH_C: {
|
|
1643
|
-
const c1x = coords[coordIdx], c1y = coords[coordIdx + 1];
|
|
1644
|
-
const c2x = coords[coordIdx + 2], c2y = coords[coordIdx + 3];
|
|
1645
|
-
const ex = coords[coordIdx + 4], ey = coords[coordIdx + 5];
|
|
1646
|
-
const targetAnchor = anchorCounter;
|
|
1647
|
-
const segStart = pts.length / 2;
|
|
1648
|
-
const arcAccum = [];
|
|
1649
|
-
const total = flattenCubicWithArcLen(prevX, prevY, c1x, c1y, c2x, c2y, ex, ey, tolerance, pts, arcAccum);
|
|
1650
|
-
for (let k = 0; k < arcAccum.length; k++) {
|
|
1651
|
-
aA.push(prevAnchor);
|
|
1652
|
-
aB.push(targetAnchor);
|
|
1653
|
-
aT.push(total > 0 ? arcAccum[k] / total : 0);
|
|
1654
|
-
}
|
|
1655
|
-
const lastIdx = segStart + arcAccum.length - 1;
|
|
1656
|
-
aA[lastIdx] = targetAnchor;
|
|
1657
|
-
aB[lastIdx] = targetAnchor;
|
|
1658
|
-
aT[lastIdx] = 0;
|
|
1659
|
-
prevX = ex;
|
|
1660
|
-
prevY = ey;
|
|
1661
|
-
prevAnchor = targetAnchor;
|
|
1662
|
-
anchorCounter++;
|
|
1663
|
-
coordIdx += 6;
|
|
1664
|
-
break;
|
|
1665
|
-
}
|
|
1666
|
-
case PATH_Z: {
|
|
1667
|
-
if (current) {
|
|
1668
|
-
current.closed = true;
|
|
1669
|
-
dropDuplicateClosingPoint();
|
|
1670
|
-
}
|
|
1671
|
-
break;
|
|
1672
|
-
}
|
|
1673
|
-
default:
|
|
1674
|
-
throw new Error(`extractPolylines: unknown command code ${cmd}`);
|
|
1675
|
-
}
|
|
1557
|
+
function sameFill(a, b) {
|
|
1558
|
+
if (a === b) return true;
|
|
1559
|
+
if ("color" in a && "color" in b) {
|
|
1560
|
+
return a.color === b.color && (a.opacity ?? 1) === (b.opacity ?? 1);
|
|
1676
1561
|
}
|
|
1677
|
-
|
|
1678
|
-
return out;
|
|
1562
|
+
return false;
|
|
1679
1563
|
}
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
const
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1564
|
+
function strokeKey(s) {
|
|
1565
|
+
if (!s) return "-";
|
|
1566
|
+
return [
|
|
1567
|
+
fillKey(s.paint),
|
|
1568
|
+
s.width ?? 1,
|
|
1569
|
+
s.join ?? "miter",
|
|
1570
|
+
s.cap ?? "butt",
|
|
1571
|
+
s.miterLimit ?? "",
|
|
1572
|
+
s.align ?? "center",
|
|
1573
|
+
(s.dash ?? []).join(",")
|
|
1574
|
+
].join(":");
|
|
1575
|
+
}
|
|
1576
|
+
function groupKey(family, weight, style, synthetic, fill, stroke, source, page) {
|
|
1577
|
+
return `${family}|${weight}|${style}|${synthetic.bold ? 1 : 0}${synthetic.italic ? 1 : 0}|${fillKey(fill)}|${strokeKey(stroke)}|${source}|${page}`;
|
|
1578
|
+
}
|
|
1579
|
+
function getOrCreateGroup(ctx, run, resolved, page, sourceOverride) {
|
|
1580
|
+
const atlasFamily = resolved.resolved.family;
|
|
1581
|
+
const resolvedWeight = resolved.resolved.weight;
|
|
1582
|
+
const resolvedStyle = resolved.resolved.style;
|
|
1583
|
+
const source = sourceOverride ?? resolved.source;
|
|
1584
|
+
const key = groupKey(
|
|
1585
|
+
atlasFamily,
|
|
1586
|
+
resolvedWeight,
|
|
1587
|
+
resolvedStyle,
|
|
1588
|
+
resolved.synthetic,
|
|
1589
|
+
run.fill,
|
|
1590
|
+
source === "outline" ? run.stroke : void 0,
|
|
1591
|
+
source,
|
|
1592
|
+
page
|
|
1593
|
+
);
|
|
1594
|
+
let g = ctx.groups.get(key);
|
|
1595
|
+
if (!g) {
|
|
1596
|
+
g = {
|
|
1597
|
+
family: atlasFamily,
|
|
1598
|
+
weight: resolvedWeight,
|
|
1599
|
+
style: resolvedStyle,
|
|
1600
|
+
synthetic: { ...resolved.synthetic },
|
|
1601
|
+
source,
|
|
1602
|
+
page,
|
|
1603
|
+
fill: run.fill,
|
|
1604
|
+
// Only the outline tier can paint it, and carrying it on an SDF group
|
|
1605
|
+
// would be a promise the renderer cannot keep.
|
|
1606
|
+
...source === "outline" && run.stroke !== void 0 ? { stroke: run.stroke } : {},
|
|
1607
|
+
quads: [],
|
|
1608
|
+
glyphs: []
|
|
1706
1609
|
};
|
|
1610
|
+
ctx.groups.set(key, g);
|
|
1707
1611
|
}
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
const
|
|
1714
|
-
const
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
const subs = dash.length > 0 ? splitForDash(pl, dash) : [pl];
|
|
1720
|
-
for (const sub2 of subs) {
|
|
1721
|
-
expandPolyline(sub2, width, join, cap, miterLimit, varyingThreshold, verts, idx, anchorA, anchorB, anchorT);
|
|
1612
|
+
return g;
|
|
1613
|
+
}
|
|
1614
|
+
function resolveGlyph(run, font, resolved, cp) {
|
|
1615
|
+
const direct = font.charMap.get(cp);
|
|
1616
|
+
if (direct) return { glyph: direct, font, resolved };
|
|
1617
|
+
const fallback = resolveGlyphFallback(run.fontFamily, run.fontWeight, run.fontStyle);
|
|
1618
|
+
const face = fallback?.dynamicFace;
|
|
1619
|
+
if (fallback && face) {
|
|
1620
|
+
const glyph = face.requestGlyph(cp);
|
|
1621
|
+
if (glyph.xadvance > 0 || glyph.width > 0) {
|
|
1622
|
+
return { glyph, font: face.font, resolved: fallback };
|
|
1722
1623
|
}
|
|
1723
1624
|
}
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
indices: new Uint32Array(idx),
|
|
1727
|
-
anchorA: new Uint32Array(anchorA),
|
|
1728
|
-
anchorB: new Uint32Array(anchorB),
|
|
1729
|
-
anchorT: new Float32Array(anchorT)
|
|
1730
|
-
};
|
|
1625
|
+
warnMissingGlyphOnce(resolved.resolved.family, cp);
|
|
1626
|
+
return null;
|
|
1731
1627
|
}
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
const
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
const
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1628
|
+
var warnedMissingGlyphs = /* @__PURE__ */ new Set();
|
|
1629
|
+
function warnMissingGlyphOnce(family, cp) {
|
|
1630
|
+
const key = `${family}|${cp}`;
|
|
1631
|
+
if (warnedMissingGlyphs.has(key)) return;
|
|
1632
|
+
warnedMissingGlyphs.add(key);
|
|
1633
|
+
const ch = String.fromCodePoint(cp);
|
|
1634
|
+
console.warn(
|
|
1635
|
+
`weasel layoutRuns: no glyph for U+${cp.toString(16).toUpperCase().padStart(4, "0")} (${JSON.stringify(ch)}) in "${family}", and the dynamic tier could not rasterize it \u2014 skipping the character. Bake it into the atlas, or call registerCanvasFont("${family}") to serve missing codepoints from installed fonts.`
|
|
1636
|
+
);
|
|
1637
|
+
}
|
|
1638
|
+
function layoutRuns(runs, opts) {
|
|
1639
|
+
const ctx = { groups: /* @__PURE__ */ new Map() };
|
|
1640
|
+
const entries = [];
|
|
1641
|
+
let prevCp;
|
|
1642
|
+
let prevFont;
|
|
1643
|
+
let prevFontSize;
|
|
1644
|
+
for (const run of runs) {
|
|
1645
|
+
const resolved = resolveFontVariant(run.fontFamily, run.fontWeight, run.fontStyle);
|
|
1646
|
+
const font = resolved.entry?.font ?? resolved.dynamicFace?.font;
|
|
1647
|
+
if (!font) {
|
|
1648
|
+
prevCp = void 0;
|
|
1649
|
+
prevFont = void 0;
|
|
1650
|
+
prevFontSize = void 0;
|
|
1651
|
+
continue;
|
|
1751
1652
|
}
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1653
|
+
const scale2 = run.fontSize / font.info.size;
|
|
1654
|
+
const tracking = run.letterSpacing;
|
|
1655
|
+
for (const ch of [...run.text]) {
|
|
1656
|
+
const cp = ch.codePointAt(0);
|
|
1657
|
+
const isNewline = cp === 10;
|
|
1658
|
+
const isSpace = cp === 32;
|
|
1659
|
+
if (isNewline) {
|
|
1660
|
+
entries.push({
|
|
1661
|
+
run,
|
|
1662
|
+
font,
|
|
1663
|
+
glyph: { id: cp, x: 0, y: 0, width: 0, height: 0, xoffset: 0, yoffset: 0, xadvance: 0, page: 0 },
|
|
1664
|
+
// A newline consumes no advance, so it takes no tracking either.
|
|
1665
|
+
cp,
|
|
1666
|
+
advance: 0,
|
|
1667
|
+
tracking: 0,
|
|
1668
|
+
kerningBefore: 0,
|
|
1669
|
+
isSpace: false,
|
|
1670
|
+
isNewline: true,
|
|
1671
|
+
resolved,
|
|
1672
|
+
fontSize: run.fontSize
|
|
1673
|
+
});
|
|
1674
|
+
prevCp = void 0;
|
|
1675
|
+
prevFont = void 0;
|
|
1676
|
+
prevFontSize = void 0;
|
|
1677
|
+
continue;
|
|
1678
|
+
}
|
|
1679
|
+
if (isSpace) {
|
|
1680
|
+
const spaceGlyph = resolved.dynamicFace ? resolved.dynamicFace.requestGlyph(32) : font.charMap.get(32);
|
|
1681
|
+
const advance = spaceGlyph ? spaceGlyph.xadvance * scale2 : run.fontSize * 0.25;
|
|
1682
|
+
let kerningBefore2 = 0;
|
|
1683
|
+
if (prevCp !== void 0 && prevFont !== void 0 && prevFontSize !== void 0) {
|
|
1684
|
+
const kAtlas = prevFont.kerningMap.get(prevCp)?.get(cp) ?? 0;
|
|
1685
|
+
kerningBefore2 = kAtlas * (prevFontSize / prevFont.info.size);
|
|
1686
|
+
}
|
|
1687
|
+
entries.push({
|
|
1688
|
+
run,
|
|
1689
|
+
font,
|
|
1690
|
+
glyph: spaceGlyph ?? { id: 32, x: 0, y: 0, width: 0, height: 0, xoffset: 0, yoffset: 0, xadvance: 0, page: 0 },
|
|
1691
|
+
cp,
|
|
1692
|
+
advance,
|
|
1693
|
+
tracking,
|
|
1694
|
+
kerningBefore: kerningBefore2,
|
|
1695
|
+
isSpace: true,
|
|
1696
|
+
isNewline: false,
|
|
1697
|
+
resolved,
|
|
1698
|
+
fontSize: run.fontSize
|
|
1699
|
+
});
|
|
1700
|
+
prevCp = cp;
|
|
1701
|
+
prevFont = font;
|
|
1702
|
+
prevFontSize = run.fontSize;
|
|
1703
|
+
continue;
|
|
1704
|
+
}
|
|
1705
|
+
const hit = resolved.dynamicFace ? { glyph: resolved.dynamicFace.requestGlyph(cp), font, resolved } : resolveGlyph(run, font, resolved, cp);
|
|
1706
|
+
if (!hit) {
|
|
1707
|
+
prevCp = cp;
|
|
1708
|
+
prevFont = font;
|
|
1709
|
+
prevFontSize = run.fontSize;
|
|
1710
|
+
continue;
|
|
1711
|
+
}
|
|
1712
|
+
const glyphFont = hit.font;
|
|
1713
|
+
const glyphScale = run.fontSize / glyphFont.info.size;
|
|
1714
|
+
let kerningBefore = 0;
|
|
1715
|
+
if (prevCp !== void 0 && prevFont !== void 0 && prevFontSize !== void 0) {
|
|
1716
|
+
const kAtlas = prevFont.kerningMap.get(prevCp)?.get(cp) ?? 0;
|
|
1717
|
+
kerningBefore = kAtlas * (prevFontSize / prevFont.info.size);
|
|
1718
|
+
}
|
|
1719
|
+
entries.push({
|
|
1720
|
+
run,
|
|
1721
|
+
font: glyphFont,
|
|
1722
|
+
glyph: hit.glyph,
|
|
1723
|
+
cp,
|
|
1724
|
+
advance: hit.glyph.xadvance * glyphScale,
|
|
1725
|
+
tracking,
|
|
1726
|
+
kerningBefore,
|
|
1727
|
+
isSpace,
|
|
1728
|
+
isNewline: false,
|
|
1729
|
+
resolved: hit.resolved,
|
|
1730
|
+
fontSize: run.fontSize
|
|
1731
|
+
});
|
|
1732
|
+
prevCp = cp;
|
|
1733
|
+
prevFont = glyphFont;
|
|
1734
|
+
prevFontSize = run.fontSize;
|
|
1764
1735
|
}
|
|
1765
1736
|
}
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
const endSrc = s === segs.length - 1 && pl.closed && closerSrcIdx >= 0 ? 0 : startSrc + 1;
|
|
1772
|
-
const base = verts.length / 2;
|
|
1773
|
-
segBaseIdx.push(base);
|
|
1774
|
-
const nxA = seg.nxUnit * seg.halfA, nyA = seg.nyUnit * seg.halfA;
|
|
1775
|
-
const nxB = seg.nxUnit * seg.halfB, nyB = seg.nyUnit * seg.halfB;
|
|
1776
|
-
verts.push(seg.ax + nxA, seg.ay + nyA);
|
|
1777
|
-
anchorA.push(plA[startSrc]);
|
|
1778
|
-
anchorB.push(plB[startSrc]);
|
|
1779
|
-
anchorT.push(plT[startSrc]);
|
|
1780
|
-
verts.push(seg.ax - nxA, seg.ay - nyA);
|
|
1781
|
-
anchorA.push(plA[startSrc]);
|
|
1782
|
-
anchorB.push(plB[startSrc]);
|
|
1783
|
-
anchorT.push(plT[startSrc]);
|
|
1784
|
-
verts.push(seg.bx + nxB, seg.by + nyB);
|
|
1785
|
-
anchorA.push(plA[endSrc]);
|
|
1786
|
-
anchorB.push(plB[endSrc]);
|
|
1787
|
-
anchorT.push(plT[endSrc]);
|
|
1788
|
-
verts.push(seg.bx - nxB, seg.by - nyB);
|
|
1789
|
-
anchorA.push(plA[endSrc]);
|
|
1790
|
-
anchorB.push(plB[endSrc]);
|
|
1791
|
-
anchorT.push(plT[endSrc]);
|
|
1792
|
-
idx.push(base, base + 1, base + 2, base + 1, base + 3, base + 2);
|
|
1793
|
-
}
|
|
1794
|
-
const joinCount = pl.closed ? segs.length : segs.length - 1;
|
|
1795
|
-
for (let j = 0; j < joinCount; j++) {
|
|
1796
|
-
emitJoin(segs, segBaseIdx, j, join, miterLimit, varyingThreshold, verts, idx, anchorA, anchorB, anchorT, segSrcIdx, plA, plB, plT);
|
|
1797
|
-
}
|
|
1798
|
-
if (!pl.closed && cap !== "butt") {
|
|
1799
|
-
const first = segs[0];
|
|
1800
|
-
const firstBase = segBaseIdx[0];
|
|
1801
|
-
emitCap(first, firstBase + 0, firstBase + 1, false, first.halfA, cap, verts, idx, anchorA, anchorB, anchorT, plA[0], plB[0], plT[0]);
|
|
1802
|
-
const last = segs[segs.length - 1];
|
|
1803
|
-
const lastBase = segBaseIdx[segs.length - 1];
|
|
1804
|
-
const lastSrc = segSrcIdx[segs.length - 1] + 1;
|
|
1805
|
-
emitCap(last, lastBase + 2, lastBase + 3, true, last.halfB, cap, verts, idx, anchorA, anchorB, anchorT, plA[lastSrc], plB[lastSrc], plT[lastSrc]);
|
|
1737
|
+
const lines = [];
|
|
1738
|
+
let cur = { entries: [], width: 0, height: 0 };
|
|
1739
|
+
function commitLine() {
|
|
1740
|
+
lines.push(cur);
|
|
1741
|
+
cur = { entries: [], width: 0, height: 0 };
|
|
1806
1742
|
}
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
};
|
|
1819
|
-
for (let i = 0; i < ptCount; i++) {
|
|
1820
|
-
const a = aA[i], b = aB[i], t = aT[i];
|
|
1821
|
-
if (a === b) {
|
|
1822
|
-
widths[i] = read(a);
|
|
1823
|
-
} else {
|
|
1824
|
-
const wa = read(a);
|
|
1825
|
-
const wb = read(b);
|
|
1826
|
-
widths[i] = wa + (wb - wa) * t;
|
|
1743
|
+
let i = 0;
|
|
1744
|
+
while (i < entries.length) {
|
|
1745
|
+
const e = entries[i];
|
|
1746
|
+
if (e.isNewline) {
|
|
1747
|
+
if (cur.entries.length === 0) {
|
|
1748
|
+
cur.height = Math.max(cur.height, e.fontSize * opts.lineHeight);
|
|
1749
|
+
cur.blank = e;
|
|
1750
|
+
}
|
|
1751
|
+
commitLine();
|
|
1752
|
+
i++;
|
|
1753
|
+
continue;
|
|
1827
1754
|
}
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
const cx = atEnd ? seg.bx : seg.ax;
|
|
1837
|
-
const cy = atEnd ? seg.by : seg.ay;
|
|
1838
|
-
const nx = seg.nxUnit * half;
|
|
1839
|
-
const ny = seg.nyUnit * half;
|
|
1840
|
-
if (cap === "square") {
|
|
1841
|
-
const ox = cx + dx * half;
|
|
1842
|
-
const oy = cy + dy * half;
|
|
1843
|
-
const lOut = verts.length / 2;
|
|
1844
|
-
verts.push(ox + nx, oy + ny);
|
|
1845
|
-
anchorA.push(endA);
|
|
1846
|
-
anchorB.push(endB);
|
|
1847
|
-
anchorT.push(endT);
|
|
1848
|
-
const rOut = verts.length / 2;
|
|
1849
|
-
verts.push(ox - nx, oy - ny);
|
|
1850
|
-
anchorA.push(endA);
|
|
1851
|
-
anchorB.push(endB);
|
|
1852
|
-
anchorT.push(endT);
|
|
1853
|
-
if (atEnd) {
|
|
1854
|
-
idx.push(leftIdx, lOut, rOut, leftIdx, rOut, rightIdx);
|
|
1855
|
-
} else {
|
|
1856
|
-
idx.push(leftIdx, rOut, lOut, leftIdx, rightIdx, rOut);
|
|
1755
|
+
if (e.isSpace) {
|
|
1756
|
+
if (cur.entries.length > 0) {
|
|
1757
|
+
cur.entries.push(e);
|
|
1758
|
+
cur.width += e.kerningBefore + e.advance + e.tracking;
|
|
1759
|
+
cur.height = Math.max(cur.height, e.fontSize * opts.lineHeight);
|
|
1760
|
+
}
|
|
1761
|
+
i++;
|
|
1762
|
+
continue;
|
|
1857
1763
|
}
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
anchorB.push(endB);
|
|
1865
|
-
anchorT.push(endT);
|
|
1866
|
-
const startAngle = Math.atan2(ny, nx);
|
|
1867
|
-
const sweep = atEnd ? -Math.PI : Math.PI;
|
|
1868
|
-
const steps = Math.max(1, Math.ceil(Math.abs(sweep) / ROUND_CAP_STEP_RAD));
|
|
1869
|
-
const stepAngle = sweep / steps;
|
|
1870
|
-
let prevIdx = leftIdx;
|
|
1871
|
-
for (let i = 1; i < steps; i++) {
|
|
1872
|
-
const ang = startAngle + i * stepAngle;
|
|
1873
|
-
const fx = cx + Math.cos(ang) * half;
|
|
1874
|
-
const fy = cy + Math.sin(ang) * half;
|
|
1875
|
-
const newIdx = verts.length / 2;
|
|
1876
|
-
verts.push(fx, fy);
|
|
1877
|
-
anchorA.push(endA);
|
|
1878
|
-
anchorB.push(endB);
|
|
1879
|
-
anchorT.push(endT);
|
|
1880
|
-
if (atEnd) idx.push(prevIdx, newIdx, pivotIdx);
|
|
1881
|
-
else idx.push(prevIdx, pivotIdx, newIdx);
|
|
1882
|
-
prevIdx = newIdx;
|
|
1764
|
+
let j = i;
|
|
1765
|
+
let wordWidth = 0;
|
|
1766
|
+
while (j < entries.length && !entries[j].isSpace && !entries[j].isNewline) {
|
|
1767
|
+
const w = entries[j];
|
|
1768
|
+
wordWidth += w.kerningBefore + w.advance + w.tracking;
|
|
1769
|
+
j++;
|
|
1883
1770
|
}
|
|
1884
|
-
if (
|
|
1885
|
-
|
|
1771
|
+
if (Number.isFinite(opts.maxWidth) && cur.width + wordWidth > opts.maxWidth && cur.entries.length > 0) {
|
|
1772
|
+
commitLine();
|
|
1773
|
+
}
|
|
1774
|
+
for (let k = i; k < j; k++) {
|
|
1775
|
+
const w = entries[k];
|
|
1776
|
+
const kerningBefore = cur.entries.length === 0 ? 0 : w.kerningBefore;
|
|
1777
|
+
cur.entries.push({ ...w, kerningBefore });
|
|
1778
|
+
cur.width += kerningBefore + w.advance + w.tracking;
|
|
1779
|
+
cur.height = Math.max(cur.height, w.fontSize * opts.lineHeight);
|
|
1780
|
+
}
|
|
1781
|
+
i = j;
|
|
1886
1782
|
}
|
|
1887
|
-
|
|
1888
|
-
function
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
let
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
anchorA: new Uint32Array(curA),
|
|
1906
|
-
anchorB: new Uint32Array(curB),
|
|
1907
|
-
anchorT: new Float32Array(curT)
|
|
1908
|
-
});
|
|
1783
|
+
if (cur.entries.length > 0) commitLine();
|
|
1784
|
+
function outlineFor(e) {
|
|
1785
|
+
const min = opts.outlineMinSize;
|
|
1786
|
+
if (min === void 0 || e.fontSize < min) return null;
|
|
1787
|
+
if (e.resolved.synthetic.bold) return null;
|
|
1788
|
+
const r = e.resolved.resolved;
|
|
1789
|
+
return glyphOutline(r.family, r.weight, r.style, e.cp);
|
|
1790
|
+
}
|
|
1791
|
+
const decorations = [];
|
|
1792
|
+
let span = null;
|
|
1793
|
+
function flushSpan() {
|
|
1794
|
+
const s = span;
|
|
1795
|
+
span = null;
|
|
1796
|
+
if (!s || s.x1 <= s.x0) return;
|
|
1797
|
+
const thickness = s.fontSize * DECORATION_THICKNESS;
|
|
1798
|
+
if (s.underline) {
|
|
1799
|
+
const y0 = s.baselineY + s.fontSize * UNDERLINE_OFFSET;
|
|
1800
|
+
decorations.push({ kind: "underline", x0: s.x0, y0, x1: s.x1, y1: y0 + thickness, fill: s.fill });
|
|
1909
1801
|
}
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
const
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
prevX = cx;
|
|
1941
|
-
prevY = cy;
|
|
1942
|
-
traveled = segFullLen;
|
|
1943
|
-
segLen = 0;
|
|
1944
|
-
if (dashRemaining <= 1e-9) {
|
|
1945
|
-
flushAndAdvance();
|
|
1946
|
-
if (onPhase) {
|
|
1947
|
-
curPts = [prevX, prevY];
|
|
1948
|
-
curA = [endA];
|
|
1949
|
-
curB = [endB];
|
|
1950
|
-
curT = [endT];
|
|
1951
|
-
}
|
|
1952
|
-
}
|
|
1953
|
-
} else {
|
|
1954
|
-
const tConsume = dashRemaining / segLen;
|
|
1955
|
-
const ix = prevX + segDx * tConsume;
|
|
1956
|
-
const iy = prevY + segDy * tConsume;
|
|
1957
|
-
traveled += dashRemaining;
|
|
1958
|
-
const frac = traveled / segFullLen;
|
|
1959
|
-
let mA, mB, mT;
|
|
1960
|
-
if (startA === endA && startB === endB) {
|
|
1961
|
-
mA = startA;
|
|
1962
|
-
mB = startB;
|
|
1963
|
-
mT = startT + (endT - startT) * frac;
|
|
1802
|
+
if (s.strikethrough) {
|
|
1803
|
+
const y0 = s.baselineY + s.fontSize * STRIKETHROUGH_OFFSET;
|
|
1804
|
+
decorations.push({ kind: "strikethrough", x0: s.x0, y0, x1: s.x1, y1: y0 + thickness, fill: s.fill });
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
const lineBoxes = [];
|
|
1808
|
+
let penY = 0;
|
|
1809
|
+
let maxLineWidth = 0;
|
|
1810
|
+
const finiteWidth = Number.isFinite(opts.maxWidth) ? opts.maxWidth : 0;
|
|
1811
|
+
for (const line of lines) {
|
|
1812
|
+
const alignShift = (() => {
|
|
1813
|
+
if (opts.align === "left") return 0;
|
|
1814
|
+
if (!Number.isFinite(opts.maxWidth)) {
|
|
1815
|
+
return opts.align === "center" ? -line.width / 2 : -line.width;
|
|
1816
|
+
}
|
|
1817
|
+
const slack = finiteWidth - line.width;
|
|
1818
|
+
return opts.align === "center" ? slack / 2 : slack;
|
|
1819
|
+
})();
|
|
1820
|
+
const lineX0 = alignShift;
|
|
1821
|
+
const baselineSource = line.entries[0] ?? line.blank;
|
|
1822
|
+
const lineBaselineY = baselineSource ? penY + baselineSource.font.common.base * (baselineSource.fontSize / baselineSource.font.info.size) : penY;
|
|
1823
|
+
let penX = lineX0;
|
|
1824
|
+
for (const e of line.entries) {
|
|
1825
|
+
penX += e.kerningBefore;
|
|
1826
|
+
const step = e.advance + e.tracking;
|
|
1827
|
+
const scale2 = e.fontSize / e.font.info.size;
|
|
1828
|
+
const baselineY = penY + e.font.common.base * scale2;
|
|
1829
|
+
if (e.run.underline || e.run.strikethrough) {
|
|
1830
|
+
if (span !== null && span.underline === e.run.underline && span.strikethrough === e.run.strikethrough && span.fontSize === e.fontSize && span.baselineY === baselineY && sameFill(span.fill, e.run.fill)) {
|
|
1831
|
+
span.x1 = penX + step;
|
|
1964
1832
|
} else {
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
if (onPhase && curPts) {
|
|
1976
|
-
curPts.push(ix, iy);
|
|
1977
|
-
curA.push(mA);
|
|
1978
|
-
curB.push(mB);
|
|
1979
|
-
curT.push(mT);
|
|
1980
|
-
}
|
|
1981
|
-
prevX = ix;
|
|
1982
|
-
prevY = iy;
|
|
1983
|
-
segDx = cx - prevX;
|
|
1984
|
-
segDy = cy - prevY;
|
|
1985
|
-
segLen = Math.hypot(segDx, segDy);
|
|
1986
|
-
flushAndAdvance();
|
|
1987
|
-
if (onPhase) {
|
|
1988
|
-
curPts = [prevX, prevY];
|
|
1989
|
-
curA = [mA];
|
|
1990
|
-
curB = [mB];
|
|
1991
|
-
curT = [mT];
|
|
1833
|
+
flushSpan();
|
|
1834
|
+
span = {
|
|
1835
|
+
underline: e.run.underline,
|
|
1836
|
+
strikethrough: e.run.strikethrough,
|
|
1837
|
+
fill: e.run.fill,
|
|
1838
|
+
fontSize: e.fontSize,
|
|
1839
|
+
baselineY,
|
|
1840
|
+
x0: penX,
|
|
1841
|
+
x1: penX + step
|
|
1842
|
+
};
|
|
1992
1843
|
}
|
|
1844
|
+
} else {
|
|
1845
|
+
flushSpan();
|
|
1846
|
+
}
|
|
1847
|
+
const outlineD = outlineFor(e);
|
|
1848
|
+
if (outlineD !== null) {
|
|
1849
|
+
const group2 = getOrCreateGroup(ctx, e.run, e.resolved, 0, "outline");
|
|
1850
|
+
group2.glyphs.push({
|
|
1851
|
+
d: outlineD,
|
|
1852
|
+
key: `${e.resolved.resolved.family}|${e.resolved.resolved.weight}|${e.resolved.resolved.style}|${e.cp}`,
|
|
1853
|
+
x: penX,
|
|
1854
|
+
baselineY,
|
|
1855
|
+
// Em space is unit-scale, so world units per em is just the size.
|
|
1856
|
+
scale: e.fontSize
|
|
1857
|
+
});
|
|
1858
|
+
penX += step;
|
|
1859
|
+
continue;
|
|
1860
|
+
}
|
|
1861
|
+
if (e.advance === 0 || e.glyph.width === 0 || e.glyph.page < 0) {
|
|
1862
|
+
penX += step;
|
|
1863
|
+
continue;
|
|
1993
1864
|
}
|
|
1865
|
+
const group = getOrCreateGroup(ctx, e.run, e.resolved, e.glyph.page);
|
|
1866
|
+
const atlasW = e.font.common.scaleW;
|
|
1867
|
+
const atlasH = e.font.common.scaleH;
|
|
1868
|
+
const qx0 = penX + e.glyph.xoffset * scale2;
|
|
1869
|
+
const qy0 = penY + e.glyph.yoffset * scale2;
|
|
1870
|
+
const qx1 = qx0 + e.glyph.width * scale2;
|
|
1871
|
+
const qy1 = qy0 + e.glyph.height * scale2;
|
|
1872
|
+
const u0 = e.glyph.x / atlasW;
|
|
1873
|
+
const v0 = e.glyph.y / atlasH;
|
|
1874
|
+
const u1 = (e.glyph.x + e.glyph.width) / atlasW;
|
|
1875
|
+
const v1 = (e.glyph.y + e.glyph.height) / atlasH;
|
|
1876
|
+
group.quads.push({ x0: qx0, y0: qy0, x1: qx1, y1: qy1, u0, v0, u1, v1, baselineY });
|
|
1877
|
+
penX += step;
|
|
1994
1878
|
}
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
anchorT: new Float32Array(curT)
|
|
1879
|
+
flushSpan();
|
|
1880
|
+
lineBoxes.push({
|
|
1881
|
+
x0: lineX0,
|
|
1882
|
+
y0: penY,
|
|
1883
|
+
x1: lineX0 + line.width,
|
|
1884
|
+
y1: penY + line.height,
|
|
1885
|
+
baselineY: lineBaselineY
|
|
2003
1886
|
});
|
|
1887
|
+
maxLineWidth = Math.max(maxLineWidth, line.width);
|
|
1888
|
+
penY += line.height;
|
|
2004
1889
|
}
|
|
2005
|
-
return out;
|
|
2006
|
-
}
|
|
2007
|
-
function makeSeg(ax, ay, bx, by, halfA, halfB) {
|
|
2008
|
-
const dx = bx - ax, dy = by - ay;
|
|
2009
|
-
const len = Math.hypot(dx, dy);
|
|
2010
|
-
if (len === 0) return null;
|
|
2011
1890
|
return {
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
nxUnit: -dy / len,
|
|
2017
|
-
nyUnit: dx / len,
|
|
2018
|
-
halfA,
|
|
2019
|
-
halfB,
|
|
2020
|
-
len
|
|
1891
|
+
groups: [...ctx.groups.values()],
|
|
1892
|
+
decorations,
|
|
1893
|
+
lines: lineBoxes,
|
|
1894
|
+
bounds: { width: maxLineWidth, height: penY }
|
|
2021
1895
|
};
|
|
2022
1896
|
}
|
|
2023
|
-
var
|
|
2024
|
-
var
|
|
2025
|
-
function
|
|
2026
|
-
|
|
2027
|
-
const
|
|
2028
|
-
const
|
|
2029
|
-
|
|
2030
|
-
const
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
const
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
const forceBevelForVarying = taperA > varyingThreshold || taperB > varyingThreshold;
|
|
2046
|
-
if (join === "bevel" || forceBevelForVarying) {
|
|
2047
|
-
emitBevel(a, aOuterEnd, bOuterStart, onPositive, verts, idx, anchorA, anchorB, anchorT, cornerA, cornerB, cornerT);
|
|
2048
|
-
return;
|
|
1897
|
+
var LAYOUT_CACHE_VARIANT_LIMIT = 8;
|
|
1898
|
+
var cache2 = /* @__PURE__ */ new WeakMap();
|
|
1899
|
+
function outlineBucket(runs, min) {
|
|
1900
|
+
if (min === void 0) return -1;
|
|
1901
|
+
const sizes = /* @__PURE__ */ new Set();
|
|
1902
|
+
for (const r of runs) sizes.add(r.fontSize);
|
|
1903
|
+
let n = 0;
|
|
1904
|
+
for (const size of sizes) if (size >= min) n++;
|
|
1905
|
+
return n;
|
|
1906
|
+
}
|
|
1907
|
+
function variantKey(runs, opts) {
|
|
1908
|
+
return `${opts.maxWidth}|${opts.lineHeight}|${opts.align}|${outlineBucket(runs, opts.outlineMinSize)}`;
|
|
1909
|
+
}
|
|
1910
|
+
function cachedLayoutRuns(runs, opts) {
|
|
1911
|
+
const fonts = glyphGeneration();
|
|
1912
|
+
let entry = cache2.get(runs);
|
|
1913
|
+
if (entry === void 0) {
|
|
1914
|
+
entry = { generation: fonts, byVariant: /* @__PURE__ */ new Map() };
|
|
1915
|
+
cache2.set(runs, entry);
|
|
1916
|
+
} else if (entry.generation !== fonts) {
|
|
1917
|
+
entry.generation = fonts;
|
|
1918
|
+
entry.byVariant.clear();
|
|
2049
1919
|
}
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
if (
|
|
2074
|
-
|
|
2075
|
-
|
|
1920
|
+
const key = variantKey(runs, opts);
|
|
1921
|
+
const hit = entry.byVariant.get(key);
|
|
1922
|
+
if (hit !== void 0) return hit;
|
|
1923
|
+
const laid = layoutRuns(runs, opts);
|
|
1924
|
+
if (entry.byVariant.size >= LAYOUT_CACHE_VARIANT_LIMIT) entry.byVariant.clear();
|
|
1925
|
+
entry.byVariant.set(key, laid);
|
|
1926
|
+
return laid;
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
// src/features/text/verticalAlign.ts
|
|
1930
|
+
function verticalAlignOffset(align, boxHeight, textHeight) {
|
|
1931
|
+
if (align === void 0 || align === "top" || boxHeight === void 0) return 0;
|
|
1932
|
+
const slack = boxHeight - textHeight;
|
|
1933
|
+
return align === "center" ? slack / 2 : slack;
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
// src/features/paths/pathFromD.ts
|
|
1937
|
+
var NUM_RE = /[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g;
|
|
1938
|
+
function tokenize(d) {
|
|
1939
|
+
const out = [];
|
|
1940
|
+
let i = 0;
|
|
1941
|
+
while (i < d.length) {
|
|
1942
|
+
const ch = d[i];
|
|
1943
|
+
if (/[A-Za-z]/.test(ch)) {
|
|
1944
|
+
let j = i + 1;
|
|
1945
|
+
while (j < d.length && !/[A-Za-z]/.test(d[j])) j++;
|
|
1946
|
+
const argSlice = d.slice(i + 1, j);
|
|
1947
|
+
out.push({ cmd: ch, args: readNumbers(argSlice) });
|
|
1948
|
+
i = j;
|
|
2076
1949
|
} else {
|
|
2077
|
-
|
|
2078
|
-
idx.push(aOuterEnd, jIdx, bOuterStart);
|
|
1950
|
+
i++;
|
|
2079
1951
|
}
|
|
2080
|
-
return;
|
|
2081
|
-
}
|
|
2082
|
-
if (join === "round") {
|
|
2083
|
-
emitRoundJoin(a, aOuterEnd, bOuterStart, onPositive, verts, idx, anchorA, anchorB, anchorT, cornerA, cornerB, cornerT);
|
|
2084
|
-
return;
|
|
2085
1952
|
}
|
|
1953
|
+
return out;
|
|
2086
1954
|
}
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
anchorT.push(cornerT);
|
|
2095
|
-
const startX = verts[aOuterEnd * 2] - cx;
|
|
2096
|
-
const startY = verts[aOuterEnd * 2 + 1] - cy;
|
|
2097
|
-
const endX = verts[bOuterStart * 2] - cx;
|
|
2098
|
-
const endY = verts[bOuterStart * 2 + 1] - cy;
|
|
2099
|
-
const r = Math.hypot(startX, startY);
|
|
2100
|
-
const startAngle = Math.atan2(startY, startX);
|
|
2101
|
-
const endAngle = Math.atan2(endY, endX);
|
|
2102
|
-
let sweep = endAngle - startAngle;
|
|
2103
|
-
if (onPositive && sweep < 0) sweep += 2 * Math.PI;
|
|
2104
|
-
else if (!onPositive && sweep > 0) sweep -= 2 * Math.PI;
|
|
2105
|
-
const steps = Math.max(1, Math.ceil(Math.abs(sweep) / ROUND_STEP_RAD));
|
|
2106
|
-
const stepAngle = sweep / steps;
|
|
2107
|
-
let prevIdx = aOuterEnd;
|
|
2108
|
-
for (let i = 1; i < steps; i++) {
|
|
2109
|
-
const ang = startAngle + i * stepAngle;
|
|
2110
|
-
const fx = cx + Math.cos(ang) * r;
|
|
2111
|
-
const fy = cy + Math.sin(ang) * r;
|
|
2112
|
-
const newIdx = verts.length / 2;
|
|
2113
|
-
verts.push(fx, fy);
|
|
2114
|
-
anchorA.push(cornerA);
|
|
2115
|
-
anchorB.push(cornerB);
|
|
2116
|
-
anchorT.push(cornerT);
|
|
2117
|
-
if (onPositive) idx.push(prevIdx, pivotIdx, newIdx);
|
|
2118
|
-
else idx.push(prevIdx, newIdx, pivotIdx);
|
|
2119
|
-
prevIdx = newIdx;
|
|
1955
|
+
function readNumbers(s) {
|
|
1956
|
+
const out = [];
|
|
1957
|
+
let m;
|
|
1958
|
+
NUM_RE.lastIndex = 0;
|
|
1959
|
+
while ((m = NUM_RE.exec(s)) !== null) {
|
|
1960
|
+
const n = Number(m[0]);
|
|
1961
|
+
if (Number.isFinite(n)) out.push(n);
|
|
2120
1962
|
}
|
|
2121
|
-
|
|
2122
|
-
else idx.push(prevIdx, bOuterStart, pivotIdx);
|
|
2123
|
-
}
|
|
2124
|
-
function emitBevel(a, aOuterEnd, bOuterStart, onPositive, verts, idx, anchorA, anchorB, anchorT, cornerA, cornerB, cornerT) {
|
|
2125
|
-
const jIdx = verts.length / 2;
|
|
2126
|
-
verts.push(a.bx, a.by);
|
|
2127
|
-
anchorA.push(cornerA);
|
|
2128
|
-
anchorB.push(cornerB);
|
|
2129
|
-
anchorT.push(cornerT);
|
|
2130
|
-
if (onPositive) idx.push(aOuterEnd, jIdx, bOuterStart);
|
|
2131
|
-
else idx.push(aOuterEnd, bOuterStart, jIdx);
|
|
2132
|
-
}
|
|
2133
|
-
function lineLineIntersect(apx, apy, adx, ady, bpx, bpy, bdx, bdy) {
|
|
2134
|
-
const denom = adx * bdy - ady * bdx;
|
|
2135
|
-
if (Math.abs(denom) < 1e-9) return null;
|
|
2136
|
-
const t = ((bpx - apx) * bdy - (bpy - apy) * bdx) / denom;
|
|
2137
|
-
return [apx + t * adx, apy + t * ady];
|
|
2138
|
-
}
|
|
2139
|
-
var UNDERLINE_OFFSET = 0.1;
|
|
2140
|
-
var STRIKETHROUGH_OFFSET = -0.3;
|
|
2141
|
-
var DECORATION_THICKNESS = 0.05;
|
|
2142
|
-
function fillKey(p) {
|
|
2143
|
-
if ("color" in p) return `s:${p.color}:${p.opacity ?? 1}`;
|
|
2144
|
-
return `nx:${Math.random()}`;
|
|
1963
|
+
return out;
|
|
2145
1964
|
}
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
1965
|
+
var ARG_COUNTS = {
|
|
1966
|
+
M: 2,
|
|
1967
|
+
L: 2,
|
|
1968
|
+
H: 1,
|
|
1969
|
+
V: 1,
|
|
1970
|
+
C: 6,
|
|
1971
|
+
S: 4,
|
|
1972
|
+
Q: 4,
|
|
1973
|
+
T: 2,
|
|
1974
|
+
A: 7,
|
|
1975
|
+
Z: 0
|
|
1976
|
+
};
|
|
1977
|
+
function pathFromD(d, onWarn) {
|
|
1978
|
+
const tokens = tokenize(d);
|
|
1979
|
+
const b = new PathBuilder();
|
|
1980
|
+
const st = {
|
|
1981
|
+
x: 0,
|
|
1982
|
+
y: 0,
|
|
1983
|
+
startX: 0,
|
|
1984
|
+
startY: 0,
|
|
1985
|
+
lastCubicCtrl: null,
|
|
1986
|
+
lastQuadCtrl: null
|
|
1987
|
+
};
|
|
1988
|
+
for (const { cmd, args } of tokens) {
|
|
1989
|
+
const upper = cmd.toUpperCase();
|
|
1990
|
+
const relative = cmd !== upper;
|
|
1991
|
+
const argCount = ARG_COUNTS[upper];
|
|
1992
|
+
if (argCount === void 0) {
|
|
1993
|
+
onWarn?.(`unknown path command: ${cmd}`);
|
|
1994
|
+
continue;
|
|
1995
|
+
}
|
|
1996
|
+
if (upper === "Z") {
|
|
1997
|
+
b.close();
|
|
1998
|
+
st.x = st.startX;
|
|
1999
|
+
st.y = st.startY;
|
|
2000
|
+
st.lastCubicCtrl = null;
|
|
2001
|
+
st.lastQuadCtrl = null;
|
|
2002
|
+
continue;
|
|
2003
|
+
}
|
|
2004
|
+
const groups = argCount === 0 ? 1 : Math.floor(args.length / argCount);
|
|
2005
|
+
for (let g = 0; g < groups; g++) {
|
|
2006
|
+
const a = args.slice(g * argCount, (g + 1) * argCount);
|
|
2007
|
+
const effectiveUpper = upper === "M" && g > 0 ? "L" : upper;
|
|
2008
|
+
runCommand(effectiveUpper, relative, a, st, b);
|
|
2009
|
+
}
|
|
2150
2010
|
}
|
|
2151
|
-
return
|
|
2011
|
+
return b.build();
|
|
2152
2012
|
}
|
|
2153
|
-
function
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
function getOrCreateGroup(ctx, run, resolved, page, sourceOverride) {
|
|
2169
|
-
const atlasFamily = resolved.resolved.family;
|
|
2170
|
-
const resolvedWeight = resolved.resolved.weight;
|
|
2171
|
-
const resolvedStyle = resolved.resolved.style;
|
|
2172
|
-
const source = sourceOverride ?? resolved.source;
|
|
2173
|
-
const key = groupKey(
|
|
2174
|
-
atlasFamily,
|
|
2175
|
-
resolvedWeight,
|
|
2176
|
-
resolvedStyle,
|
|
2177
|
-
resolved.synthetic,
|
|
2178
|
-
run.fill,
|
|
2179
|
-
source === "outline" ? run.stroke : void 0,
|
|
2180
|
-
source,
|
|
2181
|
-
page
|
|
2182
|
-
);
|
|
2183
|
-
let g = ctx.groups.get(key);
|
|
2184
|
-
if (!g) {
|
|
2185
|
-
g = {
|
|
2186
|
-
family: atlasFamily,
|
|
2187
|
-
weight: resolvedWeight,
|
|
2188
|
-
style: resolvedStyle,
|
|
2189
|
-
synthetic: { ...resolved.synthetic },
|
|
2190
|
-
source,
|
|
2191
|
-
page,
|
|
2192
|
-
fill: run.fill,
|
|
2193
|
-
// Only the outline tier can paint it, and carrying it on an SDF group
|
|
2194
|
-
// would be a promise the renderer cannot keep.
|
|
2195
|
-
...source === "outline" && run.stroke !== void 0 ? { stroke: run.stroke } : {},
|
|
2196
|
-
quads: [],
|
|
2197
|
-
glyphs: []
|
|
2198
|
-
};
|
|
2199
|
-
ctx.groups.set(key, g);
|
|
2200
|
-
}
|
|
2201
|
-
return g;
|
|
2202
|
-
}
|
|
2203
|
-
function resolveGlyph(run, font, resolved, cp) {
|
|
2204
|
-
const direct = font.charMap.get(cp);
|
|
2205
|
-
if (direct) return { glyph: direct, font, resolved };
|
|
2206
|
-
const fallback = resolveGlyphFallback(run.fontFamily, run.fontWeight, run.fontStyle);
|
|
2207
|
-
const face = fallback?.dynamicFace;
|
|
2208
|
-
if (fallback && face) {
|
|
2209
|
-
const glyph = face.requestGlyph(cp);
|
|
2210
|
-
if (glyph.xadvance > 0 || glyph.width > 0) {
|
|
2211
|
-
return { glyph, font: face.font, resolved: fallback };
|
|
2013
|
+
function runCommand(upper, relative, a, st, b) {
|
|
2014
|
+
const rx = relative ? st.x : 0;
|
|
2015
|
+
const ry = relative ? st.y : 0;
|
|
2016
|
+
switch (upper) {
|
|
2017
|
+
case "M": {
|
|
2018
|
+
const x = a[0] + rx;
|
|
2019
|
+
const y = a[1] + ry;
|
|
2020
|
+
b.moveTo(x, y);
|
|
2021
|
+
st.x = x;
|
|
2022
|
+
st.y = y;
|
|
2023
|
+
st.startX = x;
|
|
2024
|
+
st.startY = y;
|
|
2025
|
+
st.lastCubicCtrl = null;
|
|
2026
|
+
st.lastQuadCtrl = null;
|
|
2027
|
+
return;
|
|
2212
2028
|
}
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
const ch = String.fromCodePoint(cp);
|
|
2223
|
-
console.warn(
|
|
2224
|
-
`weasel layoutRuns: no glyph for U+${cp.toString(16).toUpperCase().padStart(4, "0")} (${JSON.stringify(ch)}) in "${family}", and the dynamic tier could not rasterize it \u2014 skipping the character. Bake it into the atlas, or call registerCanvasFont("${family}") to serve missing codepoints from installed fonts.`
|
|
2225
|
-
);
|
|
2226
|
-
}
|
|
2227
|
-
function layoutRuns(runs, opts) {
|
|
2228
|
-
const ctx = { groups: /* @__PURE__ */ new Map() };
|
|
2229
|
-
const entries = [];
|
|
2230
|
-
let prevCp;
|
|
2231
|
-
let prevFont;
|
|
2232
|
-
let prevFontSize;
|
|
2233
|
-
for (const run of runs) {
|
|
2234
|
-
const resolved = resolveFontVariant(run.fontFamily, run.fontWeight, run.fontStyle);
|
|
2235
|
-
const font = resolved.entry?.font ?? resolved.dynamicFace?.font;
|
|
2236
|
-
if (!font) {
|
|
2237
|
-
prevCp = void 0;
|
|
2238
|
-
prevFont = void 0;
|
|
2239
|
-
prevFontSize = void 0;
|
|
2240
|
-
continue;
|
|
2029
|
+
case "L": {
|
|
2030
|
+
const x = a[0] + rx;
|
|
2031
|
+
const y = a[1] + ry;
|
|
2032
|
+
b.lineTo(x, y);
|
|
2033
|
+
st.x = x;
|
|
2034
|
+
st.y = y;
|
|
2035
|
+
st.lastCubicCtrl = null;
|
|
2036
|
+
st.lastQuadCtrl = null;
|
|
2037
|
+
return;
|
|
2241
2038
|
}
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
entries.push({
|
|
2250
|
-
run,
|
|
2251
|
-
font,
|
|
2252
|
-
glyph: { id: cp, x: 0, y: 0, width: 0, height: 0, xoffset: 0, yoffset: 0, xadvance: 0, page: 0 },
|
|
2253
|
-
// A newline consumes no advance, so it takes no tracking either.
|
|
2254
|
-
cp,
|
|
2255
|
-
advance: 0,
|
|
2256
|
-
tracking: 0,
|
|
2257
|
-
kerningBefore: 0,
|
|
2258
|
-
isSpace: false,
|
|
2259
|
-
isNewline: true,
|
|
2260
|
-
resolved,
|
|
2261
|
-
fontSize: run.fontSize
|
|
2262
|
-
});
|
|
2263
|
-
prevCp = void 0;
|
|
2264
|
-
prevFont = void 0;
|
|
2265
|
-
prevFontSize = void 0;
|
|
2266
|
-
continue;
|
|
2267
|
-
}
|
|
2268
|
-
if (isSpace) {
|
|
2269
|
-
const spaceGlyph = resolved.dynamicFace ? resolved.dynamicFace.requestGlyph(32) : font.charMap.get(32);
|
|
2270
|
-
const advance = spaceGlyph ? spaceGlyph.xadvance * scale2 : run.fontSize * 0.25;
|
|
2271
|
-
let kerningBefore2 = 0;
|
|
2272
|
-
if (prevCp !== void 0 && prevFont !== void 0 && prevFontSize !== void 0) {
|
|
2273
|
-
const kAtlas = prevFont.kerningMap.get(prevCp)?.get(cp) ?? 0;
|
|
2274
|
-
kerningBefore2 = kAtlas * (prevFontSize / prevFont.info.size);
|
|
2275
|
-
}
|
|
2276
|
-
entries.push({
|
|
2277
|
-
run,
|
|
2278
|
-
font,
|
|
2279
|
-
glyph: spaceGlyph ?? { id: 32, x: 0, y: 0, width: 0, height: 0, xoffset: 0, yoffset: 0, xadvance: 0, page: 0 },
|
|
2280
|
-
cp,
|
|
2281
|
-
advance,
|
|
2282
|
-
tracking,
|
|
2283
|
-
kerningBefore: kerningBefore2,
|
|
2284
|
-
isSpace: true,
|
|
2285
|
-
isNewline: false,
|
|
2286
|
-
resolved,
|
|
2287
|
-
fontSize: run.fontSize
|
|
2288
|
-
});
|
|
2289
|
-
prevCp = cp;
|
|
2290
|
-
prevFont = font;
|
|
2291
|
-
prevFontSize = run.fontSize;
|
|
2292
|
-
continue;
|
|
2293
|
-
}
|
|
2294
|
-
const hit = resolved.dynamicFace ? { glyph: resolved.dynamicFace.requestGlyph(cp), font, resolved } : resolveGlyph(run, font, resolved, cp);
|
|
2295
|
-
if (!hit) {
|
|
2296
|
-
prevCp = cp;
|
|
2297
|
-
prevFont = font;
|
|
2298
|
-
prevFontSize = run.fontSize;
|
|
2299
|
-
continue;
|
|
2300
|
-
}
|
|
2301
|
-
const glyphFont = hit.font;
|
|
2302
|
-
const glyphScale = run.fontSize / glyphFont.info.size;
|
|
2303
|
-
let kerningBefore = 0;
|
|
2304
|
-
if (prevCp !== void 0 && prevFont !== void 0 && prevFontSize !== void 0) {
|
|
2305
|
-
const kAtlas = prevFont.kerningMap.get(prevCp)?.get(cp) ?? 0;
|
|
2306
|
-
kerningBefore = kAtlas * (prevFontSize / prevFont.info.size);
|
|
2307
|
-
}
|
|
2308
|
-
entries.push({
|
|
2309
|
-
run,
|
|
2310
|
-
font: glyphFont,
|
|
2311
|
-
glyph: hit.glyph,
|
|
2312
|
-
cp,
|
|
2313
|
-
advance: hit.glyph.xadvance * glyphScale,
|
|
2314
|
-
tracking,
|
|
2315
|
-
kerningBefore,
|
|
2316
|
-
isSpace,
|
|
2317
|
-
isNewline: false,
|
|
2318
|
-
resolved: hit.resolved,
|
|
2319
|
-
fontSize: run.fontSize
|
|
2320
|
-
});
|
|
2321
|
-
prevCp = cp;
|
|
2322
|
-
prevFont = glyphFont;
|
|
2323
|
-
prevFontSize = run.fontSize;
|
|
2039
|
+
case "H": {
|
|
2040
|
+
const x = a[0] + (relative ? st.x : 0);
|
|
2041
|
+
b.lineTo(x, st.y);
|
|
2042
|
+
st.x = x;
|
|
2043
|
+
st.lastCubicCtrl = null;
|
|
2044
|
+
st.lastQuadCtrl = null;
|
|
2045
|
+
return;
|
|
2324
2046
|
}
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
let i = 0;
|
|
2333
|
-
while (i < entries.length) {
|
|
2334
|
-
const e = entries[i];
|
|
2335
|
-
if (e.isNewline) {
|
|
2336
|
-
if (cur.entries.length === 0) {
|
|
2337
|
-
cur.height = Math.max(cur.height, e.fontSize * opts.lineHeight);
|
|
2338
|
-
cur.blank = e;
|
|
2339
|
-
}
|
|
2340
|
-
commitLine();
|
|
2341
|
-
i++;
|
|
2342
|
-
continue;
|
|
2047
|
+
case "V": {
|
|
2048
|
+
const y = a[0] + (relative ? st.y : 0);
|
|
2049
|
+
b.lineTo(st.x, y);
|
|
2050
|
+
st.y = y;
|
|
2051
|
+
st.lastCubicCtrl = null;
|
|
2052
|
+
st.lastQuadCtrl = null;
|
|
2053
|
+
return;
|
|
2343
2054
|
}
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2055
|
+
case "C": {
|
|
2056
|
+
const x1 = a[0] + rx, y1 = a[1] + ry;
|
|
2057
|
+
const x2 = a[2] + rx, y2 = a[3] + ry;
|
|
2058
|
+
const x = a[4] + rx, y = a[5] + ry;
|
|
2059
|
+
b.curveTo(x1, y1, x2, y2, x, y);
|
|
2060
|
+
st.x = x;
|
|
2061
|
+
st.y = y;
|
|
2062
|
+
st.lastCubicCtrl = { x: x2, y: y2 };
|
|
2063
|
+
st.lastQuadCtrl = null;
|
|
2064
|
+
return;
|
|
2352
2065
|
}
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
const
|
|
2357
|
-
|
|
2358
|
-
|
|
2066
|
+
case "S": {
|
|
2067
|
+
const ref = st.lastCubicCtrl ?? { x: st.x, y: st.y };
|
|
2068
|
+
const x1 = 2 * st.x - ref.x;
|
|
2069
|
+
const y1 = 2 * st.y - ref.y;
|
|
2070
|
+
const x2 = a[0] + rx, y2 = a[1] + ry;
|
|
2071
|
+
const x = a[2] + rx, y = a[3] + ry;
|
|
2072
|
+
b.curveTo(x1, y1, x2, y2, x, y);
|
|
2073
|
+
st.x = x;
|
|
2074
|
+
st.y = y;
|
|
2075
|
+
st.lastCubicCtrl = { x: x2, y: y2 };
|
|
2076
|
+
st.lastQuadCtrl = null;
|
|
2077
|
+
return;
|
|
2359
2078
|
}
|
|
2360
|
-
|
|
2361
|
-
|
|
2079
|
+
case "Q": {
|
|
2080
|
+
const x1 = a[0] + rx, y1 = a[1] + ry;
|
|
2081
|
+
const x = a[2] + rx, y = a[3] + ry;
|
|
2082
|
+
b.quadTo(x1, y1, x, y);
|
|
2083
|
+
st.x = x;
|
|
2084
|
+
st.y = y;
|
|
2085
|
+
st.lastQuadCtrl = { x: x1, y: y1 };
|
|
2086
|
+
st.lastCubicCtrl = null;
|
|
2087
|
+
return;
|
|
2362
2088
|
}
|
|
2363
|
-
|
|
2364
|
-
const
|
|
2365
|
-
const
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2089
|
+
case "T": {
|
|
2090
|
+
const ref = st.lastQuadCtrl ?? { x: st.x, y: st.y };
|
|
2091
|
+
const x1 = 2 * st.x - ref.x;
|
|
2092
|
+
const y1 = 2 * st.y - ref.y;
|
|
2093
|
+
const x = a[0] + rx, y = a[1] + ry;
|
|
2094
|
+
b.quadTo(x1, y1, x, y);
|
|
2095
|
+
st.x = x;
|
|
2096
|
+
st.y = y;
|
|
2097
|
+
st.lastQuadCtrl = { x: x1, y: y1 };
|
|
2098
|
+
st.lastCubicCtrl = null;
|
|
2099
|
+
return;
|
|
2369
2100
|
}
|
|
2370
|
-
|
|
2101
|
+
case "A": {
|
|
2102
|
+
const rxA = Math.abs(a[0]);
|
|
2103
|
+
const ryA = Math.abs(a[1]);
|
|
2104
|
+
const xRot = a[2];
|
|
2105
|
+
const largeArc = a[3] !== 0;
|
|
2106
|
+
const sweep = a[4] !== 0;
|
|
2107
|
+
const ex = a[5] + rx;
|
|
2108
|
+
const ey = a[6] + ry;
|
|
2109
|
+
arcToCubics(st.x, st.y, ex, ey, rxA, ryA, xRot, largeArc, sweep, b);
|
|
2110
|
+
st.x = ex;
|
|
2111
|
+
st.y = ey;
|
|
2112
|
+
st.lastCubicCtrl = null;
|
|
2113
|
+
st.lastQuadCtrl = null;
|
|
2114
|
+
return;
|
|
2115
|
+
}
|
|
2116
|
+
default:
|
|
2117
|
+
return;
|
|
2371
2118
|
}
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
return glyphOutline(r.family, r.weight, r.style, e.cp);
|
|
2119
|
+
}
|
|
2120
|
+
function arcToCubics(x1, y1, x2, y2, rx, ry, xAxisRotationDeg, largeArc, sweep, b) {
|
|
2121
|
+
if (x1 === x2 && y1 === y2) return;
|
|
2122
|
+
if (rx === 0 || ry === 0) {
|
|
2123
|
+
b.lineTo(x2, y2);
|
|
2124
|
+
return;
|
|
2379
2125
|
}
|
|
2380
|
-
const
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2126
|
+
const phi = xAxisRotationDeg * Math.PI / 180;
|
|
2127
|
+
const cosPhi = Math.cos(phi);
|
|
2128
|
+
const sinPhi = Math.sin(phi);
|
|
2129
|
+
const dx = (x1 - x2) / 2;
|
|
2130
|
+
const dy = (y1 - y2) / 2;
|
|
2131
|
+
const x1p = cosPhi * dx + sinPhi * dy;
|
|
2132
|
+
const y1p = -sinPhi * dx + cosPhi * dy;
|
|
2133
|
+
let rxs = rx * rx;
|
|
2134
|
+
let rys = ry * ry;
|
|
2135
|
+
const x1ps = x1p * x1p;
|
|
2136
|
+
const y1ps = y1p * y1p;
|
|
2137
|
+
const lambda = x1ps / rxs + y1ps / rys;
|
|
2138
|
+
let rxFixed = rx;
|
|
2139
|
+
let ryFixed = ry;
|
|
2140
|
+
if (lambda > 1) {
|
|
2141
|
+
const sqrtLambda = Math.sqrt(lambda);
|
|
2142
|
+
rxFixed = sqrtLambda * rx;
|
|
2143
|
+
ryFixed = sqrtLambda * ry;
|
|
2144
|
+
rxs = rxFixed * rxFixed;
|
|
2145
|
+
rys = ryFixed * ryFixed;
|
|
2395
2146
|
}
|
|
2396
|
-
const
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
const
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2147
|
+
const sign = largeArc === sweep ? -1 : 1;
|
|
2148
|
+
const num = rxs * rys - rxs * y1ps - rys * x1ps;
|
|
2149
|
+
const den = rxs * y1ps + rys * x1ps;
|
|
2150
|
+
const factor = sign * Math.sqrt(Math.max(0, num / den));
|
|
2151
|
+
const cxp = factor * (rxFixed * y1p) / ryFixed;
|
|
2152
|
+
const cyp = factor * -(ryFixed * x1p) / rxFixed;
|
|
2153
|
+
const cx = cosPhi * cxp - sinPhi * cyp + (x1 + x2) / 2;
|
|
2154
|
+
const cy = sinPhi * cxp + cosPhi * cyp + (y1 + y2) / 2;
|
|
2155
|
+
const angle = (ux, uy, vx, vy) => {
|
|
2156
|
+
const dot = ux * vx + uy * vy;
|
|
2157
|
+
const len = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
|
|
2158
|
+
let a = Math.acos(Math.max(-1, Math.min(1, dot / len)));
|
|
2159
|
+
if (ux * vy - uy * vx < 0) a = -a;
|
|
2160
|
+
return a;
|
|
2161
|
+
};
|
|
2162
|
+
const theta1 = angle(1, 0, (x1p - cxp) / rxFixed, (y1p - cyp) / ryFixed);
|
|
2163
|
+
let dTheta = angle(
|
|
2164
|
+
(x1p - cxp) / rxFixed,
|
|
2165
|
+
(y1p - cyp) / ryFixed,
|
|
2166
|
+
(-x1p - cxp) / rxFixed,
|
|
2167
|
+
(-y1p - cyp) / ryFixed
|
|
2168
|
+
);
|
|
2169
|
+
if (!sweep && dTheta > 0) dTheta -= 2 * Math.PI;
|
|
2170
|
+
else if (sweep && dTheta < 0) dTheta += 2 * Math.PI;
|
|
2171
|
+
const segCount = Math.max(1, Math.ceil(Math.abs(dTheta) / (Math.PI / 2)));
|
|
2172
|
+
const segAngle = dTheta / segCount;
|
|
2173
|
+
const t = 4 / 3 * Math.tan(segAngle / 4);
|
|
2174
|
+
let prevX = x1;
|
|
2175
|
+
let prevY = y1;
|
|
2176
|
+
let prevDx = -Math.sin(theta1) * rxFixed;
|
|
2177
|
+
let prevDy = Math.cos(theta1) * ryFixed;
|
|
2178
|
+
for (let i = 1; i <= segCount; i++) {
|
|
2179
|
+
const theta = theta1 + i * segAngle;
|
|
2180
|
+
const cosT = Math.cos(theta);
|
|
2181
|
+
const sinT = Math.sin(theta);
|
|
2182
|
+
const px = cosPhi * (cosT * rxFixed) - sinPhi * (sinT * ryFixed) + cx;
|
|
2183
|
+
const py = sinPhi * (cosT * rxFixed) + cosPhi * (sinT * ryFixed) + cy;
|
|
2184
|
+
const dxNew = -sinT * rxFixed;
|
|
2185
|
+
const dyNew = cosT * ryFixed;
|
|
2186
|
+
const c1x = prevX + cosPhi * (t * prevDx) - sinPhi * (t * prevDy);
|
|
2187
|
+
const c1y = prevY + sinPhi * (t * prevDx) + cosPhi * (t * prevDy);
|
|
2188
|
+
const c2x = px - cosPhi * (t * dxNew) + sinPhi * (t * dyNew);
|
|
2189
|
+
const c2y = py - sinPhi * (t * dxNew) - cosPhi * (t * dyNew);
|
|
2190
|
+
b.curveTo(c1x, c1y, c2x, c2y, px, py);
|
|
2191
|
+
prevX = px;
|
|
2192
|
+
prevY = py;
|
|
2193
|
+
prevDx = dxNew;
|
|
2194
|
+
prevDy = dyNew;
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
// src/renderer/cache/outlineMeshCache.ts
|
|
2199
|
+
var OUTLINE_FLATTEN_TOLERANCE = 1 / 4096;
|
|
2200
|
+
var OUTLINE_MESH_CACHE_LIMIT = 2048;
|
|
2201
|
+
var cache3 = /* @__PURE__ */ new Map();
|
|
2202
|
+
function outlineMesh(key, d) {
|
|
2203
|
+
const cached = cache3.get(key);
|
|
2204
|
+
if (cached !== void 0) return cached;
|
|
2205
|
+
if (cache3.size >= OUTLINE_MESH_CACHE_LIMIT) cache3 = /* @__PURE__ */ new Map();
|
|
2206
|
+
const mesh = tessellate(pathFromD(d), { flattenTolerance: OUTLINE_FLATTEN_TOLERANCE });
|
|
2207
|
+
cache3.set(key, mesh);
|
|
2208
|
+
return mesh;
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
// src/features/paths/tessellate/polyline.ts
|
|
2212
|
+
function extractPolylines(path, opts = {}) {
|
|
2213
|
+
if (path.kind === "rect") return [extractRect(path)];
|
|
2214
|
+
return extractPolygon(path, opts);
|
|
2215
|
+
}
|
|
2216
|
+
function extractRect(p) {
|
|
2217
|
+
const { x, y, width: w, height: h } = p;
|
|
2218
|
+
return {
|
|
2219
|
+
points: [x, y, x + w, y, x + w, y + h, x, y + h],
|
|
2220
|
+
closed: true,
|
|
2221
|
+
anchorA: new Uint32Array([0, 1, 2, 3]),
|
|
2222
|
+
anchorB: new Uint32Array([0, 1, 2, 3]),
|
|
2223
|
+
anchorT: new Float32Array([0, 0, 0, 0])
|
|
2224
|
+
};
|
|
2225
|
+
}
|
|
2226
|
+
function extractPolygon(p, opts) {
|
|
2227
|
+
const tolerance = opts.flattenTolerance ?? DEFAULT_FLATTEN_TOLERANCE;
|
|
2228
|
+
const { commands, coords } = p;
|
|
2229
|
+
const out = [];
|
|
2230
|
+
let anchorCounter = 0;
|
|
2231
|
+
let pts = null;
|
|
2232
|
+
let aA = null;
|
|
2233
|
+
let aB = null;
|
|
2234
|
+
let aT = null;
|
|
2235
|
+
let current = null;
|
|
2236
|
+
let coordIdx = 0;
|
|
2237
|
+
let prevX = 0;
|
|
2238
|
+
let prevY = 0;
|
|
2239
|
+
let prevAnchor = -1;
|
|
2240
|
+
const beginContour = () => {
|
|
2241
|
+
pts = [];
|
|
2242
|
+
aA = [];
|
|
2243
|
+
aB = [];
|
|
2244
|
+
aT = [];
|
|
2245
|
+
const next = { points: pts, closed: false };
|
|
2246
|
+
out.push(next);
|
|
2247
|
+
return next;
|
|
2248
|
+
};
|
|
2249
|
+
const dropDuplicateClosingPoint = () => {
|
|
2250
|
+
if (!pts || !aA || !aB || !aT) return;
|
|
2251
|
+
const n = pts.length / 2;
|
|
2252
|
+
if (n < 2) return;
|
|
2253
|
+
if (pts[0] !== pts[(n - 1) * 2] || pts[1] !== pts[(n - 1) * 2 + 1]) return;
|
|
2254
|
+
pts.length -= 2;
|
|
2255
|
+
aA.length -= 1;
|
|
2256
|
+
aB.length -= 1;
|
|
2257
|
+
aT.length -= 1;
|
|
2258
|
+
};
|
|
2259
|
+
const commit = (target) => {
|
|
2260
|
+
if (!pts || !aA || !aB || !aT) return;
|
|
2261
|
+
target.anchorA = new Uint32Array(aA);
|
|
2262
|
+
target.anchorB = new Uint32Array(aB);
|
|
2263
|
+
target.anchorT = new Float32Array(aT);
|
|
2264
|
+
};
|
|
2265
|
+
for (let cmdIdx = 0; cmdIdx < commands.length; cmdIdx++) {
|
|
2266
|
+
const cmd = commands[cmdIdx];
|
|
2267
|
+
switch (cmd) {
|
|
2268
|
+
case PATH_M: {
|
|
2269
|
+
if (current) commit(current);
|
|
2270
|
+
current = beginContour();
|
|
2271
|
+
prevX = coords[coordIdx];
|
|
2272
|
+
prevY = coords[coordIdx + 1];
|
|
2273
|
+
pts.push(prevX, prevY);
|
|
2274
|
+
aA.push(anchorCounter);
|
|
2275
|
+
aB.push(anchorCounter);
|
|
2276
|
+
aT.push(0);
|
|
2277
|
+
prevAnchor = anchorCounter;
|
|
2278
|
+
anchorCounter++;
|
|
2279
|
+
coordIdx += 2;
|
|
2280
|
+
break;
|
|
2281
|
+
}
|
|
2282
|
+
case PATH_L: {
|
|
2283
|
+
prevX = coords[coordIdx];
|
|
2284
|
+
prevY = coords[coordIdx + 1];
|
|
2285
|
+
pts.push(prevX, prevY);
|
|
2286
|
+
aA.push(anchorCounter);
|
|
2287
|
+
aB.push(anchorCounter);
|
|
2288
|
+
aT.push(0);
|
|
2289
|
+
prevAnchor = anchorCounter;
|
|
2290
|
+
anchorCounter++;
|
|
2291
|
+
coordIdx += 2;
|
|
2292
|
+
break;
|
|
2293
|
+
}
|
|
2294
|
+
case PATH_Q: {
|
|
2295
|
+
const cx = coords[coordIdx], cy = coords[coordIdx + 1];
|
|
2296
|
+
const ex = coords[coordIdx + 2], ey = coords[coordIdx + 3];
|
|
2297
|
+
const targetAnchor = anchorCounter;
|
|
2298
|
+
const segStart = pts.length / 2;
|
|
2299
|
+
const arcAccum = [];
|
|
2300
|
+
const total = flattenQuadraticWithArcLen(prevX, prevY, cx, cy, ex, ey, tolerance, pts, arcAccum);
|
|
2301
|
+
for (let k = 0; k < arcAccum.length; k++) {
|
|
2302
|
+
aA.push(prevAnchor);
|
|
2303
|
+
aB.push(targetAnchor);
|
|
2304
|
+
aT.push(total > 0 ? arcAccum[k] / total : 0);
|
|
2432
2305
|
}
|
|
2433
|
-
|
|
2434
|
-
|
|
2306
|
+
const lastIdx = segStart + arcAccum.length - 1;
|
|
2307
|
+
aA[lastIdx] = targetAnchor;
|
|
2308
|
+
aB[lastIdx] = targetAnchor;
|
|
2309
|
+
aT[lastIdx] = 0;
|
|
2310
|
+
prevX = ex;
|
|
2311
|
+
prevY = ey;
|
|
2312
|
+
prevAnchor = targetAnchor;
|
|
2313
|
+
anchorCounter++;
|
|
2314
|
+
coordIdx += 4;
|
|
2315
|
+
break;
|
|
2435
2316
|
}
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
const
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2317
|
+
case PATH_C: {
|
|
2318
|
+
const c1x = coords[coordIdx], c1y = coords[coordIdx + 1];
|
|
2319
|
+
const c2x = coords[coordIdx + 2], c2y = coords[coordIdx + 3];
|
|
2320
|
+
const ex = coords[coordIdx + 4], ey = coords[coordIdx + 5];
|
|
2321
|
+
const targetAnchor = anchorCounter;
|
|
2322
|
+
const segStart = pts.length / 2;
|
|
2323
|
+
const arcAccum = [];
|
|
2324
|
+
const total = flattenCubicWithArcLen(prevX, prevY, c1x, c1y, c2x, c2y, ex, ey, tolerance, pts, arcAccum);
|
|
2325
|
+
for (let k = 0; k < arcAccum.length; k++) {
|
|
2326
|
+
aA.push(prevAnchor);
|
|
2327
|
+
aB.push(targetAnchor);
|
|
2328
|
+
aT.push(total > 0 ? arcAccum[k] / total : 0);
|
|
2329
|
+
}
|
|
2330
|
+
const lastIdx = segStart + arcAccum.length - 1;
|
|
2331
|
+
aA[lastIdx] = targetAnchor;
|
|
2332
|
+
aB[lastIdx] = targetAnchor;
|
|
2333
|
+
aT[lastIdx] = 0;
|
|
2334
|
+
prevX = ex;
|
|
2335
|
+
prevY = ey;
|
|
2336
|
+
prevAnchor = targetAnchor;
|
|
2337
|
+
anchorCounter++;
|
|
2338
|
+
coordIdx += 6;
|
|
2339
|
+
break;
|
|
2449
2340
|
}
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2341
|
+
case PATH_Z: {
|
|
2342
|
+
if (current) {
|
|
2343
|
+
current.closed = true;
|
|
2344
|
+
dropDuplicateClosingPoint();
|
|
2345
|
+
}
|
|
2346
|
+
break;
|
|
2453
2347
|
}
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2348
|
+
default:
|
|
2349
|
+
throw new Error(`extractPolylines: unknown command code ${cmd}`);
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
if (current) commit(current);
|
|
2353
|
+
return out;
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
// src/features/paths/tessellate/stroke.ts
|
|
2357
|
+
var EMPTY_MESH = {
|
|
2358
|
+
vertices: new Float32Array(0),
|
|
2359
|
+
indices: new Uint32Array(0),
|
|
2360
|
+
anchorA: new Uint32Array(0),
|
|
2361
|
+
anchorB: new Uint32Array(0),
|
|
2362
|
+
anchorT: new Float32Array(0)
|
|
2363
|
+
};
|
|
2364
|
+
function tessellateStroke(path, stroke, opts = {}) {
|
|
2365
|
+
const width = stroke.width ?? 1;
|
|
2366
|
+
if (width <= 0) return EMPTY_MESH;
|
|
2367
|
+
const join = stroke.join ?? "miter";
|
|
2368
|
+
const cap = stroke.cap ?? "butt";
|
|
2369
|
+
const align = stroke.align ?? "center";
|
|
2370
|
+
const miterLimit = stroke.miterLimit ?? DEFAULT_MITER_LIMIT;
|
|
2371
|
+
const varyingThreshold = stroke.varyingWidthJoinThreshold ?? DEFAULT_VARYING_WIDTH_JOIN_THRESHOLD;
|
|
2372
|
+
let workingPath = path;
|
|
2373
|
+
if (path.kind === "rect" && align !== "center") {
|
|
2374
|
+
const aligned = alignedStrokeRect(path, align, width);
|
|
2375
|
+
workingPath = {
|
|
2376
|
+
kind: "rect",
|
|
2377
|
+
x: aligned.x,
|
|
2378
|
+
y: aligned.y,
|
|
2379
|
+
width: aligned.width,
|
|
2380
|
+
height: aligned.height
|
|
2381
|
+
};
|
|
2382
|
+
}
|
|
2383
|
+
const polylines = extractPolylines(workingPath, opts);
|
|
2384
|
+
if (stroke.vertexWidths && stroke.vertexWidths.length > 0) {
|
|
2385
|
+
for (const pl of polylines) populatePolylineWidths(pl, stroke.vertexWidths, width);
|
|
2386
|
+
}
|
|
2387
|
+
const dash = stroke.dash ?? [];
|
|
2388
|
+
const verts = [];
|
|
2389
|
+
const idx = [];
|
|
2390
|
+
const anchorA = [];
|
|
2391
|
+
const anchorB = [];
|
|
2392
|
+
const anchorT = [];
|
|
2393
|
+
for (const pl of polylines) {
|
|
2394
|
+
const subs = dash.length > 0 ? splitForDash(pl, dash) : [pl];
|
|
2395
|
+
for (const sub2 of subs) {
|
|
2396
|
+
expandPolyline(sub2, width, join, cap, miterLimit, varyingThreshold, verts, idx, anchorA, anchorB, anchorT);
|
|
2467
2397
|
}
|
|
2468
|
-
flushSpan();
|
|
2469
|
-
lineBoxes.push({
|
|
2470
|
-
x0: lineX0,
|
|
2471
|
-
y0: penY,
|
|
2472
|
-
x1: lineX0 + line.width,
|
|
2473
|
-
y1: penY + line.height,
|
|
2474
|
-
baselineY: lineBaselineY
|
|
2475
|
-
});
|
|
2476
|
-
maxLineWidth = Math.max(maxLineWidth, line.width);
|
|
2477
|
-
penY += line.height;
|
|
2478
2398
|
}
|
|
2479
2399
|
return {
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2400
|
+
vertices: new Float32Array(verts),
|
|
2401
|
+
indices: new Uint32Array(idx),
|
|
2402
|
+
anchorA: new Uint32Array(anchorA),
|
|
2403
|
+
anchorB: new Uint32Array(anchorB),
|
|
2404
|
+
anchorT: new Float32Array(anchorT)
|
|
2484
2405
|
};
|
|
2485
2406
|
}
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
const
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2407
|
+
function expandPolyline(pl, width, join, cap, miterLimit, varyingThreshold, verts, idx, anchorA, anchorB, anchorT) {
|
|
2408
|
+
const half = width / 2;
|
|
2409
|
+
const pts = pl.points;
|
|
2410
|
+
const ptCount = pts.length / 2;
|
|
2411
|
+
const segCount = ptCount - 1;
|
|
2412
|
+
if (segCount < 1) return;
|
|
2413
|
+
const plA = pl.anchorA ?? new Uint32Array(ptCount);
|
|
2414
|
+
const plB = pl.anchorB ?? new Uint32Array(ptCount);
|
|
2415
|
+
const plT = pl.anchorT ?? new Float32Array(ptCount);
|
|
2416
|
+
const plHalf = pl.widths ? pl.widths.map((w) => w / 2) : null;
|
|
2417
|
+
const segs = [];
|
|
2418
|
+
const segSrcIdx = [];
|
|
2419
|
+
for (let s = 0; s < segCount; s++) {
|
|
2420
|
+
const ha = plHalf ? plHalf[s] : half;
|
|
2421
|
+
const hb = plHalf ? plHalf[s + 1] : half;
|
|
2422
|
+
const seg = makeSeg(pts[s * 2], pts[s * 2 + 1], pts[(s + 1) * 2], pts[(s + 1) * 2 + 1], ha, hb);
|
|
2423
|
+
if (seg) {
|
|
2424
|
+
segs.push(seg);
|
|
2425
|
+
segSrcIdx.push(s);
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
let closerSrcIdx = -1;
|
|
2429
|
+
if (pl.closed && segs.length >= 1) {
|
|
2430
|
+
const last = segs[segs.length - 1];
|
|
2431
|
+
const first = segs[0];
|
|
2432
|
+
const haCloser = plHalf ? plHalf[ptCount - 1] : half;
|
|
2433
|
+
const hbCloser = plHalf ? plHalf[0] : half;
|
|
2434
|
+
const closer = makeSeg(last.bx, last.by, first.ax, first.ay, haCloser, hbCloser);
|
|
2435
|
+
if (closer) {
|
|
2436
|
+
segs.push(closer);
|
|
2437
|
+
closerSrcIdx = ptCount - 1;
|
|
2438
|
+
segSrcIdx.push(closerSrcIdx);
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
if (segs.length === 0) return;
|
|
2442
|
+
const segBaseIdx = [];
|
|
2443
|
+
for (let s = 0; s < segs.length; s++) {
|
|
2444
|
+
const seg = segs[s];
|
|
2445
|
+
const startSrc = segSrcIdx[s];
|
|
2446
|
+
const endSrc = s === segs.length - 1 && pl.closed && closerSrcIdx >= 0 ? 0 : startSrc + 1;
|
|
2447
|
+
const base = verts.length / 2;
|
|
2448
|
+
segBaseIdx.push(base);
|
|
2449
|
+
const nxA = seg.nxUnit * seg.halfA, nyA = seg.nyUnit * seg.halfA;
|
|
2450
|
+
const nxB = seg.nxUnit * seg.halfB, nyB = seg.nyUnit * seg.halfB;
|
|
2451
|
+
verts.push(seg.ax + nxA, seg.ay + nyA);
|
|
2452
|
+
anchorA.push(plA[startSrc]);
|
|
2453
|
+
anchorB.push(plB[startSrc]);
|
|
2454
|
+
anchorT.push(plT[startSrc]);
|
|
2455
|
+
verts.push(seg.ax - nxA, seg.ay - nyA);
|
|
2456
|
+
anchorA.push(plA[startSrc]);
|
|
2457
|
+
anchorB.push(plB[startSrc]);
|
|
2458
|
+
anchorT.push(plT[startSrc]);
|
|
2459
|
+
verts.push(seg.bx + nxB, seg.by + nyB);
|
|
2460
|
+
anchorA.push(plA[endSrc]);
|
|
2461
|
+
anchorB.push(plB[endSrc]);
|
|
2462
|
+
anchorT.push(plT[endSrc]);
|
|
2463
|
+
verts.push(seg.bx - nxB, seg.by - nyB);
|
|
2464
|
+
anchorA.push(plA[endSrc]);
|
|
2465
|
+
anchorB.push(plB[endSrc]);
|
|
2466
|
+
anchorT.push(plT[endSrc]);
|
|
2467
|
+
idx.push(base, base + 1, base + 2, base + 1, base + 3, base + 2);
|
|
2468
|
+
}
|
|
2469
|
+
const joinCount = pl.closed ? segs.length : segs.length - 1;
|
|
2470
|
+
for (let j = 0; j < joinCount; j++) {
|
|
2471
|
+
emitJoin(segs, segBaseIdx, j, join, miterLimit, varyingThreshold, verts, idx, anchorA, anchorB, anchorT, segSrcIdx, plA, plB, plT);
|
|
2472
|
+
}
|
|
2473
|
+
if (!pl.closed && cap !== "butt") {
|
|
2474
|
+
const first = segs[0];
|
|
2475
|
+
const firstBase = segBaseIdx[0];
|
|
2476
|
+
emitCap(first, firstBase + 0, firstBase + 1, false, first.halfA, cap, verts, idx, anchorA, anchorB, anchorT, plA[0], plB[0], plT[0]);
|
|
2477
|
+
const last = segs[segs.length - 1];
|
|
2478
|
+
const lastBase = segBaseIdx[segs.length - 1];
|
|
2479
|
+
const lastSrc = segSrcIdx[segs.length - 1] + 1;
|
|
2480
|
+
emitCap(last, lastBase + 2, lastBase + 3, true, last.halfB, cap, verts, idx, anchorA, anchorB, anchorT, plA[lastSrc], plB[lastSrc], plT[lastSrc]);
|
|
2508
2481
|
}
|
|
2509
|
-
const key = variantKey(runs, opts);
|
|
2510
|
-
const hit = entry.byVariant.get(key);
|
|
2511
|
-
if (hit !== void 0) return hit;
|
|
2512
|
-
const laid = layoutRuns(runs, opts);
|
|
2513
|
-
if (entry.byVariant.size >= LAYOUT_CACHE_VARIANT_LIMIT) entry.byVariant.clear();
|
|
2514
|
-
entry.byVariant.set(key, laid);
|
|
2515
|
-
return laid;
|
|
2516
|
-
}
|
|
2517
|
-
|
|
2518
|
-
// src/features/text/verticalAlign.ts
|
|
2519
|
-
function verticalAlignOffset(align, boxHeight, textHeight) {
|
|
2520
|
-
if (align === void 0 || align === "top" || boxHeight === void 0) return 0;
|
|
2521
|
-
const slack = boxHeight - textHeight;
|
|
2522
|
-
return align === "center" ? slack / 2 : slack;
|
|
2523
2482
|
}
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
const
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2483
|
+
function populatePolylineWidths(pl, vertexWidths, fallbackWidth) {
|
|
2484
|
+
const ptCount = pl.points.length / 2;
|
|
2485
|
+
if (ptCount === 0) return;
|
|
2486
|
+
const aA = pl.anchorA ?? new Uint32Array(ptCount);
|
|
2487
|
+
const aB = pl.anchorB ?? new Uint32Array(ptCount);
|
|
2488
|
+
const aT = pl.anchorT ?? new Float32Array(ptCount);
|
|
2489
|
+
const widths = new Float32Array(ptCount);
|
|
2490
|
+
const read = (i) => {
|
|
2491
|
+
const v = vertexWidths[i];
|
|
2492
|
+
return typeof v === "number" && isFinite(v) && v > 0 ? v : fallbackWidth;
|
|
2493
|
+
};
|
|
2494
|
+
for (let i = 0; i < ptCount; i++) {
|
|
2495
|
+
const a = aA[i], b = aB[i], t = aT[i];
|
|
2496
|
+
if (a === b) {
|
|
2497
|
+
widths[i] = read(a);
|
|
2538
2498
|
} else {
|
|
2539
|
-
|
|
2499
|
+
const wa = read(a);
|
|
2500
|
+
const wb = read(b);
|
|
2501
|
+
widths[i] = wa + (wb - wa) * t;
|
|
2540
2502
|
}
|
|
2541
2503
|
}
|
|
2542
|
-
|
|
2504
|
+
pl.widths = widths;
|
|
2543
2505
|
}
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2506
|
+
var ROUND_CAP_STEP_RAD = 10 * Math.PI / 180;
|
|
2507
|
+
function emitCap(seg, leftIdx, rightIdx, atEnd, half, cap, verts, idx, anchorA, anchorB, anchorT, endA, endB, endT) {
|
|
2508
|
+
const sign = atEnd ? 1 : -1;
|
|
2509
|
+
const dx = (seg.bx - seg.ax) / seg.len * sign;
|
|
2510
|
+
const dy = (seg.by - seg.ay) / seg.len * sign;
|
|
2511
|
+
const cx = atEnd ? seg.bx : seg.ax;
|
|
2512
|
+
const cy = atEnd ? seg.by : seg.ay;
|
|
2513
|
+
const nx = seg.nxUnit * half;
|
|
2514
|
+
const ny = seg.nyUnit * half;
|
|
2515
|
+
if (cap === "square") {
|
|
2516
|
+
const ox = cx + dx * half;
|
|
2517
|
+
const oy = cy + dy * half;
|
|
2518
|
+
const lOut = verts.length / 2;
|
|
2519
|
+
verts.push(ox + nx, oy + ny);
|
|
2520
|
+
anchorA.push(endA);
|
|
2521
|
+
anchorB.push(endB);
|
|
2522
|
+
anchorT.push(endT);
|
|
2523
|
+
const rOut = verts.length / 2;
|
|
2524
|
+
verts.push(ox - nx, oy - ny);
|
|
2525
|
+
anchorA.push(endA);
|
|
2526
|
+
anchorB.push(endB);
|
|
2527
|
+
anchorT.push(endT);
|
|
2528
|
+
if (atEnd) {
|
|
2529
|
+
idx.push(leftIdx, lOut, rOut, leftIdx, rOut, rightIdx);
|
|
2530
|
+
} else {
|
|
2531
|
+
idx.push(leftIdx, rOut, lOut, leftIdx, rightIdx, rOut);
|
|
2532
|
+
}
|
|
2533
|
+
return;
|
|
2551
2534
|
}
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
lastQuadCtrl: null
|
|
2576
|
-
};
|
|
2577
|
-
for (const { cmd, args } of tokens) {
|
|
2578
|
-
const upper = cmd.toUpperCase();
|
|
2579
|
-
const relative = cmd !== upper;
|
|
2580
|
-
const argCount = ARG_COUNTS[upper];
|
|
2581
|
-
if (argCount === void 0) {
|
|
2582
|
-
onWarn?.(`unknown path command: ${cmd}`);
|
|
2583
|
-
continue;
|
|
2535
|
+
if (cap === "round") {
|
|
2536
|
+
const pivotIdx = verts.length / 2;
|
|
2537
|
+
verts.push(cx, cy);
|
|
2538
|
+
anchorA.push(endA);
|
|
2539
|
+
anchorB.push(endB);
|
|
2540
|
+
anchorT.push(endT);
|
|
2541
|
+
const startAngle = Math.atan2(ny, nx);
|
|
2542
|
+
const sweep = atEnd ? -Math.PI : Math.PI;
|
|
2543
|
+
const steps = Math.max(1, Math.ceil(Math.abs(sweep) / ROUND_CAP_STEP_RAD));
|
|
2544
|
+
const stepAngle = sweep / steps;
|
|
2545
|
+
let prevIdx = leftIdx;
|
|
2546
|
+
for (let i = 1; i < steps; i++) {
|
|
2547
|
+
const ang = startAngle + i * stepAngle;
|
|
2548
|
+
const fx = cx + Math.cos(ang) * half;
|
|
2549
|
+
const fy = cy + Math.sin(ang) * half;
|
|
2550
|
+
const newIdx = verts.length / 2;
|
|
2551
|
+
verts.push(fx, fy);
|
|
2552
|
+
anchorA.push(endA);
|
|
2553
|
+
anchorB.push(endB);
|
|
2554
|
+
anchorT.push(endT);
|
|
2555
|
+
if (atEnd) idx.push(prevIdx, newIdx, pivotIdx);
|
|
2556
|
+
else idx.push(prevIdx, pivotIdx, newIdx);
|
|
2557
|
+
prevIdx = newIdx;
|
|
2584
2558
|
}
|
|
2585
|
-
if (
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2559
|
+
if (atEnd) idx.push(prevIdx, rightIdx, pivotIdx);
|
|
2560
|
+
else idx.push(prevIdx, pivotIdx, rightIdx);
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
function splitForDash(pl, dash) {
|
|
2564
|
+
const out = [];
|
|
2565
|
+
const plA = pl.anchorA ?? new Uint32Array(pl.points.length / 2);
|
|
2566
|
+
const plB = pl.anchorB ?? new Uint32Array(pl.points.length / 2);
|
|
2567
|
+
const plT = pl.anchorT ?? new Float32Array(pl.points.length / 2);
|
|
2568
|
+
let dashIdx = 0;
|
|
2569
|
+
let dashRemaining = dash[0];
|
|
2570
|
+
let onPhase = true;
|
|
2571
|
+
let curPts = onPhase ? [pl.points[0], pl.points[1]] : null;
|
|
2572
|
+
let curA = onPhase ? [plA[0]] : null;
|
|
2573
|
+
let curB = onPhase ? [plB[0]] : null;
|
|
2574
|
+
let curT = onPhase ? [plT[0]] : null;
|
|
2575
|
+
const flushAndAdvance = () => {
|
|
2576
|
+
if (curPts && curPts.length >= 4) {
|
|
2577
|
+
out.push({
|
|
2578
|
+
points: curPts,
|
|
2579
|
+
closed: false,
|
|
2580
|
+
anchorA: new Uint32Array(curA),
|
|
2581
|
+
anchorB: new Uint32Array(curB),
|
|
2582
|
+
anchorT: new Float32Array(curT)
|
|
2583
|
+
});
|
|
2592
2584
|
}
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2585
|
+
curPts = null;
|
|
2586
|
+
curA = null;
|
|
2587
|
+
curB = null;
|
|
2588
|
+
curT = null;
|
|
2589
|
+
dashIdx = (dashIdx + 1) % dash.length;
|
|
2590
|
+
dashRemaining = dash[dashIdx];
|
|
2591
|
+
onPhase = !onPhase;
|
|
2592
|
+
};
|
|
2593
|
+
let prevX = pl.points[0], prevY = pl.points[1];
|
|
2594
|
+
const ptCount = pl.points.length / 2;
|
|
2595
|
+
const segCount = pl.closed ? ptCount : ptCount - 1;
|
|
2596
|
+
for (let i = 0; i < segCount; i++) {
|
|
2597
|
+
const nextIdx = (i + 1) % ptCount;
|
|
2598
|
+
const cx = pl.points[nextIdx * 2], cy = pl.points[nextIdx * 2 + 1];
|
|
2599
|
+
const startA = plA[i], startB = plB[i], startT = plT[i];
|
|
2600
|
+
const endA = plA[nextIdx], endB = plB[nextIdx], endT = plT[nextIdx];
|
|
2601
|
+
const segFullDx = cx - prevX, segFullDy = cy - prevY;
|
|
2602
|
+
const segFullLen = Math.hypot(segFullDx, segFullDy);
|
|
2603
|
+
let segDx = segFullDx, segDy = segFullDy;
|
|
2604
|
+
let segLen = segFullLen;
|
|
2605
|
+
let traveled = 0;
|
|
2606
|
+
while (segLen > 1e-9) {
|
|
2607
|
+
if (segLen <= dashRemaining) {
|
|
2608
|
+
if (onPhase && curPts) {
|
|
2609
|
+
curPts.push(cx, cy);
|
|
2610
|
+
curA.push(endA);
|
|
2611
|
+
curB.push(endB);
|
|
2612
|
+
curT.push(endT);
|
|
2613
|
+
}
|
|
2614
|
+
dashRemaining -= segLen;
|
|
2615
|
+
prevX = cx;
|
|
2616
|
+
prevY = cy;
|
|
2617
|
+
traveled = segFullLen;
|
|
2618
|
+
segLen = 0;
|
|
2619
|
+
if (dashRemaining <= 1e-9) {
|
|
2620
|
+
flushAndAdvance();
|
|
2621
|
+
if (onPhase) {
|
|
2622
|
+
curPts = [prevX, prevY];
|
|
2623
|
+
curA = [endA];
|
|
2624
|
+
curB = [endB];
|
|
2625
|
+
curT = [endT];
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
} else {
|
|
2629
|
+
const tConsume = dashRemaining / segLen;
|
|
2630
|
+
const ix = prevX + segDx * tConsume;
|
|
2631
|
+
const iy = prevY + segDy * tConsume;
|
|
2632
|
+
traveled += dashRemaining;
|
|
2633
|
+
const frac = traveled / segFullLen;
|
|
2634
|
+
let mA, mB, mT;
|
|
2635
|
+
if (startA === endA && startB === endB) {
|
|
2636
|
+
mA = startA;
|
|
2637
|
+
mB = startB;
|
|
2638
|
+
mT = startT + (endT - startT) * frac;
|
|
2639
|
+
} else {
|
|
2640
|
+
if (frac < 0.5) {
|
|
2641
|
+
mA = startA;
|
|
2642
|
+
mB = startB;
|
|
2643
|
+
mT = startT;
|
|
2644
|
+
} else {
|
|
2645
|
+
mA = endA;
|
|
2646
|
+
mB = endB;
|
|
2647
|
+
mT = endT;
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
if (onPhase && curPts) {
|
|
2651
|
+
curPts.push(ix, iy);
|
|
2652
|
+
curA.push(mA);
|
|
2653
|
+
curB.push(mB);
|
|
2654
|
+
curT.push(mT);
|
|
2655
|
+
}
|
|
2656
|
+
prevX = ix;
|
|
2657
|
+
prevY = iy;
|
|
2658
|
+
segDx = cx - prevX;
|
|
2659
|
+
segDy = cy - prevY;
|
|
2660
|
+
segLen = Math.hypot(segDx, segDy);
|
|
2661
|
+
flushAndAdvance();
|
|
2662
|
+
if (onPhase) {
|
|
2663
|
+
curPts = [prevX, prevY];
|
|
2664
|
+
curA = [mA];
|
|
2665
|
+
curB = [mB];
|
|
2666
|
+
curT = [mT];
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2598
2669
|
}
|
|
2599
2670
|
}
|
|
2600
|
-
|
|
2671
|
+
if (curPts && curPts.length >= 4) {
|
|
2672
|
+
out.push({
|
|
2673
|
+
points: curPts,
|
|
2674
|
+
closed: false,
|
|
2675
|
+
anchorA: new Uint32Array(curA),
|
|
2676
|
+
anchorB: new Uint32Array(curB),
|
|
2677
|
+
anchorT: new Float32Array(curT)
|
|
2678
|
+
});
|
|
2679
|
+
}
|
|
2680
|
+
return out;
|
|
2601
2681
|
}
|
|
2602
|
-
function
|
|
2603
|
-
const
|
|
2604
|
-
const
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
st.lastCubicCtrl = { x: x2, y: y2 };
|
|
2652
|
-
st.lastQuadCtrl = null;
|
|
2653
|
-
return;
|
|
2654
|
-
}
|
|
2655
|
-
case "S": {
|
|
2656
|
-
const ref = st.lastCubicCtrl ?? { x: st.x, y: st.y };
|
|
2657
|
-
const x1 = 2 * st.x - ref.x;
|
|
2658
|
-
const y1 = 2 * st.y - ref.y;
|
|
2659
|
-
const x2 = a[0] + rx, y2 = a[1] + ry;
|
|
2660
|
-
const x = a[2] + rx, y = a[3] + ry;
|
|
2661
|
-
b.curveTo(x1, y1, x2, y2, x, y);
|
|
2662
|
-
st.x = x;
|
|
2663
|
-
st.y = y;
|
|
2664
|
-
st.lastCubicCtrl = { x: x2, y: y2 };
|
|
2665
|
-
st.lastQuadCtrl = null;
|
|
2666
|
-
return;
|
|
2667
|
-
}
|
|
2668
|
-
case "Q": {
|
|
2669
|
-
const x1 = a[0] + rx, y1 = a[1] + ry;
|
|
2670
|
-
const x = a[2] + rx, y = a[3] + ry;
|
|
2671
|
-
b.quadTo(x1, y1, x, y);
|
|
2672
|
-
st.x = x;
|
|
2673
|
-
st.y = y;
|
|
2674
|
-
st.lastQuadCtrl = { x: x1, y: y1 };
|
|
2675
|
-
st.lastCubicCtrl = null;
|
|
2676
|
-
return;
|
|
2677
|
-
}
|
|
2678
|
-
case "T": {
|
|
2679
|
-
const ref = st.lastQuadCtrl ?? { x: st.x, y: st.y };
|
|
2680
|
-
const x1 = 2 * st.x - ref.x;
|
|
2681
|
-
const y1 = 2 * st.y - ref.y;
|
|
2682
|
-
const x = a[0] + rx, y = a[1] + ry;
|
|
2683
|
-
b.quadTo(x1, y1, x, y);
|
|
2684
|
-
st.x = x;
|
|
2685
|
-
st.y = y;
|
|
2686
|
-
st.lastQuadCtrl = { x: x1, y: y1 };
|
|
2687
|
-
st.lastCubicCtrl = null;
|
|
2682
|
+
function makeSeg(ax, ay, bx, by, halfA, halfB) {
|
|
2683
|
+
const dx = bx - ax, dy = by - ay;
|
|
2684
|
+
const len = Math.hypot(dx, dy);
|
|
2685
|
+
if (len === 0) return null;
|
|
2686
|
+
return {
|
|
2687
|
+
ax,
|
|
2688
|
+
ay,
|
|
2689
|
+
bx,
|
|
2690
|
+
by,
|
|
2691
|
+
nxUnit: -dy / len,
|
|
2692
|
+
nyUnit: dx / len,
|
|
2693
|
+
halfA,
|
|
2694
|
+
halfB,
|
|
2695
|
+
len
|
|
2696
|
+
};
|
|
2697
|
+
}
|
|
2698
|
+
var DEFAULT_MITER_LIMIT = 10;
|
|
2699
|
+
var DEFAULT_VARYING_WIDTH_JOIN_THRESHOLD = 1.5;
|
|
2700
|
+
function emitJoin(segs, segBaseIdx, j, join, miterLimit, varyingThreshold, verts, idx, anchorA, anchorB, anchorT, segSrcIdx, plA, plB, plT) {
|
|
2701
|
+
const a = segs[j];
|
|
2702
|
+
const b = segs[(j + 1) % segs.length];
|
|
2703
|
+
const aBase = segBaseIdx[j];
|
|
2704
|
+
const bBase = segBaseIdx[(j + 1) % segs.length];
|
|
2705
|
+
const adx = a.bx - a.ax, ady = a.by - a.ay;
|
|
2706
|
+
const bdx = b.bx - b.ax, bdy = b.by - b.ay;
|
|
2707
|
+
const cross = adx * bdy - ady * bdx;
|
|
2708
|
+
if (cross === 0) return;
|
|
2709
|
+
const onPositive = cross > 0;
|
|
2710
|
+
const aOuterEnd = onPositive ? aBase + 3 : aBase + 2;
|
|
2711
|
+
const bOuterStart = onPositive ? bBase + 1 : bBase + 0;
|
|
2712
|
+
const nextSeg = (j + 1) % segs.length;
|
|
2713
|
+
const cornerSrc = segSrcIdx[nextSeg];
|
|
2714
|
+
const cornerA = plA[cornerSrc];
|
|
2715
|
+
const cornerB = plB[cornerSrc];
|
|
2716
|
+
const cornerT = plT[cornerSrc];
|
|
2717
|
+
const halfAtCorner = a.halfB;
|
|
2718
|
+
const taperA = Math.max(a.halfA, a.halfB) / Math.max(1e-9, Math.min(a.halfA, a.halfB));
|
|
2719
|
+
const taperB = Math.max(b.halfA, b.halfB) / Math.max(1e-9, Math.min(b.halfA, b.halfB));
|
|
2720
|
+
const forceBevelForVarying = taperA > varyingThreshold || taperB > varyingThreshold;
|
|
2721
|
+
if (join === "bevel" || forceBevelForVarying) {
|
|
2722
|
+
emitBevel(a, aOuterEnd, bOuterStart, onPositive, verts, idx, anchorA, anchorB, anchorT, cornerA, cornerB, cornerT);
|
|
2723
|
+
return;
|
|
2724
|
+
}
|
|
2725
|
+
if (join === "miter") {
|
|
2726
|
+
const aOX = verts[aOuterEnd * 2], aOY = verts[aOuterEnd * 2 + 1];
|
|
2727
|
+
const bOX = verts[bOuterStart * 2], bOY = verts[bOuterStart * 2 + 1];
|
|
2728
|
+
const apex = lineLineIntersect(aOX, aOY, adx, ady, bOX, bOY, -bdx, -bdy);
|
|
2729
|
+
if (!apex) {
|
|
2730
|
+
emitBevel(a, aOuterEnd, bOuterStart, onPositive, verts, idx, anchorA, anchorB, anchorT, cornerA, cornerB, cornerT);
|
|
2688
2731
|
return;
|
|
2689
2732
|
}
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
const xRot = a[2];
|
|
2694
|
-
const largeArc = a[3] !== 0;
|
|
2695
|
-
const sweep = a[4] !== 0;
|
|
2696
|
-
const ex = a[5] + rx;
|
|
2697
|
-
const ey = a[6] + ry;
|
|
2698
|
-
arcToCubics(st.x, st.y, ex, ey, rxA, ryA, xRot, largeArc, sweep, b);
|
|
2699
|
-
st.x = ex;
|
|
2700
|
-
st.y = ey;
|
|
2701
|
-
st.lastCubicCtrl = null;
|
|
2702
|
-
st.lastQuadCtrl = null;
|
|
2733
|
+
const miterLen = Math.hypot(apex[0] - a.bx, apex[1] - a.by);
|
|
2734
|
+
if (miterLen > miterLimit * halfAtCorner) {
|
|
2735
|
+
emitBevel(a, aOuterEnd, bOuterStart, onPositive, verts, idx, anchorA, anchorB, anchorT, cornerA, cornerB, cornerT);
|
|
2703
2736
|
return;
|
|
2704
2737
|
}
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
let rxFixed = rx;
|
|
2728
|
-
let ryFixed = ry;
|
|
2729
|
-
if (lambda > 1) {
|
|
2730
|
-
const sqrtLambda = Math.sqrt(lambda);
|
|
2731
|
-
rxFixed = sqrtLambda * rx;
|
|
2732
|
-
ryFixed = sqrtLambda * ry;
|
|
2733
|
-
rxs = rxFixed * rxFixed;
|
|
2734
|
-
rys = ryFixed * ryFixed;
|
|
2735
|
-
}
|
|
2736
|
-
const sign = largeArc === sweep ? -1 : 1;
|
|
2737
|
-
const num = rxs * rys - rxs * y1ps - rys * x1ps;
|
|
2738
|
-
const den = rxs * y1ps + rys * x1ps;
|
|
2739
|
-
const factor = sign * Math.sqrt(Math.max(0, num / den));
|
|
2740
|
-
const cxp = factor * (rxFixed * y1p) / ryFixed;
|
|
2741
|
-
const cyp = factor * -(ryFixed * x1p) / rxFixed;
|
|
2742
|
-
const cx = cosPhi * cxp - sinPhi * cyp + (x1 + x2) / 2;
|
|
2743
|
-
const cy = sinPhi * cxp + cosPhi * cyp + (y1 + y2) / 2;
|
|
2744
|
-
const angle = (ux, uy, vx, vy) => {
|
|
2745
|
-
const dot = ux * vx + uy * vy;
|
|
2746
|
-
const len = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
|
|
2747
|
-
let a = Math.acos(Math.max(-1, Math.min(1, dot / len)));
|
|
2748
|
-
if (ux * vy - uy * vx < 0) a = -a;
|
|
2749
|
-
return a;
|
|
2750
|
-
};
|
|
2751
|
-
const theta1 = angle(1, 0, (x1p - cxp) / rxFixed, (y1p - cyp) / ryFixed);
|
|
2752
|
-
let dTheta = angle(
|
|
2753
|
-
(x1p - cxp) / rxFixed,
|
|
2754
|
-
(y1p - cyp) / ryFixed,
|
|
2755
|
-
(-x1p - cxp) / rxFixed,
|
|
2756
|
-
(-y1p - cyp) / ryFixed
|
|
2757
|
-
);
|
|
2758
|
-
if (!sweep && dTheta > 0) dTheta -= 2 * Math.PI;
|
|
2759
|
-
else if (sweep && dTheta < 0) dTheta += 2 * Math.PI;
|
|
2760
|
-
const segCount = Math.max(1, Math.ceil(Math.abs(dTheta) / (Math.PI / 2)));
|
|
2761
|
-
const segAngle = dTheta / segCount;
|
|
2762
|
-
const t = 4 / 3 * Math.tan(segAngle / 4);
|
|
2763
|
-
let prevX = x1;
|
|
2764
|
-
let prevY = y1;
|
|
2765
|
-
let prevDx = -Math.sin(theta1) * rxFixed;
|
|
2766
|
-
let prevDy = Math.cos(theta1) * ryFixed;
|
|
2767
|
-
for (let i = 1; i <= segCount; i++) {
|
|
2768
|
-
const theta = theta1 + i * segAngle;
|
|
2769
|
-
const cosT = Math.cos(theta);
|
|
2770
|
-
const sinT = Math.sin(theta);
|
|
2771
|
-
const px = cosPhi * (cosT * rxFixed) - sinPhi * (sinT * ryFixed) + cx;
|
|
2772
|
-
const py = sinPhi * (cosT * rxFixed) + cosPhi * (sinT * ryFixed) + cy;
|
|
2773
|
-
const dxNew = -sinT * rxFixed;
|
|
2774
|
-
const dyNew = cosT * ryFixed;
|
|
2775
|
-
const c1x = prevX + cosPhi * (t * prevDx) - sinPhi * (t * prevDy);
|
|
2776
|
-
const c1y = prevY + sinPhi * (t * prevDx) + cosPhi * (t * prevDy);
|
|
2777
|
-
const c2x = px - cosPhi * (t * dxNew) + sinPhi * (t * dyNew);
|
|
2778
|
-
const c2y = py - sinPhi * (t * dxNew) - cosPhi * (t * dyNew);
|
|
2779
|
-
b.curveTo(c1x, c1y, c2x, c2y, px, py);
|
|
2780
|
-
prevX = px;
|
|
2781
|
-
prevY = py;
|
|
2782
|
-
prevDx = dxNew;
|
|
2783
|
-
prevDy = dyNew;
|
|
2738
|
+
const apexIdx = verts.length / 2;
|
|
2739
|
+
verts.push(apex[0], apex[1]);
|
|
2740
|
+
anchorA.push(cornerA);
|
|
2741
|
+
anchorB.push(cornerB);
|
|
2742
|
+
anchorT.push(cornerT);
|
|
2743
|
+
const jIdx = verts.length / 2;
|
|
2744
|
+
verts.push(a.bx, a.by);
|
|
2745
|
+
anchorA.push(cornerA);
|
|
2746
|
+
anchorB.push(cornerB);
|
|
2747
|
+
anchorT.push(cornerT);
|
|
2748
|
+
if (onPositive) {
|
|
2749
|
+
idx.push(aOuterEnd, apexIdx, bOuterStart);
|
|
2750
|
+
idx.push(aOuterEnd, bOuterStart, jIdx);
|
|
2751
|
+
} else {
|
|
2752
|
+
idx.push(aOuterEnd, bOuterStart, apexIdx);
|
|
2753
|
+
idx.push(aOuterEnd, jIdx, bOuterStart);
|
|
2754
|
+
}
|
|
2755
|
+
return;
|
|
2756
|
+
}
|
|
2757
|
+
if (join === "round") {
|
|
2758
|
+
emitRoundJoin(a, aOuterEnd, bOuterStart, onPositive, verts, idx, anchorA, anchorB, anchorT, cornerA, cornerB, cornerT);
|
|
2759
|
+
return;
|
|
2784
2760
|
}
|
|
2785
2761
|
}
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
const
|
|
2796
|
-
|
|
2797
|
-
|
|
2762
|
+
var ROUND_STEP_RAD = 10 * Math.PI / 180;
|
|
2763
|
+
function emitRoundJoin(a, aOuterEnd, bOuterStart, onPositive, verts, idx, anchorA, anchorB, anchorT, cornerA, cornerB, cornerT) {
|
|
2764
|
+
const cx = a.bx, cy = a.by;
|
|
2765
|
+
const pivotIdx = verts.length / 2;
|
|
2766
|
+
verts.push(cx, cy);
|
|
2767
|
+
anchorA.push(cornerA);
|
|
2768
|
+
anchorB.push(cornerB);
|
|
2769
|
+
anchorT.push(cornerT);
|
|
2770
|
+
const startX = verts[aOuterEnd * 2] - cx;
|
|
2771
|
+
const startY = verts[aOuterEnd * 2 + 1] - cy;
|
|
2772
|
+
const endX = verts[bOuterStart * 2] - cx;
|
|
2773
|
+
const endY = verts[bOuterStart * 2 + 1] - cy;
|
|
2774
|
+
const r = Math.hypot(startX, startY);
|
|
2775
|
+
const startAngle = Math.atan2(startY, startX);
|
|
2776
|
+
const endAngle = Math.atan2(endY, endX);
|
|
2777
|
+
let sweep = endAngle - startAngle;
|
|
2778
|
+
if (onPositive && sweep < 0) sweep += 2 * Math.PI;
|
|
2779
|
+
else if (!onPositive && sweep > 0) sweep -= 2 * Math.PI;
|
|
2780
|
+
const steps = Math.max(1, Math.ceil(Math.abs(sweep) / ROUND_STEP_RAD));
|
|
2781
|
+
const stepAngle = sweep / steps;
|
|
2782
|
+
let prevIdx = aOuterEnd;
|
|
2783
|
+
for (let i = 1; i < steps; i++) {
|
|
2784
|
+
const ang = startAngle + i * stepAngle;
|
|
2785
|
+
const fx = cx + Math.cos(ang) * r;
|
|
2786
|
+
const fy = cy + Math.sin(ang) * r;
|
|
2787
|
+
const newIdx = verts.length / 2;
|
|
2788
|
+
verts.push(fx, fy);
|
|
2789
|
+
anchorA.push(cornerA);
|
|
2790
|
+
anchorB.push(cornerB);
|
|
2791
|
+
anchorT.push(cornerT);
|
|
2792
|
+
if (onPositive) idx.push(prevIdx, pivotIdx, newIdx);
|
|
2793
|
+
else idx.push(prevIdx, newIdx, pivotIdx);
|
|
2794
|
+
prevIdx = newIdx;
|
|
2795
|
+
}
|
|
2796
|
+
if (onPositive) idx.push(prevIdx, pivotIdx, bOuterStart);
|
|
2797
|
+
else idx.push(prevIdx, bOuterStart, pivotIdx);
|
|
2798
|
+
}
|
|
2799
|
+
function emitBevel(a, aOuterEnd, bOuterStart, onPositive, verts, idx, anchorA, anchorB, anchorT, cornerA, cornerB, cornerT) {
|
|
2800
|
+
const jIdx = verts.length / 2;
|
|
2801
|
+
verts.push(a.bx, a.by);
|
|
2802
|
+
anchorA.push(cornerA);
|
|
2803
|
+
anchorB.push(cornerB);
|
|
2804
|
+
anchorT.push(cornerT);
|
|
2805
|
+
if (onPositive) idx.push(aOuterEnd, jIdx, bOuterStart);
|
|
2806
|
+
else idx.push(aOuterEnd, bOuterStart, jIdx);
|
|
2807
|
+
}
|
|
2808
|
+
function lineLineIntersect(apx, apy, adx, ady, bpx, bpy, bdx, bdy) {
|
|
2809
|
+
const denom = adx * bdy - ady * bdx;
|
|
2810
|
+
if (Math.abs(denom) < 1e-9) return null;
|
|
2811
|
+
const t = ((bpx - apx) * bdy - (bpy - apy) * bdx) / denom;
|
|
2812
|
+
return [apx + t * adx, apy + t * ady];
|
|
2798
2813
|
}
|
|
2799
2814
|
|
|
2800
2815
|
// src/renderer/cache/outlineStrokeMeshCache.ts
|
|
@@ -2827,6 +2842,38 @@ function outlineStrokeMesh(glyphKey, d, emWidth, stroke) {
|
|
|
2827
2842
|
return mesh.indices.length === 0 ? null : mesh;
|
|
2828
2843
|
}
|
|
2829
2844
|
|
|
2845
|
+
// src/renderer/cache/strokeMeshCache.ts
|
|
2846
|
+
var STROKE_CONFIGS_PER_PATH = 8;
|
|
2847
|
+
var cache5 = /* @__PURE__ */ new WeakMap();
|
|
2848
|
+
function configKey(stroke, flattenTolerance) {
|
|
2849
|
+
return [
|
|
2850
|
+
stroke.width ?? 1,
|
|
2851
|
+
stroke.cap ?? "butt",
|
|
2852
|
+
stroke.join ?? "miter",
|
|
2853
|
+
stroke.miterLimit ?? "",
|
|
2854
|
+
stroke.align ?? "center",
|
|
2855
|
+
(stroke.dash ?? []).join(","),
|
|
2856
|
+
stroke.varyingWidthJoinThreshold ?? "",
|
|
2857
|
+
flattenTolerance ?? ""
|
|
2858
|
+
].join("|");
|
|
2859
|
+
}
|
|
2860
|
+
function strokeMesh(path, stroke, flattenTolerance) {
|
|
2861
|
+
let byConfig = cache5.get(path);
|
|
2862
|
+
if (byConfig === void 0) {
|
|
2863
|
+
byConfig = /* @__PURE__ */ new Map();
|
|
2864
|
+
cache5.set(path, byConfig);
|
|
2865
|
+
}
|
|
2866
|
+
const key = configKey(stroke, flattenTolerance);
|
|
2867
|
+
const entry = byConfig.get(key);
|
|
2868
|
+
if (entry !== void 0 && entry.vertexWidths === stroke.vertexWidths) {
|
|
2869
|
+
return entry.mesh;
|
|
2870
|
+
}
|
|
2871
|
+
const mesh = tessellateStroke(path, stroke, { flattenTolerance });
|
|
2872
|
+
if (entry === void 0 && byConfig.size >= STROKE_CONFIGS_PER_PATH) byConfig.clear();
|
|
2873
|
+
byConfig.set(key, { mesh, vertexWidths: stroke.vertexWidths });
|
|
2874
|
+
return mesh;
|
|
2875
|
+
}
|
|
2876
|
+
|
|
2830
2877
|
// src/renderer/solidBatch.ts
|
|
2831
2878
|
var MAX_VERTICES_PER_BATCH = 32768;
|
|
2832
2879
|
var FLOATS_PER_VERTEX = 6;
|
|
@@ -3231,44 +3278,40 @@ function drawGroup(ctx, cmd) {
|
|
|
3231
3278
|
"weasel: clip nesting depth exceeded (max 7). You can't nest more than 7 levels of clipped containers in a single draw tree. Flatten the hierarchy or compose poses outside the scene graph."
|
|
3232
3279
|
);
|
|
3233
3280
|
}
|
|
3234
|
-
flushSolids(ctx);
|
|
3235
3281
|
pushClip(ctx, cmd.clip, newDepth);
|
|
3236
3282
|
ctx.clipDepth = newDepth;
|
|
3237
3283
|
}
|
|
3238
3284
|
for (const child of cmd.children) dispatch(ctx, child);
|
|
3239
3285
|
if (cmd.clip) {
|
|
3240
|
-
flushSolids(ctx);
|
|
3241
3286
|
popClip(ctx, cmd.clip, ctx.clipDepth - 1);
|
|
3242
3287
|
ctx.clipDepth -= 1;
|
|
3243
3288
|
}
|
|
3244
3289
|
ctx.state.pop();
|
|
3245
3290
|
}
|
|
3246
3291
|
function drawPath(ctx, cmd) {
|
|
3247
|
-
if (
|
|
3248
|
-
if (cmd.fill) {
|
|
3249
|
-
const batchablePaint = (cmd.fill.fill === void 0 || cmd.fill.fill === "solid") && (!cmd.vertexColors || cmd.vertexColors.length === 0);
|
|
3250
|
-
const solid = cmd.fill;
|
|
3251
|
-
if (batchablePaint && cmd.path.kind === "rect") {
|
|
3252
|
-
pushRect(ctx, cmd.path, solid);
|
|
3253
|
-
} else {
|
|
3254
|
-
const mesh = fillMesh(ctx, cmd.path);
|
|
3255
|
-
if (batchablePaint && canBatchMesh(mesh)) {
|
|
3256
|
-
pushMesh(ctx, mesh, solid);
|
|
3257
|
-
} else {
|
|
3258
|
-
flushSolids(ctx);
|
|
3259
|
-
const handle = meshHandle(ctx, mesh);
|
|
3260
|
-
if (cmd.vertexColors && cmd.vertexColors.length > 0 && (cmd.fill.fill === void 0 || cmd.fill.fill === "solid")) {
|
|
3261
|
-
drawPathFillVColor(ctx, cmd, solid, handle);
|
|
3262
|
-
} else if (handle.requiresStencil) {
|
|
3263
|
-
drawPathFillStencil(ctx, cmd.fill, handle);
|
|
3264
|
-
} else {
|
|
3265
|
-
drawPathFillByKind(ctx, cmd.fill, handle);
|
|
3266
|
-
}
|
|
3267
|
-
}
|
|
3268
|
-
}
|
|
3269
|
-
}
|
|
3292
|
+
if (cmd.fill) drawPathFill(ctx, cmd);
|
|
3270
3293
|
if (cmd.stroke) drawPathStroke(ctx, cmd);
|
|
3271
3294
|
}
|
|
3295
|
+
function drawPathFill(ctx, cmd) {
|
|
3296
|
+
const fill = cmd.fill;
|
|
3297
|
+
const isSolid = fill.fill === void 0 || fill.fill === "solid";
|
|
3298
|
+
const hasVColors = !!(cmd.vertexColors && cmd.vertexColors.length > 0);
|
|
3299
|
+
const solid = fill;
|
|
3300
|
+
if (isSolid && !hasVColors && cmd.path.kind === "rect") {
|
|
3301
|
+
pushRect(ctx, cmd.path, solid);
|
|
3302
|
+
return;
|
|
3303
|
+
}
|
|
3304
|
+
const mesh = fillMesh(ctx, cmd.path);
|
|
3305
|
+
if (tryStageSolid(ctx, mesh, isSolid && !hasVColors ? solid : void 0)) return;
|
|
3306
|
+
const handle = meshHandle(ctx, mesh);
|
|
3307
|
+
if (isSolid && hasVColors) {
|
|
3308
|
+
drawPathFillVColor(ctx, cmd, solid, handle);
|
|
3309
|
+
} else if (handle.requiresStencil) {
|
|
3310
|
+
drawPathFillStencil(ctx, fill, handle);
|
|
3311
|
+
} else {
|
|
3312
|
+
drawPathFillByKind(ctx, fill, handle);
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3272
3315
|
function isIdentityColorMatrix(cm) {
|
|
3273
3316
|
return cm === IDENTITY_COLOR_MATRIX || sameValues(IDENTITY_COLOR_MATRIX, cm);
|
|
3274
3317
|
}
|
|
@@ -3321,6 +3364,14 @@ function pushMesh(ctx, mesh, paint) {
|
|
|
3321
3364
|
const [r, g, b, a] = stagedColor(ctx, staged, paint);
|
|
3322
3365
|
ctx.solidBatch.pushMesh(mesh, ctx.state.transform, r, g, b, a);
|
|
3323
3366
|
}
|
|
3367
|
+
function tryStageSolid(ctx, mesh, paint) {
|
|
3368
|
+
if (paint !== void 0 && canBatchMesh(mesh)) {
|
|
3369
|
+
pushMesh(ctx, mesh, paint);
|
|
3370
|
+
return true;
|
|
3371
|
+
}
|
|
3372
|
+
flushSolids(ctx);
|
|
3373
|
+
return false;
|
|
3374
|
+
}
|
|
3324
3375
|
function flushSolids(ctx) {
|
|
3325
3376
|
const batch = ctx.solidBatch;
|
|
3326
3377
|
const staged = ctx.solidState;
|
|
@@ -3524,6 +3575,7 @@ function rasterizePathToStencil(ctx, path) {
|
|
|
3524
3575
|
gl.bindVertexArray(null);
|
|
3525
3576
|
}
|
|
3526
3577
|
function pushClip(ctx, path, newDepth) {
|
|
3578
|
+
flushSolids(ctx);
|
|
3527
3579
|
const gl = ctx.gl;
|
|
3528
3580
|
const ancestors = ancestorMask(newDepth - 1);
|
|
3529
3581
|
const newBit = 1 << newDepth;
|
|
@@ -3538,6 +3590,7 @@ function pushClip(ctx, path, newDepth) {
|
|
|
3538
3590
|
gl.stencilMask(255);
|
|
3539
3591
|
}
|
|
3540
3592
|
function popClip(ctx, path, oldDepth) {
|
|
3593
|
+
flushSolids(ctx);
|
|
3541
3594
|
const gl = ctx.gl;
|
|
3542
3595
|
gl.enable(gl.STENCIL_TEST);
|
|
3543
3596
|
const oldBit = 1 << oldDepth + 1;
|
|
@@ -3594,17 +3647,13 @@ function drawPathStroke(ctx, cmd) {
|
|
|
3594
3647
|
function drawPathStrokeUnclipped(ctx, cmd) {
|
|
3595
3648
|
const stroke = cmd.stroke;
|
|
3596
3649
|
const solid = stroke.paint;
|
|
3597
|
-
const mesh =
|
|
3650
|
+
const mesh = strokeMesh(cmd.path, stroke, ctx.flattenTolerance);
|
|
3598
3651
|
if (mesh.indices.length === 0) return;
|
|
3599
3652
|
const hasVColors = !!(stroke.vertexColors && stroke.vertexColors.length > 0);
|
|
3600
|
-
if (
|
|
3601
|
-
|
|
3602
|
-
return;
|
|
3603
|
-
}
|
|
3604
|
-
flushSolids(ctx);
|
|
3605
|
-
const handle = ctx.meshCache.uploadTransient(mesh);
|
|
3653
|
+
if (tryStageSolid(ctx, mesh, hasVColors ? void 0 : solid)) return;
|
|
3654
|
+
const handle = hasVColors ? ctx.meshCache.uploadTransient(mesh) : ctx.meshCache.uploadRecurring(mesh);
|
|
3606
3655
|
const gl = ctx.gl;
|
|
3607
|
-
if (
|
|
3656
|
+
if (hasVColors) {
|
|
3608
3657
|
const prog = ctx.pathFillVColor;
|
|
3609
3658
|
gl.useProgram(prog.handle);
|
|
3610
3659
|
gl.bindVertexArray(handle.vao);
|
|
@@ -3635,16 +3684,30 @@ function drawPathStrokeUnclipped(ctx, cmd) {
|
|
|
3635
3684
|
gl.drawElements(gl.TRIANGLES, handle.indexCount, gl.UNSIGNED_INT, 0);
|
|
3636
3685
|
gl.bindVertexArray(null);
|
|
3637
3686
|
}
|
|
3687
|
+
var DOUBLED_VERTEX_WIDTHS = /* @__PURE__ */ new WeakMap();
|
|
3688
|
+
function doubledVertexWidths(widths) {
|
|
3689
|
+
let doubled = DOUBLED_VERTEX_WIDTHS.get(widths);
|
|
3690
|
+
if (doubled === void 0) {
|
|
3691
|
+
doubled = widths.map((w) => w * 2);
|
|
3692
|
+
DOUBLED_VERTEX_WIDTHS.set(widths, doubled);
|
|
3693
|
+
}
|
|
3694
|
+
return doubled;
|
|
3695
|
+
}
|
|
3638
3696
|
function drawPathStrokeStenciled(ctx, cmd, align) {
|
|
3639
3697
|
const stroke = cmd.stroke;
|
|
3640
3698
|
const solid = stroke.paint;
|
|
3641
|
-
const widerStroke = {
|
|
3699
|
+
const widerStroke = {
|
|
3700
|
+
...stroke,
|
|
3701
|
+
width: (stroke.width ?? 1) * 2,
|
|
3702
|
+
...stroke.vertexWidths ? { vertexWidths: doubledVertexWidths(stroke.vertexWidths) } : {},
|
|
3703
|
+
align: "center"
|
|
3704
|
+
};
|
|
3705
|
+
const useVColor = !!(stroke.vertexColors && stroke.vertexColors.length > 0);
|
|
3642
3706
|
const fillHandle = fillMeshHandle(ctx, cmd.path);
|
|
3643
|
-
const ribbonMesh =
|
|
3707
|
+
const ribbonMesh = strokeMesh(cmd.path, widerStroke, ctx.flattenTolerance);
|
|
3644
3708
|
if (ribbonMesh.indices.length === 0) return;
|
|
3645
|
-
const ribbonHandle = ctx.meshCache.uploadTransient(ribbonMesh);
|
|
3709
|
+
const ribbonHandle = useVColor ? ctx.meshCache.uploadTransient(ribbonMesh) : ctx.meshCache.uploadRecurring(ribbonMesh);
|
|
3646
3710
|
const gl = ctx.gl;
|
|
3647
|
-
const useVColor = !!(stroke.vertexColors && stroke.vertexColors.length > 0);
|
|
3648
3711
|
const prog = useVColor ? ctx.pathFillVColor : ctx.pathFill;
|
|
3649
3712
|
gl.useProgram(prog.handle);
|
|
3650
3713
|
setProjAndModel(ctx, prog);
|
|
@@ -3680,11 +3743,11 @@ function drawPathStrokeStenciled(ctx, cmd, align) {
|
|
|
3680
3743
|
}
|
|
3681
3744
|
}
|
|
3682
3745
|
gl.drawElements(gl.TRIANGLES, ribbonHandle.indexCount, gl.UNSIGNED_INT, 0);
|
|
3683
|
-
if (colorVbo) gl.deleteBuffer(colorVbo);
|
|
3684
3746
|
gl.stencilMask(1);
|
|
3685
3747
|
gl.clear(gl.STENCIL_BUFFER_BIT);
|
|
3686
3748
|
gl.disable(gl.STENCIL_TEST);
|
|
3687
3749
|
gl.bindVertexArray(null);
|
|
3750
|
+
if (colorVbo) gl.deleteBuffer(colorVbo);
|
|
3688
3751
|
}
|
|
3689
3752
|
var OUTLINE_MIN_SCREEN_PX = 48;
|
|
3690
3753
|
function modelScale(m) {
|
|
@@ -3733,9 +3796,9 @@ function drawTextOutlineGroup(ctx, group, dx, dy) {
|
|
|
3733
3796
|
const mesh = outlineGroupMesh(group, dx, dy);
|
|
3734
3797
|
if (mesh) drawPathFillByKind(ctx, group.fill, ctx.meshCache.uploadTransient(mesh));
|
|
3735
3798
|
if (!group.stroke) return;
|
|
3736
|
-
const
|
|
3737
|
-
if (
|
|
3738
|
-
drawPathFillByKind(ctx, group.stroke.paint, ctx.meshCache.uploadTransient(
|
|
3799
|
+
const ribbon = outlineGroupStrokeMesh(group, dx, dy);
|
|
3800
|
+
if (ribbon) {
|
|
3801
|
+
drawPathFillByKind(ctx, group.stroke.paint, ctx.meshCache.uploadTransient(ribbon));
|
|
3739
3802
|
}
|
|
3740
3803
|
}
|
|
3741
3804
|
function outlineGroupMesh(group, dx, dy) {
|
|
@@ -3941,70 +4004,98 @@ function drawTextGroup(ctx, group, prog, dx, dy) {
|
|
|
3941
4004
|
gl.deleteBuffer(vbo);
|
|
3942
4005
|
gl.deleteBuffer(ibo);
|
|
3943
4006
|
}
|
|
4007
|
+
var IMAGE_QUAD_RING_SIZE = 64;
|
|
4008
|
+
var IMAGE_QUAD_RINGS = /* @__PURE__ */ new WeakMap();
|
|
4009
|
+
var IMAGE_QUAD_INDICES = new Uint32Array([0, 1, 2, 1, 3, 2]);
|
|
4010
|
+
var IMAGE_QUAD_STRIDE = 16;
|
|
4011
|
+
function imageQuadRing(gl, prog) {
|
|
4012
|
+
const existing = IMAGE_QUAD_RINGS.get(prog);
|
|
4013
|
+
if (existing) return existing;
|
|
4014
|
+
const aPosLoc = prog.attribute("a_position");
|
|
4015
|
+
const aUvLoc = prog.attribute("a_uv");
|
|
4016
|
+
const ibo = gl.createBuffer();
|
|
4017
|
+
if (!ibo) throw new Error("drawImage: createBuffer (IBO) returned null");
|
|
4018
|
+
const vaos = [];
|
|
4019
|
+
const vbos = [];
|
|
4020
|
+
for (let slot = 0; slot < IMAGE_QUAD_RING_SIZE; slot++) {
|
|
4021
|
+
const vao = gl.createVertexArray();
|
|
4022
|
+
const vbo = gl.createBuffer();
|
|
4023
|
+
if (!vao) throw new Error("drawImage: createVertexArray returned null");
|
|
4024
|
+
if (!vbo) throw new Error("drawImage: createBuffer returned null");
|
|
4025
|
+
gl.bindVertexArray(vao);
|
|
4026
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
|
|
4027
|
+
gl.bufferData(gl.ARRAY_BUFFER, 4 * IMAGE_QUAD_STRIDE, gl.DYNAMIC_DRAW);
|
|
4028
|
+
if (aPosLoc !== void 0) {
|
|
4029
|
+
gl.enableVertexAttribArray(aPosLoc);
|
|
4030
|
+
gl.vertexAttribPointer(aPosLoc, 2, gl.FLOAT, false, IMAGE_QUAD_STRIDE, 0);
|
|
4031
|
+
}
|
|
4032
|
+
if (aUvLoc !== void 0) {
|
|
4033
|
+
gl.enableVertexAttribArray(aUvLoc);
|
|
4034
|
+
gl.vertexAttribPointer(aUvLoc, 2, gl.FLOAT, false, IMAGE_QUAD_STRIDE, 8);
|
|
4035
|
+
}
|
|
4036
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
|
|
4037
|
+
if (slot === 0) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, IMAGE_QUAD_INDICES, gl.STATIC_DRAW);
|
|
4038
|
+
gl.bindVertexArray(null);
|
|
4039
|
+
vaos.push(vao);
|
|
4040
|
+
vbos.push(vbo);
|
|
4041
|
+
}
|
|
4042
|
+
const ring = { vaos, vbos, ibo, next: 0 };
|
|
4043
|
+
IMAGE_QUAD_RINGS.set(prog, ring);
|
|
4044
|
+
return ring;
|
|
4045
|
+
}
|
|
4046
|
+
function disposeImageQuads(gl, prog) {
|
|
4047
|
+
const ring = IMAGE_QUAD_RINGS.get(prog);
|
|
4048
|
+
if (!ring) return;
|
|
4049
|
+
for (const vao of ring.vaos) gl.deleteVertexArray(vao);
|
|
4050
|
+
for (const vbo of ring.vbos) gl.deleteBuffer(vbo);
|
|
4051
|
+
gl.deleteBuffer(ring.ibo);
|
|
4052
|
+
IMAGE_QUAD_RINGS.delete(prog);
|
|
4053
|
+
}
|
|
4054
|
+
var IMAGE_QUAD_VERTICES = new Float32Array(16);
|
|
3944
4055
|
function drawImage(ctx, cmd) {
|
|
3945
4056
|
ctx.imageCache.upload(cmd.image, cmd.image);
|
|
3946
4057
|
const gl = ctx.gl;
|
|
3947
|
-
|
|
4058
|
+
const prog = ctx.imageFill;
|
|
4059
|
+
gl.useProgram(prog.handle);
|
|
3948
4060
|
const x0 = cmd.x, y0 = cmd.y;
|
|
3949
4061
|
const x1 = cmd.x + cmd.w, y1 = cmd.y + cmd.h;
|
|
3950
|
-
const
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
const
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
gl.
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.DYNAMIC_DRAW);
|
|
3976
|
-
const stride = 16;
|
|
3977
|
-
const aPosLoc = ctx.imageFill.attribute("a_position");
|
|
3978
|
-
const aUvLoc = ctx.imageFill.attribute("a_uv");
|
|
3979
|
-
if (aPosLoc !== void 0) {
|
|
3980
|
-
gl.enableVertexAttribArray(aPosLoc);
|
|
3981
|
-
gl.vertexAttribPointer(aPosLoc, 2, gl.FLOAT, false, stride, 0);
|
|
3982
|
-
}
|
|
3983
|
-
if (aUvLoc !== void 0) {
|
|
3984
|
-
gl.enableVertexAttribArray(aUvLoc);
|
|
3985
|
-
gl.vertexAttribPointer(aUvLoc, 2, gl.FLOAT, false, stride, 8);
|
|
3986
|
-
}
|
|
3987
|
-
const ibo = gl.createBuffer();
|
|
3988
|
-
if (!ibo) throw new Error("drawImage: createBuffer (IBO) returned null");
|
|
3989
|
-
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
|
|
3990
|
-
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.DYNAMIC_DRAW);
|
|
3991
|
-
setProjAndModel(ctx, ctx.imageFill);
|
|
3992
|
-
setColorMatrixUniforms(ctx, ctx.imageFill);
|
|
4062
|
+
const v = IMAGE_QUAD_VERTICES;
|
|
4063
|
+
v[0] = x0;
|
|
4064
|
+
v[1] = y0;
|
|
4065
|
+
v[2] = 0;
|
|
4066
|
+
v[3] = 0;
|
|
4067
|
+
v[4] = x1;
|
|
4068
|
+
v[5] = y0;
|
|
4069
|
+
v[6] = 1;
|
|
4070
|
+
v[7] = 0;
|
|
4071
|
+
v[8] = x0;
|
|
4072
|
+
v[9] = y1;
|
|
4073
|
+
v[10] = 0;
|
|
4074
|
+
v[11] = 1;
|
|
4075
|
+
v[12] = x1;
|
|
4076
|
+
v[13] = y1;
|
|
4077
|
+
v[14] = 1;
|
|
4078
|
+
v[15] = 1;
|
|
4079
|
+
const ring = imageQuadRing(gl, prog);
|
|
4080
|
+
const slot = ring.next;
|
|
4081
|
+
ring.next = (slot + 1) % IMAGE_QUAD_RING_SIZE;
|
|
4082
|
+
gl.bindVertexArray(ring.vaos[slot]);
|
|
4083
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, ring.vbos[slot]);
|
|
4084
|
+
gl.bufferSubData(gl.ARRAY_BUFFER, 0, v);
|
|
4085
|
+
setProjAndModel(ctx, prog);
|
|
4086
|
+
setColorMatrixUniforms(ctx, prog);
|
|
3993
4087
|
ctx.imageCache.bind(cmd.image, 0);
|
|
3994
4088
|
gl.texParameteri(
|
|
3995
4089
|
gl.TEXTURE_2D,
|
|
3996
4090
|
gl.TEXTURE_MAG_FILTER,
|
|
3997
4091
|
cmd.sampling === "nearest" ? gl.NEAREST : gl.LINEAR
|
|
3998
4092
|
);
|
|
3999
|
-
gl.uniform1i(
|
|
4000
|
-
gl.uniform1f(
|
|
4001
|
-
gl.uniform1f(
|
|
4093
|
+
gl.uniform1i(prog.uniform("u_sampler"), 0);
|
|
4094
|
+
gl.uniform1f(prog.uniform("u_opacity"), cmd.opacity ?? 1);
|
|
4095
|
+
gl.uniform1f(prog.uniform("u_alpha"), ctx.state.alpha);
|
|
4002
4096
|
applyClipTest(ctx);
|
|
4003
4097
|
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, 0);
|
|
4004
4098
|
gl.bindVertexArray(null);
|
|
4005
|
-
gl.deleteVertexArray(vao);
|
|
4006
|
-
gl.deleteBuffer(vbo);
|
|
4007
|
-
gl.deleteBuffer(ibo);
|
|
4008
4099
|
}
|
|
4009
4100
|
|
|
4010
4101
|
// src/renderer/shaders/customPrelude.ts
|
|
@@ -4075,16 +4166,28 @@ function registerProgram(id, vert, frag) {
|
|
|
4075
4166
|
}
|
|
4076
4167
|
|
|
4077
4168
|
// src/renderer/WeaselRenderer.ts
|
|
4169
|
+
var UNIFORM_QUALIFIER = /^(?:lowp|mediump|highp|precise|invariant|centroid|flat|smooth|noperspective)$/;
|
|
4078
4170
|
function extractUniformNames(glsl) {
|
|
4079
|
-
const
|
|
4080
|
-
const reArray = /\buniform\s+\S+\s+(\w+)\s*\[\s*(\d+)\s*\]\s*;/g;
|
|
4171
|
+
const re = /\buniform\s+([^;{]+);/g;
|
|
4081
4172
|
const names = [];
|
|
4082
4173
|
let m;
|
|
4083
|
-
while ((m =
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4174
|
+
while ((m = re.exec(glsl)) !== null) {
|
|
4175
|
+
const words = m[1].trim().split(/\s+/);
|
|
4176
|
+
let i = 0;
|
|
4177
|
+
while (i < words.length && UNIFORM_QUALIFIER.test(words[i])) i++;
|
|
4178
|
+
i++;
|
|
4179
|
+
const declarators = words.slice(i).join(" ");
|
|
4180
|
+
if (!declarators) continue;
|
|
4181
|
+
for (const raw of declarators.split(",")) {
|
|
4182
|
+
const d = raw.trim();
|
|
4183
|
+
const arr = /^(\w+)\s*\[\s*(\d+)\s*\]$/.exec(d);
|
|
4184
|
+
if (arr) {
|
|
4185
|
+
const size = parseInt(arr[2], 10);
|
|
4186
|
+
for (let k = 0; k < size; k++) names.push(`${arr[1]}[${k}]`);
|
|
4187
|
+
} else if (/^\w+$/.test(d)) {
|
|
4188
|
+
names.push(d);
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4088
4191
|
}
|
|
4089
4192
|
return names;
|
|
4090
4193
|
}
|
|
@@ -4327,6 +4430,7 @@ var WeaselRenderer = class {
|
|
|
4327
4430
|
this.gradRampCache.free();
|
|
4328
4431
|
if (this.quadVbo) gl.deleteBuffer(this.quadVbo);
|
|
4329
4432
|
if (this.quadIbo) gl.deleteBuffer(this.quadIbo);
|
|
4433
|
+
disposeImageQuads(gl, this.imageFill);
|
|
4330
4434
|
this.solidBatch.dispose();
|
|
4331
4435
|
}
|
|
4332
4436
|
/**
|
|
@@ -4613,6 +4717,14 @@ function createViewportLayer(opts) {
|
|
|
4613
4717
|
id,
|
|
4614
4718
|
label,
|
|
4615
4719
|
space: "screen",
|
|
4720
|
+
reproject(outer, dims, screen) {
|
|
4721
|
+
const b = bounds(outer, dims);
|
|
4722
|
+
if (screen.x < b.x || screen.x >= b.x + b.w || screen.y < b.y || screen.y >= b.y + b.h) return null;
|
|
4723
|
+
return {
|
|
4724
|
+
x: (screen.x - b.x) / view.scale.x + view.x,
|
|
4725
|
+
y: (screen.y - b.y) / view.scale.y + view.y
|
|
4726
|
+
};
|
|
4727
|
+
},
|
|
4616
4728
|
draw: (data, outerView, dims) => {
|
|
4617
4729
|
const b = bounds(outerView, dims);
|
|
4618
4730
|
const children = [];
|
|
@@ -4639,6 +4751,14 @@ function createViewportLayer(opts) {
|
|
|
4639
4751
|
}
|
|
4640
4752
|
};
|
|
4641
4753
|
}
|
|
4754
|
+
function viewportsAt(layers, outer, dims, screen) {
|
|
4755
|
+
for (let i = layers.length - 1; i >= 0; i--) {
|
|
4756
|
+
const layer = layers[i];
|
|
4757
|
+
const point = layer.reproject(outer, dims, screen);
|
|
4758
|
+
if (point) return { layer, point };
|
|
4759
|
+
}
|
|
4760
|
+
return null;
|
|
4761
|
+
}
|
|
4642
4762
|
|
|
4643
4763
|
// src/features/parallax/deriveParallaxView.ts
|
|
4644
4764
|
function asXY(v) {
|
|
@@ -5772,6 +5892,12 @@ function runLayoutPass(scratch, moveCtx) {
|
|
|
5772
5892
|
if (layout2.contains) return layout2.contains(cPose, draggedCenter);
|
|
5773
5893
|
return draggedCenter.x >= aabb.x && draggedCenter.x < aabb.x + aabb.width && draggedCenter.y >= aabb.y && draggedCenter.y < aabb.y + aabb.height;
|
|
5774
5894
|
};
|
|
5895
|
+
const draggedArg = {
|
|
5896
|
+
id: draggedId,
|
|
5897
|
+
originPose: startWorld,
|
|
5898
|
+
pose: draggedWorld,
|
|
5899
|
+
sourceContainerId
|
|
5900
|
+
};
|
|
5775
5901
|
const consider = (id, zPath) => {
|
|
5776
5902
|
if (id === draggedId) return;
|
|
5777
5903
|
const layout2 = layoutDep.getLayout(id);
|
|
@@ -5781,6 +5907,7 @@ function runLayoutPass(scratch, moveCtx) {
|
|
|
5781
5907
|
const worldPose = composeWorldPose(poseAdapter, id, pc.compose);
|
|
5782
5908
|
const worldAABB = boundsOf2(worldPose);
|
|
5783
5909
|
if (!testInside(worldPose, worldAABB, layout2)) return;
|
|
5910
|
+
if (layout2.acceptsDrop && !layout2.acceptsDrop({ id, bounds: worldAABB }, draggedArg)) return;
|
|
5784
5911
|
candidates.push({
|
|
5785
5912
|
id,
|
|
5786
5913
|
bounds: worldAABB,
|
|
@@ -5829,20 +5956,16 @@ function runLayoutPass(scratch, moveCtx) {
|
|
|
5829
5956
|
id: cid,
|
|
5830
5957
|
pose: composeWorldPose(poseAdapter, cid, pc.compose)
|
|
5831
5958
|
}));
|
|
5832
|
-
const draggedArg = {
|
|
5833
|
-
id: draggedId,
|
|
5834
|
-
originPose: startWorld,
|
|
5835
|
-
pose: draggedWorld,
|
|
5836
|
-
sourceContainerId
|
|
5837
|
-
};
|
|
5838
5959
|
const targets = layout.getDropTargets(container, children, draggedArg);
|
|
5839
5960
|
const target = layout.snap.pickTarget(targets, { x: moveCtx.drag.current.x, y: moveCtx.drag.current.y });
|
|
5840
5961
|
if (!target) return;
|
|
5962
|
+
const reflowIds = /* @__PURE__ */ new Set();
|
|
5841
5963
|
for (const [cid, pose] of layout.reflowPoses(container, children, draggedArg, target)) {
|
|
5842
5964
|
if (asNodeId(cid) === draggedId) continue;
|
|
5843
5965
|
const parent = scene.get(asNodeId(cid))?.parent ?? null;
|
|
5844
5966
|
const local = rebaseLocalPose(poseAdapter, pose, parent, pc.compose, pc.decompose);
|
|
5845
5967
|
scratch.previews.set(asNodeId(cid), local);
|
|
5968
|
+
reflowIds.add(asNodeId(cid));
|
|
5846
5969
|
}
|
|
5847
5970
|
const sourceReflow = /* @__PURE__ */ new Map();
|
|
5848
5971
|
if (sourceContainerId && sourceContainerId !== dest.id) {
|
|
@@ -5865,10 +5988,11 @@ function runLayoutPass(scratch, moveCtx) {
|
|
|
5865
5988
|
sourceReflow.set(cid, pose);
|
|
5866
5989
|
const parent = scene.get(asNodeId(cid))?.parent ?? null;
|
|
5867
5990
|
scratch.previews.set(asNodeId(cid), rebaseLocalPose(poseAdapter, pose, parent, pc.compose, pc.decompose));
|
|
5991
|
+
reflowIds.add(asNodeId(cid));
|
|
5868
5992
|
}
|
|
5869
5993
|
}
|
|
5870
5994
|
}
|
|
5871
|
-
scratch.layoutPass = { layout, container, children, target, sourceReflow };
|
|
5995
|
+
scratch.layoutPass = { layout, container, children, target, sourceReflow, reflowIds };
|
|
5872
5996
|
}
|
|
5873
5997
|
function scenePoseAdapter(scene) {
|
|
5874
5998
|
return {
|
|
@@ -6176,7 +6300,8 @@ var moveAction = {
|
|
|
6176
6300
|
scratch.previews.clear();
|
|
6177
6301
|
},
|
|
6178
6302
|
previewIds: () => scratch.previews.keys(),
|
|
6179
|
-
previewPose: (id) => scratch.previews.get(id) ?? null
|
|
6303
|
+
previewPose: (id) => scratch.previews.get(id) ?? null,
|
|
6304
|
+
previewOpaqueIds: () => scratch.layoutPass?.reflowIds ?? null
|
|
6180
6305
|
};
|
|
6181
6306
|
}
|
|
6182
6307
|
},
|
|
@@ -6945,7 +7070,8 @@ var KIT_INSERT_KINDS = /* @__PURE__ */ new Set([
|
|
|
6945
7070
|
"line",
|
|
6946
7071
|
"polygon",
|
|
6947
7072
|
"star",
|
|
6948
|
-
"pencil"
|
|
7073
|
+
"pencil",
|
|
7074
|
+
"image"
|
|
6949
7075
|
]);
|
|
6950
7076
|
var liveInsertScratch = null;
|
|
6951
7077
|
function effectiveOriginMode(paramsOrigin, altHeld) {
|
|
@@ -8074,7 +8200,7 @@ var pinchZoomAction = {
|
|
|
8074
8200
|
const multiTouch = ctx.multiTouch;
|
|
8075
8201
|
if (!multiTouch) return {};
|
|
8076
8202
|
const startSpread = multiTouch.spread > 0 ? multiTouch.spread : 1;
|
|
8077
|
-
const scratch = { view, startSpread };
|
|
8203
|
+
const scratch = { view, startSpread, centroid: multiTouch.centroid };
|
|
8078
8204
|
return {
|
|
8079
8205
|
kind: "pinch",
|
|
8080
8206
|
onMove(moveCtx) {
|
|
@@ -8084,8 +8210,15 @@ var pinchZoomAction = {
|
|
|
8084
8210
|
if (currentSpread <= 0 || scratch.startSpread <= 0) return;
|
|
8085
8211
|
const factor = currentSpread / scratch.startSpread;
|
|
8086
8212
|
scratch.startSpread = currentSpread;
|
|
8213
|
+
const prev = scratch.centroid;
|
|
8214
|
+
scratch.centroid = centroid;
|
|
8087
8215
|
const current = scratch.view.get();
|
|
8088
|
-
|
|
8216
|
+
const zoomed = zoomAt(current, prev, factor);
|
|
8217
|
+
scratch.view.set({
|
|
8218
|
+
scale: zoomed.scale,
|
|
8219
|
+
x: zoomed.x - (centroid.x - prev.x) / zoomed.scale.x,
|
|
8220
|
+
y: zoomed.y - (centroid.y - prev.y) / zoomed.scale.y
|
|
8221
|
+
});
|
|
8089
8222
|
}
|
|
8090
8223
|
// onEnd: nothing to do — zoom was applied each frame.
|
|
8091
8224
|
};
|
|
@@ -8569,10 +8702,12 @@ function normalizeNegativeExtent(pose) {
|
|
|
8569
8702
|
}
|
|
8570
8703
|
|
|
8571
8704
|
// src/interactions/actions/defaults/flip.ts
|
|
8572
|
-
function flipSelection(selection, scene, axis, applyOps, geometryProjection) {
|
|
8705
|
+
function flipSelection(selection, scene, axis, pivot, applyOps, geometryProjection) {
|
|
8573
8706
|
const ids = selection.get();
|
|
8574
8707
|
if (ids.length === 0) return;
|
|
8575
8708
|
const geom = AUTO_POSE_DESCRIPTOR;
|
|
8709
|
+
const nodes = ids.map((id) => scene.get(id)).filter((n) => n != null);
|
|
8710
|
+
const unionPivot = pivot === "union" ? unionBounds(nodes.map((n) => geom.getBounds(n.pose))) : null;
|
|
8576
8711
|
const ops = [];
|
|
8577
8712
|
for (const id of ids) {
|
|
8578
8713
|
const node = scene.get(id);
|
|
@@ -8580,10 +8715,10 @@ function flipSelection(selection, scene, axis, applyOps, geometryProjection) {
|
|
|
8580
8715
|
ops.push(createTransformOp({
|
|
8581
8716
|
id,
|
|
8582
8717
|
from: node.pose,
|
|
8583
|
-
to: flipPoseViaDescriptor(node.pose, axis, geom),
|
|
8718
|
+
to: unionPivot ? flipPoseAboutBounds(node.pose, axis, geom, unionPivot) : flipPoseViaDescriptor(node.pose, axis, geom),
|
|
8584
8719
|
label: "Flip"
|
|
8585
8720
|
}));
|
|
8586
|
-
const g = geom.getBounds(node.pose);
|
|
8721
|
+
const g = unionPivot ?? geom.getBounds(node.pose);
|
|
8587
8722
|
const cx = g.x + g.width / 2;
|
|
8588
8723
|
const cy = g.y + g.height / 2;
|
|
8589
8724
|
const m = axis === "x" ? [-1, 0, 0, 1, 2 * cx, 0] : [1, 0, 0, -1, 0, 2 * cy];
|
|
@@ -8612,12 +8747,13 @@ var flipAction = {
|
|
|
8612
8747
|
timing: "immediate",
|
|
8613
8748
|
run: (deps, params) => {
|
|
8614
8749
|
const axis = params?.axis ?? "x";
|
|
8750
|
+
const pivot = params?.pivot ?? "each";
|
|
8615
8751
|
const selection = deps.selection;
|
|
8616
8752
|
const scene = deps.scene;
|
|
8617
8753
|
const applyOps = deps.applyOps;
|
|
8618
8754
|
const geometryProjection = deps.geometryProjection;
|
|
8619
8755
|
if (!selection || !scene) return;
|
|
8620
|
-
flipSelection(selection, scene, axis, applyOps, geometryProjection);
|
|
8756
|
+
flipSelection(selection, scene, axis, pivot, applyOps, geometryProjection);
|
|
8621
8757
|
}
|
|
8622
8758
|
},
|
|
8623
8759
|
enabled: requiresSelection
|
|
@@ -10566,6 +10702,12 @@ var DEFAULT_DEBUG_THEME = {
|
|
|
10566
10702
|
fpsText: "#e0e0e0",
|
|
10567
10703
|
fpsTextBg: "rgba(0, 0, 0, 0.6)"
|
|
10568
10704
|
};
|
|
10705
|
+
var DEFAULT_DEBUG_STROKES = {
|
|
10706
|
+
hitbox: { width: 1, dash: [2, 2] },
|
|
10707
|
+
bounds: { width: 1 },
|
|
10708
|
+
handle: { width: 1 },
|
|
10709
|
+
snap: { width: 1 }
|
|
10710
|
+
};
|
|
10569
10711
|
|
|
10570
10712
|
// src/debug/createDebugOverlayLayer.ts
|
|
10571
10713
|
function createDebugOverlayLayer({
|
|
@@ -10573,6 +10715,7 @@ function createDebugOverlayLayer({
|
|
|
10573
10715
|
config
|
|
10574
10716
|
}) {
|
|
10575
10717
|
const theme = { ...DEFAULT_DEBUG_THEME, ...config.theme ?? {} };
|
|
10718
|
+
const strokes = { ...DEFAULT_DEBUG_STROKES, ...config.strokes ?? {} };
|
|
10576
10719
|
const fpsHistory = [];
|
|
10577
10720
|
const FPS_WINDOW = 60;
|
|
10578
10721
|
return {
|
|
@@ -10584,11 +10727,11 @@ function createDebugOverlayLayer({
|
|
|
10584
10727
|
const s = sink.snapshot();
|
|
10585
10728
|
const t = viewToTransform(view);
|
|
10586
10729
|
const out = [];
|
|
10587
|
-
if (config.hitboxes) emitHitboxes(out, s, view, t, theme);
|
|
10588
|
-
if (config.bounds) emitBounds(out, s, view, t, theme);
|
|
10589
|
-
if (config.handles) emitHandles(out, s, t, theme);
|
|
10730
|
+
if (config.hitboxes) emitHitboxes(out, s, view, t, theme, strokes);
|
|
10731
|
+
if (config.bounds) emitBounds(out, s, view, t, theme, strokes);
|
|
10732
|
+
if (config.handles) emitHandles(out, s, t, theme, strokes);
|
|
10590
10733
|
if (config.origins) emitOrigins(out, s, t, theme);
|
|
10591
|
-
if (config.snap) emitSnap(out, s, t, theme);
|
|
10734
|
+
if (config.snap) emitSnap(out, s, t, theme, strokes);
|
|
10592
10735
|
if (config.ids) emitIds(out, s, t, theme);
|
|
10593
10736
|
if (config.layers) emitLayersPanel(out, s, dims, theme);
|
|
10594
10737
|
if (config.fps) {
|
|
@@ -10621,9 +10764,16 @@ function approxCircleScreen(cx, cy, r, segments = 24) {
|
|
|
10621
10764
|
function rectPath2(x, y, w, h) {
|
|
10622
10765
|
return { kind: "rect", x, y, width: w, height: h };
|
|
10623
10766
|
}
|
|
10624
|
-
function
|
|
10767
|
+
function strokeOf(s, color) {
|
|
10768
|
+
return {
|
|
10769
|
+
paint: { fill: "solid", color },
|
|
10770
|
+
width: s.width,
|
|
10771
|
+
...s.dash && s.dash.length > 0 ? { dash: [...s.dash] } : {}
|
|
10772
|
+
};
|
|
10773
|
+
}
|
|
10774
|
+
function emitHitboxes(out, s, view, t, theme, strokes) {
|
|
10625
10775
|
const fill = { fill: "solid", color: theme.hitboxFill };
|
|
10626
|
-
const stroke =
|
|
10776
|
+
const stroke = strokeOf(strokes.hitbox, theme.hitboxStroke);
|
|
10627
10777
|
for (const h of s.hitboxes) {
|
|
10628
10778
|
if (h.shape.kind === "rect") {
|
|
10629
10779
|
const [sx, sy] = worldToScreen(h.shape.x, h.shape.y, t);
|
|
@@ -10637,8 +10787,8 @@ function emitHitboxes(out, s, view, t, theme) {
|
|
|
10637
10787
|
}
|
|
10638
10788
|
}
|
|
10639
10789
|
}
|
|
10640
|
-
function emitBounds(out, s, view, t, theme) {
|
|
10641
|
-
const stroke =
|
|
10790
|
+
function emitBounds(out, s, view, t, theme, strokes) {
|
|
10791
|
+
const stroke = strokeOf(strokes.bounds, theme.bounds);
|
|
10642
10792
|
for (const b of s.bounds) {
|
|
10643
10793
|
const [sx, sy] = worldToScreen(b.bounds.x, b.bounds.y, t);
|
|
10644
10794
|
const sw = b.bounds.width * view.scale.x;
|
|
@@ -10646,8 +10796,8 @@ function emitBounds(out, s, view, t, theme) {
|
|
|
10646
10796
|
out.push({ kind: "path", path: rectPath2(sx, sy, sw, sh), stroke });
|
|
10647
10797
|
}
|
|
10648
10798
|
}
|
|
10649
|
-
function emitHandles(out, s, t, theme) {
|
|
10650
|
-
const stroke =
|
|
10799
|
+
function emitHandles(out, s, t, theme, strokes) {
|
|
10800
|
+
const stroke = strokeOf(strokes.handle, theme.handle);
|
|
10651
10801
|
for (const h of s.handles) {
|
|
10652
10802
|
const [cx, cy] = worldToScreen(h.position.x, h.position.y, t);
|
|
10653
10803
|
const horiz = {
|
|
@@ -10673,14 +10823,14 @@ function emitOrigins(out, s, t, theme) {
|
|
|
10673
10823
|
out.push({ kind: "path", path: approxCircleScreen(cx, cy, 3), fill });
|
|
10674
10824
|
}
|
|
10675
10825
|
}
|
|
10676
|
-
function emitSnap(out, s, t, theme) {
|
|
10826
|
+
function emitSnap(out, s, t, theme, strokes) {
|
|
10677
10827
|
const color = theme.snap;
|
|
10678
10828
|
for (const c of s.snap) {
|
|
10679
10829
|
const [cx, cy] = worldToScreen(c.point.x, c.point.y, t);
|
|
10680
10830
|
const cmd = {
|
|
10681
10831
|
kind: "path",
|
|
10682
10832
|
path: approxCircleScreen(cx, cy, 4),
|
|
10683
|
-
...c.accepted ? { fill: { fill: "solid", color } } : { stroke:
|
|
10833
|
+
...c.accepted ? { fill: { fill: "solid", color } } : { stroke: strokeOf(strokes.snap, color) }
|
|
10684
10834
|
};
|
|
10685
10835
|
out.push(cmd);
|
|
10686
10836
|
}
|
|
@@ -11063,11 +11213,22 @@ function reportDeadClaim(owner, warn) {
|
|
|
11063
11213
|
`[weasel] exclusive claim by "${key}" matched no binding: no \`affordance:\` or \`kindOf\` target resolved against it, so the press was dropped.`
|
|
11064
11214
|
);
|
|
11065
11215
|
}
|
|
11216
|
+
function phaseRank(spec) {
|
|
11217
|
+
if (spec === void 0) return 0;
|
|
11218
|
+
const atoms = typeof spec === "string" ? [{ channel: "&", phase: spec }] : spec;
|
|
11219
|
+
if (atoms.length === 0) return 0;
|
|
11220
|
+
let min = 2;
|
|
11221
|
+
for (const a of atoms) {
|
|
11222
|
+
const rank = (a.channel === "*" ? 0 : 1) + (a.phase === "*" ? 0 : 1);
|
|
11223
|
+
if (rank < min) min = rank;
|
|
11224
|
+
}
|
|
11225
|
+
return min;
|
|
11226
|
+
}
|
|
11066
11227
|
function specificity(spec) {
|
|
11067
11228
|
const t = targetRank(specTargetOf(spec));
|
|
11068
11229
|
const mods = "mods" in spec ? spec.mods : void 0;
|
|
11069
11230
|
const m = modsCount(mods);
|
|
11070
|
-
const p = "phase" in spec
|
|
11231
|
+
const p = phaseRank("phase" in spec ? spec.phase : void 0);
|
|
11071
11232
|
const typed = (spec.kind === "drop" || spec.kind === "paste") && spec.types !== void 0 && spec.types.length > 0 ? 2 : 1;
|
|
11072
11233
|
return [t, m, p, typed];
|
|
11073
11234
|
}
|
|
@@ -12727,8 +12888,7 @@ function useGestureDispatcher(opts) {
|
|
|
12727
12888
|
if (!cd) return;
|
|
12728
12889
|
const items = itemsFromClipboardData(cd);
|
|
12729
12890
|
if (items.length === 0) return;
|
|
12730
|
-
|
|
12731
|
-
dispatch2({
|
|
12891
|
+
const result = dispatch2({
|
|
12732
12892
|
kind: "paste",
|
|
12733
12893
|
items,
|
|
12734
12894
|
// ClipboardEvent carries no modifier state.
|
|
@@ -12737,6 +12897,7 @@ function useGestureDispatcher(opts) {
|
|
|
12737
12897
|
metaKey: false,
|
|
12738
12898
|
shiftKey: false
|
|
12739
12899
|
});
|
|
12900
|
+
if (result === "handled") e.preventDefault();
|
|
12740
12901
|
};
|
|
12741
12902
|
if (keyboard) {
|
|
12742
12903
|
window.addEventListener("keydown", onKeyDown);
|
|
@@ -13080,6 +13241,52 @@ function useViewAnimation(setView) {
|
|
|
13080
13241
|
);
|
|
13081
13242
|
return { animateTo, animateToBounds, cancel };
|
|
13082
13243
|
}
|
|
13244
|
+
function validateId(id, kind) {
|
|
13245
|
+
if (id.length === 0) {
|
|
13246
|
+
throw new Error(`weasel: ${kind} id may not be empty`);
|
|
13247
|
+
}
|
|
13248
|
+
if (RESERVED_ID_PREFIXES.has(id[0])) {
|
|
13249
|
+
throw new Error(
|
|
13250
|
+
`weasel: ${kind} id "${id}" starts with reserved sigil "${id[0]}" (reserved set: ${[...RESERVED_ID_PREFIXES].join(" ")})`
|
|
13251
|
+
);
|
|
13252
|
+
}
|
|
13253
|
+
if (RESERVED_ID_NAMES.has(id)) {
|
|
13254
|
+
throw new Error(
|
|
13255
|
+
`weasel: ${kind} id "${id}" collides with a reserved phase keyword (reserved: ${[...RESERVED_ID_NAMES].join(", ")})`
|
|
13256
|
+
);
|
|
13257
|
+
}
|
|
13258
|
+
}
|
|
13259
|
+
function defineTool(def) {
|
|
13260
|
+
validateId(def.id, "tool");
|
|
13261
|
+
const resolveCursor = (ctx) => {
|
|
13262
|
+
if (def.cursor == null) return "";
|
|
13263
|
+
return typeof def.cursor === "function" ? def.cursor(ctx) : def.cursor;
|
|
13264
|
+
};
|
|
13265
|
+
return {
|
|
13266
|
+
id: def.id,
|
|
13267
|
+
eligibility: {
|
|
13268
|
+
focus: true,
|
|
13269
|
+
capabilities: def.capabilities,
|
|
13270
|
+
...def.hotkey ? { offhand: def.hotkey } : {}
|
|
13271
|
+
},
|
|
13272
|
+
actions: def.actions,
|
|
13273
|
+
def,
|
|
13274
|
+
presentation: def.presentation,
|
|
13275
|
+
keybinding: def.keybinding,
|
|
13276
|
+
onActivate: def.onActivate,
|
|
13277
|
+
onDeactivate: def.onDeactivate,
|
|
13278
|
+
initScratch: def.initScratch ?? (() => null),
|
|
13279
|
+
cursor: resolveCursor,
|
|
13280
|
+
bindings: def.bindings,
|
|
13281
|
+
overlay: def.overlay,
|
|
13282
|
+
overlayPosition: def.overlayPosition
|
|
13283
|
+
};
|
|
13284
|
+
}
|
|
13285
|
+
|
|
13286
|
+
// src/tools/defineViewportTool.ts
|
|
13287
|
+
function defineViewportTool(def) {
|
|
13288
|
+
return defineTool(def);
|
|
13289
|
+
}
|
|
13083
13290
|
|
|
13084
13291
|
// src/interactions/actions/defaults/toolOffhand.ts
|
|
13085
13292
|
var TOOL_OFFHAND_ID = "tool.offhand";
|
|
@@ -13174,17 +13381,19 @@ function useContributions(opts) {
|
|
|
13174
13381
|
const { ordered, state } = snapshot();
|
|
13175
13382
|
return scopeBindings(ordered, state);
|
|
13176
13383
|
}, [snapshot]);
|
|
13177
|
-
const overlays = useCallback(() => {
|
|
13384
|
+
const overlays = useCallback((position = "top") => {
|
|
13178
13385
|
const { ordered, state } = snapshot();
|
|
13179
13386
|
const active = [];
|
|
13180
13387
|
const hotkey = [];
|
|
13181
13388
|
const ambient = [];
|
|
13182
13389
|
for (const entry of ordered) {
|
|
13183
13390
|
if (!entry.overlay) continue;
|
|
13391
|
+
if ((entry.overlayPosition ?? "top") !== position) continue;
|
|
13392
|
+
const layers = Array.isArray(entry.overlay) ? entry.overlay : [entry.overlay];
|
|
13184
13393
|
const scope = liveScope(entry.id, entry.eligibility ?? {}, state);
|
|
13185
|
-
if (scope === "active") active.push(
|
|
13186
|
-
else if (scope === "hotkey") hotkey.push(
|
|
13187
|
-
else if (scope === "ambient") ambient.push(
|
|
13394
|
+
if (scope === "active") active.push(...layers);
|
|
13395
|
+
else if (scope === "hotkey") hotkey.push(...layers);
|
|
13396
|
+
else if (scope === "ambient") ambient.push(...layers);
|
|
13188
13397
|
}
|
|
13189
13398
|
return [...active, ...hotkey, ...ambient];
|
|
13190
13399
|
}, [snapshot]);
|
|
@@ -14011,7 +14220,7 @@ function isBoundsGradient(fill) {
|
|
|
14011
14220
|
}
|
|
14012
14221
|
|
|
14013
14222
|
// src/features/patterns/resolveSpec.ts
|
|
14014
|
-
var
|
|
14223
|
+
var cache6 = /* @__PURE__ */ new Map();
|
|
14015
14224
|
function specKey(spec) {
|
|
14016
14225
|
const keys = Object.keys(spec).sort();
|
|
14017
14226
|
const bag = spec;
|
|
@@ -14019,10 +14228,10 @@ function specKey(spec) {
|
|
|
14019
14228
|
}
|
|
14020
14229
|
function resolvePatternSpec(spec) {
|
|
14021
14230
|
const key = specKey(spec);
|
|
14022
|
-
const hit =
|
|
14231
|
+
const hit = cache6.get(key);
|
|
14023
14232
|
if (hit !== void 0) return hit;
|
|
14024
14233
|
const built = buildTile(spec);
|
|
14025
|
-
|
|
14234
|
+
cache6.set(key, built);
|
|
14026
14235
|
return built;
|
|
14027
14236
|
}
|
|
14028
14237
|
function buildTile(spec) {
|
|
@@ -14058,7 +14267,7 @@ function resolveFillPattern(fill) {
|
|
|
14058
14267
|
}
|
|
14059
14268
|
|
|
14060
14269
|
// src/features/images/imageCache.ts
|
|
14061
|
-
var
|
|
14270
|
+
var cache7 = /* @__PURE__ */ new Map();
|
|
14062
14271
|
var subscribers = /* @__PURE__ */ new Set();
|
|
14063
14272
|
var defaultLoader = async (src) => {
|
|
14064
14273
|
if (typeof Image === "undefined" || typeof createImageBitmap === "undefined") {
|
|
@@ -14076,28 +14285,28 @@ function notify() {
|
|
|
14076
14285
|
}
|
|
14077
14286
|
function startLoad(src) {
|
|
14078
14287
|
const entry = { status: "loading" };
|
|
14079
|
-
|
|
14288
|
+
cache7.set(src, entry);
|
|
14080
14289
|
loader(src).then(
|
|
14081
14290
|
(bitmap) => {
|
|
14082
|
-
if (
|
|
14083
|
-
|
|
14291
|
+
if (cache7.get(src) !== entry) return;
|
|
14292
|
+
cache7.set(src, { status: "ready", bitmap });
|
|
14084
14293
|
notify();
|
|
14085
14294
|
},
|
|
14086
14295
|
() => {
|
|
14087
|
-
if (
|
|
14088
|
-
|
|
14296
|
+
if (cache7.get(src) !== entry) return;
|
|
14297
|
+
cache7.set(src, { status: "error" });
|
|
14089
14298
|
notify();
|
|
14090
14299
|
}
|
|
14091
14300
|
);
|
|
14092
14301
|
}
|
|
14093
14302
|
function getImageBitmap(src) {
|
|
14094
|
-
const entry =
|
|
14303
|
+
const entry = cache7.get(src);
|
|
14095
14304
|
if (entry) return entry.bitmap;
|
|
14096
14305
|
startLoad(src);
|
|
14097
14306
|
return void 0;
|
|
14098
14307
|
}
|
|
14099
14308
|
function imageStatus(src) {
|
|
14100
|
-
return
|
|
14309
|
+
return cache7.get(src)?.status ?? "idle";
|
|
14101
14310
|
}
|
|
14102
14311
|
function subscribeImageReady(cb) {
|
|
14103
14312
|
subscribers.add(cb);
|
|
@@ -14464,6 +14673,7 @@ registerBuiltInShapePainters();
|
|
|
14464
14673
|
function pickTopMostHit(ids, adapter) {
|
|
14465
14674
|
if (ids.length === 0) return null;
|
|
14466
14675
|
if (ids.length === 1) return ids[0];
|
|
14676
|
+
let candidates = ids;
|
|
14467
14677
|
const getParent = adapter?.getParent;
|
|
14468
14678
|
if (typeof getParent === "function") {
|
|
14469
14679
|
const set = new Set(ids);
|
|
@@ -14478,15 +14688,40 @@ function pickTopMostHit(ids, adapter) {
|
|
|
14478
14688
|
}
|
|
14479
14689
|
if (dropped.size > 0) {
|
|
14480
14690
|
const filtered = ids.filter((id) => !dropped.has(id));
|
|
14481
|
-
if (filtered.length ===
|
|
14482
|
-
|
|
14483
|
-
|
|
14484
|
-
|
|
14691
|
+
if (filtered.length === 1) return filtered[0];
|
|
14692
|
+
if (filtered.length > 1) candidates = filtered;
|
|
14693
|
+
}
|
|
14694
|
+
}
|
|
14695
|
+
return pickHighestZ(candidates, adapter);
|
|
14696
|
+
}
|
|
14697
|
+
function pickHighestZ(ids, adapter) {
|
|
14698
|
+
const compareZ = adapter?.compareZ;
|
|
14699
|
+
if (typeof compareZ === "function") {
|
|
14700
|
+
let best = ids[0];
|
|
14701
|
+
for (let i = 1; i < ids.length; i++) {
|
|
14702
|
+
if (compareZ(ids[i], best) >= 0) best = ids[i];
|
|
14703
|
+
}
|
|
14704
|
+
return best;
|
|
14705
|
+
}
|
|
14706
|
+
const getZIndex = adapter?.getZIndex;
|
|
14707
|
+
if (typeof getZIndex === "function") {
|
|
14708
|
+
let best = ids[0];
|
|
14709
|
+
let bestZ = zOf(getZIndex, best);
|
|
14710
|
+
for (let i = 1; i < ids.length; i++) {
|
|
14711
|
+
const z = zOf(getZIndex, ids[i]);
|
|
14712
|
+
if (z >= bestZ) {
|
|
14713
|
+
best = ids[i];
|
|
14714
|
+
bestZ = z;
|
|
14485
14715
|
}
|
|
14486
14716
|
}
|
|
14717
|
+
return best;
|
|
14487
14718
|
}
|
|
14488
14719
|
return ids[ids.length - 1];
|
|
14489
14720
|
}
|
|
14721
|
+
function zOf(getZIndex, id) {
|
|
14722
|
+
const z = getZIndex(id);
|
|
14723
|
+
return typeof z === "number" && Number.isFinite(z) ? z : -Infinity;
|
|
14724
|
+
}
|
|
14490
14725
|
|
|
14491
14726
|
// src/core/selection/selectionTarget.ts
|
|
14492
14727
|
var MULTI_RESIZE_TARGET_ID = "__weasel:multi-selection";
|
|
@@ -14891,9 +15126,19 @@ function composeAffordanceLayer(id, label, affordances) {
|
|
|
14891
15126
|
},
|
|
14892
15127
|
// The walk itself lives in `hitAffordanceRegions` so this layer and
|
|
14893
15128
|
// `buildAffordanceAt` share one implementation rather than two that can
|
|
14894
|
-
// drift. This wrapper
|
|
14895
|
-
// binding
|
|
14896
|
-
hitTest: (wx, wy, state, view, _dims, isVisible) =>
|
|
15129
|
+
// drift. This wrapper lifts the region's declared cursor and claim onto
|
|
15130
|
+
// the binding, the way `buildAffordanceAt` lifts them onto `AffordanceHit`.
|
|
15131
|
+
hitTest: (wx, wy, state, view, _dims, isVisible) => {
|
|
15132
|
+
const hit = hitAffordanceRegions(affordances, wx, wy, state, view, isVisible);
|
|
15133
|
+
if (!hit) return null;
|
|
15134
|
+
const { cursor, strength, claimedKinds } = hit.region;
|
|
15135
|
+
return {
|
|
15136
|
+
...hit.binding,
|
|
15137
|
+
...cursor !== void 0 ? { cursor } : {},
|
|
15138
|
+
...strength !== void 0 ? { strength } : {},
|
|
15139
|
+
...claimedKinds !== void 0 ? { claimedKinds } : {}
|
|
15140
|
+
};
|
|
15141
|
+
}
|
|
14897
15142
|
};
|
|
14898
15143
|
}
|
|
14899
15144
|
function asChromeState(data) {
|
|
@@ -15676,7 +15921,7 @@ function useEllipseTool() {
|
|
|
15676
15921
|
);
|
|
15677
15922
|
}
|
|
15678
15923
|
function useImageTool(options) {
|
|
15679
|
-
const { src, label = "Image" } = options;
|
|
15924
|
+
const { src, label = "Image", preview = "bitmap" } = options;
|
|
15680
15925
|
return useMemo(
|
|
15681
15926
|
() => defineTool({
|
|
15682
15927
|
id: "image",
|
|
@@ -15695,11 +15940,11 @@ function useImageTool(options) {
|
|
|
15695
15940
|
{
|
|
15696
15941
|
spec: { kind: "drag" },
|
|
15697
15942
|
actionId: "insert",
|
|
15698
|
-
opts: { params: { kind: "image", src } }
|
|
15943
|
+
opts: { params: { kind: "image", src, preview } }
|
|
15699
15944
|
}
|
|
15700
15945
|
]
|
|
15701
15946
|
}),
|
|
15702
|
-
[src, label]
|
|
15947
|
+
[src, label, preview]
|
|
15703
15948
|
);
|
|
15704
15949
|
}
|
|
15705
15950
|
function useEyedropperTool(opts) {
|
|
@@ -16166,6 +16411,19 @@ function composeOrderedLayers(layersMap, standardLayers, sceneLayers) {
|
|
|
16166
16411
|
}
|
|
16167
16412
|
return out;
|
|
16168
16413
|
}
|
|
16414
|
+
function placeToolOverlays(ordered, selectionOverlay, overlays) {
|
|
16415
|
+
const before = overlays("before-selection");
|
|
16416
|
+
const after = overlays("after-selection");
|
|
16417
|
+
const at = selectionOverlay ? ordered.indexOf(selectionOverlay) : -1;
|
|
16418
|
+
if (at >= 0) {
|
|
16419
|
+
ordered.splice(at + 1, 0, ...after);
|
|
16420
|
+
ordered.splice(at, 0, ...before);
|
|
16421
|
+
} else {
|
|
16422
|
+
ordered.push(...before, ...after);
|
|
16423
|
+
}
|
|
16424
|
+
ordered.push(...overlays("top"));
|
|
16425
|
+
return ordered;
|
|
16426
|
+
}
|
|
16169
16427
|
|
|
16170
16428
|
// src/canvas/toolPreview.ts
|
|
16171
16429
|
function* toolsInPriorityOrder(tools) {
|
|
@@ -17712,7 +17970,7 @@ function CanvasInner(props, ref) {
|
|
|
17712
17970
|
const out = composeOrderedLayers(effectiveLayersMap, standardLayers, sceneLayers);
|
|
17713
17971
|
if (decorationLayer) out.push(decorationLayer);
|
|
17714
17972
|
if (tools) {
|
|
17715
|
-
out.
|
|
17973
|
+
placeToolOverlays(out, standardLayers.selectionOverlay, tools.getActiveOverlays);
|
|
17716
17974
|
}
|
|
17717
17975
|
return out;
|
|
17718
17976
|
}, [layersMap, adapter, selectedIds, effectiveBoundsOf, multiActive2, debugSink, tools, backgroundLayer, decorationLayer]);
|
|
@@ -19379,6 +19637,7 @@ function useSceneSelectTool(args) {
|
|
|
19379
19637
|
boundsOf: wiredBoundsOf
|
|
19380
19638
|
};
|
|
19381
19639
|
}
|
|
19640
|
+
var GHOST_ALPHA = 0.85;
|
|
19382
19641
|
function usePreviewGhostLayer(args) {
|
|
19383
19642
|
const { scene, tools, sceneSlot, dispatcher } = args;
|
|
19384
19643
|
const sceneRef = useRef(scene);
|
|
@@ -19465,15 +19724,26 @@ function usePreviewGhostLayer(args) {
|
|
|
19465
19724
|
}
|
|
19466
19725
|
return [group];
|
|
19467
19726
|
};
|
|
19727
|
+
const opaqueSet = /* @__PURE__ */ new Set();
|
|
19728
|
+
for (const source of sources) {
|
|
19729
|
+
const ids = source.previewOpaqueIds?.();
|
|
19730
|
+
if (!ids) continue;
|
|
19731
|
+
for (const id of ids) opaqueSet.add(id);
|
|
19732
|
+
}
|
|
19468
19733
|
const children = [];
|
|
19734
|
+
const opaque = [];
|
|
19469
19735
|
for (const id of idSet) {
|
|
19470
19736
|
const node = sc.get(asNodeId(id));
|
|
19471
19737
|
const parent = node?.parent;
|
|
19472
19738
|
if (parent != null && idSet.has(parent)) continue;
|
|
19473
|
-
|
|
19739
|
+
const sink = opaqueSet.has(id) ? opaque : children;
|
|
19740
|
+
for (const cmd of buildSubtree(id)) sink.push(cmd);
|
|
19474
19741
|
}
|
|
19475
|
-
if (children.length === 0) return [];
|
|
19476
|
-
|
|
19742
|
+
if (children.length === 0 && opaque.length === 0) return [];
|
|
19743
|
+
const out = [];
|
|
19744
|
+
if (opaque.length > 0) out.push({ kind: "group", children: opaque });
|
|
19745
|
+
if (children.length > 0) out.push({ kind: "group", alpha: GHOST_ALPHA, children });
|
|
19746
|
+
return out;
|
|
19477
19747
|
}
|
|
19478
19748
|
}), []);
|
|
19479
19749
|
}
|
|
@@ -19483,6 +19753,7 @@ var DEFAULT_STYLE = {
|
|
|
19483
19753
|
dash: [3, 3],
|
|
19484
19754
|
lineWidth: 1
|
|
19485
19755
|
};
|
|
19756
|
+
var PREVIEW_CONTENT_OPACITY = 0.85;
|
|
19486
19757
|
function useDispatcherOverlayLayer(args) {
|
|
19487
19758
|
const { dispatcher, style } = args;
|
|
19488
19759
|
const dispatcherRef = useRef(dispatcher);
|
|
@@ -19545,6 +19816,26 @@ function useDispatcherOverlayLayer(args) {
|
|
|
19545
19816
|
pathCmd = rectPath(sx, sy, b.width * view.scale.x, b.height * view.scale.y);
|
|
19546
19817
|
break;
|
|
19547
19818
|
}
|
|
19819
|
+
case "image": {
|
|
19820
|
+
const e = ov.extras;
|
|
19821
|
+
const [sx, sy] = worldToScreen(b.x, b.y, t);
|
|
19822
|
+
const sw = b.width * view.scale.x;
|
|
19823
|
+
const sh = b.height * view.scale.y;
|
|
19824
|
+
const bmp = e.preview === "outline" || !e.src ? void 0 : getImageBitmap(e.src);
|
|
19825
|
+
if (bmp) {
|
|
19826
|
+
out.push({
|
|
19827
|
+
kind: "image",
|
|
19828
|
+
image: bmp,
|
|
19829
|
+
x: sx,
|
|
19830
|
+
y: sy,
|
|
19831
|
+
w: sw,
|
|
19832
|
+
h: sh,
|
|
19833
|
+
opacity: PREVIEW_CONTENT_OPACITY
|
|
19834
|
+
});
|
|
19835
|
+
}
|
|
19836
|
+
pathCmd = rectPath(sx, sy, sw, sh);
|
|
19837
|
+
break;
|
|
19838
|
+
}
|
|
19548
19839
|
case "ellipse": {
|
|
19549
19840
|
const [sx, sy] = worldToScreen(b.x, b.y, t);
|
|
19550
19841
|
pathCmd = ellipsePath({
|
|
@@ -19603,7 +19894,7 @@ function useDispatcherOverlayLayer(args) {
|
|
|
19603
19894
|
const cmd = {
|
|
19604
19895
|
kind: "path",
|
|
19605
19896
|
path: pathCmd,
|
|
19606
|
-
fill: ov.shape === "line" || ov.shape === "pencil" ? void 0 : { color: cfg.fill },
|
|
19897
|
+
fill: ov.shape === "line" || ov.shape === "pencil" || ov.shape === "image" ? void 0 : { color: cfg.fill },
|
|
19607
19898
|
stroke: { paint: { color: cfg.stroke }, width: cfg.lineWidth, dash: cfg.dash }
|
|
19608
19899
|
};
|
|
19609
19900
|
out.push(cmd);
|
|
@@ -20316,9 +20607,6 @@ function buildClassifyTarget(getSelection, pickBest, kindOfNode) {
|
|
|
20316
20607
|
function w2s3(wx, wy, view) {
|
|
20317
20608
|
return [(wx - view.x) * view.scale.x, (wy - view.y) * view.scale.y];
|
|
20318
20609
|
}
|
|
20319
|
-
function r2s(radiusWorld, view) {
|
|
20320
|
-
return radiusWorld * meanScale(view.scale);
|
|
20321
|
-
}
|
|
20322
20610
|
var SLOP_FILL = "rgba(255, 80, 140, 0.18)";
|
|
20323
20611
|
var SLOP_STROKE = "rgba(255, 80, 140, 0.55)";
|
|
20324
20612
|
function createSlopsDebugLayer(opts) {
|
|
@@ -20329,7 +20617,7 @@ function createSlopsDebugLayer(opts) {
|
|
|
20329
20617
|
draw: (_data, view) => {
|
|
20330
20618
|
const out = [];
|
|
20331
20619
|
const sel = opts.selectionRef.current?.current ?? [];
|
|
20332
|
-
const r =
|
|
20620
|
+
const r = HANDLE_HIT_RADIUS;
|
|
20333
20621
|
for (const id of sel) {
|
|
20334
20622
|
const b = opts.boundsOf(id);
|
|
20335
20623
|
if (!b) continue;
|
|
@@ -20366,6 +20654,7 @@ function createSlopsDebugLayer(opts) {
|
|
|
20366
20654
|
}
|
|
20367
20655
|
const editingId = opts.getEditingId();
|
|
20368
20656
|
if (editingId) {
|
|
20657
|
+
const anchorR = ANCHOR_HIT_RADIUS;
|
|
20369
20658
|
const pose = opts.getPose(editingId);
|
|
20370
20659
|
if (pose && pose.kind === "polygon") {
|
|
20371
20660
|
const anchors = enumerateAnchors(pose);
|
|
@@ -20373,7 +20662,7 @@ function createSlopsDebugLayer(opts) {
|
|
|
20373
20662
|
const [sx, sy] = w2s3(a.x, a.y, view);
|
|
20374
20663
|
out.push({
|
|
20375
20664
|
kind: "path",
|
|
20376
|
-
path: circlePath(sx, sy,
|
|
20665
|
+
path: circlePath(sx, sy, anchorR),
|
|
20377
20666
|
fill: { fill: "solid", color: SLOP_FILL },
|
|
20378
20667
|
stroke: { paint: { fill: "solid", color: SLOP_STROKE }, width: 1 }
|
|
20379
20668
|
});
|
|
@@ -20381,7 +20670,7 @@ function createSlopsDebugLayer(opts) {
|
|
|
20381
20670
|
const [csx, csy] = w2s3(a.controlIn.x, a.controlIn.y, view);
|
|
20382
20671
|
out.push({
|
|
20383
20672
|
kind: "path",
|
|
20384
|
-
path: circlePath(csx, csy,
|
|
20673
|
+
path: circlePath(csx, csy, anchorR),
|
|
20385
20674
|
fill: { fill: "solid", color: SLOP_FILL },
|
|
20386
20675
|
stroke: { paint: { fill: "solid", color: SLOP_STROKE }, width: 1 }
|
|
20387
20676
|
});
|
|
@@ -20390,7 +20679,7 @@ function createSlopsDebugLayer(opts) {
|
|
|
20390
20679
|
const [csx, csy] = w2s3(a.controlOut.x, a.controlOut.y, view);
|
|
20391
20680
|
out.push({
|
|
20392
20681
|
kind: "path",
|
|
20393
|
-
path: circlePath(csx, csy,
|
|
20682
|
+
path: circlePath(csx, csy, anchorR),
|
|
20394
20683
|
fill: { fill: "solid", color: SLOP_FILL },
|
|
20395
20684
|
stroke: { paint: { fill: "solid", color: SLOP_STROKE }, width: 1 }
|
|
20396
20685
|
});
|
|
@@ -20472,9 +20761,9 @@ function DepRegistryProviderIfRoot({ children }) {
|
|
|
20472
20761
|
function hitTestArea(scene, bounds) {
|
|
20473
20762
|
const { x, y, width: w, height: h } = bounds;
|
|
20474
20763
|
const area = [x, y, x + w, y, x + w, y + h, x, y + h];
|
|
20475
|
-
return hitTestAreaPolygon(scene, area, { x, y, width: w, height: h });
|
|
20764
|
+
return hitTestAreaPolygon(scene, area, { x, y, width: w, height: h }, true);
|
|
20476
20765
|
}
|
|
20477
|
-
function hitTestAreaPolygon(scene, area, areaBounds) {
|
|
20766
|
+
function hitTestAreaPolygon(scene, area, areaBounds, areaIsRect = false) {
|
|
20478
20767
|
const ab = areaBounds ?? boundsOf(area);
|
|
20479
20768
|
if (!ab) return [];
|
|
20480
20769
|
const hits = [];
|
|
@@ -20491,13 +20780,22 @@ function hitTestAreaPolygon(scene, area, areaBounds) {
|
|
|
20491
20780
|
if (b.x >= ab.x + ab.width || b.x + b.width <= ab.x || b.y >= ab.y + ab.height || b.y + b.height <= ab.y) {
|
|
20492
20781
|
continue;
|
|
20493
20782
|
}
|
|
20494
|
-
if (
|
|
20783
|
+
if (areaIsRect && b.x >= ab.x && b.y >= ab.y && b.x + b.width <= ab.x + ab.width && b.y + b.height <= ab.y + ab.height) {
|
|
20495
20784
|
hits.push(node.id);
|
|
20496
20785
|
continue;
|
|
20497
20786
|
}
|
|
20498
|
-
if (
|
|
20499
|
-
|
|
20787
|
+
if (silhouette) {
|
|
20788
|
+
if (silhouetteOverlapsArea(pose.coords, area)) {
|
|
20789
|
+
hits.push(node.id);
|
|
20790
|
+
}
|
|
20791
|
+
continue;
|
|
20792
|
+
}
|
|
20793
|
+
const drawn = findShapeSilhouette(node, pose);
|
|
20794
|
+
if (drawn?.kind === "polygon") {
|
|
20795
|
+
if (silhouetteOverlapsArea(drawn.coords, area)) hits.push(node.id);
|
|
20796
|
+
continue;
|
|
20500
20797
|
}
|
|
20798
|
+
hits.push(node.id);
|
|
20501
20799
|
}
|
|
20502
20800
|
return hits;
|
|
20503
20801
|
}
|
|
@@ -24324,6 +24622,14 @@ function useSceneTextEdit(scene, container, options = {}) {
|
|
|
24324
24622
|
const setter = optsRef.current.setRuns;
|
|
24325
24623
|
const data = setter ? setter(node.data, runs) : { ...node.data, runs };
|
|
24326
24624
|
sceneRef.current.update(nid, { data });
|
|
24625
|
+
},
|
|
24626
|
+
setStyle: (id, style) => {
|
|
24627
|
+
const nid = asNodeId(id);
|
|
24628
|
+
const node = sceneRef.current.get(nid);
|
|
24629
|
+
if (!node) return;
|
|
24630
|
+
const setter = optsRef.current.setStyle;
|
|
24631
|
+
const data = setter ? setter(node.data, style) : { ...node.data, style };
|
|
24632
|
+
sceneRef.current.update(nid, { data });
|
|
24327
24633
|
}
|
|
24328
24634
|
});
|
|
24329
24635
|
const onDoubleClick = useCallback((e) => {
|
|
@@ -28032,6 +28338,6 @@ function mergeContributions(...bundles) {
|
|
|
28032
28338
|
return out;
|
|
28033
28339
|
}
|
|
28034
28340
|
|
|
28035
|
-
export { ALWAYS, ANCHOR_HIT_BASE_PX, ActionsProviderIfRoot, ActiveToolContextProvider, ActiveToolContextProviderIfRoot, BUNDLE_TOOLS, COARSE_TARGET_SCALE, CURVE_REPS, ColorOverrideRegistry, CropIcon, CursorCoordsHud, DEFAULT_ALPHA_DECAY, DEFAULT_ALPHA_MIN, DEFAULT_DEBUG_THEME, DEFAULT_DEVICE_PROFILE, DEFAULT_FILL_COLOR, DEFAULT_HANDLE_SIZE2 as DEFAULT_HANDLE_SIZE, DEFAULT_PALETTE, DEFAULT_ROTATION_HANDLE_DISTANCE, DEFAULT_STROKE_COLOR, DEFAULT_TEXT_STYLE, DEFAULT_VELOCITY_DECAY, DepRegistryProviderIfRoot, DeviceProfileProvider, DivideIcon, EASINGS, EllipseIcon, ExcludeIcon, EyedropperIcon, FALLBACK_FIT_VIEW, GHOST_STROKE, HANDLE_BASE_PX, HandIcon, IDENTITY_COLOR_MATRIX, IDENTITY_POSE_COMPOSITION, IntersectIcon, KIT_SHAPE_KINDS, LassoIcon, LineIcon, MIXED, MOVE_ANCHORS, MinimapCanvas, NEVER, OUTLINE_MIN_SCREEN_PX, PATH_ANCHOR_CHROME_ID, PenIcon, PencilIcon, PickHud, PointerProviderIfRoot, PolygonIcon, RECT_ALIGN_PROJECTION, ROTATION_HANDLE_BASE_PX, RectIcon, SPRING_PRESETS, SceneCanvas, SceneViewCanvas, SelectIcon, SelectionContextProvider, SelectionContextProviderIfRoot, ShaderCompileError, StarIcon, SubtractIcon, TextIcon, UnionIcon, UnknownIcon, VERSION, WeaselProvider, WeaselRenderer, aabbCenter, actionIs, alignDeltaFor, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, alignedStrokeRect, always, and, animateLifecycle, animateOnSetPose, annulusSemiAxes, applyBooleanOp, applyHitExistingGate, applyStyleToRange, areaSelectAction, arrayAdapter, asNodeId, bezierCubic, bezierQuadratic, buildChromeCtx, buildGradientRamp, buildRuleCtx, buildSceneViewCommands, canBringForward, canHover, canSendBackward, capabilityAll, capabilityIn, capabilityIs, capabilityNot, caretIndexAt, cellAt, charOffsetToDomPosition, clampView, clearSelectionAction, clientToCanvas, clipboardCopyAction, clipboardCutAction, cloneAction, coarsePointer, composeAffordanceLayer, composePath, composeRectPose, composeSelectionPose, composeWorldPose, computeFitView2 as computeFitView, computeIndicatorCommand, computeWheelAction, cond, constrainTo45, containedThenNearest, countPathAnchors, createCellHighlightLayer, createChildrenLayer, createCornerResizeAffordance, createDebugOverlayLayer, createDebugSink, createDispatcher, createGridLayer, createGuidesLayer, createHistory2 as createHistory, createMarkdownRenderer, createMoveToIndexOp, createNodeProperties, createNodeRouting, createParallaxLayer, createPathAnchorAffordances, createPathEditingOverlayLayer, createPathLayer, createPenPreviewLayer, createReorderOp, createRotationAffordance, createScene, createSelectionHandlesLayer, createSelectionOutlineLayer, createSelectionOverlayLayer, createSetDataOp, createSetLayerOp, createSetPathOp, createSetTextOp, createTextLayer, createViewportLayer, cycleVertexColors, decomposePath, decomposeRectPose, defaultCommitAdapter, defaultDrawOne, defaultLabelTextRenderer, defaultNodeProperties, defaultNodeRouting, defaultVisibilityRules, deriveAlignmentGuides, deriveParallaxView, describeRule, domPositionToCharOffset, domToRuns, drawLayers, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic2 as easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorsAction, enterTextEditAction, enumerateAnchors, evaluate, fillInPoseFrame, fillToBoundsFrame, findNodeShape, findShapeInk, findShapeSilhouette, fitTextPose, fitToBounds, fitViewToBounds, fitZoom, focused, fontString, forEachCoalesced, freeform, gateLayer, gesturing, getAlpha01, getImageBitmap, getNodeShapes, getStylusData, gradientForBounds, gradientGeometry, hexToRgba, hitAffordanceRegions, hitAnchor, hitRotationHandle, hovering, hoveringSelected, imageStatus, inferredNodeProperties, inferredNodeRouting, insertAction, isEditableTarget2 as isEditableTarget, isPatternSpec, lassoSelectAction, layoutMarkdown, lerpColorArray, lerpOklab, lerpOklch, linear, liveScope, makeViewportZoomAction, markdownToRuns, mat3, matchAlignment, matchesKeyBinding, meanScale, measureText, measureTextBounds, measuredWidth, mergeAlphaFromPrev, mergeContributions, modeIn, modeIs, modeNot, modifierHeld, momentum, moveAction, multiActive, nearest, nearestWithin, nestedHitTester, never, nodeHasFlag, none, normalizeHex, not, nurbs, oklabToOklch, oklabToSrgbU8, oklchToOklab, openFilePicker, or, parseColor, parseColorToRgba255, parseDebugFlags, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathDistanceToPoint, pathDivide, pathExclude, pathFromD, pathInPoseFrame, pathInWorld, pathIntersect, pathIntersectsPolygon, pathIntersectsRect, pathOriginProjection, pathSubtract, pathToAnchors, pathUnion, pickTopMostHit, pinchZoomAction, planPixelRender, pointInRotatedRect, pointInTextPose, poseRotationOf, pressureToWidth, rainbowVertexColors, rebaseLocalPose, rectCorners, registerContentHandler, registerNodeShape, registerProgram, renderLabel, renderSceneToCanvas, renderSceneToPixels, resizeAction, resolveColor, resolveDeviceProfile, resolveFillPattern, resolvePatternSpec, resolveRuns, resolveTextStyle, resolveVisibility, rgbaToHex, rotateAction, rotateAroundAABBCenter, rotatePathAround, rotatePoint, rotatedRectCorners, rotationDegreesUnit, rotationHandle, roundToCell, runsToDom, runsToMarkdown, runsToPlainText, sampleGradientStops, sceneFromJSON, sceneNodeClientRect, sceneToAdapter, scopeBindings, screenToWorld, selectFromLasso, selectionAtLeast, selectionEmpty, selectionIs, setFlagOverRange, shapeCoversPoint, sliceAction, snapPoint, solidVertexColors, specificity, spiro, splitPathByLine, splitSubpaths, springPose, springVertexColors, srgbU8ToOklab, staggerVertexColors, startThresholdDrag, styleAtRange, subscribeImageReady, tessellate, tessellateStroke, textCommand, textLineBoxes, tileGrid, toHex8, toRuns, translatePoseViaDescriptor, translateRectPose, tweenPose, tweenVertexColors, unionBounds, unionBoundsPath, useActiveToolContext, useAlign, useAnimator, useArrayAdapter, useAutoCenter, useBooleans, useBooleansAdapter, useCanvasFocus, useCanvasSize, useDecayLoop, useDeviceProfile, useDistribute, useDragGesture, useDragHandle, useDragRadial, useDragRect, useDropZone, useEllipseTool, useEyedropperTool, useGestureDispatcher, useGridCellHover, useGuides, useHandTool, useHandleDrag, useHoverTracking, useImageTool, useKeybindings, useLassoTool, useLineTool, useOptionalActiveToolContext, usePenTool, usePencilTool, usePinchGesture, usePinchZoomTool, usePointerStylus, usePolygonTool, usePublishSelection, useRectTool, useResizePolicy, useRotateTool, useScene, useSceneAdapter, useSceneTextEdit, useSelectTool, useSelection, useSelectionContext, useSimulation, useSliceDep, useStandardActions, useStarTool, useTextEdit, useTextTool, useTools, useVelocityTracker, useViewAnimation, useViewTween, useZoom, verticalAlignOffset, viewToMat3, viewToTransform, viewportDragPanAction, viewportZoomAction, when, withAlpha01, withCoord, withGradientKind, worldEditToStorage, worldPoseLookup, worldToScreen, zoomAt, zoomAtLeast };
|
|
28036
|
-
//# sourceMappingURL=chunk-
|
|
28037
|
-
//# sourceMappingURL=chunk-
|
|
28341
|
+
export { ALWAYS, ANCHOR_HIT_BASE_PX, ActionsProviderIfRoot, ActiveToolContextProvider, ActiveToolContextProviderIfRoot, BUNDLE_TOOLS, COARSE_TARGET_SCALE, CURVE_REPS, ColorOverrideRegistry, CropIcon, CursorCoordsHud, DEFAULT_ALPHA_DECAY, DEFAULT_ALPHA_MIN, DEFAULT_DEBUG_STROKES, DEFAULT_DEBUG_THEME, DEFAULT_DEVICE_PROFILE, DEFAULT_FILL_COLOR, DEFAULT_HANDLE_SIZE2 as DEFAULT_HANDLE_SIZE, DEFAULT_PALETTE, DEFAULT_ROTATION_HANDLE_DISTANCE, DEFAULT_STROKE_COLOR, DEFAULT_TEXT_STYLE, DEFAULT_VELOCITY_DECAY, DepRegistryProviderIfRoot, DeviceProfileProvider, DivideIcon, EASINGS, EllipseIcon, ExcludeIcon, EyedropperIcon, FALLBACK_FIT_VIEW, GHOST_STROKE, HANDLE_BASE_PX, HandIcon, IDENTITY_COLOR_MATRIX, IDENTITY_POSE_COMPOSITION, IntersectIcon, KIT_SHAPE_KINDS, LassoIcon, LineIcon, MIXED, MOVE_ANCHORS, MinimapCanvas, NEVER, OUTLINE_MIN_SCREEN_PX, PATH_ANCHOR_CHROME_ID, PenIcon, PencilIcon, PickHud, PointerProviderIfRoot, PolygonIcon, RECT_ALIGN_PROJECTION, ROTATION_HANDLE_BASE_PX, RectIcon, SPRING_PRESETS, SceneCanvas, SceneViewCanvas, SelectIcon, SelectionContextProvider, SelectionContextProviderIfRoot, ShaderCompileError, StarIcon, SubtractIcon, TextIcon, UnionIcon, UnknownIcon, VERSION, WeaselProvider, WeaselRenderer, aabbCenter, actionIs, alignDeltaFor, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, alignedStrokeRect, always, and, animateLifecycle, animateOnSetPose, annulusSemiAxes, applyBooleanOp, applyHitExistingGate, applyStyleToRange, areaSelectAction, arrayAdapter, asNodeId, bezierCubic, bezierQuadratic, buildChromeCtx, buildGradientRamp, buildRuleCtx, buildSceneViewCommands, canBringForward, canHover, canSendBackward, capabilityAll, capabilityIn, capabilityIs, capabilityNot, caretIndexAt, cellAt, charOffsetToDomPosition, clampView, clearSelectionAction, clientToCanvas, clipboardCopyAction, clipboardCutAction, cloneAction, coarsePointer, composeAffordanceLayer, composePath, composeRectPose, composeSelectionPose, composeWorldPose, computeFitView2 as computeFitView, computeIndicatorCommand, computeWheelAction, cond, constrainTo45, containedThenNearest, countPathAnchors, createCellHighlightLayer, createChildrenLayer, createCornerResizeAffordance, createDebugOverlayLayer, createDebugSink, createDispatcher, createGridLayer, createGuidesLayer, createHistory2 as createHistory, createMarkdownRenderer, createMoveToIndexOp, createNodeProperties, createNodeRouting, createParallaxLayer, createPathAnchorAffordances, createPathEditingOverlayLayer, createPathLayer, createPenPreviewLayer, createReorderOp, createRotationAffordance, createScene, createSelectionHandlesLayer, createSelectionOutlineLayer, createSelectionOverlayLayer, createSetDataOp, createSetLayerOp, createSetPathOp, createSetTextOp, createTextLayer, createViewportLayer, cycleVertexColors, decomposePath, decomposeRectPose, defaultCommitAdapter, defaultDrawOne, defaultLabelTextRenderer, defaultNodeProperties, defaultNodeRouting, defaultVisibilityRules, defineTool, defineViewportTool, deriveAlignmentGuides, deriveParallaxView, describeRule, domPositionToCharOffset, domToRuns, drawLayers, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic2 as easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorsAction, enterTextEditAction, enumerateAnchors, evaluate, fillInPoseFrame, fillToBoundsFrame, findNodeShape, findShapeInk, findShapeSilhouette, fitTextPose, fitToBounds, fitViewToBounds, fitZoom, focused, fontString, forEachCoalesced, freeform, gateLayer, gesturing, getAlpha01, getImageBitmap, getNodeShapes, getStylusData, gradientForBounds, gradientGeometry, hexToRgba, hitAffordanceRegions, hitAnchor, hitRotationHandle, hovering, hoveringSelected, imageStatus, inferredNodeProperties, inferredNodeRouting, insertAction, isEditableTarget2 as isEditableTarget, isPatternSpec, lassoSelectAction, layoutMarkdown, lerpColorArray, lerpOklab, lerpOklch, linear, liveScope, makeViewportZoomAction, markdownToRuns, mat3, matchAlignment, matchesKeyBinding, meanScale, measureText, measureTextBounds, measuredWidth, mergeAlphaFromPrev, mergeContributions, modeIn, modeIs, modeNot, modifierHeld, momentum, moveAction, multiActive, nearest, nearestWithin, nestedHitTester, never, nodeHasFlag, none, normalizeHex, not, nurbs, oklabToOklch, oklabToSrgbU8, oklchToOklab, openFilePicker, or, parseColor, parseColorToRgba255, parseDebugFlags, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathDistanceToPoint, pathDivide, pathExclude, pathFromD, pathInPoseFrame, pathInWorld, pathIntersect, pathIntersectsPolygon, pathIntersectsRect, pathOriginProjection, pathSubtract, pathToAnchors, pathUnion, pickTopMostHit, pinchZoomAction, planPixelRender, pointInRotatedRect, pointInTextPose, poseRotationOf, pressureToWidth, rainbowVertexColors, rebaseLocalPose, rectCorners, registerContentHandler, registerNodeShape, registerProgram, renderLabel, renderSceneToCanvas, renderSceneToPixels, resizeAction, resolveColor, resolveDeviceProfile, resolveFillPattern, resolvePatternSpec, resolveRuns, resolveTextStyle, resolveVisibility, rgbaToHex, rotateAction, rotateAroundAABBCenter, rotatePathAround, rotatePoint, rotatedRectCorners, rotationDegreesUnit, rotationHandle, roundToCell, runsToDom, runsToMarkdown, runsToPlainText, sampleGradientStops, sceneFromJSON, sceneNodeClientRect, sceneToAdapter, scopeBindings, screenToWorld, selectFromLasso, selectionAtLeast, selectionEmpty, selectionIs, setFlagOverRange, shapeCoversPoint, sliceAction, snapPoint, solidVertexColors, specificity, spiro, splitPathByLine, splitSubpaths, springPose, springVertexColors, srgbU8ToOklab, staggerVertexColors, startThresholdDrag, styleAtRange, subscribeImageReady, tessellate, tessellateStroke, textCommand, textLineBoxes, tileGrid, toHex8, toRuns, translatePoseViaDescriptor, translateRectPose, tweenPose, tweenVertexColors, unionBounds, unionBoundsPath, useActiveToolContext, useAlign, useAnimator, useArrayAdapter, useAutoCenter, useBooleans, useBooleansAdapter, useCanvasFocus, useCanvasSize, useDecayLoop, useDeviceProfile, useDistribute, useDragGesture, useDragHandle, useDragRadial, useDragRect, useDropZone, useEllipseTool, useEyedropperTool, useGestureDispatcher, useGridCellHover, useGuides, useHandTool, useHandleDrag, useHoverTracking, useImageTool, useKeybindings, useLassoTool, useLineTool, useOptionalActiveToolContext, usePenTool, usePencilTool, usePinchGesture, usePinchZoomTool, usePointerStylus, usePolygonTool, usePublishSelection, useRectTool, useResizePolicy, useRotateTool, useScene, useSceneAdapter, useSceneTextEdit, useSelectTool, useSelection, useSelectionContext, useSimulation, useSliceDep, useStandardActions, useStarTool, useTextEdit, useTextTool, useTools, useVelocityTracker, useViewAnimation, useViewTween, useZoom, verticalAlignOffset, viewToMat3, viewToTransform, viewportDragPanAction, viewportZoomAction, viewportsAt, when, withAlpha01, withCoord, withGradientKind, worldEditToStorage, worldPoseLookup, worldToScreen, zoomAt, zoomAtLeast };
|
|
28342
|
+
//# sourceMappingURL=chunk-SMCZRSYB.js.map
|
|
28343
|
+
//# sourceMappingURL=chunk-SMCZRSYB.js.map
|