@weasel-js/labkit 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-EXBV7A6X.js → chunk-2ZRE7WGQ.js} +3 -3
- package/dist/{chunk-EXBV7A6X.js.map → chunk-2ZRE7WGQ.js.map} +1 -1
- package/dist/{chunk-ISEK5LXT.js → chunk-3P56ZCCJ.js} +1636 -1734
- package/dist/chunk-3P56ZCCJ.js.map +1 -0
- package/dist/{chunk-NRD3TDNQ.js → chunk-R4TACNW7.js} +1461 -1486
- package/dist/chunk-R4TACNW7.js.map +1 -0
- package/dist/index.js +3 -3
- package/dist/passthrough/weasel-canvas.js +1 -1
- package/dist/passthrough/weasel-ui.d.ts +70 -29
- package/dist/passthrough/weasel-ui.js +2 -2
- package/dist/styles.css +147 -0
- package/dist/ui/layers/index.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-ISEK5LXT.js.map +0 -1
- package/dist/chunk-NRD3TDNQ.js.map +0 -1
|
@@ -3,12 +3,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
|
3
3
|
import earcut from 'earcut';
|
|
4
4
|
import polygonClipping from 'polygon-clipping';
|
|
5
5
|
|
|
6
|
-
// ../core/dist/chunk-
|
|
7
|
-
var __defProp = Object.defineProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
6
|
+
// ../core/dist/chunk-W2WB4352.js
|
|
12
7
|
|
|
13
8
|
// ../gestures/dist/index.js
|
|
14
9
|
var GESTURE_DESCRIPTORS = [
|
|
@@ -22,7 +17,12 @@ var GESTURE_DESCRIPTORS = [
|
|
|
22
17
|
{ name: "keyHeld", hasTarget: false, arg: { name: "key", values: "free" } },
|
|
23
18
|
{ name: "contextMenu", hasTarget: true },
|
|
24
19
|
{ name: "longPress", hasTarget: true },
|
|
25
|
-
{ name: "multiTouchTap", hasTarget: false, arg: { name: "fingers", values: ["2", "3", "4"] } }
|
|
20
|
+
{ name: "multiTouchTap", hasTarget: false, arg: { name: "fingers", values: ["2", "3", "4"] } },
|
|
21
|
+
// The arg is the MIME-glob filter. No default: a spec with no `types`
|
|
22
|
+
// matches any payload, which is what an omitted arg slot means for a
|
|
23
|
+
// `'free'` arg.
|
|
24
|
+
{ name: "drop", hasTarget: false, arg: { name: "types", values: "free" } },
|
|
25
|
+
{ name: "paste", hasTarget: false, arg: { name: "types", values: "free" } }
|
|
26
26
|
];
|
|
27
27
|
var BY_NAME = new Map(
|
|
28
28
|
GESTURE_DESCRIPTORS.map((d) => [d.name, d])
|
|
@@ -50,176 +50,11 @@ var RESERVED_ID_NAMES = /* @__PURE__ */ new Set([
|
|
|
50
50
|
"engaged"
|
|
51
51
|
]);
|
|
52
52
|
var VALID_MOD_NAMES = ["mod", "shift", "alt", "ctrl", "meta"];
|
|
53
|
-
|
|
54
|
-
var ACTIVE_SIGILS = /* @__PURE__ */ new Set(["+", "?"]);
|
|
55
|
-
var MOD_NAME_SET = new Set(VALID_MOD_NAMES);
|
|
56
|
-
function parseRoute(input) {
|
|
57
|
-
const trimmed = input.trim();
|
|
58
|
-
if (!trimmed.startsWith("[")) {
|
|
59
|
-
throw new Error(`invalid route (phase brackets required): ${input}`);
|
|
60
|
-
}
|
|
61
|
-
const closeIdx = trimmed.indexOf("]");
|
|
62
|
-
if (closeIdx < 0) throw new Error(`invalid route (unclosed phase bracket): ${input}`);
|
|
63
|
-
const phaseListRaw = trimmed.slice(1, closeIdx).trim();
|
|
64
|
-
const phases = parsePhaseList(phaseListRaw, input);
|
|
65
|
-
let rest = trimmed.slice(closeIdx + 1).trim();
|
|
66
|
-
const gestureMatch = /^([A-Za-z]+)/.exec(rest);
|
|
67
|
-
if (!gestureMatch) throw new Error(`invalid route (no gesture name): ${input}`);
|
|
68
|
-
const gestureName = gestureMatch[1];
|
|
69
|
-
if (!isKnownGestureName(gestureName)) {
|
|
70
|
-
throw new Error(`invalid route (unknown gesture "${gestureName}"): ${input}`);
|
|
71
|
-
}
|
|
72
|
-
rest = rest.slice(gestureMatch[0].length).trimStart();
|
|
73
|
-
const desc = getGestureDescriptor(gestureName);
|
|
74
|
-
let arg;
|
|
75
|
-
if (rest.startsWith("(")) {
|
|
76
|
-
const closeArg = rest.indexOf(")");
|
|
77
|
-
if (closeArg < 0) throw new Error(`invalid route (unbalanced arg parens): ${input}`);
|
|
78
|
-
const argRaw = rest.slice(1, closeArg);
|
|
79
|
-
if (!desc.arg) throw new Error(`invalid route (${gestureName} has no arg): ${input}`);
|
|
80
|
-
if (argRaw !== "*" && desc.arg.values !== "free" && !desc.arg.values.includes(argRaw)) {
|
|
81
|
-
throw new Error(`invalid route ("${argRaw}" not in ${desc.arg.values.join("|")}): ${input}`);
|
|
82
|
-
}
|
|
83
|
-
arg = argRaw;
|
|
84
|
-
rest = rest.slice(closeArg + 1).trimStart();
|
|
85
|
-
} else if (desc.arg) {
|
|
86
|
-
arg = desc.arg.default ?? "*";
|
|
87
|
-
}
|
|
88
|
-
let target;
|
|
89
|
-
if (rest.startsWith("=>")) {
|
|
90
|
-
if (!desc.hasTarget) throw new Error(`invalid route (${gestureName} has no target): ${input}`);
|
|
91
|
-
rest = rest.slice(2).trimStart();
|
|
92
|
-
const tgtMatch = /^([^\s+?!@#$%^&]+)/.exec(rest);
|
|
93
|
-
if (!tgtMatch) throw new Error(`invalid route (missing target after "=>"): ${input}`);
|
|
94
|
-
target = tgtMatch[1];
|
|
95
|
-
rest = rest.slice(tgtMatch[0].length).trimStart();
|
|
96
|
-
} else if (desc.hasTarget) {
|
|
97
|
-
target = "*";
|
|
98
|
-
}
|
|
99
|
-
const modifiers = {};
|
|
100
|
-
while (rest.length > 0) {
|
|
101
|
-
const ch = rest[0];
|
|
102
|
-
if (RESERVED_SIGILS.has(ch)) {
|
|
103
|
-
throw new Error(`invalid route ("${ch}" is reserved for future use): ${input}`);
|
|
104
|
-
}
|
|
105
|
-
if (!ACTIVE_SIGILS.has(ch)) {
|
|
106
|
-
throw new Error(`invalid route (unexpected "${ch}" at "${rest}"): ${input}`);
|
|
107
|
-
}
|
|
108
|
-
const sigil = ch;
|
|
109
|
-
rest = rest.slice(1);
|
|
110
|
-
const nameMatch = /^([a-z]+)/.exec(rest);
|
|
111
|
-
if (!nameMatch) throw new Error(`invalid route (sigil "${sigil}" without modifier name): ${input}`);
|
|
112
|
-
const name = nameMatch[1];
|
|
113
|
-
if (!MOD_NAME_SET.has(name)) {
|
|
114
|
-
throw new Error(`invalid route (unknown modifier "${name}"): ${input}`);
|
|
115
|
-
}
|
|
116
|
-
if (modifiers[name] !== void 0) {
|
|
117
|
-
throw new Error(`invalid route (duplicate modifier "${name}"): ${input}`);
|
|
118
|
-
}
|
|
119
|
-
modifiers[name] = sigil === "+" ? "required" : "optional";
|
|
120
|
-
rest = rest.slice(nameMatch[0].length).trimStart();
|
|
121
|
-
}
|
|
122
|
-
return { phases, gesture: gestureName, arg, target, modifiers };
|
|
123
|
-
}
|
|
124
|
-
function parsePhaseList(raw, input) {
|
|
125
|
-
if (raw === "") throw new Error(`invalid route (empty phase list): ${input}`);
|
|
126
|
-
const tokens = raw.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
|
|
127
|
-
if (tokens.length === 0) throw new Error(`invalid route (empty phase list): ${input}`);
|
|
128
|
-
return tokens.map((t) => parsePhaseAtom(t, input));
|
|
129
|
-
}
|
|
130
|
-
var VALID_PHASES = /* @__PURE__ */ new Set(["initial", "engaged", "*"]);
|
|
131
|
-
function parsePhaseAtom(raw, input) {
|
|
132
|
-
const parts = raw.split(":");
|
|
133
|
-
if (parts.length > 2) {
|
|
134
|
-
throw new Error(`invalid route (phase atom "${raw}" has multiple ":"): ${input}`);
|
|
135
|
-
}
|
|
136
|
-
if (parts.length === 1) {
|
|
137
|
-
const phase2 = parts[0];
|
|
138
|
-
if (!VALID_PHASES.has(phase2)) {
|
|
139
|
-
throw new Error(`invalid route (unknown phase "${phase2}" in "${raw}"): ${input}`);
|
|
140
|
-
}
|
|
141
|
-
return { channel: "&", phase: phase2 };
|
|
142
|
-
}
|
|
143
|
-
const channel = parts[0];
|
|
144
|
-
const phase = parts[1];
|
|
145
|
-
if (channel === "") {
|
|
146
|
-
throw new Error(`invalid route (empty channel before ":" in "${raw}"): ${input}`);
|
|
147
|
-
}
|
|
148
|
-
if (!VALID_PHASES.has(phase)) {
|
|
149
|
-
throw new Error(`invalid route (unknown phase "${phase}" in "${raw}"): ${input}`);
|
|
150
|
-
}
|
|
151
|
-
if (channel !== "&" && channel !== "*") {
|
|
152
|
-
if (RESERVED_ID_NAMES.has(channel)) {
|
|
153
|
-
throw new Error(`invalid route (channel "${channel}" is a reserved phase keyword): ${input}`);
|
|
154
|
-
}
|
|
155
|
-
if (RESERVED_ID_PREFIXES.has(channel[0])) {
|
|
156
|
-
throw new Error(`invalid route (channel "${channel}" starts with reserved sigil "${channel[0]}"): ${input}`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return { channel, phase };
|
|
160
|
-
}
|
|
53
|
+
new Set(VALID_MOD_NAMES);
|
|
161
54
|
function formatPhaseAtom(a) {
|
|
162
55
|
if (a.channel === "&") return a.phase;
|
|
163
56
|
return `${a.channel}:${a.phase}`;
|
|
164
57
|
}
|
|
165
|
-
function collapseShiftPairs(routes) {
|
|
166
|
-
if (routes.length < 2) return [...routes];
|
|
167
|
-
const parsed = routes.map((r) => ({ raw: r, p: parseRoute(r) }));
|
|
168
|
-
const consumed = /* @__PURE__ */ new Set();
|
|
169
|
-
const out = [];
|
|
170
|
-
for (let i = 0; i < parsed.length; i++) {
|
|
171
|
-
if (consumed.has(i)) continue;
|
|
172
|
-
const a = parsed[i];
|
|
173
|
-
let twinIndex = -1;
|
|
174
|
-
for (let j = i + 1; j < parsed.length; j++) {
|
|
175
|
-
if (consumed.has(j)) continue;
|
|
176
|
-
const b = parsed[j];
|
|
177
|
-
if (isShiftTwin(a.p, b.p)) {
|
|
178
|
-
twinIndex = j;
|
|
179
|
-
break;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
if (twinIndex < 0) {
|
|
183
|
-
out.push(a.raw);
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
consumed.add(twinIndex);
|
|
187
|
-
const folded = {
|
|
188
|
-
...a.p,
|
|
189
|
-
modifiers: { ...withoutShift(a.p.modifiers), shift: "optional" }
|
|
190
|
-
};
|
|
191
|
-
out.push(formatRoute(folded));
|
|
192
|
-
}
|
|
193
|
-
return out;
|
|
194
|
-
}
|
|
195
|
-
function isShiftTwin(a, b) {
|
|
196
|
-
if (a.gesture !== b.gesture) return false;
|
|
197
|
-
if (a.arg !== b.arg) return false;
|
|
198
|
-
if (a.target !== b.target) return false;
|
|
199
|
-
if (!samePhases(a.phases, b.phases)) return false;
|
|
200
|
-
const aShift = a.modifiers.shift;
|
|
201
|
-
const bShift = b.modifiers.shift;
|
|
202
|
-
const aHas = aShift === "required";
|
|
203
|
-
const bHas = bShift === "required";
|
|
204
|
-
if (!(aHas !== bHas)) return false;
|
|
205
|
-
if (aShift === "optional" || bShift === "optional") return false;
|
|
206
|
-
for (const name of VALID_MOD_NAMES) {
|
|
207
|
-
if (name === "shift") continue;
|
|
208
|
-
if (a.modifiers[name] !== b.modifiers[name]) return false;
|
|
209
|
-
}
|
|
210
|
-
return true;
|
|
211
|
-
}
|
|
212
|
-
function samePhases(a, b) {
|
|
213
|
-
if (a.length !== b.length) return false;
|
|
214
|
-
for (let i = 0; i < a.length; i++) {
|
|
215
|
-
if (a[i].channel !== b[i].channel || a[i].phase !== b[i].phase) return false;
|
|
216
|
-
}
|
|
217
|
-
return true;
|
|
218
|
-
}
|
|
219
|
-
function withoutShift(m) {
|
|
220
|
-
const { shift: _shift, ...rest } = m;
|
|
221
|
-
return rest;
|
|
222
|
-
}
|
|
223
58
|
var MOD_ORDER = ["mod", "shift", "alt", "ctrl", "meta"];
|
|
224
59
|
function formatRoute(r) {
|
|
225
60
|
const desc = getGestureDescriptor(r.gesture);
|
|
@@ -240,122 +75,6 @@ function formatRoute(r) {
|
|
|
240
75
|
return out;
|
|
241
76
|
}
|
|
242
77
|
new Set(VALID_MOD_NAMES);
|
|
243
|
-
var MOD_NAMES = {
|
|
244
|
-
mod: "Mod",
|
|
245
|
-
shift: "Shift",
|
|
246
|
-
alt: "Alt",
|
|
247
|
-
ctrl: "Ctrl",
|
|
248
|
-
meta: "Meta"
|
|
249
|
-
};
|
|
250
|
-
var MOD_ORDER2 = ["mod", "shift", "alt", "ctrl", "meta"];
|
|
251
|
-
var ROUTE_TERMS = {
|
|
252
|
-
idle: "Not in the middle of a drag or other synchronous operation.",
|
|
253
|
-
"mid-gesture": "In the middle of a drag or other synchronous operation."
|
|
254
|
-
};
|
|
255
|
-
var ROUTE_FIELD_DEFINITIONS = {
|
|
256
|
-
phases: "Which lifecycle stage(s) a channel must be in for this route to fire. `initial` = the channel is idle; `engaged` = the channel is mid-gesture. The channel is the binding's own tool (`&`), any tool (`*`), or a named tool id.",
|
|
257
|
-
gesture: "The class of input event that triggers this route \u2014 click, drag, double-tap, keyDown/keyUp, wheel, contextMenu, or multiTouchTap.",
|
|
258
|
-
arg: "Sub-class of the gesture. Direction for wheel (up / down / *), key name for keyDown / keyUp, finger count for multiTouchTap. Other gestures have no arg slot.",
|
|
259
|
-
target: "Which hit-test result the gesture must land on. `*` matches any target; `empty` matches the empty canvas; otherwise the value names a specific hit-target kind.",
|
|
260
|
-
modifiers: "Modifier keys the user must hold (`+key`) or may optionally hold (`?key`) for this route to match. Unlisted modifiers must not be held."
|
|
261
|
-
};
|
|
262
|
-
function term(label) {
|
|
263
|
-
return { kind: "term", label, definition: ROUTE_TERMS[label] };
|
|
264
|
-
}
|
|
265
|
-
function joinAnd(parts) {
|
|
266
|
-
if (parts.length === 0) return "";
|
|
267
|
-
if (parts.length === 1) return parts[0];
|
|
268
|
-
if (parts.length === 2) return `${parts[0]} and ${parts[1]}`;
|
|
269
|
-
return `${parts.slice(0, -1).join(", ")}, and ${parts[parts.length - 1]}`;
|
|
270
|
-
}
|
|
271
|
-
function partitionModifiers(mods2) {
|
|
272
|
-
const required = [];
|
|
273
|
-
const optional = [];
|
|
274
|
-
for (const name of MOD_ORDER2) {
|
|
275
|
-
const req = mods2[name];
|
|
276
|
-
if (req === "required") required.push(MOD_NAMES[name]);
|
|
277
|
-
else if (req === "optional") optional.push(MOD_NAMES[name]);
|
|
278
|
-
}
|
|
279
|
-
return { required, optional };
|
|
280
|
-
}
|
|
281
|
-
function phaseAtomParts({ channel, phase }) {
|
|
282
|
-
const subject = channel === "&" ? "the tool" : channel === "*" ? "any tool" : `the ${channel} tool`;
|
|
283
|
-
if (phase === "initial") return [`${subject} is `, term("idle")];
|
|
284
|
-
if (phase === "engaged") return [`${subject} is `, term("mid-gesture")];
|
|
285
|
-
return [`${subject} is in any phase`];
|
|
286
|
-
}
|
|
287
|
-
function phasesParts(phases) {
|
|
288
|
-
if (phases.length === 1) return phaseAtomParts(phases[0]);
|
|
289
|
-
const out = [];
|
|
290
|
-
phases.forEach((p, i) => {
|
|
291
|
-
if (i > 0) out.push(" or ");
|
|
292
|
-
out.push(...phaseAtomParts(p));
|
|
293
|
-
});
|
|
294
|
-
return out;
|
|
295
|
-
}
|
|
296
|
-
function targetClause(target, hasTarget) {
|
|
297
|
-
if (!hasTarget || target === void 0) return "";
|
|
298
|
-
if (target === "*") return " anywhere";
|
|
299
|
-
if (target === "empty") return " on empty canvas";
|
|
300
|
-
return ` on a ${target}`;
|
|
301
|
-
}
|
|
302
|
-
function actionClause(parsed, required) {
|
|
303
|
-
const desc = getGestureDescriptor(parsed.gesture);
|
|
304
|
-
const modPrefix = required.length > 0 ? `${joinAnd(required)}-` : "";
|
|
305
|
-
const target = targetClause(parsed.target, desc.hasTarget);
|
|
306
|
-
switch (parsed.gesture) {
|
|
307
|
-
case "keyDown":
|
|
308
|
-
case "keyUp":
|
|
309
|
-
case "keyHeld": {
|
|
310
|
-
const verb = parsed.gesture === "keyDown" ? "presses" : parsed.gesture === "keyUp" ? "releases" : "holds";
|
|
311
|
-
const key = parsed.arg ?? "any key";
|
|
312
|
-
return required.length > 0 ? `the user holds ${joinAnd(required)} and ${verb} ${key}` : `the user ${verb} ${key}`;
|
|
313
|
-
}
|
|
314
|
-
case "wheel": {
|
|
315
|
-
const direction = parsed.arg === "up" ? " up" : parsed.arg === "down" ? " down" : "";
|
|
316
|
-
return `the user ${modPrefix}scrolls${direction}`;
|
|
317
|
-
}
|
|
318
|
-
case "multiTouchTap":
|
|
319
|
-
return `the user taps with ${parsed.arg ?? "multiple"} fingers`;
|
|
320
|
-
case "contextMenu":
|
|
321
|
-
return `the user ${modPrefix}opens the context menu${target}`;
|
|
322
|
-
case "longPress":
|
|
323
|
-
return `the user ${modPrefix}presses and holds${target}`;
|
|
324
|
-
case "click":
|
|
325
|
-
return `the user ${modPrefix}clicks${target}`;
|
|
326
|
-
case "pointerDown":
|
|
327
|
-
return `the user ${modPrefix}presses${target}`;
|
|
328
|
-
case "dblTap":
|
|
329
|
-
return `the user ${modPrefix}double-taps${target}`;
|
|
330
|
-
case "drag":
|
|
331
|
-
return `the user ${modPrefix}drags${target}`;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
function describeRouteParts(parsed, opts = {}) {
|
|
335
|
-
const { capitalize = true, period = true } = opts;
|
|
336
|
-
const { required, optional } = partitionModifiers(parsed.modifiers);
|
|
337
|
-
const action = actionClause(parsed, required);
|
|
338
|
-
const optClause = optional.length > 0 ? ` (${joinAnd(optional)} optional)` : "";
|
|
339
|
-
const parts = [];
|
|
340
|
-
const head = `fires when ${action}, while `;
|
|
341
|
-
parts.push(capitalize ? head.charAt(0).toUpperCase() + head.slice(1) : head);
|
|
342
|
-
parts.push(...phasesParts(parsed.phases));
|
|
343
|
-
if (optClause) parts.push(optClause);
|
|
344
|
-
if (period) parts.push(".");
|
|
345
|
-
return mergeAdjacentStrings(parts);
|
|
346
|
-
}
|
|
347
|
-
function describeRoute(parsed, opts = {}) {
|
|
348
|
-
return describeRouteParts(parsed, opts).map((p) => typeof p === "string" ? p : p.label).join("");
|
|
349
|
-
}
|
|
350
|
-
function mergeAdjacentStrings(parts) {
|
|
351
|
-
const out = [];
|
|
352
|
-
for (const p of parts) {
|
|
353
|
-
const last = out[out.length - 1];
|
|
354
|
-
if (typeof p === "string" && typeof last === "string") out[out.length - 1] = last + p;
|
|
355
|
-
else out.push(p);
|
|
356
|
-
}
|
|
357
|
-
return out;
|
|
358
|
-
}
|
|
359
78
|
function canonicalModifiers(mods2) {
|
|
360
79
|
return Object.entries(mods2).sort(([a], [b]) => a.localeCompare(b)).map(([name, req]) => `${name}=${req}`).join("&");
|
|
361
80
|
}
|
|
@@ -568,144 +287,8 @@ function matchSpec(e, spec, isMac, phaseCtx) {
|
|
|
568
287
|
}
|
|
569
288
|
}
|
|
570
289
|
var EMPTY_ENGAGED = /* @__PURE__ */ new Set();
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
GESTURE_DESCRIPTORS: () => GESTURE_DESCRIPTORS,
|
|
574
|
-
PREDICATE_TARGET: () => PREDICATE_TARGET,
|
|
575
|
-
RESERVED_ID_NAMES: () => RESERVED_ID_NAMES,
|
|
576
|
-
RESERVED_ID_PREFIXES: () => RESERVED_ID_PREFIXES,
|
|
577
|
-
ROUTE_FIELD_DEFINITIONS: () => ROUTE_FIELD_DEFINITIONS,
|
|
578
|
-
ROUTE_TERMS: () => ROUTE_TERMS,
|
|
579
|
-
buildRouteRegistry: () => buildRouteRegistry,
|
|
580
|
-
canonicalModifiers: () => canonicalModifiers,
|
|
581
|
-
collapseShiftPairs: () => collapseShiftPairs,
|
|
582
|
-
defineTool: () => defineTool,
|
|
583
|
-
defineViewportTool: () => defineViewportTool,
|
|
584
|
-
describeRoute: () => describeRoute,
|
|
585
|
-
describeRouteParts: () => describeRouteParts,
|
|
586
|
-
findConflicts: () => findConflicts,
|
|
587
|
-
findScopedConflicts: () => findScopedConflicts,
|
|
588
|
-
formatConflict: () => formatConflict,
|
|
589
|
-
formatPhaseAtom: () => formatPhaseAtom,
|
|
590
|
-
formatRoute: () => formatRoute,
|
|
591
|
-
getGestureDescriptor: () => getGestureDescriptor,
|
|
592
|
-
isKnownGestureName: () => isKnownGestureName,
|
|
593
|
-
parseRoute: () => parseRoute,
|
|
594
|
-
reportRouteConflicts: () => reportRouteConflicts
|
|
595
|
-
});
|
|
596
|
-
function validateId(id, kind) {
|
|
597
|
-
if (id.length === 0) {
|
|
598
|
-
throw new Error(`weasel: ${kind} id may not be empty`);
|
|
599
|
-
}
|
|
600
|
-
if (RESERVED_ID_PREFIXES.has(id[0])) {
|
|
601
|
-
throw new Error(
|
|
602
|
-
`weasel: ${kind} id "${id}" starts with reserved sigil "${id[0]}" (reserved set: ${[...RESERVED_ID_PREFIXES].join(" ")})`
|
|
603
|
-
);
|
|
604
|
-
}
|
|
605
|
-
if (RESERVED_ID_NAMES.has(id)) {
|
|
606
|
-
throw new Error(
|
|
607
|
-
`weasel: ${kind} id "${id}" collides with a reserved phase keyword (reserved: ${[...RESERVED_ID_NAMES].join(", ")})`
|
|
608
|
-
);
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
function defineTool(def) {
|
|
612
|
-
validateId(def.id, "tool");
|
|
613
|
-
const resolveCursor = (ctx) => {
|
|
614
|
-
if (def.cursor == null) return "";
|
|
615
|
-
return typeof def.cursor === "function" ? def.cursor(ctx) : def.cursor;
|
|
616
|
-
};
|
|
617
|
-
return {
|
|
618
|
-
id: def.id,
|
|
619
|
-
eligibility: {
|
|
620
|
-
focus: true,
|
|
621
|
-
capabilities: def.capabilities,
|
|
622
|
-
...def.hotkey ? { offhand: def.hotkey } : {}
|
|
623
|
-
},
|
|
624
|
-
actions: def.actions,
|
|
625
|
-
def,
|
|
626
|
-
presentation: def.presentation,
|
|
627
|
-
keybinding: def.keybinding,
|
|
628
|
-
onActivate: def.onActivate,
|
|
629
|
-
onDeactivate: def.onDeactivate,
|
|
630
|
-
initScratch: def.initScratch ?? (() => null),
|
|
631
|
-
cursor: resolveCursor,
|
|
632
|
-
bindings: def.bindings,
|
|
633
|
-
overlay: def.overlay
|
|
634
|
-
};
|
|
635
|
-
}
|
|
636
|
-
function defineViewportTool(def) {
|
|
637
|
-
return defineTool(def);
|
|
638
|
-
}
|
|
639
|
-
var PREDICATE_TARGET = "predicate";
|
|
640
|
-
var SPEC_KIND_TO_GESTURE = {
|
|
641
|
-
key: "keyDown",
|
|
642
|
-
"key-held": "keyHeld",
|
|
643
|
-
wheel: "wheel",
|
|
644
|
-
click: "click",
|
|
645
|
-
doubleClick: "dblTap",
|
|
646
|
-
contextMenu: "contextMenu",
|
|
647
|
-
longPress: "longPress",
|
|
648
|
-
drag: "drag",
|
|
649
|
-
pointerDown: "pointerDown",
|
|
650
|
-
multiTouch: void 0,
|
|
651
|
-
multiTouchTap: "multiTouchTap",
|
|
652
|
-
drop: void 0,
|
|
653
|
-
paste: void 0
|
|
654
|
-
};
|
|
655
|
-
function buildRouteRegistry(tools) {
|
|
656
|
-
const out = [];
|
|
657
|
-
for (const tool of tools) {
|
|
658
|
-
for (const binding of tool.bindings ?? []) {
|
|
659
|
-
const entry = entryFor(tool.id, binding.spec, binding.actionId);
|
|
660
|
-
if (entry) out.push(entry);
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
return out;
|
|
664
|
-
}
|
|
665
|
-
function entryFor(toolId, spec, actionId) {
|
|
666
|
-
const gesture = SPEC_KIND_TO_GESTURE[spec.kind];
|
|
667
|
-
if (!gesture || !isKnownGestureName(gesture)) return null;
|
|
668
|
-
const descriptor = getGestureDescriptor(gesture);
|
|
669
|
-
return {
|
|
670
|
-
toolId,
|
|
671
|
-
actionId,
|
|
672
|
-
gesture,
|
|
673
|
-
phase: phaseOf("phase" in spec ? spec.phase : void 0),
|
|
674
|
-
modifiers: parseModSpec("mods" in spec ? spec.mods : void 0),
|
|
675
|
-
arg: descriptor.arg ? argOf(spec, descriptor.arg.default) : void 0,
|
|
676
|
-
target: descriptor.hasTarget ? targetOf("target" in spec ? spec.target : void 0) : void 0,
|
|
677
|
-
spec
|
|
678
|
-
};
|
|
679
|
-
}
|
|
680
|
-
function phaseOf(phase) {
|
|
681
|
-
if (phase === void 0 || phase === "*") return "any";
|
|
682
|
-
if (phase === "initial" || phase === "engaged") return phase;
|
|
683
|
-
const distinct = new Set(phase.map((atom) => atom.phase));
|
|
684
|
-
if (distinct.size !== 1) return "any";
|
|
685
|
-
const only = [...distinct][0];
|
|
686
|
-
return only === "initial" || only === "engaged" ? only : "any";
|
|
687
|
-
}
|
|
688
|
-
function parseModSpec(mods) {
|
|
689
|
-
if (!mods) return {};
|
|
690
|
-
const out = {};
|
|
691
|
-
for (const [name, req] of Object.entries(mods)) {
|
|
692
|
-
if (req === true) out[name] = "required";
|
|
693
|
-
else if (req === "optional") out[name] = "optional";
|
|
694
|
-
}
|
|
695
|
-
return out;
|
|
696
|
-
}
|
|
697
|
-
function targetOf(target) {
|
|
698
|
-
if (target === void 0) return void 0;
|
|
699
|
-
return typeof target === "string" ? target : PREDICATE_TARGET;
|
|
700
|
-
}
|
|
701
|
-
function argOf(spec, fallback) {
|
|
702
|
-
if ("key" in spec) {
|
|
703
|
-
return Array.isArray(spec.key) ? spec.key.join("|") : spec.key;
|
|
704
|
-
}
|
|
705
|
-
if ("fingers" in spec) return String(spec.fingers);
|
|
706
|
-
if ("direction" in spec) return spec.direction ?? fallback;
|
|
707
|
-
return fallback;
|
|
708
|
-
}
|
|
290
|
+
|
|
291
|
+
// ../core/dist/chunk-W2WB4352.js
|
|
709
292
|
var DepRegistryContext = createContext(null);
|
|
710
293
|
function DepRegistryProvider({ children }) {
|
|
711
294
|
const sourcesRef = useRef(/* @__PURE__ */ new Map());
|
|
@@ -955,6 +538,77 @@ function ActionsProvider({ children }) {
|
|
|
955
538
|
function useActionsRegistry() {
|
|
956
539
|
return useContext(ActionsContext);
|
|
957
540
|
}
|
|
541
|
+
var PREDICATE_TARGET = "predicate";
|
|
542
|
+
var SPEC_KIND_TO_GESTURE = {
|
|
543
|
+
key: "keyDown",
|
|
544
|
+
"key-held": "keyHeld",
|
|
545
|
+
wheel: "wheel",
|
|
546
|
+
click: "click",
|
|
547
|
+
doubleClick: "dblTap",
|
|
548
|
+
contextMenu: "contextMenu",
|
|
549
|
+
longPress: "longPress",
|
|
550
|
+
drag: "drag",
|
|
551
|
+
pointerDown: "pointerDown",
|
|
552
|
+
multiTouch: void 0,
|
|
553
|
+
multiTouchTap: "multiTouchTap",
|
|
554
|
+
drop: "drop",
|
|
555
|
+
paste: "paste"
|
|
556
|
+
};
|
|
557
|
+
function buildRouteRegistry(tools) {
|
|
558
|
+
const out = [];
|
|
559
|
+
for (const tool of tools) {
|
|
560
|
+
for (const binding of tool.bindings ?? []) {
|
|
561
|
+
const entry = entryFor(tool.id, binding.spec, binding.actionId);
|
|
562
|
+
if (entry) out.push(entry);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return out;
|
|
566
|
+
}
|
|
567
|
+
function entryFor(toolId, spec, actionId) {
|
|
568
|
+
const gesture = SPEC_KIND_TO_GESTURE[spec.kind];
|
|
569
|
+
if (!gesture || !isKnownGestureName(gesture)) return null;
|
|
570
|
+
const descriptor = getGestureDescriptor(gesture);
|
|
571
|
+
return {
|
|
572
|
+
toolId,
|
|
573
|
+
actionId,
|
|
574
|
+
gesture,
|
|
575
|
+
phase: phaseOf("phase" in spec ? spec.phase : void 0),
|
|
576
|
+
modifiers: parseModSpec("mods" in spec ? spec.mods : void 0),
|
|
577
|
+
arg: descriptor.arg ? argOf(spec, descriptor.arg.default) : void 0,
|
|
578
|
+
target: descriptor.hasTarget ? targetOf("target" in spec ? spec.target : void 0) : void 0,
|
|
579
|
+
spec
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
function phaseOf(phase) {
|
|
583
|
+
if (phase === void 0 || phase === "*") return "any";
|
|
584
|
+
if (phase === "initial" || phase === "engaged") return phase;
|
|
585
|
+
const distinct = new Set(phase.map((atom) => atom.phase));
|
|
586
|
+
if (distinct.size !== 1) return "any";
|
|
587
|
+
const only = [...distinct][0];
|
|
588
|
+
return only === "initial" || only === "engaged" ? only : "any";
|
|
589
|
+
}
|
|
590
|
+
function parseModSpec(mods) {
|
|
591
|
+
if (!mods) return {};
|
|
592
|
+
const out = {};
|
|
593
|
+
for (const [name, req] of Object.entries(mods)) {
|
|
594
|
+
if (req === true) out[name] = "required";
|
|
595
|
+
else if (req === "optional") out[name] = "optional";
|
|
596
|
+
}
|
|
597
|
+
return out;
|
|
598
|
+
}
|
|
599
|
+
function targetOf(target) {
|
|
600
|
+
if (target === void 0) return void 0;
|
|
601
|
+
return typeof target === "string" ? target : PREDICATE_TARGET;
|
|
602
|
+
}
|
|
603
|
+
function argOf(spec, fallback) {
|
|
604
|
+
if ("key" in spec) {
|
|
605
|
+
return Array.isArray(spec.key) ? spec.key.join("|") : spec.key;
|
|
606
|
+
}
|
|
607
|
+
if ("fingers" in spec) return String(spec.fingers);
|
|
608
|
+
if ("direction" in spec) return spec.direction ?? fallback;
|
|
609
|
+
if ("types" in spec) return spec.types?.length ? spec.types.join("|") : fallback;
|
|
610
|
+
return fallback;
|
|
611
|
+
}
|
|
958
612
|
function findConflicts(tools) {
|
|
959
613
|
const entries = buildRouteRegistry(tools);
|
|
960
614
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -2687,17 +2341,17 @@ function dynamicPageTextureId(page) {
|
|
|
2687
2341
|
return `weasel-dyn-sdf-page-${page}`;
|
|
2688
2342
|
}
|
|
2689
2343
|
var uploadedVersions = /* @__PURE__ */ new WeakMap();
|
|
2690
|
-
function syncDynamicPageTexture(
|
|
2344
|
+
function syncDynamicPageTexture(cache8, pageIndex) {
|
|
2691
2345
|
const page = pages[pageIndex];
|
|
2692
2346
|
if (!page) return false;
|
|
2693
2347
|
const id = dynamicPageTextureId(pageIndex);
|
|
2694
|
-
let seen = uploadedVersions.get(
|
|
2348
|
+
let seen = uploadedVersions.get(cache8);
|
|
2695
2349
|
if (!seen) {
|
|
2696
2350
|
seen = /* @__PURE__ */ new Map();
|
|
2697
|
-
uploadedVersions.set(
|
|
2351
|
+
uploadedVersions.set(cache8, seen);
|
|
2698
2352
|
}
|
|
2699
|
-
if (!
|
|
2700
|
-
|
|
2353
|
+
if (!cache8.has(id)) {
|
|
2354
|
+
cache8.uploadR8(id, PAGE_SIZE, PAGE_SIZE, page.data);
|
|
2701
2355
|
seen.set(pageIndex, page.version);
|
|
2702
2356
|
return true;
|
|
2703
2357
|
}
|
|
@@ -2710,7 +2364,7 @@ function syncDynamicPageTexture(cache7, pageIndex) {
|
|
|
2710
2364
|
const src = (patch.y + row) * PAGE_SIZE + patch.x;
|
|
2711
2365
|
tight.set(page.data.subarray(src, src + patch.w), row * patch.w);
|
|
2712
2366
|
}
|
|
2713
|
-
|
|
2367
|
+
cache8.subImageR8(id, patch.x, patch.y, patch.w, patch.h, tight);
|
|
2714
2368
|
}
|
|
2715
2369
|
seen.set(pageIndex, page.version);
|
|
2716
2370
|
return true;
|
|
@@ -3477,7 +3131,7 @@ function serialToEntry(se, custom, logger) {
|
|
|
3477
3131
|
};
|
|
3478
3132
|
}
|
|
3479
3133
|
|
|
3480
|
-
// ../core/dist/chunk-
|
|
3134
|
+
// ../core/dist/chunk-PI56ORHE.js
|
|
3481
3135
|
function worldToScreen(worldX, worldY, view) {
|
|
3482
3136
|
return [view.panX + worldX * view.zoom.x, view.panY + worldY * view.zoom.y];
|
|
3483
3137
|
}
|
|
@@ -3791,6 +3445,8 @@ var GLMeshCache = class {
|
|
|
3791
3445
|
gl;
|
|
3792
3446
|
aPositionLoc;
|
|
3793
3447
|
map = /* @__PURE__ */ new WeakMap();
|
|
3448
|
+
/** Meshes this context has drawn at least once; see `uploadRecurring`. */
|
|
3449
|
+
seen = /* @__PURE__ */ new WeakSet();
|
|
3794
3450
|
finalizer;
|
|
3795
3451
|
pendingDeletes = [];
|
|
3796
3452
|
/** Transient resources allocated this frame; freed at end of render(). */
|
|
@@ -3815,6 +3471,19 @@ var GLMeshCache = class {
|
|
|
3815
3471
|
this.transientThisFrame.push({ vao: handle.vao, vbo, ibo });
|
|
3816
3472
|
return handle;
|
|
3817
3473
|
}
|
|
3474
|
+
/**
|
|
3475
|
+
* Upload a Mesh that may or may not recur across frames. Its first sight in
|
|
3476
|
+
* *this* context takes `uploadTransient`; every later one takes the
|
|
3477
|
+
* persistent handle. One transient upload to find out is cheaper than
|
|
3478
|
+
* stranding a persistent VAO on a mesh that never returns, whose release
|
|
3479
|
+
* would wait on GC. The transient upload does not populate the persistent
|
|
3480
|
+
* map, so steady-state reuse begins on the third frame.
|
|
3481
|
+
*/
|
|
3482
|
+
uploadRecurring(mesh) {
|
|
3483
|
+
if (this.seen.has(mesh)) return this.handleFor(mesh);
|
|
3484
|
+
this.seen.add(mesh);
|
|
3485
|
+
return this.uploadTransient(mesh);
|
|
3486
|
+
}
|
|
3818
3487
|
/**
|
|
3819
3488
|
* Free all transient resources allocated since the last call. Called by
|
|
3820
3489
|
* the renderer at the end of each `render()`. Safe under context loss.
|
|
@@ -4881,1258 +4550,1258 @@ function getMesh(path) {
|
|
|
4881
4550
|
cache.set(path, mesh);
|
|
4882
4551
|
return mesh;
|
|
4883
4552
|
}
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4553
|
+
var UNDERLINE_OFFSET = 0.1;
|
|
4554
|
+
var STRIKETHROUGH_OFFSET = -0.3;
|
|
4555
|
+
var DECORATION_THICKNESS = 0.05;
|
|
4556
|
+
function fillKey(p) {
|
|
4557
|
+
if ("color" in p) return `s:${p.color}:${p.opacity ?? 1}`;
|
|
4558
|
+
return `nx:${Math.random()}`;
|
|
4887
4559
|
}
|
|
4888
|
-
function
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
anchorB: new Uint32Array([0, 1, 2, 3]),
|
|
4895
|
-
anchorT: new Float32Array([0, 0, 0, 0])
|
|
4896
|
-
};
|
|
4560
|
+
function sameFill(a, b) {
|
|
4561
|
+
if (a === b) return true;
|
|
4562
|
+
if ("color" in a && "color" in b) {
|
|
4563
|
+
return a.color === b.color && (a.opacity ?? 1) === (b.opacity ?? 1);
|
|
4564
|
+
}
|
|
4565
|
+
return false;
|
|
4897
4566
|
}
|
|
4898
|
-
function
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
let prevX = 0;
|
|
4910
|
-
let prevY = 0;
|
|
4911
|
-
let prevAnchor = -1;
|
|
4912
|
-
const beginContour = () => {
|
|
4913
|
-
pts = [];
|
|
4914
|
-
aA = [];
|
|
4915
|
-
aB = [];
|
|
4916
|
-
aT = [];
|
|
4917
|
-
const next = { points: pts, closed: false };
|
|
4918
|
-
out.push(next);
|
|
4919
|
-
return next;
|
|
4920
|
-
};
|
|
4921
|
-
const dropDuplicateClosingPoint = () => {
|
|
4922
|
-
if (!pts || !aA || !aB || !aT) return;
|
|
4923
|
-
const n = pts.length / 2;
|
|
4924
|
-
if (n < 2) return;
|
|
4925
|
-
if (pts[0] !== pts[(n - 1) * 2] || pts[1] !== pts[(n - 1) * 2 + 1]) return;
|
|
4926
|
-
pts.length -= 2;
|
|
4927
|
-
aA.length -= 1;
|
|
4928
|
-
aB.length -= 1;
|
|
4929
|
-
aT.length -= 1;
|
|
4930
|
-
};
|
|
4931
|
-
const commit = (target) => {
|
|
4932
|
-
if (!pts || !aA || !aB || !aT) return;
|
|
4933
|
-
target.anchorA = new Uint32Array(aA);
|
|
4934
|
-
target.anchorB = new Uint32Array(aB);
|
|
4935
|
-
target.anchorT = new Float32Array(aT);
|
|
4936
|
-
};
|
|
4937
|
-
for (let cmdIdx = 0; cmdIdx < commands.length; cmdIdx++) {
|
|
4938
|
-
const cmd = commands[cmdIdx];
|
|
4939
|
-
switch (cmd) {
|
|
4940
|
-
case PATH_M2: {
|
|
4941
|
-
if (current) commit(current);
|
|
4942
|
-
current = beginContour();
|
|
4943
|
-
prevX = coords[coordIdx];
|
|
4944
|
-
prevY = coords[coordIdx + 1];
|
|
4945
|
-
pts.push(prevX, prevY);
|
|
4946
|
-
aA.push(anchorCounter);
|
|
4947
|
-
aB.push(anchorCounter);
|
|
4948
|
-
aT.push(0);
|
|
4949
|
-
prevAnchor = anchorCounter;
|
|
4950
|
-
anchorCounter++;
|
|
4951
|
-
coordIdx += 2;
|
|
4952
|
-
break;
|
|
4953
|
-
}
|
|
4954
|
-
case PATH_L2: {
|
|
4955
|
-
prevX = coords[coordIdx];
|
|
4956
|
-
prevY = coords[coordIdx + 1];
|
|
4957
|
-
pts.push(prevX, prevY);
|
|
4958
|
-
aA.push(anchorCounter);
|
|
4959
|
-
aB.push(anchorCounter);
|
|
4960
|
-
aT.push(0);
|
|
4961
|
-
prevAnchor = anchorCounter;
|
|
4962
|
-
anchorCounter++;
|
|
4963
|
-
coordIdx += 2;
|
|
4964
|
-
break;
|
|
4965
|
-
}
|
|
4966
|
-
case PATH_Q2: {
|
|
4967
|
-
const cx = coords[coordIdx], cy = coords[coordIdx + 1];
|
|
4968
|
-
const ex = coords[coordIdx + 2], ey = coords[coordIdx + 3];
|
|
4969
|
-
const targetAnchor = anchorCounter;
|
|
4970
|
-
const segStart = pts.length / 2;
|
|
4971
|
-
const arcAccum = [];
|
|
4972
|
-
const total = flattenQuadraticWithArcLen(prevX, prevY, cx, cy, ex, ey, tolerance, pts, arcAccum);
|
|
4973
|
-
for (let k = 0; k < arcAccum.length; k++) {
|
|
4974
|
-
aA.push(prevAnchor);
|
|
4975
|
-
aB.push(targetAnchor);
|
|
4976
|
-
aT.push(total > 0 ? arcAccum[k] / total : 0);
|
|
4977
|
-
}
|
|
4978
|
-
const lastIdx = segStart + arcAccum.length - 1;
|
|
4979
|
-
aA[lastIdx] = targetAnchor;
|
|
4980
|
-
aB[lastIdx] = targetAnchor;
|
|
4981
|
-
aT[lastIdx] = 0;
|
|
4982
|
-
prevX = ex;
|
|
4983
|
-
prevY = ey;
|
|
4984
|
-
prevAnchor = targetAnchor;
|
|
4985
|
-
anchorCounter++;
|
|
4986
|
-
coordIdx += 4;
|
|
4987
|
-
break;
|
|
4988
|
-
}
|
|
4989
|
-
case PATH_C2: {
|
|
4990
|
-
const c1x = coords[coordIdx], c1y = coords[coordIdx + 1];
|
|
4991
|
-
const c2x = coords[coordIdx + 2], c2y = coords[coordIdx + 3];
|
|
4992
|
-
const ex = coords[coordIdx + 4], ey = coords[coordIdx + 5];
|
|
4993
|
-
const targetAnchor = anchorCounter;
|
|
4994
|
-
const segStart = pts.length / 2;
|
|
4995
|
-
const arcAccum = [];
|
|
4996
|
-
const total = flattenCubicWithArcLen(prevX, prevY, c1x, c1y, c2x, c2y, ex, ey, tolerance, pts, arcAccum);
|
|
4997
|
-
for (let k = 0; k < arcAccum.length; k++) {
|
|
4998
|
-
aA.push(prevAnchor);
|
|
4999
|
-
aB.push(targetAnchor);
|
|
5000
|
-
aT.push(total > 0 ? arcAccum[k] / total : 0);
|
|
5001
|
-
}
|
|
5002
|
-
const lastIdx = segStart + arcAccum.length - 1;
|
|
5003
|
-
aA[lastIdx] = targetAnchor;
|
|
5004
|
-
aB[lastIdx] = targetAnchor;
|
|
5005
|
-
aT[lastIdx] = 0;
|
|
5006
|
-
prevX = ex;
|
|
5007
|
-
prevY = ey;
|
|
5008
|
-
prevAnchor = targetAnchor;
|
|
5009
|
-
anchorCounter++;
|
|
5010
|
-
coordIdx += 6;
|
|
5011
|
-
break;
|
|
5012
|
-
}
|
|
5013
|
-
case PATH_Z2: {
|
|
5014
|
-
if (current) {
|
|
5015
|
-
current.closed = true;
|
|
5016
|
-
dropDuplicateClosingPoint();
|
|
5017
|
-
}
|
|
5018
|
-
break;
|
|
5019
|
-
}
|
|
5020
|
-
default:
|
|
5021
|
-
throw new Error(`extractPolylines: unknown command code ${cmd}`);
|
|
5022
|
-
}
|
|
5023
|
-
}
|
|
5024
|
-
if (current) commit(current);
|
|
5025
|
-
return out;
|
|
4567
|
+
function strokeKey(s) {
|
|
4568
|
+
if (!s) return "-";
|
|
4569
|
+
return [
|
|
4570
|
+
fillKey(s.paint),
|
|
4571
|
+
s.width ?? 1,
|
|
4572
|
+
s.join ?? "miter",
|
|
4573
|
+
s.cap ?? "butt",
|
|
4574
|
+
s.miterLimit ?? "",
|
|
4575
|
+
s.align ?? "center",
|
|
4576
|
+
(s.dash ?? []).join(",")
|
|
4577
|
+
].join(":");
|
|
5026
4578
|
}
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
const
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
4579
|
+
function groupKey(family, weight, style, synthetic, fill, stroke, source, page) {
|
|
4580
|
+
return `${family}|${weight}|${style}|${synthetic.bold ? 1 : 0}${synthetic.italic ? 1 : 0}|${fillKey(fill)}|${strokeKey(stroke)}|${source}|${page}`;
|
|
4581
|
+
}
|
|
4582
|
+
function getOrCreateGroup(ctx, run, resolved, page, sourceOverride) {
|
|
4583
|
+
const atlasFamily = resolved.resolved.family;
|
|
4584
|
+
const resolvedWeight = resolved.resolved.weight;
|
|
4585
|
+
const resolvedStyle = resolved.resolved.style;
|
|
4586
|
+
const source = sourceOverride ?? resolved.source;
|
|
4587
|
+
const key = groupKey(
|
|
4588
|
+
atlasFamily,
|
|
4589
|
+
resolvedWeight,
|
|
4590
|
+
resolvedStyle,
|
|
4591
|
+
resolved.synthetic,
|
|
4592
|
+
run.fill,
|
|
4593
|
+
source === "outline" ? run.stroke : void 0,
|
|
4594
|
+
source,
|
|
4595
|
+
page
|
|
4596
|
+
);
|
|
4597
|
+
let g = ctx.groups.get(key);
|
|
4598
|
+
if (!g) {
|
|
4599
|
+
g = {
|
|
4600
|
+
family: atlasFamily,
|
|
4601
|
+
weight: resolvedWeight,
|
|
4602
|
+
style: resolvedStyle,
|
|
4603
|
+
synthetic: { ...resolved.synthetic },
|
|
4604
|
+
source,
|
|
4605
|
+
page,
|
|
4606
|
+
fill: run.fill,
|
|
4607
|
+
// Only the outline tier can paint it, and carrying it on an SDF group
|
|
4608
|
+
// would be a promise the renderer cannot keep.
|
|
4609
|
+
...source === "outline" && run.stroke !== void 0 ? { stroke: run.stroke } : {},
|
|
4610
|
+
quads: [],
|
|
4611
|
+
glyphs: []
|
|
5051
4612
|
};
|
|
4613
|
+
ctx.groups.set(key, g);
|
|
5052
4614
|
}
|
|
5053
|
-
|
|
5054
|
-
if (stroke.vertexWidths && stroke.vertexWidths.length > 0) {
|
|
5055
|
-
for (const pl of polylines) populatePolylineWidths(pl, stroke.vertexWidths, width);
|
|
5056
|
-
}
|
|
5057
|
-
const dash = stroke.dash ?? [];
|
|
5058
|
-
const verts = [];
|
|
5059
|
-
const idx = [];
|
|
5060
|
-
const anchorA = [];
|
|
5061
|
-
const anchorB = [];
|
|
5062
|
-
const anchorT = [];
|
|
5063
|
-
for (const pl of polylines) {
|
|
5064
|
-
const subs = dash.length > 0 ? splitForDash(pl, dash) : [pl];
|
|
5065
|
-
for (const sub2 of subs) {
|
|
5066
|
-
expandPolyline(sub2, width, join, cap, miterLimit, varyingThreshold, verts, idx, anchorA, anchorB, anchorT);
|
|
5067
|
-
}
|
|
5068
|
-
}
|
|
5069
|
-
return {
|
|
5070
|
-
vertices: new Float32Array(verts),
|
|
5071
|
-
indices: new Uint32Array(idx),
|
|
5072
|
-
anchorA: new Uint32Array(anchorA),
|
|
5073
|
-
anchorB: new Uint32Array(anchorB),
|
|
5074
|
-
anchorT: new Float32Array(anchorT)
|
|
5075
|
-
};
|
|
4615
|
+
return g;
|
|
5076
4616
|
}
|
|
5077
|
-
function
|
|
5078
|
-
const
|
|
5079
|
-
|
|
5080
|
-
const
|
|
5081
|
-
const
|
|
5082
|
-
if (
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
const plHalf = pl.widths ? pl.widths.map((w) => w / 2) : null;
|
|
5087
|
-
const segs = [];
|
|
5088
|
-
const segSrcIdx = [];
|
|
5089
|
-
for (let s = 0; s < segCount; s++) {
|
|
5090
|
-
const ha = plHalf ? plHalf[s] : half;
|
|
5091
|
-
const hb = plHalf ? plHalf[s + 1] : half;
|
|
5092
|
-
const seg = makeSeg(pts[s * 2], pts[s * 2 + 1], pts[(s + 1) * 2], pts[(s + 1) * 2 + 1], ha, hb);
|
|
5093
|
-
if (seg) {
|
|
5094
|
-
segs.push(seg);
|
|
5095
|
-
segSrcIdx.push(s);
|
|
5096
|
-
}
|
|
5097
|
-
}
|
|
5098
|
-
let closerSrcIdx = -1;
|
|
5099
|
-
if (pl.closed && segs.length >= 1) {
|
|
5100
|
-
const last = segs[segs.length - 1];
|
|
5101
|
-
const first = segs[0];
|
|
5102
|
-
const haCloser = plHalf ? plHalf[ptCount - 1] : half;
|
|
5103
|
-
const hbCloser = plHalf ? plHalf[0] : half;
|
|
5104
|
-
const closer = makeSeg(last.bx, last.by, first.ax, first.ay, haCloser, hbCloser);
|
|
5105
|
-
if (closer) {
|
|
5106
|
-
segs.push(closer);
|
|
5107
|
-
closerSrcIdx = ptCount - 1;
|
|
5108
|
-
segSrcIdx.push(closerSrcIdx);
|
|
4617
|
+
function resolveGlyph(run, font, resolved, cp) {
|
|
4618
|
+
const direct = font.charMap.get(cp);
|
|
4619
|
+
if (direct) return { glyph: direct, font, resolved };
|
|
4620
|
+
const fallback = resolveGlyphFallback(run.fontFamily, run.fontWeight, run.fontStyle);
|
|
4621
|
+
const face = fallback?.dynamicFace;
|
|
4622
|
+
if (fallback && face) {
|
|
4623
|
+
const glyph = face.requestGlyph(cp);
|
|
4624
|
+
if (glyph.xadvance > 0 || glyph.width > 0) {
|
|
4625
|
+
return { glyph, font: face.font, resolved: fallback };
|
|
5109
4626
|
}
|
|
5110
4627
|
}
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
for (let s = 0; s < segs.length; s++) {
|
|
5114
|
-
const seg = segs[s];
|
|
5115
|
-
const startSrc = segSrcIdx[s];
|
|
5116
|
-
const endSrc = s === segs.length - 1 && pl.closed && closerSrcIdx >= 0 ? 0 : startSrc + 1;
|
|
5117
|
-
const base = verts.length / 2;
|
|
5118
|
-
segBaseIdx.push(base);
|
|
5119
|
-
const nxA = seg.nxUnit * seg.halfA, nyA = seg.nyUnit * seg.halfA;
|
|
5120
|
-
const nxB = seg.nxUnit * seg.halfB, nyB = seg.nyUnit * seg.halfB;
|
|
5121
|
-
verts.push(seg.ax + nxA, seg.ay + nyA);
|
|
5122
|
-
anchorA.push(plA[startSrc]);
|
|
5123
|
-
anchorB.push(plB[startSrc]);
|
|
5124
|
-
anchorT.push(plT[startSrc]);
|
|
5125
|
-
verts.push(seg.ax - nxA, seg.ay - nyA);
|
|
5126
|
-
anchorA.push(plA[startSrc]);
|
|
5127
|
-
anchorB.push(plB[startSrc]);
|
|
5128
|
-
anchorT.push(plT[startSrc]);
|
|
5129
|
-
verts.push(seg.bx + nxB, seg.by + nyB);
|
|
5130
|
-
anchorA.push(plA[endSrc]);
|
|
5131
|
-
anchorB.push(plB[endSrc]);
|
|
5132
|
-
anchorT.push(plT[endSrc]);
|
|
5133
|
-
verts.push(seg.bx - nxB, seg.by - nyB);
|
|
5134
|
-
anchorA.push(plA[endSrc]);
|
|
5135
|
-
anchorB.push(plB[endSrc]);
|
|
5136
|
-
anchorT.push(plT[endSrc]);
|
|
5137
|
-
idx.push(base, base + 1, base + 2, base + 1, base + 3, base + 2);
|
|
5138
|
-
}
|
|
5139
|
-
const joinCount = pl.closed ? segs.length : segs.length - 1;
|
|
5140
|
-
for (let j = 0; j < joinCount; j++) {
|
|
5141
|
-
emitJoin(segs, segBaseIdx, j, join, miterLimit, varyingThreshold, verts, idx, anchorA, anchorB, anchorT, segSrcIdx, plA, plB, plT);
|
|
5142
|
-
}
|
|
5143
|
-
if (!pl.closed && cap !== "butt") {
|
|
5144
|
-
const first = segs[0];
|
|
5145
|
-
const firstBase = segBaseIdx[0];
|
|
5146
|
-
emitCap(first, firstBase + 0, firstBase + 1, false, first.halfA, cap, verts, idx, anchorA, anchorB, anchorT, plA[0], plB[0], plT[0]);
|
|
5147
|
-
const last = segs[segs.length - 1];
|
|
5148
|
-
const lastBase = segBaseIdx[segs.length - 1];
|
|
5149
|
-
const lastSrc = segSrcIdx[segs.length - 1] + 1;
|
|
5150
|
-
emitCap(last, lastBase + 2, lastBase + 3, true, last.halfB, cap, verts, idx, anchorA, anchorB, anchorT, plA[lastSrc], plB[lastSrc], plT[lastSrc]);
|
|
5151
|
-
}
|
|
4628
|
+
warnMissingGlyphOnce(resolved.resolved.family, cp);
|
|
4629
|
+
return null;
|
|
5152
4630
|
}
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
const
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
4631
|
+
var warnedMissingGlyphs = /* @__PURE__ */ new Set();
|
|
4632
|
+
function warnMissingGlyphOnce(family, cp) {
|
|
4633
|
+
const key = `${family}|${cp}`;
|
|
4634
|
+
if (warnedMissingGlyphs.has(key)) return;
|
|
4635
|
+
warnedMissingGlyphs.add(key);
|
|
4636
|
+
const ch = String.fromCodePoint(cp);
|
|
4637
|
+
console.warn(
|
|
4638
|
+
`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.`
|
|
4639
|
+
);
|
|
4640
|
+
}
|
|
4641
|
+
function layoutRuns(runs, opts) {
|
|
4642
|
+
const ctx = { groups: /* @__PURE__ */ new Map() };
|
|
4643
|
+
const entries = [];
|
|
4644
|
+
let prevCp;
|
|
4645
|
+
let prevFont;
|
|
4646
|
+
let prevFontSize;
|
|
4647
|
+
for (const run of runs) {
|
|
4648
|
+
const resolved = resolveFontVariant(run.fontFamily, run.fontWeight, run.fontStyle);
|
|
4649
|
+
const font = resolved.entry?.font ?? resolved.dynamicFace?.font;
|
|
4650
|
+
if (!font) {
|
|
4651
|
+
prevCp = void 0;
|
|
4652
|
+
prevFont = void 0;
|
|
4653
|
+
prevFontSize = void 0;
|
|
4654
|
+
continue;
|
|
4655
|
+
}
|
|
4656
|
+
const scale2 = run.fontSize / font.info.size;
|
|
4657
|
+
const tracking = run.letterSpacing;
|
|
4658
|
+
for (const ch of [...run.text]) {
|
|
4659
|
+
const cp = ch.codePointAt(0);
|
|
4660
|
+
const isNewline = cp === 10;
|
|
4661
|
+
const isSpace = cp === 32;
|
|
4662
|
+
if (isNewline) {
|
|
4663
|
+
entries.push({
|
|
4664
|
+
run,
|
|
4665
|
+
font,
|
|
4666
|
+
glyph: { id: cp, x: 0, y: 0, width: 0, height: 0, xoffset: 0, yoffset: 0, xadvance: 0, page: 0 },
|
|
4667
|
+
// A newline consumes no advance, so it takes no tracking either.
|
|
4668
|
+
cp,
|
|
4669
|
+
advance: 0,
|
|
4670
|
+
tracking: 0,
|
|
4671
|
+
kerningBefore: 0,
|
|
4672
|
+
isSpace: false,
|
|
4673
|
+
isNewline: true,
|
|
4674
|
+
resolved,
|
|
4675
|
+
fontSize: run.fontSize
|
|
4676
|
+
});
|
|
4677
|
+
prevCp = void 0;
|
|
4678
|
+
prevFont = void 0;
|
|
4679
|
+
prevFontSize = void 0;
|
|
4680
|
+
continue;
|
|
4681
|
+
}
|
|
4682
|
+
if (isSpace) {
|
|
4683
|
+
const spaceGlyph = resolved.dynamicFace ? resolved.dynamicFace.requestGlyph(32) : font.charMap.get(32);
|
|
4684
|
+
const advance = spaceGlyph ? spaceGlyph.xadvance * scale2 : run.fontSize * 0.25;
|
|
4685
|
+
let kerningBefore2 = 0;
|
|
4686
|
+
if (prevCp !== void 0 && prevFont !== void 0 && prevFontSize !== void 0) {
|
|
4687
|
+
const kAtlas = prevFont.kerningMap.get(prevCp)?.get(cp) ?? 0;
|
|
4688
|
+
kerningBefore2 = kAtlas * (prevFontSize / prevFont.info.size);
|
|
4689
|
+
}
|
|
4690
|
+
entries.push({
|
|
4691
|
+
run,
|
|
4692
|
+
font,
|
|
4693
|
+
glyph: spaceGlyph ?? { id: 32, x: 0, y: 0, width: 0, height: 0, xoffset: 0, yoffset: 0, xadvance: 0, page: 0 },
|
|
4694
|
+
cp,
|
|
4695
|
+
advance,
|
|
4696
|
+
tracking,
|
|
4697
|
+
kerningBefore: kerningBefore2,
|
|
4698
|
+
isSpace: true,
|
|
4699
|
+
isNewline: false,
|
|
4700
|
+
resolved,
|
|
4701
|
+
fontSize: run.fontSize
|
|
4702
|
+
});
|
|
4703
|
+
prevCp = cp;
|
|
4704
|
+
prevFont = font;
|
|
4705
|
+
prevFontSize = run.fontSize;
|
|
4706
|
+
continue;
|
|
4707
|
+
}
|
|
4708
|
+
const hit = resolved.dynamicFace ? { glyph: resolved.dynamicFace.requestGlyph(cp), font, resolved } : resolveGlyph(run, font, resolved, cp);
|
|
4709
|
+
if (!hit) {
|
|
4710
|
+
prevCp = cp;
|
|
4711
|
+
prevFont = font;
|
|
4712
|
+
prevFontSize = run.fontSize;
|
|
4713
|
+
continue;
|
|
4714
|
+
}
|
|
4715
|
+
const glyphFont = hit.font;
|
|
4716
|
+
const glyphScale = run.fontSize / glyphFont.info.size;
|
|
4717
|
+
let kerningBefore = 0;
|
|
4718
|
+
if (prevCp !== void 0 && prevFont !== void 0 && prevFontSize !== void 0) {
|
|
4719
|
+
const kAtlas = prevFont.kerningMap.get(prevCp)?.get(cp) ?? 0;
|
|
4720
|
+
kerningBefore = kAtlas * (prevFontSize / prevFont.info.size);
|
|
4721
|
+
}
|
|
4722
|
+
entries.push({
|
|
4723
|
+
run,
|
|
4724
|
+
font: glyphFont,
|
|
4725
|
+
glyph: hit.glyph,
|
|
4726
|
+
cp,
|
|
4727
|
+
advance: hit.glyph.xadvance * glyphScale,
|
|
4728
|
+
tracking,
|
|
4729
|
+
kerningBefore,
|
|
4730
|
+
isSpace,
|
|
4731
|
+
isNewline: false,
|
|
4732
|
+
resolved: hit.resolved,
|
|
4733
|
+
fontSize: run.fontSize
|
|
4734
|
+
});
|
|
4735
|
+
prevCp = cp;
|
|
4736
|
+
prevFont = glyphFont;
|
|
4737
|
+
prevFontSize = run.fontSize;
|
|
5172
4738
|
}
|
|
5173
4739
|
}
|
|
5174
|
-
|
|
5175
|
-
}
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
const dx = (seg.bx - seg.ax) / seg.len * sign2;
|
|
5180
|
-
const dy = (seg.by - seg.ay) / seg.len * sign2;
|
|
5181
|
-
const cx = atEnd ? seg.bx : seg.ax;
|
|
5182
|
-
const cy = atEnd ? seg.by : seg.ay;
|
|
5183
|
-
const nx = seg.nxUnit * half;
|
|
5184
|
-
const ny = seg.nyUnit * half;
|
|
5185
|
-
if (cap === "square") {
|
|
5186
|
-
const ox = cx + dx * half;
|
|
5187
|
-
const oy = cy + dy * half;
|
|
5188
|
-
const lOut = verts.length / 2;
|
|
5189
|
-
verts.push(ox + nx, oy + ny);
|
|
5190
|
-
anchorA.push(endA);
|
|
5191
|
-
anchorB.push(endB);
|
|
5192
|
-
anchorT.push(endT);
|
|
5193
|
-
const rOut = verts.length / 2;
|
|
5194
|
-
verts.push(ox - nx, oy - ny);
|
|
5195
|
-
anchorA.push(endA);
|
|
5196
|
-
anchorB.push(endB);
|
|
5197
|
-
anchorT.push(endT);
|
|
5198
|
-
if (atEnd) {
|
|
5199
|
-
idx.push(leftIdx, lOut, rOut, leftIdx, rOut, rightIdx);
|
|
5200
|
-
} else {
|
|
5201
|
-
idx.push(leftIdx, rOut, lOut, leftIdx, rightIdx, rOut);
|
|
5202
|
-
}
|
|
5203
|
-
return;
|
|
4740
|
+
const lines = [];
|
|
4741
|
+
let cur = { entries: [], width: 0, height: 0 };
|
|
4742
|
+
function commitLine() {
|
|
4743
|
+
lines.push(cur);
|
|
4744
|
+
cur = { entries: [], width: 0, height: 0 };
|
|
5204
4745
|
}
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
for (let i = 1; i < steps; i++) {
|
|
5217
|
-
const ang = startAngle + i * stepAngle;
|
|
5218
|
-
const fx = cx + Math.cos(ang) * half;
|
|
5219
|
-
const fy = cy + Math.sin(ang) * half;
|
|
5220
|
-
const newIdx = verts.length / 2;
|
|
5221
|
-
verts.push(fx, fy);
|
|
5222
|
-
anchorA.push(endA);
|
|
5223
|
-
anchorB.push(endB);
|
|
5224
|
-
anchorT.push(endT);
|
|
5225
|
-
if (atEnd) idx.push(prevIdx, newIdx, pivotIdx);
|
|
5226
|
-
else idx.push(prevIdx, pivotIdx, newIdx);
|
|
5227
|
-
prevIdx = newIdx;
|
|
4746
|
+
let i = 0;
|
|
4747
|
+
while (i < entries.length) {
|
|
4748
|
+
const e = entries[i];
|
|
4749
|
+
if (e.isNewline) {
|
|
4750
|
+
if (cur.entries.length === 0) {
|
|
4751
|
+
cur.height = Math.max(cur.height, e.fontSize * opts.lineHeight);
|
|
4752
|
+
cur.blank = e;
|
|
4753
|
+
}
|
|
4754
|
+
commitLine();
|
|
4755
|
+
i++;
|
|
4756
|
+
continue;
|
|
5228
4757
|
}
|
|
5229
|
-
if (
|
|
5230
|
-
|
|
4758
|
+
if (e.isSpace) {
|
|
4759
|
+
if (cur.entries.length > 0) {
|
|
4760
|
+
cur.entries.push(e);
|
|
4761
|
+
cur.width += e.kerningBefore + e.advance + e.tracking;
|
|
4762
|
+
cur.height = Math.max(cur.height, e.fontSize * opts.lineHeight);
|
|
4763
|
+
}
|
|
4764
|
+
i++;
|
|
4765
|
+
continue;
|
|
4766
|
+
}
|
|
4767
|
+
let j = i;
|
|
4768
|
+
let wordWidth = 0;
|
|
4769
|
+
while (j < entries.length && !entries[j].isSpace && !entries[j].isNewline) {
|
|
4770
|
+
const w = entries[j];
|
|
4771
|
+
wordWidth += w.kerningBefore + w.advance + w.tracking;
|
|
4772
|
+
j++;
|
|
4773
|
+
}
|
|
4774
|
+
if (Number.isFinite(opts.maxWidth) && cur.width + wordWidth > opts.maxWidth && cur.entries.length > 0) {
|
|
4775
|
+
commitLine();
|
|
4776
|
+
}
|
|
4777
|
+
for (let k = i; k < j; k++) {
|
|
4778
|
+
const w = entries[k];
|
|
4779
|
+
const kerningBefore = cur.entries.length === 0 ? 0 : w.kerningBefore;
|
|
4780
|
+
cur.entries.push({ ...w, kerningBefore });
|
|
4781
|
+
cur.width += kerningBefore + w.advance + w.tracking;
|
|
4782
|
+
cur.height = Math.max(cur.height, w.fontSize * opts.lineHeight);
|
|
4783
|
+
}
|
|
4784
|
+
i = j;
|
|
5231
4785
|
}
|
|
5232
|
-
|
|
5233
|
-
function
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
let
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
anchorA: new Uint32Array(curA),
|
|
5251
|
-
anchorB: new Uint32Array(curB),
|
|
5252
|
-
anchorT: new Float32Array(curT)
|
|
5253
|
-
});
|
|
4786
|
+
if (cur.entries.length > 0) commitLine();
|
|
4787
|
+
function outlineFor(e) {
|
|
4788
|
+
const min = opts.outlineMinSize;
|
|
4789
|
+
if (min === void 0 || e.fontSize < min) return null;
|
|
4790
|
+
if (e.resolved.synthetic.bold) return null;
|
|
4791
|
+
const r = e.resolved.resolved;
|
|
4792
|
+
return glyphOutline(r.family, r.weight, r.style, e.cp);
|
|
4793
|
+
}
|
|
4794
|
+
const decorations = [];
|
|
4795
|
+
let span = null;
|
|
4796
|
+
function flushSpan() {
|
|
4797
|
+
const s = span;
|
|
4798
|
+
span = null;
|
|
4799
|
+
if (!s || s.x1 <= s.x0) return;
|
|
4800
|
+
const thickness = s.fontSize * DECORATION_THICKNESS;
|
|
4801
|
+
if (s.underline) {
|
|
4802
|
+
const y0 = s.baselineY + s.fontSize * UNDERLINE_OFFSET;
|
|
4803
|
+
decorations.push({ kind: "underline", x0: s.x0, y0, x1: s.x1, y1: y0 + thickness, fill: s.fill });
|
|
5254
4804
|
}
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
const
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
prevX = cx;
|
|
5286
|
-
prevY = cy;
|
|
5287
|
-
traveled = segFullLen;
|
|
5288
|
-
segLen = 0;
|
|
5289
|
-
if (dashRemaining <= 1e-9) {
|
|
5290
|
-
flushAndAdvance();
|
|
5291
|
-
if (onPhase) {
|
|
5292
|
-
curPts = [prevX, prevY];
|
|
5293
|
-
curA = [endA];
|
|
5294
|
-
curB = [endB];
|
|
5295
|
-
curT = [endT];
|
|
5296
|
-
}
|
|
5297
|
-
}
|
|
5298
|
-
} else {
|
|
5299
|
-
const tConsume = dashRemaining / segLen;
|
|
5300
|
-
const ix = prevX + segDx * tConsume;
|
|
5301
|
-
const iy = prevY + segDy * tConsume;
|
|
5302
|
-
traveled += dashRemaining;
|
|
5303
|
-
const frac = traveled / segFullLen;
|
|
5304
|
-
let mA, mB, mT;
|
|
5305
|
-
if (startA === endA && startB === endB) {
|
|
5306
|
-
mA = startA;
|
|
5307
|
-
mB = startB;
|
|
5308
|
-
mT = startT + (endT - startT) * frac;
|
|
4805
|
+
if (s.strikethrough) {
|
|
4806
|
+
const y0 = s.baselineY + s.fontSize * STRIKETHROUGH_OFFSET;
|
|
4807
|
+
decorations.push({ kind: "strikethrough", x0: s.x0, y0, x1: s.x1, y1: y0 + thickness, fill: s.fill });
|
|
4808
|
+
}
|
|
4809
|
+
}
|
|
4810
|
+
const lineBoxes = [];
|
|
4811
|
+
let penY = 0;
|
|
4812
|
+
let maxLineWidth = 0;
|
|
4813
|
+
const finiteWidth = Number.isFinite(opts.maxWidth) ? opts.maxWidth : 0;
|
|
4814
|
+
for (const line of lines) {
|
|
4815
|
+
const alignShift = (() => {
|
|
4816
|
+
if (opts.align === "left") return 0;
|
|
4817
|
+
if (!Number.isFinite(opts.maxWidth)) {
|
|
4818
|
+
return opts.align === "center" ? -line.width / 2 : -line.width;
|
|
4819
|
+
}
|
|
4820
|
+
const slack = finiteWidth - line.width;
|
|
4821
|
+
return opts.align === "center" ? slack / 2 : slack;
|
|
4822
|
+
})();
|
|
4823
|
+
const lineX0 = alignShift;
|
|
4824
|
+
const baselineSource = line.entries[0] ?? line.blank;
|
|
4825
|
+
const lineBaselineY = baselineSource ? penY + baselineSource.font.common.base * (baselineSource.fontSize / baselineSource.font.info.size) : penY;
|
|
4826
|
+
let penX = lineX0;
|
|
4827
|
+
for (const e of line.entries) {
|
|
4828
|
+
penX += e.kerningBefore;
|
|
4829
|
+
const step = e.advance + e.tracking;
|
|
4830
|
+
const scale2 = e.fontSize / e.font.info.size;
|
|
4831
|
+
const baselineY = penY + e.font.common.base * scale2;
|
|
4832
|
+
if (e.run.underline || e.run.strikethrough) {
|
|
4833
|
+
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)) {
|
|
4834
|
+
span.x1 = penX + step;
|
|
5309
4835
|
} else {
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
if (onPhase && curPts) {
|
|
5321
|
-
curPts.push(ix, iy);
|
|
5322
|
-
curA.push(mA);
|
|
5323
|
-
curB.push(mB);
|
|
5324
|
-
curT.push(mT);
|
|
5325
|
-
}
|
|
5326
|
-
prevX = ix;
|
|
5327
|
-
prevY = iy;
|
|
5328
|
-
segDx = cx - prevX;
|
|
5329
|
-
segDy = cy - prevY;
|
|
5330
|
-
segLen = Math.hypot(segDx, segDy);
|
|
5331
|
-
flushAndAdvance();
|
|
5332
|
-
if (onPhase) {
|
|
5333
|
-
curPts = [prevX, prevY];
|
|
5334
|
-
curA = [mA];
|
|
5335
|
-
curB = [mB];
|
|
5336
|
-
curT = [mT];
|
|
4836
|
+
flushSpan();
|
|
4837
|
+
span = {
|
|
4838
|
+
underline: e.run.underline,
|
|
4839
|
+
strikethrough: e.run.strikethrough,
|
|
4840
|
+
fill: e.run.fill,
|
|
4841
|
+
fontSize: e.fontSize,
|
|
4842
|
+
baselineY,
|
|
4843
|
+
x0: penX,
|
|
4844
|
+
x1: penX + step
|
|
4845
|
+
};
|
|
5337
4846
|
}
|
|
4847
|
+
} else {
|
|
4848
|
+
flushSpan();
|
|
4849
|
+
}
|
|
4850
|
+
const outlineD = outlineFor(e);
|
|
4851
|
+
if (outlineD !== null) {
|
|
4852
|
+
const group2 = getOrCreateGroup(ctx, e.run, e.resolved, 0, "outline");
|
|
4853
|
+
group2.glyphs.push({
|
|
4854
|
+
d: outlineD,
|
|
4855
|
+
key: `${e.resolved.resolved.family}|${e.resolved.resolved.weight}|${e.resolved.resolved.style}|${e.cp}`,
|
|
4856
|
+
x: penX,
|
|
4857
|
+
baselineY,
|
|
4858
|
+
// Em space is unit-scale, so world units per em is just the size.
|
|
4859
|
+
scale: e.fontSize
|
|
4860
|
+
});
|
|
4861
|
+
penX += step;
|
|
4862
|
+
continue;
|
|
4863
|
+
}
|
|
4864
|
+
if (e.advance === 0 || e.glyph.width === 0 || e.glyph.page < 0) {
|
|
4865
|
+
penX += step;
|
|
4866
|
+
continue;
|
|
5338
4867
|
}
|
|
4868
|
+
const group = getOrCreateGroup(ctx, e.run, e.resolved, e.glyph.page);
|
|
4869
|
+
const atlasW = e.font.common.scaleW;
|
|
4870
|
+
const atlasH = e.font.common.scaleH;
|
|
4871
|
+
const qx0 = penX + e.glyph.xoffset * scale2;
|
|
4872
|
+
const qy0 = penY + e.glyph.yoffset * scale2;
|
|
4873
|
+
const qx1 = qx0 + e.glyph.width * scale2;
|
|
4874
|
+
const qy1 = qy0 + e.glyph.height * scale2;
|
|
4875
|
+
const u0 = e.glyph.x / atlasW;
|
|
4876
|
+
const v0 = e.glyph.y / atlasH;
|
|
4877
|
+
const u1 = (e.glyph.x + e.glyph.width) / atlasW;
|
|
4878
|
+
const v1 = (e.glyph.y + e.glyph.height) / atlasH;
|
|
4879
|
+
group.quads.push({ x0: qx0, y0: qy0, x1: qx1, y1: qy1, u0, v0, u1, v1, baselineY });
|
|
4880
|
+
penX += step;
|
|
5339
4881
|
}
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
anchorT: new Float32Array(curT)
|
|
4882
|
+
flushSpan();
|
|
4883
|
+
lineBoxes.push({
|
|
4884
|
+
x0: lineX0,
|
|
4885
|
+
y0: penY,
|
|
4886
|
+
x1: lineX0 + line.width,
|
|
4887
|
+
y1: penY + line.height,
|
|
4888
|
+
baselineY: lineBaselineY
|
|
5348
4889
|
});
|
|
4890
|
+
maxLineWidth = Math.max(maxLineWidth, line.width);
|
|
4891
|
+
penY += line.height;
|
|
5349
4892
|
}
|
|
5350
|
-
return out;
|
|
5351
|
-
}
|
|
5352
|
-
function makeSeg(ax, ay, bx, by, halfA, halfB) {
|
|
5353
|
-
const dx = bx - ax, dy = by - ay;
|
|
5354
|
-
const len = Math.hypot(dx, dy);
|
|
5355
|
-
if (len === 0) return null;
|
|
5356
4893
|
return {
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
nxUnit: -dy / len,
|
|
5362
|
-
nyUnit: dx / len,
|
|
5363
|
-
halfA,
|
|
5364
|
-
halfB,
|
|
5365
|
-
len
|
|
4894
|
+
groups: [...ctx.groups.values()],
|
|
4895
|
+
decorations,
|
|
4896
|
+
lines: lineBoxes,
|
|
4897
|
+
bounds: { width: maxLineWidth, height: penY }
|
|
5366
4898
|
};
|
|
5367
4899
|
}
|
|
5368
|
-
var
|
|
5369
|
-
var
|
|
5370
|
-
function
|
|
5371
|
-
|
|
5372
|
-
const
|
|
5373
|
-
const
|
|
5374
|
-
|
|
5375
|
-
const
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
const
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
const forceBevelForVarying = taperA > varyingThreshold || taperB > varyingThreshold;
|
|
5391
|
-
if (join === "bevel" || forceBevelForVarying) {
|
|
5392
|
-
emitBevel(a, aOuterEnd, bOuterStart, onPositive, verts, idx, anchorA, anchorB, anchorT, cornerA, cornerB, cornerT);
|
|
5393
|
-
return;
|
|
4900
|
+
var LAYOUT_CACHE_VARIANT_LIMIT = 8;
|
|
4901
|
+
var cache2 = /* @__PURE__ */ new WeakMap();
|
|
4902
|
+
function outlineBucket(runs, min) {
|
|
4903
|
+
if (min === void 0) return -1;
|
|
4904
|
+
const sizes = /* @__PURE__ */ new Set();
|
|
4905
|
+
for (const r of runs) sizes.add(r.fontSize);
|
|
4906
|
+
let n = 0;
|
|
4907
|
+
for (const size of sizes) if (size >= min) n++;
|
|
4908
|
+
return n;
|
|
4909
|
+
}
|
|
4910
|
+
function variantKey2(runs, opts) {
|
|
4911
|
+
return `${opts.maxWidth}|${opts.lineHeight}|${opts.align}|${outlineBucket(runs, opts.outlineMinSize)}`;
|
|
4912
|
+
}
|
|
4913
|
+
function cachedLayoutRuns(runs, opts) {
|
|
4914
|
+
const fonts = glyphGeneration();
|
|
4915
|
+
let entry = cache2.get(runs);
|
|
4916
|
+
if (entry === void 0) {
|
|
4917
|
+
entry = { generation: fonts, byVariant: /* @__PURE__ */ new Map() };
|
|
4918
|
+
cache2.set(runs, entry);
|
|
4919
|
+
} else if (entry.generation !== fonts) {
|
|
4920
|
+
entry.generation = fonts;
|
|
4921
|
+
entry.byVariant.clear();
|
|
5394
4922
|
}
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
4923
|
+
const key = variantKey2(runs, opts);
|
|
4924
|
+
const hit = entry.byVariant.get(key);
|
|
4925
|
+
if (hit !== void 0) return hit;
|
|
4926
|
+
const laid = layoutRuns(runs, opts);
|
|
4927
|
+
if (entry.byVariant.size >= LAYOUT_CACHE_VARIANT_LIMIT) entry.byVariant.clear();
|
|
4928
|
+
entry.byVariant.set(key, laid);
|
|
4929
|
+
return laid;
|
|
4930
|
+
}
|
|
4931
|
+
function verticalAlignOffset(align, boxHeight, textHeight) {
|
|
4932
|
+
if (align === void 0 || align === "top" || boxHeight === void 0) return 0;
|
|
4933
|
+
const slack = boxHeight - textHeight;
|
|
4934
|
+
return align === "center" ? slack / 2 : slack;
|
|
4935
|
+
}
|
|
4936
|
+
var NUM_RE = /[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g;
|
|
4937
|
+
function tokenize(d) {
|
|
4938
|
+
const out = [];
|
|
4939
|
+
let i = 0;
|
|
4940
|
+
while (i < d.length) {
|
|
4941
|
+
const ch = d[i];
|
|
4942
|
+
if (/[A-Za-z]/.test(ch)) {
|
|
4943
|
+
let j = i + 1;
|
|
4944
|
+
while (j < d.length && !/[A-Za-z]/.test(d[j])) j++;
|
|
4945
|
+
const argSlice = d.slice(i + 1, j);
|
|
4946
|
+
out.push({ cmd: ch, args: readNumbers(argSlice) });
|
|
4947
|
+
i = j;
|
|
4948
|
+
} else {
|
|
4949
|
+
i++;
|
|
4950
|
+
}
|
|
4951
|
+
}
|
|
4952
|
+
return out;
|
|
4953
|
+
}
|
|
4954
|
+
function readNumbers(s) {
|
|
4955
|
+
const out = [];
|
|
4956
|
+
let m;
|
|
4957
|
+
NUM_RE.lastIndex = 0;
|
|
4958
|
+
while ((m = NUM_RE.exec(s)) !== null) {
|
|
4959
|
+
const n = Number(m[0]);
|
|
4960
|
+
if (Number.isFinite(n)) out.push(n);
|
|
4961
|
+
}
|
|
4962
|
+
return out;
|
|
4963
|
+
}
|
|
4964
|
+
var ARG_COUNTS = {
|
|
4965
|
+
M: 2,
|
|
4966
|
+
L: 2,
|
|
4967
|
+
H: 1,
|
|
4968
|
+
V: 1,
|
|
4969
|
+
C: 6,
|
|
4970
|
+
S: 4,
|
|
4971
|
+
Q: 4,
|
|
4972
|
+
T: 2,
|
|
4973
|
+
A: 7,
|
|
4974
|
+
Z: 0
|
|
4975
|
+
};
|
|
4976
|
+
function pathFromD(d, onWarn) {
|
|
4977
|
+
const tokens = tokenize(d);
|
|
4978
|
+
const b = new PathBuilder();
|
|
4979
|
+
const st = {
|
|
4980
|
+
x: 0,
|
|
4981
|
+
y: 0,
|
|
4982
|
+
startX: 0,
|
|
4983
|
+
startY: 0,
|
|
4984
|
+
lastCubicCtrl: null,
|
|
4985
|
+
lastQuadCtrl: null
|
|
4986
|
+
};
|
|
4987
|
+
for (const { cmd, args } of tokens) {
|
|
4988
|
+
const upper = cmd.toUpperCase();
|
|
4989
|
+
const relative = cmd !== upper;
|
|
4990
|
+
const argCount = ARG_COUNTS[upper];
|
|
4991
|
+
if (argCount === void 0) {
|
|
4992
|
+
continue;
|
|
4993
|
+
}
|
|
4994
|
+
if (upper === "Z") {
|
|
4995
|
+
b.close();
|
|
4996
|
+
st.x = st.startX;
|
|
4997
|
+
st.y = st.startY;
|
|
4998
|
+
st.lastCubicCtrl = null;
|
|
4999
|
+
st.lastQuadCtrl = null;
|
|
5000
|
+
continue;
|
|
5001
|
+
}
|
|
5002
|
+
const groups = argCount === 0 ? 1 : Math.floor(args.length / argCount);
|
|
5003
|
+
for (let g = 0; g < groups; g++) {
|
|
5004
|
+
const a = args.slice(g * argCount, (g + 1) * argCount);
|
|
5005
|
+
const effectiveUpper = upper === "M" && g > 0 ? "L" : upper;
|
|
5006
|
+
runCommand(effectiveUpper, relative, a, st, b);
|
|
5007
|
+
}
|
|
5008
|
+
}
|
|
5009
|
+
return b.build();
|
|
5010
|
+
}
|
|
5011
|
+
function runCommand(upper, relative, a, st, b) {
|
|
5012
|
+
const rx = relative ? st.x : 0;
|
|
5013
|
+
const ry = relative ? st.y : 0;
|
|
5014
|
+
switch (upper) {
|
|
5015
|
+
case "M": {
|
|
5016
|
+
const x = a[0] + rx;
|
|
5017
|
+
const y = a[1] + ry;
|
|
5018
|
+
b.moveTo(x, y);
|
|
5019
|
+
st.x = x;
|
|
5020
|
+
st.y = y;
|
|
5021
|
+
st.startX = x;
|
|
5022
|
+
st.startY = y;
|
|
5023
|
+
st.lastCubicCtrl = null;
|
|
5024
|
+
st.lastQuadCtrl = null;
|
|
5025
|
+
return;
|
|
5026
|
+
}
|
|
5027
|
+
case "L": {
|
|
5028
|
+
const x = a[0] + rx;
|
|
5029
|
+
const y = a[1] + ry;
|
|
5030
|
+
b.lineTo(x, y);
|
|
5031
|
+
st.x = x;
|
|
5032
|
+
st.y = y;
|
|
5033
|
+
st.lastCubicCtrl = null;
|
|
5034
|
+
st.lastQuadCtrl = null;
|
|
5035
|
+
return;
|
|
5036
|
+
}
|
|
5037
|
+
case "H": {
|
|
5038
|
+
const x = a[0] + (relative ? st.x : 0);
|
|
5039
|
+
b.lineTo(x, st.y);
|
|
5040
|
+
st.x = x;
|
|
5041
|
+
st.lastCubicCtrl = null;
|
|
5042
|
+
st.lastQuadCtrl = null;
|
|
5043
|
+
return;
|
|
5044
|
+
}
|
|
5045
|
+
case "V": {
|
|
5046
|
+
const y = a[0] + (relative ? st.y : 0);
|
|
5047
|
+
b.lineTo(st.x, y);
|
|
5048
|
+
st.y = y;
|
|
5049
|
+
st.lastCubicCtrl = null;
|
|
5050
|
+
st.lastQuadCtrl = null;
|
|
5051
|
+
return;
|
|
5052
|
+
}
|
|
5053
|
+
case "C": {
|
|
5054
|
+
const x1 = a[0] + rx, y1 = a[1] + ry;
|
|
5055
|
+
const x2 = a[2] + rx, y2 = a[3] + ry;
|
|
5056
|
+
const x = a[4] + rx, y = a[5] + ry;
|
|
5057
|
+
b.curveTo(x1, y1, x2, y2, x, y);
|
|
5058
|
+
st.x = x;
|
|
5059
|
+
st.y = y;
|
|
5060
|
+
st.lastCubicCtrl = { x: x2, y: y2 };
|
|
5061
|
+
st.lastQuadCtrl = null;
|
|
5401
5062
|
return;
|
|
5402
5063
|
}
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5064
|
+
case "S": {
|
|
5065
|
+
const ref = st.lastCubicCtrl ?? { x: st.x, y: st.y };
|
|
5066
|
+
const x1 = 2 * st.x - ref.x;
|
|
5067
|
+
const y1 = 2 * st.y - ref.y;
|
|
5068
|
+
const x2 = a[0] + rx, y2 = a[1] + ry;
|
|
5069
|
+
const x = a[2] + rx, y = a[3] + ry;
|
|
5070
|
+
b.curveTo(x1, y1, x2, y2, x, y);
|
|
5071
|
+
st.x = x;
|
|
5072
|
+
st.y = y;
|
|
5073
|
+
st.lastCubicCtrl = { x: x2, y: y2 };
|
|
5074
|
+
st.lastQuadCtrl = null;
|
|
5406
5075
|
return;
|
|
5407
5076
|
}
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
anchorT.push(cornerT);
|
|
5418
|
-
if (onPositive) {
|
|
5419
|
-
idx.push(aOuterEnd, apexIdx, bOuterStart);
|
|
5420
|
-
idx.push(aOuterEnd, bOuterStart, jIdx);
|
|
5421
|
-
} else {
|
|
5422
|
-
idx.push(aOuterEnd, bOuterStart, apexIdx);
|
|
5423
|
-
idx.push(aOuterEnd, jIdx, bOuterStart);
|
|
5077
|
+
case "Q": {
|
|
5078
|
+
const x1 = a[0] + rx, y1 = a[1] + ry;
|
|
5079
|
+
const x = a[2] + rx, y = a[3] + ry;
|
|
5080
|
+
b.quadTo(x1, y1, x, y);
|
|
5081
|
+
st.x = x;
|
|
5082
|
+
st.y = y;
|
|
5083
|
+
st.lastQuadCtrl = { x: x1, y: y1 };
|
|
5084
|
+
st.lastCubicCtrl = null;
|
|
5085
|
+
return;
|
|
5424
5086
|
}
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5087
|
+
case "T": {
|
|
5088
|
+
const ref = st.lastQuadCtrl ?? { x: st.x, y: st.y };
|
|
5089
|
+
const x1 = 2 * st.x - ref.x;
|
|
5090
|
+
const y1 = 2 * st.y - ref.y;
|
|
5091
|
+
const x = a[0] + rx, y = a[1] + ry;
|
|
5092
|
+
b.quadTo(x1, y1, x, y);
|
|
5093
|
+
st.x = x;
|
|
5094
|
+
st.y = y;
|
|
5095
|
+
st.lastQuadCtrl = { x: x1, y: y1 };
|
|
5096
|
+
st.lastCubicCtrl = null;
|
|
5097
|
+
return;
|
|
5098
|
+
}
|
|
5099
|
+
case "A": {
|
|
5100
|
+
const rxA = Math.abs(a[0]);
|
|
5101
|
+
const ryA = Math.abs(a[1]);
|
|
5102
|
+
const xRot = a[2];
|
|
5103
|
+
const largeArc = a[3] !== 0;
|
|
5104
|
+
const sweep = a[4] !== 0;
|
|
5105
|
+
const ex = a[5] + rx;
|
|
5106
|
+
const ey = a[6] + ry;
|
|
5107
|
+
arcToCubics(st.x, st.y, ex, ey, rxA, ryA, xRot, largeArc, sweep, b);
|
|
5108
|
+
st.x = ex;
|
|
5109
|
+
st.y = ey;
|
|
5110
|
+
st.lastCubicCtrl = null;
|
|
5111
|
+
st.lastQuadCtrl = null;
|
|
5112
|
+
return;
|
|
5113
|
+
}
|
|
5114
|
+
default:
|
|
5115
|
+
return;
|
|
5430
5116
|
}
|
|
5431
5117
|
}
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
anchorA.push(cornerA);
|
|
5438
|
-
anchorB.push(cornerB);
|
|
5439
|
-
anchorT.push(cornerT);
|
|
5440
|
-
const startX = verts[aOuterEnd * 2] - cx;
|
|
5441
|
-
const startY = verts[aOuterEnd * 2 + 1] - cy;
|
|
5442
|
-
const endX = verts[bOuterStart * 2] - cx;
|
|
5443
|
-
const endY = verts[bOuterStart * 2 + 1] - cy;
|
|
5444
|
-
const r = Math.hypot(startX, startY);
|
|
5445
|
-
const startAngle = Math.atan2(startY, startX);
|
|
5446
|
-
const endAngle = Math.atan2(endY, endX);
|
|
5447
|
-
let sweep = endAngle - startAngle;
|
|
5448
|
-
if (onPositive && sweep < 0) sweep += 2 * Math.PI;
|
|
5449
|
-
else if (!onPositive && sweep > 0) sweep -= 2 * Math.PI;
|
|
5450
|
-
const steps = Math.max(1, Math.ceil(Math.abs(sweep) / ROUND_STEP_RAD));
|
|
5451
|
-
const stepAngle = sweep / steps;
|
|
5452
|
-
let prevIdx = aOuterEnd;
|
|
5453
|
-
for (let i = 1; i < steps; i++) {
|
|
5454
|
-
const ang = startAngle + i * stepAngle;
|
|
5455
|
-
const fx = cx + Math.cos(ang) * r;
|
|
5456
|
-
const fy = cy + Math.sin(ang) * r;
|
|
5457
|
-
const newIdx = verts.length / 2;
|
|
5458
|
-
verts.push(fx, fy);
|
|
5459
|
-
anchorA.push(cornerA);
|
|
5460
|
-
anchorB.push(cornerB);
|
|
5461
|
-
anchorT.push(cornerT);
|
|
5462
|
-
if (onPositive) idx.push(prevIdx, pivotIdx, newIdx);
|
|
5463
|
-
else idx.push(prevIdx, newIdx, pivotIdx);
|
|
5464
|
-
prevIdx = newIdx;
|
|
5118
|
+
function arcToCubics(x1, y1, x2, y2, rx, ry, xAxisRotationDeg, largeArc, sweep, b) {
|
|
5119
|
+
if (x1 === x2 && y1 === y2) return;
|
|
5120
|
+
if (rx === 0 || ry === 0) {
|
|
5121
|
+
b.lineTo(x2, y2);
|
|
5122
|
+
return;
|
|
5465
5123
|
}
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
const
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
if (
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
var DECORATION_THICKNESS = 0.05;
|
|
5487
|
-
function fillKey(p) {
|
|
5488
|
-
if ("color" in p) return `s:${p.color}:${p.opacity ?? 1}`;
|
|
5489
|
-
return `nx:${Math.random()}`;
|
|
5490
|
-
}
|
|
5491
|
-
function sameFill(a, b) {
|
|
5492
|
-
if (a === b) return true;
|
|
5493
|
-
if ("color" in a && "color" in b) {
|
|
5494
|
-
return a.color === b.color && (a.opacity ?? 1) === (b.opacity ?? 1);
|
|
5124
|
+
const phi = xAxisRotationDeg * Math.PI / 180;
|
|
5125
|
+
const cosPhi = Math.cos(phi);
|
|
5126
|
+
const sinPhi = Math.sin(phi);
|
|
5127
|
+
const dx = (x1 - x2) / 2;
|
|
5128
|
+
const dy = (y1 - y2) / 2;
|
|
5129
|
+
const x1p = cosPhi * dx + sinPhi * dy;
|
|
5130
|
+
const y1p = -sinPhi * dx + cosPhi * dy;
|
|
5131
|
+
let rxs = rx * rx;
|
|
5132
|
+
let rys = ry * ry;
|
|
5133
|
+
const x1ps = x1p * x1p;
|
|
5134
|
+
const y1ps = y1p * y1p;
|
|
5135
|
+
const lambda = x1ps / rxs + y1ps / rys;
|
|
5136
|
+
let rxFixed = rx;
|
|
5137
|
+
let ryFixed = ry;
|
|
5138
|
+
if (lambda > 1) {
|
|
5139
|
+
const sqrtLambda = Math.sqrt(lambda);
|
|
5140
|
+
rxFixed = sqrtLambda * rx;
|
|
5141
|
+
ryFixed = sqrtLambda * ry;
|
|
5142
|
+
rxs = rxFixed * rxFixed;
|
|
5143
|
+
rys = ryFixed * ryFixed;
|
|
5495
5144
|
}
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
(
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
const source = sourceOverride ?? resolved.source;
|
|
5518
|
-
const key = groupKey(
|
|
5519
|
-
atlasFamily,
|
|
5520
|
-
resolvedWeight,
|
|
5521
|
-
resolvedStyle,
|
|
5522
|
-
resolved.synthetic,
|
|
5523
|
-
run.fill,
|
|
5524
|
-
source === "outline" ? run.stroke : void 0,
|
|
5525
|
-
source,
|
|
5526
|
-
page
|
|
5145
|
+
const sign2 = largeArc === sweep ? -1 : 1;
|
|
5146
|
+
const num = rxs * rys - rxs * y1ps - rys * x1ps;
|
|
5147
|
+
const den = rxs * y1ps + rys * x1ps;
|
|
5148
|
+
const factor = sign2 * Math.sqrt(Math.max(0, num / den));
|
|
5149
|
+
const cxp = factor * (rxFixed * y1p) / ryFixed;
|
|
5150
|
+
const cyp = factor * -(ryFixed * x1p) / rxFixed;
|
|
5151
|
+
const cx = cosPhi * cxp - sinPhi * cyp + (x1 + x2) / 2;
|
|
5152
|
+
const cy = sinPhi * cxp + cosPhi * cyp + (y1 + y2) / 2;
|
|
5153
|
+
const angle = (ux, uy, vx, vy) => {
|
|
5154
|
+
const dot = ux * vx + uy * vy;
|
|
5155
|
+
const len = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
|
|
5156
|
+
let a = Math.acos(Math.max(-1, Math.min(1, dot / len)));
|
|
5157
|
+
if (ux * vy - uy * vx < 0) a = -a;
|
|
5158
|
+
return a;
|
|
5159
|
+
};
|
|
5160
|
+
const theta1 = angle(1, 0, (x1p - cxp) / rxFixed, (y1p - cyp) / ryFixed);
|
|
5161
|
+
let dTheta = angle(
|
|
5162
|
+
(x1p - cxp) / rxFixed,
|
|
5163
|
+
(y1p - cyp) / ryFixed,
|
|
5164
|
+
(-x1p - cxp) / rxFixed,
|
|
5165
|
+
(-y1p - cyp) / ryFixed
|
|
5527
5166
|
);
|
|
5528
|
-
|
|
5529
|
-
if (
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5167
|
+
if (!sweep && dTheta > 0) dTheta -= 2 * Math.PI;
|
|
5168
|
+
else if (sweep && dTheta < 0) dTheta += 2 * Math.PI;
|
|
5169
|
+
const segCount = Math.max(1, Math.ceil(Math.abs(dTheta) / (Math.PI / 2)));
|
|
5170
|
+
const segAngle = dTheta / segCount;
|
|
5171
|
+
const t = 4 / 3 * Math.tan(segAngle / 4);
|
|
5172
|
+
let prevX = x1;
|
|
5173
|
+
let prevY = y1;
|
|
5174
|
+
let prevDx = -Math.sin(theta1) * rxFixed;
|
|
5175
|
+
let prevDy = Math.cos(theta1) * ryFixed;
|
|
5176
|
+
for (let i = 1; i <= segCount; i++) {
|
|
5177
|
+
const theta = theta1 + i * segAngle;
|
|
5178
|
+
const cosT = Math.cos(theta);
|
|
5179
|
+
const sinT = Math.sin(theta);
|
|
5180
|
+
const px = cosPhi * (cosT * rxFixed) - sinPhi * (sinT * ryFixed) + cx;
|
|
5181
|
+
const py = sinPhi * (cosT * rxFixed) + cosPhi * (sinT * ryFixed) + cy;
|
|
5182
|
+
const dxNew = -sinT * rxFixed;
|
|
5183
|
+
const dyNew = cosT * ryFixed;
|
|
5184
|
+
const c1x = prevX + cosPhi * (t * prevDx) - sinPhi * (t * prevDy);
|
|
5185
|
+
const c1y = prevY + sinPhi * (t * prevDx) + cosPhi * (t * prevDy);
|
|
5186
|
+
const c2x = px - cosPhi * (t * dxNew) + sinPhi * (t * dyNew);
|
|
5187
|
+
const c2y = py - sinPhi * (t * dxNew) - cosPhi * (t * dyNew);
|
|
5188
|
+
b.curveTo(c1x, c1y, c2x, c2y, px, py);
|
|
5189
|
+
prevX = px;
|
|
5190
|
+
prevY = py;
|
|
5191
|
+
prevDx = dxNew;
|
|
5192
|
+
prevDy = dyNew;
|
|
5545
5193
|
}
|
|
5546
|
-
return g;
|
|
5547
5194
|
}
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
const
|
|
5553
|
-
if (
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
}
|
|
5559
|
-
warnMissingGlyphOnce(resolved.resolved.family, cp);
|
|
5560
|
-
return null;
|
|
5195
|
+
var OUTLINE_FLATTEN_TOLERANCE = 1 / 4096;
|
|
5196
|
+
var OUTLINE_MESH_CACHE_LIMIT = 2048;
|
|
5197
|
+
var cache3 = /* @__PURE__ */ new Map();
|
|
5198
|
+
function outlineMesh(key, d) {
|
|
5199
|
+
const cached2 = cache3.get(key);
|
|
5200
|
+
if (cached2 !== void 0) return cached2;
|
|
5201
|
+
if (cache3.size >= OUTLINE_MESH_CACHE_LIMIT) cache3 = /* @__PURE__ */ new Map();
|
|
5202
|
+
const mesh = tessellate(pathFromD(d), { flattenTolerance: OUTLINE_FLATTEN_TOLERANCE });
|
|
5203
|
+
cache3.set(key, mesh);
|
|
5204
|
+
return mesh;
|
|
5561
5205
|
}
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
if (warnedMissingGlyphs.has(key)) return;
|
|
5566
|
-
warnedMissingGlyphs.add(key);
|
|
5567
|
-
const ch = String.fromCodePoint(cp);
|
|
5568
|
-
console.warn(
|
|
5569
|
-
`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.`
|
|
5570
|
-
);
|
|
5206
|
+
function extractPolylines(path, opts = {}) {
|
|
5207
|
+
if (path.kind === "rect") return [extractRect(path)];
|
|
5208
|
+
return extractPolygon(path, opts);
|
|
5571
5209
|
}
|
|
5572
|
-
function
|
|
5573
|
-
const
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
continue;
|
|
5210
|
+
function extractRect(p) {
|
|
5211
|
+
const { x, y, width: w, height: h } = p;
|
|
5212
|
+
return {
|
|
5213
|
+
points: [x, y, x + w, y, x + w, y + h, x, y + h],
|
|
5214
|
+
closed: true,
|
|
5215
|
+
anchorA: new Uint32Array([0, 1, 2, 3]),
|
|
5216
|
+
anchorB: new Uint32Array([0, 1, 2, 3]),
|
|
5217
|
+
anchorT: new Float32Array([0, 0, 0, 0])
|
|
5218
|
+
};
|
|
5219
|
+
}
|
|
5220
|
+
function extractPolygon(p, opts) {
|
|
5221
|
+
const tolerance = opts.flattenTolerance ?? DEFAULT_FLATTEN_TOLERANCE;
|
|
5222
|
+
const { commands, coords } = p;
|
|
5223
|
+
const out = [];
|
|
5224
|
+
let anchorCounter = 0;
|
|
5225
|
+
let pts = null;
|
|
5226
|
+
let aA = null;
|
|
5227
|
+
let aB = null;
|
|
5228
|
+
let aT = null;
|
|
5229
|
+
let current = null;
|
|
5230
|
+
let coordIdx = 0;
|
|
5231
|
+
let prevX = 0;
|
|
5232
|
+
let prevY = 0;
|
|
5233
|
+
let prevAnchor = -1;
|
|
5234
|
+
const beginContour = () => {
|
|
5235
|
+
pts = [];
|
|
5236
|
+
aA = [];
|
|
5237
|
+
aB = [];
|
|
5238
|
+
aT = [];
|
|
5239
|
+
const next = { points: pts, closed: false };
|
|
5240
|
+
out.push(next);
|
|
5241
|
+
return next;
|
|
5242
|
+
};
|
|
5243
|
+
const dropDuplicateClosingPoint = () => {
|
|
5244
|
+
if (!pts || !aA || !aB || !aT) return;
|
|
5245
|
+
const n = pts.length / 2;
|
|
5246
|
+
if (n < 2) return;
|
|
5247
|
+
if (pts[0] !== pts[(n - 1) * 2] || pts[1] !== pts[(n - 1) * 2 + 1]) return;
|
|
5248
|
+
pts.length -= 2;
|
|
5249
|
+
aA.length -= 1;
|
|
5250
|
+
aB.length -= 1;
|
|
5251
|
+
aT.length -= 1;
|
|
5252
|
+
};
|
|
5253
|
+
const commit = (target) => {
|
|
5254
|
+
if (!pts || !aA || !aB || !aT) return;
|
|
5255
|
+
target.anchorA = new Uint32Array(aA);
|
|
5256
|
+
target.anchorB = new Uint32Array(aB);
|
|
5257
|
+
target.anchorT = new Float32Array(aT);
|
|
5258
|
+
};
|
|
5259
|
+
for (let cmdIdx = 0; cmdIdx < commands.length; cmdIdx++) {
|
|
5260
|
+
const cmd = commands[cmdIdx];
|
|
5261
|
+
switch (cmd) {
|
|
5262
|
+
case PATH_M2: {
|
|
5263
|
+
if (current) commit(current);
|
|
5264
|
+
current = beginContour();
|
|
5265
|
+
prevX = coords[coordIdx];
|
|
5266
|
+
prevY = coords[coordIdx + 1];
|
|
5267
|
+
pts.push(prevX, prevY);
|
|
5268
|
+
aA.push(anchorCounter);
|
|
5269
|
+
aB.push(anchorCounter);
|
|
5270
|
+
aT.push(0);
|
|
5271
|
+
prevAnchor = anchorCounter;
|
|
5272
|
+
anchorCounter++;
|
|
5273
|
+
coordIdx += 2;
|
|
5274
|
+
break;
|
|
5638
5275
|
}
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5276
|
+
case PATH_L2: {
|
|
5277
|
+
prevX = coords[coordIdx];
|
|
5278
|
+
prevY = coords[coordIdx + 1];
|
|
5279
|
+
pts.push(prevX, prevY);
|
|
5280
|
+
aA.push(anchorCounter);
|
|
5281
|
+
aB.push(anchorCounter);
|
|
5282
|
+
aT.push(0);
|
|
5283
|
+
prevAnchor = anchorCounter;
|
|
5284
|
+
anchorCounter++;
|
|
5285
|
+
coordIdx += 2;
|
|
5286
|
+
break;
|
|
5645
5287
|
}
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
const
|
|
5651
|
-
|
|
5288
|
+
case PATH_Q2: {
|
|
5289
|
+
const cx = coords[coordIdx], cy = coords[coordIdx + 1];
|
|
5290
|
+
const ex = coords[coordIdx + 2], ey = coords[coordIdx + 3];
|
|
5291
|
+
const targetAnchor = anchorCounter;
|
|
5292
|
+
const segStart = pts.length / 2;
|
|
5293
|
+
const arcAccum = [];
|
|
5294
|
+
const total = flattenQuadraticWithArcLen(prevX, prevY, cx, cy, ex, ey, tolerance, pts, arcAccum);
|
|
5295
|
+
for (let k = 0; k < arcAccum.length; k++) {
|
|
5296
|
+
aA.push(prevAnchor);
|
|
5297
|
+
aB.push(targetAnchor);
|
|
5298
|
+
aT.push(total > 0 ? arcAccum[k] / total : 0);
|
|
5299
|
+
}
|
|
5300
|
+
const lastIdx = segStart + arcAccum.length - 1;
|
|
5301
|
+
aA[lastIdx] = targetAnchor;
|
|
5302
|
+
aB[lastIdx] = targetAnchor;
|
|
5303
|
+
aT[lastIdx] = 0;
|
|
5304
|
+
prevX = ex;
|
|
5305
|
+
prevY = ey;
|
|
5306
|
+
prevAnchor = targetAnchor;
|
|
5307
|
+
anchorCounter++;
|
|
5308
|
+
coordIdx += 4;
|
|
5309
|
+
break;
|
|
5652
5310
|
}
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
}
|
|
5677
|
-
let i = 0;
|
|
5678
|
-
while (i < entries.length) {
|
|
5679
|
-
const e = entries[i];
|
|
5680
|
-
if (e.isNewline) {
|
|
5681
|
-
if (cur.entries.length === 0) {
|
|
5682
|
-
cur.height = Math.max(cur.height, e.fontSize * opts.lineHeight);
|
|
5683
|
-
cur.blank = e;
|
|
5311
|
+
case PATH_C2: {
|
|
5312
|
+
const c1x = coords[coordIdx], c1y = coords[coordIdx + 1];
|
|
5313
|
+
const c2x = coords[coordIdx + 2], c2y = coords[coordIdx + 3];
|
|
5314
|
+
const ex = coords[coordIdx + 4], ey = coords[coordIdx + 5];
|
|
5315
|
+
const targetAnchor = anchorCounter;
|
|
5316
|
+
const segStart = pts.length / 2;
|
|
5317
|
+
const arcAccum = [];
|
|
5318
|
+
const total = flattenCubicWithArcLen(prevX, prevY, c1x, c1y, c2x, c2y, ex, ey, tolerance, pts, arcAccum);
|
|
5319
|
+
for (let k = 0; k < arcAccum.length; k++) {
|
|
5320
|
+
aA.push(prevAnchor);
|
|
5321
|
+
aB.push(targetAnchor);
|
|
5322
|
+
aT.push(total > 0 ? arcAccum[k] / total : 0);
|
|
5323
|
+
}
|
|
5324
|
+
const lastIdx = segStart + arcAccum.length - 1;
|
|
5325
|
+
aA[lastIdx] = targetAnchor;
|
|
5326
|
+
aB[lastIdx] = targetAnchor;
|
|
5327
|
+
aT[lastIdx] = 0;
|
|
5328
|
+
prevX = ex;
|
|
5329
|
+
prevY = ey;
|
|
5330
|
+
prevAnchor = targetAnchor;
|
|
5331
|
+
anchorCounter++;
|
|
5332
|
+
coordIdx += 6;
|
|
5333
|
+
break;
|
|
5684
5334
|
}
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
cur.entries.push(e);
|
|
5692
|
-
cur.width += e.kerningBefore + e.advance + e.tracking;
|
|
5693
|
-
cur.height = Math.max(cur.height, e.fontSize * opts.lineHeight);
|
|
5335
|
+
case PATH_Z2: {
|
|
5336
|
+
if (current) {
|
|
5337
|
+
current.closed = true;
|
|
5338
|
+
dropDuplicateClosingPoint();
|
|
5339
|
+
}
|
|
5340
|
+
break;
|
|
5694
5341
|
}
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
}
|
|
5698
|
-
let j = i;
|
|
5699
|
-
let wordWidth = 0;
|
|
5700
|
-
while (j < entries.length && !entries[j].isSpace && !entries[j].isNewline) {
|
|
5701
|
-
const w = entries[j];
|
|
5702
|
-
wordWidth += w.kerningBefore + w.advance + w.tracking;
|
|
5703
|
-
j++;
|
|
5704
|
-
}
|
|
5705
|
-
if (Number.isFinite(opts.maxWidth) && cur.width + wordWidth > opts.maxWidth && cur.entries.length > 0) {
|
|
5706
|
-
commitLine();
|
|
5707
|
-
}
|
|
5708
|
-
for (let k = i; k < j; k++) {
|
|
5709
|
-
const w = entries[k];
|
|
5710
|
-
const kerningBefore = cur.entries.length === 0 ? 0 : w.kerningBefore;
|
|
5711
|
-
cur.entries.push({ ...w, kerningBefore });
|
|
5712
|
-
cur.width += kerningBefore + w.advance + w.tracking;
|
|
5713
|
-
cur.height = Math.max(cur.height, w.fontSize * opts.lineHeight);
|
|
5342
|
+
default:
|
|
5343
|
+
throw new Error(`extractPolylines: unknown command code ${cmd}`);
|
|
5714
5344
|
}
|
|
5715
|
-
i = j;
|
|
5716
5345
|
}
|
|
5717
|
-
if (
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5346
|
+
if (current) commit(current);
|
|
5347
|
+
return out;
|
|
5348
|
+
}
|
|
5349
|
+
var EMPTY_MESH = {
|
|
5350
|
+
vertices: new Float32Array(0),
|
|
5351
|
+
indices: new Uint32Array(0),
|
|
5352
|
+
anchorA: new Uint32Array(0),
|
|
5353
|
+
anchorB: new Uint32Array(0),
|
|
5354
|
+
anchorT: new Float32Array(0)
|
|
5355
|
+
};
|
|
5356
|
+
function tessellateStroke(path, stroke, opts = {}) {
|
|
5357
|
+
const width = stroke.width ?? 1;
|
|
5358
|
+
if (width <= 0) return EMPTY_MESH;
|
|
5359
|
+
const join = stroke.join ?? "miter";
|
|
5360
|
+
const cap = stroke.cap ?? "butt";
|
|
5361
|
+
const align = stroke.align ?? "center";
|
|
5362
|
+
const miterLimit = stroke.miterLimit ?? DEFAULT_MITER_LIMIT;
|
|
5363
|
+
const varyingThreshold = stroke.varyingWidthJoinThreshold ?? DEFAULT_VARYING_WIDTH_JOIN_THRESHOLD;
|
|
5364
|
+
let workingPath = path;
|
|
5365
|
+
if (path.kind === "rect" && align !== "center") {
|
|
5366
|
+
const aligned = alignedStrokeRect(path, align, width);
|
|
5367
|
+
workingPath = {
|
|
5368
|
+
kind: "rect",
|
|
5369
|
+
x: aligned.x,
|
|
5370
|
+
y: aligned.y,
|
|
5371
|
+
width: aligned.width,
|
|
5372
|
+
height: aligned.height
|
|
5373
|
+
};
|
|
5724
5374
|
}
|
|
5725
|
-
const
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5375
|
+
const polylines = extractPolylines(workingPath, opts);
|
|
5376
|
+
if (stroke.vertexWidths && stroke.vertexWidths.length > 0) {
|
|
5377
|
+
for (const pl of polylines) populatePolylineWidths(pl, stroke.vertexWidths, width);
|
|
5378
|
+
}
|
|
5379
|
+
const dash = stroke.dash ?? [];
|
|
5380
|
+
const verts = [];
|
|
5381
|
+
const idx = [];
|
|
5382
|
+
const anchorA = [];
|
|
5383
|
+
const anchorB = [];
|
|
5384
|
+
const anchorT = [];
|
|
5385
|
+
for (const pl of polylines) {
|
|
5386
|
+
const subs = dash.length > 0 ? splitForDash(pl, dash) : [pl];
|
|
5387
|
+
for (const sub2 of subs) {
|
|
5388
|
+
expandPolyline(sub2, width, join, cap, miterLimit, varyingThreshold, verts, idx, anchorA, anchorB, anchorT);
|
|
5735
5389
|
}
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5390
|
+
}
|
|
5391
|
+
return {
|
|
5392
|
+
vertices: new Float32Array(verts),
|
|
5393
|
+
indices: new Uint32Array(idx),
|
|
5394
|
+
anchorA: new Uint32Array(anchorA),
|
|
5395
|
+
anchorB: new Uint32Array(anchorB),
|
|
5396
|
+
anchorT: new Float32Array(anchorT)
|
|
5397
|
+
};
|
|
5398
|
+
}
|
|
5399
|
+
function expandPolyline(pl, width, join, cap, miterLimit, varyingThreshold, verts, idx, anchorA, anchorB, anchorT) {
|
|
5400
|
+
const half = width / 2;
|
|
5401
|
+
const pts = pl.points;
|
|
5402
|
+
const ptCount = pts.length / 2;
|
|
5403
|
+
const segCount = ptCount - 1;
|
|
5404
|
+
if (segCount < 1) return;
|
|
5405
|
+
const plA = pl.anchorA ?? new Uint32Array(ptCount);
|
|
5406
|
+
const plB = pl.anchorB ?? new Uint32Array(ptCount);
|
|
5407
|
+
const plT = pl.anchorT ?? new Float32Array(ptCount);
|
|
5408
|
+
const plHalf = pl.widths ? pl.widths.map((w) => w / 2) : null;
|
|
5409
|
+
const segs = [];
|
|
5410
|
+
const segSrcIdx = [];
|
|
5411
|
+
for (let s = 0; s < segCount; s++) {
|
|
5412
|
+
const ha = plHalf ? plHalf[s] : half;
|
|
5413
|
+
const hb = plHalf ? plHalf[s + 1] : half;
|
|
5414
|
+
const seg = makeSeg(pts[s * 2], pts[s * 2 + 1], pts[(s + 1) * 2], pts[(s + 1) * 2 + 1], ha, hb);
|
|
5415
|
+
if (seg) {
|
|
5416
|
+
segs.push(seg);
|
|
5417
|
+
segSrcIdx.push(s);
|
|
5739
5418
|
}
|
|
5740
5419
|
}
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
const
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
return opts.align === "center" ? slack / 2 : slack;
|
|
5753
|
-
})();
|
|
5754
|
-
const lineX0 = alignShift;
|
|
5755
|
-
const baselineSource = line.entries[0] ?? line.blank;
|
|
5756
|
-
const lineBaselineY = baselineSource ? penY + baselineSource.font.common.base * (baselineSource.fontSize / baselineSource.font.info.size) : penY;
|
|
5757
|
-
let penX = lineX0;
|
|
5758
|
-
for (const e of line.entries) {
|
|
5759
|
-
penX += e.kerningBefore;
|
|
5760
|
-
const step = e.advance + e.tracking;
|
|
5761
|
-
const scale2 = e.fontSize / e.font.info.size;
|
|
5762
|
-
const baselineY = penY + e.font.common.base * scale2;
|
|
5763
|
-
if (e.run.underline || e.run.strikethrough) {
|
|
5764
|
-
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)) {
|
|
5765
|
-
span.x1 = penX + step;
|
|
5766
|
-
} else {
|
|
5767
|
-
flushSpan();
|
|
5768
|
-
span = {
|
|
5769
|
-
underline: e.run.underline,
|
|
5770
|
-
strikethrough: e.run.strikethrough,
|
|
5771
|
-
fill: e.run.fill,
|
|
5772
|
-
fontSize: e.fontSize,
|
|
5773
|
-
baselineY,
|
|
5774
|
-
x0: penX,
|
|
5775
|
-
x1: penX + step
|
|
5776
|
-
};
|
|
5777
|
-
}
|
|
5778
|
-
} else {
|
|
5779
|
-
flushSpan();
|
|
5780
|
-
}
|
|
5781
|
-
const outlineD = outlineFor(e);
|
|
5782
|
-
if (outlineD !== null) {
|
|
5783
|
-
const group2 = getOrCreateGroup(ctx, e.run, e.resolved, 0, "outline");
|
|
5784
|
-
group2.glyphs.push({
|
|
5785
|
-
d: outlineD,
|
|
5786
|
-
key: `${e.resolved.resolved.family}|${e.resolved.resolved.weight}|${e.resolved.resolved.style}|${e.cp}`,
|
|
5787
|
-
x: penX,
|
|
5788
|
-
baselineY,
|
|
5789
|
-
// Em space is unit-scale, so world units per em is just the size.
|
|
5790
|
-
scale: e.fontSize
|
|
5791
|
-
});
|
|
5792
|
-
penX += step;
|
|
5793
|
-
continue;
|
|
5794
|
-
}
|
|
5795
|
-
if (e.advance === 0 || e.glyph.width === 0 || e.glyph.page < 0) {
|
|
5796
|
-
penX += step;
|
|
5797
|
-
continue;
|
|
5798
|
-
}
|
|
5799
|
-
const group = getOrCreateGroup(ctx, e.run, e.resolved, e.glyph.page);
|
|
5800
|
-
const atlasW = e.font.common.scaleW;
|
|
5801
|
-
const atlasH = e.font.common.scaleH;
|
|
5802
|
-
const qx0 = penX + e.glyph.xoffset * scale2;
|
|
5803
|
-
const qy0 = penY + e.glyph.yoffset * scale2;
|
|
5804
|
-
const qx1 = qx0 + e.glyph.width * scale2;
|
|
5805
|
-
const qy1 = qy0 + e.glyph.height * scale2;
|
|
5806
|
-
const u0 = e.glyph.x / atlasW;
|
|
5807
|
-
const v0 = e.glyph.y / atlasH;
|
|
5808
|
-
const u1 = (e.glyph.x + e.glyph.width) / atlasW;
|
|
5809
|
-
const v1 = (e.glyph.y + e.glyph.height) / atlasH;
|
|
5810
|
-
group.quads.push({ x0: qx0, y0: qy0, x1: qx1, y1: qy1, u0, v0, u1, v1, baselineY });
|
|
5811
|
-
penX += step;
|
|
5420
|
+
let closerSrcIdx = -1;
|
|
5421
|
+
if (pl.closed && segs.length >= 1) {
|
|
5422
|
+
const last = segs[segs.length - 1];
|
|
5423
|
+
const first = segs[0];
|
|
5424
|
+
const haCloser = plHalf ? plHalf[ptCount - 1] : half;
|
|
5425
|
+
const hbCloser = plHalf ? plHalf[0] : half;
|
|
5426
|
+
const closer = makeSeg(last.bx, last.by, first.ax, first.ay, haCloser, hbCloser);
|
|
5427
|
+
if (closer) {
|
|
5428
|
+
segs.push(closer);
|
|
5429
|
+
closerSrcIdx = ptCount - 1;
|
|
5430
|
+
segSrcIdx.push(closerSrcIdx);
|
|
5812
5431
|
}
|
|
5813
|
-
flushSpan();
|
|
5814
|
-
lineBoxes.push({
|
|
5815
|
-
x0: lineX0,
|
|
5816
|
-
y0: penY,
|
|
5817
|
-
x1: lineX0 + line.width,
|
|
5818
|
-
y1: penY + line.height,
|
|
5819
|
-
baselineY: lineBaselineY
|
|
5820
|
-
});
|
|
5821
|
-
maxLineWidth = Math.max(maxLineWidth, line.width);
|
|
5822
|
-
penY += line.height;
|
|
5823
5432
|
}
|
|
5824
|
-
return
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5433
|
+
if (segs.length === 0) return;
|
|
5434
|
+
const segBaseIdx = [];
|
|
5435
|
+
for (let s = 0; s < segs.length; s++) {
|
|
5436
|
+
const seg = segs[s];
|
|
5437
|
+
const startSrc = segSrcIdx[s];
|
|
5438
|
+
const endSrc = s === segs.length - 1 && pl.closed && closerSrcIdx >= 0 ? 0 : startSrc + 1;
|
|
5439
|
+
const base = verts.length / 2;
|
|
5440
|
+
segBaseIdx.push(base);
|
|
5441
|
+
const nxA = seg.nxUnit * seg.halfA, nyA = seg.nyUnit * seg.halfA;
|
|
5442
|
+
const nxB = seg.nxUnit * seg.halfB, nyB = seg.nyUnit * seg.halfB;
|
|
5443
|
+
verts.push(seg.ax + nxA, seg.ay + nyA);
|
|
5444
|
+
anchorA.push(plA[startSrc]);
|
|
5445
|
+
anchorB.push(plB[startSrc]);
|
|
5446
|
+
anchorT.push(plT[startSrc]);
|
|
5447
|
+
verts.push(seg.ax - nxA, seg.ay - nyA);
|
|
5448
|
+
anchorA.push(plA[startSrc]);
|
|
5449
|
+
anchorB.push(plB[startSrc]);
|
|
5450
|
+
anchorT.push(plT[startSrc]);
|
|
5451
|
+
verts.push(seg.bx + nxB, seg.by + nyB);
|
|
5452
|
+
anchorA.push(plA[endSrc]);
|
|
5453
|
+
anchorB.push(plB[endSrc]);
|
|
5454
|
+
anchorT.push(plT[endSrc]);
|
|
5455
|
+
verts.push(seg.bx - nxB, seg.by - nyB);
|
|
5456
|
+
anchorA.push(plA[endSrc]);
|
|
5457
|
+
anchorB.push(plB[endSrc]);
|
|
5458
|
+
anchorT.push(plT[endSrc]);
|
|
5459
|
+
idx.push(base, base + 1, base + 2, base + 1, base + 3, base + 2);
|
|
5460
|
+
}
|
|
5461
|
+
const joinCount = pl.closed ? segs.length : segs.length - 1;
|
|
5462
|
+
for (let j = 0; j < joinCount; j++) {
|
|
5463
|
+
emitJoin(segs, segBaseIdx, j, join, miterLimit, varyingThreshold, verts, idx, anchorA, anchorB, anchorT, segSrcIdx, plA, plB, plT);
|
|
5464
|
+
}
|
|
5465
|
+
if (!pl.closed && cap !== "butt") {
|
|
5466
|
+
const first = segs[0];
|
|
5467
|
+
const firstBase = segBaseIdx[0];
|
|
5468
|
+
emitCap(first, firstBase + 0, firstBase + 1, false, first.halfA, cap, verts, idx, anchorA, anchorB, anchorT, plA[0], plB[0], plT[0]);
|
|
5469
|
+
const last = segs[segs.length - 1];
|
|
5470
|
+
const lastBase = segBaseIdx[segs.length - 1];
|
|
5471
|
+
const lastSrc = segSrcIdx[segs.length - 1] + 1;
|
|
5472
|
+
emitCap(last, lastBase + 2, lastBase + 3, true, last.halfB, cap, verts, idx, anchorA, anchorB, anchorT, plA[lastSrc], plB[lastSrc], plT[lastSrc]);
|
|
5853
5473
|
}
|
|
5854
|
-
const key = variantKey2(runs, opts);
|
|
5855
|
-
const hit = entry.byVariant.get(key);
|
|
5856
|
-
if (hit !== void 0) return hit;
|
|
5857
|
-
const laid = layoutRuns(runs, opts);
|
|
5858
|
-
if (entry.byVariant.size >= LAYOUT_CACHE_VARIANT_LIMIT) entry.byVariant.clear();
|
|
5859
|
-
entry.byVariant.set(key, laid);
|
|
5860
|
-
return laid;
|
|
5861
5474
|
}
|
|
5862
|
-
function
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5475
|
+
function populatePolylineWidths(pl, vertexWidths, fallbackWidth) {
|
|
5476
|
+
const ptCount = pl.points.length / 2;
|
|
5477
|
+
if (ptCount === 0) return;
|
|
5478
|
+
const aA = pl.anchorA ?? new Uint32Array(ptCount);
|
|
5479
|
+
const aB = pl.anchorB ?? new Uint32Array(ptCount);
|
|
5480
|
+
const aT = pl.anchorT ?? new Float32Array(ptCount);
|
|
5481
|
+
const widths = new Float32Array(ptCount);
|
|
5482
|
+
const read2 = (i) => {
|
|
5483
|
+
const v = vertexWidths[i];
|
|
5484
|
+
return typeof v === "number" && isFinite(v) && v > 0 ? v : fallbackWidth;
|
|
5485
|
+
};
|
|
5486
|
+
for (let i = 0; i < ptCount; i++) {
|
|
5487
|
+
const a = aA[i], b = aB[i], t = aT[i];
|
|
5488
|
+
if (a === b) {
|
|
5489
|
+
widths[i] = read2(a);
|
|
5490
|
+
} else {
|
|
5491
|
+
const wa = read2(a);
|
|
5492
|
+
const wb = read2(b);
|
|
5493
|
+
widths[i] = wa + (wb - wa) * t;
|
|
5494
|
+
}
|
|
5495
|
+
}
|
|
5496
|
+
pl.widths = widths;
|
|
5866
5497
|
}
|
|
5867
|
-
var
|
|
5868
|
-
function
|
|
5869
|
-
const
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5498
|
+
var ROUND_CAP_STEP_RAD = 10 * Math.PI / 180;
|
|
5499
|
+
function emitCap(seg, leftIdx, rightIdx, atEnd, half, cap, verts, idx, anchorA, anchorB, anchorT, endA, endB, endT) {
|
|
5500
|
+
const sign2 = atEnd ? 1 : -1;
|
|
5501
|
+
const dx = (seg.bx - seg.ax) / seg.len * sign2;
|
|
5502
|
+
const dy = (seg.by - seg.ay) / seg.len * sign2;
|
|
5503
|
+
const cx = atEnd ? seg.bx : seg.ax;
|
|
5504
|
+
const cy = atEnd ? seg.by : seg.ay;
|
|
5505
|
+
const nx = seg.nxUnit * half;
|
|
5506
|
+
const ny = seg.nyUnit * half;
|
|
5507
|
+
if (cap === "square") {
|
|
5508
|
+
const ox = cx + dx * half;
|
|
5509
|
+
const oy = cy + dy * half;
|
|
5510
|
+
const lOut = verts.length / 2;
|
|
5511
|
+
verts.push(ox + nx, oy + ny);
|
|
5512
|
+
anchorA.push(endA);
|
|
5513
|
+
anchorB.push(endB);
|
|
5514
|
+
anchorT.push(endT);
|
|
5515
|
+
const rOut = verts.length / 2;
|
|
5516
|
+
verts.push(ox - nx, oy - ny);
|
|
5517
|
+
anchorA.push(endA);
|
|
5518
|
+
anchorB.push(endB);
|
|
5519
|
+
anchorT.push(endT);
|
|
5520
|
+
if (atEnd) {
|
|
5521
|
+
idx.push(leftIdx, lOut, rOut, leftIdx, rOut, rightIdx);
|
|
5879
5522
|
} else {
|
|
5880
|
-
|
|
5523
|
+
idx.push(leftIdx, rOut, lOut, leftIdx, rightIdx, rOut);
|
|
5881
5524
|
}
|
|
5525
|
+
return;
|
|
5526
|
+
}
|
|
5527
|
+
if (cap === "round") {
|
|
5528
|
+
const pivotIdx = verts.length / 2;
|
|
5529
|
+
verts.push(cx, cy);
|
|
5530
|
+
anchorA.push(endA);
|
|
5531
|
+
anchorB.push(endB);
|
|
5532
|
+
anchorT.push(endT);
|
|
5533
|
+
const startAngle = Math.atan2(ny, nx);
|
|
5534
|
+
const sweep = atEnd ? -Math.PI : Math.PI;
|
|
5535
|
+
const steps = Math.max(1, Math.ceil(Math.abs(sweep) / ROUND_CAP_STEP_RAD));
|
|
5536
|
+
const stepAngle = sweep / steps;
|
|
5537
|
+
let prevIdx = leftIdx;
|
|
5538
|
+
for (let i = 1; i < steps; i++) {
|
|
5539
|
+
const ang = startAngle + i * stepAngle;
|
|
5540
|
+
const fx = cx + Math.cos(ang) * half;
|
|
5541
|
+
const fy = cy + Math.sin(ang) * half;
|
|
5542
|
+
const newIdx = verts.length / 2;
|
|
5543
|
+
verts.push(fx, fy);
|
|
5544
|
+
anchorA.push(endA);
|
|
5545
|
+
anchorB.push(endB);
|
|
5546
|
+
anchorT.push(endT);
|
|
5547
|
+
if (atEnd) idx.push(prevIdx, newIdx, pivotIdx);
|
|
5548
|
+
else idx.push(prevIdx, pivotIdx, newIdx);
|
|
5549
|
+
prevIdx = newIdx;
|
|
5550
|
+
}
|
|
5551
|
+
if (atEnd) idx.push(prevIdx, rightIdx, pivotIdx);
|
|
5552
|
+
else idx.push(prevIdx, pivotIdx, rightIdx);
|
|
5882
5553
|
}
|
|
5883
|
-
return out;
|
|
5884
5554
|
}
|
|
5885
|
-
function
|
|
5555
|
+
function splitForDash(pl, dash) {
|
|
5886
5556
|
const out = [];
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5557
|
+
const plA = pl.anchorA ?? new Uint32Array(pl.points.length / 2);
|
|
5558
|
+
const plB = pl.anchorB ?? new Uint32Array(pl.points.length / 2);
|
|
5559
|
+
const plT = pl.anchorT ?? new Float32Array(pl.points.length / 2);
|
|
5560
|
+
let dashIdx = 0;
|
|
5561
|
+
let dashRemaining = dash[0];
|
|
5562
|
+
let onPhase = true;
|
|
5563
|
+
let curPts = onPhase ? [pl.points[0], pl.points[1]] : null;
|
|
5564
|
+
let curA = onPhase ? [plA[0]] : null;
|
|
5565
|
+
let curB = onPhase ? [plB[0]] : null;
|
|
5566
|
+
let curT = onPhase ? [plT[0]] : null;
|
|
5567
|
+
const flushAndAdvance = () => {
|
|
5568
|
+
if (curPts && curPts.length >= 4) {
|
|
5569
|
+
out.push({
|
|
5570
|
+
points: curPts,
|
|
5571
|
+
closed: false,
|
|
5572
|
+
anchorA: new Uint32Array(curA),
|
|
5573
|
+
anchorB: new Uint32Array(curB),
|
|
5574
|
+
anchorT: new Float32Array(curT)
|
|
5575
|
+
});
|
|
5576
|
+
}
|
|
5577
|
+
curPts = null;
|
|
5578
|
+
curA = null;
|
|
5579
|
+
curB = null;
|
|
5580
|
+
curT = null;
|
|
5581
|
+
dashIdx = (dashIdx + 1) % dash.length;
|
|
5582
|
+
dashRemaining = dash[dashIdx];
|
|
5583
|
+
onPhase = !onPhase;
|
|
5584
|
+
};
|
|
5585
|
+
let prevX = pl.points[0], prevY = pl.points[1];
|
|
5586
|
+
const ptCount = pl.points.length / 2;
|
|
5587
|
+
const segCount = pl.closed ? ptCount : ptCount - 1;
|
|
5588
|
+
for (let i = 0; i < segCount; i++) {
|
|
5589
|
+
const nextIdx = (i + 1) % ptCount;
|
|
5590
|
+
const cx = pl.points[nextIdx * 2], cy = pl.points[nextIdx * 2 + 1];
|
|
5591
|
+
const startA = plA[i], startB = plB[i], startT = plT[i];
|
|
5592
|
+
const endA = plA[nextIdx], endB = plB[nextIdx], endT = plT[nextIdx];
|
|
5593
|
+
const segFullDx = cx - prevX, segFullDy = cy - prevY;
|
|
5594
|
+
const segFullLen = Math.hypot(segFullDx, segFullDy);
|
|
5595
|
+
let segDx = segFullDx, segDy = segFullDy;
|
|
5596
|
+
let segLen = segFullLen;
|
|
5597
|
+
let traveled = 0;
|
|
5598
|
+
while (segLen > 1e-9) {
|
|
5599
|
+
if (segLen <= dashRemaining) {
|
|
5600
|
+
if (onPhase && curPts) {
|
|
5601
|
+
curPts.push(cx, cy);
|
|
5602
|
+
curA.push(endA);
|
|
5603
|
+
curB.push(endB);
|
|
5604
|
+
curT.push(endT);
|
|
5605
|
+
}
|
|
5606
|
+
dashRemaining -= segLen;
|
|
5607
|
+
prevX = cx;
|
|
5608
|
+
prevY = cy;
|
|
5609
|
+
traveled = segFullLen;
|
|
5610
|
+
segLen = 0;
|
|
5611
|
+
if (dashRemaining <= 1e-9) {
|
|
5612
|
+
flushAndAdvance();
|
|
5613
|
+
if (onPhase) {
|
|
5614
|
+
curPts = [prevX, prevY];
|
|
5615
|
+
curA = [endA];
|
|
5616
|
+
curB = [endB];
|
|
5617
|
+
curT = [endT];
|
|
5618
|
+
}
|
|
5619
|
+
}
|
|
5620
|
+
} else {
|
|
5621
|
+
const tConsume = dashRemaining / segLen;
|
|
5622
|
+
const ix = prevX + segDx * tConsume;
|
|
5623
|
+
const iy = prevY + segDy * tConsume;
|
|
5624
|
+
traveled += dashRemaining;
|
|
5625
|
+
const frac = traveled / segFullLen;
|
|
5626
|
+
let mA, mB, mT;
|
|
5627
|
+
if (startA === endA && startB === endB) {
|
|
5628
|
+
mA = startA;
|
|
5629
|
+
mB = startB;
|
|
5630
|
+
mT = startT + (endT - startT) * frac;
|
|
5631
|
+
} else {
|
|
5632
|
+
if (frac < 0.5) {
|
|
5633
|
+
mA = startA;
|
|
5634
|
+
mB = startB;
|
|
5635
|
+
mT = startT;
|
|
5636
|
+
} else {
|
|
5637
|
+
mA = endA;
|
|
5638
|
+
mB = endB;
|
|
5639
|
+
mT = endT;
|
|
5640
|
+
}
|
|
5641
|
+
}
|
|
5642
|
+
if (onPhase && curPts) {
|
|
5643
|
+
curPts.push(ix, iy);
|
|
5644
|
+
curA.push(mA);
|
|
5645
|
+
curB.push(mB);
|
|
5646
|
+
curT.push(mT);
|
|
5647
|
+
}
|
|
5648
|
+
prevX = ix;
|
|
5649
|
+
prevY = iy;
|
|
5650
|
+
segDx = cx - prevX;
|
|
5651
|
+
segDy = cy - prevY;
|
|
5652
|
+
segLen = Math.hypot(segDx, segDy);
|
|
5653
|
+
flushAndAdvance();
|
|
5654
|
+
if (onPhase) {
|
|
5655
|
+
curPts = [prevX, prevY];
|
|
5656
|
+
curA = [mA];
|
|
5657
|
+
curB = [mB];
|
|
5658
|
+
curT = [mT];
|
|
5659
|
+
}
|
|
5660
|
+
}
|
|
5661
|
+
}
|
|
5662
|
+
}
|
|
5663
|
+
if (curPts && curPts.length >= 4) {
|
|
5664
|
+
out.push({
|
|
5665
|
+
points: curPts,
|
|
5666
|
+
closed: false,
|
|
5667
|
+
anchorA: new Uint32Array(curA),
|
|
5668
|
+
anchorB: new Uint32Array(curB),
|
|
5669
|
+
anchorT: new Float32Array(curT)
|
|
5670
|
+
});
|
|
5892
5671
|
}
|
|
5893
5672
|
return out;
|
|
5894
5673
|
}
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
const b = new PathBuilder();
|
|
5910
|
-
const st = {
|
|
5911
|
-
x: 0,
|
|
5912
|
-
y: 0,
|
|
5913
|
-
startX: 0,
|
|
5914
|
-
startY: 0,
|
|
5915
|
-
lastCubicCtrl: null,
|
|
5916
|
-
lastQuadCtrl: null
|
|
5674
|
+
function makeSeg(ax, ay, bx, by, halfA, halfB) {
|
|
5675
|
+
const dx = bx - ax, dy = by - ay;
|
|
5676
|
+
const len = Math.hypot(dx, dy);
|
|
5677
|
+
if (len === 0) return null;
|
|
5678
|
+
return {
|
|
5679
|
+
ax,
|
|
5680
|
+
ay,
|
|
5681
|
+
bx,
|
|
5682
|
+
by,
|
|
5683
|
+
nxUnit: -dy / len,
|
|
5684
|
+
nyUnit: dx / len,
|
|
5685
|
+
halfA,
|
|
5686
|
+
halfB,
|
|
5687
|
+
len
|
|
5917
5688
|
};
|
|
5918
|
-
for (const { cmd, args } of tokens) {
|
|
5919
|
-
const upper = cmd.toUpperCase();
|
|
5920
|
-
const relative = cmd !== upper;
|
|
5921
|
-
const argCount = ARG_COUNTS[upper];
|
|
5922
|
-
if (argCount === void 0) {
|
|
5923
|
-
continue;
|
|
5924
|
-
}
|
|
5925
|
-
if (upper === "Z") {
|
|
5926
|
-
b.close();
|
|
5927
|
-
st.x = st.startX;
|
|
5928
|
-
st.y = st.startY;
|
|
5929
|
-
st.lastCubicCtrl = null;
|
|
5930
|
-
st.lastQuadCtrl = null;
|
|
5931
|
-
continue;
|
|
5932
|
-
}
|
|
5933
|
-
const groups = argCount === 0 ? 1 : Math.floor(args.length / argCount);
|
|
5934
|
-
for (let g = 0; g < groups; g++) {
|
|
5935
|
-
const a = args.slice(g * argCount, (g + 1) * argCount);
|
|
5936
|
-
const effectiveUpper = upper === "M" && g > 0 ? "L" : upper;
|
|
5937
|
-
runCommand(effectiveUpper, relative, a, st, b);
|
|
5938
|
-
}
|
|
5939
|
-
}
|
|
5940
|
-
return b.build();
|
|
5941
5689
|
}
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
}
|
|
5976
|
-
case "V": {
|
|
5977
|
-
const y = a[0] + (relative ? st.y : 0);
|
|
5978
|
-
b.lineTo(st.x, y);
|
|
5979
|
-
st.y = y;
|
|
5980
|
-
st.lastCubicCtrl = null;
|
|
5981
|
-
st.lastQuadCtrl = null;
|
|
5982
|
-
return;
|
|
5983
|
-
}
|
|
5984
|
-
case "C": {
|
|
5985
|
-
const x1 = a[0] + rx, y1 = a[1] + ry;
|
|
5986
|
-
const x2 = a[2] + rx, y2 = a[3] + ry;
|
|
5987
|
-
const x = a[4] + rx, y = a[5] + ry;
|
|
5988
|
-
b.curveTo(x1, y1, x2, y2, x, y);
|
|
5989
|
-
st.x = x;
|
|
5990
|
-
st.y = y;
|
|
5991
|
-
st.lastCubicCtrl = { x: x2, y: y2 };
|
|
5992
|
-
st.lastQuadCtrl = null;
|
|
5993
|
-
return;
|
|
5994
|
-
}
|
|
5995
|
-
case "S": {
|
|
5996
|
-
const ref = st.lastCubicCtrl ?? { x: st.x, y: st.y };
|
|
5997
|
-
const x1 = 2 * st.x - ref.x;
|
|
5998
|
-
const y1 = 2 * st.y - ref.y;
|
|
5999
|
-
const x2 = a[0] + rx, y2 = a[1] + ry;
|
|
6000
|
-
const x = a[2] + rx, y = a[3] + ry;
|
|
6001
|
-
b.curveTo(x1, y1, x2, y2, x, y);
|
|
6002
|
-
st.x = x;
|
|
6003
|
-
st.y = y;
|
|
6004
|
-
st.lastCubicCtrl = { x: x2, y: y2 };
|
|
6005
|
-
st.lastQuadCtrl = null;
|
|
6006
|
-
return;
|
|
6007
|
-
}
|
|
6008
|
-
case "Q": {
|
|
6009
|
-
const x1 = a[0] + rx, y1 = a[1] + ry;
|
|
6010
|
-
const x = a[2] + rx, y = a[3] + ry;
|
|
6011
|
-
b.quadTo(x1, y1, x, y);
|
|
6012
|
-
st.x = x;
|
|
6013
|
-
st.y = y;
|
|
6014
|
-
st.lastQuadCtrl = { x: x1, y: y1 };
|
|
6015
|
-
st.lastCubicCtrl = null;
|
|
6016
|
-
return;
|
|
6017
|
-
}
|
|
6018
|
-
case "T": {
|
|
6019
|
-
const ref = st.lastQuadCtrl ?? { x: st.x, y: st.y };
|
|
6020
|
-
const x1 = 2 * st.x - ref.x;
|
|
6021
|
-
const y1 = 2 * st.y - ref.y;
|
|
6022
|
-
const x = a[0] + rx, y = a[1] + ry;
|
|
6023
|
-
b.quadTo(x1, y1, x, y);
|
|
6024
|
-
st.x = x;
|
|
6025
|
-
st.y = y;
|
|
6026
|
-
st.lastQuadCtrl = { x: x1, y: y1 };
|
|
6027
|
-
st.lastCubicCtrl = null;
|
|
5690
|
+
var DEFAULT_MITER_LIMIT = 10;
|
|
5691
|
+
var DEFAULT_VARYING_WIDTH_JOIN_THRESHOLD = 1.5;
|
|
5692
|
+
function emitJoin(segs, segBaseIdx, j, join, miterLimit, varyingThreshold, verts, idx, anchorA, anchorB, anchorT, segSrcIdx, plA, plB, plT) {
|
|
5693
|
+
const a = segs[j];
|
|
5694
|
+
const b = segs[(j + 1) % segs.length];
|
|
5695
|
+
const aBase = segBaseIdx[j];
|
|
5696
|
+
const bBase = segBaseIdx[(j + 1) % segs.length];
|
|
5697
|
+
const adx = a.bx - a.ax, ady = a.by - a.ay;
|
|
5698
|
+
const bdx = b.bx - b.ax, bdy = b.by - b.ay;
|
|
5699
|
+
const cross = adx * bdy - ady * bdx;
|
|
5700
|
+
if (cross === 0) return;
|
|
5701
|
+
const onPositive = cross > 0;
|
|
5702
|
+
const aOuterEnd = onPositive ? aBase + 3 : aBase + 2;
|
|
5703
|
+
const bOuterStart = onPositive ? bBase + 1 : bBase + 0;
|
|
5704
|
+
const nextSeg = (j + 1) % segs.length;
|
|
5705
|
+
const cornerSrc = segSrcIdx[nextSeg];
|
|
5706
|
+
const cornerA = plA[cornerSrc];
|
|
5707
|
+
const cornerB = plB[cornerSrc];
|
|
5708
|
+
const cornerT = plT[cornerSrc];
|
|
5709
|
+
const halfAtCorner = a.halfB;
|
|
5710
|
+
const taperA = Math.max(a.halfA, a.halfB) / Math.max(1e-9, Math.min(a.halfA, a.halfB));
|
|
5711
|
+
const taperB = Math.max(b.halfA, b.halfB) / Math.max(1e-9, Math.min(b.halfA, b.halfB));
|
|
5712
|
+
const forceBevelForVarying = taperA > varyingThreshold || taperB > varyingThreshold;
|
|
5713
|
+
if (join === "bevel" || forceBevelForVarying) {
|
|
5714
|
+
emitBevel(a, aOuterEnd, bOuterStart, onPositive, verts, idx, anchorA, anchorB, anchorT, cornerA, cornerB, cornerT);
|
|
5715
|
+
return;
|
|
5716
|
+
}
|
|
5717
|
+
if (join === "miter") {
|
|
5718
|
+
const aOX = verts[aOuterEnd * 2], aOY = verts[aOuterEnd * 2 + 1];
|
|
5719
|
+
const bOX = verts[bOuterStart * 2], bOY = verts[bOuterStart * 2 + 1];
|
|
5720
|
+
const apex = lineLineIntersect(aOX, aOY, adx, ady, bOX, bOY, -bdx, -bdy);
|
|
5721
|
+
if (!apex) {
|
|
5722
|
+
emitBevel(a, aOuterEnd, bOuterStart, onPositive, verts, idx, anchorA, anchorB, anchorT, cornerA, cornerB, cornerT);
|
|
6028
5723
|
return;
|
|
6029
5724
|
}
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
const xRot = a[2];
|
|
6034
|
-
const largeArc = a[3] !== 0;
|
|
6035
|
-
const sweep = a[4] !== 0;
|
|
6036
|
-
const ex = a[5] + rx;
|
|
6037
|
-
const ey = a[6] + ry;
|
|
6038
|
-
arcToCubics(st.x, st.y, ex, ey, rxA, ryA, xRot, largeArc, sweep, b);
|
|
6039
|
-
st.x = ex;
|
|
6040
|
-
st.y = ey;
|
|
6041
|
-
st.lastCubicCtrl = null;
|
|
6042
|
-
st.lastQuadCtrl = null;
|
|
5725
|
+
const miterLen = Math.hypot(apex[0] - a.bx, apex[1] - a.by);
|
|
5726
|
+
if (miterLen > miterLimit * halfAtCorner) {
|
|
5727
|
+
emitBevel(a, aOuterEnd, bOuterStart, onPositive, verts, idx, anchorA, anchorB, anchorT, cornerA, cornerB, cornerT);
|
|
6043
5728
|
return;
|
|
6044
5729
|
}
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
let rys = ry * ry;
|
|
6064
|
-
const x1ps = x1p * x1p;
|
|
6065
|
-
const y1ps = y1p * y1p;
|
|
6066
|
-
const lambda = x1ps / rxs + y1ps / rys;
|
|
6067
|
-
let rxFixed = rx;
|
|
6068
|
-
let ryFixed = ry;
|
|
6069
|
-
if (lambda > 1) {
|
|
6070
|
-
const sqrtLambda = Math.sqrt(lambda);
|
|
6071
|
-
rxFixed = sqrtLambda * rx;
|
|
6072
|
-
ryFixed = sqrtLambda * ry;
|
|
6073
|
-
rxs = rxFixed * rxFixed;
|
|
6074
|
-
rys = ryFixed * ryFixed;
|
|
5730
|
+
const apexIdx = verts.length / 2;
|
|
5731
|
+
verts.push(apex[0], apex[1]);
|
|
5732
|
+
anchorA.push(cornerA);
|
|
5733
|
+
anchorB.push(cornerB);
|
|
5734
|
+
anchorT.push(cornerT);
|
|
5735
|
+
const jIdx = verts.length / 2;
|
|
5736
|
+
verts.push(a.bx, a.by);
|
|
5737
|
+
anchorA.push(cornerA);
|
|
5738
|
+
anchorB.push(cornerB);
|
|
5739
|
+
anchorT.push(cornerT);
|
|
5740
|
+
if (onPositive) {
|
|
5741
|
+
idx.push(aOuterEnd, apexIdx, bOuterStart);
|
|
5742
|
+
idx.push(aOuterEnd, bOuterStart, jIdx);
|
|
5743
|
+
} else {
|
|
5744
|
+
idx.push(aOuterEnd, bOuterStart, apexIdx);
|
|
5745
|
+
idx.push(aOuterEnd, jIdx, bOuterStart);
|
|
5746
|
+
}
|
|
5747
|
+
return;
|
|
6075
5748
|
}
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
const factor = sign2 * Math.sqrt(Math.max(0, num / den));
|
|
6080
|
-
const cxp = factor * (rxFixed * y1p) / ryFixed;
|
|
6081
|
-
const cyp = factor * -(ryFixed * x1p) / rxFixed;
|
|
6082
|
-
const cx = cosPhi * cxp - sinPhi * cyp + (x1 + x2) / 2;
|
|
6083
|
-
const cy = sinPhi * cxp + cosPhi * cyp + (y1 + y2) / 2;
|
|
6084
|
-
const angle = (ux, uy, vx, vy) => {
|
|
6085
|
-
const dot = ux * vx + uy * vy;
|
|
6086
|
-
const len = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
|
|
6087
|
-
let a = Math.acos(Math.max(-1, Math.min(1, dot / len)));
|
|
6088
|
-
if (ux * vy - uy * vx < 0) a = -a;
|
|
6089
|
-
return a;
|
|
6090
|
-
};
|
|
6091
|
-
const theta1 = angle(1, 0, (x1p - cxp) / rxFixed, (y1p - cyp) / ryFixed);
|
|
6092
|
-
let dTheta = angle(
|
|
6093
|
-
(x1p - cxp) / rxFixed,
|
|
6094
|
-
(y1p - cyp) / ryFixed,
|
|
6095
|
-
(-x1p - cxp) / rxFixed,
|
|
6096
|
-
(-y1p - cyp) / ryFixed
|
|
6097
|
-
);
|
|
6098
|
-
if (!sweep && dTheta > 0) dTheta -= 2 * Math.PI;
|
|
6099
|
-
else if (sweep && dTheta < 0) dTheta += 2 * Math.PI;
|
|
6100
|
-
const segCount = Math.max(1, Math.ceil(Math.abs(dTheta) / (Math.PI / 2)));
|
|
6101
|
-
const segAngle = dTheta / segCount;
|
|
6102
|
-
const t = 4 / 3 * Math.tan(segAngle / 4);
|
|
6103
|
-
let prevX = x1;
|
|
6104
|
-
let prevY = y1;
|
|
6105
|
-
let prevDx = -Math.sin(theta1) * rxFixed;
|
|
6106
|
-
let prevDy = Math.cos(theta1) * ryFixed;
|
|
6107
|
-
for (let i = 1; i <= segCount; i++) {
|
|
6108
|
-
const theta = theta1 + i * segAngle;
|
|
6109
|
-
const cosT = Math.cos(theta);
|
|
6110
|
-
const sinT = Math.sin(theta);
|
|
6111
|
-
const px = cosPhi * (cosT * rxFixed) - sinPhi * (sinT * ryFixed) + cx;
|
|
6112
|
-
const py = sinPhi * (cosT * rxFixed) + cosPhi * (sinT * ryFixed) + cy;
|
|
6113
|
-
const dxNew = -sinT * rxFixed;
|
|
6114
|
-
const dyNew = cosT * ryFixed;
|
|
6115
|
-
const c1x = prevX + cosPhi * (t * prevDx) - sinPhi * (t * prevDy);
|
|
6116
|
-
const c1y = prevY + sinPhi * (t * prevDx) + cosPhi * (t * prevDy);
|
|
6117
|
-
const c2x = px - cosPhi * (t * dxNew) + sinPhi * (t * dyNew);
|
|
6118
|
-
const c2y = py - sinPhi * (t * dxNew) - cosPhi * (t * dyNew);
|
|
6119
|
-
b.curveTo(c1x, c1y, c2x, c2y, px, py);
|
|
6120
|
-
prevX = px;
|
|
6121
|
-
prevY = py;
|
|
6122
|
-
prevDx = dxNew;
|
|
6123
|
-
prevDy = dyNew;
|
|
5749
|
+
if (join === "round") {
|
|
5750
|
+
emitRoundJoin(a, aOuterEnd, bOuterStart, onPositive, verts, idx, anchorA, anchorB, anchorT, cornerA, cornerB, cornerT);
|
|
5751
|
+
return;
|
|
6124
5752
|
}
|
|
6125
5753
|
}
|
|
6126
|
-
var
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
5754
|
+
var ROUND_STEP_RAD = 10 * Math.PI / 180;
|
|
5755
|
+
function emitRoundJoin(a, aOuterEnd, bOuterStart, onPositive, verts, idx, anchorA, anchorB, anchorT, cornerA, cornerB, cornerT) {
|
|
5756
|
+
const cx = a.bx, cy = a.by;
|
|
5757
|
+
const pivotIdx = verts.length / 2;
|
|
5758
|
+
verts.push(cx, cy);
|
|
5759
|
+
anchorA.push(cornerA);
|
|
5760
|
+
anchorB.push(cornerB);
|
|
5761
|
+
anchorT.push(cornerT);
|
|
5762
|
+
const startX = verts[aOuterEnd * 2] - cx;
|
|
5763
|
+
const startY = verts[aOuterEnd * 2 + 1] - cy;
|
|
5764
|
+
const endX = verts[bOuterStart * 2] - cx;
|
|
5765
|
+
const endY = verts[bOuterStart * 2 + 1] - cy;
|
|
5766
|
+
const r = Math.hypot(startX, startY);
|
|
5767
|
+
const startAngle = Math.atan2(startY, startX);
|
|
5768
|
+
const endAngle = Math.atan2(endY, endX);
|
|
5769
|
+
let sweep = endAngle - startAngle;
|
|
5770
|
+
if (onPositive && sweep < 0) sweep += 2 * Math.PI;
|
|
5771
|
+
else if (!onPositive && sweep > 0) sweep -= 2 * Math.PI;
|
|
5772
|
+
const steps = Math.max(1, Math.ceil(Math.abs(sweep) / ROUND_STEP_RAD));
|
|
5773
|
+
const stepAngle = sweep / steps;
|
|
5774
|
+
let prevIdx = aOuterEnd;
|
|
5775
|
+
for (let i = 1; i < steps; i++) {
|
|
5776
|
+
const ang = startAngle + i * stepAngle;
|
|
5777
|
+
const fx = cx + Math.cos(ang) * r;
|
|
5778
|
+
const fy = cy + Math.sin(ang) * r;
|
|
5779
|
+
const newIdx = verts.length / 2;
|
|
5780
|
+
verts.push(fx, fy);
|
|
5781
|
+
anchorA.push(cornerA);
|
|
5782
|
+
anchorB.push(cornerB);
|
|
5783
|
+
anchorT.push(cornerT);
|
|
5784
|
+
if (onPositive) idx.push(prevIdx, pivotIdx, newIdx);
|
|
5785
|
+
else idx.push(prevIdx, newIdx, pivotIdx);
|
|
5786
|
+
prevIdx = newIdx;
|
|
5787
|
+
}
|
|
5788
|
+
if (onPositive) idx.push(prevIdx, pivotIdx, bOuterStart);
|
|
5789
|
+
else idx.push(prevIdx, bOuterStart, pivotIdx);
|
|
5790
|
+
}
|
|
5791
|
+
function emitBevel(a, aOuterEnd, bOuterStart, onPositive, verts, idx, anchorA, anchorB, anchorT, cornerA, cornerB, cornerT) {
|
|
5792
|
+
const jIdx = verts.length / 2;
|
|
5793
|
+
verts.push(a.bx, a.by);
|
|
5794
|
+
anchorA.push(cornerA);
|
|
5795
|
+
anchorB.push(cornerB);
|
|
5796
|
+
anchorT.push(cornerT);
|
|
5797
|
+
if (onPositive) idx.push(aOuterEnd, jIdx, bOuterStart);
|
|
5798
|
+
else idx.push(aOuterEnd, bOuterStart, jIdx);
|
|
5799
|
+
}
|
|
5800
|
+
function lineLineIntersect(apx, apy, adx, ady, bpx, bpy, bdx, bdy) {
|
|
5801
|
+
const denom = adx * bdy - ady * bdx;
|
|
5802
|
+
if (Math.abs(denom) < 1e-9) return null;
|
|
5803
|
+
const t = ((bpx - apx) * bdy - (bpy - apy) * bdx) / denom;
|
|
5804
|
+
return [apx + t * adx, apy + t * ady];
|
|
6136
5805
|
}
|
|
6137
5806
|
var OUTLINE_STROKE_MESH_CACHE_LIMIT = 2048;
|
|
6138
5807
|
var WIDTH_QUANTUM = 1 / 1024;
|
|
@@ -6162,6 +5831,36 @@ function outlineStrokeMesh(glyphKey, d, emWidth, stroke) {
|
|
|
6162
5831
|
cache4.set(key, mesh);
|
|
6163
5832
|
return mesh.indices.length === 0 ? null : mesh;
|
|
6164
5833
|
}
|
|
5834
|
+
var STROKE_CONFIGS_PER_PATH = 8;
|
|
5835
|
+
var cache5 = /* @__PURE__ */ new WeakMap();
|
|
5836
|
+
function configKey(stroke, flattenTolerance) {
|
|
5837
|
+
return [
|
|
5838
|
+
stroke.width ?? 1,
|
|
5839
|
+
stroke.cap ?? "butt",
|
|
5840
|
+
stroke.join ?? "miter",
|
|
5841
|
+
stroke.miterLimit ?? "",
|
|
5842
|
+
stroke.align ?? "center",
|
|
5843
|
+
(stroke.dash ?? []).join(","),
|
|
5844
|
+
stroke.varyingWidthJoinThreshold ?? "",
|
|
5845
|
+
flattenTolerance ?? ""
|
|
5846
|
+
].join("|");
|
|
5847
|
+
}
|
|
5848
|
+
function strokeMesh(path, stroke, flattenTolerance) {
|
|
5849
|
+
let byConfig = cache5.get(path);
|
|
5850
|
+
if (byConfig === void 0) {
|
|
5851
|
+
byConfig = /* @__PURE__ */ new Map();
|
|
5852
|
+
cache5.set(path, byConfig);
|
|
5853
|
+
}
|
|
5854
|
+
const key = configKey(stroke, flattenTolerance);
|
|
5855
|
+
const entry = byConfig.get(key);
|
|
5856
|
+
if (entry !== void 0 && entry.vertexWidths === stroke.vertexWidths) {
|
|
5857
|
+
return entry.mesh;
|
|
5858
|
+
}
|
|
5859
|
+
const mesh = tessellateStroke(path, stroke, { flattenTolerance });
|
|
5860
|
+
if (entry === void 0 && byConfig.size >= STROKE_CONFIGS_PER_PATH) byConfig.clear();
|
|
5861
|
+
byConfig.set(key, { mesh, vertexWidths: stroke.vertexWidths });
|
|
5862
|
+
return mesh;
|
|
5863
|
+
}
|
|
6165
5864
|
var MAX_VERTICES_PER_BATCH = 32768;
|
|
6166
5865
|
var FLOATS_PER_VERTEX = 6;
|
|
6167
5866
|
var INITIAL_VERTICES = 256;
|
|
@@ -6563,44 +6262,40 @@ function drawGroup(ctx, cmd) {
|
|
|
6563
6262
|
"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."
|
|
6564
6263
|
);
|
|
6565
6264
|
}
|
|
6566
|
-
flushSolids(ctx);
|
|
6567
6265
|
pushClip(ctx, cmd.clip, newDepth);
|
|
6568
6266
|
ctx.clipDepth = newDepth;
|
|
6569
6267
|
}
|
|
6570
6268
|
for (const child of cmd.children) dispatch(ctx, child);
|
|
6571
6269
|
if (cmd.clip) {
|
|
6572
|
-
flushSolids(ctx);
|
|
6573
6270
|
popClip(ctx, cmd.clip, ctx.clipDepth - 1);
|
|
6574
6271
|
ctx.clipDepth -= 1;
|
|
6575
6272
|
}
|
|
6576
6273
|
ctx.state.pop();
|
|
6577
6274
|
}
|
|
6578
6275
|
function drawPath(ctx, cmd) {
|
|
6579
|
-
if (
|
|
6580
|
-
if (cmd.fill) {
|
|
6581
|
-
const batchablePaint = (cmd.fill.fill === void 0 || cmd.fill.fill === "solid") && (!cmd.vertexColors || cmd.vertexColors.length === 0);
|
|
6582
|
-
const solid = cmd.fill;
|
|
6583
|
-
if (batchablePaint && cmd.path.kind === "rect") {
|
|
6584
|
-
pushRect(ctx, cmd.path, solid);
|
|
6585
|
-
} else {
|
|
6586
|
-
const mesh = fillMesh(ctx, cmd.path);
|
|
6587
|
-
if (batchablePaint && canBatchMesh(mesh)) {
|
|
6588
|
-
pushMesh(ctx, mesh, solid);
|
|
6589
|
-
} else {
|
|
6590
|
-
flushSolids(ctx);
|
|
6591
|
-
const handle = meshHandle(ctx, mesh);
|
|
6592
|
-
if (cmd.vertexColors && cmd.vertexColors.length > 0 && (cmd.fill.fill === void 0 || cmd.fill.fill === "solid")) {
|
|
6593
|
-
drawPathFillVColor(ctx, cmd, solid, handle);
|
|
6594
|
-
} else if (handle.requiresStencil) {
|
|
6595
|
-
drawPathFillStencil(ctx, cmd.fill, handle);
|
|
6596
|
-
} else {
|
|
6597
|
-
drawPathFillByKind(ctx, cmd.fill, handle);
|
|
6598
|
-
}
|
|
6599
|
-
}
|
|
6600
|
-
}
|
|
6601
|
-
}
|
|
6276
|
+
if (cmd.fill) drawPathFill(ctx, cmd);
|
|
6602
6277
|
if (cmd.stroke) drawPathStroke(ctx, cmd);
|
|
6603
6278
|
}
|
|
6279
|
+
function drawPathFill(ctx, cmd) {
|
|
6280
|
+
const fill = cmd.fill;
|
|
6281
|
+
const isSolid = fill.fill === void 0 || fill.fill === "solid";
|
|
6282
|
+
const hasVColors = !!(cmd.vertexColors && cmd.vertexColors.length > 0);
|
|
6283
|
+
const solid = fill;
|
|
6284
|
+
if (isSolid && !hasVColors && cmd.path.kind === "rect") {
|
|
6285
|
+
pushRect(ctx, cmd.path, solid);
|
|
6286
|
+
return;
|
|
6287
|
+
}
|
|
6288
|
+
const mesh = fillMesh(ctx, cmd.path);
|
|
6289
|
+
if (tryStageSolid(ctx, mesh, isSolid && !hasVColors ? solid : void 0)) return;
|
|
6290
|
+
const handle = meshHandle(ctx, mesh);
|
|
6291
|
+
if (isSolid && hasVColors) {
|
|
6292
|
+
drawPathFillVColor(ctx, cmd, solid, handle);
|
|
6293
|
+
} else if (handle.requiresStencil) {
|
|
6294
|
+
drawPathFillStencil(ctx, fill, handle);
|
|
6295
|
+
} else {
|
|
6296
|
+
drawPathFillByKind(ctx, fill, handle);
|
|
6297
|
+
}
|
|
6298
|
+
}
|
|
6604
6299
|
function isIdentityColorMatrix(cm) {
|
|
6605
6300
|
return cm === IDENTITY_COLOR_MATRIX || sameValues(IDENTITY_COLOR_MATRIX, cm);
|
|
6606
6301
|
}
|
|
@@ -6653,6 +6348,14 @@ function pushMesh(ctx, mesh, paint) {
|
|
|
6653
6348
|
const [r, g, b, a] = stagedColor(ctx, staged, paint);
|
|
6654
6349
|
ctx.solidBatch.pushMesh(mesh, ctx.state.transform, r, g, b, a);
|
|
6655
6350
|
}
|
|
6351
|
+
function tryStageSolid(ctx, mesh, paint) {
|
|
6352
|
+
if (paint !== void 0 && canBatchMesh(mesh)) {
|
|
6353
|
+
pushMesh(ctx, mesh, paint);
|
|
6354
|
+
return true;
|
|
6355
|
+
}
|
|
6356
|
+
flushSolids(ctx);
|
|
6357
|
+
return false;
|
|
6358
|
+
}
|
|
6656
6359
|
function flushSolids(ctx) {
|
|
6657
6360
|
const batch = ctx.solidBatch;
|
|
6658
6361
|
const staged = ctx.solidState;
|
|
@@ -6856,6 +6559,7 @@ function rasterizePathToStencil(ctx, path) {
|
|
|
6856
6559
|
gl.bindVertexArray(null);
|
|
6857
6560
|
}
|
|
6858
6561
|
function pushClip(ctx, path, newDepth) {
|
|
6562
|
+
flushSolids(ctx);
|
|
6859
6563
|
const gl = ctx.gl;
|
|
6860
6564
|
const ancestors = ancestorMask(newDepth - 1);
|
|
6861
6565
|
const newBit = 1 << newDepth;
|
|
@@ -6870,6 +6574,7 @@ function pushClip(ctx, path, newDepth) {
|
|
|
6870
6574
|
gl.stencilMask(255);
|
|
6871
6575
|
}
|
|
6872
6576
|
function popClip(ctx, path, oldDepth) {
|
|
6577
|
+
flushSolids(ctx);
|
|
6873
6578
|
const gl = ctx.gl;
|
|
6874
6579
|
gl.enable(gl.STENCIL_TEST);
|
|
6875
6580
|
const oldBit = 1 << oldDepth + 1;
|
|
@@ -6926,17 +6631,13 @@ function drawPathStroke(ctx, cmd) {
|
|
|
6926
6631
|
function drawPathStrokeUnclipped(ctx, cmd) {
|
|
6927
6632
|
const stroke = cmd.stroke;
|
|
6928
6633
|
const solid = stroke.paint;
|
|
6929
|
-
const mesh =
|
|
6634
|
+
const mesh = strokeMesh(cmd.path, stroke, ctx.flattenTolerance);
|
|
6930
6635
|
if (mesh.indices.length === 0) return;
|
|
6931
6636
|
const hasVColors = !!(stroke.vertexColors && stroke.vertexColors.length > 0);
|
|
6932
|
-
if (
|
|
6933
|
-
|
|
6934
|
-
return;
|
|
6935
|
-
}
|
|
6936
|
-
flushSolids(ctx);
|
|
6937
|
-
const handle = ctx.meshCache.uploadTransient(mesh);
|
|
6637
|
+
if (tryStageSolid(ctx, mesh, hasVColors ? void 0 : solid)) return;
|
|
6638
|
+
const handle = hasVColors ? ctx.meshCache.uploadTransient(mesh) : ctx.meshCache.uploadRecurring(mesh);
|
|
6938
6639
|
const gl = ctx.gl;
|
|
6939
|
-
if (
|
|
6640
|
+
if (hasVColors) {
|
|
6940
6641
|
const prog = ctx.pathFillVColor;
|
|
6941
6642
|
gl.useProgram(prog.handle);
|
|
6942
6643
|
gl.bindVertexArray(handle.vao);
|
|
@@ -6967,16 +6668,30 @@ function drawPathStrokeUnclipped(ctx, cmd) {
|
|
|
6967
6668
|
gl.drawElements(gl.TRIANGLES, handle.indexCount, gl.UNSIGNED_INT, 0);
|
|
6968
6669
|
gl.bindVertexArray(null);
|
|
6969
6670
|
}
|
|
6671
|
+
var DOUBLED_VERTEX_WIDTHS = /* @__PURE__ */ new WeakMap();
|
|
6672
|
+
function doubledVertexWidths(widths) {
|
|
6673
|
+
let doubled = DOUBLED_VERTEX_WIDTHS.get(widths);
|
|
6674
|
+
if (doubled === void 0) {
|
|
6675
|
+
doubled = widths.map((w) => w * 2);
|
|
6676
|
+
DOUBLED_VERTEX_WIDTHS.set(widths, doubled);
|
|
6677
|
+
}
|
|
6678
|
+
return doubled;
|
|
6679
|
+
}
|
|
6970
6680
|
function drawPathStrokeStenciled(ctx, cmd, align) {
|
|
6971
6681
|
const stroke = cmd.stroke;
|
|
6972
6682
|
const solid = stroke.paint;
|
|
6973
|
-
const widerStroke = {
|
|
6683
|
+
const widerStroke = {
|
|
6684
|
+
...stroke,
|
|
6685
|
+
width: (stroke.width ?? 1) * 2,
|
|
6686
|
+
...stroke.vertexWidths ? { vertexWidths: doubledVertexWidths(stroke.vertexWidths) } : {},
|
|
6687
|
+
align: "center"
|
|
6688
|
+
};
|
|
6689
|
+
const useVColor = !!(stroke.vertexColors && stroke.vertexColors.length > 0);
|
|
6974
6690
|
const fillHandle = fillMeshHandle(ctx, cmd.path);
|
|
6975
|
-
const ribbonMesh =
|
|
6691
|
+
const ribbonMesh = strokeMesh(cmd.path, widerStroke, ctx.flattenTolerance);
|
|
6976
6692
|
if (ribbonMesh.indices.length === 0) return;
|
|
6977
|
-
const ribbonHandle = ctx.meshCache.uploadTransient(ribbonMesh);
|
|
6693
|
+
const ribbonHandle = useVColor ? ctx.meshCache.uploadTransient(ribbonMesh) : ctx.meshCache.uploadRecurring(ribbonMesh);
|
|
6978
6694
|
const gl = ctx.gl;
|
|
6979
|
-
const useVColor = !!(stroke.vertexColors && stroke.vertexColors.length > 0);
|
|
6980
6695
|
const prog = useVColor ? ctx.pathFillVColor : ctx.pathFill;
|
|
6981
6696
|
gl.useProgram(prog.handle);
|
|
6982
6697
|
setProjAndModel(ctx, prog);
|
|
@@ -7012,11 +6727,11 @@ function drawPathStrokeStenciled(ctx, cmd, align) {
|
|
|
7012
6727
|
}
|
|
7013
6728
|
}
|
|
7014
6729
|
gl.drawElements(gl.TRIANGLES, ribbonHandle.indexCount, gl.UNSIGNED_INT, 0);
|
|
7015
|
-
if (colorVbo) gl.deleteBuffer(colorVbo);
|
|
7016
6730
|
gl.stencilMask(1);
|
|
7017
6731
|
gl.clear(gl.STENCIL_BUFFER_BIT);
|
|
7018
6732
|
gl.disable(gl.STENCIL_TEST);
|
|
7019
6733
|
gl.bindVertexArray(null);
|
|
6734
|
+
if (colorVbo) gl.deleteBuffer(colorVbo);
|
|
7020
6735
|
}
|
|
7021
6736
|
var OUTLINE_MIN_SCREEN_PX = 48;
|
|
7022
6737
|
function modelScale(m) {
|
|
@@ -7065,9 +6780,9 @@ function drawTextOutlineGroup(ctx, group, dx, dy) {
|
|
|
7065
6780
|
const mesh = outlineGroupMesh(group, dx, dy);
|
|
7066
6781
|
if (mesh) drawPathFillByKind(ctx, group.fill, ctx.meshCache.uploadTransient(mesh));
|
|
7067
6782
|
if (!group.stroke) return;
|
|
7068
|
-
const
|
|
7069
|
-
if (
|
|
7070
|
-
drawPathFillByKind(ctx, group.stroke.paint, ctx.meshCache.uploadTransient(
|
|
6783
|
+
const ribbon = outlineGroupStrokeMesh(group, dx, dy);
|
|
6784
|
+
if (ribbon) {
|
|
6785
|
+
drawPathFillByKind(ctx, group.stroke.paint, ctx.meshCache.uploadTransient(ribbon));
|
|
7071
6786
|
}
|
|
7072
6787
|
}
|
|
7073
6788
|
function outlineGroupMesh(group, dx, dy) {
|
|
@@ -7393,16 +7108,28 @@ var registry3 = /* @__PURE__ */ new Map();
|
|
|
7393
7108
|
function getProgramSource(id) {
|
|
7394
7109
|
return registry3.get(id) ?? null;
|
|
7395
7110
|
}
|
|
7111
|
+
var UNIFORM_QUALIFIER = /^(?:lowp|mediump|highp|precise|invariant|centroid|flat|smooth|noperspective)$/;
|
|
7396
7112
|
function extractUniformNames(glsl) {
|
|
7397
|
-
const
|
|
7398
|
-
const reArray = /\buniform\s+\S+\s+(\w+)\s*\[\s*(\d+)\s*\]\s*;/g;
|
|
7113
|
+
const re = /\buniform\s+([^;{]+);/g;
|
|
7399
7114
|
const names = [];
|
|
7400
7115
|
let m;
|
|
7401
|
-
while ((m =
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7116
|
+
while ((m = re.exec(glsl)) !== null) {
|
|
7117
|
+
const words = m[1].trim().split(/\s+/);
|
|
7118
|
+
let i = 0;
|
|
7119
|
+
while (i < words.length && UNIFORM_QUALIFIER.test(words[i])) i++;
|
|
7120
|
+
i++;
|
|
7121
|
+
const declarators = words.slice(i).join(" ");
|
|
7122
|
+
if (!declarators) continue;
|
|
7123
|
+
for (const raw of declarators.split(",")) {
|
|
7124
|
+
const d = raw.trim();
|
|
7125
|
+
const arr = /^(\w+)\s*\[\s*(\d+)\s*\]$/.exec(d);
|
|
7126
|
+
if (arr) {
|
|
7127
|
+
const size = parseInt(arr[2], 10);
|
|
7128
|
+
for (let k = 0; k < size; k++) names.push(`${arr[1]}[${k}]`);
|
|
7129
|
+
} else if (/^\w+$/.test(d)) {
|
|
7130
|
+
names.push(d);
|
|
7131
|
+
}
|
|
7132
|
+
}
|
|
7406
7133
|
}
|
|
7407
7134
|
return names;
|
|
7408
7135
|
}
|
|
@@ -8178,6 +7905,12 @@ function runLayoutPass(scratch, moveCtx) {
|
|
|
8178
7905
|
if (layout2.contains) return layout2.contains(cPose, draggedCenter);
|
|
8179
7906
|
return draggedCenter.x >= aabb.x && draggedCenter.x < aabb.x + aabb.width && draggedCenter.y >= aabb.y && draggedCenter.y < aabb.y + aabb.height;
|
|
8180
7907
|
};
|
|
7908
|
+
const draggedArg = {
|
|
7909
|
+
id: draggedId,
|
|
7910
|
+
originPose: startWorld,
|
|
7911
|
+
pose: draggedWorld,
|
|
7912
|
+
sourceContainerId
|
|
7913
|
+
};
|
|
8181
7914
|
const consider = (id, zPath) => {
|
|
8182
7915
|
if (id === draggedId) return;
|
|
8183
7916
|
const layout2 = layoutDep.getLayout(id);
|
|
@@ -8187,6 +7920,7 @@ function runLayoutPass(scratch, moveCtx) {
|
|
|
8187
7920
|
const worldPose = composeWorldPose(poseAdapter, id, pc.compose);
|
|
8188
7921
|
const worldAABB = boundsOf2(worldPose);
|
|
8189
7922
|
if (!testInside(worldPose, worldAABB, layout2)) return;
|
|
7923
|
+
if (layout2.acceptsDrop && !layout2.acceptsDrop({ id, bounds: worldAABB }, draggedArg)) return;
|
|
8190
7924
|
candidates.push({
|
|
8191
7925
|
id,
|
|
8192
7926
|
bounds: worldAABB,
|
|
@@ -8235,20 +7969,16 @@ function runLayoutPass(scratch, moveCtx) {
|
|
|
8235
7969
|
id: cid,
|
|
8236
7970
|
pose: composeWorldPose(poseAdapter, cid, pc.compose)
|
|
8237
7971
|
}));
|
|
8238
|
-
const draggedArg = {
|
|
8239
|
-
id: draggedId,
|
|
8240
|
-
originPose: startWorld,
|
|
8241
|
-
pose: draggedWorld,
|
|
8242
|
-
sourceContainerId
|
|
8243
|
-
};
|
|
8244
7972
|
const targets = layout.getDropTargets(container, children, draggedArg);
|
|
8245
7973
|
const target = layout.snap.pickTarget(targets, { x: moveCtx.drag.current.x, y: moveCtx.drag.current.y });
|
|
8246
7974
|
if (!target) return;
|
|
7975
|
+
const reflowIds = /* @__PURE__ */ new Set();
|
|
8247
7976
|
for (const [cid, pose] of layout.reflowPoses(container, children, draggedArg, target)) {
|
|
8248
7977
|
if (asNodeId(cid) === draggedId) continue;
|
|
8249
7978
|
const parent = scene.get(asNodeId(cid))?.parent ?? null;
|
|
8250
7979
|
const local = rebaseLocalPose(poseAdapter, pose, parent, pc.compose, pc.decompose);
|
|
8251
7980
|
scratch.previews.set(asNodeId(cid), local);
|
|
7981
|
+
reflowIds.add(asNodeId(cid));
|
|
8252
7982
|
}
|
|
8253
7983
|
const sourceReflow = /* @__PURE__ */ new Map();
|
|
8254
7984
|
if (sourceContainerId && sourceContainerId !== dest.id) {
|
|
@@ -8271,10 +8001,11 @@ function runLayoutPass(scratch, moveCtx) {
|
|
|
8271
8001
|
sourceReflow.set(cid, pose);
|
|
8272
8002
|
const parent = scene.get(asNodeId(cid))?.parent ?? null;
|
|
8273
8003
|
scratch.previews.set(asNodeId(cid), rebaseLocalPose(poseAdapter, pose, parent, pc.compose, pc.decompose));
|
|
8004
|
+
reflowIds.add(asNodeId(cid));
|
|
8274
8005
|
}
|
|
8275
8006
|
}
|
|
8276
8007
|
}
|
|
8277
|
-
scratch.layoutPass = { layout, container, children, target, sourceReflow };
|
|
8008
|
+
scratch.layoutPass = { layout, container, children, target, sourceReflow, reflowIds };
|
|
8278
8009
|
}
|
|
8279
8010
|
function scenePoseAdapter(scene) {
|
|
8280
8011
|
return {
|
|
@@ -8582,7 +8313,8 @@ var moveAction = {
|
|
|
8582
8313
|
scratch.previews.clear();
|
|
8583
8314
|
},
|
|
8584
8315
|
previewIds: () => scratch.previews.keys(),
|
|
8585
|
-
previewPose: (id) => scratch.previews.get(id) ?? null
|
|
8316
|
+
previewPose: (id) => scratch.previews.get(id) ?? null,
|
|
8317
|
+
previewOpaqueIds: () => scratch.layoutPass?.reflowIds ?? null
|
|
8586
8318
|
};
|
|
8587
8319
|
}
|
|
8588
8320
|
},
|
|
@@ -9318,7 +9050,8 @@ var KIT_INSERT_KINDS = /* @__PURE__ */ new Set([
|
|
|
9318
9050
|
"line",
|
|
9319
9051
|
"polygon",
|
|
9320
9052
|
"star",
|
|
9321
|
-
"pencil"
|
|
9053
|
+
"pencil",
|
|
9054
|
+
"image"
|
|
9322
9055
|
]);
|
|
9323
9056
|
var liveInsertScratch = null;
|
|
9324
9057
|
function effectiveOriginMode(paramsOrigin, altHeld) {
|
|
@@ -10366,7 +10099,7 @@ var pinchZoomAction = {
|
|
|
10366
10099
|
const multiTouch = ctx.multiTouch;
|
|
10367
10100
|
if (!multiTouch) return {};
|
|
10368
10101
|
const startSpread = multiTouch.spread > 0 ? multiTouch.spread : 1;
|
|
10369
|
-
const scratch = { view, startSpread };
|
|
10102
|
+
const scratch = { view, startSpread, centroid: multiTouch.centroid };
|
|
10370
10103
|
return {
|
|
10371
10104
|
kind: "pinch",
|
|
10372
10105
|
onMove(moveCtx) {
|
|
@@ -10376,8 +10109,15 @@ var pinchZoomAction = {
|
|
|
10376
10109
|
if (currentSpread <= 0 || scratch.startSpread <= 0) return;
|
|
10377
10110
|
const factor = currentSpread / scratch.startSpread;
|
|
10378
10111
|
scratch.startSpread = currentSpread;
|
|
10112
|
+
const prev = scratch.centroid;
|
|
10113
|
+
scratch.centroid = centroid;
|
|
10379
10114
|
const current = scratch.view.get();
|
|
10380
|
-
|
|
10115
|
+
const zoomed = zoomAt(current, prev, factor);
|
|
10116
|
+
scratch.view.set({
|
|
10117
|
+
scale: zoomed.scale,
|
|
10118
|
+
x: zoomed.x - (centroid.x - prev.x) / zoomed.scale.x,
|
|
10119
|
+
y: zoomed.y - (centroid.y - prev.y) / zoomed.scale.y
|
|
10120
|
+
});
|
|
10381
10121
|
}
|
|
10382
10122
|
// onEnd: nothing to do — zoom was applied each frame.
|
|
10383
10123
|
};
|
|
@@ -10837,10 +10577,12 @@ function normalizeNegativeExtent(pose) {
|
|
|
10837
10577
|
if (!mutated) return pose;
|
|
10838
10578
|
return { ...pose, x: nx, y: ny, width: nw, height: nh };
|
|
10839
10579
|
}
|
|
10840
|
-
function flipSelection(selection, scene, axis, applyOps, geometryProjection) {
|
|
10580
|
+
function flipSelection(selection, scene, axis, pivot, applyOps, geometryProjection) {
|
|
10841
10581
|
const ids = selection.get();
|
|
10842
10582
|
if (ids.length === 0) return;
|
|
10843
10583
|
const geom = AUTO_POSE_DESCRIPTOR;
|
|
10584
|
+
const nodes = ids.map((id) => scene.get(id)).filter((n) => n != null);
|
|
10585
|
+
const unionPivot = pivot === "union" ? unionBounds(nodes.map((n) => geom.getBounds(n.pose))) : null;
|
|
10844
10586
|
const ops = [];
|
|
10845
10587
|
for (const id of ids) {
|
|
10846
10588
|
const node = scene.get(id);
|
|
@@ -10848,10 +10590,10 @@ function flipSelection(selection, scene, axis, applyOps, geometryProjection) {
|
|
|
10848
10590
|
ops.push(createTransformOp({
|
|
10849
10591
|
id,
|
|
10850
10592
|
from: node.pose,
|
|
10851
|
-
to: flipPoseViaDescriptor(node.pose, axis, geom),
|
|
10593
|
+
to: unionPivot ? flipPoseAboutBounds(node.pose, axis, geom, unionPivot) : flipPoseViaDescriptor(node.pose, axis, geom),
|
|
10852
10594
|
label: "Flip"
|
|
10853
10595
|
}));
|
|
10854
|
-
const g = geom.getBounds(node.pose);
|
|
10596
|
+
const g = unionPivot ?? geom.getBounds(node.pose);
|
|
10855
10597
|
const cx = g.x + g.width / 2;
|
|
10856
10598
|
const cy = g.y + g.height / 2;
|
|
10857
10599
|
const m = axis === "x" ? [-1, 0, 0, 1, 2 * cx, 0] : [1, 0, 0, -1, 0, 2 * cy];
|
|
@@ -10880,12 +10622,13 @@ var flipAction = {
|
|
|
10880
10622
|
timing: "immediate",
|
|
10881
10623
|
run: (deps, params) => {
|
|
10882
10624
|
const axis = params?.axis ?? "x";
|
|
10625
|
+
const pivot = params?.pivot ?? "each";
|
|
10883
10626
|
const selection = deps.selection;
|
|
10884
10627
|
const scene = deps.scene;
|
|
10885
10628
|
const applyOps = deps.applyOps;
|
|
10886
10629
|
const geometryProjection = deps.geometryProjection;
|
|
10887
10630
|
if (!selection || !scene) return;
|
|
10888
|
-
flipSelection(selection, scene, axis, applyOps, geometryProjection);
|
|
10631
|
+
flipSelection(selection, scene, axis, pivot, applyOps, geometryProjection);
|
|
10889
10632
|
}
|
|
10890
10633
|
},
|
|
10891
10634
|
enabled: requiresSelection
|
|
@@ -12743,11 +12486,18 @@ var DEFAULT_DEBUG_THEME = {
|
|
|
12743
12486
|
fpsText: "#e0e0e0",
|
|
12744
12487
|
fpsTextBg: "rgba(0, 0, 0, 0.6)"
|
|
12745
12488
|
};
|
|
12489
|
+
var DEFAULT_DEBUG_STROKES = {
|
|
12490
|
+
hitbox: { width: 1, dash: [2, 2] },
|
|
12491
|
+
bounds: { width: 1 },
|
|
12492
|
+
handle: { width: 1 },
|
|
12493
|
+
snap: { width: 1 }
|
|
12494
|
+
};
|
|
12746
12495
|
function createDebugOverlayLayer({
|
|
12747
12496
|
sink,
|
|
12748
12497
|
config
|
|
12749
12498
|
}) {
|
|
12750
12499
|
const theme = { ...DEFAULT_DEBUG_THEME, ...config.theme ?? {} };
|
|
12500
|
+
const strokes = { ...DEFAULT_DEBUG_STROKES, ...config.strokes ?? {} };
|
|
12751
12501
|
const fpsHistory = [];
|
|
12752
12502
|
const FPS_WINDOW = 60;
|
|
12753
12503
|
return {
|
|
@@ -12759,11 +12509,11 @@ function createDebugOverlayLayer({
|
|
|
12759
12509
|
const s = sink.snapshot();
|
|
12760
12510
|
const t = viewToTransform(view);
|
|
12761
12511
|
const out = [];
|
|
12762
|
-
if (config.hitboxes) emitHitboxes(out, s, view, t, theme);
|
|
12763
|
-
if (config.bounds) emitBounds(out, s, view, t, theme);
|
|
12764
|
-
if (config.handles) emitHandles(out, s, t, theme);
|
|
12512
|
+
if (config.hitboxes) emitHitboxes(out, s, view, t, theme, strokes);
|
|
12513
|
+
if (config.bounds) emitBounds(out, s, view, t, theme, strokes);
|
|
12514
|
+
if (config.handles) emitHandles(out, s, t, theme, strokes);
|
|
12765
12515
|
if (config.origins) emitOrigins(out, s, t, theme);
|
|
12766
|
-
if (config.snap) emitSnap(out, s, t, theme);
|
|
12516
|
+
if (config.snap) emitSnap(out, s, t, theme, strokes);
|
|
12767
12517
|
if (config.ids) emitIds(out, s, t, theme);
|
|
12768
12518
|
if (config.layers) emitLayersPanel(out, s, dims, theme);
|
|
12769
12519
|
if (config.fps) {
|
|
@@ -12796,9 +12546,16 @@ function approxCircleScreen(cx, cy, r, segments = 24) {
|
|
|
12796
12546
|
function rectPath2(x, y, w, h) {
|
|
12797
12547
|
return { kind: "rect", x, y, width: w, height: h };
|
|
12798
12548
|
}
|
|
12799
|
-
function
|
|
12549
|
+
function strokeOf(s, color) {
|
|
12550
|
+
return {
|
|
12551
|
+
paint: { fill: "solid", color },
|
|
12552
|
+
width: s.width,
|
|
12553
|
+
...s.dash && s.dash.length > 0 ? { dash: [...s.dash] } : {}
|
|
12554
|
+
};
|
|
12555
|
+
}
|
|
12556
|
+
function emitHitboxes(out, s, view, t, theme, strokes) {
|
|
12800
12557
|
const fill = { fill: "solid", color: theme.hitboxFill };
|
|
12801
|
-
const stroke =
|
|
12558
|
+
const stroke = strokeOf(strokes.hitbox, theme.hitboxStroke);
|
|
12802
12559
|
for (const h of s.hitboxes) {
|
|
12803
12560
|
if (h.shape.kind === "rect") {
|
|
12804
12561
|
const [sx, sy] = worldToScreen(h.shape.x, h.shape.y, t);
|
|
@@ -12812,8 +12569,8 @@ function emitHitboxes(out, s, view, t, theme) {
|
|
|
12812
12569
|
}
|
|
12813
12570
|
}
|
|
12814
12571
|
}
|
|
12815
|
-
function emitBounds(out, s, view, t, theme) {
|
|
12816
|
-
const stroke =
|
|
12572
|
+
function emitBounds(out, s, view, t, theme, strokes) {
|
|
12573
|
+
const stroke = strokeOf(strokes.bounds, theme.bounds);
|
|
12817
12574
|
for (const b of s.bounds) {
|
|
12818
12575
|
const [sx, sy] = worldToScreen(b.bounds.x, b.bounds.y, t);
|
|
12819
12576
|
const sw = b.bounds.width * view.scale.x;
|
|
@@ -12821,8 +12578,8 @@ function emitBounds(out, s, view, t, theme) {
|
|
|
12821
12578
|
out.push({ kind: "path", path: rectPath2(sx, sy, sw, sh), stroke });
|
|
12822
12579
|
}
|
|
12823
12580
|
}
|
|
12824
|
-
function emitHandles(out, s, t, theme) {
|
|
12825
|
-
const stroke =
|
|
12581
|
+
function emitHandles(out, s, t, theme, strokes) {
|
|
12582
|
+
const stroke = strokeOf(strokes.handle, theme.handle);
|
|
12826
12583
|
for (const h of s.handles) {
|
|
12827
12584
|
const [cx, cy] = worldToScreen(h.position.x, h.position.y, t);
|
|
12828
12585
|
const horiz = {
|
|
@@ -12848,14 +12605,14 @@ function emitOrigins(out, s, t, theme) {
|
|
|
12848
12605
|
out.push({ kind: "path", path: approxCircleScreen(cx, cy, 3), fill });
|
|
12849
12606
|
}
|
|
12850
12607
|
}
|
|
12851
|
-
function emitSnap(out, s, t, theme) {
|
|
12608
|
+
function emitSnap(out, s, t, theme, strokes) {
|
|
12852
12609
|
const color = theme.snap;
|
|
12853
12610
|
for (const c of s.snap) {
|
|
12854
12611
|
const [cx, cy] = worldToScreen(c.point.x, c.point.y, t);
|
|
12855
12612
|
const cmd = {
|
|
12856
12613
|
kind: "path",
|
|
12857
12614
|
path: approxCircleScreen(cx, cy, 4),
|
|
12858
|
-
...c.accepted ? { fill: { fill: "solid", color } } : { stroke:
|
|
12615
|
+
...c.accepted ? { fill: { fill: "solid", color } } : { stroke: strokeOf(strokes.snap, color) }
|
|
12859
12616
|
};
|
|
12860
12617
|
out.push(cmd);
|
|
12861
12618
|
}
|
|
@@ -13227,11 +12984,22 @@ function reportDeadClaim(owner, warn) {
|
|
|
13227
12984
|
`[weasel] exclusive claim by "${key}" matched no binding: no \`affordance:\` or \`kindOf\` target resolved against it, so the press was dropped.`
|
|
13228
12985
|
);
|
|
13229
12986
|
}
|
|
12987
|
+
function phaseRank(spec) {
|
|
12988
|
+
if (spec === void 0) return 0;
|
|
12989
|
+
const atoms = typeof spec === "string" ? [{ channel: "&", phase: spec }] : spec;
|
|
12990
|
+
if (atoms.length === 0) return 0;
|
|
12991
|
+
let min = 2;
|
|
12992
|
+
for (const a of atoms) {
|
|
12993
|
+
const rank = (a.channel === "*" ? 0 : 1) + (a.phase === "*" ? 0 : 1);
|
|
12994
|
+
if (rank < min) min = rank;
|
|
12995
|
+
}
|
|
12996
|
+
return min;
|
|
12997
|
+
}
|
|
13230
12998
|
function specificity(spec) {
|
|
13231
12999
|
const t = targetRank(specTargetOf(spec));
|
|
13232
13000
|
const mods = "mods" in spec ? spec.mods : void 0;
|
|
13233
13001
|
const m = modsCount(mods);
|
|
13234
|
-
const p = "phase" in spec
|
|
13002
|
+
const p = phaseRank("phase" in spec ? spec.phase : void 0);
|
|
13235
13003
|
const typed = (spec.kind === "drop" || spec.kind === "paste") && spec.types !== void 0 && spec.types.length > 0 ? 2 : 1;
|
|
13236
13004
|
return [t, m, p, typed];
|
|
13237
13005
|
}
|
|
@@ -14831,8 +14599,7 @@ function useGestureDispatcher(opts) {
|
|
|
14831
14599
|
if (!cd) return;
|
|
14832
14600
|
const items = itemsFromClipboardData(cd);
|
|
14833
14601
|
if (items.length === 0) return;
|
|
14834
|
-
|
|
14835
|
-
dispatch2({
|
|
14602
|
+
const result = dispatch2({
|
|
14836
14603
|
kind: "paste",
|
|
14837
14604
|
items,
|
|
14838
14605
|
// ClipboardEvent carries no modifier state.
|
|
@@ -14841,6 +14608,7 @@ function useGestureDispatcher(opts) {
|
|
|
14841
14608
|
metaKey: false,
|
|
14842
14609
|
shiftKey: false
|
|
14843
14610
|
});
|
|
14611
|
+
if (result === "handled") e.preventDefault();
|
|
14844
14612
|
};
|
|
14845
14613
|
if (keyboard) {
|
|
14846
14614
|
window.addEventListener("keydown", onKeyDown);
|
|
@@ -15083,6 +14851,50 @@ function usePinchGesture(canvasRef, onPinch, enabled = true) {
|
|
|
15083
14851
|
};
|
|
15084
14852
|
}, [canvasRef, enabled]);
|
|
15085
14853
|
}
|
|
14854
|
+
function validateId(id, kind) {
|
|
14855
|
+
if (id.length === 0) {
|
|
14856
|
+
throw new Error(`weasel: ${kind} id may not be empty`);
|
|
14857
|
+
}
|
|
14858
|
+
if (RESERVED_ID_PREFIXES.has(id[0])) {
|
|
14859
|
+
throw new Error(
|
|
14860
|
+
`weasel: ${kind} id "${id}" starts with reserved sigil "${id[0]}" (reserved set: ${[...RESERVED_ID_PREFIXES].join(" ")})`
|
|
14861
|
+
);
|
|
14862
|
+
}
|
|
14863
|
+
if (RESERVED_ID_NAMES.has(id)) {
|
|
14864
|
+
throw new Error(
|
|
14865
|
+
`weasel: ${kind} id "${id}" collides with a reserved phase keyword (reserved: ${[...RESERVED_ID_NAMES].join(", ")})`
|
|
14866
|
+
);
|
|
14867
|
+
}
|
|
14868
|
+
}
|
|
14869
|
+
function defineTool(def) {
|
|
14870
|
+
validateId(def.id, "tool");
|
|
14871
|
+
const resolveCursor = (ctx) => {
|
|
14872
|
+
if (def.cursor == null) return "";
|
|
14873
|
+
return typeof def.cursor === "function" ? def.cursor(ctx) : def.cursor;
|
|
14874
|
+
};
|
|
14875
|
+
return {
|
|
14876
|
+
id: def.id,
|
|
14877
|
+
eligibility: {
|
|
14878
|
+
focus: true,
|
|
14879
|
+
capabilities: def.capabilities,
|
|
14880
|
+
...def.hotkey ? { offhand: def.hotkey } : {}
|
|
14881
|
+
},
|
|
14882
|
+
actions: def.actions,
|
|
14883
|
+
def,
|
|
14884
|
+
presentation: def.presentation,
|
|
14885
|
+
keybinding: def.keybinding,
|
|
14886
|
+
onActivate: def.onActivate,
|
|
14887
|
+
onDeactivate: def.onDeactivate,
|
|
14888
|
+
initScratch: def.initScratch ?? (() => null),
|
|
14889
|
+
cursor: resolveCursor,
|
|
14890
|
+
bindings: def.bindings,
|
|
14891
|
+
overlay: def.overlay,
|
|
14892
|
+
overlayPosition: def.overlayPosition
|
|
14893
|
+
};
|
|
14894
|
+
}
|
|
14895
|
+
function defineViewportTool(def) {
|
|
14896
|
+
return defineTool(def);
|
|
14897
|
+
}
|
|
15086
14898
|
var TOOL_OFFHAND_ID = "tool.offhand";
|
|
15087
14899
|
function offhandKeyFor(trigger) {
|
|
15088
14900
|
switch (trigger) {
|
|
@@ -15173,17 +14985,19 @@ function useContributions(opts) {
|
|
|
15173
14985
|
const { ordered, state } = snapshot();
|
|
15174
14986
|
return scopeBindings(ordered, state);
|
|
15175
14987
|
}, [snapshot]);
|
|
15176
|
-
const overlays = useCallback(() => {
|
|
14988
|
+
const overlays = useCallback((position = "top") => {
|
|
15177
14989
|
const { ordered, state } = snapshot();
|
|
15178
14990
|
const active = [];
|
|
15179
14991
|
const hotkey = [];
|
|
15180
14992
|
const ambient = [];
|
|
15181
14993
|
for (const entry of ordered) {
|
|
15182
14994
|
if (!entry.overlay) continue;
|
|
14995
|
+
if ((entry.overlayPosition ?? "top") !== position) continue;
|
|
14996
|
+
const layers = Array.isArray(entry.overlay) ? entry.overlay : [entry.overlay];
|
|
15183
14997
|
const scope = liveScope(entry.id, entry.eligibility ?? {}, state);
|
|
15184
|
-
if (scope === "active") active.push(
|
|
15185
|
-
else if (scope === "hotkey") hotkey.push(
|
|
15186
|
-
else if (scope === "ambient") ambient.push(
|
|
14998
|
+
if (scope === "active") active.push(...layers);
|
|
14999
|
+
else if (scope === "hotkey") hotkey.push(...layers);
|
|
15000
|
+
else if (scope === "ambient") ambient.push(...layers);
|
|
15187
15001
|
}
|
|
15188
15002
|
return [...active, ...hotkey, ...ambient];
|
|
15189
15003
|
}, [snapshot]);
|
|
@@ -15811,7 +15625,7 @@ function isBoundsGradient(fill) {
|
|
|
15811
15625
|
}
|
|
15812
15626
|
return fill.units === "bounds";
|
|
15813
15627
|
}
|
|
15814
|
-
var
|
|
15628
|
+
var cache6 = /* @__PURE__ */ new Map();
|
|
15815
15629
|
function specKey(spec) {
|
|
15816
15630
|
const keys = Object.keys(spec).sort();
|
|
15817
15631
|
const bag = spec;
|
|
@@ -15819,10 +15633,10 @@ function specKey(spec) {
|
|
|
15819
15633
|
}
|
|
15820
15634
|
function resolvePatternSpec(spec) {
|
|
15821
15635
|
const key = specKey(spec);
|
|
15822
|
-
const hit =
|
|
15636
|
+
const hit = cache6.get(key);
|
|
15823
15637
|
if (hit !== void 0) return hit;
|
|
15824
15638
|
const built = buildTile(spec);
|
|
15825
|
-
|
|
15639
|
+
cache6.set(key, built);
|
|
15826
15640
|
return built;
|
|
15827
15641
|
}
|
|
15828
15642
|
function buildTile(spec) {
|
|
@@ -15856,7 +15670,7 @@ function resolveFillPattern(fill) {
|
|
|
15856
15670
|
const handle = resolvePatternSpec(fill.pattern);
|
|
15857
15671
|
return handle ? { ...fill, pattern: handle } : null;
|
|
15858
15672
|
}
|
|
15859
|
-
var
|
|
15673
|
+
var cache7 = /* @__PURE__ */ new Map();
|
|
15860
15674
|
var subscribers2 = /* @__PURE__ */ new Set();
|
|
15861
15675
|
var defaultLoader = async (src) => {
|
|
15862
15676
|
if (typeof Image === "undefined" || typeof createImageBitmap === "undefined") {
|
|
@@ -15874,28 +15688,28 @@ function notify() {
|
|
|
15874
15688
|
}
|
|
15875
15689
|
function startLoad(src) {
|
|
15876
15690
|
const entry = { status: "loading" };
|
|
15877
|
-
|
|
15691
|
+
cache7.set(src, entry);
|
|
15878
15692
|
loader(src).then(
|
|
15879
15693
|
(bitmap) => {
|
|
15880
|
-
if (
|
|
15881
|
-
|
|
15694
|
+
if (cache7.get(src) !== entry) return;
|
|
15695
|
+
cache7.set(src, { status: "ready", bitmap });
|
|
15882
15696
|
notify();
|
|
15883
15697
|
},
|
|
15884
15698
|
() => {
|
|
15885
|
-
if (
|
|
15886
|
-
|
|
15699
|
+
if (cache7.get(src) !== entry) return;
|
|
15700
|
+
cache7.set(src, { status: "error" });
|
|
15887
15701
|
notify();
|
|
15888
15702
|
}
|
|
15889
15703
|
);
|
|
15890
15704
|
}
|
|
15891
15705
|
function getImageBitmap(src) {
|
|
15892
|
-
const entry =
|
|
15706
|
+
const entry = cache7.get(src);
|
|
15893
15707
|
if (entry) return entry.bitmap;
|
|
15894
15708
|
startLoad(src);
|
|
15895
15709
|
return void 0;
|
|
15896
15710
|
}
|
|
15897
15711
|
function imageStatus(src) {
|
|
15898
|
-
return
|
|
15712
|
+
return cache7.get(src)?.status ?? "idle";
|
|
15899
15713
|
}
|
|
15900
15714
|
function subscribeImageReady(cb) {
|
|
15901
15715
|
subscribers2.add(cb);
|
|
@@ -16253,6 +16067,7 @@ registerBuiltInShapePainters();
|
|
|
16253
16067
|
function pickTopMostHit(ids, adapter) {
|
|
16254
16068
|
if (ids.length === 0) return null;
|
|
16255
16069
|
if (ids.length === 1) return ids[0];
|
|
16070
|
+
let candidates = ids;
|
|
16256
16071
|
const getParent = adapter?.getParent;
|
|
16257
16072
|
if (typeof getParent === "function") {
|
|
16258
16073
|
const set = new Set(ids);
|
|
@@ -16267,16 +16082,40 @@ function pickTopMostHit(ids, adapter) {
|
|
|
16267
16082
|
}
|
|
16268
16083
|
if (dropped.size > 0) {
|
|
16269
16084
|
const filtered = ids.filter((id) => !dropped.has(id));
|
|
16270
|
-
if (filtered.length ===
|
|
16271
|
-
|
|
16272
|
-
|
|
16273
|
-
|
|
16274
|
-
|
|
16085
|
+
if (filtered.length === 1) return filtered[0];
|
|
16086
|
+
if (filtered.length > 1) candidates = filtered;
|
|
16087
|
+
}
|
|
16088
|
+
}
|
|
16089
|
+
return pickHighestZ(candidates, adapter);
|
|
16090
|
+
}
|
|
16091
|
+
function pickHighestZ(ids, adapter) {
|
|
16092
|
+
const compareZ = adapter?.compareZ;
|
|
16093
|
+
if (typeof compareZ === "function") {
|
|
16094
|
+
let best = ids[0];
|
|
16095
|
+
for (let i = 1; i < ids.length; i++) {
|
|
16096
|
+
if (compareZ(ids[i], best) >= 0) best = ids[i];
|
|
16097
|
+
}
|
|
16098
|
+
return best;
|
|
16099
|
+
}
|
|
16100
|
+
const getZIndex = adapter?.getZIndex;
|
|
16101
|
+
if (typeof getZIndex === "function") {
|
|
16102
|
+
let best = ids[0];
|
|
16103
|
+
let bestZ = zOf(getZIndex, best);
|
|
16104
|
+
for (let i = 1; i < ids.length; i++) {
|
|
16105
|
+
const z = zOf(getZIndex, ids[i]);
|
|
16106
|
+
if (z >= bestZ) {
|
|
16107
|
+
best = ids[i];
|
|
16108
|
+
bestZ = z;
|
|
16275
16109
|
}
|
|
16276
16110
|
}
|
|
16111
|
+
return best;
|
|
16277
16112
|
}
|
|
16278
16113
|
return ids[ids.length - 1];
|
|
16279
16114
|
}
|
|
16115
|
+
function zOf(getZIndex, id) {
|
|
16116
|
+
const z = getZIndex(id);
|
|
16117
|
+
return typeof z === "number" && Number.isFinite(z) ? z : -Infinity;
|
|
16118
|
+
}
|
|
16280
16119
|
var MULTI_RESIZE_TARGET_ID = "__weasel:multi-selection";
|
|
16281
16120
|
function useSelectTool(adapter, options) {
|
|
16282
16121
|
const poseBoundsFn = options.poseBounds ?? ((p) => p);
|
|
@@ -16673,9 +16512,19 @@ function composeAffordanceLayer(id, label, affordances) {
|
|
|
16673
16512
|
},
|
|
16674
16513
|
// The walk itself lives in `hitAffordanceRegions` so this layer and
|
|
16675
16514
|
// `buildAffordanceAt` share one implementation rather than two that can
|
|
16676
|
-
// drift. This wrapper
|
|
16677
|
-
// binding
|
|
16678
|
-
hitTest: (wx, wy, state, view, _dims, isVisible) =>
|
|
16515
|
+
// drift. This wrapper lifts the region's declared cursor and claim onto
|
|
16516
|
+
// the binding, the way `buildAffordanceAt` lifts them onto `AffordanceHit`.
|
|
16517
|
+
hitTest: (wx, wy, state, view, _dims, isVisible) => {
|
|
16518
|
+
const hit = hitAffordanceRegions(affordances, wx, wy, state, view, isVisible);
|
|
16519
|
+
if (!hit) return null;
|
|
16520
|
+
const { cursor, strength, claimedKinds } = hit.region;
|
|
16521
|
+
return {
|
|
16522
|
+
...hit.binding,
|
|
16523
|
+
...cursor !== void 0 ? { cursor } : {},
|
|
16524
|
+
...strength !== void 0 ? { strength } : {},
|
|
16525
|
+
...claimedKinds !== void 0 ? { claimedKinds } : {}
|
|
16526
|
+
};
|
|
16527
|
+
}
|
|
16679
16528
|
};
|
|
16680
16529
|
}
|
|
16681
16530
|
function asChromeState(data) {
|
|
@@ -17835,6 +17684,19 @@ function composeOrderedLayers(layersMap, standardLayers, sceneLayers) {
|
|
|
17835
17684
|
}
|
|
17836
17685
|
return out;
|
|
17837
17686
|
}
|
|
17687
|
+
function placeToolOverlays(ordered, selectionOverlay, overlays) {
|
|
17688
|
+
const before = overlays("before-selection");
|
|
17689
|
+
const after = overlays("after-selection");
|
|
17690
|
+
const at = selectionOverlay ? ordered.indexOf(selectionOverlay) : -1;
|
|
17691
|
+
if (at >= 0) {
|
|
17692
|
+
ordered.splice(at + 1, 0, ...after);
|
|
17693
|
+
ordered.splice(at, 0, ...before);
|
|
17694
|
+
} else {
|
|
17695
|
+
ordered.push(...before, ...after);
|
|
17696
|
+
}
|
|
17697
|
+
ordered.push(...overlays("top"));
|
|
17698
|
+
return ordered;
|
|
17699
|
+
}
|
|
17838
17700
|
function* toolsInPriorityOrder(tools) {
|
|
17839
17701
|
const seen = /* @__PURE__ */ new Set();
|
|
17840
17702
|
const hotkey = tools.hotkeyEngaged ? tools.registry[tools.hotkeyEngaged] : void 0;
|
|
@@ -19263,7 +19125,7 @@ function CanvasInner(props, ref) {
|
|
|
19263
19125
|
const out = composeOrderedLayers(effectiveLayersMap, standardLayers, sceneLayers);
|
|
19264
19126
|
if (decorationLayer) out.push(decorationLayer);
|
|
19265
19127
|
if (tools) {
|
|
19266
|
-
out.
|
|
19128
|
+
placeToolOverlays(out, standardLayers.selectionOverlay, tools.getActiveOverlays);
|
|
19267
19129
|
}
|
|
19268
19130
|
return out;
|
|
19269
19131
|
}, [layersMap, adapter, selectedIds, effectiveBoundsOf, multiActive2, debugSink, tools, backgroundLayer, decorationLayer]);
|
|
@@ -20893,6 +20755,7 @@ function useSceneSelectTool(args) {
|
|
|
20893
20755
|
boundsOf: wiredBoundsOf
|
|
20894
20756
|
};
|
|
20895
20757
|
}
|
|
20758
|
+
var GHOST_ALPHA = 0.85;
|
|
20896
20759
|
function usePreviewGhostLayer(args) {
|
|
20897
20760
|
const { scene, tools, sceneSlot, dispatcher } = args;
|
|
20898
20761
|
const sceneRef = useRef(scene);
|
|
@@ -20979,15 +20842,26 @@ function usePreviewGhostLayer(args) {
|
|
|
20979
20842
|
}
|
|
20980
20843
|
return [group];
|
|
20981
20844
|
};
|
|
20845
|
+
const opaqueSet = /* @__PURE__ */ new Set();
|
|
20846
|
+
for (const source of sources) {
|
|
20847
|
+
const ids = source.previewOpaqueIds?.();
|
|
20848
|
+
if (!ids) continue;
|
|
20849
|
+
for (const id of ids) opaqueSet.add(id);
|
|
20850
|
+
}
|
|
20982
20851
|
const children = [];
|
|
20852
|
+
const opaque = [];
|
|
20983
20853
|
for (const id of idSet) {
|
|
20984
20854
|
const node = sc.get(asNodeId(id));
|
|
20985
20855
|
const parent = node?.parent;
|
|
20986
20856
|
if (parent != null && idSet.has(parent)) continue;
|
|
20987
|
-
|
|
20857
|
+
const sink = opaqueSet.has(id) ? opaque : children;
|
|
20858
|
+
for (const cmd of buildSubtree(id)) sink.push(cmd);
|
|
20988
20859
|
}
|
|
20989
|
-
if (children.length === 0) return [];
|
|
20990
|
-
|
|
20860
|
+
if (children.length === 0 && opaque.length === 0) return [];
|
|
20861
|
+
const out = [];
|
|
20862
|
+
if (opaque.length > 0) out.push({ kind: "group", children: opaque });
|
|
20863
|
+
if (children.length > 0) out.push({ kind: "group", alpha: GHOST_ALPHA, children });
|
|
20864
|
+
return out;
|
|
20991
20865
|
}
|
|
20992
20866
|
}), []);
|
|
20993
20867
|
}
|
|
@@ -20997,6 +20871,7 @@ var DEFAULT_STYLE = {
|
|
|
20997
20871
|
dash: [3, 3],
|
|
20998
20872
|
lineWidth: 1
|
|
20999
20873
|
};
|
|
20874
|
+
var PREVIEW_CONTENT_OPACITY = 0.85;
|
|
21000
20875
|
function useDispatcherOverlayLayer(args) {
|
|
21001
20876
|
const { dispatcher, style } = args;
|
|
21002
20877
|
const dispatcherRef = useRef(dispatcher);
|
|
@@ -21059,6 +20934,26 @@ function useDispatcherOverlayLayer(args) {
|
|
|
21059
20934
|
pathCmd = rectPath(sx, sy, b.width * view.scale.x, b.height * view.scale.y);
|
|
21060
20935
|
break;
|
|
21061
20936
|
}
|
|
20937
|
+
case "image": {
|
|
20938
|
+
const e = ov.extras;
|
|
20939
|
+
const [sx, sy] = worldToScreen(b.x, b.y, t);
|
|
20940
|
+
const sw = b.width * view.scale.x;
|
|
20941
|
+
const sh = b.height * view.scale.y;
|
|
20942
|
+
const bmp = e.preview === "outline" || !e.src ? void 0 : getImageBitmap(e.src);
|
|
20943
|
+
if (bmp) {
|
|
20944
|
+
out.push({
|
|
20945
|
+
kind: "image",
|
|
20946
|
+
image: bmp,
|
|
20947
|
+
x: sx,
|
|
20948
|
+
y: sy,
|
|
20949
|
+
w: sw,
|
|
20950
|
+
h: sh,
|
|
20951
|
+
opacity: PREVIEW_CONTENT_OPACITY
|
|
20952
|
+
});
|
|
20953
|
+
}
|
|
20954
|
+
pathCmd = rectPath(sx, sy, sw, sh);
|
|
20955
|
+
break;
|
|
20956
|
+
}
|
|
21062
20957
|
case "ellipse": {
|
|
21063
20958
|
const [sx, sy] = worldToScreen(b.x, b.y, t);
|
|
21064
20959
|
pathCmd = ellipsePath({
|
|
@@ -21117,7 +21012,7 @@ function useDispatcherOverlayLayer(args) {
|
|
|
21117
21012
|
const cmd = {
|
|
21118
21013
|
kind: "path",
|
|
21119
21014
|
path: pathCmd,
|
|
21120
|
-
fill: ov.shape === "line" || ov.shape === "pencil" ? void 0 : { color: cfg.fill },
|
|
21015
|
+
fill: ov.shape === "line" || ov.shape === "pencil" || ov.shape === "image" ? void 0 : { color: cfg.fill },
|
|
21121
21016
|
stroke: { paint: { color: cfg.stroke }, width: cfg.lineWidth, dash: cfg.dash }
|
|
21122
21017
|
};
|
|
21123
21018
|
out.push(cmd);
|
|
@@ -21806,9 +21701,6 @@ function buildClassifyTarget(getSelection, pickBest, kindOfNode) {
|
|
|
21806
21701
|
function w2s3(wx, wy, view) {
|
|
21807
21702
|
return [(wx - view.x) * view.scale.x, (wy - view.y) * view.scale.y];
|
|
21808
21703
|
}
|
|
21809
|
-
function r2s(radiusWorld, view) {
|
|
21810
|
-
return radiusWorld * meanScale(view.scale);
|
|
21811
|
-
}
|
|
21812
21704
|
var SLOP_FILL = "rgba(255, 80, 140, 0.18)";
|
|
21813
21705
|
var SLOP_STROKE = "rgba(255, 80, 140, 0.55)";
|
|
21814
21706
|
function createSlopsDebugLayer(opts) {
|
|
@@ -21819,7 +21711,7 @@ function createSlopsDebugLayer(opts) {
|
|
|
21819
21711
|
draw: (_data, view) => {
|
|
21820
21712
|
const out = [];
|
|
21821
21713
|
const sel = opts.selectionRef.current?.current ?? [];
|
|
21822
|
-
const r =
|
|
21714
|
+
const r = HANDLE_HIT_RADIUS;
|
|
21823
21715
|
for (const id of sel) {
|
|
21824
21716
|
const b = opts.boundsOf(id);
|
|
21825
21717
|
if (!b) continue;
|
|
@@ -21856,6 +21748,7 @@ function createSlopsDebugLayer(opts) {
|
|
|
21856
21748
|
}
|
|
21857
21749
|
const editingId = opts.getEditingId();
|
|
21858
21750
|
if (editingId) {
|
|
21751
|
+
const anchorR = ANCHOR_HIT_RADIUS;
|
|
21859
21752
|
const pose = opts.getPose(editingId);
|
|
21860
21753
|
if (pose && pose.kind === "polygon") {
|
|
21861
21754
|
const anchors = enumerateAnchors(pose);
|
|
@@ -21863,7 +21756,7 @@ function createSlopsDebugLayer(opts) {
|
|
|
21863
21756
|
const [sx, sy] = w2s3(a.x, a.y, view);
|
|
21864
21757
|
out.push({
|
|
21865
21758
|
kind: "path",
|
|
21866
|
-
path: circlePath(sx, sy,
|
|
21759
|
+
path: circlePath(sx, sy, anchorR),
|
|
21867
21760
|
fill: { fill: "solid", color: SLOP_FILL },
|
|
21868
21761
|
stroke: { paint: { fill: "solid", color: SLOP_STROKE }, width: 1 }
|
|
21869
21762
|
});
|
|
@@ -21871,7 +21764,7 @@ function createSlopsDebugLayer(opts) {
|
|
|
21871
21764
|
const [csx, csy] = w2s3(a.controlIn.x, a.controlIn.y, view);
|
|
21872
21765
|
out.push({
|
|
21873
21766
|
kind: "path",
|
|
21874
|
-
path: circlePath(csx, csy,
|
|
21767
|
+
path: circlePath(csx, csy, anchorR),
|
|
21875
21768
|
fill: { fill: "solid", color: SLOP_FILL },
|
|
21876
21769
|
stroke: { paint: { fill: "solid", color: SLOP_STROKE }, width: 1 }
|
|
21877
21770
|
});
|
|
@@ -21880,7 +21773,7 @@ function createSlopsDebugLayer(opts) {
|
|
|
21880
21773
|
const [csx, csy] = w2s3(a.controlOut.x, a.controlOut.y, view);
|
|
21881
21774
|
out.push({
|
|
21882
21775
|
kind: "path",
|
|
21883
|
-
path: circlePath(csx, csy,
|
|
21776
|
+
path: circlePath(csx, csy, anchorR),
|
|
21884
21777
|
fill: { fill: "solid", color: SLOP_FILL },
|
|
21885
21778
|
stroke: { paint: { fill: "solid", color: SLOP_STROKE }, width: 1 }
|
|
21886
21779
|
});
|
|
@@ -21958,9 +21851,9 @@ function DepRegistryProviderIfRoot({ children }) {
|
|
|
21958
21851
|
function hitTestArea(scene, bounds) {
|
|
21959
21852
|
const { x, y, width: w, height: h } = bounds;
|
|
21960
21853
|
const area = [x, y, x + w, y, x + w, y + h, x, y + h];
|
|
21961
|
-
return hitTestAreaPolygon(scene, area, { x, y, width: w, height: h });
|
|
21854
|
+
return hitTestAreaPolygon(scene, area, { x, y, width: w, height: h }, true);
|
|
21962
21855
|
}
|
|
21963
|
-
function hitTestAreaPolygon(scene, area, areaBounds) {
|
|
21856
|
+
function hitTestAreaPolygon(scene, area, areaBounds, areaIsRect = false) {
|
|
21964
21857
|
const ab = areaBounds ?? boundsOf(area);
|
|
21965
21858
|
if (!ab) return [];
|
|
21966
21859
|
const hits = [];
|
|
@@ -21977,13 +21870,22 @@ function hitTestAreaPolygon(scene, area, areaBounds) {
|
|
|
21977
21870
|
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) {
|
|
21978
21871
|
continue;
|
|
21979
21872
|
}
|
|
21980
|
-
if (
|
|
21873
|
+
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) {
|
|
21981
21874
|
hits.push(node.id);
|
|
21982
21875
|
continue;
|
|
21983
21876
|
}
|
|
21984
|
-
if (
|
|
21985
|
-
|
|
21877
|
+
if (silhouette) {
|
|
21878
|
+
if (silhouetteOverlapsArea(pose.coords, area)) {
|
|
21879
|
+
hits.push(node.id);
|
|
21880
|
+
}
|
|
21881
|
+
continue;
|
|
21882
|
+
}
|
|
21883
|
+
const drawn = findShapeSilhouette(node, pose);
|
|
21884
|
+
if (drawn?.kind === "polygon") {
|
|
21885
|
+
if (silhouetteOverlapsArea(drawn.coords, area)) hits.push(node.id);
|
|
21886
|
+
continue;
|
|
21986
21887
|
}
|
|
21888
|
+
hits.push(node.id);
|
|
21987
21889
|
}
|
|
21988
21890
|
return hits;
|
|
21989
21891
|
}
|
|
@@ -24573,5 +24475,5 @@ function oklchToOklab(L, C, h) {
|
|
|
24573
24475
|
}
|
|
24574
24476
|
|
|
24575
24477
|
export { FALLBACK_FIT_VIEW, MinimapCanvas, SceneViewCanvas, UnknownIcon, buildSceneViewCommands, computeFitView2, computeIndicatorCommand, eligibleContribution, evaluateEnabled, oklabToSrgbU8, oklchToOklab, renderSceneToCanvas, rgbaToHex, useActionsRegistry, useOptionalDepRegistry };
|
|
24576
|
-
//# sourceMappingURL=chunk-
|
|
24577
|
-
//# sourceMappingURL=chunk-
|
|
24478
|
+
//# sourceMappingURL=chunk-3P56ZCCJ.js.map
|
|
24479
|
+
//# sourceMappingURL=chunk-3P56ZCCJ.js.map
|