@pixerate/editor 0.212.0 → 0.213.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-5OC22IEO.js → chunk-DJBYJOHM.js} +419 -1
- package/dist/{chunk-3T5VCDIV.cjs → chunk-LCCMOWR7.cjs} +419 -1
- package/dist/image-editor/controller.d.cts +22 -2
- package/dist/image-editor/controller.d.ts +22 -2
- package/dist/image-editor/index.cjs +1117 -562
- package/dist/image-editor/index.d.cts +1 -0
- package/dist/image-editor/index.d.ts +1 -0
- package/dist/image-editor/index.js +841 -286
- package/dist/image-editor/path-edit.d.cts +97 -0
- package/dist/image-editor/path-edit.d.ts +97 -0
- package/dist/image-editor/paths.d.cts +14 -0
- package/dist/image-editor/paths.d.ts +14 -0
- package/dist/image-editor/renderer.d.cts +3 -0
- package/dist/image-editor/renderer.d.ts +3 -0
- package/dist/image-editor/tool-groups.d.cts +2 -2
- package/dist/image-editor/tool-groups.d.ts +2 -2
- package/dist/image-editor/types.d.cts +3 -1
- package/dist/image-editor/types.d.ts +3 -1
- package/dist/vector/index.cjs +46 -433
- package/dist/vector/index.js +30 -417
- package/package.json +1 -1
- package/skills/pixerate-image-editor/SKILL.md +2 -0
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
var _chunkQAUIZPYIcjs = require('./chunk-QAUIZPYI.cjs');
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
var _chunkGF2RBSDMcjs = require('./chunk-GF2RBSDM.cjs');
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
4
16
|
var _chunkNM3G4VQTcjs = require('./chunk-NM3G4VQT.cjs');
|
|
5
17
|
|
|
6
18
|
// src/vector/trace.ts
|
|
@@ -1342,6 +1354,412 @@ async function unwoff(view) {
|
|
|
1342
1354
|
return o;
|
|
1343
1355
|
}
|
|
1344
1356
|
|
|
1357
|
+
// src/vector/pathEdit.ts
|
|
1358
|
+
var EPS = 1e-9;
|
|
1359
|
+
var unit = (x, y) => {
|
|
1360
|
+
const l = Math.hypot(x, y);
|
|
1361
|
+
return l > 1e-12 && Number.isFinite(l) ? [x / l, y / l] : null;
|
|
1362
|
+
};
|
|
1363
|
+
function cubicOf(sp, i) {
|
|
1364
|
+
const s = _chunkQAUIZPYIcjs.subpathSegment.call(void 0, sp, i);
|
|
1365
|
+
if (s.length === 4) return s;
|
|
1366
|
+
const [a, b] = s;
|
|
1367
|
+
return [a, [a[0] + (b[0] - a[0]) / 3, a[1] + (b[1] - a[1]) / 3], [a[0] + 2 * (b[0] - a[0]) / 3, a[1] + 2 * (b[1] - a[1]) / 3], b];
|
|
1368
|
+
}
|
|
1369
|
+
var isLine = (sp, i) => _chunkQAUIZPYIcjs.subpathSegment.call(void 0, sp, i).length === 2;
|
|
1370
|
+
function startTangent(c) {
|
|
1371
|
+
for (let k = 1; k < 4; k++) {
|
|
1372
|
+
const u = unit(c[k][0] - c[0][0], c[k][1] - c[0][1]);
|
|
1373
|
+
if (u) return u;
|
|
1374
|
+
}
|
|
1375
|
+
return [1, 0];
|
|
1376
|
+
}
|
|
1377
|
+
function endTangent(c) {
|
|
1378
|
+
for (let k = 2; k >= 0; k--) {
|
|
1379
|
+
const u = unit(c[3][0] - c[k][0], c[3][1] - c[k][1]);
|
|
1380
|
+
if (u) return u;
|
|
1381
|
+
}
|
|
1382
|
+
return [1, 0];
|
|
1383
|
+
}
|
|
1384
|
+
function sample(c, n, out, withStart) {
|
|
1385
|
+
for (let i = withStart ? 0 : 1; i <= n; i++) out.push(_chunkGF2RBSDMcjs.segmentPoint.call(void 0, c, i / n));
|
|
1386
|
+
}
|
|
1387
|
+
var onAnchor = (a, h) => !h || Math.abs(h.x - a.x) < EPS && Math.abs(h.y - a.y) < EPS;
|
|
1388
|
+
function tidy(a, keepType = true) {
|
|
1389
|
+
const next = _chunkQAUIZPYIcjs.cloneAnchor.call(void 0, a);
|
|
1390
|
+
if (onAnchor(next, next.in)) delete next.in;
|
|
1391
|
+
if (onAnchor(next, next.out)) delete next.out;
|
|
1392
|
+
const shown = _chunkQAUIZPYIcjs.inferAnchorType.call(void 0, next);
|
|
1393
|
+
const type = keepType && a.type ? a.type === "symmetric" && shown !== "symmetric" ? shown === "corner" ? "corner" : "smooth" : a.type === "smooth" && shown === "corner" ? "corner" : a.type : shown;
|
|
1394
|
+
if (type === "corner") delete next.type;
|
|
1395
|
+
else next.type = type;
|
|
1396
|
+
return next;
|
|
1397
|
+
}
|
|
1398
|
+
function neighbours(sp, i) {
|
|
1399
|
+
const n = sp.anchors.length;
|
|
1400
|
+
const wrap = sp.closed && n > 1;
|
|
1401
|
+
return [i > 0 ? i - 1 : wrap ? n - 1 : -1, i < n - 1 ? i + 1 : wrap ? 0 : -1];
|
|
1402
|
+
}
|
|
1403
|
+
function goldenMin(f, a, b) {
|
|
1404
|
+
const R = 0.618033988749895;
|
|
1405
|
+
let x1 = b - R * (b - a);
|
|
1406
|
+
let x2 = a + R * (b - a);
|
|
1407
|
+
let f1 = f(x1);
|
|
1408
|
+
let f2 = f(x2);
|
|
1409
|
+
for (let i = 0; i < 40; i++) {
|
|
1410
|
+
if (f1.error < f2.error || Number.isNaN(f2.error)) {
|
|
1411
|
+
b = x2;
|
|
1412
|
+
x2 = x1;
|
|
1413
|
+
f2 = f1;
|
|
1414
|
+
x1 = b - R * (b - a);
|
|
1415
|
+
f1 = f(x1);
|
|
1416
|
+
} else {
|
|
1417
|
+
a = x1;
|
|
1418
|
+
x1 = x2;
|
|
1419
|
+
f1 = f2;
|
|
1420
|
+
x2 = a + R * (b - a);
|
|
1421
|
+
f2 = f(x2);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
return f1.error <= f2.error ? f1 : f2;
|
|
1425
|
+
}
|
|
1426
|
+
function fitAcross(pts, N, t0, t1) {
|
|
1427
|
+
const fit = (k) => {
|
|
1428
|
+
const u = [];
|
|
1429
|
+
for (let i = 0; i <= N; i++) u.push(k * i / N);
|
|
1430
|
+
for (let i = 1; i <= N; i++) u.push(k + (1 - k) * i / N);
|
|
1431
|
+
return _chunkNM3G4VQTcjs.fitCubicWithTangents.call(void 0, pts, u, t0, t1);
|
|
1432
|
+
};
|
|
1433
|
+
return goldenMin(fit, 0, 1);
|
|
1434
|
+
}
|
|
1435
|
+
function removeAnchorKeepingShape(sp, index) {
|
|
1436
|
+
const anchors = sp.anchors.map(_chunkQAUIZPYIcjs.cloneAnchor);
|
|
1437
|
+
const n = anchors.length;
|
|
1438
|
+
if (index < 0 || index >= n) return { anchors, closed: sp.closed };
|
|
1439
|
+
if ((sp.closed || index > 0 && index < n - 1) && n >= 3) {
|
|
1440
|
+
const pi = (index + n - 1) % n;
|
|
1441
|
+
const ni = (index + 1) % n;
|
|
1442
|
+
if (!(isLine(sp, pi) && isLine(sp, index))) {
|
|
1443
|
+
const left = cubicOf(sp, pi);
|
|
1444
|
+
const right = cubicOf(sp, index);
|
|
1445
|
+
const N = 16;
|
|
1446
|
+
const pts = [];
|
|
1447
|
+
sample(left, N, pts, true);
|
|
1448
|
+
sample(right, N, pts, false);
|
|
1449
|
+
const t0 = startTangent(left);
|
|
1450
|
+
const t1 = endTangent(right);
|
|
1451
|
+
const { curve } = fitAcross(pts, N, t0, t1);
|
|
1452
|
+
if ([curve.p1, curve.p2].every((p) => Number.isFinite(p[0]) && Number.isFinite(p[1]))) {
|
|
1453
|
+
anchors[pi] = tidy({ ...anchors[pi], out: { x: curve.p1[0], y: curve.p1[1] } });
|
|
1454
|
+
anchors[ni] = tidy({ ...anchors[ni], in: { x: curve.p2[0], y: curve.p2[1] } });
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
anchors.splice(index, 1);
|
|
1459
|
+
let closed = sp.closed;
|
|
1460
|
+
if (!closed && anchors.length) {
|
|
1461
|
+
anchors[0] = tidy({ ...anchors[0], in: null });
|
|
1462
|
+
const last = anchors.length - 1;
|
|
1463
|
+
anchors[last] = tidy({ ...anchors[last], out: null });
|
|
1464
|
+
}
|
|
1465
|
+
if (anchors.length < 3) closed = closed && anchors.length === 2 && anchors.some((a) => a.in || a.out);
|
|
1466
|
+
return { anchors, closed };
|
|
1467
|
+
}
|
|
1468
|
+
function removeAnchorsKeepingShape(sp, indices) {
|
|
1469
|
+
const order = [...new Set(indices)].filter((i) => i >= 0 && i < sp.anchors.length).sort((a, b) => b - a);
|
|
1470
|
+
return order.reduce((acc, i) => removeAnchorKeepingShape(acc, i), { anchors: sp.anchors.map(_chunkQAUIZPYIcjs.cloneAnchor), closed: sp.closed });
|
|
1471
|
+
}
|
|
1472
|
+
function bendSegment(sp, segment, t, p) {
|
|
1473
|
+
const anchors = sp.anchors.map(_chunkQAUIZPYIcjs.cloneAnchor);
|
|
1474
|
+
const n = anchors.length;
|
|
1475
|
+
if (segment < 0 || segment >= _chunkQAUIZPYIcjs.subpathSegmentCount.call(void 0, sp) || !Number.isFinite(t) || !Number.isFinite(p.x) || !Number.isFinite(p.y)) return { anchors, closed: sp.closed };
|
|
1476
|
+
const tc = Math.min(0.95, Math.max(0.05, t));
|
|
1477
|
+
const ia = segment;
|
|
1478
|
+
const ib = (segment + 1) % n;
|
|
1479
|
+
const a = anchors[ia];
|
|
1480
|
+
const b = anchors[ib];
|
|
1481
|
+
const k = 3 * tc * (1 - tc);
|
|
1482
|
+
const ox = (p.x - (a.x + (b.x - a.x) * tc)) / k;
|
|
1483
|
+
const oy = (p.y - (a.y + (b.y - a.y) * tc)) / k;
|
|
1484
|
+
const c1 = { x: a.x + (b.x - a.x) / 3 + ox, y: a.y + (b.y - a.y) / 3 + oy };
|
|
1485
|
+
const c2 = { x: b.x + (a.x - b.x) / 3 + ox, y: b.y + (a.y - b.y) / 3 + oy };
|
|
1486
|
+
anchors[ia] = _chunkQAUIZPYIcjs.moveAnchorHandle.call(void 0, a, "out", c1);
|
|
1487
|
+
anchors[ib] = _chunkQAUIZPYIcjs.moveAnchorHandle.call(void 0, b, "in", c2);
|
|
1488
|
+
return { anchors, closed: sp.closed };
|
|
1489
|
+
}
|
|
1490
|
+
function subpathPointAt(sp, segment, t) {
|
|
1491
|
+
const [x, y] = _chunkGF2RBSDMcjs.segmentPoint.call(void 0, _chunkQAUIZPYIcjs.subpathSegment.call(void 0, sp, segment), t);
|
|
1492
|
+
return { x, y };
|
|
1493
|
+
}
|
|
1494
|
+
function curvatureMoveAnchor(sp, index, q) {
|
|
1495
|
+
const anchors = sp.anchors.map(_chunkQAUIZPYIcjs.cloneAnchor);
|
|
1496
|
+
const a = sp.anchors[index];
|
|
1497
|
+
if (!a) return { anchors, closed: sp.closed };
|
|
1498
|
+
const own = [
|
|
1499
|
+
a.in && !onAnchor(a, a.in) ? { x: a.in.x - a.x, y: a.in.y - a.y } : null,
|
|
1500
|
+
a.out && !onAnchor(a, a.out) ? { x: a.out.x - a.x, y: a.out.y - a.y } : null
|
|
1501
|
+
];
|
|
1502
|
+
const ends = [null, null];
|
|
1503
|
+
neighbours(sp, index).forEach((j, side) => {
|
|
1504
|
+
if (j < 0 || j === index) return;
|
|
1505
|
+
const b = anchors[j];
|
|
1506
|
+
const old = Math.hypot(a.x - b.x, a.y - b.y);
|
|
1507
|
+
const s = old > 1e-9 ? Math.hypot(q.x - b.x, q.y - b.y) / old : 1;
|
|
1508
|
+
const key = side === 0 ? "out" : "in";
|
|
1509
|
+
const h = b[key];
|
|
1510
|
+
if (h) b[key] = { x: b.x + (h.x - b.x) * s, y: b.y + (h.y - b.y) * s };
|
|
1511
|
+
const o = own[side];
|
|
1512
|
+
if (o) own[side] = { x: o.x * s, y: o.y * s };
|
|
1513
|
+
ends[side] = { x: b.x, y: b.y };
|
|
1514
|
+
});
|
|
1515
|
+
const moved = { x: q.x, y: q.y };
|
|
1516
|
+
if (own[0]) moved.in = { x: q.x + own[0].x, y: q.y + own[0].y };
|
|
1517
|
+
if (own[1]) moved.out = { x: q.x + own[1].x, y: q.y + own[1].y };
|
|
1518
|
+
if (a.type) moved.type = a.type;
|
|
1519
|
+
if (a.corner) moved.corner = { ...a.corner };
|
|
1520
|
+
if (a.id) moved.id = a.id;
|
|
1521
|
+
const [prev, next] = ends;
|
|
1522
|
+
if (a.type && a.type !== "corner" && prev && next) {
|
|
1523
|
+
const dx = next.x - prev.x;
|
|
1524
|
+
const dy = next.y - prev.y;
|
|
1525
|
+
const l = Math.hypot(dx, dy);
|
|
1526
|
+
if (l > 1e-9) {
|
|
1527
|
+
const len = (h, to) => h ? Math.hypot(h.x, h.y) : Math.hypot(q.x - to.x, q.y - to.y) / 3;
|
|
1528
|
+
let li = len(own[0], prev);
|
|
1529
|
+
let lo = len(own[1], next);
|
|
1530
|
+
if (a.type === "symmetric") li = lo = (li + lo) / 2;
|
|
1531
|
+
moved.in = { x: q.x - dx * li / l, y: q.y - dy * li / l };
|
|
1532
|
+
moved.out = { x: q.x + dx * lo / l, y: q.y + dy * lo / l };
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
anchors[index] = moved;
|
|
1536
|
+
return { anchors, closed: sp.closed };
|
|
1537
|
+
}
|
|
1538
|
+
function cutSubpathAt(sp, at) {
|
|
1539
|
+
const n = sp.anchors.length;
|
|
1540
|
+
const cuts = [...new Set(at)].filter((i) => Number.isInteger(i) && i >= 0 && i < n && (sp.closed || i > 0 && i < n - 1)).sort((a, b) => a - b);
|
|
1541
|
+
if (!cuts.length || n < 2) return [{ anchors: sp.anchors.map(_chunkQAUIZPYIcjs.cloneAnchor), closed: sp.closed }];
|
|
1542
|
+
const first = cuts[0];
|
|
1543
|
+
const run = sp.closed ? Array.from({ length: n + 1 }, (_, i) => sp.anchors[(first + i) % n]) : sp.anchors;
|
|
1544
|
+
const cutSet = new Set(sp.closed ? [...cuts.map((c) => (c - first + n) % n), n] : cuts);
|
|
1545
|
+
const pieces = [];
|
|
1546
|
+
let cur = [];
|
|
1547
|
+
run.forEach((a, i) => {
|
|
1548
|
+
if (!cutSet.has(i)) {
|
|
1549
|
+
cur.push(_chunkQAUIZPYIcjs.cloneAnchor.call(void 0, a));
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1552
|
+
if (cur.length) {
|
|
1553
|
+
cur.push(tidy({ ...a, out: null }, false));
|
|
1554
|
+
pieces.push({ anchors: cur, closed: false });
|
|
1555
|
+
cur = [];
|
|
1556
|
+
}
|
|
1557
|
+
if (i < n) cur.push(tidy({ ...a, in: null }, false));
|
|
1558
|
+
});
|
|
1559
|
+
if (cur.length > 1) pieces.push({ anchors: cur, closed: false });
|
|
1560
|
+
return pieces;
|
|
1561
|
+
}
|
|
1562
|
+
function splitSubpathAt(sp, segment, t, endTolerance = 1e-6) {
|
|
1563
|
+
const n = sp.anchors.length;
|
|
1564
|
+
if (segment < 0 || segment >= _chunkQAUIZPYIcjs.subpathSegmentCount.call(void 0, sp)) return [{ anchors: sp.anchors.map(_chunkQAUIZPYIcjs.cloneAnchor), closed: sp.closed }];
|
|
1565
|
+
if (t <= endTolerance) return cutSubpathAt(sp, [segment]);
|
|
1566
|
+
if (t >= 1 - endTolerance) return cutSubpathAt(sp, [(segment + 1) % n]);
|
|
1567
|
+
const { subpath, index } = _chunkQAUIZPYIcjs.insertAnchor.call(void 0, sp, segment, t);
|
|
1568
|
+
return cutSubpathAt(subpath, [index]);
|
|
1569
|
+
}
|
|
1570
|
+
var endOf = (sp, last) => sp.anchors[last ? sp.anchors.length - 1 : 0];
|
|
1571
|
+
var dist = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
|
|
1572
|
+
function reverse(sp) {
|
|
1573
|
+
return {
|
|
1574
|
+
closed: sp.closed,
|
|
1575
|
+
anchors: sp.anchors.slice().reverse().map((a) => {
|
|
1576
|
+
const next = { x: a.x, y: a.y };
|
|
1577
|
+
if (a.out) next.in = { ...a.out };
|
|
1578
|
+
if (a.in) next.out = { ...a.in };
|
|
1579
|
+
if (a.type) next.type = a.type;
|
|
1580
|
+
if (a.corner) next.corner = { ...a.corner };
|
|
1581
|
+
if (a.id) next.id = a.id;
|
|
1582
|
+
return next;
|
|
1583
|
+
})
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
function joinSubpaths(subpaths, tolerance) {
|
|
1587
|
+
const closed = [];
|
|
1588
|
+
let open = [];
|
|
1589
|
+
for (const sp of subpaths) {
|
|
1590
|
+
const copy = { anchors: sp.anchors.map(_chunkQAUIZPYIcjs.cloneAnchor), closed: sp.closed };
|
|
1591
|
+
if (sp.closed || sp.anchors.length < 2) {
|
|
1592
|
+
if (sp.anchors.length) closed.push(copy);
|
|
1593
|
+
} else open.push(copy);
|
|
1594
|
+
}
|
|
1595
|
+
if (open.length === 1) {
|
|
1596
|
+
const sp = open[0];
|
|
1597
|
+
const first = sp.anchors[0];
|
|
1598
|
+
const last = sp.anchors[sp.anchors.length - 1];
|
|
1599
|
+
if (sp.anchors.length > 2 && dist(first, last) <= tolerance) {
|
|
1600
|
+
sp.anchors.pop();
|
|
1601
|
+
sp.anchors[0] = tidy({ ...first, in: _nullishCoalesce(last.in, () => ( null)) }, false);
|
|
1602
|
+
} else {
|
|
1603
|
+
sp.anchors[0] = tidy({ ...first, in: null }, false);
|
|
1604
|
+
sp.anchors[sp.anchors.length - 1] = tidy({ ...sp.anchors[sp.anchors.length - 1], out: null }, false);
|
|
1605
|
+
}
|
|
1606
|
+
sp.closed = true;
|
|
1607
|
+
return [...closed, sp];
|
|
1608
|
+
}
|
|
1609
|
+
while (open.length > 1) {
|
|
1610
|
+
let best = { d: Infinity, i: 0, j: 1, ei: true, ej: false };
|
|
1611
|
+
for (let i = 0; i < open.length; i++) {
|
|
1612
|
+
for (let j = i + 1; j < open.length; j++) {
|
|
1613
|
+
for (const ei of [false, true]) {
|
|
1614
|
+
for (const ej of [false, true]) {
|
|
1615
|
+
const d = dist(endOf(open[i], ei), endOf(open[j], ej));
|
|
1616
|
+
if (d < best.d) best = { d, i, j, ei, ej };
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
let a = open[best.i];
|
|
1622
|
+
let b = open[best.j];
|
|
1623
|
+
open = open.filter((_, k) => k !== best.j);
|
|
1624
|
+
if (!best.ei) a = reverse(a);
|
|
1625
|
+
if (best.ej) b = reverse(b);
|
|
1626
|
+
const anchors = a.anchors;
|
|
1627
|
+
if (best.d <= tolerance) {
|
|
1628
|
+
const [head, ...rest] = b.anchors;
|
|
1629
|
+
const l = anchors[anchors.length - 1];
|
|
1630
|
+
anchors[anchors.length - 1] = tidy({ ...l, out: _nullishCoalesce(head.out, () => ( null)) }, false);
|
|
1631
|
+
anchors.push(...rest);
|
|
1632
|
+
} else {
|
|
1633
|
+
const l = anchors[anchors.length - 1];
|
|
1634
|
+
anchors[anchors.length - 1] = tidy({ ...l, out: null }, false);
|
|
1635
|
+
anchors.push(tidy({ ...b.anchors[0], in: null }, false), ...b.anchors.slice(1));
|
|
1636
|
+
}
|
|
1637
|
+
open[best.i > best.j ? best.i - 1 : best.i] = { anchors, closed: false };
|
|
1638
|
+
}
|
|
1639
|
+
return [...closed, ...open];
|
|
1640
|
+
}
|
|
1641
|
+
function smoothSubpath(sp, amount = 1, only) {
|
|
1642
|
+
const t = Math.min(1, Math.max(0, amount));
|
|
1643
|
+
const orig = sp.anchors;
|
|
1644
|
+
const n = orig.length;
|
|
1645
|
+
const anchors = orig.map(_chunkQAUIZPYIcjs.cloneAnchor);
|
|
1646
|
+
if (n < 3 || t === 0) return { anchors, closed: sp.closed };
|
|
1647
|
+
for (let i = 0; i < n; i++) {
|
|
1648
|
+
if (!sp.closed && (i === 0 || i === n - 1)) continue;
|
|
1649
|
+
if (only && !only.has(i)) continue;
|
|
1650
|
+
const prev = orig[(i + n - 1) % n];
|
|
1651
|
+
const next = orig[(i + 1) % n];
|
|
1652
|
+
const p = orig[i];
|
|
1653
|
+
const dir = unit(next.x - prev.x, next.y - prev.y);
|
|
1654
|
+
if (!dir) continue;
|
|
1655
|
+
const li = dist(p, prev) / 3;
|
|
1656
|
+
const lo = dist(p, next) / 3;
|
|
1657
|
+
const hin = _nullishCoalesce(p.in, () => ( p));
|
|
1658
|
+
const hout = _nullishCoalesce(p.out, () => ( p));
|
|
1659
|
+
const target = (h, x, y) => ({ x: h.x + (x - h.x) * t, y: h.y + (y - h.y) * t });
|
|
1660
|
+
anchors[i] = tidy(
|
|
1661
|
+
{
|
|
1662
|
+
x: p.x,
|
|
1663
|
+
y: p.y,
|
|
1664
|
+
in: target(hin, p.x - dir[0] * li, p.y - dir[1] * li),
|
|
1665
|
+
out: target(hout, p.x + dir[0] * lo, p.y + dir[1] * lo)
|
|
1666
|
+
},
|
|
1667
|
+
false
|
|
1668
|
+
);
|
|
1669
|
+
}
|
|
1670
|
+
return { anchors, closed: sp.closed };
|
|
1671
|
+
}
|
|
1672
|
+
function segsOf(sp) {
|
|
1673
|
+
const out = [];
|
|
1674
|
+
for (let i = 0; i < _chunkQAUIZPYIcjs.subpathSegmentCount.call(void 0, sp); i++) {
|
|
1675
|
+
const s = _chunkQAUIZPYIcjs.subpathSegment.call(void 0, sp, i);
|
|
1676
|
+
out.push(s.length === 2 ? { c: s, line: true } : { c: s, line: false });
|
|
1677
|
+
}
|
|
1678
|
+
return out;
|
|
1679
|
+
}
|
|
1680
|
+
function segsToSubpath(segs, closed, types) {
|
|
1681
|
+
if (!segs.length) return null;
|
|
1682
|
+
const anchors = [];
|
|
1683
|
+
const at = (p) => ({ x: p[0], y: p[1] });
|
|
1684
|
+
anchors.push(at(segs[0].c[0]));
|
|
1685
|
+
for (const s of segs) {
|
|
1686
|
+
const end = s.c[s.c.length - 1];
|
|
1687
|
+
if (!s.line) anchors[anchors.length - 1].out = { x: s.c[1][0], y: s.c[1][1] };
|
|
1688
|
+
const next = at(end);
|
|
1689
|
+
if (!s.line) next.in = { x: s.c[2][0], y: s.c[2][1] };
|
|
1690
|
+
anchors.push(next);
|
|
1691
|
+
}
|
|
1692
|
+
if (closed) {
|
|
1693
|
+
const last = anchors.pop();
|
|
1694
|
+
if (last.in) anchors[0].in = last.in;
|
|
1695
|
+
}
|
|
1696
|
+
const typed = anchors.map((a) => {
|
|
1697
|
+
const type = types.get(`${a.x},${a.y}`);
|
|
1698
|
+
return tidy(type ? { ...a, type } : a, !!type);
|
|
1699
|
+
});
|
|
1700
|
+
return { anchors: typed, closed };
|
|
1701
|
+
}
|
|
1702
|
+
var isZeroSeg = (s) => s.c.every((p) => Math.abs(p[0] - s.c[0][0]) < 1e-9 && Math.abs(p[1] - s.c[0][1]) < 1e-9);
|
|
1703
|
+
function mergePair(a, b, tol) {
|
|
1704
|
+
const a0 = a.c[0];
|
|
1705
|
+
const a3 = a.c[a.c.length - 1];
|
|
1706
|
+
const b3 = b.c[b.c.length - 1];
|
|
1707
|
+
if (a.line && b.line) {
|
|
1708
|
+
const cx = b3[0] - a0[0];
|
|
1709
|
+
const cy = b3[1] - a0[1];
|
|
1710
|
+
const len = Math.hypot(cx, cy);
|
|
1711
|
+
if (len < 1e-12) return null;
|
|
1712
|
+
const mx = a3[0] - a0[0];
|
|
1713
|
+
const my = a3[1] - a0[1];
|
|
1714
|
+
const along = (mx * cx + my * cy) / len;
|
|
1715
|
+
return Math.abs((mx * cy - my * cx) / len) <= tol && along >= 0 && along <= len ? { c: [a0, b3], line: true } : null;
|
|
1716
|
+
}
|
|
1717
|
+
const ca = a.line ? [a0, a0, a3, a3] : a.c;
|
|
1718
|
+
const cb = b.line ? [a3, a3, b3, b3] : b.c;
|
|
1719
|
+
const ta = endTangent(ca);
|
|
1720
|
+
const tb = startTangent(cb);
|
|
1721
|
+
if (ta[0] * tb[0] + ta[1] * tb[1] < 0.99) return null;
|
|
1722
|
+
const pts = [];
|
|
1723
|
+
sample(ca, 16, pts, true);
|
|
1724
|
+
sample(cb, 16, pts, false);
|
|
1725
|
+
const { curve, error } = fitAcross(pts, 16, startTangent(ca), endTangent(cb));
|
|
1726
|
+
return error <= tol ? { c: [curve.p0, curve.p1, curve.p2, curve.p3], line: false } : null;
|
|
1727
|
+
}
|
|
1728
|
+
function removeRedundantPoints(sp, tolerance) {
|
|
1729
|
+
const tol = Math.max(tolerance, 1e-9);
|
|
1730
|
+
const types = new Map(sp.anchors.filter((a) => a.type).map((a) => [`${a.x},${a.y}`, a.type]));
|
|
1731
|
+
const segs = segsOf(sp).filter((s) => !isZeroSeg(s));
|
|
1732
|
+
if (!segs.length) return { anchors: sp.anchors.map(_chunkQAUIZPYIcjs.cloneAnchor), closed: sp.closed };
|
|
1733
|
+
const minSegs = sp.closed ? 2 : 1;
|
|
1734
|
+
let changed = true;
|
|
1735
|
+
while (changed && segs.length > minSegs) {
|
|
1736
|
+
changed = false;
|
|
1737
|
+
const n = segs.length;
|
|
1738
|
+
const joints = sp.closed ? n : n - 1;
|
|
1739
|
+
for (let j = 0; j < joints; j++) {
|
|
1740
|
+
const [i0, i1] = sp.closed ? [(j + n - 1) % n, j] : [j, j + 1];
|
|
1741
|
+
const m = mergePair(segs[i0], segs[i1], tol);
|
|
1742
|
+
if (!m) continue;
|
|
1743
|
+
segs[i0] = m;
|
|
1744
|
+
segs.splice(i1, 1);
|
|
1745
|
+
if (sp.closed && i1 === 0) segs.unshift(segs.pop());
|
|
1746
|
+
changed = true;
|
|
1747
|
+
break;
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
return _nullishCoalesce(segsToSubpath(segs, sp.closed, types), () => ( { anchors: sp.anchors.map(_chunkQAUIZPYIcjs.cloneAnchor), closed: sp.closed }));
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
|
|
1754
|
+
|
|
1755
|
+
|
|
1756
|
+
|
|
1757
|
+
|
|
1758
|
+
|
|
1759
|
+
|
|
1760
|
+
|
|
1761
|
+
|
|
1762
|
+
|
|
1345
1763
|
|
|
1346
1764
|
|
|
1347
1765
|
|
|
@@ -1350,4 +1768,4 @@ async function unwoff(view) {
|
|
|
1350
1768
|
|
|
1351
1769
|
|
|
1352
1770
|
|
|
1353
|
-
exports.VECTOR_TRACE_MAX_PIXELS = VECTOR_TRACE_MAX_PIXELS; exports.vectorTraceContours = vectorTraceContours; exports.traceField = traceField; exports.quantizeVectorTraceColors = quantizeVectorTraceColors; exports.traceVectorImage = traceVectorImage; exports.parseOpenTypeFont = parseOpenTypeFont; exports.loadOpenTypeFont = loadOpenTypeFont;
|
|
1771
|
+
exports.VECTOR_TRACE_MAX_PIXELS = VECTOR_TRACE_MAX_PIXELS; exports.vectorTraceContours = vectorTraceContours; exports.traceField = traceField; exports.quantizeVectorTraceColors = quantizeVectorTraceColors; exports.traceVectorImage = traceVectorImage; exports.parseOpenTypeFont = parseOpenTypeFont; exports.loadOpenTypeFont = loadOpenTypeFont; exports.removeAnchorKeepingShape = removeAnchorKeepingShape; exports.removeAnchorsKeepingShape = removeAnchorsKeepingShape; exports.bendSegment = bendSegment; exports.subpathPointAt = subpathPointAt; exports.curvatureMoveAnchor = curvatureMoveAnchor; exports.cutSubpathAt = cutSubpathAt; exports.splitSubpathAt = splitSubpathAt; exports.joinSubpaths = joinSubpaths; exports.smoothSubpath = smoothSubpath; exports.removeRedundantPoints = removeRedundantPoints;
|
|
@@ -2,7 +2,7 @@ import { AdjustmentKind, AdjustmentLayer, AdjustmentParamsMap, Annotation, Creat
|
|
|
2
2
|
import { type PathToSelectionOptions, type SelectionToPathOptions } from './selection/path.cjs';
|
|
3
3
|
import { type MagneticEdgeOptions } from './selection/magnetic.cjs';
|
|
4
4
|
import type { ObjectTarget } from './selection/segment/grabcut.cjs';
|
|
5
|
-
import { type DocumentPath } from './paths.cjs';
|
|
5
|
+
import { type DocumentPath, type PathEditTarget } from './paths.cjs';
|
|
6
6
|
import { type SelectionJob, type SelectionJobResult, type SelectionWorkerFactory } from './selection/worker.cjs';
|
|
7
7
|
import type { SelectionProgress } from './selection/segment/work.cjs';
|
|
8
8
|
import type { FocusAreaOptions } from './selection/segment/focus.cjs';
|
|
@@ -20,7 +20,7 @@ import { type RefineEdit } from './selection/refine-edits.cjs';
|
|
|
20
20
|
import { type LayerMaskProps } from './selection/layer-mask.cjs';
|
|
21
21
|
import { type AlphaChannel } from './selection/channels.cjs';
|
|
22
22
|
import type { AdjustmentLayerMask } from './types.cjs';
|
|
23
|
-
import { type LayerBlendMode, type ImageLayer, type LayerFilter, type LayerTarget, type PixelLayer, type RasterMask, type SmartContents, type SmartFilter, type SmartPlacement, type LiveShape, type ShapeFillRule, type VectorMask, type ShapeOperation, type ShapePaint, type ShapeStroke, type ParagraphStyle, type TextStyle, type TextOrientation, type TypeLayer, type ImageEffectAnimation } from './layers/model.cjs';
|
|
23
|
+
import { type LayerBlendMode, type ImageLayer, type LayerFilter, type LayerTarget, type PixelLayer, type RasterMask, type SmartContents, type SmartFilter, type SmartPlacement, type LiveShape, type ShapeComponent, type ShapeFillRule, type VectorMask, type ShapeOperation, type ShapePaint, type ShapeStroke, type ParagraphStyle, type TextStyle, type TextOrientation, type TypeLayer, type ImageEffectAnimation } from './layers/model.cjs';
|
|
24
24
|
import { type TextLayout, type TextShaper } from './layers/text.cjs';
|
|
25
25
|
import type { VectorSubpath } from '../vector/types.cjs';
|
|
26
26
|
import { EffectsSlot } from '../effects/helpers.cjs';
|
|
@@ -1111,6 +1111,26 @@ export declare class ImageEditorController {
|
|
|
1111
1111
|
}): DocumentPath;
|
|
1112
1112
|
/** Replaces a path's geometry (its name is kept). One undo step. */
|
|
1113
1113
|
setPathGeometry(id: string, input: VectorMaskPathInput): DocumentPath;
|
|
1114
|
+
/**
|
|
1115
|
+
* What the path tools (Path Selection, Direct Selection, Pen) edit: the active path in the Paths
|
|
1116
|
+
* panel, else the active layer when it is a shape, else the active layer's vector mask; null
|
|
1117
|
+
* when there is none of these (the Pen then starts a new Work Path).
|
|
1118
|
+
*/
|
|
1119
|
+
pathEditTarget(): PathEditTarget | null;
|
|
1120
|
+
/** The components a path tool target holds (document pixels). Throws for a target that is gone. */
|
|
1121
|
+
getPathEditComponents(target: PathEditTarget): ShapeComponent[];
|
|
1122
|
+
/**
|
|
1123
|
+
* Replaces a path tool target's components: one undo step, or none with `commitToHistory` false
|
|
1124
|
+
* (the later moves of a drag whose first move recorded one). A component that still has its
|
|
1125
|
+
* `live` shape is kept as given (drop `live` from one whose anchors changed); a shape layer keeps
|
|
1126
|
+
* at least one component.
|
|
1127
|
+
*/
|
|
1128
|
+
setPathEditComponents(target: PathEditTarget, components: readonly ShapeComponent[], commitToHistory?: boolean): void;
|
|
1129
|
+
/**
|
|
1130
|
+
* The Pen with no path to add to: a new Work Path from `components` (replacing the last one,
|
|
1131
|
+
* unless it was saved), made the active path. One undo step. Returns the path.
|
|
1132
|
+
*/
|
|
1133
|
+
startWorkPath(components: readonly ShapeComponent[]): DocumentPath;
|
|
1114
1134
|
/** Renames a path; renaming the Work Path saves it (Photoshop's Save Path). One undo step. */
|
|
1115
1135
|
renamePath(id: string, name: string): DocumentPath;
|
|
1116
1136
|
/** Duplicates a path as a saved path. One undo step. */
|
|
@@ -2,7 +2,7 @@ import { AdjustmentKind, AdjustmentLayer, AdjustmentParamsMap, Annotation, Creat
|
|
|
2
2
|
import { type PathToSelectionOptions, type SelectionToPathOptions } from './selection/path.js';
|
|
3
3
|
import { type MagneticEdgeOptions } from './selection/magnetic.js';
|
|
4
4
|
import type { ObjectTarget } from './selection/segment/grabcut.js';
|
|
5
|
-
import { type DocumentPath } from './paths.js';
|
|
5
|
+
import { type DocumentPath, type PathEditTarget } from './paths.js';
|
|
6
6
|
import { type SelectionJob, type SelectionJobResult, type SelectionWorkerFactory } from './selection/worker.js';
|
|
7
7
|
import type { SelectionProgress } from './selection/segment/work.js';
|
|
8
8
|
import type { FocusAreaOptions } from './selection/segment/focus.js';
|
|
@@ -20,7 +20,7 @@ import { type RefineEdit } from './selection/refine-edits.js';
|
|
|
20
20
|
import { type LayerMaskProps } from './selection/layer-mask.js';
|
|
21
21
|
import { type AlphaChannel } from './selection/channels.js';
|
|
22
22
|
import type { AdjustmentLayerMask } from './types.js';
|
|
23
|
-
import { type LayerBlendMode, type ImageLayer, type LayerFilter, type LayerTarget, type PixelLayer, type RasterMask, type SmartContents, type SmartFilter, type SmartPlacement, type LiveShape, type ShapeFillRule, type VectorMask, type ShapeOperation, type ShapePaint, type ShapeStroke, type ParagraphStyle, type TextStyle, type TextOrientation, type TypeLayer, type ImageEffectAnimation } from './layers/model.js';
|
|
23
|
+
import { type LayerBlendMode, type ImageLayer, type LayerFilter, type LayerTarget, type PixelLayer, type RasterMask, type SmartContents, type SmartFilter, type SmartPlacement, type LiveShape, type ShapeComponent, type ShapeFillRule, type VectorMask, type ShapeOperation, type ShapePaint, type ShapeStroke, type ParagraphStyle, type TextStyle, type TextOrientation, type TypeLayer, type ImageEffectAnimation } from './layers/model.js';
|
|
24
24
|
import { type TextLayout, type TextShaper } from './layers/text.js';
|
|
25
25
|
import type { VectorSubpath } from '../vector/types.js';
|
|
26
26
|
import { EffectsSlot } from '../effects/helpers.js';
|
|
@@ -1111,6 +1111,26 @@ export declare class ImageEditorController {
|
|
|
1111
1111
|
}): DocumentPath;
|
|
1112
1112
|
/** Replaces a path's geometry (its name is kept). One undo step. */
|
|
1113
1113
|
setPathGeometry(id: string, input: VectorMaskPathInput): DocumentPath;
|
|
1114
|
+
/**
|
|
1115
|
+
* What the path tools (Path Selection, Direct Selection, Pen) edit: the active path in the Paths
|
|
1116
|
+
* panel, else the active layer when it is a shape, else the active layer's vector mask; null
|
|
1117
|
+
* when there is none of these (the Pen then starts a new Work Path).
|
|
1118
|
+
*/
|
|
1119
|
+
pathEditTarget(): PathEditTarget | null;
|
|
1120
|
+
/** The components a path tool target holds (document pixels). Throws for a target that is gone. */
|
|
1121
|
+
getPathEditComponents(target: PathEditTarget): ShapeComponent[];
|
|
1122
|
+
/**
|
|
1123
|
+
* Replaces a path tool target's components: one undo step, or none with `commitToHistory` false
|
|
1124
|
+
* (the later moves of a drag whose first move recorded one). A component that still has its
|
|
1125
|
+
* `live` shape is kept as given (drop `live` from one whose anchors changed); a shape layer keeps
|
|
1126
|
+
* at least one component.
|
|
1127
|
+
*/
|
|
1128
|
+
setPathEditComponents(target: PathEditTarget, components: readonly ShapeComponent[], commitToHistory?: boolean): void;
|
|
1129
|
+
/**
|
|
1130
|
+
* The Pen with no path to add to: a new Work Path from `components` (replacing the last one,
|
|
1131
|
+
* unless it was saved), made the active path. One undo step. Returns the path.
|
|
1132
|
+
*/
|
|
1133
|
+
startWorkPath(components: readonly ShapeComponent[]): DocumentPath;
|
|
1114
1134
|
/** Renames a path; renaming the Work Path saves it (Photoshop's Save Path). One undo step. */
|
|
1115
1135
|
renamePath(id: string, name: string): DocumentPath;
|
|
1116
1136
|
/** Duplicates a path as a saved path. One undo step. */
|