@weasel-js/geom 1.9.4 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -16
- package/dist/3d/index.js +1 -1
- package/dist/booleans/index.d.ts +63 -29
- package/dist/booleans/index.js +86 -11
- package/dist/booleans/index.js.map +1 -1
- package/dist/{chunk-N5SYTHMR.js → chunk-755Y4YZS.js} +2 -2
- package/dist/chunk-755Y4YZS.js.map +1 -0
- package/dist/{chunk-NL423YYZ.js → chunk-7WFFCHRL.js} +8 -6
- package/dist/{chunk-NL423YYZ.js.map → chunk-7WFFCHRL.js.map} +1 -1
- package/dist/{chunk-X6GJRIQD.js → chunk-IDXY2NCA.js} +38 -16
- package/dist/chunk-IDXY2NCA.js.map +1 -0
- package/dist/chunk-NJ3IGAKR.js +16 -0
- package/dist/chunk-NJ3IGAKR.js.map +1 -0
- package/dist/{chunk-E4TJRGJD.js → chunk-OXH3NOXC.js} +2 -2
- package/dist/chunk-OXH3NOXC.js.map +1 -0
- package/dist/cubicMath-BW-HJrCE.d.ts +16 -0
- package/dist/curves/index.d.ts +1 -1
- package/dist/curves/index.js +2 -2
- package/dist/index.d.ts +899 -135
- package/dist/index.js +1545 -123
- package/dist/index.js.map +1 -1
- package/dist/{path-D0CoudGM.d.ts → path-ljHxobyu.d.ts} +10 -11
- package/dist/tessellate/index.d.ts +11 -12
- package/dist/tessellate/index.js +22 -5
- package/dist/tessellate/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-E4TJRGJD.js.map +0 -1
- package/dist/chunk-HPVINMOM.js +0 -34
- package/dist/chunk-HPVINMOM.js.map +0 -1
- package/dist/chunk-N5SYTHMR.js.map +0 -1
- package/dist/chunk-X6GJRIQD.js.map +0 -1
- package/dist/cubicMath-CpCvuEl8.d.ts +0 -37
package/dist/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { PORT_REACH, portControls, portCurvePoints } from './chunk-YQADYJJ7.js';
|
|
2
2
|
export { PORT_REACH } from './chunk-YQADYJJ7.js';
|
|
3
|
-
import { cubicPointAt } from './chunk-
|
|
4
|
-
export { cubicPointAt,
|
|
3
|
+
import { cubicPointAt, splitCubicAtT } from './chunk-NJ3IGAKR.js';
|
|
4
|
+
export { cubicPointAt, splitCubicAtT } from './chunk-NJ3IGAKR.js';
|
|
5
5
|
import { cubicEvalAt, elevateQuadraticToCubic, cubicBounds } from './chunk-URQMDR6Q.js';
|
|
6
6
|
export { cubicBounds, cubicEvalAt, elevateQuadraticToCubic, quadraticEvalAt, splitCubicAt, splitLineAt, splitQuadraticAt } from './chunk-URQMDR6Q.js';
|
|
7
|
-
import { extractPolylines } from './chunk-
|
|
8
|
-
export { boundsOfCoords, boxContainsBox, boxContainsPoint, rectToContour, unionBox } from './chunk-
|
|
9
|
-
import {
|
|
10
|
-
export { DEFAULT_FLATTEN_TOLERANCE, flattenCubic, flattenCubicWithArcLen, flattenQuadratic, flattenQuadraticWithArcLen, pointInPolygon, pointSegmentDist2, segmentsCross } from './chunk-
|
|
11
|
-
import { SINGULAR_RATIO } from './chunk-
|
|
12
|
-
export { EPS, approxEq, clamp, cross, dot, len2, sign, sub } from './chunk-
|
|
13
|
-
import { forEachSegment, PATH_M, PATH_L, PATH_Q, PATH_C, PATH_Z, pathCommandCoordCount } from './chunk-
|
|
14
|
-
export { PATH_C, PATH_CMD_LENGTHS, PATH_COMMANDS, PATH_L, PATH_M, PATH_Q, PATH_Z, forEachSegment, pathCommandCoordCount } from './chunk-
|
|
7
|
+
import { extractPolylines, rectToContour } from './chunk-7WFFCHRL.js';
|
|
8
|
+
export { boundsOfCoords, boxContainsBox, boxContainsPoint, rectToContour, unionBox } from './chunk-7WFFCHRL.js';
|
|
9
|
+
import { fillsOpenSubpaths, pointInPolygon, pointSegmentDist2, flattenQuadratic, flattenCubic, DEFAULT_FLATTEN_TOLERANCE, segmentsCross } from './chunk-IDXY2NCA.js';
|
|
10
|
+
export { DEFAULT_FLATTEN_TOLERANCE, defaultOpenFill, fillsOpenSubpaths, flattenCubic, flattenCubicWithArcLen, flattenQuadratic, flattenQuadraticWithArcLen, pointInPolygon, pointSegmentDist2, segmentsCross, setDefaultOpenFill } from './chunk-IDXY2NCA.js';
|
|
11
|
+
import { SINGULAR_RATIO } from './chunk-755Y4YZS.js';
|
|
12
|
+
export { EPS, approxEq, clamp, cross, dot, len2, sign, sub } from './chunk-755Y4YZS.js';
|
|
13
|
+
import { forEachSegment, PATH_M, PATH_L, PATH_Q, PATH_C, PATH_Z, pathCommandCoordCount } from './chunk-OXH3NOXC.js';
|
|
14
|
+
export { PATH_C, PATH_CMD_LENGTHS, PATH_COMMANDS, PATH_L, PATH_M, PATH_Q, PATH_Z, forEachSegment, pathCommandCoordCount } from './chunk-OXH3NOXC.js';
|
|
15
15
|
|
|
16
16
|
// src/view.ts
|
|
17
17
|
var ZOOM_FLOOR = 1e-9;
|
|
@@ -19,7 +19,7 @@ function normalizeZoom(zoom) {
|
|
|
19
19
|
return zoom >= ZOOM_FLOOR && zoom < Infinity ? zoom : ZOOM_FLOOR;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
// src/
|
|
22
|
+
// src/affine.ts
|
|
23
23
|
function identity() {
|
|
24
24
|
return [1, 0, 0, 1, 0, 0];
|
|
25
25
|
}
|
|
@@ -66,6 +66,16 @@ function boxToBox(sx, sy, sw, sh, dx, dy, dw, dh) {
|
|
|
66
66
|
function rotateAboutPoint(cx, cy, rad) {
|
|
67
67
|
return multiply(translate(cx, cy), multiply(rotate(rad), translate(-cx, -cy)));
|
|
68
68
|
}
|
|
69
|
+
function transformCoords(coords, m) {
|
|
70
|
+
const out = new Float64Array(coords.length);
|
|
71
|
+
const a = m[0], b = m[1], c = m[2], d = m[3], e = m[4], f = m[5];
|
|
72
|
+
for (let i = 0; i + 1 < coords.length; i += 2) {
|
|
73
|
+
const x = coords[i], y = coords[i + 1];
|
|
74
|
+
out[i] = a * x + c * y + e;
|
|
75
|
+
out[i + 1] = b * x + d * y + f;
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
69
79
|
|
|
70
80
|
// src/nearest.ts
|
|
71
81
|
function nearestOnLine(px, py, x0, y0, x1, y1) {
|
|
@@ -397,18 +407,6 @@ function unionBoundsPath(paths) {
|
|
|
397
407
|
return { kind: "rect", x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
398
408
|
}
|
|
399
409
|
|
|
400
|
-
// src/affine.ts
|
|
401
|
-
function transformCoords(coords, m) {
|
|
402
|
-
const out = new Float64Array(coords.length);
|
|
403
|
-
const a = m[0], b = m[1], c = m[2], d = m[3], e = m[4], f = m[5];
|
|
404
|
-
for (let i = 0; i + 1 < coords.length; i += 2) {
|
|
405
|
-
const x = coords[i], y = coords[i + 1];
|
|
406
|
-
out[i] = a * x + c * y + e;
|
|
407
|
-
out[i + 1] = b * x + d * y + f;
|
|
408
|
-
}
|
|
409
|
-
return out;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
410
|
// src/paths/transformPath.ts
|
|
413
411
|
var AXIS_ALIGNED_EPSILON = 1e-9;
|
|
414
412
|
function transformPath(path, m) {
|
|
@@ -453,7 +451,7 @@ function translatePolygonCopy(path, dx, dy) {
|
|
|
453
451
|
next[ci + k + 1] = coords[ci + k + 1] + dy;
|
|
454
452
|
}
|
|
455
453
|
});
|
|
456
|
-
return {
|
|
454
|
+
return { ...path, coords: next };
|
|
457
455
|
}
|
|
458
456
|
function scalePathToBounds(path, target) {
|
|
459
457
|
if (path.kind === "rect") {
|
|
@@ -483,54 +481,74 @@ function pathOrigin(path) {
|
|
|
483
481
|
|
|
484
482
|
// src/paths/splitSubpaths.ts
|
|
485
483
|
function splitSubpaths(path) {
|
|
486
|
-
const { commands, coords
|
|
484
|
+
const { commands, coords } = path;
|
|
487
485
|
const out = [];
|
|
488
486
|
let cmdStart = 0;
|
|
489
487
|
let coordStart = 0;
|
|
490
488
|
forEachSegment(commands, coords, (cmd, ci, _px, _py, i) => {
|
|
491
489
|
if (cmd !== PATH_M || i <= cmdStart) return;
|
|
492
|
-
out.push({
|
|
493
|
-
kind: "polygon",
|
|
494
|
-
commands: commands.slice(cmdStart, i),
|
|
495
|
-
coords: coords.slice(coordStart, ci),
|
|
496
|
-
fillRule
|
|
497
|
-
});
|
|
490
|
+
out.push({ ...path, commands: commands.slice(cmdStart, i), coords: coords.slice(coordStart, ci) });
|
|
498
491
|
cmdStart = i;
|
|
499
492
|
coordStart = ci;
|
|
500
493
|
});
|
|
501
|
-
out.push({
|
|
502
|
-
kind: "polygon",
|
|
503
|
-
commands: commands.slice(cmdStart),
|
|
504
|
-
coords: coords.slice(coordStart),
|
|
505
|
-
fillRule
|
|
506
|
-
});
|
|
494
|
+
out.push({ ...path, commands: commands.slice(cmdStart), coords: coords.slice(coordStart) });
|
|
507
495
|
return out;
|
|
508
496
|
}
|
|
509
497
|
|
|
498
|
+
// src/paths/splitByClosure.ts
|
|
499
|
+
var endsInZ = (sub3) => sub3.commands[sub3.commands.length - 1] === PATH_Z;
|
|
500
|
+
function hasOpenSubpath(path) {
|
|
501
|
+
return path.kind === "polygon" && path.commands.length > 0 && splitSubpaths(path).some((sub3) => !endsInZ(sub3));
|
|
502
|
+
}
|
|
503
|
+
function splitByClosure(path) {
|
|
504
|
+
const closed = [];
|
|
505
|
+
const open = [];
|
|
506
|
+
if (path.commands.length > 0) {
|
|
507
|
+
for (const sub3 of splitSubpaths(path)) (endsInZ(sub3) ? closed : open).push(sub3);
|
|
508
|
+
}
|
|
509
|
+
const join = (subs) => {
|
|
510
|
+
if (subs.length === 0) return null;
|
|
511
|
+
const commands = new Uint8Array(subs.reduce((n, s) => n + s.commands.length, 0));
|
|
512
|
+
const coords = new Float32Array(subs.reduce((n, s) => n + s.coords.length, 0));
|
|
513
|
+
let ci = 0, xi = 0;
|
|
514
|
+
for (const s of subs) {
|
|
515
|
+
commands.set(s.commands, ci);
|
|
516
|
+
coords.set(s.coords, xi);
|
|
517
|
+
ci += s.commands.length;
|
|
518
|
+
xi += s.coords.length;
|
|
519
|
+
}
|
|
520
|
+
return { ...path, commands, coords };
|
|
521
|
+
};
|
|
522
|
+
return { closed: join(closed), open: join(open) };
|
|
523
|
+
}
|
|
524
|
+
|
|
510
525
|
// src/paths/winding.ts
|
|
511
526
|
var GL_T = [0.5 - Math.sqrt(15) / 10, 0.5, 0.5 + Math.sqrt(15) / 10];
|
|
512
527
|
var GL_W = [5 / 18, 8 / 18, 5 / 18];
|
|
513
528
|
function pathSignedArea(path) {
|
|
514
529
|
if (path.kind === "rect") return path.width * path.height;
|
|
515
530
|
const { commands, coords } = path;
|
|
531
|
+
const fillsOpen = fillsOpenSubpaths(path);
|
|
532
|
+
let total = 0;
|
|
516
533
|
let twice = 0;
|
|
517
534
|
let startX = 0, startY = 0;
|
|
518
535
|
let open = false;
|
|
519
536
|
let endX = 0, endY = 0;
|
|
520
|
-
const close = () => {
|
|
521
|
-
if (open)
|
|
537
|
+
const close = (drawn) => {
|
|
538
|
+
if (open && (drawn || fillsOpen)) total += twice + endX * startY - startX * endY;
|
|
539
|
+
twice = 0;
|
|
522
540
|
open = false;
|
|
523
541
|
};
|
|
524
542
|
forEachSegment(commands, coords, (cmd, ci, px, py) => {
|
|
525
543
|
if (cmd === PATH_M) {
|
|
526
|
-
close();
|
|
544
|
+
close(false);
|
|
527
545
|
startX = endX = coords[ci];
|
|
528
546
|
startY = endY = coords[ci + 1];
|
|
529
547
|
open = true;
|
|
530
548
|
return;
|
|
531
549
|
}
|
|
532
550
|
if (cmd === PATH_Z) {
|
|
533
|
-
close();
|
|
551
|
+
close(true);
|
|
534
552
|
endX = startX;
|
|
535
553
|
endY = startY;
|
|
536
554
|
return;
|
|
@@ -569,8 +587,8 @@ function pathSignedArea(path) {
|
|
|
569
587
|
endX = x3;
|
|
570
588
|
endY = y3;
|
|
571
589
|
});
|
|
572
|
-
close();
|
|
573
|
-
return
|
|
590
|
+
close(false);
|
|
591
|
+
return total / 2;
|
|
574
592
|
}
|
|
575
593
|
function reversePath(path) {
|
|
576
594
|
const cmds = [];
|
|
@@ -627,12 +645,7 @@ function reversePath(path) {
|
|
|
627
645
|
segs.push({ cmd, args: Array.from(coords.subarray(ci, ci + n)), fromX: px, fromY: py });
|
|
628
646
|
});
|
|
629
647
|
flush(false);
|
|
630
|
-
return {
|
|
631
|
-
kind: "polygon",
|
|
632
|
-
commands: new Uint8Array(cmds),
|
|
633
|
-
coords: new Float32Array(out),
|
|
634
|
-
fillRule: path.fillRule
|
|
635
|
-
};
|
|
648
|
+
return { ...path, commands: new Uint8Array(cmds), coords: new Float32Array(out) };
|
|
636
649
|
}
|
|
637
650
|
|
|
638
651
|
// src/paths/pathFromD.ts
|
|
@@ -1063,15 +1076,26 @@ function measureError(samples, fit) {
|
|
|
1063
1076
|
return { worstIndex, worstError };
|
|
1064
1077
|
}
|
|
1065
1078
|
function fitOneCubic(samples) {
|
|
1079
|
+
const n = samples.length;
|
|
1080
|
+
return fitWithTangents(
|
|
1081
|
+
samples,
|
|
1082
|
+
unit(sub2(samples[1], samples[0])),
|
|
1083
|
+
unit(sub2(samples[n - 2], samples[n - 1])),
|
|
1084
|
+
4
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
function fitCubicWithTangents(samples, tStart, tEnd, opts = {}) {
|
|
1088
|
+
const { cp1, cp2 } = fitWithTangents(samples, tStart, tEnd, opts.passes ?? 4, opts.params);
|
|
1089
|
+
return { cp1, cp2 };
|
|
1090
|
+
}
|
|
1091
|
+
function fitWithTangents(samples, tStart, tEnd, passes, params) {
|
|
1066
1092
|
const n = samples.length;
|
|
1067
1093
|
const a = samples[0];
|
|
1068
1094
|
const e = samples[n - 1];
|
|
1069
|
-
const
|
|
1070
|
-
const tEnd = unit(sub2(samples[n - 2], e));
|
|
1071
|
-
const ts = chordLengthParam(samples);
|
|
1095
|
+
const ts = params ? [...params] : chordLengthParam(samples);
|
|
1072
1096
|
if (ts === null) return { cp1: { ...a }, cp2: { ...e }, end: e, ts: samples.map(() => 0) };
|
|
1073
1097
|
let fit = solveLS(samples, ts, a, e, tStart, tEnd);
|
|
1074
|
-
for (let pass = 0; pass <
|
|
1098
|
+
for (let pass = 0; pass < passes; pass++) {
|
|
1075
1099
|
reparameterize(samples, ts, a, e, fit);
|
|
1076
1100
|
fit = solveLS(samples, ts, a, e, tStart, tEnd);
|
|
1077
1101
|
}
|
|
@@ -1174,6 +1198,865 @@ function emitStraightCubic(b, a, c) {
|
|
|
1174
1198
|
b.curveTo(cp1x, cp1y, cp2x, cp2y, c.x, c.y);
|
|
1175
1199
|
}
|
|
1176
1200
|
|
|
1201
|
+
// src/paths/spanFit.ts
|
|
1202
|
+
var SAMPLES_PER_SEGMENT = 16;
|
|
1203
|
+
var SPLIT_SEARCH_STEPS = 40;
|
|
1204
|
+
function fitCubicThroughDeletion(prev, removed, next) {
|
|
1205
|
+
const first = segmentControls(prev, removed);
|
|
1206
|
+
const second = segmentControls(removed, next);
|
|
1207
|
+
const samples = [...sampleCubic(first), ...sampleCubic(second).slice(1)];
|
|
1208
|
+
const tStart = startTangent(first);
|
|
1209
|
+
const tEnd = startTangent(reversed(second));
|
|
1210
|
+
const fitAt = (s) => {
|
|
1211
|
+
const params = splitParams(s);
|
|
1212
|
+
const fit = fitCubicWithTangents(samples, tStart, tEnd, { params, passes: 0 });
|
|
1213
|
+
return { fit, error: squaredError(samples, params, prev, fit.cp1, fit.cp2, next) };
|
|
1214
|
+
};
|
|
1215
|
+
let lo = 0.02;
|
|
1216
|
+
let hi = 0.98;
|
|
1217
|
+
const phi = (Math.sqrt(5) - 1) / 2;
|
|
1218
|
+
let m1 = hi - phi * (hi - lo);
|
|
1219
|
+
let m2 = lo + phi * (hi - lo);
|
|
1220
|
+
let e1 = fitAt(m1).error;
|
|
1221
|
+
let e2 = fitAt(m2).error;
|
|
1222
|
+
for (let i = 0; i < SPLIT_SEARCH_STEPS; i++) {
|
|
1223
|
+
if (e1 < e2) {
|
|
1224
|
+
hi = m2;
|
|
1225
|
+
m2 = m1;
|
|
1226
|
+
e2 = e1;
|
|
1227
|
+
m1 = hi - phi * (hi - lo);
|
|
1228
|
+
e1 = fitAt(m1).error;
|
|
1229
|
+
} else {
|
|
1230
|
+
lo = m1;
|
|
1231
|
+
m1 = m2;
|
|
1232
|
+
e1 = e2;
|
|
1233
|
+
m2 = lo + phi * (hi - lo);
|
|
1234
|
+
e2 = fitAt(m2).error;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
const { cp1, cp2 } = fitCubicWithTangents(samples, tStart, tEnd, {
|
|
1238
|
+
params: splitParams((lo + hi) / 2)
|
|
1239
|
+
});
|
|
1240
|
+
return { c1: cp1, c2: cp2 };
|
|
1241
|
+
}
|
|
1242
|
+
function splitParams(s) {
|
|
1243
|
+
const out = [];
|
|
1244
|
+
for (let i = 0; i <= SAMPLES_PER_SEGMENT; i++) out.push(s * i / SAMPLES_PER_SEGMENT);
|
|
1245
|
+
for (let i = 1; i <= SAMPLES_PER_SEGMENT; i++) out.push(s + (1 - s) * i / SAMPLES_PER_SEGMENT);
|
|
1246
|
+
return out;
|
|
1247
|
+
}
|
|
1248
|
+
function squaredError(samples, params, p0, p1, p2, p3) {
|
|
1249
|
+
let sum = 0;
|
|
1250
|
+
for (let i = 0; i < samples.length; i++) {
|
|
1251
|
+
const q = cubicPointAt(p0, p1, p2, p3, params[i]);
|
|
1252
|
+
sum += (q.x - samples[i].x) ** 2 + (q.y - samples[i].y) ** 2;
|
|
1253
|
+
}
|
|
1254
|
+
return sum;
|
|
1255
|
+
}
|
|
1256
|
+
function segmentControls(a, b) {
|
|
1257
|
+
return [
|
|
1258
|
+
{ x: a.x, y: a.y },
|
|
1259
|
+
a.outHandle ?? { x: a.x, y: a.y },
|
|
1260
|
+
b.inHandle ?? { x: b.x, y: b.y },
|
|
1261
|
+
{ x: b.x, y: b.y }
|
|
1262
|
+
];
|
|
1263
|
+
}
|
|
1264
|
+
function sampleCubic([p0, p1, p2, p3]) {
|
|
1265
|
+
const out = [];
|
|
1266
|
+
for (let i = 0; i <= SAMPLES_PER_SEGMENT; i++) {
|
|
1267
|
+
out.push(cubicPointAt(p0, p1, p2, p3, i / SAMPLES_PER_SEGMENT));
|
|
1268
|
+
}
|
|
1269
|
+
return out;
|
|
1270
|
+
}
|
|
1271
|
+
function reversed([p0, p1, p2, p3]) {
|
|
1272
|
+
return [p3, p2, p1, p0];
|
|
1273
|
+
}
|
|
1274
|
+
function startTangent([p0, ...rest]) {
|
|
1275
|
+
for (const p of rest) {
|
|
1276
|
+
const dx = p.x - p0.x;
|
|
1277
|
+
const dy = p.y - p0.y;
|
|
1278
|
+
const len = Math.hypot(dx, dy);
|
|
1279
|
+
if (len > 1e-9) return { x: dx / len, y: dy / len };
|
|
1280
|
+
}
|
|
1281
|
+
return { x: 0, y: 0 };
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// src/paths/anchors.ts
|
|
1285
|
+
function pathToAnchors(path) {
|
|
1286
|
+
const { commands, coords } = path;
|
|
1287
|
+
const anchors = [];
|
|
1288
|
+
const closed = [];
|
|
1289
|
+
let current = null;
|
|
1290
|
+
forEachSegment(commands, coords, (cmd, ci) => {
|
|
1291
|
+
switch (cmd) {
|
|
1292
|
+
case PATH_M: {
|
|
1293
|
+
if (current) {
|
|
1294
|
+
anchors.push(current);
|
|
1295
|
+
closed.push(false);
|
|
1296
|
+
}
|
|
1297
|
+
current = [{ x: coords[ci], y: coords[ci + 1] }];
|
|
1298
|
+
break;
|
|
1299
|
+
}
|
|
1300
|
+
case PATH_L: {
|
|
1301
|
+
if (!current) throw new Error("pathToAnchors: L without prior M");
|
|
1302
|
+
current.push({ x: coords[ci], y: coords[ci + 1] });
|
|
1303
|
+
break;
|
|
1304
|
+
}
|
|
1305
|
+
case PATH_C: {
|
|
1306
|
+
if (!current) throw new Error("pathToAnchors: C without prior M");
|
|
1307
|
+
const prev = current[current.length - 1];
|
|
1308
|
+
prev.outHandle = { x: coords[ci], y: coords[ci + 1] };
|
|
1309
|
+
current.push({
|
|
1310
|
+
x: coords[ci + 4],
|
|
1311
|
+
y: coords[ci + 5],
|
|
1312
|
+
inHandle: { x: coords[ci + 2], y: coords[ci + 3] }
|
|
1313
|
+
});
|
|
1314
|
+
break;
|
|
1315
|
+
}
|
|
1316
|
+
case PATH_Q: {
|
|
1317
|
+
if (!current) throw new Error("pathToAnchors: Q without prior M");
|
|
1318
|
+
const handle = { x: coords[ci], y: coords[ci + 1] };
|
|
1319
|
+
const prev = current[current.length - 1];
|
|
1320
|
+
prev.outHandle = handle;
|
|
1321
|
+
current.push({ x: coords[ci + 2], y: coords[ci + 3], inHandle: { ...handle } });
|
|
1322
|
+
break;
|
|
1323
|
+
}
|
|
1324
|
+
case PATH_Z: {
|
|
1325
|
+
if (current) {
|
|
1326
|
+
foldClosingCurve(current);
|
|
1327
|
+
anchors.push(current);
|
|
1328
|
+
closed.push(true);
|
|
1329
|
+
current = null;
|
|
1330
|
+
}
|
|
1331
|
+
break;
|
|
1332
|
+
}
|
|
1333
|
+
default:
|
|
1334
|
+
throw new Error(`pathToAnchors: unknown command ${cmd}`);
|
|
1335
|
+
}
|
|
1336
|
+
});
|
|
1337
|
+
if (current) {
|
|
1338
|
+
anchors.push(current);
|
|
1339
|
+
closed.push(false);
|
|
1340
|
+
}
|
|
1341
|
+
return { anchors, closed };
|
|
1342
|
+
}
|
|
1343
|
+
function foldClosingCurve(sub3) {
|
|
1344
|
+
if (sub3.length < 2) return;
|
|
1345
|
+
const first = sub3[0];
|
|
1346
|
+
const last = sub3[sub3.length - 1];
|
|
1347
|
+
if (!last.inHandle || last.x !== first.x || last.y !== first.y) return;
|
|
1348
|
+
sub3.pop();
|
|
1349
|
+
first.inHandle = last.inHandle;
|
|
1350
|
+
}
|
|
1351
|
+
function anchorsToPath(anchors, closed, like) {
|
|
1352
|
+
const b = new PathBuilder();
|
|
1353
|
+
for (let s = 0; s < anchors.length; s++) {
|
|
1354
|
+
const sub3 = anchors[s];
|
|
1355
|
+
if (sub3.length === 0) continue;
|
|
1356
|
+
b.moveTo(sub3[0].x, sub3[0].y);
|
|
1357
|
+
for (let i = 1; i < sub3.length; i++) {
|
|
1358
|
+
const prev = sub3[i - 1];
|
|
1359
|
+
const cur = sub3[i];
|
|
1360
|
+
const hasHandle = prev.outHandle != null || cur.inHandle != null;
|
|
1361
|
+
if (!hasHandle) {
|
|
1362
|
+
b.lineTo(cur.x, cur.y);
|
|
1363
|
+
} else {
|
|
1364
|
+
const c1 = prev.outHandle ?? { x: prev.x, y: prev.y };
|
|
1365
|
+
const c2 = cur.inHandle ?? { x: cur.x, y: cur.y };
|
|
1366
|
+
b.curveTo(c1.x, c1.y, c2.x, c2.y, cur.x, cur.y);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
if (closed[s]) {
|
|
1370
|
+
const last = sub3[sub3.length - 1];
|
|
1371
|
+
const first = sub3[0];
|
|
1372
|
+
const hasHandle = last.outHandle != null || first.inHandle != null;
|
|
1373
|
+
if (hasHandle) {
|
|
1374
|
+
const c1 = last.outHandle ?? { x: last.x, y: last.y };
|
|
1375
|
+
const c2 = first.inHandle ?? { x: first.x, y: first.y };
|
|
1376
|
+
b.curveTo(c1.x, c1.y, c2.x, c2.y, first.x, first.y);
|
|
1377
|
+
}
|
|
1378
|
+
b.close();
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
const built = b.build();
|
|
1382
|
+
return like ? { ...like, commands: built.commands, coords: built.coords } : built;
|
|
1383
|
+
}
|
|
1384
|
+
var SMOOTH_THRESHOLD = 1e-3;
|
|
1385
|
+
function isAnchorSmooth(a) {
|
|
1386
|
+
if (!a.inHandle || !a.outHandle) return false;
|
|
1387
|
+
const inDX = a.inHandle.x - a.x;
|
|
1388
|
+
const inDY = a.inHandle.y - a.y;
|
|
1389
|
+
const outDX = a.outHandle.x - a.x;
|
|
1390
|
+
const outDY = a.outHandle.y - a.y;
|
|
1391
|
+
const inLen = Math.hypot(inDX, inDY);
|
|
1392
|
+
const outLen = Math.hypot(outDX, outDY);
|
|
1393
|
+
if (inLen === 0 || outLen === 0) return false;
|
|
1394
|
+
const cross2 = (inDX * outDY - inDY * outDX) / (inLen * outLen);
|
|
1395
|
+
return Math.abs(cross2) < SMOOTH_THRESHOLD;
|
|
1396
|
+
}
|
|
1397
|
+
function countPathAnchors(path) {
|
|
1398
|
+
if (path.kind === "rect") return 4;
|
|
1399
|
+
const cmds = path.commands;
|
|
1400
|
+
let n = 0;
|
|
1401
|
+
for (let i = 0; i < cmds.length; i++) {
|
|
1402
|
+
const c = cmds[i];
|
|
1403
|
+
if (c === PATH_M || c === PATH_L || c === PATH_C || c === PATH_Q) n++;
|
|
1404
|
+
}
|
|
1405
|
+
return n;
|
|
1406
|
+
}
|
|
1407
|
+
function segmentEnds(anchors, closed, sub3, segIdx) {
|
|
1408
|
+
const s = anchors[sub3];
|
|
1409
|
+
if (!s || segIdx < 0) return null;
|
|
1410
|
+
if (segIdx + 1 < s.length) return { a: s[segIdx], b: s[segIdx + 1] };
|
|
1411
|
+
if (closed?.[sub3] && s.length > 1 && segIdx === s.length - 1) return { a: s[segIdx], b: s[0] };
|
|
1412
|
+
return null;
|
|
1413
|
+
}
|
|
1414
|
+
function isStraightSegment(a, b) {
|
|
1415
|
+
return a.outHandle == null && b.inHandle == null;
|
|
1416
|
+
}
|
|
1417
|
+
function nearestOnSegment(a, b, x, y) {
|
|
1418
|
+
const p1 = a.outHandle ?? a;
|
|
1419
|
+
const p2 = b.inHandle ?? b;
|
|
1420
|
+
const hit = isStraightSegment(a, b) ? nearestOnLine(x, y, a.x, a.y, b.x, b.y) : nearestOnCubic(x, y, a.x, a.y, p1.x, p1.y, p2.x, p2.y, b.x, b.y);
|
|
1421
|
+
return { t: hit.t, d2: hit.dist * hit.dist };
|
|
1422
|
+
}
|
|
1423
|
+
function nearestSegmentT(anchors, wx, wy, closed) {
|
|
1424
|
+
let bestD2 = Infinity;
|
|
1425
|
+
let best = null;
|
|
1426
|
+
for (let s = 0; s < anchors.length; s++) {
|
|
1427
|
+
const segCount = anchors[s].length - (closed?.[s] ? 0 : 1);
|
|
1428
|
+
for (let i = 0; i < segCount; i++) {
|
|
1429
|
+
const ends = segmentEnds(anchors, closed, s, i);
|
|
1430
|
+
if (!ends) continue;
|
|
1431
|
+
const hit = nearestOnSegment(ends.a, ends.b, wx, wy);
|
|
1432
|
+
if (hit.d2 < bestD2) {
|
|
1433
|
+
bestD2 = hit.d2;
|
|
1434
|
+
best = { sub: s, segIdx: i, t: hit.t };
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
return best;
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
// src/paths/simplifyPath.ts
|
|
1442
|
+
var CUBIC_SAMPLES2 = 12;
|
|
1443
|
+
var MAX_LINE_SAMPLES = 8;
|
|
1444
|
+
var LONG_STRAIGHT = 4;
|
|
1445
|
+
function simplifyPath(path, tolerance, opts = {}) {
|
|
1446
|
+
const tol = Math.max(tolerance, 1e-6);
|
|
1447
|
+
const cosCorner = Math.cos((opts.cornerAngle ?? 40) * Math.PI / 180);
|
|
1448
|
+
const { anchors, closed } = pathToAnchors(path);
|
|
1449
|
+
const out = [];
|
|
1450
|
+
const outClosed = [];
|
|
1451
|
+
anchors.forEach((sub3, s) => {
|
|
1452
|
+
if (sub3.length < 2) return;
|
|
1453
|
+
out.push(simplifySubpath(sub3, closed[s], tol, cosCorner));
|
|
1454
|
+
outClosed.push(closed[s]);
|
|
1455
|
+
});
|
|
1456
|
+
return anchorsToPath(out, outClosed, path);
|
|
1457
|
+
}
|
|
1458
|
+
function simplifySubpath(sub3, closed, tol, cosCorner) {
|
|
1459
|
+
const n = sub3.length;
|
|
1460
|
+
const breaks = /* @__PURE__ */ new Set();
|
|
1461
|
+
for (let i = 0; i < n; i++) {
|
|
1462
|
+
if (isCorner(sub3, i, closed, cosCorner)) breaks.add(i);
|
|
1463
|
+
if (isLongStraight(sub3, i, closed)) {
|
|
1464
|
+
breaks.add(i);
|
|
1465
|
+
breaks.add((i + 1) % n);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
const corners = [...breaks].sort((a, b) => a - b);
|
|
1469
|
+
const result = [];
|
|
1470
|
+
if (closed && corners.length === 0) {
|
|
1471
|
+
const pts = sampleRun(sub3, 0, n, tol);
|
|
1472
|
+
const t = unit2(sub22(pts[1], pts[pts.length - 2]));
|
|
1473
|
+
fitRun(pts, t, neg(t), tol, result, true);
|
|
1474
|
+
return result;
|
|
1475
|
+
}
|
|
1476
|
+
const stops = closed ? [...corners, corners[0] + n] : corners;
|
|
1477
|
+
for (let k = 0; k + 1 < stops.length; k++) {
|
|
1478
|
+
const pts = sampleRun(sub3, stops[k], stops[k + 1], tol);
|
|
1479
|
+
const last = closed && k + 2 === stops.length;
|
|
1480
|
+
fitRun(pts, unit2(sub22(pts[1], pts[0])), unit2(sub22(pts[pts.length - 2], pts[pts.length - 1])), tol, result, last);
|
|
1481
|
+
}
|
|
1482
|
+
return result;
|
|
1483
|
+
}
|
|
1484
|
+
function isCorner(sub3, i, closed, cosCorner) {
|
|
1485
|
+
const n = sub3.length;
|
|
1486
|
+
const a = sub3[i];
|
|
1487
|
+
const prev = sub3[(i - 1 + n) % n];
|
|
1488
|
+
const next = sub3[(i + 1) % n];
|
|
1489
|
+
if (!closed && (i === 0 || i === n - 1)) return true;
|
|
1490
|
+
const from = a.inHandle && !same(a.inHandle, a) ? a.inHandle : prev.outHandle && !same(prev.outHandle, a) ? prev.outHandle : prev;
|
|
1491
|
+
const to = a.outHandle && !same(a.outHandle, a) ? a.outHandle : next.inHandle && !same(next.inHandle, a) ? next.inHandle : next;
|
|
1492
|
+
const d1 = unit2(sub22(a, from));
|
|
1493
|
+
const d2 = unit2(sub22(to, a));
|
|
1494
|
+
return d1.x * d2.x + d1.y * d2.y < cosCorner;
|
|
1495
|
+
}
|
|
1496
|
+
function isLongStraight(sub3, i, closed) {
|
|
1497
|
+
const n = sub3.length;
|
|
1498
|
+
const len = (k) => {
|
|
1499
|
+
if (!closed && (k < 0 || k >= n - 1)) return 0;
|
|
1500
|
+
const a2 = sub3[(k + n) % n];
|
|
1501
|
+
const b2 = sub3[(k + 1) % n];
|
|
1502
|
+
return Math.hypot(b2.x - a2.x, b2.y - a2.y);
|
|
1503
|
+
};
|
|
1504
|
+
if (!closed && i >= n - 1) return false;
|
|
1505
|
+
const a = sub3[i];
|
|
1506
|
+
const b = sub3[(i + 1) % n];
|
|
1507
|
+
if (a.outHandle || b.inHandle) return false;
|
|
1508
|
+
return len(i) >= LONG_STRAIGHT * Math.max(len(i - 1), len(i + 1));
|
|
1509
|
+
}
|
|
1510
|
+
function sampleRun(sub3, from, to, tol) {
|
|
1511
|
+
const n = sub3.length;
|
|
1512
|
+
const pts = [{ x: sub3[from % n].x, y: sub3[from % n].y }];
|
|
1513
|
+
for (let i = from; i < to; i++) {
|
|
1514
|
+
const a = sub3[i % n];
|
|
1515
|
+
const b = sub3[(i + 1) % n];
|
|
1516
|
+
if (!a.outHandle && !b.inHandle) {
|
|
1517
|
+
const len = Math.hypot(b.x - a.x, b.y - a.y);
|
|
1518
|
+
const steps = Math.min(MAX_LINE_SAMPLES, Math.max(1, Math.floor(len / (4 * tol))));
|
|
1519
|
+
for (let k = 1; k <= steps; k++) pts.push({ x: a.x + (b.x - a.x) * k / steps, y: a.y + (b.y - a.y) * k / steps });
|
|
1520
|
+
} else {
|
|
1521
|
+
const c1 = a.outHandle ?? a;
|
|
1522
|
+
const c2 = b.inHandle ?? b;
|
|
1523
|
+
for (let k = 1; k <= CUBIC_SAMPLES2; k++) pts.push(cubicPointAt(a, c1, c2, b, k / CUBIC_SAMPLES2));
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
return dedupe(pts);
|
|
1527
|
+
}
|
|
1528
|
+
function fitRun(pts, tStart, tEnd, tol, out, closing) {
|
|
1529
|
+
if (out.length === 0) out.push({ x: pts[0].x, y: pts[0].y });
|
|
1530
|
+
const segs = [];
|
|
1531
|
+
fitRecursive2(pts, tStart, tEnd, tol, segs);
|
|
1532
|
+
segs.forEach((seg, i) => {
|
|
1533
|
+
const prev = out[out.length - 1];
|
|
1534
|
+
if (seg.c1) prev.outHandle = seg.c1;
|
|
1535
|
+
const isLast = i === segs.length - 1;
|
|
1536
|
+
if (closing && isLast) {
|
|
1537
|
+
if (seg.c2) out[0].inHandle = seg.c2;
|
|
1538
|
+
return;
|
|
1539
|
+
}
|
|
1540
|
+
out.push(seg.c2 ? { x: seg.end.x, y: seg.end.y, inHandle: seg.c2 } : { x: seg.end.x, y: seg.end.y });
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
function fitRecursive2(pts, tStart, tEnd, tol, out) {
|
|
1544
|
+
const a = pts[0];
|
|
1545
|
+
const e = pts[pts.length - 1];
|
|
1546
|
+
if (pts.length <= 2 || worstFromLine(pts, a, e).dist <= tol) {
|
|
1547
|
+
out.push({ end: e });
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
const { cp1, cp2 } = fitCubicWithTangents(pts, tStart, tEnd);
|
|
1551
|
+
const worst = worstFromCubic(pts, a, cp1, cp2, e);
|
|
1552
|
+
if (worst.dist <= tol) {
|
|
1553
|
+
out.push({ c1: cp1, c2: cp2, end: e });
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
const k = worst.index;
|
|
1557
|
+
const center = unit2(sub22(pts[k + 1], pts[k - 1]));
|
|
1558
|
+
fitRecursive2(pts.slice(0, k + 1), tStart, neg(center), tol, out);
|
|
1559
|
+
fitRecursive2(pts.slice(k), center, tEnd, tol, out);
|
|
1560
|
+
}
|
|
1561
|
+
function worstFromLine(pts, a, e) {
|
|
1562
|
+
let index = 1, dist2 = 0;
|
|
1563
|
+
for (let i = 1; i < pts.length - 1; i++) {
|
|
1564
|
+
const d = nearestOnLine(pts[i].x, pts[i].y, a.x, a.y, e.x, e.y).dist;
|
|
1565
|
+
if (d > dist2) {
|
|
1566
|
+
dist2 = d;
|
|
1567
|
+
index = i;
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
return { index, dist: dist2 };
|
|
1571
|
+
}
|
|
1572
|
+
function worstFromCubic(pts, a, c1, c2, e) {
|
|
1573
|
+
let index = 1, dist2 = 0;
|
|
1574
|
+
for (let i = 1; i < pts.length - 1; i++) {
|
|
1575
|
+
const d = nearestOnCubic(pts[i].x, pts[i].y, a.x, a.y, c1.x, c1.y, c2.x, c2.y, e.x, e.y).dist;
|
|
1576
|
+
if (d > dist2) {
|
|
1577
|
+
dist2 = d;
|
|
1578
|
+
index = i;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
return { index, dist: dist2 };
|
|
1582
|
+
}
|
|
1583
|
+
function dedupe(pts) {
|
|
1584
|
+
return pts.filter((p, i) => i === 0 || !same(p, pts[i - 1]));
|
|
1585
|
+
}
|
|
1586
|
+
var same = (p, q) => p.x === q.x && p.y === q.y;
|
|
1587
|
+
var sub22 = (p, q) => ({ x: p.x - q.x, y: p.y - q.y });
|
|
1588
|
+
var neg = (p) => ({ x: -p.x, y: -p.y });
|
|
1589
|
+
function unit2(p) {
|
|
1590
|
+
const len = Math.hypot(p.x, p.y);
|
|
1591
|
+
return len === 0 ? { x: 0, y: 0 } : { x: p.x / len, y: p.y / len };
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
// src/pxExtent.ts
|
|
1595
|
+
function pxExtent(px, scale3, rotation = 0) {
|
|
1596
|
+
if (!rotation) {
|
|
1597
|
+
return {
|
|
1598
|
+
x: px / Math.abs(scale3.x),
|
|
1599
|
+
y: px / Math.abs(scale3.y)
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
const c = Math.cos(rotation), s = Math.sin(rotation);
|
|
1603
|
+
return {
|
|
1604
|
+
x: px * Math.hypot(c / scale3.x, s / scale3.y),
|
|
1605
|
+
y: px * Math.hypot(s / scale3.x, c / scale3.y)
|
|
1606
|
+
};
|
|
1607
|
+
}
|
|
1608
|
+
function scaleDelta(dx, dy, scale3) {
|
|
1609
|
+
return { x: dx * scale3.x, y: dy * scale3.y };
|
|
1610
|
+
}
|
|
1611
|
+
function withinPxBox(dx, dy, px, scale3) {
|
|
1612
|
+
const s = scaleDelta(dx, dy, scale3);
|
|
1613
|
+
return Math.abs(s.x) <= px && Math.abs(s.y) <= px;
|
|
1614
|
+
}
|
|
1615
|
+
function withinPxRadius(dx, dy, px, scale3) {
|
|
1616
|
+
const s = scaleDelta(dx, dy, scale3);
|
|
1617
|
+
return s.x * s.x + s.y * s.y <= px * px;
|
|
1618
|
+
}
|
|
1619
|
+
function standoff(anchor, normal, px, scale3) {
|
|
1620
|
+
const k = Math.sign(scale3.x * scale3.y) || 1;
|
|
1621
|
+
let nx = normal.x * scale3.y * k;
|
|
1622
|
+
let ny = normal.y * scale3.x * k;
|
|
1623
|
+
const l = Math.hypot(nx, ny) || 1;
|
|
1624
|
+
nx /= l;
|
|
1625
|
+
ny /= l;
|
|
1626
|
+
return { x: anchor.x + nx * px / scale3.x, y: anchor.y + ny * px / scale3.y, nx, ny };
|
|
1627
|
+
}
|
|
1628
|
+
function screenAngleOf(rotation, scale3) {
|
|
1629
|
+
return Math.atan2(Math.sin(rotation) * scale3.y, Math.cos(rotation) * scale3.x);
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
// src/paths/anchorOrigins.ts
|
|
1633
|
+
var at = (x) => ({ x, y: x });
|
|
1634
|
+
var keyOf = (p) => `${p.x},${p.y}`;
|
|
1635
|
+
function heldBy(path, set) {
|
|
1636
|
+
const coords = new Float32Array(path.coords.length);
|
|
1637
|
+
for (let i = 0; i < coords.length; i++) coords[i] = i >> 1;
|
|
1638
|
+
const indexed = pathToAnchors({ ...path, coords }).anchors;
|
|
1639
|
+
return indexed.map((sub3, s) => {
|
|
1640
|
+
if (sub3.length === set.anchors[s].length + 1) sub3[0].inHandle = sub3.pop().inHandle;
|
|
1641
|
+
return sub3.map((a) => ({ point: a.x, in: a.inHandle?.x, out: a.outHandle?.x }));
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1644
|
+
function writtenFrom(set) {
|
|
1645
|
+
let flat = 0;
|
|
1646
|
+
const coded = set.anchors.map((sub3) => sub3.map((a) => {
|
|
1647
|
+
const k = 3 * flat++;
|
|
1648
|
+
return {
|
|
1649
|
+
...at(k),
|
|
1650
|
+
...a.inHandle ? { inHandle: at(k + 1) } : {},
|
|
1651
|
+
...a.outHandle ? { outHandle: at(k + 2) } : {}
|
|
1652
|
+
};
|
|
1653
|
+
}));
|
|
1654
|
+
return anchorsToPath(coded, set.closed).coords;
|
|
1655
|
+
}
|
|
1656
|
+
function neighbor(sub3, closed, i, step) {
|
|
1657
|
+
for (let n = 1; n < sub3.length; n++) {
|
|
1658
|
+
let j = i + step * n;
|
|
1659
|
+
if (j < 0 || j >= sub3.length) {
|
|
1660
|
+
if (!closed) return -1;
|
|
1661
|
+
j = (j + sub3.length) % sub3.length;
|
|
1662
|
+
}
|
|
1663
|
+
if (sub3[j]) return j;
|
|
1664
|
+
}
|
|
1665
|
+
return -1;
|
|
1666
|
+
}
|
|
1667
|
+
function blended(anchors, held, closed, i) {
|
|
1668
|
+
const p = neighbor(held, closed, i, -1);
|
|
1669
|
+
const n = neighbor(held, closed, i, 1);
|
|
1670
|
+
if (p === -1 && n === -1) return null;
|
|
1671
|
+
const a = held[p === -1 ? n : p].point;
|
|
1672
|
+
const b = held[n === -1 ? p : n].point;
|
|
1673
|
+
if (p === -1 || n === -1 || a === b) return { a, b: a, t: 0 };
|
|
1674
|
+
const dp = Math.hypot(anchors[i].x - anchors[p].x, anchors[i].y - anchors[p].y);
|
|
1675
|
+
const dn = Math.hypot(anchors[i].x - anchors[n].x, anchors[i].y - anchors[n].y);
|
|
1676
|
+
return { a, b, t: dp + dn > 0 ? dp / (dp + dn) : 0 };
|
|
1677
|
+
}
|
|
1678
|
+
function copied(anchor, byPlace) {
|
|
1679
|
+
const was = byPlace.get(keyOf(anchor));
|
|
1680
|
+
if (!was) return void 0;
|
|
1681
|
+
const handle = (h) => h ? byPlace.get(`${keyOf(anchor)}:${keyOf(h)}`)?.point : void 0;
|
|
1682
|
+
return { point: was.point, in: handle(anchor.inHandle), out: handle(anchor.outHandle) };
|
|
1683
|
+
}
|
|
1684
|
+
function editAnchorSetTracked(path, edit) {
|
|
1685
|
+
const set = pathToAnchors(path);
|
|
1686
|
+
const byAnchor = /* @__PURE__ */ new Map();
|
|
1687
|
+
const byPlace = /* @__PURE__ */ new Map();
|
|
1688
|
+
heldBy(path, set).forEach((sub3, s) => sub3.forEach((held2, i) => {
|
|
1689
|
+
const anchor = set.anchors[s][i];
|
|
1690
|
+
byAnchor.set(anchor, held2);
|
|
1691
|
+
if (byPlace.has(keyOf(anchor))) return;
|
|
1692
|
+
if (anchor.inHandle) byPlace.set(`${keyOf(anchor)}:${keyOf(anchor.inHandle)}`, { point: held2.in });
|
|
1693
|
+
if (anchor.outHandle) byPlace.set(`${keyOf(anchor)}:${keyOf(anchor.outHandle)}`, { point: held2.out });
|
|
1694
|
+
byPlace.set(keyOf(anchor), { point: held2.point });
|
|
1695
|
+
}));
|
|
1696
|
+
if (edit(set) === false) return null;
|
|
1697
|
+
const held = [];
|
|
1698
|
+
const own = [];
|
|
1699
|
+
set.anchors.forEach((sub3, s) => {
|
|
1700
|
+
const subHeld = sub3.map((a) => byAnchor.get(a) ?? copied(a, byPlace));
|
|
1701
|
+
sub3.forEach((_, i) => {
|
|
1702
|
+
const h = subHeld[i];
|
|
1703
|
+
held.push(h);
|
|
1704
|
+
own.push(h ? { a: h.point, b: h.point, t: 0 } : blended(sub3, subHeld, set.closed[s], i));
|
|
1705
|
+
});
|
|
1706
|
+
});
|
|
1707
|
+
const origins = Array.from(writtenFrom(set).filter((_, i) => i % 2 === 0), (code) => {
|
|
1708
|
+
const anchor = Math.floor(code / 3);
|
|
1709
|
+
const handle = code % 3 === 1 ? held[anchor]?.in : code % 3 === 2 ? held[anchor]?.out : void 0;
|
|
1710
|
+
return handle === void 0 ? own[anchor] : { a: handle, b: handle, t: 0 };
|
|
1711
|
+
});
|
|
1712
|
+
return { path: anchorsToPath(set.anchors, set.closed, path), origins };
|
|
1713
|
+
}
|
|
1714
|
+
function remapPointValues(values, origins) {
|
|
1715
|
+
return origins.map((o) => {
|
|
1716
|
+
if (!o) return 0;
|
|
1717
|
+
const a = values[o.a] ?? 0;
|
|
1718
|
+
return o.t === 0 ? a : a + ((values[o.b] ?? 0) - a) * o.t;
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
// src/paths/anchorEdits.ts
|
|
1723
|
+
function anchorCount(set) {
|
|
1724
|
+
let n = 0;
|
|
1725
|
+
for (const sub3 of set.anchors) n += sub3.length;
|
|
1726
|
+
return n;
|
|
1727
|
+
}
|
|
1728
|
+
function locateAnchor(set, flatIndex) {
|
|
1729
|
+
if (flatIndex < 0) return null;
|
|
1730
|
+
let remaining = flatIndex;
|
|
1731
|
+
for (let sub3 = 0; sub3 < set.anchors.length; sub3++) {
|
|
1732
|
+
const len = set.anchors[sub3].length;
|
|
1733
|
+
if (remaining < len) return { sub: sub3, idx: remaining };
|
|
1734
|
+
remaining -= len;
|
|
1735
|
+
}
|
|
1736
|
+
return null;
|
|
1737
|
+
}
|
|
1738
|
+
function flatAnchorIndex(set, sub3, idx) {
|
|
1739
|
+
if (sub3 < 0 || sub3 >= set.anchors.length) return -1;
|
|
1740
|
+
if (idx < 0 || idx >= set.anchors[sub3].length) return -1;
|
|
1741
|
+
let base = 0;
|
|
1742
|
+
for (let s = 0; s < sub3; s++) base += set.anchors[s].length;
|
|
1743
|
+
return base + idx;
|
|
1744
|
+
}
|
|
1745
|
+
function anchorAt(set, flatIndex) {
|
|
1746
|
+
const loc = locateAnchor(set, flatIndex);
|
|
1747
|
+
if (!loc) return null;
|
|
1748
|
+
return set.anchors[loc.sub][loc.idx] ?? null;
|
|
1749
|
+
}
|
|
1750
|
+
function editAnchorSet(path, edit) {
|
|
1751
|
+
return editAnchorSetTracked(path, edit)?.path ?? null;
|
|
1752
|
+
}
|
|
1753
|
+
function translateAnchorBy(set, flatIndex, dx, dy) {
|
|
1754
|
+
const a = anchorAt(set, flatIndex);
|
|
1755
|
+
if (!a) return false;
|
|
1756
|
+
a.x += dx;
|
|
1757
|
+
a.y += dy;
|
|
1758
|
+
if (a.inHandle) {
|
|
1759
|
+
a.inHandle.x += dx;
|
|
1760
|
+
a.inHandle.y += dy;
|
|
1761
|
+
}
|
|
1762
|
+
if (a.outHandle) {
|
|
1763
|
+
a.outHandle.x += dx;
|
|
1764
|
+
a.outHandle.y += dy;
|
|
1765
|
+
}
|
|
1766
|
+
return true;
|
|
1767
|
+
}
|
|
1768
|
+
function moveHandleTo(set, flatIndex, side, toX, toY, breakSmoothness) {
|
|
1769
|
+
const a = anchorAt(set, flatIndex);
|
|
1770
|
+
if (!a) return false;
|
|
1771
|
+
const wasSmooth = isAnchorSmooth(a);
|
|
1772
|
+
const target = { x: toX, y: toY };
|
|
1773
|
+
if (side === "out") a.outHandle = target;
|
|
1774
|
+
else a.inHandle = target;
|
|
1775
|
+
const oppositeSide = side === "out" ? "in" : "out";
|
|
1776
|
+
const opposite = oppositeSide === "in" ? a.inHandle : a.outHandle;
|
|
1777
|
+
if (!breakSmoothness && wasSmooth && opposite) {
|
|
1778
|
+
const mirrored = { x: 2 * a.x - target.x, y: 2 * a.y - target.y };
|
|
1779
|
+
if (oppositeSide === "in") a.inHandle = mirrored;
|
|
1780
|
+
else a.outHandle = mirrored;
|
|
1781
|
+
}
|
|
1782
|
+
return true;
|
|
1783
|
+
}
|
|
1784
|
+
function insertAnchorOnSegment(set, args) {
|
|
1785
|
+
const ends = segmentEnds(set.anchors, set.closed, args.sub, args.segIdx);
|
|
1786
|
+
if (!ends) return -1;
|
|
1787
|
+
const { a, b } = ends;
|
|
1788
|
+
const at2 = args.segIdx + 1;
|
|
1789
|
+
if (isStraightSegment(a, b)) {
|
|
1790
|
+
set.anchors[args.sub].splice(at2, 0, {
|
|
1791
|
+
x: a.x + (b.x - a.x) * args.t,
|
|
1792
|
+
y: a.y + (b.y - a.y) * args.t
|
|
1793
|
+
});
|
|
1794
|
+
return flatAnchorIndex(set, args.sub, at2);
|
|
1795
|
+
}
|
|
1796
|
+
const p0 = a, p1 = a.outHandle ?? a, p2 = b.inHandle ?? b, p3 = b;
|
|
1797
|
+
const { left, right } = splitCubicAtT(p0, p1, p2, p3, args.t);
|
|
1798
|
+
a.outHandle = { x: left[1].x, y: left[1].y };
|
|
1799
|
+
b.inHandle = { x: right[2].x, y: right[2].y };
|
|
1800
|
+
set.anchors[args.sub].splice(at2, 0, {
|
|
1801
|
+
x: left[3].x,
|
|
1802
|
+
y: left[3].y,
|
|
1803
|
+
inHandle: { x: left[2].x, y: left[2].y },
|
|
1804
|
+
outHandle: { x: right[1].x, y: right[1].y }
|
|
1805
|
+
});
|
|
1806
|
+
return flatAnchorIndex(set, args.sub, at2);
|
|
1807
|
+
}
|
|
1808
|
+
function segmentAt(set, x, y, opts) {
|
|
1809
|
+
const scale3 = opts.scale ?? { x: 1, y: 1 };
|
|
1810
|
+
const onScreen = (p) => ({ x: p.x * scale3.x, y: p.y * scale3.y });
|
|
1811
|
+
const screenAnchors = set.anchors.map((sub3) => sub3.map((a) => ({
|
|
1812
|
+
...onScreen(a),
|
|
1813
|
+
...a.inHandle ? { inHandle: onScreen(a.inHandle) } : {},
|
|
1814
|
+
...a.outHandle ? { outHandle: onScreen(a.outHandle) } : {}
|
|
1815
|
+
})));
|
|
1816
|
+
const hit = nearestSegmentT(screenAnchors, x * scale3.x, y * scale3.y, set.closed);
|
|
1817
|
+
if (!hit) return null;
|
|
1818
|
+
const ends = segmentEnds(set.anchors, set.closed, hit.sub, hit.segIdx);
|
|
1819
|
+
const point = pointOnSegment(ends.a, ends.b, hit.t);
|
|
1820
|
+
const tol = opts.tolerancePx;
|
|
1821
|
+
if (!withinPxRadius(point.x - x, point.y - y, tol, scale3)) return null;
|
|
1822
|
+
if (withinPxRadius(point.x - ends.a.x, point.y - ends.a.y, tol, scale3)) return null;
|
|
1823
|
+
if (withinPxRadius(point.x - ends.b.x, point.y - ends.b.y, tol, scale3)) return null;
|
|
1824
|
+
return { sub: hit.sub, segIdx: hit.segIdx, t: hit.t, x: point.x, y: point.y };
|
|
1825
|
+
}
|
|
1826
|
+
function pointOnSegment(a, b, t) {
|
|
1827
|
+
if (isStraightSegment(a, b)) return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };
|
|
1828
|
+
return cubicPointAt(a, a.outHandle ?? a, b.inHandle ?? b, b, t);
|
|
1829
|
+
}
|
|
1830
|
+
function deleteAnchorsAt(set, flatIndexes) {
|
|
1831
|
+
const bySub = /* @__PURE__ */ new Map();
|
|
1832
|
+
for (const flat of new Set(flatIndexes)) {
|
|
1833
|
+
const loc = locateAnchor(set, flat);
|
|
1834
|
+
if (!loc) continue;
|
|
1835
|
+
const list = bySub.get(loc.sub);
|
|
1836
|
+
if (list) list.push(loc.idx);
|
|
1837
|
+
else bySub.set(loc.sub, [loc.idx]);
|
|
1838
|
+
}
|
|
1839
|
+
if (bySub.size === 0) return false;
|
|
1840
|
+
let survivors = 0;
|
|
1841
|
+
for (let s = 0; s < set.anchors.length; s++) {
|
|
1842
|
+
const doomed = bySub.get(s)?.length ?? 0;
|
|
1843
|
+
if (set.anchors[s].length - doomed >= 2) survivors++;
|
|
1844
|
+
}
|
|
1845
|
+
if (survivors === 0) return false;
|
|
1846
|
+
for (const [s, indices] of bySub) {
|
|
1847
|
+
const sub3 = set.anchors[s];
|
|
1848
|
+
if (!sub3) continue;
|
|
1849
|
+
indices.sort((a, b) => b - a);
|
|
1850
|
+
for (const i of indices) {
|
|
1851
|
+
const wraps = set.closed[s] && sub3.length > 2;
|
|
1852
|
+
const prev = wraps ? sub3[(i - 1 + sub3.length) % sub3.length] : sub3[i - 1];
|
|
1853
|
+
const next = wraps ? sub3[(i + 1) % sub3.length] : sub3[i + 1];
|
|
1854
|
+
if (prev && next && !(isStraightSegment(prev, sub3[i]) && isStraightSegment(sub3[i], next))) {
|
|
1855
|
+
const { c1, c2 } = fitCubicThroughDeletion(prev, sub3[i], next);
|
|
1856
|
+
prev.outHandle = c1;
|
|
1857
|
+
next.inHandle = c2;
|
|
1858
|
+
}
|
|
1859
|
+
sub3.splice(i, 1);
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
for (let s = set.anchors.length - 1; s >= 0; s--) {
|
|
1863
|
+
if (set.anchors[s].length < 2) {
|
|
1864
|
+
set.anchors.splice(s, 1);
|
|
1865
|
+
set.closed.splice(s, 1);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
return true;
|
|
1869
|
+
}
|
|
1870
|
+
function openSubpathAt(set, flatIndex) {
|
|
1871
|
+
const loc = locateAnchor(set, flatIndex);
|
|
1872
|
+
if (!loc) return false;
|
|
1873
|
+
if (!set.closed[loc.sub]) return false;
|
|
1874
|
+
const sub3 = set.anchors[loc.sub];
|
|
1875
|
+
if (!sub3) return false;
|
|
1876
|
+
const { inHandle, ...start } = sub3[loc.idx];
|
|
1877
|
+
const end = { x: start.x, y: start.y, ...inHandle ? { inHandle } : {} };
|
|
1878
|
+
set.anchors[loc.sub] = [start, ...sub3.slice(loc.idx + 1), ...sub3.slice(0, loc.idx), end];
|
|
1879
|
+
set.closed[loc.sub] = false;
|
|
1880
|
+
return true;
|
|
1881
|
+
}
|
|
1882
|
+
function anchorsInRect(set, rect) {
|
|
1883
|
+
const x2 = rect.x + rect.width;
|
|
1884
|
+
const y2 = rect.y + rect.height;
|
|
1885
|
+
const hits = [];
|
|
1886
|
+
let flat = 0;
|
|
1887
|
+
for (const sub3 of set.anchors) {
|
|
1888
|
+
for (const a of sub3) {
|
|
1889
|
+
if (a.x >= rect.x && a.x <= x2 && a.y >= rect.y && a.y <= y2) hits.push(flat);
|
|
1890
|
+
flat++;
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
return hits;
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
// src/paths/joinAnchors.ts
|
|
1897
|
+
var COINCIDENT = 1e-6;
|
|
1898
|
+
function averageAnchorsAt(set, flatIndexes, axis = "both") {
|
|
1899
|
+
const picked = [];
|
|
1900
|
+
for (const flat of new Set(flatIndexes)) {
|
|
1901
|
+
const loc = locateAnchor(set, flat);
|
|
1902
|
+
if (loc) picked.push({ flat, a: set.anchors[loc.sub][loc.idx] });
|
|
1903
|
+
}
|
|
1904
|
+
if (picked.length < 2) return false;
|
|
1905
|
+
const mx = picked.reduce((s, p) => s + p.a.x, 0) / picked.length;
|
|
1906
|
+
const my = picked.reduce((s, p) => s + p.a.y, 0) / picked.length;
|
|
1907
|
+
for (const { flat, a } of picked) {
|
|
1908
|
+
translateAnchorBy(set, flat, axis === "y" ? 0 : mx - a.x, axis === "x" ? 0 : my - a.y);
|
|
1909
|
+
}
|
|
1910
|
+
return true;
|
|
1911
|
+
}
|
|
1912
|
+
function subpathEndAt(set, flatIndex) {
|
|
1913
|
+
const loc = locateAnchor(set, flatIndex);
|
|
1914
|
+
if (!loc || set.closed[loc.sub]) return null;
|
|
1915
|
+
const len = set.anchors[loc.sub].length;
|
|
1916
|
+
if (loc.idx === len - 1) return { sub: loc.sub, end: "last" };
|
|
1917
|
+
if (loc.idx === 0) return { sub: loc.sub, end: "first" };
|
|
1918
|
+
return null;
|
|
1919
|
+
}
|
|
1920
|
+
function joinSubpathEnds(set, a, b) {
|
|
1921
|
+
const subA = set.anchors[a.sub];
|
|
1922
|
+
const subB = set.anchors[b.sub];
|
|
1923
|
+
if (!subA || !subB || set.closed[a.sub] || set.closed[b.sub]) return false;
|
|
1924
|
+
if (a.sub === b.sub) {
|
|
1925
|
+
if (a.end === b.end || subA.length < 2) return false;
|
|
1926
|
+
const first = subA[0];
|
|
1927
|
+
const last = subA[subA.length - 1];
|
|
1928
|
+
if (coincide(first, last) && subA.length > 2) {
|
|
1929
|
+
if (last.inHandle) first.inHandle = last.inHandle;
|
|
1930
|
+
subA.pop();
|
|
1931
|
+
}
|
|
1932
|
+
set.closed[a.sub] = true;
|
|
1933
|
+
return true;
|
|
1934
|
+
}
|
|
1935
|
+
if (a.end === "first") reverseInPlace(subA);
|
|
1936
|
+
if (b.end === "last") reverseInPlace(subB);
|
|
1937
|
+
const tail = subA[subA.length - 1];
|
|
1938
|
+
const head = subB[0];
|
|
1939
|
+
let joined;
|
|
1940
|
+
if (coincide(tail, head)) {
|
|
1941
|
+
if (head.outHandle) tail.outHandle = head.outHandle;
|
|
1942
|
+
joined = [...subA, ...subB.slice(1)];
|
|
1943
|
+
} else {
|
|
1944
|
+
joined = [...subA, ...subB];
|
|
1945
|
+
}
|
|
1946
|
+
const keep = Math.min(a.sub, b.sub);
|
|
1947
|
+
const drop = Math.max(a.sub, b.sub);
|
|
1948
|
+
set.anchors[keep] = joined;
|
|
1949
|
+
set.anchors.splice(drop, 1);
|
|
1950
|
+
set.closed.splice(drop, 1);
|
|
1951
|
+
return true;
|
|
1952
|
+
}
|
|
1953
|
+
function joinAnchorsAt(set, flatA, flatB) {
|
|
1954
|
+
if (flatA === flatB) return false;
|
|
1955
|
+
const a = subpathEndAt(set, flatA);
|
|
1956
|
+
const b = subpathEndAt(set, flatB);
|
|
1957
|
+
if (!a || !b) return false;
|
|
1958
|
+
return joinSubpathEnds(set, a, b);
|
|
1959
|
+
}
|
|
1960
|
+
function closestOpenEnds(set, accept = () => true) {
|
|
1961
|
+
const ends = [];
|
|
1962
|
+
set.anchors.forEach((sub3, s) => {
|
|
1963
|
+
if (set.closed[s] || sub3.length < 2) return;
|
|
1964
|
+
ends.push({ end: { sub: s, end: "first" }, at: sub3[0] });
|
|
1965
|
+
ends.push({ end: { sub: s, end: "last" }, at: sub3[sub3.length - 1] });
|
|
1966
|
+
});
|
|
1967
|
+
let best = null;
|
|
1968
|
+
let bestD = Infinity;
|
|
1969
|
+
for (let i = 0; i < ends.length; i++) {
|
|
1970
|
+
for (let j = i + 1; j < ends.length; j++) {
|
|
1971
|
+
const p = ends[i], q = ends[j];
|
|
1972
|
+
if (p.end.sub === q.end.sub || !accept(p.end, q.end)) continue;
|
|
1973
|
+
const d = Math.hypot(p.at.x - q.at.x, p.at.y - q.at.y);
|
|
1974
|
+
if (d < bestD) {
|
|
1975
|
+
bestD = d;
|
|
1976
|
+
best = [p.end, q.end];
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
return best;
|
|
1981
|
+
}
|
|
1982
|
+
var coincide = (p, q) => Math.hypot(p.x - q.x, p.y - q.y) <= COINCIDENT;
|
|
1983
|
+
function reverseInPlace(sub3) {
|
|
1984
|
+
sub3.reverse();
|
|
1985
|
+
for (const a of sub3) {
|
|
1986
|
+
const { inHandle, outHandle } = a;
|
|
1987
|
+
delete a.inHandle;
|
|
1988
|
+
delete a.outHandle;
|
|
1989
|
+
if (outHandle) a.inHandle = outHandle;
|
|
1990
|
+
if (inHandle) a.outHandle = inHandle;
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
// src/pathSubpaths.ts
|
|
1995
|
+
function flatSubpaths(path, tolerance) {
|
|
1996
|
+
const { commands, coords } = path;
|
|
1997
|
+
const closed = [];
|
|
1998
|
+
const open = [];
|
|
1999
|
+
let sub3 = [];
|
|
2000
|
+
forEachSegment(commands, coords, (cmd, ci, curX, curY, _i, opensSubpath) => {
|
|
2001
|
+
if (opensSubpath) sub3 = [curX, curY];
|
|
2002
|
+
switch (cmd) {
|
|
2003
|
+
case PATH_M:
|
|
2004
|
+
if (sub3.length >= 2) open.push(sub3);
|
|
2005
|
+
sub3 = [coords[ci], coords[ci + 1]];
|
|
2006
|
+
break;
|
|
2007
|
+
case PATH_L:
|
|
2008
|
+
sub3.push(coords[ci], coords[ci + 1]);
|
|
2009
|
+
break;
|
|
2010
|
+
case PATH_C:
|
|
2011
|
+
flattenCubic(
|
|
2012
|
+
curX,
|
|
2013
|
+
curY,
|
|
2014
|
+
coords[ci],
|
|
2015
|
+
coords[ci + 1],
|
|
2016
|
+
coords[ci + 2],
|
|
2017
|
+
coords[ci + 3],
|
|
2018
|
+
coords[ci + 4],
|
|
2019
|
+
coords[ci + 5],
|
|
2020
|
+
tolerance,
|
|
2021
|
+
sub3
|
|
2022
|
+
);
|
|
2023
|
+
break;
|
|
2024
|
+
case PATH_Q:
|
|
2025
|
+
flattenQuadratic(
|
|
2026
|
+
curX,
|
|
2027
|
+
curY,
|
|
2028
|
+
coords[ci],
|
|
2029
|
+
coords[ci + 1],
|
|
2030
|
+
coords[ci + 2],
|
|
2031
|
+
coords[ci + 3],
|
|
2032
|
+
tolerance,
|
|
2033
|
+
sub3
|
|
2034
|
+
);
|
|
2035
|
+
break;
|
|
2036
|
+
case PATH_Z:
|
|
2037
|
+
if (sub3.length >= 6) closed.push(sub3);
|
|
2038
|
+
else if (sub3.length >= 2) open.push(sub3);
|
|
2039
|
+
sub3 = [];
|
|
2040
|
+
break;
|
|
2041
|
+
default:
|
|
2042
|
+
throw new Error(`pathHitTest: unknown command ${cmd}`);
|
|
2043
|
+
}
|
|
2044
|
+
});
|
|
2045
|
+
if (sub3.length >= 2) open.push(sub3);
|
|
2046
|
+
return { closed, open };
|
|
2047
|
+
}
|
|
2048
|
+
function regionSubpaths(path, opts) {
|
|
2049
|
+
const flat = flatSubpaths(path, opts.tolerance ?? DEFAULT_FLATTEN_TOLERANCE);
|
|
2050
|
+
if (!fillsOpenSubpaths(path)) return flat;
|
|
2051
|
+
return {
|
|
2052
|
+
closed: [...flat.closed, ...flat.open.filter((sub3) => sub3.length >= 6)],
|
|
2053
|
+
open: flat.open.filter((sub3) => sub3.length < 6)
|
|
2054
|
+
};
|
|
2055
|
+
}
|
|
2056
|
+
function closedSubpaths(path, opts) {
|
|
2057
|
+
return regionSubpaths(path, opts).closed;
|
|
2058
|
+
}
|
|
2059
|
+
|
|
1177
2060
|
// src/screenBand.ts
|
|
1178
2061
|
function stretchOf(m) {
|
|
1179
2062
|
const e = (m.a + m.d) / 2;
|
|
@@ -1216,14 +2099,14 @@ function capsuleWithinPx(px0, py0, ax, ay, bx, by, r, slop, m, st) {
|
|
|
1216
2099
|
return ellipseWithinPx(px0 - sax, py0 - say, r, slop, st) || ellipseWithinPx(px0 - sbx, py0 - sby, r, slop, st);
|
|
1217
2100
|
}
|
|
1218
2101
|
function quadWithinPx(x, y, q, slop2) {
|
|
1219
|
-
let pos = 0,
|
|
2102
|
+
let pos = 0, neg2 = 0;
|
|
1220
2103
|
for (let i = 0; i < 8; i += 2) {
|
|
1221
2104
|
const j = (i + 2) % 8;
|
|
1222
2105
|
const cr = (q[j] - q[i]) * (y - q[i + 1]) - (q[j + 1] - q[i + 1]) * (x - q[i]);
|
|
1223
2106
|
if (cr > 0) pos++;
|
|
1224
|
-
else if (cr < 0)
|
|
2107
|
+
else if (cr < 0) neg2++;
|
|
1225
2108
|
}
|
|
1226
|
-
if (pos === 0 ||
|
|
2109
|
+
if (pos === 0 || neg2 === 0) return true;
|
|
1227
2110
|
for (let i = 0; i < 8; i += 2) {
|
|
1228
2111
|
const j = (i + 2) % 8;
|
|
1229
2112
|
if (pointSegmentDist2(x, y, q[i], q[i + 1], q[j], q[j + 1]) <= slop2) return true;
|
|
@@ -1277,62 +2160,6 @@ function distToEllipse(e0, e1, y0, y1) {
|
|
|
1277
2160
|
}
|
|
1278
2161
|
|
|
1279
2162
|
// src/pathHitTest.ts
|
|
1280
|
-
function flatSubpaths(path, tolerance) {
|
|
1281
|
-
const { commands, coords } = path;
|
|
1282
|
-
const closed = [];
|
|
1283
|
-
const open = [];
|
|
1284
|
-
let sub3 = [];
|
|
1285
|
-
forEachSegment(commands, coords, (cmd, ci, curX, curY, _i, opensSubpath) => {
|
|
1286
|
-
if (opensSubpath) sub3 = [curX, curY];
|
|
1287
|
-
switch (cmd) {
|
|
1288
|
-
case PATH_M:
|
|
1289
|
-
if (sub3.length >= 2) open.push(sub3);
|
|
1290
|
-
sub3 = [coords[ci], coords[ci + 1]];
|
|
1291
|
-
break;
|
|
1292
|
-
case PATH_L:
|
|
1293
|
-
sub3.push(coords[ci], coords[ci + 1]);
|
|
1294
|
-
break;
|
|
1295
|
-
case PATH_C:
|
|
1296
|
-
flattenCubic(
|
|
1297
|
-
curX,
|
|
1298
|
-
curY,
|
|
1299
|
-
coords[ci],
|
|
1300
|
-
coords[ci + 1],
|
|
1301
|
-
coords[ci + 2],
|
|
1302
|
-
coords[ci + 3],
|
|
1303
|
-
coords[ci + 4],
|
|
1304
|
-
coords[ci + 5],
|
|
1305
|
-
tolerance,
|
|
1306
|
-
sub3
|
|
1307
|
-
);
|
|
1308
|
-
break;
|
|
1309
|
-
case PATH_Q:
|
|
1310
|
-
flattenQuadratic(
|
|
1311
|
-
curX,
|
|
1312
|
-
curY,
|
|
1313
|
-
coords[ci],
|
|
1314
|
-
coords[ci + 1],
|
|
1315
|
-
coords[ci + 2],
|
|
1316
|
-
coords[ci + 3],
|
|
1317
|
-
tolerance,
|
|
1318
|
-
sub3
|
|
1319
|
-
);
|
|
1320
|
-
break;
|
|
1321
|
-
case PATH_Z:
|
|
1322
|
-
if (sub3.length >= 6) closed.push(sub3);
|
|
1323
|
-
else if (sub3.length >= 2) open.push(sub3);
|
|
1324
|
-
sub3 = [];
|
|
1325
|
-
break;
|
|
1326
|
-
default:
|
|
1327
|
-
throw new Error(`pathHitTest: unknown command ${cmd}`);
|
|
1328
|
-
}
|
|
1329
|
-
});
|
|
1330
|
-
if (sub3.length >= 2) open.push(sub3);
|
|
1331
|
-
return { closed, open };
|
|
1332
|
-
}
|
|
1333
|
-
function closedSubpaths(path, opts) {
|
|
1334
|
-
return flatSubpaths(path, opts.tolerance ?? DEFAULT_FLATTEN_TOLERANCE).closed;
|
|
1335
|
-
}
|
|
1336
2163
|
function pointInPath(path, x, y, opts = {}) {
|
|
1337
2164
|
if (path.kind === "rect") {
|
|
1338
2165
|
return x >= path.x && x <= path.x + path.width && y >= path.y && y <= path.y + path.height;
|
|
@@ -1530,7 +2357,7 @@ function polygonContainsPath(polygon, path, opts = {}) {
|
|
|
1530
2357
|
if (path.kind === "rect") {
|
|
1531
2358
|
subpaths = [flattenVerts(rectToVerts(path))];
|
|
1532
2359
|
} else {
|
|
1533
|
-
const { closed, open } =
|
|
2360
|
+
const { closed, open } = regionSubpaths(path, opts);
|
|
1534
2361
|
subpaths = [...closed, ...open];
|
|
1535
2362
|
}
|
|
1536
2363
|
if (subpaths.length === 0) return false;
|
|
@@ -1544,14 +2371,192 @@ function polygonContainsPath(polygon, path, opts = {}) {
|
|
|
1544
2371
|
}
|
|
1545
2372
|
function polygonIntersectsPath(polygon, path, opts = {}) {
|
|
1546
2373
|
if (polygon.length < 3) return false;
|
|
1547
|
-
|
|
1548
|
-
|
|
2374
|
+
const filled = opts.filled !== false;
|
|
2375
|
+
if (path.kind === "rect" && filled) return polygonsIntersect(rectToVerts(path), polygon);
|
|
2376
|
+
const { closed, open } = path.kind === "rect" ? { closed: [flattenVerts(rectToVerts(path))], open: [] } : regionSubpaths(path, opts);
|
|
1549
2377
|
const flatPoly = flattenVerts(polygon);
|
|
1550
2378
|
if (anyVertexInPolygon(closed, flatPoly) || anyVertexInPolygon(open, flatPoly)) return true;
|
|
1551
2379
|
if (anyEdgeCrossesPolygon(closed, polygon) || anyOpenEdgeCrossesPolygon(open, polygon)) {
|
|
1552
2380
|
return true;
|
|
1553
2381
|
}
|
|
1554
|
-
return closed.length > 0 && pointInPath(path, polygon[0].x, polygon[0].y, opts);
|
|
2382
|
+
return filled && closed.length > 0 && pointInPath(path, polygon[0].x, polygon[0].y, opts);
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
// src/paths/markers.ts
|
|
2386
|
+
function circlePath(cx, cy, r, segments = 32) {
|
|
2387
|
+
const cmds = new Uint8Array(segments + 1);
|
|
2388
|
+
const coords = new Float32Array(segments * 2);
|
|
2389
|
+
cmds[0] = PATH_M;
|
|
2390
|
+
coords[0] = cx + r;
|
|
2391
|
+
coords[1] = cy;
|
|
2392
|
+
for (let i = 1; i < segments; i++) {
|
|
2393
|
+
cmds[i] = PATH_L;
|
|
2394
|
+
const theta = i / segments * Math.PI * 2;
|
|
2395
|
+
coords[i * 2] = cx + r * Math.cos(theta);
|
|
2396
|
+
coords[i * 2 + 1] = cy + r * Math.sin(theta);
|
|
2397
|
+
}
|
|
2398
|
+
cmds[segments] = PATH_Z;
|
|
2399
|
+
return { kind: "polygon", commands: cmds, coords, fillRule: "nonzero" };
|
|
2400
|
+
}
|
|
2401
|
+
function squarePath(cx, cy, size) {
|
|
2402
|
+
const half = size / 2;
|
|
2403
|
+
const x0 = cx - half, y0 = cy - half;
|
|
2404
|
+
const x1 = cx + half, y1 = cy + half;
|
|
2405
|
+
return {
|
|
2406
|
+
kind: "polygon",
|
|
2407
|
+
commands: new Uint8Array([PATH_M, PATH_L, PATH_L, PATH_L, PATH_Z]),
|
|
2408
|
+
coords: new Float32Array([x0, y0, x1, y0, x1, y1, x0, y1]),
|
|
2409
|
+
fillRule: "nonzero"
|
|
2410
|
+
};
|
|
2411
|
+
}
|
|
2412
|
+
function rectMarkerPath(x, y, width, height) {
|
|
2413
|
+
return {
|
|
2414
|
+
kind: "polygon",
|
|
2415
|
+
commands: new Uint8Array([PATH_M, PATH_L, PATH_L, PATH_L, PATH_Z]),
|
|
2416
|
+
coords: Float32Array.from(rectToContour(x, y, width, height)),
|
|
2417
|
+
fillRule: "nonzero"
|
|
2418
|
+
};
|
|
2419
|
+
}
|
|
2420
|
+
function roundRectPath(x, y, width, height, r, cornerSamples = 8) {
|
|
2421
|
+
const radius = Math.max(0, Math.min(r, Math.min(width, height) / 2));
|
|
2422
|
+
if (radius === 0) return rectMarkerPath(x, y, width, height);
|
|
2423
|
+
const HALF_PI = Math.PI / 2;
|
|
2424
|
+
const corners = [
|
|
2425
|
+
[x + radius, y + radius, Math.PI],
|
|
2426
|
+
[x + width - radius, y + radius, -HALF_PI],
|
|
2427
|
+
[x + width - radius, y + height - radius, 0],
|
|
2428
|
+
[x + radius, y + height - radius, HALF_PI]
|
|
2429
|
+
];
|
|
2430
|
+
const perCorner = cornerSamples + 1;
|
|
2431
|
+
const count = corners.length * perCorner;
|
|
2432
|
+
const cmds = new Uint8Array(count + 1);
|
|
2433
|
+
const coords = new Float32Array(count * 2);
|
|
2434
|
+
let i = 0;
|
|
2435
|
+
for (const [cx, cy, start] of corners) {
|
|
2436
|
+
for (let s = 0; s <= cornerSamples; s++) {
|
|
2437
|
+
const theta = start + s / cornerSamples * HALF_PI;
|
|
2438
|
+
cmds[i] = i === 0 ? PATH_M : PATH_L;
|
|
2439
|
+
coords[i * 2] = cx + radius * Math.cos(theta);
|
|
2440
|
+
coords[i * 2 + 1] = cy + radius * Math.sin(theta);
|
|
2441
|
+
i++;
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
cmds[count] = PATH_Z;
|
|
2445
|
+
return { kind: "polygon", commands: cmds, coords, fillRule: "nonzero" };
|
|
2446
|
+
}
|
|
2447
|
+
|
|
2448
|
+
// src/glMat3.ts
|
|
2449
|
+
function create(a, b, c, d, tx, ty) {
|
|
2450
|
+
return new Float64Array([a, b, 0, c, d, 0, tx, ty, 1]);
|
|
2451
|
+
}
|
|
2452
|
+
function identity2() {
|
|
2453
|
+
return create(1, 0, 0, 1, 0, 0);
|
|
2454
|
+
}
|
|
2455
|
+
function multiply2(out, m) {
|
|
2456
|
+
const a = out[0], b = out[1];
|
|
2457
|
+
const c = out[3], d = out[4];
|
|
2458
|
+
const tx = out[6], ty = out[7];
|
|
2459
|
+
const ma = m[0], mb = m[1];
|
|
2460
|
+
const mc = m[3], md = m[4];
|
|
2461
|
+
const mtx = m[6], mty = m[7];
|
|
2462
|
+
return create(
|
|
2463
|
+
a * ma + c * mb,
|
|
2464
|
+
// new a
|
|
2465
|
+
b * ma + d * mb,
|
|
2466
|
+
// new b
|
|
2467
|
+
a * mc + c * md,
|
|
2468
|
+
// new c
|
|
2469
|
+
b * mc + d * md,
|
|
2470
|
+
// new d
|
|
2471
|
+
a * mtx + c * mty + tx,
|
|
2472
|
+
// new tx
|
|
2473
|
+
b * mtx + d * mty + ty
|
|
2474
|
+
// new ty
|
|
2475
|
+
);
|
|
2476
|
+
}
|
|
2477
|
+
function translated(m, tx, ty) {
|
|
2478
|
+
const t = create(1, 0, 0, 1, tx, ty);
|
|
2479
|
+
return multiply2(m, t);
|
|
2480
|
+
}
|
|
2481
|
+
function scaled(m, sx, sy) {
|
|
2482
|
+
const s = create(sx, 0, 0, sy, 0, 0);
|
|
2483
|
+
return multiply2(m, s);
|
|
2484
|
+
}
|
|
2485
|
+
function toAffine(m) {
|
|
2486
|
+
return [m[0], m[1], m[3], m[4], m[6], m[7]];
|
|
2487
|
+
}
|
|
2488
|
+
function fromAffine(a) {
|
|
2489
|
+
return create(a[0], a[1], a[2], a[3], a[4], a[5]);
|
|
2490
|
+
}
|
|
2491
|
+
function invert2(m) {
|
|
2492
|
+
const inv = invert(toAffine(m));
|
|
2493
|
+
return inv && fromAffine(inv);
|
|
2494
|
+
}
|
|
2495
|
+
function toUniform(m) {
|
|
2496
|
+
return Float32Array.from(m);
|
|
2497
|
+
}
|
|
2498
|
+
function apply(m, x, y) {
|
|
2499
|
+
const a = m[0], b = m[1];
|
|
2500
|
+
const c = m[3], d = m[4];
|
|
2501
|
+
const tx = m[6], ty = m[7];
|
|
2502
|
+
return [a * x + c * y + tx, b * x + d * y + ty];
|
|
2503
|
+
}
|
|
2504
|
+
function screenToClip(width, height) {
|
|
2505
|
+
return create(
|
|
2506
|
+
2 / width,
|
|
2507
|
+
// a
|
|
2508
|
+
0,
|
|
2509
|
+
// b
|
|
2510
|
+
0,
|
|
2511
|
+
// c
|
|
2512
|
+
-2 / height,
|
|
2513
|
+
// d
|
|
2514
|
+
-1,
|
|
2515
|
+
// tx
|
|
2516
|
+
1
|
|
2517
|
+
// ty
|
|
2518
|
+
);
|
|
2519
|
+
}
|
|
2520
|
+
function meanScaleOf(m) {
|
|
2521
|
+
return Math.sqrt(Math.abs(m[0] * m[4] - m[1] * m[3]));
|
|
2522
|
+
}
|
|
2523
|
+
function minScaleOf(m) {
|
|
2524
|
+
const sum = m[0] * m[0] + m[1] * m[1] + m[3] * m[3] + m[4] * m[4];
|
|
2525
|
+
const det = m[0] * m[4] - m[1] * m[3];
|
|
2526
|
+
return Math.sqrt(Math.max(0, (sum - Math.sqrt(Math.max(0, sum * sum - 4 * det * det))) / 2));
|
|
2527
|
+
}
|
|
2528
|
+
var mat3 = {
|
|
2529
|
+
identity: identity2,
|
|
2530
|
+
toAffine,
|
|
2531
|
+
fromAffine,
|
|
2532
|
+
multiply: multiply2,
|
|
2533
|
+
translated,
|
|
2534
|
+
scaled,
|
|
2535
|
+
invert: invert2,
|
|
2536
|
+
toUniform,
|
|
2537
|
+
apply,
|
|
2538
|
+
screenToClip,
|
|
2539
|
+
meanScaleOf,
|
|
2540
|
+
minScaleOf
|
|
2541
|
+
};
|
|
2542
|
+
|
|
2543
|
+
// src/viewToMat3.ts
|
|
2544
|
+
function viewToMat3(view) {
|
|
2545
|
+
const sx = view.scale.x;
|
|
2546
|
+
const sy = view.scale.y;
|
|
2547
|
+
const tx = -view.x * sx + 0;
|
|
2548
|
+
const ty = -view.y * sy + 0;
|
|
2549
|
+
return new Float64Array([
|
|
2550
|
+
sx,
|
|
2551
|
+
0,
|
|
2552
|
+
0,
|
|
2553
|
+
0,
|
|
2554
|
+
sy,
|
|
2555
|
+
0,
|
|
2556
|
+
tx,
|
|
2557
|
+
ty,
|
|
2558
|
+
1
|
|
2559
|
+
]);
|
|
1555
2560
|
}
|
|
1556
2561
|
|
|
1557
2562
|
// src/place.ts
|
|
@@ -1560,14 +2565,14 @@ function alignAlong(start, extent, size, align) {
|
|
|
1560
2565
|
if (align === "end") return start + extent - size;
|
|
1561
2566
|
return start + (extent - size) / 2;
|
|
1562
2567
|
}
|
|
1563
|
-
function positionOn(anchor, overlay, placement,
|
|
2568
|
+
function positionOn(anchor, overlay, placement, offset2, crossOffset) {
|
|
1564
2569
|
const { width, height } = overlay;
|
|
1565
2570
|
const { side, align } = placement;
|
|
1566
2571
|
if (side === "top" || side === "bottom") {
|
|
1567
|
-
const y = side === "bottom" ? anchor.y + anchor.height +
|
|
2572
|
+
const y = side === "bottom" ? anchor.y + anchor.height + offset2 : anchor.y - offset2 - height;
|
|
1568
2573
|
return { x: alignAlong(anchor.x, anchor.width, width, align) + crossOffset, y, width, height };
|
|
1569
2574
|
}
|
|
1570
|
-
const x = side === "right" ? anchor.x + anchor.width +
|
|
2575
|
+
const x = side === "right" ? anchor.x + anchor.width + offset2 : anchor.x - offset2 - width;
|
|
1571
2576
|
return { x, y: alignAlong(anchor.y, anchor.height, height, align) + crossOffset, width, height };
|
|
1572
2577
|
}
|
|
1573
2578
|
function clampAxis(start, size, low, extent, padding) {
|
|
@@ -1604,13 +2609,13 @@ function fitsAlongSide(rect, boundary, side, padding) {
|
|
|
1604
2609
|
return start >= low && end <= high;
|
|
1605
2610
|
}
|
|
1606
2611
|
function placeRect(options) {
|
|
1607
|
-
const { anchor, overlay, boundary, placement, offset = 0, crossOffset = 0 } = options;
|
|
2612
|
+
const { anchor, overlay, boundary, placement, offset: offset2 = 0, crossOffset = 0 } = options;
|
|
1608
2613
|
const { padding = 0, flip = true } = options;
|
|
1609
2614
|
let resolved = placement;
|
|
1610
|
-
let rect = positionOn(anchor, overlay, resolved,
|
|
2615
|
+
let rect = positionOn(anchor, overlay, resolved, offset2, crossOffset);
|
|
1611
2616
|
if (flip && !fitsAlongSide(rect, boundary, resolved.side, padding)) {
|
|
1612
2617
|
const opposite = { side: OPPOSITE[resolved.side], align: resolved.align };
|
|
1613
|
-
const flipped = positionOn(anchor, overlay, opposite,
|
|
2618
|
+
const flipped = positionOn(anchor, overlay, opposite, offset2, crossOffset);
|
|
1614
2619
|
if (fitsAlongSide(flipped, boundary, opposite.side, padding)) {
|
|
1615
2620
|
resolved = opposite;
|
|
1616
2621
|
rect = flipped;
|
|
@@ -1801,6 +2806,423 @@ function resolveEasing(spec) {
|
|
|
1801
2806
|
throw new Error("resolveEasing: unrecognized easing spec");
|
|
1802
2807
|
}
|
|
1803
2808
|
|
|
1804
|
-
|
|
2809
|
+
// src/meanScale.ts
|
|
2810
|
+
function meanScale(s) {
|
|
2811
|
+
return Math.sqrt(Math.abs(s.x * s.y));
|
|
2812
|
+
}
|
|
2813
|
+
|
|
2814
|
+
// src/poseRotation.ts
|
|
2815
|
+
function poseRotationOf(pose) {
|
|
2816
|
+
const p = pose;
|
|
2817
|
+
if (p.rotation && p.x != null && p.y != null && p.width != null && p.height != null) {
|
|
2818
|
+
return { cx: p.x + p.width / 2, cy: p.y + p.height / 2, rotation: p.rotation };
|
|
2819
|
+
}
|
|
2820
|
+
return null;
|
|
2821
|
+
}
|
|
2822
|
+
function rotatePathAround(path, cx, cy, rotation) {
|
|
2823
|
+
if (path.kind === "rect") {
|
|
2824
|
+
return rotateRectCorners(path.x, path.y, path.width, path.height, cx, cy, rotation);
|
|
2825
|
+
}
|
|
2826
|
+
return rotatePolygon(path, cx, cy, rotation);
|
|
2827
|
+
}
|
|
2828
|
+
function rotatePolygon(path, cx, cy, rotation) {
|
|
2829
|
+
const cos = Math.cos(rotation);
|
|
2830
|
+
const sin = Math.sin(rotation);
|
|
2831
|
+
const { commands, coords } = path;
|
|
2832
|
+
const next = new Float32Array(coords.length);
|
|
2833
|
+
forEachSegment(commands, coords, (cmd, ci) => {
|
|
2834
|
+
for (let k = 0, len = pathCommandCoordCount(cmd); k < len; k += 2) {
|
|
2835
|
+
const dx = coords[ci + k] - cx;
|
|
2836
|
+
const dy = coords[ci + k + 1] - cy;
|
|
2837
|
+
next[ci + k] = cx + dx * cos - dy * sin;
|
|
2838
|
+
next[ci + k + 1] = cy + dx * sin + dy * cos;
|
|
2839
|
+
}
|
|
2840
|
+
});
|
|
2841
|
+
return { ...path, coords: next };
|
|
2842
|
+
}
|
|
2843
|
+
function rotateRectCorners(x, y, w, h, cx, cy, rotation) {
|
|
2844
|
+
const cos = Math.cos(rotation);
|
|
2845
|
+
const sin = Math.sin(rotation);
|
|
2846
|
+
const rotate2 = (px, py) => {
|
|
2847
|
+
const dx = px - cx;
|
|
2848
|
+
const dy = py - cy;
|
|
2849
|
+
return [cx + dx * cos - dy * sin, cy + dx * sin + dy * cos];
|
|
2850
|
+
};
|
|
2851
|
+
const [x0, y0] = rotate2(x, y);
|
|
2852
|
+
const [x1, y1] = rotate2(x + w, y);
|
|
2853
|
+
const [x2, y2] = rotate2(x + w, y + h);
|
|
2854
|
+
const [x3, y3] = rotate2(x, y + h);
|
|
2855
|
+
const commands = new Uint8Array([PATH_M, PATH_L, PATH_L, PATH_L, PATH_Z]);
|
|
2856
|
+
const coords = new Float32Array([x0, y0, x1, y1, x2, y2, x3, y3]);
|
|
2857
|
+
return { kind: "polygon", commands, coords, fillRule: "nonzero" };
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
// src/poseFrame.ts
|
|
2861
|
+
function poseLinearOf(pose) {
|
|
2862
|
+
const t = pose?.transform;
|
|
2863
|
+
if (!Array.isArray(t) || t.length !== 4) return null;
|
|
2864
|
+
if (t[0] === 1 && t[1] === 0 && t[2] === 0 && t[3] === 1) return null;
|
|
2865
|
+
return t;
|
|
2866
|
+
}
|
|
2867
|
+
function poseFrameOf(pose) {
|
|
2868
|
+
const p = pose;
|
|
2869
|
+
if (!p || p.x == null || p.y == null || p.width == null || p.height == null) return null;
|
|
2870
|
+
const linear3 = poseLinearOf(p);
|
|
2871
|
+
if (!p.rotation && !linear3) return null;
|
|
2872
|
+
const cx = p.x + p.width / 2;
|
|
2873
|
+
const cy = p.y + p.height / 2;
|
|
2874
|
+
if (!linear3) return rotateAboutPoint(cx, cy, p.rotation);
|
|
2875
|
+
const [a, b, c, d] = linear3;
|
|
2876
|
+
const about = [a, b, c, d, cx - (a * cx + c * cy), cy - (b * cx + d * cy)];
|
|
2877
|
+
return p.rotation ? multiply(rotateAboutPoint(cx, cy, p.rotation), about) : about;
|
|
2878
|
+
}
|
|
2879
|
+
function poseFrameInverseOf(pose) {
|
|
2880
|
+
if (!poseLinearOf(pose)) {
|
|
2881
|
+
const r = poseRotationOf(pose);
|
|
2882
|
+
return r && rotateAboutPoint(r.cx, r.cy, -r.rotation);
|
|
2883
|
+
}
|
|
2884
|
+
const frame = poseFrameOf(pose);
|
|
2885
|
+
return frame && invert(frame);
|
|
2886
|
+
}
|
|
2887
|
+
function pathThroughPoseFrame(path, pose) {
|
|
2888
|
+
if (poseLinearOf(pose)) {
|
|
2889
|
+
const frame = poseFrameOf(pose);
|
|
2890
|
+
return frame ? transformPath(path, frame) : path;
|
|
2891
|
+
}
|
|
2892
|
+
const r = poseRotationOf(pose);
|
|
2893
|
+
return r ? rotatePathAround(path, r.cx, r.cy, r.rotation) : path;
|
|
2894
|
+
}
|
|
2895
|
+
|
|
2896
|
+
// src/paths/anchorSymmetry.ts
|
|
2897
|
+
function near(a, b, tol) {
|
|
2898
|
+
if (!a || !b) return a === b;
|
|
2899
|
+
return Math.abs(a.x - b.x) <= tol && Math.abs(a.y - b.y) <= tol;
|
|
2900
|
+
}
|
|
2901
|
+
function image(m, p) {
|
|
2902
|
+
if (!p) return void 0;
|
|
2903
|
+
const [x, y] = applyToPoint(m, p.x, p.y);
|
|
2904
|
+
return { x, y };
|
|
2905
|
+
}
|
|
2906
|
+
function linear2(m, d) {
|
|
2907
|
+
return { x: m[0] * d.x + m[2] * d.y, y: m[1] * d.x + m[3] * d.y };
|
|
2908
|
+
}
|
|
2909
|
+
function landing(m, a, b, tol) {
|
|
2910
|
+
if (!near(image(m, a), b, tol)) return null;
|
|
2911
|
+
const i = image(m, a.inHandle);
|
|
2912
|
+
const o = image(m, a.outHandle);
|
|
2913
|
+
if (near(i, b.inHandle, tol) && near(o, b.outHandle, tol)) return false;
|
|
2914
|
+
if (near(i, b.outHandle, tol) && near(o, b.inHandle, tol)) return true;
|
|
2915
|
+
return null;
|
|
2916
|
+
}
|
|
2917
|
+
function anchorSymmetry(set, m, tolerance = 1e-6) {
|
|
2918
|
+
const n = anchorCount(set);
|
|
2919
|
+
const partners = [];
|
|
2920
|
+
const flips = [];
|
|
2921
|
+
const taken = /* @__PURE__ */ new Set();
|
|
2922
|
+
for (let i = 0; i < n; i++) {
|
|
2923
|
+
const a = anchorAt(set, i);
|
|
2924
|
+
let found = -1;
|
|
2925
|
+
let flip = false;
|
|
2926
|
+
for (let j = 0; j < n && found < 0; j++) {
|
|
2927
|
+
if (taken.has(j)) continue;
|
|
2928
|
+
const how = landing(m, a, anchorAt(set, j), tolerance);
|
|
2929
|
+
if (how === null) continue;
|
|
2930
|
+
found = j;
|
|
2931
|
+
flip = how;
|
|
2932
|
+
}
|
|
2933
|
+
if (found < 0) return null;
|
|
2934
|
+
taken.add(found);
|
|
2935
|
+
partners.push(found);
|
|
2936
|
+
flips.push(flip);
|
|
2937
|
+
}
|
|
2938
|
+
return { m, partners, flips };
|
|
2939
|
+
}
|
|
2940
|
+
function anchorSymmetries(set, group, tolerance = 1e-6) {
|
|
2941
|
+
return group.map((m) => anchorSymmetry(set, m, tolerance)).filter((s) => s !== null);
|
|
2942
|
+
}
|
|
2943
|
+
function averaged(d, syms, fixes, apply2) {
|
|
2944
|
+
let x = d.x;
|
|
2945
|
+
let y = d.y;
|
|
2946
|
+
let k = 1;
|
|
2947
|
+
for (const s of syms) {
|
|
2948
|
+
if (!fixes(s)) continue;
|
|
2949
|
+
const p = apply2(s.m, d);
|
|
2950
|
+
x += p.x;
|
|
2951
|
+
y += p.y;
|
|
2952
|
+
k++;
|
|
2953
|
+
}
|
|
2954
|
+
return { x: x / k, y: y / k };
|
|
2955
|
+
}
|
|
2956
|
+
function translateAnchorsSymmetric(set, flats, dx, dy, syms) {
|
|
2957
|
+
const dragged = new Set(flats);
|
|
2958
|
+
const moves = /* @__PURE__ */ new Map();
|
|
2959
|
+
for (const flat of flats) {
|
|
2960
|
+
const d = averaged({ x: dx, y: dy }, syms, (s) => s.partners[flat] === flat, linear2);
|
|
2961
|
+
moves.set(flat, d);
|
|
2962
|
+
for (const s of syms) {
|
|
2963
|
+
const j = s.partners[flat];
|
|
2964
|
+
if (j === void 0 || dragged.has(j) || moves.has(j)) continue;
|
|
2965
|
+
moves.set(j, linear2(s.m, d));
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
let changed = false;
|
|
2969
|
+
for (const [flat, d] of moves) changed = translateAnchorBy(set, flat, d.x, d.y) || changed;
|
|
2970
|
+
return changed;
|
|
2971
|
+
}
|
|
2972
|
+
var other = (side) => side === "in" ? "out" : "in";
|
|
2973
|
+
function moveHandleSymmetric(set, flat, side, toX, toY, breakSmoothness, syms) {
|
|
2974
|
+
const a = anchorAt(set, flat);
|
|
2975
|
+
if (!a || !(side === "in" ? a.inHandle : a.outHandle)) return false;
|
|
2976
|
+
const sideUnder = (s, sd) => s.flips[flat] ? other(sd) : sd;
|
|
2977
|
+
const fixesHandle = (s) => s.partners[flat] === flat && sideUnder(s, side) === side;
|
|
2978
|
+
const to = averaged({ x: toX, y: toY }, syms, fixesHandle, image);
|
|
2979
|
+
const targets = /* @__PURE__ */ new Map();
|
|
2980
|
+
const put = (f, sd, p) => {
|
|
2981
|
+
const key = `${f}:${sd}`;
|
|
2982
|
+
if (!targets.has(key)) targets.set(key, { flat: f, side: sd, to: p });
|
|
2983
|
+
};
|
|
2984
|
+
put(flat, side, to);
|
|
2985
|
+
for (const s of syms) put(s.partners[flat], sideUnder(s, side), image(s.m, to));
|
|
2986
|
+
const opposite = side === "in" ? a.outHandle : a.inHandle;
|
|
2987
|
+
const drivenBySymmetry = targets.has(`${flat}:${other(side)}`);
|
|
2988
|
+
if (!drivenBySymmetry && !breakSmoothness && opposite && isAnchorSmooth(a)) {
|
|
2989
|
+
const opp = { x: 2 * a.x - to.x, y: 2 * a.y - to.y };
|
|
2990
|
+
put(flat, other(side), opp);
|
|
2991
|
+
for (const s of syms) put(s.partners[flat], sideUnder(s, other(side)), image(s.m, opp));
|
|
2992
|
+
}
|
|
2993
|
+
for (const t of targets.values()) {
|
|
2994
|
+
const b = anchorAt(set, t.flat);
|
|
2995
|
+
if (!b) continue;
|
|
2996
|
+
if (t.side === "in") b.inHandle = t.to;
|
|
2997
|
+
else b.outHandle = t.to;
|
|
2998
|
+
}
|
|
2999
|
+
return true;
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
// src/paths/bendSegment.ts
|
|
3003
|
+
var T_MARGIN = 0.05;
|
|
3004
|
+
function bendSegment(set, grip, dx, dy) {
|
|
3005
|
+
const ends = segmentEnds(set.anchors, set.closed, grip.sub, grip.segIdx);
|
|
3006
|
+
if (!ends) return false;
|
|
3007
|
+
const { a, b } = ends;
|
|
3008
|
+
const aSmooth = isAnchorSmooth(a);
|
|
3009
|
+
const bSmooth = isAnchorSmooth(b);
|
|
3010
|
+
const straight = isStraightSegment(a, b);
|
|
3011
|
+
const p1 = a.outHandle ?? (straight ? lerp(a, b, 1 / 3) : { x: a.x, y: a.y });
|
|
3012
|
+
const p2 = b.inHandle ?? (straight ? lerp(a, b, 2 / 3) : { x: b.x, y: b.y });
|
|
3013
|
+
const t = Math.min(1 - T_MARGIN, Math.max(T_MARGIN, grip.t));
|
|
3014
|
+
const u = 1 - t;
|
|
3015
|
+
const k = 1 / (3 * t * u * (u * u + t * t));
|
|
3016
|
+
a.outHandle = { x: p1.x + k * u * dx, y: p1.y + k * u * dy };
|
|
3017
|
+
b.inHandle = { x: p2.x + k * t * dx, y: p2.y + k * t * dy };
|
|
3018
|
+
if (aSmooth) a.inHandle = alignedOpposite(a, a.outHandle, a.inHandle);
|
|
3019
|
+
if (bSmooth) b.outHandle = alignedOpposite(b, b.inHandle, b.outHandle);
|
|
3020
|
+
return true;
|
|
3021
|
+
}
|
|
3022
|
+
function lerp(a, b, f) {
|
|
3023
|
+
return { x: a.x + (b.x - a.x) * f, y: a.y + (b.y - a.y) * f };
|
|
3024
|
+
}
|
|
3025
|
+
function alignedOpposite(anchor, moved, other2) {
|
|
3026
|
+
const mx = moved.x - anchor.x;
|
|
3027
|
+
const my = moved.y - anchor.y;
|
|
3028
|
+
const len = Math.hypot(mx, my);
|
|
3029
|
+
if (len === 0) return other2;
|
|
3030
|
+
const keep = Math.hypot(other2.x - anchor.x, other2.y - anchor.y);
|
|
3031
|
+
return { x: anchor.x - mx / len * keep, y: anchor.y - my / len * keep };
|
|
3032
|
+
}
|
|
3033
|
+
|
|
3034
|
+
// src/paths/handleEdits.ts
|
|
3035
|
+
function retractHandles(set, flatIndexes) {
|
|
3036
|
+
let changed = false;
|
|
3037
|
+
for (const flat of new Set(flatIndexes)) {
|
|
3038
|
+
const a = anchorAt(set, flat);
|
|
3039
|
+
if (!a) continue;
|
|
3040
|
+
if (a.inHandle) {
|
|
3041
|
+
changed = changed || !same2(a.inHandle, a);
|
|
3042
|
+
delete a.inHandle;
|
|
3043
|
+
}
|
|
3044
|
+
if (a.outHandle) {
|
|
3045
|
+
changed = changed || !same2(a.outHandle, a);
|
|
3046
|
+
delete a.outHandle;
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
return changed;
|
|
3050
|
+
}
|
|
3051
|
+
function makeHandlesSymmetric(set, flatIndexes) {
|
|
3052
|
+
let changed = false;
|
|
3053
|
+
for (const flat of new Set(flatIndexes)) {
|
|
3054
|
+
const loc = locateAnchor(set, flat);
|
|
3055
|
+
if (!loc) continue;
|
|
3056
|
+
const sub3 = set.anchors[loc.sub];
|
|
3057
|
+
const isEnd = !set.closed[loc.sub] && (loc.idx === 0 || loc.idx === sub3.length - 1);
|
|
3058
|
+
if (isEnd) continue;
|
|
3059
|
+
changed = symmetrize(sub3[loc.idx]) || changed;
|
|
3060
|
+
}
|
|
3061
|
+
return changed;
|
|
3062
|
+
}
|
|
3063
|
+
function symmetrize(a) {
|
|
3064
|
+
const out = a.outHandle ? offset(a, a.outHandle) : null;
|
|
3065
|
+
const inFlipped = a.inHandle ? scale2(offset(a, a.inHandle), -1) : null;
|
|
3066
|
+
const outLen = out ? Math.hypot(out.x, out.y) : 0;
|
|
3067
|
+
const inLen = inFlipped ? Math.hypot(inFlipped.x, inFlipped.y) : 0;
|
|
3068
|
+
if (outLen === 0 && inLen === 0) return false;
|
|
3069
|
+
let dir;
|
|
3070
|
+
let len;
|
|
3071
|
+
if (outLen > 0 && inLen > 0) {
|
|
3072
|
+
const sum = { x: out.x / outLen + inFlipped.x / inLen, y: out.y / outLen + inFlipped.y / inLen };
|
|
3073
|
+
const sumLen = Math.hypot(sum.x, sum.y);
|
|
3074
|
+
dir = sumLen > 1e-9 ? scale2(sum, 1 / sumLen) : scale2(out, 1 / outLen);
|
|
3075
|
+
len = (outLen + inLen) / 2;
|
|
3076
|
+
} else if (outLen > 0) {
|
|
3077
|
+
dir = scale2(out, 1 / outLen);
|
|
3078
|
+
len = outLen;
|
|
3079
|
+
} else {
|
|
3080
|
+
dir = scale2(inFlipped, 1 / inLen);
|
|
3081
|
+
len = inLen;
|
|
3082
|
+
}
|
|
3083
|
+
const nextOut = { x: a.x + dir.x * len, y: a.y + dir.y * len };
|
|
3084
|
+
const nextIn = { x: a.x - dir.x * len, y: a.y - dir.y * len };
|
|
3085
|
+
if (same2(a.outHandle, nextOut) && same2(a.inHandle, nextIn)) return false;
|
|
3086
|
+
a.outHandle = nextOut;
|
|
3087
|
+
a.inHandle = nextIn;
|
|
3088
|
+
return true;
|
|
3089
|
+
}
|
|
3090
|
+
function handleAlongItsAngle(anchor, handle, to) {
|
|
3091
|
+
const d = offset(anchor, handle);
|
|
3092
|
+
const len = Math.hypot(d.x, d.y);
|
|
3093
|
+
if (len === 0) return { x: to.x, y: to.y };
|
|
3094
|
+
const ux = d.x / len;
|
|
3095
|
+
const uy = d.y / len;
|
|
3096
|
+
const along = Math.max(0, (to.x - anchor.x) * ux + (to.y - anchor.y) * uy);
|
|
3097
|
+
return { x: anchor.x + ux * along, y: anchor.y + uy * along };
|
|
3098
|
+
}
|
|
3099
|
+
function offset(from, to) {
|
|
3100
|
+
return { x: to.x - from.x, y: to.y - from.y };
|
|
3101
|
+
}
|
|
3102
|
+
function scale2(p, k) {
|
|
3103
|
+
return { x: p.x * k, y: p.y * k };
|
|
3104
|
+
}
|
|
3105
|
+
function same2(p, q) {
|
|
3106
|
+
return !!p && Math.abs(p.x - q.x) < 1e-9 && Math.abs(p.y - q.y) < 1e-9;
|
|
3107
|
+
}
|
|
3108
|
+
|
|
3109
|
+
// src/paths/nearestAnchor.ts
|
|
3110
|
+
function nearestPathAnchor(paths, point, px, scale3, accept) {
|
|
3111
|
+
let best = null;
|
|
3112
|
+
let bestD2 = px * px;
|
|
3113
|
+
for (const { id, path } of paths) {
|
|
3114
|
+
const { anchors, closed } = pathToAnchors(path);
|
|
3115
|
+
for (let sub3 = 0; sub3 < anchors.length; sub3++) {
|
|
3116
|
+
const list = anchors[sub3];
|
|
3117
|
+
for (let idx = 0; idx < list.length; idx++) {
|
|
3118
|
+
const a = list[idx];
|
|
3119
|
+
const s = scaleDelta(a.x - point.x, a.y - point.y, scale3);
|
|
3120
|
+
const d2 = s.x * s.x + s.y * s.y;
|
|
3121
|
+
if (d2 > bestD2 || best !== null && d2 === bestD2) continue;
|
|
3122
|
+
const end = closed[sub3] ? null : idx === list.length - 1 ? "last" : idx === 0 ? "first" : null;
|
|
3123
|
+
const hit = { id, sub: sub3, idx, x: a.x, y: a.y, end };
|
|
3124
|
+
if (accept && !accept(hit)) continue;
|
|
3125
|
+
best = hit;
|
|
3126
|
+
bestD2 = d2;
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
return best;
|
|
3131
|
+
}
|
|
3132
|
+
function reverseAnchors(anchors) {
|
|
3133
|
+
return anchors.map((a) => {
|
|
3134
|
+
const { inHandle, outHandle, ...rest } = a;
|
|
3135
|
+
const r = { ...rest };
|
|
3136
|
+
if (outHandle) r.inHandle = { ...outHandle };
|
|
3137
|
+
if (inHandle) r.outHandle = { ...inHandle };
|
|
3138
|
+
return r;
|
|
3139
|
+
}).reverse();
|
|
3140
|
+
}
|
|
3141
|
+
|
|
3142
|
+
// src/paths/pathInWorld.ts
|
|
3143
|
+
function pathInPoseFrame(path, pose) {
|
|
3144
|
+
if (path.kind === "rect") {
|
|
3145
|
+
if (path.x === pose.x && path.y === pose.y && path.width === pose.width && path.height === pose.height) return path;
|
|
3146
|
+
return { kind: "rect", x: pose.x, y: pose.y, width: pose.width, height: pose.height };
|
|
3147
|
+
}
|
|
3148
|
+
const b = boundsOfPath(path);
|
|
3149
|
+
if (b.x === pose.x && b.y === pose.y && b.width === pose.width && b.height === pose.height) {
|
|
3150
|
+
return path;
|
|
3151
|
+
}
|
|
3152
|
+
const m = boxToBox(b.x, b.y, b.width, b.height, pose.x, pose.y, pose.width, pose.height);
|
|
3153
|
+
return transformPath(path, m);
|
|
3154
|
+
}
|
|
3155
|
+
function pathInWorld(path, pose) {
|
|
3156
|
+
const local = pathInPoseFrame(path, pose);
|
|
3157
|
+
const frame = poseFrameOf(pose);
|
|
3158
|
+
if (!frame) return local;
|
|
3159
|
+
return transformPath(local, frame);
|
|
3160
|
+
}
|
|
3161
|
+
function worldEditToStorage(pose, worldPath) {
|
|
3162
|
+
const back = poseFrameInverseOf(pose);
|
|
3163
|
+
const frame = back && poseFrameOf(pose);
|
|
3164
|
+
const unrotated = back ? transformPath(worldPath, back) : worldPath;
|
|
3165
|
+
const b = boundsOfPath(unrotated);
|
|
3166
|
+
const aligned = translatePath(unrotated, -b.x, -b.y);
|
|
3167
|
+
let x = b.x, y = b.y;
|
|
3168
|
+
if (frame) {
|
|
3169
|
+
const [cx, cy] = applyToPoint(frame, b.x + b.width / 2, b.y + b.height / 2);
|
|
3170
|
+
x = cx - b.width / 2;
|
|
3171
|
+
y = cy - b.height / 2;
|
|
3172
|
+
}
|
|
3173
|
+
return {
|
|
3174
|
+
pose: { ...pose, x, y, width: b.width, height: b.height },
|
|
3175
|
+
path: aligned
|
|
3176
|
+
};
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3179
|
+
// src/typedArrayJson.ts
|
|
3180
|
+
var TYPED_ARRAY_CONSTRUCTORS = {
|
|
3181
|
+
i8: Int8Array,
|
|
3182
|
+
u8: Uint8Array,
|
|
3183
|
+
u8c: Uint8ClampedArray,
|
|
3184
|
+
i16: Int16Array,
|
|
3185
|
+
u16: Uint16Array,
|
|
3186
|
+
i32: Int32Array,
|
|
3187
|
+
u32: Uint32Array,
|
|
3188
|
+
f32: Float32Array,
|
|
3189
|
+
f64: Float64Array
|
|
3190
|
+
};
|
|
3191
|
+
var TAGS = new Map(
|
|
3192
|
+
Object.keys(TYPED_ARRAY_CONSTRUCTORS).map((tag) => [TYPED_ARRAY_CONSTRUCTORS[tag].name, tag])
|
|
3193
|
+
);
|
|
3194
|
+
var classOf = (value) => Object.prototype.toString.call(value).slice(8, -1);
|
|
3195
|
+
function tagTypedArray(value) {
|
|
3196
|
+
if (!ArrayBuffer.isView(value)) return void 0;
|
|
3197
|
+
const tag = TAGS.get(classOf(value));
|
|
3198
|
+
return tag && { __ta: tag, data: Array.from(value) };
|
|
3199
|
+
}
|
|
3200
|
+
function untagTypedArray(value) {
|
|
3201
|
+
if (value === null || typeof value !== "object") return void 0;
|
|
3202
|
+
const { __ta: tag, data } = value;
|
|
3203
|
+
if (typeof tag !== "string" || !Object.hasOwn(TYPED_ARRAY_CONSTRUCTORS, tag) || !Array.isArray(data)) return void 0;
|
|
3204
|
+
return TYPED_ARRAY_CONSTRUCTORS[tag].from(data);
|
|
3205
|
+
}
|
|
3206
|
+
var isPlain = (value) => classOf(value) === "Object";
|
|
3207
|
+
function mapDeep(value, leaf) {
|
|
3208
|
+
const swapped = leaf(value);
|
|
3209
|
+
if (swapped !== void 0) return swapped;
|
|
3210
|
+
if (Array.isArray(value)) return value.map((v) => mapDeep(v, leaf));
|
|
3211
|
+
if (!isPlain(value)) return value;
|
|
3212
|
+
const out = {};
|
|
3213
|
+
for (const key of Object.keys(value)) out[key] = mapDeep(value[key], leaf);
|
|
3214
|
+
return out;
|
|
3215
|
+
}
|
|
3216
|
+
function tagTypedArrays(value) {
|
|
3217
|
+
return mapDeep(value, tagTypedArray);
|
|
3218
|
+
}
|
|
3219
|
+
function reviveTypedArrays(value) {
|
|
3220
|
+
return mapDeep(value, untagTypedArray);
|
|
3221
|
+
}
|
|
3222
|
+
function typedArrayReplacer(_key, value) {
|
|
3223
|
+
return tagTypedArray(value) ?? value;
|
|
3224
|
+
}
|
|
3225
|
+
|
|
3226
|
+
export { EASINGS, PathBuilder, SPRING_PRESETS, TYPED_ARRAY_CONSTRUCTORS, ZOOM_FLOOR, anchorAt, anchorCount, anchorSymmetries, anchorSymmetry, anchorsInRect, anchorsToPath, applyToPoint, averageAnchorsAt, bendSegment, boundsOfPath, boxToBox, circlePath, clampRectWithin, closestOpenEnds, composePath, countPathAnchors, cubicBezierEasing, decomposePath, deleteAnchorsAt, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorSet, editAnchorSetTracked, ellipsePath, fitCubicThroughDeletion, fitCubicWithTangents, flatAnchorIndex, handleAlongItsAngle, hasOpenSubpath, identity, insertAnchorOnSegment, invert, isAnchorSmooth, isStraightSegment, joinAnchorsAt, joinSubpathEnds, linePath, linear, locateAnchor, makeHandlesSymmetric, mat3, meanScale, moveHandleSymmetric, moveHandleTo, multiply, nearestOnCubic, nearestOnLine, nearestOnPath, nearestOnQuadratic, nearestPathAnchor, nearestSegmentT, normalizeZoom, openSubpathAt, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathDistanceToPoint, pathFromD, pathInPoseFrame, pathInWorld, pathIntersectsPolygon, pathIntersectsRect, pathSignedArea, pathThroughPoseFrame, pathToAnchors, placeRect, pointAlongPath, pointInPath, polygonContainsPath, polygonFromPoints, polygonIntersectsPath, polylineFromPoints, portControls2 as portControls, portCurvePoints2 as portCurvePoints, poseFrameInverseOf, poseFrameOf, poseLinearOf, poseRotationOf, pxExtent, rectMarkerPath, rectPath, regularPolygonPath, remapPointValues, resolveEasing, retractHandles, reverseAnchors, reversePath, reviveTypedArrays, rotate, rotateAboutPoint, rotatePathAround, roundRectPath, scale, scaleDelta, scalePathToBounds, schneiderFit, screenAngleOf, segmentAt, segmentEnds, simplifyPath, splitByClosure, splitSubpaths, squarePath, standoff, starPath, strokeHitTest, subpathEndAt, tagTypedArray, tagTypedArrays, transformCoords, transformPath, translate, translateAnchorBy, translateAnchorsSymmetric, translatePath, typedArrayReplacer, unionBoundsPath, untagTypedArray, viewToMat3, withinPxBox, withinPxRadius, worldEditToStorage };
|
|
1805
3227
|
//# sourceMappingURL=index.js.map
|
|
1806
3228
|
//# sourceMappingURL=index.js.map
|